diff options
202 files changed, 33309 insertions, 1182 deletions
@@ -10,6 +10,11 @@ For a more comprehensive changelog of the latest experimental code, see: - Added optional "pause, when entering commands" feature, that was only available in the original interpreter for Hercules rendering. + SCI: + - Fix a missing dialog line in QfG3 which awards the player with 3 additional + points. This is a bug in the original game interpreter. Due to this bug, + it was not possible to get all points in the original game. + 1.8.1 (2016-05-25) New ports: - Added Nintendo 3DS port. diff --git a/backends/platform/3ds/sprite.cpp b/backends/platform/3ds/sprite.cpp index a0aee385a9..f97c611473 100644 --- a/backends/platform/3ds/sprite.cpp +++ b/backends/platform/3ds/sprite.cpp @@ -62,7 +62,7 @@ void Sprite::create(uint16 width, uint16 height, const Graphics::PixelFormat &f) actualHeight = height; format = f; w = MAX(nextHigher2(width), 64u); - h = MAX(nextHigher2(height), 64u);; + h = MAX(nextHigher2(height), 64u); pitch = w * format.bytesPerPixel; dirtyPixels = true; diff --git a/backends/platform/android/android.mk b/backends/platform/android/android.mk index e11ad0724e..38128c62b0 100644 --- a/backends/platform/android/android.mk +++ b/backends/platform/android/android.mk @@ -3,7 +3,7 @@ # These must be incremented for each market upload ANDROID_VERSIONCODE = 6 -ANDROID_TARGET_VERSION = 14 +ANDROID_TARGET_VERSION = 23 NDK_BUILD = $(ANDROID_NDK)/ndk-build APP_ABI=$(ABI) SDK_ANDROID = $(ANDROID_SDK)/tools/android @@ -20,6 +20,7 @@ RESOURCES = \ $(PATH_BUILD_RES)/layout/main.xml \ $(PATH_BUILD_RES)/drawable/scummvm.png \ $(PATH_BUILD_RES)/drawable/scummvm_big.png \ + $(PATH_BUILD_RES)/drawable-xhdpi/leanback_icon.png \ $(PATH_BUILD_RES)/drawable-xhdpi/ouya_icon.png DIST_ANDROID_MK = $(PATH_DIST)/jni/Android.mk diff --git a/backends/platform/android/events.cpp b/backends/platform/android/events.cpp index 8039981a92..b146945a01 100644 --- a/backends/platform/android/events.cpp +++ b/backends/platform/android/events.cpp @@ -101,7 +101,9 @@ enum { JKEYCODE_MEDIA_NEXT = 87, JKEYCODE_MEDIA_PREVIOUS = 88, JKEYCODE_MEDIA_REWIND = 89, - JKEYCODE_MEDIA_FAST_FORWARD = 90 + JKEYCODE_MEDIA_FAST_FORWARD = 90, + JKEYCODE_MEDIA_PLAY = 126, + JKEYCODE_MEDIA_PAUSE = 127 }; // five-way navigation control @@ -380,6 +382,19 @@ void OSystem_Android::pushEvent(int type, int arg1, int arg2, int arg3, return; + case JKEYCODE_MEDIA_PAUSE: + case JKEYCODE_MEDIA_PLAY: + case JKEYCODE_MEDIA_PLAY_PAUSE: + if (arg1 == JACTION_DOWN) { + e.type = Common::EVENT_MAINMENU; + + lockMutex(_event_queue_lock); + _event_queue.push(e); + unlockMutex(_event_queue_lock); + } + + return; + case JKEYCODE_CAMERA: case JKEYCODE_SEARCH: if (arg1 == JACTION_DOWN) @@ -888,6 +903,10 @@ void OSystem_Android::pushEvent(int type, int arg1, int arg2, int arg3, e.kbd.ascii = Common::ASCII_ESCAPE; break; + case JKEYCODE_BUTTON_Y: + e.type = Common::EVENT_MAINMENU; + break; + default: LOGW("unmapped gamepad key: %d", arg2); return; diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java b/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java index 32c65d3395..e81000d8b1 100644 --- a/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java +++ b/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java @@ -119,6 +119,8 @@ public class ScummVMEvents implements case KeyEvent.KEYCODE_MENU: case KeyEvent.KEYCODE_CAMERA: case KeyEvent.KEYCODE_SEARCH: + case KeyEvent.KEYCODE_MEDIA_PLAY: + case KeyEvent.KEYCODE_MEDIA_PAUSE: break; default: diff --git a/common/array.h b/common/array.h index db1a62ba34..e9b97aa38a 100644 --- a/common/array.h +++ b/common/array.h @@ -361,6 +361,84 @@ protected: }; +/** + * Double linked list with sorted nodes. + */ +template<class T> +class SortedArray : public Array<T> { +public: + typedef T *iterator; + typedef uint size_type; + + SortedArray(int (*comparator)(const void *, const void *)) { + _comparator = comparator; + } + + /** + * Inserts element at the sorted position. + */ + void insert(const T &element) { + if (!this->_size) { + this->insert_aux(this->_storage, &element, &element + 1); + return; + } + + T *where = (T *)bsearchMin(element, this->front(), this->_size, sizeof(T), _comparator); + insert(where, element); + } + + T &operator[](size_type idx) { + error("Operation not allowed with SortedArray"); + } + + void insert_at(size_type idx, const T &element) { + error("Operation not allowed with SortedArray"); + } + + void insert_at(size_type idx, const Array<T> &array) { + error("Operation not allowed with SortedArray"); + } + + void insert(iterator pos, const T &element) { + error("Operation not allowed with SortedArray"); + } + + void push_back(const T &element) { + error("Operation not allowed with SortedArray"); + } + + void push_back(const Array<T> &array) { + error("Operation not allowed with SortedArray"); + } + +private: + // Based on code Copyright (C) 2008-2009 Ksplice, Inc. + // Author: Tim Abbott <tabbott@ksplice.com> + // Licensed under GPLv2+ + void *bsearchMin(void *key, void *base, uint num, uint size_, + int (*cmp)(const void *key, const void *elt)) { + uint start_ = 0, end_ = num; + int result; + + while (start_ < end_) { + uint mid = start_ + (end_ - start_) / 2; + + result = cmp(key, (byte *)base + mid * size_); + if (result < 0) + end_ = mid; + else if (result > 0) + start_ = mid + 1; + else + return (void *)((byte *)base + mid * size_); + } + + return (void *)((byte *)base + start_ * size_); + } + +private: + int (*_comparator)(const void *, const void *); +}; + } // End of namespace Common #endif diff --git a/common/recorderfile.cpp b/common/recorderfile.cpp index 71f8272b44..04802aa0c8 100644 --- a/common/recorderfile.cpp +++ b/common/recorderfile.cpp @@ -30,6 +30,8 @@ #include "graphics/surface.h" #include "graphics/scaler.h" +#ifdef ENABLE_EVENTRECORDER + #define RECORD_VERSION 1 namespace Common { @@ -714,3 +716,5 @@ void PlaybackFile::checkRecordedMD5() { } + +#endif // ENABLE_EVENTRECORDER diff --git a/common/scummsys.h b/common/scummsys.h index 5e1069fb46..3513ee2d7d 100644 --- a/common/scummsys.h +++ b/common/scummsys.h @@ -215,6 +215,10 @@ #include "config.h" #endif +// Now we need to adjust some settings when running tests +#ifdef COMPILING_TESTS +#undef ENABLE_EVENTRECORDER +#endif // In the following we configure various targets, in particular those // which can't use our "configure" tool and hence don't use config.h. diff --git a/common/str.cpp b/common/str.cpp index c41e958349..3ff49a90c6 100644 --- a/common/str.cpp +++ b/common/str.cpp @@ -144,7 +144,7 @@ void String::ensureCapacity(uint32 new_size, bool keep_old) { // Allocate new storage newStorage = new char[newCapacity]; assert(newStorage); - + // Copy old data if needed, elsewise reset the new storage. if (keep_old) { @@ -447,12 +447,12 @@ void String::replace(uint32 pos, uint32 count, const char *str) { replace(pos, count, str, 0, strlen(str)); } -void String::replace(iterator begin, iterator end, const String &str) { - replace(begin - _str, end - begin, str._str, 0, str._size); +void String::replace(iterator begin_, iterator end_, const String &str) { + replace(begin_ - _str, end_ - begin_, str._str, 0, str._size); } -void String::replace(iterator begin, iterator end, const char *str) { - replace(begin - _str, end - begin, str, 0, strlen(str)); +void String::replace(iterator begin_, iterator end_, const char *str) { + replace(begin_ - _str, end_ - begin_, str, 0, strlen(str)); } void String::replace(uint32 posOri, uint32 countOri, const String &str, @@ -472,21 +472,21 @@ void String::replace(uint32 posOri, uint32 countOri, const char *str, _size = newSize; // Push the old characters to the end of the string - for (uint32 i = _size; i >= posOri + countDest; i--) + for (uint32 i = _size; i >= posOri + countDest; i--) _str[i] = _str[i - offset]; } else if (countOri > countDest){ uint32 offset = countOri - countDest; ///< Number of positions that we have to pull back // Pull the remainder string back - for (uint32 i = posOri + countDest; i < _size; i++) - _str[i] = _str[i + offset]; + for (uint32 i = posOri + countDest; i < _size; i++) + _str[i] = _str[i + offset]; - _size -= offset; + _size -= offset; } // Copy the replaced part of the string - for (uint32 i = 0; i < countDest; i++) + for (uint32 i = 0; i < countDest; i++) _str[posOri + i] = str[posDest + i]; } diff --git a/dists/android/AndroidManifest.xml b/dists/android/AndroidManifest.xml index d605d9b3cc..14161668fe 100644 --- a/dists/android/AndroidManifest.xml +++ b/dists/android/AndroidManifest.xml @@ -15,6 +15,7 @@ <application android:label="@string/app_name" android:description="@string/app_desc" + android:isGame="true" android:icon="@drawable/scummvm"> <activity android:name=".ScummVMActivity" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" @@ -27,6 +28,14 @@ <category android:name="tv.ouya.intent.category.GAME"/> </intent-filter> </activity> + <activity android:name=".ScummVMActivity" + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:banner="@drawable/leanback_icon"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LEANBACK_LAUNCHER"/> + </intent-filter> + </activity> </application> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> @@ -42,4 +51,11 @@ <uses-configuration android:reqTouchScreen="stylus" android:reqKeyboardType="qwerty"/> + + <uses-feature android:name="android.hardware.touchscreen" + android:required="false" /> + + <uses-feature android:name="android.software.leanback" + android:required="false" /> + </manifest> diff --git a/dists/android/AndroidManifest.xml.in b/dists/android/AndroidManifest.xml.in index d90e282e3d..de2f2d905e 100644 --- a/dists/android/AndroidManifest.xml.in +++ b/dists/android/AndroidManifest.xml.in @@ -15,6 +15,7 @@ <application android:label="@string/app_name" android:description="@string/app_desc" + android:isGame="true" android:icon="@drawable/scummvm"> <activity android:name=".ScummVMActivity" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" @@ -27,6 +28,14 @@ <category android:name="tv.ouya.intent.category.GAME"/> </intent-filter> </activity> + <activity android:name=".ScummVMActivity" + android:theme="@android:style/Theme.NoTitleBar.Fullscreen" + android:banner="@drawable/leanback_icon"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LEANBACK_LAUNCHER"/> + </intent-filter> + </activity> </application> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> @@ -42,4 +51,11 @@ <uses-configuration android:reqTouchScreen="stylus" android:reqKeyboardType="qwerty"/> + + <uses-feature android:name="android.hardware.touchscreen" + android:required="false" /> + + <uses-feature android:name="android.software.leanback" + android:required="false" /> + </manifest> diff --git a/dists/android/res/drawable-xhdpi/leanback_icon.png b/dists/android/res/drawable-xhdpi/leanback_icon.png Binary files differnew file mode 100644 index 0000000000..28a7196b7f --- /dev/null +++ b/dists/android/res/drawable-xhdpi/leanback_icon.png diff --git a/dists/macosx/scummvm_appcast.xml b/dists/macosx/scummvm_appcast.xml index 3d47c94bbc..35fbc54908 100644 --- a/dists/macosx/scummvm_appcast.xml +++ b/dists/macosx/scummvm_appcast.xml @@ -6,6 +6,16 @@ <description>Most recent changes with links to updates.</description> <language>en</language> <item> + <title>Version 1.8.1</title> + <sparkle:releaseNotesLink> + https://scummvm.org/frs/scummvm/1.8.1/ReleaseNotes + </sparkle:releaseNotesLink> + <pubDate>Wed, 25 May 2016 19:26:00 +0000</pubDate> + <enclosure url="https://www.scummvm.org/frs/scummvm/1.8.1/scummvm-1.8.1-macosx.dmg" + sparkle:version="1.8.1" length="15791070" type="application/octet-stream" + sparkle:dsaSignature="MC0CFQDF0u/pGH51pMPzCbsv07eCNxuGDQIUdrKWVTznbF69fzuzIieR4Lc0U2Y=" /> + </item> + <item> <title>Version 1.8.0</title> <sparkle:releaseNotesLink> https://scummvm.org/frs/scummvm/1.8.0/ReleaseNotes diff --git a/doc/de/Neues b/doc/de/Neues index 8015feb05b..8d459a1864 100644 --- a/doc/de/Neues +++ b/doc/de/Neues @@ -11,6 +11,11 @@ Programmcodes finden Sie auf Englisch unter: Diese Funktion war im originalen Interpreter nur im Hercules-Darstellungsmodus verfУМgbar. + SCI: + - Fehlende Dialogzeile in QfG3 hinzugefУМgt, die mit drei zusУЄtzlichen Punkten + belohnt wird. Diese Dialogzeile fehlt im Originalspiel, weshalb + es bislang unmУЖglich war, die maximale Punktzahl im Spiel zu erreichen. + 1.8.1 (25.05.2016) Neue Portierungen: - Portierung fУМr den Nintendo 3DS hinzugefУМgt. diff --git a/engines/access/access.cpp b/engines/access/access.cpp index c12761af4a..6f91bd76dd 100644 --- a/engines/access/access.cpp +++ b/engines/access/access.cpp @@ -244,7 +244,7 @@ void AccessEngine::freeCells() { } } -void AccessEngine::speakText(ASurface *s, const Common::String &msg) { +void AccessEngine::speakText(BaseSurface *s, const Common::String &msg) { Common::String lines = msg; Common::String line; int curPage = 0; @@ -325,7 +325,7 @@ void AccessEngine::speakText(ASurface *s, const Common::String &msg) { } } -void AccessEngine::printText(ASurface *s, const Common::String &msg) { +void AccessEngine::printText(BaseSurface *s, const Common::String &msg) { Common::String lines = msg; Common::String line; int width = 0; diff --git a/engines/access/access.h b/engines/access/access.h index 2ca4a3468e..972dd4c380 100644 --- a/engines/access/access.h +++ b/engines/access/access.h @@ -156,8 +156,8 @@ public: MusicManager *_midi; VideoPlayer *_video; - ASurface *_destIn; - ASurface *_current; + BaseSurface *_destIn; + BaseSurface *_current; ASurface _buffer1; ASurface _buffer2; ASurface _vidBuf; @@ -280,8 +280,8 @@ public: /** * Draw a string on a given surface and update text positioning */ - void printText(ASurface *s, const Common::String &msg); - void speakText(ASurface *s, const Common::String &msg); + void printText(BaseSurface *s, const Common::String &msg); + void speakText(BaseSurface *s, const Common::String &msg); /** * Load a savegame diff --git a/engines/access/amazon/amazon_game.cpp b/engines/access/amazon/amazon_game.cpp index 0a671d23d2..8467d8b623 100644 --- a/engines/access/amazon/amazon_game.cpp +++ b/engines/access/amazon/amazon_game.cpp @@ -496,7 +496,7 @@ void AmazonEngine::drawHelp(const Common::String str) { _files->loadScreen(95, 2); if (_moreHelp == 1) { - ASurface *oldDest = _destIn; + BaseSurface *oldDest = _destIn; _destIn = _screen; int oldClip = _screen->_clipHeight; _screen->_clipHeight = 200; diff --git a/engines/access/asurface.cpp b/engines/access/asurface.cpp index 2518ff6ad8..37f4c7082e 100644 --- a/engines/access/asurface.cpp +++ b/engines/access/asurface.cpp @@ -107,10 +107,11 @@ void ImageEntryList::addToList(ImageEntry &ie) { /*------------------------------------------------------------------------*/ -int ASurface::_clipWidth; -int ASurface::_clipHeight; +int BaseSurface::_clipWidth; +int BaseSurface::_clipHeight; -ASurface::ASurface(): Graphics::ManagedSurface() { +BaseSurface::BaseSurface(): Graphics::Screen(0, 0) { + free(); // Free the 0x0 surface allocated by Graphics::Screen _leftSkip = _rightSkip = 0; _topSkip = _bottomSkip = 0; _lastBoundsX = _lastBoundsY = 0; @@ -121,16 +122,16 @@ ASurface::ASurface(): Graphics::ManagedSurface() { _maxChars = 0; } -ASurface::~ASurface() { +BaseSurface::~BaseSurface() { _savedBlock.free(); } -void ASurface::clearBuffer() { +void BaseSurface::clearBuffer() { byte *pSrc = (byte *)getPixels(); Common::fill(pSrc, pSrc + w * h, 0); } -void ASurface::plotImage(SpriteResource *sprite, int frameNum, const Common::Point &pt) { +void BaseSurface::plotImage(SpriteResource *sprite, int frameNum, const Common::Point &pt) { SpriteFrame *frame = sprite->getFrame(frameNum); Common::Rect r(pt.x, pt.y, pt.x + frame->w, pt.y + frame->h); @@ -144,38 +145,38 @@ void ASurface::plotImage(SpriteResource *sprite, int frameNum, const Common::Poi } } -void ASurface::copyBuffer(Graphics::ManagedSurface *src) { +void BaseSurface::copyBuffer(Graphics::ManagedSurface *src) { blitFrom(*src); } -void ASurface::plotF(SpriteFrame *frame, const Common::Point &pt) { +void BaseSurface::plotF(SpriteFrame *frame, const Common::Point &pt) { sPlotF(frame, Common::Rect(pt.x, pt.y, pt.x + frame->w, pt.y + frame->h)); } -void ASurface::plotB(SpriteFrame *frame, const Common::Point &pt) { +void BaseSurface::plotB(SpriteFrame *frame, const Common::Point &pt) { sPlotB(frame, Common::Rect(pt.x, pt.y, pt.x + frame->w, pt.y + frame->h)); } -void ASurface::sPlotF(SpriteFrame *frame, const Common::Rect &bounds) { +void BaseSurface::sPlotF(SpriteFrame *frame, const Common::Rect &bounds) { transBlitFrom(*frame, Common::Rect(0, 0, frame->w, frame->h), bounds, TRANSPARENCY, false); } -void ASurface::sPlotB(SpriteFrame *frame, const Common::Rect &bounds) { +void BaseSurface::sPlotB(SpriteFrame *frame, const Common::Rect &bounds) { transBlitFrom(*frame, Common::Rect(0, 0, frame->w, frame->h), bounds, TRANSPARENCY, true); } -void ASurface::copyBlock(ASurface *src, const Common::Rect &bounds) { +void BaseSurface::copyBlock(BaseSurface *src, const Common::Rect &bounds) { copyRectToSurface(*src, bounds.left, bounds.top, bounds); } -void ASurface::copyTo(ASurface *dest) { +void BaseSurface::copyTo(BaseSurface *dest) { if (dest->empty()) dest->create(this->w, this->h); dest->blitFrom(*this); } -void ASurface::saveBlock(const Common::Rect &bounds) { +void BaseSurface::saveBlock(const Common::Rect &bounds) { _savedBounds = bounds; _savedBounds.clip(Common::Rect(0, 0, this->w, this->h)); @@ -185,7 +186,7 @@ void ASurface::saveBlock(const Common::Rect &bounds) { _savedBlock.copyRectToSurface(*this, 0, 0, _savedBounds); } -void ASurface::restoreBlock() { +void BaseSurface::restoreBlock() { if (!_savedBounds.isEmpty()) { copyRectToSurface(_savedBlock, _savedBounds.left, _savedBounds.top, Common::Rect(0, 0, _savedBlock.w, _savedBlock.h)); @@ -195,26 +196,26 @@ void ASurface::restoreBlock() { } } -void ASurface::drawRect() { +void BaseSurface::drawRect() { Graphics::ManagedSurface::fillRect(Common::Rect(_orgX1, _orgY1, _orgX2, _orgY2), _lColor); } -void ASurface::drawLine(int x1, int y1, int x2, int y2, int col) { +void BaseSurface::drawLine(int x1, int y1, int x2, int y2, int col) { Graphics::ManagedSurface::drawLine(x1, y1, x2, y2, col); } -void ASurface::drawLine() { +void BaseSurface::drawLine() { Graphics::ManagedSurface::drawLine(_orgX1, _orgY1, _orgX2, _orgY1, _lColor); } -void ASurface::drawBox() { +void BaseSurface::drawBox() { Graphics::ManagedSurface::drawLine(_orgX1, _orgY1, _orgX2, _orgY1, _lColor); Graphics::ManagedSurface::drawLine(_orgX1, _orgY2, _orgX2, _orgY2, _lColor); Graphics::ManagedSurface::drawLine(_orgX2, _orgY1, _orgX2, _orgY1, _lColor); Graphics::ManagedSurface::drawLine(_orgX2, _orgY2, _orgX2, _orgY2, _lColor); } -void ASurface::flipHorizontal(ASurface &dest) { +void BaseSurface::flipHorizontal(BaseSurface &dest) { dest.create(this->w, this->h); for (int y = 0; y < h; ++y) { const byte *pSrc = (const byte *)getBasePtr(this->w - 1, y); @@ -225,27 +226,27 @@ void ASurface::flipHorizontal(ASurface &dest) { } } -void ASurface::moveBufferLeft() { +void BaseSurface::moveBufferLeft() { byte *p = (byte *)getPixels(); Common::copy(p + TILE_WIDTH, p + (w * h), p); } -void ASurface::moveBufferRight() { +void BaseSurface::moveBufferRight() { byte *p = (byte *)getPixels(); Common::copy_backward(p, p + (pitch * h) - TILE_WIDTH, p + (pitch * h)); } -void ASurface::moveBufferUp() { +void BaseSurface::moveBufferUp() { byte *p = (byte *)getPixels(); Common::copy(p + (pitch * TILE_HEIGHT), p + (pitch * h), p); } -void ASurface::moveBufferDown() { +void BaseSurface::moveBufferDown() { byte *p = (byte *)getPixels(); Common::copy_backward(p, p + (pitch * (h - TILE_HEIGHT)), p + (pitch * h)); } -bool ASurface::clip(Common::Rect &r) { +bool BaseSurface::clip(Common::Rect &r) { int skip; _leftSkip = _rightSkip = 0; _topSkip = _bottomSkip = 0; diff --git a/engines/access/asurface.h b/engines/access/asurface.h index ec18ec09c3..64ddf3d0ee 100644 --- a/engines/access/asurface.h +++ b/engines/access/asurface.h @@ -27,7 +27,7 @@ #include "common/array.h" #include "common/memstream.h" #include "common/rect.h" -#include "graphics/managed_surface.h" +#include "graphics/screen.h" #include "access/data.h" namespace Access { @@ -35,11 +35,16 @@ namespace Access { class SpriteResource; class SpriteFrame; -class ASurface : virtual public Graphics::ManagedSurface { +/** + * Base Access surface class. This derivces from Graphics::Screen + * because it has logic we'll need for our own Screen class that + * derives from this one + */ +class BaseSurface : virtual public Graphics::Screen { private: Graphics::Surface _savedBlock; - void flipHorizontal(ASurface &dest); + void flipHorizontal(BaseSurface &dest); protected: Common::Rect _savedBounds; public: @@ -57,9 +62,9 @@ public: public: static int _clipWidth, _clipHeight; public: - ASurface(); + BaseSurface(); - virtual ~ASurface(); + virtual ~BaseSurface(); void clearBuffer(); @@ -85,7 +90,7 @@ public: */ void plotB(SpriteFrame *frame, const Common::Point &pt); - virtual void copyBlock(ASurface *src, const Common::Rect &bounds); + virtual void copyBlock(BaseSurface *src, const Common::Rect &bounds); virtual void restoreBlock(); @@ -99,7 +104,7 @@ public: virtual void copyBuffer(Graphics::ManagedSurface *src); - void copyTo(ASurface *dest); + void copyTo(BaseSurface *dest); void saveBlock(const Common::Rect &bounds); @@ -114,6 +119,17 @@ public: bool clip(Common::Rect &r); }; +class ASurface : public BaseSurface { +protected: + /** + * Override the addDirtyRect from Graphics::Screen, since for standard + * surfaces we don't need dirty rects to be tracked + */ + virtual void addDirtyRect(const Common::Rect &r) {} +public: + ASurface() : BaseSurface() {} +}; + class SpriteFrame : public ASurface { public: SpriteFrame(AccessEngine *vm, Common::SeekableReadStream *stream, int frameSize); diff --git a/engines/access/bubble_box.cpp b/engines/access/bubble_box.cpp index e55701900a..29b58a3f1b 100644 --- a/engines/access/bubble_box.cpp +++ b/engines/access/bubble_box.cpp @@ -228,7 +228,7 @@ void BubbleBox::drawBubble(int index) { void BubbleBox::doBox(int item, int box) { FontManager &fonts = _vm->_fonts; - ASurface &screen = *_vm->_screen; + Screen &screen = *_vm->_screen; _startItem = item; _startBox = box; diff --git a/engines/access/detection.cpp b/engines/access/detection.cpp index 2cd7e50f0f..368753f117 100644 --- a/engines/access/detection.cpp +++ b/engines/access/detection.cpp @@ -94,7 +94,7 @@ public: } virtual const char *getOriginalCopyright() const { - return "Access Engine (c) 1989-1994 Access Software"; + return "Access Engine (C) 1989-1994 Access Software"; } virtual bool hasFeature(MetaEngineFeature f) const; diff --git a/engines/access/font.cpp b/engines/access/font.cpp index 6ae65e43f0..8e02f80769 100644 --- a/engines/access/font.cpp +++ b/engines/access/font.cpp @@ -139,7 +139,7 @@ bool Font::getLine(Common::String &s, int maxWidth, Common::String &line, int &w return true; } -void Font::drawString(ASurface *s, const Common::String &msg, const Common::Point &pt) { +void Font::drawString(BaseSurface *s, const Common::String &msg, const Common::Point &pt) { Common::Point currPt = pt; const char *msgP = msg.c_str(); @@ -149,7 +149,7 @@ void Font::drawString(ASurface *s, const Common::String &msg, const Common::Poin } } -int Font::drawChar(ASurface *s, char c, Common::Point &pt) { +int Font::drawChar(BaseSurface *s, char c, Common::Point &pt) { Graphics::Surface &ch = _chars[c - ' ']; Graphics::Surface dest = s->getSubArea(Common::Rect(pt.x, pt.y, pt.x + ch.w, pt.y + ch.h)); diff --git a/engines/access/font.h b/engines/access/font.h index 6a812051ca..9234078af2 100644 --- a/engines/access/font.h +++ b/engines/access/font.h @@ -78,12 +78,12 @@ public: /** * Draw a string on a given surface */ - void drawString(ASurface *s, const Common::String &msg, const Common::Point &pt); + void drawString(BaseSurface *s, const Common::String &msg, const Common::Point &pt); /** * Draw a character on a given surface */ - int drawChar(ASurface *s, char c, Common::Point &pt); + int drawChar(BaseSurface *s, char c, Common::Point &pt); }; diff --git a/engines/access/screen.cpp b/engines/access/screen.cpp index 9700640b71..2d29ad0718 100644 --- a/engines/access/screen.cpp +++ b/engines/access/screen.cpp @@ -253,7 +253,7 @@ void Screen::restoreScreen() { _screenYOff = _screenSave._screenYOff; } -void Screen::copyBlock(ASurface *src, const Common::Rect &bounds) { +void Screen::copyBlock(BaseSurface *src, const Common::Rect &bounds) { Common::Rect destBounds = bounds; destBounds.translate(_windowXAdd, _windowYAdd + _screenYOff); @@ -264,22 +264,22 @@ void Screen::copyBlock(ASurface *src, const Common::Rect &bounds) { void Screen::restoreBlock() { if (!_savedBounds.isEmpty()) addDirtyRect(_savedBounds); - ASurface::restoreBlock(); + BaseSurface::restoreBlock(); } void Screen::drawRect() { addDirtyRect(Common::Rect(_orgX1, _orgY1, _orgX2, _orgY2)); - ASurface::drawRect(); + BaseSurface::drawRect(); } void Screen::drawBox() { addDirtyRect(Common::Rect(_orgX1, _orgY1, _orgX2, _orgY2)); - ASurface::drawBox(); + BaseSurface::drawBox(); } void Screen::copyBuffer(Graphics::ManagedSurface *src) { addDirtyRect(Common::Rect(0, 0, src->w, src->h)); - ASurface::copyBuffer(src); + BaseSurface::copyBuffer(src); } void Screen::setPaletteCycle(int startCycle, int endCycle, int timer) { diff --git a/engines/access/screen.h b/engines/access/screen.h index a022741f91..1c1932511d 100644 --- a/engines/access/screen.h +++ b/engines/access/screen.h @@ -45,7 +45,7 @@ struct ScreenSave { int _screenYOff; }; -class Screen : public virtual ASurface, public virtual Graphics::Screen { +class Screen : public BaseSurface { private: AccessEngine *_vm; byte _tempPalette[PALETTE_SIZE]; @@ -86,7 +86,7 @@ public: */ virtual void update(); - virtual void copyBlock(ASurface *src, const Common::Rect &bounds); + virtual void copyBlock(BaseSurface *src, const Common::Rect &bounds); virtual void restoreBlock(); diff --git a/engines/access/video.cpp b/engines/access/video.cpp index e3ff457c3b..22842a5855 100644 --- a/engines/access/video.cpp +++ b/engines/access/video.cpp @@ -48,7 +48,7 @@ VideoPlayer::~VideoPlayer() { closeVideo(); } -void VideoPlayer::setVideo(ASurface *vidSurface, const Common::Point &pt, int rate) { +void VideoPlayer::setVideo(BaseSurface *vidSurface, const Common::Point &pt, int rate) { _vidSurface = vidSurface; vidSurface->_orgX1 = pt.x; vidSurface->_orgY1 = pt.y; @@ -87,14 +87,14 @@ void VideoPlayer::setVideo(ASurface *vidSurface, const Common::Point &pt, int ra _videoEnd = false; } -void VideoPlayer::setVideo(ASurface *vidSurface, const Common::Point &pt, const Common::String filename, int rate) { +void VideoPlayer::setVideo(BaseSurface *vidSurface, const Common::Point &pt, const Common::String filename, int rate) { // Open up video stream _videoData = _vm->_files->loadFile(filename); setVideo(vidSurface, pt, rate); } -void VideoPlayer::setVideo(ASurface *vidSurface, const Common::Point &pt, const FileIdent &videoFile, int rate) { +void VideoPlayer::setVideo(BaseSurface *vidSurface, const Common::Point &pt, const FileIdent &videoFile, int rate) { // Open up video stream _videoData = _vm->_files->loadFile(videoFile); diff --git a/engines/access/video.h b/engines/access/video.h index 83c8995d3e..65dff3ebea 100644 --- a/engines/access/video.h +++ b/engines/access/video.h @@ -40,7 +40,7 @@ class VideoPlayer : public Manager { VideoFlags _flags; }; private: - ASurface *_vidSurface; + BaseSurface *_vidSurface; Resource *_videoData; VideoHeader _header; byte *_startCoord; @@ -51,7 +51,7 @@ private: Common::Rect _videoBounds; void getFrame(); - void setVideo(ASurface *vidSurface, const Common::Point &pt, int rate); + void setVideo(BaseSurface *vidSurface, const Common::Point &pt, int rate); public: int _videoFrame; bool _soundFlag; @@ -64,8 +64,8 @@ public: /** * Start up a video */ - void setVideo(ASurface *vidSurface, const Common::Point &pt, const FileIdent &videoFile, int rate); - void setVideo(ASurface *vidSurface, const Common::Point &pt, const Common::String filename, int rate); + void setVideo(BaseSurface *vidSurface, const Common::Point &pt, const FileIdent &videoFile, int rate); + void setVideo(BaseSurface *vidSurface, const Common::Point &pt, const Common::String filename, int rate); /** * Decodes a frame of the video diff --git a/engines/agi/global.cpp b/engines/agi/global.cpp index 23256f27fb..6f83f02a4e 100644 --- a/engines/agi/global.cpp +++ b/engines/agi/global.cpp @@ -21,6 +21,7 @@ */ #include "common/config-manager.h" +#include "audio/mixer.h" #include "agi/agi.h" #include "agi/graphics.h" diff --git a/engines/agi/graphics.cpp b/engines/agi/graphics.cpp index 6d3563a451..24cd4f43d3 100644 --- a/engines/agi/graphics.cpp +++ b/engines/agi/graphics.cpp @@ -605,7 +605,7 @@ void GfxMgr::render_BlockEGA(int16 x, int16 y, int16 width, int16 height, bool c switch (_upscaledHires) { case DISPLAY_UPSCALED_640x400: - offsetDisplay += _displayScreenWidth;; + offsetDisplay += _displayScreenWidth; break; default: break; @@ -660,7 +660,7 @@ void GfxMgr::render_BlockCGA(int16 x, int16 y, int16 width, int16 height, bool c switch (_upscaledHires) { case DISPLAY_UPSCALED_640x400: - offsetDisplay += _displayScreenWidth;; + offsetDisplay += _displayScreenWidth; break; default: break; @@ -743,7 +743,7 @@ void GfxMgr::render_BlockHercules(int16 x, int16 y, int16 width, int16 height, b offsetVisual += SCRIPT_WIDTH - width; offsetDisplay += _displayScreenWidth - displayWidth; - offsetDisplay += _displayScreenWidth;; + offsetDisplay += _displayScreenWidth; remainingHeight--; } @@ -1178,13 +1178,17 @@ void GfxMgr::drawCharacterOnDisplay(int16 x, int16 y, const byte character, byte #define SHAKE_HORIZONTAL_PIXELS 4 // Sierra used some EGA port trickery to do it, we have to do it by copying pixels around +// +// Shaking locations: +// - Fanmade "Enclosure" right during the intro +// - Space Quest 2 almost right at the start when getting captured (after walking into the space ship) void GfxMgr::shakeScreen(int16 repeatCount) { int shakeNr, shakeCount; uint8 *blackSpace; int16 shakeHorizontalPixels = SHAKE_HORIZONTAL_PIXELS * (2 + _displayWidthMulAdjust); int16 shakeVerticalPixels = SHAKE_VERTICAL_PIXELS * (1 + _displayHeightMulAdjust); - if ((blackSpace = (uint8 *)calloc(shakeVerticalPixels * _displayScreenWidth, 1)) == NULL) + if ((blackSpace = (uint8 *)calloc(shakeHorizontalPixels * _displayScreenWidth, 1)) == NULL) return; shakeCount = repeatCount * 8; // effectively 4 shakes per repeat diff --git a/engines/agi/preagi.cpp b/engines/agi/preagi.cpp index 7e2e65a3eb..f8630db0b6 100644 --- a/engines/agi/preagi.cpp +++ b/engines/agi/preagi.cpp @@ -20,6 +20,7 @@ * */ +#include "audio/mixer.h" #include "audio/softsynth/pcspk.h" #include "common/debug-channels.h" @@ -50,6 +51,8 @@ PreAgiEngine::PreAgiEngine(OSystem *syst, const AGIGameDescription *gameDesc) : memset(&_game, 0, sizeof(struct AgiGame)); memset(&_debug, 0, sizeof(struct AgiDebug)); memset(&_mouse, 0, sizeof(struct Mouse)); + + _speakerHandle = new Audio::SoundHandle(); } void PreAgiEngine::initialize() { @@ -74,7 +77,7 @@ void PreAgiEngine::initialize() { _gfx->initVideo(); _speakerStream = new Audio::PCSpeaker(_mixer->getOutputRate()); - _mixer->playStream(Audio::Mixer::kSFXSoundType, &_speakerHandle, + _mixer->playStream(Audio::Mixer::kSFXSoundType, _speakerHandle, _speakerStream, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); debugC(2, kDebugLevelMain, "Detect game"); @@ -89,8 +92,9 @@ void PreAgiEngine::initialize() { } PreAgiEngine::~PreAgiEngine() { - _mixer->stopHandle(_speakerHandle); + _mixer->stopHandle(*_speakerHandle); delete _speakerStream; + delete _speakerHandle; delete _picture; delete _gfx; diff --git a/engines/agi/preagi.h b/engines/agi/preagi.h index d6026a5d4d..6950aa30cd 100644 --- a/engines/agi/preagi.h +++ b/engines/agi/preagi.h @@ -26,6 +26,7 @@ #include "agi/agi.h" namespace Audio { +class SoundHandle; class PCSpeaker; } @@ -110,7 +111,7 @@ private: int _defaultColor; Audio::PCSpeaker *_speakerStream; - Audio::SoundHandle _speakerHandle; + Audio::SoundHandle *_speakerHandle; }; } // End of namespace Agi diff --git a/engines/agi/saveload.cpp b/engines/agi/saveload.cpp index aa46cf4e9f..fc4aea3169 100644 --- a/engines/agi/saveload.cpp +++ b/engines/agi/saveload.cpp @@ -829,7 +829,7 @@ SavedGameSlotIdArray AgiEngine::getSavegameSlotIds() { filenames = _saveFileMan->listSavefiles(_targetName + ".###"); Common::StringArray::iterator it; - Common::StringArray::iterator end = filenames.end();; + Common::StringArray::iterator end = filenames.end(); // convert to lower-case, just to be sure for (it = filenames.begin(); it != end; it++) { diff --git a/engines/agi/sound.cpp b/engines/agi/sound.cpp index edf17960ad..8834068ace 100644 --- a/engines/agi/sound.cpp +++ b/engines/agi/sound.cpp @@ -29,9 +29,19 @@ #include "agi/sound_pcjr.h" #include "common/textconsole.h" +#include "audio/mixer.h" namespace Agi { +SoundGen::SoundGen(AgiBase *vm, Audio::Mixer *pMixer) : _vm(vm), _mixer(pMixer) { + _sampleRate = pMixer->getOutputRate(); + _soundHandle = new Audio::SoundHandle(); +} + +SoundGen::~SoundGen() { + delete _soundHandle; +} + // // TODO: add support for variable sampling rate in the output device // diff --git a/engines/agi/sound.h b/engines/agi/sound.h index 4b668e8cf2..8aa7a5d1df 100644 --- a/engines/agi/sound.h +++ b/engines/agi/sound.h @@ -23,7 +23,10 @@ #ifndef AGI_SOUND_H #define AGI_SOUND_H -#include "audio/mixer.h" +namespace Audio { +class Mixer; +class SoundHandle; +} namespace Agi { @@ -71,11 +74,8 @@ class SoundMgr; class SoundGen { public: - SoundGen(AgiBase *vm, Audio::Mixer *pMixer) : _vm(vm), _mixer(pMixer) { - _sampleRate = pMixer->getOutputRate(); - } - - virtual ~SoundGen() {} + SoundGen(AgiBase *vm, Audio::Mixer *pMixer); + virtual ~SoundGen(); virtual void play(int resnum) = 0; virtual void stop(void) = 0; @@ -83,7 +83,7 @@ public: AgiBase *_vm; Audio::Mixer *_mixer; - Audio::SoundHandle _soundHandle; + Audio::SoundHandle *_soundHandle; uint32 _sampleRate; }; diff --git a/engines/agi/sound_2gs.cpp b/engines/agi/sound_2gs.cpp index b1bcee3920..4992b6516c 100644 --- a/engines/agi/sound_2gs.cpp +++ b/engines/agi/sound_2gs.cpp @@ -27,6 +27,7 @@ #include "common/memstream.h" #include "common/str-array.h" #include "common/textconsole.h" +#include "audio/mixer.h" #include "agi/agi.h" #include "agi/sound_2gs.h" @@ -55,11 +56,11 @@ SoundGen2GS::SoundGen2GS(AgiBase *vm, Audio::Mixer *pMixer) : SoundGen(vm, pMixe // Load instruments _disableMidi = !loadInstruments(); - _mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + _mixer->playStream(Audio::Mixer::kMusicSoundType, _soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); } SoundGen2GS::~SoundGen2GS() { - _mixer->stopHandle(_soundHandle); + _mixer->stopHandle(*_soundHandle); delete[] _wavetable; delete[] _out; } diff --git a/engines/agi/sound_pcjr.cpp b/engines/agi/sound_pcjr.cpp index 9d556e048a..0a0c456e14 100644 --- a/engines/agi/sound_pcjr.cpp +++ b/engines/agi/sound_pcjr.cpp @@ -54,6 +54,7 @@ * */ +#include "audio/mixer.h" #include "agi/agi.h" #include "agi/sound.h" #include "agi/sound_pcjr.h" @@ -123,7 +124,7 @@ SoundGenPCJr::SoundGenPCJr(AgiBase *vm, Audio::Mixer *pMixer) : SoundGen(vm, pMi memset(_channel, 0, sizeof(_channel)); memset(_tchannel, 0, sizeof(_tchannel)); - _mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + _mixer->playStream(Audio::Mixer::kMusicSoundType, _soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); _v1data = NULL; _v1size = 0; @@ -132,7 +133,7 @@ SoundGenPCJr::SoundGenPCJr(AgiBase *vm, Audio::Mixer *pMixer) : SoundGen(vm, pMi SoundGenPCJr::~SoundGenPCJr() { free(_chanData); - _mixer->stopHandle(_soundHandle); + _mixer->stopHandle(*_soundHandle); } void SoundGenPCJr::play(int resnum) { diff --git a/engines/agi/sound_sarien.cpp b/engines/agi/sound_sarien.cpp index 939c3d2c77..1b3542b89c 100644 --- a/engines/agi/sound_sarien.cpp +++ b/engines/agi/sound_sarien.cpp @@ -21,6 +21,7 @@ */ #include "common/random.h" +#include "audio/mixer.h" #include "agi/agi.h" @@ -92,11 +93,11 @@ SoundGenSarien::SoundGenSarien(AgiBase *vm, Audio::Mixer *pMixer) : SoundGen(vm, debug(0, "Initializing sound: envelopes disabled"); } - _mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + _mixer->playStream(Audio::Mixer::kMusicSoundType, _soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); } SoundGenSarien::~SoundGenSarien() { - _mixer->stopHandle(_soundHandle); + _mixer->stopHandle(*_soundHandle); free(_sndBuffer); } diff --git a/engines/agi/systemui.cpp b/engines/agi/systemui.cpp index aeb1ded4a2..1f26267ad6 100644 --- a/engines/agi/systemui.cpp +++ b/engines/agi/systemui.cpp @@ -585,7 +585,7 @@ void SystemUI::readSavedGameSlots(bool filterNonexistant, bool withAutoSaveSlot) slotIdArray.push_back(SYSTEMUI_SAVEDGAME_MAXIMUM_SLOTS); // so that the loop will process all slots SavedGameSlotIdArray::iterator it; - SavedGameSlotIdArray::iterator end = slotIdArray.end();; + SavedGameSlotIdArray::iterator end = slotIdArray.end(); for (it = slotIdArray.begin(); it != end; it++) { curSlotId = *it; diff --git a/engines/agos/animation.cpp b/engines/agos/animation.cpp index 83682d567b..123e5805dd 100644 --- a/engines/agos/animation.cpp +++ b/engines/agos/animation.cpp @@ -57,6 +57,7 @@ MoviePlayer::MoviePlayer(AGOSEngine_Feeble *vm) memset(baseName, 0, sizeof(baseName)); _ticks = 0; + _bgSoundStream = nullptr; } MoviePlayer::~MoviePlayer() { diff --git a/engines/agos/feeble.cpp b/engines/agos/feeble.cpp index bfd11fa8ea..91772621ad 100644 --- a/engines/agos/feeble.cpp +++ b/engines/agos/feeble.cpp @@ -39,6 +39,7 @@ AGOSEngine_Feeble::AGOSEngine_Feeble(OSystem *system, const AGOSGameDescription _moviePlayer = 0; _vgaCurSpritePriority = 0; _mouseToggle = false; + _opcodesFeeble = nullptr; } AGOSEngine_Feeble::~AGOSEngine_Feeble() { diff --git a/engines/cge/detection.cpp b/engines/cge/detection.cpp index 52168dc934..82d27f8d54 100644 --- a/engines/cge/detection.cpp +++ b/engines/cge/detection.cpp @@ -123,7 +123,7 @@ public: } virtual const char *getOriginalCopyright() const { - return "Soltys (c) 1994-1996 L.K. Avalon"; + return "Soltys (C) 1994-1996 L.K. Avalon"; } virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const; diff --git a/engines/cge2/detection.cpp b/engines/cge2/detection.cpp index 01119bb1fd..2b84d167c7 100644 --- a/engines/cge2/detection.cpp +++ b/engines/cge2/detection.cpp @@ -119,7 +119,7 @@ public: } virtual const char *getOriginalCopyright() const { - return "Sfinx (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon"; + return "Sfinx (C) 1994-1997 Janus B. Wisniewski and L.K. Avalon"; } virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const; diff --git a/engines/gnap/character.cpp b/engines/gnap/character.cpp new file mode 100644 index 0000000000..36d849acbf --- /dev/null +++ b/engines/gnap/character.cpp @@ -0,0 +1,1415 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gnap/gnap.h" +#include "gnap/character.h" +#include "gnap/gamesys.h" + +namespace Gnap { + +Character::Character(GnapEngine *vm) : _vm(vm) { + _pos = Common::Point(0, 0); + _idleFacing = kDirIdleLeft; + _actionStatus = 0; + _sequenceId = 0; + _sequenceDatNum = 0; + _id = 0; + _gridX = 0; + _gridY = 0; + + _walkNodesCount = 0; + _walkDestX = _walkDestY = 0; + _walkDeltaX = _walkDeltaY = 0; + _walkDirX = _walkDirY = 0; + _walkDirXIncr = _walkDirYIncr = 0; + + for(int i = 0; i < kMaxGridStructs; i++) { + _walkNodes[i]._id = 0; + _walkNodes[i]._sequenceId = 0; + _walkNodes[i]._deltaX = 0; + _walkNodes[i]._deltaY = 0; + _walkNodes[i]._gridX1 = 0; + _walkNodes[i]._gridY1 = 0; + } +} + +Character::~Character() {} + +void Character::walkStep() { + for (int i = 1; i < _vm->_gridMaxX; ++i) { + Common::Point checkPt = Common::Point(_pos.x + i, _pos.y); + if (!_vm->isPointBlocked(checkPt)) { + walkTo(checkPt, -1, -1, 1); + break; + } + + checkPt = Common::Point(_pos.x - i, _pos.y); + if (!_vm->isPointBlocked(checkPt)) { + walkTo(checkPt, -1, -1, 1); + break; + } + + checkPt = Common::Point(_pos.x, _pos.y + 1); + if (!_vm->isPointBlocked(checkPt)) { + walkTo(checkPt, -1, -1, 1); + break; + } + + checkPt = Common::Point(_pos.x, _pos.y - 1); + if (!_vm->isPointBlocked(checkPt)) { + walkTo(checkPt, -1, -1, 1); + break; + } + + checkPt = Common::Point(_pos.x + 1, _pos.y + 1); + if (!_vm->isPointBlocked(checkPt)) { + walkTo(checkPt, -1, -1, 1); + break; + } + + checkPt = Common::Point(_pos.x - 1, _pos.y + 1); + if (!_vm->isPointBlocked(checkPt)) { + walkTo(checkPt, -1, -1, 1); + break; + } + + checkPt = Common::Point(_pos.x + 1, _pos.y - 1); + if (!_vm->isPointBlocked(checkPt)) { + walkTo(checkPt, -1, -1, 1); + break; + } + + checkPt = Common::Point(_pos.x - 1, _pos.y - 1); + if (!_vm->isPointBlocked(checkPt)) { + walkTo(checkPt, -1, -1, 1); + break; + } + } +} +/************************************************************************************************/ + +PlayerGnap::PlayerGnap(GnapEngine * vm) : Character(vm) { + _brainPulseNum = 0; + _brainPulseRndValue = 0; +} + +int PlayerGnap::getSequenceId(int kind, Common::Point gridPos) { + int sequenceId = 0; + + switch (kind) { + case kGSPullOutDevice: + if (gridPos.x > 0 && gridPos.y > 0) { + if (_pos.y > gridPos.y) { + if (_pos.x > gridPos.x) { + sequenceId = 0x83F; + _idleFacing = kDirUpLeft; + } else { + sequenceId = 0x83D; + _idleFacing = kDirUpRight; + } + } else { + if (_pos.x > gridPos.x) { + sequenceId = 0x83B; + _idleFacing = kDirBottomLeft; + } else { + sequenceId = 0x839; + _idleFacing = kDirBottomRight; + } + } + } else { + switch (_idleFacing) { + case kDirBottomRight: + sequenceId = 0x839; + break; + case kDirBottomLeft: + sequenceId = 0x83B; + break; + case kDirUpRight: + sequenceId = 0x83D; + break; + default: + sequenceId = 0x83F; + break; + } + } + break; + + case kGSPullOutDeviceNonWorking: + if (gridPos.x > 0 && gridPos.y > 0) { + if (_pos.y > gridPos.y) { + if (_pos.x > gridPos.x) { + sequenceId = 0x829; + _idleFacing = kDirUpLeft; + } else { + sequenceId = 0x828; + _idleFacing = kDirUpRight; + } + } else { + if (_pos.x > gridPos.x) { + sequenceId = 0x827; + _idleFacing = kDirBottomLeft; + } else { + sequenceId = 0x826; + _idleFacing = kDirBottomRight; + } + } + } else { + switch (_idleFacing) { + case kDirBottomRight: + sequenceId = 0x826; + break; + case kDirBottomLeft: + sequenceId = 0x827; + break; + case kDirUpRight: + sequenceId = 0x828; + break; + default: + sequenceId = 0x829; + break; + } + } + break; + + case kGSScratchingHead: + if (gridPos.x > 0 && gridPos.y > 0) { + if (_pos.y > gridPos.y) { + if (_pos.x > gridPos.x) { + sequenceId = 0x834; + _idleFacing = kDirBottomLeft; + } else { + sequenceId = 0x885; + _idleFacing = kDirUpRight; + } + } else { + if (_pos.x > gridPos.x) { + sequenceId = 0x834; + _idleFacing = kDirBottomLeft; + } else { + sequenceId = 0x833; + _idleFacing = kDirBottomRight; + } + } + } else { + switch (_idleFacing) { + case kDirBottomRight: + sequenceId = 0x833; + _idleFacing = kDirBottomRight; + break; + case kDirBottomLeft: + sequenceId = 0x834; + _idleFacing = kDirBottomLeft; + break; + case kDirUpRight: + sequenceId = 0x885; + _idleFacing = kDirUpRight; + break; + default: + sequenceId = 0x834; + _idleFacing = kDirBottomLeft; + break; + } + } + break; + + case kGSIdle: + if (gridPos.x > 0 && gridPos.y > 0) { + if (_pos.y > gridPos.y) { + if (_pos.x > gridPos.x) { + sequenceId = 0x7BC; + _idleFacing = kDirUpLeft; + } else { + sequenceId = 0x7BB; + _idleFacing = kDirUpRight; + } + } else { + if (_pos.x > gridPos.x) { + sequenceId = 0x7BA; + _idleFacing = kDirBottomLeft; + } else { + sequenceId = 0x7B9; + _idleFacing = kDirBottomRight; + } + } + } else { + switch (_idleFacing) { + case kDirBottomRight: + sequenceId = 0x7B9; + break; + case kDirBottomLeft: + sequenceId = 0x7BA; + break; + case kDirUpRight: + sequenceId = 0x7BB; + break; + default: + sequenceId = 0x7BC; + break; + } + } + break; + + case kGSBrainPulsating: + _brainPulseNum = (_brainPulseNum + 1) & 1; + if (gridPos.x > 0 && gridPos.y > 0) { + if (_pos.y > gridPos.y) { + if (_pos.x > gridPos.x) { + sequenceId = _brainPulseRndValue + _brainPulseNum + 0x812; + _idleFacing = kDirUpLeft; + } else { + sequenceId = _brainPulseRndValue + _brainPulseNum + 0x7FE; + _idleFacing = kDirUpRight; + } + } else { + if (_pos.x > gridPos.x) { + sequenceId = _brainPulseRndValue + _brainPulseNum + 0x7D6; + _idleFacing = kDirBottomLeft; + } else { + sequenceId = _brainPulseRndValue + _brainPulseNum + 0x7EA; + _idleFacing = kDirBottomRight; + } + } + } else { + switch (_idleFacing) { + case kDirBottomRight: + sequenceId = _brainPulseRndValue + _brainPulseNum + 0x7EA; + break; + case kDirBottomLeft: + sequenceId = _brainPulseRndValue + _brainPulseNum + 0x7D6; + break; + case kDirUpRight: + sequenceId = _brainPulseRndValue + _brainPulseNum + 0x7FE; + break; + default: + sequenceId = _brainPulseRndValue + _brainPulseNum + 0x812; + break; + } + } + break; + + case kGSImpossible: + if (gridPos.x > 0 && gridPos.y > 0) { + if (_pos.y > gridPos.y) { + if (_pos.x > gridPos.x) { + sequenceId = 0x831; + _idleFacing = kDirBottomLeft; + } else { + sequenceId = 0x7A8; + _idleFacing = kDirBottomRight; + } + } else { + if (_pos.x > gridPos.x) { + sequenceId = 0x831; + _idleFacing = kDirBottomLeft; + } else { + if (_pos.x % 2) + sequenceId = 0x7A8; + else + sequenceId = 0x89A; + _idleFacing = kDirBottomRight; + } + } + } else if (_idleFacing != kDirBottomRight && _idleFacing != kDirUpRight) { + sequenceId = 0x831; + _idleFacing = kDirBottomLeft; + } else { + if (_vm->_currentSceneNum % 2) + sequenceId = 0x7A8; + else + sequenceId = 0x89A; + _idleFacing = kDirBottomRight; + } + break; + + case kGSDeflect: + if (gridPos.x > 0 && gridPos.y > 0) { + if (_pos.y > gridPos.y) { + if (_pos.x > gridPos.x) { + sequenceId = 0x830; + _idleFacing = kDirUpLeft; + } else { + sequenceId = 0x82F; + _idleFacing = kDirUpRight; + } + } else { + if (_pos.x > gridPos.x) { + sequenceId = 0x82E; + _idleFacing = kDirBottomLeft; + } else { + sequenceId = 0x7A7; + _idleFacing = kDirBottomRight; + } + } + } else { + switch (_idleFacing) { + case kDirBottomRight: + sequenceId = 0x7A7; + break; + case kDirBottomLeft: + sequenceId = 0x82E; + break; + case kDirUpLeft: + sequenceId = 0x830; + break; + case kDirUpRight: + sequenceId = 0x82F; + break; + case kDirIdleLeft: + case kDirIdleRight: + break; + } + } + break; + + case kGSUseDevice: + switch (_idleFacing) { + case kDirBottomRight: + sequenceId = 0x83A; + break; + case kDirBottomLeft: + sequenceId = 0x83C; + break; + case kDirUpLeft: + sequenceId = 0x840; + break; + case kDirUpRight: + sequenceId = 0x83E; + break; + case kDirIdleLeft: + case kDirIdleRight: + break; + } + break; + + case kGSMoan1: + if (gridPos.x > 0 && gridPos.y > 0) { + if (_pos.y > gridPos.y) { + if (_pos.x > gridPos.x) { + sequenceId = 0x832; + _idleFacing = kDirBottomLeft; + } else { + sequenceId = 0x7AA; + _idleFacing = kDirBottomRight; + } + } else { + if (_pos.x > gridPos.x) { + sequenceId = 0x832; + _idleFacing = kDirBottomLeft; + } else { + sequenceId = 0x7AA; + _idleFacing = kDirBottomRight; + } + } + } else if (_idleFacing != kDirBottomRight && _idleFacing != kDirUpRight) { + sequenceId = 0x832; + _idleFacing = kDirBottomLeft; + } else { + sequenceId = 0x7AA; + _idleFacing = kDirBottomRight; + } + break; + + case kGSMoan2: + if (gridPos.x > 0 && gridPos.y > 0) { + if (_pos.x > gridPos.x) { + sequenceId = 0x832; + _idleFacing = kDirBottomLeft; + } else { + sequenceId = 0x7AA; + _idleFacing = kDirBottomRight; + } + } else if (_idleFacing != kDirBottomRight && _idleFacing != kDirUpRight) { + sequenceId = 0x832; + _idleFacing = kDirBottomLeft; + } else { + sequenceId = 0x7AA; + _idleFacing = kDirBottomRight; + } + break; + } + + return sequenceId | 0x10000; +} + +void PlayerGnap::useJointOnPlatypus() { + PlayerPlat& plat = *_vm->_plat; + + _vm->setGrabCursorSprite(-1); + if (doPlatypusAction(1, 0, 0x107C1, 0)) { + _actionStatus = 100; + _vm->_gameSys->setAnimation(0, 0, 1); + _vm->_gameSys->setAnimation(0x10876, plat._id, 0); + _vm->_gameSys->insertSequence(0x10875, _id, + makeRid(_sequenceDatNum, _sequenceId), _id, + kSeqSyncWait, 0, 15 * (5 * _pos.x - 30), 48 * (_pos.y - 7)); + _sequenceDatNum = 1; + _sequenceId = 0x875; + _vm->_gameSys->insertSequence(0x10876, plat._id, + plat._sequenceId | (plat._sequenceDatNum << 16), plat._id, + kSeqSyncWait, 0, 15 * (5 * plat._pos.x - 25), 48 * (plat._pos.y - 7)); + plat._sequenceDatNum = 1; + plat._sequenceId = 0x876; + plat._idleFacing = kDirIdleLeft; + playSequence(0x107B5); + walkStep(); + while (_vm->_gameSys->getAnimationStatus(0) != 2 && !_vm->_gameDone) { + _vm->updateMouseCursor(); + _vm->gameUpdateTick(); + } + _vm->_gameSys->setAnimation(0, 0, 0); + _actionStatus = -1; + } else { + playSequence(getSequenceId(kGSScratchingHead, plat._pos) | 0x10000); + } +} + +void PlayerGnap::kissPlatypus(int callback) { + PlayerPlat& plat = *_vm->_plat; + + if (doPlatypusAction(-1, 0, 0x107D1, callback)) { + _actionStatus = 100; + _vm->_gameSys->setAnimation(0, 0, 1); + _vm->_gameSys->setAnimation(0x10847, _id, 0); + _vm->_gameSys->insertSequence(0x10847, _id, + makeRid(_sequenceDatNum, _sequenceId), _id, + kSeqSyncWait, 0, 15 * (5 * _pos.x - 20) - (21 - _vm->_gridMinX), 48 * (_pos.y - 6) - (146 - _vm->_gridMinY)); + _sequenceDatNum = 1; + _sequenceId = 0x847; + _vm->_gameSys->insertSequence(0x107CB, plat._id, + makeRid(plat._sequenceDatNum, plat._sequenceId), plat._id, + kSeqSyncWait, _vm->getSequenceTotalDuration(0x10847), 75 * plat._pos.x - plat._gridX, 48 * plat._pos.y - plat._gridY); + plat._sequenceDatNum = 1; + plat._sequenceId = 0x7CB; + plat._idleFacing = kDirIdleLeft; + playSequence(0x107B5); + while (_vm->_gameSys->getAnimationStatus(0) != 2 && !_vm->_gameDone) { + _vm->updateMouseCursor(); + _vm->doCallback(callback); + _vm->gameUpdateTick(); + } + _vm->_gameSys->setAnimation(0, 0, 0); + _actionStatus = -1; + } else { + playSequence(getSequenceId(kGSScratchingHead, plat._pos) | 0x10000); + } +} + +void PlayerGnap::useDeviceOnPlatypus() { + PlayerPlat& plat = *_vm->_plat; + + playSequence(makeRid(1, getSequenceId(kGSPullOutDevice, plat._pos))); + + if (plat._idleFacing != kDirIdleLeft) { + _vm->_gameSys->insertSequence(makeRid(1, 0x7D5), plat._id, + makeRid(plat._sequenceDatNum, plat._sequenceId), plat._id, + kSeqSyncWait, 0, 75 * plat._pos.x - plat._gridX, 48 * plat._pos.y - plat._gridY); + plat._sequenceId = 0x7D5; + plat._sequenceDatNum = 1; + } else { + _vm->_gameSys->insertSequence(makeRid(1, 0x7D4), plat._id, + makeRid(plat._sequenceDatNum, plat._sequenceId), plat._id, + kSeqSyncWait, 0, 75 * plat._pos.x - plat._gridX, 48 * plat._pos.y - plat._gridY); + plat._sequenceId = 0x7D4; + plat._sequenceDatNum = 1; + } + + int newSequenceId = getSequenceId(kGSUseDevice, Common::Point(0, 0)); + _vm->_gameSys->insertSequence(makeRid(1, newSequenceId), _id, + makeRid(_sequenceDatNum, _sequenceId), _id, + kSeqSyncWait, 0, 75 * _pos.x - _gridX, 48 * _pos.y - _gridY); + _sequenceId = newSequenceId; + _sequenceDatNum = 1; +} + +void PlayerGnap::initBrainPulseRndValue() { + _brainPulseRndValue = 2 * _vm->getRandom(10); +} + +void PlayerGnap::playSequence(int sequenceId) { + _vm->_timers[2] = _vm->getRandom(30) + 20; + _vm->_timers[3] = 300; + idle(); + _vm->_gameSys->insertSequence(sequenceId, _id, + makeRid(_sequenceDatNum, _sequenceId), _id, + kSeqScale | kSeqSyncWait, 0, 75 * _pos.x - _gridX, 48 * _pos.y - _gridY); + _sequenceId = ridToEntryIndex(sequenceId); + _sequenceDatNum = ridToDatIndex(sequenceId); +} + +void PlayerGnap::updateIdleSequence() { + if (_actionStatus < 0) { + if (_vm->_timers[2] > 0) { + if (_vm->_timers[3] == 0) { + _vm->_timers[2] = 60; + _vm->_timers[3] = 300; + if (_idleFacing == kDirBottomRight) { + switch (_vm->getRandom(5)) { + case 0: + playSequence(0x107A6); + break; + case 1: + playSequence(0x107AA); + break; + case 2: + playSequence(0x10841); + break; + default: + playSequence(0x108A2); + break; + } + } else if (_idleFacing == kDirBottomLeft) { + if (_vm->getRandom(5) > 2) + playSequence(0x10832); + else + playSequence(0x10842); + } + } + } else { + _vm->_timers[2] = _vm->getRandom(30) + 20; + if (_idleFacing == kDirBottomRight) { + _vm->_gameSys->insertSequence(0x107BD, _id, + makeRid(_sequenceDatNum, _sequenceId), _id, + kSeqSyncWait, 0, 75 * _pos.x - _gridX, 48 * _pos.y - _gridY); + _sequenceId = 0x7BD; + _sequenceDatNum = 1; + } else if (_idleFacing == kDirBottomLeft) { + _vm->_gameSys->insertSequence(0x107BE, _id, + makeRid(_sequenceDatNum, _sequenceId), _id, + kSeqSyncWait, 0, 75 * _pos.x - _gridX, 48 * _pos.y - _gridY); + _sequenceId = 0x7BE; + _sequenceDatNum = 1; + } + } + } else { + _vm->_timers[2] = _vm->getRandom(30) + 20; + _vm->_timers[3] = 300; + } +} + +void PlayerGnap::updateIdleSequence2() { + if (_actionStatus < 0) { + if (_vm->_timers[2] > 0) { + if (_vm->_timers[3] == 0) { + _vm->_timers[2] = 60; + _vm->_timers[3] = 300; + if (_idleFacing == kDirBottomRight) { + playSequence(0x107AA); + } else if (_idleFacing == kDirBottomLeft) { + playSequence(0x10832); + } + } + } else { + _vm->_timers[2] = _vm->getRandom(30) + 20; + if (_idleFacing == kDirBottomRight) { + _vm->_gameSys->insertSequence(0x107BD, _id, + makeRid(_sequenceDatNum, _sequenceId), _id, + kSeqSyncWait, 0, 75 * _pos.x - _gridX, 48 * _pos.y - _gridY); + _sequenceId = 0x7BD; + _sequenceDatNum = 1; + } else if (_idleFacing == kDirBottomLeft) { + _vm->_gameSys->insertSequence(0x107BE, _id, + makeRid(_sequenceDatNum, _sequenceId), _id, + kSeqSyncWait, 0, 75 * _pos.x - _gridX, 48 * _pos.y - _gridY); + _sequenceId = 0x7BE; + _sequenceDatNum = 1; + } + } + } else { + _vm->_timers[2] = _vm->getRandom(30) + 20; + _vm->_timers[3] = 300; + } +} + +void PlayerGnap::initPos(int gridX, int gridY, Facing facing) { + _vm->_timers[2] = 30; + _vm->_timers[3] = 300; + _pos = Common::Point(gridX, gridY); + if (facing == kDirIdleLeft) + _idleFacing = kDirBottomRight; + else + _idleFacing = facing; + + if (_idleFacing == kDirBottomLeft) { + _sequenceId = 0x7B8; + } else { + _sequenceId = 0x7B5; + _idleFacing = kDirBottomRight; + } + _id = 20 * _pos.y; + _sequenceDatNum = 1; + _vm->_gameSys->insertSequence(makeRid(1, _sequenceId), 20 * _pos.y, + 0, 0, + kSeqScale, 0, 75 * _pos.x - _gridX, 48 * _pos.y - _gridY); +} + +int PlayerGnap::getWalkSequenceId(int deltaX, int deltaY) { + static const int walkSequenceIds[9] = { + 0x7B2, 0x000, 0x7B4, + 0x7AD, 0x000, 0x7AE, + 0x7B1, 0x000, 0x7B3 + }; + + int id = 3 * (deltaX + 1) + deltaY + 1; + assert(id >= 0 && id < 9); + + return walkSequenceIds[id]; +} + +bool PlayerGnap::walkTo(Common::Point gridPos, int animationIndex, int sequenceId, int flags) { + PlayerPlat& plat = *_vm->_plat; + int datNum = flags & 3; + + _vm->_timers[2] = 200; + _vm->_timers[3] = 300; + + int gridX = gridPos.x; + if (gridX < 0) + gridX = (_vm->_leftClickMouseX - _vm->_gridMinX + 37) / 75; + + int gridY = gridPos.y; + if (gridY < 0) + gridY = (_vm->_leftClickMouseY - _vm->_gridMinY + 24) / 48; + + _walkDestX = CLIP(gridX, 0, _vm->_gridMaxX - 1); + _walkDestY = CLIP(gridY, 0, _vm->_gridMaxY - 1); + + if (animationIndex >= 0 && _walkDestX == plat._pos.x && _walkDestY == plat._pos.y) + plat.makeRoom(); + + bool done = findPath1(_pos.x, _pos.y, 0); + + if (!done) + done = findPath2(_pos.x, _pos.y, 0); + + if (!done) + done = findPath3(_pos.x, _pos.y); + + if (!done) + done = findPath4(_pos.x, _pos.y); + + idle(); + + int gnapSequenceId = _sequenceId; + int gnapId = _id; + int gnapSequenceDatNum = _sequenceDatNum; + + debugC(kDebugBasic, "_gnap->_walkNodesCount: %d", _walkNodesCount); + + for (int index = 0; index < _walkNodesCount; ++index) { + _walkNodes[index]._id = index + 20 * _walkNodes[index]._gridY1; + if (_walkNodes[index]._deltaX == 1 && _walkNodes[index]._deltaY == 0) { + if (index % 2) { + _vm->_gameSys->insertSequence(makeRid(datNum, 0x7AB), _walkNodes[index]._id, + makeRid(gnapSequenceDatNum, gnapSequenceId), gnapId, + kSeqScale | kSeqSyncWait, 0, 75 * _walkNodes[index]._gridX1 - _gridX, 48 * _walkNodes[index]._gridY1 - _gridY); + _walkNodes[index]._sequenceId = 0x7AB; + gnapSequenceId = 0x7AB; + } else { + _vm->_gameSys->insertSequence(makeRid(datNum, 0x7AC), _walkNodes[index]._id, + makeRid(gnapSequenceDatNum, gnapSequenceId), gnapId, + kSeqScale | kSeqSyncWait, 0, 75 * _walkNodes[index]._gridX1 - _gridX, 48 * _walkNodes[index]._gridY1 - _gridY); + _walkNodes[index]._sequenceId = 0x7AC; + gnapSequenceId = 0x7AC; + } + } else if (_walkNodes[index]._deltaX == -1 && _walkNodes[index]._deltaY == 0) { + if (index % 2) { + _vm->_gameSys->insertSequence(makeRid(datNum, 0x7AF), _walkNodes[index]._id, + makeRid(gnapSequenceDatNum, gnapSequenceId), gnapId, + kSeqScale | kSeqSyncWait, 0, 75 * _walkNodes[index]._gridX1 - _gridX, 48 * _walkNodes[index]._gridY1 - _gridY); + _walkNodes[index]._sequenceId = 0x7AF; + gnapSequenceId = 0x7AF; + } else { + _vm->_gameSys->insertSequence(makeRid(datNum, 0x7B0), _walkNodes[index]._id, + makeRid(gnapSequenceDatNum, gnapSequenceId), gnapId, + kSeqScale | kSeqSyncWait, 0, 75 * _walkNodes[index]._gridX1 - _gridX, 48 * _walkNodes[index]._gridY1 - _gridY); + _walkNodes[index]._sequenceId = 0x7B0; + gnapSequenceId = 0x7B0; + } + } else { + if (_walkNodes[index]._deltaY == -1) + _walkNodes[index]._id -= 10; + else + _walkNodes[index]._id += 10; + int newSequenceId = getWalkSequenceId(_walkNodes[index]._deltaX, _walkNodes[index]._deltaY); + _vm->_gameSys->insertSequence(makeRid(datNum, newSequenceId), _walkNodes[index]._id, + makeRid(gnapSequenceDatNum, gnapSequenceId), gnapId, + kSeqScale | kSeqSyncWait, 0, 75 * _walkNodes[index]._gridX1 - _gridX, 48 * _walkNodes[index]._gridY1 - _gridY); + _walkNodes[index]._sequenceId = newSequenceId; + gnapSequenceId = newSequenceId; + } + gnapId = _walkNodes[index]._id; + gnapSequenceDatNum = datNum; + } + + if (flags & 8) { + if (_walkNodesCount > 0) { + _sequenceId = gnapSequenceId; + _id = gnapId; + _idleFacing = getWalkFacing(_walkNodes[_walkNodesCount - 1]._deltaX, _walkNodes[_walkNodesCount - 1]._deltaY); + _sequenceDatNum = datNum; + if (animationIndex >= 0) + _vm->_gameSys->setAnimation(makeRid(_sequenceDatNum, _sequenceId), _id, animationIndex); + } else if (animationIndex >= 0) { + _vm->_gameSys->setAnimation(0x107D3, 1, animationIndex); + _vm->_gameSys->insertSequence(0x107D3, 1, 0, 0, kSeqNone, 0, 0, 0); + } + } else { + if (sequenceId >= 0 && sequenceId != -1) { + _sequenceId = ridToEntryIndex(sequenceId); + _sequenceDatNum = ridToDatIndex(sequenceId); + if (_sequenceId == 0x7B9) { + _idleFacing = kDirBottomRight; + } else { + switch (_sequenceId) { + case 0x7BA: + _idleFacing = kDirBottomLeft; + break; + case 0x7BB: + _idleFacing = kDirUpRight; + break; + case 0x7BC: + _idleFacing = kDirUpLeft; + break; + } + } + } else { + if (_walkNodesCount > 0) { + _sequenceId = getWalkStopSequenceId(_walkNodes[_walkNodesCount - 1]._deltaX, _walkNodes[_walkNodesCount - 1]._deltaY); + _idleFacing = getWalkFacing(_walkNodes[_walkNodesCount - 1]._deltaX, _walkNodes[_walkNodesCount - 1]._deltaY); + } else if (gridX >= 0 || gridY >= 0) { + switch (_idleFacing) { + case kDirBottomRight: + _sequenceId = 0x7B9; + break; + case kDirBottomLeft: + _sequenceId = 0x7BA; + break; + case kDirUpRight: + _sequenceId = 0x7BB; + break; + default: + _sequenceId = 0x7BC; + break; + } + } else { + int dirX = _vm->_leftClickMouseX - (_vm->_gridMinX + 75 * _pos.x); + int dirY = _vm->_leftClickMouseY - (_vm->_gridMinY + 48 * _pos.y); + if (dirX == 0) + dirX = 1; + if (dirY == 0) + dirY = 1; + _sequenceId = getWalkStopSequenceId(dirX / abs(dirX), dirY / abs(dirY)); + _idleFacing = getWalkFacing(dirX / abs(dirX), dirY / abs(dirY)); + } + _sequenceDatNum = datNum; + } + + if (animationIndex < 0) { + _id = 20 * _walkDestY + 1; + } else { + _id = _walkNodesCount + animationIndex + 20 * _walkDestY; + _vm->_gameSys->setAnimation(makeRid(_sequenceDatNum, _sequenceId), _walkNodesCount + animationIndex + 20 * _walkDestY, animationIndex); + } + + if (flags & 4) { + _vm->_gameSys->insertSequence(makeRid(_sequenceDatNum, _sequenceId), _id, + makeRid(gnapSequenceDatNum, gnapSequenceId), gnapId, + kSeqScale | kSeqSyncWait, 0, 0, 0); + } else { + _vm->_gameSys->insertSequence(makeRid(_sequenceDatNum, _sequenceId), _id, + makeRid(gnapSequenceDatNum, gnapSequenceId), gnapId, + kSeqScale | kSeqSyncWait, 0, 75 * _walkDestX - _gridX, 48 * _walkDestY - _gridY); + } + } + + _pos = Common::Point(_walkDestX, _walkDestY); + + return done; +} + +int PlayerGnap::getShowSequenceId(int index, int gridX, int gridY) { + int sequenceId; + Facing facing = _idleFacing; + + if (gridY > 0 && gridX > 0) { + if (_pos.x > gridX) + _idleFacing = kDirUpLeft; + else + _idleFacing = kDirUpRight; + } else if (_idleFacing != kDirBottomRight && _idleFacing != kDirUpRight) { + _idleFacing = kDirUpLeft; + } else { + _idleFacing = kDirUpRight; + } + + switch (index) { + case 0: + if (_idleFacing == kDirUpRight) + sequenceId = 0x8A0; + else + sequenceId = 0x8A1; + break; + case 1: + if (_idleFacing == kDirUpRight) + sequenceId = 0x880; + else + sequenceId = 0x895; + break; + case 2: + if (_idleFacing == kDirUpRight) + sequenceId = 0x884; + else + sequenceId = 0x899; + break; + //Skip 3 + case 4: + if (_idleFacing == kDirUpRight) + sequenceId = 0x881; + else + sequenceId = 0x896; + break; + case 5: + if (_idleFacing == kDirUpRight) + sequenceId = 0x883; + else + sequenceId = 0x898; + break; + case 6: + if (_idleFacing == kDirUpRight) + sequenceId = 0x87E; + else + sequenceId = 0x893; + break; + case 7: + if (_idleFacing == kDirUpRight) + sequenceId = 0x848; + else + sequenceId = 0x890; + break; + case 8: + case 12: + if (_idleFacing == kDirUpRight) + sequenceId = 0x87D; + else + sequenceId = 0x892; + break; + case 9: + if (_idleFacing == kDirUpRight) + sequenceId = 0x882; + else + sequenceId = 0x897; + break; + case 10: + case 11: + if (_idleFacing == kDirUpRight) + sequenceId = 0x87C; + else + sequenceId = 0x891; + break; + case 13: + if (_idleFacing == kDirUpRight) + sequenceId = 0x888; + else + sequenceId = 0x89D; + break; + case 14: + if (_idleFacing == kDirUpRight) + sequenceId = 0x87F; + else + sequenceId = 0x894; + break; + case 15: + if (_idleFacing == kDirUpRight) + sequenceId = 0x87B; + else + sequenceId = 0x8A3; + break; + case 16: + if (_idleFacing == kDirUpRight) + sequenceId = 0x877; + else + sequenceId = 0x88C; + break; + //Skip 17 + case 18: + sequenceId = 0x887; + break; + case 19: + case 25: + if (_idleFacing == kDirUpRight) + sequenceId = 0x87A; + else + sequenceId = 0x88F; + break; + case 20: + if (_idleFacing == kDirUpRight) + sequenceId = 0x878; + else + sequenceId = 0x88D; + break; + case 21: + if (_idleFacing == kDirUpRight) + sequenceId = 0x879; + else + sequenceId = 0x88E; + break; + case 22: + if (_idleFacing == kDirUpRight) + sequenceId = 0x88A; + else + sequenceId = 0x89F; + break; + case 23: + if (_idleFacing == kDirUpRight) + sequenceId = 0x889; + else + sequenceId = 0x89E; + break; + case 24: + if (_idleFacing == kDirUpRight) + sequenceId = 0x886; + else + sequenceId = 0x89B; + break; + //Skip 26 + //Skip 27 + //Skip 28 + //Skip 29 + default: + _idleFacing = facing; + sequenceId = getSequenceId(kGSImpossible, Common::Point(0, 0)); + break; + } + + return sequenceId; +} + +void PlayerGnap::idle() { + if (_sequenceDatNum == 1 && + (_sequenceId == 0x7A6 || _sequenceId == 0x7AA || + _sequenceId == 0x832 || _sequenceId == 0x841 || + _sequenceId == 0x842 || _sequenceId == 0x8A2 || + _sequenceId == 0x833 || _sequenceId == 0x834 || + _sequenceId == 0x885 || _sequenceId == 0x7A8 || + _sequenceId == 0x831 || _sequenceId == 0x89A)) { + _vm->_gameSys->insertSequence(getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, _id, + makeRid(_sequenceDatNum, _sequenceId), _id, + kSeqSyncExists, 0, 75 * _pos.x - _gridX, 48 * _pos.y - _gridY); + _sequenceId = getSequenceId(kGSIdle, Common::Point(0, 0)); + _sequenceDatNum = 1; + } +} + +void PlayerGnap::actionIdle(int sequenceId) { + if (_sequenceId != -1 && ridToDatIndex(sequenceId) == _sequenceDatNum && ridToEntryIndex(sequenceId) == _sequenceId) { + _vm->_gameSys->insertSequence(getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, _id, + makeRid(_sequenceDatNum, _sequenceId), _id, + kSeqSyncExists, 0, 75 * _pos.x - _gridX, 48 * _pos.y - _gridY); + _sequenceId = getSequenceId(kGSIdle, Common::Point(0, 0)); + _sequenceDatNum = 1; + } +} + +void PlayerGnap::playImpossible(Common::Point gridPos) { + playSequence(getSequenceId(kGSImpossible, gridPos) | 0x10000); +} + +void PlayerGnap::playScratchingHead(Common::Point gridPos) { + playSequence(getSequenceId(kGSScratchingHead, gridPos) | 0x10000); +} + +void PlayerGnap::playMoan1(Common::Point gridPos) { + playSequence(getSequenceId(kGSMoan1, gridPos) | 0x10000); +} + +void PlayerGnap::playMoan2(Common::Point gridPos) { + playSequence(getSequenceId(kGSMoan2, gridPos) | 0x10000); +} + +void PlayerGnap::playBrainPulsating(Common::Point gridPos) { + playSequence(getSequenceId(kGSBrainPulsating, gridPos) | 0x10000); +} + +void PlayerGnap::playPullOutDevice(Common::Point gridPos) { + playSequence(getSequenceId(kGSPullOutDevice, gridPos) | 0x10000); +} + +void PlayerGnap::playPullOutDeviceNonWorking(Common::Point gridPos) { + playSequence(getSequenceId(kGSPullOutDeviceNonWorking, gridPos) | 0x10000); +} + +void PlayerGnap::playUseDevice(Common::Point gridPos) { + playSequence(getSequenceId(kGSUseDevice, gridPos) | 0x10000); +} + +void PlayerGnap::playIdle(Common::Point gridPos) { + playSequence(getSequenceId(kGSIdle, gridPos) | 0x10000); +} + +void PlayerGnap::playShowItem(int itemIndex, int gridLookX, int gridLookY) { + playSequence(getShowSequenceId(itemIndex, gridLookX, gridLookY) | 0x10000); +} + +void PlayerGnap::playShowCurrItem(Common::Point destPos, int gridLookX, int gridLookY) { + PlayerPlat& plat = *_vm->_plat; + + if (plat._pos == destPos) + plat.makeRoom(); + walkTo(destPos, -1, -1, 1); + playShowItem(_vm->_grabCursorSpriteIndex, gridLookX, gridLookY); +} + +bool PlayerGnap::doPlatypusAction(int gridX, int gridY, int platSequenceId, int callback) { + PlayerPlat& plat = *_vm->_plat; + bool result = false; + + if (_actionStatus <= -1 && plat._actionStatus <= -1) { + _actionStatus = 100; + Common::Point checkPt = plat._pos + Common::Point(gridX, gridY); + if (_vm->isPointBlocked(checkPt) && (_pos != checkPt)) { + plat.walkStep(); + checkPt = plat._pos + Common::Point(gridX, gridY); + } + + if (!_vm->isPointBlocked(checkPt) && (_pos != checkPt)) { + walkTo(checkPt, 0, 0x107B9, 1); + while (_vm->_gameSys->getAnimationStatus(0) != 2 && !_vm->_gameDone) { + _vm->updateMouseCursor(); + _vm->doCallback(callback); + _vm->gameUpdateTick(); + } + _vm->_gameSys->setAnimation(0, 0, 0); + if (_pos == plat._pos + Common::Point(gridX, gridY)) { + _vm->_gameSys->setAnimation(platSequenceId, plat._id, 1); + plat.playSequence(platSequenceId); + while (_vm->_gameSys->getAnimationStatus(1) != 2 && !_vm->_gameDone) { + _vm->updateMouseCursor(); + _vm->doCallback(callback); + _vm->gameUpdateTick(); + } + result = true; + } + } + _actionStatus = -1; + } + return result; +} + +void PlayerGnap::useDisguiseOnPlatypus() { + _vm->_gameSys->setAnimation(0x10846, _id, 0); + playSequence(0x10846); + while (_vm->_gameSys->getAnimationStatus(0) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + + _vm->_newSceneNum = 47; + _vm->_isLeavingScene = true; + _vm->_sceneDone = true; + _vm->setFlag(kGFPlatypusDisguised); +} + +/************************************************************************************************/ + +PlayerPlat::PlayerPlat(GnapEngine * vm) : Character(vm) {} + +int PlayerPlat::getSequenceId(int kind, Common::Point gridPos) { + // The original had 3 parameters, all always set to 0. + // The code to handle the other values has been removed. + + int sequenceId = 0x7CB; + + if (_idleFacing != kDirIdleLeft) { + sequenceId = 0x7CC; + _idleFacing = kDirIdleRight; + } + + return sequenceId | 0x10000; +} + +void PlayerPlat::playSequence(int sequenceId) { + _vm->_gameSys->insertSequence(sequenceId, _id, + makeRid(_sequenceDatNum, _sequenceId), _id, + kSeqScale | kSeqSyncWait, 0, 75 * _pos.x - _gridX, 48 * _pos.y - _gridY); + _sequenceId = ridToEntryIndex(sequenceId); + _sequenceDatNum = ridToDatIndex(sequenceId); +} + +void PlayerPlat::updateIdleSequence() { + if (_actionStatus < 0 && _vm->_gnap->_actionStatus < 0) { + if (_vm->_timers[0] > 0) { + if (_vm->_timers[1] == 0) { + _vm->_timers[1] = _vm->getRandom(20) + 30; + int rnd = _vm->getRandom(10); + if (_idleFacing != kDirIdleLeft) { + if (rnd != 0 || _sequenceId != 0x7CA) { + if (rnd != 1 || _sequenceId != 0x7CA) + playSequence(0x107CA); + else + playSequence(0x10845); + } else { + playSequence(0x107CC); + } + } else if (rnd != 0 || _sequenceId != 0x7C9) { + if (rnd != 1 || _sequenceId != 0x7C9) { + if (rnd != 2 || _sequenceId != 0x7C9) + playSequence(0x107C9); + else + playSequence(0x108A4); + } else { + playSequence(0x10844); + } + } else { + playSequence(0x107CB); + } + } + } else { + _vm->_timers[0] = _vm->getRandom(75) + 75; + makeRoom(); + } + } else { + _vm->_timers[0] = 100; + _vm->_timers[1] = 35; + } +} + +void PlayerPlat::updateIdleSequence2() { + PlayerGnap& gnap = *_vm->_gnap; + + if (_actionStatus < 0 && gnap._actionStatus < 0) { + if (_vm->_timers[0]) { + if (!_vm->_timers[1]) { + _vm->_timers[1] = _vm->getRandom(20) + 30; + if (_idleFacing != kDirIdleLeft) { + if (_vm->getRandom(10) >= 2 || _sequenceId != 0x7CA) + playSequence(0x107CA); + else + playSequence(0x107CC); + } else { + if (_vm->getRandom(10) >= 2 || _sequenceId != 0x7C9) { + playSequence(0x107C9); + } else { + playSequence(0x107CB); + } + } + } + } else { + _vm->_timers[0] = _vm->getRandom(75) + 75; + makeRoom(); + } + } else { + _vm->_timers[0] = 100; + _vm->_timers[1] = 35; + } +} + +void PlayerPlat::initPos(int gridX, int gridY, Facing facing) { + _vm->_timers[0] = 50; + _vm->_timers[1] = 20; + _pos = Common::Point(gridX, gridY); + if (facing == kDirIdleLeft) + _idleFacing = kDirIdleLeft; + else + _idleFacing = facing; + if (_idleFacing == kDirIdleRight) { + _sequenceId = 0x7D1; + } else { + _sequenceId = 0x7C1; + _idleFacing = kDirIdleLeft; + } + _id = 20 * _pos.y; + _sequenceDatNum = 1; + _vm->_gameSys->insertSequence(makeRid(1, _sequenceId), 20 * _pos.y, + 0, 0, + kSeqScale, 0, 75 * _pos.x - _gridX, 48 * _pos.y - _gridY); +} + +int PlayerPlat::getWalkSequenceId(int deltaX, int deltaY) { + static const int walkSequenceIds[9] = { + 0x7C5, 0x000, 0x7C8, + 0x7C4, 0x000, 0x7C7, + 0x7C3, 0x000, 0x7C6 + }; + + int id = 3 * (deltaX + 1) + deltaY + 1; + assert(id >= 0 && id < 9); + + return walkSequenceIds[id]; +} + +bool PlayerPlat::walkTo(Common::Point gridPos, int animationIndex, int sequenceId, int flags) { + // Note: flags is always 1. The code could be simplified. + + int datNum = flags & 3; + PlayerGnap& gnap = *_vm->_gnap; + + _vm->_timers[1] = 60; + + int gridX = gridPos.x; + if (gridX < 0) + gridX = (_vm->_leftClickMouseX - _vm->_gridMinX + 37) / 75; + + int gridY = gridPos.y; + if (gridY < 0) + gridY = (_vm->_leftClickMouseY - _vm->_gridMinY + 24) / 48; + + _walkDestX = CLIP(gridX, 0, _vm->_gridMaxX - 1); + _walkDestY = CLIP(gridY, 0, _vm->_gridMaxY - 1); + + if (animationIndex >= 0 && gnap._pos == Common::Point(_walkDestX, _walkDestY)) + gnap.walkStep(); + + bool done = findPath1(_pos.x, _pos.y, 0); + + if (!done) + done = findPath2(_pos.x, _pos.y, 0); + + if (!done) + done = findPath3(_pos.x, _pos.y); + + if (!done) + done = findPath4(_pos.x, _pos.y); + + int platSequenceId = _sequenceId; + int platId = _id; + int platSequenceDatNum = _sequenceDatNum; + + for (int index = 0; index < _walkNodesCount; ++index) { + _walkNodes[index]._id = index + 20 * _walkNodes[index]._gridY1; + if (_walkNodes[index]._deltaX == 1 && _walkNodes[index]._deltaY == 0) { + if (index % 2) { + _vm->_gameSys->insertSequence(makeRid(datNum, 0x7CD), _walkNodes[index]._id, + makeRid(platSequenceDatNum, platSequenceId), platId, + kSeqScale | kSeqSyncWait, 0, 75 * _walkNodes[index]._gridX1 - _gridX, 48 * _walkNodes[index]._gridY1 - _gridY); + _walkNodes[index]._sequenceId = 0x7CD; + platSequenceId = 0x7CD; + } else { + _vm->_gameSys->insertSequence(makeRid(datNum, 0x7CE), _walkNodes[index]._id, + makeRid(platSequenceDatNum, platSequenceId), platId, + kSeqScale | kSeqSyncWait, 0, 75 * _walkNodes[index]._gridX1 - _gridX, 48 * _walkNodes[index]._gridY1 - _gridY); + _walkNodes[index]._sequenceId = 0x7CE; + platSequenceId = 0x7CE; + } + } else if (_walkNodes[index]._deltaX == -1 && _walkNodes[index]._deltaY == 0) { + if (index % 2) { + _vm->_gameSys->insertSequence(makeRid(datNum, 0x7CF), _walkNodes[index]._id, + makeRid(platSequenceDatNum, platSequenceId), platId, + kSeqScale | kSeqSyncWait, 0, 75 * _walkNodes[index]._gridX1 - _gridX, 48 * _walkNodes[index]._gridY1 - _gridY); + _walkNodes[index]._sequenceId = 0x7CF; + platSequenceId = 0x7CF; + } else { + _vm->_gameSys->insertSequence(makeRid(datNum, 0x7D0), _walkNodes[index]._id, + makeRid(platSequenceDatNum, platSequenceId), platId, + kSeqScale | kSeqSyncWait, 0, 75 * _walkNodes[index]._gridX1 - _gridX, 48 * _walkNodes[index]._gridY1 - _gridY); + _walkNodes[index]._sequenceId = 0x7D0; + platSequenceId = 0x7D0; + } + } else { + if (_walkNodes[index]._deltaY == -1) + _walkNodes[index]._id -= 10; + else + _walkNodes[index]._id += 10; + int newSequenceId = getWalkSequenceId(_walkNodes[index]._deltaX, _walkNodes[index]._deltaY); + _vm->_gameSys->insertSequence(makeRid(datNum, newSequenceId), _walkNodes[index]._id, + makeRid(platSequenceDatNum, platSequenceId), platId, + kSeqScale | kSeqSyncWait, 0, 75 * _walkNodes[index]._gridX1 - _gridX, 48 * _walkNodes[index]._gridY1 - _gridY); + _walkNodes[index]._sequenceId = newSequenceId; + platSequenceId = newSequenceId; + } + platId = _walkNodes[index]._id; + platSequenceDatNum = datNum; + } + + if (flags & 8) { + if (_walkNodesCount > 0) { + _sequenceId = platSequenceId; + _id = platId; + _sequenceDatNum = datNum; + if (_walkNodes[_walkNodesCount - 1]._deltaX > 0) + _idleFacing = kDirIdleLeft; + else if (_walkNodes[_walkNodesCount - 1]._deltaX < 0) + _idleFacing = kDirIdleRight; + else if (_walkNodes[_walkNodesCount - 1]._gridX1 % 2) + _idleFacing = kDirIdleRight; + else + _idleFacing = kDirIdleLeft; + if (animationIndex >= 0) + _vm->_gameSys->setAnimation(makeRid(_sequenceDatNum, _sequenceId), _id, animationIndex); + } else if (animationIndex >= 0) { + _vm->_gameSys->setAnimation(0x107D3, 1, animationIndex); + _vm->_gameSys->insertSequence(0x107D3, 1, 0, 0, kSeqNone, 0, 0, 0); + } + } else { + if (sequenceId >= 0 && sequenceId != -1) { + _sequenceId = ridToEntryIndex(sequenceId); + _sequenceDatNum = ridToDatIndex(sequenceId); + if (_sequenceId == 0x7C2) { + _idleFacing = kDirIdleLeft; + } else if (_sequenceId == 0x7D2) { + _idleFacing = kDirIdleRight; + } + } else { + if (_walkNodesCount > 0) { + if (_walkNodes[_walkNodesCount - 1]._deltaX > 0) { + _sequenceId = 0x7C2; + _idleFacing = kDirIdleLeft; + } else if (_walkNodes[_walkNodesCount - 1]._deltaX < 0) { + _sequenceId = 0x7D2; + _idleFacing = kDirIdleRight; + } else if (_walkNodes[0]._deltaX > 0) { + _sequenceId = 0x7C2; + _idleFacing = kDirIdleLeft; + } else if (_walkNodes[0]._deltaX < 0) { + _sequenceId = 0x7D2; + _idleFacing = kDirIdleRight; + } else { + _sequenceId = 0x7D2; + _idleFacing = kDirIdleRight; + } + } else if (_idleFacing != kDirIdleLeft) { + _sequenceId = 0x7D2; + } else { + _sequenceId = 0x7C2; + } + _sequenceDatNum = datNum; + } + + if (animationIndex < 0) { + _id = 20 * _walkDestY; + } else { + _id = animationIndex + 20 * _walkDestY; + _vm->_gameSys->setAnimation(makeRid(_sequenceDatNum, _sequenceId), animationIndex + 20 * _walkDestY, animationIndex); + } + + if (flags & 4) + _vm->_gameSys->insertSequence(makeRid(_sequenceDatNum, _sequenceId), _id, + makeRid(platSequenceDatNum, platSequenceId), platId, + 9, 0, 0, 0); + else + _vm->_gameSys->insertSequence(makeRid(_sequenceDatNum, _sequenceId), _id, + makeRid(platSequenceDatNum, platSequenceId), platId, + 9, 0, 75 * _walkDestX - _gridX, 48 * _walkDestY - _gridY); + } + + _pos = Common::Point(_walkDestX, _walkDestY); + + return done; +} +} // End of namespace Gnap diff --git a/engines/gnap/character.h b/engines/gnap/character.h new file mode 100644 index 0000000000..27e98be15c --- /dev/null +++ b/engines/gnap/character.h @@ -0,0 +1,146 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GNAP_CHARACTER_H +#define GNAP_CHARACTER_H + +namespace Gnap { + +class GnapEngine; + +enum Facing { + kDirIdleLeft = 0, + kDirBottomRight = 1, + kDirBottomLeft = 3, + kDirIdleRight = 4, + kDirUpLeft = 5, + kDirUpRight = 7 +}; + +struct GridStruct { + int _deltaX, _deltaY; + int _gridX1, _gridY1; + int _sequenceId; + int _id; +}; + +const int kMaxGridStructs = 30; + +class Character { +public: + Character(GnapEngine *vm); + virtual ~Character(); + + void walkStep(); + + virtual int getSequenceId(int kind, Common::Point gridPos) = 0; + virtual void playSequence(int sequenceId) = 0; + virtual void updateIdleSequence() = 0; + virtual void updateIdleSequence2() = 0; + virtual void initPos(int gridX, int gridY, Facing facing) = 0; + virtual int getWalkSequenceId(int deltaX, int deltaY) = 0; + virtual bool walkTo(Common::Point gridPos, int animationIndex, int sequenceId, int flags) = 0; + + Common::Point _pos; + Facing _idleFacing; + int _actionStatus; + int _sequenceId; + int _sequenceDatNum; + int _id; + int _gridX; + int _gridY; + int _walkNodesCount; + GridStruct _walkNodes[kMaxGridStructs]; + int _walkDestX, _walkDestY; + int _walkDeltaX, _walkDeltaY, _walkDirX, _walkDirY, _walkDirXIncr, _walkDirYIncr; + +protected: + GnapEngine *_vm; +}; + +class PlayerGnap : public Character { +public: + PlayerGnap(GnapEngine *vm); + virtual int getSequenceId(int kind, Common::Point gridPos); + virtual void initPos(int gridX, int gridY, Facing facing); + virtual void playSequence(int sequenceId); + virtual void updateIdleSequence(); + virtual void updateIdleSequence2(); + virtual int getWalkSequenceId(int deltaX, int deltaY); + virtual bool walkTo(Common::Point gridPos, int animationIndex, int sequenceId, int flags); + + void actionIdle(int sequenceId); + bool doPlatypusAction(int gridX, int gridY, int platSequenceId, int callback); + int getShowSequenceId(int index, int gridX, int gridY); + Facing getWalkFacing(int deltaX, int deltaY); + int getWalkStopSequenceId(int deltaX, int deltaY); + void idle(); + void initBrainPulseRndValue(); + void kissPlatypus(int callback); + void playBrainPulsating(Common::Point gridPos = Common::Point(0, 0)); + void playIdle(Common::Point gridPos = Common::Point(0, 0)); + void playImpossible(Common::Point gridPos = Common::Point(0, 0)); + void playMoan1(Common::Point gridPos = Common::Point(0, 0)); + void playMoan2(Common::Point gridPos = Common::Point(0, 0)); + void playPullOutDevice(Common::Point gridPos = Common::Point(0, 0)); + void playPullOutDeviceNonWorking(Common::Point gridPos = Common::Point(0, 0)); + void playScratchingHead(Common::Point gridPos = Common::Point(0, 0)); + void playShowCurrItem(Common::Point destPos, int gridLookX, int gridLookY); + void playShowItem(int itemIndex, int gridLookX, int gridLookY); + void playUseDevice(Common::Point gridPos = Common::Point(0, 0)); + void useDeviceOnPlatypus(); + void useDisguiseOnPlatypus(); + void useJointOnPlatypus(); + + int _brainPulseNum; + int _brainPulseRndValue; + +private: + bool findPath1(int gridX, int gridY, int index); + bool findPath2(int gridX, int gridY, int index); + bool findPath3(int gridX, int gridY); + bool findPath4(int gridX, int gridY); +}; + +class PlayerPlat : public Character { +public: + PlayerPlat(GnapEngine *vm); + virtual ~PlayerPlat() {} + virtual int getSequenceId(int kind = 0, Common::Point gridPos = Common::Point(0, 0)); + virtual void initPos(int gridX, int gridY, Facing facing); + virtual void playSequence(int sequenceId); + virtual void updateIdleSequence(); + virtual void updateIdleSequence2(); + virtual int getWalkSequenceId(int deltaX, int deltaY); + virtual bool walkTo(Common::Point gridPos, int animationIndex, int sequenceId, int flags); + + void makeRoom(); + +private: + bool findPath1(int gridX, int gridY, int index); + bool findPath2(int gridX, int gridY, int index); + bool findPath3(int gridX, int gridY); + bool findPath4(int gridX, int gridY); +}; +} // End of namespace Gnap + +#endif // GNAP_CHARACTER_H diff --git a/engines/gnap/configure.engine b/engines/gnap/configure.engine new file mode 100644 index 0000000000..7aa538fd2c --- /dev/null +++ b/engines/gnap/configure.engine @@ -0,0 +1,3 @@ +# This file is included from the main "configure" script +# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] +add_engine gnap "UFOs" no diff --git a/engines/gnap/datarchive.cpp b/engines/gnap/datarchive.cpp new file mode 100644 index 0000000000..c74766bd03 --- /dev/null +++ b/engines/gnap/datarchive.cpp @@ -0,0 +1,124 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/dcl.h" +#include "common/file.h" +#include "common/stream.h" +#include "common/substream.h" + +#include "gnap/gnap.h" +#include "gnap/datarchive.h" + +#include "engines/util.h" + +namespace Gnap { + +// DatArchive + +DatArchive::DatArchive(const char *filename) { + _fd = new Common::File(); + if (!_fd->open(filename)) + error("DatArchive::DatArchive() Could not open %s", filename); + _fd->skip(8); // Skip signature + _fd->skip(2); // Skip unknown + _fd->skip(2); // Skip unknown + _entriesCount = _fd->readUint32LE(); + debugC(kDebugBasic, "_entriesCount: %d", _entriesCount); + _fd->skip(4); // Skip unknown + _entries = new DatEntry[_entriesCount]; + for (int i = 0; i < _entriesCount; ++i) { + _entries[i]._ofs = _fd->readUint32LE(); + _entries[i]._outSize1 = _fd->readUint32LE(); + _entries[i]._type = _fd->readUint32LE(); + _entries[i]._outSize2 = _fd->readUint32LE(); + } +} + +DatArchive::~DatArchive() { + _fd->close(); + delete _fd; + delete[] _entries; +} + +byte *DatArchive::load(int index) { + _fd->seek(_entries[index]._ofs); + debugC(kDebugBasic, "_entries[index].outSize2: %d; _entries[index].outSize1: %d", _entries[index]._outSize2, _entries[index]._outSize1); + byte *buffer = new byte[_entries[index]._outSize1]; + if (!Common::decompressDCL(_fd, buffer, _entries[index]._outSize2, _entries[index]._outSize1)) + error("DatArchive::load() Error during decompression of entry %d", index); + return buffer; +} + +// DatManager + +DatManager::DatManager() { + for (int i = 0; i < kMaxDatArchives; ++i) + _datArchives[i] = nullptr; +} + +DatManager::~DatManager() { + for (int i = 0; i < kMaxDatArchives; ++i) + delete _datArchives[i]; +} + +void DatManager::open(int index, const char *filename) { + close(index); + _datArchives[index] = new DatArchive(filename); + warning("Loading %s - %d", filename, index); +} + +void DatManager::close(int index) { + delete _datArchives[index]; + _datArchives[index] = nullptr; +} + +byte *DatManager::loadResource(int resourceId) { + const int datIndex = ridToDatIndex(resourceId); + const int entryIndex = ridToEntryIndex(resourceId); + return _datArchives[datIndex] ? _datArchives[datIndex]->load(entryIndex) : 0; +} + +uint32 DatManager::getResourceType(int resourceId) { + const int datIndex = ridToDatIndex(resourceId); + const int entryIndex = ridToEntryIndex(resourceId); + return _datArchives[datIndex] ? _datArchives[datIndex]->getEntryType(entryIndex) : 0; +} + +uint32 DatManager::getResourceSize(int resourceId) { + const int datIndex = ridToDatIndex(resourceId); + const int entryIndex = ridToEntryIndex(resourceId); + return _datArchives[datIndex] ? _datArchives[datIndex]->getEntrySize(entryIndex) : 0; +} + +int ridToDatIndex(int resourceId) { + return (resourceId & 0xFFFF0000) >> 16; +} + +int ridToEntryIndex(int resourceId) { + return resourceId & 0xFFFF; +} + +int makeRid(int datIndex, int entryIndex) { + return (datIndex << 16) | entryIndex; +} + +} // End of namespace Gnap diff --git a/engines/gnap/datarchive.h b/engines/gnap/datarchive.h new file mode 100644 index 0000000000..e9220f8e6f --- /dev/null +++ b/engines/gnap/datarchive.h @@ -0,0 +1,80 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GNAP_DATARCHIVE_H +#define GNAP_DATARCHIVE_H + +#include "common/array.h" +#include "common/events.h" +#include "common/file.h" +#include "common/memstream.h" +#include "common/random.h" +#include "common/str.h" +#include "common/substream.h" +#include "common/system.h" +#include "engines/engine.h" + +namespace Gnap { + +struct DatEntry { + uint32 _ofs; + uint32 _outSize1; + uint32 _type; + uint32 _outSize2; +}; + +class DatArchive { +public: + DatArchive(const char *filename); + ~DatArchive(); + byte *load(int index); + int getCount() const { return _entriesCount; } + uint32 getEntryType(int index) { return _entries[index]._type; } + uint32 getEntrySize(int index) { return _entries[index]._outSize1; } +protected: + Common::File *_fd; + int _entriesCount; + DatEntry *_entries; +}; + +const int kMaxDatArchives = 2; + +class DatManager { +public: + DatManager(); + ~DatManager(); + void open(int index, const char *filename); + void close(int index); + byte *loadResource(int resourceId); + uint32 getResourceType(int resourceId); + uint32 getResourceSize(int resourceId); +protected: + DatArchive *_datArchives[kMaxDatArchives]; +}; + +int ridToDatIndex(int resourceId); +int ridToEntryIndex(int resourceId); +int makeRid(int datIndex, int entryIndex); + +} // End of namespace Gnap + +#endif // GNAP_DATARCHIVE_H diff --git a/engines/gnap/debugger.cpp b/engines/gnap/debugger.cpp new file mode 100644 index 0000000000..07f3f6714c --- /dev/null +++ b/engines/gnap/debugger.cpp @@ -0,0 +1,42 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gnap/debugger.h" +#include "gnap/gnap.h" + +namespace Gnap { + +Debugger::Debugger(GnapEngine *vm) : GUI::Debugger(), _vm(vm) { + // Register methods + registerCmd("hotspots", WRAP_METHOD(Debugger, Cmd_Hotspots)); + + // Set fields + _showHotspotNumber = false; +} + +bool Debugger::Cmd_Hotspots(int argc, const char **argv) { + _showHotspotNumber ^= 1; + + return true; +} + +} // End of namespace Gnap diff --git a/engines/gnap/debugger.h b/engines/gnap/debugger.h new file mode 100644 index 0000000000..ac83cc5504 --- /dev/null +++ b/engines/gnap/debugger.h @@ -0,0 +1,54 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GNAP_DEBUGGER_H +#define GNAP_DEBUGGER_H + +#include "common/scummsys.h" +#include "gui/debugger.h" + +namespace Gnap { + +class GnapEngine; + +class Debugger : public GUI::Debugger { +private: + GnapEngine *_vm; +public: + /* + * Specifies whether to show the hotspot IDs + */ + bool _showHotspotNumber; +protected: + /** + * List the active hotspots during the current time period + */ + bool Cmd_Hotspots(int argc, const char **argv); + +public: + Debugger(GnapEngine *vm); + virtual ~Debugger() {} +}; + +} // End of namespace Gnap + +#endif diff --git a/engines/gnap/detection.cpp b/engines/gnap/detection.cpp new file mode 100644 index 0000000000..a7e9eece4a --- /dev/null +++ b/engines/gnap/detection.cpp @@ -0,0 +1,197 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gnap/gnap.h" + +#include "common/config-manager.h" +#include "engines/advancedDetector.h" +#include "common/savefile.h" +#include "common/system.h" +#include "base/plugins.h" +#include "graphics/thumbnail.h" + +static const PlainGameDescriptor gnapGames[] = { + { "gnap", "Gnap" }, + { 0, 0 } +}; + +namespace Gnap { + +static const ADGameDescription gameDescriptions[] = { + { + "gnap", "", + { + {"stock_n.dat", 0, "46819043d019a2f36b727cc2bdd6980f", 12515823}, + AD_LISTEND + }, + Common::EN_ANY, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO0() + }, + { + "gnap", "", + { + {"stock_n.dat", 0, "46819043d019a2f36b727cc2bdd6980f", 12995485}, + AD_LISTEND + }, + Common::RU_RUS, Common::kPlatformWindows, ADGF_UNSTABLE, GUIO0() + }, + + AD_TABLE_END_MARKER +}; + +} // End of namespace Gnap + +class GnapMetaEngine : public AdvancedMetaEngine { +public: + GnapMetaEngine() : AdvancedMetaEngine(Gnap::gameDescriptions, sizeof(ADGameDescription), gnapGames) { + _singleId = "gnap"; + _maxScanDepth = 3; + } + + virtual const char *getName() const { + return "Gnap"; + } + + virtual const char *getOriginalCopyright() const { + return "Gnap (C) Artech Digital Entertainment 1997"; + } + + virtual bool hasFeature(MetaEngineFeature f) const; + virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; + virtual int getMaximumSaveSlot() const; + virtual SaveStateList listSaves(const char *target) const; + SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const; + virtual void removeSaveState(const char *target, int slot) const; +}; + +bool GnapMetaEngine::hasFeature(MetaEngineFeature f) const { + return + (f == kSupportsListSaves) || + (f == kSupportsLoadingDuringStartup) || + (f == kSupportsDeleteSave) || + (f == kSavesSupportMetaInfo) || + (f == kSavesSupportThumbnail) || + (f == kSavesSupportCreationDate); +} + +bool Gnap::GnapEngine::hasFeature(EngineFeature f) const { + return + (f == kSupportsRTL) || + (f == kSupportsLoadingDuringRuntime) || + (f == kSupportsSavingDuringRuntime); +} + +void GnapMetaEngine::removeSaveState(const char *target, int slot) const { + Common::String fileName = Common::String::format("%s.%03d", target, slot); + g_system->getSavefileManager()->removeSavefile(fileName); +} + +int GnapMetaEngine::getMaximumSaveSlot() const { return 99; } + +SaveStateList GnapMetaEngine::listSaves(const char *target) const { + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Common::StringArray filenames; + Common::String saveDesc; + Common::String pattern = Common::String::format("%s.0##", target); + Gnap::GnapSavegameHeader header; + + filenames = saveFileMan->listSavefiles(pattern); + + SaveStateList saveList; + for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) { + const char *ext = strrchr(file->c_str(), '.'); + int slot = ext ? atoi(ext + 1) : -1; + + if (slot >= 0 && slot < getMaximumSaveSlot()) { + Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(*file); + + if (in) { + Gnap::GnapEngine::readSavegameHeader(in, header); + saveList.push_back(SaveStateDescriptor(slot, header._saveName)); + + if (header._thumbnail) { + header._thumbnail->free(); + delete header._thumbnail; + } + delete in; + } + } + } + + // Sort saves based on slot number. + Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator()); + return saveList; +} + +SaveStateDescriptor GnapMetaEngine::querySaveMetaInfos(const char *target, int slot) const { + Common::String fileName = Common::String::format("%s.%03d", target, slot); + Common::InSaveFile *file = g_system->getSavefileManager()->openForLoading(fileName); + if (file) { + char saveIdentBuffer[5]; + file->read(saveIdentBuffer, 5); + + int32 version = file->readByte(); + if (version > GNAP_SAVEGAME_VERSION) { + delete file; + return SaveStateDescriptor(); + } + + char saveName[256]; + char ch; + int i = 0; + while ((ch = (char)file->readByte()) != '\0') + saveName[i++] = ch; + + SaveStateDescriptor desc(slot, saveName); + + if (version != 1) { + Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*file); + desc.setThumbnail(thumbnail); + } + + int year = file->readSint16LE(); + int month = file->readSint16LE(); + int day = file->readSint16LE(); + int hour = file->readSint16LE(); + int minutes = file->readSint16LE(); + + desc.setSaveDate(year, month, day); + desc.setSaveTime(hour, minutes); + + delete file; + return desc; + } + + return SaveStateDescriptor(); +} + +bool GnapMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { + if (desc) { + *engine = new Gnap::GnapEngine(syst, desc); + } + return desc != 0; +} + +#if PLUGIN_ENABLED_DYNAMIC(GNAP) + REGISTER_PLUGIN_DYNAMIC(GNAP, PLUGIN_TYPE_ENGINE, GnapMetaEngine); +#else + REGISTER_PLUGIN_STATIC(GNAP, PLUGIN_TYPE_ENGINE, GnapMetaEngine); +#endif diff --git a/engines/gnap/fontdata.h b/engines/gnap/fontdata.h new file mode 100644 index 0000000000..ef39df960e --- /dev/null +++ b/engines/gnap/fontdata.h @@ -0,0 +1,849 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GNAP_FONTDATA_H +#define GNAP_FONTDATA_H + +namespace Gnap { + +struct FONT_CHAR_INFO { + const byte _width; // width, in bits (or pixels), of the character + const uint16 _offset; // offset of the character's bitmap, in bytes, into the the FONT_INFO's data array +}; + +/* +** Font data for DejaVu Sans 9pt +*/ + +/* Character bitmaps for DejaVu Sans 9pt */ +const byte _dejaVuSans9ptCharBitmaps[] = { + /* @0 ' ' (5 pixels wide) */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + 0x00, 0x00, /* */ + + /* @10 '!' (1 pixels wide) */ + 0x1B, 0xF0, /* ## ###### */ + + /* @12 '"' (3 pixels wide) */ + 0x00, 0x70, /* ### */ + 0x00, 0x00, /* */ + 0x00, 0x70, /* ### */ + + /* @18 '#' (8 pixels wide) */ + 0x04, 0x00, /* # */ + 0x14, 0x80, /* # # # */ + 0x0F, 0x80, /* ##### */ + 0x04, 0xE0, /* # ### */ + 0x1C, 0x80, /* ### # */ + 0x07, 0xC0, /* ##### */ + 0x04, 0xA0, /* # # # */ + 0x00, 0x80, /* # */ + + /* @34 '$' (5 pixels wide) */ + 0x09, 0xC0, /* # ### */ + 0x11, 0x20, /* # # # */ + 0x7F, 0xF0, /* ########### */ + 0x12, 0x20, /* # # # */ + 0x0E, 0x40, /* ### # */ + + /* @44 '%' (10 pixels wide) */ + 0x00, 0xE0, /* ### */ + 0x01, 0x10, /* # # */ + 0x11, 0x10, /* # # # */ + 0x0C, 0xE0, /* ## ### */ + 0x03, 0x00, /* ## */ + 0x01, 0x80, /* ## */ + 0x0E, 0x60, /* ### ## */ + 0x11, 0x10, /* # # # */ + 0x11, 0x00, /* # # */ + 0x0E, 0x00, /* ### */ + + /* @64 '&' (8 pixels wide) */ + 0x0E, 0x00, /* ### */ + 0x19, 0xE0, /* ## #### */ + 0x10, 0x90, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x12, 0x20, /* # # # */ + 0x0C, 0x00, /* ## */ + 0x14, 0x00, /* # # */ + 0x13, 0x00, /* # ## */ + + /* @80 ''' (1 pixels wide) */ + 0x00, 0x70, /* ### */ + + /* @82 '(' (3 pixels wide) */ + 0x07, 0xC0, /* ##### */ + 0x38, 0x38, /* ### ### */ + 0x20, 0x08, /* # # */ + + /* @88 ')' (3 pixels wide) */ + 0x20, 0x08, /* # # */ + 0x38, 0x38, /* ### ### */ + 0x07, 0xC0, /* ##### */ + + /* @94 '*' (5 pixels wide) */ + 0x01, 0x20, /* # # */ + 0x00, 0xC0, /* ## */ + 0x03, 0xF0, /* ###### */ + 0x00, 0xC0, /* ## */ + 0x01, 0x20, /* # # */ + + /* @104 '+' (7 pixels wide) */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x1F, 0xC0, /* ####### */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + + /* @118 ',' (1 pixels wide) */ + 0x38, 0x00, /* ### */ + + /* @120 '-' (3 pixels wide) */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + + /* @126 '.' (1 pixels wide) */ + 0x18, 0x00, /* ## */ + + /* @128 '/' (4 pixels wide) */ + 0x30, 0x00, /* ## */ + 0x0E, 0x00, /* ### */ + 0x01, 0xC0, /* ### */ + 0x00, 0x30, /* ## */ + + /* @136 '0' (6 pixels wide) */ + 0x07, 0xC0, /* ##### */ + 0x18, 0x30, /* ## ## */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x18, 0x30, /* ## ## */ + 0x07, 0xC0, /* ##### */ + + /* @148 '1' (5 pixels wide) */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + + /* @158 '2' (6 pixels wide) */ + 0x10, 0x20, /* # # */ + 0x18, 0x10, /* ## # */ + 0x14, 0x10, /* # # # */ + 0x12, 0x10, /* # # # */ + 0x11, 0x30, /* # # ## */ + 0x10, 0xE0, /* # ### */ + + /* @170 '3' (6 pixels wide) */ + 0x08, 0x20, /* # # */ + 0x10, 0x10, /* # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x0E, 0xE0, /* ### ### */ + + /* @182 '4' (6 pixels wide) */ + 0x06, 0x00, /* ## */ + 0x05, 0x80, /* # ## */ + 0x04, 0x40, /* # # */ + 0x04, 0x30, /* # ## */ + 0x1F, 0xF0, /* ######### */ + 0x04, 0x00, /* # */ + + /* @194 '5' (6 pixels wide) */ + 0x08, 0xF0, /* # #### */ + 0x10, 0x90, /* # # # */ + 0x10, 0x90, /* # # # */ + 0x10, 0x90, /* # # # */ + 0x19, 0x90, /* ## ## # */ + 0x0F, 0x00, /* #### */ + + /* @206 '6' (6 pixels wide) */ + 0x07, 0xC0, /* ##### */ + 0x19, 0x20, /* ## # # */ + 0x10, 0x90, /* # # # */ + 0x10, 0x90, /* # # # */ + 0x19, 0x90, /* ## ## # */ + 0x0F, 0x20, /* #### # */ + + /* @218 '7' (6 pixels wide) */ + 0x00, 0x10, /* # */ + 0x10, 0x10, /* # # */ + 0x0C, 0x10, /* ## # */ + 0x03, 0x10, /* ## # */ + 0x00, 0xD0, /* ## # */ + 0x00, 0x30, /* ## */ + + /* @230 '8' (6 pixels wide) */ + 0x0E, 0xE0, /* ### ### */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x0E, 0xE0, /* ### ### */ + + /* @242 '9' (6 pixels wide) */ + 0x09, 0xE0, /* # #### */ + 0x13, 0x30, /* # ## ## */ + 0x12, 0x10, /* # # # */ + 0x12, 0x10, /* # # # */ + 0x09, 0x30, /* # # ## */ + 0x07, 0xC0, /* ##### */ + + /* @254 ':' (1 pixels wide) */ + 0x19, 0x80, /* ## ## */ + + /* @256 ';' (1 pixels wide) */ + 0x39, 0x80, /* ### ## */ + + /* @258 '<' (8 pixels wide) */ + 0x03, 0x00, /* ## */ + 0x03, 0x00, /* ## */ + 0x03, 0x00, /* ## */ + 0x04, 0x80, /* # # */ + 0x04, 0x80, /* # # */ + 0x04, 0x80, /* # # */ + 0x0C, 0xC0, /* ## ## */ + 0x08, 0x40, /* # # */ + + /* @274 '=' (8 pixels wide) */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + 0x05, 0x00, /* # # */ + + /* @290 '>' (8 pixels wide) */ + 0x08, 0x40, /* # # */ + 0x0C, 0xC0, /* ## ## */ + 0x04, 0x80, /* # # */ + 0x04, 0x80, /* # # */ + 0x04, 0x80, /* # # */ + 0x03, 0x00, /* ## */ + 0x03, 0x00, /* ## */ + 0x03, 0x00, /* ## */ + + /* @306 '?' (5 pixels wide) */ + 0x00, 0x20, /* # */ + 0x00, 0x10, /* # */ + 0x1B, 0x10, /* ## ## # */ + 0x00, 0x90, /* # # */ + 0x00, 0x60, /* ## */ + + /* @316 '@' (11 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x10, 0x40, /* # # */ + 0x20, 0x20, /* # # */ + 0x47, 0x10, /* # ### # */ + 0x48, 0x90, /* # # # # */ + 0x48, 0x90, /* # # # # */ + 0x48, 0x90, /* # # # # */ + 0x4F, 0x90, /* # ##### # */ + 0x28, 0x20, /* # # # */ + 0x04, 0x60, /* # ## */ + 0x03, 0x80, /* ### */ + + /* @338 'A' (8 pixels wide) */ + 0x10, 0x00, /* # */ + 0x0E, 0x00, /* ### */ + 0x05, 0xC0, /* # ### */ + 0x04, 0x30, /* # ## */ + 0x04, 0x30, /* # ## */ + 0x05, 0xC0, /* # ### */ + 0x0E, 0x00, /* ### */ + 0x10, 0x00, /* # */ + + /* @354 'B' (6 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x0E, 0xE0, /* ### ### */ + + /* @366 'C' (6 pixels wide) */ + 0x07, 0xC0, /* ##### */ + 0x08, 0x20, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x08, 0x20, /* # # */ + + /* @378 'D' (7 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x08, 0x20, /* # # */ + 0x07, 0xC0, /* ##### */ + + /* @392 'E' (6 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + + /* @404 'F' (5 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + + /* @414 'G' (7 pixels wide) */ + 0x07, 0xC0, /* ##### */ + 0x08, 0x20, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x0F, 0x20, /* #### # */ + + /* @428 'H' (7 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x1F, 0xF0, /* ######### */ + + /* @442 'I' (1 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + + /* @444 'J' (3 pixels wide) */ + 0x40, 0x00, /* # */ + 0x40, 0x00, /* # */ + 0x3F, 0xF0, /* ########## */ + + /* @450 'K' (6 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x00, /* # */ + 0x02, 0x80, /* # # */ + 0x04, 0x40, /* # # */ + 0x08, 0x20, /* # # */ + 0x10, 0x10, /* # # */ + + /* @462 'L' (5 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + + /* @472 'M' (8 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x60, /* ## */ + 0x01, 0x80, /* ## */ + 0x06, 0x00, /* ## */ + 0x06, 0x00, /* ## */ + 0x01, 0x80, /* ## */ + 0x00, 0x60, /* ## */ + 0x1F, 0xF0, /* ######### */ + + /* @488 'N' (7 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x30, /* ## */ + 0x00, 0xC0, /* ## */ + 0x01, 0x00, /* # */ + 0x06, 0x00, /* ## */ + 0x18, 0x00, /* ## */ + 0x1F, 0xF0, /* ######### */ + + /* @502 'O' (7 pixels wide) */ + 0x07, 0xC0, /* ##### */ + 0x08, 0x20, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x08, 0x20, /* # # */ + 0x07, 0xC0, /* ##### */ + + /* @516 'P' (6 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x00, 0xE0, /* ### */ + + /* @528 'Q' (7 pixels wide) */ + 0x07, 0xC0, /* ##### */ + 0x08, 0x20, /* # # */ + 0x10, 0x10, /* # # */ + 0x10, 0x10, /* # # */ + 0x30, 0x10, /* ## # */ + 0x48, 0x20, /* # # # */ + 0x07, 0xC0, /* ##### */ + + /* @542 'R' (7 pixels wide) */ + 0x1F, 0xF0, /* ######### */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x01, 0x10, /* # # */ + 0x03, 0x10, /* ## # */ + 0x0C, 0xE0, /* ## ### */ + 0x10, 0x00, /* # */ + + /* @556 'S' (6 pixels wide) */ + 0x08, 0xE0, /* # ### */ + 0x11, 0x90, /* # ## # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x0E, 0x20, /* ### # */ + + /* @568 'T' (7 pixels wide) */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + + /* @582 'U' (7 pixels wide) */ + 0x0F, 0xF0, /* ######## */ + 0x18, 0x00, /* ## */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x18, 0x00, /* ## */ + 0x0F, 0xF0, /* ######## */ + + /* @596 'V' (8 pixels wide) */ + 0x00, 0x30, /* ## */ + 0x01, 0xC0, /* ### */ + 0x06, 0x00, /* ## */ + 0x18, 0x00, /* ## */ + 0x18, 0x00, /* ## */ + 0x06, 0x00, /* ## */ + 0x01, 0xC0, /* ### */ + 0x00, 0x30, /* ## */ + + /* @612 'W' (11 pixels wide) */ + 0x00, 0x10, /* # */ + 0x00, 0xE0, /* ### */ + 0x07, 0x00, /* ### */ + 0x18, 0x00, /* ## */ + 0x07, 0x80, /* #### */ + 0x00, 0x70, /* ### */ + 0x07, 0x80, /* #### */ + 0x18, 0x00, /* ## */ + 0x07, 0x00, /* ### */ + 0x00, 0xE0, /* ### */ + 0x00, 0x10, /* # */ + + /* @634 'X' (7 pixels wide) */ + 0x10, 0x10, /* # # */ + 0x08, 0x30, /* # ## */ + 0x06, 0xC0, /* ## ## */ + 0x01, 0x00, /* # */ + 0x06, 0xC0, /* ## ## */ + 0x08, 0x30, /* # ## */ + 0x10, 0x10, /* # # */ + + /* @648 'Y' (7 pixels wide) */ + 0x00, 0x10, /* # */ + 0x00, 0x60, /* ## */ + 0x01, 0x80, /* ## */ + 0x1E, 0x00, /* #### */ + 0x01, 0x80, /* ## */ + 0x00, 0x60, /* ## */ + 0x00, 0x10, /* # */ + + /* @662 'Z' (7 pixels wide) */ + 0x18, 0x10, /* ## # */ + 0x14, 0x10, /* # # # */ + 0x12, 0x10, /* # # # */ + 0x11, 0x10, /* # # # */ + 0x10, 0x90, /* # # # */ + 0x10, 0x50, /* # # # */ + 0x10, 0x30, /* # ## */ + + /* @676 '[' (2 pixels wide) */ + 0x7F, 0xF0, /* ########### */ + 0x40, 0x10, /* # # */ + + /* @680 '\' (4 pixels wide) */ + 0x00, 0x30, /* ## */ + 0x01, 0xC0, /* ### */ + 0x0E, 0x00, /* ### */ + 0x30, 0x00, /* ## */ + + /* @688 ']' (2 pixels wide) */ + 0x40, 0x10, /* # # */ + 0x7F, 0xF0, /* ########### */ + + /* @692 '^' (6 pixels wide) */ + 0x00, 0x40, /* # */ + 0x00, 0x20, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x10, /* # */ + 0x00, 0x20, /* # */ + 0x00, 0x40, /* # */ + + /* @704 '_' (6 pixels wide) */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + 0x80, 0x00, /* # */ + + /* @716 '`' (2 pixels wide) */ + 0x00, 0x08, /* # */ + 0x00, 0x10, /* # */ + + /* @720 'a' (6 pixels wide) */ + 0x0C, 0x80, /* ## # */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x0A, 0x40, /* # # # */ + 0x1F, 0x80, /* ###### */ + + /* @732 'b' (6 pixels wide) */ + 0x1F, 0xF8, /* ########## */ + 0x18, 0xC0, /* ## ## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x18, 0xC0, /* ## ## */ + 0x0F, 0x80, /* ##### */ + + /* @744 'c' (5 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x18, 0xC0, /* ## ## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x08, 0x80, /* # # */ + + /* @754 'd' (6 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x18, 0xC0, /* ## ## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x18, 0xC0, /* ## ## */ + 0x1F, 0xF8, /* ########## */ + + /* @766 'e' (6 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x0A, 0xC0, /* # # ## */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x12, 0xC0, /* # # ## */ + 0x0B, 0x80, /* # ### */ + + /* @778 'f' (4 pixels wide) */ + 0x00, 0x40, /* # */ + 0x1F, 0xF0, /* ######### */ + 0x00, 0x48, /* # # */ + 0x00, 0x48, /* # # */ + + /* @786 'g' (6 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x58, 0xC0, /* # ## ## */ + 0x90, 0x40, /* # # # */ + 0x90, 0x40, /* # # # */ + 0xD8, 0xC0, /* ## ## ## */ + 0x7F, 0xC0, /* ######### */ + + /* @798 'h' (6 pixels wide) */ + 0x1F, 0xF8, /* ########## */ + 0x00, 0x80, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x1F, 0x80, /* ###### */ + + /* @810 'i' (1 pixels wide) */ + 0x1F, 0xD0, /* ####### # */ + + /* @812 'j' (2 pixels wide) */ + 0x80, 0x00, /* # */ + 0xFF, 0xD0, /* ########## # */ + + /* @816 'k' (5 pixels wide) */ + 0x1F, 0xF8, /* ########## */ + 0x02, 0x00, /* # */ + 0x05, 0x00, /* # # */ + 0x08, 0x80, /* # # */ + 0x10, 0x40, /* # # */ + + /* @826 'l' (1 pixels wide) */ + 0x1F, 0xF8, /* ########## */ + + /* @828 'm' (9 pixels wide) */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x1F, 0x80, /* ###### */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x1F, 0x80, /* ###### */ + + /* @846 'n' (6 pixels wide) */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0x80, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + 0x1F, 0x80, /* ###### */ + + /* @858 'o' (6 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x18, 0xC0, /* ## ## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x18, 0xC0, /* ## ## */ + 0x0F, 0x80, /* ##### */ + + /* @870 'p' (6 pixels wide) */ + 0xFF, 0xC0, /* ########## */ + 0x18, 0xC0, /* ## ## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x18, 0xC0, /* ## ## */ + 0x0F, 0x80, /* ##### */ + + /* @882 'q' (6 pixels wide) */ + 0x0F, 0x80, /* ##### */ + 0x18, 0xC0, /* ## ## */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + 0x18, 0xC0, /* ## ## */ + 0xFF, 0xC0, /* ########## */ + + /* @894 'r' (4 pixels wide) */ + 0x1F, 0xC0, /* ####### */ + 0x00, 0x80, /* # */ + 0x00, 0x40, /* # */ + 0x00, 0x40, /* # */ + + /* @902 's' (5 pixels wide) */ + 0x09, 0x80, /* # ## */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x0C, 0x80, /* ## # */ + + /* @912 't' (4 pixels wide) */ + 0x00, 0x40, /* # */ + 0x1F, 0xF0, /* ######### */ + 0x10, 0x40, /* # # */ + 0x10, 0x40, /* # # */ + + /* @920 'u' (6 pixels wide) */ + 0x0F, 0xC0, /* ###### */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x10, 0x00, /* # */ + 0x08, 0x00, /* # */ + 0x1F, 0xC0, /* ####### */ + + /* @932 'v' (6 pixels wide) */ + 0x00, 0xC0, /* ## */ + 0x07, 0x00, /* ### */ + 0x18, 0x00, /* ## */ + 0x18, 0x00, /* ## */ + 0x07, 0x00, /* ### */ + 0x00, 0xC0, /* ## */ + + /* @944 'w' (9 pixels wide) */ + 0x00, 0xC0, /* ## */ + 0x07, 0x00, /* ### */ + 0x18, 0x00, /* ## */ + 0x07, 0x00, /* ### */ + 0x00, 0xC0, /* ## */ + 0x07, 0x00, /* ### */ + 0x18, 0x00, /* ## */ + 0x07, 0x00, /* ### */ + 0x00, 0xC0, /* ## */ + + /* @962 'x' (6 pixels wide) */ + 0x10, 0x40, /* # # */ + 0x0D, 0x80, /* ## ## */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x0D, 0x80, /* ## ## */ + 0x10, 0x40, /* # # */ + + /* @974 'y' (6 pixels wide) */ + 0x80, 0xC0, /* # ## */ + 0x83, 0x00, /* # ## */ + 0x4C, 0x00, /* # ## */ + 0x38, 0x00, /* ### */ + 0x07, 0x00, /* ### */ + 0x00, 0xC0, /* ## */ + + /* @986 'z' (5 pixels wide) */ + 0x18, 0x40, /* ## # */ + 0x14, 0x40, /* # # # */ + 0x12, 0x40, /* # # # */ + 0x11, 0x40, /* # # # */ + 0x10, 0xC0, /* # ## */ + + /* @996 '{' (5 pixels wide) */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x7D, 0xF0, /* ##### ##### */ + 0x40, 0x10, /* # # */ + 0x40, 0x10, /* # # */ + + /* @1006 '|' (1 pixels wide) */ + 0xFF, 0xF0, /* ############ */ + + /* @1008 '}' (5 pixels wide) */ + 0x40, 0x10, /* # # */ + 0x40, 0x10, /* # # */ + 0x7D, 0xF0, /* ##### ##### */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + + /* @1018 '~' (8 pixels wide) */ + 0x02, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x01, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x02, 0x00, /* # */ + 0x01, 0x00, /* # */ +}; + +/* Character descriptors for DejaVu Sans 9pt */ +/* { [Char width in bits], [Offset into dejaVuSans9ptCharBitmaps in bytes] } */ +const FONT_CHAR_INFO _dejaVuSans9ptCharDescriptors[] = { + {5, 0}, /* */ + {1, 10}, /* ! */ + {3, 12}, /* " */ + {8, 18}, /* # */ + {5, 34}, /* $ */ + {10, 44}, /* % */ + {8, 64}, /* & */ + {1, 80}, /* ' */ + {3, 82}, /* ( */ + {3, 88}, /* ) */ + {5, 94}, /* * */ + {7, 104}, /* + */ + {1, 118}, /* , */ + {3, 120}, /* - */ + {1, 126}, /* . */ + {4, 128}, /* / */ + {6, 136}, /* 0 */ + {5, 148}, /* 1 */ + {6, 158}, /* 2 */ + {6, 170}, /* 3 */ + {6, 182}, /* 4 */ + {6, 194}, /* 5 */ + {6, 206}, /* 6 */ + {6, 218}, /* 7 */ + {6, 230}, /* 8 */ + {6, 242}, /* 9 */ + {1, 254}, /* : */ + {1, 256}, /* ; */ + {8, 258}, /* < */ + {8, 274}, /* = */ + {8, 290}, /* > */ + {5, 306}, /* ? */ + {11, 316}, /* @ */ + {8, 338}, /* A */ + {6, 354}, /* B */ + {6, 366}, /* C */ + {7, 378}, /* D */ + {6, 392}, /* E */ + {5, 404}, /* F */ + {7, 414}, /* G */ + {7, 428}, /* H */ + {1, 442}, /* I */ + {3, 444}, /* J */ + {6, 450}, /* K */ + {5, 462}, /* L */ + {8, 472}, /* M */ + {7, 488}, /* N */ + {7, 502}, /* O */ + {6, 516}, /* P */ + {7, 528}, /* Q */ + {7, 542}, /* R */ + {6, 556}, /* S */ + {7, 568}, /* T */ + {7, 582}, /* U */ + {8, 596}, /* V */ + {11, 612}, /* W */ + {7, 634}, /* X */ + {7, 648}, /* Y */ + {7, 662}, /* Z */ + {2, 676}, /* [ */ + {4, 680}, /* \ */ + {2, 688}, /* ] */ + {6, 692}, /* ^ */ + {6, 704}, /* _ */ + {2, 716}, /* ` */ + {6, 720}, /* a */ + {6, 732}, /* b */ + {5, 744}, /* c */ + {6, 754}, /* d */ + {6, 766}, /* e */ + {4, 778}, /* f */ + {6, 786}, /* g */ + {6, 798}, /* h */ + {1, 810}, /* i */ + {2, 812}, /* j */ + {5, 816}, /* k */ + {1, 826}, /* l */ + {9, 828}, /* m */ + {6, 846}, /* n */ + {6, 858}, /* o */ + {6, 870}, /* p */ + {6, 882}, /* q */ + {4, 894}, /* r */ + {5, 902}, /* s */ + {4, 912}, /* t */ + {6, 920}, /* u */ + {6, 932}, /* v */ + {9, 944}, /* w */ + {6, 962}, /* x */ + {6, 974}, /* y */ + {5, 986}, /* z */ + {5, 996}, /* { */ + {1, 1006}, /* | */ + {5, 1008}, /* } */ + {8, 1018}, /* ~ */ +}; + +} // End of namespace Gnap + +#endif // GNAP_RESOURCE_H diff --git a/engines/gnap/gamesys.cpp b/engines/gnap/gamesys.cpp new file mode 100644 index 0000000000..e59662f08a --- /dev/null +++ b/engines/gnap/gamesys.cpp @@ -0,0 +1,1260 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gnap/gamesys.h" +#include "gnap/fontdata.h" +#include "graphics/fontman.h" +#include "graphics/font.h" +#include "image/bmp.h" + +namespace Gnap { + +void GfxItem::testUpdRect(const Common::Rect &updRect) { + Common::Rect intersectingRect; + if (!_updFlag && _prevFrame._spriteId != -1 && + _updRectsCount < 20 && intersectRect(intersectingRect, _prevFrame._rect, updRect)) + _updRects[_updRectsCount++] = intersectingRect; +} + +// GameSys + +GameSys::GameSys(GnapEngine *vm) : _vm(vm) { + _newSpriteDrawItemsCount = 0; + _removeSequenceItemsCount = 0; + _removeSpriteDrawItemsCount = 0; + _grabSpriteId = -1; + _grabSpriteChanged = false; + _reqRemoveSequenceItem = false; + _removeSequenceItemSequenceId = -1; + _removeSequenceItemValue = 0; + _gfxItemsCount = 0; + _animationsCount = 0; + _backgroundImageValue3 = 0; + _backgroundImageValue1 = 0; + _backgroundImageValue4 = 1000; + _backgroundImageValue2 = 1000; + _gameSysClock = 0; + _lastUpdateClock = 0; + _backgroundSurface = nullptr; + _frontSurface = nullptr; + for (int i = 0; i < kMaxAnimations; ++i) { + _animations[i]._sequenceId = -1; + _animations[i]._id = -1; + _animations[i]._status = 0; + } + _removeSequenceItems->_sequenceId = -1; + _removeSequenceItems->_id = -1; + _removeSequenceItems->_forceFrameReset = false; + _removeSpriteDrawItems->_id = -1; + _removeSpriteDrawItems->_surface = nullptr; + + _grabSpriteSurface1 = _grabSpriteSurface2 = nullptr; + + _screenRect = Common::Rect(0, 0, 800, 600); +} + +GameSys::~GameSys() { + if (_frontSurface) + _frontSurface->free(); + delete _frontSurface; +} + +void GameSys::insertSequence(int sequenceId, int id, int sequenceId2, int id2, int flags, int totalDuration, int16 x, int16 y) { + debugC(kDebugBasic, "GameSys::insertSequence() [%08X, %d] -> [%08X, %d] (%d, %d)", sequenceId, id, sequenceId2, id2, x, y); + Sequence sequence; + SequenceResource *sequenceResource = _vm->_sequenceCache->get(sequenceId); + sequenceResource->_sequenceId = sequenceId; + sequence._sequenceId = sequenceId; + sequence._id = id != -1 ? id : sequenceResource->_defaultId; + sequence._sequenceId2 = sequenceId2 != (int32)0x80000000 ? sequenceId2 : sequenceResource->_sequenceId2; + sequence._id2 = id2 != -1 ? id2 : sequenceResource->_defaultId2; + sequence._flags = flags != -1 ? flags : sequenceResource->_flags; + sequence._totalDuration = totalDuration != -1 ? totalDuration : sequenceResource->_totalDuration; + sequence._x = (x < 10000 && x > -10000) ? x : sequenceResource->_xOffs; + sequence._y = (y < 10000 && y > -10000) ? y : sequenceResource->_yOffs; + _fatSequenceItems.push_back(sequence); +} + +void GameSys::insertDirtyRect(const Common::Rect &rect) { + _dirtyRects.push_back(rect); +} + +void GameSys::removeSequence(int sequenceId, int id, bool resetFl) { + //WaitForSingleObject(removeSequence2Mutex, INFINITE); + if (_removeSequenceItemsCount < kMaxSequenceItems) { + _removeSequenceItems[_removeSequenceItemsCount]._sequenceId = sequenceId; + _removeSequenceItems[_removeSequenceItemsCount]._id = id; + _removeSequenceItems[_removeSequenceItemsCount]._forceFrameReset = resetFl; + ++_removeSequenceItemsCount; + //ResetEvent(removeSequenceItemsEvent); + //ReleaseMutex(removeSequence2Mutex); + //WaitForSingleObject(removeSequenceItemsEvent, INFINITE); + } +} + +void GameSys::invalidateGrabCursorSprite(int id, Common::Rect &rect, Graphics::Surface *surface1, Graphics::Surface *surface2) { + //WaitForSingleObject(grabSpriteMutex, INFINITE); + _grabSpriteId = id; + _grabSpriteRect = rect; + _grabSpriteSurface2 = surface2; + _grabSpriteSurface1 = surface1; + //ResetEvent(grabSpriteEvent); + _grabSpriteChanged = true; + //ReleaseMutex(grabSpriteMutex); + //WaitForSingleObject(grabSpriteEvent, INFINITE); +} + +void GameSys::requestClear2(bool resetFl) { + _fatSequenceItems.clear(); + _seqItems.clear(); + for (int i = 0; i < _gfxItemsCount; ++i) { + GfxItem *gfxItem = &_gfxItems[i]; + gfxItem->_sequenceId = -1; + gfxItem->_animation = nullptr; + if (resetFl) { + gfxItem->_currFrame._duration = 0; + gfxItem->_currFrame._spriteId = -1; + gfxItem->_currFrame._soundId = -1; + gfxItem->_updFlag = true; + } else { + gfxItem->_updFlag = false; + } + } + _lastUpdateClock = 0; + _gameSysClock = 0; +} + +void GameSys::requestClear1() { + _gfxItemsCount = 0; + _fatSequenceItems.clear(); + _seqItems.clear(); + _lastUpdateClock = 0; + _gameSysClock = 0; +} + +void GameSys::requestRemoveSequence(int sequenceId, int id) { + //WaitForSingleObject(removeSequence2Mutex, INFINITE); + _reqRemoveSequenceItem = true; + _removeSequenceItemSequenceId = sequenceId; + _removeSequenceItemValue = id; + + handleReqRemoveSequenceItem(); //CHECKME? + + //ResetEvent(reqClearEvent); + //ReleaseMutex(removeSequence2Mutex); + //WaitForSingleObject(reqClearEvent, INFINITE); +} + +void GameSys::waitForUpdate() { + //ResetEvent(updateEvent); + //WaitForSingleObject(updateEvent, INFINITE); +} + +int GameSys::isSequenceActive(int sequenceId, int id) { + for (uint i = 0; i < _seqItems.size(); ++i) + if (_seqItems[i]._sequenceId == sequenceId && _seqItems[i]._id == id) + return true; + return false; +} + +void GameSys::setBackgroundSurface(Graphics::Surface *surface, int a4, int a5, int a6, int a7) { + debugC(kDebugBasic, "GameSys::setBackgroundSurface() Setting background image"); + + _backgroundSurface = surface; + if (!_backgroundSurface) { + return; + } + + if (!_frontSurface || _frontSurface->w != surface->w || _frontSurface->h != surface->h) { + debugC(kDebugBasic, "GameSys::setBackgroundSurface() Creating background working surface"); + if (_frontSurface) + _frontSurface->free(); + delete _frontSurface; + _frontSurface = new Graphics::Surface(); + _frontSurface->create(surface->w, surface->h, surface->format); + } + + memcpy(_frontSurface->getPixels(), surface->getPixels(), surface->pitch * surface->h); + _vm->_system->copyRectToScreen(_frontSurface->getPixels(), _frontSurface->pitch, 0, 0, _frontSurface->w, _frontSurface->h); + + _backgroundImageValue1 = a4; + _backgroundImageValue3 = a6; + _backgroundImageValue2 = a5; + _backgroundImageValue4 = a7; + _lastUpdateClock = 0; + _gameSysClock = 0; +} + +void GameSys::setScaleValues(int a1, int a2, int a3, int a4) { + _backgroundImageValue1 = a1; + _backgroundImageValue3 = a3; + _backgroundImageValue2 = a2; + _backgroundImageValue4 = a4; +} + +void GameSys::insertSpriteDrawItem(Graphics::Surface *surface, int x, int y, int id) { + if (surface && _newSpriteDrawItemsCount < kMaxSpriteDrawItems) { + _newSpriteDrawItems[_newSpriteDrawItemsCount]._id = id; + _newSpriteDrawItems[_newSpriteDrawItemsCount]._rect = Common::Rect(x, y, x + surface->w, y + surface->h); + _newSpriteDrawItems[_newSpriteDrawItemsCount]._surface = surface; + ++_newSpriteDrawItemsCount; + } +} + +void GameSys::removeSpriteDrawItem(Graphics::Surface *surface, int id) { + if (surface && _removeSpriteDrawItemsCount < kMaxSpriteDrawItems) { + _removeSpriteDrawItems[_removeSpriteDrawItemsCount]._id = id; + _removeSpriteDrawItems[_removeSpriteDrawItemsCount]._surface = surface; + ++_removeSpriteDrawItemsCount; + } +} + +void GameSys::drawSpriteToBackground(int x, int y, int resourceId) { + SpriteResource *spriteResource = _vm->_spriteCache->get(resourceId); + uint32 *sourcePalette = spriteResource->_palette; + byte *sourcePixels = spriteResource->_pixels; + int spriteWidth = spriteResource->_width; + int spriteHeight = spriteResource->_height; + Common::Rect dstRect(0, 0, spriteWidth, spriteHeight); + blitSprite32(_backgroundSurface, x, y, sourcePixels, spriteResource->_width, dstRect, sourcePalette, spriteResource->_transparent); + _vm->_spriteCache->release(resourceId); + + // Add dirty rect so the modified background is redrawn + insertDirtyRect(Common::Rect(x, y, x + spriteWidth, y + spriteHeight)); +} + +Graphics::Surface *GameSys::allocSurface(int width, int height) { + Graphics::Surface *surface = new Graphics::Surface(); + surface->create(width, height, _backgroundSurface->format); + surface->fillRect(Common::Rect(0, 0, surface->w, surface->h), 0xFFFFFF00); + return surface; +} + +Graphics::Surface *GameSys::createSurface(int resourceId) { + debugC(kDebugBasic, "GameSys::createSurface() resourceId: %08X", resourceId); + + SpriteResource *spriteResource = _vm->_spriteCache->get(resourceId); + Graphics::Surface *surface = allocSurface(spriteResource->_width, spriteResource->_height); + _vm->_spriteCache->release(resourceId); + + drawSpriteToSurface(surface, 0, 0, resourceId); + + return surface; +} + +void GameSys::drawSpriteToSurface(Graphics::Surface *surface, int x, int y, int resourceId) { + SpriteResource *spriteResource = _vm->_spriteCache->get(resourceId); + uint32 *sourcePalette = spriteResource->_palette; + byte *sourcePixels = spriteResource->_pixels; + Common::Rect dstRect(0, 0, spriteResource->_width, spriteResource->_height); + blitSprite32(surface, x, y, sourcePixels, spriteResource->_width, dstRect, sourcePalette, true); + _vm->_spriteCache->release(resourceId); +} + +void GameSys::drawTextToSurface(Graphics::Surface *surface, int x, int y, byte r, byte g, byte b, const char *text) { + bool doDirty = false; + + if (!surface) { + surface = _backgroundSurface; + doDirty = true; + } + + uint32 color = surface->format.RGBToColor(r, g, b); + if (_vm->_font) { + _vm->_font->drawString(surface, text, x, y, _vm->_font->getStringWidth(text), color); + + if (doDirty) + insertDirtyRect(Common::Rect(x, y, x + _vm->_font->getStringWidth(text), y + _vm->_font->getFontHeight())); + } else { + for (const char *cp = text; *cp != 0; ++cp) { + byte c = *cp; + if (c < 32 || c >= 127) + c = (byte)'_'; + c -= 32; + int w = _dejaVuSans9ptCharDescriptors[c]._width; + const byte *data = _dejaVuSans9ptCharBitmaps + _dejaVuSans9ptCharDescriptors[c]._offset; + for (int xc = 0; xc < w; ++xc) { + for (int yc = 15; yc >= 0; --yc) { + byte *dst = (byte *)surface->getBasePtr(x + xc, y + yc); + if (data[1 - (yc >> 3)] & (1 << (yc & 7))) + WRITE_LE_UINT32(dst, color); + } + data += 2; + } + x += w + 1; + } + + if (doDirty) + insertDirtyRect(Common::Rect(x, y, x + getTextWidth(text), y + 16)); + } +} + +int GameSys::getTextHeight(const char *text) { + byte height = 0; + for (const char *cp = text; *cp != 0; ++cp) { + byte c = *cp; + if (c < 32 || c >= 127) + c = (byte)'_'; + c -= 32; + height = MAX(height, _dejaVuSans9ptCharDescriptors[c]._width); + } + return height; +} + +int GameSys::getTextWidth(const char *text) { + int width = 0; + for (const char *cp = text; *cp != 0; ++cp) { + byte c = *cp; + if (c < 32 || c >= 127) + c = (byte)'_'; + c -= 32; + width += _dejaVuSans9ptCharDescriptors[c]._width + 1; + } + return width; +} + +void GameSys::fillSurface(Graphics::Surface *surface, int x, int y, int width, int height, byte r, byte g, byte b) { + Common::Rect rect(x, y, x + width, y + height); + if (!surface) { + _backgroundSurface->fillRect(rect, _backgroundSurface->format.RGBToColor(r, g, b)); + insertDirtyRect(rect); + } else { + surface->fillRect(rect, surface->format.RGBToColor(r, g, b)); + } +} + +void GameSys::setAnimation(int sequenceId, int id, int animationIndex) { + if (animationIndex < kMaxAnimations) { + _animations[animationIndex]._sequenceId = sequenceId; + _animations[animationIndex]._id = id; + _animations[animationIndex]._status = 0; + } +} + +int GameSys::getAnimationStatus(int animationIndex) { + int result = -1; + if (animationIndex < kMaxAnimations) + result = _animations[animationIndex]._status; + return result; +} + +int GameSys::getSpriteWidthById(int resourceId) { + SpriteResource *spriteResource = _vm->_spriteCache->get(resourceId); + const int width = spriteResource->_width; + _vm->_spriteCache->release(resourceId); + return width; +} + +int GameSys::getSpriteHeightById(int resourceId) { + SpriteResource *spriteResource = _vm->_spriteCache->get(resourceId); + const int height = spriteResource->_height; + _vm->_spriteCache->release(resourceId); + return height; +} + +Graphics::Surface *GameSys::loadBitmap(int resourceId) { + debugC(kDebugBasic, "GameSys::loadBitmap() resourceId: %08X", resourceId); + if (_vm->_dat->getResourceType(resourceId) != 1) + return nullptr; + byte *resourceData = _vm->_dat->loadResource(resourceId); + uint32 resourceSize = _vm->_dat->getResourceSize(resourceId); + Common::MemoryReadStream stream(resourceData, resourceSize, DisposeAfterUse::NO); + Graphics::Surface *bmpSurface; + Image::BitmapDecoder bmp; + if (!bmp.loadStream(stream)) + error("GameSys::loadBitmap() Could not load bitmap resource %08X", resourceId); + bmpSurface = bmp.getSurface()->convertTo(_vm->_system->getScreenFormat()); + delete[] resourceData; + return bmpSurface; +} + +void GameSys::drawBitmap(int resourceId) { + assert(_backgroundSurface); + + Graphics::Surface *bmpSurface = loadBitmap(resourceId); + if (!bmpSurface) + error("GameSys::drawBitmap() Error loading the bitmap"); + + if (bmpSurface->format != _backgroundSurface->format + || bmpSurface->w != _backgroundSurface->w || bmpSurface->h != _backgroundSurface->h) + error("GameSys::drawBitmap() Different bitmap properties than current background"); + + byte *src = (byte *)bmpSurface->getPixels(); + byte *dst = (byte *)_backgroundSurface->getPixels(); + const int pitch = bmpSurface->pitch; + int height = bmpSurface->h; + while (height--) { + memcpy(dst, src, pitch); + src += pitch; + dst += pitch; + } + + bmpSurface->free(); + delete bmpSurface; + + insertDirtyRect(Common::Rect(0, 0, 800, 600)); +} + +Sequence *GameSys::seqFind(int sequenceId, int id, int *outIndex) { + for (uint i = 0; i < _seqItems.size(); ++i) + if (_seqItems[i]._sequenceId == sequenceId && _seqItems[i]._id == id) { + if (outIndex) + *outIndex = i; + return &_seqItems[i]; + } + return nullptr; +} + +int GameSys::seqLocateGfx(int sequenceId, int id, int *outGfxIndex) { + for (int i = 0; i < _gfxItemsCount; ++i) { + GfxItem *gfxItem = &_gfxItems[i]; + if (gfxItem->_sequenceId == sequenceId && gfxItem->_id == id) { + if (outGfxIndex) + *outGfxIndex = i; + return gfxItem->_sequenceId; + } + if (gfxItem->_id > id) { + if (outGfxIndex) + *outGfxIndex = i; + return 0; + } + } + if (outGfxIndex) + *outGfxIndex = _gfxItemsCount; + return 0; +} + +void GameSys::seqInsertGfx(int index, int duration) { + Sequence *seqItem = &_seqItems[index]; + SequenceResource *sequenceResource = _vm->_sequenceCache->get(seqItem->_sequenceId); + + if (sequenceResource->_animationsCount > 50 - _gfxItemsCount) + return; + + int gfxIndex; + seqLocateGfx(seqItem->_sequenceId, seqItem->_id, &gfxIndex); + + if (gfxIndex != _gfxItemsCount) + memmove(&_gfxItems[gfxIndex + sequenceResource->_animationsCount], &_gfxItems[gfxIndex], sizeof(GfxItem) * (_gfxItemsCount - gfxIndex)); + _gfxItemsCount += sequenceResource->_animationsCount; + + for (int i = 0; i < sequenceResource->_animationsCount; ++i) { + GfxItem *gfxItem = &_gfxItems[i + gfxIndex]; + SequenceAnimation *animation = &sequenceResource->_animations[i]; + + debugC(kDebugBasic, "GameSys::seqInsertGfx() seqItem->sequenceId: %08X", seqItem->_sequenceId); + + gfxItem->_sequenceId = seqItem->_sequenceId; + gfxItem->_id = seqItem->_id; + gfxItem->_animation = animation; + gfxItem->_currFrameNum = 0; + gfxItem->_flags = 0; + gfxItem->_delayTicks = seqItem->_totalDuration + animation->_additionalDelay; + gfxItem->_updFlag = false; + gfxItem->_updRectsCount = 0; + gfxItem->_prevFrame._duration = 0; + gfxItem->_prevFrame._spriteId = -1; + gfxItem->_prevFrame._soundId = -1; + int totalDuration = duration; + if ((seqItem->_flags & kSeqUnk) && totalDuration > 0) { + gfxItem->_prevFrame._duration = 1; + if (gfxItem->_delayTicks <= totalDuration) + gfxItem->_delayTicks = 0; + else + gfxItem->_delayTicks -= totalDuration + 1; + gfxItem->_updFlag = false; + } else if (gfxItem->_delayTicks <= totalDuration) { + int j; + totalDuration -= gfxItem->_delayTicks; + gfxItem->_delayTicks = 0; + for (j = gfxItem->_currFrameNum; j < animation->_framesCount && animation->frames[j]._duration <= totalDuration; ++j) { + if (animation->frames[j]._soundId != -1) + _soundIds.push_back((gfxItem->_sequenceId & 0xFFFF0000) | animation->frames[j]._soundId); + totalDuration -= animation->frames[j]._duration; + } + if (animation->_framesCount > j) + gfxItem->_currFrame = animation->frames[j++]; + else + gfxItem->_currFrame = animation->frames[j - 1]; + if (gfxItem->_currFrame._spriteId != -1 && (seqItem->_x != 0 || seqItem->_y != 0)) + gfxItem->_currFrame._rect.translate(seqItem->_x, seqItem->_y); + // Update sprite scaling + if ((seqItem->_flags & kSeqScale) && gfxItem->_currFrame._rect.bottom >= _backgroundImageValue1 && gfxItem->_currFrame._rect.bottom <= _backgroundImageValue3) { + int scaleValue = _backgroundImageValue2 + (gfxItem->_currFrame._rect.bottom - _backgroundImageValue1) * + (_backgroundImageValue4 - _backgroundImageValue2) / + (_backgroundImageValue3 - _backgroundImageValue1); + gfxItem->_currFrame._rect.top = gfxItem->_currFrame._rect.bottom - scaleValue * (gfxItem->_currFrame._rect.bottom - gfxItem->_currFrame._rect.top) / 1000; + gfxItem->_currFrame._rect.right = scaleValue * (gfxItem->_currFrame._rect.right - gfxItem->_currFrame._rect.left) / 1000 + gfxItem->_currFrame._rect.left; + gfxItem->_currFrame._isScaled = true; + } + gfxItem->_currFrame._duration -= totalDuration; + if (gfxItem->_currFrame._soundId != -1) + _soundIds.push_back((gfxItem->_sequenceId & 0xFFFF0000) | gfxItem->_currFrame._soundId); + gfxItem->_currFrameNum = j; + gfxItem->_updFlag = true; + } else { + gfxItem->_delayTicks -= totalDuration + 1; + gfxItem->_updFlag = false; + } + } + + for (int k = 0; k < kMaxAnimations; ++k) { + if (_animations[k]._sequenceId != -1 && _animations[k]._sequenceId == seqItem->_sequenceId && _animations[k]._id == seqItem->_id) { + _animations[k]._status = 1; + break; + } + } +} + +void GameSys::seqRemoveGfx(int sequenceId, int id) { + int gfxIndex; + if (seqLocateGfx(sequenceId, id, &gfxIndex)) { + GfxItem *gfxItem = &_gfxItems[gfxIndex]; + while (gfxIndex < _gfxItemsCount && gfxItem->_sequenceId == sequenceId && gfxItem->_id == id) { + if (gfxItem->_prevFrame._spriteId == -1) { + --_gfxItemsCount; + if (gfxIndex != _gfxItemsCount) + memmove(&_gfxItems[gfxIndex], &_gfxItems[gfxIndex + 1], sizeof(GfxItem) * (_gfxItemsCount - gfxIndex)); + } else { + gfxItem->_sequenceId = -1; + gfxItem->_animation = nullptr; + gfxItem->_currFrame._duration = 0; + gfxItem->_currFrame._spriteId = -1; + gfxItem->_currFrame._soundId = -1; + gfxItem->_updFlag = true; + ++gfxIndex; + gfxItem = &_gfxItems[gfxIndex]; + } + } + } +} + +bool GameSys::updateSequenceDuration(int sequenceId, int id, int *outDuration) { + bool found = false; + int duration = 0x7FFFFFFF; + for (int i = 0; i < _gfxItemsCount; ++i) { + GfxItem *gfxItem = &_gfxItems[i]; + if (gfxItem->_sequenceId == sequenceId && gfxItem->_id == id) { + found = true; + SequenceAnimation *animation = gfxItem->_animation; + if (animation) { + if (gfxItem->_currFrameNum < animation->_framesCount) + return false; + if (gfxItem->_updFlag) { + if (gfxItem->_currFrame._duration > 0) + return false; + if (-gfxItem->_currFrame._duration < duration) + duration = -gfxItem->_currFrame._duration; + } else { + if (gfxItem->_prevFrame._duration > 0) + return false; + if (-gfxItem->_prevFrame._duration < duration) + duration = -gfxItem->_prevFrame._duration; + } + } + } + } + if (found) + *outDuration = duration; + return found; +} + +void GameSys::updateAnimationsStatus(int sequenceId, int id) { + Animation *foundAnimation = nullptr; + for (int animationIndex = 0; animationIndex < kMaxAnimations; ++animationIndex) { + Animation *animation = &_animations[animationIndex]; + if (animation->_sequenceId != -1 && animation->_sequenceId == sequenceId && animation->_id == id) { + foundAnimation = animation; + break; + } + } + + if (!foundAnimation) + return; + + bool foundSequence = false; + for (int i = 0; i < _gfxItemsCount; ++i) { + GfxItem *gfxItem = &_gfxItems[i]; + SequenceAnimation *animation = gfxItem->_animation; + if (gfxItem->_sequenceId == sequenceId && gfxItem->_id == id && animation) { + foundSequence = true; + if (animation->_framesCount > gfxItem->_currFrameNum || + (gfxItem->_updFlag && gfxItem->_currFrame._duration > 1) || + gfxItem->_prevFrame._duration > 1) + foundSequence = false; + break; + } + } + + if (foundSequence) { + foundAnimation->_sequenceId = -1; + foundAnimation->_status = 2; + } +} + +void GameSys::restoreBackgroundRect(const Common::Rect &rect) { + Common::Rect clipRect; + if (!intersectRect(clipRect, rect, _screenRect)) + return; + byte *src = (byte *)_backgroundSurface->getBasePtr(clipRect.left, clipRect.top); + byte *dst = (byte *)_frontSurface->getBasePtr(clipRect.left, clipRect.top); + const int bytes = _backgroundSurface->format.bytesPerPixel * clipRect.width(); + int height = clipRect.height(); + while (height--) { + memcpy(dst, src, bytes); + src += _backgroundSurface->pitch; + dst += _frontSurface->pitch; + } +} + +void GameSys::blitSurface32(Graphics::Surface *destSurface, int x, int y, Graphics::Surface *sourceSurface, + Common::Rect &sourceRect, bool transparent) { + + const int sourcePitch = sourceSurface->pitch; + byte *dst = (byte *)destSurface->getBasePtr(x, y); + byte *src = (byte *)sourceSurface->getBasePtr(sourceRect.left, sourceRect.top); + int width = sourceRect.width(); + int height = sourceRect.height(); + while (height--) { + byte *rsrc = src; + byte *rdst = dst; + for (int xc = 0; xc < width; ++xc) { + uint32 pixel = READ_LE_UINT32(rsrc); + if (!transparent || pixel != 0xFFFFFF00) + WRITE_LE_UINT32(rdst, pixel); + rsrc += 4; + rdst += 4; + } + dst += destSurface->pitch; + src += sourcePitch; + } +} + +void GameSys::blitSprite32(Graphics::Surface *destSurface, int x, int y, byte *sourcePixels, + int sourceWidth, Common::Rect &sourceRect, uint32 *sourcePalette, bool transparent) { + + const int sourcePitch = (sourceWidth + 3) & 0xFFFFFFFC; + byte *dst = (byte *)destSurface->getBasePtr(x, y); + byte *src = sourcePixels + sourceRect.left + sourcePitch * sourceRect.top; + int width = sourceRect.width(); + int height = sourceRect.height(); + while (height--) { + byte *rdst = dst; + for (int xc = 0; xc < width; ++xc) { + byte srcPixel = src[xc]; + if (!transparent || srcPixel) { + uint32 rgb = sourcePalette[srcPixel]; + rdst[0] = 0xFF; + rdst[1] = rgb & 0x000000FF; + rdst[2] = (rgb & 0x0000FF00) >> 8; + rdst[3] = (rgb & 0x00FF0000) >> 16; + } + rdst += 4; + } + dst += destSurface->pitch; + src += sourcePitch; + } +} + +void GameSys::blitSpriteScaled32(Graphics::Surface *destSurface, Common::Rect &frameRect, + Common::Rect &destRect, byte *sourcePixels, int sourceWidth, Common::Rect &sourceRect, uint32 *sourcePalette) { + + if (frameRect.height() <= 0 || frameRect.width() <= 0) + return; + + const int ys = ((sourceRect.bottom - sourceRect.top - 1) << 16) / (frameRect.bottom - frameRect.top - 1); + const int xs = ((sourceRect.right - sourceRect.left - 1) << 16) / (frameRect.right - frameRect.left - 1); + const int destPitch = destSurface->pitch; + const int sourcePitch = (sourceWidth + 3) & 0xFFFFFFFC; + + if (!frameRect.equals(destRect)) { + byte *dst = (byte *)destSurface->getBasePtr(destRect.left, destRect.top); + byte *src = sourcePixels + sourcePitch * sourceRect.top + sourceRect.left; + const int height = destRect.bottom - destRect.top; + const int width = destRect.right - destRect.left; + int yi = ys * (destRect.top - frameRect.top); + byte *hsrc = src + sourcePitch * ((yi + 0x8000) >> 16); + for (int i = 0; i < height; ++i) { + byte *wdst = dst; + int xi = xs * (destRect.left - frameRect.left); + byte *wsrc = hsrc + ((xi + 0x8000) >> 16); + for (int j = 0; j < width; ++j) { + byte srcPixel = *wsrc; + if (srcPixel) { + uint32 rgb = sourcePalette[srcPixel]; + wdst[0] = 0xFF; + wdst[1] = rgb & 0x000000FF; + wdst[2] = (rgb & 0x0000FF00) >> 8; + wdst[3] = (rgb & 0x00FF0000) >> 16; + } + wdst += 4; + xi += xs; + wsrc = hsrc + ((xi + 0x8000) >> 16); + } + dst += destPitch; + yi += ys; + hsrc = src + sourcePitch * ((yi + 0x8000) >> 16); + } + } else { + byte *dst = (byte *)destSurface->getBasePtr(frameRect.left, frameRect.top); + byte *src = sourcePixels + sourcePitch * sourceRect.top + sourceRect.left; + const int height = frameRect.bottom - frameRect.top; + const int width = frameRect.right - frameRect.left; + byte *hsrc = sourcePixels + sourcePitch * sourceRect.top + sourceRect.left; + int yi = 0; + for (int i = 0; i < height; ++i) { + byte *wdst = dst; + byte *wsrc = hsrc; + int xi = 0; + for (int j = 0; j < width; ++j) { + byte srcPixel = *wsrc; + if (srcPixel) { + uint32 rgb = sourcePalette[srcPixel]; + wdst[0] = 0xFF; + wdst[1] = rgb & 0x000000FF; + wdst[2] = (rgb & 0x0000FF00) >> 8; + wdst[3] = (rgb & 0x00FF0000) >> 16; + } + wdst += 4; + xi += xs; + wsrc = hsrc + ((xi + 0x8000) >> 16); + } + dst += destPitch; + yi += ys; + hsrc = src + sourcePitch * ((yi + 0x8000) >> 16); + } + } + +} + +void GameSys::seqDrawStaticFrame(Graphics::Surface *surface, SequenceFrame &frame, Common::Rect *subRect) { + debugC(kDebugBasic, "GameSys::seqDrawStaticFrame() rect: (%d, %d, %d, %d)", + frame._rect.left, frame._rect.top, frame._rect.right, frame._rect.bottom); + + Common::Rect srcRect = subRect ? *subRect : frame._rect; + Common::Rect clipRect; + + if (!intersectRect(clipRect, srcRect, _screenRect)) { + debugC(kDebugBasic, "GameSys::seqDrawStaticFrame() Surface not inside screen"); + return; + } + + const int x = clipRect.left, y = clipRect.top; + + clipRect.translate(-frame._rect.left, -frame._rect.top); + + // TODO Save transparent flag somewhere + blitSurface32(_frontSurface, x, y, surface, clipRect, true); +} + +void GameSys::seqDrawSpriteFrame(SpriteResource *spriteResource, SequenceFrame &frame, Common::Rect *subRect) { + debugC(kDebugBasic, "GameSys::seqDrawSpriteFrame() spriteId: %04X; rect: (%d, %d, %d, %d)", + frame._spriteId, frame._rect.left, frame._rect.top, frame._rect.right, frame._rect.bottom); + + Common::Rect srcRect = subRect ? *subRect : frame._rect; + Common::Rect clipRect; + + if (!intersectRect(clipRect, srcRect, _screenRect)) { + debugC(kDebugBasic, "GameSys::seqDrawSpriteFrame() Sprite not inside screen"); + return; + } + + uint32 *sourcePalette = spriteResource->_palette; + byte *sourcePixels = spriteResource->_pixels; + + const int x = clipRect.left, y = clipRect.top; + + debugC(kDebugBasic, "GameSys::seqDrawSpriteFrame() destX: %d; destY: %d; frame.isScaled: %d", x, y, frame._isScaled ? 1 : 0); + + // 32bit sprite drawing + if (frame._isScaled) { + Common::Rect sourceRect(0, 0, spriteResource->_width, spriteResource->_height); + blitSpriteScaled32(_frontSurface, frame._rect, clipRect, sourcePixels, spriteResource->_width, sourceRect, sourcePalette); + } else { + clipRect.translate(-frame._rect.left, -frame._rect.top); + blitSprite32(_frontSurface, x, y, sourcePixels, spriteResource->_width, clipRect, sourcePalette, true); + } +} + +void GameSys::drawSprites() { + debugC(kDebugBasic, "GameSys::drawSprites() _gfxItemsCount: %d", _gfxItemsCount); + + // Restore dirty background and collect rects to be redrawn for all sprites + // which aren't marked to be redrawn yet + Common::Rect intersectingRect; + for (uint i = 0; i < _dirtyRects.size(); ++i) { + restoreBackgroundRect(_dirtyRects[i]); + for (int j = 0; j < _gfxItemsCount; ++j) + _gfxItems[j].testUpdRect(_dirtyRects[i]); + } + + for (int k = 0; k < _gfxItemsCount; ++k) { + GfxItem *gfxItem2 = &_gfxItems[k]; + + if (!gfxItem2->_updFlag) + continue; + + if (gfxItem2->_prevFrame._spriteId != -1) { + bool transparent = false; + if (gfxItem2->_currFrame._spriteId != -1) { + if (gfxItem2->_flags) { + transparent = true; + } else { + int resourceId = (gfxItem2->_sequenceId & 0xFFFF0000) | gfxItem2->_currFrame._spriteId; + SpriteResource *spriteResource = _vm->_spriteCache->get(resourceId); + transparent = spriteResource->_transparent; + _vm->_spriteCache->release(resourceId); + } + } + if (gfxItem2->_currFrame._spriteId == -1 || !gfxItem2->_prevFrame._rect.equals(gfxItem2->_currFrame._rect) || !transparent) { + restoreBackgroundRect(gfxItem2->_prevFrame._rect); + for (int l = 0; l < _gfxItemsCount; ++l) + _gfxItems[l].testUpdRect(gfxItem2->_prevFrame._rect); + } + } + + if (gfxItem2->_currFrame._spriteId != -1) { + bool transparent = false; + if (gfxItem2->_flags) { + transparent = true; + } else { + int resourceId = (gfxItem2->_sequenceId & 0xFFFF0000) | gfxItem2->_currFrame._spriteId; + SpriteResource *spriteResource = _vm->_spriteCache->get(resourceId); + transparent = spriteResource->_transparent; + _vm->_spriteCache->release(resourceId); + } + if (gfxItem2->_prevFrame._spriteId == -1 || !gfxItem2->_prevFrame._rect.equals(gfxItem2->_currFrame._rect) || transparent) { + for (int l = k; l < _gfxItemsCount; ++l) + _gfxItems[l].testUpdRect(gfxItem2->_currFrame._rect); + } + } + } + + for (int m = 0; m < _gfxItemsCount; ++m) { + GfxItem *gfxItem5 = &_gfxItems[m]; + + debugC(kDebugBasic, "DrawGfxItem(%d) updFlag: %d; currFrame.spriteId: %04X; updRectsCount: %d; flags: %04X; sequenceId: %08X", + m, gfxItem5->_updFlag, gfxItem5->_currFrame._spriteId, gfxItem5->_updRectsCount, gfxItem5->_flags, gfxItem5->_sequenceId); + + if (gfxItem5->_updFlag) { + if (gfxItem5->_currFrame._spriteId != -1) { + if (gfxItem5->_flags) { + seqDrawStaticFrame(gfxItem5->_surface, gfxItem5->_currFrame, nullptr); + } else { + int resourceId = (gfxItem5->_sequenceId & 0xFFFF0000) | gfxItem5->_currFrame._spriteId; + SpriteResource *spriteResource = _vm->_spriteCache->get(resourceId); + seqDrawSpriteFrame(spriteResource, gfxItem5->_currFrame, nullptr); + _vm->_spriteCache->release(resourceId); + } + } + } else if (gfxItem5->_updRectsCount > 0) { + if (gfxItem5->_flags) { + for (int n = 0; n < gfxItem5->_updRectsCount; ++n) + seqDrawStaticFrame(gfxItem5->_surface, gfxItem5->_prevFrame, &gfxItem5->_updRects[n]); + } else { + int resourceId = (gfxItem5->_sequenceId & 0xFFFF0000) | gfxItem5->_prevFrame._spriteId; + SpriteResource *spriteResource = _vm->_spriteCache->get(resourceId); + for (int n = 0; n < gfxItem5->_updRectsCount; ++n) + seqDrawSpriteFrame(spriteResource, gfxItem5->_prevFrame, &gfxItem5->_updRects[n]); + _vm->_spriteCache->release(resourceId); + } + } + } + + debugC(kDebugBasic, "GameSys::drawSprites() OK"); +} + +void GameSys::updateRect(const Common::Rect &r) { + debugC(kDebugBasic, "GameSys::updateRect() %d, %d, %d, %d [%d, %d]", r.left, r.top, r.right, r.bottom, r.width(), r.height()); + if (r.width() > 0 && r.height() > 0) { + byte *pixels = (byte *)_frontSurface->getBasePtr(r.left, r.top); + _vm->_system->copyRectToScreen(pixels, _frontSurface->pitch, r.left, r.top, + r.width(), r.height()); + } +} + +void GameSys::updateScreen() { + debugC(kDebugBasic, "GameSys::updateScreen()"); + + for (uint i = 0; i < _dirtyRects.size(); ++i) + updateRect(_dirtyRects[i]); + + if (_dirtyRects.size() > 0) { + _dirtyRects.clear(); + _lastUpdateClock = 0; + _gameSysClock = 0; + } + + Common::Rect dstRect, srcRect, rcSrc2; + + for (int j = 0; j < _gfxItemsCount; ++j) { + + GfxItem *gfxItem = &_gfxItems[j]; + + if (!gfxItem->_updFlag) + continue; + + if (gfxItem->_prevFrame._spriteId == -1 || + !intersectRect(srcRect, _screenRect, gfxItem->_prevFrame._rect)) { + if (gfxItem->_currFrame._spriteId != -1 && intersectRect(rcSrc2, _screenRect, gfxItem->_currFrame._rect)) + updateRect(rcSrc2); + } else if (gfxItem->_currFrame._spriteId != -1 && + intersectRect(rcSrc2, _screenRect, gfxItem->_currFrame._rect)) { + updateRect(srcRect); + updateRect(rcSrc2); + } + gfxItem->_prevFrame = gfxItem->_currFrame; + } + + updateRect(Common::Rect(0, 0, 800, 600)); + + debugC(kDebugBasic, "GameSys::updateScreen() OK"); +} + +void GameSys::handleReqRemoveSequenceItem() { + if (_reqRemoveSequenceItem) { + int gfxIndex2; + _reqRemoveSequenceItem = false; + if (seqFind(_removeSequenceItemSequenceId, _removeSequenceItemValue, &gfxIndex2)) + _seqItems.remove_at(gfxIndex2); + if (seqLocateGfx(_removeSequenceItemSequenceId, _removeSequenceItemValue, &gfxIndex2)) { + int gfxIndex2a = gfxIndex2; + for (GfxItem *gfxItem = &_gfxItems[gfxIndex2a]; + gfxIndex2a < _gfxItemsCount && gfxItem->_sequenceId == _removeSequenceItemSequenceId && gfxItem->_id == _removeSequenceItemValue; + gfxItem = &_gfxItems[gfxIndex2a]) + ++gfxIndex2a; + _gfxItemsCount -= gfxIndex2a - gfxIndex2; + if (_gfxItemsCount != gfxIndex2) + memmove(&_gfxItems[gfxIndex2], &_gfxItems[gfxIndex2a], sizeof(GfxItem) * (_gfxItemsCount - gfxIndex2)); + } + } +} + +void GameSys::handleReqRemoveSequenceItems() { + if (_removeSequenceItemsCount > 0) { + for (int i = 0; i < _removeSequenceItemsCount; ++i) { + int gfxIndex; + if (seqFind(_removeSequenceItems[i]._sequenceId, _removeSequenceItems[i]._id, &gfxIndex)) + _seqItems.remove_at(gfxIndex); + seqLocateGfx(_removeSequenceItems[i]._sequenceId, _removeSequenceItems[i]._id, &gfxIndex); + for (GfxItem *gfxItem = &_gfxItems[gfxIndex]; + gfxIndex < _gfxItemsCount && gfxItem->_sequenceId == _removeSequenceItems[i]._sequenceId && gfxItem->_id == _removeSequenceItems[i]._id; + gfxItem = &_gfxItems[gfxIndex]) { + gfxItem->_sequenceId = -1; + gfxItem->_animation = nullptr; + if (_removeSequenceItems[i]._forceFrameReset) { + gfxItem->_currFrame._duration = 0; + gfxItem->_currFrame._spriteId = -1; + gfxItem->_currFrame._soundId = -1; + gfxItem->_updFlag = true; + } else { + gfxItem->_updFlag = false; + } + ++gfxIndex; + } + } + _removeSequenceItemsCount = 0; + } +} + +void GameSys::handleReqRemoveSpriteDrawItems() { + if (_removeSpriteDrawItemsCount > 0) { + for (int j = 0; j < _removeSpriteDrawItemsCount; ++j) { + for (int i = 0; i < _gfxItemsCount; ++i) { + GfxItem *gfxItem = &_gfxItems[i]; + if (gfxItem->_sequenceId == -1 && !gfxItem->_animation && gfxItem->_flags + && gfxItem->_id == _removeSpriteDrawItems[j]._id && _removeSpriteDrawItems[j]._surface == gfxItem->_surface) { + gfxItem->_flags = 0; + gfxItem->_currFrame._duration = 0; + gfxItem->_currFrame._spriteId = -1; + gfxItem->_currFrame._soundId = -1; + gfxItem->_updFlag = true; + } + } + } + _removeSpriteDrawItemsCount = 0; + } +} + +void GameSys::fatUpdateFrame() { + debugC(kDebugBasic, "GameSys::fatUpdateFrame()"); + + int32 clockDelta = _gameSysClock - _lastUpdateClock; + _lastUpdateClock = _gameSysClock; + + debugC(kDebugBasic, "GameSys::fatUpdateFrame() clockDelta: %d", clockDelta); + + if (clockDelta <= 0) + return; + + int duration, currFrameNum; + + for (int i = 0; i < _gfxItemsCount; ++i) { + GfxItem *gfxItem = &_gfxItems[i]; + SequenceAnimation *animation = gfxItem->_animation; + if ((gfxItem->_sequenceId != -1 && animation) || gfxItem->_prevFrame._spriteId != -1 || gfxItem->_prevFrame._duration > 0) { + if (gfxItem->_sequenceId != -1 && !gfxItem->_updFlag) { + Sequence *seqItem = seqFind(gfxItem->_sequenceId, gfxItem->_id, nullptr); + if (!animation) { + gfxItem->_sequenceId = -1; + gfxItem->_animation = nullptr; + gfxItem->_currFrame._duration = 0; + gfxItem->_currFrame._spriteId = -1; + gfxItem->_currFrame._soundId = -1; + gfxItem->_updFlag = true; + } else if (!seqItem) { + gfxItem->_animation = nullptr; + gfxItem->_currFrame._duration = 0; + gfxItem->_currFrame._spriteId = -1; + gfxItem->_currFrame._soundId = -1; + gfxItem->_updFlag = true; + } else if ((seqItem->_flags & kSeqUnk) && clockDelta > 1) { + if (gfxItem->_delayTicks < clockDelta) { + duration = clockDelta - gfxItem->_delayTicks; + gfxItem->_delayTicks = 0; + if (gfxItem->_prevFrame._duration <= duration) + gfxItem->_prevFrame._duration = 1; + else + gfxItem->_prevFrame._duration -= duration; + } else { + gfxItem->_delayTicks -= clockDelta; + } + gfxItem->_updFlag = false; + } else if (gfxItem->_delayTicks < clockDelta) { + duration = clockDelta - gfxItem->_delayTicks; + gfxItem->_delayTicks = 0; + if (gfxItem->_prevFrame._duration <= duration) { + bool v20 = false; + if (gfxItem->_prevFrame._duration > 0) { + duration -= gfxItem->_prevFrame._duration; + gfxItem->_prevFrame._duration = -duration; + } else { + gfxItem->_prevFrame._duration = 0; + v20 = true; + } + currFrameNum = gfxItem->_currFrameNum; + if (animation->_framesCount > currFrameNum) { + while (animation->_framesCount > currFrameNum + && animation->frames[currFrameNum]._duration <= duration) { + if (animation->frames[currFrameNum]._soundId != -1) + _soundIds.push_back((gfxItem->_sequenceId & 0xFFFF0000) | animation->frames[currFrameNum]._soundId); + duration -= animation->frames[currFrameNum]._duration; + ++currFrameNum; + } + if (animation->_framesCount > currFrameNum) + gfxItem->_currFrame = animation->frames[currFrameNum++]; + else + gfxItem->_currFrame = animation->frames[currFrameNum - 1]; + if (gfxItem->_currFrame._spriteId != -1 && (seqItem->_x != 0 || seqItem->_y != 0)) + gfxItem->_currFrame._rect.translate(seqItem->_x, seqItem->_y); + // Update sprite scaling + if ((seqItem->_flags & kSeqScale) && gfxItem->_currFrame._rect.bottom >= _backgroundImageValue1 && gfxItem->_currFrame._rect.bottom <= _backgroundImageValue3) { + int v17 = _backgroundImageValue2 + (gfxItem->_currFrame._rect.bottom - _backgroundImageValue1) * + (_backgroundImageValue4 - _backgroundImageValue2) / + (_backgroundImageValue3 - _backgroundImageValue1); + gfxItem->_currFrame._rect.top = gfxItem->_currFrame._rect.bottom - v17 * (gfxItem->_currFrame._rect.bottom - gfxItem->_currFrame._rect.top) / 1000; + gfxItem->_currFrame._rect.right = v17 * (gfxItem->_currFrame._rect.right - gfxItem->_currFrame._rect.left) / 1000 + gfxItem->_currFrame._rect.left; + gfxItem->_currFrame._isScaled = true; + } + gfxItem->_currFrame._duration -= duration; + if (gfxItem->_currFrame._soundId != -1) + _soundIds.push_back((gfxItem->_sequenceId & 0xFFFF0000) | gfxItem->_currFrame._soundId); + gfxItem->_currFrameNum = currFrameNum; + gfxItem->_updFlag = true; + } else if (v20 && gfxItem->_prevFrame._spriteId == -1) { + --_gfxItemsCount; + if (_gfxItemsCount != i) + memmove(&_gfxItems[i], &_gfxItems[i + 1], sizeof(GfxItem) * (_gfxItemsCount - i)); + --i; + } else { + gfxItem->_updFlag = false; + } + } else { + gfxItem->_prevFrame._duration -= duration; + gfxItem->_updFlag = false; + } + } else { + gfxItem->_delayTicks -= clockDelta; + gfxItem->_updFlag = false; + } + } + } else { + --_gfxItemsCount; + if (_gfxItemsCount != i) + memmove(&_gfxItems[i], &_gfxItems[i + 1], sizeof(GfxItem) * (_gfxItemsCount - i)); + --i; + } + } + + if (_newSpriteDrawItemsCount > 0) { + debugC(kDebugBasic, "_newSpriteDrawItemsCount: %d", _newSpriteDrawItemsCount); + for (int k = 0; k < _newSpriteDrawItemsCount; ++k) { + if (_gfxItemsCount < 50) { + int insertIndex; + seqLocateGfx(-1, _newSpriteDrawItems[k]._id, &insertIndex); + if (_gfxItemsCount != insertIndex) + memmove(&_gfxItems[insertIndex + 1], &_gfxItems[insertIndex], sizeof(GfxItem) * (_gfxItemsCount - insertIndex)); + ++_gfxItemsCount; + GfxItem *gfxItem = &_gfxItems[insertIndex]; + gfxItem->_sequenceId = -1; + gfxItem->_id = _newSpriteDrawItems[k]._id; + gfxItem->_animation = nullptr; + gfxItem->_currFrameNum = 0; + gfxItem->_flags = 1; + gfxItem->_delayTicks = 0; + gfxItem->_updFlag = true; + gfxItem->_updRectsCount = 0; + gfxItem->_surface = _newSpriteDrawItems[k]._surface; + gfxItem->_prevFrame._duration = 0; + gfxItem->_prevFrame._spriteId = -1; + gfxItem->_prevFrame._soundId = -1; + gfxItem->_currFrame._duration = 0; + gfxItem->_currFrame._isScaled = false; + gfxItem->_currFrame._rect = _newSpriteDrawItems[k]._rect; + gfxItem->_currFrame._spriteId = _newSpriteDrawItems[k]._surface ? 0xCAFEBABE : -1;// TODO + gfxItem->_currFrame._soundId = -1; + } + } + _newSpriteDrawItemsCount = 0; + } + + if (_grabSpriteChanged) { + for (int i = 0; i < _gfxItemsCount; ++i) { + GfxItem *gfxItem = &_gfxItems[i]; + if (gfxItem->_sequenceId == -1 && !gfxItem->_animation && gfxItem->_flags + && gfxItem->_id == _grabSpriteId && gfxItem->_surface == _grabSpriteSurface1) { + gfxItem->_currFrame._duration = 0; + gfxItem->_currFrame._isScaled = false; + gfxItem->_currFrame._rect = _grabSpriteRect; + gfxItem->_currFrame._spriteId = _grabSpriteSurface2 ? 1 : -1;// TODO + gfxItem->_currFrame._soundId = -1; + gfxItem->_updFlag = true; + gfxItem->_surface = _grabSpriteSurface2; + break; + } + } + _grabSpriteChanged = false; + } + + debugC(kDebugBasic, "GameSys::fatUpdateFrame() _fatSequenceItems.size(): %d", _fatSequenceItems.size()); + + for (uint i = 0; i < _fatSequenceItems.size(); ++i) { + Sequence *seqItem = &_fatSequenceItems[i]; + if (((seqItem->_flags & kSeqSyncWait) || (seqItem->_flags & kSeqSyncExists)) && seqItem->_sequenceId2 != -1) { + duration = 0; + if (((seqItem->_flags & kSeqSyncExists) && seqLocateGfx(seqItem->_sequenceId2, seqItem->_id2, nullptr)) || + updateSequenceDuration(seqItem->_sequenceId2, seqItem->_id2, &duration)) { + int index = -1; + bool found = false; + if (seqItem->_sequenceId2 == seqItem->_sequenceId && seqItem->_id == seqItem->_id2 && + seqFind(seqItem->_sequenceId, seqItem->_id, &index)) { + _seqItems[index] = *seqItem; + found = true; + } else if (_seqItems.size() < 50) { + index = _seqItems.size(); + _seqItems.push_back(*seqItem); + found = true; + } + if (found) { + seqRemoveGfx(seqItem->_sequenceId2, seqItem->_id2); + seqRemoveGfx(seqItem->_sequenceId, seqItem->_id); + _fatSequenceItems.remove_at(i); + --i; + seqInsertGfx(index, duration); + } + } + } else { + if (seqItem->_totalDuration < clockDelta) { + int index; + bool found = false; + duration = clockDelta - seqItem->_totalDuration; + seqItem->_totalDuration = 0; + if (seqFind(seqItem->_sequenceId, seqItem->_id, &index)) { + _seqItems[index] = *seqItem; + found = true; + } else if (_seqItems.size() < 50) { + index = _seqItems.size(); + _seqItems.push_back(*seqItem); + found = true; + } + if (found) { + seqRemoveGfx(seqItem->_sequenceId, seqItem->_id); + _fatSequenceItems.remove_at(i); + --i; + seqInsertGfx(index, duration - 1); + } + } else { + seqItem->_totalDuration -= clockDelta; + } + } + } + + debugC(kDebugBasic, "GameSys::fatUpdateFrame() _seqItems.size(): %d", _seqItems.size()); + + for (uint i = 0; i < _seqItems.size(); ++i) { + Sequence *seqItem = &_seqItems[i]; + if (seqLocateGfx(seqItem->_sequenceId, seqItem->_id, nullptr)) { + updateAnimationsStatus(seqItem->_sequenceId, seqItem->_id); + if (seqItem->_flags & kSeqLoop) { + int gfxDuration; + if (updateSequenceDuration(seqItem->_sequenceId, seqItem->_id, &gfxDuration)) { + seqRemoveGfx(seqItem->_sequenceId, seqItem->_id); + seqInsertGfx(i, gfxDuration); + } + } + } else { + _seqItems.remove_at(i); + --i; + } + } +} + +void GameSys::fatUpdate() { + debugC(kDebugBasic, "GameSys::fatUpdate() _gfxItemsCount: %d", _gfxItemsCount); + + for (int i = 0; i < _gfxItemsCount; ++i) { + _gfxItems[i]._updFlag = false; + _gfxItems[i]._updRectsCount = 0; + } + + handleReqRemoveSequenceItem(); + handleReqRemoveSequenceItems(); + handleReqRemoveSpriteDrawItems(); + + fatUpdateFrame(); +} + +void GameSys::updatePlaySounds() { + for (uint i = 0; i < _soundIds.size(); ++i) + _vm->playSound(_soundIds[i], false); + _soundIds.clear(); +} + +bool intersectRect(Common::Rect &intersectingRect, const Common::Rect &r1, const Common::Rect &r2) { + if (r1.intersects(r2)) { + intersectingRect = r1.findIntersectingRect(r2); + return true; + } else + return false; +} + +} // End of namespace Gnap diff --git a/engines/gnap/gamesys.h b/engines/gnap/gamesys.h new file mode 100644 index 0000000000..98014f1bac --- /dev/null +++ b/engines/gnap/gamesys.h @@ -0,0 +1,211 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GNAP_GAMESYS_H +#define GNAP_GAMESYS_H + +#include "gnap/gnap.h" +#include "gnap/resource.h" +#include "common/array.h" +#include "common/rect.h" +#include "graphics/surface.h" + +namespace Gnap { + +const int kMaxSequenceItems = 40; +const int kMaxSpriteDrawItems = 30; +const int kMaxSoundIds = 50; +const int kMaxSeqItems = 50; +const int kMaxUpdRects = 20; +const int kMaxGfxItems = 50; +const int kMaxAnimations = 12; + +enum { + kSeqNone = 0x00, + kSeqScale = 0x01, // Enable scaling + kSeqLoop = 0x02, // Loop + kSeqUnk = 0x04, // Unknown + kSeqSyncWait = 0x08, // Start if other sequence is done + kSeqSyncExists = 0x20 // Start if other sequence exists +}; + +struct Sequence { + int32 _sequenceId; + int32 _id; + int32 _sequenceId2; + int32 _id2; + uint32 _flags; + int32 _totalDuration; + int16 _x, _y; +}; + +struct SpriteDrawItem { + int _id; + Common::Rect _rect; + Graphics::Surface *_surface; +}; + +struct RemoveSequenceItem { + int _sequenceId; + int _id; + bool _forceFrameReset; +}; + +struct RemoveSpriteDrawItem { + int _id; + Graphics::Surface *_surface; +}; + +struct GfxItem { + int _sequenceId; + int _id; + int _flags; + SequenceAnimation *_animation; + int _currFrameNum; + int _delayTicks; + bool _updFlag; + int _updRectsCount; + Graphics::Surface *_surface; + Common::Rect _updRects[kMaxUpdRects]; + SequenceFrame _prevFrame; + SequenceFrame _currFrame; + void testUpdRect(const Common::Rect &updRect); +}; + +struct Animation { + int _sequenceId; + int _id; + int _status; +}; + +class GameSys { +public: + GameSys(GnapEngine *vm); + ~GameSys(); + void insertSequence(int sequenceId, int id, int sequenceId2, int id2, int flags, int totalDuration, int16 x, int16 y); + void insertDirtyRect(const Common::Rect &rect); + void removeSequence(int sequenceId, int id, bool resetFl); + void invalidateGrabCursorSprite(int id, Common::Rect &rect, Graphics::Surface *surface1, Graphics::Surface *surface2); + void requestClear2(bool resetFl); + void requestClear1(); + void requestRemoveSequence(int sequenceId, int id); + void waitForUpdate(); + int isSequenceActive(int sequenceId, int id); + void setBackgroundSurface(Graphics::Surface *surface, int a4, int a5, int a6, int a7); + void setScaleValues(int a1, int a2, int a3, int a4); + void insertSpriteDrawItem(Graphics::Surface *surface, int x, int y, int id); + void removeSpriteDrawItem(Graphics::Surface *surface, int id); + void drawSpriteToBackground(int x, int y, int resourceId); + Graphics::Surface *allocSurface(int width, int height); + Graphics::Surface *createSurface(int resourceId); + void drawSpriteToSurface(Graphics::Surface *surface, int x, int y, int resourceId); + void drawTextToSurface(Graphics::Surface *surface, int x, int y, byte r, byte g, byte b, const char *text); + int getTextHeight(const char *text); + int getTextWidth(const char *text); + void fillSurface(Graphics::Surface *surface, int x, int y, int width, int height, byte r, byte g, byte b); + void setAnimation(int sequenceId, int id, int animationIndex); + int getAnimationStatus(int animationIndex); + int getSpriteWidthById(int resourceId); + int getSpriteHeightById(int resourceId); + Graphics::Surface *loadBitmap(int resourceId); + void drawBitmap(int resourceId); +public: + GnapEngine *_vm; + + Common::Array<Common::Rect> _dirtyRects; + + SpriteDrawItem _newSpriteDrawItems[kMaxSpriteDrawItems]; + int _newSpriteDrawItemsCount; + + RemoveSequenceItem _removeSequenceItems[kMaxSequenceItems]; + int _removeSequenceItemsCount; + + RemoveSpriteDrawItem _removeSpriteDrawItems[kMaxSpriteDrawItems]; + int _removeSpriteDrawItemsCount; + + int _grabSpriteId; + Common::Rect _grabSpriteRect; + bool _grabSpriteChanged; + Graphics::Surface *_grabSpriteSurface1, *_grabSpriteSurface2; + + bool _reqRemoveSequenceItem; + int _removeSequenceItemSequenceId, _removeSequenceItemValue; + + Common::Array<int> _soundIds; + + //////////////////////////////////////////////////////////////////////////// + + Common::Array<Sequence> _seqItems; + Common::Array<Sequence> _fatSequenceItems; + + GfxItem _gfxItems[kMaxGfxItems]; + int _gfxItemsCount; + + Animation _animations[kMaxAnimations]; + int _animationsCount; + + int _backgroundImageValue3, _backgroundImageValue1; + int _backgroundImageValue4, _backgroundImageValue2; + + int32 _gameSysClock, _lastUpdateClock; + + Graphics::Surface *_backgroundSurface; + Graphics::Surface *_frontSurface; + Common::Rect _screenRect; + + Sequence *seqFind(int sequenceId, int id, int *outIndex); + int seqLocateGfx(int sequenceId, int id, int *outGfxIndex); + void seqInsertGfx(int index, int duration); + void seqRemoveGfx(int sequenceId, int id); + bool updateSequenceDuration(int sequenceId, int id, int *outDuration); + void updateAnimationsStatus(int sequenceId, int id); + + void restoreBackgroundRect(const Common::Rect &rect); + + void blitSurface32(Graphics::Surface *destSurface, int x, int y, Graphics::Surface *sourceSurface, + Common::Rect &sourceRect, bool transparent); + void blitSprite32(Graphics::Surface *destSurface, int x, int y, byte *sourcePixels, + int sourceWidth, Common::Rect &sourceRect, uint32 *sourcePalette, bool transparent); + void blitSpriteScaled32(Graphics::Surface *destSurface, Common::Rect &frameRect, + Common::Rect &destRect, byte *sourcePixels, int sourceWidth, Common::Rect &sourceRect, uint32 *sourcePalette); + + void seqDrawStaticFrame(Graphics::Surface *surface, SequenceFrame &frame, Common::Rect *subRect); + void seqDrawSpriteFrame(SpriteResource *spriteResource, SequenceFrame &frame, Common::Rect *subRect); + + void drawSprites(); + void updateRect(const Common::Rect &r); + void updateScreen(); + + void handleReqRemoveSequenceItem(); + void handleReqRemoveSequenceItems(); + void handleReqRemoveSpriteDrawItems(); + void fatUpdateFrame(); + void fatUpdate(); + void updatePlaySounds(); + +}; + +bool intersectRect(Common::Rect &intersectingRect, const Common::Rect &r1, const Common::Rect &r2); + +} // End of namespace Gnap + +#endif // GNAP_GAMESYS_H diff --git a/engines/gnap/gnap.cpp b/engines/gnap/gnap.cpp new file mode 100644 index 0000000000..ed2d25f3de --- /dev/null +++ b/engines/gnap/gnap.cpp @@ -0,0 +1,1190 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "graphics/cursorman.h" +#include "gnap/gnap.h" +#include "gnap/datarchive.h" +#include "gnap/gamesys.h" +#include "gnap/resource.h" +#include "gnap/sound.h" + +#include "common/config-manager.h" +#include "common/debug-channels.h" +#include "common/timer.h" + +#include "engines/util.h" + +namespace Gnap { + +static const int kCursors[] = { + LOOK_CURSOR, + GRAB_CURSOR, + TALK_CURSOR, + PLAT_CURSOR +}; + +static const int kDisabledCursors[] = { + NOLOOK_CURSOR, + NOGRAB_CURSOR, + NOTALK_CURSOR, + NOPLAT_CURSOR +}; + +static const char *kCursorNames[] = { + "LOOK_CURSOR", + "GRAB_CURSOR", + "TALK_CURSOR", + "PLAT_CURSOR", + "NOLOOK_CURSOR", + "NOGRAB_CURSOR", + "NOTALK_CURSOR", + "NOPLAT_CURSOR", + "EXIT_L_CURSOR", + "EXIT_R_CURSOR", + "EXIT_U_CURSOR", + "EXIT_D_CURSOR", + "EXIT_NE_CURSOR", + "EXIT_NW_CURSOR", + "EXIT_SE_CURSOR", + "EXIT_SW_CURSOR", + "WAIT_CURSOR" +}; + + +static const int kCursorSpriteIds[30] = { + 0x005, 0x008, 0x00A, 0x004, 0x009, 0x003, + 0x006, 0x007, 0x00D, 0x00F, 0x00B, 0x00C, + 0x019, 0x01C, 0x015, 0x014, 0x010, 0x01A, + 0x018, 0x013, 0x011, 0x012, 0x01B, 0x016, + 0x017, 0x01D, 0x01E, 0x01F, 0x76A, 0x76B +}; + +static const char *kSceneNames[] = { + "open", "pigpn", "truck", "creek", "mafrm", "frbrn", "inbrn", "crash", + "porch", "barbk", "kitch", "bar", "juke", "wash", "john", "jkbox", + "brawl", "stret", "frtoy", "intoy", "frgro", "park", "cash", "ingro", + "frcir", "booth", "circ", "outcl", "incln", "monk", "elcir", "beer", + "pig2", "trk2", "creek", "frbrn", "inbrn", "mafrm", "infrm", "efair", + "fair", "souv", "chick", "ship", "kiss", "disco", "boot", "can", + "can2", "drive", "tung", "puss", "space", "phone", "can3" +}; + +GnapEngine::GnapEngine(OSystem *syst, const ADGameDescription *gd) : + Engine(syst), _gameDescription(gd) { + + _random = new Common::RandomSource("gnap"); + DebugMan.addDebugChannel(kDebugBasic, "basic", "Basic debug level"); + + Engine::syncSoundSettings(); + _scene = nullptr; + _music = nullptr; + _tempThumbnail = nullptr; + + _wasSavegameLoaded = false; + for (int i = 0; i < kMaxTimers; ++i) + _savedTimers[i] = _timers[i] = 0; + + _isWaiting = false; + _sceneWaiting = false; + + _mousePos = Common::Point(0, 0); + _currGrabCursorX = _currGrabCursorY = 0; + + _idleTimerIndex = -1; + _menuStatus = 0; + _menuSpritesIndex = -1; + _menuDone = false; + _menuBackgroundSurface = nullptr; + _menuQuitQuerySprite = nullptr; + _largeSprite = nullptr; + _menuSaveLoadSprite = nullptr; + _menuSprite2 = nullptr; + _menuSprite1 = nullptr; + _spriteHandle = nullptr; + _cursorSprite = nullptr; + _savegameIndex = -1; + _gridMinX = 0; + _gridMinY = 0; + _gridMaxX = 0; + _gridMaxY = 0; + _toyUfoNextSequenceId = -1; + _toyUfoSequenceId = -1; + _toyUfoId = -1; + _toyUfoActionStatus = -1; + _toyUfoX = 0; + _toyUfoY = 0; + _s18GarbageCanPos = 0; + + for (int i = 0; i < 7; i++) + _savegameSprites[i] = nullptr; + for (int i = 0; i < 30; i++) + _menuInventorySprites[i] = nullptr; +} + +GnapEngine::~GnapEngine() { + delete _random; + delete _music; + delete _tempThumbnail; +} + +Common::Error GnapEngine::run() { + // Initialize the graphics mode to RGBA8888 + Graphics::PixelFormat format = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0); + initGraphics(800, 600, true, &format); + + // We do not support color conversion yet + if (_system->getScreenFormat() != format) + return Common::kUnsupportedColorMode; + + _lastUpdateClock = 0; + + // >>>>> Variable initialization + _cursorIndex = -1; + _verbCursor = 1; + + _loadGameSlot = -1; + if (ConfMan.hasKey("save_slot")) + _loadGameSlot = ConfMan.getInt("save_slot"); + + invClear(); + clearFlags(); + + _grabCursorSprite = nullptr; + _newGrabCursorSpriteIndex = -1; + _backgroundSurface = nullptr; + _isStockDatLoaded = false; + _gameDone = false; + _isPaused = false; + _pauseSprite = nullptr; + + //////////////////////////////////////////////////////////////////////////// + + _exe = new Common::PEResources(); + if (!_exe->loadFromEXE("ufos.exe")) + error("Could not load ufos.exe"); + +#ifdef USE_FREETYPE2 + Common::SeekableReadStream *stream = _exe->getResource(Common::kPEFont, 2000); + _font = Graphics::loadTTFFont(*stream, 24); + if (!_font) + warning("Unable to load font"); + delete stream; +#else + _font = nullptr; +#endif + + _dat = new DatManager(); + _spriteCache = new SpriteCache(_dat); + _soundCache = new SoundCache(_dat); + _sequenceCache = new SequenceCache(_dat); + _gameSys = new GameSys(this); + _soundMan = new SoundMan(this); + _debugger = new Debugger(this); + _gnap = new PlayerGnap(this); + _plat = new PlayerPlat(this); + + _menuBackgroundSurface = nullptr; + + initGlobalSceneVars(); + mainLoop(); + + delete _plat; + delete _gnap; + delete _soundMan; + delete _gameSys; + delete _sequenceCache; + delete _soundCache; + delete _spriteCache; + delete _dat; + delete _debugger; + delete _font; + delete _exe; + + return Common::kNoError; +} + +void GnapEngine::updateEvents() { + Common::Event event; + + while (_eventMan->pollEvent(event)) { + switch (event.type) { + case Common::EVENT_KEYDOWN: + // Check for debugger + if (event.kbd.keycode == Common::KEYCODE_d && (event.kbd.flags & Common::KBD_CTRL)) { + // Attach to the debugger + _debugger->attach(); + _debugger->onFrame(); + } + + _keyPressState[event.kbd.keycode] = 1; + _keyDownState[event.kbd.keycode] = 1; + break; + case Common::EVENT_KEYUP: + _keyDownState[event.kbd.keycode] = 0; + break; + case Common::EVENT_MOUSEMOVE: + _mousePos = event.mouse; + break; + case Common::EVENT_LBUTTONUP: + _mouseButtonState._left = false; + break; + case Common::EVENT_LBUTTONDOWN: + _leftClickMouseX = event.mouse.x; + _leftClickMouseY = event.mouse.y; + _mouseButtonState._left = true; + _mouseClickState._left = true; + break; + case Common::EVENT_RBUTTONUP: + _mouseButtonState._right = false; + break; + case Common::EVENT_RBUTTONDOWN: + _mouseButtonState._right = true; + _mouseClickState._right = true; + break; + case Common::EVENT_QUIT: + quitGame(); + break; + default: + break; + } + } +} + +void GnapEngine::gameUpdateTick() { + updateEvents(); + + if (shouldQuit()) { + _gameDone = true; + _sceneDone = true; + } + + int currClock = _system->getMillis(); + if (currClock >= _lastUpdateClock + 66) { + _gameSys->fatUpdate(); + _gameSys->drawSprites(); + _gameSys->updateScreen(); + _gameSys->updatePlaySounds(); + _gameSys->_gameSysClock++; + updateTimers(); + _lastUpdateClock = currClock; + } + + _soundMan->update(); + _system->updateScreen(); + _system->delayMillis(5); +} + +void GnapEngine::saveTimers() { + for (int i = 0; i < kMaxTimers; ++i ) + _savedTimers[i] = _timers[i]; +} + +void GnapEngine::restoreTimers() { + for (int i = 0; i < kMaxTimers; ++i ) + _timers[i] = _savedTimers[i]; +} + +void GnapEngine::pauseGame() { + if (!_isPaused) { + saveTimers(); + hideCursor(); + setGrabCursorSprite(-1); + _pauseSprite = _gameSys->createSurface(0x1076C); + _gameSys->insertSpriteDrawItem(_pauseSprite, (800 - _pauseSprite->w) / 2, (600 - _pauseSprite->h) / 2, 356); + _lastUpdateClock = 0; + gameUpdateTick(); + playMidi("pause.mid"); + _isPaused = true; + } +} + +void GnapEngine::resumeGame() { + if (_isPaused) { + restoreTimers(); + _gameSys->removeSpriteDrawItem(_pauseSprite, 356); + _lastUpdateClock = 0; + gameUpdateTick(); + deleteSurface(&_pauseSprite); + stopMidi(); + _isPaused = false; + clearAllKeyStatus1(); + _mouseClickState._left = false; + _mouseClickState._right = false; + showCursor(); + _gameSys->_gameSysClock = 0; + _gameSys->_lastUpdateClock = 0; + } +} + +void GnapEngine::updatePause() { + while (_isPaused && !_gameDone) { + gameUpdateTick(); + if (isKeyStatus1(Common::KEYCODE_p)) { + clearKeyStatus1(Common::KEYCODE_p); + resumeGame(); + } + } +} + +int GnapEngine::getRandom(int max) { + return _random->getRandomNumber(max - 1); +} + +int GnapEngine::readSavegameDescription(int savegameNum, Common::String &description) { + description = Common::String::format("Savegame %d", savegameNum); + return 0; +} + +int GnapEngine::loadSavegame(int savegameNum) { + return 1; +} + +void GnapEngine::delayTicks(int val, int idx = 0, bool updateCursor = false) { + int startTick = _timers[idx]; + + _timers[idx] = val; + + while (_timers[idx] && !_gameDone) { + gameUpdateTick(); + + if (updateCursor) + updateGrabCursorSprite(0, 0); + } + + startTick -= _timers[idx]; + if (startTick < 0) + startTick = 0; + + _timers[idx] = startTick; +} + +void GnapEngine::delayTicksA(int val, int idx) { + delayTicks(val, idx); +} + +void GnapEngine::delayTicksCursor(int val) { + delayTicks(val, 0, true); +} + +void GnapEngine::setHotspot(int index, int16 x1, int16 y1, int16 x2, int16 y2, uint16 flags, + int16 walkX, int16 walkY) { + _hotspots[index]._rect = Common::Rect(x1, y1, x2, y2); + _hotspots[index]._flags = flags; + _hotspotsWalkPos[index] = Common::Point(walkX, walkY); +} + +int GnapEngine::getHotspotIndexAtPos(Common::Point pos) { + for (int i = 0; i < _hotspotsCount; ++i) { + if (!_hotspots[i].isFlag(SF_DISABLED) && _hotspots[i].isPointInside(pos)) + return i; + } + return -1; +} + +void GnapEngine::updateCursorByHotspot() { + if (!_isWaiting) { + int hotspotIndex = getHotspotIndexAtPos(_mousePos); + + if (_debugger->_showHotspotNumber) { + // NOTE This causes some display glitches + char t[256]; + sprintf(t, "hotspot = %2d", hotspotIndex); + if (!_font) + _gameSys->fillSurface(nullptr, 10, 10, 80, 16, 0, 0, 0); + else + _gameSys->fillSurface(nullptr, 8, 9, _font->getStringWidth(t) + 10, _font->getFontHeight() + 2, 0, 0, 0); + _gameSys->drawTextToSurface(nullptr, 10, 10, 255, 255, 255, t); + } + + if (hotspotIndex < 0) + setCursor(kDisabledCursors[_verbCursor]); + else if (_hotspots[hotspotIndex]._flags & SF_EXIT_L_CURSOR) + setCursor(EXIT_L_CURSOR); + else if (_hotspots[hotspotIndex]._flags & SF_EXIT_R_CURSOR) + setCursor(EXIT_R_CURSOR); + else if (_hotspots[hotspotIndex]._flags & SF_EXIT_U_CURSOR) + setCursor(EXIT_U_CURSOR); + else if (_hotspots[hotspotIndex]._flags & SF_EXIT_D_CURSOR) + setCursor(EXIT_D_CURSOR); + else if (_hotspots[hotspotIndex]._flags & SF_EXIT_NE_CURSOR) + setCursor(EXIT_NE_CURSOR); + else if (_hotspots[hotspotIndex]._flags & SF_EXIT_NW_CURSOR) + setCursor(EXIT_NW_CURSOR); + else if (_hotspots[hotspotIndex]._flags & SF_EXIT_SE_CURSOR) + setCursor(EXIT_SE_CURSOR); + else if (_hotspots[hotspotIndex]._flags & SF_EXIT_SW_CURSOR) + setCursor(EXIT_SW_CURSOR); + else if (_hotspots[hotspotIndex]._flags & (1 << _verbCursor)) + setCursor(kCursors[_verbCursor]); + else + setCursor(kDisabledCursors[_verbCursor]); + } + // Update platypus hotspot + _hotspots[0]._rect = Common::Rect(_gridMinX + 75 * _plat->_pos.x - 30, _gridMinY + 48 * _plat->_pos.y - 100 + , _gridMinX + 75 * _plat->_pos.x + 30, _gridMinY + 48 * _plat->_pos.y); +} + +int GnapEngine::getClickedHotspotId() { + int result = -1; + if (_isWaiting) + _mouseClickState._left = false; + else if (_mouseClickState._left) { + int hotspotIndex = getHotspotIndexAtPos(Common::Point(_leftClickMouseX, _leftClickMouseY)); + if (hotspotIndex >= 0) { + _mouseClickState._left = false; + _timers[3] = 300; + result = hotspotIndex; + } + } + return result; +} + +int GnapEngine::getInventoryItemSpriteNum(int index) { + return kCursorSpriteIds[index]; +} + +void GnapEngine::updateMouseCursor() { + if (_mouseClickState._right) { + // Switch through the verb cursors + _mouseClickState._right = false; + _timers[3] = 300; + _verbCursor = (_verbCursor + 1) % 4; + if (!isFlag(kGFPlatypus) && _verbCursor == PLAT_CURSOR && _cursorValue == 1) + _verbCursor = (_verbCursor + 1) % 4; + if (!_isWaiting) + setCursor(kDisabledCursors[_verbCursor]); + setGrabCursorSprite(-1); + } + if (_isWaiting && ((_gnap->_actionStatus < 0 && _plat->_actionStatus < 0) || _sceneWaiting)) { + setCursor(kDisabledCursors[_verbCursor]); + _isWaiting = false; + } else if (!_isWaiting && (_gnap->_actionStatus >= 0 || _plat->_actionStatus >= 0) && !_sceneWaiting) { + setCursor(WAIT_CURSOR); + _isWaiting = true; + } +} + +void GnapEngine::setVerbCursor(int verbCursor) { + _verbCursor = verbCursor; + if (!_isWaiting) + setCursor(kDisabledCursors[_verbCursor]); +} + +void GnapEngine::setCursor(int cursorIndex) { + if (_cursorIndex != cursorIndex) { + const char *cursorName = kCursorNames[cursorIndex]; + Graphics::WinCursorGroup *cursorGroup = Graphics::WinCursorGroup::createCursorGroup(*_exe, Common::WinResourceID(cursorName)); + if (cursorGroup) { + Graphics::Cursor *cursor = cursorGroup->cursors[0].cursor; + CursorMan.replaceCursor(cursor->getSurface(), cursor->getWidth(), cursor->getHeight(), + cursor->getHotspotX(), cursor->getHotspotY(), cursor->getKeyColor()); + CursorMan.replaceCursorPalette(cursor->getPalette(), 0, 256); + delete cursorGroup; + } + _cursorIndex = cursorIndex; + } +} + +void GnapEngine::showCursor() { + CursorMan.showMouse(true); +} + +void GnapEngine::hideCursor() { + CursorMan.showMouse(false); +} + +void GnapEngine::setGrabCursorSprite(int index) { + freeGrabCursorSprite(); + if (index >= 0) { + createGrabCursorSprite(makeRid(1, kCursorSpriteIds[index])); + setVerbCursor(GRAB_CURSOR); + } + _grabCursorSpriteIndex = index; +} + +void GnapEngine::createGrabCursorSprite(int spriteId) { + _grabCursorSprite = _gameSys->createSurface(spriteId); + _gameSys->insertSpriteDrawItem(_grabCursorSprite, + _mousePos.x - (_grabCursorSprite->w / 2), + _mousePos.y - (_grabCursorSprite->h / 2), + 300); + delayTicks(5); +} + +void GnapEngine::freeGrabCursorSprite() { + if (_grabCursorSprite) { + _gameSys->removeSpriteDrawItem(_grabCursorSprite, 300); + _gameSys->removeSpriteDrawItem(_grabCursorSprite, 301); + delayTicks(5); + deleteSurface(&_grabCursorSprite); + } +} + +void GnapEngine::updateGrabCursorSprite(int x, int y) { + if (_grabCursorSprite) { + int newGrabCursorX = _mousePos.x - (_grabCursorSprite->w / 2) - x; + int newGrabCursorY = _mousePos.y - (_grabCursorSprite->h / 2) - y; + if (_currGrabCursorX != newGrabCursorX || _currGrabCursorY != newGrabCursorY) { + _currGrabCursorX = newGrabCursorX; + _currGrabCursorY = newGrabCursorY; + Common::Rect rect(newGrabCursorX, newGrabCursorY, + newGrabCursorX + _grabCursorSprite->w, newGrabCursorY + _grabCursorSprite->h); + _gameSys->invalidateGrabCursorSprite(300, rect, _grabCursorSprite, _grabCursorSprite); + } + } +} + +void GnapEngine::invClear() { + _inventory = 0; +} + +void GnapEngine::invAdd(int itemId) { + _inventory |= (1 << itemId); +} + +void GnapEngine::invRemove(int itemId) { + _inventory &= ~(1 << itemId); +} + +bool GnapEngine::invHas(int itemId) { + return (_inventory & (1 << itemId)) != 0; +} + +void GnapEngine::clearFlags() { + _gameFlags = 0; +} + +void GnapEngine::setFlag(int num) { + _gameFlags |= (1 << num); +} + +void GnapEngine::clearFlag(int num) { + _gameFlags &= ~(1 << num); +} + +bool GnapEngine::isFlag(int num) { + return (_gameFlags & (1 << num)) != 0; +} + +Graphics::Surface *GnapEngine::addFullScreenSprite(int resourceId, int id) { + _fullScreenSpriteId = id; + _fullScreenSprite = _gameSys->createSurface(resourceId); + _gameSys->insertSpriteDrawItem(_fullScreenSprite, 0, 0, id); + return _fullScreenSprite; +} + +void GnapEngine::removeFullScreenSprite() { + _gameSys->removeSpriteDrawItem(_fullScreenSprite, _fullScreenSpriteId); + deleteSurface(&_fullScreenSprite); +} + +void GnapEngine::showFullScreenSprite(int resourceId) { + hideCursor(); + setGrabCursorSprite(-1); + addFullScreenSprite(resourceId, 256); + while (!_mouseClickState._left && !isKeyStatus1(Common::KEYCODE_ESCAPE) + && !isKeyStatus1(Common::KEYCODE_SPACE) && !isKeyStatus1(Common::KEYCODE_RETURN) && !_gameDone) { + gameUpdateTick(); + } + _mouseClickState._left = false; + clearKeyStatus1(Common::KEYCODE_ESCAPE); + clearKeyStatus1(Common::KEYCODE_RETURN); + clearKeyStatus1(Common::KEYCODE_SPACE); + removeFullScreenSprite(); + showCursor(); +} + +void GnapEngine::queueInsertDeviceIcon() { + _gameSys->insertSequence(0x10849, 20, 0, 0, kSeqNone, 0, _deviceX1, _deviceY1); +} + +void GnapEngine::insertDeviceIconActive() { + _gameSys->insertSequence(0x1084A, 21, 0, 0, kSeqNone, 0, _deviceX1, _deviceY1); +} + +void GnapEngine::removeDeviceIconActive() { + _gameSys->removeSequence(0x1084A, 21, true); +} + +void GnapEngine::setDeviceHotspot(int hotspotIndex, int x1, int y1, int x2, int y2) { + _deviceX1 = x1; + _deviceX2 = x2; + _deviceY1 = y1; + _deviceY2 = y2; + if (x1 == -1) + _deviceX1 = 730; + if (x2 == -1) + _deviceX2 = 780; + if (y1 == -1) + _deviceY1 = 14; + if (y2 == -1) + _deviceY2 = 79; + + _hotspots[hotspotIndex]._rect = Common::Rect(_deviceX1, _deviceY1, _deviceX2, _deviceY2); + _hotspots[hotspotIndex]._flags = SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR; +} + +int GnapEngine::getSequenceTotalDuration(int resourceId) { + SequenceResource *sequenceResource = _sequenceCache->get(resourceId); + int maxValue = 0; + for (int i = 0; i < sequenceResource->_animationsCount; ++i) { + SequenceAnimation *animation = &sequenceResource->_animations[i]; + if (animation->_additionalDelay + animation->_maxTotalDuration > maxValue) + maxValue = animation->_additionalDelay + animation->_maxTotalDuration; + } + int totalDuration = maxValue + sequenceResource->_totalDuration; + _sequenceCache->release(resourceId); + return totalDuration; +} + +bool GnapEngine::isSoundPlaying(int resourceId) { + return _soundMan->isSoundPlaying(resourceId); +} + +void GnapEngine::playSound(int resourceId, bool looping) { + debugC(kDebugBasic, "playSound(%08X, %d)", resourceId, looping); + _soundMan->playSound(resourceId, looping); +} + +void GnapEngine::stopSound(int resourceId) { + _soundMan->stopSound(resourceId); +} + +void GnapEngine::setSoundVolume(int resourceId, int volume) { + _soundMan->setSoundVolume(resourceId, volume); +} + +void GnapEngine::updateTimers() { + for (int i = 0; i < kMaxTimers; ++i) + if (_timers[i] > 0) + --_timers[i]; +} + +void GnapEngine::initGameFlags(int num) { + invClear(); + invAdd(kItemMagazine); + switch (num) { + case 1: + setFlag(kGFPlatypusTalkingToAssistant); + break; + case 2: + clearFlags(); + break; + case 3: + invAdd(kItemDiceQuarterHole); + clearFlags(); + break; + case 4: + invAdd(kItemDiceQuarterHole); + invAdd(kItemHorn); + invAdd(kItemLightbulb); + clearFlags(); + setFlag(kGFPlatypus); + setFlag(kGFMudTaken); + setFlag(kGFNeedleTaken); + setFlag(kGFTwigTaken); + setFlag(kGFUnk04); + setFlag(kGFKeysTaken); + setFlag(kGFGrassTaken); + setFlag(kGFBarnPadlockOpen); + break; + } +} + +void GnapEngine::loadStockDat() { + if (!_isStockDatLoaded) { + _isStockDatLoaded = true; + _dat->open(1, "stock_n.dat"); + // The pre-loading of data is skipped as it's no longer required on modern hardware + } +} + +void GnapEngine::mainLoop() { + _newCursorValue = 1; + _cursorValue = -1; + _newSceneNum = 0; + _currentSceneNum = 55; + _prevSceneNum = 55; + invClear(); + clearFlags(); + _grabCursorSpriteIndex = -1; + _grabCursorSprite = nullptr; + + loadStockDat(); + + if (_loadGameSlot != -1) { + // Load a savegame + int slot = _loadGameSlot; + _loadGameSlot = -1; + loadGameState(slot); + _wasSavegameLoaded = true; + + showCursor(); + } + + while (!_gameDone) { + debugC(kDebugBasic, "New scene: %d", _newSceneNum); + + _prevSceneNum = _currentSceneNum; + _currentSceneNum = _newSceneNum; + + debugC(kDebugBasic, "GnapEngine::mainLoop() _prevSceneNum: %d; _currentSceneNum: %d", _prevSceneNum, _currentSceneNum); + + if (_newCursorValue != _cursorValue) { + debugC(kDebugBasic, "_newCursorValue: %d", _newCursorValue); + _cursorValue = _newCursorValue; + if (!_wasSavegameLoaded) + initGameFlags(_cursorValue); + } + + _sceneSavegameLoaded = _wasSavegameLoaded; + _wasSavegameLoaded = false; + + initScene(); + + runSceneLogic(); + afterScene(); + + _soundMan->stopAll(); + + // Force purge all resources + _sequenceCache->purge(true); + _soundCache->purge(true); + _spriteCache->purge(true); + } + + if (_backgroundSurface) + deleteSurface(&_backgroundSurface); + + _dat->close(1); +} + +void GnapEngine::initScene() { + Common::String datFilename; + + _isLeavingScene = false; + _sceneDone = false; + _newSceneNum = 55; + _gnap->_actionStatus = -1; + _plat->_actionStatus = -1; + _gnap->initBrainPulseRndValue(); + hideCursor(); + clearAllKeyStatus1(); + _mouseClickState._left = false; + _mouseClickState._right = false; + _sceneClickedHotspot = -1; + + datFilename = Common::String::format("%s_n.dat", kSceneNames[_currentSceneNum]); + + debugC(kDebugBasic, "GnapEngine::initScene() datFilename: %s", datFilename.c_str()); + + _dat->open(0, datFilename.c_str()); + + int backgroundId = initSceneLogic(); + + if (!_backgroundSurface) { + if (_currentSceneNum != 0) + _backgroundSurface = _gameSys->loadBitmap(makeRid(1, 0x8AA)); + else + _backgroundSurface = _gameSys->loadBitmap(makeRid(0, backgroundId)); + _gameSys->setBackgroundSurface(_backgroundSurface, 0, 500, 1, 1000); + } + + if (_currentSceneNum != 0 && _currentSceneNum != 16 && _currentSceneNum != 47 && + _currentSceneNum != 48 && _currentSceneNum != 54) { + _gameSys->drawBitmap(backgroundId); + } + + if ((_cursorValue == 4 && isFlag(kGFGnapControlsToyUFO)) || _currentSceneNum == 41) + playSound(makeRid(1, 0x8F6), true); + +} + +void GnapEngine::endSceneInit() { + showCursor(); + if (_newGrabCursorSpriteIndex >= 0) + setGrabCursorSprite(_newGrabCursorSpriteIndex); +} + +void GnapEngine::afterScene() { + if (_gameDone) + return; + + if (_newCursorValue == _cursorValue && _newSceneNum != 0 && _newSceneNum != 16 && + _newSceneNum != 47 && _newSceneNum != 48 && _newSceneNum != 54 && _newSceneNum != 49 && + _newSceneNum != 50 && _newSceneNum != 51 && _newSceneNum != 52) + _newGrabCursorSpriteIndex = _grabCursorSpriteIndex; + else + _newGrabCursorSpriteIndex = -1; + + setGrabCursorSprite(-1); + + _gameSys->requestClear2(false); + _gameSys->requestClear1(); + _gameSys->waitForUpdate(); + + _gameSys->requestClear2(false); + _gameSys->requestClear1(); + _gameSys->waitForUpdate(); + + screenEffect(0, 0, 0, 0); + + _dat->close(0); + + for (int animationIndex = 0; animationIndex < 12; ++animationIndex) + _gameSys->setAnimation(0, 0, animationIndex); + + clearKeyStatus1(Common::KEYCODE_p); + + _mouseClickState._left = false; + _mouseClickState._right = false; + +} + +void GnapEngine::checkGameKeys() { + if (isKeyStatus1(Common::KEYCODE_p)) { + clearKeyStatus1(Common::KEYCODE_p); + pauseGame(); + updatePause(); + } +} + +void GnapEngine::startSoundTimerA(int timerIndex) { + _soundTimerIndexA = timerIndex; + _timers[timerIndex] = getRandom(50) + 100; +} + +int GnapEngine::playSoundA() { + static const int kSoundIdsA[] = { + 0x93E, 0x93F, 0x941, 0x942, 0x943, 0x944, + 0x945, 0x946, 0x947, 0x948, 0x949 + }; + + int soundId = -1; + + if (!_timers[_soundTimerIndexA]) { + _timers[_soundTimerIndexA] = getRandom(50) + 100; + soundId = kSoundIdsA[getRandom(11)]; + playSound(soundId | 0x10000, false); + } + return soundId; +} + +void GnapEngine::startSoundTimerB(int timerIndex) { + _soundTimerIndexB = timerIndex; + _timers[timerIndex] = getRandom(50) + 150; +} + +int GnapEngine::playSoundB() { + static const int kSoundIdsB[] = { + 0x93D, 0x929, 0x92A, 0x92B, 0x92C, 0x92D, + 0x92E, 0x92F, 0x930, 0x931, 0x932, 0x933, + 0x934, 0x935, 0x936, 0x937, 0x938, 0x939, + 0x93A + }; + + int soundId = -1; + + if (!_timers[_soundTimerIndexB]) { + _timers[_soundTimerIndexB] = getRandom(50) + 150; + soundId = kSoundIdsB[getRandom(19)]; + playSound(soundId | 0x10000, false); + } + return soundId; +} + +void GnapEngine::startSoundTimerC(int timerIndex) { + _soundTimerIndexC = timerIndex; + _timers[timerIndex] = getRandom(50) + 150; +} + +int GnapEngine::playSoundC() { + static const int kSoundIdsC[] = { + 0x918, 0x91F, 0x920, 0x922, 0x923, 0x924, + 0x926 + }; + + int soundId = -1; + + if (!_timers[_soundTimerIndexC]) { + _timers[_soundTimerIndexC] = getRandom(50) + 150; + soundId = kSoundIdsC[getRandom(7)] ; + playSound(soundId | 0x10000, false); + } + return soundId; +} + +void GnapEngine::startIdleTimer(int timerIndex) { + _idleTimerIndex = timerIndex; + _timers[timerIndex] = 3000; +} + +void GnapEngine::updateIdleTimer() { + if (!_timers[_idleTimerIndex]) { + _timers[_idleTimerIndex] = 3000; + _gameSys->insertSequence(0x1088B, 255, 0, 0, kSeqNone, 0, 0, 75); + } +} + +void GnapEngine::screenEffect(int dir, byte r, byte g, byte b) { + int startVal = 0; + if (dir == 1) + startVal = 300; + + for (int y = startVal; y < startVal + 300 && !_gameDone; y += 50) { + _gameSys->fillSurface(nullptr, 0, y, 800, 50, r, g, b); + _gameSys->fillSurface(nullptr, 0, 549 - y + 1, 800, 50, r, g, b); + gameUpdateTick(); + _system->delayMillis(50); + } +} + +bool GnapEngine::isKeyStatus1(int key) { + return _keyPressState[key] != 0; +} + +bool GnapEngine::isKeyStatus2(int key) { + return _keyDownState[key] != 0; +} + +void GnapEngine::clearKeyStatus1(int key) { + _keyPressState[key] = 0; + _keyDownState[key] = 0; +} + +void GnapEngine::clearAllKeyStatus1() { + _keyStatus1[0] = 0; + _keyStatus1[1] = 0; + memset(_keyPressState, 0, sizeof(_keyPressState)); + memset(_keyDownState, 0, sizeof(_keyDownState)); +} + +void GnapEngine::deleteSurface(Graphics::Surface **surface) { + if (surface && *surface) { + (*surface)->free(); + delete *surface; + *surface = nullptr; + } +} + +bool GnapEngine::testWalk(int animationIndex, int someStatus, int gridX1, int gridY1, int gridX2, int gridY2) { + if (_mouseClickState._left && someStatus == _gnap->_actionStatus) { + _isLeavingScene = false; + _gameSys->setAnimation(0, 0, animationIndex); + _gnap->_actionStatus = -1; + _plat->_actionStatus = -1; + _gnap->walkTo(Common::Point(gridX1, gridY1), -1, -1, 1); + _plat->walkTo(Common::Point(gridX2, gridY2), -1, -1, 1); + _mouseClickState._left = false; + return true; + } + return false; +} + +void GnapEngine::doCallback(int callback) { + switch (callback) { + case 8: + case 10: + case 20: + _scene->updateAnimationsCb(); + break; + } +} + +//////////////////////////////////////////////////////////////////////////////// + +void GnapEngine::initGlobalSceneVars() { + // Shared by scenes 17 && 18 + _s18GarbageCanPos = 8; + + // Toy UFO + _toyUfoId = 0; + _toyUfoActionStatus = -1; + _toyUfoX = 0; + _toyUfoY = 50; +} + +void GnapEngine::playSequences(int fullScreenSpriteId, int sequenceId1, int sequenceId2, int sequenceId3) { + setGrabCursorSprite(-1); + _gameSys->setAnimation(sequenceId2, _gnap->_id, 0); + _gameSys->insertSequence(sequenceId2, _gnap->_id, + makeRid(_gnap->_sequenceDatNum, _gnap->_sequenceId), _gnap->_id, + kSeqSyncWait, 0, 15 * (5 * _gnap->_pos.x - 25), 48 * (_gnap->_pos.y - 8)); + _gnap->_sequenceId = sequenceId2; + _gnap->_sequenceDatNum = 0; + while (_gameSys->getAnimationStatus(0) != 2 && !_gameDone) + gameUpdateTick(); + hideCursor(); + addFullScreenSprite(fullScreenSpriteId, 255); + _gameSys->setAnimation(sequenceId1, 256, 0); + _gameSys->insertSequence(sequenceId1, 256, 0, 0, kSeqNone, 0, 0, 0); + while (_gameSys->getAnimationStatus(0) != 2 && !_gameDone) + gameUpdateTick(); + _gameSys->setAnimation(sequenceId3, _gnap->_id, 0); + _gameSys->insertSequence(sequenceId3, _gnap->_id, + makeRid(_gnap->_sequenceDatNum, _gnap->_sequenceId), _gnap->_id, + kSeqSyncWait, 0, 15 * (5 * _gnap->_pos.x - 25), 48 * (_gnap->_pos.y - 8)); + removeFullScreenSprite(); + showCursor(); + _gnap->_sequenceId = sequenceId3; +} + +void GnapEngine::toyUfoSetStatus(int flagNum) { + clearFlag(kGFUnk16); + clearFlag(kGFJointTaken); + clearFlag(kGFUnk18); + clearFlag(kGFGroceryStoreHatTaken); + setFlag(flagNum); +} + +int GnapEngine::toyUfoGetSequenceId() { + if (isFlag(kGFUnk16)) + return 0x84E; + if (isFlag(kGFJointTaken)) + return 0x84B; + if (isFlag(kGFUnk18)) + return 0x84D; + if (isFlag(kGFGroceryStoreHatTaken)) + return 0x84C; + return 0x84E; +} + +bool GnapEngine::toyUfoCheckTimer() { + if (!isFlag(kGFGnapControlsToyUFO) || isFlag(kGFUnk18) || _timers[9] || + _toyUfoSequenceId == 0x870 || _toyUfoSequenceId == 0x871 || _toyUfoSequenceId == 0x872 || _toyUfoSequenceId == 0x873) + return false; + _sceneDone = true; + _newSceneNum = 41; + return true; +} + +void GnapEngine::toyUfoFlyTo(int destX, int destY, int minX, int maxX, int minY, int maxY, int animationIndex) { + GridStruct flyNodes[34]; + + if (destX == -1) + destX = _leftClickMouseX; + + if (destY == -1) + destY = _leftClickMouseY; + + int clippedDestX = CLIP(destX, minX, maxX); + int clippedDestY = CLIP(destY, minY, maxY); + int dirX = 0, dirY = 0; // 0, -1 or 1 + + if (clippedDestX != _toyUfoX) + dirX = (clippedDestX - _toyUfoX) / ABS(clippedDestX - _toyUfoX); + + if (clippedDestY != _toyUfoY) + dirY = (clippedDestY - _toyUfoY) / ABS(clippedDestY - _toyUfoY); + + int deltaX = ABS(clippedDestX - _toyUfoX); + int deltaY = ABS(clippedDestY - _toyUfoY); + + int i = 0; + if (deltaY > deltaX) { + int flyDirYIncr = 32; + int gridDistY = deltaY / flyDirYIncr; + int curMove = 0; + while (curMove < deltaY && i < 34) { + if (gridDistY - 5 >= i) { + flyDirYIncr = MIN(36, 8 * i + 8); + } else { + flyDirYIncr = MAX(6, flyDirYIncr - 3); + } + curMove += flyDirYIncr; + flyNodes[i]._gridX1 = _toyUfoX + dirX * deltaX * curMove / deltaY; + flyNodes[i]._gridY1 = _toyUfoY + dirY * curMove; + ++i; + } + } else { + int flyDirXIncr = 36; + int gridDistX = deltaX / flyDirXIncr; + int curMove = 0; + while (curMove < deltaX && i < 34) { + if (gridDistX - 5 >= i) { + flyDirXIncr = MIN(38, 8 * i + 8); + } else { + flyDirXIncr = MAX(6, flyDirXIncr - 3); + } + curMove += flyDirXIncr; + flyNodes[i]._gridX1 = _toyUfoX + dirX * curMove; + flyNodes[i]._gridY1 = _toyUfoY + dirY * deltaY * curMove / deltaX; + ++i; + } + } + + int nodesCount = i - 1; + + _toyUfoX = clippedDestX; + _toyUfoY = clippedDestY; + + if (nodesCount > 0) { + int seqId = 0; + if (isFlag(kGFUnk16)) + seqId = 0x867; + else if (isFlag(kGFJointTaken)) + seqId = 0x84F; + else if (isFlag(kGFUnk18)) + seqId = 0x85F; + else if (isFlag(kGFGroceryStoreHatTaken)) + seqId = 0x857; + else + error("Unhandled flag in GnapEngine::toyUfoFlyTo(): 0x%x", _gameFlags); + flyNodes[0]._sequenceId = seqId; + flyNodes[0]._id = 0; + _gameSys->insertSequence(seqId | 0x10000, 0, + _toyUfoSequenceId | 0x10000, _toyUfoId, + kSeqSyncWait, 0, flyNodes[0]._gridX1 - 365, flyNodes[0]._gridY1 - 128); + for (i = 1; i < nodesCount; ++i) { + flyNodes[i]._sequenceId = seqId + (i % 8); + flyNodes[i]._id = i; + _gameSys->insertSequence(flyNodes[i]._sequenceId | 0x10000, flyNodes[i]._id, + flyNodes[i - 1]._sequenceId | 0x10000, flyNodes[i - 1]._id, + kSeqSyncWait, 0, + flyNodes[i]._gridX1 - 365, flyNodes[i]._gridY1 - 128); + } + + _toyUfoSequenceId = flyNodes[nodesCount - 1]._sequenceId; + _toyUfoId = flyNodes[nodesCount - 1]._id; + + if (animationIndex >= 0) + _gameSys->setAnimation(_toyUfoSequenceId | 0x10000, _toyUfoId, animationIndex); + + } +} + +void GnapEngine::playMidi(const char *name) { + if (_music) + return; + + _music = new MusicPlayer(name); + _music->playSMF(true); +} + +void GnapEngine::stopMidi() { + if (_music) { + _music->stop(); + delete _music; + _music = nullptr; + } +} +} // End of namespace Gnap diff --git a/engines/gnap/gnap.h b/engines/gnap/gnap.h new file mode 100644 index 0000000000..84c40e2969 --- /dev/null +++ b/engines/gnap/gnap.h @@ -0,0 +1,477 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GNAP_GNAP_H +#define GNAP_GNAP_H + +#include "common/array.h" +#include "common/events.h" +#include "common/file.h" +#include "common/memstream.h" +#include "common/random.h" +#include "common/savefile.h" +#include "common/serializer.h" +#include "common/str.h" +#include "common/substream.h" +#include "common/system.h" +#include "common/winexe.h" +#include "common/winexe_pe.h" +#include "engines/engine.h" +#include "graphics/pixelformat.h" +#include "graphics/wincursor.h" +#include "graphics/fontman.h" +#include "graphics/font.h" +#include "graphics/fonts/ttf.h" + +#include "gnap/debugger.h" +#include "gnap/resource.h" +#include "gnap/scenes/scenecore.h" +#include "gnap/character.h" +#include "gnap/music.h" + +struct ADGameDescription; + +namespace Gnap { + +class DatManager; +class SequenceResource; +class SpriteResource; +class GameSys; +class SoundMan; +class MusicPlayer; + +#define GNAP_SAVEGAME_VERSION 2 + +struct MouseButtonState { + bool _left; + bool _right; + MouseButtonState() : _left(false), _right(false) { + } +}; + +struct Hotspot { + Common::Rect _rect; + uint16 _flags; + + bool isPointInside(Common::Point pos) const { + return _rect.contains(pos); + } + + bool isFlag(uint16 flag) const { + return (_flags & flag) != 0; + } + + void clearRect() { + _rect = Common::Rect(0, 0, 0, 0); + } +}; + +const int kMaxTimers = 10; + +enum GnapDebugChannels { + kDebugBasic = 1 << 0, + kDebugMusic = 1 << 1 +}; + +enum { + SF_NONE = 0x0000, + SF_LOOK_CURSOR = 0x0001, + SF_GRAB_CURSOR = 0x0002, + SF_TALK_CURSOR = 0x0004, + SF_PLAT_CURSOR = 0x0008, + SF_DISABLED = 0x0010, + SF_WALKABLE = 0x0020, + SF_EXIT_L_CURSOR = 0x0040, + SF_EXIT_R_CURSOR = 0x0080, + SF_EXIT_U_CURSOR = 0x0100, + SF_EXIT_D_CURSOR = 0x0200, + SF_EXIT_NW_CURSOR = 0x0400, + SF_EXIT_NE_CURSOR = 0x0800, + SF_EXIT_SW_CURSOR = 0x1000, + SF_EXIT_SE_CURSOR = 0x2000 +}; + +enum { + LOOK_CURSOR = 0, + GRAB_CURSOR = 1, + TALK_CURSOR = 2, + PLAT_CURSOR = 3, + NOLOOK_CURSOR = 4, + NOGRAB_CURSOR = 5, + NOTALK_CURSOR = 6, + NOPLAT_CURSOR = 7, + EXIT_L_CURSOR = 8, + EXIT_R_CURSOR = 9, + EXIT_U_CURSOR = 10, + EXIT_D_CURSOR = 11, + EXIT_NE_CURSOR = 12, + EXIT_NW_CURSOR = 13, + EXIT_SE_CURSOR = 14, + EXIT_SW_CURSOR = 15, + WAIT_CURSOR = 16 +}; + +enum { + kGSPullOutDevice = 0, + kGSPullOutDeviceNonWorking = 1, + kGSIdle = 2, + kGSBrainPulsating = 3, + kGSImpossible = 4, + kGSScratchingHead = 5, + kGSDeflect = 6, + kGSUseDevice = 7, + kGSMoan1 = 8, + kGSMoan2 = 9 +}; + +enum { + kItemMagazine = 0, + kItemMud = 1, + kItemGrass = 2, + kItemDisguise = 3, + kItemNeedle = 4, + kItemTwig = 5, + kItemGas = 6, + kItemKeys = 7, + kItemDice = 8, + kItemTongs = 9, + kItemQuarter = 10, + kItemQuarterWithHole = 11, + kItemDiceQuarterHole = 12, + kItemWrench = 13, + kItemCowboyHat = 14, + kItemGroceryStoreHat = 15, + kItemBanana = 16, + kItemTickets = 17, + kItemPicture = 18, + kItemEmptyBucket = 19, + kItemBucketWithBeer = 20, + kItemBucketWithPill = 21, + kItemPill = 22, + kItemHorn = 23, + kItemJoint = 24, + kItemChickenBucket = 25, + kItemGum = 26, + kItemSpring = 27, + kItemLightbulb = 28, + kItemCereals = 29 +}; + +enum { + kGFPlatypus = 0, + kGFMudTaken = 1, + kGFNeedleTaken = 2, + kGFTwigTaken = 3, + kGFUnk04 = 4, + kGFKeysTaken = 5, + kGFGrassTaken = 6, + kGFBarnPadlockOpen = 7, + kGFTruckFilledWithGas = 8, + kGFTruckKeysUsed = 9, + kGFPlatypusDisguised = 10, + kGFSceneFlag1 = 11, + kGFGnapControlsToyUFO = 12, + kGFUnk13 = 13, // Tongue Fight Won? + kGFUnk14 = 14, + kGFSpringTaken = 15, + kGFUnk16 = 16, + kGFJointTaken = 17, + kGFUnk18 = 18, + kGFGroceryStoreHatTaken = 19, + kGFPictureTaken = 20, + kGFUnk21 = 21, + kGFUnk22 = 22, + kGFUnk23 = 23, + kGFUnk24 = 24, + kGFUnk25 = 25, + kGFPlatypusTalkingToAssistant = 26, + kGFUnk27 = 27, + kGFUnk28 = 28, + kGFGasTaken = 29, + kGFUnk30 = 30, + kGFUnk31 = 31 +}; + +struct GnapSavegameHeader { + uint8 _version; + Common::String _saveName; + Graphics::Surface *_thumbnail; + int _year, _month, _day; + int _hour, _minute; +}; + +class GnapEngine : public Engine { +protected: + Common::Error run(); + virtual bool hasFeature(EngineFeature f) const; +public: + GnapEngine(OSystem *syst, const ADGameDescription *gd); + ~GnapEngine(); +private: + const ADGameDescription *_gameDescription; + Graphics::PixelFormat _pixelFormat; + int _loadGameSlot; + +public: + Common::RandomSource *_random; + Common::PEResources *_exe; + + DatManager *_dat; + SpriteCache *_spriteCache; + SoundCache *_soundCache; + SequenceCache *_sequenceCache; + GameSys *_gameSys; + SoundMan *_soundMan; + Debugger *_debugger; + Scene *_scene; + PlayerGnap *_gnap; + PlayerPlat *_plat; + MusicPlayer *_music; + Graphics::Font *_font; + + Common::MemoryWriteStreamDynamic *_tempThumbnail; + + int _lastUpdateClock; + bool _gameDone; + + byte _keyPressState[512]; + byte _keyDownState[512]; + + bool _isPaused; + Graphics::Surface *_pauseSprite; + int _timers[kMaxTimers], _savedTimers[kMaxTimers]; + + MouseButtonState _mouseButtonState; + MouseButtonState _mouseClickState; + + uint32 _keyStatus1[2]; + + bool _sceneSavegameLoaded, _wasSavegameLoaded; + + Graphics::Surface *_backgroundSurface; + int _prevSceneNum, _currentSceneNum, _newSceneNum; + bool _sceneDone, _sceneWaiting; + + uint32 _inventory, _gameFlags; + + Hotspot _hotspots[20]; + Common::Point _hotspotsWalkPos[20]; + int _hotspotsCount; + int _sceneClickedHotspot; + + bool _isWaiting; + bool _isLeavingScene; + + bool _isStockDatLoaded; + + int _newCursorValue, _cursorValue; + + int _verbCursor, _cursorIndex; + Common::Point _mousePos; + int _leftClickMouseX, _leftClickMouseY; + + Graphics::Surface *_grabCursorSprite; + int _currGrabCursorX, _currGrabCursorY; + int _grabCursorSpriteIndex, _newGrabCursorSpriteIndex; + + Graphics::Surface *_fullScreenSprite; + int _fullScreenSpriteId; + + int _deviceX1, _deviceY1, _deviceX2, _deviceY2; + + int _soundTimerIndexA; + int _soundTimerIndexB; + int _soundTimerIndexC; + int _idleTimerIndex; + + void updateEvents(); + void gameUpdateTick(); + void saveTimers(); + void restoreTimers(); + + void pauseGame(); + void resumeGame(); + void updatePause(); + + int getRandom(int max); + + int readSavegameDescription(int savegameNum, Common::String &description); + int loadSavegame(int savegameNum); + Common::Error saveGameState(int slot, const Common::String &desc); + Common::Error loadGameState(int slot); + Common::String generateSaveName(int slot); + void synchronize(Common::Serializer &s); + void writeSavegameHeader(Common::OutSaveFile *out, GnapSavegameHeader &header); + static bool readSavegameHeader(Common::InSaveFile *in, GnapSavegameHeader &header); + + void delayTicks(int val, int idx, bool updateCursor); + void delayTicksA(int val, int idx); + void delayTicksCursor(int val); + + void setHotspot(int index, int16 x1, int16 y1, int16 x2, int16 y2, uint16 flags = SF_NONE, + int16 walkX = -1, int16 walkY = -1); + int getHotspotIndexAtPos(Common::Point pos); + void updateCursorByHotspot(); + int getClickedHotspotId(); + + int getInventoryItemSpriteNum(int index); + + void updateMouseCursor(); + void setVerbCursor(int verbCursor); + void setCursor(int cursorIndex); + void showCursor(); + void hideCursor(); + + void setGrabCursorSprite(int index); + void createGrabCursorSprite(int spriteId); + void freeGrabCursorSprite(); + void updateGrabCursorSprite(int x, int y); + + void invClear(); + void invAdd(int itemId); + void invRemove(int itemId); + bool invHas(int itemId); + + void clearFlags(); + void setFlag(int num); + void clearFlag(int num); + bool isFlag(int num); + + Graphics::Surface *addFullScreenSprite(int resourceId, int id); + void removeFullScreenSprite(); + void showFullScreenSprite(int resourceId); + + void queueInsertDeviceIcon(); + void insertDeviceIconActive(); + void removeDeviceIconActive(); + void setDeviceHotspot(int hotspotIndex, int x1, int y1, int x2, int y2); + + int getSequenceTotalDuration(int resourceId); + + bool isSoundPlaying(int resourceId); + void playSound(int resourceId, bool looping); + void stopSound(int resourceId); + void setSoundVolume(int resourceId, int volume); + + void updateTimers(); + + void initGameFlags(int num); + void loadStockDat(); + + void mainLoop(); + void initScene(); + void endSceneInit(); + void afterScene(); + + int initSceneLogic(); + void runSceneLogic(); + + void checkGameKeys(); + + void startSoundTimerA(int timerIndex); + int playSoundA(); + void startSoundTimerB(int timerIndex); + int playSoundB(); + void startSoundTimerC(int timerIndex); + int playSoundC(); + void startIdleTimer(int timerIndex); + void updateIdleTimer(); + + void screenEffect(int dir, byte r, byte g, byte b); + + bool isKeyStatus1(int key); + bool isKeyStatus2(int key); + void clearKeyStatus1(int key); + void clearAllKeyStatus1(); + + void deleteSurface(Graphics::Surface **surface); + + // Menu + int _menuStatus; + int _menuSpritesIndex; + bool _menuDone; + Graphics::Surface *_menuBackgroundSurface; + Graphics::Surface *_menuQuitQuerySprite; + Graphics::Surface *_largeSprite; + Graphics::Surface *_menuSaveLoadSprite; + Graphics::Surface *_menuSprite2; + Graphics::Surface *_menuSprite1; + char _savegameFilenames[7][30]; + Graphics::Surface *_savegameSprites[7]; + Graphics::Surface *_spriteHandle; + Graphics::Surface *_cursorSprite; + int _menuInventoryIndices[30]; + Graphics::Surface *_menuInventorySprites[30]; + int _savegameIndex; + void createMenuSprite(); + void freeMenuSprite(); + void initMenuHotspots1(); + void initMenuHotspots2(); + void initMenuQuitQueryHotspots(); + void initSaveLoadHotspots(); + void drawInventoryFrames(); + void insertInventorySprites(); + void removeInventorySprites(); + void runMenu(); + void updateMenuStatusInventory(); + void updateMenuStatusMainMenu(); + void updateMenuStatusSaveGame(); + void updateMenuStatusLoadGame(); + void updateMenuStatusQueryQuit(); + + // Grid common + int _gridMinX, _gridMinY; + int _gridMaxX, _gridMaxY; + bool isPointBlocked(int gridX, int gridY); + bool isPointBlocked(Common::Point gridPos); + void initSceneGrid(int gridMinX, int gridMinY, int gridMaxX, int gridMaxY); + bool testWalk(int animationIndex, int someStatus, int gridX1, int gridY1, int gridX2, int gridY2); + + // Gnap + void doCallback(int callback); + + // Scenes + int _toyUfoNextSequenceId, _toyUfoSequenceId; + int _toyUfoId; + int _toyUfoActionStatus; + int _toyUfoX; + int _toyUfoY; + + void initGlobalSceneVars(); + void playSequences(int fullScreenSpriteId, int sequenceId1, int sequenceId2, int sequenceId3); + + // Shared by scenes 17 & 18 + int _s18GarbageCanPos; + + // Scene 4x + void toyUfoSetStatus(int flagNum); + int toyUfoGetSequenceId(); + bool toyUfoCheckTimer(); + void toyUfoFlyTo(int destX, int destY, int minX, int maxX, int minY, int maxY, int animationIndex); + + void playMidi(const char *name); + void stopMidi(); +}; + +} // End of namespace Gnap + +#endif // GNAP_GNAP_H diff --git a/engines/gnap/grid.cpp b/engines/gnap/grid.cpp new file mode 100644 index 0000000000..aa6da71395 --- /dev/null +++ b/engines/gnap/grid.cpp @@ -0,0 +1,995 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gnap/gnap.h" +#include "gnap/datarchive.h" +#include "gnap/gamesys.h" +#include "gnap/resource.h" + +namespace Gnap { + +void GnapEngine::initSceneGrid(int gridMinX, int gridMinY, int gridMaxX, int gridMaxY) { + _gridMinX = gridMinX; + _gridMinY = gridMinY; + _gridMaxX = gridMaxX; + _gridMaxY = gridMaxY; + _gnap->_gridX = 410 - gridMinX; + _gnap->_gridY = 450 - gridMinY; + _plat->_gridX = 396 - gridMinX; + _plat->_gridY = 347 - gridMinY; +} + +bool GnapEngine::isPointBlocked(Common::Point gridPos) { + return isPointBlocked(gridPos.x, gridPos.y); +} + +bool GnapEngine::isPointBlocked(int gridX, int gridY) { + + if (gridX < 0 || gridX >= _gridMaxX || gridY < 0 || gridY >= _gridMaxY) + return true; + + if ((_gnap->_pos == Common::Point(gridX, gridY)) || (_plat->_pos == Common::Point(gridX, gridY))) + return true; + + Common::Point pos = Common::Point(_gridMinX + 75 * gridX, _gridMinY + 48 * gridY); + + for (int i = 0; i < _hotspotsCount; ++i) { + if (_hotspots[i].isPointInside(pos) && !(_hotspots[i]._flags & SF_WALKABLE)) + return true; + } + + return false; +} + +/******************************************************************************/ + +int PlayerGnap::getWalkStopSequenceId(int deltaX, int deltaY) { + static const int gnapWalkStopSequenceIds[9] = { + 0x7BC, 0x7BA, 0x7BA, + 0x7BC, 0x000, 0x7BA, + 0x7BB, 0x7B9, 0x7B9 + }; + + int id = 3 * (deltaX + 1) + deltaY + 1; + assert (id >= 0 && id < 9 ); + return gnapWalkStopSequenceIds[id]; +} + +Facing PlayerGnap::getWalkFacing(int deltaX, int deltaY) { + static const Facing gnapWalkFacings[9] = { + kDirUpLeft, kDirBottomLeft, kDirBottomLeft, + kDirUpLeft, kDirIdleLeft, kDirBottomLeft, + kDirUpRight, kDirBottomRight, kDirBottomRight + }; + + int id = 3 * (deltaX + 1) + deltaY + 1; + assert (id >= 0 && id < 9 ); + return gnapWalkFacings[id]; +} + +bool PlayerGnap::findPath1(int gridX, int gridY, int index) { + _walkNodesCount = index; + _walkDirXIncr = 0; + _walkDirYIncr = 0; + _walkDeltaX = ABS(_walkDestX - gridX); + _walkDeltaY = ABS(_walkDestY - gridY); + + if (_walkDeltaX) + _walkDirX = (_walkDestX - gridX) / _walkDeltaX; + else + _walkDirX = 0; + + if (_walkDeltaY) + _walkDirY = (_walkDestY - gridY) / _walkDeltaY; + else + _walkDirY = 0; + + while (_walkDirXIncr < _walkDeltaX && _walkDirYIncr < _walkDeltaY) { + _walkNodes[_walkNodesCount]._gridX1 = gridX + _walkDirX * _walkDirXIncr; + _walkNodes[_walkNodesCount]._gridY1 = gridY + _walkDirY * _walkDirYIncr; + if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirXIncr; + ++_walkDirYIncr; + } else if (_walkDeltaY - _walkDirYIncr > _walkDeltaX - _walkDirXIncr) { + if (!_vm->isPointBlocked(_walkNodes[_walkNodesCount]._gridX1, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = 0; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirYIncr; + } else if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = 0; + ++_walkDirXIncr; + } else + return false; + } else { + if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = 0; + ++_walkDirXIncr; + } else if (!_vm->isPointBlocked(_walkNodes[_walkNodesCount]._gridX1, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = 0; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirYIncr; + } else + return false; + } + ++_walkNodesCount; + } + + while (_walkDirXIncr < _walkDeltaX) { + _walkNodes[_walkNodesCount]._gridX1 = gridX + _walkDirX * _walkDirXIncr; + _walkNodes[_walkNodesCount]._gridY1 = _walkDestY; + if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkDestY)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = 0; + ++_walkDirXIncr; + ++_walkNodesCount; + } else + return false; + } + + while (_walkDirYIncr < _walkDeltaY) { + _walkNodes[_walkNodesCount]._gridX1 = _walkDestX; + _walkNodes[_walkNodesCount]._gridY1 = gridY + _walkDirY * _walkDirYIncr; + if (!_vm->isPointBlocked(_walkDestX, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = 0; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirYIncr; + ++_walkNodesCount; + } else + return false; + } + + return true; +} + +bool PlayerGnap::findPath2(int gridX, int gridY, int index) { + _walkNodesCount = index; + _walkDirXIncr = 0; + _walkDirYIncr = 0; + _walkDeltaX = ABS(_walkDestX - gridX); + _walkDeltaY = ABS(_walkDestY - gridY); + + if (_walkDeltaX) + _walkDirX = (_walkDestX - gridX) / _walkDeltaX; + else + _walkDirX = 0; + + if (_walkDeltaY) + _walkDirY = (_walkDestY - gridY) / _walkDeltaY; + else + _walkDirY = 0; + + while (_walkDeltaY < _walkDeltaX - _walkDirXIncr) { + _walkNodes[_walkNodesCount]._gridX1 = gridX + _walkDirX * _walkDirXIncr; + _walkNodes[_walkNodesCount]._gridY1 = gridY; + if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, gridY)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = 0; + ++_walkDirXIncr; + ++_walkNodesCount; + } else + return false; + } + + while (_walkDeltaX < _walkDeltaY - _walkDirYIncr) { + _walkNodes[_walkNodesCount]._gridX1 = gridX; + _walkNodes[_walkNodesCount]._gridY1 = gridY + _walkDirY * _walkDirYIncr; + if (!_vm->isPointBlocked(gridX, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = 0; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirYIncr; + ++_walkNodesCount; + } else + return false; + } + + while (_walkDirXIncr < _walkDeltaX && _walkDirYIncr < _walkDeltaY) { + _walkNodes[_walkNodesCount]._gridX1 = gridX + _walkDirX * _walkDirXIncr; + _walkNodes[_walkNodesCount]._gridY1 = gridY + _walkDirY * _walkDirYIncr; + if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirXIncr; + ++_walkDirYIncr; + } else if (_walkDeltaY - _walkDirYIncr > _walkDeltaX - _walkDirXIncr) { + if (!_vm->isPointBlocked(_walkNodes[_walkNodesCount]._gridX1, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = 0; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirYIncr; + } else if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = 0; + ++_walkDirXIncr; + } else + return false; + } else { + if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = 0; + ++_walkDirXIncr; + } else if (!_vm->isPointBlocked(_walkNodes[_walkNodesCount]._gridX1, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = 0; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirYIncr; + } else + return false; + } + ++_walkNodesCount; + } + + while (_walkDirXIncr < _walkDeltaX) { + _walkNodes[_walkNodesCount]._gridX1 = gridX + _walkDirX * _walkDirXIncr; + _walkNodes[_walkNodesCount]._gridY1 = _walkDestY; + if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkDestY)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = 0; + ++_walkDirXIncr; + ++_walkNodesCount; + } else + return false; + } + + while (_walkDirYIncr < _walkDeltaY) { + _walkNodes[_walkNodesCount]._gridX1 = _walkDestX; + _walkNodes[_walkNodesCount]._gridY1 = gridY + _walkDirY * _walkDirYIncr; + if (!_vm->isPointBlocked(_walkDestX, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = 0; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirYIncr; + ++_walkNodesCount; + } else + return false; + } + + return true; +} + +bool PlayerGnap::findPath3(int gridX, int gridY) { + int gridIncr = 1; + bool done = false; + + while (!done && gridIncr < _vm->_gridMaxX) { + if (!_vm->isPointBlocked(gridX + gridIncr, gridY) && findPath1(gridX + gridIncr, gridY, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = gridX + i; + _walkNodes[i]._gridY1 = gridY; + _walkNodes[i]._deltaX = 1; + _walkNodes[i]._deltaY = 0; + } + done = true; + break; + } + if (!_vm->isPointBlocked(gridX - gridIncr, gridY) && findPath1(gridX - gridIncr, gridY, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = gridX - i; + _walkNodes[i]._gridY1 = gridY; + _walkNodes[i]._deltaX = -1; + _walkNodes[i]._deltaY = 0; + } + done = true; + break; + } + if (!_vm->isPointBlocked(gridX, gridY + gridIncr) && findPath1(gridX, gridY + gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = gridX; + _walkNodes[i]._gridY1 = gridY + i; + _walkNodes[i]._deltaX = 0; + _walkNodes[i]._deltaY = 1; + } + done = true; + break; + } + if (!_vm->isPointBlocked(gridX, gridY - gridIncr) && findPath1(gridX, gridY - gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = gridX; + _walkNodes[i]._gridY1 = gridY - i; + _walkNodes[i]._deltaX = 0; + _walkNodes[i]._deltaY = -1; + } + done = true; + break; + } + if (!_vm->isPointBlocked(gridX + gridIncr, gridY + gridIncr) && findPath1(gridX + gridIncr, gridY + gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = gridX + i; + _walkNodes[i]._gridY1 = gridY + i; + _walkNodes[i]._deltaX = 1; + _walkNodes[i]._deltaY = 1; + } + done = true; + break; + } + if (!_vm->isPointBlocked(gridX - gridIncr, gridY + gridIncr) && findPath1(gridX - gridIncr, gridY + gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = gridX - i; + _walkNodes[i]._gridY1 = gridY + i; + _walkNodes[i]._deltaX = -1; + _walkNodes[i]._deltaY = 1; + } + done = true; + break; + } + if (!_vm->isPointBlocked(gridX + gridIncr, gridY - gridIncr) && findPath1(gridX + gridIncr, gridY - gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = gridX + i; + _walkNodes[i]._gridY1 = gridY - i; + _walkNodes[i]._deltaX = 1; + _walkNodes[i]._deltaY = -1; + } + done = true; + break; + } + if (!_vm->isPointBlocked(gridX - gridIncr, gridY - gridIncr) && findPath1(gridX - gridIncr, gridY - gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = gridX - i; + _walkNodes[i]._gridY1 = gridY - i; + _walkNodes[i]._deltaX = -1; + _walkNodes[i]._deltaY = -1; + } + done = true; + break; + } + if (!_vm->isPointBlocked(gridX + gridIncr, gridY) && findPath2(gridX + gridIncr, gridY, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = gridX + i; + _walkNodes[i]._gridY1 = gridY; + _walkNodes[i]._deltaX = 1; + _walkNodes[i]._deltaY = 0; + } + done = true; + break; + } + if (!_vm->isPointBlocked(gridX - gridIncr, gridY) && findPath2(gridX - gridIncr, gridY, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = gridX - i; + _walkNodes[i]._gridY1 = gridY; + _walkNodes[i]._deltaX = -1; + _walkNodes[i]._deltaY = 0; + } + done = true; + break; + } + if (!_vm->isPointBlocked(gridX, gridY + gridIncr) && findPath2(gridX, gridY + gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = gridX; + _walkNodes[i]._gridY1 = gridY + i; + _walkNodes[i]._deltaX = 0; + _walkNodes[i]._deltaY = 1; + } + done = true; + break; + } + if (!_vm->isPointBlocked(gridX, gridY - gridIncr) && findPath2(gridX, gridY - gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = gridX; + _walkNodes[i]._gridY1 = gridY - i; + _walkNodes[i]._deltaX = 0; + _walkNodes[i]._deltaY = -1; + } + done = true; + break; + } + if (!_vm->isPointBlocked(gridX + gridIncr, gridY + gridIncr) && findPath2(gridX + gridIncr, gridY + gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = gridX + i; + _walkNodes[i]._gridY1 = gridY + i; + _walkNodes[i]._deltaX = 1; + _walkNodes[i]._deltaY = 1; + } + done = true; + break; + } + if (!_vm->isPointBlocked(gridX - gridIncr, gridY + gridIncr) && findPath2(gridX - gridIncr, gridY + gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = gridX - i; + _walkNodes[i]._gridY1 = gridY + i; + _walkNodes[i]._deltaX = -1; + _walkNodes[i]._deltaY = 1; + } + done = true; + break; + } + if (!_vm->isPointBlocked(gridX + gridIncr, gridY - gridIncr) && findPath2(gridX + gridIncr, gridY - gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = gridX + i; + _walkNodes[i]._gridY1 = gridY - i; + _walkNodes[i]._deltaX = 1; + _walkNodes[i]._deltaY = -1; + } + done = true; + break; + } + if (!_vm->isPointBlocked(gridX - gridIncr, gridY - gridIncr) && findPath2(gridX - gridIncr, gridY - gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = gridX - i; + _walkNodes[i]._gridY1 = gridY - i; + _walkNodes[i]._deltaX = -1; + _walkNodes[i]._deltaY = -1; + } + done = true; + break; + } + ++gridIncr; + } + + return done; +} + +bool PlayerGnap::findPath4(int gridX, int gridY) { + bool result = false; + + _walkNodesCount = 0; + _walkDirXIncr = 0; + _walkDirYIncr = 0; + _walkDeltaX = ABS(_walkDestX - gridX); + _walkDeltaY = ABS(_walkDestY - gridY); + + if (_walkDeltaX) + _walkDirX = (_walkDestX - gridX) / _walkDeltaX; + else + _walkDirX = 0; + + if (_walkDeltaY) + _walkDirY = (_walkDestY - gridY) / _walkDeltaY; + else + _walkDirY = 0; + + while (_walkDirXIncr < _walkDeltaX && _walkDirYIncr < _walkDeltaY) { + _walkNodes[_walkNodesCount]._gridX1 = gridX + _walkDirX * _walkDirXIncr; + _walkNodes[_walkNodesCount]._gridY1 = gridY + _walkDirY * _walkDirYIncr; + if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirXIncr; + ++_walkDirYIncr; + } else if (_walkDeltaY - _walkDirYIncr > _walkDeltaX - _walkDirXIncr) { + if (!_vm->isPointBlocked(_walkNodes[_walkNodesCount]._gridX1, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = 0; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirYIncr; + } else if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = 0; + ++_walkDirXIncr; + } else { + _walkDeltaX = _walkDirXIncr; + _walkDeltaY = _walkDirYIncr; + --_walkNodesCount; + } + } else { + if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = 0; + ++_walkDirXIncr; + } else if (!_vm->isPointBlocked(_walkNodes[_walkNodesCount]._gridX1, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = 0; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirYIncr; + } else { + _walkDeltaX = _walkDirXIncr; + _walkDeltaY = _walkDirYIncr; + --_walkNodesCount; + } + } + ++_walkNodesCount; + } + + while (_walkDirXIncr < _walkDeltaX) { + _walkNodes[_walkNodesCount]._gridX1 = gridX + _walkDirX * _walkDirXIncr; + _walkNodes[_walkNodesCount]._gridY1 = _walkDestY; + if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkDestY)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = 0; + ++_walkDirXIncr; + ++_walkNodesCount; + } else { + _walkDeltaX = _walkDirXIncr; + } + } + + while (_walkDirYIncr < _walkDeltaY) { + _walkNodes[_walkNodesCount]._gridX1 = _walkDestX; + _walkNodes[_walkNodesCount]._gridY1 = gridY + _walkDirY * _walkDirYIncr; + if (!_vm->isPointBlocked(_walkDestX, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = 0; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirYIncr; + ++_walkNodesCount; + } else { + _walkDeltaY = _walkDirYIncr; + } + } + + if (gridX + _walkDirX * _walkDirXIncr != _walkDestX || gridY + _walkDirY * _walkDirYIncr != _walkDestY) { + _walkDestX = gridX + _walkDirX * _walkDirXIncr; + _walkDestY = gridY + _walkDirY * _walkDirYIncr; + result = false; + } else { + result = true; + } + + return result; +} + +/******************************************************************************/ + +bool PlayerPlat::findPath1(int gridX, int gridY, int index) { + _walkNodesCount = index; + _walkDirXIncr = 0; + _walkDirYIncr = 0; + _walkDeltaX = ABS(_walkDestX - gridX); + _walkDeltaY = ABS(_walkDestY - gridY); + + if (_walkDeltaX) + _walkDirX = (_walkDestX - gridX) / _walkDeltaX; + else + _walkDirX = 0; + + if (_walkDeltaY) + _walkDirY = (_walkDestY - gridY) / _walkDeltaY; + else + _walkDirY = 0; + + while (_walkDirXIncr < _walkDeltaX && _walkDirYIncr < _walkDeltaY) { + _walkNodes[_walkNodesCount]._gridX1 = gridX + _walkDirX * _walkDirXIncr; + _walkNodes[_walkNodesCount]._gridY1 = gridY + _walkDirY * _walkDirYIncr; + if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirXIncr; + ++_walkDirYIncr; + } else if (_walkDeltaY - _walkDirYIncr > _walkDeltaX - _walkDirXIncr) { + if (!_vm->isPointBlocked(_walkNodes[_walkNodesCount]._gridX1, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = 0; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirYIncr; + } else if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = 0; + ++_walkDirXIncr; + } else + return false; + } else { + if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = 0; + ++_walkDirXIncr; + } else if (!_vm->isPointBlocked(_walkNodes[_walkNodesCount]._gridX1, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = 0; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirYIncr; + } else + return false; + } + ++_walkNodesCount; + } + + while (_walkDirXIncr < _walkDeltaX) { + _walkNodes[_walkNodesCount]._gridX1 = gridX + _walkDirX * _walkDirXIncr; + _walkNodes[_walkNodesCount]._gridY1 = _walkDestY; + if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkDestY)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = 0; + ++_walkDirXIncr; + ++_walkNodesCount; + } else + return false; + } + + while (_walkDirYIncr < _walkDeltaY) { + _walkNodes[_walkNodesCount]._gridX1 = _walkDestX; + _walkNodes[_walkNodesCount]._gridY1 = gridY + _walkDirY * _walkDirYIncr; + if (!_vm->isPointBlocked(_walkDestX, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = 0; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirYIncr; + ++_walkNodesCount; + } else + return false; + } + + return true; +} + +bool PlayerPlat::findPath2(int gridX, int gridY, int index) { + _walkNodesCount = index; + _walkDirXIncr = 0; + _walkDirYIncr = 0; + _walkDeltaX = ABS(_walkDestX - gridX); + _walkDeltaY = ABS(_walkDestY - gridY); + + if (_walkDeltaX) + _walkDirX = (_walkDestX - gridX) / _walkDeltaX; + else + _walkDirX = 0; + + if (_walkDeltaY) + _walkDirY = (_walkDestY - gridY) / _walkDeltaY; + else + _walkDirY = 0; + + while (_walkDeltaY < _walkDeltaX - _walkDirXIncr) { + _walkNodes[_walkNodesCount]._gridX1 = gridX + _walkDirX * _walkDirXIncr; + _walkNodes[_walkNodesCount]._gridY1 = gridY; + if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, gridY)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = 0; + ++_walkDirXIncr; + ++_walkNodesCount; + } else + return false; + } + + while (_walkDeltaX < _walkDeltaY - _walkDirYIncr) { + _walkNodes[_walkNodesCount]._gridX1 = gridX; + _walkNodes[_walkNodesCount]._gridY1 = gridY + _walkDirY * _walkDirYIncr; + if (!_vm->isPointBlocked(gridX, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = 0; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirYIncr; + ++_walkNodesCount; + } else + return false; + } + + while (_walkDirXIncr < _walkDeltaX && _walkDirYIncr < _walkDeltaY) { + _walkNodes[_walkNodesCount]._gridX1 = gridX + _walkDirX * _walkDirXIncr; + _walkNodes[_walkNodesCount]._gridY1 = gridY + _walkDirY * _walkDirYIncr; + if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirXIncr; + ++_walkDirYIncr; + } else if (_walkDeltaY - _walkDirYIncr > _walkDeltaX - _walkDirXIncr) { + if (!_vm->isPointBlocked(_walkNodes[_walkNodesCount]._gridX1, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = 0; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirYIncr; + } else if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = 0; + ++_walkDirXIncr; + } else + return false; + } else { + if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = 0; + ++_walkDirXIncr; + } else if (!_vm->isPointBlocked(_walkNodes[_walkNodesCount]._gridX1, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = 0; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirYIncr; + } else + return false; + } + ++_walkNodesCount; + } + + while (_walkDirXIncr < _walkDeltaX) { + _walkNodes[_walkNodesCount]._gridX1 = gridX + _walkDirX * _walkDirXIncr; + _walkNodes[_walkNodesCount]._gridY1 = _walkDestY; + if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkDestY)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = 0; + ++_walkDirXIncr; + ++_walkNodesCount; + } else + return false; + } + + while (_walkDirYIncr < _walkDeltaY) { + _walkNodes[_walkNodesCount]._gridX1 = _walkDestX; + _walkNodes[_walkNodesCount]._gridY1 = gridY + _walkDirY * _walkDirYIncr; + if (!_vm->isPointBlocked(_walkDestX, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = 0; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirYIncr; + ++_walkNodesCount; + } else + return false; + } + + return true; +} + +bool PlayerPlat::findPath3(int gridX, int gridY) { + int gridIncr = 1; + bool done = false; + + while (!done && gridIncr < _vm->_gridMaxX) { + if (!_vm->isPointBlocked(_pos.x + gridIncr, _pos.y) && findPath1(_pos.x + gridIncr, _pos.y, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = _pos.x + i; + _walkNodes[i]._gridY1 = _pos.y; + _walkNodes[i]._deltaX = 1; + _walkNodes[i]._deltaY = 0; + } + done = true; + break; + } + if (!_vm->isPointBlocked(_pos.x - gridIncr, _pos.y) && findPath1(_pos.x - gridIncr, _pos.y, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = _pos.x - i; + _walkNodes[i]._gridY1 = _pos.y; + _walkNodes[i]._deltaX = -1; + _walkNodes[i]._deltaY = 0; + } + done = true; + break; + } + if (!_vm->isPointBlocked(_pos.x, _pos.y + gridIncr) && findPath1(_pos.x, _pos.y + gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = _pos.x; + _walkNodes[i]._gridY1 = _pos.y + i; + _walkNodes[i]._deltaX = 0; + _walkNodes[i]._deltaY = 1; + } + done = true; + break; + } + if (!_vm->isPointBlocked(_pos.x, _pos.y - gridIncr) && findPath1(_pos.x, _pos.y - gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = _pos.x; + _walkNodes[i]._gridY1 = _pos.y - i; + _walkNodes[i]._deltaX = 0; + _walkNodes[i]._deltaY = -1; + } + done = true; + break; + } + if (!_vm->isPointBlocked(_pos.x + gridIncr, _pos.y + gridIncr) && findPath1(_pos.x + gridIncr, _pos.y + gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = _pos.x + i; + _walkNodes[i]._gridY1 = _pos.y + i; + _walkNodes[i]._deltaX = 1; + _walkNodes[i]._deltaY = 1; + } + done = true; + break; + } + if (!_vm->isPointBlocked(_pos.x - gridIncr, _pos.y + gridIncr) && findPath1(_pos.x - gridIncr, _pos.y + gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = _pos.x - i; + _walkNodes[i]._gridY1 = _pos.y + i; + _walkNodes[i]._deltaX = -1; + _walkNodes[i]._deltaY = 1; + } + done = true; + break; + } + if (!_vm->isPointBlocked(_pos.x + gridIncr, _pos.y - gridIncr) && findPath1(_pos.x + gridIncr, _pos.y - gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = _pos.x + i; + _walkNodes[i]._gridY1 = _pos.y - i; + _walkNodes[i]._deltaX = 1; + _walkNodes[i]._deltaY = -1; + } + done = true; + break; + } + if (!_vm->isPointBlocked(_pos.x - gridIncr, _pos.y - gridIncr) && findPath1(_pos.x - gridIncr, _pos.y - gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = _pos.x - i; + _walkNodes[i]._gridY1 = _pos.y - i; + _walkNodes[i]._deltaX = -1; + _walkNodes[i]._deltaY = -1; + } + done = true; + break; + } + if (!_vm->isPointBlocked(_pos.x + gridIncr, _pos.y) && findPath2(_pos.x + gridIncr, _pos.y, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = _pos.x + i; + _walkNodes[i]._gridY1 = _pos.y; + _walkNodes[i]._deltaX = 1; + _walkNodes[i]._deltaY = 0; + } + done = true; + break; + } + if (!_vm->isPointBlocked(_pos.x - gridIncr, _pos.y) && findPath2(_pos.x - gridIncr, _pos.y, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = _pos.x - i; + _walkNodes[i]._gridY1 = _pos.y; + _walkNodes[i]._deltaX = -1; + _walkNodes[i]._deltaY = 0; + } + done = true; + break; + } + if (!_vm->isPointBlocked(_pos.x, _pos.y + gridIncr) && findPath2(_pos.x, _pos.y + gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = _pos.x; + _walkNodes[i]._gridY1 = _pos.y + i; + _walkNodes[i]._deltaX = 0; + _walkNodes[i]._deltaY = 1; + } + done = true; + break; + } + if (!_vm->isPointBlocked(_pos.x, _pos.y - gridIncr) && findPath2(_pos.x, _pos.y - gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = _pos.x; + _walkNodes[i]._gridY1 = _pos.y - i; + _walkNodes[i]._deltaX = 0; + _walkNodes[i]._deltaY = -1; + } + done = true; + break; + } + if (!_vm->isPointBlocked(_pos.x + gridIncr, _pos.y + gridIncr) && findPath2(_pos.x + gridIncr, _pos.y + gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = _pos.x + i; + _walkNodes[i]._gridY1 = _pos.y + i; + _walkNodes[i]._deltaX = 1; + _walkNodes[i]._deltaY = 1; + } + done = true; + break; + } + if (!_vm->isPointBlocked(_pos.x - gridIncr, _pos.y + gridIncr) && findPath2(_pos.x - gridIncr, _pos.y + gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = _pos.x - i; + _walkNodes[i]._gridY1 = _pos.y + i; + _walkNodes[i]._deltaX = -1; + _walkNodes[i]._deltaY = 1; + } + done = true; + break; + } + if (!_vm->isPointBlocked(_pos.x + gridIncr, _pos.y - gridIncr) && findPath2(_pos.x + gridIncr, _pos.y - gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = _pos.x + i; + _walkNodes[i]._gridY1 = _pos.y - i; + _walkNodes[i]._deltaX = 1; + _walkNodes[i]._deltaY = -1; + } + done = true; + break; + } + if (!_vm->isPointBlocked(_pos.x - gridIncr, _pos.y - gridIncr) && findPath2(_pos.x - gridIncr, _pos.y - gridIncr, gridIncr)) { + for (int i = 0; i < gridIncr; ++i) { + _walkNodes[i]._gridX1 = _pos.x - i; + _walkNodes[i]._gridY1 = _pos.y - i; + _walkNodes[i]._deltaX = -1; + _walkNodes[i]._deltaY = -1; + } + done = true; + break; + } + ++gridIncr; + } + + return done; +} + +bool PlayerPlat::findPath4(int gridX, int gridY) { + bool result = false; + + _walkNodesCount = 0; + _walkDirXIncr = 0; + _walkDirYIncr = 0; + _walkDeltaX = ABS(_walkDestX - gridX); + _walkDeltaY = ABS(_walkDestY - gridY); + + if (_walkDeltaX) + _walkDirX = (_walkDestX - gridX) / _walkDeltaX; + else + _walkDirX = 0; + + if (_walkDeltaY) + _walkDirY = (_walkDestY - gridY) / _walkDeltaY; + else + _walkDirY = 0; + + while (_walkDirXIncr < _walkDeltaX && _walkDirYIncr < _walkDeltaY) { + _walkNodes[_walkNodesCount]._gridX1 = gridX + _walkDirX * _walkDirXIncr; + _walkNodes[_walkNodesCount]._gridY1 = gridY + _walkDirY * _walkDirYIncr; + if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirXIncr; + ++_walkDirYIncr; + } else if (_walkDeltaY - _walkDirYIncr > _walkDeltaX - _walkDirXIncr) { + if (!_vm->isPointBlocked(_walkNodes[_walkNodesCount]._gridX1, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = 0; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirYIncr; + } else if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = 0; + ++_walkDirXIncr; + } else { + _walkDeltaX = _walkDirXIncr; + _walkDeltaY = _walkDirYIncr; + --_walkNodesCount; + } + } else { + if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = 0; + ++_walkDirXIncr; + } else if (!_vm->isPointBlocked(_walkNodes[_walkNodesCount]._gridX1, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = 0; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirYIncr; + } else { + _walkDeltaX = _walkDirXIncr; + _walkDeltaY = _walkDirYIncr; + --_walkNodesCount; + } + } + ++_walkNodesCount; + } + + while (_walkDirXIncr < _walkDeltaX) { + _walkNodes[_walkNodesCount]._gridX1 = gridX + _walkDirX * _walkDirXIncr; + _walkNodes[_walkNodesCount]._gridY1 = _walkDestY; + if (!_vm->isPointBlocked(_walkDirX + _walkNodes[_walkNodesCount]._gridX1, _walkDestY)) { + _walkNodes[_walkNodesCount]._deltaX = _walkDirX; + _walkNodes[_walkNodesCount]._deltaY = 0; + ++_walkDirXIncr; + ++_walkNodesCount; + } else { + _walkDeltaX = _walkDirXIncr; + } + } + + while (_walkDirYIncr < _walkDeltaY) { + _walkNodes[_walkNodesCount]._gridX1 = _walkDestX; + _walkNodes[_walkNodesCount]._gridY1 = gridY + _walkDirY * _walkDirYIncr; + if (!_vm->isPointBlocked(_walkDestX, _walkDirY + _walkNodes[_walkNodesCount]._gridY1)) { + _walkNodes[_walkNodesCount]._deltaX = 0; + _walkNodes[_walkNodesCount]._deltaY = _walkDirY; + ++_walkDirYIncr; + ++_walkNodesCount; + } else { + _walkDeltaY = _walkDirYIncr; + } + } + + if (gridX + _walkDirX * _walkDirXIncr != _walkDestX || gridY + _walkDirY * _walkDirYIncr != _walkDestY) { + _walkDestX = gridX + _walkDirX * _walkDirXIncr; + _walkDestY = gridY + _walkDirY * _walkDirYIncr; + result = false; + } else { + result = true; + } + + return result; +} + +void PlayerPlat::makeRoom() { + int rndGridX, rndGridY; + do { + rndGridY = _vm->getRandom(_vm->_gridMaxY); + rndGridX = _vm->getRandom(_vm->_gridMaxX); + } while (ABS(rndGridX - _pos.x) > 4 || ABS(rndGridY - _pos.y) > 3 || + _vm->isPointBlocked(rndGridX, rndGridY)); + walkTo(Common::Point(rndGridX, rndGridY), -1, -1, 1); +} + +} // End of namespace Gnap diff --git a/engines/gnap/menu.cpp b/engines/gnap/menu.cpp new file mode 100644 index 0000000000..2bfe7300df --- /dev/null +++ b/engines/gnap/menu.cpp @@ -0,0 +1,888 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/config-manager.h" +#include "common/savefile.h" +#include "common/translation.h" + +#include "gui/saveload.h" +#include "graphics/thumbnail.h" + +#include "gnap/gnap.h" +#include "gnap/datarchive.h" +#include "gnap/gamesys.h" +#include "gnap/resource.h" + +namespace Gnap { + +void GnapEngine::createMenuSprite() { + _menuBackgroundSurface = _gameSys->createSurface(0x10002); +} + +void GnapEngine::freeMenuSprite() { + _gameSys->removeSpriteDrawItem(_menuBackgroundSurface, 260); + delayTicksCursor(5); + deleteSurface(&_menuBackgroundSurface); +} + +void GnapEngine::initMenuHotspots1() { + int curId = 0; + + for (int i = 0; i < 3; ++i) { + int top = 74 * i + 69; + for (int j = 0; j < 3; ++j) { + int left = 87 * j + 262; + _hotspots[curId]._rect = Common::Rect(left, top, left + 79, top + 66); + _hotspots[curId]._flags = SF_NONE; + ++curId; + } + } + + _hotspots[curId]._rect = Common::Rect(330, 350, 430, 460); + _hotspots[curId]._flags = SF_GRAB_CURSOR; + + ++curId; + _hotspots[curId]._rect = Common::Rect(180, 15, 620, 580); + _hotspots[curId]._flags = SF_NONE; + + ++curId; + _hotspots[curId]._rect = Common::Rect(0, 0, 799, 599); + _hotspots[curId]._flags = SF_NONE; + + _hotspotsCount = curId + 1; +} + +void GnapEngine::initMenuHotspots2() { + int curId = 0; + + for (int i = 0; i < 4; ++i) { + int top = 48 * i + 85; + _hotspots[curId]._rect = Common::Rect(312, top, 465, top + 37); + _hotspots[curId]._flags = SF_GRAB_CURSOR; + ++curId; + } + + _hotspots[curId]._rect = Common::Rect(500, 72, 527, 99); + _hotspots[curId]._flags = SF_DISABLED; + + ++curId; + _hotspots[curId]._rect = Common::Rect(330, 350, 430, 460); + _hotspots[curId]._flags = SF_GRAB_CURSOR; + + ++curId; + _hotspots[curId]._rect = Common::Rect(180, 15, 620, 580); + _hotspots[curId]._flags = SF_NONE; + + ++curId; + _hotspots[curId]._rect = Common::Rect(0, 0, 799, 599); + _hotspots[curId]._flags = SF_NONE; + + _hotspotsCount = curId + 1; +} + +void GnapEngine::initMenuQuitQueryHotspots() { + _hotspots[0]._rect = Common::Rect(311, 197, 377, 237); + _hotspots[0]._flags = SF_GRAB_CURSOR; + + _hotspots[1]._rect = Common::Rect(403, 197, 469, 237); + _hotspots[1]._flags = SF_GRAB_CURSOR; + + _hotspots[2]._rect = Common::Rect(330, 350, 430, 460); + _hotspots[2]._flags = SF_GRAB_CURSOR; + + _hotspots[3]._rect = Common::Rect(180, 15, 620, 580); + _hotspots[3]._flags = SF_NONE; + + _hotspots[4]._rect = Common::Rect(0, 0, 799, 599); + _hotspots[4]._flags = SF_NONE; + + _hotspotsCount = 5; +} + +void GnapEngine::initSaveLoadHotspots() { + int curId = 0; + + for (int i = 0; i < 7; ++i ) { + int top = 31 * i + 74; + _hotspots[curId]._rect = Common::Rect(288, top, 379, top + 22); + _hotspots[curId]._flags = SF_GRAB_CURSOR; + ++curId; + } + + if (_menuStatus == 2) { + _hotspots[curId]._rect = Common::Rect(416, 160, 499, 188); + _hotspots[curId]._flags = SF_GRAB_CURSOR; + ++curId; + } + + _hotspots[curId]._rect = Common::Rect(416, 213, 499, 241); + _hotspots[curId]._flags = SF_GRAB_CURSOR; + + ++curId; + _hotspots[curId]._rect = Common::Rect(330, 350, 430, 460); + _hotspots[curId]._flags = SF_GRAB_CURSOR; + + ++curId; + _hotspots[curId]._rect = Common::Rect(180, 15, 620, 580); + _hotspots[curId]._flags = SF_NONE; + + ++curId; + _hotspots[curId]._rect = Common::Rect(0, 0, 799, 599); + _hotspots[curId]._flags = SF_NONE; + + _hotspotsCount = curId + 1; +} + +void GnapEngine::drawInventoryFrames() { + for (int i = 0; i < 9; ++i) + _gameSys->drawSpriteToSurface(_menuBackgroundSurface, _hotspots[i]._rect.left - 93, _hotspots[i]._rect.top, 0x10001); +} + +void GnapEngine::insertInventorySprites() { + for (int i = 0; i < 9; ++i) { + _menuInventoryIndices[i] = -1; + _gameSys->removeSpriteDrawItem(_menuInventorySprites[_sceneClickedHotspot], 261); + _menuInventorySprites[i] = 0; + } + + _menuSpritesIndex = 0; + + for (int index = 0; index < 30 && _menuSpritesIndex < 9; ++index) { + if (invHas(index)) { + _gameSys->drawSpriteToSurface(_menuBackgroundSurface, + _hotspots[_menuSpritesIndex]._rect.left - 93, _hotspots[_menuSpritesIndex]._rect.top, 0x10000); + _menuInventorySprites[_menuSpritesIndex] = _gameSys->createSurface(getInventoryItemSpriteNum(index) | 0x10000); + if (index != _grabCursorSpriteIndex) { + _menuInventoryIndices[_menuSpritesIndex] = index; + _gameSys->insertSpriteDrawItem(_menuInventorySprites[_menuSpritesIndex], + _hotspots[_menuSpritesIndex]._rect.left + ((79 - _menuInventorySprites[_menuSpritesIndex]->w) / 2), + _hotspots[_menuSpritesIndex]._rect.top + ((66 - _menuInventorySprites[_menuSpritesIndex]->h) / 2), + 261); + } + _hotspots[_menuSpritesIndex]._flags = SF_GRAB_CURSOR; + ++_menuSpritesIndex; + } + } +} + +void GnapEngine::removeInventorySprites() { + for (int i = 0; i < _menuSpritesIndex; ++i) + if (_menuInventorySprites[i]) + _gameSys->removeSpriteDrawItem(_menuInventorySprites[i], 261); + delayTicksCursor(5); + for (int j = 0; j < _menuSpritesIndex; ++j) { + if (_menuInventorySprites[j]) { + deleteSurface(&_menuInventorySprites[j]); + _menuInventorySprites[j] = 0; + _menuInventoryIndices[j] = -1; + } + } + _menuSpritesIndex = 0; +} + +void GnapEngine::runMenu() { + _spriteHandle = nullptr; + _cursorSprite = nullptr; + _menuSprite1 = nullptr; + _menuSprite2 = nullptr; + _menuSaveLoadSprite = nullptr; + _menuQuitQuerySprite = nullptr; + + _menuStatus = 0; + _menuDone = false; + + delete _tempThumbnail; + _tempThumbnail = new Common::MemoryWriteStreamDynamic; + Graphics::saveThumbnail(*_tempThumbnail); + + createMenuSprite(); + insertDeviceIconActive(); + + for (int i = 0; i < 7; ++i) { + _savegameFilenames[i][0] = 0; + _savegameSprites[i] = nullptr; + } + + if (_menuStatus == 0) { + invAdd(kItemMagazine); + setGrabCursorSprite(-1); + hideCursor(); + initMenuHotspots1(); + drawInventoryFrames(); + insertInventorySprites(); + _gameSys->insertSpriteDrawItem(_menuBackgroundSurface, 93, 0, 260); + showCursor(); + // SetCursorPos(400, 300); + setVerbCursor(GRAB_CURSOR); + // pollMessages(); + } + + _timers[2] = 10; + + while (!isKeyStatus1(Common::KEYCODE_BACKSPACE) && !isKeyStatus1(Common::KEYCODE_ESCAPE) && !_sceneDone && !_menuDone) { + updateCursorByHotspot(); + + switch (_menuStatus) { + case 0: + updateMenuStatusInventory(); + break; + case 1: + updateMenuStatusMainMenu(); + break; + case 2: + updateMenuStatusSaveGame(); + break; + case 3: + updateMenuStatusLoadGame(); + break; + case 4: + updateMenuStatusQueryQuit(); + break; + } + + gameUpdateTick(); + } + + removeInventorySprites(); + if (_spriteHandle) + _gameSys->removeSpriteDrawItem(_spriteHandle, 261); + if (_menuSprite1) + _gameSys->removeSpriteDrawItem(_menuSprite1, 262); + if (_menuSprite2) + _gameSys->removeSpriteDrawItem(_menuSprite2, 262); + for (int i = 0; i < 7; ++i) + if (_savegameSprites[i]) + _gameSys->removeSpriteDrawItem(_savegameSprites[i], 263); + if (_cursorSprite) + _gameSys->removeSpriteDrawItem(_cursorSprite, 264); + if (_menuSaveLoadSprite) + _gameSys->removeSpriteDrawItem(_menuSaveLoadSprite, 262); + if (_menuQuitQuerySprite) + _gameSys->removeSpriteDrawItem(_menuQuitQuerySprite, 262); + if (_menuBackgroundSurface) + _gameSys->removeSpriteDrawItem(_menuBackgroundSurface, 260); + + delayTicksCursor(5); + + deleteSurface(&_spriteHandle); + deleteSurface(&_menuSprite1); + deleteSurface(&_menuSprite2); + for (int i = 0; i < 7; ++i) + deleteSurface(&_savegameSprites[i]); + deleteSurface(&_cursorSprite); + deleteSurface(&_menuSaveLoadSprite); + deleteSurface(&_menuQuitQuerySprite); + + _sceneClickedHotspot = -1; + + _timers[2] = getRandom(20) + 30; + _timers[3] = getRandom(200) + 50; + _timers[0] = getRandom(75) + 75; + _timers[1] = getRandom(20) + 30; + + clearAllKeyStatus1(); + + _mouseClickState._left = false; + + removeDeviceIconActive(); + + freeMenuSprite();//??? CHECKME +} + +void GnapEngine::updateMenuStatusInventory() { + static const struct { + int item1, item2, resultItem; + } kCombineItems[] = { + {kItemGrass, kItemMud, kItemDisguise}, + {kItemDice, kItemQuarterWithHole, kItemDiceQuarterHole}, + {kItemPill, kItemBucketWithBeer, kItemBucketWithPill} + }; + + updateGrabCursorSprite(0, 0); + _hotspots[0]._rect = Common::Rect(262, 69, 341, 135); + _sceneClickedHotspot = -1; + if (_timers[2] == 0) + _sceneClickedHotspot = getClickedHotspotId(); + if (_sceneClickedHotspot == -1 || _sceneClickedHotspot >= _menuSpritesIndex) { + if (_sceneClickedHotspot == _hotspotsCount - 3) { + if (_grabCursorSpriteIndex == -1) { + _timers[2] = 10; + playSound(0x108F4, false); + _menuStatus = 1; + Common::Rect dirtyRect(_hotspots[0]._rect.left, _hotspots[0]._rect.top, _hotspots[2]._rect.right, _hotspots[_hotspotsCount - 4]._rect.bottom); + drawInventoryFrames(); + initMenuHotspots2(); + removeInventorySprites(); + if (!_menuSprite1) + _menuSprite1 = _gameSys->createSurface(0x104F8); + _gameSys->insertSpriteDrawItem(_menuSprite1, 288, 79, 262); + _gameSys->insertDirtyRect(dirtyRect); + } else { + playSound(0x108F5, false); + } + } else if (_sceneClickedHotspot == _hotspotsCount - 1) { + _timers[2] = 10; + playSound(0x108F5, false); + _menuDone = true; + } + } else if (_sceneClickedHotspot != -1 && _menuInventoryIndices[_sceneClickedHotspot] != -1 && _grabCursorSpriteIndex == -1) { + _gameSys->removeSpriteDrawItem(_menuInventorySprites[_sceneClickedHotspot], 261); + setGrabCursorSprite(_menuInventoryIndices[_sceneClickedHotspot]); + _menuInventoryIndices[_sceneClickedHotspot] = -1; + } else if (_sceneClickedHotspot != -1 && _menuInventoryIndices[_sceneClickedHotspot] == -1 && _grabCursorSpriteIndex != -1) { + _menuInventoryIndices[_sceneClickedHotspot] = _grabCursorSpriteIndex; + _gameSys->insertSpriteDrawItem(_menuInventorySprites[_sceneClickedHotspot], + _hotspots[_sceneClickedHotspot]._rect.left + ((79 - _menuInventorySprites[_sceneClickedHotspot]->w) / 2), + _hotspots[_sceneClickedHotspot]._rect.top + (66 - _menuInventorySprites[_sceneClickedHotspot]->h) / 2, + 261); + setGrabCursorSprite(-1); + } else if (_sceneClickedHotspot != -1 && _menuInventoryIndices[_sceneClickedHotspot] != -1 && _grabCursorSpriteIndex != -1) { + int combineIndex = -1; + for (int i = 0; i < ARRAYSIZE(kCombineItems); ++i) { + if ((_grabCursorSpriteIndex == kCombineItems[i].item1 && _menuInventoryIndices[_sceneClickedHotspot] == kCombineItems[i].item2) || + (_grabCursorSpriteIndex == kCombineItems[i].item2 && _menuInventoryIndices[_sceneClickedHotspot] == kCombineItems[i].item1)) { + combineIndex = i; + break; + } + } + if (combineIndex >= 0) { + invRemove(kCombineItems[combineIndex].item1); + invRemove(kCombineItems[combineIndex].item2); + invAdd(kCombineItems[combineIndex].resultItem); + playSound(0x108AE, false); + deleteSurface(&_spriteHandle); // CHECKME + _spriteHandle = _gameSys->createSurface(0x10001); + _gameSys->insertSpriteDrawItem(_spriteHandle, _hotspots[_menuSpritesIndex - 1]._rect.left, _hotspots[_menuSpritesIndex - 1]._rect.top, 261); + setGrabCursorSprite(kCombineItems[combineIndex].resultItem); + removeInventorySprites(); + insertInventorySprites(); + delayTicksCursor(5); + } else { + playSound(0x108F5, false); + } + } +} + +void GnapEngine::updateMenuStatusMainMenu() { + _hotspots[0]._rect = Common::Rect(312, 85, 465, 122); + _sceneClickedHotspot = -1; + if (!_timers[2]) + _sceneClickedHotspot = getClickedHotspotId(); + + if (_sceneClickedHotspot != 1 && _sceneClickedHotspot != 0) { + if (_sceneClickedHotspot != 2 && _hotspotsCount - 1 != _sceneClickedHotspot) { + if (_sceneClickedHotspot == 3) { + // Quit + _timers[2] = 10; + playSound(0x108F4, false); + _gameSys->removeSpriteDrawItem(_menuSprite1, 262); + initMenuQuitQueryHotspots(); + _menuStatus = 4; + if (!_menuQuitQuerySprite) + _menuQuitQuerySprite = _gameSys->createSurface(0x104FC); + _gameSys->insertSpriteDrawItem(_menuQuitQuerySprite, 254, 93, 262); + } else if (_sceneClickedHotspot == 4) { + // Pause ? + playSound(0x108F4, false); + Common::Rect dirtyRect(0, 0, 799, 599); + hideCursor(); + _largeSprite = _gameSys->allocSurface(800, 600); + + for (int i = 0; i < 3; ++i) { + _timers[2] = 10; + + if (i == 0) { + _gameSys->drawSpriteToSurface(_largeSprite, 0, 0, 0x1078D); + _gameSys->insertSpriteDrawItem(_largeSprite, 0, 0, 300); + playMidi("pause.mid"); + } else if (i == 1) { + _gameSys->drawSpriteToSurface(_largeSprite, 0, 0, 0x1078E); + _gameSys->insertDirtyRect(dirtyRect); + } else if (i == 2) { + _gameSys->drawSpriteToSurface(_largeSprite, 0, 0, 0x1078F); + _gameSys->insertDirtyRect(dirtyRect); + } + + while (!_mouseClickState._left && !isKeyStatus1(Common::KEYCODE_ESCAPE) && !isKeyStatus1(Common::KEYCODE_RETURN) + && !isKeyStatus1(Common::KEYCODE_SPACE) && !_timers[2] && !_gameDone) + gameUpdateTick(); + + playSound(0x108F5, false); + _mouseClickState._left = false; + clearKeyStatus1(Common::KEYCODE_ESCAPE); + clearKeyStatus1(Common::KEYCODE_RETURN); + clearKeyStatus1(Common::KEYCODE_SPACE); + } + + _gameSys->removeSpriteDrawItem(_largeSprite, 300); + delayTicksCursor(5); + deleteSurface(&_largeSprite); + showCursor(); + } else if (_hotspotsCount - 3 == _sceneClickedHotspot) { + // Button - Return to the inventory + _timers[2] = 10; + playSound(0x108F4, false); + initMenuHotspots1(); + _menuStatus = 0; + if (_menuSprite1) + _gameSys->removeSpriteDrawItem(_menuSprite1, 262); + insertInventorySprites(); + Common::Rect dirtyRect(_hotspots[0]._rect.left, _hotspots[0]._rect.top, _hotspots[2]._rect.right, _hotspots[_hotspotsCount - 4]._rect.bottom); + _gameSys->insertDirtyRect(dirtyRect); + } + } else { + // Resume + playSound(0x108F5, false); + _menuDone = true; + } + } else { + // Save / Load +#if 1 + _timers[2] = 10; + playSound(0x108F4, false); + + if (_sceneClickedHotspot == 1) { + GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true); + int16 savegameId = dialog->runModalWithCurrentTarget(); + Common::String savegameDescription = dialog->getResultString(); + delete dialog; + + if (savegameId != -1) { + saveGameState(savegameId, savegameDescription); + } + } else { + GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false); + int16 savegameId = dialog->runModalWithCurrentTarget(); + delete dialog; + + if (savegameId != -1) { + loadGameState(savegameId); + _wasSavegameLoaded = true; + _menuDone = true; + _sceneDone = true; + playSound(0x108F4, false); + } else { + playSound(0x108F5, false); + } + } + } +#else + // NOTE: + // This is the code for the original behavior. + // It's currently not working prolery, but could be + // fixed to replace the ScummVM screens currently + // used. + _timers[2] = 10; + playSound(0x108F4, false); + _gameSys->removeSpriteDrawItem(_menuSprite1, 262); + if (_menuSaveLoadSprite) + deleteSurface(&_menuSaveLoadSprite); + if (_sceneClickedHotspot == 1) { + // Save + _menuStatus = 2; + initSaveLoadHotspots(); + _menuSaveLoadSprite = _gameSys->createSurface(0x104FB); + } else { + // Load + _menuStatus = 3; + initSaveLoadHotspots(); + _menuSaveLoadSprite = _gameSys->createSurface(0x104FA); + } + _gameSys->insertSpriteDrawItem(_menuSaveLoadSprite, 403, 72, 262); + if (!_menuSprite2) + _menuSprite2 = _gameSys->createSurface(0x104F9); + _gameSys->insertSpriteDrawItem(_menuSprite2, 277, 66, 262); + for (int i = 0; i < 7; ++i) { + Common::String savegameDescription; + if (!_savegameSprites[i]) + _savegameSprites[i] = _gameSys->allocSurface(111, 40); + if (readSavegameDescription(i + 1, savegameDescription) == 0) + strncpy(_savegameFilenames[i], savegameDescription.c_str(), 40); + _gameSys->drawTextToSurface(_savegameSprites[i], 0, 0, 255, 0, 0, _savegameFilenames[i]); + _gameSys->insertSpriteDrawItem(_savegameSprites[i], 288, _hotspots[i].top, 263); + } + _savegameIndex = -1; + } +#endif +} + +Common::Error GnapEngine::saveGameState(int slot, const Common::String &desc) { + Common::OutSaveFile *out = g_system->getSavefileManager()->openForSaving( + generateSaveName(slot)); + if (!out) + return Common::kCreatingFileFailed; + + GnapSavegameHeader header; + header._saveName = desc; + writeSavegameHeader(out, header); + + Common::Serializer s(nullptr, out); + synchronize(s); + + out->finalize(); + delete out; + + return Common::kNoError; +} + +void GnapEngine::synchronize(Common::Serializer &s) { + if (s.isSaving()) { + s.syncAsSint32LE(_currentSceneNum); + s.syncAsSint32LE(_prevSceneNum); + s.syncAsSint32LE(_cursorValue); + s.syncAsUint32LE(_inventory); + s.syncAsUint32LE(_gameFlags); + } else { + s.syncAsSint32LE(_newSceneNum); + s.syncAsSint32LE(_currentSceneNum); + s.syncAsSint32LE(_newCursorValue); + s.syncAsUint32LE(_inventory); + s.syncAsUint32LE(_gameFlags); + + if (isFlag(kGFUnk24)) + _timers[9] = 600; + } +} + +const char *const SAVEGAME_STR = "GNAP"; +#define SAVEGAME_STR_SIZE 4 +void GnapEngine::writeSavegameHeader(Common::OutSaveFile *out, GnapSavegameHeader &header) { + // Write out a savegame header + out->write(SAVEGAME_STR, SAVEGAME_STR_SIZE + 1); + + out->writeByte(GNAP_SAVEGAME_VERSION); + + // Write savegame name + out->writeString(header._saveName); + out->writeByte('\0'); + + // This implies the menu is used + // If we want to save/load at any time, then a check should be added + out->write(_tempThumbnail->getData(), _tempThumbnail->size()); + + // Write out the save date/time + TimeDate td; + g_system->getTimeAndDate(td); + out->writeSint16LE(td.tm_year + 1900); + out->writeSint16LE(td.tm_mon + 1); + out->writeSint16LE(td.tm_mday); + out->writeSint16LE(td.tm_hour); + out->writeSint16LE(td.tm_min); +} + +bool GnapEngine::readSavegameHeader(Common::InSaveFile *in, GnapSavegameHeader &header) { + char saveIdentBuffer[SAVEGAME_STR_SIZE + 1]; + header._thumbnail = nullptr; + + // Validate the header Id + in->read(saveIdentBuffer, SAVEGAME_STR_SIZE + 1); + if (strncmp(saveIdentBuffer, SAVEGAME_STR, SAVEGAME_STR_SIZE)) + return false; + + header._version = in->readByte(); + if (header._version > GNAP_SAVEGAME_VERSION) + return false; + + // Read in the string + header._saveName.clear(); + char ch; + while ((ch = (char)in->readByte()) != '\0') + header._saveName += ch; + + // Get the thumbnail, saved in v2 or later + if (header._version == 1) + header._thumbnail = nullptr; + else { + header._thumbnail = Graphics::loadThumbnail(*in); + if (!header._thumbnail) + return false; + } + + // Read in save date/time + header._year = in->readSint16LE(); + header._month = in->readSint16LE(); + header._day = in->readSint16LE(); + header._hour = in->readSint16LE(); + header._minute = in->readSint16LE(); + + return true; +} + +Common::Error GnapEngine::loadGameState(int slot) { + Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading( + generateSaveName(slot)); + if (!saveFile) + return Common::kReadingFailed; + + Common::Serializer s(saveFile, nullptr); + + // Load the savegame header + GnapSavegameHeader header; + if (!readSavegameHeader(saveFile, header)) + error("Invalid savegame"); + + if (header._thumbnail) { + header._thumbnail->free(); + delete header._thumbnail; + } + + synchronize(s); + delete saveFile; + + _loadGameSlot = slot; + return Common::kNoError; +} + +Common::String GnapEngine::generateSaveName(int slot) { + return Common::String::format("%s.%03d", _targetName.c_str(), slot); +} + +void GnapEngine::updateMenuStatusSaveGame() { +#if 0 + // NOTE: + // This is the code for the original screen game. + // It could be eventually fixed and could replace + // the ScummVM screens currently used. + + char v43[30]; + int v46; + v43[0] = '\0'; + _hotspots[0]._x1 = 288; + _hotspots[0]._y1 = 74; + _hotspots[0]._x2 = 379; + _hotspots[0]._y2 = 96; + _sceneClickedHotspot = -1; + + if (!_timers[2]) + _sceneClickedHotspot = getClickedHotspotId(); + + if (_hotspotsCount - 3 == _sceneClickedHotspot) { + // Button + _timers[2] = 10; + playSound(0x108F4, false); + _menuStatus = 1; + warning("writeSavegame(_savegameIndex + 1, (int)&_savegameFilenames[30 * _savegameIndex], 1);"); + } else if (_hotspotsCount - 4 == _sceneClickedHotspot) { + // Cancel + _timers[2] = 10; + playSound(0x108F5, false); + _menuStatus = 1; + if (strcmp(v43, _savegameFilenames[_savegameIndex]) && _savegameIndex != -1) { + strcpy(_savegameFilenames[_savegameIndex], v43); + if (_savegameSprites[_savegameIndex] != nullptr) { + _gameSys->removeSpriteDrawItem(_savegameSprites[_savegameIndex], 263); + delayTicksCursor(5); + warning("memFreeHandle(_savegameSprites[_savegameIndex]);"); + } + int v16 = _gameSys->getSpriteWidthById(0x104F9); + warning("_savegameSprites[_savegameIndex] = allocSprite(v16, 40, 128, 0);"); + } + } else if (_hotspotsCount - 5 == _sceneClickedHotspot) { + // OK + _timers[2] = 10; + playSound(0x108F4, false); + if (_savegameIndex != -1) + warning("writeSavegame(_savegameIndex + 1, (int)&_savegameFilenames[30 * _savegameIndex], 1);"); + _menuStatus = 1; + } else if (_hotspotsCount - 1 == _sceneClickedHotspot) { + // in background + _menuDone = true; + } else if (_sceneClickedHotspot != -1 && _hotspotsCount - 2 != _sceneClickedHotspot) { + // Savegame name + _timers[2] = 10; + playSound(0x108F4, false); + if (strcmp(v43, _savegameFilenames[_savegameIndex]) & (_savegameIndex != -1)) { + strcpy(_savegameFilenames[_savegameIndex], v43); + if (_savegameSprites[_savegameIndex] != nullptr) { + _gameSys->removeSpriteDrawItem(_savegameSprites[_savegameIndex], 263); + delayTicksCursor(5); + warning("memFreeHandle(_savegameSprites[_savegameIndex]);"); + } + int v18 = _gameSys->getSpriteWidthById(0x104F9); + _savegameSprites[_savegameIndex] = _gameSys->allocSurface(v18, 40); + _gameSys->drawTextToSurface(_savegameSprites[_savegameIndex], 0, 0, 255, 0, 0, _savegameFilenames[_savegameIndex]); + _gameSys->insertSpriteDrawItem(_savegameSprites[_savegameIndex], 288, _hotspots[_savegameIndex]._y1, 263); + } + _savegameIndex = _sceneClickedHotspot; + v46 = strlen(_savegameFilenames[_sceneClickedHotspot]); + strcpy(v43, _savegameFilenames[_sceneClickedHotspot]); + if (_cursorSprite == nullptr) { + int v19 = _gameSys->getTextHeight("_"); + int v20 = _gameSys->getTextWidth("_"); + _cursorSprite = _gameSys->allocSurface(v20, v19); + _gameSys->drawTextToSurface(_cursorSprite, 0, 0, 255, 0, 0, "_"); + } else { + _gameSys->removeSpriteDrawItem(_cursorSprite, 264); + } + int v21 = _hotspots[_savegameIndex]._x2; + int v22 = v21 - _gameSys->getTextWidth("_"); + if (v22 > _gameSys->getTextWidth(_savegameFilenames[_savegameIndex]) + 288) { + int v25 = _gameSys->getTextWidth(_savegameFilenames[_savegameIndex]) + 288; + _gameSys->insertSpriteDrawItem(_cursorSprite, v25, _hotspots[_savegameIndex]._y1, 264); + } else { + int v23 = _hotspots[_savegameIndex]._x2; + int v24 = v23 - _gameSys->getTextWidth("_"); + _gameSys->insertSpriteDrawItem(_cursorSprite, v24, _hotspots[_savegameIndex]._y1, 264); + } + } + + updateEvents(); + Common::Event event; + _eventMan->pollEvent(event); + + Common::KeyCode keycode = event.kbd.keycode; + if (_savegameIndex != -1 && keycode) { + if ((keycode < Common::KEYCODE_a || keycode > Common::KEYCODE_z) && (keycode < Common::KEYCODE_0 || keycode > Common::KEYCODE_9) && keycode != Common::KEYCODE_SPACE) { + if (keycode == Common::KEYCODE_BACKSPACE) { + if (v46 > 0) + --v46; + _savegameFilenames[_savegameIndex][v46] = '\0'; + if (_savegameSprites[_savegameIndex] != nullptr) { + _gameSys->removeSpriteDrawItem(_savegameSprites[_savegameIndex], 263); + warning("memFreeHandle(_savegameSprites[_savegameIndex]);"); + } + int v32 = _gameSys->getSpriteWidthById(0x104F9); + _savegameSprites[_savegameIndex] = _gameSys->allocSurface(v32, 40); + _gameSys->drawTextToSurface(_savegameSprites[_savegameIndex], 0, 0, 255, 0, 0, _savegameFilenames[_savegameIndex]); + _gameSys->insertSpriteDrawItem(_savegameSprites[_savegameIndex], 288, _hotspots[_savegameIndex]._y1, 263); + _gameSys->removeSpriteDrawItem(_cursorSprite, 264); + int v33 = _hotspots[_savegameIndex]._y1; + int v34 = _gameSys->getTextWidth(_savegameFilenames[_savegameIndex]); + _gameSys->insertSpriteDrawItem(_cursorSprite, _hotspots[_savegameIndex]._x1 + v34, v33, 264); + } else if (keycode == Common::KEYCODE_RETURN) { + _menuStatus = 1; + warning("writeSavegame(_savegameIndex + 1, (int)&_savegameFilenames[30 * _savegameIndex], 1);"); + } + } else { + _savegameFilenames[_savegameIndex][v46] = event.kbd.ascii; + if (v46 < 28) + ++v46; + _savegameFilenames[_savegameIndex][v46] = '\0'; + if (_gameSys->getTextWidth(_savegameFilenames[_savegameIndex]) > 91) { + --v46; + _savegameFilenames[_savegameIndex][v46] = '\0'; + } + _gameSys->drawTextToSurface(_savegameSprites[_savegameIndex], 0, 0, 255, 0, 0, _savegameFilenames[_savegameIndex]); + int v26 = _gameSys->getTextWidth(_savegameFilenames[_savegameIndex]); + Common::Rect rect; + rect.right = _hotspots[_savegameIndex]._x1 + v26; + int v27 = rect.right; + rect.left = v27 - 2 * _gameSys->getTextWidth("W"); + rect.top = _hotspots[_savegameIndex]._y1; + rect.bottom = _hotspots[_savegameIndex]._y2; + _gameSys->insertDirtyRect(rect); + _gameSys->removeSpriteDrawItem(_cursorSprite, 264); + int v28 = _hotspots[_savegameIndex]._x2; + int v29 = _gameSys->getTextWidth("_"); + if (v28 - v29 > rect.right) + _gameSys->insertSpriteDrawItem(_cursorSprite, rect.right, rect.top, 264); + else { + int v30 = _hotspots[_savegameIndex]._x2; + int v31 = v30 - _gameSys->getTextWidth("_"); + _gameSys->insertSpriteDrawItem(_cursorSprite, v31, rect.top, 264); + } + clearKeyStatus1(8); + } + } + +// warning("keybChar = 0;"); + if (_menuStatus == 1 || _menuDone) { + _gameSys->removeSpriteDrawItem(_menuSprite2, 262); + _gameSys->removeSpriteDrawItem(_menuSaveLoadSprite, 262); + for (int i = 0; i < 7; ++i) + _gameSys->removeSpriteDrawItem(_savegameSprites[i], 263); + _gameSys->removeSpriteDrawItem(_cursorSprite, 264); + if (!_menuDone) { + initMenuHotspots2(); + _gameSys->insertSpriteDrawItem(_menuSprite1, 288, 79, 262); + } + } +#endif +} + +void GnapEngine::updateMenuStatusLoadGame() { + _hotspots[0]._rect = Common::Rect(288, 74, 379, 96); + _sceneClickedHotspot = -1; + if (!_timers[2]) + _sceneClickedHotspot = getClickedHotspotId(); + if (_sceneClickedHotspot != -1 && _hotspotsCount - 2 != _sceneClickedHotspot) { + _timers[2] = 10; + if (_hotspotsCount - 4 <= _sceneClickedHotspot) { + playSound(0x108F5, false); + _gameSys->removeSpriteDrawItem(_menuSprite2, 262); + _gameSys->removeSpriteDrawItem(_menuSaveLoadSprite, 262); + for (int i = 0; i < 7; ++i) + _gameSys->removeSpriteDrawItem(_savegameSprites[i], 263); + if (_hotspotsCount - 1 == _sceneClickedHotspot) { + _menuDone = true; + } else { + _menuStatus = 1; + initMenuHotspots2(); + _gameSys->insertSpriteDrawItem(_menuSprite1, 288, 79, 262); + } + } else if (loadSavegame(_sceneClickedHotspot + 1)) { + playSound(0x108F5, false); + } else { + playSound(0x108F4, false); + _sceneDone = true; + } + } +} + +void GnapEngine::updateMenuStatusQueryQuit() { + _hotspots[0]._rect = Common::Rect(311, 197, 377, 237); + _sceneClickedHotspot = -1; + + if (!_timers[2]) + _sceneClickedHotspot = getClickedHotspotId(); + + /* _sceneClickedHotspot + 0 Yes + 1 No + 2 Button + 3 Display + 4 Background + */ + + if (_sceneClickedHotspot == 0) { + // Quit the game + playSound(0x108F5, false); + _gameSys->removeSpriteDrawItem(_menuQuitQuerySprite, 262); + _sceneDone = true; + _gameDone = true; + } else if (_sceneClickedHotspot == 4) { + // Exit the device + playSound(0x108F4, false); + _gameSys->removeSpriteDrawItem(_menuQuitQuerySprite, 262); + _menuDone = true; + } else if (_sceneClickedHotspot != -1) { + // Return to the main menu + playSound(0x108F4, false); + _gameSys->removeSpriteDrawItem(_menuQuitQuerySprite, 262); + _timers[2] = 10; + _menuStatus = 1; + initMenuHotspots2(); + _gameSys->insertSpriteDrawItem(_menuSprite1, 288, 79, 262); + } +} + +} // End of namespace Gnap diff --git a/engines/gnap/module.mk b/engines/gnap/module.mk new file mode 100644 index 0000000000..ab507cbf94 --- /dev/null +++ b/engines/gnap/module.mk @@ -0,0 +1,32 @@ +MODULE := engines/gnap + +MODULE_OBJS := \ + character.o \ + datarchive.o \ + debugger.o \ + detection.o \ + gamesys.o \ + gnap.o \ + grid.o \ + menu.o \ + music.o \ + resource.o \ + sound.o \ + scenes/arcade.o \ + scenes/groupcs.o \ + scenes/group0.o \ + scenes/group1.o \ + scenes/group2.o \ + scenes/group3.o \ + scenes/group4.o \ + scenes/group5.o \ + scenes/intro.o \ + scenes/scenecore.o + +# This module can be built as a plugin +ifeq ($(ENABLE_GNAP), DYNAMIC_PLUGIN) +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk diff --git a/engines/gnap/music.cpp b/engines/gnap/music.cpp new file mode 100644 index 0000000000..af33786a8f --- /dev/null +++ b/engines/gnap/music.cpp @@ -0,0 +1,104 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +// MIDI and digital music class + +#include "audio/mididrv.h" +#include "audio/midiparser.h" +#include "common/debug.h" +#include "common/file.h" + +#include "gnap/music.h" +#include "gnap/gnap.h" + +namespace Gnap { + +MusicPlayer::MusicPlayer(const char *filename) : _filename(filename) { + + MidiPlayer::createDriver(); + + int ret = _driver->open(); + if (ret == 0) { + if (_nativeMT32) + _driver->sendMT32Reset(); + else + _driver->sendGMReset(); + + _driver->setTimerCallback(this, &timerCallback); + } +} + +void MusicPlayer::sendToChannel(byte channel, uint32 b) { + if (!_channelsTable[channel]) { + _channelsTable[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel(); + // If a new channel is allocated during the playback, make sure + // its volume is correctly initialized. + if (_channelsTable[channel]) + _channelsTable[channel]->volume(_channelsVolume[channel] * _masterVolume / 255); + } + + if (_channelsTable[channel]) + _channelsTable[channel]->send(b); +} + +void MusicPlayer::playSMF(bool loop) { + Common::StackLock lock(_mutex); + + stop(); + + // Load MIDI resource data + Common::File musicFile; + musicFile.open(_filename); + if (!musicFile.isOpen()) { + debugC(2, kDebugMusic, "Cannot open music file %s", _filename.c_str()); + return; + } + int midiMusicSize = musicFile.size(); + free(_midiData); + _midiData = (byte *)malloc(midiMusicSize); + musicFile.read(_midiData, midiMusicSize); + musicFile.close(); + + MidiParser *parser = MidiParser::createParser_SMF(); + if (parser->loadMusic(_midiData, midiMusicSize)) { + parser->setTrack(0); + parser->setMidiDriver(this); + parser->setTimerRate(_driver->getBaseTempo()); + parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1); + + _parser = parser; + + syncVolume(); + + _isLooping = loop; + _isPlaying = true; + } else { + debugC(2, kDebugMusic, "Cannot play music file %s", _filename.c_str()); + delete parser; + } +} + +void MusicPlayer::stop() { + Audio::MidiPlayer::stop(); +} + +} // End of namespace Gnap diff --git a/engines/gnap/music.h b/engines/gnap/music.h new file mode 100644 index 0000000000..c5938937eb --- /dev/null +++ b/engines/gnap/music.h @@ -0,0 +1,50 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +// Music class + +#ifndef GNAP_MUSIC_H +#define GNAP_MUSIC_H + +#include "audio/midiplayer.h" + +namespace Gnap { + +// Taken from Draci, which took it from MADE, which took it from SAGA. + +class MusicPlayer : public Audio::MidiPlayer { +public: + MusicPlayer(const char *filename); + + void playSMF(bool loop); + void stop(); + + // Overload Audio::MidiPlayer method + virtual void sendToChannel(byte channel, uint32 b); + +protected: + Common::String _filename; +}; + +} // End of namespace Gnap + +#endif diff --git a/engines/gnap/resource.cpp b/engines/gnap/resource.cpp new file mode 100644 index 0000000000..8244213a7f --- /dev/null +++ b/engines/gnap/resource.cpp @@ -0,0 +1,121 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gnap/gnap.h" +#include "gnap/resource.h" + +namespace Gnap { + +// SequenceFrame + +void SequenceFrame::loadFromStream(Common::MemoryReadStream &stream) { + _duration = stream.readUint16LE(); + _isScaled = (stream.readUint16LE() != 0); + _rect.left = stream.readUint32LE(); + _rect.top = stream.readUint32LE(); + _rect.right = stream.readUint32LE(); + _rect.bottom = stream.readUint32LE(); + _spriteId = stream.readUint32LE(); + _soundId = stream.readUint32LE(); + + // Skip an unused value + stream.readUint32LE(); + + debugC(kDebugBasic, "SequenceFrame() spriteId: %d; soundId: %d", _spriteId, _soundId); +} + +// SequenceAnimation + +void SequenceAnimation::loadFromStream(Common::MemoryReadStream &stream) { + // Skip two unused values + stream.readUint32LE(); + + _additionalDelay = stream.readUint32LE(); + _framesCount = stream.readUint16LE(); + _maxTotalDuration = stream.readUint16LE(); + debugC(kDebugBasic, "SequenceAnimation() framesCount: %d", _framesCount); + frames = new SequenceFrame[_framesCount]; + for (int i = 0; i < _framesCount; ++i) + frames[i].loadFromStream(stream); +} + +// SequenceResource +SequenceResource::SequenceResource(byte *data, uint32 size) { + Common::MemoryReadStream stream(data, size, DisposeAfterUse::NO); + + // Skip an unused value + stream.readUint32LE(); + + _sequenceId = stream.readUint32LE(); + _defaultId = stream.readUint32LE(); + _sequenceId2 = stream.readUint32LE(); + _defaultId2 = stream.readUint32LE(); + _flags = stream.readUint32LE(); + _totalDuration = stream.readUint32LE(); + _xOffs = stream.readUint16LE(); + _yOffs = stream.readUint16LE(); + _animationsCount = stream.readUint32LE(); + _animations = new SequenceAnimation[_animationsCount]; + debugC(kDebugBasic, "SequenceResource() _animationsCount: %d", _animationsCount); + for (int i = 0; i < _animationsCount; ++i) { + uint32 animationOffs = stream.readUint32LE(); + debugC(kDebugBasic, "animationOffs: %08X", animationOffs); + uint32 oldOffs = stream.pos(); + stream.seek(animationOffs); + _animations[i].loadFromStream(stream); + stream.seek(oldOffs); + } +} + +SequenceResource::~SequenceResource() { + delete[] _animations; +} + +// SpriteResource +SpriteResource::SpriteResource(byte *data, uint32 size) { + _data = data; + _width = READ_LE_UINT16(_data); + _height = READ_LE_UINT16(_data + 2); + _unknownVal1 = READ_LE_UINT16(_data + 4); + _unknownVal2 = READ_LE_UINT16(_data + 6); + _transparent = (READ_LE_UINT16(_data + 8) != 0); + _colorsCount = READ_LE_UINT16(_data + 10); + _palette = (uint32 *)(_data + 12); + _pixels = _data + 12 + _colorsCount * 4; + debugC(kDebugBasic, "SpriteResource() width: %d; height: %d; colorsCount: %d", _width, _height, _colorsCount); +} + +SpriteResource::~SpriteResource() { + delete[] _data; +} + +// SoundResource +SoundResource::SoundResource(byte *data, uint32 size) { + _data = data; + _size = size; +} + +SoundResource::~SoundResource() { + delete[] _data; +} + +} // End of namespace Gnap diff --git a/engines/gnap/resource.h b/engines/gnap/resource.h new file mode 100644 index 0000000000..f4a3669eda --- /dev/null +++ b/engines/gnap/resource.h @@ -0,0 +1,190 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GNAP_RESOURCE_H +#define GNAP_RESOURCE_H + +#include "common/array.h" +#include "common/events.h" +#include "common/file.h" +#include "common/memstream.h" +#include "common/str.h" +#include "common/stream.h" +#include "common/substream.h" +#include "common/system.h" + +#include "graphics/surface.h" + +#include "gnap/datarchive.h" + +namespace Gnap { + +enum { + kResTypeSprite = 0, + kResTypeBitmap = 1, + kResTypeSound = 2, + kResTypeSequence = 3 +}; + +struct SequenceFrame { + int16 _duration; + bool _isScaled; + Common::Rect _rect; + int32 _spriteId; + int32 _soundId; + void loadFromStream(Common::MemoryReadStream &stream); +}; + +struct SequenceAnimation { + int32 _additionalDelay; + int16 _framesCount; + int16 _maxTotalDuration; + SequenceFrame *frames; + + SequenceAnimation() : frames(nullptr), _additionalDelay(0), _framesCount(0), _maxTotalDuration(0) {} + ~SequenceAnimation() { delete[] frames; } + void loadFromStream(Common::MemoryReadStream &stream); +}; + +class SequenceResource { +public: + SequenceResource(byte *data, uint32 size); + ~SequenceResource(); +public: + int32 _sequenceId; + int32 _defaultId; + int32 _sequenceId2; + uint32 _defaultId2; + uint32 _flags; + int32 _totalDuration; + int16 _xOffs; + int16 _yOffs; + int32 _animationsCount; + SequenceAnimation *_animations; +}; + +class SpriteResource { +public: + SpriteResource(byte *data, uint32 size); + ~SpriteResource(); +public: + byte *_data; + byte *_pixels; + uint32 *_palette; + int16 _width, _height; + uint16 _unknownVal1; + uint16 _unknownVal2; + bool _transparent; + uint16 _colorsCount; +}; + +class SoundResource { +public: + SoundResource(byte *data, uint32 size); + ~SoundResource(); +public: + byte *_data; + uint32 _size; +}; + +template <class ResourceClass, int ResourceType, bool FreeAfterLoad> +class ResourceCacheTemplate { +public: + + ResourceCacheTemplate(DatManager *dat) : _dat(dat) { + } + + ~ResourceCacheTemplate() { + } + + ResourceClass *get(int resourceId) { + Resource *resource = find(resourceId); + if (!resource) { + debug(9, "Loading resource type %d with ID %08X from disk", ResourceType, resourceId); + resource = new Resource(load(resourceId)); + _cache[resourceId] = resource; + } else { + debug(9, "Resource type %d with ID %08X was in cache", ResourceType, resourceId); + } + resource->_isLocked = true; + return resource->_obj; + } + + void release(int resourceId) { + Resource *resource = find(resourceId); + if (resource) + resource->_isLocked = false; + } + + void purge(bool force = false) { + for (CacheMapIterator it = _cache.begin(); it != _cache.end(); ++it) { + Resource *resource = it->_value; + if (force || !resource->_isLocked) { + delete resource; + _cache.erase(it); + } + } + } + +protected: + + struct Resource { + ResourceClass *_obj; + bool _isLocked; + Resource(ResourceClass *obj) : _obj(obj), _isLocked(false) {} + ~Resource() { delete _obj; } + }; + + typedef Common::HashMap<int, Resource *> CacheMap; + typedef typename CacheMap::iterator CacheMapIterator; + + DatManager *_dat; + CacheMap _cache; + + Resource *find(int resourceId) { + CacheMapIterator it = _cache.find(resourceId); + if (it != _cache.end()) + return it->_value; + return nullptr; + } + + ResourceClass *load(int resourceId) { + if (_dat->getResourceType(resourceId) != ResourceType) + error("ResourceCache::load() Wrong resource type: Expected %d, got %d", ResourceType, _dat->getResourceType(resourceId)); + + byte *resourceData = _dat->loadResource(resourceId); + uint32 resourceSize = _dat->getResourceSize(resourceId); + ResourceClass *obj = new ResourceClass(resourceData, resourceSize); + if (FreeAfterLoad) + delete[] resourceData; + return obj; + } + +}; + +typedef ResourceCacheTemplate<SpriteResource, kResTypeSprite, false> SpriteCache; +typedef ResourceCacheTemplate<SoundResource, kResTypeSound, false> SoundCache; +typedef ResourceCacheTemplate<SequenceResource, kResTypeSequence, true> SequenceCache; + +} // End of namespace Gnap + +#endif // GNAP_RESOURCE_H diff --git a/engines/gnap/scenes/arcade.cpp b/engines/gnap/scenes/arcade.cpp new file mode 100644 index 0000000000..028a9006d0 --- /dev/null +++ b/engines/gnap/scenes/arcade.cpp @@ -0,0 +1,2729 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gnap/gnap.h" +#include "gnap/gamesys.h" +#include "gnap/resource.h" +#include "gnap/scenes/arcade.h" + +namespace Gnap { + +static const ObstacleDef kObstacleDefs[] = { + {0xB4, 15}, {0xCB, 14}, {0xCD, 13}, {0xCF, 15}, {0xBA, 14}, + {0xCD, 13}, {0xCF, 12}, {0xCB, 15}, {0xBD, 13}, {0xCF, 12}, + {0xCD, 11}, {0xCB, 15}, {0xB7, 12}, {0xCD, 11}, {0xCB, 10}, + {0xCF, 15}, {0xCF, 14}, {0xBD, 13}, {0xCF, 12}, {0xCD, 11}, + {0xCB, 15}, {0xCB, 13}, {0xB4, 12}, {0xCB, 11}, {0xCD, 10}, + {0xCF, 15}, {0xCD, 12}, {0xBA, 12}, {0xCD, 12}, {0xCF, 12}, + {0xCB, 15}, {0xCB, 9}, {0xCD, 9}, {0xCF, 9}, {0xCD, 9}, + {0xCB, 9}, {0xCD, 9}, {0xCF, 5}, {0xBD, 13}, {0xCF, 8}, + {0xCB, 8}, {0xCD, 15}, {0xB4, 1}, {0xBD, 7}, {0xCF, 7}, + {0xCD, 7}, {0xCB, 7}, {0xCD, 7}, {0xCF, 15}, {0xCF, 15} +}; + +Scene49::Scene49(GnapEngine *vm) : Scene(vm) { + _scoreBarFlash = false; + _scoreBarPos = -1; + _scoreLevel = -1; + _obstacleIndex = -1; + _truckSequenceId = -1; + _truckId = -1; + _truckLaneNum = -1; + + for (int i = 0; i < 5; i++) { + _obstacles[i]._currSequenceId = -1; + _obstacles[i]._closerSequenceId = -1; + _obstacles[i]._passedSequenceId = -1; + _obstacles[i]._splashSequenceId = -1; + _obstacles[i]._collisionSequenceId = -1; + _obstacles[i]._prevId = -1; + _obstacles[i]._currId = -1; + _obstacles[i]._laneNum = -1; + } +} + +int Scene49::init() { + GameSys& gameSys = *_vm->_gameSys; + + gameSys.setAnimation(0, 0, 0); + gameSys.setAnimation(0, 0, 1); + for (int i = 0; i < 5; ++i) + gameSys.setAnimation(0, 0, i + 2); + _vm->_timers[2] = 0; + _vm->_timers[0] = 0; + _vm->_timers[1] = 0; + _vm->clearKeyStatus1(Common::KEYCODE_ESCAPE); + _vm->clearKeyStatus1(Common::KEYCODE_RIGHT); + _vm->clearKeyStatus1(Common::KEYCODE_LEFT); + return 0xD5; +} + +void Scene49::updateHotspots() { + _vm->_hotspotsCount = 0; +} + +void Scene49::checkObstacles() { + if (_vm->_timers[2] == 0) { + if (_vm->_timers[3] == 0) { + for (int i = 0; i < 5; ++i) + clearObstacle(i); + } + + for (int j = 0; j < 5; ++j) { + if (_obstacles[j]._currSequenceId == 0) { + _vm->_timers[3] = 35; + _obstacles[j]._currSequenceId = kObstacleDefs[_obstacleIndex]._sequenceId; + switch (_obstacles[j]._currSequenceId) { + case 0xB4: + _obstacles[j]._laneNum = 1; + _obstacles[j]._closerSequenceId = 180; + _obstacles[j]._passedSequenceId = 181; + _obstacles[j]._splashSequenceId = 182; + _obstacles[j]._collisionSequenceId = 192; + break; + case 0xB7: + _obstacles[j]._laneNum = 2; + _obstacles[j]._closerSequenceId = 183; + _obstacles[j]._passedSequenceId = 184; + _obstacles[j]._splashSequenceId = 185; + _obstacles[j]._collisionSequenceId = 193; + break; + case 0xBD: + _obstacles[j]._laneNum = 3; + _obstacles[j]._closerSequenceId = 189; + _obstacles[j]._passedSequenceId = 190; + _obstacles[j]._splashSequenceId = 191; + _obstacles[j]._collisionSequenceId = 195; + break; + case 0xBA: + _obstacles[j]._laneNum = 2; + _obstacles[j]._closerSequenceId = 186; + _obstacles[j]._passedSequenceId = 187; + _obstacles[j]._splashSequenceId = 188; + _obstacles[j]._collisionSequenceId = 194; + break; + case 0xCB: + _obstacles[j]._laneNum = 1; + _obstacles[j]._closerSequenceId = 203; + _obstacles[j]._passedSequenceId = 204; + _obstacles[j]._splashSequenceId = 0; + _obstacles[j]._collisionSequenceId = 209; + break; + case 0xCD: + _obstacles[j]._laneNum = 2; + _obstacles[j]._closerSequenceId = 205; + _obstacles[j]._passedSequenceId = 206; + _obstacles[j]._splashSequenceId = 0; + _obstacles[j]._collisionSequenceId = 210; + break; + case 0xCF: + _obstacles[j]._laneNum = 3; + _obstacles[j]._closerSequenceId = 207; + _obstacles[j]._passedSequenceId = 208; + _obstacles[j]._splashSequenceId = 0; + _obstacles[j]._collisionSequenceId = 211; + break; + } + _obstacles[j]._prevId = _truckId; + _obstacles[j]._currId = _obstacles[j]._prevId; + _vm->_gameSys->setAnimation(_obstacles[j]._currSequenceId, _obstacles[j]._currId, j + 2); + _vm->_gameSys->insertSequence(_obstacles[j]._currSequenceId, _obstacles[j]._currId, 0, 0, kSeqNone, 0, 0, -50); + _vm->_timers[2] = kObstacleDefs[_obstacleIndex]._ticks; + ++_obstacleIndex; + if (_obstacleIndex == 50) + _obstacleIndex = 0; + break; + } + } + } +} + +void Scene49::updateObstacle(int id) { + GameSys& gameSys = *_vm->_gameSys; + + Scene49Obstacle &obstacle = _obstacles[id]; + obstacle._currId = obstacle._prevId; + + switch (obstacle._laneNum) { + case 1: + obstacle._prevId = _truckId + 1; + break; + case 2: + if (_truckLaneNum != 2 && _truckLaneNum != 3) + obstacle._prevId = _truckId - 1; + else + obstacle._prevId = _truckId + 1; + break; + case 3: + if (_truckLaneNum != 1 && _truckLaneNum != 2) + obstacle._prevId = _truckId; + else + obstacle._prevId = _truckId - 1; + break; + } + + if (obstacle._currSequenceId == obstacle._closerSequenceId) { + if (_truckLaneNum == obstacle._laneNum) { + if (obstacle._splashSequenceId) { + gameSys.setAnimation(obstacle._collisionSequenceId, obstacle._prevId, id + 2); + gameSys.insertSequence(obstacle._collisionSequenceId, obstacle._prevId, + obstacle._currSequenceId, obstacle._currId, + kSeqSyncWait, 0, 0, -50); + obstacle._currSequenceId = obstacle._collisionSequenceId; + _vm->playSound(0xE0, false); + increaseScore(30); + } else if ((obstacle._laneNum == 1 && _truckSequenceId == 0xB0) || + (obstacle._laneNum == 2 && (_truckSequenceId == 0xB1 || _truckSequenceId == 0xB2)) || + (obstacle._laneNum == 3 && _truckSequenceId == 0xB3)) { + gameSys.setAnimation(obstacle._passedSequenceId, obstacle._prevId, id + 2); + gameSys.insertSequence(obstacle._passedSequenceId, obstacle._prevId, + obstacle._currSequenceId, obstacle._currId, + kSeqSyncWait, 0, 0, -50); + obstacle._currSequenceId = obstacle._passedSequenceId; + } else { + gameSys.setAnimation(obstacle._collisionSequenceId, 256, 0); + gameSys.setAnimation(obstacle._passedSequenceId, obstacle._prevId, id + 2); + gameSys.insertSequence(obstacle._passedSequenceId, obstacle._prevId, + obstacle._currSequenceId, obstacle._currId, + kSeqSyncWait, 0, 0, -50); + gameSys.insertSequence(obstacle._collisionSequenceId, 256, + _truckSequenceId, _truckId, + kSeqSyncExists, 0, 0, -50); + _truckSequenceId = obstacle._collisionSequenceId; + _truckId = 256; + obstacle._currSequenceId = obstacle._passedSequenceId; + _vm->playSound(0xE1, false); + decreaseScore(30); + } + } else { + gameSys.setAnimation(obstacle._passedSequenceId, obstacle._prevId, id + 2); + gameSys.insertSequence(obstacle._passedSequenceId, obstacle._prevId, + obstacle._currSequenceId, obstacle._currId, + kSeqSyncWait, 0, 0, -50); + obstacle._currSequenceId = obstacle._passedSequenceId; + } + } else if (obstacle._currSequenceId == obstacle._passedSequenceId) { + if (_truckLaneNum == obstacle._laneNum) { + if (obstacle._splashSequenceId) { + gameSys.setAnimation(obstacle._collisionSequenceId, obstacle._prevId, id + 2); + gameSys.insertSequence(obstacle._collisionSequenceId, obstacle._prevId, + obstacle._currSequenceId, obstacle._currId, + kSeqSyncWait, 0, 0, -50); + obstacle._currSequenceId = obstacle._collisionSequenceId; + _vm->playSound(0xE0, false); + increaseScore(30); + } + } else if (obstacle._splashSequenceId) { + gameSys.setAnimation(obstacle._splashSequenceId, obstacle._prevId, id + 2); + gameSys.insertSequence(obstacle._splashSequenceId, obstacle._prevId, + obstacle._currSequenceId, obstacle._currId, + kSeqSyncWait, 0, 0, -50); + obstacle._currSequenceId = obstacle._splashSequenceId; + } + } else { + gameSys.setAnimation(0, 0, id + 2); + clearObstacle(id); + } +} + +void Scene49::increaseScore(int amount) { + if (_scoreBarPos + amount <= 556) { + _scoreBarPos += amount; + _vm->_gameSys->fillSurface(nullptr, _scoreBarPos, 508, amount, 22, 255, 0, 0); + } + + _scoreLevel = (_scoreBarPos + amount >= 556) ? 1 : 0; +} + +void Scene49::decreaseScore(int amount) { + if (_scoreBarPos >= 226 && _scoreLevel == 0) { + if (_scoreBarFlash) + refreshScoreBar(); + _vm->_gameSys->fillSurface(nullptr, _scoreBarPos, 508, amount, 22, 89, 0, 5); + _scoreBarPos -= amount; + _scoreLevel = 0; + } +} + +void Scene49::refreshScoreBar() { + if (_scoreBarFlash) + _vm->_gameSys->fillSurface(nullptr, 226, 508, 330, 22, 255, 0, 0); + else + _vm->_gameSys->fillSurface(nullptr, 226, 508, 330, 22, 89, 0, 5); + _scoreBarFlash = !_scoreBarFlash; +} + +void Scene49::clearObstacle(int index) { + _obstacles[index]._currSequenceId = 0; + _obstacles[index]._closerSequenceId = 0; + _obstacles[index]._passedSequenceId = 0; + _obstacles[index]._splashSequenceId = 0; + _obstacles[index]._collisionSequenceId = 0; + _obstacles[index]._prevId = 0; + _obstacles[index]._currId = 0; + _obstacles[index]._laneNum = 0; +} + +void Scene49::run() { + GameSys& gameSys = *_vm->_gameSys; + + bool animToggle6 = false; + bool animToggle5 = false; + bool animToggle4 = false; + bool animToggle3 = false; + bool streetAnimToggle = false; + bool bgAnimToggle = false; + + _vm->playSound(0xE2, true); + _vm->setSoundVolume(0xE2, 75); + + _vm->hideCursor(); + _vm->setGrabCursorSprite(-1); + + _scoreBarPos = 196; + _scoreLevel = 0; + _scoreBarFlash = false; + + switch (_vm->getRandom(3)) { + case 0: + _truckSequenceId = 0xAD; + _truckLaneNum = 1; + break; + case 1: + _truckSequenceId = 0xAE; + _truckLaneNum = 2; + break; + case 2: + _truckSequenceId = 0xAF; + _truckLaneNum = 3; + break; + } + + int bgWidth1 = gameSys.getSpriteWidthById(0x5E); + int bgX1 = 600; + + int bgWidth2 = gameSys.getSpriteWidthById(0x5F); + int bgX2 = 400; + + int bgWidth3 = gameSys.getSpriteWidthById(4); + int bgX3 = 700; + + int bgWidth4 = gameSys.getSpriteWidthById(5); + int bgX4 = 500; + + int bgWidth5 = gameSys.getSpriteWidthById(6); + int bgX5 = 300; + + int bgWidth6 = gameSys.getSpriteWidthById(7); + int bgX6 = 100; + + gameSys.setAnimation(0xC8, 251, 1); + gameSys.setAnimation(_truckSequenceId, 256, 0); + gameSys.insertSequence(0xC9, 256, 0, 0, kSeqNone, 0, 600, 85); + gameSys.insertSequence(0xCA, 257, 0, 0, kSeqNone, 0, 400, 100); + gameSys.insertSequence(0xC4, 256, 0, 0, kSeqNone, 0, 700, 140); + gameSys.insertSequence(0xC5, 257, 0, 0, kSeqNone, 0, 500, 160); + gameSys.insertSequence(0xC6, 258, 0, 0, kSeqNone, 0, 300, 140); + gameSys.insertSequence(0xC7, 259, 0, 0, kSeqNone, 0, 100, 140); + gameSys.insertSequence(0xC8, 251, 0, 0, kSeqNone, 0, 0, -50); + gameSys.insertSequence(_truckSequenceId, 256, 0, 0, kSeqNone, 0, 0, -50); + + _vm->_timers[0] = 2; + + for (int i = 0; i < 5; ++i) + clearObstacle(i); + + _obstacleIndex = 0; + + _vm->_timers[2] = _vm->getRandom(20) + 10; + + _truckId = 256; + _vm->_timers[3] = 35; + + while (!_vm->_sceneDone) { + if (_vm->_timers[0] == 0) { + // Update background animations (clouds etc.) + --bgX1; + bgX2 -= 2; + bgX3 -= 5; + --bgX4; + --bgX5; + --bgX6; + if (bgX1 <= -bgWidth1) + bgX1 = 799; + if (bgX2 <= -bgWidth2) + bgX2 = 799; + if (bgX3 <= -bgWidth3) + bgX3 = 799; + if (bgX4 <= -bgWidth4) + bgX4 = 799; + if (bgX5 <= -bgWidth5) + bgX5 = 799; + if (bgX6 <= -bgWidth6) + bgX6 = 799; + bgAnimToggle = !bgAnimToggle; + gameSys.insertSequence(0xC9, (bgAnimToggle ? 1 : 0) + 256, 0xC9, (bgAnimToggle ? 0 : 1) + 256, kSeqSyncWait, 0, bgX1, 85); + gameSys.insertSequence(0xCA, (bgAnimToggle ? 1 : 0) + 257, 0xCA, (bgAnimToggle ? 0 : 1) + 257, kSeqSyncWait, 0, bgX2, 100); + gameSys.insertSequence(0xC4, (bgAnimToggle ? 1 : 0) + 256, 0xC4, (bgAnimToggle ? 0 : 1) + 256, kSeqSyncWait, 0, bgX3, 140); + gameSys.insertSequence(0xC5, (bgAnimToggle ? 1 : 0) + 257, 0xC5, (bgAnimToggle ? 0 : 1) + 257, kSeqSyncWait, 0, bgX4, 160); + gameSys.insertSequence(0xC6, (bgAnimToggle ? 1 : 0) + 258, 0xC6, (bgAnimToggle ? 0 : 1) + 258, kSeqSyncWait, 0, bgX5, 140); + gameSys.insertSequence(0xC7, (bgAnimToggle ? 1 : 0) + 259, 0xC7, (bgAnimToggle ? 0 : 1) + 259, kSeqSyncWait, 0, bgX6, 140); + _vm->_timers[0] = 2; + } + + if (gameSys.getAnimationStatus(1) == 2) { + streetAnimToggle = !streetAnimToggle; + gameSys.setAnimation(0xC8, (streetAnimToggle ? 1 : 0) + 251, 1); + gameSys.insertSequence(0xC8, (streetAnimToggle ? 1 : 0) + 251, 200, (streetAnimToggle ? 0 : 1) + 251, kSeqSyncWait, 0, 0, -50); + } + + checkObstacles(); + + if (gameSys.getAnimationStatus(0) == 2) { + switch (_truckSequenceId) { + case 0xB1: + _truckLaneNum = 1; + break; + case 0xB0: + case 0xB3: + _truckLaneNum = 2; + break; + case 0xB2: + _truckLaneNum = 3; + break; + } + animToggle3 = !animToggle3; + if (_truckLaneNum == 1) { + gameSys.setAnimation(0xAD, (animToggle3 ? 1 : 0) + 256, 0); + gameSys.insertSequence(0xAD, (animToggle3 ? 1 : 0) + 256, _truckSequenceId, _truckId, kSeqSyncWait, 0, 0, -50); + _truckSequenceId = 0xAD; + } else if (_truckLaneNum == 2) { + gameSys.setAnimation(0xAE, (animToggle3 ? 1 : 0) + 256, 0); + gameSys.insertSequence(0xAE, (animToggle3 ? 1 : 0) + 256, _truckSequenceId, _truckId, kSeqSyncWait, 0, 0, -50); + _truckSequenceId = 0xAE; + } else { + gameSys.setAnimation(0xAF, (animToggle3 ? 1 : 0) + 256, 0); + gameSys.insertSequence(0xAF, (animToggle3 ? 1 : 0) + 256, _truckSequenceId, _truckId, kSeqSyncWait, 0, 0, -50); + _truckSequenceId = 0xAF; + } + _truckId = (animToggle3 ? 1 : 0) + 256; + if (_scoreLevel == 1) { + if (!gameSys.isSequenceActive(0xD4, 266)) { + gameSys.setAnimation(0xD4, 266, 8); + gameSys.insertSequence(0xD4, 266, 0, 0, kSeqNone, 0, 0, -50); + } + ++_scoreLevel; + _vm->_timers[1] = 2; + animToggle4 = false; + animToggle5 = false; + animToggle6 = false; + _scoreBarFlash = false; + } + } + + if (_scoreLevel != 0 && !_vm->_timers[1]) { + refreshScoreBar(); + _vm->_timers[1] = 8; + if (animToggle6) { + if (animToggle5) { + if (animToggle4 && !gameSys.isSequenceActive(212, 266)) + gameSys.insertSequence(212, 266, 0, 0, kSeqNone, 0, 0, -50); + animToggle4 = !animToggle4; + } + animToggle5 = !animToggle5; + } + animToggle6 = !animToggle6; + } + + updateAnimations(); + + if (clearKeyStatus()) { + _vm->_sceneDone = true; + _vm->_newSceneNum = 2; + _vm->_newCursorValue = 1; + } + + if (_vm->isKeyStatus1(Common::KEYCODE_RIGHT)) { + // Steer right + if (_truckSequenceId == 0xB3) + _truckLaneNum = 2; + if (_truckSequenceId == 0xB1) + _truckLaneNum = 1; + if (_truckLaneNum != 3 && _truckLaneNum != 2) { + if (_scoreLevel) { + _vm->_sceneDone = true; + _vm->_newSceneNum = 47; + } + } else { + int steerSequenceId = (_truckLaneNum == 3) ? 0xB3 : 0xB1; + if (_truckSequenceId == 0xAE || _truckSequenceId == 0xAF) { + gameSys.setAnimation(steerSequenceId, 256, 0); + gameSys.insertSequence(steerSequenceId, 256, _truckSequenceId, _truckId, kSeqSyncExists, 0, 0, -50); + _truckSequenceId = steerSequenceId; + _truckId = 256; + } + } + _vm->clearKeyStatus1(Common::KEYCODE_RIGHT); + } + + if (_vm->isKeyStatus1(Common::KEYCODE_LEFT)) { + // Steer left + if (_truckSequenceId == 0xB0) + _truckLaneNum = 2; + if (_truckSequenceId == 0xB2) + _truckLaneNum = 3; + if (_truckLaneNum == 1 || _truckLaneNum == 2) { + int steerSequenceId = (_truckLaneNum == 1) ? 0xB0 : 0xB2; + if (_truckSequenceId == 0xAD || _truckSequenceId == 0xAE) { + gameSys.setAnimation(steerSequenceId, 256, 0); + gameSys.insertSequence(steerSequenceId, 256, _truckSequenceId, _truckId, kSeqSyncExists, 0, 0, -50); + _truckSequenceId = steerSequenceId; + _truckId = 256; + } + } + _vm->clearKeyStatus1(Common::KEYCODE_LEFT); + } + _vm->gameUpdateTick(); + } + _vm->stopSound(0xE2); +} + +void Scene49::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + + for (int i = 0; i < 5; ++i) { + if (gameSys.getAnimationStatus(i + 2) == 2) { + if (_obstacles[i]._currSequenceId) + updateObstacle(i); + } + } + + if (gameSys.getAnimationStatus(8) == 2) { + _vm->_sceneDone = true; + _vm->_newSceneNum = 47; + } +} + +/*****************************************************************************/ + +Scene50::Scene50(GnapEngine *vm) : Scene(vm) { + _fightDone = false; + + _roundNum = -1; + _timeRemaining = -1; + _leftTongueRoundsWon = -1; + _rightTongueRoundsWon = -1; + _leftTongueSequenceId = -1; + _leftTongueId = -1; + _leftTongueNextSequenceId = -1; + _leftTongueNextId = -1; + _rightTongueSequenceId = -1; + _rightTongueId = -1; + _rightTongueNextSequenceId = -1; + _rightTongueNextId = -1; + _leftTongueEnergy = -1; + _rightTongueEnergy = -1; + + _timesPlayed = 0; + _timesPlayedModifier = 0; + _attackCounter = 0; + _leftTongueEnergyBarPos = 10; + _leftTongueNextIdCtr = 0; + _rightTongueEnergyBarPos = 10; + _rightTongueNextIdCtr = 0; +} + +int Scene50::init() { + return 0xC7; +} + +void Scene50::updateHotspots() { + _vm->_hotspotsCount = 0; +} + +bool Scene50::tongueWinsRound(int tongueNum) { + if (tongueNum == 1) + ++_leftTongueRoundsWon; + else + ++_rightTongueRoundsWon; + playWinBadgeAnim(tongueNum); + bool fightOver = _rightTongueRoundsWon == 2 || _leftTongueRoundsWon == 2; + playWinAnim(tongueNum, fightOver); + return fightOver; +} + +void Scene50::playWinAnim(int tongueNum, bool fightOver) { + if (tongueNum == 1) { + if (fightOver) { + _vm->_gameSys->insertSequence(0xAD, 140, 0xAC, 140, kSeqSyncWait, 0, 0, 0); + _vm->_gameSys->insertSequence(0xB4, 100, _leftTongueSequenceId, _leftTongueId, kSeqSyncWait, 0, 0, 0); + _vm->_gameSys->insertSequence(0xBD, 100, _rightTongueSequenceId, _rightTongueId, kSeqSyncWait, 0, 0, 0); + _vm->_gameSys->insertSequence(0xBC, 100, 0xBD, 100, kSeqSyncWait, 0, 0, 0); + _leftTongueSequenceId = 0xB4; + _rightTongueSequenceId = 0xBC; + _rightTongueId = 100; + _leftTongueId = 100; + _vm->_gameSys->setAnimation(0xB4, 100, 6); + _vm->_gameSys->setAnimation(_rightTongueSequenceId, 100, 5); + waitForAnim(6); + waitForAnim(5); + _vm->invAdd(kItemGum); + _vm->setFlag(kGFUnk13); + } else { + _vm->_gameSys->insertSequence(0xB4, 100, _leftTongueSequenceId, _leftTongueId, kSeqSyncWait, 0, 0, 0); + _vm->_gameSys->insertSequence(0xBD, 100, _rightTongueSequenceId, _rightTongueId, kSeqSyncWait, 0, 0, 0); + _vm->_gameSys->insertSequence(0xBC, 100, 0xBD, 100, kSeqSyncWait, 0, 0, 0); + _leftTongueSequenceId = 0xB4; + _rightTongueSequenceId = 0xBC; + _rightTongueId = 100; + _leftTongueId = 100; + _vm->_gameSys->setAnimation(0xB4, 100, 6); + _vm->_gameSys->setAnimation(_rightTongueSequenceId, 100, 5); + waitForAnim(6); + waitForAnim(5); + } + } else { + _vm->_gameSys->insertSequence(0xBE, 100, _rightTongueSequenceId, _rightTongueId, kSeqSyncWait, 0, 0, 0); + _vm->_gameSys->setAnimation(0xBE, 100, 5); + waitForAnim(5); + _vm->_gameSys->insertSequence(0xBF, 100, 0xBE, 100, kSeqSyncWait, 0, 0, 0); + _vm->_gameSys->insertSequence(0xB5, 100, _leftTongueSequenceId, _leftTongueId, kSeqSyncWait, 0, 0, 0); + _rightTongueSequenceId = 0xBF; + _leftTongueSequenceId = 0xB5; + _rightTongueId = 100; + _leftTongueId = 100; + _vm->_gameSys->setAnimation(0xB5, 100, 6); + _vm->_gameSys->setAnimation(_rightTongueSequenceId, 100, 5); + waitForAnim(6); + waitForAnim(5); + } + _vm->delayTicksA(1, 7); +} + +void Scene50::delayTicks() { + _vm->delayTicksA(3, 7); +} + +void Scene50::initRound() { + _leftTongueEnergy = 10; + _rightTongueEnergy = 10; + _fightDone = false; + _vm->_timers[3] = getRightTongueActionTicks(); + _vm->_timers[4] = 0; + _vm->_timers[6] = 0; + _vm->_gameSys->fillSurface(nullptr, 91, 73, 260, 30, 212, 0, 0); + _vm->_gameSys->fillSurface(nullptr, 450, 73, 260, 30, 212, 0, 0); + _timeRemaining = 40; + drawCountdown(40); +} + +bool Scene50::updateCountdown() { + if (!_vm->_timers[5]) { + --_timeRemaining; + if (_timeRemaining < 0) { + return true; + } else { + _vm->_timers[5] = 15; + drawCountdown(_timeRemaining); + } + } + return false; +} + +void Scene50::drawCountdown(int value) { + char str[8]; + sprintf(str, "%02d", value); + _vm->_gameSys->fillSurface(nullptr, 371, 505, 50, 27, 0, 0, 0); + _vm->_gameSys->drawTextToSurface(nullptr, 381, 504, 255, 255, 255, str); +} + +void Scene50::playTonguesIdle() { + _vm->_gameSys->insertSequence(0xBA, 100, _leftTongueSequenceId, _leftTongueId, kSeqSyncWait, 0, 0, 0); + _vm->_gameSys->insertSequence(0xC2, 100, _rightTongueSequenceId, _rightTongueId, kSeqSyncWait, 0, 0, 0); + _leftTongueSequenceId = 0xBA; + _rightTongueSequenceId = 0xC2; + _rightTongueNextSequenceId = -1; + _leftTongueNextSequenceId = -1; + _leftTongueId = 100; + _rightTongueId = 100; + _vm->_gameSys->setAnimation(0xC2, 100, 5); + _vm->_gameSys->setAnimation(_leftTongueSequenceId, _leftTongueId, 6); +} + +void Scene50::playRoundAnim(int roundNum) { + int sequenceId = 0; + + switch (roundNum) { + case 1: + sequenceId = 0xAF; + break; + case 2: + sequenceId = 0xB0; + break; + case 3: + sequenceId = 0xB1; + break; + } + + _vm->_gameSys->insertSequence(sequenceId, 256, 0, 0, kSeqNone, 0, 0, 0); + _vm->_gameSys->setAnimation(sequenceId, 256, 7); + waitForAnim(7); + + _vm->_gameSys->insertSequence(0xAB, 256, sequenceId, 256, kSeqSyncWait, 0, 0, 0); + _vm->_gameSys->setAnimation(0xAB, 256, 7); + waitForAnim(7); +} + +bool Scene50::updateEnergyBars(int newLeftBarPos, int newRightBarPos) { + if (newLeftBarPos != _leftTongueEnergyBarPos) { + if (newLeftBarPos < 0) + newLeftBarPos = 0; + _leftTongueEnergyBarPos = newLeftBarPos; + _vm->_gameSys->fillSurface(nullptr, 26 * newLeftBarPos + 91, 73, 260 - 26 * newLeftBarPos, 30, 0, 0, 0); + } + + if (newRightBarPos != _rightTongueEnergyBarPos) { + if (newRightBarPos < 0) + newRightBarPos = 0; + _rightTongueEnergyBarPos = newRightBarPos; + if (newRightBarPos != 10) + _vm->_gameSys->fillSurface(nullptr, 26 * (9 - newRightBarPos) + 450, 73, 26, 30, 0, 0, 0); + } + + if (newLeftBarPos * newRightBarPos > 0) + return false; + + _leftTongueEnergyBarPos = 10; + _rightTongueEnergyBarPos = 10; + return true; +} + +void Scene50::waitForAnim(int animationIndex) { + GameSys& gameSys = *_vm->_gameSys; + + while (gameSys.getAnimationStatus(animationIndex) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + + gameSys.setAnimation(0, 0, animationIndex); +} + +int Scene50::checkInput() { + int sequenceId = -1; + + if (_vm->isKeyStatus1(Common::KEYCODE_RIGHT)) { + _vm->clearKeyStatus1(Common::KEYCODE_RIGHT); + sequenceId = 0xB6; + } else if (_vm->isKeyStatus1(Common::KEYCODE_LEFT)) { + _vm->clearKeyStatus1(Common::KEYCODE_LEFT); + sequenceId = 0xB3; + } else if (_vm->isKeyStatus1(Common::KEYCODE_ESCAPE)) { + _vm->clearKeyStatus1(Common::KEYCODE_ESCAPE); + _fightDone = true; + } + + return sequenceId; +} + +int Scene50::getRightTongueAction() { + int sequenceId = -1; + + if (!_vm->_timers[3]) { + _vm->_timers[3] = getRightTongueActionTicks(); + if (_rightTongueEnergy >= _leftTongueEnergy) { + switch (_vm->getRandom(5)) { + case 0: + sequenceId = 0xBE; + break; + case 1: + sequenceId = 0xBE; + break; + case 2: + sequenceId = 0xBB; + break; + case 3: + sequenceId = 0xBB; + break; + case 4: + sequenceId = 0xBB; + break; + } + } else { + switch (_vm->getRandom(4)) { + case 0: + sequenceId = 0xBE; + break; + case 1: + sequenceId = 0xBB; + break; + case 2: + sequenceId = 0xBE; + break; + case 3: + sequenceId = 0xBE; + break; + } + } + } + + return sequenceId; +} + +void Scene50::updateAnimations() { + if (!_vm->_timers[4]) + _attackCounter = 0; + + if (_vm->_gameSys->getAnimationStatus(5) == 2) { + if (_rightTongueSequenceId == 0xBE) { + if (_leftTongueSequenceId != 0xB3 && _leftTongueSequenceId != 0xB8) + _rightTongueNextSequenceId = 0xBF; + else + _rightTongueNextSequenceId = 0xC0; + } + if (_rightTongueNextSequenceId == -1) + _rightTongueNextSequenceId = 0xC2; + if (_rightTongueNextSequenceId == 0xBF) { + _leftTongueNextId = getLeftTongueNextId(); + _rightTongueNextId = getRightTongueNextId(); + _vm->_gameSys->setAnimation(_rightTongueNextSequenceId, _rightTongueNextId, 5); + _vm->_gameSys->setAnimation(0xB9, _leftTongueNextId, 6); + _vm->_gameSys->insertSequence(_rightTongueNextSequenceId, _rightTongueNextId, _rightTongueSequenceId, _rightTongueId, kSeqSyncWait, 0, 0, 0); + _vm->_gameSys->insertSequence(0xB9, _leftTongueNextId, _leftTongueSequenceId, _leftTongueId, kSeqSyncExists, 0, 0, 0); + _rightTongueSequenceId = _rightTongueNextSequenceId; + _rightTongueNextSequenceId = -1; + _leftTongueSequenceId = 0xB9; + _leftTongueNextSequenceId = -1; + _rightTongueId = _rightTongueNextId; + _leftTongueId = _leftTongueNextId; + _leftTongueEnergy -= _vm->getRandom(1) + 1; + } else { + _rightTongueNextId = getRightTongueNextId(); + _vm->_gameSys->setAnimation(_rightTongueNextSequenceId, _rightTongueNextId, 5); + _vm->_gameSys->insertSequence(_rightTongueNextSequenceId, _rightTongueNextId, _rightTongueSequenceId, _rightTongueId, kSeqSyncWait, 0, 0, 0); + _rightTongueSequenceId = _rightTongueNextSequenceId; + _rightTongueNextSequenceId = -1; + _rightTongueId = _rightTongueNextId; + } + } + + if (_vm->_gameSys->getAnimationStatus(6) == 2) { + if (_leftTongueSequenceId == 0xB6) { + ++_attackCounter; + if (_timesPlayedModifier + 3 <= _attackCounter) { + _leftTongueNextSequenceId = 0xB8; + } else { + _vm->_timers[4] = 20; + if (_rightTongueSequenceId != 0xBB && _rightTongueSequenceId != 0xC0 && _vm->getRandom(7) != _roundNum) + _leftTongueNextSequenceId = 0xB7; + else + _leftTongueNextSequenceId = 0xB8; + } + } + if (_leftTongueNextSequenceId == 0xB3) + --_attackCounter; + if (_leftTongueNextSequenceId == -1) + _leftTongueNextSequenceId = 0xBA; + if (_leftTongueNextSequenceId == 0xB7) { + _leftTongueNextId = getLeftTongueNextId(); + _rightTongueNextId = getRightTongueNextId(); + _vm->_gameSys->setAnimation(_leftTongueNextSequenceId, _leftTongueNextId, 6); + _vm->_gameSys->setAnimation(0xC1, _rightTongueNextId, 5); + _vm->_gameSys->insertSequence(_leftTongueNextSequenceId, _leftTongueNextId, _leftTongueSequenceId, _leftTongueId, kSeqSyncWait, 0, 0, 0); + _vm->_gameSys->insertSequence(0xC1, _rightTongueNextId, _rightTongueSequenceId, _rightTongueId, kSeqSyncExists, 0, 0, 0); + _leftTongueSequenceId = _leftTongueNextSequenceId; + _leftTongueNextSequenceId = -1; + _rightTongueSequenceId = 0xC1; + _rightTongueNextSequenceId = -1; + _rightTongueId = _rightTongueNextId; + _leftTongueId = _leftTongueNextId; + --_rightTongueEnergy; + } else if (_leftTongueNextSequenceId != 0xB8 || _rightTongueSequenceId != 0xC2) { + _leftTongueNextId = getLeftTongueNextId(); + _vm->_gameSys->setAnimation(_leftTongueNextSequenceId, _leftTongueNextId, 6); + _vm->_gameSys->insertSequence(_leftTongueNextSequenceId, _leftTongueNextId, _leftTongueSequenceId, _leftTongueId, kSeqSyncWait, 0, 0, 0); + _leftTongueSequenceId = _leftTongueNextSequenceId; + _leftTongueNextSequenceId = -1; + _leftTongueId = _leftTongueNextId; + } else { + _leftTongueNextId = getLeftTongueNextId(); + _rightTongueNextId = getRightTongueNextId(); + _vm->_gameSys->setAnimation(0xBB, _rightTongueNextId, 5); + _vm->_gameSys->setAnimation(_leftTongueNextSequenceId, _leftTongueNextId, 6); + _vm->_gameSys->insertSequence(_leftTongueNextSequenceId, _leftTongueNextId, _leftTongueSequenceId, _leftTongueId, kSeqSyncWait, 0, 0, 0); + _vm->_gameSys->insertSequence(0xBB, _rightTongueNextId, _rightTongueSequenceId, _rightTongueId, kSeqSyncExists, 0, 0, 0); + _rightTongueSequenceId = 0xBB; + _rightTongueId = _rightTongueNextId; + _rightTongueNextSequenceId = -1; + _leftTongueSequenceId = _leftTongueNextSequenceId; + _leftTongueNextSequenceId = -1; + _leftTongueId = _leftTongueNextId; + } + } +} + +int Scene50::getRightTongueActionTicks() { + return 15 - 5 * _roundNum + 1; +} + +int Scene50::getLeftTongueNextId() { + _leftTongueNextIdCtr = (_leftTongueNextIdCtr + 1) % 3; + return _leftTongueNextIdCtr + 100; +} + +int Scene50::getRightTongueNextId() { + _rightTongueNextIdCtr = (_rightTongueNextIdCtr + 1) % 3; + return _rightTongueNextIdCtr + 100; +} + +void Scene50::playWinBadgeAnim(int tongueNum) { + int sequenceId; + + if (tongueNum == 1) { + if (_leftTongueRoundsWon == 1) + sequenceId = 0xC3; + else + sequenceId = 0xC4; + } else { + if (_rightTongueRoundsWon == 1) + sequenceId = 0xC5; + else + sequenceId = 0xC6; + } + + _vm->_gameSys->setAnimation(sequenceId, 120, 7); + _vm->_gameSys->insertSequence(sequenceId, 120, 0, 0, kSeqNone, 0, 0, 0); + waitForAnim(7); +} + +void Scene50::run() { + ++_timesPlayed; + _timesPlayedModifier = _timesPlayed / 4; + _leftTongueRoundsWon = 0; + _rightTongueRoundsWon = 0; + // initFont(); + _leftTongueSequenceId = 186; + _rightTongueSequenceId = 194; + _rightTongueNextSequenceId = -1; + _leftTongueNextSequenceId = -1; + _leftTongueId = 100; + _rightTongueId = 100; + + _vm->_gameSys->setAnimation(194, 100, 5); + _vm->_gameSys->setAnimation(_leftTongueSequenceId, _leftTongueId, 6); + _vm->_gameSys->insertSequence(_leftTongueSequenceId, _leftTongueId, 0, 0, kSeqNone, 0, 0, 0); + _vm->_gameSys->insertSequence(_rightTongueSequenceId, _rightTongueId, 0, 0, kSeqNone, 0, 0, 0); + _vm->_gameSys->insertSequence(172, 140, 0, 0, kSeqNone, 0, 0, 0); + _vm->endSceneInit(); + + initRound(); + + _roundNum = 1; + + _vm->setGrabCursorSprite(-1); + _vm->hideCursor(); + + _vm->delayTicksA(1, 7); + + playRoundAnim(_roundNum); + + _vm->_timers[5] = 15; + + while (!_fightDone && !_vm->_gameDone) { + int playerSequenceId = checkInput(); + if (playerSequenceId != -1) + _leftTongueNextSequenceId = playerSequenceId; + + int rightSequenceId = getRightTongueAction(); + if (rightSequenceId != -1) + _rightTongueNextSequenceId = rightSequenceId; + + updateAnimations(); + + if (updateCountdown() || + updateEnergyBars(_leftTongueEnergy, _rightTongueEnergy)) { + bool v0; + if (_rightTongueEnergy < _leftTongueEnergy) + v0 = tongueWinsRound(1); + else + v0 = tongueWinsRound(2); + if (v0) { + delayTicks(); + _fightDone = true; + } else { + ++_roundNum; + initRound(); + playTonguesIdle(); + updateEnergyBars(_leftTongueEnergy, _rightTongueEnergy); + playRoundAnim(_roundNum); + _vm->_timers[5] = 15; + } + } + _vm->gameUpdateTick(); + } + + _vm->_gameSys->setAnimation(0, 0, 7); + _vm->_gameSys->setAnimation(0, 0, 6); + _vm->_gameSys->setAnimation(0, 0, 5); + _vm->_gameSys->setAnimation(0, 0, 3); + + _vm->showCursor(); +} + +/*****************************************************************************/ + +static const int kDigitSequenceIds[] = { + 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, + 0xCF, 0xD0, 0xD1, 0xD2, 0xD3 +}; + +static const int kDigitPositions[4] = { + 0, 34, 83, 119 +}; + +/* + 0xBA Falling banana peel + 0xBC Banana peel goes away + 0xBD Falling coin + 0xBE Fallen coin + 0xC0 Falling banknote + 0xB6 Platypus tripping (right) + 0xB7 Platypus tripping (left) + 0x76 Platypus jumping (right) +*/ + +Scene51::Scene51(GnapEngine *vm) : Scene(vm) { + _dropLoseCash = false; + + _cashAmount = -1; + _guySequenceId = -1; + _guyNextSequenceId = -1; + _itemsCaughtCtr = -1; + _dropSpeedTicks = -1; + _nextDropItemKind = -1; + _itemInsertX = -1; + _itemInsertDirection = -1; + _platypusSequenceId = -1; + _platypusNextSequenceId = -1; + _platypusJumpSequenceId = -1; + _itemsCtr = -1; + _itemsCtr1 = -1; + _itemsCtr2 = -1; + + for (int i = 0; i < 4; i++) { + _digits[i] = 0; + _digitSequenceIds[i] = -1; + } + + for (int i = 0; i < 6; i++) { + _items[i]._currSequenceId = -1; + _items[i]._droppedSequenceId = 0; + _items[i]._x = 0; + _items[i]._y = 0; + _items[i]._collisionX = 0; + _items[i]._canCatch = false; + _items[i]._isCollision = false; + _items[i]._x2 = 0; + _items[i]._id = -1; + } +} + +int Scene51::init() { + _vm->_gameSys->setAnimation(0, 0, 0); + for (int i = 0; i < 6; ++i) + _vm->_gameSys->setAnimation(0, 0, i + 1); + return 0xD4; +} + +void Scene51::updateHotspots() { + _vm->_hotspotsCount = 0; +} + +void Scene51::clearItem(Scene51Item *item) { + item->_currSequenceId = 0; + item->_droppedSequenceId = 0; + item->_x = 0; + item->_y = 0; + item->_x2 = 0; + item->_collisionX = 0; + item->_canCatch = false; +} + +void Scene51::dropNextItem() { + if (_vm->_timers[0]) + return; + + int index = 0; + while (index < 6 && _items[index]._currSequenceId) + ++index; + + if (index == 6) + return; + + switch (_nextDropItemKind) { + case 0: + if (_vm->getRandom(10) != 0 || _itemsCtr2 >= 2) { + _items[index]._currSequenceId = 0xBD; + } else { + --_itemsCtr1; + _items[index]._currSequenceId = 0xBA; + ++_itemsCtr2; + } + break; + + case 1: + if (_vm->getRandom(8) != 0 || _itemsCtr2 >= 2) { + if (_vm->getRandom(5) == 0) { + if (_itemInsertDirection) + _itemInsertX -= 70; + else + _itemInsertX += 70; + } + _items[index]._currSequenceId = 0xBD; + } else { + --_itemsCtr1; + _items[index]._currSequenceId = 0xBA; + ++_itemsCtr2; + } + break; + + case 2: + if (_vm->getRandom(6) != 0 || _itemsCtr2 >= 2) { + _items[index]._currSequenceId = 0xBD; + } else { + --_itemsCtr1; + _items[index]._currSequenceId = 0xBA; + ++_itemsCtr2; + } + break; + + case 3: + case 4: + if (_itemsCtr == 0) + _itemsCtr1 = 3; + _items[index]._currSequenceId = 0xC0; + break; + + case 5: + case 6: + if (_vm->getRandom(5) != 0 || _itemsCtr2 >= 2) { + if (_vm->getRandom(5) != 0) + _items[index]._currSequenceId = 0xBD; + else + _items[index]._currSequenceId = 0xC0; + } else { + --_itemsCtr1; + _items[index]._currSequenceId = 0xBA; + ++_itemsCtr2; + } + break; + + case 7: + if (_vm->getRandom(5) != 0 || _itemsCtr2 >= 2) { + if (_vm->getRandom(5) == 0) { + if (_itemInsertDirection) + _itemInsertX -= 40; + else + _itemInsertX += 40; + } + if (_vm->getRandom(9) != 0) + _items[index]._currSequenceId = 0xBD; + else + _items[index]._currSequenceId = 0xC0; + } else { + --_itemsCtr1; + _items[index]._currSequenceId = 0xBA; + ++_itemsCtr2; + } + break; + + default: + if (_vm->getRandom(4) != 0 || _itemsCtr2 >= 2) { + if (_vm->getRandom(9) != 0) + _items[index]._currSequenceId = 0xBD; + else + _items[index]._currSequenceId = 0xC0; + } else { + --_itemsCtr1; + _items[index]._currSequenceId = 0xBA; + ++_itemsCtr2; + } + break; + } + + if (_itemInsertDirection) { + _itemInsertX -= 73; + if (_itemInsertX < 129) { + _itemInsertX += 146; + _itemInsertDirection = 0; + } + } else { + _itemInsertX += 73; + if (_itemInsertX > 685) { + _itemInsertX -= 146; + _itemInsertDirection = 1; + } + } + + if (_itemInsertX > 685) + _itemInsertX = 685; + + if (_itemInsertX < 129) + _itemInsertX = 129; + + if (_items[index]._currSequenceId == 0xBA) { + _items[index]._x2 = _vm->getRandom(350) + 200; + _items[index]._x = _items[index]._x2 - 362; + _items[index]._y = 15; + _items[index]._id = 249 - index; + } else { + _items[index]._collisionX = _itemInsertX; + _items[index]._x = _items[index]._collisionX - 395; + if (_items[index]._currSequenceId == 0xC0) + _items[index]._x -= 65; + _items[index]._id = index + 250; + _items[index]._canCatch = true; + } + + _vm->_gameSys->setAnimation(_items[index]._currSequenceId, _items[index]._id, index + 1); + _vm->_gameSys->insertSequence(_items[index]._currSequenceId, _items[index]._id, 0, 0, + kSeqNone, 0, _items[index]._x, _items[index]._y); + + _vm->_timers[0] = _dropSpeedTicks; + + if (_nextDropItemKind >= 3) + _vm->_timers[0] = 20; + + if (_nextDropItemKind >= 5) + _vm->_timers[0] = 5; + + if (_nextDropItemKind == 8) + _vm->_timers[0] = 4; + + ++_itemsCtr; +} + +void Scene51::updateItemAnimations() { + for (int i = 0; i < 6; ++i) { + if (_vm->_gameSys->getAnimationStatus(i + 1) == 2) + updateItemAnimation(&_items[i], i); + } +} + +int Scene51::checkCollision(int sequenceId) { + if (!isJumping(sequenceId)) + return false; + + bool jumpingLeft = false, jumpingRight = false; + int v8 = 0, v4 = 0; + int result = 0; + + bool checkFl = false; + for (int i = 0; i < 6; i++) + checkFl |= _items[i]._isCollision; + + if (!checkFl) + return false; + + if (isJumpingRight(sequenceId)) { + v8 = getPosRight(sequenceId); + v4 = getPosRight(sequenceId + 1); + jumpingRight = true; + } else if (isJumpingLeft(sequenceId)) { + v4 = getPosLeft(sequenceId - 1) + 33; + v8 = getPosLeft(sequenceId) + 33; + jumpingLeft = true; + } + + if (jumpingRight || jumpingLeft) { + int v5 = 0; + int i; + for (i = 0; i < 6; ++i) { + if (_items[i]._isCollision) { + if (jumpingRight && _items[i]._x2 > v8 && _items[i]._x2 < v4) { + v5 = v8 - 359; + if (v5 == 0) + v5 = 1; + _platypusNextSequenceId = 0xB6; + break; + } else if (jumpingLeft && _items[i]._x2 < v4 && _items[i]._x2 > v8) { + v5 = v8 - 344; + if (v5 == 0) + v5 = 1; + _platypusNextSequenceId = 0xB7; + break; + } + } + } + if (v5) { + _vm->_gameSys->setAnimation(0xBC, _items[i]._id, i + 1); + _vm->_gameSys->insertSequence(0xBC, _items[i]._id, _items[i]._currSequenceId, _items[i]._id, kSeqSyncWait, 0, _items[i]._x, 15); + _items[i]._isCollision = false; + _items[i]._currSequenceId = 0xBC; + --_itemsCtr2; + } + result = v5; + } + + return result; +} + +void Scene51::updateItemAnimation(Scene51Item *item, int index) { + + switch (item->_currSequenceId) { + case 0xBD: + case 0xC0: + case 0xC1: + // Falling coin and banknote + if (!itemIsCaught(item)) { + if (_dropLoseCash) { + if (item->_currSequenceId == 0xBD) + _cashAmount -= 2; + else + _cashAmount -= 25; + if (_cashAmount < 0) + _cashAmount = 0; + updateCash(_cashAmount); + } + item->_droppedSequenceId = item->_currSequenceId + 1; + if (item->_currSequenceId != 0xC0) { + item->_canCatch = false; + _dropLoseCash = true; + _itemsCtr = 0; + _vm->_timers[0] = 10; + } + if (item->_droppedSequenceId) { + _vm->_gameSys->setAnimation(item->_droppedSequenceId, item->_id, index + 1); + _vm->_gameSys->insertSequence(item->_droppedSequenceId, item->_id, item->_currSequenceId, item->_id, kSeqSyncWait, 0, item->_x, item->_y); + item->_currSequenceId = item->_droppedSequenceId; + item->_y = 0; + } + } else { + _vm->_gameSys->removeSequence(item->_currSequenceId, item->_id, true); + _vm->_gameSys->setAnimation(0, 0, index + 1); + _vm->playSound(0xDA, false); + if (incCashAmount(item->_currSequenceId) == 1995) { + winMinigame(); + _vm->_sceneDone = true; + } else { + clearItem(item); + ++_itemsCaughtCtr; + if (_itemsCaughtCtr == 5) + --_dropSpeedTicks; + if (_itemsCaughtCtr == 8) + --_dropSpeedTicks; + if (_itemsCaughtCtr == 11) + --_dropSpeedTicks; + if (_itemsCaughtCtr == 14) + --_dropSpeedTicks; + if (_itemsCaughtCtr >= 15 && _dropSpeedTicks > 4) + --_dropSpeedTicks; + if (_itemsCtr1 <= _itemsCaughtCtr) { + ++_nextDropItemKind; + _dropSpeedTicks = 10; + _itemsCtr = 0; + _itemsCtr1 = 20; + _dropLoseCash = false; + _itemsCaughtCtr = 0; + removeCollidedItems(); + } + } + } + break; + + case 0xBE: + // Fallen coin + item->_droppedSequenceId = item->_currSequenceId + 1; + if (item->_droppedSequenceId) { + _vm->_gameSys->setAnimation(item->_droppedSequenceId, item->_id, index + 1); + _vm->_gameSys->insertSequence(item->_droppedSequenceId, item->_id, item->_currSequenceId, item->_id, kSeqSyncWait, 0, item->_x, item->_y); + item->_currSequenceId = item->_droppedSequenceId; + item->_y = 0; + } + break; + + case 0xBF: + case 0xC2: + // Bouncing coin and banknote + _vm->_gameSys->setAnimation(0, 0, index + 1); + _vm->_gameSys->removeSequence(item->_currSequenceId, item->_id, true); + clearItem(item); + break; + + case 0xBA: + // Falling banana peel + item->_droppedSequenceId = 0xBB; + item->_y = 15; + if (item->_droppedSequenceId) { + _vm->_gameSys->setAnimation(item->_droppedSequenceId, item->_id, index + 1); + _vm->_gameSys->insertSequence(item->_droppedSequenceId, item->_id, item->_currSequenceId, item->_id, kSeqSyncWait, 0, item->_x, item->_y); + item->_currSequenceId = item->_droppedSequenceId; + item->_y = 0; + } + break; + + case 0xBB: + item->_isCollision = true; + item->_droppedSequenceId = 0; + _vm->_gameSys->setAnimation(0, 0, index + 1); + break; + + case 0xBC: + _vm->_gameSys->removeSequence(item->_currSequenceId, item->_id, true); + _vm->_gameSys->setAnimation(0, 0, index + 1); + clearItem(item); + break; + + default: + if (item->_droppedSequenceId) { + _vm->_gameSys->setAnimation(item->_droppedSequenceId, item->_id, index + 1); + _vm->_gameSys->insertSequence(item->_droppedSequenceId, item->_id, item->_currSequenceId, item->_id, kSeqSyncWait, 0, item->_x, item->_y); + item->_currSequenceId = item->_droppedSequenceId; + item->_y = 0; + } + break; + } +} + +void Scene51::removeCollidedItems() { + for (int i = 0; i < 6; ++i) { + if (_items[i]._isCollision) { + _vm->_gameSys->removeSequence(_items[i]._currSequenceId, _items[i]._id, true); + _vm->_gameSys->setAnimation(0, 0, i + 1); + clearItem(&_items[i]); + } + } + _itemsCtr2 = 0; +} + +int Scene51::itemIsCaught(Scene51Item *item) { + if (!item->_canCatch) + return 0; + + if (isJumpingRight(_platypusJumpSequenceId)) { + int v4 = getPosRight(_platypusJumpSequenceId) + 97; + if (item->_collisionX < v4 && v4 - item->_collisionX < 56) + return 1; + } else { + int v2 = getPosLeft(_platypusJumpSequenceId); + if (item->_collisionX > v2 && item->_collisionX - v2 < 56) + return 1; + } + + if (item->_currSequenceId == 0xC1) { + int v3 = item->_collisionX + 100; + if (isJumpingRight(_platypusJumpSequenceId)) { + if (ABS(getPosRight(_platypusJumpSequenceId) + 46 - v3) < 56) + return 1; + } else if (ABS(getPosLeft(_platypusJumpSequenceId) + 46 - v3) < 56) { + return 1; + } + } + + return 0; +} + +bool Scene51::isJumpingRight(int sequenceId) { + return sequenceId >= 0x76 && sequenceId <= 0x95; +} + +bool Scene51::isJumpingLeft(int sequenceId) { + return sequenceId >= 0x96 && sequenceId <= 0xB5; +} + +bool Scene51::isJumping(int sequenceId) { + return sequenceId >= 0x76 && sequenceId <= 0xB5; +} + +void Scene51::waitForAnim(int animationIndex) { + while (_vm->_gameSys->getAnimationStatus(animationIndex) != 2 && _vm->_gameDone) { + updateItemAnimations(); + _vm->gameUpdateTick(); + } +} + +int Scene51::getPosRight(int sequenceId) { + static const int kRightPosTbl[] = { + 131, 159, 178, 195, 203, 219, 238, 254, + 246, 274, 293, 310, 318, 334, 353, 369, + 362, 390, 409, 426, 434, 450, 469, 485, + 477, 505, 524, 541, 549, 565, 584, 600 + }; + + if (sequenceId >= 118 && sequenceId <= 149) + return kRightPosTbl[sequenceId - 118]; + return -1; +} + +int Scene51::getPosLeft(int sequenceId) { + static const int kLeftPosTbl[] = { + 580, 566, 550, 536, 526, 504, 488, 469, + 460, 446, 430, 416, 406, 384, 368, 349, + 342, 328, 312, 298, 288, 266, 250, 231, + 220, 206, 190, 176, 166, 144, 128, 109 + }; + + if (sequenceId >= 150 && sequenceId <= 181) + return kLeftPosTbl[sequenceId - 150]; + return -1; +} + +void Scene51::playIntroAnim() { + int soundCtr = 0; + + _platypusSequenceId = 0x76; + _platypusNextSequenceId = 0x76; + + for (int i = 0; i < 6; ++i) + clearItem(&_items[i]); + + _items[0]._currSequenceId = 0xBA; + _items[0]._x2 = 320; + _items[0]._x = -42; + _items[0]._y = 15; + _items[0]._id = 249; + _items[0]._isCollision = true; + + _vm->_gameSys->insertSequence(_platypusSequenceId, 256, 0, 0, kSeqNone, 0, -179, 0); + _vm->_gameSys->insertSequence(0xBA, 249, 0, 0, kSeqNone, 0, _items[0]._x, _items[0]._y); + _vm->_gameSys->setAnimation(0xBA, 249, 1); + _vm->_gameSys->setAnimation(_platypusSequenceId, 256, 0); + + while (_platypusSequenceId < 0x80) { + waitForAnim(0); + ++_platypusNextSequenceId; + _vm->_gameSys->setAnimation(_platypusNextSequenceId, 256, 0); + _vm->_gameSys->insertSequence(_platypusNextSequenceId, 256, _platypusSequenceId, 256, kSeqSyncWait, 0, -179, 0); + _platypusSequenceId = _platypusNextSequenceId; + ++soundCtr; + if (soundCtr % 4 == 0) + _vm->playSound(0xD6, false); + } + + _platypusNextSequenceId = 0x75; + + while (_platypusSequenceId != 0x84) { + waitForAnim(0); + ++_platypusNextSequenceId; + int oldSequenceId = _platypusNextSequenceId; + int v0 = checkCollision(_platypusNextSequenceId); + _vm->_gameSys->setAnimation(_platypusNextSequenceId, 256, 0); + _vm->_gameSys->insertSequence(_platypusNextSequenceId, 256, _platypusSequenceId, 256, kSeqSyncWait, 0, v0, 0); + _platypusSequenceId = _platypusNextSequenceId; + if (v0) { + _platypusNextSequenceId = oldSequenceId; + } else { + ++soundCtr; + if (soundCtr % 4 == 0) + _vm->playSound(0xD6, false); + } + } + waitForAnim(0); +} + +void Scene51::updateGuyAnimation() { + if (!_vm->_timers[4]) { + _vm->_timers[4] = _vm->getRandom(20) + 60; + + switch (_vm->getRandom(5)) { + case 0: + _guyNextSequenceId = 0xC3; + break; + case 1: + _guyNextSequenceId = 0xC4; + break; + case 2: + _guyNextSequenceId = 0xC5; + break; + case 3: + _guyNextSequenceId = 0xC6; + break; + case 4: + _guyNextSequenceId = 0xC7; + break; + } + + _vm->_gameSys->insertSequence(_guyNextSequenceId, 39, _guySequenceId, 39, kSeqSyncWait, 0, 0, 0); + _guySequenceId = _guyNextSequenceId; + _guyNextSequenceId = -1; + } +} + +int Scene51::incCashAmount(int sequenceId) { + switch (sequenceId) { + case 0xBD: + _cashAmount += 10; + break; + case 0xC0: + case 0xC1: + _cashAmount += 100; + break; + case 0xB6: + case 0xB7: + _cashAmount -= 10 * _vm->getRandom(5) + 50; + if (_cashAmount < 0) + _cashAmount = 0; + break; + } + if (_cashAmount > 1995) + _cashAmount = 1995; + updateCash(_cashAmount); + return _cashAmount; +} + +void Scene51::winMinigame() { + updateCash(1995); + _vm->playSound(0xDA, false); + _vm->delayTicksA(1, 5); + _vm->_newSceneNum = 48; + _vm->invRemove(kItemBanana); +} + +void Scene51::playCashAppearAnim() { + _vm->_gameSys->setAnimation(0xC8, 252, 0); + _vm->_gameSys->insertSequence(0xC8, 252, 0, 0, kSeqNone, 0, -20, -20); + + while (_vm->_gameSys->getAnimationStatus(0) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); +} + +void Scene51::updateCash(int amount) { + drawDigit(amount / 1000, 0); + drawDigit(amount / 100 % 10, 1); + drawDigit(amount / 10 % 10, 2); + drawDigit(amount % 10, 3); +} + +void Scene51::drawDigit(int digit, int position) { + if (digit != _digits[position]) { + _vm->_gameSys->insertSequence(kDigitSequenceIds[digit], 253 + position, + _digitSequenceIds[position], 253 + position, + kSeqSyncWait, 0, kDigitPositions[position] - 20, -20); + _digitSequenceIds[position] = kDigitSequenceIds[digit]; + _digits[position] = digit; + } +} + +void Scene51::initCashDisplay() { + for (int position = 0; position < 4; ++position) { + _digits[position] = 0; + _digitSequenceIds[position] = kDigitSequenceIds[0]; + _vm->_gameSys->insertSequence(kDigitSequenceIds[0], 253 + position, 0, 0, kSeqNone, 0, kDigitPositions[position] - 20, -20); + } + _cashAmount = 0; +} + +void Scene51::run() { + int soundCtr = 0; + bool isIdle = true; + + _itemsCtr = 0; + _vm->_newSceneNum = _vm->_prevSceneNum; + _cashAmount = 0; + _platypusJumpSequenceId = 0x84; + _vm->endSceneInit(); + + _vm->hideCursor(); + _vm->setGrabCursorSprite(-1); + + _guySequenceId = 0xC3; + _guyNextSequenceId = -1; + + _vm->_gameSys->insertSequence(0xC3, 39, 0, 0, kSeqNone, 0, 0, 0); + _vm->_timers[4] = _vm->getRandom(20) + 60; + + playCashAppearAnim(); + initCashDisplay(); + playIntroAnim(); + + _platypusNextSequenceId = 0x74; + _vm->_gameSys->setAnimation(0x74, 256, 0); + _vm->_gameSys->insertSequence(_platypusNextSequenceId, 256, _platypusSequenceId, 256, kSeqSyncWait, 0, getPosRight(_platypusJumpSequenceId) - 362, 0); + _platypusSequenceId = _platypusNextSequenceId; + + _itemInsertDirection = 0; + _itemInsertX = 685; + _dropSpeedTicks = 10; + _nextDropItemKind = 0; + + for (int i = 0; i < 6; ++i) + clearItem(&_items[i]); + + _itemInsertX = _vm->getRandom(556) + 129; + _vm->_timers[0] = 15; + + _itemsCaughtCtr = 0; + _dropLoseCash = false; + _itemsCtr1 = 20; + + _vm->clearKeyStatus1(Common::KEYCODE_RIGHT); + _vm->clearKeyStatus1(Common::KEYCODE_LEFT); + _vm->clearKeyStatus1(Common::KEYCODE_UP); + _vm->clearKeyStatus1(Common::KEYCODE_SPACE); + _vm->clearKeyStatus1(Common::KEYCODE_ESCAPE); + + bool isCollision = false; + bool startWalk = true; + + while (!_vm->_sceneDone) { + if (clearKeyStatus()) + _vm->_sceneDone = true; + + _vm->gameUpdateTick(); + + updateGuyAnimation(); + dropNextItem(); + updateItemAnimations(); + + if (_vm->isKeyStatus2(Common::KEYCODE_UP) || _vm->isKeyStatus2(Common::KEYCODE_SPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_UP); + _vm->clearKeyStatus1(Common::KEYCODE_SPACE); + if (isJumpingRight(_platypusJumpSequenceId)) { + waitForAnim(0); + _vm->_gameSys->setAnimation(0xB8, 256, 0); + _vm->_gameSys->insertSequence(0xB8, 256, _platypusSequenceId, 256, kSeqSyncWait, 0, getPosRight(_platypusJumpSequenceId) - 348, 0); + _platypusSequenceId = 0xB8; + waitForAnim(0); + _platypusNextSequenceId += 6; + if (_platypusNextSequenceId > 0x95) + _platypusNextSequenceId = 0x95; + _platypusJumpSequenceId = _platypusNextSequenceId; + } else { + waitForAnim(0); + _vm->_gameSys->setAnimation(0xB9, 256, 0); + _vm->_gameSys->insertSequence(0xB9, 256, _platypusSequenceId, 256, kSeqSyncWait, 0, getPosLeft(_platypusJumpSequenceId) - 338, 0); + _platypusSequenceId = 0xB9; + waitForAnim(0); + _platypusNextSequenceId += 6; + if (_platypusNextSequenceId > 0xB5) + _platypusNextSequenceId = 0xB5; + _platypusJumpSequenceId = _platypusNextSequenceId; + } + isIdle = false; + } + + while (_vm->isKeyStatus2(Common::KEYCODE_RIGHT) && _platypusNextSequenceId != 0x96 && !_vm->_gameDone) { + if (_platypusNextSequenceId == 0xB6) + _platypusNextSequenceId = 0x76; + updateItemAnimations(); + if (startWalk) { + _platypusNextSequenceId = 0x86; + startWalk = false; + } + + if (_vm->_gameSys->getAnimationStatus(0) == 2) { + int collisionX = checkCollision(_platypusNextSequenceId); + if (collisionX) + incCashAmount(_platypusNextSequenceId); + _vm->_gameSys->setAnimation(_platypusNextSequenceId, 256, 0); + _vm->_gameSys->insertSequence(_platypusNextSequenceId, 256, _platypusSequenceId, 256, kSeqSyncWait, 0, collisionX, 0); + _platypusSequenceId = _platypusNextSequenceId; + if (collisionX) { + isCollision = true; + ++_platypusJumpSequenceId; + _platypusNextSequenceId = _platypusJumpSequenceId; + } else { + _platypusJumpSequenceId = _platypusNextSequenceId; + } + if (isJumpingRight(_platypusJumpSequenceId)) { + ++_platypusNextSequenceId; + if (!isCollision) { + if (_vm->isKeyStatus2(Common::KEYCODE_UP) || _vm->isKeyStatus2(Common::KEYCODE_SPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_UP); + _vm->clearKeyStatus1(Common::KEYCODE_SPACE); + waitForAnim(0); + _vm->_gameSys->setAnimation(0xB8, 256, 0); + _vm->_gameSys->insertSequence(0xB8, 256, _platypusSequenceId, 256, kSeqSyncWait, 0, getPosRight(_platypusJumpSequenceId) - 348, 0); + _platypusSequenceId = 0xB8; + waitForAnim(0); + _platypusNextSequenceId += 6; + if (_platypusNextSequenceId > 0x95) + _platypusNextSequenceId = 0x95; + _platypusJumpSequenceId = _platypusNextSequenceId; + } else { + ++soundCtr; + if (soundCtr % 4 == 0) + _vm->playSound(0xD6, false); + } + } + } else { + _platypusNextSequenceId = 150 - (_platypusJumpSequenceId - 150); + } + isCollision = false; + isIdle = false; + } + _vm->gameUpdateTick(); + } + + while (_vm->isKeyStatus2(Common::KEYCODE_LEFT) && _platypusNextSequenceId != 0xB6 && !_vm->_gameDone) { + updateItemAnimations(); + if (startWalk) { + _platypusNextSequenceId = 0xA5; + startWalk = false; + } + + if (_vm->_gameSys->getAnimationStatus(0) == 2) { + int collisionX = checkCollision(_platypusNextSequenceId); + if (collisionX) + incCashAmount(_platypusNextSequenceId); + _vm->_gameSys->setAnimation(_platypusNextSequenceId, 256, 0); + _vm->_gameSys->insertSequence(_platypusNextSequenceId, 256, _platypusSequenceId, 256, kSeqSyncWait, 0, collisionX, 0); + _platypusSequenceId = _platypusNextSequenceId; + if (collisionX) { + isCollision = true; + ++_platypusJumpSequenceId; + _platypusNextSequenceId = _platypusJumpSequenceId; + } else { + _platypusJumpSequenceId = _platypusNextSequenceId; + } + if (isJumpingLeft(_platypusJumpSequenceId)) { + ++_platypusNextSequenceId; + if (!isCollision) { + if (_vm->isKeyStatus2(Common::KEYCODE_UP) || _vm->isKeyStatus2(Common::KEYCODE_SPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_UP); + _vm->clearKeyStatus1(Common::KEYCODE_SPACE); + waitForAnim(0); + _vm->_gameSys->setAnimation(0xB9, 256, 0); + _vm->_gameSys->insertSequence(0xB9, 256, _platypusSequenceId, 256, kSeqSyncWait, 0, getPosLeft(_platypusJumpSequenceId) - 338, 0); + _platypusSequenceId = 0xB9; + waitForAnim(0); + _platypusNextSequenceId += 6; + if (_platypusNextSequenceId > 0xB5) + _platypusNextSequenceId = 0xB5; + _platypusJumpSequenceId = _platypusNextSequenceId; + } else { + ++soundCtr; + if (soundCtr % 4 == 0) + _vm->playSound(0xD6, false); + } + } + } else { + _platypusNextSequenceId = 182 - (_platypusJumpSequenceId - 118); + } + isCollision = false; + isIdle = false; + } + _vm->gameUpdateTick(); + } + + if (!isIdle && _vm->_gameSys->getAnimationStatus(0) == 2) { + if (isJumpingRight(_platypusJumpSequenceId)) { + _vm->_gameSys->setAnimation(0x74, 256, 0); + _vm->_gameSys->insertSequence(0x74, 256, _platypusSequenceId, 256, kSeqSyncWait, 0, getPosRight(_platypusJumpSequenceId) - 362, 0); + _platypusSequenceId = 0x74; + } else { + _vm->_gameSys->setAnimation(0x75, 256, 0); + _vm->_gameSys->insertSequence(0x75, 256, _platypusSequenceId, 256, kSeqSyncWait, 0, getPosLeft(_platypusJumpSequenceId) - 341, 0); + _platypusSequenceId = 0x75; + } + waitForAnim(0); + isIdle = true; + } + } + + _vm->clearKeyStatus1(Common::KEYCODE_ESCAPE); + _vm->clearKeyStatus1(Common::KEYCODE_UP); + _vm->clearKeyStatus1(Common::KEYCODE_SPACE); + _vm->clearKeyStatus1(Common::KEYCODE_RIGHT); + _vm->clearKeyStatus1(Common::KEYCODE_LEFT); + + _vm->_gameSys->setAnimation(0, 0, 0); + for (int i = 0; i < 6; ++i) + _vm->_gameSys->setAnimation(0, 0, i + 1); + + _vm->showCursor(); +} + +/*****************************************************************************/ + +Scene52::Scene52(GnapEngine *vm) : Scene(vm) { + _gameScore = 0; + _aliensInitialized = false; + _alienDirection = 0; + _soundToggle = false; + _arcadeScreenBottom = 0; + _shipsLeft = 0; + _shipPosX = 0; + _shipCannonPosX = 0; + _shipCannonPosY = 0; + _shipCannonFiring = false; + _shipCannonFired = false; + _shipCannonWidth = 0; + _shipCannonHeight = 0; + _shipCannonTopY = 0; + _shipMidX = 0; + _shipMidY = 0; + _shipFlag = false; + _alienSpeed = 0; + _alienWidth = 0; + _alienHeight = 0; + _alienLeftX = 0; + _alienTopY = 0; + _alienRowDownCtr = 0; + _alienWave = false; + _alienSingle = false; + _alienCounter = 0; + _bottomAlienFlag = false; + _aliensCount = 0; + _nextUfoSequenceId = -1; + _ufoSequenceId = -1; +} + +int Scene52::init() { + initAnims(); + return 0x2B; +} + +void Scene52::updateHotspots() { + _vm->_hotspotsCount = 0; +} + +void Scene52::update() { + for (int rowNum = 0; rowNum < 7 && !_vm->_gameDone; ++rowNum) { + _vm->gameUpdateTick(); + if (_vm->_gameSys->getAnimationStatus(_alienRowAnims[rowNum]) == 2) { + updateAlienRow(rowNum); + rowNum = 0; + } + } + + if (_liveAlienRows == 0 && !_alienSingle) { + _alienWave = false; + _vm->playSound(0x30, false); + ++_alienCounter; + if (_alienCounter != 3) { + _vm->_timers[0] = 50; + _vm->_timers[2] = 100; + _alienRowDownCtr = 0; + _alienSingle = true; + } + } + + if (_alienSingle && !_vm->_timers[0]) { + initAliens(); + _alienSingle = false; + _vm->_timers[2] = 5; + _alienWave = true; + } + + if ((_alienRowDownCtr || _liveAlienRows == 0) && !_alienSingle) { + moveDownAlienRow(); + _alienRowDownCtr = 0; + } + + if (_vm->isKeyStatus1(Common::KEYCODE_UP) || _vm->isKeyStatus1(Common::KEYCODE_SPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_SPACE); + _vm->clearKeyStatus1(Common::KEYCODE_UP); + if (!_aliensCount) + fireShipCannon(_shipPosX); + } + + if (_shipCannonFiring) + updateShipCannon(); + + fireAlienCannon(); + updateAlienCannons(); + + if (_aliensCount == 1) { + _alienWave = false; + _vm->_timers[3] = 20; + _vm->_timers[2] = 100; + ++_aliensCount; + } + + if (_aliensCount && !_vm->_timers[3]) { + updateAliens(); + loseShip(); + if (_shipsLeft != 0) { + _vm->_timers[3] = 40; + while (_vm->_timers[3] && !_vm->_gameDone) { + updateAlienCannons(); + if (_shipCannonFiring) + updateShipCannon(); + _vm->gameUpdateTick(); + } + initAliens(); + _shipPosX = (800 - _shipMidX) / 2; + _vm->_gameSys->setAnimation(_nextUfoSequenceId, 256, 7); + _vm->_gameSys->insertSequence(_nextUfoSequenceId, 256, 0, 0, kSeqNone, 0, _shipPosX, _arcadeScreenBottom); + _ufoSequenceId = _nextUfoSequenceId; + _vm->_timers[2] = 5; + _alienWave = true; + } else { + _vm->_sceneDone = true; + } + } + + _nextUfoSequenceId = 34; + if (_ufoSequenceId != 34) + _shipFlag = true; + + if (_shipFlag) { + if (_vm->_gameSys->getAnimationStatus(7) == 2) { + _vm->_gameSys->setAnimation(_nextUfoSequenceId, 256, 7); + _vm->_gameSys->insertSequence(_nextUfoSequenceId, 256, _ufoSequenceId, 256, kSeqSyncWait, 0, _shipPosX, _arcadeScreenBottom); + _ufoSequenceId = _nextUfoSequenceId; + } + _shipFlag = false; + } + + if (_alienWave && !_vm->_timers[0]) { + playSound(); + int delay = CLIP(_alienSpeed, 2, 10); + _vm->_timers[0] = delay; + } +} + +void Scene52::initShipCannon(int bottomY) { + _shipCannonFired = false; + _shipCannonWidth = MAX(_vm->_gameSys->getSpriteWidthById(14), _vm->_gameSys->getSpriteWidthById(16)); + _shipCannonHeight = MAX(_vm->_gameSys->getSpriteHeightById(14), _vm->_gameSys->getSpriteHeightById(16)); + _shipCannonTopY = bottomY - _shipCannonHeight; + _shipCannonFiring = false; +} + +void Scene52::initAlienCannons() { + for (int i = 0; i < 3; ++i) { + _alienCannonIds[i] = 0; + _alienCannonFired[i] = 0; + } + _alienCannonSequenceIds[0] = 30; + _alienCannonSequenceIds[1] = 31; + _alienCannonSequenceIds[2] = 32; +} + +void Scene52::fireShipCannon(int posX) { + if (_vm->_timers[1]) + return; + + int cannonNum = getFreeShipCannon(); + if (cannonNum != -1) { + _shipCannonPosX = _shipMidX / 2 + posX - _shipCannonWidth / 2; + _shipCannonPosY = _shipCannonTopY; + _vm->_gameSys->setAnimation(0x23, cannonNum + 256, cannonNum + 8); + _vm->_gameSys->insertSequence(0x23, cannonNum + 256, 0, 0, kSeqNone, 0, _shipCannonPosX, _shipCannonPosY); + _vm->playSound(0x2D, false); + if (shipCannonHitShield(cannonNum)) { + _vm->_gameSys->setAnimation(0, 0, cannonNum + 8); + _vm->_gameSys->removeSequence(0x23, cannonNum + 256, true); + } else { + _shipCannonFired = true; + _shipCannonPosY -= 13; + _shipCannonFiring = true; + } + _vm->_timers[1] = 5; + } +} + +void Scene52::fireAlienCannon() { + if (_vm->_timers[2]) + return; + + int cannonNum = getFreeAlienCannon(); + if (cannonNum != -1) { + int alienX1 = _alienLeftX + _alienRowXOfs[0]; + int alienX2 = _alienLeftX + _alienRowXOfs[0] + 5 * _alienWidth - (_alienWidth / 2 - 15); + _alienCannonPosX[cannonNum] = _vm->getRandom(alienX2 - alienX1) + alienX1; + _alienCannonPosY[cannonNum] = 104; + _alienCannonFired[cannonNum] = 1; + _vm->_gameSys->setAnimation(_alienCannonSequenceIds[cannonNum], _alienCannonIds[cannonNum] + 256, cannonNum + 9); + _vm->_gameSys->insertSequence(_alienCannonSequenceIds[cannonNum], _alienCannonIds[cannonNum] + 256, 0, 0, + kSeqNone, 0, _alienCannonPosX[cannonNum], _alienCannonPosY[cannonNum]); + _alienCannonPosY[cannonNum] -= 13; + _vm->_timers[2] = 5; + } +} + +int Scene52::getFreeShipCannon() { + if (!_shipCannonFired) + return 0; + return -1; +} + +int Scene52::getFreeAlienCannon() { + for (int i = 0; i < 3; ++i) + if (!_alienCannonFired[i]) + return i; + return -1; +} + +void Scene52::updateShipCannon() { + if (_shipCannonFired && _vm->_gameSys->getAnimationStatus(8) == 2) { + _shipCannonPosY -= 13; + if (_shipCannonPosY - 13 >= 135) { + if (updateHitAlien()) { + _vm->_gameSys->setAnimation(0, 0, 8); + _vm->_gameSys->removeSequence(35, 256, true); + _shipCannonFired = false; + drawScore(_gameScore); + } else { + _vm->_gameSys->setAnimation(35, 256, 8); + _vm->_gameSys->insertSequence(35, 256, 35, 256, kSeqSyncWait, 0, _shipCannonPosX, _shipCannonPosY); + _shipCannonPosY -= 13; + } + } else { + _vm->_gameSys->setAnimation(0, 0, 8); + _vm->_gameSys->removeSequence(35, 256, true); + _shipCannonFired = false; + } + } +} + +void Scene52::updateAlienCannons() { + for (int i = 0; i < 3; ++i) { + if (_alienCannonFired[i] && _vm->_gameSys->getAnimationStatus(i + 9) == 2) { + _alienCannonPosY[i] += 13; + if (_shipCannonHeight + _alienCannonPosY[i] + 13 <= 550) { + if (alienCannonHitShip(i)) { + _vm->_gameSys->setAnimation(0, 0, i + 9); + _alienCannonFired[i] = 0; + shipExplode(); + } else if (alienCannonHitShield(i)) { + _alienCannonFired[i] = 0; + } else { + _vm->_gameSys->insertSequence(_alienCannonSequenceIds[i], 1 - _alienCannonIds[i] + 256, 0, 0, + kSeqNone, 0, _alienCannonPosX[i], _alienCannonPosY[i]); + _vm->_gameSys->setAnimation(_alienCannonSequenceIds[i], 1 - _alienCannonIds[i] + 256, i + 9); + _alienCannonIds[i] = 1 - _alienCannonIds[i]; + _alienCannonPosY[i] += 13; + } + } else { + _vm->_gameSys->setAnimation(0, 0, i + 9); + _alienCannonFired[i] = 0; + } + } + } +} + +void Scene52::initAliens() { + if (!_aliensInitialized) { + initAlienSize(); + _aliensInitialized = true; + } + + _liveAlienRows = 0; + _alienSpeed = 0; + _bottomAlienFlag = false; + _aliensCount = 0; + _alienSingle = false; + _alienRowDownCtr = 0; + + initShields(); + + _alienRowKind[0] = -1; + _alienRowKind[1] = -1; + _alienRowKind[2] = -1; + _alienRowKind[3] = -1; + _alienRowKind[4] = _vm->getRandom(2) != 0 ? 24 : 27; + _alienRowKind[5] = _vm->getRandom(2) != 0 ? 25 : 28; + _alienRowKind[6] = _vm->getRandom(2) != 0 ? 26 : 29; + + for (int i = 0; i < 7; ++i) { + _alienRowAnims[i] = i; + _alienRowXOfs[i] = 0; + initAlienRowKind(i, _alienRowKind[i]); + insertAlienRow(i); + } +} + +void Scene52::initAlienRowKind(int rowNum, int alienKind) { + for (int i = 0; i < 5; ++i) + _items[rowNum][i] = alienKind; +} + +void Scene52::insertAlienRow(int rowNum) { + if (_alienRowKind[rowNum] >= 0) { + insertAlienRowAliens(rowNum); + _alienRowIds[rowNum] = 256; + _vm->_gameSys->setAnimation(_alienRowKind[rowNum], _alienRowIds[rowNum], _alienRowAnims[rowNum]); + ++_liveAlienRows; + } +} + +void Scene52::insertAlienRowAliens(int rowNum) { + int xOffs = _alienLeftX; + int yOffs = _alienTopY - 52 * rowNum - _alienHeight + 10; + for (int i = 0; i < 5; ++i) { + if (_items[rowNum][i] >= 0) { + _vm->_gameSys->insertSequence(_items[rowNum][i], i + 256, 0, 0, kSeqNone, 0, xOffs, yOffs); + ++_alienSpeed; + } + xOffs += _alienWidth; + } +} + +void Scene52::updateAlienRow(int rowNum) { + if (_alienRowKind[rowNum] != -1 && !checkAlienRow(rowNum)) { + updateAlienRowXOfs(); + _alienRowIds[rowNum] = -1; + int xOffs = _alienLeftX + _alienRowXOfs[rowNum]; + int yOffs = _alienTopY - 52 * rowNum - _alienHeight + 10; + for (int i = 0; i < 5; ++i) { + if (_items[rowNum][i] >= 0) { + _vm->_gameSys->insertSequence(_items[rowNum][i], i + 256, _items[rowNum][i], i + 256, kSeqSyncWait, 0, xOffs, yOffs); + if (_alienRowIds[rowNum] == -1) + _alienRowIds[rowNum] = i + 256; + } else if (_items[rowNum][i] == -2) { + _vm->_gameSys->removeSequence(_alienRowKind[rowNum], i + 256, true); + _items[rowNum][i] = -1; + --_alienSpeed; + } + xOffs += _alienWidth; + } + if (_alienRowIds[rowNum] == -1) { + _vm->_gameSys->setAnimation(0, 0, _alienRowAnims[rowNum]); + // MessageBoxA(0, "No live aliens!", "Error 3:", 0x30u); + } else { + _vm->_gameSys->setAnimation(_alienRowKind[rowNum], _alienRowIds[rowNum], _alienRowAnims[rowNum]); + } + if (rowNum == 1) { + for (int j = 0; j < 3; ++j) { + if (_shieldSpriteIds[j] != -1) { + _vm->_gameSys->fillSurface(nullptr, _shieldPosX[j], _arcadeScreenBottom - 44, 33, 44, 0, 0, 0); + _shieldSpriteIds[j] = -1; + } + } + } + if (rowNum == 0 && _bottomAlienFlag) + shipExplode(); + } +} + +void Scene52::moveDownAlienRow() { + int v2[5], v3, v1, v0, v4; + + for (int i = 0; i < 5; ++i) + v2[i] = _items[0][i]; + + v3 = _alienRowIds[0]; + v1 = _alienRowAnims[0]; + v0 = _alienRowKind[0]; + v4 = _alienRowXOfs[0]; + + for (int j = 0; j < 7; ++j) { + for (int i = 0; i < 5; ++i) + _items[j][i] = _items[j + 1][i]; + _alienRowIds[j] = _alienRowIds[j + 1]; + _alienRowAnims[j] = _alienRowAnims[j + 1]; + _alienRowKind[j] = _alienRowKind[j + 1]; + _alienRowXOfs[j] = _alienRowXOfs[j + 1]; + } + + for (int i = 0; i < 5; ++i) + _items[6][i] = v2[i]; + + _alienRowIds[6] = v3; + _alienRowAnims[6] = v1; + _alienRowKind[6] = v0; + _alienRowXOfs[6] = v4; + + updateAlien(6); + initAlienRowKind(6, _alienRowKind[6]); + insertAlienRow(6); + + _bottomAlienFlag = _alienRowKind[0] > -1; +} + +int Scene52::updateHitAlien() { + int result = 0, rowNum, ya; + + int y = _shipCannonTopY - _shipCannonPosY; + + if (y == 26) { + rowNum = 1; + ya = _shipCannonPosY + 26; + } else { + if (y % 52) + return 0; + rowNum = y / 52 + 1; + ya = _shipCannonPosY; + } + + if (rowNum < 7) { + int hitAlienNum = getHitAlienNum(rowNum); + if (hitAlienNum != -1 && _items[rowNum][hitAlienNum] >= 0) { + _gameScore = ((_items[rowNum][hitAlienNum] - 24) % 3 + _gameScore + 1) % 1000; + _items[rowNum][hitAlienNum] = -2; + _vm->playSound(0x2C, false); + _vm->_gameSys->insertSequence(0x21, 266, 0, 0, + kSeqNone, 0, _alienLeftX + hitAlienNum * _alienWidth + _alienRowXOfs[rowNum] - 10, ya - _alienHeight); + result = 1; + } + } + + return result; +} + +int Scene52::getHitAlienNum(int rowNum) { + int result = -1; + + int v3 = _alienLeftX + _alienRowXOfs[rowNum]; + + if (_shipCannonPosX >= v3) { + int v8 = _alienWidth / 2 - 15; + if (v3 + 5 * _alienWidth - v8 >= _shipCannonPosX) { + int v4 = v3 + _alienWidth; + if (_shipCannonPosX >= v4 - v8) { + int v5 = v4 + _alienWidth; + if (_shipCannonPosX >= v5 - v8) { + int v6 = v5 + _alienWidth; + if (_shipCannonPosX >= v6 - v8) { + int v7 = v6 + _alienWidth; + if (_shipCannonPosX >= v7 - v8) { + if (_shipCannonPosX >= v7 + _alienWidth - v8) + result = -1; + else + result = 4; + } else { + result = 3; + } + } else { + result = 2; + } + } else { + result = 1; + } + } else { + result = 0; + } + } else { + result = -1; + } + } else { + result = -1; + } + return result; +} + +int Scene52::alienCannonHitShip(int cannonNum) { + int result = 0; + + if (_aliensCount) { + result = 0; + } else { + int cannonY = _alienCannonPosY[cannonNum] - 13; + if (_arcadeScreenBottom <= cannonY) { + if (_shipMidY + _arcadeScreenBottom > cannonY) { + if (_alienCannonPosX[cannonNum] >= _shipPosX) + result = _alienCannonPosX[cannonNum] < _shipMidX + _shipPosX; + else + result = 0; + } else { + result = 0; + } + } else { + result = 0; + } + } + return result; +} + +int Scene52::alienCannonHitShield(int cannonNum) { + int result = 0; + + int v3 = _alienCannonPosY[cannonNum] + 39; + if (_arcadeScreenBottom - 44 > v3) + return 0; + + if (_arcadeScreenBottom <= v3) + return 0; + + if (_alienCannonPosX[cannonNum] < _shieldPosX[0]) + return 0; + + if (_alienCannonPosX[cannonNum] > _shieldPosX[2] + 33) + return 0; + + int shieldNum = -1; + if (_alienCannonPosX[cannonNum] < _shieldPosX[0] + 33) + shieldNum = 0; + + if (shieldNum < 0 && _alienCannonPosX[cannonNum] < _shieldPosX[1]) + return 0; + + if (shieldNum < 0 && _alienCannonPosX[cannonNum] < _shieldPosX[1] + 33) + shieldNum = 1; + + if (shieldNum < 0) { + if (_alienCannonPosX[cannonNum] < _shieldPosX[2]) + return 0; + shieldNum = 2; + } + + if (_shieldSpriteIds[shieldNum] == -1) { + result = 0; + } else { + ++_shieldSpriteIds[shieldNum]; + if (_shieldSpriteIds[shieldNum] <= 21) { + _vm->_gameSys->drawSpriteToBackground(_shieldPosX[shieldNum], _arcadeScreenBottom - 44, _shieldSpriteIds[shieldNum]); + } else { + _vm->_gameSys->fillSurface(nullptr, _shieldPosX[shieldNum], _arcadeScreenBottom - 44, 33, 44, 0, 0, 0); + _shieldSpriteIds[shieldNum] = -1; + } + _vm->_gameSys->setAnimation(0, 0, cannonNum + 9); + _vm->_gameSys->insertSequence(0x21, shieldNum + 257, 0, 0, kSeqNone, 0, _alienCannonPosX[cannonNum] - 18, _arcadeScreenBottom - 44); + _vm->playSound(0x2C, false); + result = 1; + } + + return result; +} + +bool Scene52::shipCannonHitShield(int cannonNum) { + bool result = false; + + if (_shipCannonPosX < _shieldPosX[0]) + return result; + + if (_shipCannonPosX > _shieldPosX[2] + 33) + return result; + + int shieldNum = -1; + if (_shipCannonPosX < _shieldPosX[0] + 33) + shieldNum = 0; + + if (shieldNum < 0 && _shipCannonPosX < _shieldPosX[1]) + return result; + + if (shieldNum < 0 && _shipCannonPosX < _shieldPosX[1] + 33) + shieldNum = 1; + + if (shieldNum < 0) { + if (_shipCannonPosX < _shieldPosX[2]) + return result; + shieldNum = 2; + } + + if (_shieldSpriteIds[shieldNum] == -1) { + result = false; + } else { + ++_shieldSpriteIds[shieldNum]; + if (_shieldSpriteIds[shieldNum] <= 21) { + _vm->_gameSys->drawSpriteToBackground(_shieldPosX[shieldNum], _arcadeScreenBottom - 44, _shieldSpriteIds[shieldNum]); + } else { + _vm->_gameSys->fillSurface(nullptr, _shieldPosX[shieldNum], _arcadeScreenBottom - 44, 33, 44, 0, 0, 0); + _shieldSpriteIds[shieldNum] = -1; + } + _vm->_gameSys->insertSequence(0x21, shieldNum + 257, 0, 0, kSeqNone, 0, _shipCannonPosX - 18, _arcadeScreenBottom - 44); + _vm->playSound(0x2C, false); + result = true; + } + + return result; +} + +bool Scene52::shipCannonHitAlien() { + bool result = false; + + if (_aliensCount || checkAlienRow(0)) + return false; + + int alienNextX = _alienLeftX + _alienRowXOfs[0]; + if (_shipMidX + _shipPosX >= alienNextX) { + int startX = _alienWidth / 2 - 15; + if (alienNextX + 5 * _alienWidth - startX >= _shipPosX) { + int alienNextDeltaX = alienNextX + _alienWidth; + if (_items[0][0] <= -1 || alienNextDeltaX - startX <= _shipPosX) { + alienNextDeltaX += _alienWidth; + if (_items[0][1] <= -1 || alienNextDeltaX - startX <= _shipPosX) { + alienNextDeltaX += _alienWidth; + if (_items[0][2] <= -1 || alienNextDeltaX - startX <= _shipPosX) { + alienNextDeltaX += _alienWidth; + if (_items[0][3] <= -1 || alienNextDeltaX - startX <= _shipPosX) { + alienNextDeltaX += _alienWidth; + result = _items[0][4] > -1 && alienNextDeltaX - startX > _shipPosX; + } else { + result = true; + } + } else { + result = true; + } + } else { + result = true; + } + } else { + result = true; + } + } else { + result = false; + } + } else { + result = false; + } + + return result; +} + +void Scene52::shipExplode() { + if (!_aliensCount) { + _vm->_gameSys->setAnimation(0, 0, 7); + _vm->_gameSys->removeSequence(_ufoSequenceId, 256, true); + _vm->playSound(0x2C, false); + _vm->_gameSys->insertSequence(0x21, 266, 0, 0, kSeqNone, 0, _shipPosX, _arcadeScreenBottom); + _aliensCount = 1; + _vm->playSound(0x31, false); + } +} + +bool Scene52::checkAlienRow(int rowNum) { + for (int i = 0; i < 5; ++i) { + if (_items[rowNum][i] >= 0) + return false; + } + + bool found = false; + for (int j = 0; j < 5; ++j) + if (_items[rowNum][j] == -2) { + _vm->_gameSys->removeSequence(_alienRowKind[rowNum], j + 256, true); + _items[rowNum][j] = -1; + --_alienSpeed; + found = true; + } + + if (found) { + _vm->_gameSys->setAnimation(0, 0, _alienRowAnims[rowNum]); + --_liveAlienRows; + } + + if (_liveAlienRows < 0) + _liveAlienRows = 0; + + return true; +} + +void Scene52::updateAlienRowXOfs() { + int amount = 2 * (3 - _liveAlienRows) + 1; + + if (_alienSpeed == 2) + amount *= 4; + else if (_alienSpeed == 1) + amount *= 10; + + if (_alienDirection) { + for (int i = 0; i < 7; ++i) { + _alienRowXOfs[i] -= amount; + if (_alienRowXOfs[i] <= -100) { + _alienRowXOfs[i] = -100; + _alienDirection = 0; + ++_alienRowDownCtr; + } + } + } else { + for (int j = 0; j < 7; ++j) { + _alienRowXOfs[j] += amount; + if (_alienRowXOfs[j] >= 100) { + _alienRowXOfs[j] = 100; + _alienDirection = 1; + ++_alienRowDownCtr; + } + } + } +} + +void Scene52::initAlienSize() { + _alienWidth = _vm->_gameSys->getSpriteWidthById(0); + if (_vm->_gameSys->getSpriteWidthById(1) > _alienWidth) + _alienWidth = _vm->_gameSys->getSpriteWidthById(1); + if (_vm->_gameSys->getSpriteWidthById(4) > _alienWidth) + _alienWidth = _vm->_gameSys->getSpriteWidthById(4); + if (_vm->_gameSys->getSpriteWidthById(5) > _alienWidth) + _alienWidth = _vm->_gameSys->getSpriteWidthById(5); + if (_vm->_gameSys->getSpriteWidthById(12) > _alienWidth) + _alienWidth = _vm->_gameSys->getSpriteWidthById(12); + if (_vm->_gameSys->getSpriteWidthById(13) > _alienWidth) + _alienWidth = _vm->_gameSys->getSpriteWidthById(13); + + _alienHeight = _vm->_gameSys->getSpriteHeightById(0); + if (_vm->_gameSys->getSpriteHeightById(1) > _alienHeight) + _alienHeight = _vm->_gameSys->getSpriteHeightById(1); + if (_vm->_gameSys->getSpriteHeightById(4) > _alienHeight) + _alienHeight = _vm->_gameSys->getSpriteHeightById(4); + if (_vm->_gameSys->getSpriteHeightById(5) > _alienHeight) + _alienHeight = _vm->_gameSys->getSpriteHeightById(5); + if (_vm->_gameSys->getSpriteHeightById(12) > _alienHeight) + _alienHeight = _vm->_gameSys->getSpriteHeightById(12); + if (_vm->_gameSys->getSpriteHeightById(13) > _alienHeight) + _alienHeight = _vm->_gameSys->getSpriteHeightById(13); + + _alienTopY = _shipCannonTopY + 52; + _alienLeftX = (800 - 5 * _alienWidth) / 2; +} + +void Scene52::playSound() { + if (_soundToggle) { + _vm->playSound(0x2F, false); + _soundToggle = false; + } else { + _vm->playSound(0x2E, false); + _soundToggle = true; + } +} + +void Scene52::updateAliens() { + for (int i = 0; i < 7; ++i) + updateAlien(i); +} + +void Scene52::updateAlien(int rowNum) { + if (_alienRowKind[rowNum] >= 0 && !checkAlienRow(rowNum)) { + for (int i = 0; i < 5; ++i) { + if (_items[rowNum][i] >= 0) + _items[rowNum][i] = -2; + } + checkAlienRow(rowNum); + } +} + +void Scene52::loseShip() { + --_shipsLeft; + if (_shipsLeft == 2) { + _vm->_gameSys->fillSurface(nullptr, 120, 140, _shipMidX, _shipMidY, 0, 0, 0); + } else if (_shipsLeft == 1) { + _vm->_gameSys->fillSurface(nullptr, 120, 185, _shipMidX, _shipMidY, 0, 0, 0); + } +} + +void Scene52::initShields() { + for (int i = 0; i < 3; ++i) { + _vm->_gameSys->drawSpriteToBackground(_shieldPosX[i], _arcadeScreenBottom - 44, 17); + _shieldSpriteIds[i] = 17; + } +} + +void Scene52::initAnims() { + for (int i = 0; i < 7; ++i) + _vm->_gameSys->setAnimation(0, 0, i); + _vm->_gameSys->setAnimation(0, 0, 7); + for (int j = 0; j < 1; ++j) + _vm->_gameSys->setAnimation(0, 0, j + 8); + for (int k = 0; k < 3; ++k) + _vm->_gameSys->setAnimation(0, 0, k + 9); +} + +void Scene52::drawScore(int score) { + char str[4]; + sprintf(str, "%03d", score); + _vm->_gameSys->fillSurface(nullptr, 420, 80, 48, 30, 0, 0, 0); + _vm->_gameSys->drawTextToSurface(nullptr, 420, 80, 255, 255, 255, str); +} + +void Scene52::run() { + _vm->_timers[1] = 0; + + _vm->hideCursor(); + + _gameScore = 0; + _vm->_gameSys->drawTextToSurface(nullptr, 300, 80, 255, 255, 255, "SCORE"); + _vm->_gameSys->drawTextToSurface(nullptr, 468, 80, 255, 255, 255, "0"); + + drawScore(0); + + _shipMidX = 33; + _shipMidY = _vm->_gameSys->getSpriteHeightById(15); + _shipPosX = (800 - _shipMidX) / 2; + _arcadeScreenBottom = 496; + _arcadeScreenRight = 595 - _shipMidX; + _arcadeScreenLeft = 210; + _shipsLeft = 3; + _alienCounter = 0; + + _shieldPosX[0] = 247; + _shieldPosX[1] = 387; + _shieldPosX[2] = 525; + + for (int i = 0; i < 3; ++i) + _shieldSpriteIds[i] = -1; + + _vm->_gameSys->drawSpriteToBackground(120, 140, 0xF); + _vm->_gameSys->drawSpriteToBackground(120, 185, 0xF); + + initShipCannon(_arcadeScreenBottom); + initAlienCannons(); + initAliens(); + + _nextUfoSequenceId = 0x22; + _vm->_gameSys->setAnimation(0x22, 256, 7); + _vm->_gameSys->insertSequence(_nextUfoSequenceId, 256, 0, 0, kSeqNone, 0, _shipPosX, _arcadeScreenBottom); + + _ufoSequenceId = _nextUfoSequenceId; + + _vm->clearKeyStatus1(Common::KEYCODE_RIGHT); + _vm->clearKeyStatus1(Common::KEYCODE_LEFT); + _vm->clearKeyStatus1(Common::KEYCODE_SPACE); + _vm->clearKeyStatus1(Common::KEYCODE_UP); + _vm->clearKeyStatus1(Common::KEYCODE_ESCAPE); + + _vm->_timers[2] = 5; + _shipFlag = false; + + _vm->_timers[0] = 10; + _alienWave = true; + + while (!_vm->_sceneDone) { + _vm->gameUpdateTick(); + + while (_vm->isKeyStatus2(Common::KEYCODE_RIGHT)) { + update(); + if (_vm->_gameSys->getAnimationStatus(7) == 2) { + if (_shipPosX < _arcadeScreenRight) { + _shipPosX += 15; + if (_shipPosX > _arcadeScreenRight) + _shipPosX = _arcadeScreenRight; + _vm->_gameSys->setAnimation(_nextUfoSequenceId, 256, 7); + _vm->_gameSys->insertSequence(_nextUfoSequenceId, 256, _ufoSequenceId, 256, kSeqSyncWait, 0, _shipPosX, _arcadeScreenBottom); + _ufoSequenceId = _nextUfoSequenceId; + if (_bottomAlienFlag && shipCannonHitAlien()) + shipExplode(); + } + break; + } + } + + while (_vm->isKeyStatus2(Common::KEYCODE_LEFT)) { + update(); + if (_vm->_gameSys->getAnimationStatus(7) == 2) { + if (_shipPosX > _arcadeScreenLeft) { + _shipPosX -= 15; + if (_shipPosX < _arcadeScreenLeft) + _shipPosX = _arcadeScreenLeft; + _vm->_gameSys->setAnimation(_nextUfoSequenceId, 256, 7); + _vm->_gameSys->insertSequence(_nextUfoSequenceId, 256, _ufoSequenceId, 256, kSeqSyncWait, 0, _shipPosX, _arcadeScreenBottom); + _ufoSequenceId = _nextUfoSequenceId; + if (_bottomAlienFlag && shipCannonHitAlien()) + shipExplode(); + } + break; + } + } + + update(); + + if (clearKeyStatus()) { + _alienWave = false; + _vm->_gameSys->waitForUpdate(); + initAnims(); + _vm->clearKeyStatus1(Common::KEYCODE_SPACE); + _vm->_sceneDone = true; + } + } + _vm->_gameSys->waitForUpdate(); +} + +} // End of namespace Gnap diff --git a/engines/gnap/scenes/arcade.h b/engines/gnap/scenes/arcade.h new file mode 100644 index 0000000000..e472e00508 --- /dev/null +++ b/engines/gnap/scenes/arcade.h @@ -0,0 +1,290 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GNAP_ARCADE_H +#define GNAP_ARCADE_H + +#include "gnap/debugger.h" + +namespace Gnap { + +class GnapEngine; +class CutScene; + +struct Scene49Obstacle { + int _currSequenceId; + int _closerSequenceId; + int _passedSequenceId; + int _splashSequenceId; + int _collisionSequenceId; + int _prevId; + int _currId; + int _laneNum; +}; + +struct ObstacleDef { + int _sequenceId; + int _ticks; +}; + +class Scene49: public Scene { +public: + Scene49(GnapEngine *vm); + ~Scene49() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _scoreBarPos; + int _scoreLevel; + bool _scoreBarFlash; + int _obstacleIndex; + Scene49Obstacle _obstacles[5]; + int _truckSequenceId; + int _truckId; + int _truckLaneNum; + + void checkObstacles(); + void updateObstacle(int id); + void increaseScore(int amount); + void decreaseScore(int amount); + void refreshScoreBar(); + void clearObstacle(int index); +}; + +/*****************************************************************************/ + +class Scene50: public Scene { +public: + Scene50(GnapEngine *vm); + ~Scene50() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + bool _fightDone; + int _timesPlayed; + int _timesPlayedModifier; + int _attackCounter; + int _roundNum; + int _timeRemaining; + int _leftTongueRoundsWon; + int _rightTongueRoundsWon; + int _leftTongueEnergyBarPos; + int _rightTongueEnergyBarPos; + int _leftTongueSequenceId; + int _leftTongueId; + int _leftTongueNextSequenceId; + int _leftTongueNextId; + int _leftTongueNextIdCtr; + int _rightTongueSequenceId; + int _rightTongueId; + int _rightTongueNextSequenceId; + int _rightTongueNextId; + int _rightTongueNextIdCtr; + int _leftTongueEnergy; + int _rightTongueEnergy; + + bool tongueWinsRound(int tongueNum); + void playWinAnim(int tongueNum, bool fightOver); + void delayTicks(); + void initRound(); + bool updateCountdown(); + void drawCountdown(int value); + void playTonguesIdle(); + void playRoundAnim(int roundNum); + bool updateEnergyBars(int newLeftBarPos, int newRightBarPos); + void waitForAnim(int animationIndex); + int checkInput(); + int getRightTongueAction(); + int getRightTongueActionTicks(); + int getLeftTongueNextId(); + int getRightTongueNextId(); + void playWinBadgeAnim(int tongueNum); +}; + +/*****************************************************************************/ + +struct Scene51Item { + int _currSequenceId; + int _droppedSequenceId; + int _x, _y; + int _collisionX; + bool _canCatch; + bool _isCollision; + int _x2; + int _id; +}; + +class Scene51: public Scene { +public: + Scene51(GnapEngine *vm); + ~Scene51() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations() {}; + virtual void updateAnimationsCb() {}; + +private: + bool _dropLoseCash; + + int _cashAmount; + int _digits[4]; + int _digitSequenceIds[4]; + int _guySequenceId; + int _guyNextSequenceId; + int _itemsCaughtCtr; + int _dropSpeedTicks; + int _nextDropItemKind; + int _itemInsertX; + int _itemInsertDirection; + int _platypusSequenceId; + int _platypusNextSequenceId; + int _platypusJumpSequenceId; + int _itemsCtr; + int _itemsCtr1; + int _itemsCtr2; + + Scene51Item _items[6]; + + void clearItem(Scene51Item *item); + void dropNextItem(); + void updateItemAnimations(); + int checkCollision(int sequenceId); + void updateItemAnimation(Scene51Item *item, int index); + void removeCollidedItems(); + int itemIsCaught(Scene51Item *item); + bool isJumpingRight(int sequenceId); + bool isJumpingLeft(int sequenceId); + bool isJumping(int sequenceId); + void waitForAnim(int animationIndex); + int getPosRight(int sequenceId); + int getPosLeft(int sequenceId); + void playIntroAnim(); + void updateGuyAnimation(); + int incCashAmount(int sequenceId); + void winMinigame(); + void playCashAppearAnim(); + void updateCash(int amount); + void drawDigit(int digit, int position); + void initCashDisplay(); +}; + +/*****************************************************************************/ + +class Scene52: public Scene { +public: + Scene52(GnapEngine *vm); + ~Scene52() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations() {}; + virtual void updateAnimationsCb() {}; + +private: + int _liveAlienRows; + int _gameScore; + bool _soundToggle; + int _arcadeScreenLeft; + int _arcadeScreenRight; + int _arcadeScreenBottom; + int _shipsLeft; + int _shieldSpriteIds[3]; + int _shieldPosX[3]; + int _shipPosX; + int _shipCannonPosX, _shipCannonPosY; + bool _shipCannonFiring; + bool _shipCannonFired; + int _shipCannonWidth, _shipCannonHeight; + int _shipCannonTopY; + int _shipMidX, _shipMidY; + bool _shipFlag; + bool _aliensInitialized; + int _alienSpeed, _alienDirection; + int _alienWidth, _alienHeight; + int _alienLeftX, _alienTopY; + int _alienRowDownCtr; + int _alienRowKind[8]; + int _alienRowAnims[8]; + int _alienRowIds[8]; + int _alienRowXOfs[8]; + int _alienCannonFired[3]; + int _alienCannonPosX[3]; + int _alienCannonPosY[3]; + int _alienCannonSequenceIds[3]; + int _alienCannonIds[3]; + bool _alienWave, _alienSingle; + int _alienCounter; + bool _bottomAlienFlag; + int _aliensCount; + int _items[8][5]; + int _nextUfoSequenceId, _ufoSequenceId; + + void update(); + void initShipCannon(int bottomY); + void initAlienCannons(); + void fireShipCannon(int posX); + void fireAlienCannon(); + int getFreeShipCannon(); + int getFreeAlienCannon(); + void updateShipCannon(); + void updateAlienCannons(); + void initAliens(); + void initAlienRowKind(int rowNum, int alienKind); + void insertAlienRow(int rowNum); + void insertAlienRowAliens(int rowNum); + void updateAlienRow(int rowNum); + void moveDownAlienRow(); + int updateHitAlien(); + int getHitAlienNum(int rowNum); + int alienCannonHitShip(int cannonNum); + int alienCannonHitShield(int cannonNum); + bool shipCannonHitShield(int cannonNum); + bool shipCannonHitAlien(); + void shipExplode(); + bool checkAlienRow(int rowNum); + void updateAlienRowXOfs(); + void initAlienSize(); + void playSound(); + void updateAliens(); + void updateAlien(int rowNum); + void loseShip(); + void initShields(); + void initAnims(); + void drawScore(int score); +}; + +} // End of namespace Gnap + +#endif // GNAP_ARCADE_H diff --git a/engines/gnap/scenes/group0.cpp b/engines/gnap/scenes/group0.cpp new file mode 100644 index 0000000000..b2351b08ad --- /dev/null +++ b/engines/gnap/scenes/group0.cpp @@ -0,0 +1,3570 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gnap/gnap.h" +#include "gnap/character.h" +#include "gnap/gamesys.h" +#include "gnap/resource.h" +#include "gnap/scenes/group0.h" + +namespace Gnap { + +Scene01::Scene01(GnapEngine *vm) : Scene(vm) { + _pigsIdCtr = 0; + _smokeIdCtr = 0; + _spaceshipSurface = nullptr; +} + +Scene01::~Scene01() { + delete _spaceshipSurface; +} + +int Scene01::init() { + GameSys& gameSys = *_vm->_gameSys; + + gameSys.setAnimation(0, 0, 0); + gameSys.setAnimation(0, 0, 3); + return 0x88; +} + +void Scene01::updateHotspots() { + _vm->setHotspot(kHS01Platypus, 0, 0, 0, 0, SF_DISABLED | SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS01ExitTruck, 780, 226, 800, 455, SF_EXIT_R_CURSOR | SF_WALKABLE, 10, 6); + _vm->setHotspot(kHS01Mud, 138, 282, 204, 318, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 3, 5); + _vm->setHotspot(kHS01Pigs, 408, 234, 578, 326, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 6, 4); + _vm->setHotspot(kHS01Spaceship, 0, 200, 94, 292, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 1, 6); + _vm->setHotspot(kHS01WalkArea1, 0, 0, 162, 426); + _vm->setHotspot(kHS01WalkArea2, 162, 0, 237, 396); + _vm->setHotspot(kHS01WalkArea3, 237, 0, 319, 363); + _vm->setHotspot(kHS01WalkArea4, 520, 0, 800, 404); + _vm->setHotspot(kHS01WalkArea5, 300, 447, 800, 600); + _vm->setHotspot(kHS01WalkArea6, 678, 0, 800, 404); + _vm->setHotspot(kHS01WalkArea7, 0, 0, 520, 351); + _vm->setHotspot(kHS01WalkArea8, 0, 546, 300, 600); + _vm->setDeviceHotspot(kHS01Device, -1, -1, -1, -1); + if (_vm->isFlag(kGFPlatypus)) + _vm->_hotspots[kHS01Platypus]._flags = SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR; + if (_vm->isFlag(kGFMudTaken)) + _vm->_hotspots[kHS01Mud]._flags = SF_WALKABLE | SF_DISABLED; + _vm->_hotspotsCount = 14; +} + +void Scene01::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->playSound(0x1091C, true); + _vm->startSoundTimerC(5); + + gameSys.setAnimation(134, 20, 4); + gameSys.insertSequence(134, 20, 0, 0, kSeqNone, 0, 0, 0); + + gameSys.setAnimation(0x7F, 40, 2); + gameSys.insertSequence(0x7F, 40, 0, 0, kSeqNone, 0, 0, 0); + + _vm->_timers[4] = _vm->getRandom(100) + 300; + + if (!_vm->isFlag(kGFMudTaken)) + gameSys.insertSequence(129, 40, 0, 0, kSeqNone, 0, 0, 0); + + _vm->queueInsertDeviceIcon(); + + if (_vm->_prevSceneNum == 2) { + gnap.initPos(11, 6, kDirBottomLeft); + if (_vm->isFlag(kGFPlatypus)) + plat.initPos(12, 6, kDirIdleRight); + _vm->endSceneInit(); + if (_vm->isFlag(kGFPlatypus)) + plat.walkTo(Common::Point(9, 6), -1, 0x107C2, 1); + gnap.walkTo(Common::Point(8, 6), -1, 0x107B9, 1); + } else { + gnap.initPos(1, 6, kDirBottomRight); + if (_vm->isFlag(kGFPlatypus)) + plat.initPos(1, 7, kDirIdleLeft); + _vm->endSceneInit(); + } + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + _vm->testWalk(0, 3, -1, -1, -1, -1); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS01Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS01Platypus: + if (gnap._actionStatus < 0 && _vm->isFlag(kGFPlatypus)) { + if (_vm->_grabCursorSpriteIndex == kItemDisguise) { + gnap.useDisguiseOnPlatypus(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + if (_vm->isFlag(kGFKeysTaken)) + gnap.playMoan1(plat._pos); + else + gnap.playScratchingHead(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS01Spaceship: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[4], 0, 2); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap._idleFacing = kDirUpLeft; + if (gnap.walkTo(_vm->_hotspotsWalkPos[4], 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1)) + gnap._actionStatus = kAS01LookSpaceship; + break; + case GRAB_CURSOR: + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS01Mud: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[2], 2, 3); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(3, 3)); + break; + case GRAB_CURSOR: + gnap.walkTo(_vm->_hotspotsWalkPos[2], 0, gnap.getSequenceId(kGSIdle, Common::Point(2, 3)) | 0x10000, 1); + gnap._actionStatus = kAS01TakeMud; + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS01Pigs: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[3], 7, 2); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.walkTo(_vm->_hotspotsWalkPos[3], 0, gnap.getSequenceId(kGSIdle, Common::Point(7, 2)) | 0x10000, 1); + gnap._actionStatus = kAS01LookPigs; + break; + case GRAB_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.walkTo(_vm->_hotspotsWalkPos[3], 0, gnap.getSequenceId(kGSIdle, Common::Point(7, 2)) | 0x10000, 1); + gnap._actionStatus = kAS01UsePigs; + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.walkTo(_vm->_hotspotsWalkPos[3], 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(7, 2)) | 0x10000, 1); + gnap._actionStatus = kAS01LookPigs; + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS01ExitTruck: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + gnap.walkTo(_vm->_hotspotsWalkPos[1], 0, 0x107AB, 1); + gnap._actionStatus = kAS01LeaveScene; + if (_vm->isFlag(kGFPlatypus)) + plat.walkTo(_vm->_hotspotsWalkPos[1] + Common::Point(0, 1), -1, 0x107CD, 1); + _vm->_newSceneNum = 2; + } + break; + + case kHS01WalkArea1: + case kHS01WalkArea2: + case kHS01WalkArea3: + case kHS01WalkArea4: + case kHS01WalkArea5: + case kHS01WalkArea6: + case kHS01WalkArea7: + case kHS01WalkArea8: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left && gnap._actionStatus < 0) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + + } + + updateAnimations(); + + if (!_vm->isSoundPlaying(0x1091C)) + _vm->playSound(0x1091C, true); + + if (!_vm->_isLeavingScene) { + if (plat._actionStatus < 0 && _vm->isFlag(kGFPlatypus)) + plat.updateIdleSequence(); + if (gnap._actionStatus < 0) + gnap.updateIdleSequence(); + if (_vm->_timers[4] == 0) { + // Update bird animation + _vm->_timers[4] = _vm->getRandom(100) + 300; + if (_vm->getRandom(1) == 0) + gameSys.insertSequence(0x84, 180, 0, 0, kSeqNone, 0, 0, 0); + else + gameSys.insertSequence(0x83, 180, 0, 0, kSeqNone, 0, 0, 0); + } + _vm->playSoundC(); + } + + _vm->checkGameKeys(); + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } +} + +void Scene01::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS01LookSpaceship: + _spaceshipSurface = gameSys.createSurface(47); + gameSys.insertSpriteDrawItem(_spaceshipSurface, 0, 0, 255); + gameSys.setAnimation(133, 256, 0); + gameSys.insertSequence(133, 256, 0, 0, kSeqNone, 0, 0, 0); + gnap._actionStatus = kAS01LookSpaceshipDone; + break; + + case kAS01LookSpaceshipDone: + gameSys.removeSequence(133, 256, true); + gameSys.removeSpriteDrawItem(_spaceshipSurface, 255); + _vm->deleteSurface(&_spaceshipSurface); + gnap._actionStatus = -1; + break; + + case kAS01LeaveScene: + _vm->_sceneDone = true; + break; + + case kAS01TakeMud: + gnap.playPullOutDevice(Common::Point(2, 3)); + gnap.playUseDevice(); + gameSys.insertSequence(128, 40, 129, 40, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(128, 40, 3); + gnap._actionStatus = -1; + break; + + case kAS01LookPigs: + _vm->playSound(0x8A, false); + _vm->playSound(0x8B, false); + _vm->playSound(0x8C, false); + gnap._actionStatus = -1; + break; + + case kAS01UsePigs: + gnap.playPullOutDevice(Common::Point(7, 2)); + gnap.playUseDevice(); + gameSys.insertSequence(135, 39, 0, 0, kSeqNone, 25, _vm->getRandom(140) - 40, 0); + gnap._actionStatus = -1; + break; + } + } + + if (gameSys.getAnimationStatus(3) == 2) { + gameSys.setAnimation(0, 0, 3); + _vm->invAdd(kItemMud); + _vm->setGrabCursorSprite(kItemMud); + _vm->setFlag(kGFMudTaken); + updateHotspots(); + } + + if (gameSys.getAnimationStatus(4) == 2) { + _smokeIdCtr = (_smokeIdCtr + 1) % 2; + gameSys.setAnimation(0x86, _smokeIdCtr + 20, 4); + gameSys.insertSequence(0x86, _smokeIdCtr + 20, + 0x86, (_smokeIdCtr + 1) % 2 + 20, + kSeqSyncWait, 0, 0, 0); + } + + if (gameSys.getAnimationStatus(2) == 2) { + _pigsIdCtr = (_pigsIdCtr + 1) % 2; + gameSys.setAnimation(0x7F, _pigsIdCtr + 40, 2); + gameSys.insertSequence(0x7F, _pigsIdCtr + 40, + 0x7F, (_pigsIdCtr + 1) % 2 + 40, + kSeqSyncWait, 0, 0, 0); + } +} + +/*****************************************************************************/ + +Scene02::Scene02(GnapEngine *vm) : Scene(vm) { + _truckGrillCtr = 0; + _nextChickenSequenceId = 0; + _currChickenSequenceId = 0; + _gnapTruckSequenceId = 0; +} + +int Scene02::init() { + _vm->_gameSys->setAnimation(0, 0, 0); + return _vm->isFlag(kGFTruckKeysUsed) ? 0x15A : 0x15B; +} + +void Scene02::updateHotspots() { + _vm->setHotspot(kHS02Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + if (!_vm->isFlag(kGFPlatypus)) + _vm->_hotspots[kHS02Platypus]._flags |= SF_DISABLED; + _vm->setHotspot(kHS02Chicken, 606, 455, 702, 568, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 7, 8); + _vm->setHotspot(kHS02Truck1, 385, 258, 464, 304, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 6, 5); + _vm->setHotspot(kHS02Truck2, 316, 224, 390, 376, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 5, 6); + _vm->setHotspot(kHS02TruckGrill, 156, 318, 246, 390, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 2, 7); + _vm->setHotspot(kHS02ExitHouse, 480, 120, 556, 240, SF_EXIT_U_CURSOR, 7, 5); + _vm->setHotspot(kHS02ExitBarn, 610, 0, 800, 164, SF_EXIT_U_CURSOR, 10, 5); + _vm->setHotspot(kHS02ExitCreek, 780, 336, 800, 556, SF_EXIT_R_CURSOR | SF_WALKABLE, 10, 8); + _vm->setHotspot(kHS02ExitPigpen, 0, 300, 20, 600, SF_EXIT_L_CURSOR | SF_WALKABLE, 0, 8); + _vm->setHotspot(kHS02WalkArea1, 92, 140, 304, 430, SF_NONE, 3, 1); + _vm->setHotspot(kHS02WalkArea2, 0, 0, 800, 380); + _vm->setHotspot(kHS02WalkArea3, 0, 0, 386, 445); + _vm->setHotspot(kHS02WalkArea4, 386, 0, 509, 410); + _vm->setDeviceHotspot(kHS02Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 14; +} + +void Scene02::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->playSound(0x1091C, true); + _vm->startSoundTimerC(6); + + _currChickenSequenceId = 0x14B; + gameSys.setAnimation(0x14B, 179, 2); + gameSys.insertSequence(0x14B, 179, 0, 0, kSeqNone, 0, 0, 0); + + _nextChickenSequenceId = -1; + _vm->_timers[5] = _vm->getRandom(20) + 30; + _vm->_timers[4] = _vm->getRandom(100) + 300; + + _vm->queueInsertDeviceIcon(); + + switch (_vm->_prevSceneNum) { + case 3: + gnap.initPos(11, 6, kDirBottomLeft); + if (_vm->isFlag(kGFPlatypus)) + plat.initPos(12, 6, kDirIdleRight); + _vm->endSceneInit(); + if (_vm->isFlag(kGFPlatypus)) + plat.walkTo(Common::Point(9, 6), -1, 0x107C2, 1); + gnap.walkTo(Common::Point(8, 6), -1, 0x107BA, 1); + break; + case 4: + gnap.initPos(_vm->_hotspotsWalkPos[6].x, _vm->_hotspotsWalkPos[6].y, kDirBottomLeft); + if (_vm->isFlag(kGFPlatypus)) + plat.initPos(_vm->_hotspotsWalkPos[6].x + 1, _vm->_hotspotsWalkPos[6].y, kDirIdleRight); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(7, 6), 0, 0x107B9, 1); + if (_vm->isFlag(kGFPlatypus)) + plat.walkTo(Common::Point(8, 6), 1, 0x107C2, 1); + updateHotspots(); + gameSys.waitForUpdate(); + break; + case 47: + _vm->clearFlag(kGFUnk25); + gnap.initPos(5, 6, kDirBottomLeft); + plat.initPos(6, 7, kDirIdleRight); + _vm->endSceneInit(); + break; + case 49: + gnap.initPos(5, 6, kDirBottomRight); + if (_vm->isFlag(kGFPlatypus)) + plat.initPos(6, 7, kDirIdleLeft); + _vm->endSceneInit(); + break; + default: + gnap.initPos(-1, 6, kDirBottomRight); + if (_vm->isFlag(kGFPlatypus)) + plat.initPos(-1, 7, kDirIdleLeft); + _vm->endSceneInit(); + if (_vm->isFlag(kGFPlatypus)) + plat.walkTo(Common::Point(2, 7), -1, 0x107C2, 1); + gnap.walkTo(Common::Point(2, 8), -1, 0x107B9, 1); + break; + } + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->testWalk(0, 6, 7, 6, 8, 6); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS02Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS02Platypus: + if (gnap._actionStatus < 0 && _vm->isFlag(kGFPlatypus)) { + if (_vm->_grabCursorSpriteIndex == kItemDisguise) { + gnap.useDisguiseOnPlatypus(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + if (_vm->isFlag(kGFKeysTaken)) + gnap.playMoan1(plat._pos); + else + gnap.playScratchingHead(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS02Chicken: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemTwig) { + gnap._idleFacing = kDirUpRight; + Common::Point destPos = _vm->_hotspotsWalkPos[_vm->_sceneClickedHotspot] + Common::Point(0, 1); + gnap.walkTo(destPos, 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS02UseTwigWithChicken; + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[1] + Common::Point(0, 1), 9, 8); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan2(Common::Point(9, 8)); + break; + case GRAB_CURSOR: + gnap._idleFacing = kDirBottomRight; + if (gnap.walkTo(_vm->_hotspotsWalkPos[1], 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1)) + gnap._actionStatus = kAS02GrabChicken; + else + gnap._actionStatus = -1; + break; + case TALK_CURSOR: + gnap._idleFacing = kDirBottomRight; + gnap.walkTo(_vm->_hotspotsWalkPos[1], 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS02TalkChicken; + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS02Truck1: + case kHS02Truck2: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemKeys) { + if (gnap.walkTo(_vm->_hotspotsWalkPos[3], 0, gnap.getSequenceId(kGSIdle, Common::Point(2, 2)) | 0x10000, 1)) { + _vm->setGrabCursorSprite(-1); + _vm->invRemove(kItemKeys); + if (_vm->isFlag(kGFTruckFilledWithGas)) + gnap._actionStatus = kAS02UseTruckGas; + else + gnap._actionStatus = kAS02UseTruckNoGas; + } + } else if (_vm->_grabCursorSpriteIndex == kItemGas) { + _vm->_hotspots[kHS02WalkArea4]._flags |= SF_WALKABLE; + if (gnap.walkTo(_vm->_hotspotsWalkPos[2], 0, gnap.getSequenceId(kGSIdle, Common::Point(2, 2)) | 0x10000, 1)) + gnap._actionStatus = kAS02UseGasWithTruck; + _vm->_hotspots[kHS02WalkArea4]._flags &= ~SF_WALKABLE; + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[2], 2, 2); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(2, 2)); + break; + case GRAB_CURSOR: + if (_vm->isFlag(kGFTruckKeysUsed)) { + if (gnap.walkTo(_vm->_hotspotsWalkPos[3], 0, gnap.getSequenceId(kGSIdle, Common::Point(2, 2)) | 0x10000, 1)) { + if (_vm->isFlag(kGFTruckFilledWithGas)) + gnap._actionStatus = kAS02UseTruckGas; + else + gnap._actionStatus = kAS02UseTruckNoGas; + } + } else { + gnap._idleFacing = kDirIdleRight; + if (gnap.walkTo(_vm->_hotspotsWalkPos[3], 0, gnap.getSequenceId(kGSIdle, Common::Point(2, 2)) | 0x10000, 1)) + gnap._actionStatus = kAS02UseTruckNoKeys; + } + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS02TruckGrill: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[4], 2, 4); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan2(Common::Point(2, 4)); + break; + case GRAB_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.walkTo(_vm->_hotspotsWalkPos[4], 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS02GrabTruckGrill; + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS02ExitHouse: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + gnap.walkTo(_vm->_hotspotsWalkPos[6], 0, 0x107AD, 1); + gnap._actionStatus = kAS02LeaveScene; + if (_vm->isFlag(kGFPlatypus)) + plat.walkTo(_vm->_hotspotsWalkPos[6] + Common::Point(1, 0), -1, 0x107C1, 1); + updateHotspots(); + _vm->_newSceneNum = 4; + } + break; + + case kHS02ExitBarn: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + gnap.walkTo(_vm->_hotspotsWalkPos[7], 0, 0x107AD, 1); + gnap._actionStatus = kAS02LeaveScene; + if (_vm->isFlag(kGFPlatypus)) + plat.walkTo(_vm->_hotspotsWalkPos[7] + Common::Point(1, 0), -1, 0x107C1, 1); + updateHotspots(); + _vm->_newSceneNum = 5; + } + break; + + case kHS02ExitCreek: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + gnap.walkTo(_vm->_hotspotsWalkPos[8], 0, 0x107AB, 1); + gnap._actionStatus = kAS02LeaveScene; + if (_vm->isFlag(kGFPlatypus)) + plat.walkTo(_vm->_hotspotsWalkPos[8], -1, 0x107CD, 1); + _vm->_newSceneNum = 3; + } + break; + + case kHS02ExitPigpen: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + gnap.walkTo(_vm->_hotspotsWalkPos[9], 0, 0x107AF, 1); + gnap._actionStatus = kAS02LeaveScene; + if (_vm->isFlag(kGFPlatypus)) + plat.walkTo(_vm->_hotspotsWalkPos[9], -1, 0x107CF, 1); + _vm->_newSceneNum = 1; + } + break; + + case kHS02WalkArea1: + case kHS02WalkArea2: + case kHS02WalkArea3: + case kHS02WalkArea4: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left && gnap._actionStatus < 0) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + } + + updateAnimations(); + + if (!_vm->isSoundPlaying(0x1091C)) + _vm->playSound(0x1091C, true); + + if (!_vm->_isLeavingScene) { + if (plat._actionStatus < 0 && _vm->isFlag(kGFPlatypus)) + plat.updateIdleSequence(); + if (gnap._actionStatus < 0) + gnap.updateIdleSequence(); + if (!_vm->_timers[4]) { + // Update bird animation + _vm->_timers[4] = _vm->getRandom(100) + 300; + if (_vm->getRandom(2) != 0) + gameSys.insertSequence(0x156, 256, 0, 0, kSeqNone, 0, 0, 0); + else + gameSys.insertSequence(0x154, 256, 0, 0, kSeqNone, 0, 0, 0); + } + if (!_vm->_timers[5] && _nextChickenSequenceId == -1 && gnap._actionStatus != 7 && gnap._actionStatus != 8) { + if (_vm->getRandom(6) != 0) { + _nextChickenSequenceId = 0x14B; + _vm->_timers[5] = _vm->getRandom(20) + 30; + } else { + _nextChickenSequenceId = 0x14D; + _vm->_timers[5] = _vm->getRandom(20) + 50; + } + } + _vm->playSoundC(); + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } +} + +void Scene02::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + switch (gnap._actionStatus) { + case kAS02UseTruckNoKeys: + gameSys.insertSequence(0x14E, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(0x14E, gnap._id, 0); + gnap._sequenceId = 0x14E; + gnap._sequenceDatNum = 0; + gnap._actionStatus = kAS02UseTruckNoKeysDone; + break; + case kAS02UseGasWithTruck: + gameSys.insertSequence(0x151, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(0x151, gnap._id, 0); + gnap._sequenceId = 0x151; + gnap._sequenceDatNum = 0; + _vm->invRemove(kItemGas); + _vm->setGrabCursorSprite(-1); + _vm->setFlag(kGFTruckFilledWithGas); + gnap._actionStatus = kAS02UseGasWithTruckDone; + break; + case kAS02UseTruckGas: + _vm->_timers[5] = 9999; + _vm->_timers[4] = 9999; + _vm->hideCursor(); + _vm->setGrabCursorSprite(-1); + if (!_vm->isFlag(kGFTruckKeysUsed)) { + gameSys.insertSequence(0x14F, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.waitForUpdate(); + _vm->setFlag(kGFTruckKeysUsed); + gnap._sequenceId = 0x14F; + gnap._sequenceDatNum = 0; + _vm->invRemove(kItemKeys); + _vm->setGrabCursorSprite(-1); + } + _vm->_newSceneNum = 47; + _vm->_sceneDone = true; + break; + case kAS02UseTruckNoGas: + _vm->hideCursor(); + _vm->setGrabCursorSprite(-1); + _vm->_timers[4] = 250; + if (!_vm->isFlag(kGFTruckKeysUsed)) { + gameSys.insertSequence(0x14F, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.waitForUpdate(); + _vm->setFlag(kGFTruckKeysUsed); + gnap._sequenceId = 0x14F; + gnap._sequenceDatNum = 0; + _vm->invRemove(kItemKeys); + _vm->setGrabCursorSprite(-1); + } + _vm->_newSceneNum = 47; + _vm->_sceneDone = true; + _vm->setFlag(kGFUnk25); + break; + case kAS02GrabTruckGrill: + switch (_truckGrillCtr) { + case 0: + _gnapTruckSequenceId = 0x158; + break; + case 1: + _gnapTruckSequenceId = 0x159; + break; + case 2: + _gnapTruckSequenceId = 0x157; + break; + } + _truckGrillCtr = (_truckGrillCtr + 1) % 3; + gameSys.insertSequence(_gnapTruckSequenceId, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_gnapTruckSequenceId, gnap._id, 0); + gnap._sequenceId = _gnapTruckSequenceId; + gnap._sequenceDatNum = 0; + gnap._actionStatus = -1; + break; + case kAS02LeaveScene: + _vm->_sceneDone = true; + break; + case kAS02TalkChicken: + _nextChickenSequenceId = 0x14C; + break; + case kAS02GrabChicken: + _nextChickenSequenceId = 0x150; + _vm->_timers[2] = 100; + break; + case kAS02GrabChickenDone: + gameSys.insertSequence(0x107B5, gnap._id, 0x150, 179, kSeqSyncWait, 0, 75 * gnap._pos.x - gnap._gridX, 48 * gnap._pos.y - gnap._gridY); + gnap._sequenceId = 0x7B5; + gnap._sequenceDatNum = 1; + _currChickenSequenceId = 0x14B; + gameSys.setAnimation(0x14B, 179, 2); + gameSys.insertSequence(_currChickenSequenceId, 179, 0, 0, kSeqNone, 0, 0, 0); + gnap._actionStatus = -1; + _vm->_timers[5] = 30; + break; + case kAS02UseTwigWithChicken: + gnap.playShowItem(5, 0, 0); + gameSys.insertSequence(0x155, 179, _currChickenSequenceId, 179, kSeqSyncExists, 0, 0, 0); + _currChickenSequenceId = 0x155; + _nextChickenSequenceId = -1; + gnap._actionStatus = -1; + break; + case kAS02UseTruckNoKeysDone: + case kAS02UseGasWithTruckDone: + default: + gnap._actionStatus = -1; + break; + } + } + + if (gameSys.getAnimationStatus(2) == 2) { + if (_nextChickenSequenceId == 0x150) { + gameSys.setAnimation(_nextChickenSequenceId, 179, 0); + gameSys.insertSequence(_nextChickenSequenceId, 179, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.removeSequence(_currChickenSequenceId, 179, true); + _nextChickenSequenceId = -1; + _currChickenSequenceId = -1; + gnap._actionStatus = kAS02GrabChickenDone; + _vm->_timers[5] = 500; + } else if (_nextChickenSequenceId == 0x14C) { + gameSys.setAnimation(_nextChickenSequenceId, 179, 2); + gameSys.insertSequence(_nextChickenSequenceId, 179, _currChickenSequenceId, 179, kSeqSyncWait, 0, 0, 0); + _currChickenSequenceId = _nextChickenSequenceId; + _nextChickenSequenceId = -1; + gnap._actionStatus = -1; + } else if (_nextChickenSequenceId != -1) { + gameSys.setAnimation(_nextChickenSequenceId, 179, 2); + gameSys.insertSequence(_nextChickenSequenceId, 179, _currChickenSequenceId, 179, kSeqSyncWait, 0, 0, 0); + _currChickenSequenceId = _nextChickenSequenceId; + _nextChickenSequenceId = -1; + } + } +} + +/*****************************************************************************/ + +Scene03::Scene03(GnapEngine *vm) : Scene(vm) { + _nextPlatSequenceId = -1; + _platypusScared = false; + _platypusHypnotized = false; + _nextFrogSequenceId = -1; + _currFrogSequenceId = -1; +} + +int Scene03::init() { + GameSys& gameSys = *_vm->_gameSys; + + gameSys.setAnimation(0, 0, 0); + gameSys.setAnimation(0, 0, 1); + gameSys.setAnimation(0, 0, 5); + return 0x1CC; +} + +void Scene03::updateHotspots() { + _vm->setHotspot(kHS03Platypus, 0, 0, 0, 0, SF_DISABLED | SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS03Grass, 646, 408, 722, 458, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 9, 6); + _vm->setHotspot(kHS03ExitTruck, 218, 64, 371, 224, SF_EXIT_U_CURSOR | SF_WALKABLE, 4, 4); + _vm->setHotspot(kHS03Creek, 187, 499, 319, 587, SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 3, 7); + _vm->setHotspot(kHS03TrappedPlatypus, 450, 256, 661, 414, SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 2, 5); + _vm->setHotspot(kHS03WalkAreas1, 0, 500, 300, 600); + _vm->setHotspot(kHS03WalkAreas2, 300, 447, 800, 600); + _vm->setHotspot(kHS03PlatypusWalkArea, 235, 0, 800, 600); + _vm->setHotspot(kHS03WalkAreas3, 0, 0, 800, 354); + _vm->setDeviceHotspot(kHS03Device, -1, -1, -1, -1); + if (_vm->isFlag(kGFPlatypus)) + _vm->_hotspots[kHS03Platypus]._flags = SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR; + if (_vm->isFlag(kGFGrassTaken)) + _vm->_hotspots[kHS03Grass]._flags = SF_WALKABLE | SF_DISABLED; + if (_vm->isFlag(kGFPlatypus)) + _vm->_hotspots[kHS03TrappedPlatypus]._flags = SF_DISABLED; + if (_vm->isFlag(kGFPlatypus) || _platypusHypnotized) + _vm->_hotspots[kHS03PlatypusWalkArea]._flags |= SF_WALKABLE; + _vm->_hotspotsCount = 10; +} + +void Scene03::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->playSound(0x10925, true); + _vm->startSoundTimerC(7); + + gameSys.insertSequence(0x1CA, 251, 0, 0, kSeqLoop, 0, 0, 0); + gameSys.insertSequence(0x1CB, 251, 0, 0, kSeqLoop, 0, 0, 0); + + _platypusHypnotized = false; + gnap.initPos(3, 4, kDirBottomRight); + + gameSys.insertSequence(0x1C6, 253, 0, 0, kSeqNone, 0, 0, 0); + + _currFrogSequenceId = 0x1C6; + _nextFrogSequenceId = -1; + gameSys.setAnimation(0x1C6, 253, 2); + + _vm->_timers[6] = _vm->getRandom(20) + 30; + _vm->_timers[4] = _vm->getRandom(100) + 300; + _vm->_timers[5] = _vm->getRandom(100) + 200; + + if (_vm->isFlag(kGFPlatypus)) { + plat.initPos(5, 4, kDirIdleLeft); + } else { + _vm->_timers[1] = _vm->getRandom(40) + 20; + gameSys.setAnimation(0x1C2, 99, 1); + gameSys.insertSequence(0x1C2, 99, 0, 0, kSeqNone, 0, 0, 0); + plat._sequenceId = 0x1C2; + plat._sequenceDatNum = 0; + } + + gameSys.insertSequence(0x1C4, 255, 0, 0, kSeqNone, 0, 0, 0); + + if (!_vm->isFlag(kGFGrassTaken)) + gameSys.insertSequence(0x1B2, 253, 0, 0, kSeqNone, 0, 0, 0); + + _vm->queueInsertDeviceIcon(); + + _vm->endSceneInit(); + + if (_vm->isFlag(kGFPlatypus)) + plat.walkTo(Common::Point(4, 7), -1, 0x107C2, 1); + gnap.walkTo(Common::Point(3, 6), -1, 0x107B9, 1); + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS03Platypus: + if (gnap._actionStatus < 0 && _vm->isFlag(kGFPlatypus)) { + if (_vm->_grabCursorSpriteIndex == kItemDisguise) { + gnap.useDisguiseOnPlatypus(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + if (_vm->isFlag(kGFKeysTaken)) + gnap.playMoan1(plat._pos); + else + gnap.playScratchingHead(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS03Grass: + if (gnap._actionStatus < 0) { + if (_vm->isFlag(kGFGrassTaken)) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowItem(_vm->_grabCursorSpriteIndex, 9, 6); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(9, 6)); + break; + case GRAB_CURSOR: + gnap.playPullOutDevice(Common::Point(9, 6)); + gnap.playUseDevice(); + gameSys.insertSequence(0x1B3, 253, 0x1B2, 253, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(0x1B3, 253, 5); + _vm->_hotspots[kHS03Grass]._flags |= SF_WALKABLE | SF_DISABLED; + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS03ExitTruck: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_hotspots[kHS03PlatypusWalkArea]._flags |= SF_WALKABLE; + gnap.walkTo(_vm->_hotspotsWalkPos[2], 0, 0x107AD, 1); + gnap._actionStatus = kAS03LeaveScene; + if (_vm->isFlag(kGFPlatypus)) + plat.walkTo(_vm->_hotspotsWalkPos[2], -1, 0x107C2, 1); + _vm->_hotspots[kHS03PlatypusWalkArea]._flags &= ~SF_WALKABLE; + if (_vm->_cursorValue == 1) + _vm->_newSceneNum = 2; + else + _vm->_newSceneNum = 33; + } + break; + + case kHS03Creek: + if (gnap._actionStatus == -1) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan2(Common::Point(2, 8)); + break; + case GRAB_CURSOR: + if (!_vm->isFlag(kGFPlatypus)) + _vm->_hotspots[kHS03PlatypusWalkArea]._flags |= SF_WALKABLE; + if (gnap.walkTo(_vm->_hotspotsWalkPos[3], 0, gnap.getSequenceId(kGSIdle, _vm->_hotspotsWalkPos[3] + Common::Point(1, 1)) | 0x10000, 1)) + gnap._actionStatus = kAS03GrabCreek; + if (!_vm->isFlag(kGFPlatypus)) + _vm->_hotspots[kHS03PlatypusWalkArea]._flags &= ~SF_WALKABLE; + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS03TrappedPlatypus: + if (gnap._actionStatus < 0) { + if (_vm->isFlag(kGFPlatypus)) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowItem(_vm->_grabCursorSpriteIndex, 8, 4); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(8, 4)); + break; + case GRAB_CURSOR: + if (_platypusHypnotized) { + gnap.walkTo(Common::Point(7, 6), 0, 0x107B5, 1); + gnap._actionStatus = kAS03FreePlatypus; + } else { + gnap.walkTo(_vm->_hotspotsWalkPos[4], 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + if (_platypusScared) + gnap._actionStatus = kAS03GrabScaredPlatypus; + else + gnap._actionStatus = kAS03GrabPlatypus; + } + break; + case TALK_CURSOR: + if (_platypusHypnotized) { + gnap.playBrainPulsating(Common::Point(8, 4)); + } else { + gnap._idleFacing = kDirBottomRight; + gnap.walkTo(_vm->_hotspotsWalkPos[4], 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + if (_platypusScared) + gnap._actionStatus = kAS03HypnotizeScaredPlat; + else + gnap._actionStatus = kAS03HypnotizePlat; + } + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS03Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS03WalkAreas1: + case kHS03WalkAreas2: + case kHS03WalkAreas3: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + case kHS03PlatypusWalkArea: + if (gnap._actionStatus < 0) { + if (_vm->isFlag(kGFPlatypus) || _platypusHypnotized) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + } else { + gnap.walkTo(_vm->_hotspotsWalkPos[4], 0, 0x107B5, 1); + if (_platypusScared) + gnap._actionStatus = kAS03GrabScaredPlatypus; + else + gnap._actionStatus = kAS03GrabPlatypus; + } + } + break; + + default: + if (_vm->_mouseClickState._left && gnap._actionStatus < 0) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + + } + + updateAnimations(); + + if (!_vm->isSoundPlaying(0x10925)) + _vm->playSound(0x10925, true); + + if (!_vm->_isLeavingScene) { + if (plat._actionStatus < 0 && _vm->isFlag(kGFPlatypus)) + plat.updateIdleSequence(); + if (gnap._actionStatus < 0) + gnap.updateIdleSequence(); + if (!_vm->_timers[1] && !_platypusScared) { + _vm->_timers[1] = _vm->getRandom(40) + 20; + if (gnap._actionStatus < 0 && plat._actionStatus < 0 && !_vm->isFlag(kGFPlatypus) && !_platypusHypnotized) + _nextPlatSequenceId = 450; + } + if (!_vm->_timers[6]) { + _vm->_timers[6] = _vm->getRandom(20) + 30; + if (gnap._actionStatus < 0 && plat._actionStatus < 0 && _nextFrogSequenceId == -1) { + if (_vm->getRandom(5) == 1) + _nextFrogSequenceId = 0x1C6; + else + _nextFrogSequenceId = 0x1C7; + } + } + if (!_vm->_timers[4]) { + // Update bird animation + _vm->_timers[4] = _vm->getRandom(100) + 300; + if (gnap._actionStatus < 0 && plat._actionStatus < 0) + gameSys.insertSequence(_vm->getRandom(2) != 0 ? 0x1C8 : 0x1C3, 253, 0, 0, kSeqNone, 0, 0, 0); + } + if (!_vm->_timers[5]) { + _vm->_timers[5] = _vm->getRandom(100) + 200; + if (gnap._actionStatus < 0 && plat._actionStatus < 0) { + gameSys.setAnimation(0x1C5, 253, 4); + gameSys.insertSequence(0x1C5, 253, 0, 0, kSeqNone, 0, 0, 0); + } + } + _vm->playSoundC(); + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + _vm->_timers[5] = _vm->getRandom(100) + 200; + _vm->_timers[4] = _vm->getRandom(100) + 300; + _vm->_timers[6] = _vm->getRandom(20) + 30; + } + + _vm->gameUpdateTick(); + } +} + +void Scene03::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS03LeaveScene: + _vm->_sceneDone = true; + break; + case kAS03FreePlatypus: + _nextPlatSequenceId = 0x1BC; + break; + case kAS03FreePlatypusDone: + gnap._actionStatus = -1; + plat._pos = Common::Point(6, 6); + plat._idleFacing = kDirIdleRight; + plat._id = 120; + gameSys.insertSequence(0x107CA, plat._id, 0x1BC, 99, + kSeqSyncWait, 0, 75 * plat._pos.x - plat._gridX, 48 * plat._pos.y - plat._gridY); + gameSys.insertSequence(0x1B7, 99, 0, 0, kSeqNone, 0, 0, 0); + plat._sequenceDatNum = 1; + plat._sequenceId = 0x7CA; + _vm->setFlag(kGFPlatypus); + _nextPlatSequenceId = -1; + updateHotspots(); + break; + case kAS03HypnotizePlat: + gnap.playBrainPulsating(); + _vm->addFullScreenSprite(0x106, 255); + gameSys.setAnimation(0x1C9, 256, 1); + gameSys.insertSequence(0x1C9, 256, 0, 0, kSeqNone, 0, 0, 0); + while (gameSys.getAnimationStatus(1) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + _vm->removeFullScreenSprite(); + gameSys.setAnimation(0x1BA, 99, 1); + gameSys.insertSequence(0x1BA, 99, plat._sequenceId | (plat._sequenceDatNum << 16), 99, kSeqSyncExists, 0, 0, 0); + plat._sequenceDatNum = 0; + plat._sequenceId = 0x1BA; + gnap._actionStatus = -1; + _platypusHypnotized = true; + updateHotspots(); + break; + case kAS03HypnotizeScaredPlat: + gnap.playBrainPulsating(); + gameSys.insertSequence(0x1BF, 99, plat._sequenceId | (plat._sequenceDatNum << 16), 99, kSeqSyncExists, 0, 0, 0); + gameSys.setAnimation(0x1BF, 99, 1); + while (gameSys.getAnimationStatus(1) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + _vm->addFullScreenSprite(0x106, 255); + gameSys.setAnimation(0x1C9, 256, 1); + gameSys.insertSequence(0x1C9, 256, 0, 0, kSeqNone, 0, 0, 0); + while (gameSys.getAnimationStatus(1) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + _vm->removeFullScreenSprite(); + gameSys.setAnimation(0x1BA, 99, 1); + gameSys.insertSequence(0x1BA, 99, 447, 99, kSeqSyncWait, 0, 0, 0); + plat._sequenceDatNum = 0; + plat._sequenceId = 0x1BA; + gnap._actionStatus = -1; + _platypusHypnotized = true; + updateHotspots(); + break; + case kAS03GrabPlatypus: + _nextPlatSequenceId = 0x1BD; + _platypusHypnotized = false; + break; + case kAS03GrabScaredPlatypus: + _nextPlatSequenceId = 0x1C0; + _platypusHypnotized = false; + break; + case kAS03GrabCreek: + gameSys.insertSequence(0x1B4, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(0x1B4, gnap._id, 0); + gnap._sequenceId = 0x1B4; + gnap._sequenceDatNum = 0; + gnap._actionStatus = kAS03GrabCreekDone; + break; + default: + gnap._actionStatus = -1; + break; + } + } + + if (gameSys.getAnimationStatus(1) == 2) { + if (_nextPlatSequenceId == 0x1BD || _nextPlatSequenceId == 0x1C0) { + gameSys.setAnimation(0, 0, 1); + _platypusScared = true; + gameSys.insertSequence(0x1B5, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(_nextPlatSequenceId, 99, plat._sequenceId | (plat._sequenceDatNum << 16), 99, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x1B5; + gnap._sequenceDatNum = 0; + gnap._idleFacing = kDirIdleLeft; + plat._sequenceId = _nextPlatSequenceId; + plat._sequenceDatNum = 0; + gameSys.setAnimation(_nextPlatSequenceId, 99, 1); + _nextPlatSequenceId = -1; + gnap._actionStatus = -1; + } else if (_nextPlatSequenceId == 0x1BC) { + gnap._pos = Common::Point(3, 6); + gameSys.insertSequence(0x1B6, 120, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x1BC, 99, plat._sequenceId | (plat._sequenceDatNum << 16), 99, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(0x1BC, 99, 0); + gnap._id = 20 * gnap._pos.y; + gnap._sequenceId = 0x1B6; + gnap._sequenceDatNum = 0; + gnap._idleFacing = kDirIdleLeft; + gnap._actionStatus = kAS03FreePlatypusDone; + _nextPlatSequenceId = -1; + } else if (_nextPlatSequenceId == 0x1C2 && !_platypusScared) { + gameSys.setAnimation(0, 0, 1); + gameSys.insertSequence(0x1C2, 99, plat._sequenceId | (plat._sequenceDatNum << 16), 99, kSeqSyncWait, 0, 0, 0); + plat._sequenceId = 0x1C2; + plat._sequenceDatNum = 0; + gameSys.setAnimation(0x1C2, 99, 1); + _nextPlatSequenceId = -1; + } else if (_nextPlatSequenceId == -1 && _platypusScared && !_platypusHypnotized) { + gameSys.setAnimation(0, 0, 1); + gameSys.setAnimation(0x1BE, 99, 1); + gameSys.insertSequence(0x1BE, 99, plat._sequenceId | (plat._sequenceDatNum << 16), 99, kSeqSyncWait, 0, 0, 0); + plat._sequenceId = 0x1BE; + plat._sequenceDatNum = 0; + _nextPlatSequenceId = -1; + } + } + + if (gameSys.getAnimationStatus(2) == 2 && _nextFrogSequenceId != -1) { + gameSys.setAnimation(_nextFrogSequenceId, 253, 2); + gameSys.insertSequence(_nextFrogSequenceId, 253, _currFrogSequenceId, 253, kSeqSyncWait, 0, 0, 0); + _currFrogSequenceId = _nextFrogSequenceId; + _nextFrogSequenceId = -1; + } + + if (gameSys.getAnimationStatus(5) == 2) { + gameSys.setAnimation(0, 0, 5); + _vm->invAdd(kItemGrass); + _vm->setGrabCursorSprite(kItemGrass); + _vm->setFlag(kGFGrassTaken); + updateHotspots(); + } +} + +/*****************************************************************************/ + +Scene04::Scene04(GnapEngine *vm) : Scene(vm) { + _dogIdCtr = 0; + _triedWindow = false; + _nextDogSequenceId = -1; + _currDogSequenceId = -1; +} + +int Scene04::init() { + GameSys& gameSys = *_vm->_gameSys; + + gameSys.setAnimation(0, 0, 0); + gameSys.setAnimation(0, 0, 1); + gameSys.setAnimation(0, 0, 2); + return 0x214; +} + +void Scene04::updateHotspots() { + _vm->setHotspot(kHS04Platypus, 0, 0, 0, 0, SF_DISABLED | SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS04Twig, 690, 394, 769, 452, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 9, 6); + _vm->setHotspot(kHS04Dog, 550, 442, 680, 552, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 6, 8); + _vm->setHotspot(kHS04Axe, 574, 342, 680, 412, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 6, 7); + _vm->setHotspot(kHS04Door, 300, 244, 386, 410, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 6, 7); + _vm->setHotspot(kHS04ExitTruck, 226, 580, 688, 600, SF_EXIT_D_CURSOR | SF_WALKABLE, 5, 9); + _vm->setHotspot(kHS04Window, 121, 295, 237, 342, SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 3, 7); + _vm->setHotspot(kHS04ExitBarn, 585, 154, 800, 276, SF_EXIT_U_CURSOR, 10, 8); + _vm->setHotspot(kHS04WalkArea1, 0, 0, 562, 461); + _vm->setHotspot(kHS04WalkArea2, 562, 0, 800, 500); + _vm->setDeviceHotspot(kHS04Device, -1, -1, -1, -1); + if (_vm->isFlag(kGFPlatypus)) + _vm->_hotspots[kHS04Platypus]._flags = SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR; + if (_vm->isFlag(kGFTwigTaken)) + _vm->_hotspots[kHS04Twig]._flags = SF_WALKABLE | SF_DISABLED; + if (_vm->isFlag(kGFPlatypusTalkingToAssistant) || _vm->_cursorValue == 1) + _vm->_hotspots[kHS04Axe]._flags = SF_DISABLED; + _vm->_hotspotsCount = 11; +} + +void Scene04::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->playSound(0x1091C, true); + _vm->startSoundTimerC(4); + + gameSys.insertSequence(0x210, 139 - _dogIdCtr, 0, 0, kSeqNone, 0, 0, 0); + + _currDogSequenceId = 0x210; + _nextDogSequenceId = -1; + + gameSys.setAnimation(0x210, 139 - _dogIdCtr, 3); + _dogIdCtr = (_dogIdCtr + 1) % 2; + _vm->_timers[6] = _vm->getRandom(20) + 60; + _vm->_timers[5] = _vm->getRandom(150) + 300; + _vm->_timers[7] = _vm->getRandom(150) + 200; + _vm->_timers[8] = _vm->getRandom(150) + 400; + + if (!_vm->isFlag(kGFPlatypusTalkingToAssistant) && _vm->_cursorValue == 4) + gameSys.insertSequence(0x212, 100, 0, 0, kSeqNone, 0, 0, 0); + + if (!_vm->isFlag(kGFTwigTaken)) + gameSys.insertSequence(0x1FE, 100, 0, 0, kSeqNone, 0, 0, 0); + + _vm->queueInsertDeviceIcon(); + + if (_vm->isFlag(kGFPlatypusDisguised)) { + _vm->_timers[3] = 300; + _vm->setGrabCursorSprite(kItemKeys); + gnap._pos = Common::Point(4, 7); + gnap._id = 140; + plat._pos = Common::Point(6, 7); + plat._id = 141; + gameSys.insertSequence(0x107B5, 140, 0, 0, kSeqNone, 0, 300 - gnap._gridX, 336 - gnap._gridY); + gameSys.insertSequence(0x20C, 141, 0, 0, kSeqNone, 0, 0, 0); + gameSys.insertSequence(0x208, 121, 0, 0, kSeqNone, 0, 0, 0); + gameSys.insertSequence(0x209, 121, 0x208, 121, kSeqSyncWait, 0, 0, 0); + _vm->endSceneInit(); + _vm->invRemove(kItemDisguise); + _vm->invAdd(kItemKeys); + _vm->setFlag(kGFKeysTaken); + _vm->clearFlag(kGFPlatypusDisguised); + plat._sequenceId = 0x20C; + plat._sequenceDatNum = 0; + plat._idleFacing = kDirBottomRight; + gnap._sequenceId = 0x7B5; + gnap._sequenceDatNum = 1; + gameSys.waitForUpdate(); + } else { + gameSys.insertSequence(0x209, 121, 0, 0, kSeqNone, 0, 0, 0); + if (_vm->_prevSceneNum == 2) { + gnap.initPos(5, 11, kDirUpRight); + if (_vm->isFlag(kGFPlatypus)) + plat.initPos(6, 11, kDirUpLeft); + _vm->endSceneInit(); + if (_vm->isFlag(kGFPlatypus)) + plat.walkTo(Common::Point(5, 8), -1, 0x107C2, 1); + gnap.walkTo(Common::Point(6, 9), -1, 0x107BA, 1); + } else if (_vm->_prevSceneNum == 38) { + gnap.initPos(5, 7, kDirBottomRight); + plat.initPos(4, 7, kDirIdleLeft); + _vm->endSceneInit(); + } else { + gnap.initPos(12, 9, kDirBottomRight); + if (_vm->isFlag(kGFPlatypus)) + plat.initPos(12, 8, kDirIdleLeft); + _vm->endSceneInit(); + if (_vm->isFlag(kGFPlatypus)) + plat.walkTo(Common::Point(9, 8), -1, 0x107C2, 1); + gnap.walkTo(Common::Point(9, 9), -1, 0x107BA, 1); + } + } + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->testWalk(0, 4, -1, -1, -1, -1); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS04Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS04Platypus: + if (gnap._actionStatus < 0 && _vm->isFlag(kGFPlatypus)) { + if (_vm->_grabCursorSpriteIndex == kItemDisguise) { + gnap.useDisguiseOnPlatypus(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + if (_vm->isFlag(kGFKeysTaken)) + gnap.playMoan1(plat._pos); + else + gnap.playScratchingHead(plat._pos); + break; + case GRAB_CURSOR: + if (_vm->_cursorValue == 4) + gnap.kissPlatypus(0); + else + gnap.playMoan1(plat._pos); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS04Twig: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowItem(_vm->_grabCursorSpriteIndex, 9, 6); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(_vm->_hotspotsWalkPos[1]); + break; + case GRAB_CURSOR: + gnap.playPullOutDevice(_vm->_hotspotsWalkPos[1]); + gnap.playUseDevice(_vm->_hotspotsWalkPos[1]); + gameSys.insertSequence(0x1FD, 100, 510, 100, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(0x1FD, 100, 2); + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS04Axe: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[3], 9, 5); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan2(_vm->_hotspotsWalkPos[3]); + break; + case GRAB_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.walkTo(_vm->_hotspotsWalkPos[3], 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS04GrabAxe; + _vm->setFlag(kGFPlatypusTalkingToAssistant); + updateHotspots(); + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS04Dog: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[2], 9, 7); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + if (gnap.walkTo(gnap._pos, 0, -1, 1)) { + gnap.playMoan2(_vm->_hotspotsWalkPos[2]); + _nextDogSequenceId = 0x20F; + } + break; + case GRAB_CURSOR: + gnap._idleFacing = kDirBottomRight; + if (gnap.walkTo(_vm->_hotspotsWalkPos[2], 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1)) + gnap._actionStatus = kAS04GrabDog; + break; + case TALK_CURSOR: + gnap._idleFacing = kDirBottomRight; + if (gnap.walkTo(gnap._pos, 0, -1, 1)) { + gnap.playBrainPulsating(_vm->_hotspotsWalkPos[2]); + _nextDogSequenceId = 0x20E; + } + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS04Door: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[_vm->_sceneClickedHotspot], 4, 3); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + case PLAT_CURSOR: + gnap.playScratchingHead(Common::Point(4, 3)); + break; + case GRAB_CURSOR: + if (_vm->_cursorValue == 1) { + gnap.walkTo(_vm->_hotspotsWalkPos[4], 0, 0x107BC, 1); + gnap._actionStatus = kAS04OpenDoor; + _vm->_timers[5] = 300; + gnap._idleFacing = kDirUpLeft; + } else { + _vm->_isLeavingScene = true; + gnap.walkTo(_vm->_hotspotsWalkPos[4], 0, 0x107BC, 1); + gnap._actionStatus = kAS04LeaveScene; + _vm->_newSceneNum = 38; + } + break; + case TALK_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS04ExitTruck: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + gnap.walkTo(_vm->_hotspotsWalkPos[5], 0, 0x107AE, 1); + gnap._actionStatus = kAS04LeaveScene; + if (_vm->isFlag(kGFPlatypus)) + plat.walkTo(_vm->_hotspotsWalkPos[5], -1, 0x107C7, 1); + if (_vm->_cursorValue == 1) + _vm->_newSceneNum = 2; + else + _vm->_newSceneNum = 33; + } + break; + + case kHS04Window: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[_vm->_sceneClickedHotspot], 2, 3); + } else if (_vm->isFlag(kGFKeysTaken)) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + if (gnap.walkTo(_vm->_hotspotsWalkPos[7], 0, gnap.getSequenceId(kGSIdle, Common::Point(10, 2)) | 0x10000, 1)) { + if (_triedWindow) { + gnap._actionStatus = kAS04GetKeyAnother; + } else { + gnap._actionStatus = kAS04GetKeyFirst; + _triedWindow = true; + } + } + break; + case GRAB_CURSOR: + gnap.playScratchingHead(_vm->_hotspotsWalkPos[7]); + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS04ExitBarn: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + gnap.walkTo(_vm->_hotspotsWalkPos[8], 0, 0x107AB, 1); + gnap._actionStatus = kAS04LeaveScene; + if (_vm->isFlag(kGFPlatypus)) + plat.walkTo(_vm->_hotspotsWalkPos[8] + Common::Point(0, 1), -1, 0x107C1, 1); + if (_vm->_cursorValue == 1) + _vm->_newSceneNum = 5; + else + _vm->_newSceneNum = 35; + } + break; + + case kHS04WalkArea1: + case kHS04WalkArea2: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left && gnap._actionStatus < 0) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + + } + + updateAnimations(); + + if (!_vm->isSoundPlaying(0x1091C)) + _vm->playSound(0x1091C, true); + + if (!_vm->_isLeavingScene) { + if (plat._actionStatus < 0 && _vm->isFlag(kGFPlatypus)) + plat.updateIdleSequence2(); + if (gnap._actionStatus < 0) + gnap.updateIdleSequence2(); + if (!_vm->_timers[5]) { + _vm->_timers[5] = _vm->getRandom(150) + 300; + if (gnap._actionStatus < 0) + gameSys.insertSequence(0x20D, 79, 0, 0, kSeqNone, 0, 0, 0); + } + if (!_vm->_timers[7]) { + _vm->_timers[7] = _vm->getRandom(150) + 200; + gameSys.insertSequence(0x1FC, 59, 0, 0, kSeqNone, 0, 0, 0); + } + if (!_vm->_timers[6]) { + _vm->_timers[6] = _vm->getRandom(20) + 60; + if (_nextDogSequenceId == -1) + _nextDogSequenceId = 0x210; + } + if (!_vm->_timers[8]) { + _vm->_timers[8] = _vm->getRandom(150) + 400; + gameSys.insertSequence(0x213, 20, 0, 0, kSeqNone, 0, 0, 0); + } + _vm->playSoundC(); + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } +} + +void Scene04::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS04LeaveScene: + _vm->_sceneDone = true; + break; + case kAS04OpenDoor: + gameSys.insertSequence(0x205, gnap._id, 0, 0, kSeqNone, 0, 0, 0); + gameSys.insertSequence(0x207, 121, 521, 121, kSeqSyncWait, 0, 0, 0); + gnap._pos = Common::Point(6, 7); + gameSys.insertSequence(0x107B5, gnap._id, + makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, + kSeqSyncWait, _vm->getSequenceTotalDuration(0x205) - 1, 450 - gnap._gridX, 336 - gnap._gridY); + gameSys.setAnimation(0x107B5, gnap._id, 0); + gnap._sequenceId = 0x7B5; + gnap._sequenceDatNum = 1; + gnap._actionStatus = kAS04OpenDoorDone; + break; + case kAS04OpenDoorDone: + gameSys.insertSequence(0x209, 121, 0x207, 121, kSeqSyncWait, 0, 0, 0); + gnap._actionStatus = -1; + break; + case kAS04GetKeyFirst: + gameSys.insertSequence(0x204, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(0x204, gnap._id, 0); + gnap._sequenceId = 0x204; + gnap._sequenceDatNum = 0; + gnap._actionStatus = kAS04GetKeyFirst2; + break; + case kAS04GetKeyFirst2: + gameSys.insertSequence(0x206, 255, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x1FF, 256, 0, 0, kSeqNone, 0, 0, 0); + gameSys.insertSequence(0x20B, 256, 0, 0, kSeqNone, 0, 0, 0); + gameSys.setAnimation(0x20B, 256, 0); + gnap._sequenceId = 0x206; + gnap._sequenceDatNum = 0; + gnap._actionStatus = kAS04GetKeyFirstDone; + break; + case kAS04GetKeyFirstDone: + gameSys.requestRemoveSequence(0x1FF, 256); + gameSys.requestRemoveSequence(0x20B, 256); + gameSys.insertSequence(0x107B5, gnap._id, + makeRid(gnap._sequenceDatNum, gnap._sequenceId), 255, + kSeqSyncWait, 0, 75 * gnap._pos.x - gnap._gridX, 48 * gnap._pos.y - gnap._gridY); + gnap._idleFacing = kDirBottomRight; + gnap._sequenceId = 0x7B5; + gnap._sequenceDatNum = 1; + gnap._actionStatus = -1; + break; + case kAS04GetKeyAnother: + gameSys.insertSequence(0x202, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(0x202, gnap._id, 0); + gnap._sequenceId = 0x202; + gnap._sequenceDatNum = 0; + gnap._actionStatus = kAS04GetKeyAnother2; + break; + case kAS04GetKeyAnother2: + gameSys.insertSequence(0x203, 255, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x1FF, 256, 0, 0, kSeqNone, 0, 0, 0); + gameSys.insertSequence(0x20A, 256, 0, 0, kSeqNone, 0, 0, 0); + gameSys.setAnimation(0x20A, 256, 0); + gnap._sequenceId = 0x203; + gnap._sequenceDatNum = 0; + gnap._actionStatus = kAS04GetKeyAnotherDone; + break; + case kAS04GetKeyAnotherDone: + gameSys.removeSequence(0x1FF, 256, true); + gameSys.removeSequence(0x20A, 256, true); + gameSys.insertSequence(0x107B5, gnap._id, + makeRid(gnap._sequenceDatNum, gnap._sequenceId), 255, + kSeqSyncWait, 0, 75 * gnap._pos.x - gnap._gridX, 48 * gnap._pos.y - gnap._gridY); + gnap._sequenceId = 0x7B5; + gnap._sequenceDatNum = 1; + gnap._idleFacing = kDirBottomRight; + gnap._actionStatus = -1; + break; + case kAS04GrabDog: + _nextDogSequenceId = 0x201; + break; + case kAS04GrabAxe: + gameSys.insertSequence(0x211, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.requestRemoveSequence(0x212, 100); + gnap._sequenceDatNum = 0; + gnap._sequenceId = 0x211; + gnap._actionStatus = -1; + break; + default: + gnap._actionStatus = -1; + break; + } + } + + if (gameSys.getAnimationStatus(2) == 2) { + gameSys.setAnimation(0, 0, 2); + _vm->invAdd(kItemTwig); + _vm->setGrabCursorSprite(kItemTwig); + _vm->setFlag(kGFTwigTaken); + updateHotspots(); + } + + if (gameSys.getAnimationStatus(3) == 2) { + if (_nextDogSequenceId == 0x201) { + gameSys.insertSequence(_nextDogSequenceId, 139 - _dogIdCtr, + _currDogSequenceId, 139 - (_dogIdCtr + 1) % 2, + kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x200, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextDogSequenceId, 139 - _dogIdCtr, 3); + _dogIdCtr = (_dogIdCtr + 1) % 2; + _currDogSequenceId = 0x201; + gnap._sequenceId = 0x200; + gnap._sequenceDatNum = 0; + gnap._actionStatus = -1; + _vm->_timers[6] = _vm->getRandom(20) + 60; + _nextDogSequenceId = -1; + } else if (_nextDogSequenceId != -1) { + gameSys.insertSequence(_nextDogSequenceId, 139 - _dogIdCtr, + _currDogSequenceId, 139 - (_dogIdCtr + 1) % 2, + kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextDogSequenceId, 139 - _dogIdCtr, 3); + _dogIdCtr = (_dogIdCtr + 1) % 2; + _currDogSequenceId = _nextDogSequenceId; + _nextDogSequenceId = -1; + } + } +} + +/*****************************************************************************/ + +Scene05::Scene05(GnapEngine *vm) : Scene(vm) { + _nextChickenSequenceId = -1; + _currChickenSequenceId = -1; +} + +int Scene05::init() { + GameSys& gameSys = *_vm->_gameSys; + + gameSys.setAnimation(0, 0, 0); + gameSys.setAnimation(0, 0, 1); + gameSys.setAnimation(0, 0, 3); + return _vm->isFlag(kGFBarnPadlockOpen) ? 0x151 : 0x150; +} + +void Scene05::updateHotspots() { + _vm->setHotspot(kHS05Platypus, 0, 0, 0, 0, SF_DISABLED | SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS05Haystack, 236, 366, 372, 442, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 5, 7); + _vm->setHotspot(kHS05Padlock, 386, 230, 626, 481, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 5, 7); + _vm->setHotspot(kHS05Ladder, 108, 222, 207, 444, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 4, 7); + _vm->setHotspot(kHS05ExitHouse, 0, 395, 20, 600, SF_EXIT_L_CURSOR | SF_WALKABLE, 0, 8); + _vm->setHotspot(kHS05Chicken, 612, 462, 722, 564, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 7, 8); + _vm->setHotspot(kHS05WalkArea1, 104, 0, 421, 480); + _vm->setHotspot(kHS05WalkArea2, 422, 0, 800, 487); + _vm->setHotspot(kHS05WalkArea3, 0, 0, 104, 499); + _vm->setDeviceHotspot(kHS05Device, -1, -1, -1, -1); + if (_vm->isFlag(kGFPlatypus)) + _vm->_hotspots[kHS05Platypus]._flags = SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR; + if (_vm->isFlag(kGFBarnPadlockOpen)) + _vm->_hotspots[kHS05Padlock]._flags = SF_EXIT_U_CURSOR; + _vm->_hotspotsCount = 10; +} + +void Scene05::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->playSound(0x1091C, true); + _vm->startSoundTimerC(7); + + _currChickenSequenceId = 0x142; + gameSys.setAnimation(0x142, 100, 3); + gameSys.insertSequence(0x142, 100, 0, 0, kSeqNone, 0, 0, 0); + + _nextChickenSequenceId = -1; + + _vm->_timers[5] = _vm->getRandom(10) + 30; + _vm->_timers[6] = _vm->getRandom(150) + 300; + + if (_vm->isFlag(kGFBarnPadlockOpen)) + gameSys.insertSequence(0x14A, 141, 0, 0, kSeqNone, 0, 0, 0); + + _vm->queueInsertDeviceIcon(); + + if (_vm->_prevSceneNum != 6 && _vm->_prevSceneNum != 36) { + gnap.initPos(-1, 8, kDirBottomRight); + if (_vm->isFlag(kGFPlatypus)) + plat.initPos(-1, 9, kDirIdleLeft); + _vm->endSceneInit(); + if (_vm->isFlag(kGFPlatypus)) + plat.walkTo(Common::Point(2, 8), -1, 0x107C2, 1); + gnap.walkTo(Common::Point(2, 9), -1, 0x107B9, 1); + } else { + gnap.initPos(6, 8, kDirBottomRight); + if (_vm->isFlag(kGFPlatypus)) + plat.initPos(7, 9, kDirIdleLeft); + _vm->endSceneInit(); + } + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->testWalk(0, 12, -1, -1, -1, -1); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS05Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS05Platypus: + if (gnap._actionStatus < 0 && _vm->isFlag(kGFPlatypus)) { + if (_vm->_grabCursorSpriteIndex == kItemDisguise) { + gnap.useDisguiseOnPlatypus(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + if (_vm->isFlag(kGFKeysTaken)) + gnap.playMoan1(plat._pos); + else + gnap.playScratchingHead(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS05Haystack: + if (gnap._actionStatus < 0 && plat._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[1] + Common::Point(-2, 0), 4, 5); + } else if (_vm->isFlag(kGFNeedleTaken)) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(_vm->_hotspotsWalkPos[1] + Common::Point(0, -1)); + break; + case GRAB_CURSOR: + case TALK_CURSOR: + gnap.playImpossible(); + break; + case PLAT_CURSOR: + if (_vm->isFlag(kGFPlatypus)) { + gnap.useDeviceOnPlatypus(); + if (plat.walkTo(_vm->_hotspotsWalkPos[1], 1, 0x107C2, 1)) { + plat._actionStatus = kAS05PlatSearchHaystack; + plat._idleFacing = kDirIdleRight; + } + if (gnap._pos.x == 4 && (gnap._pos.y == 8 || gnap._pos.y == 7)) + gnap.walkStep(); + gnap.playIdle(plat._pos); + } + break; + } + } + } + break; + + case kHS05Chicken: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemTwig) { + gnap._idleFacing = kDirUpRight; + Common::Point checkPt = _vm->_hotspotsWalkPos[_vm->_sceneClickedHotspot] + Common::Point(0, 1); + gnap.walkTo(checkPt, 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS05UseTwigWithChicken; + } else if (_vm->_grabCursorSpriteIndex >= 0) + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[_vm->_sceneClickedHotspot] + Common::Point(0, 1), 9, 7); + else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan2(Common::Point(9, 7)); + break; + case GRAB_CURSOR: + gnap._idleFacing = kDirBottomRight; + gnap.walkTo(_vm->_hotspotsWalkPos[5], 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS05GrabChicken; + break; + case TALK_CURSOR: + gnap._idleFacing = kDirBottomRight; + gnap.walkTo(_vm->_hotspotsWalkPos[5], 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS05TalkChicken; + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS05Ladder: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[_vm->_sceneClickedHotspot], 2, 5); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan2(Common::Point(2, 4)); + break; + case GRAB_CURSOR: + gnap._idleFacing = kDirBottomLeft; + gnap.walkTo(_vm->_hotspotsWalkPos[3], 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS05GrabLadder; + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS05Padlock: + if (_vm->isFlag(kGFBarnPadlockOpen)) { + _vm->_isLeavingScene = true; + Common::Point destPt = _vm->_hotspotsWalkPos[2] + Common::Point(- 1, 1); + gnap.walkTo(destPt, 0, -1, 1); + gnap._actionStatus = kAS05EnterBarn; + if (_vm->_cursorValue == 1) + _vm->_newSceneNum = 6; + else + _vm->_newSceneNum = 36; + } else if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemNeedle) { + if (gnap.walkTo(_vm->_hotspotsWalkPos[2], 0, + gnap.getSequenceId(kGSIdle, _vm->_hotspotsWalkPos[2]) | 0x10000, 1)) + gnap._actionStatus = kAS05PickPadlock; + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[2], 7, 4); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(7, 4)); + break; + case GRAB_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.walkTo(_vm->_hotspotsWalkPos[_vm->_sceneClickedHotspot], 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS05TryPickPadlock; + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS05ExitHouse: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + gnap.walkTo(_vm->_hotspotsWalkPos[4], 0, 0x107AF, 1); + gnap._actionStatus = kAS05LeaveScene; + if (_vm->isFlag(kGFPlatypus)) + plat.walkTo(_vm->_hotspotsWalkPos[4] + Common::Point(0, 1), -1, 0x107C7, 1); + if (_vm->_cursorValue == 1) + _vm->_newSceneNum = 4; + else + _vm->_newSceneNum = 37; + } + break; + + case kHS05WalkArea1: + case kHS05WalkArea2: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + case kHS05WalkArea3: + // Nothing + break; + + default: + if (_vm->_mouseClickState._left && gnap._actionStatus < 0) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + + } + + updateAnimations(); + + if (!_vm->isSoundPlaying(0x1091C)) + _vm->playSound(0x1091C, true); + + if (!_vm->_isLeavingScene) { + if (_vm->isFlag(kGFPlatypus)) + plat.updateIdleSequence(); + gnap.updateIdleSequence(); + if (!_vm->_timers[5]) { + _vm->_timers[5] = _vm->getRandom(20) + 30; + if (gnap._actionStatus != kAS05TalkChicken && _nextChickenSequenceId == -1) { + if (_vm->getRandom(4) != 0) + _nextChickenSequenceId = 0x142; + else + _nextChickenSequenceId = 0x143; + } + } + if (!_vm->_timers[6]) { + _vm->_timers[6] = _vm->getRandom(150) + 300; + if (gnap._actionStatus < 0) + gameSys.insertSequence(0x149, 39, 0, 0, kSeqNone, 0, 0, 0); + } + _vm->playSoundC(); + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + _vm->_timers[5] = _vm->getRandom(20) + 30; + } + + _vm->gameUpdateTick(); + } +} + +void Scene05::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS05LeaveScene: + _vm->_sceneDone = true; + gnap._actionStatus = -1; + break; + case kAS05TryPickPadlock: + gameSys.insertSequence(0x148, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x148; + gnap._sequenceDatNum = 0; + gnap._actionStatus = -1; + break; + case kAS05PickPadlock: + gameSys.setAnimation(0x147, gnap._id, 0); + gameSys.insertSequence(0x147, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x147; + gnap._sequenceDatNum = 0; + _vm->setFlag(kGFBarnPadlockOpen); + _vm->setFlag(kGFSceneFlag1); + _vm->setGrabCursorSprite(-1); + _vm->_newSceneNum = 6; + _vm->_timers[2] = 100; + _vm->invRemove(kItemNeedle); + gnap._actionStatus = kAS05LeaveScene; + break; + case kAS05TalkChicken: + _nextChickenSequenceId = 0x144; + gnap._actionStatus = -1; + break; + case kAS05GrabChicken: + _nextChickenSequenceId = 0x14B; + break; + case kAS05GrabLadder: + while (gameSys.isSequenceActive(0x149, 39) && !_vm->_gameDone) + _vm->gameUpdateTick(); + gameSys.insertSequence(0x14E, gnap._id + 1, 0, 0, kSeqNone, 0, 0, 0); + gameSys.insertSequence(0x14D, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x14D; + gnap._sequenceDatNum = 0; + _vm->_timers[2] = 200; + _vm->_timers[6] = 300; + gnap._actionStatus = -1; + break; + case kAS05EnterBarn: + gameSys.insertSequence(0x107B1, 1, + makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, + kSeqSyncWait, 0, 75 * gnap._pos.x - gnap._gridX, 48 * gnap._pos.y - gnap._gridY); + gameSys.setAnimation(0x107B1, 1, 0); + gnap._actionStatus = kAS05LeaveScene; + break; + case kAS05UseTwigWithChicken: + gnap.playShowItem(5, 0, 0); + _nextChickenSequenceId = 0x14F; + gnap._actionStatus = -1; + break; + } + } + + if (gameSys.getAnimationStatus(1) == 2) { + PlayerPlat& plat = *_vm->_plat; + if (plat._sequenceId == 0x146) { + plat._pos = Common::Point(4, 8); + gameSys.insertSequence(0x107C1, 160, 0x146, 256, kSeqSyncWait, 0, 300 - plat._gridX, 384 - plat._gridY); + plat._sequenceId = 0x7C1; + plat._sequenceDatNum = 1; + plat._id = 20 * plat._pos.y; + _vm->invAdd(kItemNeedle); + _vm->setFlag(kGFNeedleTaken); + _vm->setGrabCursorSprite(kItemNeedle); + _vm->showCursor(); + _vm->_timers[1] = 30; + plat._actionStatus = -1; + } + if (plat._actionStatus == kAS05PlatSearchHaystack) { + gameSys.setAnimation(0, 0, 1); + gameSys.insertSequence(0x145, plat._id, plat._sequenceId | (plat._sequenceDatNum << 16), plat._id, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x146, 256, 0x145, plat._id, kSeqSyncWait, 0, 0, 0); + _vm->hideCursor(); + _vm->setGrabCursorSprite(-1); + plat._sequenceId = 0x146; + plat._sequenceDatNum = 0; + gameSys.setAnimation(0x146, 256, 1); + _vm->_timers[1] = 300; + } + } + + if (gameSys.getAnimationStatus(3) == 2) { + if (_nextChickenSequenceId == 0x14B) { + gameSys.setAnimation(_nextChickenSequenceId, 100, 3); + gameSys.insertSequence(_nextChickenSequenceId, 100, _currChickenSequenceId, 100, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x14C, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceDatNum = 0; + gnap._sequenceId = 0x14C; + _currChickenSequenceId = _nextChickenSequenceId; + _nextChickenSequenceId = -1; + gnap._actionStatus = -1; + } else if (_nextChickenSequenceId != -1) { + gameSys.setAnimation(_nextChickenSequenceId, 100, 3); + gameSys.insertSequence(_nextChickenSequenceId, 100, _currChickenSequenceId, 100, kSeqSyncWait, 0, 0, 0); + _currChickenSequenceId = _nextChickenSequenceId; + _nextChickenSequenceId = -1; + } + } +} + +/*****************************************************************************/ + +Scene06::Scene06(GnapEngine *vm) : Scene(vm) { + _horseTurnedBack = false; + _nextPlatSequenceId = -1; + _nextHorseSequenceId = -1; + _currHorseSequenceId = -1; +} + +int Scene06::init() { + GameSys& gameSys = *_vm->_gameSys; + + gameSys.setAnimation(0, 0, 0); + gameSys.setAnimation(0, 0, 1); + gameSys.setAnimation(0, 0, 2); + if (_vm->isFlag(kGFSceneFlag1)) { + _vm->playSound(0x11B, false); + _vm->clearFlag(kGFSceneFlag1); + } + return 0x101; +} + +void Scene06::updateHotspots() { + _vm->setHotspot(kHS06Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS06Gas, 300, 120, 440, 232, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 4, 7); + _vm->setHotspot(kHS06Ladder, 497, 222, 614, 492, SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 6, 8); + _vm->setHotspot(kHS06Horse, 90, 226, 259, 376, SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 4, 7); + _vm->setHotspot(kHS06ExitOutsideBarn, 226, 580, 688, 600, SF_EXIT_D_CURSOR | SF_WALKABLE, 5, 10); + _vm->setHotspot(kHS06WalkArea1, 0, 0, 200, 515); + _vm->setHotspot(kHS06WalkArea2, 200, 0, 285, 499); + _vm->setHotspot(kHS06WalkArea3, 688, 0, 800, 499); + _vm->setHotspot(kHS06WalkArea4, 475, 469, 800, 505); + _vm->setHotspot(kHS06WalkArea5, 0, 0, 800, 504); + _vm->setDeviceHotspot(kHS06Device, -1, -1, -1, -1); + if (_vm->isFlag(kGFGasTaken)) + _vm->_hotspots[kHS06Ladder]._flags = SF_DISABLED; + if (_vm->_cursorValue == 4) { + _vm->_hotspots[kHS06Ladder]._flags = SF_DISABLED; + _vm->_hotspots[kHS06Gas]._flags = SF_DISABLED; + } + _vm->_hotspotsCount = 11; +} + +void Scene06::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + bool triedDeviceOnGas = false; + + _vm->startSoundTimerC(7); + + _horseTurnedBack = false; + gameSys.insertSequence(0xF1, 120, 0, 0, kSeqNone, 0, 0, 0); + + _currHorseSequenceId = 0xF1; + _nextHorseSequenceId = -1; + + gameSys.setAnimation(0xF1, 120, 2); + _vm->_timers[4] = _vm->getRandom(40) + 25; + + if (_vm->isFlag(kGFUnk04)) + gameSys.insertSequence(0xF7, 20, 0, 0, kSeqNone, 0, 0, 0); + else + gameSys.insertSequence(0xF8, 20, 0, 0, kSeqNone, 0, 0, 0); + + if (!_vm->isFlag(kGFGasTaken) && _vm->_cursorValue != 4) + gameSys.insertSequence(0xFE, 20, 0, 0, kSeqNone, 0, 0, 0); + + _vm->queueInsertDeviceIcon(); + + gnap.initPos(5, 12, kDirBottomRight); + plat.initPos(6, 12, kDirIdleLeft); + _vm->endSceneInit(); + + plat.walkTo(Common::Point(6, 8), -1, 0x107C2, 1); + gnap.walkTo(Common::Point(5, 8), -1, 0x107B9, 1); + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->testWalk(0, 5, -1, -1, -1, -1); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS06Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS06Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemDisguise) { + gnap.useDisguiseOnPlatypus(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + if (_vm->isFlag(kGFKeysTaken)) + gnap.playMoan1(plat._pos); + else + gnap.playScratchingHead(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS06Gas: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[_vm->_sceneClickedHotspot], 5, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(5, 0)); + break; + case GRAB_CURSOR: + if (_vm->isFlag(kGFUnk04)) { + gnap.playImpossible(); + } else if (triedDeviceOnGas) { + _vm->_hotspots[kHS06WalkArea5]._flags |= SF_WALKABLE; + gnap.walkTo(_vm->_hotspotsWalkPos[1], 0, 0x107BC, 1); + _vm->_hotspots[kHS06WalkArea5]._flags &= ~SF_WALKABLE; + gnap._actionStatus = kAS06TryToGetGas; + } else { + triedDeviceOnGas = true; + gnap.playPullOutDeviceNonWorking(_vm->_hotspotsWalkPos[1]); + } + break; + case TALK_CURSOR: + case PLAT_CURSOR: + if (_vm->isFlag(kGFUnk04)) + gnap.playImpossible(); + else + gnap.playScratchingHead(Common::Point(5, 0)); + break; + } + } + } + break; + + case kHS06Ladder: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[_vm->_sceneClickedHotspot], 8, 4); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(8, 4)); + break; + case GRAB_CURSOR: + if (_vm->isFlag(kGFGasTaken)) + gnap.playImpossible(); + else { + gnap.walkTo(_vm->_hotspotsWalkPos[2], 0, 0x107BB, 1); + gnap._actionStatus = kAS06TryToClimbLadder; + _vm->setFlag(kGFGasTaken); + } + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS06Horse: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemTwig && _horseTurnedBack) { + _vm->_hotspots[kHS06WalkArea5]._flags |= SF_WALKABLE; + gnap.walkTo(_vm->_hotspotsWalkPos[3], 0, 0x107BC, 1); + _vm->_hotspots[kHS06WalkArea5]._flags &= ~SF_WALKABLE; + gnap._idleFacing = kDirUpLeft; + plat.walkTo(Common::Point(6, 8), 1, 0x107C2, 1); + plat._idleFacing = kDirIdleLeft; + gnap._actionStatus = kAS06UseTwigOnHorse; + _vm->setGrabCursorSprite(-1); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[_vm->_sceneClickedHotspot], 3, 2); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(3, 2)); + break; + case TALK_CURSOR: + if (_horseTurnedBack) { + gnap.walkTo(_vm->_hotspotsWalkPos[3], 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(3, 2)) | 0x10000, 1); + } else { + gnap._idleFacing = kDirBottomLeft; + _vm->_hotspots[kHS06WalkArea5]._flags |= SF_WALKABLE; + gnap.walkTo(_vm->_hotspotsWalkPos[3], 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + _vm->_hotspots[kHS06WalkArea5]._flags &= ~SF_WALKABLE; + gnap._actionStatus = kAS06TalkToHorse; + } + break; + case GRAB_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS06ExitOutsideBarn: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + gnap.walkTo(_vm->_hotspotsWalkPos[4], 0, 0x107AE, 1); + gnap._actionStatus = kAS06LeaveScene; + if (_vm->_cursorValue == 1) + _vm->_newSceneNum = 5; + else + _vm->_newSceneNum = 35; + } + break; + + case kHS06WalkArea1: + case kHS06WalkArea2: + case kHS06WalkArea3: + case kHS06WalkArea4: + case kHS06WalkArea5: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left && gnap._actionStatus < 0) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + + } + + updateAnimations(); + + if (!_vm->_isLeavingScene) { + if (plat._actionStatus < 0) + plat.updateIdleSequence(); + if (gnap._actionStatus < 0) + gnap.updateIdleSequence(); + if (!_vm->_timers[4]) { + _vm->_timers[4] = _vm->getRandom(40) + 25; + if (gnap._actionStatus < 0 && plat._actionStatus < 0 && _nextHorseSequenceId == -1) { + if (_horseTurnedBack) { + _nextHorseSequenceId = 0xF5; + } else { + switch (_vm->getRandom(5)) { + case 0: + case 1: + case 2: + _nextHorseSequenceId = 0xF1; + break; + case 3: + _nextHorseSequenceId = 0xF3; + break; + case 4: + _nextHorseSequenceId = 0xF4; + break; + } + } + } + } + _vm->playSoundC(); + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } +} + +void Scene06::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS06LeaveScene: + _vm->_sceneDone = true; + gnap._actionStatus = -1; + break; + case kAS06TryToGetGas: + gameSys.insertSequence(0xFC, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0xFC; + gnap._sequenceDatNum = 0; + gnap._actionStatus = -1; + break; + case kAS06TryToClimbLadder: + gameSys.insertSequence(0xFF, 20, 0xFE, 20, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(0xFD, gnap._id, 0); + gameSys.insertSequence(0xFD, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0xFD; + gnap._sequenceDatNum = 0; + gnap._actionStatus = kAS06TryToClimbLadderDone; + break; + case kAS06TryToClimbLadderDone: + gnap._pos = Common::Point(6, 7); + gnap._actionStatus = -1; + break; + case kAS06TalkToHorse: + _nextHorseSequenceId = 0xF6; + break; + case kAS06UseTwigOnHorse: + _nextPlatSequenceId = 0xFB; + break; + default: + gnap._actionStatus = -1; + break; + } + } + + if (gameSys.getAnimationStatus(1) == 2) { + gameSys.setAnimation(0, 0, 1); + if (plat._sequenceId == 0xFA) { + gameSys.setAnimation(0, 0, 1); + _vm->invAdd(kItemGas); + _vm->setFlag(kGFGasTaken); + _vm->_hotspots[kHS06Ladder]._flags = SF_DISABLED; + _vm->setGrabCursorSprite(kItemGas); + plat._actionStatus = -1; + plat._pos = Common::Point(6, 8); + gameSys.insertSequence(0x107C1, plat._id, 0, 0, kSeqNone, 0, 450 - plat._gridX, 384 - plat._gridY); + plat._sequenceId = 0x7C1; + plat._sequenceDatNum = 1; + _vm->setFlag(kGFUnk04); + gnap._actionStatus = -1; + _vm->showCursor(); + } + if (_nextPlatSequenceId == 0xFB) { + gameSys.setAnimation(0, 0, 1); + _nextHorseSequenceId = 0xF2; + plat._actionStatus = 6; + } + } + + if (gameSys.getAnimationStatus(2) == 2 && _nextHorseSequenceId != -1) { + switch (_nextHorseSequenceId) { + case 0xF2: + _vm->setGrabCursorSprite(-1); + _vm->hideCursor(); + gameSys.setAnimation(0xFA, 256, 1); + gameSys.insertSequence(0xF2, 120, _currHorseSequenceId, 120, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x100, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0xF7, 20, 0xF8, 20, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0xFB, plat._id, plat._sequenceId | (plat._sequenceDatNum << 16), plat._id, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0xFA, 256, 0xFB, plat._id, kSeqSyncWait, 0, 0, 0); + plat._sequenceId = 0xFA; + plat._sequenceDatNum = 0; + gameSys.insertSequence(0x107B7, gnap._id, 0x100, gnap._id, + kSeqSyncWait, 0, 75 * gnap._pos.x - gnap._gridX, 48 * gnap._pos.y - gnap._gridY); + gnap._sequenceId = 0x7B7; + gnap._sequenceDatNum = 1; + _currHorseSequenceId = _nextHorseSequenceId; + _nextHorseSequenceId = -1; + _nextPlatSequenceId = -1; + _vm->invRemove(kItemTwig); + break; + case 0xF6: + gameSys.setAnimation(_nextHorseSequenceId, 120, 2); + gameSys.insertSequence(0xF6, 120, _currHorseSequenceId, 120, kSeqSyncWait, 0, 0, 0); + _horseTurnedBack = true; + _currHorseSequenceId = _nextHorseSequenceId; + _nextHorseSequenceId = -1; + gnap._actionStatus = -1; + break; + default: + gameSys.setAnimation(_nextHorseSequenceId, 120, 2); + gameSys.insertSequence(_nextHorseSequenceId, 120, _currHorseSequenceId, 120, kSeqSyncWait, 0, 0, 0); + _currHorseSequenceId = _nextHorseSequenceId; + _nextHorseSequenceId = -1; + break; + } + } +} + +/*****************************************************************************/ + +Scene07::Scene07(GnapEngine *vm) : Scene(vm) { +} + +int Scene07::init() { + return 0x92; +} + +void Scene07::updateHotspots() { + _vm->setHotspot(kHS07Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS07ExitHouse, 700, 125, 799, 290, SF_EXIT_NE_CURSOR); + _vm->setHotspot(kHS07Dice, 200, 290, 270, 360, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS07WalkArea1, 0, 0, 325, 445); + _vm->setHotspot(kHS07WalkArea2, 325, 0, 799, 445, _vm->_isLeavingScene ? SF_WALKABLE : SF_NONE); + _vm->setHotspot(kHS07WalkArea3, 160, 0, 325, 495); + _vm->setDeviceHotspot(kHS07Device, -1, -1, -1, -1); + if (_vm->isFlag(kGFPlatypus)) + _vm->_hotspots[kHS07Dice]._flags = SF_DISABLED; + _vm->_hotspotsCount = 7; +} + +void Scene07::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->queueInsertDeviceIcon(); + gameSys.insertSequence(0x8C, 1, 0, 0, kSeqLoop, 0, 0, 0); + gameSys.insertSequence(0x90, 1, 0, 0, kSeqLoop, 0, 0, 0); + + _vm->invRemove(kItemGas); + _vm->invRemove(kItemNeedle); + + if (!_vm->isFlag(kGFPlatypus)) + gameSys.insertSequence(0x8D, 1, 0, 0, kSeqNone, 0, 0, 0); + + if (_vm->_prevSceneNum == 8) { + gnap.initPos(7, 7, kDirBottomLeft); + plat.initPos(9, 7, kDirIdleRight); + _vm->endSceneInit(); + } else { + gnap._pos = Common::Point(6, 7); + gnap._id = 140; + gnap._sequenceId = 0x8F; + gnap._sequenceDatNum = 0; + gnap._idleFacing = kDirBottomRight; + gameSys.insertSequence(0x8F, 140, 0, 0, kSeqNone, 0, 0, 0); + gameSys.setAnimation(makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, 0); + gnap._actionStatus = kAS07Wait; + plat._pos = Common::Point(3, 8); + plat._id = 160; + plat._sequenceId = 0x91; + plat._sequenceDatNum = 0; + plat._idleFacing = kDirIdleLeft; + gameSys.insertSequence(0x91, 160, 0, 0, kSeqNone, 0, 0, 0); + _vm->endSceneInit(); + } + + _vm->_timers[3] = 600; + _vm->_timers[4] = _vm->getRandom(40) + 50; + + while (!_vm->_sceneDone) { + if (!_vm->isSoundPlaying(0x10919)) + _vm->playSound(0x10919, true); + + if (_vm->testWalk(0, 1, 8, 7, 6, 7)) + updateHotspots(); + + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS07Platypus: + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + break; + } + break; + + case kHS07ExitHouse: + _vm->_isLeavingScene = true; + if (gnap._pos.x > 8) + gnap.walkTo(Common::Point(gnap._pos.x, 7), 0, 0x107AD, 1); + else + gnap.walkTo(Common::Point(8, 7), 0, 0x107AD, 1); + gnap._actionStatus = kAS07LeaveScene; + break; + + case kHS07Dice: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(4, 8), 3, 3); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + break; + case GRAB_CURSOR: + _vm->setFlag(kGFPlatypus); + _vm->invAdd(kItemDice); + updateHotspots(); + gnap.playPullOutDevice(Common::Point(3, 3)); + gameSys.setAnimation(0x8E, 1, 2); + gameSys.insertSequence(0x8E, 1, 141, 1, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(gnap.getSequenceId(kGSUseDevice, Common::Point(0, 0)) | 0x10000, gnap._id, + makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, + kSeqSyncWait, 0, 75 * gnap._pos.x - gnap._gridX, 48 * gnap._pos.y - gnap._gridY); + gnap._sequenceId = gnap.getSequenceId(kGSUseDevice, Common::Point(0, 0)); + gnap._sequenceDatNum = 1; + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + break; + + case kHS07Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + _vm->_timers[4] = _vm->getRandom(40) + 50; + } + break; + + case kHS07WalkArea1: + case kHS07WalkArea2: + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + case kHS07WalkArea3: + // Nothing + break; + + default: + if (_vm->_mouseClickState._left) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + } + + updateAnimations(); + + if (!_vm->_isLeavingScene) { + gnap.updateIdleSequence(); + if (plat._actionStatus < 0 && gnap._actionStatus < 0) { + if (_vm->_timers[0]) { + if (!_vm->_timers[1]) { + _vm->_timers[1] = _vm->getRandom(20) + 30; + int gnapRandomValue = _vm->getRandom(20); + if (plat._idleFacing != kDirIdleLeft) { + if (gnapRandomValue == 0 && plat._sequenceId == 0x7CA) + plat.playSequence(0x107CC); + else if (gnapRandomValue == 1 && plat._sequenceId == 0x7CA) + plat.playSequence(0x10845); + else if (plat._pos.y == 9) + plat.playSequence(0x107CA); + } else if (gnapRandomValue == 0 && plat._sequenceId == 0x7C9) + plat.playSequence(0x107CB); + else if (gnapRandomValue == 1 && plat._sequenceId == 0x7C9) + plat.playSequence(0x10844); + else if (plat._pos.y == 9) + plat.playSequence(0x107C9); + gameSys.setAnimation(plat._sequenceId | (plat._sequenceDatNum << 16), plat._id, 1); + } + } else { + _vm->_timers[0] = _vm->getRandom(75) + 75; + plat.makeRoom(); + } + } else { + _vm->_timers[0] = 100; + _vm->_timers[1] = 35; + } + playRandomSound(4); + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + _vm->_timers[4] = _vm->getRandom(40) + 50; + } + _vm->gameUpdateTick(); + } +} + +void Scene07::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS07LeaveScene: + _vm->_newSceneNum = 8; + _vm->_sceneDone = true; + break; + } + gnap._actionStatus = -1; + } + + if (gameSys.getAnimationStatus(2) == 2) { + gameSys.setAnimation(0, 0, 2); + _vm->setGrabCursorSprite(kItemDice); + } +} + +/*****************************************************************************/ + +Scene08::Scene08(GnapEngine *vm) : Scene(vm) { + _nextDogSequenceId = -1; + _currDogSequenceId = -1; + _nextManSequenceId = -1; + _currManSequenceId = -1; +} + +int Scene08::init() { + return 0x150; +} + +void Scene08::updateHotspots() { + _vm->setHotspot(kH08SPlatypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS08ExitBackdoor, 0, 280, 10, 400, SF_EXIT_L_CURSOR | SF_WALKABLE); + _vm->setHotspot(kHS08ExitCrash, 200, 590, 400, 599, SF_EXIT_D_CURSOR | SF_WALKABLE); + _vm->setHotspot(kHS08Man, 510, 150, 610, 380, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS08Door, 350, 170, 500, 410, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS08Meat, 405, 450, 480, 485, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS08Bone, 200, 405, 270, 465, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS08Toy, 540, 430, 615, 465, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS08WalkArea1, 290, 340, -1, -1); + _vm->setHotspot(kHS08WalkArea2, 0, 0, 799, 420); + _vm->setDeviceHotspot(kHS08Device, -1, -1, -1, -1); + if (_vm->isFlag(kGFBarnPadlockOpen)) + _vm->_hotspots[kHS08Meat]._flags = SF_WALKABLE | SF_DISABLED; + if (_vm->isFlag(kGFTruckFilledWithGas)) + _vm->_hotspots[kHS08Bone]._flags = SF_WALKABLE | SF_DISABLED; + if (_vm->isFlag(kGFTruckKeysUsed)) + _vm->_hotspots[kHS08Toy]._flags = SF_WALKABLE | SF_DISABLED; + _vm->_hotspotsCount = 11; +} + +void Scene08::updateAnimationsCb() { + GameSys& gameSys = *_vm->_gameSys; + + if (gameSys.getAnimationStatus(3) == 2) { + gameSys.setAnimation(_nextDogSequenceId, 100, 3); + gameSys.insertSequence(_nextDogSequenceId, 100, _currDogSequenceId, 100, kSeqSyncWait, 0, 0, 0); + _currDogSequenceId = _nextDogSequenceId; + if ( _nextDogSequenceId != 0x135 ) + _nextDogSequenceId = 0x134; + } +} + +void Scene08::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->queueInsertDeviceIcon(); + + gameSys.insertSequence(0x14F, 1, 0, 0, kSeqLoop, 0, 0, 0); + gameSys.insertSequence(0x14E, 256, 0, 0, kSeqNone, 0, 0, 0); + + _currDogSequenceId = 0x135; + _nextDogSequenceId = 0x135; + + gameSys.setAnimation(0x135, 100, 3); + gameSys.insertSequence(_currDogSequenceId, 100, 0, 0, kSeqNone, 0, 0, 0); + + _currManSequenceId = 0x140; + _nextManSequenceId = -1; + + gameSys.setAnimation(0x140, 100, 2); + gameSys.insertSequence(_currManSequenceId, 100, 0, 0, kSeqNone, 0, 0, 0); + + _vm->_timers[4] = _vm->getRandom(50) + 75; + + if (!_vm->isFlag(kGFBarnPadlockOpen)) + gameSys.insertSequence(0x144, 1, 0, 0, kSeqNone, 0, 0, 0); + + if (!_vm->isFlag(kGFTruckFilledWithGas)) + gameSys.insertSequence(0x145, 1, 0, 0, kSeqNone, 0, 0, 0); + + if (!_vm->isFlag(kGFTruckKeysUsed)) + gameSys.insertSequence(0x146, 1, 0, 0, kSeqNone, 0, 0, 0); + + gnap.initPos(-1, 8, kDirBottomRight); + plat.initPos(-1, 7, kDirIdleLeft); + + _vm->endSceneInit(); + + gnap.walkTo(Common::Point(1, 8), -1, 0x107B9, 1); + plat.walkTo(Common::Point(1, 7), -1, 0x107C2, 1); + + _vm->_timers[5] = _vm->getRandom(40) + 50; + + while (!_vm->_sceneDone) { + if (!_vm->isSoundPlaying(0x10919)) + _vm->playSound(0x10919, true); + + _vm->testWalk(0, 0, -1, -1, -1, -1); + + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS08Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + _vm->_timers[4] = _vm->getRandom(50) + 75; + _vm->_timers[5] = _vm->getRandom(40) + 50; + } + break; + + case kH08SPlatypus: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + if (_vm->isFlag(kGFSceneFlag1)) + gnap.playMoan1(plat._pos); + else + gnap.playScratchingHead(plat._pos); + break; + case GRAB_CURSOR: + gnap.actionIdle(0x14D); + gnap.kissPlatypus(8); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + break; + } + } + break; + + case kHS08ExitBackdoor: + _vm->_isLeavingScene = true; + gnap.actionIdle(0x14D); + gnap.walkTo(Common::Point(0, 6), 0, 0x107AF, 1); + gnap._actionStatus = kAS08LeaveScene; + plat.walkTo(Common::Point(0, 7), 1, 0x107CF, 1); + _vm->_newSceneNum = 9; + break; + + case kHS08ExitCrash: + _vm->_isLeavingScene = true; + gnap.actionIdle(0x14D); + gnap.walkTo(Common::Point(3, 9), 0, 0x107AE, 1); + gnap._actionStatus = kAS08LeaveScene; + plat.walkTo(Common::Point(4, 9), 1, 0x107C1, 1); + _vm->_newSceneNum = 7; + break; + + case kHS08Man: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(6, 6), 7, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.actionIdle(0x14D); + gnap.walkTo(Common::Point(6, 6), 0, 0x107BB, 1); + gnap._actionStatus = kAS08LookMan; + gnap._idleFacing = kDirUpRight; + break; + case GRAB_CURSOR: + gnap.playImpossible(); + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpLeft; + gnap.actionIdle(0x14D); + gnap.walkTo(Common::Point(8, 6), 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS08TalkMan; + break; + case PLAT_CURSOR: + gnap.actionIdle(0x14D); + gnap.useDeviceOnPlatypus(); + plat.walkTo(Common::Point(6, 6), 1, 0x107C2, 1); + plat._actionStatus = kAS08PlatWithMan; + plat._idleFacing = kDirIdleLeft; + gnap.playIdle(Common::Point(6, 6)); + break; + } + } + break; + + case kHS08Door: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(4, 7), 5, 0); + gameSys.setAnimation(makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, 0); + gnap._actionStatus = kAS08GrabDog; + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(6, 0)); + gameSys.setAnimation(makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, 0); + gnap._actionStatus = kAS08LookDog; + break; + case GRAB_CURSOR: + gnap.walkTo(Common::Point(4, 7), 0, 0x107BB, 1); + gnap._actionStatus = kAS08GrabDog; + gnap._idleFacing = kDirUpRight; + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.actionIdle(0x14D); + gnap.walkTo(Common::Point(4, 7), 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS08TalkDog; + break; + case PLAT_CURSOR: + _vm->setFlag(kGFSceneFlag1); + gnap.actionIdle(0x14D); + gnap.useDeviceOnPlatypus(); + plat.walkTo(Common::Point(3, 7), 1, 0x107C2, 1); + plat._actionStatus = kAS08PlatWithDog; + plat._idleFacing = kDirIdleLeft; + gnap.playIdle(Common::Point(3, 7)); + break; + } + } + break; + + case kHS08Meat: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(6, 8), 5, 6); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(6, 7)); + break; + case GRAB_CURSOR: + if (_currDogSequenceId == 0x135) { + gnap.playScratchingHead(Common::Point(6, 7)); + } else { + gnap.actionIdle(0x14D); + gnap.playPullOutDevice(Common::Point(6, 7)); + gnap.playUseDevice(); + _nextDogSequenceId = 0x149; + } + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + break; + + case kHS08Bone: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(2, 7), 3, 6); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(3, 6)); + break; + case GRAB_CURSOR: + if (_currDogSequenceId == 0x135) { + gnap.playScratchingHead(Common::Point(3, 6)); + } else { + gnap.actionIdle(0x14D); + gnap.playPullOutDevice(Common::Point(3, 6)); + gnap.playUseDevice(); + _nextDogSequenceId = 0x14A; + } + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + break; + + case kHS08Toy: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(8, 7), 7, 6); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(7, 6)); + break; + case GRAB_CURSOR: + if (_currDogSequenceId == 0x135) { + gnap.playScratchingHead(Common::Point(7, 6)); + } else { + gnap.actionIdle(0x14D); + gnap.playPullOutDevice(Common::Point(7, 6)); + gnap.playUseDevice(); + _nextDogSequenceId = 0x14B; + } + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + break; + + case kHS08WalkArea1: + case kHS08WalkArea2: + gnap.actionIdle(0x14D); + gnap.walkTo(Common::Point(-1, 6), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left) { + gnap.actionIdle(0x14D); + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + } + + updateAnimations(); + + if (!_vm->_isLeavingScene) { + plat.updateIdleSequence(); + gnap.updateIdleSequence(); + if (!_vm->_timers[4]) { + _vm->_timers[4] = _vm->getRandom(50) + 125; + if (gnap._actionStatus < 0 && plat._actionStatus < 0 && _nextManSequenceId == -1 && + (_currDogSequenceId == 0x134 || _currDogSequenceId == 0x135)) { + int _gnapRandomValue = _vm->getRandom(4); + switch (_gnapRandomValue) { + case 0: + _nextManSequenceId = 0x138; + break; + case 1: + _nextManSequenceId = 0x136; + break; + case 2: + _nextManSequenceId = 0x13B; + break; + case 3: + _nextManSequenceId = 0x13A; + break; + } + } + } + playRandomSound(5); + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + _vm->_timers[4] = _vm->getRandom(50) + 75; + _vm->_timers[5] = _vm->getRandom(40) + 50; + } + + _vm->gameUpdateTick(); + } +} + +void Scene08::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS08LeaveScene: + _vm->_sceneDone = true; + gnap._actionStatus = -1; + break; + case kAS08TalkMan: + _nextManSequenceId = 0x13F; + gnap._actionStatus = -1; + break; + case kAS08LookMan: + _nextManSequenceId = 0x140; + gnap._actionStatus = -1; + break; + case kAS08LookDog: + _nextManSequenceId = 0x137; + gnap._actionStatus = -1; + break; + case kAS08GrabDog: + if (_currDogSequenceId == 0x135) + _nextDogSequenceId = 0x133; + else + _nextDogSequenceId = 0x13C; + gnap._actionStatus = -1; + break; + case kAS08TalkDog: + if (_currDogSequenceId == 0x135) + _nextDogSequenceId = 0x133; + else + _nextDogSequenceId = 0x13C; + gnap._actionStatus = -1; + break; + } + } + + if (gameSys.getAnimationStatus(1) == 2) { + gameSys.setAnimation(0, 0, 1); + switch (plat._actionStatus) { + case kAS08PlatWithDog: + _nextDogSequenceId = 0x147; + break; + case kAS08PlatWithMan: + _nextManSequenceId = 0x140; + plat._actionStatus = -1; + break; + } + } + + if (gameSys.getAnimationStatus(2) == 2 && _nextManSequenceId != -1) { + gameSys.setAnimation(_nextManSequenceId, 100, 2); + gameSys.insertSequence(_nextManSequenceId, 100, _currManSequenceId, 100, kSeqSyncWait, 0, 0, 0); + _currManSequenceId = _nextManSequenceId; + _nextManSequenceId = -1; + } + + if (gameSys.getAnimationStatus(3) == 2) { + if (_currDogSequenceId == 0x147) + plat._actionStatus = -1; + if (_currDogSequenceId == 0x149 || _currDogSequenceId == 0x14A || _currDogSequenceId == 0x14B) { + if (_vm->getRandom(2) != 0) + _nextManSequenceId = 0x13D; + else + _nextManSequenceId = 0x13E; + } else if (_currDogSequenceId == 0x133) + _nextManSequenceId = 0x139; + if (_nextDogSequenceId == 0x149 || _nextDogSequenceId == 0x14A || _nextDogSequenceId == 0x14B) { + gameSys.setAnimation(_nextDogSequenceId, 100, 3); + gameSys.insertSequence(_nextDogSequenceId, 100, _currDogSequenceId, 100, kSeqSyncWait, 0, 0, 0); + switch (_nextDogSequenceId) { + case 0x149: + _vm->setFlag(kGFBarnPadlockOpen); + _vm->_hotspots[kHS08Meat]._flags = SF_DISABLED | SF_WALKABLE; + gameSys.removeSequence(0x144, 1, true); + break; + case 0x14A: + _vm->setFlag(kGFTruckFilledWithGas); + _vm->_hotspots[kHS08Bone]._flags = SF_DISABLED | SF_WALKABLE; + gameSys.removeSequence(0x145, 1, true); + break; + case 0x14B: + _vm->setFlag(kGFTruckKeysUsed); + _vm->_hotspots[kHS08Toy]._flags = SF_DISABLED | SF_WALKABLE; + gameSys.removeSequence(0x146, 1, true); + break; + } + _currDogSequenceId = _nextDogSequenceId; + _nextDogSequenceId = 0x134; + } else if (_nextDogSequenceId == 0x147) { + gameSys.setAnimation(_nextDogSequenceId, 100, 3); + gameSys.insertSequence(_nextDogSequenceId, 100, _currDogSequenceId, 100, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x148, 160, plat._sequenceId | (plat._sequenceDatNum << 16), plat._id, kSeqSyncWait, 0, 0, 0); + _currDogSequenceId = _nextDogSequenceId; + _nextDogSequenceId = 0x134; + plat._pos = Common::Point(1, 8); + plat._id = 160; + plat._sequenceId = 0x148; + plat._idleFacing = kDirIdleRight; + plat._sequenceDatNum = 0; + if (gnap._pos == Common::Point(1, 8)) + gnap.walkStep(); + } else if (_nextDogSequenceId != -1) { + gameSys.setAnimation(_nextDogSequenceId, 100, 3); + gameSys.insertSequence(_nextDogSequenceId, 100, _currDogSequenceId, 100, kSeqSyncWait, 0, 0, 0); + _currDogSequenceId = _nextDogSequenceId; + if (_nextDogSequenceId != 0x135) + _nextDogSequenceId = 0x134; + if (_currDogSequenceId == 0x133) { + _vm->_timers[2] = _vm->getRandom(30) + 20; + _vm->_timers[3] = _vm->getRandom(50) + 200; + gameSys.insertSequence(0x14D, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x14D; + gnap._idleFacing = kDirUpRight; + gnap._sequenceDatNum = 0; + gnap._actionStatus = -1; + } + } + } +} + +/*****************************************************************************/ + +Scene09::Scene09(GnapEngine *vm) : Scene(vm) { +} + +int Scene09::init() { + return 0x4E; +} + +void Scene09::updateHotspots() { + _vm->setHotspot(kHS09Platypus, 0, 200, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS09ExitKitchen, 280, 200, 380, 400, SF_EXIT_U_CURSOR); + _vm->setHotspot(kHS09ExitHouse, 790, 200, 799, 450, SF_EXIT_R_CURSOR | SF_WALKABLE); + _vm->setHotspot(kHS09Trash, 440, 310, 680, 420, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS09WalkArea1, 0, 0, 799, 400); + _vm->setHotspot(kHS09WalkArea2, 0, 0, 630, 450); + _vm->setHotspot(kHS09WalkArea2, 0, 0, 175, 495); + _vm->setDeviceHotspot(kHS09Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 8; +} + +void Scene09::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->queueInsertDeviceIcon(); + + gameSys.insertSequence(0x4D, 1, 0, 0, kSeqLoop, 0, 0, 0); + gameSys.insertSequence(0x4B, 2, 0, 0, kSeqNone, 0, 0, 0); + + if (_vm->_prevSceneNum == 8) { + gnap.initPos(11, 8, kDirBottomLeft); + plat.initPos(12, 7, kDirIdleRight); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(9, 8), -1, 0x107BA, 1); + plat.walkTo(Common::Point(9, 7), -1, 0x107D2, 1); + } else { + gnap.initPos(4, 7, kDirBottomRight); + plat.initPos(5, 7, kDirIdleLeft); + _vm->endSceneInit(); + } + + _vm->_timers[4] = _vm->getRandom(150) + 50; + _vm->_timers[5] = _vm->getRandom(40) + 50; + + while (!_vm->_sceneDone) { + if (!_vm->isSoundPlaying(0x10919)) + _vm->playSound(0x10919, true); + + _vm->testWalk(0, 0, -1, -1, -1, -1); + + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS09Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + _vm->_timers[4] = _vm->getRandom(150) + 50; + _vm->_timers[5] = _vm->getRandom(40) + 50; + } + break; + + case kHS09Platypus: + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + break; + } + break; + + case kHS09ExitKitchen: + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 10; + gnap.walkTo(Common::Point(4, 7), 0, 0x107BF, 1); + gnap._actionStatus = kAS09LeaveScene; + plat.walkTo(Common::Point(4, 8), -1, 0x107D2, 1); + plat._idleFacing = kDirIdleRight; + break; + + case kHS09ExitHouse: + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 8; + gnap.walkTo(Common::Point(10, -1), 0, 0x107AB, 1); + gnap._actionStatus = kAS09LeaveScene; + plat.walkTo(Common::Point(10, -1), -1, 0x107CD, 1); + plat._idleFacing = kDirIdleRight; + break; + + case kHS09Trash: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(9, 6), 8, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(8, 3)); + break; + case GRAB_CURSOR: + gnap._actionStatus = kAS09SearchTrash; + gnap.walkTo(Common::Point(9, 6), 0, 0x107BC, 1); + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + break; + + case kHS09WalkArea1: + case kHS09WalkArea2: + case kHS09WalkArea3: + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + } + + updateAnimations(); + + if (!_vm->_isLeavingScene && gnap._actionStatus != 1 && gnap._actionStatus != 2) { + plat.updateIdleSequence(); + gnap.updateIdleSequence(); + if (!_vm->_timers[4]) { + _vm->_timers[4] = _vm->getRandom(150) + 100; + if (_vm->_timers[4] & 1) + gameSys.insertSequence(0x49, 1, 0, 0, kSeqNone, 0, 0, 0); + else + gameSys.insertSequence(0x4A, 1, 0, 0, kSeqNone, 0, 0, 0); + } + playRandomSound(5); + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + _vm->_timers[4] = _vm->getRandom(150) + 50; + _vm->_timers[5] = _vm->getRandom(40) + 50; + } + + _vm->gameUpdateTick(); + } +} + +void Scene09::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS09LeaveScene: + _vm->_sceneDone = true; + gnap._actionStatus = -1; + break; + case kAS09SearchTrash: + gameSys.setAnimation(0x4C, 120, 0); + gameSys.insertSequence(0x4C, 120, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.removeSequence(0x4B, 2, true); + gnap._sequenceId = 0x4C; + gnap._id = 120; + gnap._idleFacing = kDirUpLeft; + gnap._sequenceDatNum = 0; + gnap._pos = Common::Point(9, 6); + gnap._actionStatus = kAS09SearchTrashDone; + break; + case kAS09SearchTrashDone: + gameSys.insertSequence(0x4B, 2, 0, 0, kSeqNone, 0, 0, 0); + _vm->_timers[2] = 360; + _vm->_timers[4] = _vm->getRandom(150) + 100; + gnap._actionStatus = -1; + break; + } + } +} + +} // End of namespace Gnap diff --git a/engines/gnap/scenes/group0.h b/engines/gnap/scenes/group0.h new file mode 100644 index 0000000000..e06380926d --- /dev/null +++ b/engines/gnap/scenes/group0.h @@ -0,0 +1,401 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GNAP_GROUP0_H +#define GNAP_GROUP0_H + +#include "gnap/debugger.h" + +namespace Gnap { + +enum { + kHS01Platypus = 0, + kHS01ExitTruck = 1, + kHS01Mud = 2, + kHS01Pigs = 3, + kHS01Spaceship = 4, + kHS01Device = 5, + kHS01WalkArea1 = 6, + kHS01WalkArea2 = 7, + kHS01WalkArea3 = 8, + kHS01WalkArea4 = 9, + kHS01WalkArea5 = 10, + kHS01WalkArea6 = 11, + kHS01WalkArea7 = 12, + kHS01WalkArea8 = 13 +}; + +enum { + kHS02Platypus = 0, + kHS02Chicken = 1, + kHS02Truck1 = 2, + kHS02Truck2 = 3, + kHS02TruckGrill = 4, + kHS02Device = 5, + kHS02ExitHouse = 6, + kHS02ExitBarn = 7, + kHS02ExitCreek = 8, + kHS02ExitPigpen = 9, + kHS02WalkArea1 = 10, + kHS02WalkArea2 = 11, + kHS02WalkArea3 = 12, + kHS02WalkArea4 = 13 +}; + +enum { + kHS03Platypus = 0, + kHS03Grass = 1, + kHS03ExitTruck = 2, + kHS03Creek = 3, + kHS03TrappedPlatypus = 4, + kHS03Device = 5, + kHS03WalkAreas1 = 6, + kHS03WalkAreas2 = 7, + kHS03PlatypusWalkArea = 8, + kHS03WalkAreas3 = 9 +}; + +enum { + kHS04Platypus = 0, + kHS04Twig = 1, + kHS04Dog = 2, + kHS04Axe = 3, + kHS04Door = 4, + kHS04ExitTruck = 5, + kHS04Device = 6, + kHS04Window = 7, + kHS04ExitBarn = 8, + kHS04WalkArea1 = 9, + kHS04WalkArea2 = 10 +}; + +enum { + kHS05Platypus = 0, + kHS05Haystack = 1, + kHS05Padlock = 2, + kHS05Ladder = 3, + kHS05ExitHouse = 4, + kHS05Chicken = 5, + kHS05Device = 6, + kHS05WalkArea1 = 7, + kHS05WalkArea2 = 8, + kHS05WalkArea3 = 9 +}; + +enum { + kHS06Platypus = 0, + kHS06Gas = 1, + kHS06Ladder = 2, + kHS06Horse = 3, + kHS06ExitOutsideBarn = 4, + kHS06Device = 5, + kHS06WalkArea1 = 6, + kHS06WalkArea2 = 7, + kHS06WalkArea3 = 8, + kHS06WalkArea4 = 9, + kHS06WalkArea5 = 10 +}; + +enum { + kHS07Platypus = 0, + kHS07ExitHouse = 1, + kHS07Dice = 2, + kHS07Device = 3, + kHS07WalkArea1 = 4, + kHS07WalkArea2 = 5, + kHS07WalkArea3 = 6 +}; + +enum { + kH08SPlatypus = 0, + kHS08ExitBackdoor = 1, + kHS08ExitCrash = 2, + kHS08Man = 3, + kHS08Door = 4, + kHS08Meat = 5, + kHS08Bone = 6, + kHS08Toy = 7, + kHS08WalkArea1 = 8, + kHS08Device = 9, + kHS08WalkArea2 = 10 +}; + +enum { + kHS09Platypus = 0, + kHS09ExitKitchen = 1, + kHS09ExitHouse = 2, + kHS09Trash = 3, + kHS09Device = 4, + kHS09WalkArea1 = 5, + kHS09WalkArea2 = 6, + kHS09WalkArea3 = 7 +}; + +enum { + kAS01LookSpaceship = 1, + kAS01LookSpaceshipDone = 2, + kAS01LeaveScene = 3, + kAS01TakeMud = 5, + kAS01LookPigs = 6, + kAS01UsePigs = 7 +}; + +enum { + kAS02UseTruckNoKeys = 0, + kAS02UseGasWithTruck = 1, + kAS02UseTruckGas = 2, + kAS02UseTruckNoGas = 3, + kAS02GrabTruckGrill = 5, + kAS02LeaveScene = 6, + kAS02TalkChicken = 7, + kAS02GrabChicken = 8, + kAS02GrabChickenDone = 9, + kAS02UseTruckNoKeysDone = 11, + kAS02UseGasWithTruckDone = 12, + kAS02UseTwigWithChicken = 16 +}; + +enum { + kAS03LeaveScene = 0, + kAS03FreePlatypus = 1, + kAS03HypnotizePlat = 2, + kAS03HypnotizeScaredPlat= 3, + kAS03FreePlatypusDone = 4, + kAS03GrabPlatypus = 5, + kAS03GrabCreek = 6, + kAS03GrabCreekDone = 7, + kAS03GrabScaredPlatypus = 8 +}; + +enum { + kAS04OpenDoor = 1, + kAS04GetKeyFirst = 2, + kAS04GetKeyAnother = 3, + kAS04LeaveScene = 4, + kAS04GetKeyFirstDone = 6, + kAS04GetKeyFirst2 = 7, + kAS04GetKeyAnother2 = 8, + kAS04GetKeyAnotherDone = 9, + kAS04OpenDoorDone = 10, + kAS04GrabDog = 12, + kAS04GrabAxe = 13 +}; + +enum { + kAS05PlatSearchHaystack = 0, + kAS05TryPickPadlock = 1, + kAS05PickPadlock = 2, + kAS05TalkChicken = 3, + kAS05GrabChicken = 4, + kAS05GrabLadder = 5, + kAS05EnterBarn = 6, + kAS05UseTwigWithChicken = 11, + kAS05LeaveScene = 12 +}; + +enum { + kAS06TryToGetGas = 0, + kAS06TryToClimbLadder = 1, + kAS06TryToClimbLadderDone = 2, + kAS06TalkToHorse = 3, + kAS06UseTwigOnHorse = 4, + kAS06LeaveScene = 5 +}; + +enum { + kAS07Wait = 0, + kAS07LeaveScene = 1 +}; + +enum { + kAS08LeaveScene = 0, + kAS08TalkMan = 1, + kAS08LookMan = 2, + kAS08LookDog = 3, + kAS08GrabDog = 4, + kAS08TalkDog = 5, + kAS08PlatWithMan = 6, + kAS08PlatWithDog = 7 +}; + +enum { + kAS09LeaveScene = 0, + kAS09SearchTrash = 1, + kAS09SearchTrashDone = 2 +}; + +/*****************************************************************************/ + +class GnapEngine; +class CutScene; + +class Scene01: public Scene { +public: + Scene01(GnapEngine *vm); + virtual ~Scene01(); + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {} + +private: + int _pigsIdCtr; + int _smokeIdCtr; + Graphics::Surface *_spaceshipSurface; +}; + +class Scene02: public Scene { +public: + Scene02(GnapEngine *vm); + virtual ~Scene02() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {} + +private: + int _truckGrillCtr; + int _nextChickenSequenceId; + int _currChickenSequenceId; + int _gnapTruckSequenceId; +}; + +class Scene03: public Scene { +public: + Scene03(GnapEngine *vm); + virtual ~Scene03() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {} + +private: + bool _platypusHypnotized; + bool _platypusScared; + int _nextPlatSequenceId; + int _nextFrogSequenceId; + int _currFrogSequenceId; +}; + +class Scene04: public Scene { +public: + Scene04(GnapEngine *vm); + virtual ~Scene04() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {} + +private: + bool _triedWindow; + int _dogIdCtr; + int _nextDogSequenceId; + int _currDogSequenceId; +}; + +class Scene05: public Scene { +public: + Scene05(GnapEngine *vm); + virtual ~Scene05() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {} + +private: + int _nextChickenSequenceId; + int _currChickenSequenceId; +}; + +class Scene06: public Scene { +public: + Scene06(GnapEngine *vm); + virtual ~Scene06() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {} + +private: + bool _horseTurnedBack; + int _nextPlatSequenceId; + int _nextHorseSequenceId; + int _currHorseSequenceId; +}; + +class Scene07: public Scene { +public: + Scene07(GnapEngine *vm); + virtual ~Scene07() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {} +}; + +class Scene08: public Scene { +public: + Scene08(GnapEngine *vm); + virtual ~Scene08() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb(); + +private: + int _nextDogSequenceId; + int _currDogSequenceId; + int _nextManSequenceId; + int _currManSequenceId; +}; + +class Scene09: public Scene { +public: + Scene09(GnapEngine *vm); + virtual ~Scene09() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {} +}; + +} // End of namespace Gnap + +#endif // GNAP_GROUP0_H diff --git a/engines/gnap/scenes/group1.cpp b/engines/gnap/scenes/group1.cpp new file mode 100644 index 0000000000..bd152c7f39 --- /dev/null +++ b/engines/gnap/scenes/group1.cpp @@ -0,0 +1,4500 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gnap/gnap.h" +#include "gnap/gamesys.h" +#include "gnap/resource.h" +#include "gnap/scenes/group1.h" + +namespace Gnap { + +Scene10::Scene10(GnapEngine *vm) : Scene(vm) { + _nextCookSequenceId = -1; + _currCookSequenceId = -1; +} + +int Scene10::init() { + GameSys& gameSys = *_vm->_gameSys; + + gameSys.setAnimation(0, 0, 0); + gameSys.setAnimation(0, 0, 1); + gameSys.setAnimation(0, 0, 2); + return 0x10F; +} + +void Scene10::updateHotspots() { + _vm->setHotspot(kHS10Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS10ExitBar, 0, 75, 85, 455, SF_EXIT_NW_CURSOR); + _vm->setHotspot(kHS10ExitBackdoor, 75, 590, 500, 599, SF_EXIT_D_CURSOR | SF_WALKABLE); + _vm->setHotspot(kHS10Cook, 370, 205, 495, 460, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS10Tongs, 250, 290, 350, 337, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS10Box, 510, 275, 565, 330, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS10Oven, 690, 280, 799, 420, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS10WalkArea1, 59, 0, 495, 460); + _vm->setHotspot(kHS10WalkArea2, 495, 0, 650, 420); + _vm->setHotspot(kHS10WalkArea3, 651, 0, 725, 400); + _vm->setHotspot(kHS10WalkArea4, 725, 0, 799, 441); + _vm->setDeviceHotspot(kHS10Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 12; +} + +void Scene10::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _currCookSequenceId = 0x103; + + gameSys.setAnimation(0x103, 100, 2); + gameSys.insertSequence(0x103, 100, 0, 0, kSeqNone, 0, 0, 0); + + _nextCookSequenceId = 0x106; + if (!_vm->isFlag(kGFMudTaken)) + gameSys.insertSequence(0x107, 100, 0, 0, kSeqNone, 0, 0, 0); + + _vm->queueInsertDeviceIcon(); + + if (_vm->_prevSceneNum == 9) { + gnap.initPos(11, 8, kDirBottomLeft); + plat.initPos(12, 7, kDirIdleRight); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(9, 8), -1, 0x107BA, 1); + plat.walkTo(Common::Point(9, 7), -1, 0x107D2, 1); + } else { + gnap.initPos(-1, 7, kDirBottomRight); + plat.initPos(-2, 8, kDirIdleLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(1, 7), -1, 0x107B9, 1); + plat.walkTo(Common::Point(1, 8), -1, 0x107C2, 1); + } + + _vm->_timers[4] = _vm->getRandom(80) + 150; + _vm->_timers[5] = _vm->getRandom(100) + 100; + + while (!_vm->_sceneDone) { + if (!_vm->isSoundPlaying(0x1091E)) + _vm->playSound(0x1091E, true); + + if (!_vm->isSoundPlaying(0x1091A)) + _vm->playSound(0x1091A, true); + + _vm->updateMouseCursor(); + + _vm->testWalk(0, 0, -1, -1, -1, -1); + + _vm->updateCursorByHotspot(); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS10Platypus: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + if (_vm->isFlag(kGFMudTaken)) + gnap.playMoan1(plat._pos); + else + gnap.playScratchingHead(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(10); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + break; + } + } + break; + + case kHS10ExitBar: + _vm->_isLeavingScene = true; + gnap.actionIdle(0x10C); + gnap.walkTo(Common::Point(0, 7), 0, 0x107AF, 1); + gnap._actionStatus = kAS10LeaveScene; + plat.walkTo(Common::Point(0, 7), -1, 0x107CF, 1); + _vm->_newSceneNum = 11; + break; + + case kHS10ExitBackdoor: + _vm->_isLeavingScene = true; + gnap.actionIdle(0x10C); + gnap.walkTo(Common::Point(2, 9), 0, 0x107AE, 1); + gnap._actionStatus = kAS10LeaveScene; + plat.walkTo(Common::Point(3, 9), -1, 0x107C7, 1); + _vm->_newSceneNum = 9; + break; + + case kHS10Cook: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(4, 8), 6, 0); + gameSys.setAnimation(makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, 0); + gnap._actionStatus = kAS10AnnoyCook; + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(6, 0)); + break; + case GRAB_CURSOR: + gnap.playImpossible(); + gnap._idleFacing = kDirBottomRight; + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.actionIdle(0x10C); + gnap.walkTo(Common::Point(4, 8), 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS10AnnoyCook; + break; + case PLAT_CURSOR: + gnap.actionIdle(0x10C); + gnap.useDeviceOnPlatypus(); + plat.walkTo(Common::Point(4, 6), -1, -1, 1); + gnap.walkTo(Common::Point(4, 8), 0, 0x107BB, 1); + gnap._actionStatus = kAS10AnnoyCook; + break; + } + } + break; + + case kHS10Tongs: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(3, 7), 4, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + if (_vm->isFlag(kGFMudTaken)) + gnap.playMoan2(Common::Point(-1, -1)); + else + gnap.playScratchingHead(Common::Point(4, 3)); + break; + case GRAB_CURSOR: + if (_vm->isFlag(kGFMudTaken)) + gnap.playMoan2(Common::Point(-1, -1)); + else { + gnap.actionIdle(0x10C); + gnap.walkTo(Common::Point(4, 8), 0, 0x107BB, 1); + gnap._actionStatus = kAS10AnnoyCook; + } + break; + case TALK_CURSOR: + gnap.playImpossible(); + break; + case PLAT_CURSOR: + if (_vm->isFlag(kGFMudTaken)) + gnap.playMoan2(Common::Point(-1, -1)); + else { + gnap.actionIdle(0x10C); + gnap.useDeviceOnPlatypus(); + plat.walkTo(Common::Point(3, 7), -1, -1, 1); + gnap.walkTo(Common::Point(4, 8), 0, 0x107BB, 1); + gnap._actionStatus = kAS10AnnoyCook; + } + break; + } + } + break; + + case kHS10Box: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(7, 6), 6, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(7, 3)); + break; + case GRAB_CURSOR: + gnap.actionIdle(0x10C); + gnap.walkTo(Common::Point(4, 8), 0, 0x107BB, 1); + gnap._actionStatus = kAS10AnnoyCook; + break; + case TALK_CURSOR: + gnap.playImpossible(); + break; + case PLAT_CURSOR: + if (_vm->isFlag(kGFMudTaken)) + gnap.playMoan2(Common::Point(-1, -1)); + else { + _vm->invAdd(kItemTongs); + _vm->setFlag(kGFMudTaken); + gnap.actionIdle(0x10C); + gnap.useDeviceOnPlatypus(); + plat.walkTo(Common::Point(7, 6), 1, 0x107D2, 1); + plat._actionStatus = kAS10PlatWithBox; + plat._idleFacing = kDirIdleRight; + _vm->_largeSprite = gameSys.createSurface(0xC3); + gnap.playIdle(Common::Point(7, 6)); + } + break; + } + } + break; + + case kHS10Oven: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(9, 6), 10, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playSequence(gnap.getSequenceId(kGSDeflect, Common::Point(10, 5)) | 0x10000); + break; + case GRAB_CURSOR: + gnap.actionIdle(0x10C); + gnap.walkTo(Common::Point(9, 6), 0, 0x107BB, 1); + gameSys.insertSequence(0x10E, 120, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x10E; + gnap._id = 120; + gnap._idleFacing = kDirUpRight; + gnap._sequenceDatNum = 0; + gnap._pos = Common::Point(9, 6); + _vm->_timers[2] = 360; + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + break; + + case kHS10WalkArea1: + case kHS10WalkArea2: + case kHS10WalkArea3: + case kHS10WalkArea4: + gnap.actionIdle(0x10C); + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + case kHS10Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + default: + if (_vm->_mouseClickState._left) { + gnap.actionIdle(0x10C); + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + } + + updateAnimations(); + + if (!_vm->_isLeavingScene) { + plat.updateIdleSequence(); + gnap.updateIdleSequence(); + if (!_vm->_timers[4]) { + _vm->_timers[4] = _vm->getRandom(80) + 150; + _vm->playSound(0x12B, false); + } + if (!_vm->_timers[5]) { + _vm->_timers[5] = _vm->getRandom(100) + 100; + int _gnapRandomValue = _vm->getRandom(4); + if (_gnapRandomValue) { + int sequenceId; + if (_gnapRandomValue == 1) { + sequenceId = 0x8A5; + } else if (_gnapRandomValue == 2) { + sequenceId = 0x8A6; + } else { + sequenceId = 0x8A7; + } + gameSys.insertSequence(sequenceId | 0x10000, 179, 0, 0, kSeqNone, 0, 0, 0); + } + } + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } +} + +void Scene10::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS10LeaveScene: + _vm->_sceneDone = true; + break; + case kAS10AnnoyCook: + _nextCookSequenceId = 0x105; + break; + } + } + + if (gameSys.getAnimationStatus(1) == 2) { + gameSys.setAnimation(0, 0, 1); + switch (plat._actionStatus) { + case kAS10PlatWithBox: + _nextCookSequenceId = 0x109; + break; + } + } + + if (gameSys.getAnimationStatus(2) == 2 && _nextCookSequenceId != -1) { + + switch (_nextCookSequenceId) { + case 0x109: + plat._pos = Common::Point(4, 8); + gameSys.insertSequence(0x109, 100, _currCookSequenceId, 100, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x107C9, 160, + plat._sequenceId | (plat._sequenceDatNum << 16), plat._id, + kSeqSyncWait, _vm->getSequenceTotalDuration(0x109) + _vm->getSequenceTotalDuration(0x10A) + _vm->getSequenceTotalDuration(0x10843), + 75 * plat._pos.x - plat._gridX, 48 * plat._pos.y - plat._gridY); + gameSys.removeSequence(0x107, 100, true); + _currCookSequenceId = 0x109; + _nextCookSequenceId = 0x843; + plat._sequenceId = 0x7C9; + plat._id = 160; + plat._idleFacing = kDirIdleLeft; + plat._sequenceDatNum = 1; + break; + case 0x843: + _vm->hideCursor(); + gameSys.insertSpriteDrawItem(_vm->_largeSprite, 0, 0, 300); + gameSys.insertSequence(0x10843, 301, _currCookSequenceId, 100, kSeqSyncWait, 0, 0, 0); + _currCookSequenceId = 0x843; + _nextCookSequenceId = 0x10A; + break; + case 0x10A: + gameSys.insertSequence(_nextCookSequenceId, 100, 0x10843, 301, kSeqSyncWait, 0, 0, 0); + _currCookSequenceId = _nextCookSequenceId; + _nextCookSequenceId = 0x104; + _vm->showCursor(); + gameSys.removeSpriteDrawItem(_vm->_largeSprite, 300); + _vm->delayTicksCursor(5); + _vm->deleteSurface(&_vm->_largeSprite); + _vm->setGrabCursorSprite(kItemTongs); + if (plat._actionStatus == kAS10PlatWithBox) + plat._actionStatus = -1; + if (gnap._pos == Common::Point(4, 8)) + gnap.walkStep(); + break; + default: + gameSys.insertSequence(_nextCookSequenceId, 100, _currCookSequenceId, 100, kSeqSyncWait, 0, 0, 0); + _currCookSequenceId = _nextCookSequenceId; + break; + } + + switch (_currCookSequenceId) { + case 0x106: + if (gnap._actionStatus >= 0 || plat._actionStatus >= 0) + _nextCookSequenceId = 0x106; + else { + int rnd = _vm->getRandom(7); + switch (rnd) { + case 0: + _nextCookSequenceId = 0x104; + break; + case 1: + _nextCookSequenceId = 0x103; + break; + case 2: + _nextCookSequenceId = 0x106; + gameSys.insertSequence(0x10D, 1, 0, 0, kSeqNone, 0, 0, 0); + break; + default: + _nextCookSequenceId = 0x106; + } + } + break; + case 0x103: + if (gnap._actionStatus >= 0 || plat._actionStatus >= 0) + _nextCookSequenceId = 0x106; + else if (_vm->getRandom(7) == 0) + _nextCookSequenceId = 0x104; + else + _nextCookSequenceId = 0x106; + break; + case 0x104: + if (gnap._actionStatus >= 0 || plat._actionStatus >= 0) + _nextCookSequenceId = 0x106; + else if (_vm->getRandom(7) == 0) + _nextCookSequenceId = 0x103; + else + _nextCookSequenceId = 0x106; + break; + case 0x105: + if (gnap._actionStatus >= 0 || plat._actionStatus >= 0) + _nextCookSequenceId = 0x106; + else { + int rnd = _vm->getRandom(7); + switch (rnd) { + case 0: + _nextCookSequenceId = 0x104; + break; + case 1: + _nextCookSequenceId = 0x103; + break; + default: + _nextCookSequenceId = 0x106; + } + } + _vm->_timers[2] = _vm->getRandom(30) + 20; + _vm->_timers[3] = 300; + gameSys.insertSequence(0x10C, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x10C; + gnap._idleFacing = kDirUpRight; + gnap._sequenceDatNum = 0; + gnap._actionStatus = -1; + plat._actionStatus = -1; + break; + } + if (_currCookSequenceId == 0x843) + gameSys.setAnimation(_currCookSequenceId | 0x10000, 301, 2); + else + gameSys.setAnimation(_currCookSequenceId, 100, 2); + } +} + +void Scene10::updateAnimationsCb() { + GameSys& gameSys = *_vm->_gameSys; + + if (gameSys.getAnimationStatus(2) == 2) { + gameSys.setAnimation(_nextCookSequenceId, 100, 2); + gameSys.insertSequence(_nextCookSequenceId, 100, _currCookSequenceId, 100, kSeqSyncWait, 0, 0, 0); + _currCookSequenceId = _nextCookSequenceId; + _nextCookSequenceId = 0x106; + } +} + +/*****************************************************************************/ + +Scene11::Scene11(GnapEngine *vm) : Scene(vm) { + _billardBallCtr = 0; + _nextHookGuySequenceId = -1; + _currHookGuySequenceId = -1; + _nextGoggleGuySequenceId = -1; + _currGoggleGuySequenceId = -1; +} + +int Scene11::init() { + GameSys& gameSys = *_vm->_gameSys; + + gameSys.setAnimation(0, 0, 0); + gameSys.setAnimation(0, 0, 3); + gameSys.setAnimation(0, 0, 2); + if (_vm->_prevSceneNum == 10 || _vm->_prevSceneNum == 13) { + _vm->playSound(0x108EC, false); + _vm->playSound(0x10928, false); + } + return 0x209; +} + +void Scene11::updateHotspots() { + _vm->setHotspot(kHS11Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS11ExitKitchen, 420, 140, 520, 345, SF_EXIT_U_CURSOR); + _vm->setHotspot(kHS11ExitToilet, 666, 130, 740, 364, SF_EXIT_R_CURSOR); + _vm->setHotspot(kHS11ExitLeft, 0, 350, 10, 599, SF_EXIT_L_CURSOR | SF_WALKABLE); + _vm->setHotspot(kHS11GoggleGuy, 90, 185, 185, 340, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS11HookGuy, 210, 240, 340, 430, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS11Billard, 640, 475, 700, 530, SF_WALKABLE | SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS11WalkArea1, 0, 0, 365, 453); + _vm->setHotspot(kHS11WalkArea2, 0, 0, 629, 353); + _vm->setHotspot(kHS11WalkArea3, 629, 0, 799, 364); + _vm->setHotspot(kHS11WalkArea4, 735, 0, 799, 397); + _vm->setHotspot(kHS11WalkArea5, 510, 540, 799, 599); + _vm->setDeviceHotspot(kHS11Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 13; +} + +void Scene11::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + bool flag = true; + + _vm->_timers[7] = 50; + _vm->_hotspots[kHS11Billard]._flags |= SF_DISABLED; + + _currGoggleGuySequenceId = 0x1F9; + _currHookGuySequenceId = 0x201; + + switch (_vm->_prevSceneNum) { + case 13: + gnap.initPos(8, 5, kDirBottomLeft); + plat.initPos(9, 6, kDirIdleRight); + break; + case 47: + gnap.initPos(8, 5, kDirBottomLeft); + plat.initPos(9, 5, kDirIdleRight); + _currGoggleGuySequenceId = 0x1FA; + _currHookGuySequenceId = 0x1FF; + _vm->_timers[7] = 180; + break; + case 12: + gnap.initPos(-1, 9, kDirBottomRight); + plat.initPos(-2, 8, kDirIdleLeft); + break; + default: + gnap.initPos(6, 6, kDirBottomLeft); + plat.initPos(6, 5, kDirIdleRight); + break; + } + + _vm->queueInsertDeviceIcon(); + + gameSys.insertSequence(_currHookGuySequenceId, 120, 0, 0, kSeqNone, 0, 0, 0); + + _nextHookGuySequenceId = -1; + + gameSys.setAnimation(_currHookGuySequenceId, 120, 3); + gameSys.insertSequence(_currGoggleGuySequenceId, 121, 0, 0, kSeqNone, 0, 0, 0); + + _nextGoggleGuySequenceId = -1; + + gameSys.setAnimation(_currGoggleGuySequenceId, 121, 2); + + _vm->_timers[5] = _vm->getRandom(100) + 75; + _vm->_timers[4] = _vm->getRandom(40) + 20; + _vm->_timers[6] = _vm->getRandom(100) + 100; + _vm->endSceneInit(); + + if (_vm->_prevSceneNum == 12) { + gnap.walkTo(Common::Point(2, 8), -1, 0x107B9, 1); + plat.walkTo(Common::Point(1, 8), -1, 0x107C2, 1); + } + + gameSys.insertSequence(0x208, 256, 0, 0, kSeqNone, 40, 0, 0); + + while (!_vm->_sceneDone) { + _vm->testWalk(0, 0, -1, -1, -1, -1); + + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS11Platypus: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + break; + } + } + break; + + case kHS11ExitKitchen: + _vm->_isLeavingScene = true; + gnap.walkTo(Common::Point(6, 5), 0, 0x107BF, 1); + gnap._actionStatus = kAS11LeaveScene; + plat.walkTo(Common::Point(6, 6), -1, -1, 1); + _vm->_newSceneNum = 10; + break; + + case kHS11ExitToilet: + _vm->_isLeavingScene = true; + gnap.walkTo(Common::Point(8, 5), 0, 0x107BF, 1); + gnap._actionStatus = kAS11LeaveScene; + plat.walkTo(Common::Point(8, 6), -1, -1, 1); + _vm->_newSceneNum = 13; + break; + + case kHS11ExitLeft: + _vm->_isLeavingScene = true; + gnap.walkTo(Common::Point(-1, 8), 0, 0x107AF, 1); + gnap._actionStatus = kAS11LeaveScene; + plat.walkTo(Common::Point(-1, 9), -1, 0x107CF, 1); + _vm->_newSceneNum = 12; + break; + + case kHS11GoggleGuy: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemMagazine) { + gnap.walkTo(Common::Point(3, 7), 0, 0x107BC, 1); + gnap._actionStatus = kAS11ShowMagazineToGoggleGuy; + gnap.playShowItem(_vm->_grabCursorSpriteIndex, 2, 0); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(3, 7), 2, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(1, 6)); + break; + case GRAB_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpLeft; + gnap.walkTo(Common::Point(3, 7), 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS11TalkGoggleGuy; + break; + } + } + } + break; + + case kHS11HookGuy: + if (gnap._actionStatus < 0) { + gnap._idleFacing = kDirUpRight; + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.walkTo(Common::Point(5, 6), 0, 0x107BC, 9); + gnap._actionStatus = kAS11ShowItemToHookGuy; + gnap.playShowItem(_vm->_grabCursorSpriteIndex, 4, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playSequence(gnap.getSequenceId(kGSDeflect, Common::Point(3, 6)) | 0x10000); + break; + case GRAB_CURSOR: + gnap.walkTo(Common::Point(5, 6), 0, 0x107BC, 1); + gnap._actionStatus = kAS11GrabHookGuy; + break; + case TALK_CURSOR: + gnap._idleFacing = kDirBottomLeft; + gnap.walkTo(Common::Point(5, 6), 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS11TalkHookGuy; + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS11Billard: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(Common::Point(9, 8)); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(9, 8)); + break; + case GRAB_CURSOR: + gnap.walkTo(Common::Point(9, 8), 0, 0x107BA, 1); + gnap._actionStatus = kAS11GrabBillardBall; + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(Common::Point(9, 8)); + break; + } + } + } + break; + + case kHS11WalkArea1: + case kHS11WalkArea2: + case kHS11WalkArea3: + case kHS11WalkArea4: + case kHS11WalkArea5: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + case kHS11Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + default: + if (_vm->_mouseClickState._left && gnap._actionStatus < 0) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + + } + + updateAnimations(); + + if (!_vm->_isLeavingScene) { + if (flag && !_vm->_timers[7]) { + flag = false; + gameSys.setAnimation(0x207, 257, 4); + gameSys.insertSequence(0x207, 257, 0, 0, kSeqNone, 0, 0, 0); + } + plat.updateIdleSequence2(); + gnap.updateIdleSequence2(); + if (!_vm->_timers[5]) { + _vm->_timers[5] = _vm->getRandom(100) + 75; + if (gnap._actionStatus < 0 && plat._actionStatus < 0 && _nextGoggleGuySequenceId == -1) { + if (_vm->getRandom(2)) + _nextGoggleGuySequenceId = 0x1F6; + else + _nextGoggleGuySequenceId = 0x1F9; + } + } + if (!_vm->_timers[4]) { + _vm->_timers[4] = _vm->getRandom(40) + 20; + if (gnap._actionStatus < 0 && plat._actionStatus < 0 && _nextHookGuySequenceId == -1) { + if (_currHookGuySequenceId == 0x201) { + switch (_vm->getRandom(7)) { + case 0: + _nextHookGuySequenceId = 0x200; + break; + case 1: + _nextHookGuySequenceId = 0x205; + break; + case 2: + _nextHookGuySequenceId = 0x202; + break; + default: + _nextHookGuySequenceId = 0x201; + break; + } + } else { + _nextHookGuySequenceId = 0x201; + } + } + } + if (!_vm->_timers[6]) { + _vm->_timers[6] = _vm->getRandom(100) + 100; + int _gnapRandomValue = _vm->getRandom(3); + switch (_gnapRandomValue) { + case 0: + gameSys.insertSequence(0x8A5 | 0x10000, 179, 0, 0, kSeqNone, 0, 0, 0); + break; + case 1: + gameSys.insertSequence(0x8A7 | 0x10000, 179, 0, 0, kSeqNone, 0, 0, 0); + break; + case 2: + gameSys.insertSequence(0x8A6 | 0x10000, 179, 0, 0, kSeqNone, 0, 0, 0); + break; + } + } + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + _vm->_timers[5] = _vm->getRandom(50) + 75; + _vm->_timers[4] = _vm->getRandom(40) + 20; + } + + _vm->gameUpdateTick(); + } +} + +void Scene11::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + if (gnap._actionStatus != kAS11GrabBillardBall) + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS11LeaveScene: + _vm->_sceneDone = true; + break; + case kAS11ShowMagazineToGoggleGuy: + _nextGoggleGuySequenceId = 0x1F7; + break; + case kAS11TalkGoggleGuy: + _nextGoggleGuySequenceId = 0x1FB; + break; + case kAS11GrabHookGuy: + _nextHookGuySequenceId = 0x204; + break; + case kAS11ShowItemToHookGuy: + _nextHookGuySequenceId = 0x203; + break; + case kAS11TalkHookGuy: + _nextHookGuySequenceId = 0x206; + break; + case kAS11GrabBillardBall: + if (gameSys.getAnimationStatus(2) == 2 && gameSys.getAnimationStatus(3) == 2) { + gameSys.setAnimation(0, 0, 0); + _vm->_timers[2] = _vm->getRandom(30) + 20; + _vm->_timers[3] = _vm->getRandom(50) + 200; + gameSys.insertSequence(0x1F4, 255, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x1F4; + gnap._id = 255; + gnap._sequenceDatNum = 0; + gameSys.removeSequence(0x207, 257, true); + gameSys.removeSequence(0x208, 256, true); + _nextGoggleGuySequenceId = 0x1F8; + _vm->_timers[5] = _vm->getRandom(100) + 75; + gameSys.insertSequence(_nextGoggleGuySequenceId, 121, _currGoggleGuySequenceId, 121, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextGoggleGuySequenceId, 121, 2); + _currGoggleGuySequenceId = _nextGoggleGuySequenceId; + _nextGoggleGuySequenceId = -1; + switch (_billardBallCtr) { + case 0: + _nextHookGuySequenceId = 0x1FC; + break; + case 1: + _nextHookGuySequenceId = 0x1FD; + break; + default: + _nextHookGuySequenceId = 0x1FE; + break; + } + ++_billardBallCtr; + gameSys.insertSequence(_nextHookGuySequenceId, 120, _currHookGuySequenceId, 120, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextHookGuySequenceId, 120, 3); + _currHookGuySequenceId = _nextHookGuySequenceId; + _nextHookGuySequenceId = -1; + _vm->_timers[4] = _vm->getRandom(40) + 20; + gameSys.insertSequence(0x208, 256, 0, 0, kSeqNone, _vm->getSequenceTotalDuration(0x1F4) - 5, 0, 0); + _vm->_hotspots[kHS11Billard]._flags |= SF_DISABLED; + gameSys.setAnimation(0x207, 257, 4); + gameSys.insertSequence(0x207, 257, 0, 0, kSeqNone, _vm->getSequenceTotalDuration(0x1FE), 0, 0); + gnap._actionStatus = -1; + } + break; + } + } + + if (gameSys.getAnimationStatus(2) == 2 && _nextGoggleGuySequenceId != -1) { + _vm->_timers[5] = _vm->getRandom(100) + 75; + gameSys.insertSequence(_nextGoggleGuySequenceId, 121, _currGoggleGuySequenceId, 121, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextGoggleGuySequenceId, 121, 2); + _currGoggleGuySequenceId = _nextGoggleGuySequenceId; + _nextGoggleGuySequenceId = -1; + if (gnap._actionStatus >= 1 && gnap._actionStatus <= 4) + gnap._actionStatus = -1; + } + + if (gameSys.getAnimationStatus(3) == 2) { + if (_nextHookGuySequenceId == 0x204) { + gameSys.setAnimation(_nextHookGuySequenceId, 120, 3); + gameSys.insertSequence(0x204, 120, _currHookGuySequenceId, 120, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x1F5, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + _currHookGuySequenceId = 0x204; + _nextHookGuySequenceId = -1; + gnap._sequenceId = 0x1F5; + gnap._sequenceDatNum = 0; + _vm->_timers[4] = _vm->getRandom(40) + 20; + _vm->_timers[2] = _vm->getRandom(20) + 70; + _vm->_timers[3] = _vm->getRandom(50) + 200; + if (gnap._actionStatus == kAS11GrabHookGuy) + gnap._actionStatus = -1; + } else if (_nextHookGuySequenceId != -1) { + gameSys.insertSequence(_nextHookGuySequenceId, 120, _currHookGuySequenceId, 120, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextHookGuySequenceId, 120, 3); + _currHookGuySequenceId = _nextHookGuySequenceId; + _nextHookGuySequenceId = -1; + _vm->_timers[4] = _vm->getRandom(40) + 20; + if (gnap._actionStatus >= 6 && gnap._actionStatus <= 9) + gnap._actionStatus = -1; + } + } + + if (gameSys.getAnimationStatus(4) == 2) { + gameSys.setAnimation(0, 0, 4); + _vm->_hotspots[kHS11Billard]._flags &= ~SF_DISABLED; + } +} + +/*****************************************************************************/ + +Scene12::Scene12(GnapEngine *vm) : Scene(vm) { + _nextBeardGuySequenceId = -1; + _currBeardGuySequenceId = -1; + _nextToothGuySequenceId = -1; + _currToothGuySequenceId = -1; + _nextBarkeeperSequenceId = -1; + _currBarkeeperSequenceId = -1; +} + +int Scene12::init() { + return 0x209; +} + +void Scene12::updateHotspots() { + _vm->setHotspot(kHS12Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS12ExitRight, 790, 360, 799, 599, SF_EXIT_R_CURSOR); + _vm->setHotspot(kHS12ToothGuy, 80, 180, 160, 380, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS12Barkeeper, 490, 175, 580, 238, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS12BeardGuy, 620, 215, 720, 350, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS12Jukebox, 300, 170, 410, 355, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS12WalkArea1, 0, 0, 260, 460); + _vm->setHotspot(kHS12WalkArea2, 0, 0, 380, 410); + _vm->setHotspot(kHS12WalkArea3, 0, 0, 799, 395); + _vm->setHotspot(kHS12WalkArea4, 585, 0, 799, 455); + _vm->setDeviceHotspot(kHS12Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 11; +} + +void Scene12::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + int v18 = 1; + + _vm->queueInsertDeviceIcon(); + + gameSys.insertSequence(0x207, 256, 0, 0, kSeqNone, 0, 0, 0); + gameSys.insertSequence(0x200, 50, 0, 0, kSeqNone, 0, 0, 0); + + _currToothGuySequenceId = 0x200; + _nextToothGuySequenceId = -1; + + gameSys.setAnimation(0x200, 50, 2); + gameSys.insertSequence(0x202, 50, 0, 0, kSeqNone, 0, 0, 0); + + _currBeardGuySequenceId = 0x202; + _nextBeardGuySequenceId = -1; + + gameSys.setAnimation(0x202, 50, 4); + gameSys.insertSequence(0x203, 50, 0, 0, kSeqNone, 0, 0, 0); + + _currBarkeeperSequenceId = 0x203; + _nextBarkeeperSequenceId = -1; + + gameSys.setAnimation(0x203, 50, 3); + + _vm->_timers[4] = 30; + _vm->_timers[6] = _vm->getRandom(30) + 20; + _vm->_timers[5] = _vm->getRandom(30) + 20; + _vm->_timers[7] = _vm->getRandom(100) + 100; + + if (_vm->_prevSceneNum == 15) { + gnap.initPos(5, 6, kDirBottomRight); + plat.initPos(3, 7, kDirIdleLeft); + _vm->endSceneInit(); + } else { + gnap.initPos(11, 8, kDirBottomLeft); + plat.initPos(12, 8, kDirIdleRight); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(8, 8), -1, 0x107BA, 1); + plat.walkTo(Common::Point(9, 8), -1, 0x107D2, 1); + } + + while (!_vm->_sceneDone) { + _vm->testWalk(0, 0, -1, -1, -1, -1); + + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + _vm->updateGrabCursorSprite(0, 0); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS12Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS12Platypus: + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + break; + } + break; + + case kHS12ExitRight: + _vm->_isLeavingScene = true; + gnap.walkTo(Common::Point(10, -1), 0, 0x107AB, 1); + gnap._actionStatus = kAS12LeaveScene; + plat.walkTo(Common::Point(10, -1), -1, -1, 1); + _vm->_newSceneNum = 11; + break; + + case kHS12ToothGuy: + if (_vm->_grabCursorSpriteIndex == kItemQuarter) { + _vm->_largeSprite = gameSys.createSurface(0x141); + gnap.walkTo(Common::Point(3, 7), 0, 0x107BC, 9); + gnap._idleFacing = kDirUpLeft; + gnap._actionStatus = kAS12QuarterToToothGuy; + gnap.playShowItem(_vm->_grabCursorSpriteIndex, 2, 0); + _vm->setGrabCursorSprite(-1); + } else if (_vm->_grabCursorSpriteIndex == kItemQuarterWithHole) { + gnap.walkTo(Common::Point(3, 7), 0, 0x107BC, 9); + gnap._idleFacing = kDirUpLeft; + gnap._actionStatus = kAS12QuarterWithHoleToToothGuy; + gnap.playShowItem(_vm->_grabCursorSpriteIndex, 2, 0); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.walkTo(Common::Point(3, 7), 0, 0x107BC, 9); + gnap._idleFacing = kDirUpLeft; + gnap._actionStatus = kAS12ShowItemToToothGuy; + gnap.playShowItem(_vm->_grabCursorSpriteIndex, 2, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(1, 2)); + break; + case GRAB_CURSOR: + gnap.walkTo(Common::Point(3, 7), 0, 0x107BC, 1); + gnap._actionStatus = kAS12GrabToothGuy; + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpLeft; + gnap.walkTo(Common::Point(3, 7), 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS12TalkToothGuy; + break; + case PLAT_CURSOR: + gnap.useDeviceOnPlatypus(); + plat.walkTo(Common::Point(3, 7), 1, 0x107D2, 1); + plat._actionStatus = kAS12PlatWithToothGuy; + plat._idleFacing = kDirIdleRight; + gnap.playIdle(Common::Point(2, 7)); + break; + } + } + break; + + case kHS12Barkeeper: + if (_vm->_grabCursorSpriteIndex == kItemQuarter || _vm->_grabCursorSpriteIndex == kItemQuarterWithHole) { + gnap.walkTo(Common::Point(6, 6), 0, 0x107BB, 9); + gnap._idleFacing = kDirUpRight; + gnap._actionStatus = kAS12QuarterWithBarkeeper; + gnap.playShowItem(_vm->_grabCursorSpriteIndex, 7, 0); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.walkTo(Common::Point(6, 6), 0, 0x107BB, 9); + gnap._idleFacing = kDirUpRight; + gnap._actionStatus = kAS12ShowItemToBarkeeper; + gnap.playShowItem(_vm->_grabCursorSpriteIndex, 7, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.walkTo(Common::Point(6, 6), 0, 0x107BB, 1); + gnap._idleFacing = kDirUpRight; + gnap._actionStatus = kAS12LookBarkeeper; + break; + case GRAB_CURSOR: + gnap.playImpossible(); + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.walkTo(Common::Point(6, 6), 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS12TalkBarkeeper; + break; + case PLAT_CURSOR: + gnap.playPullOutDevice(plat._pos); + gameSys.setAnimation(makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, 0); + gnap._actionStatus = kAS12PlatWithBarkeeper; + break; + } + } + break; + + case kHS12BeardGuy: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.walkTo(Common::Point(7, 6), 0, 0x107BB, 9); + gnap._idleFacing = kDirUpRight; + gnap._actionStatus = kAS12ShowItemToBeardGuy; + gnap.playShowItem(_vm->_grabCursorSpriteIndex, 8, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.walkTo(Common::Point(7, 6), 0, 0x107BB, 1); + gnap._idleFacing = kDirUpRight; + gnap._actionStatus = kAS12LookBeardGuy; + break; + case GRAB_CURSOR: + // NOTE Bug in the original. It has 9 as flags which seems wrong here. + gnap.walkTo(Common::Point(7, 6), 0, 0x107BB, 1); + gnap._idleFacing = kDirUpRight; + gnap._actionStatus = kAS12GrabBeardGuy; + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.walkTo(Common::Point(7, 6), 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS12TalkBeardGuy; + break; + case PLAT_CURSOR: + gnap.useDeviceOnPlatypus(); + plat.walkTo(Common::Point(7, 6), 1, 0x107C2, 1); + plat._actionStatus = kAS12PlatWithBeardGuy; + plat._idleFacing = kDirIdleLeft; + gnap.playIdle(Common::Point(7, 6)); + break; + } + } + break; + + case kHS12Jukebox: + _vm->_newSceneNum = 15; + _vm->_isLeavingScene = true; + gnap.walkTo(Common::Point(5, 6), 0, 0x107BC, 1); + gnap._actionStatus = kAS12LeaveScene; + break; + + case kHS12WalkArea1: + case kHS12WalkArea2: + case kHS12WalkArea3: + case kHS12WalkArea4: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left && gnap._actionStatus < 0) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + + } + + updateAnimations(); + + if (!_vm->_isLeavingScene) { + plat.updateIdleSequence(); + gnap.updateIdleSequence(); + if (!_vm->_timers[4]) { + _vm->_timers[4] = 15; + if (_nextToothGuySequenceId == -1) { + if (v18 == 0 && _currBeardGuySequenceId == 0x202 && _currBarkeeperSequenceId == 0x203 && gnap._actionStatus < 0 && plat._actionStatus < 0) { + if (_vm->getRandom(2) != 0) + _nextToothGuySequenceId = 0x1EC; + else + _nextToothGuySequenceId = 0x204; + } else if (_currToothGuySequenceId != 0x200) + _nextToothGuySequenceId = 0x200; + v18 = (v18 + 1) % 15; + } + } + if (!_vm->_timers[5]) { + _vm->_timers[5] = _vm->getRandom(30) + 20; + if (_nextBarkeeperSequenceId == -1 && gnap._actionStatus < 0 && plat._actionStatus < 0) { + if (v18 == 0 && _currToothGuySequenceId == 0x200 && _currBeardGuySequenceId == 0x202 && gnap._actionStatus < 0 && plat._actionStatus < 0) { + if (_vm->getRandom(2) != 0) + _nextBarkeeperSequenceId = 0x208; + else + _nextBarkeeperSequenceId = 0x1FB; + } else + _nextBarkeeperSequenceId = 0x203; + v18 = (v18 + 1) % 15; + } + } + if (!_vm->_timers[6]) { + _vm->_timers[6] = _vm->getRandom(30) + 15; + if (_nextBeardGuySequenceId == -1 && gnap._actionStatus < 0 && plat._actionStatus < 0) { + if (v18 == 0 && _currToothGuySequenceId == 0x200 && _currBarkeeperSequenceId == 0x203 && gnap._actionStatus < 0 && plat._actionStatus < 0) + _nextBeardGuySequenceId = 0x1F2; + else + _nextBeardGuySequenceId = 0x202; + v18 = (v18 + 1) % 15; + } + } + if (!_vm->_timers[7]) { + _vm->_timers[7] = _vm->getRandom(100) + 100; + int _gnapRandomValue = _vm->getRandom(3); + switch (_gnapRandomValue) { + case 0: + gameSys.insertSequence(0x8A5 | 0x10000, 179, 0, 0, kSeqNone, 0, 0, 0); + break; + case 1: + gameSys.insertSequence(0x8A7 | 0x10000, 179, 0, 0, kSeqNone, 0, 0, 0); + break; + case 2: + gameSys.insertSequence(0x8A6 | 0x10000, 179, 0, 0, kSeqNone, 0, 0, 0); + break; + } + } + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + _vm->_timers[4] = 30; + _vm->_timers[5] = _vm->getRandom(30) + 20; + _vm->_timers[6] = _vm->getRandom(30) + 20; + } + + _vm->gameUpdateTick(); + } +} + +void Scene12::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS12LeaveScene: + _vm->_sceneDone = true; + break; + case kAS12TalkToothGuy: + if (_vm->isKeyStatus1(Common::KEYCODE_j)) { + // Easter egg + _vm->clearKeyStatus1(Common::KEYCODE_j); + _nextToothGuySequenceId = 0x206; + } else { + _nextToothGuySequenceId = 0x1EE; + } + break; + case 3: + break; + case kAS12GrabToothGuy: + if (_vm->isKeyStatus1(Common::KEYCODE_j)) { + _vm->clearKeyStatus1(Common::KEYCODE_j); + _nextToothGuySequenceId = 0x206; + } else { + _nextToothGuySequenceId = 0x1EF; + } + break; + case kAS12ShowItemToToothGuy: + if (_vm->isKeyStatus1(Common::KEYCODE_j)) { + _vm->clearKeyStatus1(Common::KEYCODE_j); + _nextToothGuySequenceId = 0x206; + } else { + _nextToothGuySequenceId = 0x1ED; + } + break; + case kAS12QuarterWithHoleToToothGuy: + if (_vm->isKeyStatus1(Common::KEYCODE_j)) { + _vm->clearKeyStatus1(Common::KEYCODE_j); + _nextToothGuySequenceId = 0x206; + } else { + _nextToothGuySequenceId = 0x1EA; + } + break; + case kAS12QuarterToToothGuy: + if (_vm->isKeyStatus1(Common::KEYCODE_j)) { + _vm->clearKeyStatus1(Common::KEYCODE_j); + _nextToothGuySequenceId = 0x206; + } else { + _nextToothGuySequenceId = 0x1E9; + } + break; + case kAS12QuarterToToothGuyDone: + gnap._actionStatus = -1; + _vm->showCursor(); + gameSys.removeSpriteDrawItem(_vm->_largeSprite, 300); + _vm->deleteSurface(&_vm->_largeSprite); + _vm->setGrabCursorSprite(kItemQuarterWithHole); + break; + case kAS12TalkBeardGuy: + _nextBeardGuySequenceId = 0x1F4; + break; + case kAS12LookBeardGuy: + _nextBeardGuySequenceId = 0x1F3; + break; + case kAS12GrabBeardGuy: + _nextBeardGuySequenceId = 0x1F1; + break; + case kAS12ShowItemToBeardGuy: + _nextBeardGuySequenceId = 0x1F0; + break; + case kAS12TalkBarkeeper: + if (_vm->getRandom(2) != 0) + _nextBarkeeperSequenceId = 0x1FD; + else + _nextBarkeeperSequenceId = 0x1FF; + break; + case kAS12LookBarkeeper: + _nextBarkeeperSequenceId = 0x1F8; + break; + case 14: + _nextBarkeeperSequenceId = 0x1F6; + break; + case kAS12ShowItemToBarkeeper: + _nextBarkeeperSequenceId = 0x1F5; + break; + case kAS12QuarterWithBarkeeper: + _nextBarkeeperSequenceId = 0x1FA; + break; + case kAS12PlatWithBarkeeper: + _nextBarkeeperSequenceId = 0x1F9; + break; + } + } + + if (gameSys.getAnimationStatus(1) == 2) { + gameSys.setAnimation(0, 0, 1); + switch (plat._actionStatus) { + case kAS12PlatWithToothGuy: + _nextToothGuySequenceId = 0x1EB; + break; + case kAS12PlatWithBeardGuy: + _nextBeardGuySequenceId = 0x1F3; + break; + } + } + + if (gameSys.getAnimationStatus(2) == 2) { + if (_currToothGuySequenceId == 0x1E9) { + gameSys.setAnimation(0, 0, 2); + _vm->hideCursor(); + gameSys.setAnimation(0x10843, 301, 0); + gnap._actionStatus = kAS12QuarterToToothGuyDone; + gameSys.insertSpriteDrawItem(_vm->_largeSprite, 0, 0, 300); + gameSys.insertSequence(0x10843, 301, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x107B7, gnap._id, 0x10843, 301, + kSeqSyncWait, 0, 75 * gnap._pos.x - gnap._gridX, 48 * gnap._pos.y - gnap._gridY); + gnap._sequenceId = 0x7B7; + gnap._sequenceDatNum = 1; + _vm->setFlag(kGFTwigTaken); + _vm->invAdd(kItemQuarterWithHole); + _vm->invRemove(kItemQuarter); + } + if (_nextToothGuySequenceId == 0x1EF) { + gameSys.setAnimation(_nextToothGuySequenceId, 50, 2); + gameSys.insertSequence(_nextToothGuySequenceId, 50, _currToothGuySequenceId, 50, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x205, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + _currToothGuySequenceId = _nextToothGuySequenceId; + _nextToothGuySequenceId = -1; + gnap._sequenceId = 0x205; + gnap._sequenceDatNum = 0; + _vm->_timers[4] = 40; + _vm->_timers[2] = _vm->getRandom(20) + 70; + _vm->_timers[3] = _vm->getRandom(50) + 200; + if (gnap._actionStatus == kAS12GrabToothGuy) + gnap._actionStatus = -1; + } else if (_nextToothGuySequenceId != -1) { + gameSys.insertSequence(_nextToothGuySequenceId, 50, _currToothGuySequenceId, 50, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextToothGuySequenceId, 50, 2); + _currToothGuySequenceId = _nextToothGuySequenceId; + _nextToothGuySequenceId = -1; + _vm->_timers[4] = 50; + if (gnap._actionStatus >= kAS12TalkToothGuy && gnap._actionStatus <= kAS12QuarterToToothGuy && _currToothGuySequenceId != 0x1E9 && + _currToothGuySequenceId != 0x1EC && _currToothGuySequenceId != 0x200) + gnap._actionStatus = -1; + if (plat._actionStatus == kAS12PlatWithToothGuy) + plat._actionStatus = -1; + } + } + + if (gameSys.getAnimationStatus(3) == 2) { + if (gnap._actionStatus == kAS12PlatWithBarkeeper && _currBarkeeperSequenceId == 0x1F9) { + gnap._actionStatus = -1; + gnap.playIdle(Common::Point(7, 6)); + _vm->_timers[5] = 0; + } + if (_nextBarkeeperSequenceId != -1) { + gameSys.insertSequence(_nextBarkeeperSequenceId, 50, _currBarkeeperSequenceId, 50, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextBarkeeperSequenceId, 50, 3); + _currBarkeeperSequenceId = _nextBarkeeperSequenceId; + _nextBarkeeperSequenceId = -1; + _vm->_timers[5] = _vm->getRandom(30) + 20; + if (gnap._actionStatus >= kAS12TalkBarkeeper && gnap._actionStatus <= kAS12QuarterWithBarkeeper && _currBarkeeperSequenceId != 0x203 && + _currBarkeeperSequenceId != 0x1FB && _currBarkeeperSequenceId != 0x208) + gnap._actionStatus = -1; + } + } + + if (gameSys.getAnimationStatus(4) == 2 && _nextBeardGuySequenceId != -1) { + gameSys.insertSequence(_nextBeardGuySequenceId, 50, _currBeardGuySequenceId, 50, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextBeardGuySequenceId, 50, 4); + _currBeardGuySequenceId = _nextBeardGuySequenceId; + _nextBeardGuySequenceId = -1; + _vm->_timers[6] = _vm->getRandom(30) + 20; + if (gnap._actionStatus >= kAS12TalkBeardGuy && gnap._actionStatus <= kAS12ShowItemToBeardGuy && _currBeardGuySequenceId != 0x202 && _currBeardGuySequenceId != 0x1F2) + gnap._actionStatus = -1; + if (plat._actionStatus == kAS12PlatWithBeardGuy) + plat._actionStatus = -1; + } +} + +/*****************************************************************************/ + +Scene13::Scene13(GnapEngine *vm) : Scene(vm) { + _backToiletCtr = -1; +} + +int Scene13::init() { + _vm->playSound(0x108EC, false); + return 0xAC; +} + +void Scene13::updateHotspots() { + _vm->setHotspot(kHS13Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS13ExitBar, 113, 160, 170, 455, SF_EXIT_L_CURSOR); + _vm->setHotspot(kHS13BackToilet, 385, 195, 478, 367, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS13FrontToilet, 497, 182, 545, 432, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS13Urinal, 680, 265, 760, 445, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS13Scribble, 560, 270, 660, 370, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS13Sink, 310, 520, 560, 599, SF_WALKABLE | SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS13WalkArea1, 268, 270, 325, 385); + _vm->setHotspot(kHS13WalkArea2, 0, 0, 52, 599); + _vm->setHotspot(kHS13WalkArea3, 0, 0, 113, 550); + _vm->setHotspot(kHS13WalkArea4, 0, 0, 226, 438); + _vm->setHotspot(kHS13WalkArea5, 0, 0, 268, 400); + _vm->setHotspot(kHS13WalkArea6, 0, 0, 799, 367); + _vm->setHotspot(kHS13WalkArea7, 478, 0, 799, 401); + _vm->setHotspot(kHS13WalkArea8, 545, 0, 799, 473); + _vm->setHotspot(kHS13WalkArea9, 0, 549, 799, 599); + _vm->setDeviceHotspot(kHS13Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 17; +} + +void Scene13::showScribble() { + GameSys& gameSys = *_vm->_gameSys; + + _vm->hideCursor(); + _vm->_largeSprite = gameSys.createSurface(0x6F); + gameSys.insertSpriteDrawItem(_vm->_largeSprite, 0, 0, 300); + while (!_vm->_mouseClickState._left && !_vm->isKeyStatus1(Common::KEYCODE_ESCAPE) && + !_vm->isKeyStatus1(Common::KEYCODE_SPACE) && !_vm->isKeyStatus1(Common::KEYCODE_RETURN) && !_vm->_gameDone) + _vm->gameUpdateTick(); + _vm->_mouseClickState._left = false; + _vm->clearKeyStatus1(Common::KEYCODE_ESCAPE); + _vm->clearKeyStatus1(Common::KEYCODE_RETURN); + _vm->clearKeyStatus1(Common::KEYCODE_SPACE); + gameSys.removeSpriteDrawItem(_vm->_largeSprite, 300); + _vm->deleteSurface(&_vm->_largeSprite); + _vm->showCursor(); +} + +void Scene13::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + int currSoundId = 0; + + _vm->queueInsertDeviceIcon(); + gameSys.insertSequence(0xAA, 256, 0, 0, kSeqNone, 0, 0, 0); + + if (_vm->_prevSceneNum == 14) { + gnap.initPos(6, 6, kDirBottomLeft); + plat.initPos(9, 8, kDirIdleLeft); + } else { + gnap.initPos(3, 7, kDirBottomRight); + plat.initPos(2, 7, kDirIdleLeft); + } + + _vm->endSceneInit(); + + _vm->_timers[4] = _vm->getRandom(20) + 20; + _vm->_timers[5] = _vm->getRandom(50) + 50; + + while (!_vm->_sceneDone) { + if (!_vm->isSoundPlaying(0x1091A)) + _vm->playSound(0x1091A, true); + + _vm->testWalk(0, 0, -1, -1, -1, -1); + + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS13Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + _vm->_timers[4] = _vm->getRandom(20) + 20; + _vm->_timers[5] = _vm->getRandom(50) + 50; + } + break; + + case kHS13Platypus: + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + break; + } + break; + + case kHS13ExitBar: + _vm->_isLeavingScene = true; + gnap.walkTo(Common::Point(2, 7), 0, 0x107C0, 1); + gnap._actionStatus = kAS13LeaveScene; + plat.walkTo(Common::Point(2, 8), -1, -1, 1); + if (_vm->isFlag(kGFUnk14) || _vm->isFlag(kGFSpringTaken)) { + _vm->_newSceneNum = 11; + } else { + _vm->setFlag(kGFSpringTaken); + _vm->_newSceneNum = 47; + } + break; + + case kHS13BackToilet: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(5, 5), 6, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + case GRAB_CURSOR: + case TALK_CURSOR: + if (gnap._pos == Common::Point(5, 5)) { + _backToiletCtr = MIN(5, _backToiletCtr + 1); + gameSys.setAnimation(_backToiletCtr + 0xA3, gnap._id, 0); + gameSys.insertSequence(_backToiletCtr + 0xA3, gnap._id, + makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, + kSeqScale | kSeqSyncWait, 0, 0, 0); + gnap._actionStatus = kAS13Wait; + gnap._sequenceId = _backToiletCtr + 0xA3; + gnap._idleFacing = kDirUpRight; + gnap._sequenceDatNum = 0; + } else { + gnap.walkTo(Common::Point(5, 5), 0, 0x107BB, 1); + gnap._actionStatus = kAS13BackToilet; + gnap._idleFacing = kDirUpRight; + } + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + break; + + case kHS13FrontToilet: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(6, 7), 7, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + case GRAB_CURSOR: + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.walkTo(Common::Point(6, 7), 0, 0xA9, 5); + gnap._actionStatus = kAS13FrontToilet; + gnap._idleFacing = kDirBottomRight; + break; + } + } + break; + + case kHS13Scribble: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(7, 7), 8, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.walkTo(Common::Point(7, 7), 0, 0x107BB, 1); + gnap._actionStatus = kAS13LookScribble; + gnap._idleFacing = kDirUpRight; + break; + case GRAB_CURSOR: + gnap.playScratchingHead(); + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.walkTo(Common::Point(7, 7), -1, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)), 1); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + break; + + case kHS13Urinal: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(8, 7), 9, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playSequence(gnap.getSequenceId(kGSDeflect, Common::Point(9, 6))); + gnap.walkTo(gnap._pos, 0, -1, 1); + gnap._actionStatus = kAS13Wait; + break; + case GRAB_CURSOR: + gnap.walkTo(Common::Point(8, 7), 0, -1, 1); + gnap._actionStatus = kAS13GrabUrinal; + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + break; + + case kHS13Sink: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playSequence(gnap.getSequenceId(kGSDeflect, Common::Point(5, 9))); + gnap.walkTo(gnap._pos, 0, -1, 1); + gnap._actionStatus = kAS13Wait; + break; + case GRAB_CURSOR: + gnap.walkTo(Common::Point(4, 8), 0, 0x107B9, 1); + gnap._actionStatus = kAS13GrabSink; + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + break; + + case kHS13WalkArea2: + case kHS13WalkArea3: + case kHS13WalkArea4: + case kHS13WalkArea5: + case kHS13WalkArea6: + case kHS13WalkArea7: + case kHS13WalkArea8: + case kHS13WalkArea9: + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + case kHS13WalkArea1: + // Nothing + break; + + default: + if (_vm->_mouseClickState._left) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + } + + updateAnimations(); + + if (!_vm->_isLeavingScene) { + plat.updateIdleSequence(); + if (plat._pos.y == 5 || plat._pos.y == 6) + plat.walkTo(Common::Point(-1, 7), -1, -1, 1); + if (gnap._actionStatus < 0) + gnap.updateIdleSequence(); + if (!_vm->_timers[4]) { + _vm->_timers[4] = _vm->getRandom(20) + 20; + switch (_vm->getRandom(5)) { + case 0: + _vm->playSound(0xD2, false); + break; + case 1: + _vm->playSound(0xD3, false); + break; + case 2: + _vm->playSound(0xD4, false); + break; + case 3: + _vm->playSound(0xD5, false); + break; + case 4: + _vm->playSound(0xD6, false); + break; + } + } + if (!_vm->_timers[5]) { + int newSoundId; + _vm->_timers[5] = _vm->getRandom(50) + 50; + switch (_vm->getRandom(7)) { + case 0: + newSoundId = 0xD7; + _vm->_timers[5] = 2 * _vm->getRandom(50) + 100; + break; + case 1: + case 2: + newSoundId = 0xCF; + break; + case 3: + case 4: + newSoundId = 0xD0; + break; + default: + newSoundId = 0xD1; + break; + } + if (newSoundId != currSoundId) { + _vm->playSound(newSoundId, false); + currSoundId = newSoundId; + } + } + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + _vm->_timers[4] = _vm->getRandom(20) + 20; + _vm->_timers[5] = _vm->getRandom(50) + 50; + } + + _vm->gameUpdateTick(); + } +} + +void Scene13::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS13LeaveScene: + _vm->_sceneDone = true; + gnap._actionStatus = -1; + break; + case kAS13BackToilet: + _backToiletCtr = MIN(5, _backToiletCtr + 1); + gameSys.insertSequence(_backToiletCtr + 0xA3, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, 9, 0, 0, 0); + gnap._sequenceId = _backToiletCtr + 0xA3; + gnap._sequenceDatNum = 0; + gnap._actionStatus = -1; + break; + case kAS13FrontToilet: + _vm->_sceneDone = true; + _vm->_newSceneNum = 14; + break; + case kAS13LookScribble: + gnap._actionStatus = -1; + showScribble(); + break; + case kAS13GrabSink: + gameSys.setAnimation(0xAB, 160, 0); + gameSys.insertSequence(0xAB, 160, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.removeSequence(0xAA, 256, true); + gnap._sequenceId = 0xAB; + gnap._id = 160; + gnap._idleFacing = kDirBottomRight; + gnap._sequenceDatNum = 0; + gnap._pos = Common::Point(4, 8); + _vm->_timers[2] = 360; + gnap._actionStatus = kAS13GrabSinkDone; + break; + case kAS13GrabSinkDone: + gameSys.insertSequence(0xAA, 256, 0, 0, kSeqNone, 0, 0, 0); + gnap._actionStatus = -1; + break; + case kAS13Wait: + gnap._actionStatus = -1; + break; + case kAS13GrabUrinal: + gameSys.setAnimation(0xA2, 120, 0); + gameSys.insertSequence(0xA2, 120, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0xA2; + gnap._id = 120; + gnap._idleFacing = kDirBottomLeft; + gnap._sequenceDatNum = 0; + gnap._pos = Common::Point(4, 6); + _vm->_timers[2] = 360; + gnap._actionStatus = kAS13Wait; + break; + } + } + + if (gameSys.getAnimationStatus(1) == 2) { + gameSys.setAnimation(0, 0, 1); + _vm->_plat->_actionStatus = -1; + } +} + +/*****************************************************************************/ + +Scene14::Scene14(GnapEngine *vm) : Scene(vm) { +} + +int Scene14::init() { + GameSys& gameSys = *_vm->_gameSys; + + gameSys.setAnimation(0, 0, 0); + gameSys.setAnimation(0, 0, 1); + return 0x27; +} + +void Scene14::updateHotspots() { + _vm->setHotspot(kHS14Platypus, 0, 0, 0, 0); + _vm->setHotspot(kHS14Exit, 0, 590, 799, 599, SF_EXIT_D_CURSOR); + _vm->setHotspot(kHS14Coin, 330, 390, 375, 440, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS14Toilet, 225, 250, 510, 500, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setDeviceHotspot(kHS14Device, -1, -1, -1, -1); + if (_vm->isFlag(kGFNeedleTaken)) + _vm->_hotspots[kHS14Coin]._flags = SF_DISABLED; + _vm->_hotspotsCount = 5; +} + +void Scene14::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + _vm->_largeSprite = nullptr; + _vm->queueInsertDeviceIcon(); + + if (!_vm->isFlag(kGFNeedleTaken)) + gameSys.insertSequence(0x23, 10, 0, 0, kSeqNone, 0, 0, 0); + + _vm->endSceneInit(); + + if (!_vm->isFlag(kGFNeedleTaken) && _vm->invHas(kItemTongs)) + _vm->_largeSprite = gameSys.createSurface(1); + + if (!_vm->isFlag(kGFNeedleTaken)) { + gameSys.insertSequence(0x24, 10, 0x23, 10, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x24; + _vm->_timers[2] = _vm->getRandom(40) + 50; + } + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS14Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS14Exit: + _vm->_sceneDone = true; + _vm->_newSceneNum = 13; + break; + + case kHS14Coin: + if (_vm->_grabCursorSpriteIndex == kItemTongs) { + _vm->invAdd(kItemQuarter); + _vm->setFlag(kGFNeedleTaken); + _vm->setGrabCursorSprite(-1); + _vm->hideCursor(); + gameSys.setAnimation(0x26, 10, 0); + gameSys.insertSequence(0x26, 10, gnap._sequenceId, 10, kSeqSyncWait, 0, 0, 0); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + _vm->playSound(0x108E9, false); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + _vm->playSound(0x108E9, false); + break; + case GRAB_CURSOR: + gameSys.insertSequence(0x25, 10, gnap._sequenceId, 10, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x23, 10, 0x25, 10, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x23; + break; + case TALK_CURSOR: + _vm->playSound((_vm->getRandom(5) + 0x8D5) | 0x10000, false); + break; + case PLAT_CURSOR: + gameSys.insertSequence(0x107A8, 1, 0, 0, kSeqNone, 0, 900 - gnap._gridX, 576 - gnap._gridY); + break; + } + } + break; + + case kHS14Toilet: + if (_vm->_grabCursorSpriteIndex >= 0) { + gameSys.insertSequence(0x107A8, 1, 0, 0, kSeqNone, 0, 900 - gnap._gridX, 576 - gnap._gridY); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + case GRAB_CURSOR: + _vm->playSound(0x108B1, false); + break; + case TALK_CURSOR: + _vm->playSound((_vm->getRandom(5) + 0x8D5) | 0x10000, false); + break; + case PLAT_CURSOR: + gameSys.insertSequence(0x107A8, 1, 0, 0, kSeqNone, 0, 900 - gnap._gridX, 576 - gnap._gridY); + break; + } + } + break; + + default: + _vm->_mouseClickState._left = false; + break; + } + + updateAnimations(); + _vm->checkGameKeys(); + + if (!_vm->isFlag(kGFNeedleTaken) && !_vm->_timers[2]) { + gameSys.insertSequence(0x24, 10, gnap._sequenceId, 10, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x24; + _vm->_timers[2] = _vm->getRandom(40) + 50; + } + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } + + if (_vm->_largeSprite) + _vm->deleteSurface(&_vm->_largeSprite); +} + +void Scene14::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + gameSys.insertSpriteDrawItem(_vm->_largeSprite, 0, 0, 300); + gameSys.setAnimation(0x10843, 301, 1); + gameSys.insertSequence(0x10843, 301, 0x26, 10, kSeqSyncWait, 0, 0, 0); + } + + if (gameSys.getAnimationStatus(1) == 2) { + gameSys.setAnimation(0, 0, 1); + _vm->_sceneDone = true; + _vm->_newSceneNum = 13; + _vm->_grabCursorSpriteIndex = kItemQuarter; + } +} + +/*****************************************************************************/ + +Scene15::Scene15(GnapEngine *vm) : Scene(vm) { + _nextRecordSequenceId = -1; + _currRecordSequenceId = -1; + _nextSlotSequenceId = -1; + _currSlotSequenceId = -1; + _nextUpperButtonSequenceId = -1; + _currUpperButtonSequenceId = -1; + _nextLowerButtonSequenceId = -1; + _currLowerButtonSequenceId = -1; +} + +int Scene15::init() { + return 0xDD; +} + +void Scene15::updateHotspots() { + _vm->setHotspot(kHS15Platypus, 0, 0, 0, 0, SF_DISABLED); + _vm->setHotspot(kHS15Exit, 50, 590, 750, 599, SF_EXIT_D_CURSOR); + _vm->setHotspot(kHS15Button1, 210, 425, 260, 475, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS15Button2, 280, 425, 325, 475, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS15Button3, 340, 425, 385, 475, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS15Button4, 400, 425, 445, 475, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS15Button5, 460, 425, 510, 475, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS15Button6, 520, 425, 560, 475, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS15ButtonA, 205, 480, 250, 535, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS15ButtonB, 270, 480, 320, 535, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS15ButtonC, 335, 480, 380, 535, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS15ButtonD, 395, 480, 445, 535, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS15ButtonE, 460, 480, 505, 535, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS15ButtonF, 515, 480, 560, 535, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS15CoinSlot, 585, 475, 620, 535, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS15PlayButton, 622, 431, 650, 482, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setDeviceHotspot(kHS15Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 17; +} + +void Scene15::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + _currSlotSequenceId = -1; + _currUpperButtonSequenceId = -1; + _currLowerButtonSequenceId = -1; + _nextSlotSequenceId = -1; + _nextUpperButtonSequenceId = -1; + _nextLowerButtonSequenceId = -1; + _currRecordSequenceId = 0xD5; + _nextRecordSequenceId = -1; + + gameSys.setAnimation(0xD5, 1, 0); + gameSys.insertSequence(_currRecordSequenceId, 1, 0, 0, kSeqNone, 0, 0, 0); + + _vm->queueInsertDeviceIcon(); + + _vm->endSceneInit(); + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->_hotspots[kHS15Platypus].clearRect(); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS15Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS15Exit: + _vm->_newSceneNum = 12; + _vm->_isLeavingScene = true; + break; + + case kHS15CoinSlot: + if (_vm->_grabCursorSpriteIndex == kItemQuarter || _vm->_grabCursorSpriteIndex == kItemQuarterWithHole) { + _nextSlotSequenceId = 0xDC; // Insert coin + } else if (_vm->_grabCursorSpriteIndex == kItemDiceQuarterHole) { + _nextSlotSequenceId = 0xDB; + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gameSys.insertSequence(0x107A8, 1, 0, 0, kSeqNone, 0, 900 - gnap._gridX, 576 - gnap._gridY); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + case GRAB_CURSOR: + _vm->playSound(0x108E9, false); + break; + case TALK_CURSOR: + _vm->playSound((_vm->getRandom(5) + 0x8D5) | 0x10000, false); + break; + case PLAT_CURSOR: + gameSys.insertSequence(0x107A8, 1, 0, 0, kSeqNone, 0, 900 - gnap._gridX, 576 - gnap._gridY); + break; + } + } + break; + + case kHS15PlayButton: + if (_vm->_grabCursorSpriteIndex >= 0) { + gameSys.insertSequence(0x107A8, 1, 0, 0, kSeqNone, 0, 900 - gnap._gridX, 576 - gnap._gridY); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + if (_vm->isFlag(kGFGnapControlsToyUFO) || _vm->isFlag(kGFUnk13)) + _vm->playSound(0x108E9, false); + else + _nextSlotSequenceId = 0xDA; + break; + case GRAB_CURSOR: + if (_vm->isFlag(kGFGnapControlsToyUFO) || _vm->isFlag(kGFUnk13)) + _nextSlotSequenceId = 0xD9; + else + _nextSlotSequenceId = 0xDA; + break; + case TALK_CURSOR: + _vm->playSound((_vm->getRandom(5) + 0x8D5) | 0x10000, false); + break; + case PLAT_CURSOR: + gameSys.insertSequence(0x107A8, 1, 0, 0, kSeqNone, 0, 900 - gnap._gridX, 576 - gnap._gridY); + break; + } + } + break; + + case kHS15Button1: + case kHS15Button2: + case kHS15Button3: + case kHS15Button4: + case kHS15Button5: + case kHS15Button6: + if (_vm->_grabCursorSpriteIndex >= 0) { + gameSys.insertSequence(0x107A8, 1, 0, 0, kSeqNone, 0, 900 - gnap._gridX, 576 - gnap._gridY); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + _vm->playSound(0x108E9, false); + break; + case GRAB_CURSOR: + _nextUpperButtonSequenceId = _vm->_sceneClickedHotspot + 0xC5; + break; + case TALK_CURSOR: + _vm->playSound((_vm->getRandom(5) + 0x8D5) | 0x10000, false); + break; + case PLAT_CURSOR: + gameSys.insertSequence(0x107A8, 1, 0, 0, kSeqNone, 0, 900 - gnap._gridX, 576 - gnap._gridY); + break; + } + } + break; + + case kHS15ButtonA: + case kHS15ButtonB: + case kHS15ButtonC: + case kHS15ButtonD: + case kHS15ButtonE: + case kHS15ButtonF: + if (_vm->_grabCursorSpriteIndex >= 0) { + gameSys.insertSequence(0x107A8, 1, 0, 0, kSeqNone, 0, 900 - gnap._gridX, 576 - gnap._gridY); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + _vm->playSound(0x108E9, false); + break; + case GRAB_CURSOR: + _nextLowerButtonSequenceId = _vm->_sceneClickedHotspot + 0xC5; + break; + case TALK_CURSOR: + _vm->playSound((_vm->getRandom(5) + 0x8D5) | 0x10000, false); + break; + case PLAT_CURSOR: + gameSys.insertSequence(0x107A8, 1, 0, 0, kSeqNone, 0, 900 - gnap._gridX, 576 - gnap._gridY); + break; + } + } + break; + + default: + _vm->_mouseClickState._left = false; + break; + + } + + updateAnimations(); + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } +} + +void Scene15::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + + if (gameSys.getAnimationStatus(0) == 2) { + if (_vm->_isLeavingScene) { + _vm->_sceneDone = true; + } else if (_nextSlotSequenceId != -1) { + gameSys.setAnimation(_nextSlotSequenceId, 1, 0); + gameSys.insertSequence(_nextSlotSequenceId, 1, 0, 0, kSeqNone, 0, 0, 0); + _currSlotSequenceId = _nextSlotSequenceId; + _nextSlotSequenceId = -1; + switch (_currSlotSequenceId) { + case 0xDC: + if (_vm->_grabCursorSpriteIndex == kItemQuarter) { + _vm->invRemove(kItemQuarter); + } else { + _vm->invRemove(kItemQuarterWithHole); + _vm->setFlag(kGFUnk13); + } + _vm->setGrabCursorSprite(-1); + break; + case 0xDB: + _vm->setFlag(kGFUnk14); + _vm->setGrabCursorSprite(-1); + _nextSlotSequenceId = 0xD8; + break; + case 0xD9: + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + _vm->clearFlag(kGFGnapControlsToyUFO); + _vm->invAdd(kItemQuarter); + _vm->_newGrabCursorSpriteIndex = kItemQuarter; + } else if (_vm->isFlag(kGFUnk13)) { + _vm->clearFlag(kGFUnk13); + _vm->invAdd(kItemQuarterWithHole); + _vm->_newGrabCursorSpriteIndex = kItemQuarterWithHole; + } + _vm->_newSceneNum = 12; + _vm->_isLeavingScene = true; + break; + case 0xD8: + case 0xDA: + if (_currUpperButtonSequenceId != -1) { + gameSys.removeSequence(_currUpperButtonSequenceId, 1, true); + _currUpperButtonSequenceId = -1; + } + if (_currLowerButtonSequenceId != -1) { + gameSys.removeSequence(_currLowerButtonSequenceId, 1, true); + _currLowerButtonSequenceId = -1; + } + break; + } + } else if (_nextRecordSequenceId != -1) { + gameSys.setAnimation(_nextRecordSequenceId, 1, 0); + gameSys.insertSequence(_nextRecordSequenceId, 1, _currRecordSequenceId, 1, kSeqSyncWait, 0, 0, 0); + _currRecordSequenceId = _nextRecordSequenceId; + _nextRecordSequenceId = -1; + if (_currRecordSequenceId == 0xD3) { + _vm->invRemove(kItemDiceQuarterHole); + _vm->_newSceneNum = 16; + _vm->_isLeavingScene = true; + } + gameSys.removeSequence(_currUpperButtonSequenceId, 1, true); + _currUpperButtonSequenceId = -1; + gameSys.removeSequence(_currLowerButtonSequenceId, 1, true); + _currLowerButtonSequenceId = -1; + } else if (_nextUpperButtonSequenceId != -1) { + gameSys.setAnimation(_nextUpperButtonSequenceId, 1, 0); + if (_currUpperButtonSequenceId == -1) + gameSys.insertSequence(_nextUpperButtonSequenceId, 1, 0, 0, kSeqNone, 0, 0, 0); + else + gameSys.insertSequence(_nextUpperButtonSequenceId, 1, _currUpperButtonSequenceId, 1, kSeqSyncWait, 0, 0, 0); + _currUpperButtonSequenceId = _nextUpperButtonSequenceId; + _nextUpperButtonSequenceId = -1; + if (_currLowerButtonSequenceId != -1 && _vm->isFlag(kGFUnk14)) { + if (_currUpperButtonSequenceId == 0xCC && _currLowerButtonSequenceId == 0xCE) + _nextRecordSequenceId = 0xD3; + else + _nextRecordSequenceId = 0xD4; + } + } else if (_nextLowerButtonSequenceId != -1) { + gameSys.setAnimation(_nextLowerButtonSequenceId, 1, 0); + if (_currLowerButtonSequenceId == -1) + gameSys.insertSequence(_nextLowerButtonSequenceId, 1, 0, 0, kSeqNone, 0, 0, 0); + else + gameSys.insertSequence(_nextLowerButtonSequenceId, 1, _currLowerButtonSequenceId, 1, kSeqSyncWait, 0, 0, 0); + _currLowerButtonSequenceId = _nextLowerButtonSequenceId; + _nextLowerButtonSequenceId = -1; + if (_currUpperButtonSequenceId != -1 && _vm->isFlag(kGFUnk14)) { + if (_currUpperButtonSequenceId == 0xCC && _currLowerButtonSequenceId == 0xCE) + _nextRecordSequenceId = 0xD3; + else + _nextRecordSequenceId = 0xD4; + } + } + } +} + +/*****************************************************************************/ + +Scene17::Scene17(GnapEngine *vm) : Scene(vm) { + _platTryGetWrenchCtr = 0; + _wrenchCtr = 2; + _nextCarWindowSequenceId = -1; + _nextWrenchSequenceId = -1; + _canTryGetWrench = true; + _platPhoneCtr = 0; + _nextPhoneSequenceId = -1; + _currPhoneSequenceId = -1; + _currWrenchSequenceId = -1; + _currCarWindowSequenceId = -1; +} + +int Scene17::init() { + return 0x263; +} + +void Scene17::updateHotspots() { + _vm->setHotspot(kHS17Platypus, 1, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS17Phone1, 61, 280, 97, 322, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 1, 7); + _vm->setHotspot(kHS17Phone2, 80, 204, 178, 468, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 1, 7); + _vm->setHotspot(kHS17ExitGrubCity, 196, 207, 280, 304, SF_EXIT_U_CURSOR, 3, 5); + _vm->setHotspot(kHS17ExitToyStore, 567, 211, 716, 322, SF_EXIT_U_CURSOR, 5, 6); + _vm->setHotspot(kHS17Wrench, 586, 455, 681, 547, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 5, 7); + _vm->setHotspot(kHS17WalkArea1, 0, 0, 800, 434); + _vm->setHotspot(kHS17WalkArea2, 541, 0, 800, 600); + _vm->setHotspot(kHS17WalkArea3, 0, 204, 173, 468); + _vm->setDeviceHotspot(kHS17Device, -1, -1, -1, -1); + if (_vm->isFlag(kGFGrassTaken)) + _vm->_hotspots[kHS17Wrench]._flags = SF_NONE; + if (_vm->isFlag(kGFPlatypusTalkingToAssistant)) { + _vm->_hotspots[kHS17Device]._flags = SF_DISABLED; + _vm->_hotspots[kHS17Platypus]._flags = SF_DISABLED; + } + _vm->_hotspotsCount = 10; +} + +void Scene17::update() { + _vm->gameUpdateTick(); + _vm->updateMouseCursor(); + _vm->updateGrabCursorSprite(0, 0); + if (_vm->_mouseClickState._left) { + _vm->_gnap->walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } +} + +void Scene17::platHangUpPhone() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + int savedGnapActionStatus = gnap._actionStatus; + + if (plat._actionStatus == kAS17PlatPhoningAssistant) { + gnap._actionStatus = kAS17PlatHangUpPhone; + _vm->updateMouseCursor(); + _platPhoneCtr = 0; + plat._actionStatus = -1; + gameSys.setAnimation(0x257, 254, 4); + gameSys.insertSequence(0x257, 254, _currPhoneSequenceId, 254, kSeqSyncExists, 0, 0, 0); + while (gameSys.getAnimationStatus(4) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + gameSys.setAnimation(0x25B, plat._id, 1); + gameSys.insertSequence(0x25B, plat._id, plat._sequenceId | (plat._sequenceDatNum << 16), plat._id, kSeqSyncWait, 0, 0, 0); + plat._sequenceId = 0x25B; + plat._sequenceDatNum = 0; + _currPhoneSequenceId = -1; + _nextPhoneSequenceId = -1; + _vm->clearFlag(kGFPlatypusTalkingToAssistant); + while (gameSys.getAnimationStatus(1) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + gnap._actionStatus = savedGnapActionStatus; + _vm->updateMouseCursor(); + } + updateHotspots(); +} + +void Scene17::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->playSound(0x10940, true); + _vm->startSoundTimerA(8); + _vm->_sceneWaiting = false; + _vm->_timers[4] = _vm->getRandom(100) + 200; + _vm->_timers[3] = 200; + _vm->_timers[5] = _vm->getRandom(30) + 80; + _vm->_timers[6] = _vm->getRandom(30) + 200; + _vm->_timers[7] = _vm->getRandom(100) + 100; + + if (_vm->isFlag(kGFTruckKeysUsed)) { + gameSys.insertSequence(0x25F, 20, 0, 0, kSeqNone, 0, 0, 0); + } else { + if (_vm->_s18GarbageCanPos >= 8) { + gameSys.insertSequence(0x260, 20, 0, 0, kSeqNone, 0, 97, 1); + } else if (_vm->_s18GarbageCanPos >= 6) { + gameSys.insertSequence(0x260, 20, 0, 0, kSeqNone, 0, 68, 2); + } else if (_vm->_s18GarbageCanPos >= 5) { + gameSys.insertSequence(0x260, 20, 0, 0, kSeqNone, 0, 23, -1); + } else if (_vm->_s18GarbageCanPos >= 4) { + gameSys.insertSequence(0x260, 20, 0, 0, kSeqNone, 0, -11, -5); + } else { + gameSys.insertSequence(0x260, 20, 0, 0, kSeqNone, 0, -54, -8); + } + } + + if (_vm->isFlag(kGFGroceryStoreHatTaken)) + gameSys.insertSequence(0x262, 1, 0, 0, kSeqNone, 0, 0, 0); + + _vm->queueInsertDeviceIcon(); + + if (_vm->isFlag(kGFGrassTaken)) + _currWrenchSequenceId = 0x22D; + else + _currWrenchSequenceId = 0x22F; + + _currCarWindowSequenceId = 0x244; + + if (_vm->isFlag(kGFUnk14)) + gameSys.insertSequence(0x261, 1, 0, 0, kSeqNone, 0, 0, 0); + + gameSys.setAnimation(_currWrenchSequenceId, 40, 2); + gameSys.insertSequence(_currWrenchSequenceId, 40, 0, 0, kSeqNone, 0, 0, 0); + + if (_vm->isFlag(kGFGrassTaken)) { + gameSys.setAnimation(0, 0, 3); + } else { + gameSys.setAnimation(_currCarWindowSequenceId, 40, 3); + gameSys.insertSequence(_currCarWindowSequenceId, 40, 0, 0, kSeqNone, 0, 0, 0); + } + + _canTryGetWrench = true; + + if (_vm->isFlag(kGFUnk18)) + gameSys.insertSequence(0x24F, 100, 0, 0, kSeqNone, 0, 0, 0); + + if (_vm->_prevSceneNum == 53 || _vm->_prevSceneNum == 18 || _vm->_prevSceneNum == 20 || _vm->_prevSceneNum == 19) { + if (_vm->_prevSceneNum == 20) { + gnap.initPos(4, 6, kDirBottomRight); + plat.initPos(5, 6, kDirIdleLeft); + _vm->endSceneInit(); + plat.walkTo(Common::Point(5, 9), -1, 0x107C2, 1); + gnap.walkTo(Common::Point(4, 8), -1, 0x107B9, 1); + } else if (_vm->isFlag(kGFUnk27)) { + gnap.initPos(3, 9, kDirUpLeft); + plat._pos = _vm->_hotspotsWalkPos[2]; + plat._id = 20 * _vm->_hotspotsWalkPos[2].y; + gameSys.insertSequence(0x25A, 20 * _vm->_hotspotsWalkPos[2].y, 0, 0, kSeqNone, 0, 0, 0); + gameSys.insertSequence(0x257, 254, 0, 0, kSeqNone, 0, 0, 0); + plat._sequenceId = 0x25A; + plat._sequenceDatNum = 0; + _vm->endSceneInit(); + _vm->clearFlag(kGFSpringTaken); + _vm->clearFlag(kGFUnk16); + plat._actionStatus = kAS17PlatPhoningAssistant; + platHangUpPhone(); + gameSys.setAnimation(0, 0, 4); + _vm->clearFlag(kGFPlatypusTalkingToAssistant); + _vm->clearFlag(kGFUnk27); + updateHotspots(); + } else if (_vm->isFlag(kGFUnk25)) { + _vm->clearFlag(kGFSpringTaken); + _vm->clearFlag(kGFUnk16); + plat.initPos(7, 9, kDirIdleLeft); + gnap._pos = _vm->_hotspotsWalkPos[2]; + gnap._id = 20 * _vm->_hotspotsWalkPos[2].y; + gameSys.insertSequence(601, 20 * _vm->_hotspotsWalkPos[2].y, 0, 0, kSeqNone, 0, 0, 0); + gnap._sequenceDatNum = 0; + gnap._sequenceId = 601; + gnap._actionStatus = kAS17GnapHangUpPhone; + _vm->clearFlag(kGFUnk25); + gameSys.insertSequence(0x251, 254, 0, 0, kSeqNone, 0, 0, 0); + _vm->endSceneInit(); + gameSys.setAnimation(0x257, 254, 0); + gameSys.insertSequence(0x257, 254, 0x251, 254, kSeqSyncWait, 0, 0, 0); + } else if (_vm->isFlag(kGFPlatypusTalkingToAssistant)) { + _vm->clearFlag(kGFSpringTaken); + _vm->clearFlag(kGFUnk16); + _vm->_sceneWaiting = true; + gnap.initPos(3, 9, kDirUpLeft); + plat._pos = _vm->_hotspotsWalkPos[2]; + plat._id = 20 * _vm->_hotspotsWalkPos[2].y; + _currPhoneSequenceId = 0x251; + gameSys.insertSequence(0x25A, 20 * _vm->_hotspotsWalkPos[2].y, 0, 0, kSeqNone, 0, 0, 0); + gameSys.insertSequence(_currPhoneSequenceId, 254, 0, 0, kSeqNone, 0, 0, 0); + plat._sequenceId = 0x25A; + plat._sequenceDatNum = 0; + _vm->endSceneInit(); + gameSys.setAnimation(_currPhoneSequenceId, 254, 1); + plat._actionStatus = kAS17PlatPhoningAssistant; + updateHotspots(); + } else if (_vm->_prevSceneNum == 18) { + gnap.initPos(6, 6, kDirBottomRight); + plat.initPos(5, 6, kDirIdleLeft); + _vm->endSceneInit(); + plat.walkTo(Common::Point(5, 9), -1, 0x107C2, 1); + gnap.walkTo(Common::Point(4, 8), -1, 0x107B9, 1); + } else { + if (_vm->isFlag(kGFSpringTaken)) { + gnap.initPos(_vm->_hotspotsWalkPos[2].x, _vm->_hotspotsWalkPos[2].y, kDirBottomRight); + plat.initPos(1, 9, kDirIdleLeft); + _vm->endSceneInit(); + } else { + gnap.initPos(3, 7, kDirBottomRight); + plat.initPos(1, 7, kDirIdleLeft); + _vm->endSceneInit(); + } + _vm->clearFlag(kGFSpringTaken); + _vm->clearFlag(kGFUnk16); + _vm->endSceneInit(); + } + } else { + gnap._pos = Common::Point(3, 6); + gnap._id = 120; + gnap._sequenceId = 0x23D; + gnap._sequenceDatNum = 0; + gnap._idleFacing = kDirBottomRight; + gameSys.insertSequence(makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, 0, 0, kSeqNone, 0, 0, 0); + plat._pos = Common::Point(-1, 8); + plat._id = 160; + gameSys.insertSequence(0x241, 160, 0, 0, kSeqNone, 0, 0, 0); + gameSys.insertSequence(0x107C1, plat._id, 0x241, plat._id, + kSeqScale | kSeqSyncWait, 0, 75 * plat._pos.x - plat._gridX, 48 * plat._pos.y - plat._gridY); + gameSys.insertSequence(0x22C, 2, 0, 0, kSeqNone, 0, 0, 0); + _vm->delayTicksA(2, 9); + _vm->endSceneInit(); + plat._sequenceId = 0x7C1; + plat._sequenceDatNum = 1; + plat._idleFacing = kDirBottomRight; + plat.walkTo(Common::Point(2, 9), -1, 0x107C2, 1); + } + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS17Device: + if (gnap._actionStatus < 0 || gnap._actionStatus == 3) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS17Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemJoint) { + if (_vm->isFlag(kGFGrassTaken)) { + gnap.useJointOnPlatypus(); + } else { + gnap.useDeviceOnPlatypus(); + plat.walkTo(_vm->_hotspotsWalkPos[6], 1, 0x107C2, 1); + gnap.walkTo(_vm->_hotspotsWalkPos[6] + Common::Point(1, 0), 0, 0x107BA, 1); + plat._actionStatus = kAS17GetWrench1; + gnap._actionStatus = kAS17GetWrench1; + _vm->_timers[5] = _vm->getRandom(30) + 80; + _vm->setGrabCursorSprite(-1); + _vm->invRemove(kItemJoint); + } + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playScratchingHead(plat._pos); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + case GRAB_CURSOR: + gnap.playScratchingHead(plat._pos); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS17Wrench: + if (gnap._actionStatus < 0) { + if (_vm->isFlag(kGFGrassTaken)) { + gnap.playImpossible(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[_vm->_sceneClickedHotspot], 8, 7); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + case GRAB_CURSOR: + gnap.playScratchingHead(Common::Point(8, 7)); + break; + case TALK_CURSOR: + gnap.playImpossible(); + break; + case PLAT_CURSOR: + if (_canTryGetWrench) { + platHangUpPhone(); + gnap.useDeviceOnPlatypus(); + plat.walkTo(_vm->_hotspotsWalkPos[6] + Common::Point(1, 0), 1, 0x107C2, 1); + plat._actionStatus = kAS17TryGetWrench; + gnap._actionStatus = kAS17TryGetWrench; + _vm->_timers[5] = _vm->getRandom(30) + 80; + } else + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS17Phone1: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemDiceQuarterHole) { + gnap.walkTo(_vm->_hotspotsWalkPos[2], 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS17PutCoinIntoPhone; + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[2], 1, 3); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(1, 3)); + break; + case GRAB_CURSOR: + if (_vm->isFlag(kGFUnk18)) { + platHangUpPhone(); + gnap.walkTo(gnap._pos, 0, gnap.getSequenceId(kGSIdle, _vm->_hotspotsWalkPos[2]) | 0x10000, 1); + gnap._actionStatus = kAS17GetCoinFromPhone; + } else + gnap.playImpossible(); + break; + case TALK_CURSOR: + gnap.playImpossible(); + break; + case PLAT_CURSOR: + if (_vm->isFlag(kGFUnk18)) { + platHangUpPhone(); + _vm->_isLeavingScene = true; + gnap.useDeviceOnPlatypus(); + plat._idleFacing = kDirUpLeft; + plat.walkTo(_vm->_hotspotsWalkPos[2], 1, 0x107C2, 1); + _vm->setFlag(kGFUnk16); + plat._actionStatus = kAS17PlatUsePhone; + gnap._actionStatus = kAS17PlatUsePhone; + } else + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS17Phone2: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemDiceQuarterHole) { + gnap.walkTo(_vm->_hotspotsWalkPos[2], 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS17PutCoinIntoPhone; + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[2], 1, 3); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(1, 3)); + break; + case GRAB_CURSOR: + if (_vm->isFlag(kGFUnk18)) { + platHangUpPhone(); + _vm->_isLeavingScene = true; + gnap._idleFacing = kDirUpLeft; + gnap.walkTo(_vm->_hotspotsWalkPos[2], 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS17GnapUsePhone; + _vm->setFlag(kGFSpringTaken); + } else + gnap.playImpossible(); + break; + case TALK_CURSOR: + gnap.playImpossible(); + break; + case PLAT_CURSOR: + if (_vm->isFlag(kGFUnk18)) { + platHangUpPhone(); + _vm->_isLeavingScene = true; + gnap.useDeviceOnPlatypus(); + plat._idleFacing = kDirUpLeft; + plat.walkTo(_vm->_hotspotsWalkPos[2], 1, 0x107C2, 1); + _vm->setFlag(kGFUnk16); + plat._actionStatus = kAS17PlatUsePhone; + gnap._actionStatus = kAS17PlatUsePhone; + } else + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS17ExitToyStore: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 18; + gnap._idleFacing = kDirUpRight; + gnap.walkTo(_vm->_hotspotsWalkPos[5], 0, 0x107BB, 1); + gnap._actionStatus = kAS17LeaveScene; + if (plat._actionStatus != kAS17PlatPhoningAssistant) + plat.walkTo(_vm->_hotspotsWalkPos[5] + Common::Point(-1, 0), -1, 0x107C2, 1); + } + break; + + case kHS17ExitGrubCity: + if (gnap._actionStatus < 0) { + platHangUpPhone(); + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 20; + gnap._idleFacing = kDirUpLeft; + gnap.walkTo(_vm->_hotspotsWalkPos[3], 0, 0x107BC, 1); + gnap._actionStatus = kAS17LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[3] + Common::Point(1, 0), -1, 0x107C2, 1); + } + break; + + case kHS17WalkArea1: + case kHS17WalkArea2: + case kHS17WalkArea3: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left && gnap._actionStatus < 0) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = 0; + } + break; + } + + updateAnimations(); + + if (!_vm->isSoundPlaying(0x10940)) + _vm->playSound(0x10940, true); + + if (!_vm->_isLeavingScene) { + if (plat._actionStatus < 0) + plat.updateIdleSequence2(); + gnap.updateIdleSequence2(); + if (!_vm->_timers[4]) { + _vm->_timers[4] = _vm->getRandom(100) + 200; + if (gnap._actionStatus < 0 && plat._actionStatus < 0) + gameSys.insertSequence(0x22B, 21, 0, 0, kSeqNone, 0, 0, 0); + } + if (!_vm->_timers[7]) { + _vm->_timers[7] = _vm->getRandom(100) + 100; + if (gnap._actionStatus < 0 && plat._actionStatus < 0) { + switch (_vm->getRandom(3)) { + case 0: + gameSys.insertSequence(0x25C, 255, 0, 0, kSeqNone, 0, 0, 0); + break; + case 1: + gameSys.insertSequence(0x25D, 255, 0, 0, kSeqNone, 0, 0, 0); + break; + case 2: + gameSys.insertSequence(0x25E, 255, 0, 0, kSeqNone, 0, 0, 0); + break; + } + } + } + if (plat._actionStatus < 0 && !_vm->_timers[5]) { + _vm->_timers[5] = _vm->getRandom(30) + 80; + if (_vm->isFlag(kGFGrassTaken) && _nextWrenchSequenceId == -1) { + _nextWrenchSequenceId = 0x236; + } else if (_canTryGetWrench) { + switch (_vm->getRandom(6)) { + case 0: + _nextWrenchSequenceId = 0x231; + break; + case 1: + _nextWrenchSequenceId = 0x232; + break; + case 2: + case 3: + _nextWrenchSequenceId = 0x23C; + break; + case 4: + case 5: + _nextWrenchSequenceId = 0x22E; + break; + } + } else { + --_wrenchCtr; + if (_wrenchCtr) { + switch (_vm->getRandom(6)) { + case 0: + _nextWrenchSequenceId = 0x237; + break; + case 1: + _nextWrenchSequenceId = 0x238; + break; + case 2: + _nextWrenchSequenceId = 0x239; + break; + case 3: + _nextWrenchSequenceId = 0x23A; + break; + case 4: + _nextWrenchSequenceId = 0x23B; + break; + case 5: + _nextWrenchSequenceId = 0x235; + break; + } + } else { + _wrenchCtr = 2; + _nextWrenchSequenceId = 0x235; + } + } + } + if (!_vm->_timers[6]) { + _vm->_timers[6] = _vm->getRandom(30) + 200; + if (_nextCarWindowSequenceId == -1 && !_vm->isFlag(kGFGrassTaken)) + _nextCarWindowSequenceId = 0x246; + } + _vm->playSoundA(); + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } +} + +void Scene17::updateAnimations() { + static const int kPlatPhoneSequenceIds[] = { + 0x251, 0x252, 0x253, 0x254, 0x255, 0x256, 0x257 + }; + + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS17GetWrench1: + gnap._actionStatus = kAS17GetWrenchGnapReady; + break; + case kAS17GetCoinFromPhone: + gnap.playPullOutDevice(Common::Point(1, 3)); + gnap.playUseDevice(); + gameSys.setAnimation(0x250, 100, 0); + gameSys.insertSequence(0x250, 100, 591, 100, kSeqSyncWait, 0, 0, 0); + _vm->invAdd(kItemDiceQuarterHole); + _vm->clearFlag(kGFUnk18); + gnap._actionStatus = kAS17GetCoinFromPhoneDone; + break; + case kAS17GetCoinFromPhoneDone: + _vm->setGrabCursorSprite(kItemDiceQuarterHole); + gnap._actionStatus = -1; + break; + case kAS17PutCoinIntoPhone: + gameSys.setAnimation(0x24C, gnap._id, 0); + gameSys.insertSequence(0x24C, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceDatNum = 0; + gnap._sequenceId = 0x24C; + _vm->invRemove(kItemDiceQuarterHole); + _vm->setGrabCursorSprite(-1); + _vm->setFlag(kGFUnk18); + gnap._actionStatus = kAS17PutCoinIntoPhoneDone; + break; + case kAS17PutCoinIntoPhoneDone: + gameSys.insertSequence(0x24F, 100, 0, 0, kSeqNone, 0, 0, 0); + gnap._actionStatus = -1; + break; + case kAS17GnapUsePhone: + gameSys.setAnimation(0x24D, gnap._id, 0); + gameSys.insertSequence(0x24D, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._actionStatus = kAS17LeaveScene; + _vm->_newSceneNum = 53; + break; + case kAS17GnapHangUpPhone: + gameSys.insertSequence(0x258, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceDatNum = 0; + gnap._sequenceId = 0x258; + gnap._actionStatus = -1; + break; + case kAS17LeaveScene: + _vm->_sceneDone = true; + break; + } + } + + if (gameSys.getAnimationStatus(1) == 2) { + gameSys.setAnimation(0, 0, 1); + switch (plat._actionStatus) { + case kAS17TryGetWrench: + plat._actionStatus = -1; + ++_platTryGetWrenchCtr; + if (_platTryGetWrenchCtr % 2 != 0) + _nextWrenchSequenceId = 0x233; + else + _nextWrenchSequenceId = 0x234; + _canTryGetWrench = false; + break; + case kAS17GetWrench1: + _nextWrenchSequenceId = 0x230; + break; + case kAS17GetWrench2: + _nextCarWindowSequenceId = 0x249; + break; + case kAS17GetWrenchDone: + plat._actionStatus = -1; + _vm->invAdd(kItemWrench); + _vm->setGrabCursorSprite(kItemWrench); + break; + case kAS17PlatUsePhone: + gameSys.setAnimation(0x24E, plat._id, 1); + gameSys.insertSequence(0x24E, plat._id, plat._sequenceId | (plat._sequenceDatNum << 16), plat._id, kSeqSyncWait, 0, 0, 0); + plat._sequenceDatNum = 0; + plat._sequenceId = 0x24E; + plat._actionStatus = kAS17LeaveScene; + _vm->_newSceneNum = 53; + break; + case kAS17PlatPhoningAssistant: + ++_platPhoneCtr; + if (_platPhoneCtr >= 7) { + _platPhoneCtr = 0; + _nextPhoneSequenceId = -1; + _currPhoneSequenceId = -1; + gameSys.insertSequence(0x25B, plat._id, 0x25A, plat._id, kSeqSyncWait, 0, 0, 0); + plat._sequenceDatNum = 0; + plat._sequenceId = 0x25B; + plat._actionStatus = -1; + _vm->clearFlag(kGFPlatypusTalkingToAssistant); + _vm->_sceneWaiting = false; + updateHotspots(); + } else { + _nextPhoneSequenceId = kPlatPhoneSequenceIds[_platPhoneCtr]; + gameSys.setAnimation(_nextPhoneSequenceId, 254, 1); + gameSys.insertSequence(_nextPhoneSequenceId, 254, _currPhoneSequenceId, 254, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x25A, plat._id, 0x25A, plat._id, kSeqSyncWait, 0, 0, 0); + plat._sequenceDatNum = 0; + plat._sequenceId = 0x25A; + _currPhoneSequenceId = _nextPhoneSequenceId; + } + break; + case kAS17LeaveScene: + _vm->_sceneDone = true; + break; + } + } + + if (gameSys.getAnimationStatus(2) == 2) { + switch (_nextWrenchSequenceId) { + case 0x233: + gnap._actionStatus = -1; + gameSys.insertSequence(0x243, plat._id, + plat._sequenceId | (plat._sequenceDatNum << 16), plat._id, + kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(_nextWrenchSequenceId, 40, _currWrenchSequenceId, 40, kSeqSyncWait, 0, 0, 0); + _currWrenchSequenceId = _nextWrenchSequenceId; + _nextWrenchSequenceId = -1; + plat._sequenceId = 0x243; + plat._sequenceDatNum = 0; + gameSys.setAnimation(0x243, plat._id, 1); + break; + case 0x234: + gnap._actionStatus = -1; + gameSys.insertSequence(0x242, plat._id, + plat._sequenceId | (plat._sequenceDatNum << 16), plat._id, + kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(_nextWrenchSequenceId, 40, _currWrenchSequenceId, 40, kSeqSyncWait, 0, 0, 0); + _currWrenchSequenceId = _nextWrenchSequenceId; + _nextWrenchSequenceId = -1; + plat._sequenceId = 0x242; + plat._sequenceDatNum = 0; + gameSys.setAnimation(0x242, plat._id, 1); + break; + case 0x231: + if (_vm->getRandom(2) != 0) + _nextCarWindowSequenceId = 0x245; + else + _nextCarWindowSequenceId = 0x248; + gameSys.setAnimation(0, 0, 2); + break; + case 0x232: + _nextCarWindowSequenceId = 0x247; + gameSys.setAnimation(0, 0, 2); + break; + case 0x22E: + case 0x235: + if (_nextWrenchSequenceId == 0x235) + _vm->_hotspots[kHS17Wrench]._flags &= ~SF_DISABLED; + else + _vm->_hotspots[kHS17Wrench]._flags |= SF_DISABLED; + _canTryGetWrench = !_canTryGetWrench; + gameSys.setAnimation(_nextWrenchSequenceId, 40, 2); + gameSys.insertSequence(_nextWrenchSequenceId, 40, _currWrenchSequenceId, 40, kSeqSyncWait, 0, 0, 0); + _currWrenchSequenceId = _nextWrenchSequenceId; + _nextWrenchSequenceId = -1; + break; + case 0x230: + if (gnap._actionStatus == kAS17GetWrenchGnapReady) { + gameSys.setAnimation(0, 0, 2); + if (_canTryGetWrench) { + gameSys.insertSequence(0x22E, 40, _currWrenchSequenceId, 40, kSeqSyncWait, 0, 0, 0); + _currWrenchSequenceId = 0x22E; + _canTryGetWrench = false; + } + gameSys.setAnimation(0x23F, plat._id, 1); + gameSys.insertSequence(0x10875, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x23F, plat._id, + plat._sequenceId | (plat._sequenceDatNum << 16), plat._id, + kSeqSyncWait, 0, 0, 0); + gnap._sequenceDatNum = 1; + plat._sequenceDatNum = 0; + gnap._sequenceId = 0x875; + plat._sequenceId = 0x23F; + gnap.walkTo(Common::Point(3, 8), -1, 0x107B9, 1); + plat._actionStatus = kAS17GetWrench2; + } + break; + default: + if (_nextWrenchSequenceId != -1) { + gameSys.setAnimation(_nextWrenchSequenceId, 40, 2); + gameSys.insertSequence(_nextWrenchSequenceId, 40, _currWrenchSequenceId, 40, kSeqSyncWait, 0, 0, 0); + _currWrenchSequenceId = _nextWrenchSequenceId; + _nextWrenchSequenceId = -1; + } + break; + } + } + + if (gameSys.getAnimationStatus(3) == 2) { + switch (_nextCarWindowSequenceId) { + case 0x246: + gameSys.setAnimation(_nextCarWindowSequenceId, 40, 3); + gameSys.insertSequence(_nextCarWindowSequenceId, 40, _currCarWindowSequenceId, 40, kSeqSyncWait, 0, 0, 0); + _currCarWindowSequenceId = _nextCarWindowSequenceId; + _nextCarWindowSequenceId = -1; + break; + case 0x245: + case 0x247: + case 0x248: + gameSys.setAnimation(_nextWrenchSequenceId, 40, 2); + gameSys.insertSequence(_nextWrenchSequenceId, 40, _currWrenchSequenceId, 40, kSeqSyncWait, 0, 0, 0); + while (gameSys.getAnimationStatus(2) != 2) + update(); + gameSys.setAnimation(_nextCarWindowSequenceId, 40, 3); + gameSys.insertSequence(_nextCarWindowSequenceId, 40, _currCarWindowSequenceId, 40, kSeqSyncWait, 0, 0, 0); + _currCarWindowSequenceId = _nextCarWindowSequenceId; + _nextCarWindowSequenceId = -1; + _currWrenchSequenceId = _nextWrenchSequenceId; + _nextWrenchSequenceId = -1; + break; + case 0x249: + gameSys.setAnimation(0x230, 40, 2); + gameSys.setAnimation(0x240, plat._id, 1); + gameSys.insertSequence(0x230, 40, _currWrenchSequenceId, 40, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(_nextCarWindowSequenceId, 40, _currCarWindowSequenceId, 40, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x240, plat._id, plat._sequenceId, plat._id, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x23E, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x23E; + gnap._sequenceDatNum = 0; + plat._sequenceId = 0x240; + plat._sequenceDatNum = 0; + gameSys.setAnimation(0x24A, 40, 3); + gameSys.insertSequence(0x24A, 40, _nextCarWindowSequenceId, 40, kSeqSyncWait, 0, 0, 0); + while (gameSys.getAnimationStatus(2) != 2) { + update(); + if (gameSys.getAnimationStatus(3) == 2) { + gameSys.setAnimation(0x24A, 40, 3); + gameSys.insertSequence(0x24A, 40, 586, 40, kSeqSyncWait, 0, 0, 0); + } + } + gameSys.insertSequence(0x22D, 40, 560, 40, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(0x24B, 40, 3); + gameSys.insertSequence(0x24B, 40, 586, 40, kSeqSyncWait, 0, 0, 0); + _currCarWindowSequenceId = 0x24B; + _nextCarWindowSequenceId = -1; + _currWrenchSequenceId = 0x22D; + _nextWrenchSequenceId = -1; + _vm->setFlag(kGFGrassTaken); + gnap._actionStatus = -1; + plat._actionStatus = 2; + updateHotspots(); + _vm->_timers[5] = _vm->getRandom(30) + 80; + break; + } + } + +} + +/*****************************************************************************/ + +static const int kScene18SequenceIds[] = { + 0x219, 0x21A, 0x21B, 0x21C, 0x21D +}; + +Scene18::Scene18(GnapEngine *vm) : Scene(vm) { + _cowboyHatSurface = nullptr; + + _platPhoneCtr = 0; + _platPhoneIter = 0; + _nextPhoneSequenceId = -1; + _currPhoneSequenceId = -1; +} + +Scene18::~Scene18() { + delete _cowboyHatSurface; +} + +int Scene18::init() { + _vm->_gameSys->setAnimation(0, 0, 3); + return 0x222; +} + +void Scene18::updateHotspots() { + _vm->setHotspot(kHS18Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS18GarbageCan, _vm->_gridMinX + 75 * _vm->_s18GarbageCanPos - 35, _vm->_gridMinY + 230, _vm->_gridMinX + 75 * _vm->_s18GarbageCanPos + 35, _vm->_gridMinY + 318, + SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, _vm->_s18GarbageCanPos, 7); + _vm->setHotspot(kHS18ExitToyStore, 460, 238, 592, 442, SF_EXIT_U_CURSOR, 7, 7); + _vm->setHotspot(kHS18ExitPhoneBooth, 275, 585, 525, 600, SF_EXIT_D_CURSOR | SF_WALKABLE, 5, 10); + _vm->setHotspot(kHS18ExitGrubCity, 0, 350, 15, 600, SF_EXIT_L_CURSOR, 0, 9); + _vm->setHotspot(kHS18HydrantTopValve, 100, 345, 182, 410, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 1, 8); + _vm->setHotspot(kHS18HydrantRightValve, 168, 423, 224, 470, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 3, 7); + _vm->setHotspot(kHS18CowboyHat, 184, 63, 289, 171, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 4, 7); + _vm->setHotspot(kHS18WalkArea1, 0, 0, 800, 448); + _vm->setHotspot(kHS18WalkArea2, 0, 0, 214, 515); + _vm->setDeviceHotspot(kHS18Device, -1, -1, -1, -1); + if (_vm->isFlag(kGFTruckFilledWithGas)) { + if (_vm->isFlag(kGFTruckKeysUsed)) { + _vm->_hotspots[kHS18HydrantTopValve]._flags = SF_DISABLED; + _vm->_hotspots[kHS18HydrantRightValve]._rect.left = 148; + _vm->_hotspots[kHS18HydrantRightValve]._rect.top = 403; + _vm->_hotspots[kHS18GarbageCan]._flags = SF_DISABLED; + _vm->_hotspotsWalkPos[kHS18GarbageCan] = Common::Point(3, 7); + } else { + _vm->_hotspots[kHS18HydrantTopValve]._rect.top = 246; + } + } else if (_vm->isFlag(kGFBarnPadlockOpen)) { + _vm->_hotspots[kHS18HydrantRightValve]._flags = SF_DISABLED; + _vm->_hotspots[kHS18HydrantTopValve]._rect.left = 105; + _vm->_hotspots[kHS18HydrantTopValve]._rect.right = 192; + } else if (_vm->isFlag(kGFTruckKeysUsed)) { + _vm->_hotspots[kHS18GarbageCan]._rect = Common::Rect(115, 365, 168, 470); + _vm->_hotspots[kHS18GarbageCan]._flags = SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR; + _vm->_hotspotsWalkPos[kHS18GarbageCan] = Common::Point(3, 7); + } + if (_vm->isFlag(kGFPlatypusDisguised)) + _vm->_hotspots[kHS18GarbageCan]._flags = SF_DISABLED; + if (_vm->isFlag(kGFPlatypusTalkingToAssistant)) { + _vm->_hotspots[kHS18Device]._flags = SF_DISABLED; + _vm->_hotspots[kHS18HydrantTopValve]._flags = SF_DISABLED; + _vm->_hotspots[kHS18HydrantRightValve]._flags = SF_DISABLED; + _vm->_hotspots[kHS18Platypus]._flags = SF_DISABLED; + } + if (_vm->isFlag(kGFUnk14)) { + _vm->_hotspots[kHS18HydrantTopValve]._flags = SF_DISABLED; + _vm->_hotspots[kHS18CowboyHat]._flags = SF_DISABLED; + } + _vm->_hotspotsCount = 11; +} + +void Scene18::gnapCarryGarbageCanTo(int gridX) { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + int gnapSeqId, gnapId, gnapDatNum, gnapGridX; + int destGridX, direction; + + int curGridX = (_vm->_leftClickMouseX - _vm->_gridMinX + 37) / 75; + + if (curGridX >= gnap._pos.x) + destGridX = curGridX - 1; + else + destGridX = curGridX + 1; + + if (gridX < 0) + gridX = 4; + + if (destGridX <= gridX) + destGridX = gridX; + + int nextGridX = _vm->_gridMaxX - 1; + if (nextGridX >= destGridX) + nextGridX = destGridX; + + if (nextGridX == gnap._pos.x) { + gnapSeqId = gnap._sequenceId; + gnapId = gnap._id; + gnapDatNum = gnap._sequenceDatNum; + gnapGridX = gnap._pos.x; + if (gnap._pos.x <= curGridX) + direction = 1; + else + direction = -1; + } else { + PlayerPlat& plat = *_vm->_plat; + if (gnap._pos.y == plat._pos.y) { + if (nextGridX >= gnap._pos.x) { + if (nextGridX >= plat._pos.x && gnap._pos.x <= plat._pos.x) + plat.makeRoom(); + } else if (nextGridX <= plat._pos.x && gnap._pos.x >= plat._pos.x) { + plat.makeRoom(); + } + } + gnapSeqId = gnap._sequenceId; + gnapId = gnap._id; + gnapDatNum = gnap._sequenceDatNum; + gnapGridX = gnap._pos.x; + int seqId; + if (nextGridX < gnap._pos.x) { + direction = -1; + seqId = 0x204; + } else { + direction = 1; + seqId = 0x203; + } + + int seqId2 = 20 * gnap._pos.y + 1; + do { + if (_vm->isPointBlocked(gnapGridX + direction, gnap._pos.y)) + break; + seqId2 += direction; + gameSys.insertSequence(seqId, seqId2, + gnapSeqId | (gnapDatNum << 16), gnapId, + kSeqSyncWait, 0, 75 * gnapGridX - gnap._gridX, 48 * gnap._pos.y - gnap._gridY); + gnapSeqId = seqId; + gnapId = seqId2; + gnapDatNum = 0; + gnapGridX += direction; + } while (nextGridX != gnapGridX); + } + + if (direction == 1) + gnap._sequenceId = 0x20A; + else + gnap._sequenceId = 0x209; + gnap._sequenceDatNum = 0; + + if (direction == 1) + gnap._idleFacing = kDirBottomRight; + else + gnap._idleFacing = kDirBottomLeft; + + gnap._id = 20 * gnap._pos.y + 1; + + gameSys.setAnimation(makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, 0); + gameSys.insertSequence(makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, + gnapSeqId | (gnapDatNum << 16), gnapId, + kSeqScale | kSeqSyncWait, 0, 75 * gnapGridX - gnap._gridX, 48 * gnap._pos.y - gnap._gridY); + + gnap._pos.x = gnapGridX; +} + +void Scene18::putDownGarbageCan(int animationIndex) { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (animationIndex >= 0) { + while (gameSys.getAnimationStatus(animationIndex) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + } + if (gnap._idleFacing != kDirIdleLeft && gnap._idleFacing != kDirBottomRight && gnap._idleFacing != kDirUpRight) + _vm->_s18GarbageCanPos = gnap._pos.x - 1; + else + _vm->_s18GarbageCanPos = gnap._pos.x + 1; + _vm->clearFlag(kGFPlatypusDisguised); + updateHotspots(); + if (gnap._idleFacing != kDirIdleLeft && gnap._idleFacing != kDirBottomRight && gnap._idleFacing != kDirUpRight) { + gameSys.insertSequence(0x107BA, gnap._id, + makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, + kSeqSyncWait, 0, 75 * gnap._pos.x - gnap._gridX, 48 * gnap._pos.y - gnap._gridY); + gnap._sequenceId = 0x7BA; + } else { + gameSys.insertSequence(0x107B9, gnap._id, + makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, + kSeqSyncWait, 0, 75 * gnap._pos.x - gnap._gridX, 48 * gnap._pos.y - gnap._gridY); + gnap._sequenceId = 0x7B9; + } + gnap._sequenceDatNum = 1; + gameSys.insertSequence(0x1FB, 19, 0, 0, kSeqNone, 0, 15 * (5 * _vm->_s18GarbageCanPos - 40), 0); + gameSys.setAnimation(0x1FA, 19, 4); + gameSys.insertSequence(0x1FA, 19, 507, 19, kSeqSyncWait, 0, 15 * (5 * _vm->_s18GarbageCanPos - 40), 0); + while (gameSys.getAnimationStatus(4) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); +} + +void Scene18::platEndPhoning(bool platFl) { + GameSys& gameSys = *_vm->_gameSys; + PlayerPlat& plat = *_vm->_plat; + + if (_vm->isFlag(kGFPlatypusTalkingToAssistant)) { + _platPhoneIter = 0; + _platPhoneCtr = 0; + plat._actionStatus = -1; + if (_currPhoneSequenceId != -1) { + gameSys.setAnimation(0x21E, 254, 3); + gameSys.insertSequence(0x21E, 254, _currPhoneSequenceId, 254, kSeqSyncExists, 0, 0, 0); + while (gameSys.getAnimationStatus(3) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + } + gameSys.removeSequence(0x21F, 254, true); + gameSys.setAnimation(0, 0, 3); + _vm->clearFlag(kGFPlatypusTalkingToAssistant); + if (platFl) { + plat._actionStatus = kAS18PlatComesHere; + _vm->_timers[6] = 50; + _vm->_sceneWaiting = true; + } + _currPhoneSequenceId = -1; + _nextPhoneSequenceId = -1; + updateHotspots(); + } +} + +void Scene18::closeHydrantValve() { + PlayerGnap& gnap = *_vm->_gnap; + + gnap._actionStatus = kAS18LeaveScene; + _vm->updateMouseCursor(); + if (_vm->isFlag(kGFTruckFilledWithGas)) { + gnap.walkTo(_vm->_hotspotsWalkPos[kHS18HydrantRightValve], 0, 0x107BA, 1); + if (_vm->isFlag(kGFTruckKeysUsed)) { + gnap._actionStatus = kAS18CloseRightValveWithGarbageCan; + waitForGnapAction(); + } else { + gnap._actionStatus = kAS18CloseRightValveNoGarbageCan; + waitForGnapAction(); + } + } else if (_vm->isFlag(kGFBarnPadlockOpen)) { + gnap.walkTo(_vm->_hotspotsWalkPos[kHS18HydrantTopValve], 0, 0x107BA, 1); + gnap._actionStatus = kAS18CloseTopValve; + waitForGnapAction(); + } +} + +void Scene18::waitForGnapAction() { + PlayerGnap& gnap = *_vm->_gnap; + + while (gnap._actionStatus >= 0 && !_vm->_gameDone) { + updateAnimations(); + _vm->gameUpdateTick(); + } +} + +void Scene18::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _cowboyHatSurface = nullptr; + + _vm->playSound(0x10940, true); + _vm->startSoundTimerA(4); + _vm->_timers[5] = _vm->getRandom(100) + 100; + _vm->queueInsertDeviceIcon(); + _vm->clearFlag(kGFPlatypusDisguised); + + if (!_vm->isFlag(kGFUnk14)) + gameSys.insertSequence(0x1F8, 19, 0, 0, kSeqNone, 0, 0, 0); + + if (_vm->isFlag(kGFTruckKeysUsed)) { + if (_vm->isFlag(kGFTruckFilledWithGas)) { + gameSys.insertSequence(0x214, 39, 0, 0, kSeqLoop, 0, 0, 0); + gameSys.insertSequence(0x20D, 39, 0, 0, kSeqLoop, 0, 0, 0); + _vm->playSound(0x22B, true); + } else { + gameSys.insertSequence(0x1F9, 19, 0, 0, kSeqNone, 0, 0, 0); + } + } else { + gameSys.insertSequence(0x1FA, 19, 0, 0, kSeqNone, 0, 15 * (5 * _vm->_s18GarbageCanPos - 40), 0); + if (_vm->isFlag(kGFTruckFilledWithGas)) { + gameSys.insertSequence(0x212, 39, 0, 0, kSeqLoop, 0, 0, 0); + gameSys.insertSequence(0x20D, 39, 0, 0, kSeqLoop, 0, 0, 0); + _vm->playSound(0x22B, true); + } else if (_vm->isFlag(kGFBarnPadlockOpen)) { + gameSys.insertSequence(0x20E, 39, 0, 0, kSeqLoop, 0, 0, 0); + gameSys.insertSequence(0x217, 39, 0, 0, kSeqLoop, 0, 0, 0); + _vm->playSound(0x22B, true); + } + } + + if (_vm->isFlag(kGFPlatypusTalkingToAssistant)) { + if (_vm->_prevSceneNum == 17) + gnap.initPos(4, 11, kDirBottomRight); + else + gnap.initPos(4, 7, kDirBottomRight); + _platPhoneCtr = _vm->getRandom(5); + if (_vm->isFlag(kGFUnk27)) { + gameSys.insertSequence(0x21E, 254, 0, 0, kSeqNone, 0, 0, 0); + _vm->endSceneInit(); + _currPhoneSequenceId = -1; + platEndPhoning(true); + _vm->clearFlag(kGFUnk27); + } else { + _currPhoneSequenceId = kScene18SequenceIds[_platPhoneCtr]; + _platPhoneIter = 0; + gameSys.insertSequence(0x21F, 254, 0, 0, kSeqNone, 0, 0, 0); + gameSys.insertSequence(_currPhoneSequenceId, 254, 0, 0, kSeqNone, 0, 0, 0); + _vm->endSceneInit(); + } + if (_vm->isFlag(kGFUnk27)) { + platEndPhoning(true); + _vm->clearFlag(kGFUnk27); + } else { + gameSys.setAnimation(_currPhoneSequenceId, 254, 3); + } + gnap.walkTo(Common::Point(4, 8), -1, 0x107B9, 1); + } else { + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + _vm->clearFlag(kGFGnapControlsToyUFO); + _vm->setGrabCursorSprite(kItemCowboyHat); + _vm->_prevSceneNum = 19; + } + if (_vm->_prevSceneNum == 17) { + gnap.initPos(4, 11, kDirBottomRight); + plat.initPos(5, 11, kDirIdleLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(4, 8), -1, 0x107B9, 1); + plat.walkTo(Common::Point(5, 9), -1, 0x107C2, 1); + } else if (_vm->_prevSceneNum == 19) { + gnap.initPos(7, 7, kDirBottomRight); + plat.initPos(8, 7, kDirIdleLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(7, 8), -1, 0x107B9, 1); + plat.walkTo(Common::Point(8, 8), -1, 0x107C2, 1); + } else { + gnap.initPos(-1, 10, kDirBottomRight); + plat.initPos(-1, 10, kDirIdleLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(3, 7), -1, 0x107B9, 1); + plat.walkTo(Common::Point(3, 8), -1, 0x107C2, 1); + } + } + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->testWalk(0, 20, -1, -1, -1, -1); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS18Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS18Platypus: + if (gnap._actionStatus < 0) { + if (_vm->isFlag(kGFPlatypusDisguised)) { + gnapCarryGarbageCanTo(-1); + putDownGarbageCan(0); + } + if (_vm->_grabCursorSpriteIndex == kItemJoint) { + gnap.useJointOnPlatypus(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowItem(_vm->_grabCursorSpriteIndex, plat._pos.x, plat._pos.y); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS18CowboyHat: + if (gnap._actionStatus == kAS18StandingOnHydrant) { + gnap._actionStatus = kAS18GrabCowboyHat; + _vm->_sceneWaiting = false; + } else if (gnap._actionStatus < 0) { + if (_vm->isFlag(kGFPlatypusDisguised)) { + gnapCarryGarbageCanTo(-1); + putDownGarbageCan(0); + } + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS18CowboyHat], 3, 2); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(3, 2)); + break; + case GRAB_CURSOR: + gnap.walkTo(_vm->_hotspotsWalkPos[kHS18CowboyHat], 0, gnap.getSequenceId(kGSPullOutDeviceNonWorking, Common::Point(3, 2)) | 0x10000, 1); + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS18GarbageCan: + if (gnap._actionStatus < 0) { + if (_vm->isFlag(kGFUnk14)) { + if (_vm->_grabCursorSpriteIndex >= 0) + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS18GarbageCan], 1, 5); + else + gnap.playImpossible(); + } else { + if (_vm->isFlag(kGFPlatypusTalkingToAssistant)) + platEndPhoning(true); + if (_vm->_grabCursorSpriteIndex >= 0) { + if (!_vm->isFlag(kGFTruckKeysUsed)) { + Common::Point destPos; + destPos.x = _vm->_hotspotsWalkPos[kHS18GarbageCan].x - (gnap._pos.x < _vm->_s18GarbageCanPos ? 1 : -1); + destPos.y = _vm->_hotspotsWalkPos[kHS18GarbageCan].y; + gnap.playShowCurrItem(destPos, _vm->_hotspotsWalkPos[kHS18GarbageCan].x, _vm->_hotspotsWalkPos[kHS18GarbageCan].y); + } else + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS18GarbageCan], 2, 4); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + if (!_vm->isFlag(kGFTruckKeysUsed)) + gnap.playScratchingHead(Common::Point(_vm->_hotspotsWalkPos[kHS18GarbageCan].x - (gnap._pos.x < _vm->_s18GarbageCanPos ? 1 : -1), _vm->_hotspotsWalkPos[kHS18GarbageCan].y)); + else if (!_vm->isFlag(kGFTruckFilledWithGas)) + gnap.playScratchingHead(Common::Point(2, 4)); + break; + case GRAB_CURSOR: + if (!_vm->isFlag(kGFTruckKeysUsed)) { + gnap.walkTo(_vm->_hotspotsWalkPos[kHS18GarbageCan] + Common::Point((gnap._pos.x < _vm->_s18GarbageCanPos ? 1 : -1), 0), + -1, -1, 1); + gnap.walkTo(gnap._pos, 0, gnap.getSequenceId(kGSIdle, Common::Point(_vm->_s18GarbageCanPos, gnap._pos.y)) | 0x10000, 1); + gnap._actionStatus = kAS18GrabGarbageCanFromStreet; + } else if (!_vm->isFlag(kGFTruckFilledWithGas)) { + if (gnap.walkTo(_vm->_hotspotsWalkPos[kHS18GarbageCan], 0, -1, 1)) + gnap._actionStatus = kAS18GrabGarbageCanFromHydrant; + } + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + } + break; + + case kHS18HydrantTopValve: + if (gnap._actionStatus < 0) { + if (_vm->isFlag(kGFPlatypusDisguised)) { + // While carrying garbage can + if (_vm->_grabCursorSpriteIndex >= 0) { + gnapCarryGarbageCanTo(-1); + putDownGarbageCan(0); + gnap.playShowItem(_vm->_grabCursorSpriteIndex, 0, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnapCarryGarbageCanTo(-1); + putDownGarbageCan(0); + gnap.playScratchingHead(); + break; + case GRAB_CURSOR: + if (_vm->isFlag(kGFTruckFilledWithGas)) { + gnapCarryGarbageCanTo(2); + gnap._actionStatus = kAS18PutGarbageCanOnRunningHydrant; + } else if (!_vm->isFlag(kGFBarnPadlockOpen)) { + gnapCarryGarbageCanTo(2); + gnap._actionStatus = kAS18PutGarbageCanOnHydrant; + } else { + gnapCarryGarbageCanTo(-1); + putDownGarbageCan(0); + gnap.playImpossible(); + } + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnapCarryGarbageCanTo(-1); + putDownGarbageCan(0); + gnap.playImpossible(); + break; + } + } + } else { + if (_vm->_grabCursorSpriteIndex == kItemWrench) { + gnap.walkTo(gnap._pos, 0, gnap.getSequenceId(kGSIdle, Common::Point(2, 8)) | 0x10000, 1); + gnap._actionStatus = kAS18OpenTopValve; + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS18HydrantTopValve], 1, 5); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(1, 5)); + break; + case GRAB_CURSOR: + if (_vm->isFlag(kGFBarnPadlockOpen)) { + _vm->_hotspots[kHS18WalkArea2]._flags |= SF_WALKABLE; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS18HydrantTopValve], 0, 0x107BA, 1); + _vm->_hotspots[kHS18WalkArea2]._flags &= ~SF_WALKABLE; + gnap._actionStatus = kAS18CloseTopValve; + } else + gnap.playImpossible(); + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + } + break; + + case kHS18HydrantRightValve: + if (gnap._actionStatus < 0) { + if (_vm->isFlag(kGFUnk14)) { + if (_vm->_grabCursorSpriteIndex == -1) { + gnap.playImpossible(); + } else { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS18HydrantRightValve], 1, 5); + } + } else { + if (_vm->isFlag(kGFPlatypusDisguised)) { + gnapCarryGarbageCanTo(-1); + putDownGarbageCan(0); + } + if (_vm->_grabCursorSpriteIndex == kItemWrench) { + gnap.walkTo(gnap._pos, 0, gnap.getSequenceId(kGSIdle, Common::Point(2, 8)) | 0x10000, 1); + if (_vm->isFlag(kGFTruckKeysUsed)) + gnap._actionStatus = kAS18OpenRightValveWithGarbageCan; + else + gnap._actionStatus = kAS18OpenRightValveNoGarbageCan; + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS18HydrantRightValve], 1, 5); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(1, 5)); + break; + case GRAB_CURSOR: + if (_vm->isFlag(kGFTruckFilledWithGas)) { + gnap.walkTo(_vm->_hotspotsWalkPos[kHS18HydrantRightValve], 0, 0x107BA, 1); + if (_vm->isFlag(kGFTruckKeysUsed)) + gnap._actionStatus = kAS18CloseRightValveWithGarbageCan; + else + gnap._actionStatus = kAS18CloseRightValveNoGarbageCan; + } + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + } + break; + + case kHS18ExitToyStore: + if (gnap._actionStatus < 0) { + if (_vm->isFlag(kGFPlatypusDisguised)) { + gnapCarryGarbageCanTo(-1); + putDownGarbageCan(0); + } + if (_vm->isFlag(kGFPictureTaken)) { + gnap.playImpossible(); + } else { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 19; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS18ExitToyStore], 0, 0x107C0, 1); + gnap._actionStatus = kAS18LeaveScene; + if (!_vm->isFlag(kGFPlatypusTalkingToAssistant)) + plat.walkTo(_vm->_hotspotsWalkPos[kHS18ExitToyStore] + Common::Point(1, 0), -1, 0x107C2, 1); + } + } + break; + + case kHS18ExitPhoneBooth: + if (gnap._actionStatus < 0) { + if (_vm->isFlag(kGFPlatypusDisguised)) { + gnapCarryGarbageCanTo(-1); + putDownGarbageCan(0); + } + closeHydrantValve(); + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 17; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS18ExitPhoneBooth], 0, 0x107AE, 1); + gnap._actionStatus = kAS18LeaveScene; + if (_vm->isFlag(kGFPlatypusTalkingToAssistant)) + _vm->setFlag(kGFUnk27); + else + plat.walkTo(_vm->_hotspotsWalkPos[kHS18ExitPhoneBooth] + Common::Point(1, 0), -1, 0x107C2, 1); + } + break; + + case kHS18ExitGrubCity: + if (gnap._actionStatus < 0) { + if (_vm->isFlag(kGFPlatypusDisguised)) { + gnapCarryGarbageCanTo(-1); + putDownGarbageCan(0); + } + closeHydrantValve(); + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 20; + _vm->_hotspots[kHS18WalkArea2]._flags |= SF_WALKABLE; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS18ExitGrubCity], 0, 0x107B2, 1); + gnap._actionStatus = kAS18LeaveScene; + if (_vm->isFlag(kGFPlatypusTalkingToAssistant)) + platEndPhoning(false); + else + plat.walkTo(_vm->_hotspotsWalkPos[kHS18ExitGrubCity] + Common::Point(0, -1), -1, 0x107CF, 1); + _vm->_hotspots[kHS18WalkArea2]._flags &= ~SF_WALKABLE; + } + break; + + case kHS18WalkArea1: + case kHS18WalkArea2: + if (gnap._actionStatus < 0) { + if (_vm->isFlag(kGFPlatypusDisguised)) { + gnapCarryGarbageCanTo(-1); + putDownGarbageCan(0); + } else { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + } + _vm->_mouseClickState._left = false; + } + break; + + default: + if (gnap._actionStatus != kAS18StandingOnHydrant && _vm->_mouseClickState._left) { + if (_vm->isFlag(kGFPlatypusDisguised)) { + gnapCarryGarbageCanTo(-1); + putDownGarbageCan(0); + } else { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + } + _vm->_mouseClickState._left = false; + } + break; + } + + updateAnimations(); + + if (!_vm->isSoundPlaying(0x10940)) + _vm->playSound(0x10940, true); + + if ((_vm->isFlag(kGFTruckFilledWithGas) || _vm->isFlag(kGFBarnPadlockOpen)) && !_vm->isSoundPlaying(0x22B) && + gnap._actionStatus != kAS18OpenRightValveNoGarbageCanDone && gnap._actionStatus != kAS18OpenRightValveNoGarbageCan && + gnap._actionStatus != kAS18OpenTopValve && gnap._actionStatus != kAS18OpenTopValveDone && + gnap._actionStatus != kAS18OpenRightValveWithGarbageCan && gnap._actionStatus != kAS18OpenRightValveWithGarbageCanDone) + _vm->playSound(0x22B, true); + + if (!_vm->_isLeavingScene) { + if (!_vm->isFlag(kGFPlatypusTalkingToAssistant)) { + if (plat._actionStatus == kAS18PlatComesHere) { + if (!_vm->_timers[6]) { + plat._actionStatus = -1; + _vm->_sceneWaiting = false; + plat.initPos(-1, 10, kDirIdleLeft); + plat.walkTo(Common::Point(3, 9), -1, 0x107C2, 1); + _vm->clearFlag(kGFPlatypusTalkingToAssistant); + } + } else { + _vm->_hotspots[kHS18WalkArea1]._rect.bottom += 48; + _vm->_hotspots[kHS18WalkArea2]._rect.left += 75; + plat.updateIdleSequence(); + _vm->_hotspots[kHS18WalkArea2]._rect.left -= 75; + _vm->_hotspots[kHS18WalkArea1]._rect.bottom -= 48; + } + if (!_vm->_timers[5]) { + _vm->_timers[5] = _vm->getRandom(100) + 100; + if (gnap._actionStatus < 0) { + if (_vm->getRandom(2) == 1) + gameSys.insertSequence(0x220, 255, 0, 0, kSeqNone, 0, 0, 0); + else + gameSys.insertSequence(0x221, 255, 0, 0, kSeqNone, 0, 0, 0); + } + } + _vm->playSoundA(); + } + if (!_vm->isFlag(kGFPlatypusDisguised)) + gnap.updateIdleSequence(); + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } + + if (_vm->isFlag(kGFGnapControlsToyUFO)) + _vm->deleteSurface(&_cowboyHatSurface); +} + +void Scene18::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS18GrabGarbageCanFromStreet: + if (gnap._idleFacing != kDirUpRight && gnap._idleFacing != kDirBottomRight) { + gameSys.insertSequence(0x1FC, gnap._id, + makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, + kSeqSyncWait, 0, 75 * gnap._pos.x - 675, 0); + gnap._sequenceDatNum = 0; + gnap._sequenceId = 0x1FC; + } else { + gameSys.insertSequence(0x1FD, gnap._id, + makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, + kSeqSyncWait, 0, 75 * gnap._pos.x - 525, 0); + gnap._sequenceDatNum = 0; + gnap._sequenceId = 0x1FD; + } + gameSys.removeSequence(0x1FA, 19, true); + _vm->setFlag(kGFPlatypusDisguised); + updateHotspots(); + gnap._actionStatus = -1; + break; + case kAS18GrabGarbageCanFromHydrant: + gameSys.insertSequence(0x1FE, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.removeSequence(0x1F9, 19, true); + gnap._sequenceDatNum = 0; + gnap._sequenceId = 0x1FE; + _vm->clearFlag(kGFTruckKeysUsed); + _vm->setFlag(kGFPlatypusDisguised); + updateHotspots(); + gnap._actionStatus = -1; + break; + case kAS18CloseRightValveNoGarbageCan: + gameSys.insertSequence(0x205, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.removeSequence(0x20D, 39, true); + gameSys.removeSequence(0x212, 39, true); + gameSys.removeSequence(0x211, 39, true); + _vm->stopSound(0x22B); + gnap._sequenceDatNum = 0; + gnap._sequenceId = 0x205; + _vm->clearFlag(kGFTruckFilledWithGas); + _vm->invAdd(kItemWrench); + _vm->setGrabCursorSprite(kItemWrench); + updateHotspots(); + gnap._actionStatus = -1; + break; + case kAS18OpenTopValve: + _vm->setFlag(kGFBarnPadlockOpen); + updateHotspots(); + gnap.playPullOutDevice(Common::Point(2, 7)); + gnap.playUseDevice(); + gameSys.insertSequence(0x20C, 19, 0, 0, kSeqNone, 0, 0, 0); + _vm->_hotspots[kHS18WalkArea2]._flags |= SF_WALKABLE; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS18HydrantTopValve], 0, 0x107BB, 1); + _vm->_hotspots[kHS18WalkArea2]._flags &= ~SF_WALKABLE; + gnap._actionStatus = kAS18OpenTopValveDone; + break; + case kAS18OpenTopValveDone: + _vm->setGrabCursorSprite(-1); + gameSys.insertSequence(0x208, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x216, 39, 0, 0, kSeqNone, 21, 0, 0); + gameSys.removeSequence(0x20C, 19, true); + gameSys.setAnimation(0x217, 39, 5); + gameSys.insertSequence(0x217, 39, 0x216, 39, kSeqLoop | kSeqSyncWait, 0, 0, 0); + while (gameSys.getAnimationStatus(5) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + _vm->playSound(0x22B, true); + gameSys.insertSequence(0x20E, 39, 0, 0, kSeqNone, 0, 0, 0); + gnap._sequenceDatNum = 0; + gnap._sequenceId = 0x208; + _vm->invRemove(kItemWrench); + _vm->setGrabCursorSprite(-1); + gnap._actionStatus = -1; + break; + case kAS18CloseTopValve: + gameSys.insertSequence(0x206, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.removeSequence(0x20E, 39, true); + gameSys.removeSequence(0x216, 39, true); + gameSys.removeSequence(0x217, 39, true); + _vm->stopSound(0x22B); + gnap._sequenceDatNum = 0; + gnap._sequenceId = 0x206; + _vm->clearFlag(kGFBarnPadlockOpen); + _vm->invAdd(kItemWrench); + _vm->setGrabCursorSprite(kItemWrench); + updateHotspots(); + gnap._actionStatus = -1; + break; + case kAS18GrabCowboyHat: + gameSys.setAnimation(0x200, gnap._id, 0); + gameSys.insertSequence(0x200, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceDatNum = 0; + gnap._sequenceId = 0x200; + gnap._actionStatus = kAS18GrabCowboyHatDone; + break; + case kAS18GrabCowboyHatDone: + _vm->hideCursor(); + _vm->setGrabCursorSprite(-1); + _cowboyHatSurface = _vm->addFullScreenSprite(0x1D2, 255); + gameSys.setAnimation(0x218, 256, 0); + gameSys.insertSequence(0x218, 256, 0, 0, kSeqNone, 0, 0, 0); + while (gameSys.getAnimationStatus(0) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + _vm->_newSceneNum = 18; + _vm->invAdd(kItemCowboyHat); + _vm->invAdd(kItemWrench); + _vm->setFlag(kGFGnapControlsToyUFO); + _vm->setFlag(kGFUnk14); + _vm->clearFlag(kGFTruckFilledWithGas); + _vm->setFlag(kGFTruckKeysUsed); + _vm->setFlag(kGFUnk14); // Useless, already set + updateHotspots(); + gnap._actionStatus = kAS18LeaveScene; + break; + case kAS18LeaveScene: + _vm->_sceneDone = true; + gnap._actionStatus = -1; + break; + case kAS18PutGarbageCanOnRunningHydrant: + _vm->setFlag(kGFTruckKeysUsed); + _vm->clearFlag(kGFPlatypusDisguised); + gameSys.requestRemoveSequence(0x211, 39); + gameSys.requestRemoveSequence(0x212, 39); + gameSys.insertSequence(0x210, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + _vm->stopSound(0x22B); + gameSys.setAnimation(0x210, gnap._id, 0); + gnap._sequenceDatNum = 0; + gnap._sequenceId = 0x210; + gnap._actionStatus = kAS18PutGarbageCanOnRunningHydrant2; + break; + case kAS18PutGarbageCanOnRunningHydrant2: + _vm->playSound(0x22B, true); + gameSys.setAnimation(0x1FF, gnap._id, 0); + gameSys.insertSequence(0x1FF, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceDatNum = 0; + gnap._sequenceId = 0x1FF; + _vm->_sceneWaiting = true; + gnap._actionStatus = kAS18StandingOnHydrant; + break; + case kAS18StandingOnHydrant: + gameSys.setAnimation(0x1FF, gnap._id, 0); + gameSys.insertSequence(0x1FF, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + break; + case kAS18OpenRightValveNoGarbageCan: + case kAS18OpenRightValveWithGarbageCan: + _vm->setFlag(kGFTruckFilledWithGas); + updateHotspots(); + gnap.playPullOutDevice(Common::Point(2, 7)); + gnap.playUseDevice(); + gameSys.insertSequence(0x20B, 19, 0, 0, kSeqNone, 0, 0, 0); + _vm->_hotspots[kHS18WalkArea2]._flags |= SF_WALKABLE; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS18HydrantRightValve], 0, 0x107BA, 1); + _vm->_hotspots[kHS18WalkArea2]._flags &= ~SF_WALKABLE; + if (gnap._actionStatus == kAS18OpenRightValveNoGarbageCan) + gnap._actionStatus = kAS18OpenRightValveNoGarbageCanDone; + else + gnap._actionStatus = kAS18OpenRightValveWithGarbageCanDone; + break; + case kAS18OpenRightValveWithGarbageCanDone: + _vm->setGrabCursorSprite(-1); + gameSys.insertSequence(0x207, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x213, 39, 0, 0, kSeqNone, 21, 0, 0); + gameSys.requestRemoveSequence(0x1F9, 19); + gameSys.removeSequence(0x20B, 19, true); + gameSys.setAnimation(0x213, 39, 5); + gameSys.insertSequence(0x214, 39, 0x213, 39, kSeqLoop | kSeqSyncWait, 0, 0, 0); + while (gameSys.getAnimationStatus(5) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + _vm->playSound(0x22B, true); + gameSys.insertSequence(0x20D, 39, 0, 0, kSeqNone, 0, 0, 0); + gnap._sequenceDatNum = 0; + gnap._sequenceId = 0x207; + _vm->invRemove(kItemWrench); + gnap._actionStatus = -1; + break; + case kAS18OpenRightValveNoGarbageCanDone: + _vm->setGrabCursorSprite(-1); + gameSys.insertSequence(0x207, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x211, 39, 0, 0, kSeqNone, 21, 0, 0); + gameSys.removeSequence(0x20B, 19, true); + gameSys.setAnimation(0x211, 39, 5); + gameSys.insertSequence(0x212, 39, 0x211, 39, kSeqLoop | kSeqSyncWait, 0, 0, 0); + while (gameSys.getAnimationStatus(5) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + _vm->playSound(0x22B, true); + gameSys.insertSequence(0x20D, 39, 0, 0, kSeqNone, 0, 0, 0); + gnap._sequenceDatNum = 0; + gnap._sequenceId = 0x207; + _vm->invRemove(kItemWrench); + gnap._actionStatus = -1; + break; + case kAS18CloseRightValveWithGarbageCan: + gameSys.insertSequence(0x205, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.removeSequence(0x20D, 39, true); + gameSys.insertSequence(0x215, 39, 0x214, 39, kSeqSyncWait, 0, 0, 0); + _vm->stopSound(0x22B); + gameSys.setAnimation(0x1F9, 19, 0); + gameSys.insertSequence(0x1F9, 19, 0x215, 39, kSeqSyncWait, 0, 0, 0); + _vm->clearFlag(kGFTruckFilledWithGas); + _vm->invAdd(kItemWrench); + _vm->setGrabCursorSprite(kItemWrench); + gameSys.insertSequence(0x107B5, gnap._id, 517, gnap._id, kSeqSyncWait, 0, 75 * gnap._pos.x - gnap._gridX, 48 * gnap._pos.y - gnap._gridY); + updateHotspots(); + gnap._sequenceDatNum = 1; + gnap._sequenceId = 0x7B5; + gnap._actionStatus = kAS18CloseRightValveWithGarbageCanDone; + break; + case kAS18CloseRightValveWithGarbageCanDone: + gnap._actionStatus = -1; + break; + case kAS18PutGarbageCanOnHydrant: + _vm->setFlag(kGFTruckKeysUsed); + _vm->clearFlag(kGFPlatypusDisguised); + gameSys.insertSequence(0x20F, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(0x20F, gnap._id, 0); + gnap._sequenceDatNum = 0; + gnap._sequenceId = 0x20F; + gnap._actionStatus = kAS18PutGarbageCanOnHydrantDone; + break; + case kAS18PutGarbageCanOnHydrantDone: + gameSys.insertSequence(0x1F9, 19, 0x20F, gnap._id, kSeqNone, 0, 0, 0); + updateHotspots(); + gnap._actionStatus = -1; + break; + } + } + + if (gameSys.getAnimationStatus(3) == 2) { + gameSys.setAnimation(0, 0, 3); + ++_platPhoneIter; + if (_platPhoneIter <= 4) { + ++_platPhoneCtr; + _nextPhoneSequenceId = kScene18SequenceIds[_platPhoneCtr % 5]; + gameSys.setAnimation(_nextPhoneSequenceId, 254, 3); + gameSys.insertSequence(_nextPhoneSequenceId, 254, _currPhoneSequenceId, 254, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x21F, 254, 0x21F, 254, kSeqSyncWait, 0, 0, 0); + _currPhoneSequenceId = _nextPhoneSequenceId; + } else { + platEndPhoning(true); + } + } +} + +/*****************************************************************************/ + +static const int kS19ShopAssistantSequenceIds[] = { + 0x6F, 0x70, 0x71, 0x72, 0x73 +}; + +Scene19::Scene19(GnapEngine *vm) : Scene(vm) { + _toyGrabCtr = 0; + _shopAssistantCtr = 0; + _currShopAssistantSequenceId = -1; + _nextShopAssistantSequenceId = -1; + + _pictureSurface = nullptr; +} + +Scene19::~Scene19() { + delete _pictureSurface; +} + +int Scene19::init() { + _vm->playSound(0x79, false); + return _vm->isFlag(kGFPlatypusTalkingToAssistant) ? 0x77 : 0x76; +} + +void Scene19::updateHotspots() { + _vm->setHotspot(kHS19Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS19ExitOutsideToyStore, 36, 154, 142, 338, SF_EXIT_NW_CURSOR, 4, 6); + _vm->setHotspot(kHS19Picture, 471, 237, 525, 283, SF_DISABLED, 7, 2); + _vm->setHotspot(kHS19ShopAssistant, 411, 151, 575, 279, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 5, 7); + _vm->setHotspot(kHS19Phone, 647, 166, 693, 234, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 9, 0); + _vm->setHotspot(kHS19Toy1, 181, 11, 319, 149, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 3, 0); + _vm->setHotspot(kHS19Toy2, 284, 85, 611, 216, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 6, 0); + _vm->setHotspot(kHS19Toy3, 666, 38, 755, 154, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 9, 0); + _vm->setHotspot(kHS19Toy4, 154, 206, 285, 327, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 3, 3); + _vm->setHotspot(kHS19Toy5, 494, 301, 570, 448, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 7, 5); + _vm->setHotspot(kHS19Toy6, 0, 320, 188, 600, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 1, 6); + _vm->setHotspot(kHS19Toy7, 597, 434, 800, 600, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 9, 8); + _vm->setHotspot(kHS19WalkArea1, 0, 0, 170, 600); + _vm->setHotspot(kHS19WalkArea2, 622, 0, 800, 600); + _vm->setHotspot(kHS19WalkArea3, 0, 0, 800, 437); + _vm->setDeviceHotspot(kHS19Device, -1, -1, -1, -1); + if (_vm->isFlag(kGFPlatypusTalkingToAssistant)) { + _vm->_hotspots[kHS19Toy1]._flags = SF_DISABLED; + _vm->_hotspots[kHS19Toy2]._flags = SF_DISABLED; + _vm->_hotspots[kHS19Toy3]._flags = SF_DISABLED; + _vm->_hotspots[kHS19Toy4]._flags = SF_DISABLED; + _vm->_hotspots[kHS19Toy5]._flags = SF_DISABLED; + _vm->_hotspots[kHS19Toy6]._flags = SF_DISABLED; + _vm->_hotspots[kHS19Toy7]._flags = SF_DISABLED; + _vm->_hotspots[kHS19ShopAssistant]._flags = SF_DISABLED; + _vm->_hotspots[kHS19Phone]._flags = SF_DISABLED; + _vm->_hotspots[kHS19Platypus]._flags = SF_DISABLED; + _vm->_hotspots[kHS19Picture]._flags = SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR; + } + _vm->_hotspotsCount = 16; +} + +void Scene19::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->queueInsertDeviceIcon(); + _toyGrabCtr = 0; + _pictureSurface = nullptr; + + gameSys.insertSequence(0x74, 254, 0, 0, kSeqNone, 0, 0, 0); + gameSys.insertSequence(0x75, 254, 0, 0, kSeqNone, 0, 0, 0); + + if (!_vm->isFlag(kGFPictureTaken)) + gameSys.insertSequence(0x69, 19, 0, 0, kSeqNone, 0, 0, 0); + + if (_vm->isFlag(kGFPlatypusTalkingToAssistant)) { + gnap.initPos(3, 6, kDirBottomRight); + _currShopAssistantSequenceId = kS19ShopAssistantSequenceIds[_vm->getRandom(5)]; + _nextShopAssistantSequenceId = _currShopAssistantSequenceId; + gameSys.setAnimation(_currShopAssistantSequenceId, 20, 4); + gameSys.insertSequence(0x6E, 254, 0, 0, kSeqNone, 0, 0, 0); + gameSys.insertSequence(_currShopAssistantSequenceId, 20, 0, 0, kSeqNone, 0, 0, 0); + _shopAssistantCtr = 0; + _vm->endSceneInit(); + gnap.walkTo(Common::Point(4, 9), -1, 0x107B9, 1); + updateHotspots(); + } else { + _currShopAssistantSequenceId = 0x6D; + _nextShopAssistantSequenceId = -1; + gameSys.setAnimation(0x6D, 20, 4); + gameSys.insertSequence(_currShopAssistantSequenceId, 20, 0, 0, kSeqNone, 0, 0, 0); + _vm->_timers[6] = _vm->getRandom(40) + 50; + gnap.initPos(3, 6, kDirBottomRight); + plat.initPos(4, 6, kDirIdleLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(4, 9), -1, 0x107B9, 1); + plat.walkTo(Common::Point(5, 9), -1, 0x107C2, 1); + } + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->testWalk(0, 5, -1, -1, -1, -1); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS19Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS19Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemJoint) { + gnap.useJointOnPlatypus(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(plat._pos); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS19ExitOutsideToyStore: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 18; + _vm->_hotspots[kHS19WalkArea1]._flags |= SF_WALKABLE; + gnap.walkTo(_vm->_hotspotsWalkPos[1], 0, 0x107B2, 1); + gnap._actionStatus = kAS19LeaveScene; + if (_vm->isFlag(kGFPlatypusTalkingToAssistant)) + _vm->setFlag(kGFUnk27); + else + plat.walkTo(_vm->_hotspotsWalkPos[1] + Common::Point(1, 0), -1, 0x107C5, 1); + _vm->_hotspots[kHS19WalkArea1]._flags &= ~SF_WALKABLE; + } + break; + + case kHS19Picture: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[_vm->_sceneClickedHotspot], 6, 2); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(6, 2)); + break; + case GRAB_CURSOR: + if (!_vm->isFlag(kGFPictureTaken)) { + gnap.walkTo(gnap._pos, 0, gnap.getSequenceId(kGSIdle, _vm->_hotspotsWalkPos[_vm->_sceneClickedHotspot]) | 0x10000, 1); + gnap._actionStatus = kAS19GrabPicture; + } + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS19ShopAssistant: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[_vm->_sceneClickedHotspot], 6, 2); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(6, 2)); + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.walkTo(_vm->_hotspotsWalkPos[_vm->_sceneClickedHotspot], 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS19TalkShopAssistant; + break; + case GRAB_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS19Toy1: + case kHS19Toy2: + case kHS19Toy3: + case kHS19Toy4: + case kHS19Toy5: + case kHS19Toy6: + case kHS19Toy7: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(_vm->_hotspotsWalkPos[_vm->_sceneClickedHotspot]); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan2(_vm->_hotspotsWalkPos[_vm->_sceneClickedHotspot]); + break; + case GRAB_CURSOR: + gnap.walkTo(_vm->_hotspotsWalkPos[_vm->_sceneClickedHotspot], 0, -1, 1); + gnap.playIdle(_vm->_hotspotsWalkPos[_vm->_sceneClickedHotspot]); + gnap._actionStatus = kAS19GrabToy; + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS19Phone: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[_vm->_sceneClickedHotspot], 9, 1); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(9, 1)); + break; + case GRAB_CURSOR: + gnap.walkTo(_vm->_hotspotsWalkPos[_vm->_sceneClickedHotspot], 0, -1, 1); + gnap.playIdle(Common::Point(8, 2)); + gnap._actionStatus = kAS19UsePhone; + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS19WalkArea1: + case kHS19WalkArea2: + case kHS19WalkArea3: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = 0; + } + } + + updateAnimations(); + + if (!_vm->_isLeavingScene) { + gnap.updateIdleSequence(); + if (!_vm->isFlag(kGFPlatypusTalkingToAssistant)) { + plat.updateIdleSequence(); + if (!_vm->_timers[6] && _nextShopAssistantSequenceId == -1) { + _vm->_timers[6] = _vm->getRandom(40) + 50; + if (_vm->getRandom(4) != 0) { + _nextShopAssistantSequenceId = 0x64; + } else if (_vm->isFlag(kGFPictureTaken)) { + _nextShopAssistantSequenceId = 0x64; + } else { + _nextShopAssistantSequenceId = 0x6C; + } + } + } + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } + + if (_pictureSurface) + _vm->deleteSurface(&_pictureSurface); +} + +void Scene19::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS19UsePhone: + _nextShopAssistantSequenceId = 0x67; + break; + case kAS19GrabToy: + ++_toyGrabCtr; + switch (_toyGrabCtr) { + case 1: + _nextShopAssistantSequenceId = 0x62; + break; + case 2: + _nextShopAssistantSequenceId = 0x6B; + break; + case 3: + _nextShopAssistantSequenceId = 0x66; + break; + default: + _nextShopAssistantSequenceId = 0x65; + break; + } + break; + case kAS19GrabPicture: + gnap.playPullOutDevice(Common::Point(6, 2)); + gnap.playUseDevice(); + gameSys.setAnimation(0x68, 19, 0); + gameSys.insertSequence(0x68, 19, 105, 19, kSeqSyncWait, 0, 0, 0); + _vm->invAdd(kItemPicture); + _vm->setFlag(kGFPictureTaken); + updateHotspots(); + gnap._actionStatus = kAS19GrabPictureDone; + break; + case kAS19GrabPictureDone: + _vm->setGrabCursorSprite(-1); + _vm->hideCursor(); + _pictureSurface = _vm->addFullScreenSprite(0xF, 255); + gameSys.setAnimation(0x61, 256, 0); + gameSys.insertSequence(0x61, 256, 0, 0, kSeqNone, 0, 0, 0); + while (gameSys.getAnimationStatus(0) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + + _vm->setFlag(kGFUnk27); + _vm->showCursor(); + _vm->_newSceneNum = 17; + _vm->_isLeavingScene = true; + _vm->_sceneDone = true; + _nextShopAssistantSequenceId = -1; + break; + case kAS19TalkShopAssistant: + _nextShopAssistantSequenceId = 0x6D; + gnap._actionStatus = -1; + break; + case kAS19LeaveScene: + _vm->_sceneDone = true; + break; + } + } + + if (gameSys.getAnimationStatus(4) == 2) { + switch (_nextShopAssistantSequenceId) { + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + _shopAssistantCtr = (_shopAssistantCtr + 1) % 5; + _nextShopAssistantSequenceId = kS19ShopAssistantSequenceIds[_shopAssistantCtr]; + gameSys.setAnimation(_nextShopAssistantSequenceId, 20, 4); + gameSys.insertSequence(_nextShopAssistantSequenceId, 20, _currShopAssistantSequenceId, 20, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x6E, 254, 0x6E, 254, kSeqSyncWait, 0, 0, 0); + _currShopAssistantSequenceId = _nextShopAssistantSequenceId; + break; + case 0x62: + case 0x66: + case 0x6B: + gameSys.setAnimation(_nextShopAssistantSequenceId, 20, 4); + gameSys.insertSequence(_nextShopAssistantSequenceId, 20, _currShopAssistantSequenceId, 20, kSeqSyncWait, 0, 0, 0); + _currShopAssistantSequenceId = _nextShopAssistantSequenceId; + _nextShopAssistantSequenceId = -1; + _vm->_timers[5] = 10; + while (_vm->_timers[5] && !_vm->_gameDone) + _vm->gameUpdateTick(); + + gnap.playIdle(Common::Point(6, 2)); + gnap._actionStatus = -1; + break; + case 0x67: + gameSys.setAnimation(_nextShopAssistantSequenceId, 20, 4); + gameSys.insertSequence(_nextShopAssistantSequenceId, 20, _currShopAssistantSequenceId, 20, kSeqSyncWait, 0, 0, 0); + _currShopAssistantSequenceId = _nextShopAssistantSequenceId; + _nextShopAssistantSequenceId = -1; + gnap._actionStatus = -1; + break; + case 0x65: + gnap.playIdle(Common::Point(6, 2)); + gameSys.setAnimation(_nextShopAssistantSequenceId, 20, 0); + gameSys.insertSequence(_nextShopAssistantSequenceId, 20, _currShopAssistantSequenceId, 20, kSeqSyncWait, 0, 0, 0); + _currShopAssistantSequenceId = _nextShopAssistantSequenceId; + _nextShopAssistantSequenceId = -1; + _vm->_newSceneNum = 18; + gnap._actionStatus = kAS19LeaveScene; + break; + case 0x6D: + gameSys.setAnimation(_nextShopAssistantSequenceId, 20, 4); + gameSys.insertSequence(_nextShopAssistantSequenceId, 20, _currShopAssistantSequenceId, 20, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x69, 19, 0x69, 19, kSeqSyncWait, _vm->getSequenceTotalDuration(_nextShopAssistantSequenceId), 0, 0); + _currShopAssistantSequenceId = _nextShopAssistantSequenceId; + _nextShopAssistantSequenceId = -1; + break; + case 0x64: + case 0x6C: + gameSys.setAnimation(_nextShopAssistantSequenceId, 20, 4); + gameSys.insertSequence(_nextShopAssistantSequenceId, 20, _currShopAssistantSequenceId, 20, kSeqSyncWait, 0, 0, 0); + _currShopAssistantSequenceId = _nextShopAssistantSequenceId; + _nextShopAssistantSequenceId = -1; + break; + } + } +} + +} // End of namespace Gnap diff --git a/engines/gnap/scenes/group1.h b/engines/gnap/scenes/group1.h new file mode 100644 index 0000000000..30771d017a --- /dev/null +++ b/engines/gnap/scenes/group1.h @@ -0,0 +1,454 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GNAP_GROUP1_H +#define GNAP_GROUP1_H + +#include "gnap/debugger.h" + +namespace Gnap { + +enum { + kHS10Platypus = 0, + kHS10ExitBar = 1, + kHS10ExitBackdoor = 2, + kHS10Cook = 3, + kHS10Tongs = 4, + kHS10Box = 5, + kHS10Oven = 6, + kHS10WalkArea1 = 7, + kHS10Device = 8, + kHS10WalkArea2 = 9, + kHS10WalkArea3 = 10, + kHS10WalkArea4 = 11 +}; + +enum { + kHS11Platypus = 0, + kHS11ExitKitchen = 1, + kHS11ExitToilet = 2, + kHS11ExitLeft = 3, + kHS11GoggleGuy = 4, + kHS11HookGuy = 5, + kHS11Billard = 6, + kHS11WalkArea1 = 7, + kHS11Device = 8, + kHS11WalkArea2 = 9, + kHS11WalkArea3 = 10, + kHS11WalkArea4 = 11, + kHS11WalkArea5 = 12 +}; + +enum { + kHS12Platypus = 0, + kHS12ExitRight = 1, + kHS12ToothGuy = 2, + kHS12Barkeeper = 3, + kHS12BeardGuy = 4, + kHS12Jukebox = 5, + kHS12WalkArea1 = 6, + kHS12Device = 7, + kHS12WalkArea2 = 8, + kHS12WalkArea3 = 9, + kHS12WalkArea4 = 10 +}; + +enum { + kHS13Platypus = 0, + kHS13ExitBar = 1, + kHS13WalkArea1 = 2, + kHS13BackToilet = 3, + kHS13FrontToilet= 4, + kHS13Urinal = 5, + kHS13Scribble = 6, + kHS13Sink = 7, + kHS13WalkArea2 = 8, + kHS13Device = 9, + kHS13WalkArea3 = 10, + kHS13WalkArea4 = 11, + kHS13WalkArea5 = 12, + kHS13WalkArea6 = 13, + kHS13WalkArea7 = 14, + kHS13WalkArea8 = 15, + kHS13WalkArea9 = 16 +}; + +enum { + kHS14Platypus = 0, + kHS14Exit = 1, + kHS14Coin = 2, + kHS14Toilet = 3, + kHS14Device = 4 +}; + +enum { + kHS15Platypus = 0, + kHS15Exit = 1, + kHS15Button1 = 2, + kHS15Button2 = 3, + kHS15Button3 = 4, + kHS15Button4 = 5, + kHS15Button5 = 6, + kHS15Button6 = 7, + kHS15ButtonA = 8, + kHS15ButtonB = 9, + kHS15ButtonC = 10, + kHS15ButtonD = 11, + kHS15ButtonE = 12, + kHS15ButtonF = 13, + kHS15CoinSlot = 14, + kHS15PlayButton = 15, + kHS15Device = 16 +}; + +enum { + kHS17Platypus = 0, + kHS17Phone1 = 1, + kHS17Phone2 = 2, + kHS17ExitGrubCity = 3, + kHS17Device = 4, + kHS17ExitToyStore = 5, + kHS17Wrench = 6, + kHS17WalkArea1 = 7, + kHS17WalkArea2 = 8, + kHS17WalkArea3 = 9 +}; + +enum { + kHS18Platypus = 0, + kHS18GarbageCan = 1, + kHS18Device = 2, + kHS18ExitToyStore = 3, + kHS18ExitPhoneBooth = 4, + kHS18ExitGrubCity = 5, + kHS18HydrantTopValve = 6, + kHS18HydrantRightValve = 7, + kHS18CowboyHat = 8, + kHS18WalkArea1 = 9, + kHS18WalkArea2 = 10 +}; + +enum { + kHS19Platypus = 0, + kHS19ExitOutsideToyStore= 1, + kHS19Device = 2, + kHS19Picture = 3, + kHS19ShopAssistant = 4, + kHS19Toy1 = 5, + kHS19Toy2 = 6, + kHS19Toy3 = 7, + kHS19Phone = 8, + kHS19Toy4 = 9, + kHS19Toy5 = 10, + kHS19Toy6 = 11, + kHS19Toy7 = 12, + kHS19WalkArea1 = 13, + kHS19WalkArea2 = 14, + kHS19WalkArea3 = 15 +}; + +enum { + kAS10LeaveScene = 0, + kAS10AnnoyCook = 1, + kAS10PlatWithBox = 4 +}; + +enum { + kAS11LeaveScene = 0, + kAS11ShowMagazineToGoggleGuy = 3, + kAS11TalkGoggleGuy = 4, + kAS11GrabHookGuy = 6, + kAS11ShowItemToHookGuy = 8, + kAS11TalkHookGuy = 9, + kAS11GrabBillardBall = 11 +}; + +enum { + kAS12LeaveScene = 0, + kAS12QuarterToToothGuyDone = 1, + kAS12TalkToothGuy = 2, + kAS12GrabToothGuy = 4, + kAS12ShowItemToToothGuy = 5, + kAS12QuarterWithHoleToToothGuy = 6, + kAS12QuarterToToothGuy = 7, + kAS12TalkBeardGuy = 8, + kAS12LookBeardGuy = 9, + kAS12GrabBeardGuy = 10, + kAS12ShowItemToBeardGuy = 11, + kAS12TalkBarkeeper = 12, + kAS12LookBarkeeper = 13, + kAS12ShowItemToBarkeeper = 15, + kAS12QuarterWithBarkeeper = 16, + kAS12PlatWithBarkeeper = 17, + kAS12PlatWithToothGuy = 18, + kAS12PlatWithBeardGuy = 19 +}; + +enum { + kAS13LeaveScene = 0, + kAS13BackToilet = 1, + kAS13FrontToilet = 2, + kAS13LookScribble = 6, + kAS13GrabSink = 7, + kAS13GrabSinkDone = 8, + kAS13Wait = 12, + kAS13GrabUrinal = 13 +}; + +enum { + kAS17TryGetWrench = 0, + kAS17GetWrench2 = 1, + kAS17GetWrenchDone = 2, + kAS17GetWrench1 = 3, + kAS17PlatUsePhone = 4, + kAS17PutCoinIntoPhone = 5, + kAS17GetCoinFromPhone = 6, + kAS17GetCoinFromPhoneDone = 7, + kAS17PutCoinIntoPhoneDone = 8, + kAS17GnapUsePhone = 9, + kAS17GetWrenchGnapReady = 10, + kAS17GnapHangUpPhone = 11, + kAS17PlatPhoningAssistant = 12, + kAS17PlatHangUpPhone = 14, + kAS17LeaveScene = 15 +}; + +enum { + kAS18OpenRightValveNoGarbageCanDone = 0, + kAS18OpenRightValveNoGarbageCan = 1, + kAS18CloseRightValveNoGarbageCan = 2, + kAS18OpenTopValveDone = 3, + kAS18OpenTopValve = 4, + kAS18CloseTopValve = 5, + kAS18GrabGarbageCanFromStreet = 6, + kAS18GrabCowboyHat = 7, + kAS18GrabGarbageCanFromHydrant = 8, + kAS18PutGarbageCanOnRunningHydrant = 9, + kAS18PutGarbageCanOnRunningHydrant2 = 10, + kAS18GrabCowboyHatDone = 11, + kAS18StandingOnHydrant = 12, + kAS18OpenRightValveWithGarbageCan = 13, + kAS18OpenRightValveWithGarbageCanDone = 14, + kAS18CloseRightValveWithGarbageCan = 15, + kAS18PutGarbageCanOnHydrant = 16, + kAS18PutGarbageCanOnHydrantDone = 17, + kAS18PlatComesHere = 18, + kAS18CloseRightValveWithGarbageCanDone = 19, + kAS18LeaveScene = 20 +}; + +enum { + kAS19UsePhone = 0, + kAS19GrabToy = 1, + kAS19GrabPicture = 2, + kAS19GrabPictureDone = 3, + kAS19TalkShopAssistant = 4, + kAS19LeaveScene = 5 +}; + +/*****************************************************************************/ + +class GnapEngine; +class CutScene; + +class Scene10: public Scene { +public: + Scene10(GnapEngine *vm); + virtual ~Scene10() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb(); + +private: + int _nextCookSequenceId; + int _currCookSequenceId; +}; + +class Scene11: public Scene { +public: + Scene11(GnapEngine *vm); + virtual ~Scene11() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _billardBallCtr; + int _nextHookGuySequenceId; + int _currHookGuySequenceId; + int _nextGoggleGuySequenceId; + int _currGoggleGuySequenceId; +}; + +class Scene12: public Scene { +public: + Scene12(GnapEngine *vm); + virtual ~Scene12() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _nextBeardGuySequenceId; + int _currBeardGuySequenceId; + int _nextToothGuySequenceId; + int _currToothGuySequenceId; + int _nextBarkeeperSequenceId; + int _currBarkeeperSequenceId; +}; + +class Scene13: public Scene { +public: + Scene13(GnapEngine *vm); + virtual ~Scene13() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _backToiletCtr; + + void showScribble(); +}; + +class Scene14: public Scene { +public: + Scene14(GnapEngine *vm); + virtual ~Scene14() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; +}; + +class Scene15: public Scene { +public: + Scene15(GnapEngine *vm); + virtual ~Scene15() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _nextRecordSequenceId; + int _currRecordSequenceId; + int _nextSlotSequenceId; + int _currSlotSequenceId; + int _nextUpperButtonSequenceId; + int _currUpperButtonSequenceId; + int _nextLowerButtonSequenceId; + int _currLowerButtonSequenceId; +}; + +class Scene17: public Scene { +public: + Scene17(GnapEngine *vm); + virtual ~Scene17() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + bool _canTryGetWrench; + int _wrenchCtr; + int _platPhoneCtr; + int _platTryGetWrenchCtr; + int _nextPhoneSequenceId; + int _currPhoneSequenceId; + int _nextWrenchSequenceId; + int _currWrenchSequenceId; + int _nextCarWindowSequenceId; + int _currCarWindowSequenceId; + + void update(); + void platHangUpPhone(); +}; + +class Scene18: public Scene { +public: + Scene18(GnapEngine *vm); + virtual ~Scene18(); + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + Graphics::Surface *_cowboyHatSurface; + + int _platPhoneCtr; + int _platPhoneIter; + int _nextPhoneSequenceId; + int _currPhoneSequenceId; + + void gnapCarryGarbageCanTo(int a5); + void putDownGarbageCan(int animationIndex); + void platEndPhoning(bool platFl); + void closeHydrantValve(); + void waitForGnapAction(); +}; + +class Scene19: public Scene { +public: + Scene19(GnapEngine *vm); + virtual ~Scene19(); + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _currShopAssistantSequenceId; + int _nextShopAssistantSequenceId; + int _toyGrabCtr; + int _shopAssistantCtr; + + Graphics::Surface *_pictureSurface; +}; + +} // End of namespace Gnap + +#endif // GNAP_GROUP1_H diff --git a/engines/gnap/scenes/group2.cpp b/engines/gnap/scenes/group2.cpp new file mode 100644 index 0000000000..522a3f4337 --- /dev/null +++ b/engines/gnap/scenes/group2.cpp @@ -0,0 +1,3423 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gnap/gnap.h" +#include "gnap/gamesys.h" +#include "gnap/resource.h" +#include "gnap/scenes/group2.h" + +namespace Gnap { + +Scene20::Scene20(GnapEngine *vm) : Scene(vm) { + _stonerGuyCtr = 3; + _stonerGuyShowingJoint = false; + _groceryStoreGuyCtr = 0; + _currStonerGuySequenceId = -1; + _nextStonerGuySequenceId = -1; + _currGroceryStoreGuySequenceId = -1; + _nextGroceryStoreGuySequenceId = -1; +} + +int Scene20::init() { + return 0x186; +} + +void Scene20::updateHotspots() { + _vm->setHotspot(kHS20Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS20GroceryStoreHat, 114, 441, 174, 486, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 0, 7); + _vm->setHotspot(kHS20ExitParkingLot, 0, 300, 15, 600, SF_EXIT_L_CURSOR | SF_WALKABLE, 0, 7); + _vm->setHotspot(kHS20StonerGuy, 276, 290, 386, 450, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 3, 8); + _vm->setHotspot(kHS20GroceryStoreGuy, 123, 282, 258, 462, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 3, 7); + _vm->setHotspot(kHS20ExitInsideGrubCity, 519, 250, 581, 413, SF_EXIT_L_CURSOR, 8, 7); + _vm->setHotspot(kHS20ExitOutsideCircusWorld, 660, 222, 798, 442, SF_EXIT_NE_CURSOR, 9, 6); + _vm->setHotspot(kHS20ExitOutsideToyStore, 785, 350, 800, 600, SF_EXIT_R_CURSOR, 11, 8); + _vm->setHotspot(kHS20ExitPhone, 250, 585, 650, 600, SF_EXIT_D_CURSOR | SF_WALKABLE, 5, 10); + _vm->setHotspot(kHS20WalkArea1, 0, 0, 800, 468); + _vm->setHotspot(kHS20WalkArea2, 605, 0, 800, 600); + _vm->setDeviceHotspot(kHS20Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 12; +} + +void Scene20::updateAnimationsCb() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(2) == 2) { + switch (_nextStonerGuySequenceId) { + case 0x16B: + if (!_vm->_timers[4]) { + _stonerGuyShowingJoint = false; + gameSys.insertSequence(0x16B, 21, _currStonerGuySequenceId, 21, kSeqSyncWait, 0, 0, 0); + _currStonerGuySequenceId = 0x16B; + _nextStonerGuySequenceId = -1; + } + break; + case 0x16A: + // Grab joint + gnap.playPullOutDevice(Common::Point(4, 4)); + gnap.playUseDevice(); + gameSys.setAnimation(0x16A, 21, 0); + gameSys.insertSequence(0x16A, 21, _currStonerGuySequenceId, 21, kSeqSyncWait, 0, 0, 0); + _currStonerGuySequenceId = 0x16A; + _nextStonerGuySequenceId = -1; + _vm->invAdd(kItemJoint); + _vm->setFlag(kGFJointTaken); + _stonerGuyShowingJoint = false; + gnap._actionStatus = kAS20GrabJointDone; + break; + case 0x16E: + gameSys.setAnimation(0x16E, 21, 2); + gameSys.insertSequence(0x16E, 21, _currStonerGuySequenceId, 21, kSeqSyncWait, 0, 0, 0); + _currStonerGuySequenceId = 0x16E; + _nextStonerGuySequenceId = -1; + _nextGroceryStoreGuySequenceId = 0x175; + break; + case 0x16D: + gameSys.setAnimation(_nextStonerGuySequenceId, 21, 2); + gameSys.setAnimation(_nextStonerGuySequenceId, 21, 0); + gameSys.insertSequence(_nextStonerGuySequenceId, 21, _currStonerGuySequenceId, 21, kSeqSyncWait, 0, 0, 0); + _currStonerGuySequenceId = _nextStonerGuySequenceId; + _nextStonerGuySequenceId = -1; + gnap._actionStatus = kAS20ActionDone; + break; + case 0x16F: + gameSys.setAnimation(_nextStonerGuySequenceId, 21, 2); + gameSys.setAnimation(0x17A, 20, 3); + gameSys.insertSequence(_nextStonerGuySequenceId, 21, _currStonerGuySequenceId, 21, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x17A, 20, _currGroceryStoreGuySequenceId, 20, kSeqSyncWait, 0, 0, 0); + _currGroceryStoreGuySequenceId = 0x17A; + _nextGroceryStoreGuySequenceId = -1; + _currStonerGuySequenceId = _nextStonerGuySequenceId; + _nextStonerGuySequenceId = -1; + break; + case 0x171: + _stonerGuyCtr = (_stonerGuyCtr + 1) % 3; + switch (_stonerGuyCtr) { + case 1: + _nextStonerGuySequenceId = 0x171; + break; + case 2: + _nextStonerGuySequenceId = 0x172; + break; + case 3: + _nextStonerGuySequenceId = 0x173; + break; + default: + _nextStonerGuySequenceId = 0x171; + break; + } + gameSys.insertSequence(_nextStonerGuySequenceId, 21, _currStonerGuySequenceId, 21, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x17C, 20, _currGroceryStoreGuySequenceId, 20, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(0x17C, 20, 3); + gameSys.setAnimation(_nextStonerGuySequenceId, 21, 2); + _currGroceryStoreGuySequenceId = 0x17C; + _nextGroceryStoreGuySequenceId = -1; + _currStonerGuySequenceId = _nextStonerGuySequenceId; + _nextStonerGuySequenceId = -1; + break; + default: + _nextStonerGuySequenceId = 0x16C; + gameSys.setAnimation(0x16C, 21, 2); + gameSys.insertSequence(_nextStonerGuySequenceId, 21, _currStonerGuySequenceId, 21, kSeqSyncWait, 0, 0, 0); + _currStonerGuySequenceId = _nextStonerGuySequenceId; + _nextStonerGuySequenceId = -1; + break; + } + } +} + +void Scene20::stopSounds() { + _vm->stopSound(0x18E); + _vm->stopSound(0x18F); + _vm->stopSound(0x190); + _vm->stopSound(0x191); + _vm->stopSound(0x194); + _vm->stopSound(0x195); + _vm->stopSound(0x192); + _vm->stopSound(0x193); + _vm->stopSound(0x196); + _vm->stopSound(0x197); + _vm->stopSound(0x198); + _vm->stopSound(0x199); + _vm->stopSound(0x19A); +} + +void Scene20::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->playSound(0x10940, true); + _vm->startSoundTimerA(8); + + _stonerGuyShowingJoint = false; + _vm->_timers[7] = _vm->getRandom(100) + 100; + + _stonerGuyCtr = (_stonerGuyCtr + 1) % 3; + switch (_stonerGuyCtr) { + case 1: + _currStonerGuySequenceId = 0x171; + break; + case 2: + _currStonerGuySequenceId = 0x172; + break; + case 3: + _currStonerGuySequenceId = 0x173; + break; + } + + _nextStonerGuySequenceId = -1; + gameSys.setAnimation(_currStonerGuySequenceId, 21, 2); + gameSys.insertSequence(_currStonerGuySequenceId, 21, 0, 0, kSeqNone, 0, 0, 0); + + _vm->_timers[6] = _vm->getRandom(20) + 30; + + _currGroceryStoreGuySequenceId = 0x17C; + _nextGroceryStoreGuySequenceId = -1; + gameSys.setAnimation(0x17C, 20, 3); + gameSys.insertSequence(0x17C, 20, 0, 0, kSeqNone, 0, 0, 0); + + _vm->_timers[5] = _vm->getRandom(50) + 130; + if (_vm->isFlag(kGFGroceryStoreHatTaken)) + gameSys.insertSequence(0x17F, 20, 0, 0, kSeqNone, 0, 0, 0); + else + gameSys.insertSequence(0x174, 20, 0, 0, kSeqNone, 0, 0, 0); + + _vm->queueInsertDeviceIcon(); + + if (_vm->isFlag(kGFSceneFlag1)) { + _vm->clearFlag(kGFSceneFlag1); + _vm->endSceneInit(); + gameSys.setAnimation(0x182, 140, 0); + gameSys.insertSequence(0x182, 140, 0, 0, kSeqNone, 0, 0, 0); + while (gameSys.getAnimationStatus(0) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + + gnap.initPos(11, 8, kDirBottomLeft); + plat.initPos(11, 9, kDirIdleRight); + gnap.walkTo(Common::Point(5, 8), -1, 0x107BA, 1); + plat.walkTo(Common::Point(6, 9), -1, 0x107C2, 1); + } else { + switch (_vm->_prevSceneNum) { + case 17: + gnap.initPos(5, 11, kDirBottomRight); + plat.initPos(6, 11, kDirIdleLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(5, 8), -1, 0x107B9, 1); + plat.walkTo(Common::Point(6, 9), -1, 0x107C2, 1); + break; + case 18: + gnap.initPos(11, 8, kDirBottomLeft); + plat.initPos(11, 9, kDirIdleRight); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(5, 8), -1, 0x107BA, 1); + plat.walkTo(Common::Point(6, 9), -1, 0x107C2, 1); + break; + case 21: + gnap.initPos(-1, 8, kDirBottomLeft); + plat.initPos(-1, 9, kDirIdleRight); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(3, 8), -1, 0x107B9, 1); + plat.walkTo(Common::Point(3, 9), -1, 0x107C2, 1); + break; + case 22: + gnap.initPos(7, 6, kDirBottomRight); + plat.initPos(8, 6, kDirIdleLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(8, 8), -1, 0x107B9, 1); + plat.walkTo(Common::Point(9, 9), -1, 0x107C2, 1); + break; + default: + gnap.initPos(8, 6, kDirBottomLeft); + plat.initPos(9, 6, kDirIdleRight); + _vm->endSceneInit(); + _vm->_hotspots[kHS20WalkArea2]._flags |= SF_WALKABLE; + gnap.walkTo(Common::Point(8, 8), -1, 0x107BA, 1); + plat.walkTo(Common::Point(9, 9), -1, 0x107C2, 1); + _vm->_hotspots[kHS20WalkArea2]._flags &= ~SF_WALKABLE; + break; + } + } + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->testWalk(0, 0, -1, -1, -1, -1); + _vm->testWalk(0, 1, 7, 9, 8, 9); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS20Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS20Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemJoint) { + gnap.useJointOnPlatypus(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(20); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS20ExitParkingLot: + if (gnap._actionStatus < 0) { + if (_stonerGuyShowingJoint) + _vm->_timers[4] = 0; + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 21; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS20ExitParkingLot], 0, 0x107AF, 1); + gnap._actionStatus = kAS20LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS20ExitParkingLot] + Common::Point(0, 1), -1, 0x107CF, 1); + plat._idleFacing = kDirIdleRight; + } + break; + + case kHS20ExitPhone: + if (gnap._actionStatus < 0) { + if (_stonerGuyShowingJoint) + _vm->_timers[4] = 0; + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 17; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS20ExitPhone], 0, 0x107AE, 1); + gnap._actionStatus = kAS20LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS20ExitPhone] + Common::Point(1, 0), -1, 0x107C2, 1); + } + break; + + case kHS20ExitOutsideToyStore: + if (gnap._actionStatus < 0) { + if (_stonerGuyShowingJoint) + _vm->_timers[4] = 0; + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 18; + _vm->_hotspots[kHS20WalkArea2]._flags |= SF_WALKABLE; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS20ExitOutsideToyStore], 0, 0x107AB, 1); + gnap._actionStatus = kAS20LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS20ExitOutsideToyStore] + Common::Point(0, 1), -1, 0x107CD, 1); + _vm->_hotspots[kHS20WalkArea2]._flags &= ~SF_WALKABLE; + } + break; + + case kHS20ExitInsideGrubCity: + if (gnap._actionStatus < 0) { + if (_stonerGuyShowingJoint) + _vm->_timers[4] = 0; + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 22; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS20ExitInsideGrubCity] + Common::Point(0, - 1), 0, 0x107BB, 1); + gnap._actionStatus = kAS20LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS20ExitInsideGrubCity] + Common::Point(1, 0), -1, 0x107C2, 1); + plat._idleFacing = kDirIdleRight; + } + break; + + case kHS20ExitOutsideCircusWorld: + if (gnap._actionStatus < 0) { + if (_stonerGuyShowingJoint) + _vm->_timers[4] = 0; + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 24; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS20ExitOutsideCircusWorld], 0, 0x107BB, 1); + gnap._actionStatus = kAS20LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS20ExitOutsideCircusWorld] + Common::Point(1, 0), -1, 0x107C2, 1); + } + break; + + case kHS20StonerGuy: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS20StonerGuy], 5, 4); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan2(Common::Point(5, 4)); + break; + case GRAB_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS20StonerGuy], 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + if (_stonerGuyShowingJoint) + gnap._actionStatus = kAS20GrabJoint; + else + gnap.playImpossible(); + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS20StonerGuy], 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + if (_vm->isFlag(kGFJointTaken)) + gnap._actionStatus = kAS20TalkStonerGuyNoJoint; + else + gnap._actionStatus = kAS20TalkStonerGuyHasJoint; + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS20GroceryStoreGuy: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS20GroceryStoreGuy], 2, 4); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(2, 3)); + break; + case GRAB_CURSOR: + _stonerGuyShowingJoint = false; + gnap._idleFacing = kDirUpLeft; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS20GroceryStoreGuy], 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS20GrabGroceryStoreGuy; + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpLeft; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS20GroceryStoreGuy], 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS20TalkGroceryStoreGuy; + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS20GroceryStoreHat: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemCowboyHat) { + gnap._idleFacing = kDirUpRight; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS20GroceryStoreHat], 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS20SwitchGroceryStoreHat; + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS20GroceryStoreHat], 1, 6); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(1, 6)); + break; + case GRAB_CURSOR: + _stonerGuyShowingJoint = false; + gnap._idleFacing = kDirUpLeft; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS20GroceryStoreGuy], 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS20GrabGroceryStoreHat; + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS20WalkArea1: + case kHS20WalkArea2: + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + + } + + updateAnimations(); + + if (!_vm->isSoundPlaying(0x10940)) + _vm->playSound(0x10940, true); + + if (!_vm->_isLeavingScene) { + if (plat._actionStatus < 0) { + _vm->_hotspots[kHS20WalkArea1]._rect.bottom += 48; + plat.updateIdleSequence(); + _vm->_hotspots[kHS20WalkArea1]._rect.bottom -= 48; + } + if (gnap._actionStatus < 0) + gnap.updateIdleSequence(); + if (gnap._actionStatus < 0 && !_vm->_timers[5] && _nextGroceryStoreGuySequenceId == -1) { + _vm->_timers[5] = _vm->getRandom(50) + 130; + if (_vm->getRandom(4) != 0) + _nextGroceryStoreGuySequenceId = 0x17C; + else + _nextGroceryStoreGuySequenceId = 0x17A; + } + if (!_vm->_timers[7]) { + _vm->_timers[7] = _vm->getRandom(100) + 100; + if (gnap._actionStatus < 0 && plat._actionStatus < 0) { + switch (_vm->getRandom(3)) { + case 0: + gameSys.insertSequence(0x183, 253, 0, 0, kSeqNone, 0, 0, 0); + break; + case 1: + gameSys.insertSequence(0x184, 253, 0, 0, kSeqNone, 0, 0, 0); + break; + case 2: + gameSys.insertSequence(0x185, 253, 0, 0, kSeqNone, 0, 0, 0); + break; + } + } + } + _vm->playSoundA(); + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + _vm->gameUpdateTick(); + } +} + +void Scene20::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS20LeaveScene: + _vm->_sceneDone = true; + break; + case kAS20TalkStonerGuyNoJoint: + gameSys.setAnimation(0x170, 21, 2); + gameSys.setAnimation(0x17B, 20, 3); + gameSys.insertSequence(0x17B, 20, _currGroceryStoreGuySequenceId, 20, kSeqSyncExists, 0, 0, 0); + gameSys.insertSequence(0x170, 21, _currStonerGuySequenceId, 21, kSeqSyncExists, 0, 0, 0); + _vm->stopSound(0x1A1); + stopSounds(); + _currGroceryStoreGuySequenceId = 0x17B; + _currStonerGuySequenceId = 0x170; + _nextGroceryStoreGuySequenceId = -1; + _nextStonerGuySequenceId = 0x16E; + _vm->_timers[5] = 100; + _vm->_timers[6] = 100; + break; + case kAS20TalkStonerGuyHasJoint: + gameSys.setAnimation(0x168, 21, 2); + gameSys.setAnimation(379, 20, 3); + gameSys.insertSequence(0x17B, 20, _currGroceryStoreGuySequenceId, 20, kSeqSyncExists, 0, 0, 0); + gameSys.insertSequence(0x170, 21, _currStonerGuySequenceId, 21, kSeqSyncExists, 0, 0, 0); + gameSys.insertSequence(0x168, 21, 0x170, 21, kSeqSyncWait, 0, 0, 0); + _vm->stopSound(0x1A1); + stopSounds(); + _currGroceryStoreGuySequenceId = 0x17B; + _currStonerGuySequenceId = 0x168; + _nextGroceryStoreGuySequenceId = -1; + _nextStonerGuySequenceId = 0x16B; + _vm->_timers[5] = 200; + _vm->_timers[6] = 200; + _vm->_timers[4] = 100; + _stonerGuyShowingJoint = true; + gnap._actionStatus = -1; + break; + case kAS20GrabJoint: + _nextStonerGuySequenceId = 0x16A; + break; + case kAS20ActionDone: + gnap._actionStatus = -1; + break; + case kAS20TalkGroceryStoreGuy: + gameSys.setAnimation(0x170, 21, 2); + gameSys.setAnimation(0x17B, 20, 3); + gameSys.insertSequence(0x17B, 20, _currGroceryStoreGuySequenceId, 20, kSeqSyncExists, 0, 0, 0); + gameSys.insertSequence(0x170, 21, _currStonerGuySequenceId, 21, kSeqSyncExists, 0, 0, 0); + _vm->stopSound(0x1A1); + stopSounds(); + _currGroceryStoreGuySequenceId = 0x17B; + _currStonerGuySequenceId = 0x170; + _groceryStoreGuyCtr = (_groceryStoreGuyCtr + 1) % 2; + if (_groceryStoreGuyCtr != 0) + _nextGroceryStoreGuySequenceId = 0x176; + else + _nextGroceryStoreGuySequenceId = 0x177; + _vm->_timers[5] = 100; + _vm->_timers[6] = 100; + break; + case kAS20GrabGroceryStoreGuy: + gameSys.setAnimation(0x170, 21, 2); + gameSys.setAnimation(0x17B, 20, 3); + gameSys.insertSequence(0x170, 21, _currStonerGuySequenceId, 21, kSeqSyncExists, 0, 0, 0); + gameSys.insertSequence(0x17B, 20, _currGroceryStoreGuySequenceId, 20, kSeqSyncExists, 0, 0, 0); + _vm->stopSound(0x1A1); + stopSounds(); + _currGroceryStoreGuySequenceId = 0x17B; + _currStonerGuySequenceId = 0x170; + _vm->_timers[5] = 120; + _vm->_timers[6] = 120; + _nextGroceryStoreGuySequenceId = 0x178; + break; + case kAS20GrabGroceryStoreHat: + gameSys.setAnimation(0x170, 21, 2); + gameSys.setAnimation(0x17B, 20, 3); + gameSys.insertSequence(0x17B, 20, _currGroceryStoreGuySequenceId, 20, kSeqSyncExists, 0, 0, 0); + gameSys.insertSequence(0x170, 21, _currStonerGuySequenceId, 21, kSeqSyncExists, 0, 0, 0); + _vm->stopSound(0x1A1); + stopSounds(); + _currGroceryStoreGuySequenceId = 0x17B; + _currStonerGuySequenceId = 0x170; + _nextGroceryStoreGuySequenceId = 0x179; + break; + case kAS20SwitchGroceryStoreHat: + _vm->setGrabCursorSprite(-1); + gameSys.setAnimation(0x180, gnap._id, 0); + gameSys.insertSequence(0x180, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x180; + gnap._sequenceDatNum = 0; + _vm->invRemove(kItemCowboyHat); + _vm->invAdd(kItemGroceryStoreHat); + gnap._actionStatus = kAS20SwitchGroceryStoreHatDone; + break; + case kAS20SwitchGroceryStoreHatDone: + gameSys.insertSequence(0x17F, 20, 372, 20, kSeqSyncWait, 0, 0, 0); + _vm->setFlag(kGFGroceryStoreHatTaken); + _vm->hideCursor(); + _vm->setGrabCursorSprite(-1); + _vm->addFullScreenSprite(0x12C, 255); + gameSys.setAnimation(0x181, 256, 0); + gameSys.insertSequence(0x181, 256, 0, 0, kSeqNone, 0, 0, 0); + while (gameSys.getAnimationStatus(0) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + _vm->removeFullScreenSprite(); + _vm->showCursor(); + _vm->setGrabCursorSprite(kItemGroceryStoreHat); + gnap._idleFacing = kDirBottomRight; + gnap.walkTo(Common::Point(3, 8), -1, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = -1; + break; + case kAS20GrabJointDone: + _vm->setGrabCursorSprite(kItemJoint); + gnap._actionStatus = -1; + break; + } + } + + if (gameSys.getAnimationStatus(3) == 2) { + switch (_nextGroceryStoreGuySequenceId) { + case 0x176: + case 0x177: + gameSys.setAnimation(_nextGroceryStoreGuySequenceId, 20, 3); + gameSys.insertSequence(_nextGroceryStoreGuySequenceId, 20, _currGroceryStoreGuySequenceId, 20, kSeqSyncWait, 0, 0, 0); + _currGroceryStoreGuySequenceId = _nextGroceryStoreGuySequenceId; + _nextGroceryStoreGuySequenceId = -1; + _nextStonerGuySequenceId = 0x16D; + break; + case 0x178: + gameSys.setAnimation(_nextGroceryStoreGuySequenceId, 20, 3); + gameSys.setAnimation(0x17D, gnap._id, 0); + gameSys.insertSequence(_nextGroceryStoreGuySequenceId, 20, _currGroceryStoreGuySequenceId, 20, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x17D, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x17D; + gnap._sequenceDatNum = 0; + gnap._actionStatus = kAS20ActionDone; + gameSys.setAnimation(0x16D, 21, 2); + gameSys.insertSequence(0x16D, 21, _currStonerGuySequenceId, 21, kSeqSyncWait, 0, 0, 0); + _currStonerGuySequenceId = 0x16D; + _currGroceryStoreGuySequenceId = 0x178; + _nextGroceryStoreGuySequenceId = -1; + _nextStonerGuySequenceId = -1; + break; + case 0x179: + gameSys.setAnimation(_nextGroceryStoreGuySequenceId, 20, 3); + gameSys.setAnimation(0x16D, 21, 0); + gameSys.insertSequence(_nextGroceryStoreGuySequenceId, 20, _currGroceryStoreGuySequenceId, 20, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x17E, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x17E; + gnap._sequenceDatNum = 0; + gnap._actionStatus = kAS20ActionDone; + gameSys.setAnimation(0x16D, 21, 2); + gameSys.insertSequence(0x16D, 21, _currStonerGuySequenceId, 21, kSeqSyncWait, 0, 0, 0); + _currStonerGuySequenceId = 0x16D; + _currGroceryStoreGuySequenceId = 377; + _nextGroceryStoreGuySequenceId = -1; + _nextStonerGuySequenceId = -1; + gnap.walkTo(Common::Point(4, 8), -1, 0x107BB, 1); + break; + case 0x17C: + gameSys.setAnimation(0, 0, 3); + _nextStonerGuySequenceId = 0x171; + break; + case 0x17A: + gameSys.setAnimation(0, 0, 3); + _nextStonerGuySequenceId = 0x16F; + break; + case 0x175: + gameSys.setAnimation(0x175, 20, 0); + gameSys.setAnimation(0x175, 20, 3); + gameSys.insertSequence(0x175, 20, _currGroceryStoreGuySequenceId, 20, kSeqSyncWait, 0, 0, 0); + _currGroceryStoreGuySequenceId = 0x175; + _nextGroceryStoreGuySequenceId = -1; + gnap._actionStatus = kAS20ActionDone; + break; + default: + if (_nextGroceryStoreGuySequenceId != -1) { + gameSys.setAnimation(_nextGroceryStoreGuySequenceId, 20, 3); + gameSys.insertSequence(_nextGroceryStoreGuySequenceId, 20, _currGroceryStoreGuySequenceId, 20, kSeqSyncWait, 0, 0, 0); + _currGroceryStoreGuySequenceId = _nextGroceryStoreGuySequenceId; + _nextGroceryStoreGuySequenceId = -1; + } + break; + } + } + + updateAnimationsCb(); +} + +/*****************************************************************************/ + +Scene21::Scene21(GnapEngine *vm) : Scene(vm) { + _currOldLadySequenceId = -1; + _nextOldLadySequenceId = -1; +} + +int Scene21::init() { + _vm->_gameSys->setAnimation(0, 0, 3); + + return _vm->isFlag(kGFTwigTaken) ? 0x94 : 0x93; +} + +void Scene21::updateHotspots() { + _vm->setHotspot(kHS21Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS21Banana, 94, 394, 146, 430, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 2, 6); + _vm->setHotspot(kHS21OldLady, 402, 220, 528, 430, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 4, 7); + _vm->setHotspot(kHS21ExitOutsideGrubCity, 522, 498, 800, 600, SF_EXIT_SE_CURSOR | SF_WALKABLE, 5, 10); + _vm->setHotspot(kHS21WalkArea1, 0, 0, 800, 440); + _vm->setHotspot(kHS21WalkArea2, 698, 0, 800, 600); + _vm->setDeviceHotspot(kHS21Device, -1, -1, -1, -1); + if (_vm->isFlag(kGFUnk04) || !_vm->isFlag(kGFTwigTaken)) + _vm->_hotspots[kHS21Banana]._flags = SF_WALKABLE | SF_DISABLED; + if (_vm->isFlag(kGFTwigTaken)) + _vm->_hotspots[kHS21OldLady]._flags = SF_DISABLED; + _vm->_hotspotsCount = 7; +} + +void Scene21::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->playSound(0x10940, true); + _vm->startSoundTimerA(6); + _vm->_timers[5] = _vm->getRandom(100) + 100; + _vm->queueInsertDeviceIcon(); + + if (_vm->isFlag(kGFTwigTaken)) { + if (_vm->isFlag(kGFKeysTaken)) { + gnap.initPos(5, 8, kDirBottomRight); + plat.initPos(6, 8, kDirIdleLeft); + gameSys.insertSequence(0x8E, 2, 0, 0, kSeqNone, 0, 0, 0); + if (!_vm->isFlag(kGFUnk04)) + gameSys.insertSequence(0x8D, 59, 0, 0, kSeqNone, 0, 0, 0); + _vm->endSceneInit(); + _vm->clearFlag(kGFKeysTaken); + } else { + gnap.initPos(5, 11, kDirBottomRight); + plat.initPos(6, 11, kDirIdleLeft); + if (!_vm->isFlag(kGFUnk04)) + gameSys.insertSequence(0x8D, 59, 0, 0, kSeqNone, 0, 0, 0); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(5, 8), -1, 0x107B9, 1); + plat.walkTo(Common::Point(6, 8), -1, 0x107C2, 1); + } + } else { + gnap.initPos(5, 11, kDirBottomRight); + plat.initPos(6, 11, kDirIdleLeft); + _currOldLadySequenceId = 0x89; + gameSys.setAnimation(0x89, 79, 3); + gameSys.insertSequence(_currOldLadySequenceId, 79, 0, 0, kSeqNone, 0, 0, 0); + _nextOldLadySequenceId = -1; + _vm->_timers[4] = _vm->getRandom(30) + 50; + _vm->endSceneInit(); + gnap.walkTo(Common::Point(5, 8), -1, 0x107B9, 1); + plat.walkTo(Common::Point(6, 8), -1, 0x107C2, 1); + } + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS21Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS21Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemJoint) { + gnap.useJointOnPlatypus(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS21Banana: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowItem(_vm->_grabCursorSpriteIndex, 2, 5); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(2, 5)); + break; + case GRAB_CURSOR: + gnap.walkTo(gnap._pos, 0, gnap.getSequenceId(kGSIdle, _vm->_hotspotsWalkPos[kHS21Banana]) | 0x10000, 1); + gnap.playPullOutDevice(Common::Point(2, 5)); + gnap.playUseDevice(); + gnap._actionStatus = kAS21GrabBanana; + break; + case TALK_CURSOR: + case PLAT_CURSOR: + break; + } + } + } + break; + + case kHS21OldLady: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemGroceryStoreHat) { + _vm->_newSceneNum = 47; + gnap.walkTo(Common::Point(4, 6), 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS21UseHatWithOldLady; + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(4, 6), 7, 4); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(7, 4)); + break; + case GRAB_CURSOR: + gnap._idleFacing = kDirUpLeft; + _vm->_hotspots[kHS21WalkArea1]._flags |= SF_WALKABLE; + gnap.walkTo(Common::Point(7, 6), 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS21GrabOldLady; + _vm->_hotspots[kHS21WalkArea1]._flags &= ~SF_WALKABLE; + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS21OldLady], 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS21TalkOldLady; + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS21ExitOutsideGrubCity: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 20; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS21ExitOutsideGrubCity], 0, 0x107B3, 1); + gnap._actionStatus = kAS21LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS21ExitOutsideGrubCity] + Common::Point(1, 0), -1, 0x107C2, 1); + } + break; + + case kHS21WalkArea1: + case kHS21WalkArea2: + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + + } + + updateAnimations(); + + if (!_vm->isSoundPlaying(0x10940)) + _vm->playSound(0x10940, true); + + if (!_vm->_isLeavingScene) { + plat.updateIdleSequence(); + gnap.updateIdleSequence(); + if (!_vm->isFlag(kGFTwigTaken) && !_vm->_timers[4] && _nextOldLadySequenceId == -1 && gnap._actionStatus == -1) { + _vm->_timers[4] = _vm->getRandom(30) + 50; + switch (_vm->getRandom(5)) { + case 0: + _nextOldLadySequenceId = 0x88; + break; + case 1: + _nextOldLadySequenceId = 0x8A; + break; + default: + _nextOldLadySequenceId = 0x89; + break; + } + } + if (!_vm->_timers[5]) { + _vm->_timers[5] = _vm->getRandom(100) + 100; + gameSys.insertSequence(0x92, 255, 0, 0, kSeqNone, 0, 0, 0); + } + _vm->playSoundA(); + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } +} + +void Scene21::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS21TalkOldLady: + _nextOldLadySequenceId = 0x8B; + gnap._actionStatus = -1; + break; + case kAS21GrabBanana: + gameSys.setAnimation(0x8C, 59, 0); + gameSys.insertSequence(0x8C, 59, 141, 59, kSeqSyncWait, 0, 0, 0); + _vm->setFlag(kGFUnk04); + _vm->invAdd(kItemBanana); + updateHotspots(); + gnap._actionStatus = kAS21GrabBananaDone; + break; + case kAS21GrabBananaDone: + _vm->setGrabCursorSprite(kItemBanana); + gnap._actionStatus = -1; + break; + case kAS21GrabOldLady: + _vm->_timers[4] = _vm->getRandom(30) + 50; + _nextOldLadySequenceId = 0x87; + break; + case kAS21UseHatWithOldLady: + gameSys.setAnimation(0x8F, gnap._id, 0); + gameSys.insertSequence(0x8F, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceDatNum = 0; + gnap._sequenceId = 0x8F; + gnap._actionStatus = kAS21UseHatWithOldLadyDone; + _vm->invAdd(kItemTickets); + _vm->invRemove(kItemGroceryStoreHat); + _vm->setGrabCursorSprite(-1); + break; + case kAS21UseHatWithOldLadyDone: + _nextOldLadySequenceId = 0x91; + break; + case kAS21LeaveScene: + _vm->_sceneDone = true; + break; + } + } + + if (gameSys.getAnimationStatus(3) == 2 && _nextOldLadySequenceId != -1) { + if (_nextOldLadySequenceId == 0x87) { + gameSys.setAnimation(_nextOldLadySequenceId, 79, 3); + gameSys.insertSequence(_nextOldLadySequenceId, 79, _currOldLadySequenceId, 79, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x86, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x86; + gnap._sequenceDatNum = 0; + gnap._actionStatus = -1; + _currOldLadySequenceId = _nextOldLadySequenceId; + _nextOldLadySequenceId = -1; + } else if (_nextOldLadySequenceId == 0x91) { + gameSys.setAnimation(0x91, 79, 0); + gameSys.insertSequence(_nextOldLadySequenceId, 79, _currOldLadySequenceId, 79, kSeqSyncWait, 0, 0, 0); + gnap._actionStatus = kAS21LeaveScene; + _currOldLadySequenceId = _nextOldLadySequenceId; + _nextOldLadySequenceId = -1; + } else { + gameSys.setAnimation(_nextOldLadySequenceId, 79, 3); + gameSys.insertSequence(_nextOldLadySequenceId, 79, _currOldLadySequenceId, 79, kSeqSyncWait, 0, 0, 0); + _currOldLadySequenceId = _nextOldLadySequenceId; + _nextOldLadySequenceId = -1; + } + } +} + +/*****************************************************************************/ + +Scene22::Scene22(GnapEngine *vm) : Scene(vm) { + _caughtBefore = false; + _cashierCtr = 3; + _nextCashierSequenceId = -1; +} + +int Scene22::init() { + return 0x5E; +} + +void Scene22::updateHotspots() { + _vm->setHotspot(kHS22Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS22ExitOutsideGrubCity, 0, 180, 184, 472, SF_EXIT_L_CURSOR, 3, 6); + _vm->setHotspot(kHS22ExitBackGrubCity, 785, 405, 800, 585, SF_EXIT_R_CURSOR, 11, 9); + _vm->setHotspot(kHS22Cashier, 578, 230, 660, 376, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 6, 8); + _vm->setHotspot(kHS22WalkArea1, 553, 0, 800, 542); + _vm->setHotspot(kHS22WalkArea2, 0, 0, 552, 488); + _vm->setDeviceHotspot(kHS22Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 7; +} + +void Scene22::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + gameSys.insertSequence(0x5D, 254, 0, 0, kSeqNone, 0, 0, 0); + + _currCashierSequenceId = 0x59; + _nextCashierSequenceId = -1; + + gameSys.setAnimation(0x59, 1, 3); + gameSys.insertSequence(_currCashierSequenceId, 1, 0, 0, kSeqNone, 0, 0, 0); + + _vm->_timers[6] = _vm->getRandom(30) + 20; + + _vm->queueInsertDeviceIcon(); + + if (_vm->_prevSceneNum == 20) { + gnap.initPos(2, 8, kDirBottomRight); + plat.initPos(1, 8, kDirIdleLeft); + _vm->endSceneInit(); + } else { + gnap.initPos(11, _vm->_hotspotsWalkPos[kHS22ExitBackGrubCity].y, kDirBottomRight); + plat.initPos(11, _vm->_hotspotsWalkPos[kHS22ExitBackGrubCity].y + 1, kDirIdleLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(8, 8), -1, 0x107B9, 1); + plat.walkTo(Common::Point(9, 8), -1, 0x107C2, 1); + } + + if (_vm->isFlag(kGFSceneFlag1)) { + int storeDetectiveSeqId; + _vm->setGrabCursorSprite(-1); + _vm->invRemove(kItemCereals); + if (_caughtBefore) { + switch (_vm->getRandom(3)) { + case 0: + storeDetectiveSeqId = 0x55; + break; + case 1: + storeDetectiveSeqId = 0x56; + break; + default: + storeDetectiveSeqId = 0x57; + break; + } + } else { + _caughtBefore = true; + storeDetectiveSeqId = 0x54; + } + gameSys.waitForUpdate(); + gameSys.requestClear1(); + gameSys.drawSpriteToBackground(0, 0, 0x44); + gameSys.setAnimation(storeDetectiveSeqId, 256, 4); + gameSys.insertSequence(storeDetectiveSeqId, 256, 0, 0, kSeqNone, 0, 0, 0); + while (gameSys.getAnimationStatus(4) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + + _vm->_sceneDone = true; + _vm->_newSceneNum = 20; + _caughtBefore = true; + } + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->testWalk(0, 0, -1, -1, -1, -1); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS22Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS22Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemJoint) { + gnap.useJointOnPlatypus(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS22ExitOutsideGrubCity: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 20; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS22ExitOutsideGrubCity], 0, 0x107AF, 1); + gnap._actionStatus = kAS22LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS22ExitOutsideGrubCity] + Common::Point(0, 1), -1, 0x107C2, 1); + } + break; + + case kHS22ExitBackGrubCity: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 23; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS22ExitBackGrubCity], 0, 0x107AB, 1); + gnap._actionStatus = kAS22LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS22ExitBackGrubCity] + Common::Point(0, 1), -1, 0x107C2, 1); + } + break; + + case kHS22Cashier: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS22Cashier], 8, 4); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(8, 4)); + break; + case GRAB_CURSOR: + gnap.playImpossible(); + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS22Cashier], 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS22TalkCashier; + break; + case PLAT_CURSOR: + gnap.useDeviceOnPlatypus(); + break; + } + } + } + break; + + case kHS22WalkArea1: + case kHS22WalkArea2: + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + } + + updateAnimations(); + + if (!_vm->_isLeavingScene) { + plat.updateIdleSequence(); + gnap.updateIdleSequence(); + if (!_vm->_timers[6] && _nextCashierSequenceId == -1) { + _vm->_timers[6] = _vm->getRandom(30) + 20; + if (_vm->getRandom(8) != 0) { + _nextCashierSequenceId = 0x59; + } else { + _cashierCtr = (_cashierCtr + 1) % 3; + switch (_cashierCtr) { + case 1: + _nextCashierSequenceId = 0x58; + break; + case 2: + _nextCashierSequenceId = 0x5A; + break; + case 3: + _nextCashierSequenceId = 0x5B; + break; + default: + _nextCashierSequenceId = 0x58; + break; + } + } + } + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + _vm->_timers[2] = _vm->getRandom(30) + 20; + _vm->_timers[3] = 400; + _vm->_timers[1] = _vm->getRandom(20) + 30; + _vm->_timers[0] = _vm->getRandom(75) + 75; + } + + _vm->gameUpdateTick(); + } +} + +void Scene22::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS22LeaveScene: + _vm->_sceneDone = true; + break; + case kAS22TalkCashier: + _nextCashierSequenceId = 0x5C; + break; + } + gnap._actionStatus = -1; + } + + if (gameSys.getAnimationStatus(3) == 2 && _nextCashierSequenceId != -1) { + gameSys.setAnimation(_nextCashierSequenceId, 1, 3); + gameSys.insertSequence(_nextCashierSequenceId, 1, _currCashierSequenceId, 1, kSeqSyncWait, 0, 0, 0); + _currCashierSequenceId = _nextCashierSequenceId; + _nextCashierSequenceId = -1; + } +} + +/*****************************************************************************/ + +Scene23::Scene23(GnapEngine *vm) : Scene(vm) { + _currStoreClerkSequenceId = -1; + _nextStoreClerkSequenceId = -1; +} + +int Scene23::init() { + return 0xC0; +} + +void Scene23::updateHotspots() { + _vm->setHotspot(kHS23Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS23ExitFrontGrubCity, 0, 250, 15, 550, SF_EXIT_L_CURSOR | SF_WALKABLE, 0, 7); + _vm->setHotspot(kHS23Cereals, 366, 332, 414, 408, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 4, 7); + _vm->setHotspot(kHS23WalkArea1, 0, 0, 340, 460); + _vm->setHotspot(kHS23WalkArea2, 340, 0, 800, 501); + _vm->setDeviceHotspot(kHS23Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 6; +} + +void Scene23::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->_timers[4] = _vm->getRandom(100) + 200; + _vm->_timers[5] = _vm->getRandom(100) + 200; + + _currStoreClerkSequenceId = 0xB4; + _nextStoreClerkSequenceId = -1; + + gameSys.setAnimation(0xB4, 1, 4); + gameSys.insertSequence(_currStoreClerkSequenceId, 1, 0, 0, kSeqNone, 0, 0, 0); + + _vm->queueInsertDeviceIcon(); + + gnap.initPos(-1, 7, kDirBottomRight); + plat.initPos(-2, 7, kDirIdleLeft); + gameSys.insertSequence(0xBD, 255, 0, 0, kSeqNone, 0, 0, 0); + gameSys.insertSequence(0xBF, 2, 0, 0, kSeqNone, 0, 0, 0); + _vm->endSceneInit(); + + plat.walkTo(Common::Point(1, 7), -1, 0x107C2, 1); + + if (_vm->isFlag(kGFUnk24)) { + gnap.walkTo(Common::Point(2, 7), -1, 0x107B9, 1); + } else { + gnap.walkTo(Common::Point(2, 7), 0, 0x107B9, 1); + while (gameSys.getAnimationStatus(0) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + + _vm->playSequences(0x48, 0xBA, 0xBB, 0xBC); + _vm->setFlag(kGFUnk24); + } + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->testWalk(0, 3, -1, -1, -1, -1); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS23Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS23Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemJoint) { + gnap.useJointOnPlatypus(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS23Cereals: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS23Cereals], 5, 4); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + if (_vm->isFlag(kGFSceneFlag1)) + gnap.playMoan2(); + else { + gnap.walkTo(_vm->_hotspotsWalkPos[kHS23Cereals], 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS23LookCereals; + } + break; + case GRAB_CURSOR: + if (_vm->isFlag(kGFSceneFlag1)) + gnap.playImpossible(); + else { + gnap._idleFacing = kDirBottomRight; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS23Cereals], 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + _vm->setFlag(kGFSceneFlag1); + gnap._actionStatus = kAS23GrabCereals; + _vm->invAdd(kItemCereals); + } + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS23ExitFrontGrubCity: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 22; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS23ExitFrontGrubCity], 0, 0x107AF, 1); + gnap._actionStatus = kAS23LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS23ExitFrontGrubCity] + Common::Point(0, -1), -1, 0x107C2, 1); + } + break; + + case kHS23WalkArea1: + case kHS23WalkArea2: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + } + + updateAnimations(); + + if (!_vm->_isLeavingScene) { + plat.updateIdleSequence(); + gnap.updateIdleSequence(); + if (!_vm->_timers[4] && gnap._actionStatus == -1) { + _vm->_timers[4] = _vm->getRandom(100) + 200; + switch (_vm->getRandom(4)) { + case 0: + gameSys.insertSequence(0xB7, 256, 0, 0, kSeqNone, 0, 0, 0); + break; + case 1: + gameSys.insertSequence(0xB8, 256, 0, 0, kSeqNone, 0, 0, 0); + break; + case 2: + case 3: + gameSys.insertSequence(0xB9, 256, 0, 0, kSeqNone, 0, 0, 0); + break; + } + } + if (!_vm->_timers[5]) { + _vm->_timers[5] = _vm->getRandom(100) + 200; + switch (_vm->getRandom(3)) { + case 0: + _vm->playSound(0xCE, false); + break; + case 1: + _vm->playSound(0xD0, false); + break; + case 2: + _vm->playSound(0xCF, false); + break; + } + } + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } +} + +void Scene23::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS23LookCereals: + _vm->showFullScreenSprite(0x48); + gnap._actionStatus = -1; + break; + case kAS23GrabCereals: + gameSys.setAnimation(0xBE, gnap._id, 0); + gameSys.insertSequence(0xBE, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.requestRemoveSequence(0xBF, 2); + gnap._sequenceDatNum = 0; + gnap._sequenceId = 0xBE; + gnap._actionStatus = kAS23GrabCerealsDone; + break; + case kAS23GrabCerealsDone: + _vm->setGrabCursorSprite(kItemCereals); + gnap._actionStatus = -1; + break; + case kAS23LeaveScene: + _vm->_sceneDone = true; + break; + } + } + + if (gameSys.getAnimationStatus(4) == 2 && _nextStoreClerkSequenceId == -1) { + switch (_vm->getRandom(8)) { + case 0: + case 1: + case 2: + _nextStoreClerkSequenceId = 0xB4; + break; + case 3: + case 4: + case 5: + _nextStoreClerkSequenceId = 0xB5; + break; + default: + _nextStoreClerkSequenceId = 0xB6; + break; + } + gameSys.setAnimation(_nextStoreClerkSequenceId, 1, 4); + gameSys.insertSequence(_nextStoreClerkSequenceId, 1, _currStoreClerkSequenceId, 1, kSeqSyncWait, 0, 0, 0); + _currStoreClerkSequenceId = _nextStoreClerkSequenceId; + _nextStoreClerkSequenceId = -1; + } +} + +/*****************************************************************************/ + +Scene24::Scene24(GnapEngine *vm) : Scene(vm) { + _currWomanSequenceId = -1; + _nextWomanSequenceId = -1; + _boySequenceId = -1; + _girlSequenceId = -1; +} + +int Scene24::init() { + return 0x3B; +} + +void Scene24::updateHotspots() { + _vm->setHotspot(kHS24Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS24ExitCircusWorld, 785, 128, 800, 600, SF_EXIT_R_CURSOR, 8, 7); + _vm->setHotspot(kHS24ExitOutsideGrubCity, 0, 213, 91, 600, SF_EXIT_NW_CURSOR, 1, 8); + _vm->setHotspot(kHS24WalkArea1, 0, 0, 0, 0); + _vm->setHotspot(kHS24WalkArea2, 530, 0, 800, 600); + _vm->setHotspot(kHS24WalkArea3, 0, 0, 800, 517); + _vm->setDeviceHotspot(kHS24Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 7; +} + +void Scene24::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + int counter = 0; + + _vm->playSound(0x10940, true); + _vm->startSoundTimerA(9); + + _vm->_timers[7] = _vm->getRandom(100) + 100; + + gameSys.insertSequence(0x2F, 256, 0, 0, kSeqNone, 0, 0, 0); + + _vm->_timers[4] = _vm->getRandom(20) + 50; + _vm->_timers[5] = _vm->getRandom(20) + 40; + _vm->_timers[6] = _vm->getRandom(50) + 30; + + gameSys.insertSequence(0x36, 20, 0, 0, kSeqNone, 0, 0, 0); + gameSys.insertSequence(0x30, 20, 0, 0, kSeqNone, 0, 0, 0); + gameSys.insertSequence(0x35, 20, 0, 0, kSeqNone, 0, 0, 0); + + _currWomanSequenceId = 0x35; + _girlSequenceId = 0x36; + _boySequenceId = 0x30; + + _vm->queueInsertDeviceIcon(); + + if (_vm->_prevSceneNum == 20) { + gnap.initPos(1, 8, kDirBottomRight); + plat.initPos(2, 8, kDirIdleLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(1, 9), -1, 0x107B9, 1); + plat.walkTo(Common::Point(2, 9), -1, 0x107C2, 1); + } else { + gnap.initPos(8, 8, kDirBottomLeft); + plat.initPos(8, 8, kDirIdleRight); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(2, 8), -1, 0x107BA, 1); + plat.walkTo(Common::Point(3, 8), -1, 0x107C2, 1); + } + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->testWalk(0, 0, -1, -1, -1, -1); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + + case kHS24Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS24Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemJoint) { + gnap.useJointOnPlatypus(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS24ExitCircusWorld: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 25; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS24ExitCircusWorld], 0, 0x107AB, 1); + gnap._actionStatus = kAS24LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS24ExitCircusWorld] + Common::Point(1, 0), -1, 0x107C2, 1); + } + break; + + case kHS24ExitOutsideGrubCity: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 20; + gnap._idleFacing = kDirUpRight; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS24ExitOutsideGrubCity], 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS24LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS24ExitOutsideGrubCity] + Common::Point(1, 0), -1, 0x107C2, 1); + } + break; + + case kHS24WalkArea1: + case kHS24WalkArea2: + case kHS24WalkArea3: + if (gnap._actionStatus == -1) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + } + + updateAnimations(); + + if (!_vm->isSoundPlaying(0x10940)) + _vm->playSound(0x10940, true); + + if (!_vm->_isLeavingScene) { + plat.updateIdleSequence(); + gnap.updateIdleSequence(); + if (!_vm->_timers[4]) { + _vm->_timers[4] = _vm->getRandom(20) + 50; + gameSys.insertSequence(0x37, 20, _girlSequenceId, 20, kSeqSyncWait, 0, 0, 0); + _girlSequenceId = 0x37; + } + if (!_vm->_timers[5]) { + _vm->_timers[5] = _vm->getRandom(20) + 40; + gameSys.insertSequence(0x31, 20, _boySequenceId, 20, kSeqSyncWait, 0, 0, 0); + _boySequenceId = 0x31; + } + if (!_vm->_timers[6]) { + _vm->_timers[6] = _vm->getRandom(50) + 30; + counter = (counter + 1) % 3; + switch (counter) { + case 0: + _nextWomanSequenceId = 0x32; + break; + case 1: + _nextWomanSequenceId = 0x33; + break; + case 2: + _nextWomanSequenceId = 0x34; + break; + } + gameSys.insertSequence(_nextWomanSequenceId, 20, _currWomanSequenceId, 20, kSeqSyncWait, 0, 0, 0); + _currWomanSequenceId = _nextWomanSequenceId; + } + if (!_vm->_timers[7]) { + _vm->_timers[7] = _vm->getRandom(100) + 100; + switch (_vm->getRandom(3)) { + case 0: + gameSys.insertSequence(0x38, 253, 0, 0, kSeqNone, 0, 0, 0); + break; + case 1: + gameSys.insertSequence(0x39, 253, 0, 0, kSeqNone, 0, 0, 0); + break; + case 2: + gameSys.insertSequence(0x3A, 253, 0, 0, kSeqNone, 0, 0, 0); + break; + } + } + _vm->playSoundA(); + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + _vm->gameUpdateTick(); + } +} + +void Scene24::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + if (gnap._actionStatus == kAS24LeaveScene) + _vm->_sceneDone = true; + gnap._actionStatus = -1; + } +} + +/*****************************************************************************/ + +Scene25::Scene25(GnapEngine *vm) : Scene(vm) { + _currTicketVendorSequenceId = -1; + _nextTicketVendorSequenceId = -1; +} + +int Scene25::init() { + return 0x62; +} + +void Scene25::updateHotspots() { + _vm->setHotspot(kHS25Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS25TicketVendor, 416, 94, 574, 324, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 5, 5); + _vm->setHotspot(kHS25ExitOutsideCircusWorld, 0, 519, 205, 600, SF_EXIT_SW_CURSOR, 5, 10); + _vm->setHotspot(kHS25ExitInsideCircusWorld, 321, 70, 388, 350, SF_EXIT_NE_CURSOR, 3, 6); + _vm->setHotspot(kHS25Posters1, 0, 170, 106, 326, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 1, 7); + _vm->setHotspot(kHS25Posters2, 146, 192, 254, 306, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 3, 7); + _vm->setHotspot(kHS25Posters3, 606, 162, 654, 368, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 7, 7); + _vm->setHotspot(kHS25Posters4, 708, 114, 754, 490, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 7, 8); + _vm->setHotspot(kHS25WalkArea1, 0, 0, 800, 439); + _vm->setHotspot(kHS25WalkArea2, 585, 0, 800, 600); + _vm->setDeviceHotspot(kHS25Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 11; +} + +void Scene25::playAnims(int index) { + if (index > 4) + return; + + GameSys& gameSys = *_vm->_gameSys; + + _vm->hideCursor(); + _vm->setGrabCursorSprite(-1); + switch (index) { + case 1: + _vm->_largeSprite = gameSys.createSurface(0x25); + break; + case 2: + _vm->_largeSprite = gameSys.createSurface(0x26); + break; + case 3: + _vm->_largeSprite = gameSys.createSurface(0x27); + break; + case 4: + _vm->_largeSprite = gameSys.createSurface(0x28); + break; + } + gameSys.insertSpriteDrawItem(_vm->_largeSprite, 0, 0, 300); + _vm->delayTicksCursor(5); + while (!_vm->_mouseClickState._left && !_vm->isKeyStatus1(Common::KEYCODE_ESCAPE) && !_vm->isKeyStatus1(Common::KEYCODE_SPACE) && + !_vm->isKeyStatus1(Common::KEYCODE_RETURN) && !_vm->_gameDone) { + _vm->gameUpdateTick(); + } + _vm->_mouseClickState._left = false; + _vm->clearKeyStatus1(Common::KEYCODE_ESCAPE); + _vm->clearKeyStatus1(Common::KEYCODE_RETURN); + _vm->clearKeyStatus1(Common::KEYCODE_SPACE); + gameSys.removeSpriteDrawItem(_vm->_largeSprite, 300); + _vm->delayTicksCursor(5); + _vm->deleteSurface(&_vm->_largeSprite); + _vm->showCursor(); +} + +void Scene25::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->playSound(0x10940, true); + _vm->startSoundTimerA(5); + + _currTicketVendorSequenceId = 0x52; + gameSys.setAnimation(0x52, 39, 3); + gameSys.insertSequence(_currTicketVendorSequenceId, 39, 0, 0, kSeqNone, 0, 0, 0); + + _nextTicketVendorSequenceId = -1; + _vm->_timers[4] = _vm->getRandom(20) + 20; + + _vm->queueInsertDeviceIcon(); + + if (_vm->_prevSceneNum == 24) { + gnap.initPos(5, 11, kDirUpLeft); + plat.initPos(6, 11, kDirIdleRight); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(5, 7), -1, 0x107BA, 1); + plat.walkTo(Common::Point(6, 7), -1, 0x107C2, 1); + } else { + gnap.initPos(5, 6, kDirBottomRight); + plat.initPos(6, 6, kDirIdleLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(5, 8), -1, 0x107B9, 1); + plat.walkTo(Common::Point(6, 8), -1, 0x107C2, 1); + } + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS25Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS25Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemJoint) { + gnap.useJointOnPlatypus(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS25TicketVendor: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemTickets) { + gnap._actionStatus = kAS25ShowTicketToVendor; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS25TicketVendor], 0, gnap.getSequenceId(kGSIdle, Common::Point(9, 4)) | 0x10000, 1); + gnap.playPullOutDevice(); + gnap.playUseDevice(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS25TicketVendor], 6, 1); + _nextTicketVendorSequenceId = 0x5B; + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(6, 1)); + _nextTicketVendorSequenceId = (_vm->getRandom(2) == 1) ? 0x59 : 0x56; + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS25TicketVendor], 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS25TalkTicketVendor; + break; + case GRAB_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS25ExitOutsideCircusWorld: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 24; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS25ExitOutsideCircusWorld], 0, 0x107B4, 1); + gnap._actionStatus = kAS25LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS25ExitOutsideCircusWorld] + Common::Point(1, 0), -1, 0x107C2, 1); + } + break; + + case kHS25ExitInsideCircusWorld: + if (gnap._actionStatus < 0) { + if (_vm->isFlag(kGFNeedleTaken)) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 26; + _vm->_hotspots[kHS25WalkArea1]._flags |= SF_WALKABLE; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS25ExitInsideCircusWorld], 0, 0x107B1, 1); + gnap._actionStatus = kAS25LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS25ExitInsideCircusWorld] + Common::Point(1, 0), -1, 0x107C2, 1); + _vm->_hotspots[kHS25WalkArea1]._flags &= ~SF_WALKABLE; + } else { + _vm->_hotspots[kHS25WalkArea1]._flags |= SF_WALKABLE; + gnap.walkTo(Common::Point(4, 5), 0, 0x107BB, 1); + gnap._actionStatus = kAS25EnterCircusWihoutTicket; + _vm->_hotspots[kHS25WalkArea1]._flags &= ~SF_WALKABLE; + } + } + break; + + case kHS25Posters1: + case kHS25Posters2: + case kHS25Posters3: + case kHS25Posters4: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.walkTo(_vm->_hotspotsWalkPos[_vm->_sceneClickedHotspot], -1, -1, 1); + if (_vm->_sceneClickedHotspot == 5 || _vm->_sceneClickedHotspot == 6) + gnap._idleFacing = kDirUpLeft; + else if (_vm->_sceneClickedHotspot == 8) + gnap._idleFacing = kDirBottomRight; + else + gnap._idleFacing = kDirUpRight; + gnap.playIdle(); + playAnims(8 - _vm->_sceneClickedHotspot + 1); + break; + case GRAB_CURSOR: + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playMoan2(); + break; + } + } + } + break; + + case kHS25WalkArea1: + case kHS25WalkArea2: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + } + + updateAnimations(); + + if (!_vm->_isLeavingScene) { + plat.updateIdleSequence(); + gnap.updateIdleSequence(); + if (!_vm->_timers[4] && _nextTicketVendorSequenceId == -1 && gnap._actionStatus == -1) { + _vm->_timers[4] = _vm->getRandom(20) + 20; + switch (_vm->getRandom(13)) { + case 0: + _nextTicketVendorSequenceId = 0x54; + break; + case 1: + _nextTicketVendorSequenceId = 0x58; + break; + case 2: + _nextTicketVendorSequenceId = 0x55; + break; + case 3: + _nextTicketVendorSequenceId = 0x5A; + break; + case 4: + case 5: + case 6: + case 7: + _nextTicketVendorSequenceId = 0x5B; + break; + case 8: + case 9: + case 10: + case 11: + _nextTicketVendorSequenceId = 0x5C; + break; + case 12: + _nextTicketVendorSequenceId = 0x5D; + break; + default: + _nextTicketVendorSequenceId = 0x52; + break; + } + } + _vm->playSoundA(); + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } +} + +void Scene25::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS25TalkTicketVendor: + _nextTicketVendorSequenceId = (_vm->getRandom(2) == 1) ? 0x57 : 0x5F; + gnap._actionStatus = -1; + break; + case kAS25EnterCircusWihoutTicket: + _nextTicketVendorSequenceId = 0x5E; + gameSys.setAnimation(0x5E, 39, 0); + gameSys.setAnimation(_nextTicketVendorSequenceId, 39, 3); + gameSys.insertSequence(_nextTicketVendorSequenceId, 39, _currTicketVendorSequenceId, 39, kSeqSyncExists, 0, 0, 0); + gameSys.insertSequence(0x60, 2, 0, 0, kSeqNone, 0, 0, 0); + _currTicketVendorSequenceId = _nextTicketVendorSequenceId; + _nextTicketVendorSequenceId = -1; + _vm->_hotspots[kHS25WalkArea1]._flags |= SF_WALKABLE; + gnap.playIdle(); + gnap.walkTo(_vm->_hotspotsWalkPos[3], -1, 0x107BB, 1); + _vm->_hotspots[kHS25WalkArea1]._flags &= ~SF_WALKABLE; + gnap._actionStatus = kAS25EnterCircusWihoutTicketDone; + break; + case kAS25EnterCircusWihoutTicketDone: + gnap._actionStatus = -1; + break; + case kAS25ShowTicketToVendor: + _vm->setGrabCursorSprite(-1); + _vm->invRemove(kItemTickets); + _vm->setFlag(kGFNeedleTaken); + gameSys.setAnimation(0x61, 40, 0); + gameSys.insertSequence(0x61, 40, 0, 0, kSeqNone, 0, 0, 0); + gnap._actionStatus = kAS25ShowTicketToVendorDone; + break; + case kAS25ShowTicketToVendorDone: + _nextTicketVendorSequenceId = 0x53; + break; + case kAS25LeaveScene: + _vm->_sceneDone = true; + break; + } + } + + if (gameSys.getAnimationStatus(3) == 2) { + if (_nextTicketVendorSequenceId == 0x53) { + gameSys.insertSequence(_nextTicketVendorSequenceId, 39, _currTicketVendorSequenceId, 39, kSeqSyncWait, 0, 0, 0); + _currTicketVendorSequenceId = _nextTicketVendorSequenceId; + _nextTicketVendorSequenceId = -1; + gnap._actionStatus = -1; + } else if (_nextTicketVendorSequenceId != -1) { + gameSys.setAnimation(_nextTicketVendorSequenceId, 39, 3); + gameSys.insertSequence(_nextTicketVendorSequenceId, 39, _currTicketVendorSequenceId, 39, kSeqSyncWait, 0, 0, 0); + _currTicketVendorSequenceId = _nextTicketVendorSequenceId; + _nextTicketVendorSequenceId = -1; + } + } +} + +/*****************************************************************************/ + +Scene26::Scene26(GnapEngine *vm) : Scene(vm) { + _currKidSequenceId = -1; + _nextKidSequenceId = -1; +} + +int Scene26::init() { + return _vm->isFlag(kGFUnk23) ? 0x61 : 0x60; +} + +void Scene26::updateHotspots() { + _vm->setHotspot(kHS26Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS26ExitOutsideCircusWorld, 0, 590, 300, 600, SF_EXIT_D_CURSOR | SF_WALKABLE, 1, 10); + _vm->setHotspot(kHS26ExitOutsideClown, 200, 265, 265, 350, SF_EXIT_U_CURSOR, 3, 8); + _vm->setHotspot(kHS26ExitArcade, 0, 295, 150, 400, SF_EXIT_NW_CURSOR, 2, 8); + _vm->setHotspot(kHS26ExitElephant, 270, 290, 485, 375, SF_EXIT_U_CURSOR, 5, 8); + _vm->setHotspot(kHS26ExitBeerStand, 530, 290, 620, 350, SF_EXIT_NE_CURSOR, 5, 8); + _vm->setHotspot(kHS26WalkArea1, 0, 0, 800, 500); + _vm->setHotspot(kHS26WalkArea2, 281, 0, 800, 600); + _vm->setDeviceHotspot(kHS26Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 9; +} + +void Scene26::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->startSoundTimerB(7); + _vm->playSound(0x1093B, true); + + _currKidSequenceId = 0x5B; + _nextKidSequenceId = -1; + gameSys.setAnimation(0x5B, 160, 3); + gameSys.insertSequence(_currKidSequenceId, 160, 0, 0, kSeqNone, 0, 0, 0); + + _vm->_timers[5] = _vm->getRandom(20) + 50; + _vm->_timers[4] = _vm->getRandom(20) + 50; + _vm->_timers[6] = _vm->getRandom(50) + 100; + + _vm->queueInsertDeviceIcon(); + + gameSys.insertSequence(0x58, 40, 0, 0, kSeqLoop, 0, 0, 0); + gameSys.insertSequence(0x5C, 40, 0, 0, kSeqLoop, 0, 0, 0); + gameSys.insertSequence(0x5D, 40, 0, 0, kSeqLoop, 0, 0, 0); + gameSys.insertSequence(0x5E, 40, 0, 0, kSeqLoop, 0, 0, 0); + + if (_vm->_prevSceneNum == 25) { + gnap.initPos(-1, 8, kDirBottomRight); + plat.initPos(-2, 8, kDirIdleLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(2, 8), -1, 0x107B9, 1); + plat.walkTo(Common::Point(1, 8), -1, 0x107C2, 1); + } else { + gnap.initPos(2, 8, kDirBottomRight); + plat.initPos(3, 8, kDirIdleLeft); + _vm->endSceneInit(); + } + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS26Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS26Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemJoint) { + gnap.useJointOnPlatypus(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS26ExitOutsideCircusWorld: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 25; + gnap.walkTo(Common::Point(-1, _vm->_hotspotsWalkPos[kHS26ExitOutsideCircusWorld].y), 0, 0x107AE, 1); + gnap._actionStatus = kAS26LeaveScene; + } + break; + + case kHS26ExitOutsideClown: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 27; + gnap.walkTo(Common::Point(-1, _vm->_hotspotsWalkPos[kHS26ExitOutsideClown].y), 0, 0x107BC, 1); + gnap._actionStatus = kAS26LeaveScene; + } + break; + + case kHS26ExitArcade: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 29; + gnap.walkTo(Common::Point(-1, _vm->_hotspotsWalkPos[kHS26ExitArcade].y), 0, 0x107BC, 1); + gnap._actionStatus = kAS26LeaveScene; + } + break; + + case kHS26ExitElephant: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 30; + gnap.walkTo(Common::Point(-1, _vm->_hotspotsWalkPos[kHS26ExitElephant].y), 0, 0x107BC, 1); + gnap._actionStatus = kAS26LeaveScene; + } + break; + + case kHS26ExitBeerStand: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 31; + gnap.walkTo(Common::Point(-1, _vm->_hotspotsWalkPos[kHS26ExitBeerStand].y), 0, 0x107BB, 1); + gnap._actionStatus = kAS26LeaveScene; + } + break; + + case kHS26WalkArea1: + case kHS26WalkArea2: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + } + + updateAnimations(); + + if (!_vm->isSoundPlaying(0x1093B)) + _vm->playSound(0x1093B, true); + + if (!_vm->_isLeavingScene) { + plat.updateIdleSequence(); + gnap.updateIdleSequence(); + if (!_vm->_timers[5] && _nextKidSequenceId == -1) { + _vm->_timers[5] = _vm->getRandom(20) + 50; + if (_vm->getRandom(5) != 0) + _nextKidSequenceId = 0x5B; + else + _nextKidSequenceId = 0x5A; + } + if (!_vm->_timers[4]) { + _vm->_timers[4] = _vm->getRandom(20) + 130; + gameSys.insertSequence(0x59, 40, 0, 0, kSeqNone, 0, 0, 0); + } + if (!_vm->_timers[6]) { + _vm->_timers[6] = _vm->getRandom(50) + 100; + gameSys.insertSequence(0x5F, 40, 0, 0, kSeqNone, 0, 0, 0); + } + _vm->playSoundB(); + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } +} + +void Scene26::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + if (gnap._actionStatus == kAS26LeaveScene) + _vm->_sceneDone = true; + gnap._actionStatus = -1; + } + + if (gameSys.getAnimationStatus(3) == 2 && _nextKidSequenceId != -1) { + gameSys.insertSequence(_nextKidSequenceId, 160, _currKidSequenceId, 160, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextKidSequenceId, 160, 3); + _currKidSequenceId = _nextKidSequenceId; + _nextKidSequenceId = -1; + } +} + +/*****************************************************************************/ + +Scene27::Scene27(GnapEngine *vm) : Scene(vm) { + _nextJanitorSequenceId = -1; + _currJanitorSequenceId = -1; +} + +int Scene27::init() { + return 0xD5; +} + +void Scene27::updateHotspots() { + _vm->setHotspot(kHS27Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS27Janitor, 488, 204, 664, 450, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 8, 8); + _vm->setHotspot(kHS27Bucket, 129, 406, 186, 453, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 3, 6); + _vm->setHotspot(kHS27ExitCircus, 200, 585, 700, 600, SF_EXIT_D_CURSOR | SF_WALKABLE, 5, 9); + _vm->setHotspot(kHS27ExitArcade, 0, 0, 15, 600, SF_EXIT_L_CURSOR, 0, 6); + _vm->setHotspot(kHS27ExitBeerStand, 785, 0, 800, 600, SF_EXIT_R_CURSOR, 11, 7); + _vm->setHotspot(kHS27ExitClown, 340, 240, 460, 420, SF_EXIT_U_CURSOR, 6, 8); + _vm->setHotspot(kHS27WalkArea1, 0, 0, 800, 507); + _vm->setDeviceHotspot(kHS27Device, -1, -1, -1, -1); + if (_vm->isFlag(kGFUnk13)) + _vm->_hotspots[kHS27Bucket]._flags = SF_DISABLED; + _vm->_hotspotsCount = 9; +} + +void Scene27::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->playSound(0x1093B, true); + _vm->startSoundTimerB(4); + _vm->_timers[7] = _vm->getRandom(100) + 300; + _vm->queueInsertDeviceIcon(); + + if (!_vm->isFlag(kGFUnk13)) + gameSys.insertSequence(0xD3, 39, 0, 0, kSeqNone, 0, 0, 0); + + gameSys.insertSequence(0xCB, 39, 0, 0, kSeqNone, 0, 0, 0); + + _currJanitorSequenceId = 0xCB; + _nextJanitorSequenceId = -1; + + gameSys.setAnimation(0xCB, 39, 3); + _vm->_timers[5] = _vm->getRandom(20) + 60; + + switch (_vm->_prevSceneNum) { + case 26: + gnap.initPos(7, 12, kDirBottomRight); + plat.initPos(6, 12, kDirIdleLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(7, 8), -1, 0x107B9, 1); + plat.walkTo(Common::Point(6, 8), -1, 0x107C2, 1); + break; + case 29: + gnap.initPos(-1, 8, kDirBottomRight); + plat.initPos(-1, 9, kDirIdleLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(3, 8), -1, 0x107B9, 1); + plat.walkTo(Common::Point(3, 9), -1, 0x107C2, 1); + break; + case 31: + gnap.initPos(12, 8, kDirBottomLeft); + plat.initPos(12, 9, kDirIdleRight); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(8, 8), -1, 0x107BA, 1); + plat.walkTo(Common::Point(8, 9), -1, 0x107C2, 1); + break; + default: + gnap.initPos(6, 8, kDirBottomRight); + plat.initPos(5, 9, kDirIdleLeft); + _vm->endSceneInit(); + break; + } + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + _vm->_sceneClickedHotspot = -1; + if (gnap._actionStatus < 0) + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS27Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS27Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemJoint) { + gnap.useJointOnPlatypus(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS27Janitor: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemPicture) { + gnap._idleFacing = kDirUpLeft; + if (gnap.walkTo(_vm->_hotspotsWalkPos[kHS27Janitor], 0, 0x107BC, 1)) + gnap._actionStatus = kAS27ShowPictureToJanitor; + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS27Janitor], 7, 3); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(6, 3)); + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpLeft; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS27Janitor], 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS27TalkJanitor; + break; + case GRAB_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS27Bucket: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowItem(_vm->_grabCursorSpriteIndex, 3, 3); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(3, 3)); + break; + case GRAB_CURSOR: + gnap._idleFacing = kDirUpLeft; + gnap.walkTo(gnap._pos, 0, gnap.getSequenceId(kGSIdle, _vm->_hotspotsWalkPos[kHS27Bucket]) | 0x10000, 1); + gnap._actionStatus = kAS27GrabBucket; + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS27ExitCircus: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 26; + gnap.walkTo(Common::Point(-1, _vm->_hotspotsWalkPos[kHS27ExitCircus].y), 0, 0x107AE, 1); + gnap._actionStatus = kAS27LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS27ExitCircus] + Common::Point(1, 0), -1, 0x107C7, 1); + } + break; + + case kHS27ExitArcade: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 29; + gnap.walkTo(Common::Point(-1, _vm->_hotspotsWalkPos[kHS27ExitArcade].y), 0, 0x107AF, 1); + gnap._actionStatus = kAS27LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS27ExitArcade] + Common::Point(0, 1), -1, 0x107CF, 1); + } + break; + + case kHS27ExitBeerStand: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 31; + gnap.walkTo(Common::Point(-1, _vm->_hotspotsWalkPos[kHS27ExitBeerStand].y), 0, 0x107AB, 1); + gnap._actionStatus = kAS27LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS27ExitBeerStand] + Common::Point(0, 1), -1, 0x107CD, 1); + } + break; + + case kHS27ExitClown: + if (gnap._actionStatus < 0) { + if (_vm->isFlag(kGFPlatypus)) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 28; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS27ExitClown], 0, 0x107AD, 1); + gnap._actionStatus = kAS27LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS27ExitClown] + Common::Point(1, 0), -1, 0x107C4, 1); + } else { + _vm->_hotspots[kHS27WalkArea1]._flags |= SF_WALKABLE; + gnap.walkTo(Common::Point(_vm->_hotspotsWalkPos[kHS27ExitClown].x, 7), 0, 0x107BC, 1); + _vm->_hotspots[kHS27WalkArea1]._flags &= SF_WALKABLE; + gnap._actionStatus = kAS27TryEnterClownTent; + } + } + break; + + case kHS27WalkArea1: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left && gnap._actionStatus < 0) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + + } + + updateAnimations(); + + if (!_vm->isSoundPlaying(0x1093B)) + _vm->playSound(0x1093B, true); + + if (!_vm->_isLeavingScene) { + plat.updateIdleSequence(); + if (gnap._actionStatus < 0) + gnap.updateIdleSequence(); + if (!_vm->_timers[5]) { + _vm->_timers[5] = _vm->getRandom(20) + 60; + if (gnap._actionStatus < 0) { + if (_vm->getRandom(3) != 0) + _nextJanitorSequenceId = 0xCB; + else + _nextJanitorSequenceId = 0xCF; + } + } + if (!_vm->_timers[7]) { + _vm->_timers[7] = _vm->getRandom(100) + 300; + if (gnap._actionStatus < 0) + gameSys.insertSequence(0xD4, 120, 0, 0, kSeqNone, 0, 0, 0); + } + _vm->playSoundB(); + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } +} + +void Scene27::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS27TalkJanitor: + switch (_vm->getRandom(3)) { + case 0: + _nextJanitorSequenceId = 0xCC; + break; + case 1: + _nextJanitorSequenceId = 0xCD; + break; + case 2: + _nextJanitorSequenceId = 0xCE; + break; + } + break; + case kAS27GrabBucket: + gnap.playPullOutDevice(); + gnap.playUseDevice(); + _vm->_hotspots[kHS27Bucket]._flags = SF_DISABLED; + _vm->invAdd(kItemEmptyBucket); + _vm->setFlag(kGFUnk13); + gameSys.setAnimation(0xD2, 39, 0); + gameSys.insertSequence(0xD2, 39, 211, 39, kSeqSyncWait, 0, 0, 0); + gnap._actionStatus = kAS27GrabBucketDone; + break; + case kAS27GrabBucketDone: + _vm->setGrabCursorSprite(kItemEmptyBucket); + gnap._actionStatus = -1; + break; + case kAS27ShowPictureToJanitor: + _nextJanitorSequenceId = 0xD0; + break; + case kAS27TryEnterClownTent: + _nextJanitorSequenceId = 0xD1; + gameSys.insertSequence(0xD1, 39, _currJanitorSequenceId, 39, kSeqSyncExists, 0, 0, 0); + gameSys.setAnimation(_nextJanitorSequenceId, 39, 3); + gameSys.setAnimation(_nextJanitorSequenceId, 39, 0); + _currJanitorSequenceId = _nextJanitorSequenceId; + _nextJanitorSequenceId = -1; + gnap._actionStatus = kAS27TryEnterClownTentDone; + break; + case kAS27TryEnterClownTentDone: + _vm->_hotspots[kHS27WalkArea1]._flags |= SF_WALKABLE; + gnap.walkTo(Common::Point(_vm->_hotspotsWalkPos[7].x, 9), -1, 0x107BC, 1); + _vm->_hotspots[kHS27WalkArea1]._flags &= ~SF_WALKABLE; + gnap._actionStatus = -1; + break; + case kAS27EnterClownTent: + gnap.walkTo(gnap._pos, 0, 0x107B2, 1); + gnap._actionStatus = kAS27LeaveScene; + break; + case kAS27LeaveScene: + _vm->_sceneDone = true; + break; + } + } + + if (gameSys.getAnimationStatus(3) == 2) { + switch (_nextJanitorSequenceId) { + case -1: + _nextJanitorSequenceId = 0xCB; + gameSys.insertSequence(0xCB, 39, _currJanitorSequenceId, 39, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextJanitorSequenceId, 39, 3); + _currJanitorSequenceId = _nextJanitorSequenceId; + _nextJanitorSequenceId = -1; + break; + case 0xCC: + case 0xCD: + case 0xCE: + gnap._actionStatus = -1; + gameSys.insertSequence(_nextJanitorSequenceId, 39, _currJanitorSequenceId, 39, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextJanitorSequenceId, 39, 3); + gameSys.setAnimation(_nextJanitorSequenceId, 39, 0); + _currJanitorSequenceId = _nextJanitorSequenceId; + _nextJanitorSequenceId = -1; + break; + case 0xD0: + // Show picture to janitor + gnap.playPullOutDevice(); + gnap.playUseDevice(); + gameSys.insertSequence(_nextJanitorSequenceId, 39, _currJanitorSequenceId, 39, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextJanitorSequenceId, 39, 0); + gnap._actionStatus = kAS27EnterClownTent; + _currJanitorSequenceId = _nextJanitorSequenceId; + _nextJanitorSequenceId = -1; + _vm->setFlag(kGFPlatypus); + _vm->setGrabCursorSprite(-1); + _vm->invRemove(kItemPicture); + _vm->_newSceneNum = 28; + break; + default: + gameSys.insertSequence(_nextJanitorSequenceId, 39, _currJanitorSequenceId, 39, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextJanitorSequenceId, 39, 3); + _currJanitorSequenceId = _nextJanitorSequenceId; + _nextJanitorSequenceId = -1; + break; + } + } +} + +/*****************************************************************************/ + +Scene28::Scene28(GnapEngine *vm) : Scene(vm) { + _currClownSequenceId = -1; + _nextClownSequenceId = -1; + _clownTalkCtr = 0; +} + +int Scene28::init() { + return 0x125; +} + +void Scene28::updateHotspots() { + _vm->setHotspot(kHS28Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS28Horn, 148, 352, 215, 383, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 1, 7); + _vm->setHotspot(kHS28Clown, 130, 250, 285, 413, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 3, 5); + _vm->setHotspot(kHS28ExitOutsideClown, 660, 190, 799, 400, SF_EXIT_R_CURSOR, 9, 6); + _vm->setHotspot(kHS28EmptyBucket, 582, 421, 643, 478, SF_WALKABLE | SF_DISABLED, 9, 7); + _vm->setHotspot(kHS28WalkArea1, 0, 0, 799, 523); + _vm->setHotspot(kHS28WalkArea2, 0, 0, 0, 0, 7, SF_DISABLED); + _vm->setDeviceHotspot(kHS28Device, -1, -1, -1, -1); + if (_vm->invHas(kItemHorn)) + _vm->_hotspots[kHS28Horn]._flags = SF_DISABLED; + if (_vm->isFlag(kGFUnk22)) + _vm->_hotspots[kHS28EmptyBucket]._flags = SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR; + _vm->_hotspotsCount = 8; +} + +void Scene28::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->playSound(0x1093C, true); + _nextClownSequenceId = -1; + _vm->queueInsertDeviceIcon(); + gameSys.insertSequence(0x124, 255, 0, 0, kSeqNone, 0, 0, 0); + + if (_vm->isFlag(kGFUnk22)) + gameSys.insertSequence(0x112, 99, 0, 0, kSeqNone, 0, 0, 0); + + if (_vm->isFlag(kGFMudTaken)) { + if (_vm->isFlag(kGFUnk21)) { + gameSys.setAnimation(0x11C, 39, 3); + gameSys.insertSequence(0x11C, 39, 0, 0, kSeqNone, 0, 0, 0); + if (!_vm->invHas(kItemHorn)) + gameSys.insertSequence(0x118, 59, 0, 0, kSeqNone, 0, 0, 0); + _currClownSequenceId = 0x11C; + } else { + _currClownSequenceId = 0x11B; + gameSys.setAnimation(0x11B, 39, 3); + gameSys.insertSequence(_currClownSequenceId, 39, 0, 0, kSeqNone, 0, 0, 0); + _vm->_timers[4] = _vm->getRandom(20) + 80; + } + gnap.initPos(8, 8, kDirBottomLeft); + plat.initPos(9, 8, kDirIdleRight); + _vm->endSceneInit(); + } else { + gameSys.insertSequence(0x11B, 39, 0, 0, kSeqNone, 0, 0, 0); + gnap.initPos(8, 8, kDirBottomLeft); + plat.initPos(9, 8, kDirIdleRight); + _vm->endSceneInit(); + _vm->playSequences(0xF7, 0x121, 0x122, 0x123); + _currClownSequenceId = 0x115; + _vm->setFlag(kGFMudTaken); + gameSys.setAnimation(0x115, 39, 3); + gameSys.insertSequence(_currClownSequenceId, 39, 0x11B, 39, kSeqSyncWait, 0, 0, 0); + _nextClownSequenceId = -1; + _vm->_timers[4] = _vm->getRandom(20) + 80; + gnap._actionStatus = kAS28GnapWaiting; + while (gameSys.getAnimationStatus(3) != 2 && !_vm->_gameDone) { + _vm->gameUpdateTick(); + _vm->updateMouseCursor(); + } + gnap._actionStatus = -1; + } + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS28Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS28Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemJoint) { + gnap.useJointOnPlatypus(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS28Horn: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(2, 8), 3, 4); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(2, 4)); + break; + case GRAB_CURSOR: + if (_vm->isFlag(kGFUnk21)) { + if (!_vm->invHas(kItemHorn)) { + gnap.walkTo(gnap._pos, 0, gnap.getSequenceId(kGSIdle, _vm->_hotspotsWalkPos[kHS28Horn]) | 0x10000, 1); + gnap._actionStatus = kAS28GrabHornSuccess; + } + } else { + gnap._idleFacing = kDirUpLeft; + gnap.walkTo(Common::Point(2, 8), 0, 0x107BB, 1); + _vm->_hotspots[kHS28WalkArea1]._flags |= SF_WALKABLE; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS28Horn], 0, 0x107BB, 1); + _vm->_hotspots[kHS28WalkArea1]._flags &= ~SF_WALKABLE; + gnap._actionStatus = kAS28GrabHornFails; + } + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS28Clown: + if (gnap._actionStatus < 0) { + if (_vm->isFlag(kGFUnk21)) { + if (_vm->_verbCursor == LOOK_CURSOR) + gnap.playScratchingHead(Common::Point(5, 2)); + else + gnap.playImpossible(); + } else if (_vm->_grabCursorSpriteIndex == kItemBucketWithBeer) { + gnap._idleFacing = kDirUpLeft; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS28Clown], 0, 0x107BC, 1); + gnap.playPullOutDevice(); + gnap.playUseDevice(); + gnap._actionStatus = kAS28UseBeerBucketWithClown; + } else if (_vm->_grabCursorSpriteIndex == kItemBucketWithPill) { + gnap._idleFacing = kDirUpLeft; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS28Clown], 0, 0x107BC, 1); + gnap.playPullOutDevice(); + gnap.playUseDevice(); + gnap._actionStatus = kAS28UsePillBucketWithClown; + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS28Clown], 2, 4); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(5, 2)); + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpLeft; + gnap.walkTo(Common::Point(5, 8), 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS28TalkClown; + break; + case GRAB_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS28ExitOutsideClown: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 27; + _vm->_hotspots[kHS28WalkArea1]._flags |= SF_WALKABLE; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS28ExitOutsideClown], 0, 0x107BF, 1); + gnap._actionStatus = kAS28LeaveScene; + _vm->_hotspots[kHS28WalkArea1]._flags &= ~SF_WALKABLE; + plat.walkTo(_vm->_hotspotsWalkPos[kHS28ExitOutsideClown] + Common::Point(-1, 0), -1, 0x107C2, 1); + } + break; + + case kHS28EmptyBucket: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowItem(_vm->_grabCursorSpriteIndex, 8, 6); + } else if (_vm->isFlag(kGFUnk21)) { + gnap.playImpossible(Common::Point(8, 6)); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(8, 6)); + break; + case GRAB_CURSOR: + gnap.walkTo(gnap._pos, 0, gnap.getSequenceId(kGSIdle, _vm->_hotspotsWalkPos[kHS28EmptyBucket]) | 0x10000, 1); + gnap._actionStatus = kAS28GrabEmptyBucket; + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS28WalkArea1: + case kHS28WalkArea2: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left && gnap._actionStatus < 0) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + } + + updateAnimations(); + + if (!_vm->isSoundPlaying(0x1093C)) + _vm->playSound(0x1093C, true); + + if (!_vm->_isLeavingScene) { + plat.updateIdleSequence(); + gnap.updateIdleSequence(); + if (!_vm->_timers[4]) { + _vm->_timers[4] = _vm->getRandom(20) + 80; + if (gnap._actionStatus < 0 && !_vm->isFlag(kGFUnk21)) + _nextClownSequenceId = 0x114; + } + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } +} + +void Scene28::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS28UseBeerBucketWithClown: + _vm->setFlag(kGFUnk22); + _nextClownSequenceId = 0x113; + _vm->invRemove(kItemBucketWithBeer); + updateHotspots(); + break; + case kAS28UsePillBucketWithClown: + _nextClownSequenceId = 0x116; + _vm->invRemove(kItemBucketWithPill); + _vm->setFlag(kGFUnk22); + _vm->setFlag(kGFUnk21); + updateHotspots(); + break; + case kAS28GrabHornFails: + _nextClownSequenceId = 0x119; + break; + case kAS28GrabHornSuccess: + gnap.playPullOutDevice(); + gnap.playUseDevice(); + gameSys.setAnimation(0x117, 59, 0); + gameSys.insertSequence(0x117, 59, 280, 59, kSeqSyncWait, 0, 0, 0); + gnap._actionStatus = kAS28GrabHornSuccessDone; + break; + case kAS28GrabHornSuccessDone: + _vm->hideCursor(); + _vm->setGrabCursorSprite(-1); + _vm->addFullScreenSprite(0xF6, 255); + gameSys.setAnimation(0x120, 256, 0); + gameSys.insertSequence(0x120, 256, 0, 0, kSeqNone, 0, 0, 0); + while (gameSys.getAnimationStatus(0) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + + _vm->removeFullScreenSprite(); + _vm->showCursor(); + _vm->setGrabCursorSprite(kItemHorn); + _vm->invAdd(kItemHorn); + updateHotspots(); + gnap._actionStatus = -1; + break; + case kAS28GrabEmptyBucket: + gnap.playPullOutDevice(); + gnap.playUseDevice(); + gameSys.setAnimation(0x111, 99, 0); + gameSys.insertSequence(0x111, 99, 274, 99, kSeqSyncWait, 0, 0, 0); + gnap._actionStatus = kAS28GrabEmptyBucketDone; + break; + case kAS28GrabEmptyBucketDone: + _vm->setGrabCursorSprite(kItemEmptyBucket); + _vm->clearFlag(kGFUnk22); + updateHotspots(); + _vm->invAdd(kItemEmptyBucket); + gnap._actionStatus = -1; + break; + case kAS28GrabHornFailsDone: + gameSys.insertSequence(0x107B5, gnap._id, 281, 39, kSeqSyncWait, 0, 75 * gnap._pos.x - gnap._gridX, 48 * gnap._pos.y - gnap._gridY); + gnap._sequenceId = 0x7B5; + gnap._sequenceDatNum = 1; + gameSys.insertSequence(0x11B, 39, 0, 0, kSeqNone, 0, 0, 0); + _currClownSequenceId = 0x11B; + _nextClownSequenceId = -1; + gnap._actionStatus = -1; + gnap.walkTo(Common::Point(2, 8), -1, 0x107BB, 1); + break; + case kAS28TalkClown: + // The original was only using the first two sequences, + // due to a bug. + _clownTalkCtr = (_clownTalkCtr + 1) % 3; + if (_clownTalkCtr == 0) + _nextClownSequenceId = 0x11D; + else if (_clownTalkCtr == 1) + _nextClownSequenceId = 0x11E; + else if (_clownTalkCtr == 2) + _nextClownSequenceId = 0x11F; + break; + case kAS28GnapWaiting: + gnap._actionStatus = -1; + break; + case kAS28LeaveScene: + _vm->_sceneDone = true; + break; + } + } + + if (gameSys.getAnimationStatus(3) == 2) { + switch (_nextClownSequenceId) { + case 0x113: + _vm->setGrabCursorSprite(-1); + gameSys.setAnimation(_nextClownSequenceId, 39, 0); + gameSys.insertSequence(0x112, 99, 0, 0, kSeqNone, _vm->getSequenceTotalDuration(_nextClownSequenceId), 0, 0); + gameSys.insertSequence(_nextClownSequenceId, 39, _currClownSequenceId, 39, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x11B, 39, _nextClownSequenceId, 39, kSeqSyncWait, 0, 0, 0); + _currClownSequenceId = 0x11B; + _nextClownSequenceId = -1; + gnap._actionStatus = kAS28GnapWaiting; + break; + case 0x116: + _vm->setGrabCursorSprite(-1); + gameSys.setAnimation(_nextClownSequenceId, 39, 0); + gameSys.insertSequence(0x112, 99, 0, 0, kSeqNone, _vm->getSequenceTotalDuration(_nextClownSequenceId), 0, 0); + gameSys.insertSequence(_nextClownSequenceId, 39, _currClownSequenceId, 39, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x11C, 39, _nextClownSequenceId, 39, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0x118, 59, 0, 0, kSeqNone, _vm->getSequenceTotalDuration(_nextClownSequenceId), 0, 0); + _currClownSequenceId = _nextClownSequenceId; + _nextClownSequenceId = -1; + gnap._actionStatus = kAS28GnapWaiting; + break; + case 0x11D: + case 0x11E: + case 0x11F: + gnap._actionStatus = -1; + break; + case 0x119: + gameSys.insertSequence(_nextClownSequenceId, 39, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextClownSequenceId, 39, 0); + gameSys.removeSequence(_currClownSequenceId, 39, true); + gnap._actionStatus = kAS28GrabHornFailsDone; + gnap._sequenceId = _nextClownSequenceId; + gnap._sequenceDatNum = 0; + _nextClownSequenceId = -1; + break; + } + if (_nextClownSequenceId != -1) { + gameSys.insertSequence(_nextClownSequenceId, 39, _currClownSequenceId, 39, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextClownSequenceId, 39, 3); + _currClownSequenceId = _nextClownSequenceId; + _nextClownSequenceId = -1; + } + } +} + +/*****************************************************************************/ + +Scene29::Scene29(GnapEngine *vm) : Scene(vm) { + _currMonkeySequenceId = -1; + _nextMonkeySequenceId = -1; + _currManSequenceId = -1; + _nextManSequenceId = -1; +} + +int Scene29::init() { + return 0xF6; +} + +void Scene29::updateHotspots() { + _vm->setHotspot(kHS29Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS29Monkey, 410, 374, 518, 516, SF_WALKABLE | SF_DISABLED, 3, 7); + _vm->setHotspot(kHS29ExitCircus, 150, 585, 650, 600, SF_EXIT_D_CURSOR | SF_WALKABLE, 5, 9); + _vm->setHotspot(kHS29ExitOutsideClown, 785, 0, 800, 600, SF_EXIT_R_CURSOR | SF_WALKABLE, 11, 9); + _vm->setHotspot(kHS29Arcade, 88, 293, 155, 384, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 3, 8); + _vm->setHotspot(kHS29WalkArea1, 0, 0, 800, 478); + _vm->setDeviceHotspot(kHS29Device, -1, -1, -1, -1); + if (_vm->invHas(kItemHorn)) + _vm->_hotspots[kHS29Monkey]._flags = SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR; + _vm->_hotspotsCount = 7; +} + +void Scene29::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->playSound(0x1093B, true); + _vm->startSoundTimerB(6); + _vm->queueInsertDeviceIcon(); + + if (_vm->invHas(kItemHorn)) { + _currMonkeySequenceId = 0xE8; + _nextMonkeySequenceId = -1; + gameSys.setAnimation(0xE8, 159, 4); + gameSys.insertSequence(_currMonkeySequenceId, 159, 0, 0, kSeqNone, 0, 0, 0); + gameSys.insertSequence(0xED, 39, 0, 0, kSeqNone, 0, 0, 0); + _currManSequenceId = 0xED; + _nextManSequenceId = -1; + gameSys.setAnimation(0xED, 39, 3); + _vm->_timers[4] = _vm->getRandom(20) + 60; + } else { + gameSys.insertSequence(0xF4, 19, 0, 0, kSeqNone, 0, 0, 0); + gameSys.setAnimation(0, 0, 4); + gameSys.insertSequence(0xED, 39, 0, 0, kSeqNone, 0, 0, 0); + gameSys.setAnimation(0, 0, 3); + } + + gameSys.insertSequence(0xF3, 39, 0, 0, kSeqLoop, 0, 0, 0); + gameSys.insertSequence(0xF5, 38, 0, 0, kSeqLoop, 0, 0, 0); + + if (_vm->_prevSceneNum == 27) { + gnap.initPos(12, 7, kDirBottomRight); + plat.initPos(12, 8, kDirIdleLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(8, 7), -1, 0x107B9, 1); + plat.walkTo(Common::Point(8, 8), -1, 0x107C2, 1); + } else { + gnap.initPos(-1, 7, kDirBottomRight); + plat.initPos(-2, 7, kDirIdleLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(2, 7), -1, 0x107B9, 1); + plat.walkTo(Common::Point(1, 7), -1, 0x107C2, 1); + } + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS29Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS29Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemJoint) { + gnap.useJointOnPlatypus(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS29Monkey: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemBanana) { + gnap._idleFacing = kDirBottomRight; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS29Monkey], 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS29UseBananaWithMonkey; + _vm->_newSceneNum = 51; + _vm->_isLeavingScene = true; + _vm->setGrabCursorSprite(-1); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS29Monkey], 5, 6); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(5, 6)); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(_vm->_hotspotsWalkPos[kHS29Monkey]); + break; + case GRAB_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS29ExitCircus: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 26; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS29ExitCircus], 0, 0x107AE, 1); + gnap._actionStatus = kAS29LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS29ExitCircus] + Common::Point(1, 0), -1, -1, 1); + } + break; + + case kHS29ExitOutsideClown: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 27; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS29ExitOutsideClown], 0, 0x107AB, 1); + gnap._actionStatus = kAS29LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS29ExitOutsideClown] + Common::Point(0, -1), -1, 0x107CD, 1); + } + break; + + case kHS29Arcade: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemDiceQuarterHole) { + _vm->setGrabCursorSprite(-1); + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 52; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS29Arcade], 0, -1, 1); + gnap.playIdle(_vm->_hotspotsWalkPos[kHS29Arcade]); + gnap._actionStatus = kAS29LeaveScene; + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS29Arcade], 2, 3); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan2(); + break; + case GRAB_CURSOR: + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS29WalkArea1: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + } + + updateAnimations(); + + if (!_vm->isSoundPlaying(0x1093B)) + _vm->playSound(0x1093B, true); + + if (!_vm->_isLeavingScene) { + if (gnap._actionStatus < 0) { + gnap.updateIdleSequence(); + plat.updateIdleSequence(); + } + if (!_vm->_timers[4]) { + if (_vm->invHas(kItemHorn)) { + _vm->_timers[4] = _vm->getRandom(20) + 60; + if (gnap._actionStatus < 0) { + switch (_vm->getRandom(5)) { + case 0: + _nextManSequenceId = 0xED; + break; + case 1: + _nextManSequenceId = 0xEE; + break; + case 2: + _nextManSequenceId = 0xEF; + break; + case 3: + _nextManSequenceId = 0xF0; + break; + case 4: + _nextManSequenceId = 0xF1; + break; + } + } + } + } + _vm->playSoundB(); + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } +} + +void Scene29::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS29UseBananaWithMonkey: + _nextMonkeySequenceId = 0xE5; + break; + case kAS29LeaveScene: + _vm->_sceneDone = true; + break; + } + } + + if (gameSys.getAnimationStatus(3) == 2 && _nextManSequenceId != -1) { + gameSys.insertSequence(_nextManSequenceId, 39, _currManSequenceId, 39, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextManSequenceId, 39, 3); + _currManSequenceId = _nextManSequenceId; + _nextManSequenceId = -1; + } + + if (gameSys.getAnimationStatus(4) == 2) { + if (_nextMonkeySequenceId == 0xE5) { + gameSys.insertSequence(0xF2, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceDatNum = 0; + gnap._sequenceId = 0xF2; + gameSys.setAnimation(0xE6, 159, 0); + gameSys.setAnimation(0, 159, 4); + gameSys.insertSequence(_nextMonkeySequenceId, 159, _currMonkeySequenceId, 159, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0xE6, 159, _nextMonkeySequenceId, 159, kSeqSyncWait, 0, 0, 0); + gnap._actionStatus = kAS29LeaveScene; + _currMonkeySequenceId = 0xE6; + _nextMonkeySequenceId = -1; + _vm->_timers[5] = 30; + while (_vm->_timers[5] && !_vm->_gameDone) + _vm->gameUpdateTick(); + + _vm->_plat->walkTo(Common::Point(0, 8), 1, 0x107CF, 1); + + while (gameSys.getAnimationStatus(1) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + } else if (_nextMonkeySequenceId == -1) { + switch (_vm->getRandom(6)) { + case 0: + _nextMonkeySequenceId = 0xE8; + break; + case 1: + _nextMonkeySequenceId = 0xE9; + break; + case 2: + _nextMonkeySequenceId = 0xEA; + break; + case 3: + _nextMonkeySequenceId = 0xEB; + break; + case 4: + _nextMonkeySequenceId = 0xEC; + break; + case 5: + _nextMonkeySequenceId = 0xE7; + break; + } + gameSys.insertSequence(_nextMonkeySequenceId, 159, _currMonkeySequenceId, 159, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextMonkeySequenceId, 159, 4); + _currMonkeySequenceId = _nextMonkeySequenceId; + _nextMonkeySequenceId = -1; + } else { + gameSys.insertSequence(_nextMonkeySequenceId, 159, _currMonkeySequenceId, 159, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextMonkeySequenceId, 159, 4); + _currMonkeySequenceId = _nextMonkeySequenceId; + _nextMonkeySequenceId = -1; + } + } +} + +} // End of namespace Gnap diff --git a/engines/gnap/scenes/group2.h b/engines/gnap/scenes/group2.h new file mode 100644 index 0000000000..8f56594f16 --- /dev/null +++ b/engines/gnap/scenes/group2.h @@ -0,0 +1,407 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GNAP_GROUP2_H +#define GNAP_GROUP2_H + +#include "gnap/debugger.h" + +namespace Gnap { + +enum { + kHS20Platypus = 0, + kHS20GroceryStoreHat = 1, + kHS20ExitParkingLot = 2, + kHS20StonerGuy = 3, + kHS20GroceryStoreGuy = 4, + kHS20Device = 5, + kHS20ExitInsideGrubCity = 6, + kHS20ExitOutsideCircusWorld = 7, + kHS20ExitOutsideToyStore = 8, + kHS20ExitPhone = 9, + kHS20WalkArea1 = 10, + kHS20WalkArea2 = 11 +}; + +enum { + kHS21Platypus = 0, + kHS21Banana = 1, + kHS21OldLady = 2, + kHS21Device = 3, + kHS21ExitOutsideGrubCity = 4, + kHS21WalkArea1 = 5, + kHS21WalkArea2 = 6 +}; + +enum { + kHS22Platypus = 0, + kHS22ExitOutsideGrubCity = 1, + kHS22ExitBackGrubCity = 2, + kHS22Cashier = 3, + kHS22Device = 4, + kHS22WalkArea1 = 5, + kHS22WalkArea2 = 6 +}; + +enum { + kHS23Platypus = 0, + kHS23ExitFrontGrubCity = 1, + kHS23Device = 2, + kHS23Cereals = 3, + kHS23WalkArea1 = 4, + kHS23WalkArea2 = 5 +}; + +enum { + kHS24Platypus = 0, + kHS24ExitCircusWorld = 1, + kHS24ExitOutsideGrubCity = 2, + kHS24Device = 3, + kHS24WalkArea1 = 4, + kHS24WalkArea2 = 5, + kHS24WalkArea3 = 6 +}; + +enum { + kHS25Platypus = 0, + kHS25TicketVendor = 1, + kHS25ExitOutsideCircusWorld = 2, + kHS25ExitInsideCircusWorld = 3, + kHS25Device = 4, + kHS25Posters1 = 5, + kHS25Posters2 = 6, + kHS25Posters3 = 7, + kHS25Posters4 = 8, + kHS25WalkArea1 = 9, + kHS25WalkArea2 = 10 +}; + +enum { + kHS26Platypus = 0, + kHS26ExitOutsideCircusWorld = 1, + kHS26ExitOutsideClown = 2, + kHS26ExitArcade = 3, + kHS26ExitElephant = 4, + kHS26ExitBeerStand = 5, + kHS26Device = 6, + kHS26WalkArea1 = 7, + kHS26WalkArea2 = 8 +}; + +enum { + kHS27Platypus = 0, + kHS27Janitor = 1, + kHS27Device = 2, + kHS27Bucket = 3, + kHS27ExitCircus = 4, + kHS27ExitArcade = 5, + kHS27ExitBeerStand = 6, + kHS27ExitClown = 7, + kHS27WalkArea1 = 8 +}; + +enum { + kHS28Platypus = 0, + kHS28Horn = 1, + kHS28Clown = 2, + kHS28ExitOutsideClown = 3, + kHS28EmptyBucket = 4, + kHS28Device = 5, + kHS28WalkArea1 = 6, + kHS28WalkArea2 = 7 +}; + +enum { + kHS29Platypus = 0, + kHS29Monkey = 1, + kHS29Device = 2, + kHS29ExitCircus = 3, + kHS29ExitOutsideClown = 4, + kHS29Arcade = 5, + kHS29WalkArea1 = 6 +}; + +enum { + kAS20LeaveScene = 0, + kAS20TalkStonerGuyNoJoint = 2, + kAS20TalkStonerGuyHasJoint = 3, + kAS20GrabJoint = 4, + kAS20ActionDone = 5, + kAS20TalkGroceryStoreGuy = 6, + kAS20GrabGroceryStoreGuy = 9, + kAS20GrabGroceryStoreHat = 10, + kAS20SwitchGroceryStoreHat = 11, + kAS20SwitchGroceryStoreHatDone = 12, + kAS20GrabJointDone = 13 +}; + +enum { + kAS21TalkOldLady = 0, + kAS21GrabBanana = 1, + kAS21GrabBananaDone = 2, + kAS21GrabOldLady = 3, + kAS21UseHatWithOldLady = 4, + kAS21UseHatWithOldLadyDone = 5, + kAS21LeaveScene = 6 +}; + +enum { + kAS22LeaveScene = 0, + kAS22TalkCashier = 1 +}; + +enum { + kAS23LookCereals = 0, + kAS23GrabCereals = 1, + kAS23GrabCerealsDone = 2, + kAS23LeaveScene = 3 +}; + +enum { + kAS24LeaveScene = 0 +}; + +enum { + kAS25TalkTicketVendor = 0, + kAS25EnterCircusWihoutTicket = 1, + kAS25ShowTicketToVendor = 2, + kAS25ShowTicketToVendorDone = 3, + kAS25EnterCircusWihoutTicketDone = 4, + kAS25LeaveScene = 5 +}; + +enum { + kAS26LeaveScene = 0 +}; + +enum { + kAS27TalkJanitor = 0, + kAS27GrabBucket = 1, + kAS27GrabBucketDone = 2, + kAS27ShowPictureToJanitor = 3, + kAS27TryEnterClownTent = 4, + kAS27TryEnterClownTentDone = 5, + kAS27EnterClownTent = 6, + kAS27LeaveScene = 7 +}; + +enum { + kAS28UseBeerBucketWithClown = 0, + kAS28UsePillBucketWithClown = 1, + kAS28GrabHornFails = 2, + kAS28GrabEmptyBucket = 3, + kAS28GrabHornSuccess = 4, + kAS28GrabHornSuccessDone = 5, + kAS28GrabEmptyBucketDone = 6, + kAS28GrabHornFailsDone = 7, + kAS28TalkClown = 8, + kAS28GnapWaiting = 9, + kAS28LeaveScene = 10 +}; + +enum { + kAS29UseBananaWithMonkey = 0, + kAS29LeaveScene = 2 +}; + +class GnapEngine; +class CutScene; + +class Scene20: public Scene { +public: + Scene20(GnapEngine *vm); + virtual ~Scene20() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb(); + +private: + int _currStonerGuySequenceId; + int _nextStonerGuySequenceId; + int _currGroceryStoreGuySequenceId; + int _nextGroceryStoreGuySequenceId; + int _stonerGuyCtr; + int _groceryStoreGuyCtr; + bool _stonerGuyShowingJoint; + + void stopSounds(); +}; + +class Scene21: public Scene { +public: + Scene21(GnapEngine *vm); + virtual ~Scene21() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _currOldLadySequenceId; + int _nextOldLadySequenceId; +}; + +class Scene22: public Scene { +public: + Scene22(GnapEngine *vm); + virtual ~Scene22() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _currCashierSequenceId; + int _nextCashierSequenceId; + bool _caughtBefore; + int _cashierCtr; +}; + +class Scene23: public Scene { +public: + Scene23(GnapEngine *vm); + virtual ~Scene23() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _currStoreClerkSequenceId; + int _nextStoreClerkSequenceId; +}; + +class Scene24: public Scene { +public: + Scene24(GnapEngine *vm); + virtual ~Scene24() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _currWomanSequenceId; + int _nextWomanSequenceId; + int _boySequenceId; + int _girlSequenceId; +}; + +class Scene25: public Scene { +public: + Scene25(GnapEngine *vm); + virtual ~Scene25() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _currTicketVendorSequenceId; + int _nextTicketVendorSequenceId; + + void playAnims(int index); +}; + +class Scene26: public Scene { +public: + Scene26(GnapEngine *vm); + virtual ~Scene26() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _currKidSequenceId; + int _nextKidSequenceId; +}; + +class Scene27: public Scene { +public: + Scene27(GnapEngine *vm); + virtual ~Scene27() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _nextJanitorSequenceId; + int _currJanitorSequenceId; +}; + +class Scene28: public Scene { +public: + Scene28(GnapEngine *vm); + virtual ~Scene28() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _currClownSequenceId; + int _nextClownSequenceId; + int _clownTalkCtr; +}; + +class Scene29: public Scene { +public: + Scene29(GnapEngine *vm); + virtual ~Scene29() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _currMonkeySequenceId; + int _nextMonkeySequenceId; + int _currManSequenceId; + int _nextManSequenceId; +}; + +} // End of namespace Gnap + +#endif // GNAP_GROUP1_H diff --git a/engines/gnap/scenes/group3.cpp b/engines/gnap/scenes/group3.cpp new file mode 100644 index 0000000000..98a4f6c454 --- /dev/null +++ b/engines/gnap/scenes/group3.cpp @@ -0,0 +1,1612 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gnap/gnap.h" +#include "gnap/gamesys.h" +#include "gnap/resource.h" +#include "gnap/scenes/group3.h" + +namespace Gnap { + +Scene30::Scene30(GnapEngine *vm) : Scene(vm) { + _kidSequenceId = -1; +} + +int Scene30::init() { + return _vm->isFlag(kGFUnk23) ? 0x10B : 0x10A; +} + +void Scene30::updateHotspots() { + _vm->setHotspot(kHS30Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS30PillMachine, 598, 342, 658, 426, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 9, 7); + _vm->setHotspot(kHS30ExitCircus, 100, 590 - _vm->_deviceY1, 700, 600, SF_EXIT_D_CURSOR | SF_WALKABLE, 5, 9); + _vm->setHotspot(kHS30WalkArea1, 0, 0, 800, 514); + _vm->setDeviceHotspot(kHS30Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 5; +} + +void Scene30::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + bool hasTakenPill = false; + + _vm->playSound(0x1093B, true); + _vm->startSoundTimerB(6); + + _vm->queueInsertDeviceIcon(); + + if (_vm->isFlag(kGFUnk23)) + gameSys.insertSequence(0x106, 1, 0, 0, kSeqNone, 0, 0, 0); + + if (!_vm->isFlag(kGFUnk13)) + gameSys.insertSequence(0x107, 1, 0, 0, kSeqNone, 0, 0, 0); + _vm->_timers[5] = _vm->getRandom(50) + 180; + + gameSys.insertSequence(0x101, 40, 0, 0, kSeqNone, 0, 0, 0); + _vm->_timers[4] = _vm->getRandom(100) + 300; + + _kidSequenceId = 0x101; + gnap.initPos(7, 12, kDirBottomRight); + plat.initPos(6, 12, kDirIdleLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(7, 8), -1, 0x107B9, 1); + plat.walkTo(Common::Point(6, 8), -1, 0x107C2, 1); + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS30Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS30Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemJoint) { + gnap.useJointOnPlatypus(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS30PillMachine: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemDiceQuarterHole && !_vm->isFlag(kGFUnk23)) { + _vm->_hotspots[kHS30WalkArea1]._flags |= SF_WALKABLE; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS30PillMachine], 0, 0x107BC, 1); + _vm->_hotspots[kHS30WalkArea1]._flags &= ~SF_WALKABLE; + gnap._actionStatus = kAS30UsePillMachine; + hasTakenPill = true; + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS30PillMachine], 8, 5); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.walkTo(Common::Point(9, 8), 0, 0x107BC, 1); + gnap._actionStatus = kAS30LookPillMachine; + break; + case GRAB_CURSOR: + gnap.playScratchingHead(Common::Point(8, 5)); + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(Common::Point(8, 5)); + break; + } + } + } + break; + + case kHS30ExitCircus: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + if (hasTakenPill) + _vm->_newSceneNum = 47; + else + _vm->_newSceneNum = 26; + gnap.walkTo(Common::Point(-1, _vm->_hotspotsWalkPos[kHS30ExitCircus].y), 0, 0x107AE, 1); + gnap._actionStatus = kAS30LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS30ExitCircus] + Common::Point(1, 0), -1, 0x107C2, 1); + } + break; + + case kHS30WalkArea1: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + } + + updateAnimations(); + + if (!_vm->isSoundPlaying(0x1093B)) + _vm->playSound(0x1093B, true); + + if (!_vm->_isLeavingScene) { + plat.updateIdleSequence(); + if (gnap._actionStatus < 0) + gnap.updateIdleSequence(); + if (!_vm->_timers[4]) { + _vm->_timers[4] = _vm->getRandom(100) + 300; + if (gnap._actionStatus < 0) { + if (_vm->getRandom(5) == 1) { + gameSys.insertSequence(0xFF, 40, 0, 0, kSeqNone, 0, 0, 0); + gameSys.insertSequence(0x100, 40, _kidSequenceId, 40, kSeqSyncWait, 0, 0, 0); + _kidSequenceId = 0x100; + } else { + gameSys.insertSequence(0xFE, 40, 0, 0, kSeqNone, 0, 0, 0); + } + } + } + if (!_vm->_timers[5]) { + _vm->_timers[5] = _vm->getRandom(50) + 180; + if (gnap._actionStatus < 0) { + if (!_vm->isFlag(kGFUnk23) || hasTakenPill) + gameSys.insertSequence(0x109, 20, 0, 0, kSeqNone, 0, 0, 0); + else + gameSys.insertSequence(0x108, 20, 0, 0, kSeqNone, 0, 0, 0); + } + } + _vm->playSoundB(); + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } +} + +void Scene30::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS30LeaveScene: + _vm->_sceneDone = true; + break; + case kAS30UsePillMachine: + _vm->setGrabCursorSprite(-1); + gameSys.setAnimation(0x105, gnap._id, 0); + gameSys.insertSequence(0x105, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x105; + gnap._sequenceDatNum = 0; + gnap._actionStatus = kAS30UsePillMachine2; + break; + case kAS30UsePillMachine2: + _vm->hideCursor(); + _vm->setGrabCursorSprite(-1); + _vm->addFullScreenSprite(0x3F, 255); + gameSys.removeSequence(0x105, gnap._id, true); + gameSys.setAnimation(0x102, 256, 0); + gameSys.insertSequence(0x102, 256, 0, 0, kSeqNone, 0, 0, 0); + while (gameSys.getAnimationStatus(0) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + gameSys.setAnimation(0x103, gnap._id, 0); + gameSys.insertSequence(0x103, gnap._id, 0, 0, kSeqNone, 0, 0, 0); + _vm->removeFullScreenSprite(); + _vm->showCursor(); + gnap._actionStatus = kAS30UsePillMachine3; + _vm->invAdd(kItemPill); + _vm->setFlag(kGFUnk23); + break; + case kAS30UsePillMachine3: + gameSys.setAnimation(0x104, gnap._id, 0); + gameSys.insertSequence(0x104, gnap._id, makeRid(gnap._sequenceDatNum, 0x103), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x104; + gnap._sequenceDatNum = 0; + gnap._actionStatus = kAS30UsePillMachine4; + _vm->setGrabCursorSprite(kItemDiceQuarterHole); + break; + case kAS30UsePillMachine4: + gameSys.insertSequence(0x106, 1, 0, 0, kSeqNone, 0, 0, 0); + gnap.walkTo(_vm->_hotspotsWalkPos[kHS30PillMachine] + Common::Point(0, 1), -1, 0x107BC, 1); + gnap._actionStatus = -1; + break; + case kAS30LookPillMachine: + if (_vm->isFlag(kGFUnk23)) + _vm->showFullScreenSprite(0xE3); + else + _vm->showFullScreenSprite(0xE2); + gnap._actionStatus = -1; + break; + } + } +} + +/*****************************************************************************/ + +Scene31::Scene31(GnapEngine *vm) : Scene(vm) { + _beerGuyDistracted = false; + _currClerkSequenceId = -1; + _nextClerkSequenceId = -1; + _clerkMeasureCtr = -1; + _clerkMeasureMaxCtr = 3; +} + +int Scene31::init() { + return 0x105; +} + +void Scene31::updateHotspots() { + _vm->setHotspot(kHS31Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS31MeasuringClown, 34, 150, 256, 436, SF_WALKABLE | SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 0, 6); + _vm->setHotspot(kHS31BeerBarrel, 452, 182, 560, 306, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 6, 7); + _vm->setHotspot(kHS31ExitCircus, 150, 585, 650, 600, SF_EXIT_D_CURSOR | SF_WALKABLE, 5, 9); + _vm->setHotspot(kHS31ExitOutsideClown, 0, 0, 15, 600, SF_EXIT_L_CURSOR | SF_WALKABLE, 0, 8); + _vm->setHotspot(kHS31WalkArea1, 0, 0, 800, 480); + _vm->setDeviceHotspot(kHS31Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 7; +} + +void Scene31::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->playSound(0x1093B, true); + _vm->startSoundTimerB(6); + _vm->queueInsertDeviceIcon(); + + _beerGuyDistracted = false; + gameSys.insertSequence(0xFB, 39, 0, 0, kSeqNone, 0, 0, 0); + + _currClerkSequenceId = 0xFB; + _nextClerkSequenceId = -1; + + gameSys.setAnimation(0xFB, 39, 3); + + _vm->_timers[4] = _vm->getRandom(20) + 60; + _vm->_timers[5] = _vm->getRandom(50) + 180; + + if (_vm->_prevSceneNum == 27) { + gnap.initPos(-1, 8, kDirBottomLeft); + plat.initPos(-1, 9, kDirIdleRight); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(3, 8), -1, 0x107BA, 1); + plat.walkTo(Common::Point(3, 9), -1, 0x107D2, 1); + } else { + gnap.initPos(7, 12, kDirBottomRight); + plat.initPos(6, 12, kDirIdleLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(7, 8), -1, 0x107BA, 1); + plat.walkTo(Common::Point(6, 8), -1, 0x107D2, 1); + } + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS31Device: + if (gnap._actionStatus < 0 || gnap._actionStatus == kAS31PlatMeasuringClown) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS31Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemJoint) { + gnap.useJointOnPlatypus(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS31MeasuringClown: + if (gnap._actionStatus < 0 || gnap._actionStatus == kAS31PlatMeasuringClown) { + if (gnap._actionStatus == kAS31PlatMeasuringClown) { + if (_vm->_verbCursor == LOOK_CURSOR) + gnap.playScratchingHead(Common::Point(2, 2)); + else + gnap.playImpossible(); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS31MeasuringClown] + Common::Point(0, 1), 2, 2); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(2, 2)); + break; + case GRAB_CURSOR: + gnap.walkTo(_vm->_hotspotsWalkPos[kHS31MeasuringClown] + Common::Point(0, 1), -1, -1, 1); + _vm->_hotspots[kHS31WalkArea1]._flags |= SF_WALKABLE; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS31MeasuringClown], 0, 0x107B9, 1); + _vm->_hotspots[kHS31WalkArea1]._flags &= ~SF_WALKABLE; + gnap._actionStatus = kAS31UseMeasuringClown; + _vm->_timers[4] = 300; + break; + case TALK_CURSOR: + gnap.playImpossible(); + break; + case PLAT_CURSOR: + if (!_vm->invHas(kItemBucketWithBeer)) { + gnap.useDeviceOnPlatypus(); + plat.walkTo(_vm->_hotspotsWalkPos[kHS31MeasuringClown] + Common::Point(0, 1), 1, 0x107C2, 1); + _vm->_hotspots[kHS31WalkArea1]._flags |= SF_WALKABLE; + plat.walkTo(_vm->_hotspotsWalkPos[kHS31MeasuringClown], 1, 0x107C2, 1); + _vm->_hotspots[kHS31WalkArea1]._flags &= ~SF_WALKABLE; + plat._actionStatus = kAS31PlatMeasuringClown; + gnap._actionStatus = kAS31PlatMeasuringClown; + _vm->_timers[4] = 300; + } else + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS31BeerBarrel: + if (gnap._actionStatus < 0 || gnap._actionStatus == kAS31PlatMeasuringClown) { + if (_vm->_grabCursorSpriteIndex == kItemEmptyBucket && _beerGuyDistracted) { + _vm->setGrabCursorSprite(-1); + gnap.walkTo(gnap._pos, -1, gnap.getSequenceId(kGSIdle, _vm->_hotspotsWalkPos[kHS31BeerBarrel]) | 0x10000, 1); + _clerkMeasureMaxCtr += 5; + gameSys.insertSequence(0xF8, 59, 0, 0, kSeqNone, 0, 0, 0); + gnap.playPullOutDevice(Common::Point(6, 8)); + gnap.playUseDevice(); + gnap.walkTo(_vm->_hotspotsWalkPos[kHS31BeerBarrel], 0, 0x107BC, 1); + gnap._actionStatus = kAS31FillEmptyBucketWithBeer; + _vm->_timers[4] = 300; + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS31BeerBarrel], 6, 2); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(6, 2)); + break; + case GRAB_CURSOR: + if (_beerGuyDistracted) { + gnap.playScratchingHead(Common::Point(6, 2)); + } else { + gnap.walkTo(_vm->_hotspotsWalkPos[kHS31BeerBarrel], 0, 0x107BC, 1); + gnap._actionStatus = kAS31UseBeerBarrel; + gnap._idleFacing = kDirUpLeft; + } + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS31ExitCircus: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 26; + gnap.walkTo(Common::Point(-1, _vm->_hotspotsWalkPos[kHS31ExitCircus].y), 0, 0x107AE, 1); + gnap._actionStatus = kAS31LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS31ExitCircus] + Common::Point(1, 0), -1, -1, 1); + } + break; + + case kHS31ExitOutsideClown: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 27; + gnap.walkTo(Common::Point(-1, _vm->_hotspotsWalkPos[kHS31ExitOutsideClown].y), 0, 0x107AF, 1); + gnap._actionStatus = kAS31LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS31ExitOutsideClown] + Common::Point(0, 1), -1, 0x107CF, 1); + } + break; + + case kHS31WalkArea1: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + } + + updateAnimations(); + + if (!_vm->isSoundPlaying(0x1093B)) + _vm->playSound(0x1093B, true); + + if (!_vm->_isLeavingScene) { + if (plat._actionStatus < 0) + plat.updateIdleSequence(); + if (gnap._actionStatus < 0) + gnap.updateIdleSequence(); + if (!_vm->_timers[4]) { + _vm->_timers[4] = _vm->getRandom(20) + 60; + if (gnap._actionStatus < 0 && _nextClerkSequenceId == -1) { + switch (_vm->getRandom(6)){ + case 0: + _nextClerkSequenceId = 0xFF; + break; + case 1: + _nextClerkSequenceId = 0x100; + break; + case 2: + _nextClerkSequenceId = 0x101; + break; + default: + _nextClerkSequenceId = 0xFB; + break; + } + } + } + if (!_vm->_timers[5]) { + _vm->_timers[5] = _vm->getRandom(50) + 180; + if (gnap._actionStatus < 0) { + if (_vm->getRandom(2) != 0) + gameSys.insertSequence(0x104, 20, 0, 0, kSeqNone, 0, 0, 0); + else + gameSys.insertSequence(0x103, 20, 0, 0, kSeqNone, 0, 0, 0); + } + } + _vm->playSoundB(); + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } +} + +void Scene31::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS31UseBeerBarrel: + _nextClerkSequenceId = 0xFE; + break; + case kAS31FillEmptyBucketWithBeer: + gameSys.setAnimation(0x102, 59, 0); + gameSys.insertSequence(0x102, 59, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._pos = Common::Point(5, 7); + gnap._sequenceDatNum = 0; + gnap._sequenceId = 0x102; + gnap._id = 59; + gnap._actionStatus = kAS31FillEmptyBucketWithBeerDone; + break; + case kAS31FillEmptyBucketWithBeerDone: + gnap._idleFacing = kDirBottomLeft; + gnap.playPullOutDevice(); + gnap.playUseDevice(); + gameSys.insertSequence(0xF9, 59, 0xF8, 59, kSeqSyncWait, 0, 0, 0); + gnap._actionStatus = -1; + _vm->invAdd(kItemBucketWithBeer); + _vm->invRemove(kItemEmptyBucket); + _vm->setGrabCursorSprite(kItemBucketWithBeer); + break; + case kAS31UseMeasuringClown: + _nextClerkSequenceId = 0xFA; + _clerkMeasureMaxCtr = 1; + break; + case kAS31LeaveScene: + _vm->_sceneDone = true; + gnap._actionStatus = -1; + break; + } + } + + if (gameSys.getAnimationStatus(1) == 2) { + gameSys.setAnimation(0, 0, 1); + if (plat._actionStatus == kAS31PlatMeasuringClown) { + _vm->_sceneWaiting = true; + _beerGuyDistracted = true; + _nextClerkSequenceId = 0xFA; + } + } + + if (gameSys.getAnimationStatus(3) == 2) { + switch (_nextClerkSequenceId) { + case 0xFA: + gameSys.insertSequence(_nextClerkSequenceId, 39, _currClerkSequenceId, 39, kSeqSyncWait, 0, 0, 0); + gameSys.insertSequence(0xFC, 39, _nextClerkSequenceId, 39, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(0xFC, 39, 3); + _currClerkSequenceId = 0xFC; + _nextClerkSequenceId = 0xFC; + _clerkMeasureCtr = 0; + break; + case 0xFC: + ++_clerkMeasureCtr; + if (_clerkMeasureCtr >= _clerkMeasureMaxCtr) { + if (gnap._actionStatus != 5) + plat._actionStatus = -1; + _vm->_timers[0] = 40; + gameSys.insertSequence(0xFD, 39, _currClerkSequenceId, 39, kSeqSyncWait, 0, 0, 0); + _currClerkSequenceId = 0xFD; + _nextClerkSequenceId = -1; + if (gnap._actionStatus != kAS31FillEmptyBucketWithBeerDone && gnap._actionStatus != kAS31FillEmptyBucketWithBeer) + gnap._actionStatus = -1; + _beerGuyDistracted = false; + _clerkMeasureMaxCtr = 3; + gameSys.setAnimation(0xFD, 39, 3); + _vm->_sceneWaiting = false; + } else { + gameSys.insertSequence(_nextClerkSequenceId, 39, _currClerkSequenceId, 39, kSeqSyncWait, 0, 0, 0); + _currClerkSequenceId = _nextClerkSequenceId; + _nextClerkSequenceId = 0xFC; + gameSys.setAnimation(0xFC, 39, 3); + } + break; + case 0xFE: + gameSys.insertSequence(_nextClerkSequenceId, 39, _currClerkSequenceId, 39, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextClerkSequenceId, 39, 3); + _currClerkSequenceId = _nextClerkSequenceId; + _nextClerkSequenceId = -1; + gnap._actionStatus = -1; + break; + default: + if (_nextClerkSequenceId != -1) { + gameSys.insertSequence(_nextClerkSequenceId, 39, _currClerkSequenceId, 39, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextClerkSequenceId, 39, 3); + _currClerkSequenceId = _nextClerkSequenceId; + _nextClerkSequenceId = -1; + } + break; + } + } +} + +/*****************************************************************************/ + +Scene32::Scene32(GnapEngine *vm) : Scene(vm) {} + +int Scene32::init() { + _vm->_gameSys->setAnimation(0, 0, 0); + return _vm->isFlag(kGFPlatypusTalkingToAssistant) ? 0xF : 0x10; +} + +void Scene32::updateHotspots() { + _vm->setHotspot(kHS32Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS32ExitTruck, 780, 226, 800, 455, SF_EXIT_R_CURSOR | SF_WALKABLE, 10, 6); + _vm->setHotspot(kHS32WalkArea1, 0, 0, 162, 426); + _vm->setHotspot(kHS32WalkArea2, 162, 0, 237, 396); + _vm->setHotspot(kHS32WalkArea3, 237, 0, 319, 363); + _vm->setHotspot(kHS32WalkArea4, 520, 0, 800, 404); + _vm->setHotspot(kHS32WalkArea5, 300, 447, 800, 600); + _vm->setHotspot(kHS32WalkArea6, 678, 0, 800, 404); + _vm->setHotspot(kHS32WalkArea7, 0, 0, 520, 351); + _vm->setHotspot(kHS32WalkArea8, 0, 546, 300, 600); + _vm->setDeviceHotspot(kHS32Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 11; +} + +void Scene32::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->playSound(0x1091C, true); + _vm->startSoundTimerC(5); + _vm->queueInsertDeviceIcon(); + _vm->_timers[4] = _vm->getRandom(100) + 300; + + if (_vm->_prevSceneNum == 33) { + gnap.initPos(11, 6, kDirBottomLeft); + plat.initPos(12, 6, kDirIdleRight); + _vm->endSceneInit(); + plat.walkTo(Common::Point(9, 6), -1, 0x107D2, 1); + gnap.walkTo(Common::Point(8, 6), -1, 0x107BA, 1); + } else { + gnap.initPos(1, 6, kDirBottomRight); + plat.initPos(1, 7, kDirIdleLeft); + _vm->endSceneInit(); + } + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + _vm->testWalk(0, 0, -1, -1, -1, -1); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS32Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS32Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(plat._pos); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(plat._pos); + break; + } + } + } + break; + + case kHS32ExitTruck: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->setGrabCursorSprite(-1); + gnap.walkTo(_vm->_hotspotsWalkPos[kHS32ExitTruck], 0, 0x107AB, 1); + gnap._actionStatus = kAS32LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS32ExitTruck] + Common::Point(0, 1), -1, 0x107CD, 1); + _vm->_newSceneNum = 33; + } + break; + + case kHS32WalkArea1: + case kHS32WalkArea2: + case kHS32WalkArea3: + case kHS32WalkArea4: + case kHS32WalkArea5: + case kHS32WalkArea6: + case kHS32WalkArea7: + case kHS32WalkArea8: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + } + + if (_vm->_mouseClickState._left && gnap._actionStatus < 0) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = 0; + } + + updateAnimations(); + + if (!_vm->isSoundPlaying(0x1091C)) + _vm->playSound(0x1091C, true); + + if (!_vm->_isLeavingScene) { + if (plat._actionStatus < 0) + plat.updateIdleSequence(); + if (gnap._actionStatus < 0) + gnap.updateIdleSequence(); + if (!_vm->_timers[4]) { + _vm->_timers[4] = _vm->getRandom(100) + 300; + if (_vm->getRandom(2) != 0) + gameSys.insertSequence(0x0E, 180, 0, 0, kSeqNone, 0, 0, 0); + else + gameSys.insertSequence(0x0D, 180, 0, 0, kSeqNone, 0, 0, 0); + } + _vm->playSoundC(); + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + _vm->gameUpdateTick(); + } +} + +void Scene32::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + if (_vm->_gnap->_actionStatus == kAS32LeaveScene) + _vm->_sceneDone = true; + } +} + +/*****************************************************************************/ + +Scene33::Scene33(GnapEngine *vm) : Scene(vm) { + _currChickenSequenceId = -1; + _nextChickenSequenceId = -1; +} + +int Scene33::init() { + return _vm->isFlag(kGFPlatypusTalkingToAssistant) ? 0x84 : 0x85; +} + +void Scene33::updateHotspots() { + _vm->setHotspot(kHS33Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS33Chicken, 606, 455, 702, 568, SF_WALKABLE | SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 7, 8); + _vm->setHotspot(kHS33ExitHouse, 480, 120, 556, 240, SF_EXIT_U_CURSOR, 7, 3); + _vm->setHotspot(kHS33ExitBarn, 610, 75, 800, 164, SF_EXIT_U_CURSOR, 10, 3); + _vm->setHotspot(kHS33ExitCreek, 780, 336, 800, 556, SF_EXIT_R_CURSOR | SF_WALKABLE, 10, 8); + _vm->setHotspot(kHS33ExitPigpen, 0, 300, 20, 600, SF_EXIT_L_CURSOR | SF_WALKABLE, 0, 8); + _vm->setHotspot(kHS33WalkArea1, 120, 0, 514, 458); + _vm->setHotspot(kHS33WalkArea2, 0, 0, 800, 452); + _vm->setDeviceHotspot(kHS33Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 9; +} + +void Scene33::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->playSound(0x1091C, true); + _vm->startSoundTimerC(6); + _vm->queueInsertDeviceIcon(); + + _currChickenSequenceId = 0x7E; + gameSys.setAnimation(0x7E, 179, 2); + gameSys.insertSequence(_currChickenSequenceId, 179, 0, 0, kSeqNone, 0, 0, 0); + _nextChickenSequenceId = -1; + _vm->_timers[5] = _vm->getRandom(20) + 30; + _vm->_timers[4] = _vm->getRandom(100) + 300; + + switch (_vm->_prevSceneNum) { + case 34: + gnap.initPos(11, 7, kDirBottomLeft); + plat.initPos(12, 7, kDirIdleRight); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(8, 7), -1, 0x107BA, 1); + plat.walkTo(Common::Point(9, 7), -1, 0x107D2, 1); + break; + case 37: + gnap.initPos(7, 7, kDirBottomRight); + plat.initPos(8, 7, kDirIdleLeft); + _vm->endSceneInit(); + break; + case 32: + gnap.initPos(-1, 6, kDirBottomRight); + plat.initPos(-1, 7, kDirIdleLeft); + _vm->endSceneInit(); + plat.walkTo(Common::Point(2, 7), -1, 0x107C2, 1); + gnap.walkTo(Common::Point(2, 8), -1, 0x107B9, 1); + break; + default: + gnap.initPos(3, 7, kDirBottomRight); + plat.initPos(2, 7, kDirIdleLeft); + _vm->endSceneInit(); + break; + } + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + _vm->testWalk(0, 0, 7, 6, 8, 6); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS33Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + + case kHS33Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(plat._pos); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(plat._pos); + break; + } + } + } + break; + + case kHS33Chicken: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(7, 9), 9, 8); + } else { + switch (_vm->_verbCursor) { + case GRAB_CURSOR: + gnap._idleFacing = kDirBottomRight; + if (gnap.walkTo(_vm->_hotspotsWalkPos[kHS33Chicken], 0, gnap.getSequenceId(kGSIdle, Common::Point(0, 0)) | 0x10000, 1)) + gnap._actionStatus = kAS33UseChicken; + else + gnap._actionStatus = -1; + break; + case TALK_CURSOR: + gnap._idleFacing = kDirBottomRight; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS33Chicken], 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS33TalkChicken; + break; + case LOOK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS33ExitHouse: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + gnap._actionStatus = kAS33LeaveScene; + _vm->_newSceneNum = 37; + if (gnap._pos.x > 6) + gnap.walkTo(gnap._pos, 0, 0x107AD, 1); + else + gnap.walkTo(Common::Point(6, 7), 0, 0x107B1, 1); + } + break; + + case kHS33ExitBarn: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + gnap._actionStatus = kAS33LeaveScene; + _vm->_newSceneNum = 35; + if (gnap._pos.x > 7) + gnap.walkTo(gnap._pos, 0, 0x107AD, 1); + else + gnap.walkTo(Common::Point(7, 7), 0, 0x107B1, 1); + } + break; + + case kHS33ExitCreek: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS33ExitCreek], 0, 0x107AB, 1); + gnap._actionStatus = kAS33LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS33ExitCreek], -1, 0x107CD, 1); + _vm->_newSceneNum = 34; + } + break; + + case kHS33ExitPigpen: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS33ExitPigpen], 0, 0x107AF, 1); + gnap._actionStatus = kAS33LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS33ExitPigpen], -1, 0x107CF, 1); + _vm->_newSceneNum = 32; + } + break; + + case kHS33WalkArea1: + case kHS33WalkArea2: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left && gnap._actionStatus < 0) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + } + + updateAnimations(); + + if (!_vm->isSoundPlaying(0x1091C)) + _vm->playSound(0x1091C, true); + + if (!_vm->_isLeavingScene) { + if (plat._actionStatus < 0) + plat.updateIdleSequence(); + if (gnap._actionStatus < 0) + gnap.updateIdleSequence(); + if (!_vm->_timers[4]) { + _vm->_timers[4] = _vm->getRandom(100) + 300; + if (_vm->getRandom(2) != 0) + gameSys.insertSequence(0x83, 256, 0, 0, kSeqNone, 0, 0, 0); + else + gameSys.insertSequence(0x82, 256, 0, 0, kSeqNone, 0, 0, 0); + } + if (!_vm->_timers[5] && _nextChickenSequenceId == -1 && gnap._actionStatus != kAS33TalkChicken && gnap._actionStatus != kAS33UseChicken) { + if (_vm->getRandom(6) != 0) { + _nextChickenSequenceId = 0x7E; + _vm->_timers[5] = _vm->getRandom(20) + 30; + } else { + _nextChickenSequenceId = 0x80; + _vm->_timers[5] = _vm->getRandom(20) + 50; + } + } + _vm->playSoundC(); + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + _vm->gameUpdateTick(); + } +} + +void Scene33::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + switch (gnap._actionStatus) { + case kAS33LeaveScene: + _vm->_sceneDone = true; + break; + case kAS33TalkChicken: + _nextChickenSequenceId = 0x7F; + break; + case kAS33UseChicken: + _nextChickenSequenceId = 0x81; + _vm->_timers[2] = 100; + break; + case kAS33UseChickenDone: + gameSys.insertSequence(0x107B5, gnap._id, 0x81, 179, kSeqSyncWait, 0, 75 * gnap._pos.x - gnap._gridX, 48 * gnap._pos.y - gnap._gridY); + gnap._sequenceId = 0x7B5; + gnap._sequenceDatNum = 1; + _currChickenSequenceId = 0x7E; + gameSys.setAnimation(0x7E, 179, 2); + gameSys.insertSequence(_currChickenSequenceId, 179, 0, 0, kSeqNone, 0, 0, 0); + gnap._actionStatus = -1; + _vm->_timers[5] = 30; + break; + default: + gnap._actionStatus = -1; + break; + } + } + + if (gameSys.getAnimationStatus(2) == 2) { + if (_nextChickenSequenceId == 0x81) { + gameSys.setAnimation(_nextChickenSequenceId, 179, 0); + gameSys.insertSequence(_nextChickenSequenceId, 179, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gameSys.removeSequence(_currChickenSequenceId, 179, true); + _nextChickenSequenceId = -1; + _currChickenSequenceId = -1; + gnap._actionStatus = kAS33UseChickenDone; + _vm->_timers[5] = 500; + } else if (_nextChickenSequenceId == 0x7F) { + gameSys.setAnimation(_nextChickenSequenceId, 179, 2); + gameSys.insertSequence(_nextChickenSequenceId, 179, _currChickenSequenceId, 179, kSeqSyncWait, 0, 0, 0); + _currChickenSequenceId = _nextChickenSequenceId; + _nextChickenSequenceId = -1; + gnap._actionStatus = -1; + } else if (_nextChickenSequenceId != -1) { + gameSys.setAnimation(_nextChickenSequenceId, 179, 2); + gameSys.insertSequence(_nextChickenSequenceId, 179, _currChickenSequenceId, 179, kSeqSyncWait, 0, 0, 0); + _currChickenSequenceId = _nextChickenSequenceId; + _nextChickenSequenceId = -1; + } + } +} + +/*****************************************************************************/ + +Scene38::Scene38(GnapEngine *vm) : Scene(vm) { +} + +int Scene38::init() { + GameSys& gameSys = *_vm->_gameSys; + + gameSys.setAnimation(0, 0, 0); + gameSys.setAnimation(0, 0, 1); + return 0xA5; +} + +void Scene38::updateHotspots() { + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->setHotspot(kHS38Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS38ExitHouse, 150, 585, 650, 600, SF_EXIT_D_CURSOR, 0, 8); + _vm->setHotspot(kHS38ExitCave, 430, 440, 655, 470, SF_WALKABLE, 0, 8); + _vm->setHotspot(kHS38TrapDoorLid1, 525, 265, 640, 350, SF_DISABLED); + _vm->setHotspot(kHS38TrapDoorLid2, 555, 350, 670, 430, SF_DISABLED); + _vm->setHotspot(kHS38HuntingTrophy, 170, 85, 250, 190, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 0, 8); + _vm->setHotspot(kHS38WalkArea1, 330, 270, 640, 380, SF_DISABLED | SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 0, 8); + _vm->setHotspot(kHS38WalkArea2, 0, 0, 799, 396); + _vm->setHotspot(kHS38WalkArea3, 0, 585, 799, 599, SF_WALKABLE | SF_DISABLED); + _vm->setHotspot(kHS38WalkArea4, 0, 0, 97, 445); + _vm->setHotspot(kHS38WalkArea5, 770, 0, 799, 445); + _vm->setHotspot(kHS38WalkArea6, 393, 0, 698, 445, SF_WALKABLE | SF_DISABLED); + _vm->setDeviceHotspot(kHS38Device, -1, -1, -1, -1); + if (plat._actionStatus == kAS38PlatypusHoldingTrapDoor) + _vm->_hotspots[kHS38Platypus]._flags = SF_WALKABLE | SF_DISABLED; + if (plat._actionStatus == kAS38PlatypusHoldingTrapDoor) + _vm->_hotspots[kHS38ExitCave]._flags = SF_EXIT_D_CURSOR; + else if (gnap._actionStatus == kAS38HoldingHuntingTrophy) + _vm->_hotspots[kHS38ExitCave]._flags = SF_EXIT_D_CURSOR; + if (plat._actionStatus == kAS38PlatypusHoldingTrapDoor) + _vm->_hotspots[kHS38TrapDoorLid1]._flags = SF_DISABLED; + else if (gnap._actionStatus == kAS38HoldingHuntingTrophy) + _vm->_hotspots[kHS38TrapDoorLid1]._flags = SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR; + if (plat._actionStatus == kAS38PlatypusHoldingTrapDoor) + _vm->_hotspots[kHS38TrapDoorLid2]._flags = SF_DISABLED; + else if (gnap._actionStatus == kAS38HoldingHuntingTrophy) + _vm->_hotspots[kHS38TrapDoorLid2]._flags = SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR; + if (plat._actionStatus == kAS38PlatypusHoldingTrapDoor) + _vm->_hotspots[kHS38WalkArea6]._flags = SF_NONE; + _vm->_hotspotsCount = 13; +} + +void Scene38::run() { + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->queueInsertDeviceIcon(); + _vm->_gameSys->insertSequence(0x9B, 0, 0, 0, kSeqNone, 0, 0, 0); + + if (_vm->_prevSceneNum == 39) { + gnap.initPos(3, 7, kDirBottomLeft); + plat.initPos(4, 7, kDirIdleRight); + } else { + gnap.initPos(3, 8, kDirBottomRight); + plat.initPos(4, 8, kDirIdleLeft); + } + _vm->endSceneInit(); + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS38Device: + _vm->runMenu(); + updateHotspots(); + break; + + case kHS38Platypus: + if (gnap._actionStatus == kAS38HoldingHuntingTrophy) { + gnap._actionStatus = kAS38ReleaseHuntingTrophy; + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(plat._pos); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(plat._pos); + break; + } + } + break; + + case kHS38ExitHouse: + if (gnap._actionStatus == kAS38HoldingHuntingTrophy) { + gnap._actionStatus = kAS38ReleaseHuntingTrophy; + } else { + _vm->_isLeavingScene = true; + gnap.walkTo(Common::Point(-1, -1), 0, 0x107AE, 1); + gnap._actionStatus = kAS38LeaveScene; + _vm->_newSceneNum = 37; + } + break; + + case kHS38ExitCave: + if (gnap._actionStatus == kAS38HoldingHuntingTrophy) { + gnap._actionStatus = kAS38ReleaseHuntingTrophy; + if (plat._actionStatus == kAS38PlatypusHoldingTrapDoor) + _vm->_isLeavingScene = true; + } else if (plat._actionStatus == kAS38PlatypusHoldingTrapDoor) { + _vm->_sceneWaiting = false; + _vm->_isLeavingScene = true; + gnap.walkTo(Common::Point(5, 7), 0, 0x107BB, 1); + _vm->_newSceneNum = 39; + gnap._actionStatus = kAS38ExitCave; + } + break; + + case kHS38TrapDoorLid1: + case kHS38TrapDoorLid2: + if (gnap._actionStatus == kAS38HoldingHuntingTrophy) { + if (_vm->_verbCursor == PLAT_CURSOR && plat._actionStatus != kAS38PlatypusHoldingTrapDoor) + gnap._actionStatus = kAS38UsePlatypusWithTrapDoor; + else + gnap._actionStatus = kAS38ReleaseHuntingTrophy; + } + break; + + case kHS38HuntingTrophy: + if (gnap._actionStatus != kAS38HoldingHuntingTrophy) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(3, 6), 2, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(); + break; + case GRAB_CURSOR: + if (plat._actionStatus == kAS38PlatypusHoldingTrapDoor) + gnap.playImpossible(); + else { + gnap.walkTo(Common::Point(3, 6), 0, 0x107BB, 1); + plat.walkTo(Common::Point(4, 8), -1, -1, 1); + gnap._actionStatus = kAS38UseHuntingTrophy; + } + break; + case TALK_CURSOR: + gnap.playBrainPulsating(Common::Point(2, 0)); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS38WalkArea1: + // Nothing + break; + + case kHS38WalkArea2: + case kHS38WalkArea3: + case kHS38WalkArea4: + case kHS38WalkArea5: + case kHS38WalkArea6: + if (gnap._actionStatus == kAS38HoldingHuntingTrophy) + gnap._actionStatus = kAS38ReleaseHuntingTrophy; + else if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left) { + if (gnap._actionStatus == kAS38HoldingHuntingTrophy) + gnap._actionStatus = kAS38ReleaseHuntingTrophy; + else if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + } + + updateAnimations(); + + if (!_vm->_isLeavingScene) { + plat.updateIdleSequence(); + gnap.updateIdleSequence(); + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } +} + +void Scene38::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case kAS38LeaveScene: + _vm->_sceneDone = true; + break; + case kAS38ExitCave: + gameSys.removeSequence(plat._sequenceId | (plat._sequenceDatNum << 16), plat._id, true); + gameSys.insertSequence(0xA3, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0xA3; + gnap._sequenceDatNum = 0; + gameSys.setAnimation(0xA3, gnap._id, 0); + gnap._actionStatus = kAS38LeaveScene; + break; + case kAS38UseHuntingTrophy: + gameSys.removeSequence(0x9B, 0, true); + gameSys.insertSequence(0x9C, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x9C; + gnap._sequenceDatNum = 0; + gameSys.setAnimation(0x9C, gnap._id, 0); + gnap._actionStatus = kAS38HoldingHuntingTrophy; + updateHotspots(); + break; + case kAS38HoldingHuntingTrophy: + if (plat._actionStatus != kAS38PlatypusHoldingTrapDoor) + _vm->_sceneWaiting = true; + if (gnap._sequenceId == 0xA4) { + gameSys.insertSequence(0x9D, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x9D; + } else { + gameSys.insertSequence(0xA4, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0xA4; + } + gnap._sequenceDatNum = 0; + gameSys.setAnimation(gnap._sequenceId, gnap._id, 0); + break; + case kAS38ReleaseHuntingTrophy: + if (gnap._sequenceId == 0x9E) { + gameSys.insertSequence(0x9B, 0, 0, 0, kSeqNone, 0, 0, 0); + gnap._actionStatus = -1; + } else if (plat._actionStatus == kAS38PlatypusHoldingTrapDoor) { + gameSys.insertSequence(0xA0, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0xA0; + gnap._sequenceDatNum = 0; + gnap._pos = Common::Point(3, 6); + gnap._idleFacing = kDirBottomRight; + if (_vm->_isLeavingScene) { + _vm->_sceneWaiting = false; + gnap.walkTo(Common::Point(5, 7), 0, 0x107BB, 1); + _vm->_newSceneNum = 39; + gnap._actionStatus = kAS38ExitCave; + } else { + gnap._actionStatus = -1; + } + } else { + gameSys.insertSequence(0x9E, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x9E; + gnap._sequenceDatNum = 0; + gnap._pos = Common::Point(3, 6); + gnap._idleFacing = kDirBottomRight; + gameSys.setAnimation(0x9E, gnap._id, 0); + _vm->_sceneWaiting = false; + updateHotspots(); + } + break; + case kAS38UsePlatypusWithTrapDoor: + _vm->_sceneWaiting = false; + gameSys.insertSequence(0x9F, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x9F; + gnap._sequenceDatNum = 0; + gameSys.setAnimation(0x9F, gnap._id, 0); + gnap._actionStatus = kAS38HoldingHuntingTrophy; + if (plat._idleFacing != kDirIdleLeft) + plat.playSequence(0x107D5); + else + plat.playSequence(0x107D4); + plat.walkTo(Common::Point(8, 7), -1, 0x107D2, 1); + gameSys.insertSequence(0xA1, gnap._id + 1, plat._sequenceId | (plat._sequenceDatNum << 16), plat._id, kSeqSyncWait, 0, 0, 0); + plat._sequenceId = 0xA1; + plat._sequenceDatNum = 0; + plat._id = gnap._id + 1; + gameSys.setAnimation(0xA1, gnap._id + 1, 1); + plat._actionStatus = kAS38PlatypusHoldingTrapDoor; + updateHotspots(); + break; + } + } + + if (gameSys.getAnimationStatus(1) == 2) { + gameSys.setAnimation(0, 0, 1); + if (plat._actionStatus == kAS38PlatypusHoldingTrapDoor) { + gameSys.insertSequence(0xA2, plat._id, plat._sequenceId | (plat._sequenceDatNum << 16), plat._id, kSeqSyncWait, 0, 0, 0); + plat._sequenceId = 0xA2; + plat._sequenceDatNum = 0; + updateHotspots(); + _vm->_sceneWaiting = true; + } + } +} + +/*****************************************************************************/ + +Scene39::Scene39(GnapEngine *vm) : Scene(vm) { + _currGuySequenceId = -1; + _nextGuySequenceId = -1; +} + +int Scene39::init() { + GameSys& gameSys = *_vm->_gameSys; + + gameSys.setAnimation(0, 0, 0); + gameSys.setAnimation(0, 0, 1); + return 0x35; +} + +void Scene39::updateHotspots() { + _vm->setHotspot(kHS39Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS39ExitInsideHouse, 0, 0, 140, 206, SF_EXIT_U_CURSOR, 4, 8); + _vm->setHotspot(kHS39ExitUfoParty, 360, 204, 480, 430, SF_EXIT_R_CURSOR, 6, 8); + _vm->setHotspot(kHS39Sign, 528, 232, 607, 397, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 7, 3); + _vm->setHotspot(kHS39WalkArea1, 0, 0, 800, 466); + _vm->setHotspot(kHS39WalkArea2, 502, 466, 800, 600); + _vm->setDeviceHotspot(kHS39Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 7; +} + +void Scene39::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + // Bug in the original? Timer was never initialized. + _vm->_timers[5] = 0; + + _vm->queueInsertDeviceIcon(); + _currGuySequenceId = 0x33; + + gameSys.setAnimation(0x33, 21, 3); + gameSys.insertSequence(_currGuySequenceId, 21, 0, 0, kSeqNone, 0, 0, 0); + gameSys.insertSequence(0x34, 21, 0, 0, kSeqLoop, 0, 0, 0); + + _nextGuySequenceId = -1; + if (_vm->_prevSceneNum == 38) { + gnap.initPos(3, 7, kDirUpRight); + plat.initPos(2, 7, kDirUpLeft); + _vm->endSceneInit(); + } else { + gnap.initPos(4, 7, kDirBottomRight); + plat.initPos(5, 7, kDirIdleLeft); + _vm->endSceneInit(); + } + + while (!_vm->_sceneDone) { + if (!_vm->isSoundPlaying(0x1094B)) { + _vm->playSound(0x1094B, true); + _vm->setSoundVolume(0x1094B, 60); + } + + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->testWalk(0, 0, -1, -1, -1, -1); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS39Device: + _vm->runMenu(); + updateHotspots(); + _vm->_timers[5] = _vm->getRandom(20) + 50; + break; + + case kHS39Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(plat._pos); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(plat._pos); + break; + } + } + } + break; + + case kHS39ExitUfoParty: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_sceneDone = true; + gnap.walkTo(gnap._pos, 0, 0x107AB, 1); + gnap._actionStatus = kAS39LeaveScene; + _vm->_newSceneNum = 40; + } + break; + + case kHS39Sign: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.walkTo(_vm->_hotspotsWalkPos[kHS39Sign], 0, -1, 1); + gnap.playIdle(_vm->_hotspotsWalkPos[kHS39Sign]); + _vm->showFullScreenSprite(0x1C); + break; + case GRAB_CURSOR: + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS39ExitInsideHouse: + if (gnap._actionStatus < 0) { + _vm->_sceneDone = true; + _vm->_isLeavingScene = true; + _vm->_newSceneNum = 38; + } + break; + + case kHS39WalkArea1: + case kHS39WalkArea2: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + default: + if (_vm->_mouseClickState._left && gnap._actionStatus < 0) { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + _vm->_mouseClickState._left = false; + } + break; + } + + updateAnimations(); + + if (!_vm->_isLeavingScene) { + if (plat._actionStatus < 0) + plat.updateIdleSequence(); + if (gnap._actionStatus < 0) + gnap.updateIdleSequence(); + if (!_vm->_timers[5]) { + _vm->_timers[5] = _vm->getRandom(20) + 50; + switch (_vm->getRandom(4)) { + case 0: + _nextGuySequenceId = 0x30; + break; + case 1: + _nextGuySequenceId = 0x31; + break; + case 2: + _nextGuySequenceId = 0x32; + break; + case 3: + _nextGuySequenceId = 0x33; + break; + } + } + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + _vm->_timers[5] = _vm->getRandom(20) + 50; + } + + _vm->gameUpdateTick(); + } +} + +void Scene39::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + if (gnap._actionStatus == kAS39LeaveScene) + _vm->_sceneDone = true; + else + gnap._actionStatus = -1; + } + + if (gameSys.getAnimationStatus(3) == 2 && _nextGuySequenceId != -1) { + gameSys.setAnimation(_nextGuySequenceId, 21, 3); + gameSys.insertSequence(_nextGuySequenceId, 21, _currGuySequenceId, 21, kSeqSyncWait, 0, 0, 0); + _currGuySequenceId = _nextGuySequenceId; + _nextGuySequenceId = -1; + } +} + +} // End of namespace Gnap diff --git a/engines/gnap/scenes/group3.h b/engines/gnap/scenes/group3.h new file mode 100644 index 0000000000..6fbbdd79aa --- /dev/null +++ b/engines/gnap/scenes/group3.h @@ -0,0 +1,240 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GNAP_GROUP3_H +#define GNAP_GROUP3_H + +#include "gnap/debugger.h" + +namespace Gnap { + +enum { + kHS30Platypus = 0, + kHS30PillMachine = 1, + kHS30Device = 2, + kHS30ExitCircus = 3, + kHS30WalkArea1 = 4 +}; + +enum { + kHS31Platypus = 0, + kHS31MeasuringClown = 1, + kHS31BeerBarrel = 2, + kHS31Device = 3, + kHS31ExitCircus = 4, + kHS31ExitOutsideClown = 5, + kHS31WalkArea1 = 6 +}; + +enum { + kHS32Platypus = 0, + kHS32ExitTruck = 1, + kHS32Device = 2, + kHS32WalkArea1 = 3, + kHS32WalkArea2 = 4, + kHS32WalkArea3 = 5, + kHS32WalkArea4 = 6, + kHS32WalkArea5 = 7, + kHS32WalkArea6 = 8, + kHS32WalkArea7 = 9, + kHS32WalkArea8 = 10 +}; + +enum { + kHS33Platypus = 0, + kHS33Chicken = 1, + kHS33Device = 2, + kHS33ExitHouse = 3, + kHS33ExitBarn = 4, + kHS33ExitCreek = 5, + kHS33ExitPigpen = 6, + kHS33WalkArea1 = 7, + kHS33WalkArea2 = 8 +}; + +enum { + kHS38Platypus = 0, + kHS38ExitHouse = 1, + kHS38ExitCave = 2, + kHS38TrapDoorLid1 = 3, + kHS38TrapDoorLid2 = 4, + kHS38HuntingTrophy = 5, + kHS38WalkArea1 = 6, + kHS38Device = 7, + kHS38WalkArea2 = 8, + kHS38WalkArea3 = 9, + kHS38WalkArea4 = 10, + kHS38WalkArea5 = 11, + kHS38WalkArea6 = 12 +}; + +enum { + kHS39Platypus = 0, + kHS39ExitInsideHouse = 1, + kHS39ExitUfoParty = 2, + kHS39Sign = 3, + kHS39Device = 4, + kHS39WalkArea1 = 5, + kHS39WalkArea2 = 6 +}; + +enum { + kAS30LeaveScene = 0, + kAS30UsePillMachine = 1, + kAS30UsePillMachine2 = 2, + kAS30LookPillMachine = 3, + kAS30UsePillMachine3 = 4, + kAS30UsePillMachine4 = 5 +}; + +enum { + kAS31UseBeerBarrel = 1, + kAS31FillEmptyBucketWithBeer = 2, + kAS31FillEmptyBucketWithBeerDone = 3, + kAS31PlatMeasuringClown = 4, + kAS31UseMeasuringClown = 5, + kAS31LeaveScene = 6 +}; + +enum { + kAS32LeaveScene = 0 +}; + +enum { + kAS33LeaveScene = 0, + kAS33TalkChicken = 1, + kAS33UseChicken = 2, + kAS33UseChickenDone = 3 +}; + +enum { + kAS38LeaveScene = 0, + kAS38ExitCave = 1, + kAS38UseHuntingTrophy = 2, + kAS38HoldingHuntingTrophy = 3, + kAS38ReleaseHuntingTrophy = 4, + kAS38UsePlatypusWithTrapDoor = 5, + kAS38PlatypusHoldingTrapDoor = 6 +}; + +enum { + kAS39LeaveScene = 0 +}; + +/*****************************************************************************/ + +class GnapEngine; +class CutScene; + +class Scene30: public Scene { +public: + Scene30(GnapEngine *vm); + virtual ~Scene30() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _kidSequenceId; +}; + +class Scene31: public Scene { +public: + Scene31(GnapEngine *vm); + virtual ~Scene31() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + bool _beerGuyDistracted; + int _currClerkSequenceId; + int _nextClerkSequenceId; + int _clerkMeasureCtr; + int _clerkMeasureMaxCtr; +}; + +class Scene32: public Scene { +public: + Scene32(GnapEngine *vm); + virtual ~Scene32() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; +}; + +class Scene33: public Scene { +public: + Scene33(GnapEngine *vm); + virtual ~Scene33() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _currChickenSequenceId; + int _nextChickenSequenceId; +}; + +class Scene38: public Scene { +public: + Scene38(GnapEngine *vm); + virtual ~Scene38() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; +}; + +class Scene39: public Scene { +public: + Scene39(GnapEngine *vm); + virtual ~Scene39() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _currGuySequenceId; + int _nextGuySequenceId; +}; + +} // End of namespace Gnap + +#endif // GNAP_GROUP3_H diff --git a/engines/gnap/scenes/group4.cpp b/engines/gnap/scenes/group4.cpp new file mode 100644 index 0000000000..f37be2c25d --- /dev/null +++ b/engines/gnap/scenes/group4.cpp @@ -0,0 +1,2799 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gnap/gnap.h" +#include "gnap/gamesys.h" +#include "gnap/resource.h" +#include "gnap/scenes/group4.h" + +namespace Gnap { + +Scene40::Scene40(GnapEngine *vm) : Scene(vm) { +} + +int Scene40::init() { + GameSys& gameSys = *_vm->_gameSys; + + gameSys.setAnimation(0, 0, 0); + gameSys.setAnimation(0, 0, 1); + return _vm->isFlag(kGFUnk23) ? 0x01 : 0x00; +} + +void Scene40::updateHotspots() { + _vm->setHotspot(kHS40Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_DISABLED | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS40ExitCave, 169, 510, 264, 600, SF_EXIT_D_CURSOR, 0, 8); + _vm->setHotspot(kHS40ExitToyStand, 238, 297, 328, 376, SF_EXIT_L_CURSOR, 0, 8); + _vm->setHotspot(kHS40ExitBBQ, 328, 220, 401, 306, SF_EXIT_L_CURSOR, 0, 8); + _vm->setHotspot(kHS40ExitUfo, 421, 215, 501, 282, SF_EXIT_U_CURSOR, 0, 8); + _vm->setHotspot(kHS40ExitKissinBooth, 476, 284, 556, 345, SF_EXIT_R_CURSOR, 0, 8); + _vm->setHotspot(kHS40ExitDancefloor, 317, 455, 445, 600, SF_EXIT_D_CURSOR, 0, 8); + _vm->setHotspot(kHS40ExitShoe, 455, 346, 549, 417, SF_EXIT_D_CURSOR, 0, 8); + _vm->setDeviceHotspot(kHS40Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 9; +} + +void Scene40::run() { + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->queueInsertDeviceIcon(); + _vm->endSceneInit(); + + while (!_vm->_sceneDone) { + if (!_vm->isSoundPlaying(0x1094B)) + _vm->playSound(0x1094B, true); + + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->testWalk(0, 0, -1, -1, -1, -1); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS40Device: + _vm->runMenu(); + updateHotspots(); + break; + + case kHS40Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(plat._pos); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(plat._pos); + break; + } + } + } + break; + + case kHS40ExitCave: + if (gnap._actionStatus < 0) { + _vm->_newSceneNum = 39; + _vm->_sceneDone = true; + } + break; + + case kHS40ExitToyStand: + if (gnap._actionStatus < 0) { + _vm->_newSceneNum = 41; + _vm->_sceneDone = true; + } + break; + + case kHS40ExitBBQ: + if (gnap._actionStatus < 0) { + _vm->_newSceneNum = 42; + _vm->_sceneDone = true; + } + break; + + case kHS40ExitUfo: + if (gnap._actionStatus < 0) { + _vm->_newSceneNum = 43; + _vm->_sceneDone = true; + } + break; + + case kHS40ExitKissinBooth: + if (gnap._actionStatus < 0) { + _vm->_newSceneNum = 44; + _vm->_sceneDone = true; + } + break; + + case kHS40ExitDancefloor: + if (gnap._actionStatus < 0) { + _vm->_newSceneNum = 45; + _vm->_sceneDone = true; + } + break; + + case kHS40ExitShoe: + if (gnap._actionStatus < 0) { + _vm->_newSceneNum = 46; + _vm->_sceneDone = true; + } + break; + + default: + if (_vm->_mouseClickState._left && gnap._actionStatus < 0) + _vm->_mouseClickState._left = false; + break; + + } + + updateAnimations(); + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } +} + +void Scene40::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + if (gnap._actionStatus) + gnap._actionStatus = -1; + else + _vm->_sceneDone = true; + } +} + +/*****************************************************************************/ + +Scene41::Scene41(GnapEngine *vm) : Scene(vm) { + _currKidSequenceId = -1; + _nextKidSequenceId = -1; + _currToyVendorSequenceId = -1; + _nextToyVendorSequenceId = -1; +} + +int Scene41::init() { + GameSys& gameSys = *_vm->_gameSys; + + gameSys.setAnimation(0, 0, 0); + gameSys.setAnimation(0, 0, 1); + gameSys.setAnimation(0, 0, 2); + return 0x129; +} + +void Scene41::updateHotspots() { + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + _vm->setHotspot(kHS41Platypus, 0, 0, 0, 0, SF_DISABLED); + _vm->setHotspot(kHS41UfoExitLeft, 0, 0, 10, 500, SF_EXIT_L_CURSOR | SF_DISABLED); + _vm->setHotspot(kHS41UfoExitRight, 790, 0, 799, 500, SF_EXIT_R_CURSOR); + _vm->setHotspot(kHS41UfoWalkArea1, 0, 0, 800, 470, SF_DISABLED); + _vm->setDeviceHotspot(kHS41UfoDevice, -1, -1, -1, -1); + _vm->_hotspotsCount = 5; + } else { + _vm->setHotspot(kHS41Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS41ExitCave, 150, 590, 650, 600, SF_EXIT_D_CURSOR | SF_WALKABLE, 5, 9); + _vm->setHotspot(kHS41Exit, 0, 100, 10, 599, SF_EXIT_L_CURSOR | SF_DISABLED, 0, 8); + _vm->setHotspot(kHS41ExitBBQ, 790, 100, 799, 599, SF_EXIT_R_CURSOR | SF_WALKABLE, 10, 8); + _vm->setHotspot(kHS41ToyVendor, 320, 150, 430, 310, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS41Kid, 615, 340, 710, 460, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS41ToyUfo, 0, 0, 0, 0, SF_GRAB_CURSOR); + _vm->setHotspot(kHS41WalkArea1, 0, 0, 800, 470); + _vm->setDeviceHotspot(kHS41Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 9; + } +} + +void Scene41::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->queueInsertDeviceIcon(); + + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + _vm->_toyUfoX = 770; + if (_vm->_toyUfoY < 0 || _vm->_toyUfoY > 300) + _vm->_toyUfoY = 150; + if (!_vm->_timers[9]) + gnap._actionStatus = kAS41GiveBackToyUfo; + } else { + if (!_vm->isFlag(kGFUnk16) && !_vm->isFlag(kGFJointTaken) && !_vm->isFlag(kGFUnk18) && !_vm->isFlag(kGFGroceryStoreHatTaken)) + _vm->toyUfoSetStatus(kGFUnk16); + _vm->_toyUfoX = 600; + _vm->_toyUfoY = 200; + } + + _vm->_toyUfoId = 0; + _vm->_toyUfoActionStatus = -1; + _vm->_toyUfoSequenceId = _vm->toyUfoGetSequenceId(); + _vm->_toyUfoNextSequenceId = _vm->_toyUfoSequenceId; + + gameSys.setAnimation(_vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, 2); + gameSys.insertSequence(_vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, 0, 0, kSeqNone, 0, _vm->_toyUfoX - 274, _vm->_toyUfoY - 128); + gameSys.insertSequence(0x128, 0, 0, 0, kSeqLoop, 0, 0, 0); + + if (_vm->isFlag(kGFGnapControlsToyUFO)) + _currKidSequenceId = 0x11B; + else + _currKidSequenceId = 0x11D; + + _nextKidSequenceId = -1; + + gameSys.setAnimation(_currKidSequenceId, 1, 4); + gameSys.insertSequence(_currKidSequenceId, 1, 0, 0, kSeqNone, 0, 0, 0); + + _currToyVendorSequenceId = 0x118; + _nextToyVendorSequenceId = -1; + + gameSys.setAnimation(0x118, 1, 3); + gameSys.insertSequence(_currToyVendorSequenceId, 1, 0, 0, kSeqNone, 0, 0, 0); + gameSys.insertSequence(0x127, 2, 0, 0, kSeqNone, 0, 0, 0); + + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + gnap._sequenceId = 0x120; + gnap._sequenceDatNum = 0; + gnap._idleFacing = kDirUpRight; + gnap._pos = Common::Point(7, 7); + gnap._id = 140; + gameSys.insertSequence(0x120, 140, 0, 0, kSeqNone, 0, 0, 0); + gameSys.setAnimation(makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, 0); + plat.initPos(8, 10, kDirBottomLeft); + _vm->endSceneInit(); + } else if (_vm->_prevSceneNum == 45) { + gnap.initPos(-1, 8, kDirUpRight); + plat.initPos(-2, 8, kDirUpLeft); + _vm->endSceneInit(); + plat.walkTo(Common::Point(1, 8), -1, 0x107C2, 1); + gnap.walkTo(Common::Point(2, 8), -1, 0x107B9, 1); + } else if (_vm->_prevSceneNum == 42) { + gnap.initPos(11, 8, kDirUpRight); + plat.initPos(11, 9, kDirUpLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(8, 8), -1, 0x107BA, 1); + plat.walkTo(Common::Point(9, 8), -1, 0x107D2, 1); + } else { + gnap.initPos(5, 8, kDirBottomRight); + plat.initPos(6, 8, kDirBottomLeft); + _vm->endSceneInit(); + } + + _vm->_timers[4] = _vm->getRandom(100) + 100; + _vm->_timers[5] = _vm->getRandom(30) + 20; + + while (!_vm->_sceneDone) { + if (!_vm->isSoundPlaying(0x1094B)) + _vm->playSound(0x1094B, true); + + if (!_vm->isFlag(kGFGnapControlsToyUFO)) + _vm->_hotspots[kHS41ToyUfo]._rect = Common::Rect(_vm->_toyUfoX - 25, _vm->_toyUfoY - 20, _vm->_toyUfoX + 25, _vm->_toyUfoY + 20); + + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->testWalk(0, 0, -1, -1, -1, -1); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + switch (_vm->_sceneClickedHotspot) { + case kHS41UfoExitLeft: + if (_vm->_toyUfoActionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_toyUfoActionStatus = kAS41ToyUfoLeaveScene; + _vm->_newSceneNum = 45; + _vm->toyUfoFlyTo(-35, -1, -35, 799, 0, 300, 2); + } + break; + + case kHS41UfoExitRight: + if (_vm->_toyUfoActionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_toyUfoActionStatus = kAS41ToyUfoLeaveScene; + _vm->_newSceneNum = 42; + _vm->toyUfoFlyTo(835, -1, 0, 835, 0, 300, 2); + } + break; + + case kHS41UfoDevice: + _vm->runMenu(); + updateHotspots(); + _vm->_timers[4] = _vm->getRandom(100) + 100; + _vm->_timers[5] = _vm->getRandom(30) + 20; + break; + } + } else { + switch (_vm->_sceneClickedHotspot) { + case kHS41Device: + _vm->runMenu(); + updateHotspots(); + _vm->_timers[4] = _vm->getRandom(100) + 100; + _vm->_timers[5] = _vm->getRandom(30) + 20; + break; + + case kHS41Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(plat._pos); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(plat._pos); + break; + } + } + } + break; + + case kHS41ExitCave: + _vm->_isLeavingScene = true; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS41ExitCave], 0, 0x107AE, 1); + gnap._actionStatus = kAS41LeaveScene; + _vm->_newSceneNum = 40; + break; + + case kHS41Exit: + _vm->_isLeavingScene = true; + gnap.walkTo(Common::Point(_vm->_hotspotsWalkPos[kHS41Exit].x, -1), 0, 0x107AF, 1); + gnap._actionStatus = kAS41LeaveScene; + plat.walkTo(Common::Point(_vm->_hotspotsWalkPos[kHS41Exit].x, -1), -1, 0x107CF, 1); + _vm->_newSceneNum = 45; + break; + + case kHS41ExitBBQ: + _vm->_isLeavingScene = true; + gnap.walkTo(Common::Point(_vm->_hotspotsWalkPos[kHS41ExitBBQ].x, -1), 0, 0x107AB, 1); + gnap._actionStatus = kAS41LeaveScene; + plat.walkTo(Common::Point(_vm->_hotspotsWalkPos[kHS41ExitBBQ].x, -1), -1, 0x107CD, 1); + _vm->_newSceneNum = 42; + break; + + case kHS41ToyVendor: + if (_vm->_grabCursorSpriteIndex == kItemDiceQuarterHole) { + gnap._actionStatus = kAS41UseQuarterWithToyVendor; + gnap.walkTo(Common::Point(4, 7), 0, 0x107BB, 9); + gnap.playShowItem(_vm->_grabCursorSpriteIndex, 5, 0); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(4, 7), 5, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(Common::Point(5, 0)); + break; + case GRAB_CURSOR: + gnap.playImpossible(); + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.walkTo(Common::Point(4, 7), 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS41TalkToyVendor; + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + break; + + case kHS41Kid: + if (_vm->_grabCursorSpriteIndex == kItemChickenBucket) { + gnap.walkTo(Common::Point(7, 7), 0, 0x107BB, 1); + gnap._idleFacing = kDirUpRight; + gnap._actionStatus = kAS41UseChickenBucketWithKid; + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(7, 7), 8, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(9, 0)); + break; + case GRAB_CURSOR: + gnap.walkTo(Common::Point(7, 7), 0, 0x107BB, 1); + gnap._idleFacing = kDirUpRight; + gnap._actionStatus = kAS41GrabKid; + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.walkTo(Common::Point(7, 7), 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + break; + + case kHS41ToyUfo: + if (_vm->_grabCursorSpriteIndex == kItemGum) { + gnap.playPullOutDevice(Common::Point(9, 0)); + gameSys.setAnimation(makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, 0); + gnap._actionStatus = kAS41UseGumWithToyUfo; + } + break; + + case kHS41WalkArea1: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + } + } + + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + if (!_vm->_timers[9] && gnap._actionStatus < 0) { + gnap._actionStatus = kAS41GiveBackToyUfo; + if (gnap._sequenceId == 0x121 || gnap._sequenceId == 0x122) { + gameSys.insertSequence(0x123, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x123; + gnap._sequenceDatNum = 0; + gameSys.setAnimation(0x123, gnap._id, 0); + } + } + } + + if (_vm->_mouseClickState._left && gnap._actionStatus < 0) { + _vm->_mouseClickState._left = false; + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + int sequenceId; + if (_vm->_leftClickMouseX >= 400) { + if (gnap._sequenceId == 0x11F || gnap._sequenceId == 0x120 || gnap._sequenceId == 0x123 || gnap._sequenceId == 0x126) + sequenceId = 0x120; + else if (_vm->_leftClickMouseX - _vm->_toyUfoX >= 400) + sequenceId = 0x126; + else + sequenceId = 0x123; + } else { + if (gnap._sequenceId == 0x121 || gnap._sequenceId == 0x125 || gnap._sequenceId == 0x122) + sequenceId = 0x122; + else if (_vm->_toyUfoX - _vm->_leftClickMouseX >= 400) + sequenceId = 0x125; + else + sequenceId = 0x121; + } + gameSys.insertSequence(sequenceId, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = sequenceId; + gnap._sequenceDatNum = 0; + gameSys.setAnimation(sequenceId, gnap._id, 0); + _vm->_toyUfoActionStatus = kAS41ToyUfoRefresh; + _vm->toyUfoFlyTo(-1, -1, 0, 799, 0, 300, 2); + } else { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + } + } + + updateAnimations(); + + if (!_vm->_isLeavingScene) { + if (plat._actionStatus < 0) + plat.updateIdleSequence(); + if (gnap._actionStatus < 0 && !_vm->isFlag(kGFGnapControlsToyUFO)) + gnap.updateIdleSequence(); + if (!_vm->_timers[4]) { + _vm->_timers[4] = _vm->getRandom(100) + 100; + if (gnap._actionStatus < 0 && plat._actionStatus < 0 && _vm->_toyUfoActionStatus == -1 && _nextToyVendorSequenceId == -1) { + switch (_vm->getRandom(3)) { + case 0: + _nextToyVendorSequenceId = 0x113; + break; + case 1: + _nextToyVendorSequenceId = 0x117; + break; + case 2: + _nextToyVendorSequenceId = 0x119; + break; + } + if (_nextToyVendorSequenceId == _currToyVendorSequenceId) + _nextToyVendorSequenceId = -1; + } + } + if (!_vm->_timers[5]) { + _vm->_timers[5] = _vm->getRandom(30) + 20; + if (gnap._actionStatus < 0 && plat._actionStatus < 0 && _vm->_toyUfoActionStatus == -1 && _nextKidSequenceId == -1) { + if (_vm->isFlag(kGFGnapControlsToyUFO)) + _nextKidSequenceId = 0x11B; + else if (_vm->getRandom(3) != 0) + _nextKidSequenceId = 0x11D; + else + _nextKidSequenceId = 0x11E; + } + } + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + _vm->_timers[4] = _vm->getRandom(100) + 100; + _vm->_timers[5] = _vm->getRandom(30) + 20; + } + _vm->gameUpdateTick(); + } +} + +void Scene41::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + switch (gnap._actionStatus) { + case kAS41LeaveScene: + gameSys.setAnimation(0, 0, 0); + _vm->_sceneDone = true; + gnap._actionStatus = -1; + break; + case kAS41UseQuarterWithToyVendor: + gameSys.setAnimation(0, 0, 0); + _nextToyVendorSequenceId = 0x114; + gnap._actionStatus = -1; + break; + case kAS41TalkToyVendor: + gameSys.setAnimation(0, 0, 0); + _nextToyVendorSequenceId = 0x116; + gnap._actionStatus = -1; + break; + case kAS41UseGumWithToyUfo: + gameSys.setAnimation(0, 0, 0); + gnap.playUseDevice(Common::Point(9, 0)); + gnap._actionStatus = -1; + _vm->setGrabCursorSprite(-1); + _vm->invRemove(kItemGum); + _vm->_toyUfoActionStatus = kAS41UfoGumAttached; + break; + case kAS41UseChickenBucketWithKid: + if (gameSys.getAnimationStatus(4) == 2) { + _vm->_timers[2] = _vm->getRandom(30) + 20; + _vm->_timers[3] = _vm->getRandom(50) + 200; + _vm->setGrabCursorSprite(-1); + gameSys.insertSequence(0x11F, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x11F; + gnap._sequenceDatNum = 0; + gameSys.setAnimation(0x11F, gnap._id, 0); + _nextKidSequenceId = 0x11A; + gameSys.insertSequence(0x11A, 1, _currKidSequenceId, 1, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextKidSequenceId, 1, 4); + _currKidSequenceId = _nextKidSequenceId; + _nextKidSequenceId = 0x11B; + _vm->_timers[5] = _vm->getRandom(30) + 20; + gnap._actionStatus = -1; + _vm->setFlag(kGFGnapControlsToyUFO); + updateHotspots(); + _vm->_timers[9] = 600; + } + break; + case kAS41GrabKid: + if (gameSys.getAnimationStatus(3) == 2 && gameSys.getAnimationStatus(4) == 2) { + _vm->_timers[2] = _vm->getRandom(30) + 20; + _vm->_timers[3] = _vm->getRandom(50) + 200; + gameSys.insertSequence(0x110, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x110; + gnap._sequenceDatNum = 0; + gameSys.setAnimation(0x110, gnap._id, 0); + _nextToyVendorSequenceId = 0x111; + gameSys.insertSequence(0x111, 1, _currToyVendorSequenceId, 1, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextToyVendorSequenceId, 1, 3); + _currToyVendorSequenceId = _nextToyVendorSequenceId; + _nextToyVendorSequenceId = -1; + _vm->_timers[4] = _vm->getRandom(100) + 100; + _nextKidSequenceId = 0x10F; + gameSys.insertSequence(0x10F, 1, _currKidSequenceId, 1, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextKidSequenceId, 1, 4); + _currKidSequenceId = _nextKidSequenceId; + _nextKidSequenceId = -1; + _vm->_timers[5] = _vm->getRandom(30) + 20; + gnap._actionStatus = -1; + } + break; + case kAS41GiveBackToyUfo: + if (gameSys.getAnimationStatus(3) == 2 && gameSys.getAnimationStatus(4) == 2) { + _vm->_timers[2] = _vm->getRandom(30) + 20; + _vm->_timers[3] = _vm->getRandom(50) + 200; + gameSys.insertSequence(0x124, gnap._id, + makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, + kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x124; + gnap._sequenceDatNum = 0; + gameSys.setAnimation(0x124, gnap._id, 0); + _nextToyVendorSequenceId = 0x112; + gameSys.insertSequence(0x112, 1, _currToyVendorSequenceId, 1, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextToyVendorSequenceId, 1, 3); + _currToyVendorSequenceId = _nextToyVendorSequenceId; + _nextToyVendorSequenceId = -1; + _vm->_timers[4] = _vm->getRandom(100) + 100; + _nextKidSequenceId = 0x11C; + gameSys.insertSequence(0x11C, 1, _currKidSequenceId, 1, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextKidSequenceId, 1, 4); + _currKidSequenceId = _nextKidSequenceId; + _nextKidSequenceId = -1; + _vm->_timers[5] = _vm->getRandom(30) + 20; + gnap._actionStatus = -1; + _vm->clearFlag(kGFGnapControlsToyUFO); + updateHotspots(); + } + break; + } + } + + if (gameSys.getAnimationStatus(2) == 2) { + switch (_vm->_toyUfoActionStatus) { + case kAS41ToyUfoLeaveScene: + _vm->_sceneDone = true; + break; + case kAS41UfoGumAttached: + _vm->_toyUfoNextSequenceId = 0x873; + gameSys.insertSequence(0x10873, _vm->_toyUfoId, _vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, + kSeqSyncWait, 0, _vm->_toyUfoX - 365, _vm->_toyUfoY - 128); + _vm->_toyUfoSequenceId = _vm->_toyUfoNextSequenceId; + gameSys.setAnimation(_vm->_toyUfoNextSequenceId | 0x10000, _vm->_toyUfoId, 2); + _vm->toyUfoSetStatus(kGFJointTaken); + break; + default: + _vm->_toyUfoNextSequenceId = _vm->toyUfoGetSequenceId(); + gameSys.insertSequence(_vm->_toyUfoNextSequenceId | 0x10000, _vm->_toyUfoId + 1, _vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, + kSeqSyncWait, 0, _vm->_toyUfoX - 274, _vm->_toyUfoY - 128); + _vm->_toyUfoSequenceId = _vm->_toyUfoNextSequenceId; + ++_vm->_toyUfoId; + gameSys.setAnimation(_vm->_toyUfoNextSequenceId | 0x10000, _vm->_toyUfoId, 2); + break; + } + _vm->_toyUfoActionStatus = -1; + } + + if (gameSys.getAnimationStatus(3) == 2 && _nextToyVendorSequenceId != -1) { + gameSys.insertSequence(_nextToyVendorSequenceId, 1, _currToyVendorSequenceId, 1, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextToyVendorSequenceId, 1, 3); + _currToyVendorSequenceId = _nextToyVendorSequenceId; + _nextToyVendorSequenceId = -1; + _vm->_timers[4] = _vm->getRandom(100) + 100; + } + + if (gameSys.getAnimationStatus(4) == 2 && _nextKidSequenceId != -1) { + gameSys.insertSequence(_nextKidSequenceId, 1, _currKidSequenceId, 1, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextKidSequenceId, 1, 4); + _currKidSequenceId = _nextKidSequenceId; + _nextKidSequenceId = -1; + _vm->_timers[5] = _vm->getRandom(30) + 20; + if (_currKidSequenceId == 0x11E) { + _vm->_toyUfoActionStatus = kAS41ToyUfoRefresh; + _vm->toyUfoFlyTo(_vm->getRandom(300) + 500, _vm->getRandom(225) + 75, 0, 799, 0, 300, 2); + } + } +} + +/*****************************************************************************/ + +Scene42::Scene42(GnapEngine *vm) : Scene(vm) { + _currBBQVendorSequenceId = -1; + _nextBBQVendorSequenceId = -1; +} + +int Scene42::init() { + GameSys& gameSys = *_vm->_gameSys; + + gameSys.setAnimation(0, 0, 0); + gameSys.setAnimation(0, 0, 1); + gameSys.setAnimation(0, 0, 2); + if (_vm->isFlag(kGFPictureTaken) || (_vm->isFlag(kGFUnk18) && _vm->isFlag(kGFUnk23))) + return 0x153; + return 0x152; +} + +void Scene42::updateHotspots() { + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + _vm->setHotspot(kHS42Platypus, 0, 0, 0, 0, SF_DISABLED); + _vm->setHotspot(kHS42UfoExitLeft, 0, 0, 10, 599, SF_EXIT_L_CURSOR); + _vm->setHotspot(kHS42UfoExitRight, 790, 0, 799, 599, SF_EXIT_R_CURSOR); + _vm->setHotspot(kHS42UfoHotSauce, 335, 110, 440, 175, SF_DISABLED); + _vm->setDeviceHotspot(kHS42UfoDevice, -1, 534, -1, 599); + if ((_vm->isFlag(kGFPictureTaken) || _vm->isFlag(kGFUnk18)) && _vm->isFlag(kGFUnk23) && !_vm->isFlag(kGFUnk24)) + _vm->_hotspots[kHS42UfoHotSauce]._flags = SF_GRAB_CURSOR; + _vm->_hotspotsCount = 5; + } else { + _vm->setHotspot(kHS42Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS42ExitUfoParty, 150, 585, 650, 600, SF_EXIT_D_CURSOR | SF_WALKABLE, 5, 9); + _vm->setHotspot(kHS42ExitToyStand, 0, 100, 10, 599, SF_EXIT_L_CURSOR, 0, 8); + _vm->setHotspot(kHS42ExitUfo, 790, 100, 799, 599, SF_EXIT_R_CURSOR, 10, 8); + _vm->setHotspot(kHS42BBQVendor, 410, 200, 520, 365, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 4, 8); + _vm->setHotspot(kHS42ChickenLeg, 530, 340, 620, 430, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 8, 7); + _vm->setHotspot(kHS42WalkArea1, 0, 0, 800, 445); + _vm->setHotspot(kHS42WalkArea2, 240, 0, 550, 495); + _vm->setDeviceHotspot(kHS42Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 9; + } +} + +void Scene42::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->queueInsertDeviceIcon(); + + _currBBQVendorSequenceId = 0x14A; + _nextBBQVendorSequenceId = -1; + + gameSys.setAnimation(0x14A, 1, 2); + gameSys.insertSequence(_currBBQVendorSequenceId, 1, 0, 0, kSeqNone, 0, 0, 0); + + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + _vm->_toyUfoId = 0; + _vm->_toyUfoActionStatus = -1; + if (_vm->_prevSceneNum == 43 && _vm->isFlag(kGFUnk18)) { + _vm->_toyUfoSequenceId = 0x872; + _vm->_toyUfoNextSequenceId = _vm->toyUfoGetSequenceId(); + gameSys.insertSequence(_vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, 0, 0, kSeqNone, 0, 0, 0); + _vm->_toyUfoX = 317; + _vm->_toyUfoY = 61; + _vm->toyUfoSetStatus(kGFJointTaken); + _vm->setFlag(kGFPictureTaken); + _vm->_timers[9] = 600; + } else { + _vm->_toyUfoSequenceId = _vm->toyUfoGetSequenceId(); + _vm->_toyUfoNextSequenceId = _vm->_toyUfoSequenceId; + if (_vm->_prevSceneNum == 41) + _vm->_toyUfoX = 30; + else + _vm->_toyUfoX = 770; + gameSys.insertSequence(_vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, 0, 0, kSeqNone, 0, _vm->_toyUfoX - 274, _vm->_toyUfoY - 128); + } + gameSys.setAnimation(_vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, 3); + _vm->endSceneInit(); + if (_vm->_toyUfoSequenceId == 0x872) + _vm->setGrabCursorSprite(-1); + } else if (_vm->_prevSceneNum == 41) { + gnap.initPos(-1, 8, kDirUpRight); + plat.initPos(-1, 9, kDirUpLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(2, 8), -1, 0x107B9, 1); + plat.walkTo(Common::Point(1, 8), -1, 0x107C2, 1); + } else if (_vm->_prevSceneNum == 43) { + gnap.initPos(11, 8, kDirUpRight); + plat.initPos(11, 9, kDirUpLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(8, 8), -1, 0x107BA, 1); + plat.walkTo(Common::Point(9, 8), -1, 0x107D2, 1); + } else { + gnap.initPos(5, 11, kDirUpRight); + plat.initPos(6, 11, kDirUpLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(5, 8), -1, 0x107BA, 1); + plat.walkTo(Common::Point(6, 8), -1, 0x107C2, 1); + } + + while (!_vm->_sceneDone) { + if (!_vm->isSoundPlaying(0x1094B)) + _vm->playSound(0x1094B, true); + + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->testWalk(0, 0, -1, -1, -1, -1); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + switch (_vm->_sceneClickedHotspot) { + case kHS42UfoExitLeft: + if (_vm->_toyUfoActionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_toyUfoActionStatus = kAS42ToyUfoLeaveScene; + _vm->_newSceneNum = 41; + _vm->toyUfoFlyTo(-35, -1, -35, 799, 0, 300, 3); + } + break; + + case kHS42UfoExitRight: + if (_vm->_toyUfoActionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_toyUfoActionStatus = kAS42ToyUfoLeaveScene; + _vm->_newSceneNum = 43; + _vm->toyUfoFlyTo(835, -1, 0, 835, 0, 300, 3); + } + break; + + case kHS42UfoHotSauce: + if (_vm->isFlag(kGFJointTaken)) { + _vm->_toyUfoActionStatus = kAS42ToyUfoPickUpHotSauce; + _vm->toyUfoFlyTo(384, 77, 0, 799, 0, 300, 3); + _vm->_timers[9] = 600; + } else { + _vm->_toyUfoActionStatus = kAS42ToyUfoRefresh; + _vm->toyUfoFlyTo(-1, -1, 0, 799, 0, 300, 3); + } + break; + + case kHS42UfoDevice: + _vm->runMenu(); + updateHotspots(); + break; + } + } else { + switch (_vm->_sceneClickedHotspot) { + case kHS42Device: + _vm->runMenu(); + updateHotspots(); + _vm->_timers[4] = _vm->getRandom(20) + 30; + break; + + case kHS42Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(plat._pos); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(plat._pos); + break; + } + } + } + break; + + case kHS42ExitUfoParty: + _vm->_isLeavingScene = true; + gnap.walkTo(Common::Point(gnap._pos.x, _vm->_hotspotsWalkPos[kHS42ExitUfoParty].y), 0, 0x107AE, 1); + gnap._actionStatus = kAS42LeaveScene; + plat.walkTo(Common::Point(plat._pos.x, _vm->_hotspotsWalkPos[kHS42ExitUfoParty].y), -1, 0x107C7, 1); + _vm->_newSceneNum = 40; + break; + + case kHS42ExitToyStand: + _vm->_isLeavingScene = true; + gnap.walkTo(Common::Point(_vm->_hotspotsWalkPos[kHS42ExitToyStand].x, gnap._pos.y), 0, 0x107AF, 1); + gnap._actionStatus = kAS42LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS42ExitToyStand], -1, 0x107CF, 1); + _vm->_newSceneNum = 41; + break; + + case kHS42ExitUfo: + _vm->_isLeavingScene = true; + gnap.walkTo(Common::Point(_vm->_hotspotsWalkPos[kHS42ExitUfo].x, gnap._pos.y), 0, 0x107AB, 1); + gnap._actionStatus = kAS42LeaveScene; + plat.walkTo(_vm->_hotspotsWalkPos[kHS42ExitUfo], -1, 0x107CD, 1); + _vm->_newSceneNum = 43; + break; + + case kHS42BBQVendor: + if (_vm->_grabCursorSpriteIndex == kItemDiceQuarterHole) { + gnap.walkTo(_vm->_hotspotsWalkPos[kHS42BBQVendor], 0, 0x107BB, 1); + gnap._actionStatus = kAS42UseQuarterWithBBQVendor; + if (plat._pos.y < 9) + plat.walkTo(Common::Point(plat._pos.x, 9), -1, -1, 1); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS42BBQVendor], _vm->_hotspotsWalkPos[kHS42BBQVendor].x + 1, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(_vm->_hotspotsWalkPos[kHS42BBQVendor].x - 1, 0)); + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS42BBQVendor], 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = kAS42TalkBBQVendor; + break; + case GRAB_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + break; + + case kHS42ChickenLeg: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS42ChickenLeg], _vm->_hotspotsWalkPos[kHS42ChickenLeg].x - 1, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(_vm->_hotspotsWalkPos[kHS42ChickenLeg].x - 1, 0)); + break; + case GRAB_CURSOR: + gnap.walkTo(_vm->_hotspotsWalkPos[kHS42ChickenLeg], 0, 0x107BC, 1); + gnap._actionStatus = kAS42GrabChickenLeg; + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + break; + + case kHS42WalkArea1: + case kHS42WalkArea2: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + } + } + + if (_vm->_mouseClickState._left && gnap._actionStatus < 0) { + _vm->_mouseClickState._left = false; + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + _vm->_toyUfoActionStatus = kAS42ToyUfoRefresh; + _vm->toyUfoFlyTo(-1, -1, 0, 799, 0, 300, 3); + } else { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + } + } + + updateAnimations(); + + _vm->toyUfoCheckTimer(); + + if (!_vm->_isLeavingScene) { + if (plat._actionStatus < 0 && !_vm->isFlag(kGFGnapControlsToyUFO)) + plat.updateIdleSequence(); + if (gnap._actionStatus < 0 && !_vm->isFlag(kGFGnapControlsToyUFO)) + gnap.updateIdleSequence(); + if (!_vm->_timers[4]) { + _vm->_timers[4] = _vm->getRandom(20) + 30; + if (gnap._actionStatus < 0 && plat._actionStatus < 0 && _nextBBQVendorSequenceId == -1) { + switch (_vm->getRandom(8)) { + case 0: + _nextBBQVendorSequenceId = 0x14C; + break; + case 1: + case 2: + _nextBBQVendorSequenceId = 0x149; + break; + case 3: + case 4: + case 5: + case 6: + _nextBBQVendorSequenceId = 0x14D; + break; + case 7: + _nextBBQVendorSequenceId = 0x14A; + break; + } + if (_nextBBQVendorSequenceId == _currBBQVendorSequenceId && _nextBBQVendorSequenceId != 0x14D) + _nextBBQVendorSequenceId = -1; + } + } + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + _vm->_timers[4] = _vm->getRandom(20) + 30; + } + + _vm->gameUpdateTick(); + } +} + +void Scene42::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + switch (gnap._actionStatus) { + case kAS42LeaveScene: + gameSys.setAnimation(0, 0, 0); + gnap._actionStatus = -1; + _vm->_sceneDone = true; + break; + case kAS42TalkBBQVendor: + gameSys.setAnimation(0, 0, 0); + gnap._actionStatus = -1; + _nextBBQVendorSequenceId = 0x14B; + break; + case kAS42UseQuarterWithBBQVendor: + case kAS42GrabChickenLeg: + if (gameSys.getAnimationStatus(2) == 2) { + int sequenceId; + if (gnap._actionStatus == kAS42UseQuarterWithBBQVendor) { + _vm->invRemove(kItemDiceQuarterHole); + _vm->invAdd(kItemChickenBucket); + _vm->setGrabCursorSprite(-1); + sequenceId = 0x150; + _nextBBQVendorSequenceId = 0x148; + } else if (_vm->isFlag(kGFUnk27)) { + if (_vm->isFlag(kGFUnk28)) { + sequenceId = 0x7B7; + _nextBBQVendorSequenceId = 0x145; + } else { + _vm->setFlag(kGFUnk28); + sequenceId = 0x14F; + _nextBBQVendorSequenceId = 0x147; + } + } else { + _vm->setFlag(kGFUnk27); + sequenceId = 0x14E; + _nextBBQVendorSequenceId = 0x146; + } + if (sequenceId == 0x7B7) { + gameSys.insertSequence(0x107B7, gnap._id, + makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, + kSeqSyncWait, _vm->getSequenceTotalDuration(_nextBBQVendorSequenceId), + 75 * gnap._pos.x - gnap._gridX, 48 * gnap._pos.y - gnap._gridY); + gnap._sequenceDatNum = 1; + } else { + gameSys.insertSequence(sequenceId, gnap._id, + makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, + kSeqSyncWait, 0, 0, 0); + gnap._sequenceDatNum = 0; + } + gnap._sequenceId = sequenceId; + gameSys.setAnimation(sequenceId | (gnap._sequenceDatNum << 16), gnap._id, 0); + if (gnap._actionStatus == kAS42UseQuarterWithBBQVendor) + gnap._actionStatus = kAS42UseQuarterWithBBQVendorDone; + else + gnap._actionStatus = -1; + gameSys.insertSequence(_nextBBQVendorSequenceId, 1, _currBBQVendorSequenceId, 1, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextBBQVendorSequenceId, 1, 2); + _currBBQVendorSequenceId = _nextBBQVendorSequenceId; + if (_nextBBQVendorSequenceId == 0x145) + _nextBBQVendorSequenceId = 0x14A; + else + _nextBBQVendorSequenceId = -1; + _vm->_timers[4] = _vm->getRandom(20) + 30; + _vm->_timers[2] = _vm->getRandom(30) + 20; + _vm->_timers[3] = _vm->getRandom(50) + 200; + } + break; + case kAS42UseQuarterWithBBQVendorDone: + gameSys.setAnimation(0, 0, 0); + _vm->setGrabCursorSprite(kItemChickenBucket); + gnap._actionStatus = -1; + break; + default: + gameSys.setAnimation(0, 0, 0); + gnap._actionStatus = -1; + break; + } + } + + if (gameSys.getAnimationStatus(2) == 2 && _nextBBQVendorSequenceId != -1) { + gameSys.insertSequence(_nextBBQVendorSequenceId, 1, _currBBQVendorSequenceId, 1, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextBBQVendorSequenceId, 1, 2); + _currBBQVendorSequenceId = _nextBBQVendorSequenceId; + _nextBBQVendorSequenceId = -1; + _vm->_timers[4] = _vm->getRandom(20) + 30; + } + + if (gameSys.getAnimationStatus(3) == 2) { + switch (_vm->_toyUfoActionStatus) { + case kAS42ToyUfoLeaveScene: + _vm->_sceneDone = true; + break; + case kAS42ToyUfoPickUpHotSauce: + gameSys.insertSequence(0x10870, _vm->_toyUfoId, _vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, kSeqSyncWait, 0, 0, 0); + _vm->setFlag(kGFUnk24); + updateHotspots(); + _vm->toyUfoSetStatus(kGFGroceryStoreHatTaken); + _vm->_toyUfoSequenceId = 0x870; + gameSys.setAnimation(0x10870, _vm->_toyUfoId, 3); + _vm->_toyUfoActionStatus = -1; + _vm->_toyUfoX = 0x181; + _vm->_toyUfoY = 53; + break; + default: + if (_vm->_toyUfoSequenceId == 0x872) { + _vm->hideCursor(); + _vm->addFullScreenSprite(0x13E, 255); + gameSys.setAnimation(0x151, 256, 0); + gameSys.insertSequence(0x151, 256, 0, 0, kSeqNone, 0, 0, 0); + while (gameSys.getAnimationStatus(0) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + _vm->removeFullScreenSprite(); + _vm->showCursor(); + } + _vm->_toyUfoNextSequenceId = _vm->toyUfoGetSequenceId(); + gameSys.setAnimation(_vm->_toyUfoNextSequenceId | 0x10000, (_vm->_toyUfoId + 1) % 10, 3); + gameSys.insertSequence(_vm->_toyUfoNextSequenceId | 0x10000, (_vm->_toyUfoId + 1) % 10, + _vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, + kSeqSyncWait, 0, _vm->_toyUfoX - 274, _vm->_toyUfoY - 128); + _vm->_toyUfoSequenceId = _vm->_toyUfoNextSequenceId; + _vm->_toyUfoId = (_vm->_toyUfoId + 1) % 10; + break; + } + _vm->_toyUfoActionStatus = -1; + } +} + +/*****************************************************************************/ + +Scene43::Scene43(GnapEngine *vm) : Scene(vm) { + _currTwoHeadedGuySequenceId = -1; + _nextTwoHeadedGuySequenceId = -1; +} + +int Scene43::init() { + GameSys& gameSys = *_vm->_gameSys; + + gameSys.setAnimation(0, 0, 0); + gameSys.setAnimation(0, 0, 1); + gameSys.setAnimation(0, 0, 2); + return 0x13F; +} + +void Scene43::updateHotspots() { + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + _vm->setHotspot(kHS43Platypus, 0, 0, 0, 0, SF_DISABLED); + _vm->setHotspot(kHS43UfoExitLeft, 0, 0, 10, 599, SF_EXIT_L_CURSOR); + _vm->setHotspot(kHS43UfoExitRight, 790, 0, 799, 599, SF_EXIT_R_CURSOR); + _vm->setHotspot(kHS43UfoKey, 140, 170, 185, 260, SF_GRAB_CURSOR); + _vm->setHotspot(kHS43UfoBucket, 475, 290, 545, 365, SF_DISABLED); + _vm->setDeviceHotspot(kHS43UfoDevice, -1, 534, -1, 599); + if (_vm->isFlag(kGFGroceryStoreHatTaken)) + _vm->_hotspots[kHS43UfoBucket]._flags = SF_GRAB_CURSOR; + // NOTE Bug in the original. Key hotspot wasn't disabled. + if (_vm->isFlag(kGFUnk14)) + _vm->_hotspots[kHS43UfoKey]._flags = SF_DISABLED; + _vm->_hotspotsCount = 6; + } else { + _vm->setHotspot(kHS43Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS43ExitUfoParty, 150, 580, 650, 600, SF_EXIT_D_CURSOR | SF_WALKABLE, 5, 9); + _vm->setHotspot(kHS43ExitBBQ, 0, 100, 10, 599, SF_EXIT_L_CURSOR, 0, 8); + _vm->setHotspot(kHS43ExitKissinBooth, 790, 100, 799, 599, SF_EXIT_R_CURSOR, 10, 8); + _vm->setHotspot(kHS43TwoHeadedGuy, 470, 240, 700, 470, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS43Key, 140, 170, 185, 260, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS43Ufo, 110, 0, 690, 350, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS43WalkArea1, 0, 0, 800, 445); + _vm->setHotspot(kHS43WalkArea2, 465, 0, 800, 493); + _vm->setDeviceHotspot(kHS43Device, -1, -1, -1, -1); + if (_vm->isFlag(kGFUnk14)) + _vm->_hotspots[kHS43Key]._flags = SF_DISABLED; + _vm->_hotspotsCount = 10; + } +} + +void Scene43::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->queueInsertDeviceIcon(); + + if (!_vm->isFlag(kGFUnk14)) + gameSys.insertSequence(0x1086F, 1, 0, 0, kSeqNone, 0, 0, 0); + + _currTwoHeadedGuySequenceId = 0x13C; + _nextTwoHeadedGuySequenceId = -1; + + gameSys.setAnimation(0x13C, 1, 2); + gameSys.insertSequence(_currTwoHeadedGuySequenceId, 1, 0, 0, kSeqNone, 0, 0, 0); + + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + _vm->_toyUfoId = 0; + _vm->_toyUfoActionStatus = -1; + _vm->_toyUfoSequenceId = _vm->toyUfoGetSequenceId(); + _vm->_toyUfoNextSequenceId = _vm->_toyUfoSequenceId; + if (_vm->_prevSceneNum == 42) + _vm->_toyUfoX = 30; + else + _vm->_toyUfoX = 770; + gameSys.setAnimation(_vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, 3); + gameSys.insertSequence(_vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, 0, 0, kSeqNone, 0, _vm->_toyUfoX - 274, _vm->_toyUfoY - 128); + _vm->endSceneInit(); + } else { + switch (_vm->_prevSceneNum) { + case 42: + gnap.initPos(-1, 8, kDirUpRight); + plat.initPos(-1, 9, kDirUpLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(2, 8), -1, 0x107B9, 1); + plat.walkTo(Common::Point(1, 8), -1, 0x107C2, 1); + break; + case 44: + gnap.initPos(11, 8, kDirUpRight); + plat.initPos(11, 9, kDirUpLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(8, 8), -1, 0x107BA, 1); + plat.walkTo(Common::Point(9, 8), -1, 0x107D2, 1); + break; + case 54: + gnap.initPos(4, 7, kDirBottomLeft); + plat.initPos(11, 8, kDirUpLeft); + _vm->endSceneInit(); + plat.walkTo(Common::Point(9, 8), -1, 0x107D2, 1); + break; + default: + gnap.initPos(5, 11, kDirUpRight); + plat.initPos(6, 11, kDirUpLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(5, 8), -1, 0x107BA, 1); + plat.walkTo(Common::Point(6, 8), -1, 0x107C2, 1); + break; + } + } + + while (!_vm->_sceneDone) { + if (!_vm->isSoundPlaying(0x1094B)) + _vm->playSound(0x1094B, true); + + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->testWalk(0, 0, -1, -1, -1, -1); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + switch (_vm->_sceneClickedHotspot) { + case kHS43UfoDevice: + _vm->runMenu(); + updateHotspots(); + _vm->_timers[4] = _vm->getRandom(100) + 100; + break; + + case kHS43UfoExitLeft: + if (_vm->_toyUfoActionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_toyUfoActionStatus = 4; + _vm->_newSceneNum = 42; + _vm->toyUfoFlyTo(-35, -1, -35, 799, 0, 300, 3); + } + break; + + case kHS43UfoExitRight: + if (_vm->_toyUfoActionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_toyUfoActionStatus = 4; + _vm->_newSceneNum = 44; + _vm->toyUfoFlyTo(835, -1, 0, 835, 0, 300, 3); + } + break; + + case kHS43UfoKey: + if (_vm->isFlag(kGFJointTaken)) { + _vm->_toyUfoActionStatus = 6; + _vm->toyUfoFlyTo(163, 145, 0, 799, 0, 300, 3); + } else { + _vm->_toyUfoActionStatus = 5; + _vm->toyUfoFlyTo(-1, -1, 0, 799, 0, 300, 3); + } + break; + + case kHS43UfoBucket: + _vm->_toyUfoActionStatus = 7; + _vm->toyUfoFlyTo(497, 143, 0, 799, 0, 300, 3); + _vm->_timers[9] = 600; + break; + } + } else { + switch (_vm->_sceneClickedHotspot) { + case kHS43Device: + _vm->runMenu(); + updateHotspots(); + _vm->_timers[4] = _vm->getRandom(100) + 100; + break; + + case kHS43Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(plat._pos); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(plat._pos); + break; + } + } + } + break; + + case kHS43ExitUfoParty: + _vm->_isLeavingScene = true; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS43ExitUfoParty], 0, 0x107AE, 1); + gnap._actionStatus = 0; + plat.walkTo(_vm->_hotspotsWalkPos[kHS43ExitUfoParty], -1, 0x107C7, 1); + _vm->_newSceneNum = 40; + break; + + case kHS43ExitBBQ: + _vm->_isLeavingScene = true; + gnap.walkTo(Common::Point(_vm->_hotspotsWalkPos[kHS43ExitBBQ].x, gnap._pos.y), 0, 0x107AF, 1); + gnap._actionStatus = 0; + plat.walkTo(_vm->_hotspotsWalkPos[kHS43ExitBBQ], -1, 0x107CF, 1); + _vm->_newSceneNum = 42; + break; + + case kHS43ExitKissinBooth: + _vm->_isLeavingScene = true; + gnap.walkTo(Common::Point(_vm->_hotspotsWalkPos[kHS43ExitKissinBooth].x, gnap._pos.y), 0, 0x107AB, 1); + gnap._actionStatus = 0; + plat.walkTo(_vm->_hotspotsWalkPos[kHS43ExitKissinBooth], -1, 0x107CD, 1); + _vm->_newSceneNum = 44; + break; + + case kHS43TwoHeadedGuy: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(6, 8), 7, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(7, 0)); + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.walkTo(Common::Point(5, 8), 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = 2; + break; + case GRAB_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + break; + + case kHS43Key: + case kHS43Ufo: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(Common::Point(3, 7), 2, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(); + break; + case GRAB_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.walkTo(Common::Point(3, 7), 0, 67515, 1); + gnap._actionStatus = 1; + break; + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + break; + + case kHS43WalkArea1: + case kHS43WalkArea2: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + } + } + + if (_vm->_mouseClickState._left && gnap._actionStatus < 0) { + _vm->_mouseClickState._left = false; + if (_vm->isFlag(kGFGnapControlsToyUFO) && (_vm->_toyUfoActionStatus == 5 || _vm->_toyUfoActionStatus == -1)) { + _vm->_toyUfoActionStatus = 5; + _vm->toyUfoFlyTo(-1, -1, 0, 799, 0, 300, 3); + } else { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + } + } + + updateAnimations(); + + _vm->toyUfoCheckTimer(); + + if (!_vm->_isLeavingScene) { + if (plat._actionStatus < 0 && !_vm->isFlag(kGFGnapControlsToyUFO)) + plat.updateIdleSequence(); + if (gnap._actionStatus < 0 && !_vm->isFlag(kGFGnapControlsToyUFO)) + gnap.updateIdleSequence(); + if (!_vm->_timers[4] && (!_vm->isFlag(kGFGnapControlsToyUFO) || !_vm->isFlag(kGFGroceryStoreHatTaken))) { + _vm->_timers[4] = _vm->getRandom(100) + 100; + if (gnap._actionStatus < 0 && plat._actionStatus < 0 && _nextTwoHeadedGuySequenceId == -1) { + switch (_vm->getRandom(5)) { + case 0: + _nextTwoHeadedGuySequenceId = 0x13C; + break; + case 1: + _nextTwoHeadedGuySequenceId = 0x134; + break; + case 2: + _nextTwoHeadedGuySequenceId = 0x135; + break; + case 3: + _nextTwoHeadedGuySequenceId = 0x136; + break; + case 4: + _nextTwoHeadedGuySequenceId = 0x13A; + break; + } + if (_nextTwoHeadedGuySequenceId == _currTwoHeadedGuySequenceId) + _nextTwoHeadedGuySequenceId = -1; + } + } + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + _vm->_timers[4] = _vm->getRandom(100) + 100; + } + + _vm->gameUpdateTick(); + } + + if (_vm->_newSceneNum == 54) + _vm->clearFlag(kGFGnapControlsToyUFO); +} + +void Scene43::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + switch (gnap._actionStatus) { + case 0: + gameSys.setAnimation(0, 0, 0); + _vm->_sceneDone = true; + break; + + case 1: + if (gameSys.getAnimationStatus(2) == 2) { + _vm->_timers[2] = _vm->getRandom(30) + 20; + _vm->_timers[3] = _vm->getRandom(50) + 200; + gameSys.insertSequence(0x13D, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x13D; + gnap._sequenceDatNum = 0; + gameSys.setAnimation(0x13D, gnap._id, 0); + _nextTwoHeadedGuySequenceId = 0x13B; + gameSys.insertSequence(0x13B, 1, _currTwoHeadedGuySequenceId, 1, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextTwoHeadedGuySequenceId, 1, 2); + _currTwoHeadedGuySequenceId = _nextTwoHeadedGuySequenceId; + _nextTwoHeadedGuySequenceId = -1; + _vm->_timers[4] = _vm->getRandom(100) + 100; + gnap._actionStatus = -1; + } + break; + + default: + gameSys.setAnimation(0, 0, 0); + gnap._actionStatus = -1; + break; + } + } + + if (gameSys.getAnimationStatus(2) == 2) { + if (_currTwoHeadedGuySequenceId == 0x13A) { + if (_vm->isFlag(kGFGroceryStoreHatTaken)) { + _nextTwoHeadedGuySequenceId = 0x13E; + _vm->stopSound(0x108F6); + } else if (_vm->getRandom(2) != 0) { + _nextTwoHeadedGuySequenceId = 0x137; + } else { + _nextTwoHeadedGuySequenceId = 0x138; + } + } else if (_currTwoHeadedGuySequenceId == 0x13E) { + _vm->_sceneDone = true; + _vm->_newSceneNum = 54; + } + if (_nextTwoHeadedGuySequenceId != -1) { + gameSys.insertSequence(_nextTwoHeadedGuySequenceId, 1, _currTwoHeadedGuySequenceId, 1, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextTwoHeadedGuySequenceId, 1, 2); + _currTwoHeadedGuySequenceId = _nextTwoHeadedGuySequenceId; + _nextTwoHeadedGuySequenceId = -1; + _vm->_timers[4] = _vm->getRandom(100) + 100; + } + } + + if (gameSys.getAnimationStatus(3) == 2) { + switch (_vm->_toyUfoActionStatus) { + case 4: + _vm->_sceneDone = true; + _vm->_toyUfoActionStatus = -1; + break; + case 6: + gameSys.insertSequence(0x10871, _vm->_toyUfoId, _vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, kSeqSyncWait, 0, 0, 0); + gameSys.removeSequence(0x1086F, 1, true); + _vm->setFlag(kGFUnk14); + updateHotspots(); + _vm->toyUfoSetStatus(kGFUnk18); + _vm->_toyUfoSequenceId = 0x871; + gameSys.setAnimation(0x10871, _vm->_toyUfoId, 3); + _vm->_toyUfoActionStatus = -1; + _vm->_toyUfoX = 96; + _vm->_toyUfoY = 131; + break; + case 7: + gameSys.insertSequence(0x10874, _vm->_toyUfoId, _vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, kSeqSyncWait, 0, 0, 0); + _vm->_toyUfoSequenceId = 0x874; + gameSys.setAnimation(0x10874, _vm->_toyUfoId, 3); + _vm->_toyUfoActionStatus = 8; + _vm->setFlag(kGFJointTaken); + gnap._actionStatus = 3; + break; + case 8: + _nextTwoHeadedGuySequenceId = 0x13A; + _vm->_toyUfoX = 514; + _vm->_toyUfoY = 125; + _vm->toyUfoFlyTo(835, 125, 0, 835, 0, 300, 3); + _vm->_toyUfoActionStatus = 9; + break; + case 9: + // Nothing + break; + default: + _vm->_toyUfoNextSequenceId = _vm->toyUfoGetSequenceId(); + gameSys.insertSequence(_vm->_toyUfoNextSequenceId | 0x10000, _vm->_toyUfoId + 1, + _vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, + kSeqSyncWait, 0, _vm->_toyUfoX - 274, _vm->_toyUfoY - 128); + _vm->_toyUfoSequenceId = _vm->_toyUfoNextSequenceId; + ++_vm->_toyUfoId; + gameSys.setAnimation(_vm->_toyUfoNextSequenceId | 0x10000, _vm->_toyUfoId, 3); + _vm->_toyUfoActionStatus = -1; + break; + } + } +} + +/*****************************************************************************/ + +Scene44::Scene44(GnapEngine *vm) : Scene(vm) { + _nextSpringGuySequenceId = -1; + _nextKissingLadySequenceId = -1; + _currSpringGuySequenceId = -1; + _currKissingLadySequenceId = -1; +} + +int Scene44::init() { + GameSys& gameSys = *_vm->_gameSys; + + gameSys.setAnimation(0, 0, 0); + gameSys.setAnimation(0, 0, 1); + gameSys.setAnimation(0, 0, 2); + gameSys.setAnimation(0, 0, 3); + return 0xFF; +} + +void Scene44::updateHotspots() { + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + _vm->setHotspot(kHS44Platypus, 0, 0, 0, 0, SF_DISABLED); + _vm->setHotspot(kHS44UfoExitLeft, 0, 0, 10, 599, SF_EXIT_L_CURSOR); + _vm->setHotspot(kHS44UfoExitRight, 790, 0, 799, 599, SF_EXIT_R_CURSOR); + _vm->setDeviceHotspot(kHS44UfoDevice, -1, 534, -1, 599); + _vm->_hotspotsCount = 4; + } else { + _vm->setHotspot(kHS44Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS44ExitUfoParty, 150, 580, 650, 600, SF_EXIT_D_CURSOR | SF_WALKABLE, 5, 9); + _vm->setHotspot(kHS44ExitUfo, 0, 100, 10, 599, SF_EXIT_L_CURSOR, 0, 8); + _vm->setHotspot(kHS44ExitShow, 790, 100, 799, 599, SF_EXIT_R_CURSOR, 10, 8); + _vm->setHotspot(kHS44KissingLady, 300, 160, 400, 315, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 5, 7); + _vm->setHotspot(kHS44Spring, 580, 310, 635, 375, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 7, 8); + _vm->setHotspot(kHS44SpringGuy, 610, 375, 690, 515, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 7, 8); + _vm->setHotspot(kHS44WalkArea1, 0, 0, 800, 445); + _vm->setHotspot(kHS44WalkArea2, 617, 0, 800, 600); + _vm->setDeviceHotspot(kHS44Device, -1, -1, -1, -1); + if (_vm->isFlag(kGFUnk13)) + _vm->_hotspots[kHS44KissingLady]._flags = SF_DISABLED; + if (_vm->isFlag(kGFSpringTaken)) + _vm->_hotspots[kHS44Spring]._flags = SF_DISABLED; + _vm->_hotspotsCount = 10; + } +} + +void Scene44::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->queueInsertDeviceIcon(); + + gameSys.insertSequence(0xF7, 0, 0, 0, kSeqLoop, 0, 0, 0); + gameSys.insertSequence(0xFC, 256, 0, 0, kSeqNone, 0, 0, 0); + + if (_vm->isFlag(kGFSpringTaken)) + _currSpringGuySequenceId = 0xF8; + else + _currSpringGuySequenceId = 0xF9; + + _nextSpringGuySequenceId = -1; + gameSys.setAnimation(_currSpringGuySequenceId, 1, 4); + gameSys.insertSequence(_currSpringGuySequenceId, 1, 0, 0, kSeqNone, 0, 0, 0); + + if (_vm->isFlag(kGFUnk13)) { + if (_vm->_prevSceneNum != 50 || _vm->_sceneSavegameLoaded) { + _currKissingLadySequenceId = 0xF6; + _nextKissingLadySequenceId = -1; + } else { + _vm->setGrabCursorSprite(kItemGum); + _currKissingLadySequenceId = 0xF5; + _nextKissingLadySequenceId = 0xF6; + gameSys.setAnimation(0xF5, 1, 2); + } + } else { + _currKissingLadySequenceId = 0xEC; + _nextKissingLadySequenceId = -1; + gameSys.setAnimation(0xEC, 1, 2); + } + + gameSys.insertSequence(_currKissingLadySequenceId, 1, 0, 0, kSeqNone, 0, 0, 0); + + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + _vm->_toyUfoId = 0; + _vm->_toyUfoActionStatus = -1; + _vm->_toyUfoSequenceId = _vm->toyUfoGetSequenceId(); + _vm->_toyUfoNextSequenceId = _vm->_toyUfoSequenceId; + if (_vm->_prevSceneNum == 43) + _vm->_toyUfoX = 30; + else + _vm->_toyUfoX = 770; + gameSys.setAnimation(_vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, 3); + gameSys.insertSequence(_vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, 0, 0, kSeqNone, 0, _vm->_toyUfoX - 274, _vm->_toyUfoY - 128); + _vm->endSceneInit(); + } else { + switch (_vm->_prevSceneNum) { + case 43: + gnap.initPos(-1, 8, kDirUpRight); + plat.initPos(-1, 7, kDirUpLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(2, 8), -1, 0x107B9, 1); + plat.walkTo(Common::Point(1, 8), -1, 0x107C2, 1); + break; + case 46: + gnap.initPos(11, 8, kDirUpRight); + plat.initPos(11, 8, kDirUpLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(6, 8), -1, 0x107BA, 1); + plat.walkTo(Common::Point(7, 8), -1, 0x107D2, 1); + break; + case 50: + gnap.initPos(4, 8, kDirBottomRight); + if (_vm->_sceneSavegameLoaded) { + plat.initPos(_vm->_hotspotsWalkPos[4].x, _vm->_hotspotsWalkPos[4].y, kDirIdleRight); + } else if (!_vm->isFlag(kGFUnk13)) { + _vm->_timers[0] = 50; + _vm->_timers[1] = 20; + plat._pos = Common::Point(5, 8); + plat._sequenceId = 0xFD; + plat._idleFacing = kDirIdleLeft; + plat._id = 160; + plat._sequenceDatNum = 0; + gameSys.insertSequence(0xFD, 160, 0, 0, kSeqNone, 0, 0, 0); + } + _vm->endSceneInit(); + break; + default: + gnap.initPos(5, 11, kDirUpRight); + plat.initPos(6, 11, kDirUpLeft); + _vm->endSceneInit(); + plat.walkTo(Common::Point(6, 8), -1, 0x107C2, 1); + gnap.walkTo(Common::Point(5, 8), -1, 0x107BA, 1); + break; + } + } + + while (!_vm->_sceneDone) { + if (!_vm->isSoundPlaying(0x1094B)) + _vm->playSound(0x1094B, true); + + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->testWalk(0, 0, -1, -1, -1, -1); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + switch (_vm->_sceneClickedHotspot) { + case kHS44UfoExitLeft: + if (_vm->_toyUfoActionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_toyUfoActionStatus = 6; + _vm->_newSceneNum = 43; + _vm->toyUfoFlyTo(-35, -1, -35, 799, 0, 300, 3); + } + break; + + case kHS44UfoExitRight: + if (_vm->_toyUfoActionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_toyUfoActionStatus = 6; + _vm->_newSceneNum = 46; + _vm->toyUfoFlyTo(835, -1, 0, 835, 0, 300, 3); + } + break; + + case kHS44UfoDevice: + _vm->runMenu(); + updateHotspots(); + _vm->_timers[4] = _vm->getRandom(20) + 20; + break; + } + } else if (_vm->_sceneClickedHotspot <= 9) { + switch (_vm->_sceneClickedHotspot) { + case kHS44Device: + _vm->runMenu(); + updateHotspots(); + _vm->_timers[4] = _vm->getRandom(20) + 20; + break; + + case kHS44Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(plat._pos); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(plat._pos); + break; + } + } + } + break; + + case kHS44ExitUfoParty: + _vm->_isLeavingScene = true; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS44ExitUfoParty], 0, 0x107AE, 1); + gnap._actionStatus = 0; + _vm->_newSceneNum = 40; + break; + + case kHS44ExitUfo: + _vm->_isLeavingScene = true; + gnap.walkTo(Common::Point(_vm->_hotspotsWalkPos[kHS44ExitUfo].x, gnap._pos.y), 0, 0x107AF, 1); + gnap._actionStatus = 0; + plat.walkTo(Common::Point(_vm->_hotspotsWalkPos[kHS44ExitUfo].x, plat._pos.y), -1, 0x107CF, 1); + _vm->_newSceneNum = 43; + break; + + case kHS44ExitShow: + _vm->_isLeavingScene = true; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS44ExitShow], 0, 0x107AB, 1); + gnap._actionStatus = 0; + _vm->_newSceneNum = 46; + break; + + case kHS44KissingLady: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap._actionStatus = 2; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS44KissingLady], 0, -1, 9); + gnap.playShowItem(_vm->_grabCursorSpriteIndex, _vm->_hotspotsWalkPos[kHS44KissingLady].x - 1, _vm->_hotspotsWalkPos[kHS44KissingLady].y); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(4, 3)); + break; + case GRAB_CURSOR: + gnap.playImpossible(); + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpLeft; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS44KissingLady], 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = 1; + break; + case PLAT_CURSOR: + gnap.useDeviceOnPlatypus(); + plat.walkTo(Common::Point(6, 7), 1, 0x107D2, 1); + if (gnap._pos == Common::Point(7, 7)) + gnap.walkStep(); + gnap.playIdle(Common::Point(5, 7)); + plat._actionStatus = 4; + break; + } + } + break; + + case kHS44Spring: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS44Spring], 8, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(8, 7)); + break; + case GRAB_CURSOR: + gnap.playPullOutDevice(Common::Point(8, 0)); + gnap.playUseDevice(Common::Point(8, 0)); + _nextSpringGuySequenceId = 0xFB; + _vm->invAdd(kItemSpring); + _vm->setFlag(kGFSpringTaken); + updateHotspots(); + break; + case TALK_CURSOR: + gnap.playImpossible(); + break; + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + break; + + case kHS44SpringGuy: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS44SpringGuy], 8, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + if (_vm->isFlag(kGFSpringTaken)) + gnap.playMoan1(Common::Point(8, 7)); + else + gnap.playScratchingHead(Common::Point(8, 7)); + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS44SpringGuy], -1, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + break; + case GRAB_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + break; + + case kHS44WalkArea1: + case kHS44WalkArea2: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + + } + } + + if (_vm->_mouseClickState._left && gnap._actionStatus < 0) { + _vm->_mouseClickState._left = false; + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + _vm->_toyUfoActionStatus = 7; + _vm->toyUfoFlyTo(-1, -1, 0, 799, 0, 300, 3); + } else { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + } + } + + updateAnimations(); + _vm->toyUfoCheckTimer(); + + if (!_vm->_isLeavingScene) { + if (plat._actionStatus < 0 && !_vm->isFlag(kGFGnapControlsToyUFO) && _currKissingLadySequenceId != 0xF5) + plat.updateIdleSequence(); + if (gnap._actionStatus < 0 && !_vm->isFlag(kGFGnapControlsToyUFO)) + gnap.updateIdleSequence(); + if (!_vm->_timers[4]) { + _vm->_timers[4] = _vm->getRandom(20) + 20; + if (gnap._actionStatus < 0 && plat._actionStatus < 0 && _nextKissingLadySequenceId == -1) { + switch (_vm->getRandom(20)) { + case 0: + _nextKissingLadySequenceId = 0xED; + break; + case 1: + _nextKissingLadySequenceId = 0xEE; + break; + case 2: + _nextKissingLadySequenceId = 0xF0; + break; + case 3: + _nextKissingLadySequenceId = 0xF3; + break; + case 4: + _nextKissingLadySequenceId = 0xF4; + break; + default: + _nextKissingLadySequenceId = 0xEC; + break; + } + if (_nextKissingLadySequenceId != 0xEC && _nextKissingLadySequenceId == _currKissingLadySequenceId) + _nextKissingLadySequenceId = -1; + } + } + if (!_vm->_timers[5]) { + _vm->_timers[5] = _vm->getRandom(20) + 20; + if (gnap._actionStatus < 0 && plat._actionStatus < 0 && _nextSpringGuySequenceId == -1) { + if (_vm->getRandom(5) != 0) { + if (!_vm->isFlag(kGFSpringTaken)) + _nextSpringGuySequenceId = 0xF9; + } else { + if (_vm->isFlag(kGFSpringTaken)) + _nextSpringGuySequenceId = 0xF8; + else + _nextSpringGuySequenceId = 0xFA; + } + } + } + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + _vm->_timers[4] = _vm->getRandom(20) + 20; + } + + _vm->gameUpdateTick(); + } +} + +void Scene44::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case 0: + _vm->_sceneDone = true; + break; + case 1: + _nextKissingLadySequenceId = 0xEF; + break; + case 2: + _nextKissingLadySequenceId = 0xF2; + break; + } + gnap._actionStatus = -1; + } + + if (gameSys.getAnimationStatus(1) == 2) { + gameSys.setAnimation(0, 0, 1); + switch (plat._actionStatus) { + case 4: + if (gameSys.getAnimationStatus(2) == 2) { + gameSys.insertSequence(0xFE, plat._id, plat._sequenceId | (plat._sequenceDatNum << 16), plat._id, kSeqSyncWait, 0, 0, 0); + plat._sequenceId = 0xFE; + plat._sequenceDatNum = 0; + gameSys.setAnimation(0xFE, plat._id, 1); + gameSys.removeSequence(_currKissingLadySequenceId, 1, true); + plat._actionStatus = 5; + } + break; + case 5: + _vm->_sceneDone = true; + _vm->_newSceneNum = 50; + break; + default: + plat._actionStatus = -1; + break; + } + } + + if (gameSys.getAnimationStatus(2) == 2) { + if (_nextKissingLadySequenceId == 0xF6) { + gameSys.insertSequence(_nextKissingLadySequenceId, 1, _currKissingLadySequenceId, 1, kSeqSyncWait, 0, 0, 0); + plat.initPos(5, 8, kDirIdleLeft); + _currKissingLadySequenceId = _nextKissingLadySequenceId; + _nextKissingLadySequenceId = -1; + gameSys.setAnimation(0, 0, 2); + } else if (_nextKissingLadySequenceId != -1) { + gameSys.insertSequence(_nextKissingLadySequenceId, 1, _currKissingLadySequenceId, 1, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextKissingLadySequenceId, 1, 2); + _currKissingLadySequenceId = _nextKissingLadySequenceId; + _nextKissingLadySequenceId = -1; + _vm->_timers[4] = _vm->getRandom(20) + 20; + } + } + + if (gameSys.getAnimationStatus(4) == 2) { + if (_currSpringGuySequenceId == 0xFB) { + _vm->setGrabCursorSprite(kItemSpring); + _nextSpringGuySequenceId = 0xF8; + } + if (_nextSpringGuySequenceId != -1) { + gameSys.insertSequence(_nextSpringGuySequenceId, 1, _currSpringGuySequenceId, 1, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextSpringGuySequenceId, 1, 4); + _currSpringGuySequenceId = _nextSpringGuySequenceId; + _nextSpringGuySequenceId = -1; + _vm->_timers[5] = _vm->getRandom(20) + 20; + } + } + + if (gameSys.getAnimationStatus(3) == 2) { + switch (_vm->_toyUfoActionStatus) { + case 6: + _vm->_sceneDone = true; + break; + default: + _vm->_toyUfoNextSequenceId = _vm->toyUfoGetSequenceId(); + gameSys.insertSequence(_vm->_toyUfoNextSequenceId | 0x10000, _vm->_toyUfoId + 1, + _vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, + kSeqSyncWait, 0, _vm->_toyUfoX - 274, _vm->_toyUfoY - 128); + _vm->_toyUfoSequenceId = _vm->_toyUfoNextSequenceId; + ++_vm->_toyUfoId; + gameSys.setAnimation(_vm->_toyUfoNextSequenceId | 0x10000, _vm->_toyUfoId, 3); + break; + } + _vm->_toyUfoActionStatus = -1; + } +} + +/*****************************************************************************/ + +Scene45::Scene45(GnapEngine *vm) : Scene(vm) { + _currDancerSequenceId = -1; +} + +int Scene45::init() { + GameSys& gameSys = *_vm->_gameSys; + + gameSys.setAnimation(0, 0, 0); + gameSys.setAnimation(0, 0, 1); + gameSys.setAnimation(0, 0, 2); + gameSys.setAnimation(0, 0, 3); + gameSys.setAnimation(0, 0, 4); + gameSys.setAnimation(0, 0, 5); + return _vm->isFlag(kGFUnk23) ? 0xA2 : 0xA1; +} + +void Scene45::updateHotspots() { + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + _vm->setHotspot(kHS45Platypus, 0, 0, 0, 0, SF_DISABLED); + _vm->setHotspot(kHS45UfoExitLeft, 0, 0, 10, 599, SF_EXIT_L_CURSOR); + _vm->setHotspot(kHS45UfoExitRight, 794, 0, 799, 599, SF_EXIT_R_CURSOR | SF_DISABLED); + _vm->setDeviceHotspot(kHS45UfoDevice, -1, 534, -1, 599); + _vm->_hotspotsCount = 4; + } else { + _vm->setHotspot(kHS45Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS45ExitUfoParty, 150, 580, 650, 600, SF_EXIT_D_CURSOR | SF_WALKABLE, 5, 9); + _vm->setHotspot(kHS45ExitShoe, 0, 100, 10, 599, SF_EXIT_L_CURSOR, 0, 8); + _vm->setHotspot(kHS45ExitRight, 794, 100, 799, 599, SF_EXIT_R_CURSOR | SF_DISABLED, 10, 8); + _vm->setHotspot(kHS45ExitDiscoBall, 200, 0, 600, 10, SF_DISABLED); + _vm->setHotspot(kHS45DiscoBall, 370, 10, 470, 125, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 6, 7); + _vm->setHotspot(kHS45WalkArea1, 0, 0, 800, 472); + _vm->setDeviceHotspot(kHS45Device, -1, -1, -1, -1); + if (_vm->isFlag(kGFUnk22)) { + _vm->_hotspots[kHS45Platypus]._flags = SF_DISABLED; + _vm->_hotspots[kHS45ExitUfoParty]._flags = SF_DISABLED; + _vm->_hotspots[kHS45ExitShoe]._flags = SF_DISABLED; + _vm->_hotspots[kHS45ExitRight]._flags = SF_DISABLED; + _vm->_hotspots[kHS45ExitDiscoBall]._flags = SF_EXIT_U_CURSOR; + } + if (_vm->isFlag(kGFUnk23) || _vm->isFlag(kGFUnk22)) + _vm->_hotspots[kHS45DiscoBall]._flags = SF_DISABLED; + _vm->_hotspotsCount = 8; + } +} + +void Scene45::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + if (!_vm->isSoundPlaying(0x1094A)) + _vm->playSound(0x1094A, true); + + _vm->queueInsertDeviceIcon(); + + gameSys.insertSequence(0x96, 1, 0, 0, kSeqNone, 0, 0, 0); + gameSys.setAnimation(0x96, 1, 3); + gameSys.insertSequence(0x99, 1, 0, 0, kSeqNone, 0, 0, 0); + gameSys.setAnimation(0x99, 1, 4); + _currDancerSequenceId = 0x8F; + gameSys.setAnimation(_currDancerSequenceId, 1, 2); + gameSys.insertSequence(_currDancerSequenceId, 1, 0, 0, kSeqNone, 0, 0, 0); + + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + _vm->_toyUfoId = 0; + _vm->_toyUfoActionStatus = -1; + _vm->_toyUfoSequenceId = _vm->toyUfoGetSequenceId(); + _vm->_toyUfoNextSequenceId = _vm->_toyUfoSequenceId; + if (_vm->_prevSceneNum == 46) + _vm->_toyUfoX = 30; + else + _vm->_toyUfoX = 770; + gameSys.setAnimation(_vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, 5); + gameSys.insertSequence(_vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, 0, 0, kSeqNone, 0, _vm->_toyUfoX - 274, _vm->_toyUfoY - 128); + _vm->endSceneInit(); + } else if (_vm->isFlag(kGFUnk22)) { + gnap._sequenceId = 0x9E; + gnap._sequenceDatNum = 0; + gnap._id = 1; + gameSys.setAnimation(0x9E, 1, 0); + gnap._actionStatus = 1; + gameSys.insertSequence(gnap._sequenceId, gnap._id, 0, 0, kSeqNone, 0, 0, 0); + plat.initPos(4, 8, kDirIdleLeft); + _vm->endSceneInit(); + } else if (_vm->_prevSceneNum == 46) { + gnap.initPos(-1, 8, kDirUpRight); + plat.initPos(-1, 9, kDirUpLeft); + _vm->endSceneInit(); + plat.walkTo(Common::Point(4, 8), -1, 0x107C2, 1); + gnap.walkTo(Common::Point(2, 7), -1, 0x107B9, 1); + } else if (_vm->_prevSceneNum == 41) { + gnap.initPos(11, 8, kDirUpRight); + plat.initPos(11, 9, kDirUpLeft); + _vm->endSceneInit(); + plat.walkTo(Common::Point(4, 8), -1, 0x107D2, 1); + gnap.walkTo(Common::Point(10, 9), -1, 0x107BA, 1); + } else { + gnap.initPos(2, 11, kDirUpRight); + plat.initPos(6, 11, kDirUpLeft); + _vm->endSceneInit(); + plat.walkTo(Common::Point(4, 8), -1, 0x107C2, 1); + gnap.walkTo(Common::Point(2, 7), -1, 0x107B9, 1); + } + + if (!_vm->isFlag(kGFUnk21) && !_vm->isFlag(kGFGnapControlsToyUFO)) { + _vm->setFlag(kGFUnk21); + _vm->setGrabCursorSprite(-1); + gameSys.setAnimation(0x9D, gnap._id, 0); + gameSys.insertSequence(0x9D, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + while (gameSys.getAnimationStatus(0) != 2 && !_vm->_gameDone) { + _vm->gameUpdateTick(); + if (gameSys.getAnimationStatus(2) == 2) { + gameSys.setAnimation(0, 0, 2); + int newSeqId = _vm->getRandom(7) + 0x8F; + gameSys.insertSequence(newSeqId, 1, _currDancerSequenceId, 1, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(newSeqId, 1, 2); + _currDancerSequenceId = newSeqId; + } + if (gameSys.getAnimationStatus(3) == 2 && gameSys.getAnimationStatus(4) == 2) { + gameSys.insertSequence(0x96, 1, 0x96, 1, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(0x96, 1, 3); + gameSys.insertSequence(0x99, 1, 0x99, 1, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(0x99, 1, 4); + } + } + gnap._sequenceId = 0x9D; + gnap._sequenceDatNum = 0; + _vm->hideCursor(); + _vm->addFullScreenSprite(0x8A, 255); + gameSys.setAnimation(0xA0, 256, 0); + gameSys.insertSequence(0xA0, 256, 0, 0, kSeqNone, 0, 0, 0); + while (gameSys.getAnimationStatus(0) != 2 && !_vm->_gameDone) + _vm->gameUpdateTick(); + gameSys.setAnimation(0x107BD, gnap._id, 0); + gameSys.insertSequence(0x107BD, gnap._id, + makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, + kSeqSyncWait, 0, 75 * gnap._pos.x - gnap._gridX, 48 * gnap._pos.y - gnap._gridY); + _vm->removeFullScreenSprite(); + _vm->showCursor(); + gnap._sequenceId = 0x7BD; + gnap._sequenceDatNum = 1; + } + + plat.playSequence(0x9A); + gameSys.setAnimation(plat._sequenceId, plat._id, 1); + + while (!_vm->_sceneDone) { + if (!_vm->isSoundPlaying(0x1094A)) + _vm->playSound(0x1094A, true); + + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->testWalk(0, 0, -1, -1, -1, -1); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + switch (_vm->_sceneClickedHotspot) { + case kHS45UfoExitLeft: + if (_vm->_toyUfoActionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_toyUfoActionStatus = 2; + _vm->_newSceneNum = 46; + _vm->toyUfoFlyTo(-35, -1, -35, 799, 0, 300, 5); + } + break; + + case kHS45UfoExitRight: + if (_vm->_toyUfoActionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_toyUfoActionStatus = 2; + _vm->_newSceneNum = 41; + _vm->toyUfoFlyTo(835, -1, 0, 835, 0, 300, 5); + } + break; + + case kHS45UfoDevice: + _vm->runMenu(); + updateHotspots(); + break; + } + } else { + switch (_vm->_sceneClickedHotspot) { + case kHS45Device: + _vm->runMenu(); + updateHotspots(); + break; + + case kHS45Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(plat._pos); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + plat.playSequence(0x9A); + gameSys.setAnimation(plat._sequenceId, plat._id, 1); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(plat._pos); + break; + } + } + } + break; + + case kHS45ExitUfoParty: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + gnap.walkTo(Common::Point(gnap._pos.x, _vm->_hotspotsWalkPos[kHS45ExitUfoParty].y), 0, 0x107AE, 1); + gnap._actionStatus = 0; + _vm->_newSceneNum = 40; + } + break; + + case kHS45ExitShoe: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + gnap.walkTo(Common::Point(_vm->_hotspotsWalkPos[kHS45ExitShoe].x, gnap._pos.y), 0, 0x107AF, 1); + gnap._actionStatus = 0; + plat.walkTo(Common::Point(_vm->_hotspotsWalkPos[kHS45ExitShoe].x, plat._pos.y), -1, 0x107CF, 1); + _vm->_newSceneNum = 46; + } + break; + + case kHS45ExitRight: + if (gnap._actionStatus < 0) { + _vm->_isLeavingScene = true; + gnap.walkTo(Common::Point(_vm->_hotspotsWalkPos[kHS45ExitRight].x, gnap._pos.y), 0, 0x107AB, 1); + gnap._actionStatus = 0; + plat.walkTo(Common::Point(_vm->_hotspotsWalkPos[kHS45ExitRight].x, plat._pos.y), -1, 0x107CD, 1); + _vm->_newSceneNum = 41; + } + break; + + case kHS45ExitDiscoBall: + _vm->clearFlag(kGFUnk22); + _vm->setFlag(kGFUnk23); + _vm->_sceneDone = true; + _vm->_newSceneNum = 54; + break; + + case kHS45DiscoBall: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex == kItemSpring) { + gnap.walkTo(_vm->_hotspotsWalkPos[kHS45DiscoBall], 0, 0x9F, 5); + gnap._actionStatus = 1; + _vm->setGrabCursorSprite(-1); + _vm->invRemove(kItemSpring); + } else if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowItem(_vm->_grabCursorSpriteIndex, 5, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playScratchingHead(Common::Point(5, 0)); + break; + case GRAB_CURSOR: + case TALK_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + } + break; + + case kHS45WalkArea1: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + } + } + + if (_vm->_mouseClickState._left && gnap._actionStatus < 0) { + _vm->_mouseClickState._left = false; + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + _vm->_toyUfoActionStatus = 3; + _vm->toyUfoFlyTo(-1, -1, 0, 799, 0, 300, 5); + } else { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + } + } + + updateAnimations(); + _vm->toyUfoCheckTimer(); + + if (!_vm->_isLeavingScene && gnap._actionStatus < 0 && !_vm->isFlag(kGFGnapControlsToyUFO)) + gnap.updateIdleSequence(); + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } + + _vm->_sceneWaiting = false; +} + +void Scene45::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case 0: + _vm->_sceneDone = true; + break; + case 1: + _vm->_sceneWaiting = true; + _vm->setFlag(kGFUnk22); + updateHotspots(); + gameSys.insertSequence(0x9E, gnap._id, makeRid(gnap._sequenceDatNum, gnap._sequenceId), gnap._id, kSeqSyncWait, 0, 0, 0); + gnap._sequenceId = 0x9E; + gnap._sequenceDatNum = 0; + gameSys.setAnimation(0x9E, gnap._id, 0); + break; + default: + gnap._actionStatus = -1; + break; + } + } + + if (gameSys.getAnimationStatus(1) == 2) { + gameSys.setAnimation(0, 0, 1); + if (_vm->getRandom(2) != 0) + plat.playSequence(0x9B); + else + plat.playSequence(0x9C); + gameSys.setAnimation(plat._sequenceId, plat._id, 1); + } + + if (gameSys.getAnimationStatus(2) == 2) { + gameSys.setAnimation(0, 0, 2); + int newSeqId = _vm->getRandom(7) + 0x8F; + gameSys.insertSequence(newSeqId, 1, _currDancerSequenceId, 1, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(newSeqId, 1, 2); + _currDancerSequenceId = newSeqId; + } + + if (gameSys.getAnimationStatus(3) == 2 && gameSys.getAnimationStatus(4) == 2) { + gameSys.insertSequence(0x96, 1, 0x96, 1, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(0x96, 1, 3); + gameSys.insertSequence(0x99, 1, 0x99, 1, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(0x99, 1, 4); + } + + if (gameSys.getAnimationStatus(5) == 2) { + switch (_vm->_toyUfoActionStatus) { + case 2: + _vm->_sceneDone = true; + break; + default: + _vm->_toyUfoNextSequenceId = _vm->toyUfoGetSequenceId(); + gameSys.insertSequence(_vm->_toyUfoNextSequenceId | 0x10000, _vm->_toyUfoId + 1, + _vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, + kSeqSyncWait, 0, _vm->_toyUfoX - 274, _vm->_toyUfoY - 128); + _vm->_toyUfoSequenceId = _vm->_toyUfoNextSequenceId; + ++_vm->_toyUfoId; + gameSys.setAnimation(_vm->_toyUfoNextSequenceId | 0x10000, _vm->_toyUfoId, 5); + break; + } + _vm->_toyUfoActionStatus = -1; + } +} + +/*****************************************************************************/ + +Scene46::Scene46(GnapEngine *vm) : Scene(vm) { + _currSackGuySequenceId = -1; + _nextItchyGuySequenceId = -1; + _nextSackGuySequenceId = -1; + _currItchyGuySequenceId = -1; +} + +int Scene46::init() { + GameSys& gameSys = *_vm->_gameSys; + + gameSys.setAnimation(0, 0, 0); + gameSys.setAnimation(0, 0, 1); + gameSys.setAnimation(0, 0, 2); + gameSys.setAnimation(0, 0, 3); + gameSys.setAnimation(0, 0, 4); + return 0x4E; +} + +void Scene46::updateHotspots() { + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + _vm->setHotspot(kHS46Platypus, 0, 0, 0, 0, SF_DISABLED); + _vm->setHotspot(kHS46UfoExitLeft, 0, 0, 10, 599, SF_EXIT_L_CURSOR); + _vm->setHotspot(kHS46UfoExitRight, 790, 0, 799, 599, SF_EXIT_R_CURSOR); + _vm->setDeviceHotspot(kHS46UfoDevice, -1, 534, -1, 599); + _vm->_hotspotsCount = 4; + } else { + _vm->setHotspot(kHS46Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS46ExitUfoParty, 150, 580, 650, 600, SF_EXIT_D_CURSOR | SF_WALKABLE, 5, 9); + _vm->setHotspot(kHS46ExitKissinBooth, 0, 100, 10, 599, SF_EXIT_L_CURSOR, 0, 8); + _vm->setHotspot(kHS46ExitDisco, 790, 100, 799, 599, SF_EXIT_R_CURSOR, 10, 8); + _vm->setHotspot(kHS46SackGuy, 180, 370, 235, 490, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 3, 8); + _vm->setHotspot(kHS46ItchyGuy, 535, 210, 650, 480, SF_PLAT_CURSOR | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR, 6, 8); + _vm->setHotspot(kHS46WalkArea1, 0, 0, 800, 485); + _vm->setDeviceHotspot(kHS46Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 8; + } +} + +void Scene46::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + PlayerPlat& plat = *_vm->_plat; + + _vm->queueInsertDeviceIcon(); + gameSys.insertSequence(0x4D, 0, 0, 0, kSeqLoop, 0, 0, 0); + + _currSackGuySequenceId = 0x4B; + _nextSackGuySequenceId = -1; + gameSys.setAnimation(0x4B, 1, 3); + gameSys.insertSequence(_currSackGuySequenceId, 1, 0, 0, kSeqNone, 0, 0, 0); + + _currItchyGuySequenceId = 0x47; + _nextItchyGuySequenceId = -1; + gameSys.setAnimation(0x47, 1, 4); + gameSys.insertSequence(_currItchyGuySequenceId, 1, 0, 0, kSeqNone, 0, 0, 0); + + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + _vm->_toyUfoId = 0; + _vm->_toyUfoActionStatus = -1; + _vm->_toyUfoSequenceId = _vm->toyUfoGetSequenceId(); + _vm->_toyUfoNextSequenceId = _vm->_toyUfoSequenceId; + if (_vm->_prevSceneNum == 44) + _vm->_toyUfoX = 30; + else + _vm->_toyUfoX = 770; + gameSys.setAnimation(_vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, 2); + gameSys.insertSequence(_vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, 0, 0, kSeqNone, 0, _vm->_toyUfoX - 274, _vm->_toyUfoY - 128); + _vm->endSceneInit(); + } else if (_vm->_prevSceneNum == 44) { + gnap.initPos(-1, 8, kDirUpRight); + plat.initPos(-1, 8, kDirUpLeft); + _vm->endSceneInit(); + plat.walkTo(Common::Point(1, 8), -1, 0x107C2, 1); + gnap.walkTo(Common::Point(2, 8), -1, 0x107B9, 1); + } else if (_vm->_prevSceneNum == 45) { + gnap.initPos(11, 8, kDirUpRight); + plat.initPos(12, 8, kDirUpLeft); + _vm->endSceneInit(); + gnap.walkTo(Common::Point(8, 8), -1, 0x107BA, 1); + plat.walkTo(Common::Point(9, 8), -1, 0x107D2, 1); + } else { + gnap.initPos(5, 11, kDirUpRight); + plat.initPos(6, 11, kDirUpLeft); + _vm->endSceneInit(); + plat.walkTo(Common::Point(5, 8), -1, 0x107C2, 1); + gnap.walkTo(Common::Point(6, 8), -1, 0x107BA, 1); + } + + _vm->_timers[4] = _vm->getRandom(50) + 80; + _vm->_timers[5] = _vm->getRandom(50) + 80; + + while (!_vm->_sceneDone) { + if (!_vm->isSoundPlaying(0x1094B)) + _vm->playSound(0x1094B, true); + + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->testWalk(0, 0, -1, -1, -1, -1); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + switch (_vm->_sceneClickedHotspot) { + case kHS46UfoExitLeft: + if (_vm->_toyUfoActionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_toyUfoActionStatus = 3; + _vm->_newSceneNum = 44; + _vm->toyUfoFlyTo(-35, -1, -35, 799, 0, 300, 2); + } + break; + + case kHS46UfoExitRight: + if (_vm->_toyUfoActionStatus < 0) { + _vm->_isLeavingScene = true; + _vm->_toyUfoActionStatus = 3; + _vm->_newSceneNum = 45; + _vm->toyUfoFlyTo(835, -1, 0, 835, 0, 300, 2); + } + break; + + case kHS46UfoDevice: + _vm->runMenu(); + updateHotspots(); + break; + } + } else { + switch (_vm->_sceneClickedHotspot) { + case kHS46Device: + _vm->runMenu(); + updateHotspots(); + break; + + case kHS46Platypus: + if (gnap._actionStatus < 0) { + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playImpossible(plat._pos); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(plat._pos); + break; + case GRAB_CURSOR: + gnap.kissPlatypus(0); + break; + case TALK_CURSOR: + gnap.playBrainPulsating(plat._pos); + plat.playSequence(plat.getSequenceId()); + break; + case PLAT_CURSOR: + gnap.playImpossible(plat._pos); + break; + } + } + } + break; + + case kHS46SackGuy: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS46SackGuy], 2, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(Common::Point(_vm->_hotspotsWalkPos[kHS46SackGuy].x + 1, 0)); + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpLeft; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS46SackGuy], 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = 2; + break; + case GRAB_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + break; + + case kHS46ItchyGuy: + if (_vm->_grabCursorSpriteIndex >= 0) { + gnap.playShowCurrItem(_vm->_hotspotsWalkPos[kHS46ItchyGuy], 7, 0); + } else { + switch (_vm->_verbCursor) { + case LOOK_CURSOR: + gnap.playMoan1(Common::Point(_vm->_hotspotsWalkPos[kHS46ItchyGuy].x - 1, 0)); + break; + case TALK_CURSOR: + gnap._idleFacing = kDirUpRight; + gnap.walkTo(_vm->_hotspotsWalkPos[kHS46ItchyGuy], 0, gnap.getSequenceId(kGSBrainPulsating, Common::Point(0, 0)) | 0x10000, 1); + gnap._actionStatus = 1; + break; + case GRAB_CURSOR: + case PLAT_CURSOR: + gnap.playImpossible(); + break; + } + } + break; + + case kHS46ExitUfoParty: + _vm->_isLeavingScene = true; + gnap.walkTo(Common::Point(gnap._pos.x, _vm->_hotspotsWalkPos[kHS46ExitUfoParty].y), 0, 0x107AE, 1); + gnap._actionStatus = 0; + _vm->_newSceneNum = 40; + break; + + case kHS46ExitKissinBooth: + _vm->_isLeavingScene = true; + gnap.walkTo(Common::Point(_vm->_hotspotsWalkPos[kHS46ExitKissinBooth].x, gnap._pos.y), 0, 0x107AF, 1); + gnap._actionStatus = 0; + plat.walkTo(Common::Point(_vm->_hotspotsWalkPos[kHS46ExitKissinBooth].x, plat._pos.y), -1, 0x107CF, 1); + _vm->_newSceneNum = 44; + break; + + case kHS46ExitDisco: + _vm->_isLeavingScene = true; + gnap.walkTo(Common::Point(_vm->_hotspotsWalkPos[kHS46ExitDisco].x, gnap._pos.y), 0, 0x107AB, 1); + gnap._actionStatus = 0; + plat.walkTo(Common::Point(_vm->_hotspotsWalkPos[kHS46ExitDisco].x, plat._pos.y), -1, 0x107CD, 1); + _vm->_newSceneNum = 45; + break; + + case kHS46WalkArea1: + if (gnap._actionStatus < 0) + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + break; + } + } + + if (_vm->_mouseClickState._left && gnap._actionStatus < 0) { + _vm->_mouseClickState._left = false; + if (_vm->isFlag(kGFGnapControlsToyUFO)) { + _vm->_toyUfoActionStatus = 4; + _vm->toyUfoFlyTo(-1, -1, 0, 799, 0, 300, 2); + } else { + gnap.walkTo(Common::Point(-1, -1), -1, -1, 1); + } + } + + updateAnimations(); + _vm->toyUfoCheckTimer(); + + if (!_vm->_isLeavingScene) { + if (plat._actionStatus < 0 && !_vm->isFlag(kGFGnapControlsToyUFO)) + plat.updateIdleSequence(); + if (gnap._actionStatus < 0 && !_vm->isFlag(kGFGnapControlsToyUFO)) + gnap.updateIdleSequence(); + if (!_vm->_timers[4]) { + _vm->_timers[4] = _vm->getRandom(50) + 80; + if (gnap._actionStatus < 0 && plat._actionStatus < 0 && _nextItchyGuySequenceId == -1) { + if (_vm->getRandom(2) != 0) + _nextItchyGuySequenceId = 0x49; + else + _nextItchyGuySequenceId = 0x48; + } + } + if (!_vm->_timers[5]) { + _vm->_timers[5] = _vm->getRandom(50) + 80; + if (gnap._actionStatus < 0 && plat._actionStatus < 0 && _nextSackGuySequenceId == -1) + _nextSackGuySequenceId = 0x4C; + } + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + + _vm->gameUpdateTick(); + } +} + +void Scene46::updateAnimations() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (gameSys.getAnimationStatus(0) == 2) { + gameSys.setAnimation(0, 0, 0); + switch (gnap._actionStatus) { + case 0: + _vm->_sceneDone = true; + break; + case 1: + _nextItchyGuySequenceId = 0x46; + break; + case 2: + _nextSackGuySequenceId = 0x4A; + break; + } + gnap._actionStatus = -1; + } + + if (gameSys.getAnimationStatus(3) == 2 && _nextSackGuySequenceId != -1) { + gameSys.insertSequence(_nextSackGuySequenceId, 1, _currSackGuySequenceId, 1, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextSackGuySequenceId, 1, 3); + _currSackGuySequenceId = _nextSackGuySequenceId; + _nextSackGuySequenceId = -1; + _vm->_timers[5] = _vm->getRandom(50) + 80; + } + + if (gameSys.getAnimationStatus(4) == 2 && _nextItchyGuySequenceId != -1) { + gameSys.insertSequence(_nextItchyGuySequenceId, 1, _currItchyGuySequenceId, 1, kSeqSyncWait, 0, 0, 0); + gameSys.setAnimation(_nextItchyGuySequenceId, 1, 4); + _currItchyGuySequenceId = _nextItchyGuySequenceId; + _nextItchyGuySequenceId = -1; + _vm->_timers[4] = _vm->getRandom(50) + 80; + } + + if (gameSys.getAnimationStatus(2) == 2) { + switch (_vm->_toyUfoActionStatus) { + case 3: + _vm->_sceneDone = true; + break; + default: + _vm->_toyUfoNextSequenceId = _vm->toyUfoGetSequenceId(); + gameSys.insertSequence(_vm->_toyUfoNextSequenceId | 0x10000, _vm->_toyUfoId + 1, + _vm->_toyUfoSequenceId | 0x10000, _vm->_toyUfoId, + kSeqSyncWait, 0, _vm->_toyUfoX - 274, _vm->_toyUfoY - 128); + _vm->_toyUfoSequenceId = _vm->_toyUfoNextSequenceId; + ++_vm->_toyUfoId; + gameSys.setAnimation(_vm->_toyUfoNextSequenceId | 0x10000, _vm->_toyUfoId, 2); + break; + } + _vm->_toyUfoActionStatus = -1; + } +} + +} // End of namespace Gnap diff --git a/engines/gnap/scenes/group4.h b/engines/gnap/scenes/group4.h new file mode 100644 index 0000000000..afcd62e9e7 --- /dev/null +++ b/engines/gnap/scenes/group4.h @@ -0,0 +1,298 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GNAP_GROUP4_H +#define GNAP_GROUP4_H + +#include "gnap/debugger.h" + +namespace Gnap { + +enum { + kHS40Platypus = 0, + kHS40ExitCave = 1, + kHS40ExitToyStand = 2, + kHS40ExitBBQ = 3, + kHS40ExitUfo = 4, + kHS40ExitKissinBooth = 5, + kHS40ExitDancefloor = 6, + kHS40ExitShoe = 7, + kHS40Device = 8 +}; + +enum { + kHS41Platypus = 0, + kHS41ExitCave = 1, + kHS41Exit = 2, + kHS41ExitBBQ = 3, + kHS41ToyVendor = 4, + kHS41Kid = 5, + kHS41ToyUfo = 6, + kHS41Device = 7, + kHS41WalkArea1 = 8 +}; + +enum { + kHS41UfoExitLeft = 1, + kHS41UfoExitRight = 2, + kHS41UfoDevice = 3, + kHS41UfoWalkArea1 = 4 +}; + +enum { + kHS42Platypus = 0, + kHS42ExitUfoParty = 1, + kHS42ExitToyStand = 2, + kHS42ExitUfo = 3, + kHS42BBQVendor = 4, + kHS42ChickenLeg = 5, + kHS42Device = 6, + kHS42WalkArea1 = 7, + kHS42WalkArea2 = 8 +}; + +enum { + kHS42UfoExitLeft = 1, + kHS42UfoExitRight = 2, + kHS42UfoHotSauce = 3, + kHS42UfoDevice = 4 +}; + +enum { + kHS43Platypus = 0, + kHS43Device = 1, + kHS43ExitUfoParty = 2, + kHS43ExitBBQ = 3, + kHS43ExitKissinBooth = 4, + kHS43TwoHeadedGuy = 5, + kHS43Key = 6, + kHS43Ufo = 7, + kHS43WalkArea1 = 8, + kHS43WalkArea2 = 9 +}; + +enum { + kHS43UfoExitLeft = 1, + kHS43UfoExitRight = 2, + kHS43UfoKey = 3, + kHS43UfoBucket = 4, + kHS43UfoDevice = 5 +}; + +enum { + kHS44Platypus = 0, + kHS44ExitUfoParty = 1, + kHS44ExitUfo = 2, + kHS44ExitShow = 3, + kHS44KissingLady = 4, + kHS44Spring = 5, + kHS44SpringGuy = 6, + kHS44Device = 7, + kHS44WalkArea1 = 8, + kHS44WalkArea2 = 9 +}; + +enum { + kHS44UfoExitLeft = 1, + kHS44UfoExitRight = 2, + kHS44UfoDevice = 3 +}; + +enum { + kHS45Platypus = 0, + kHS45ExitUfoParty = 1, + kHS45ExitShoe = 2, + kHS45ExitRight = 3, + kHS45ExitDiscoBall = 4, + kHS45DiscoBall = 5, + kHS45Device = 6, + kHS45WalkArea1 = 7 +}; + +enum { + kHS45UfoExitLeft = 1, + kHS45UfoExitRight = 2, + kHS45UfoDevice = 3 +}; + +enum { + kHS46Platypus = 0, + kHS46ExitUfoParty = 1, + kHS46ExitKissinBooth = 2, + kHS46ExitDisco = 3, + kHS46SackGuy = 4, + kHS46ItchyGuy = 5, + kHS46Device = 6, + kHS46WalkArea1 = 7 +}; + +enum { + kHS46UfoExitLeft = 1, + kHS46UfoExitRight = 2, + kHS46UfoDevice = 3 +}; + +enum { + kAS41LeaveScene = 0, + kAS41UseQuarterWithToyVendor = 1, + kAS41TalkToyVendor = 2, + kAS41UseGumWithToyUfo = 3, + kAS41UseChickenBucketWithKid = 4, + kAS41GrabKid = 5, + kAS41GiveBackToyUfo = 6, + kAS41ToyUfoLeaveScene = 7, + kAS41ToyUfoRefresh = 8, + kAS41UfoGumAttached = 9 +}; + +enum { + kAS42LeaveScene = 0, + kAS42TalkBBQVendor = 1, + kAS42UseQuarterWithBBQVendor = 2, + kAS42UseQuarterWithBBQVendorDone = 3, + kAS42GrabChickenLeg = 4, + kAS42ToyUfoLeaveScene = 5, + kAS42ToyUfoRefresh = 6, + kAS42ToyUfoPickUpHotSauce = 7 +}; + +/*****************************************************************************/ + +class GnapEngine; +class CutScene; + +class Scene40: public Scene { +public: + Scene40(GnapEngine *vm); + virtual ~Scene40() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; +}; + +class Scene41: public Scene { +public: + Scene41(GnapEngine *vm); + virtual ~Scene41() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _currKidSequenceId; + int _nextKidSequenceId; + int _currToyVendorSequenceId; + int _nextToyVendorSequenceId; +}; + +class Scene42: public Scene { +public: + Scene42(GnapEngine *vm); + virtual ~Scene42() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _currBBQVendorSequenceId; + int _nextBBQVendorSequenceId; +}; + +class Scene43: public Scene { +public: + Scene43(GnapEngine *vm); + virtual ~Scene43() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _currTwoHeadedGuySequenceId; + int _nextTwoHeadedGuySequenceId; +}; + +class Scene44: public Scene { +public: + Scene44(GnapEngine *vm); + virtual ~Scene44() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _nextSpringGuySequenceId; + int _nextKissingLadySequenceId; + int _currSpringGuySequenceId; + int _currKissingLadySequenceId; +}; + +class Scene45: public Scene { +public: + Scene45(GnapEngine *vm); + virtual ~Scene45() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _currDancerSequenceId; +}; + +class Scene46: public Scene { +public: + Scene46(GnapEngine *vm); + virtual ~Scene46() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations(); + virtual void updateAnimationsCb() {}; + +private: + int _currSackGuySequenceId; + int _nextItchyGuySequenceId; + int _nextSackGuySequenceId; + int _currItchyGuySequenceId; +}; + +} // End of namespace Gnap + +#endif // GNAP_GROUP4_H diff --git a/engines/gnap/scenes/group5.cpp b/engines/gnap/scenes/group5.cpp new file mode 100644 index 0000000000..46f4c51e5d --- /dev/null +++ b/engines/gnap/scenes/group5.cpp @@ -0,0 +1,381 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gnap/gnap.h" +#include "gnap/gamesys.h" +#include "gnap/resource.h" +#include "gnap/scenes/group5.h" + +namespace Gnap { + +Scene53::Scene53(GnapEngine *vm) : Scene(vm) { + _isGnapPhoning = false; + _currHandSequenceId = -1; + _callsMadeCtr = 0; + _callsRndUsed = 0; +} + +int Scene53::init() { + GameSys& gameSys = *_vm->_gameSys; + + gameSys.setAnimation(0, 0, 0); + gameSys.setAnimation(0, 0, 1); + return 0x75; +} + +void Scene53::updateHotspots() { + _vm->setHotspot(kHS53Platypus, 0, 0, 0, 0, SF_WALKABLE | SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR); + _vm->setHotspot(kHS53PhoneKey1, 336, 238, 361, 270, SF_GRAB_CURSOR); + _vm->setHotspot(kHS53PhoneKey2, 376, 243, 405, 274, SF_GRAB_CURSOR); + _vm->setHotspot(kHS53PhoneKey3, 415, 248, 441, 276, SF_GRAB_CURSOR); + _vm->setHotspot(kHS53PhoneKey4, 329, 276, 358, 303, SF_GRAB_CURSOR); + _vm->setHotspot(kHS53PhoneKey5, 378, 282, 408, 311, SF_GRAB_CURSOR); + _vm->setHotspot(kHS53PhoneKey6, 417, 286, 446, 319, SF_GRAB_CURSOR); + _vm->setHotspot(kHS53PhoneKey7, 332, 311, 361, 342, SF_GRAB_CURSOR); + _vm->setHotspot(kHS53PhoneKey8, 376, 318, 407, 349, SF_GRAB_CURSOR); + _vm->setHotspot(kHS53PhoneKey9, 417, 320, 447, 353, SF_GRAB_CURSOR); + _vm->setHotspot(kHS53PhoneKey0, 377, 352, 405, 384, SF_GRAB_CURSOR); + _vm->setHotspot(kHS53PhoneKeySharp, 419, 358, 450, 394, SF_GRAB_CURSOR); + _vm->setHotspot(kHS53PhoneKeyStar, 328, 346, 359, 379, SF_GRAB_CURSOR); + _vm->setHotspot(kHS53PhoneExit, 150, 585, 650, 600, SF_EXIT_D_CURSOR); + + _vm->setDeviceHotspot(kHS53Device, -1, -1, -1, -1); + _vm->_hotspotsCount = 15; +} + +int Scene53::pressPhoneNumberButton(int phoneNumber, int buttonNum) { + static const int kGnapHandSequenceIds[13] = { + 0x00, + 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, + 0x4C, 0x4D, 0x4E, 0x50, 0x51, 0x4F + }; + + static const int kPlatypusHandSequenceIds[13] = { + 0x00, + 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5A, 0x5C, 0x5D, 0x5B + }; + + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + if (_isGnapPhoning) { + gameSys.setAnimation(kGnapHandSequenceIds[buttonNum], 40, 6); + gameSys.insertSequence(kGnapHandSequenceIds[buttonNum], 40, _currHandSequenceId, 40, kSeqSyncWait, 0, 0, 0); + _currHandSequenceId = kGnapHandSequenceIds[buttonNum]; + } else { + gameSys.setAnimation(kPlatypusHandSequenceIds[buttonNum], 40, 6); + gameSys.insertSequence(kPlatypusHandSequenceIds[buttonNum], 40, _currHandSequenceId, 40, kSeqSyncWait, 0, 0, 0); + _currHandSequenceId = kPlatypusHandSequenceIds[buttonNum]; + } + + gnap._actionStatus = 6; + while (gameSys.getAnimationStatus(6) != 2 && !_vm->_gameDone) { + _vm->updateMouseCursor(); + _vm->gameUpdateTick(); + } + gnap._actionStatus = -1; + + if (buttonNum < 11) + phoneNumber = buttonNum % 10 + 10 * phoneNumber; + + return phoneNumber; +} + +int Scene53::getRandomCallIndex() { + int index, tries = 0; + if (_callsRndUsed == 0x7FFF) + _callsRndUsed = 0; + do { + index = _vm->getRandom(16); + if (++tries == 300) + _callsRndUsed = 0; + } while (_callsRndUsed & (1 << index)); + _callsRndUsed |= (1 << index); + return index; +} + +void Scene53::runRandomCall() { + static const int kCallSequenceIds[15] = { + 0x60, 0x61, 0x62, 0x63, 0x64, + 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6A, 0x6B, 0x6C, 0x6D, 0x71 + }; + + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + ++_callsMadeCtr; + if (_callsMadeCtr <= 10) { + int index; + + do { + index = getRandomCallIndex(); + } while (!_isGnapPhoning && (index == 0 || index == 3 || index == 4 || index == 11)); + gameSys.setAnimation(kCallSequenceIds[index], 1, 6); + gameSys.insertSequence(kCallSequenceIds[index], 1, 0, 0, kSeqNone, 16, 0, 0); + } else { + gameSys.setAnimation(0x74, 1, 6); + gameSys.insertSequence(0x74, 1, 0, 0, kSeqNone, 16, 0, 0); + _callsMadeCtr = 0; + } + + gnap._actionStatus = 1; + while (gameSys.getAnimationStatus(6) != 2 && !_vm->_gameDone) { + _vm->updateMouseCursor(); + _vm->gameUpdateTick(); + } + gnap._actionStatus = -1; +} + +void Scene53::runChitChatLine() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + bool flag = false; + int sequenceId = -1; + + gameSys.setAnimation(0x6E, 1, 6); + gameSys.insertSequence(0x6E, 1, 0, 0, kSeqNone, 16, 0, 0); + + gnap._actionStatus = 1; + while (gameSys.getAnimationStatus(6) != 2 && !_vm->_gameDone) { + _vm->updateMouseCursor(); + _vm->gameUpdateTick(); + } + gnap._actionStatus = -1; + + if (_vm->isFlag(kGFSpringTaken)) { + gameSys.insertSequence(0x45, 40, _currHandSequenceId, 40, kSeqSyncWait, 0, 0, 0); + _currHandSequenceId = 0x45; + } else { + gameSys.insertSequence(0x45, 40, _currHandSequenceId, 40, kSeqSyncWait, 0, 0, 0); + _currHandSequenceId = 0x5E; + } + + _vm->_hotspots[kHS53Device]._flags = SF_DISABLED; + + while (!flag) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->testWalk(0, 0, -1, -1, -1, -1); + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case 2: + sequenceId = 0x6F; + flag = 1; + break; + case 3: + sequenceId = 0x70; + flag = 1; + break; + case 4: + sequenceId = 0x71; + flag = 1; + break; + case 14: + sequenceId = -1; + flag = 1; + _vm->_isLeavingScene = true; + _vm->_sceneDone = true; + gnap._actionStatus = 0; + _vm->_newSceneNum = 17; + break; + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + pressPhoneNumberButton(0, _vm->_sceneClickedHotspot - 1); + break; + } + + if (flag && sequenceId != -1) { + _vm->stopSound(0xA0); + pressPhoneNumberButton(0, _vm->_sceneClickedHotspot - 1); + gnap._actionStatus = 1; + gameSys.setAnimation(sequenceId, 1, 6); + gameSys.insertSequence(sequenceId, 1, 0, 0, kSeqNone, 16, 0, 0); + gnap._actionStatus = 1; + while (gameSys.getAnimationStatus(6) != 2 && !_vm->_gameDone) { + _vm->updateMouseCursor(); + _vm->gameUpdateTick(); + } + gnap._actionStatus = -1; + gameSys.setAnimation(0x72, 1, 6); + gameSys.insertSequence(0x72, 1, 0, 0, kSeqNone, 16, 0, 0); + gnap._actionStatus = 1; + while (gameSys.getAnimationStatus(6) != 2 && !_vm->_gameDone) { + _vm->updateMouseCursor(); + _vm->gameUpdateTick(); + } + gnap._actionStatus = -1; + } + } + + updateHotspots(); + + gnap._actionStatus = 1; + + if (_vm->isFlag(kGFSpringTaken)) { + gameSys.setAnimation(0x73, 40, 6); + gameSys.insertSequence(0x73, 40, _currHandSequenceId, 40, kSeqSyncWait, 0, 0, 0); + while (gameSys.getAnimationStatus(6) != 2 && !_vm->_gameDone) { + _vm->updateMouseCursor(); + _vm->gameUpdateTick(); + } + _currHandSequenceId = 0x73; + gnap._actionStatus = -1; + } +} + +void Scene53::run() { + GameSys& gameSys = *_vm->_gameSys; + PlayerGnap& gnap = *_vm->_gnap; + + int phoneNumber = 0; + int phoneNumberLen = 0; + + _vm->queueInsertDeviceIcon(); + + if (_vm->isFlag(kGFSpringTaken)) { + _currHandSequenceId = 0x45; + _isGnapPhoning = true; + } else { + _currHandSequenceId = 0x5E; + _isGnapPhoning = false; + } + + gameSys.insertSequence(_currHandSequenceId, 40, 0, 0, kSeqNone, 0, 0, 0); + _vm->endSceneInit(); + _vm->setVerbCursor(GRAB_CURSOR); + _vm->playSound(0xA0, true); + + while (!_vm->_sceneDone) { + _vm->updateMouseCursor(); + _vm->updateCursorByHotspot(); + + _vm->testWalk(0, 0, -1, -1, -1, -1); + + _vm->_sceneClickedHotspot = _vm->getClickedHotspotId(); + _vm->updateGrabCursorSprite(0, 0); + + switch (_vm->_sceneClickedHotspot) { + case kHS53Device: + if (gnap._actionStatus < 0) { + _vm->runMenu(); + updateHotspots(); + } + break; + case kHS53PhoneKey1: + case kHS53PhoneKey2: + case kHS53PhoneKey3: + case kHS53PhoneKey4: + case kHS53PhoneKey5: + case kHS53PhoneKey6: + case kHS53PhoneKey7: + case kHS53PhoneKey8: + case kHS53PhoneKey9: + case kHS53PhoneKey0: + _vm->stopSound(0xA0); + ++phoneNumberLen; + phoneNumber = pressPhoneNumberButton(phoneNumber, _vm->_sceneClickedHotspot - 1); + debugC(kDebugBasic, "phoneNumber: %d", phoneNumber); + if (phoneNumberLen == 7) { + gnap._actionStatus = 1; + if (_vm->isFlag(kGFSpringTaken)) { + gameSys.setAnimation(0x73, 40, 6); + gameSys.insertSequence(0x73, 40, _currHandSequenceId, 40, kSeqSyncWait, 0, 0, 0); + while (gameSys.getAnimationStatus(6) != 2 && !_vm->_gameDone) { + _vm->updateMouseCursor(); + _vm->gameUpdateTick(); + } + _currHandSequenceId = 0x73; + gnap._actionStatus = -1; + } + if (phoneNumber == 7284141) { + runChitChatLine(); + phoneNumber = 0; + phoneNumberLen = 0; + _vm->_sceneDone = true; + _vm->_newSceneNum = 17; + } else if (phoneNumber != 5556789 || _vm->isFlag(kGFPictureTaken)) { + runRandomCall(); + phoneNumber = 0; + phoneNumberLen = 0; + _vm->_sceneDone = true; + _vm->_newSceneNum = 17; + } else { + phoneNumber = 0; + phoneNumberLen = 0; + _vm->_sceneDone = true; + _vm->_newSceneNum = 17; + if (_isGnapPhoning) + _vm->setFlag(kGFUnk25); + else + _vm->setFlag(kGFPlatypusTalkingToAssistant); + } + } + break; + case kHS53PhoneKeySharp: + case kHS53PhoneKeyStar: + pressPhoneNumberButton(0, _vm->_sceneClickedHotspot - 1); + break; + case kHS53PhoneExit: + if (gnap._actionStatus < 0) { + gnap._actionStatus = 1; + if (_vm->isFlag(kGFSpringTaken)) { + gameSys.setAnimation(0x73, 40, 6); + gameSys.insertSequence(0x73, 40, _currHandSequenceId, 40, kSeqSyncWait, 0, 0, 0); + while (gameSys.getAnimationStatus(6) != 2 && !_vm->_gameDone) { + _vm->updateMouseCursor(); + _vm->gameUpdateTick(); + } + _currHandSequenceId = 0x73; + gnap._actionStatus = -1; + } + _vm->_isLeavingScene = true; + _vm->_sceneDone = true; + gnap._actionStatus = 0; + _vm->_newSceneNum = 17; + } + break; + } + + _vm->checkGameKeys(); + + if (_vm->isKeyStatus1(Common::KEYCODE_BACKSPACE)) { + _vm->clearKeyStatus1(Common::KEYCODE_BACKSPACE); + _vm->runMenu(); + updateHotspots(); + } + _vm->gameUpdateTick(); + } +} + +} // End of namespace Gnap diff --git a/engines/gnap/scenes/group5.h b/engines/gnap/scenes/group5.h new file mode 100644 index 0000000000..dd238ec65c --- /dev/null +++ b/engines/gnap/scenes/group5.h @@ -0,0 +1,77 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GNAP_GROUP5_H +#define GNAP_GROUP5_H + +#include "gnap/debugger.h" +#include "gnap/scenes/scenecore.h" + +namespace Gnap { + +enum { + kHS53Platypus = 0, + kHS53Device = 1, + kHS53PhoneKey1 = 2, + kHS53PhoneKey2 = 3, + kHS53PhoneKey3 = 4, + kHS53PhoneKey4 = 5, + kHS53PhoneKey5 = 6, + kHS53PhoneKey6 = 7, + kHS53PhoneKey7 = 8, + kHS53PhoneKey8 = 9, + kHS53PhoneKey9 = 10, + kHS53PhoneKey0 = 11, + kHS53PhoneKeySharp = 12, + kHS53PhoneKeyStar = 13, + kHS53PhoneExit = 14 +}; + +/*****************************************************************************/ + +class GnapEngine; + +class Scene53: public Scene { +public: + Scene53(GnapEngine *vm); + virtual ~Scene53() {} + + virtual int init(); + virtual void updateHotspots(); + virtual void run(); + virtual void updateAnimations() {}; + virtual void updateAnimationsCb() {}; + +private: + bool _isGnapPhoning; + int _currHandSequenceId; + int _callsMadeCtr; + uint _callsRndUsed; + + int pressPhoneNumberButton(int phoneNumber, int buttonNum); + int getRandomCallIndex(); + void runRandomCall(); + void runChitChatLine(); +}; + +} // End of namespace Gnap +#endif // GNAP_GROUP5_H diff --git a/engines/gnap/scenes/groupcs.cpp b/engines/gnap/scenes/groupcs.cpp new file mode 100644 index 0000000000..c096eae27c --- /dev/null +++ b/engines/gnap/scenes/groupcs.cpp @@ -0,0 +1,430 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gnap/gnap.h" +#include "gnap/gamesys.h" +#include "gnap/resource.h" +#include "gnap/scenes/groupcs.h" + + +namespace Gnap { + +Scene16::Scene16(GnapEngine *vm) : CutScene(vm) {} + +int Scene16::init() { + _sequenceIdArr[0] = 0x1F2; + _sequenceIdArr[1] = 0x201; + _sequenceIdArr[2] = 0x1FC; + _sequenceIdArr[3] = 0x1F4; + _sequenceIdArr[4] = 0x1FB; + _sequenceIdArr[5] = 0x1F0; + _sequenceIdArr[6] = 0x1FD; + _sequenceIdArr[7] = 0x1FE; + _sequenceIdArr[8] = 0x1F7; + _sequenceIdArr[9] = 0x1F9; + _sequenceIdArr[10] = 0x1F8; + _sequenceIdArr[11] = 0x1F1; + _sequenceIdArr[12] = 0x202; + _sequenceIdArr[13] = 0x1F6; + _sequenceIdArr[14] = 0x1F3; + _sequenceIdArr[15] = 0x1FA; + _sequenceIdArr[16] = 0x1FF; + _sequenceIdArr[17] = 0x200; + _sequenceIdArr[18] = 0x203; + _sequenceIdArr[19] = 0x206; + _sequenceIdArr[20] = 0x207; + _sequenceIdArr[21] = 0x204; + _sequenceIdArr[22] = 0x205; + _resourceIdArr[0] = 0x1C; + _resourceIdArr[1] = 2; + _resourceIdArr[2] = 0x1B; + _resourceIdArr[3] = 0; + _resourceIdArr[4] = 0x167; + _resourceIdArr[5] = 1; + _resourceIdArr[6] = 0x15B; + _resourceIdArr[7] = 0x15A; + _resourceIdArr[8] = 0x170; + _resourceIdArr[9] = 0x1EB; + _resourceIdArr[10] = 0x1EC; + _resourceIdArr[11] = 0x1BE; + _resourceIdArr[12] = 0x1BF; + _sequenceCountArr[0] = 4; + _sequenceCountArr[1] = 1; + _sequenceCountArr[2] = 1; + _sequenceCountArr[3] = 6; + _sequenceCountArr[4] = 1; + _sequenceCountArr[5] = 3; + _sequenceCountArr[6] = 1; + _sequenceCountArr[7] = 1; + _sequenceCountArr[8] = 1; + _sequenceCountArr[9] = 1; + _sequenceCountArr[10] = 1; + _sequenceCountArr[11] = 1; + _sequenceCountArr[12] = 1; + _itemsCount = 13; + + return -1; +} + +/*****************************************************************************/ + +Scene471::Scene471(GnapEngine *vm) : CutScene(vm) {} + +int Scene471::init() { + _sequenceIdArr[0] = 0x301; + _sequenceIdArr[1] = 0x305; + _sequenceIdArr[2] = 0x302; + _sequenceIdArr[3] = 0x304; + _sequenceIdArr[4] = 0x300; + _resourceIdArr[0] = 3; + _resourceIdArr[1] = 0; + _resourceIdArr[2] = 1; + _resourceIdArr[3] = 0; + _resourceIdArr[4] = 2; + _sequenceCountArr[0] = 1; + _sequenceCountArr[1] = 1; + _sequenceCountArr[2] = 1; + _sequenceCountArr[3] = 1; + _sequenceCountArr[4] = 1; + _canSkip[0] = false; + _canSkip[1] = false; + _canSkip[2] = false; + _canSkip[3] = false; + _canSkip[4] = false; + _itemsCount = 5; + + return -1; +} + +Scene472::Scene472(GnapEngine *vm) : CutScene(vm) {} + +int Scene472::init() { + _sequenceIdArr[0] = 0x306; + _sequenceIdArr[1] = 0x309; + _sequenceIdArr[2] = 0x307; + _sequenceIdArr[3] = 0x308; + _sequenceIdArr[4] = 0x30A; + _resourceIdArr[0] = 0x8E; + _resourceIdArr[1] = 0x90; + _resourceIdArr[2] = 0x8F; + _resourceIdArr[3] = 0x91; + _sequenceCountArr[0] = 2; + _sequenceCountArr[1] = 1; + _sequenceCountArr[2] = 1; + _sequenceCountArr[3] = 1; + _canSkip[0] = false; + _canSkip[1] = false; + _canSkip[2] = false; + _canSkip[3] = false; + _itemsCount = 4; + + return -1; +} + +Scene473::Scene473(GnapEngine *vm) : CutScene(vm) {} + +int Scene473::init() { + _sequenceIdArr[0] = 0x320; + _sequenceIdArr[1] = 0x321; + _resourceIdArr[0] = 0x142; + _resourceIdArr[1] = 0x143; + _sequenceCountArr[0] = 1; + _sequenceCountArr[1] = 1; + _canSkip[0] = false; + _canSkip[1] = false; + _itemsCount = 2; + + return -1; +} + +Scene474::Scene474(GnapEngine *vm) : CutScene(vm) {} + +int Scene474::init() { + _sequenceIdArr[0] = 0x30C; + _sequenceIdArr[1] = 0x30D; + _sequenceIdArr[2] = 0x30B; + _resourceIdArr[0] = 0x142; + _resourceIdArr[1] = 0x141; + _resourceIdArr[2] = 0x177; + _sequenceCountArr[0] = 1; + _sequenceCountArr[1] = 1; + _sequenceCountArr[2] = 1; + _canSkip[0] = false; + _canSkip[1] = false; + _canSkip[2] = false; + _itemsCount = 3; + + return -1; +} + +Scene475::Scene475(GnapEngine *vm) : CutScene(vm) {} + +int Scene475::init() { + _sequenceIdArr[0] = 0x30E; + _sequenceIdArr[1] = 0x30F; + _sequenceIdArr[2] = 0x310; + _sequenceIdArr[3] = 0x311; + _resourceIdArr[0] = 0x206; + _resourceIdArr[1] = 0x207; + _sequenceCountArr[0] = 3; + _sequenceCountArr[1] = 1; + _canSkip[0] = false; + _canSkip[1] = false; + _itemsCount = 2; + + return -1; +} + +Scene476::Scene476(GnapEngine *vm) : CutScene(vm) {} + +int Scene476::init() { + _sequenceIdArr[0] = 0x31E; + _sequenceIdArr[1] = 0x31F; + _resourceIdArr[0] = 0x2FA; + _sequenceCountArr[0] = 2; + _canSkip[0] = false; + _itemsCount = 1; + + return -1; +} + +Scene477::Scene477(GnapEngine *vm) : CutScene(vm) {} + +int Scene477::init() { + int v0, v4, v2, v3; + + _sequenceIdArr[0] = 0x316; + _sequenceIdArr[1] = 0x31A; + _sequenceIdArr[2] = 0x314; + _sequenceIdArr[3] = 0x31B; + int v1 = 4; + if (!_vm->isFlag(kGFTwigTaken)) { + _sequenceIdArr[4] = 0x31C; + v1 = 5; + } + if (!_vm->isFlag(kGFPlatypusTalkingToAssistant)) + _sequenceIdArr[v1++] = 0x31D; + v4 = v1; + _sequenceIdArr[v1] = 0x319; + v0 = v1 + 1; + v3 = v0; + _sequenceIdArr[v0++] = 0x317; + _sequenceIdArr[v0++] = 0x312; + _sequenceIdArr[v0] = 0x31A; + v2 = v0 + 1; + if (!_vm->isFlag(kGFTwigTaken)) + _sequenceIdArr[v2++] = 0x31C; + if (!_vm->isFlag(kGFPlatypusTalkingToAssistant)) + _sequenceIdArr[v2++] = 0x31D; + _sequenceIdArr[v2] = 0x313; + _sequenceIdArr[v2 + 1] = 0x315; + _resourceIdArr[0] = 0x2B8; + _resourceIdArr[1] = 0x20C; + _resourceIdArr[2] = 0x2B8; + _resourceIdArr[3] = 0x20B; + _resourceIdArr[4] = 0x20B; + _sequenceCountArr[0] = v4; + _sequenceCountArr[1] = 1; + _sequenceCountArr[2] = v2 - v3; + _sequenceCountArr[3] = 1; + _sequenceCountArr[4] = 1; + _canSkip[0] = false; + _canSkip[1] = false; + _canSkip[2] = false; + _canSkip[3] = false; + _canSkip[4] = false; + _itemsCount = 5; + + return -1; +} + +/*****************************************************************************/ + +Scene48::Scene48(GnapEngine *vm) : CutScene(vm) {} + +int Scene48::init() { + _sequenceIdArr[0] = 390; + _sequenceIdArr[1] = 391; + _sequenceIdArr[2] = 392; + _sequenceIdArr[3] = 393; + _sequenceIdArr[4] = 394; + _sequenceIdArr[5] = 395; + _sequenceIdArr[6] = 396; + _sequenceIdArr[7] = 397; + _sequenceIdArr[8] = 398; + _sequenceIdArr[9] = 399; + _sequenceIdArr[10] = 400; + _sequenceIdArr[11] = 401; + _sequenceIdArr[12] = 402; + _resourceIdArr[0] = 238; + _resourceIdArr[1] = 42; + _resourceIdArr[2] = 2; + _resourceIdArr[3] = 37; + _resourceIdArr[4] = 35; + _resourceIdArr[5] = 38; + _resourceIdArr[6] = 39; + _resourceIdArr[7] = 40; + _resourceIdArr[8] = 41; + _resourceIdArr[9] = 36; + _resourceIdArr[10] = 41; + _resourceIdArr[11] = 388; + _resourceIdArr[12] = 387; + _sequenceCountArr[0] = 1; + _sequenceCountArr[1] = 1; + _sequenceCountArr[2] = 1; + _sequenceCountArr[3] = 1; + _sequenceCountArr[4] = 1; + _sequenceCountArr[5] = 1; + _sequenceCountArr[6] = 1; + _sequenceCountArr[7] = 1; + _sequenceCountArr[8] = 1; + _sequenceCountArr[9] = 1; + _sequenceCountArr[10] = 1; + _sequenceCountArr[11] = 1; + _sequenceCountArr[12] = 1; + _canSkip[0] = false; + _canSkip[1] = false; + _canSkip[2] = false; + _canSkip[3] = false; + _canSkip[4] = false; + _canSkip[5] = false; + _canSkip[6] = false; + _canSkip[7] = false; + _canSkip[8] = false; + _canSkip[9] = false; + _canSkip[10] = false; + _canSkip[11] = false; + _canSkip[12] = false; + _itemsCount = 13; + + return -1; +} + +/*****************************************************************************/ + +Scene541::Scene541(GnapEngine *vm) : CutScene(vm) {} + +int Scene541::init() { + _sequenceIdArr[0] = 0x1BE; + _sequenceIdArr[1] = 0x1BF; + _sequenceIdArr[2] = 0x1BA; + _sequenceIdArr[3] = 0x1BB; + _sequenceIdArr[4] = 0x1BD; + _sequenceIdArr[5] = 0x1BC; + _resourceIdArr[0] = 0x3C; + _resourceIdArr[1] = 0x43; + _resourceIdArr[2] = 0x44; + if (_vm->isFlag(kGFPictureTaken)) + _resourceIdArr[3] = 0x47; + else + _resourceIdArr[3] = 0x46; + _resourceIdArr[4] = 0x45; + _sequenceCountArr[0] = 1; + _sequenceCountArr[1] = 1; + _sequenceCountArr[2] = 1; + _sequenceCountArr[3] = 2; + _sequenceCountArr[4] = 1; + _canSkip[0] = false; + _canSkip[1] = false; + _canSkip[2] = false; + _canSkip[3] = false; + _canSkip[4] = false; + _itemsCount = 5; + + return -1; +} + +Scene542::Scene542(GnapEngine *vm) : CutScene(vm) {} + +int Scene542::init() { + _sequenceIdArr[0] = 0x1C9; + _sequenceIdArr[1] = 0x1C7; + _sequenceIdArr[2] = 0x1CC; + _sequenceIdArr[3] = 0x1C8; + _sequenceIdArr[4] = 0x1CB; + _sequenceIdArr[5] = 0x1C0; + _sequenceIdArr[6] = 0x1CA; + _sequenceIdArr[7] = 0x1CE; + _sequenceIdArr[8] = 0x1CD; + _sequenceIdArr[9] = 0x1C1; + _sequenceIdArr[10] = 0x1C2; + _sequenceIdArr[11] = 0x1C3; + _sequenceIdArr[12] = 0x1C4; + _sequenceIdArr[13] = 0x1C6; + _sequenceIdArr[14] = 0x1C5; + _sequenceIdArr[15] = 0x1D0; + _sequenceIdArr[16] = 0x1D0; + _sequenceIdArr[17] = 0x1D0; + _resourceIdArr[0] = 0xD5; + _resourceIdArr[1] = 0x14C; + _resourceIdArr[2] = 0xD5; + _resourceIdArr[3] = 0xBF; + _resourceIdArr[4] = 0xD6; + _resourceIdArr[5] = 0x154; + _resourceIdArr[6] = 0x155; + _resourceIdArr[7] = 0xB9; + _resourceIdArr[8] = 0xBA; + _resourceIdArr[9] = 0x17B; + _resourceIdArr[10] = 0x17A; + _resourceIdArr[11] = 0x17C; + _resourceIdArr[12] = 0x17A; + _resourceIdArr[13] = 0x1B7; + _resourceIdArr[14] = 0x1B8; + _resourceIdArr[15] = 0x1B9; + _sequenceCountArr[0] = 2; + _sequenceCountArr[1] = 1; + _sequenceCountArr[2] = 2; + _sequenceCountArr[3] = 1; + _sequenceCountArr[4] = 1; + _sequenceCountArr[5] = 1; + _sequenceCountArr[6] = 1; + _sequenceCountArr[7] = 1; + _sequenceCountArr[8] = 1; + _sequenceCountArr[9] = 1; + _sequenceCountArr[10] = 1; + _sequenceCountArr[11] = 1; + _sequenceCountArr[12] = 1; + _sequenceCountArr[13] = 1; + _sequenceCountArr[14] = 1; + _sequenceCountArr[15] = 1; + _canSkip[0] = false; + _canSkip[1] = false; + _canSkip[2] = false; + _canSkip[3] = false; + _canSkip[4] = false; + _canSkip[5] = false; + _canSkip[6] = false; + _canSkip[7] = false; + _canSkip[8] = false; + _canSkip[9] = false; + _canSkip[10] = false; + _canSkip[11] = false; + _canSkip[12] = false; + _canSkip[13] = true; + _canSkip[14] = true; + _canSkip[15] = false; + _itemsCount = 16; + + return -1; +} + +} // End of namespace Gnap diff --git a/engines/gnap/scenes/groupcs.h b/engines/gnap/scenes/groupcs.h new file mode 100644 index 0000000000..58033564ce --- /dev/null +++ b/engines/gnap/scenes/groupcs.h @@ -0,0 +1,122 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GNAP_GROUPCS_H +#define GNAP_GROUPCS_H + +#include "gnap/debugger.h" + +namespace Gnap { + +class GnapEngine; +class CutScene; + +class Scene16: public CutScene { +public: + Scene16(GnapEngine *vm); + virtual ~Scene16() {} + + virtual int init(); +}; + +class Scene471: public CutScene { +public: + Scene471(GnapEngine *vm); + virtual ~Scene471() {} + + virtual int init(); +}; + +class Scene472: public CutScene { +public: + Scene472(GnapEngine *vm); + virtual ~Scene472() {} + + virtual int init(); +}; + +class Scene473: public CutScene { +public: + Scene473(GnapEngine *vm); + virtual ~Scene473() {} + + virtual int init(); +}; + +class Scene474: public CutScene { +public: + Scene474(GnapEngine *vm); + virtual ~Scene474() {} + + virtual int init(); +}; + +class Scene475: public CutScene { +public: + Scene475(GnapEngine *vm); + virtual ~Scene475() {} + + virtual int init(); +}; + +class Scene476: public CutScene { +public: + Scene476(GnapEngine *vm); + virtual ~Scene476() {} + + virtual int init(); +}; + +class Scene477: public CutScene { +public: + Scene477(GnapEngine *vm); + virtual ~Scene477() {} + + virtual int init(); +}; + +class Scene48: public CutScene { +public: + Scene48(GnapEngine *vm); + virtual ~Scene48() {} + + virtual int init(); +}; + +class Scene541: public CutScene { +public: + Scene541(GnapEngine *vm); + virtual ~Scene541() {} + + virtual int init(); +}; + +class Scene542: public CutScene { +public: + Scene542(GnapEngine *vm); + virtual ~Scene542() {} + + virtual int init(); +}; +} // End of namespace Gnap + +#endif // GNAP_GROUPCS_H diff --git a/engines/gnap/scenes/intro.cpp b/engines/gnap/scenes/intro.cpp new file mode 100644 index 0000000000..b4ba2f5201 --- /dev/null +++ b/engines/gnap/scenes/intro.cpp @@ -0,0 +1,182 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "video/avi_decoder.h" + +#include "gnap/gnap.h" +#include "gnap/gamesys.h" +#include "gnap/resource.h" +#include "gnap/scenes/intro.h" + +namespace Gnap { + +SceneIntro::SceneIntro(GnapEngine *vm) : Scene(vm) { +} + +int SceneIntro::init() { + return 0x37C; +} + +void SceneIntro::run() { + const int animIdArr[] = { + 0x356, 0x357, 0x358, 0x35A, 0x35F, + 0x360, 0x361, 0x362, 0x363, 0x364, + 0x365, 0x368, 0x369, 0x36B, 0x378, + 0x36C, 0x36D, 0x36E, 0x36F, 0x370, + 0x371, 0x372, 0x373, 0x374, 0x375, + 0x376, 0x377, 0x378, 0x379, 0x37A, + 0x37B, 0}; + + const int backgroundIdArr[] = { + 0x354, 0x355, 0, 1, 3, + 4, 5, 6, 7, 8, + 7, 9, 0xA, 0xB, 0xC, + 0xD, 0xE, 0xF, 0x10, 0x11, + 0x12, 0x13, 0x17, 0x14, 0x19, + 0x1A, 0x14, 0x15, 0x16, 0x14, + 0x19, 0}; + + GameSys& gameSys = *_vm->_gameSys; + int index = 0; + bool skip = false; + + _vm->hideCursor(); + _vm->_dat->open(1, "musop_n.dat"); + + Video::VideoDecoder *videoDecoder = new Video::AVIDecoder(); + if (!videoDecoder->loadFile("hoffman.avi")) { + delete videoDecoder; + warning("Unable to open video 'hoffman.avi' - Skipping intro"); + return; + } + videoDecoder->start(); + + int vidPosX = (800 - videoDecoder->getWidth()) / 2; + int vidPosY = (600 - videoDecoder->getHeight()) / 2; + bool skipVideo = false; + + _vm->screenEffect(1, 255, 255, 255); + + while (!_vm->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) { + if (videoDecoder->needsUpdate()) { + const Graphics::Surface *frame = videoDecoder->decodeNextFrame(); + if (frame) { + if (frame->format.bytesPerPixel == 1) { + _vm->_system->copyRectToScreen(frame->getPixels(), frame->pitch, vidPosX, vidPosY, frame->w, frame->h); + } else if (frame->format.bytesPerPixel != 4) { + Graphics::Surface *frame1 = frame->convertTo(_vm->_system->getScreenFormat()); + _vm->_system->copyRectToScreen(frame1->getPixels(), frame1->pitch, vidPosX, vidPosY, frame1->w, frame1->h); + frame1->free(); + delete frame1; + } else { + // The intro AVI is played upside down, it's the only video played in the English version + for (uint16 y = 0; y < frame->h / 2; y++) { + uint32 *ptrFrom = (uint32 *)frame->getBasePtr(0, y); + uint32 *ptrTo = (uint32 *)frame->getBasePtr(0, frame->h - y - 1); + for (uint16 x = 0; x < frame->w; x++) { + uint32 t = *ptrFrom; + *ptrFrom = *ptrTo; + *ptrTo = t; + ptrFrom++; + ptrTo++; + } + } + + Graphics::Surface *frame1 = frame->convertTo(_vm->_system->getScreenFormat()); + _vm->_system->copyRectToScreen(frame1->getPixels(), frame1->pitch, vidPosX, vidPosY, frame1->w, frame1->h); + frame1->free(); + delete frame1; + } + _vm->_system->updateScreen(); + } + } + + Common::Event event; + while (g_system->getEventManager()->pollEvent(event)) { + if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || + event.type == Common::EVENT_LBUTTONUP) + skipVideo = true; + } + + _vm->_system->delayMillis(10); + } + + delete videoDecoder; + + gameSys.drawSpriteToBackground(0, 0, backgroundIdArr[index]); + gameSys.insertSequence(0x356, 2, 0, 0, kSeqNone, 0, 0, 0); + gameSys.setAnimation(0x356, 2, 0); + + while (!_vm->_sceneDone) { + _vm->gameUpdateTick(); + + if (gameSys.getAnimationStatus(0) == 2 || skip ) { + skip = false; + gameSys.requestClear2(false); + gameSys.requestClear1(); + if ( index == 11 || index == 1 ) + _vm->screenEffect(0, 0, 0, 0); + + gameSys.setAnimation(0, 0, 0); + if (++index >= 31) + _vm->_sceneDone = true; + else { + gameSys.insertSequence(animIdArr[index], 2, 0, 0, kSeqNone, 0, 0, 0); + if (index == 2) { + _vm->playSound(0x10000, false); + gameSys.insertSequence(0x359, 2, 0, 0, 0, 0, 0, 0); + } else if (index == 3) + gameSys.insertSequence(0x35B, 2, 0, 0, kSeqNone, 0, 0, 0); + else if (index == 12) + gameSys.insertSequence(0x36A, 2, 0, 0, kSeqNone, 0, 0, 0); + + gameSys.drawSpriteToBackground(0, 0, backgroundIdArr[index]); + gameSys.setAnimation(animIdArr[index], 2, 0); + + if (index == 11) + _vm->stopSound(0x10000); + } + } + + if (_vm->isKeyStatus1(Common::KEYCODE_ESCAPE) || _vm->isKeyStatus1(Common::KEYCODE_SPACE) || _vm->isKeyStatus1(Common::KEYCODE_RETURN)) { + _vm->clearKeyStatus1(Common::KEYCODE_ESCAPE); + _vm->clearKeyStatus1(Common::KEYCODE_SPACE); + _vm->clearKeyStatus1(Common::KEYCODE_RETURN); + if (index == 0) { + skip = true; + _vm->stopSound(0x3CF); + } else if (index == 1) + skip = true; + else + _vm->_sceneDone = true; + } + } + + _vm->stopSound(0x10000); + + _vm->_newSceneNum = 1; + _vm->_newCursorValue = 1; + + _vm->_dat->open(1, "stock_n.dat"); +} + +} // End of namespace Gnap diff --git a/engines/gnap/scenes/intro.h b/engines/gnap/scenes/intro.h new file mode 100644 index 0000000000..15aedfc4fc --- /dev/null +++ b/engines/gnap/scenes/intro.h @@ -0,0 +1,47 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GNAP_INTRO_H +#define GNAP_INTRO_H + +#include "gnap/debugger.h" +#include "gnap/scenes/scenecore.h" + +namespace Gnap { + +class GnapEngine; + +class SceneIntro: public Scene { +public: + SceneIntro(GnapEngine *vm); + virtual ~SceneIntro() {} + + virtual int init(); + virtual void updateHotspots() {} + virtual void run(); + virtual void updateAnimations() {} + virtual void updateAnimationsCb() {} +}; + +} // End of namespace Gnap + +#endif // GNAP_INTRO_H diff --git a/engines/gnap/scenes/scenecore.cpp b/engines/gnap/scenes/scenecore.cpp new file mode 100644 index 0000000000..fb6f91c954 --- /dev/null +++ b/engines/gnap/scenes/scenecore.cpp @@ -0,0 +1,740 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gnap/gnap.h" +#include "gnap/gamesys.h" +#include "gnap/resource.h" + +#include "gnap/scenes/scenecore.h" + +#include "gnap/scenes/arcade.h" +#include "gnap/scenes/groupcs.h" +#include "gnap/scenes/group0.h" +#include "gnap/scenes/group1.h" +#include "gnap/scenes/group2.h" +#include "gnap/scenes/group3.h" +#include "gnap/scenes/group4.h" +#include "gnap/scenes/group5.h" +#include "gnap/scenes/intro.h" + +namespace Gnap { + +int GnapEngine::initSceneLogic() { + int backgroundId = -1; + + switch (_currentSceneNum) { + case 0: + _scene = new SceneIntro(this); + backgroundId = _scene->init(); + _gameSys->setScaleValues(0, 500, 1, 1000); + break; + case 1: + _scene = new Scene01(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 2: + _scene = new Scene02(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 3: + _scene = new Scene03(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 4: + _scene = new Scene04(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 5: + _scene = new Scene05(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 6: + _scene = new Scene06(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 7: + _scene = new Scene07(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 8: + _scene = new Scene08(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 9: + _scene = new Scene09(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 10: + _scene = new Scene10(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 11: + _scene = new Scene11(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 12: + _scene = new Scene12(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 13: + _scene = new Scene13(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 14: + _scene = new Scene14(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + break; + case 15: + _scene = new Scene15(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + break; + case 16: + case 47: + case 48: + case 54: + backgroundId = -1; + _gameSys->setScaleValues(0, 500, 1, 1000); + break; + case 17: + _scene = new Scene17(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 18: + _scene = new Scene18(this); + backgroundId = _scene->init(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + _scene->updateHotspots(); + break; + case 19: + _scene = new Scene19(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 20: + _scene = new Scene20(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 21: + _scene = new Scene21(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 22: + _scene = new Scene22(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 23: + _scene = new Scene23(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 24: + _scene = new Scene24(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 136, 11, 10); + break; + case 25: + _scene = new Scene25(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 26: + _scene = new Scene26(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 27: + _scene = new Scene27(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 28: + _scene = new Scene28(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 29: + _scene = new Scene29(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 30: + _scene = new Scene30(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 31: + _scene = new Scene31(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 32: + _scene = new Scene32(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 33: + _scene = new Scene33(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 34: + _scene = new Scene03(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 35: + _scene = new Scene05(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 36: + _scene = new Scene06(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 37: + _scene = new Scene04(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 38: + _scene = new Scene38(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 39: + _scene = new Scene39(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 40: + _scene = new Scene40(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 41: + _scene = new Scene41(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 42: + _scene = new Scene42(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 43: + _scene = new Scene43(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 44: + _scene = new Scene44(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 45: + _scene = new Scene45(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 46: + _scene = new Scene46(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 49: + _scene = new Scene49(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 50: + _scene = new Scene50(this); + backgroundId = _scene->init(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 51: + _scene = new Scene51(this); + backgroundId = _scene->init(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 52: + _scene = new Scene52(this); + backgroundId = _scene->init(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + case 53: + _scene = new Scene53(this); + backgroundId = _scene->init(); + _scene->updateHotspots(); + _gameSys->setScaleValues(0, 500, 1, 1000); + initSceneGrid(21, 146, 11, 10); + break; + } + + return backgroundId; +} + +void GnapEngine::runSceneLogic() { + switch (_currentSceneNum) { + case 0: + _scene->run(); + delete _scene; + if (_newSceneNum == 55) + _newSceneNum = 8; + break; + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + _scene->run(); + delete _scene; + if (_newSceneNum == 55) + _newSceneNum = 4; + break; + case 7: + _scene->run(); + delete _scene; + if (_newSceneNum == 55) + _newSceneNum = 8; + break; + case 8: + _scene->run(); + delete _scene; + if (_newSceneNum == 55) + _newSceneNum = 9; + break; + case 9: + _scene->run(); + delete _scene; + if (_newSceneNum == 55) + _newSceneNum = 10; + break; + case 10: + case 12: + case 13: + _scene->run(); + delete _scene; + if (_newSceneNum == 55) + _newSceneNum = 11; + break; + case 11: + case 15: + _scene->run(); + delete _scene; + if (_newSceneNum == 55) + _newSceneNum = 12; + break; + case 14: + _scene->run(); + delete _scene; + if (_newSceneNum == 55) + _newSceneNum = 13; + break; + case 16: + _scene = new Scene16(this); + _scene->init(); + _newSceneNum = 17; + _newCursorValue = 3; + _scene->run(); + delete _scene; + break; + case 17: + case 18: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + case 28: + case 29: + case 30: + case 31: + _scene->run(); + delete _scene; + if (_newSceneNum == 55) + _newSceneNum = 20; + break; + case 19: + _scene->run(); + delete _scene; + if (_newSceneNum == 55) + _newSceneNum = 19; + break; + case 20: + _scene->run(); + delete _scene; + if (_newSceneNum == 55) + _newSceneNum = 22; + break; + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + case 38: + case 39: + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + _scene->run(); + delete _scene; + if (_newSceneNum == 55) + _newSceneNum = 37; + break; + case 47: + if (_prevSceneNum == 49) { + _scene = new Scene471(this); + _scene->init(); + _newSceneNum = 7; + _newCursorValue = 2; + } else if (_prevSceneNum == 13) { + _scene = new Scene472(this); + _scene->init(); + _newSceneNum = 11; + } else if (!isFlag(kGFPlatypusDisguised) && _prevSceneNum == 2) {//CHECKME + if (isFlag(kGFUnk25)) { + _scene = new Scene473(this); + _scene->init(); + _newSceneNum = 2; + } else { + _scene = new Scene474(this); + _scene->init(); + _newSceneNum = 49; + } + } else if (_prevSceneNum == 21) { + _scene = new Scene475(this); + _scene->init(); + _newSceneNum = 21; + setFlag(kGFTwigTaken); + setFlag(kGFKeysTaken); + } else if (_prevSceneNum == 30) { + _scene = new Scene476(this); + _scene->init(); + _newSceneNum = 26; + } else if (isFlag(kGFPlatypusDisguised) && _cursorValue == 1) { + _scene = new Scene477(this); + _scene->init(); + _newSceneNum = 4; + } + _scene->run(); + delete _scene; + break; + case 48: + _scene = new Scene48(this); + _scene->init(); + _newSceneNum = 33; + _newCursorValue = 4; + _scene->run(); + delete _scene; + break; + case 49: + _scene->run(); + delete _scene; + if (_newSceneNum == 55) + _newSceneNum = 47; + break; + case 50: + _scene->run(); + delete _scene; + _newSceneNum = _prevSceneNum; + break; + case 51: + _scene->run(); + delete _scene; + break; + case 52: + _scene->run(); + delete _scene; + _newSceneNum = _prevSceneNum; + break; + case 53: + _scene->run(); + delete _scene; + if (_newSceneNum == 55) + _newSceneNum = 53; + break; + case 54: + if (_prevSceneNum == 45) { + _scene = new Scene541(this); + _scene->init(); + _newSceneNum = 43; + _scene->run(); + delete _scene; + } else { + _scene = new Scene542(this); + _scene->init(); + _scene->run(); + delete _scene; + _gameDone = true; + } + break; + } +} + +void Scene::playRandomSound(int timerIndex) { + if (!_vm->_timers[timerIndex]) { + _vm->_timers[timerIndex] = _vm->getRandom(40) + 50; + switch (_vm->getRandom(4)) { + case 0: + _vm->playSound(0x1091B, false); + break; + case 1: + _vm->playSound(0x10921, false); + break; + case 2: + _vm->playSound(0x10927, false); + break; + case 3: + _vm->playSound(0x1091D, false); + break; + } + } +} + +bool Scene::clearKeyStatus() { + if (_vm->isKeyStatus1(Common::KEYCODE_ESCAPE)) { + _vm->clearKeyStatus1(Common::KEYCODE_ESCAPE); + _vm->clearKeyStatus1(Common::KEYCODE_UP); + _vm->clearKeyStatus1(Common::KEYCODE_RIGHT); + _vm->clearKeyStatus1(Common::KEYCODE_LEFT); + _vm->clearKeyStatus1(Common::KEYCODE_p); + return true; + } + + if (_vm->isKeyStatus1(Common::KEYCODE_p)) { + _vm->clearKeyStatus1(Common::KEYCODE_p); + _vm->pauseGame(); + _vm->updatePause(); + } + + return false; +} + +/****************************************************************************/ + +CutScene::CutScene(GnapEngine *vm) : Scene(vm) { + _itemsCount = -1; + + for (int i = 0; i < 16; i++) { + _resourceIdArr[i] = -1; + _sequenceCountArr[i] = -1; + _canSkip[i] = false; + } + + for (int i = 0; i < 50; i++) + _sequenceIdArr[i] = -1; +} + +void CutScene::run() { + GameSys& gameSys = *_vm->_gameSys; + + int itemIndex = 0; + int soundId = -1; + int volume = 100; + int duration = 0; + bool skip = false; + + if (_vm->_prevSceneNum == 2) { + soundId = 0x36B; + duration = MAX(1, 300 / _vm->getSequenceTotalDuration(_sequenceIdArr[_itemsCount - 1])); + _vm->_timers[0] = 0; + } + + if (soundId != -1) + _vm->playSound(soundId, false); + + _vm->hideCursor(); + + gameSys.drawSpriteToBackground(0, 0, _resourceIdArr[0]); + + for (int j = 0; j < _sequenceCountArr[0]; ++j) + gameSys.insertSequence(_sequenceIdArr[j], j + 2, 0, 0, kSeqNone, 0, 0, 0); + gameSys.setAnimation(_sequenceIdArr[0], 2, 0); + + _vm->clearKeyStatus1(Common::KEYCODE_ESCAPE); + _vm->clearKeyStatus1(Common::KEYCODE_SPACE); + _vm->clearKeyStatus1(Common::KEYCODE_RETURN); + + _vm->_mouseClickState._left = false; + + int firstSequenceIndex = 0; + while (!_vm->_sceneDone) { + _vm->gameUpdateTick(); + + if (gameSys.getAnimationStatus(0) == 2 || skip) { + skip = false; + gameSys.requestClear2(false); + gameSys.requestClear1(); + gameSys.setAnimation(0, 0, 0); + firstSequenceIndex += _sequenceCountArr[itemIndex++]; + if (itemIndex >= _itemsCount) { + _vm->_sceneDone = true; + } else { + for (int m = 0; m < _sequenceCountArr[itemIndex]; ++m) + gameSys.insertSequence(_sequenceIdArr[firstSequenceIndex + m], m + 2, 0, 0, kSeqNone, 0, 0, 0); + gameSys.drawSpriteToBackground(0, 0, _resourceIdArr[itemIndex]); + gameSys.setAnimation(_sequenceIdArr[firstSequenceIndex], 2, 0); + } + } + + if (_vm->isKeyStatus1(Common::KEYCODE_ESCAPE) || _vm->isKeyStatus1(Common::KEYCODE_SPACE) || _vm->isKeyStatus1(Common::KEYCODE_RETURN)) { + _vm->clearKeyStatus1(Common::KEYCODE_ESCAPE); + _vm->clearKeyStatus1(Common::KEYCODE_SPACE); + _vm->clearKeyStatus1(Common::KEYCODE_RETURN); + if (_canSkip[itemIndex]) + skip = true; + else + _vm->_sceneDone = true; + } + + if (!_vm->_timers[0] && itemIndex == _itemsCount - 1) { + _vm->_timers[0] = 2; + volume = MAX(1, volume - duration); + _vm->setSoundVolume(soundId, volume); + } + } + + if (soundId != -1) + _vm->stopSound(soundId); +} + +} // End of namespace Gnap diff --git a/engines/gnap/scenes/scenecore.h b/engines/gnap/scenes/scenecore.h new file mode 100644 index 0000000000..c54b5a7bc5 --- /dev/null +++ b/engines/gnap/scenes/scenecore.h @@ -0,0 +1,70 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GNAP_SCENECORE_H +#define GNAP_SCENECORE_H + +#include "gnap/debugger.h" + +namespace Gnap { + +class GnapEngine; + +class Scene { +public: + Scene(GnapEngine *vm) : _vm(vm) {}; + virtual ~Scene() {}; + + void playRandomSound(int timerIndex); + bool clearKeyStatus(); + + virtual int init() = 0; + virtual void updateHotspots() = 0; + virtual void run() = 0; + virtual void updateAnimations() = 0; + virtual void updateAnimationsCb() = 0; + +protected: + GnapEngine *_vm; +}; + +class CutScene : public Scene { +public: + CutScene(GnapEngine *vm); + virtual ~CutScene() {}; + + virtual int init() = 0; + void updateHotspots() {} + void run(); + void updateAnimations() {} + void updateAnimationsCb() {} + +protected: + int _itemsCount; + int _resourceIdArr[16]; + int _sequenceCountArr[16]; + int _sequenceIdArr[50]; + bool _canSkip[16]; +}; +} // End of namespace Gnap + +#endif // GNAP_SCENECORE_H diff --git a/engines/gnap/sound.cpp b/engines/gnap/sound.cpp new file mode 100644 index 0000000000..75cfb5555c --- /dev/null +++ b/engines/gnap/sound.cpp @@ -0,0 +1,96 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "gnap/sound.h" +#include "audio/audiostream.h" +#include "audio/decoders/wave.h" + +namespace Gnap { + +SoundMan::SoundMan(GnapEngine *vm) + : _vm(vm) { +} + +SoundMan::~SoundMan() { +} + +void SoundMan::playSound(int resourceId, bool looping) { + SoundItem soundItem; + soundItem._resourceId = resourceId; + + SoundResource *soundResource = _vm->_soundCache->get(resourceId); + Common::MemoryReadStream *stream = new Common::MemoryReadStream(soundResource->_data, soundResource->_size, DisposeAfterUse::NO); + Audio::AudioStream *audioStream = Audio::makeLoopingAudioStream(Audio::makeWAVStream(stream, DisposeAfterUse::YES), looping ? 0 : 1); + + _vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, &soundItem._handle, audioStream); + + _items.push_back(soundItem); + +} + +void SoundMan::stopSound(int resourceId) { + const int index = find(resourceId); + if (index >= 0) { + _vm->_soundCache->release(_items[index]._resourceId); + _vm->_mixer->stopHandle(_items[index]._handle); + _items.remove_at(index); + } +} + +void SoundMan::setSoundVolume(int resourceId, int volume) { + if (resourceId == -1 || volume < 0 || volume > 100) + return; + + const int index = find(resourceId); + int realVol = volume * 2.55; + _vm->_mixer->setChannelVolume(_items[index]._handle, realVol); +} + +bool SoundMan::isSoundPlaying(int resourceId) { + const int index = find(resourceId); + return index >= 0 && _vm->_mixer->isSoundHandleActive(_items[index]._handle); +} + +void SoundMan::stopAll() { + for (int index = 0; index < (int)_items.size(); ++index) { + _vm->_soundCache->release(_items[index]._resourceId); + _vm->_mixer->stopHandle(_items[index]._handle); + } +} + +void SoundMan::update() { + for (int index = 0; index < (int)_items.size(); ++index) + if (!_vm->_mixer->isSoundHandleActive(_items[index]._handle)) { + _vm->_soundCache->release(_items[index]._resourceId); + _items.remove_at(index); + --index; + } +} + +int SoundMan::find(int resourceId) { + for (int index = 0; index < (int)_items.size(); ++index) + if (_items[index]._resourceId == resourceId) + return index; + return -1; +} + +} // End of namespace Gnap diff --git a/engines/gnap/sound.h b/engines/gnap/sound.h new file mode 100644 index 0000000000..de3981245d --- /dev/null +++ b/engines/gnap/sound.h @@ -0,0 +1,57 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef GNAP_SOUND_H +#define GNAP_SOUND_H + +#include "gnap/gnap.h" +#include "gnap/resource.h" +#include "audio/mixer.h" +#include "common/array.h" + +namespace Gnap { + +struct SoundItem { + int _resourceId; + Audio::SoundHandle _handle; +}; + +class SoundMan { +public: + SoundMan(GnapEngine *vm); + ~SoundMan(); + void playSound(int resourceId, bool looping); + void stopSound(int resourceId); + void setSoundVolume(int resourceId, int volume); + bool isSoundPlaying(int resourceId); + void stopAll(); + void update(); +protected: + GnapEngine *_vm; + Common::Array<SoundItem> _items; + + int find(int resourceId); +}; + +} // End of namespace Gnap + +#endif // GNAP_SOUND_H diff --git a/engines/groovie/cell.cpp b/engines/groovie/cell.cpp index 24fac17e44..5fceb8f74e 100644 --- a/engines/groovie/cell.cpp +++ b/engines/groovie/cell.cpp @@ -30,6 +30,7 @@ CellGame::CellGame() { _stack_index = _boardStackPtr = 0; _flag4 = false; _flag2 = false; + _flag1 = false; _coeff3 = 0; _moveCount = 0; diff --git a/engines/groovie/cursor.cpp b/engines/groovie/cursor.cpp index 442f0bfada..d56698095f 100644 --- a/engines/groovie/cursor.cpp +++ b/engines/groovie/cursor.cpp @@ -35,7 +35,7 @@ namespace Groovie { // Cursor Manager GrvCursorMan::GrvCursorMan(OSystem *system) : - _syst(system), _lastTime(0), _current(255), _cursor(NULL) { + _syst(system), _lastTime(0), _current(255), _cursor(NULL), _lastFrame(0) { } GrvCursorMan::~GrvCursorMan() { diff --git a/engines/groovie/font.h b/engines/groovie/font.h index 23e060faf3..5c479b6919 100644 --- a/engines/groovie/font.h +++ b/engines/groovie/font.h @@ -44,7 +44,7 @@ private: int _maxHeight, _maxWidth; struct Glyph { - Glyph() : pixels(0) {} + Glyph() : pixels(0), width(0), height(0), julia(0) {} ~Glyph() { delete[] pixels; } byte width; diff --git a/engines/groovie/graphics.cpp b/engines/groovie/graphics.cpp index e0c198f377..45956416cd 100644 --- a/engines/groovie/graphics.cpp +++ b/engines/groovie/graphics.cpp @@ -31,7 +31,7 @@ namespace Groovie { GraphicsMan::GraphicsMan(GroovieEngine *vm) : - _vm(vm), _changed(false), _fading(0) { + _vm(vm), _changed(false), _fading(0), _fadeStartTime(0) { // Create the game surfaces _foreground.create(640, 320, _vm->_pixelFormat); _background.create(640, 320, _vm->_pixelFormat); diff --git a/engines/groovie/music.cpp b/engines/groovie/music.cpp index cf65e012c8..9244dd7c31 100644 --- a/engines/groovie/music.cpp +++ b/engines/groovie/music.cpp @@ -44,7 +44,8 @@ namespace Groovie { MusicPlayer::MusicPlayer(GroovieEngine *vm) : _vm(vm), _isPlaying(false), _backgroundFileRef(0), _gameVolume(100), - _prevCDtrack(0), _backgroundDelay(0) { + _prevCDtrack(0), _backgroundDelay(0), _fadingStartTime(0), _fadingStartVolume(0), + _fadingEndVolume(0), _fadingDuration(0), _userVolume(0) { } MusicPlayer::~MusicPlayer() { @@ -391,6 +392,8 @@ MusicPlayerXMI::MusicPlayerXMI(GroovieEngine *vm, const Common::String >lName) bool milesAudioEnabled = true; MidiParser::XMidiNewTimbreListProc newTimbreListProc = NULL; + _musicType = 0; + if (milesAudioEnabled) { // 7th Guest uses FAT.AD/FAT.OPL/FAT.MT // 11th Hour uses SAMPLE.AD/SAMPLE.OPL/SAMPLE.MT diff --git a/engines/groovie/music.h b/engines/groovie/music.h index c549527c77..8b46cddc1f 100644 --- a/engines/groovie/music.h +++ b/engines/groovie/music.h @@ -141,7 +141,7 @@ private: // Timbres class Timbre { public: - Timbre() : data(NULL) {} + Timbre() : data(NULL), patch(0), bank(0), size(0) {} byte patch; byte bank; uint32 size; diff --git a/engines/groovie/player.cpp b/engines/groovie/player.cpp index dea32386f2..bbc8918902 100644 --- a/engines/groovie/player.cpp +++ b/engines/groovie/player.cpp @@ -29,7 +29,8 @@ namespace Groovie { VideoPlayer::VideoPlayer(GroovieEngine *vm) : - _vm(vm), _syst(vm->_system), _file(NULL), _audioStream(NULL), _fps(0), _overrideSpeed(false) { + _vm(vm), _syst(vm->_system), _file(NULL), _audioStream(NULL), _fps(0), _overrideSpeed(false), _flags(0), + _begunPlaying(false), _millisBetweenFrames(0), _lastFrameTime(0) { } bool VideoPlayer::load(Common::SeekableReadStream *file, uint16 flags) { diff --git a/engines/hopkins/detection.cpp b/engines/hopkins/detection.cpp index cc1e84f5f8..cfdbf8030c 100644 --- a/engines/hopkins/detection.cpp +++ b/engines/hopkins/detection.cpp @@ -111,7 +111,7 @@ public: } virtual const char *getOriginalCopyright() const { - return "Hopkins FBI (c)1997-2003 MP Entertainment"; + return "Hopkins FBI (C)1997-2003 MP Entertainment"; } virtual bool hasFeature(MetaEngineFeature f) const; diff --git a/engines/lab/detection.cpp b/engines/lab/detection.cpp index 1fd3ca8944..30890b5acf 100644 --- a/engines/lab/detection.cpp +++ b/engines/lab/detection.cpp @@ -127,7 +127,7 @@ public: } virtual const char *getOriginalCopyright() const { - return "Labyrinth of Time (c) 2004 The Wyrmkeep Entertainment Co. and Terra Nova Development"; + return "Labyrinth of Time (C) 2004 The Wyrmkeep Entertainment Co. and Terra Nova Development"; } virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { diff --git a/engines/mads/detection.cpp b/engines/mads/detection.cpp index b3ba60b6d0..4736503a38 100644 --- a/engines/mads/detection.cpp +++ b/engines/mads/detection.cpp @@ -149,7 +149,7 @@ public: } virtual const char *getOriginalCopyright() const { - return "MADS (c)"; + return "MADS (C)"; } virtual bool hasFeature(MetaEngineFeature f) const; diff --git a/engines/mads/dragonsphere/dragonsphere_scenes.cpp b/engines/mads/dragonsphere/dragonsphere_scenes.cpp index a18d03d143..c20eeb72fa 100644 --- a/engines/mads/dragonsphere/dragonsphere_scenes.cpp +++ b/engines/mads/dragonsphere/dragonsphere_scenes.cpp @@ -201,7 +201,7 @@ Common::String DragonsphereScene::formAnimName(char sepChar, int suffixNum) { /*------------------------------------------------------------------------*/ -void SceneInfoDragonsphere::loadCodes(MSurface &depthSurface, int variant) { +void SceneInfoDragonsphere::loadCodes(BaseSurface &depthSurface, int variant) { Common::String ext = Common::String::format(".WW%d", variant); Common::String fileName = Resources::formatName(RESPREFIX_RM, _sceneId, ext); if (!Common::File::exists(fileName)) @@ -217,7 +217,7 @@ void SceneInfoDragonsphere::loadCodes(MSurface &depthSurface, int variant) { f.close(); } -void SceneInfoDragonsphere::loadCodes(MSurface &depthSurface, Common::SeekableReadStream *stream) { +void SceneInfoDragonsphere::loadCodes(BaseSurface &depthSurface, Common::SeekableReadStream *stream) { byte *destP = (byte *)depthSurface.getPixels(); byte *walkMap = new byte[stream->size()]; stream->read(walkMap, stream->size()); diff --git a/engines/mads/dragonsphere/dragonsphere_scenes.h b/engines/mads/dragonsphere/dragonsphere_scenes.h index e9b48715db..22d894b9d9 100644 --- a/engines/mads/dragonsphere/dragonsphere_scenes.h +++ b/engines/mads/dragonsphere/dragonsphere_scenes.h @@ -647,9 +647,9 @@ public: class SceneInfoDragonsphere : public SceneInfo { friend class SceneInfo; protected: - virtual void loadCodes(MSurface &depthSurface, int variant); + virtual void loadCodes(BaseSurface &depthSurface, int variant); - virtual void loadCodes(MSurface &depthSurface, Common::SeekableReadStream *stream); + virtual void loadCodes(BaseSurface &depthSurface, Common::SeekableReadStream *stream); /** * Constructor diff --git a/engines/mads/font.cpp b/engines/mads/font.cpp index 3828c3df8e..684418da91 100644 --- a/engines/mads/font.cpp +++ b/engines/mads/font.cpp @@ -145,7 +145,7 @@ void Font::setColorMode(SelectionMode mode) { } } -int Font::writeString(MSurface *surface, const Common::String &msg, const Common::Point &pt, +int Font::writeString(BaseSurface *surface, const Common::String &msg, const Common::Point &pt, int spaceWidth, int width) { int xEnd; if (width > 0) diff --git a/engines/mads/font.h b/engines/mads/font.h index 486cadcfff..a27de6e283 100644 --- a/engines/mads/font.h +++ b/engines/mads/font.h @@ -86,7 +86,7 @@ public: int maxWidth() const { return _maxWidth; } int getWidth(const Common::String &msg, int spaceWidth = -1); int getHeight() const { return _maxHeight; } - int writeString(MSurface *surface, const Common::String &msg, const Common::Point &pt, + int writeString(BaseSurface *surface, const Common::String &msg, const Common::Point &pt, int spaceWidth = 0, int width = 0); }; diff --git a/engines/mads/messages.cpp b/engines/mads/messages.cpp index 2bee77dae7..773ebd309c 100644 --- a/engines/mads/messages.cpp +++ b/engines/mads/messages.cpp @@ -558,7 +558,7 @@ void TextDisplayList::setDirtyAreas2() { } } -void TextDisplayList::draw(MSurface *s) { +void TextDisplayList::draw(BaseSurface *s) { for (uint idx = 0; idx < size(); ++idx) { TextDisplay &td = (*this)[idx]; if (td._active && (td._expire >= 0)) { diff --git a/engines/mads/messages.h b/engines/mads/messages.h index 2b673a8a4d..ced8c5bb6d 100644 --- a/engines/mads/messages.h +++ b/engines/mads/messages.h @@ -170,7 +170,7 @@ public: * Draw any text in the list to the specified surface * @param surface Surface */ - void draw(MSurface *s); + void draw(BaseSurface *s); /** * Determine dirty areas for active text areas diff --git a/engines/mads/msurface.cpp b/engines/mads/msurface.cpp index 40c69c0f08..8ea9c39bf5 100644 --- a/engines/mads/msurface.cpp +++ b/engines/mads/msurface.cpp @@ -30,9 +30,9 @@ namespace MADS { -MADSEngine *MSurface::_vm = nullptr; +MADSEngine *BaseSurface::_vm = nullptr; -int MSurface::scaleValue(int value, int scale, int err) { +int BaseSurface::scaleValue(int value, int scale, int err) { int scaled = 0; while (value--) { err -= scale; @@ -44,7 +44,7 @@ int MSurface::scaleValue(int value, int scale, int err) { return scaled; } -void MSurface::drawSprite(const Common::Point &pt, SpriteInfo &info, const Common::Rect &clipRect) { +void BaseSurface::drawSprite(const Common::Point &pt, SpriteInfo &info, const Common::Rect &clipRect) { enum { kStatusSkip, kStatusScale, @@ -171,7 +171,7 @@ void MSurface::drawSprite(const Common::Point &pt, SpriteInfo &info, const Commo delete[] scaledLineBuf; } -void MSurface::scrollX(int xAmount) { +void BaseSurface::scrollX(int xAmount) { if (xAmount == 0) return; @@ -203,7 +203,7 @@ void MSurface::scrollX(int xAmount) { markAllDirty(); } -void MSurface::scrollY(int yAmount) { +void BaseSurface::scrollY(int yAmount) { if (yAmount == 0) return; @@ -238,7 +238,7 @@ void MSurface::scrollY(int yAmount) { delete[] tempData; } -void MSurface::translate(Common::Array<RGB6> &palette) { +void BaseSurface::translate(Common::Array<RGB6> &palette) { for (int y = 0; y < this->h; ++y) { byte *pDest = (byte *)getBasePtr(0, y); @@ -251,7 +251,7 @@ void MSurface::translate(Common::Array<RGB6> &palette) { markAllDirty(); } -void MSurface::translate(byte map[PALETTE_COUNT]) { +void BaseSurface::translate(byte map[PALETTE_COUNT]) { for (int y = 0; y < this->h; ++y) { byte *pDest = (byte *)getBasePtr(0, y); @@ -263,7 +263,7 @@ void MSurface::translate(byte map[PALETTE_COUNT]) { markAllDirty(); } -MSurface *MSurface::flipHorizontal() const { +BaseSurface *BaseSurface::flipHorizontal() const { MSurface *dest = new MSurface(this->w, this->h); for (int y = 0; y < this->h; ++y) { @@ -277,7 +277,7 @@ MSurface *MSurface::flipHorizontal() const { return dest; } -void MSurface::copyRectTranslate(MSurface &srcSurface, const byte *paletteMap, +void BaseSurface::copyRectTranslate(BaseSurface &srcSurface, const byte *paletteMap, const Common::Point &destPos, const Common::Rect &srcRect) { // Loop through the lines for (int yCtr = 0; yCtr < srcRect.height(); ++yCtr) { @@ -294,7 +294,7 @@ void MSurface::copyRectTranslate(MSurface &srcSurface, const byte *paletteMap, destPos.y + srcRect.height())); } -void MSurface::copyFrom(MSurface &src, const Common::Point &destPos, int depth, +void BaseSurface::copyFrom(BaseSurface &src, const Common::Point &destPos, int depth, DepthSurface *depthSurface, int scale, bool flipped, int transparentColor) { int destX = destPos.x, destY = destPos.y; int frameWidth = src.w; @@ -337,15 +337,13 @@ void MSurface::copyFrom(MSurface &src, const Common::Point &destPos, int depth, if (destX < 0) { copyRect.left += -destX; destX = 0; - } - else if (destX + copyRect.width() > w) { + } else if (destX + copyRect.width() > w) { copyRect.right -= destX + copyRect.width() - w; } if (destY < 0) { copyRect.top += -destY; destY = 0; - } - else if (destY + copyRect.height() > h) { + } else if (destY + copyRect.height() > h) { copyRect.bottom -= destY + copyRect.height() - h; } diff --git a/engines/mads/msurface.h b/engines/mads/msurface.h index e92770900d..5b5a1d62c1 100644 --- a/engines/mads/msurface.h +++ b/engines/mads/msurface.h @@ -25,7 +25,7 @@ #include "common/scummsys.h" #include "common/rect.h" -#include "graphics/managed_surface.h" +#include "graphics/screen.h" #include "mads/palette.h" namespace MADS { @@ -48,9 +48,11 @@ struct SpriteInfo { }; /* - * MADS graphics surface + * Base MADS surface class. This derivces from Graphics::Screen + * because it has logic we'll need for our own Screen class that + * derives from this one */ -class MSurface : virtual public Graphics::ManagedSurface { +class BaseSurface : public Graphics::Screen { private: /** * Helper method for calculating new dimensions when scaling a sprite @@ -72,17 +74,19 @@ public: /** * Basic constructor */ - MSurface() : Graphics::ManagedSurface() {} + BaseSurface() : Graphics::Screen(0, 0) { + free(); // Free the 0x0 surface allocated by Graphics::Screen + } /** * Constructor for a surface with fixed dimensions */ - MSurface(int width, int height) : Graphics::ManagedSurface(width, height) {} + BaseSurface(int width, int height) : Graphics::Screen(width, height) {} /** * Destructor */ - virtual ~MSurface() {} + virtual ~BaseSurface() {} /** * Return a rect containing the bounds of the surface @@ -142,13 +146,13 @@ public: /** * Create a new surface which is a flipped horizontal copy of the current one */ - MSurface *flipHorizontal() const; + BaseSurface *flipHorizontal() const; /** * Copy an area from one surface to another, translating it using a palette * map as it's done */ - void copyRectTranslate(MSurface &srcSurface, const byte *paletteMap, + void copyRectTranslate(BaseSurface &srcSurface, const byte *paletteMap, const Common::Point &destPos, const Common::Rect &srcRect); /** @@ -161,10 +165,22 @@ public: * @param flipped Flag for whether image is to be flipped * @param transparentColor Transparency palette index */ - void copyFrom(MSurface &src, const Common::Point &destPos, int depth, DepthSurface *depthSurface, + void copyFrom(BaseSurface &src, const Common::Point &destPos, int depth, DepthSurface *depthSurface, int scale, bool flipped, int transparentColor = -1); }; +class MSurface : public BaseSurface { +protected: + /** + * Override the addDirtyRect from Graphics::Screen, since for standard + * surfaces we don't need dirty rects to be tracked + */ + virtual void addDirtyRect(const Common::Rect &r) {} +public: + MSurface() : BaseSurface() {} + MSurface(int width, int height) : BaseSurface(width, height) {} +}; + class DepthSurface : public MSurface { public: /** diff --git a/engines/mads/nebular/nebular_scenes.cpp b/engines/mads/nebular/nebular_scenes.cpp index 40228b4b7d..9502d273ea 100644 --- a/engines/mads/nebular/nebular_scenes.cpp +++ b/engines/mads/nebular/nebular_scenes.cpp @@ -311,7 +311,7 @@ Common::String NebularScene::formAnimName(char sepChar, int suffixNum) { /*------------------------------------------------------------------------*/ -void SceneInfoNebular::loadCodes(MSurface &depthSurface, int variant) { +void SceneInfoNebular::loadCodes(BaseSurface &depthSurface, int variant) { File f(Resources::formatName(RESPREFIX_RM, _sceneId, ".DAT")); MadsPack codesPack(&f); Common::SeekableReadStream *stream = codesPack.getItemStream(variant + 1); @@ -322,7 +322,7 @@ void SceneInfoNebular::loadCodes(MSurface &depthSurface, int variant) { f.close(); } -void SceneInfoNebular::loadCodes(MSurface &depthSurface, Common::SeekableReadStream *stream) { +void SceneInfoNebular::loadCodes(BaseSurface &depthSurface, Common::SeekableReadStream *stream) { byte *destP = (byte *)depthSurface.getPixels(); byte *endP = (byte *)depthSurface.getBasePtr(0, depthSurface.h); diff --git a/engines/mads/nebular/nebular_scenes.h b/engines/mads/nebular/nebular_scenes.h index 58a6d1c98f..b600c6dbe1 100644 --- a/engines/mads/nebular/nebular_scenes.h +++ b/engines/mads/nebular/nebular_scenes.h @@ -1373,9 +1373,9 @@ public: class SceneInfoNebular : public SceneInfo { friend class SceneInfo; protected: - virtual void loadCodes(MSurface &depthSurface, int variant); + virtual void loadCodes(BaseSurface &depthSurface, int variant); - virtual void loadCodes(MSurface &depthSurface, Common::SeekableReadStream *stream); + virtual void loadCodes(BaseSurface &depthSurface, Common::SeekableReadStream *stream); /** * Constructor diff --git a/engines/mads/phantom/phantom_scenes.cpp b/engines/mads/phantom/phantom_scenes.cpp index 7ef627ceeb..bfb521e369 100644 --- a/engines/mads/phantom/phantom_scenes.cpp +++ b/engines/mads/phantom/phantom_scenes.cpp @@ -174,7 +174,7 @@ Common::String PhantomScene::formAnimName(char sepChar, int suffixNum) { /*------------------------------------------------------------------------*/ -void SceneInfoPhantom::loadCodes(MSurface &depthSurface, int variant) { +void SceneInfoPhantom::loadCodes(BaseSurface &depthSurface, int variant) { Common::String ext = Common::String::format(".WW%d", variant); Common::String fileName = Resources::formatName(RESPREFIX_RM, _sceneId, ext); if (!Common::File::exists(fileName)) @@ -190,7 +190,7 @@ void SceneInfoPhantom::loadCodes(MSurface &depthSurface, int variant) { f.close(); } -void SceneInfoPhantom::loadCodes(MSurface &depthSurface, Common::SeekableReadStream *stream) { +void SceneInfoPhantom::loadCodes(BaseSurface &depthSurface, Common::SeekableReadStream *stream) { byte *destP = (byte *)depthSurface.getPixels(); byte *walkMap = new byte[stream->size()]; stream->read(walkMap, stream->size()); diff --git a/engines/mads/phantom/phantom_scenes.h b/engines/mads/phantom/phantom_scenes.h index a6a8395a2c..6b7ab697f3 100644 --- a/engines/mads/phantom/phantom_scenes.h +++ b/engines/mads/phantom/phantom_scenes.h @@ -474,9 +474,9 @@ public: class SceneInfoPhantom : public SceneInfo { friend class SceneInfo; protected: - virtual void loadCodes(MSurface &depthSurface, int variant); + virtual void loadCodes(BaseSurface &depthSurface, int variant); - virtual void loadCodes(MSurface &depthSurface, Common::SeekableReadStream *stream); + virtual void loadCodes(BaseSurface &depthSurface, Common::SeekableReadStream *stream); /** * Constructor diff --git a/engines/mads/scene_data.cpp b/engines/mads/scene_data.cpp index 5323178ec7..21fd4f9026 100644 --- a/engines/mads/scene_data.cpp +++ b/engines/mads/scene_data.cpp @@ -128,7 +128,7 @@ SceneInfo *SceneInfo::init(MADSEngine *vm) { } void SceneInfo::load(int sceneId, int variant, const Common::String &resName, - int flags, DepthSurface &depthSurface, MSurface &bgSurface) { + int flags, DepthSurface &depthSurface, BaseSurface &bgSurface) { bool sceneFlag = sceneId >= 0; // Figure out the resource to use @@ -299,7 +299,7 @@ void SceneInfo::load(int sceneId, int variant, const Common::String &resName, } } -void SceneInfo::loadPalette(int sceneId, int artFileNum, const Common::String &resName, int flags, MSurface &bgSurface) { +void SceneInfo::loadPalette(int sceneId, int artFileNum, const Common::String &resName, int flags, BaseSurface &bgSurface) { bool sceneFlag = sceneId >= 0; Common::String resourceName; bool isV2 = (_vm->getGameID() != GType_RexNebular); @@ -351,7 +351,7 @@ void SceneInfo::loadPalette(int sceneId, int artFileNum, const Common::String &r } } -void SceneInfo::loadMadsV1Background(int sceneId, const Common::String &resName, int flags, MSurface &bgSurface) { +void SceneInfo::loadMadsV1Background(int sceneId, const Common::String &resName, int flags, BaseSurface &bgSurface) { bool sceneFlag = sceneId >= 0; Common::String resourceName; Common::SeekableReadStream *stream; @@ -397,7 +397,7 @@ void SceneInfo::loadMadsV1Background(int sceneId, const Common::String &resName, artFile.close(); } -void SceneInfo::loadMadsV2Background(int sceneId, const Common::String &resName, int flags, MSurface &bgSurface) { +void SceneInfo::loadMadsV2Background(int sceneId, const Common::String &resName, int flags, BaseSurface &bgSurface) { Common::String tileMapResourceName = Resources::formatName(RESPREFIX_RM, sceneId, ".MM"); File tileMapFile(tileMapResourceName); MadsPack tileMapPack(&tileMapFile); diff --git a/engines/mads/scene_data.h b/engines/mads/scene_data.h index 41a08f74eb..a28c42c5ab 100644 --- a/engines/mads/scene_data.h +++ b/engines/mads/scene_data.h @@ -189,36 +189,36 @@ public: * loads the data */ void load(int sceneId, int variant, const Common::String &resName, int flags, - DepthSurface &depthSurface, MSurface &bgSurface); + DepthSurface &depthSurface, BaseSurface &bgSurface); /** * Loads the palette for a scene */ - void loadPalette(int sceneId, int artFileNum, const Common::String &resName, int flags, MSurface &bgSurface); + void loadPalette(int sceneId, int artFileNum, const Common::String &resName, int flags, BaseSurface &bgSurface); /** * Loads a V1 game background */ - void loadMadsV1Background(int sceneId, const Common::String &resName, int flags, MSurface &bgSurface); + void loadMadsV1Background(int sceneId, const Common::String &resName, int flags, BaseSurface &bgSurface); /** * Loads a V2 game background */ - void loadMadsV2Background(int sceneId, const Common::String &resName, int flags, MSurface &bgSurface); + void loadMadsV2Background(int sceneId, const Common::String &resName, int flags, BaseSurface &bgSurface); /** * Loads the given surface with depth information of a given scene * @param depthSurface Depth/walk surface * @param variant Variant number to load */ - virtual void loadCodes(MSurface &depthSurface, int variant) = 0; + virtual void loadCodes(BaseSurface &depthSurface, int variant) = 0; /** * Loads the given surface with depth information of a given scene * @param depthSurface Depth/walk surface * @param stream Stream to load the data from */ - virtual void loadCodes(MSurface &depthSurface, Common::SeekableReadStream *stream) = 0; + virtual void loadCodes(BaseSurface &depthSurface, Common::SeekableReadStream *stream) = 0; }; } // End of namespace MADS diff --git a/engines/mads/screen.cpp b/engines/mads/screen.cpp index 05f9de61e2..b17b6e93b8 100644 --- a/engines/mads/screen.cpp +++ b/engines/mads/screen.cpp @@ -201,7 +201,7 @@ void DirtyAreas::mergeAreas(int idx1, int idx2) { da1._textActive = true; } -void DirtyAreas::copy(MSurface *srcSurface, MSurface *destSurface, const Common::Point &posAdjust) { +void DirtyAreas::copy(BaseSurface *srcSurface, BaseSurface *destSurface, const Common::Point &posAdjust) { for (uint i = 0; i < size(); ++i) { const Common::Rect &srcBounds = (*this)[i]._bounds; @@ -555,7 +555,7 @@ void ScreenObjects::synchronize(Common::Serializer &s) { /*------------------------------------------------------------------------*/ -Screen::Screen(): Graphics::Screen(), MSurface() { +Screen::Screen(): BaseSurface() { // Create the screen surface separately on another surface, since the screen // surface will be subject to change as the clipping area is altered _rawSurface.create(MADS_SCREEN_WIDTH, MADS_SCREEN_HEIGHT); diff --git a/engines/mads/screen.h b/engines/mads/screen.h index 626080580e..eeb15453f8 100644 --- a/engines/mads/screen.h +++ b/engines/mads/screen.h @@ -25,7 +25,6 @@ #include "common/scummsys.h" #include "common/array.h" -#include "graphics/screen.h" #include "mads/msurface.h" #include "mads/action.h" @@ -118,7 +117,7 @@ public: * @param destSurface Dest surface * @param posAdjust Position adjustment */ - void copy(MSurface *srcSurface, MSurface *destSurface, const Common::Point &posAdjust); + void copy(BaseSurface *srcSurface, BaseSurface *destSurface, const Common::Point &posAdjust); /** * Use the lsit of dirty areas to copy areas of the screen surface to @@ -129,7 +128,6 @@ public: void reset(); }; - class ScreenObject { public: bool _active; @@ -208,7 +206,7 @@ public: void synchronize(Common::Serializer &s); }; -class Screen : virtual public Graphics::Screen, virtual public MSurface { +class Screen : public BaseSurface { private: uint16 _random; MSurface _rawSurface; diff --git a/engines/mads/sprites.cpp b/engines/mads/sprites.cpp index fc8ddf22d2..84060ccdfe 100644 --- a/engines/mads/sprites.cpp +++ b/engines/mads/sprites.cpp @@ -337,7 +337,7 @@ void SpriteSlots::drawSprites(MSurface *s) { s->copyFrom(*sprite, Common::Point(xp, yp), slot._depth, &scene._depthSurface, -1, flipped, sprite->getTransparencyIndex()); } else { - MSurface *spr = sprite; + BaseSurface *spr = sprite; if (flipped) { // Create a flipped copy of the sprite temporarily spr = sprite->flipHorizontal(); diff --git a/engines/mads/user_interface.cpp b/engines/mads/user_interface.cpp index 8f7cb0a24b..204f71fe43 100644 --- a/engines/mads/user_interface.cpp +++ b/engines/mads/user_interface.cpp @@ -161,7 +161,7 @@ void UISlots::draw(bool updateFlag, bool delFlag) { MSprite *sprite = asset->getFrame(frameNumber - 1); if (flipped) { - MSurface *spr = sprite->flipHorizontal(); + BaseSurface *spr = sprite->flipHorizontal(); userInterface.mergeFrom(spr, spr->getBounds(), slot._position, sprite->getTransparencyIndex()); spr->free(); @@ -429,7 +429,7 @@ void UserInterface::drawTextElements() { } } -void UserInterface::mergeFrom(MSurface *src, const Common::Rect &srcBounds, +void UserInterface::mergeFrom(BaseSurface *src, const Common::Rect &srcBounds, const Common::Point &destPos, int transparencyIndex) { // Validation of the rectangle and position int destX = destPos.x, destY = destPos.y; diff --git a/engines/mads/user_interface.h b/engines/mads/user_interface.h index 9232dc1bb1..6c9485998a 100644 --- a/engines/mads/user_interface.h +++ b/engines/mads/user_interface.h @@ -238,7 +238,7 @@ public: * @param destPos Destination position to draw in current surface * @param transparencyIndex Transparency color */ - void mergeFrom(MSurface *src, const Common::Rect &srcBounds, const Common::Point &destPos, + void mergeFrom(BaseSurface *src, const Common::Rect &srcBounds, const Common::Point &destPos, int transparencyIndex = -1); /** diff --git a/engines/parallaction/adlib.cpp b/engines/parallaction/adlib.cpp index 568ad190aa..a981a5553b 100644 --- a/engines/parallaction/adlib.cpp +++ b/engines/parallaction/adlib.cpp @@ -277,6 +277,15 @@ public: _channels[i].init(this, i); _isOpen = false; + + _opl = NULL; + memset(_voices, 0, sizeof(_voices)); + + _lastVoice = 0; + _percussionMask = 0; + + _adlibTimerProc = NULL; + _adlibTimerParam = NULL; } int open(); diff --git a/engines/parallaction/balloons.cpp b/engines/parallaction/balloons.cpp index 234abff59a..c0a516eb0f 100644 --- a/engines/parallaction/balloons.cpp +++ b/engines/parallaction/balloons.cpp @@ -55,7 +55,7 @@ protected: } public: - WrappedLineFormatter(Font *font) : _font(font) { } + WrappedLineFormatter(Font *font) : _font(font), _lines(0), _lineWidth(0) { } virtual ~WrappedLineFormatter() { } virtual void calc(const Common::String &text, uint16 maxwidth) { @@ -136,7 +136,7 @@ protected: } public: - StringExtent_NS(Font *font) : WrappedLineFormatter(font) { } + StringExtent_NS(Font *font) : WrappedLineFormatter(font), _width(0), _height(0) { } uint width() const { return _width; } uint height() const { return _height; } @@ -189,7 +189,8 @@ protected: } public: - StringWriter_NS(Parallaction_ns *vm, Font *font) : WrappedLineFormatter(font), _vm(vm) { } + StringWriter_NS(Parallaction_ns *vm, Font *font) : WrappedLineFormatter(font), _vm(vm), + _width(0), _height(0), _color(0), _surf(NULL) { } void write(const Common::String &text, uint maxWidth, byte color, Graphics::Surface *surf) { StringExtent_NS se(_font); @@ -464,7 +465,7 @@ protected: } public: - StringExtent_BR(Font *font) : WrappedLineFormatter(font) { } + StringExtent_BR(Font *font) : WrappedLineFormatter(font), _width(0), _height(0) { } uint width() const { return _width; } uint height() const { return _height; } @@ -480,7 +481,8 @@ class StringWriter_BR : public WrappedLineFormatter { Graphics::Surface *_surf; protected: - StringWriter_BR(Font *font, byte color) : WrappedLineFormatter(font) { + StringWriter_BR(Font *font, byte color) : WrappedLineFormatter(font), _width(0), _height(0), + _color(color), _x(0), _y(0), _surf(NULL) { } @@ -504,7 +506,8 @@ protected: } public: - StringWriter_BR(Font *font) : WrappedLineFormatter(font) { } + StringWriter_BR(Font *font) : WrappedLineFormatter(font), _width(0), _height(0), + _color(0), _x(0), _y(0), _surf(NULL) { } void write(const Common::String &text, uint maxWidth, byte color, Graphics::Surface *surf) { StringExtent_BR se(_font); diff --git a/engines/parallaction/debug.cpp b/engines/parallaction/debug.cpp index a7087c64d7..0bf2babb5b 100644 --- a/engines/parallaction/debug.cpp +++ b/engines/parallaction/debug.cpp @@ -32,6 +32,7 @@ namespace Parallaction { Debugger::Debugger(Parallaction *vm) : GUI::Debugger() { _vm = vm; + _mouseState = MOUSE_ENABLED_SHOW; registerCmd("continue", WRAP_METHOD(Debugger, cmdExit)); registerCmd("location", WRAP_METHOD(Debugger, Cmd_Location)); diff --git a/engines/parallaction/dialogue.cpp b/engines/parallaction/dialogue.cpp index 62e2152816..771715b95e 100644 --- a/engines/parallaction/dialogue.cpp +++ b/engines/parallaction/dialogue.cpp @@ -140,6 +140,19 @@ DialogueManager::DialogueManager(Parallaction *vm, ZonePtr z) : _vm(vm), _z(z) { _cmdList = 0; _answerId = 0; + + _faceId = 0; + + _q = NULL; + memset(_visAnswers, 0, sizeof(_visAnswers)); + _numVisAnswers = 0; + + _selection = _oldSelection = 0; + + _isKeyDown = false; + _downKey = 0; + + _mouseButtons = 0; } void DialogueManager::start() { @@ -412,7 +425,8 @@ protected: } public: - DialogueManager_ns(Parallaction_ns *vm, ZonePtr z) : DialogueManager(vm, z), _vm(vm) { + DialogueManager_ns(Parallaction_ns *vm, ZonePtr z) : DialogueManager(vm, z), _vm(vm), + _passwordChanged(false), _askPassword(false) { _ballonPos._questionBalloon = Common::Point(140, 10); _ballonPos._questionChar = Common::Point(190, 80); _ballonPos._answerChar = Common::Point(10, 80); diff --git a/engines/parallaction/disk_br.cpp b/engines/parallaction/disk_br.cpp index 7458065b8c..af2d2b82e0 100644 --- a/engines/parallaction/disk_br.cpp +++ b/engines/parallaction/disk_br.cpp @@ -762,14 +762,11 @@ Common::String AmigaDisk_br::selectArchive(const Common::String& name) { } -Disk_br::Disk_br(Parallaction *vm) : _vm(vm), _baseDir(0) { - +Disk_br::Disk_br(Parallaction *vm) : _vm(vm), _baseDir(0), _language(0) { } Disk_br::~Disk_br() { _sset.clear(); } - - } // namespace Parallaction diff --git a/engines/parallaction/disk_ns.cpp b/engines/parallaction/disk_ns.cpp index 28e61b04f9..c25236acbd 100644 --- a/engines/parallaction/disk_ns.cpp +++ b/engines/parallaction/disk_ns.cpp @@ -238,7 +238,7 @@ void Disk_ns::setLanguage(uint16 language) { #pragma mark - -DosDisk_ns::DosDisk_ns(Parallaction* vm) : Disk_ns(vm) { +DosDisk_ns::DosDisk_ns(Parallaction* vm) : Disk_ns(vm), _gfx(NULL) { } diff --git a/engines/parallaction/exec.cpp b/engines/parallaction/exec.cpp index ceba072173..3d4e9bd803 100644 --- a/engines/parallaction/exec.cpp +++ b/engines/parallaction/exec.cpp @@ -81,7 +81,7 @@ void ProgramExec::runScripts(ProgramList::iterator first, ProgramList::iterator return; } -ProgramExec::ProgramExec() : _modCounter(0) { +ProgramExec::ProgramExec() : _modCounter(0), _instructionNames(NULL) { } diff --git a/engines/parallaction/font.cpp b/engines/parallaction/font.cpp index 57b04deb12..f1c3b89ae8 100644 --- a/engines/parallaction/font.cpp +++ b/engines/parallaction/font.cpp @@ -302,7 +302,7 @@ protected: } public: - DosFont(Cnv *cnv) : _data(cnv), _pitch(cnv->_width) { + DosFont(Cnv *cnv) : _data(cnv), _pitch(cnv->_width), _cp(NULL), _bufPitch(0) { } ~DosFont() { diff --git a/engines/parallaction/gfxbase.cpp b/engines/parallaction/gfxbase.cpp index f1499f7782..819804bfe7 100644 --- a/engines/parallaction/gfxbase.cpp +++ b/engines/parallaction/gfxbase.cpp @@ -32,7 +32,8 @@ namespace Parallaction { GfxObj::GfxObj(uint objType, Frames *frames, const char* name) : _frames(frames), x(0), y(0), z(0), _prog(0), _flags(0), - type(objType), frame(0), layer(3), scale(100), _hasMask(false), _hasPath(false) { + type(objType), frame(0), layer(3), scale(100), _hasMask(false), _hasPath(false), + transparentKey(0), _maskId(0), _pathId(0) { if (name) { _name = strdup(name); diff --git a/engines/parallaction/graphics.cpp b/engines/parallaction/graphics.cpp index 06b315016a..162671b68a 100644 --- a/engines/parallaction/graphics.cpp +++ b/engines/parallaction/graphics.cpp @@ -743,6 +743,8 @@ Gfx::Gfx(Parallaction* vm) : _nextProjectorPos = 0; _hbCircleRadius = 0; + _overlayMode = false; + _unpackedBitmap = new byte[MAXIMUM_UNPACKED_BITMAP_SIZE]; assert(_unpackedBitmap); diff --git a/engines/parallaction/graphics.h b/engines/parallaction/graphics.h index 55e2bbca84..03b4dd97ef 100644 --- a/engines/parallaction/graphics.h +++ b/engines/parallaction/graphics.h @@ -57,7 +57,7 @@ protected: public: - Font() {} + Font() : _color(0) {} virtual ~Font() {} virtual void setColor(byte color) { diff --git a/engines/parallaction/gui_br.cpp b/engines/parallaction/gui_br.cpp index ae3d136b0e..17d759d26f 100644 --- a/engines/parallaction/gui_br.cpp +++ b/engines/parallaction/gui_br.cpp @@ -43,7 +43,8 @@ protected: int _fadeSteps; public: - SplashInputState_BR(Parallaction *vm, const Common::String &name, MenuInputHelper *helper) : MenuInputState(name, helper), _vm(vm) { + SplashInputState_BR(Parallaction *vm, const Common::String &name, MenuInputHelper *helper) : MenuInputState(name, helper), _vm(vm), + _timeOut(0), _startTime(0), _fadeSteps(0) { } virtual MenuInputState* run() { @@ -382,6 +383,9 @@ public: _menuObj->getRect(0, _menuRect); _cellW = _menuRect.width() / 3; _cellH = _menuRect.height() / 2; + + _menuObjId = _mscMenuObjId = _sfxMenuObjId = 0; + _sfxStatus = _mscStatus = 0; } ~IngameMenuInputState_BR() { diff --git a/engines/parallaction/gui_ns.cpp b/engines/parallaction/gui_ns.cpp index 3d977c9e51..3c312c4f2d 100644 --- a/engines/parallaction/gui_ns.cpp +++ b/engines/parallaction/gui_ns.cpp @@ -43,7 +43,8 @@ protected: Parallaction *_vm; public: - SplashInputState_NS(Parallaction *vm, const Common::String &name, MenuInputHelper *helper) : MenuInputState(name, helper), _vm(vm) { + SplashInputState_NS(Parallaction *vm, const Common::String &name, MenuInputHelper *helper) : MenuInputState(name, helper), _vm(vm), + _timeOut(0), _startTime(0) { } virtual MenuInputState* run() { @@ -298,7 +299,7 @@ class LoadGameInputState_NS : public MenuInputState { Parallaction *_vm; public: - LoadGameInputState_NS(Parallaction *vm, MenuInputHelper *helper) : MenuInputState("loadgame", helper), _vm(vm) { } + LoadGameInputState_NS(Parallaction *vm, MenuInputHelper *helper) : MenuInputState("loadgame", helper), _vm(vm), _result(false) { } virtual MenuInputState* run() { if (!_result) { @@ -477,6 +478,11 @@ public: _labels[0] = 0; _labels[1] = 0; + _fail = false; + _len = 0; + _startTime = 0; + _state = 0; + _codeSelectBlocks[0] = Common::Rect( 111, 129, 127, 153 ); // na _codeSelectBlocks[1] = Common::Rect( 128, 120, 144, 144 ); // wa _codeSelectBlocks[2] = Common::Rect( 145, 111, 161, 135 ); // ra @@ -689,6 +695,9 @@ public: ShowCreditsInputState_NS(Parallaction *vm, MenuInputHelper *helper) : MenuInputState("showcredits", helper), _vm(vm) { _labels[0] = 0; _labels[1] = 0; + + _current = 0; + _startTime = 0; } ~ShowCreditsInputState_NS() { @@ -827,6 +836,8 @@ public: _labels[1] = 0; _labels[2] = 0; _labels[3] = 0; + + _allPartsComplete = false; } void destroyLabels() { diff --git a/engines/parallaction/input.cpp b/engines/parallaction/input.cpp index 290af339bb..2cd85d7f1c 100644 --- a/engines/parallaction/input.cpp +++ b/engines/parallaction/input.cpp @@ -70,6 +70,9 @@ Input::Input(Parallaction *vm) : _vm(vm) { _mouseButtons = 0; _delayedActionZone.reset(); + _inputMode = 0; + _hasKeyPressEvent = false; + _dinoCursor = 0; _dougCursor = 0; _donnaCursor = 0; diff --git a/engines/parallaction/objects.cpp b/engines/parallaction/objects.cpp index 8f895f1532..950d62a841 100644 --- a/engines/parallaction/objects.cpp +++ b/engines/parallaction/objects.cpp @@ -145,6 +145,8 @@ Program::Program() { _locals = new LocalVariable[NUM_LOCALS]; _numLocals = 0; _status = kProgramIdle; + _ip = 0; + _loopStart = 0; } Program::~Program() { @@ -259,6 +261,8 @@ Answer::Answer() { _noFlags = 0; _yesFlags = 0; _hasCounterCondition = false; + _counterValue = 0; + _counterOp = 0; } bool Answer::textIsNull() { @@ -298,6 +302,7 @@ Instruction::Instruction() { // common _immediate = 0; + _endif = 0; // BRA specific _text = 0; diff --git a/engines/parallaction/parallaction_ns.cpp b/engines/parallaction/parallaction_ns.cpp index 144c2b3a98..5fd6d87985 100644 --- a/engines/parallaction/parallaction_ns.cpp +++ b/engines/parallaction/parallaction_ns.cpp @@ -395,7 +395,7 @@ void Parallaction_ns::changeLocation() { changeCharacter(locname.character()); } - strcpy(g_saveData1, locname.location()); + Common::strlcpy(g_saveData1, locname.location(), 30); parseLocation(g_saveData1); if (_location._startPosition.x != -1000) { diff --git a/engines/parallaction/parser.cpp b/engines/parallaction/parser.cpp index c37cee692e..725a8b5996 100644 --- a/engines/parallaction/parser.cpp +++ b/engines/parallaction/parser.cpp @@ -226,6 +226,7 @@ uint16 Script::readLineToken(bool errorOnEOF) { void Parser::reset() { _currentOpcodes = 0; _currentStatements = 0; + _lookup = 0; _statements.clear(); _opcodes.clear(); diff --git a/engines/parallaction/parser.h b/engines/parallaction/parser.h index 7b77f58eb0..e7ae7dcc36 100644 --- a/engines/parallaction/parser.h +++ b/engines/parallaction/parser.h @@ -405,7 +405,7 @@ protected: virtual void parseRValue(ScriptVar &var, const char *str); public: - ProgramParser_br(Parallaction_br *vm) : ProgramParser_ns((Parallaction_ns*)vm), _vm(vm) { + ProgramParser_br(Parallaction_br *vm) : ProgramParser_ns((Parallaction_ns*)vm), _vm(vm), _openIfStatement(0) { } virtual void init(); diff --git a/engines/parallaction/saveload.cpp b/engines/parallaction/saveload.cpp index eff088d5ee..0f4ceae7a5 100644 --- a/engines/parallaction/saveload.cpp +++ b/engines/parallaction/saveload.cpp @@ -93,7 +93,7 @@ void SaveLoad_ns::doLoadGame(uint16 slot) { uint16 _si; for (_si = 0; _si < _vm->_numLocations; _si++) { s = f->readLine(); - strcpy(_vm->_locationNames[_si], s.c_str()); + Common::strlcpy(_vm->_locationNames[_si], s.c_str(), 32); s = f->readLine(); _vm->_localFlags[_si] = atoi(s.c_str()); diff --git a/engines/parallaction/sound_br.cpp b/engines/parallaction/sound_br.cpp index d13b318ace..0147d3cd90 100644 --- a/engines/parallaction/sound_br.cpp +++ b/engines/parallaction/sound_br.cpp @@ -86,7 +86,7 @@ protected: byte *_trackEnd; public: - MidiParser_MSC() : byte_11C5A(false) { + MidiParser_MSC() : byte_11C5A(false), _beats(0), _lastEvent(0), _trackEnd(NULL) { } }; @@ -467,6 +467,11 @@ SoundMan_br::SoundMan_br(Parallaction_br *vm) : _vm(vm) { _musicEnabled = true; _sfxEnabled = true; + + _sfxLooping = false; + _sfxVolume = 0; + _sfxRate = 0; + _sfxChannel = 0; } SoundMan_br::~SoundMan_br() { diff --git a/engines/parallaction/sound_ns.cpp b/engines/parallaction/sound_ns.cpp index 692389b490..6073f82b82 100644 --- a/engines/parallaction/sound_ns.cpp +++ b/engines/parallaction/sound_ns.cpp @@ -323,6 +323,11 @@ void AmigaSoundMan_ns::playLocationMusic(const char *location) { SoundMan_ns::SoundMan_ns(Parallaction_ns *vm) : _vm(vm) { _mixer = _vm->_mixer; + _sfxLooping = false; + _sfxVolume = 0; + _sfxRate = 0; + _sfxChannel = 0; + _musicType = 0; } void SoundMan_ns::setMusicVolume(int value) { @@ -330,7 +335,7 @@ void SoundMan_ns::setMusicVolume(int value) { } void SoundMan_ns::setMusicFile(const char *filename) { - strcpy(_musicFile, filename); + Common::strlcpy(_musicFile, filename, PATH_LEN); } void SoundMan_ns::execute(int command, const char *parm = 0) { diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index 1661f92cfe..51fb52bb21 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -2565,9 +2565,14 @@ bool Console::cmdVMVars(int argc, const char **argv) { case 1: case 2: case 3: { - // for global, local, temp and param, we need an index if (argc < 3) { - debugPrintf("Variable number must be specified for requested type\n"); + for (int i = 0; i < s->variablesMax[varType]; ++i) { + curValue = &s->variables[varType][i]; + debugPrintf("%s var %d == %04x:%04x", varNames[varType], i, PRINT_REG(*curValue)); + printBasicVarInfo(*curValue); + debugPrintf("\n"); + } + return true; } if (argc > 4) { diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index 3463d05e77..0ede307e6b 100644 --- a/engines/sci/engine/kernel_tables.h +++ b/engines/sci/engine/kernel_tables.h @@ -465,7 +465,7 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(CelWide), SIG_SCI16, SIGFOR_ALL, "ii(i)", NULL, kCelWide_workarounds }, #ifdef ENABLE_SCI32 { "CelHigh", kCelHigh32, SIG_SCI32, SIGFOR_ALL, "iii", NULL, NULL }, - { "CelWide", kCelWide32, SIG_SCI32, SIGFOR_ALL, "iii", NULL, NULL }, + { "CelWide", kCelWide32, SIG_SCI32, SIGFOR_ALL, "iii", NULL, kCelWide_workarounds }, #endif { MAP_CALL(CheckFreeSpace), SIG_SCI32, SIGFOR_ALL, "r.*", NULL, NULL }, { MAP_CALL(CheckFreeSpace), SIG_SCI11, SIGFOR_ALL, "r(i)", NULL, NULL }, diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp index d5540f72b1..6c51ec4284 100644 --- a/engines/sci/engine/kgraphics32.cpp +++ b/engines/sci/engine/kgraphics32.cpp @@ -216,6 +216,9 @@ reg_t kTextSize32(EngineState *s, int argc, reg_t *argv) { g_sci->_gfxText32->setFont(argv[2].toUint16()); reg_t *rect = s->_segMan->derefRegPtr(argv[0], 4); + if (rect == nullptr) { + error("kTextSize: %04x:%04x cannot be dereferenced", PRINT_REG(argv[0])); + } Common::String text = s->_segMan->getString(argv[1]); int16 maxWidth = argc > 3 ? argv[3].toSint16() : 0; diff --git a/engines/sci/engine/kscripts.cpp b/engines/sci/engine/kscripts.cpp index 303de079aa..6fd130bceb 100644 --- a/engines/sci/engine/kscripts.cpp +++ b/engines/sci/engine/kscripts.cpp @@ -260,9 +260,6 @@ reg_t kDisposeScript(EngineState *s, int argc, reg_t *argv) { if (argc != 2) { return s->r_acc; } else { - // This exists in the KQ5CD and GK1 interpreter. We know it is used - // when GK1 starts up, before the Sierra logo. - warning("kDisposeScript called with 2 parameters, still untested"); return argv[1]; } } diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index 0cc1e752e1..302f046458 100644 --- a/engines/sci/engine/savegame.cpp +++ b/engines/sci/engine/savegame.cpp @@ -806,8 +806,6 @@ void GfxPalette32::saveLoadWithSerializer(Common::Serializer &s) { s.syncAsUint16LE(cycler->numTimesPaused); } } - - // TODO: _clutTable } #endif diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp index 3832f4cf04..0cb8ff48d7 100644 --- a/engines/sci/engine/workarounds.cpp +++ b/engines/sci/engine/workarounds.cpp @@ -407,6 +407,7 @@ const SciWorkaroundEntry kCelWide_workarounds[] = { { GID_PQ2, -1, 255, 0, "DIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when showing picture within windows, called with 2nd/3rd parameters as objects { GID_SQ1, 1, 255, 0, "DIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // DEMO: Called with 2nd/3rd parameters as objects when clicking on the menu - bug #5012 { GID_FANMADE, -1, 979, 0, "DIcon", "setSize", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // In The Gem Scenario and perhaps other fanmade games, this is called with 2nd/3rd parameters as objects - bug #5144 + { GID_LSL6HIRES, -1, 94, 0, "ll6ControlPanel", "init", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // when opening the "controls" panel from the main menu, the third argument is missing SCI_WORKAROUNDENTRY_TERMINATOR }; diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp index 693bc5f196..77d333a717 100644 --- a/engines/sci/graphics/celobj32.cpp +++ b/engines/sci/graphics/celobj32.cpp @@ -120,6 +120,7 @@ struct SCALER_NoScale { const int16 _sourceY; SCALER_NoScale(const CelObj &celObj, const int16 maxWidth, const Common::Point &scaledPosition) : + _row(nullptr), _reader(celObj, FLIP ? celObj._width : maxWidth), _lastIndex(celObj._width - 1), _sourceX(scaledPosition.x), @@ -166,6 +167,7 @@ struct SCALER_Scale { static int16 _valuesY[1024]; SCALER_Scale(const CelObj &celObj, const Common::Rect &targetRect, const Common::Point &scaledPosition, const Ratio scaleX, const Ratio scaleY) : + _row(nullptr), #ifndef NDEBUG _maxX(targetRect.right - 1), #endif @@ -525,30 +527,30 @@ int CelObj::_nextCacheId = 1; CelCache *CelObj::_cache = nullptr; int CelObj::searchCache(const CelInfo32 &celInfo, int *nextInsertIndex) const { + *nextInsertIndex = -1; int oldestId = _nextCacheId + 1; - int oldestIndex = -1; + int oldestIndex = 0; for (int i = 0, len = _cache->size(); i < len; ++i) { CelCacheEntry &entry = (*_cache)[i]; - if (entry.celObj != nullptr) { - if (entry.celObj->_info == celInfo) { - entry.id = ++_nextCacheId; - return i; + if (entry.celObj == nullptr) { + if (*nextInsertIndex == -1) { + *nextInsertIndex = i; } - - if (oldestId > entry.id) { - oldestId = entry.id; - oldestIndex = i; - } - } else if (oldestIndex == -1) { + } else if (entry.celObj->_info == celInfo) { + entry.id = ++_nextCacheId; + return i; + } else if (oldestId > entry.id) { + oldestId = entry.id; oldestIndex = i; } } - // NOTE: Unlike the original SCI engine code, the out-param - // here is only updated if there was not a cache hit. - *nextInsertIndex = oldestIndex; + if (*nextInsertIndex == -1) { + *nextInsertIndex = oldestIndex; + } + return -1; } @@ -734,7 +736,11 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int int cacheIndex = searchCache(_info, &cacheInsertIndex); if (cacheIndex != -1) { CelCacheEntry &entry = (*_cache)[cacheIndex]; - *this = *dynamic_cast<CelObjView *>(entry.celObj); + const CelObjView *const cachedCelObj = dynamic_cast<CelObjView *>(entry.celObj); + if (cachedCelObj == nullptr) { + error("Expected a CelObjView in cache slot %d", cacheIndex); + } + *this = *cachedCelObj; entry.id = ++_nextCacheId; return; } @@ -866,7 +872,11 @@ CelObjView *CelObjView::duplicate() const { } byte *CelObjView::getResPointer() const { - return g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, _info.resourceId), false)->data; + const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, _info.resourceId), false); + if (resource == nullptr) { + error("Failed to load view %d from resource manager", _info.resourceId); + } + return resource->data; } #pragma mark - @@ -885,7 +895,11 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) { int cacheIndex = searchCache(_info, &cacheInsertIndex); if (cacheIndex != -1) { CelCacheEntry &entry = (*_cache)[cacheIndex]; - *this = *dynamic_cast<CelObjPic *>(entry.celObj); + const CelObjPic *const cachedCelObj = dynamic_cast<CelObjPic *>(entry.celObj); + if (cachedCelObj == nullptr) { + error("Expected a CelObjPic in cache slot %d", cacheIndex); + } + *this = *cachedCelObj; entry.id = ++_nextCacheId; return; } @@ -979,7 +993,11 @@ CelObjPic *CelObjPic::duplicate() const { } byte *CelObjPic::getResPointer() const { - return g_sci->getResMan()->findResource(ResourceId(kResourceTypePic, _info.resourceId), false)->data; + const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypePic, _info.resourceId), false); + if (resource == nullptr) { + error("Failed to load pic %d from resource manager", _info.resourceId); + } + return resource->data; } #pragma mark - diff --git a/engines/sci/graphics/celobj32.h b/engines/sci/graphics/celobj32.h index 0bb4b03ae2..6e401b3df4 100644 --- a/engines/sci/graphics/celobj32.h +++ b/engines/sci/graphics/celobj32.h @@ -474,7 +474,7 @@ private: bool analyzeForRemap() const; public: - CelObjView(GuiResourceId viewId, int16 loopNo, int16 celNo); + CelObjView(const GuiResourceId viewId, const int16 loopNo, const int16 celNo); virtual ~CelObjView() override {}; using CelObj::draw; @@ -525,7 +525,7 @@ public: */ int16 _priority; - CelObjPic(GuiResourceId pictureId, int16 celNo); + CelObjPic(const GuiResourceId pictureId, const int16 celNo); virtual ~CelObjPic() override {}; using CelObj::draw; @@ -546,7 +546,7 @@ public: */ class CelObjMem : public CelObj { public: - CelObjMem(reg_t bitmap); + CelObjMem(const reg_t bitmap); virtual ~CelObjMem() override {}; virtual CelObjMem *duplicate() const override; @@ -562,7 +562,7 @@ public: */ class CelObjColor : public CelObj { public: - CelObjColor(uint8 color, int16 width, int16 height); + CelObjColor(const uint8 color, const int16 width, const int16 height); virtual ~CelObjColor() override {}; using CelObj::draw; diff --git a/engines/sci/graphics/compare.cpp b/engines/sci/graphics/compare.cpp index 729eeeaf81..130416ff60 100644 --- a/engines/sci/graphics/compare.cpp +++ b/engines/sci/graphics/compare.cpp @@ -162,12 +162,20 @@ reg_t GfxCompare::kernelCantBeHere32(const reg_t curObject, const reg_t listRefe // rects before operating on them, but this call leverages SCI16 engine // code that operates on inclusive rects, so the rect's bottom-right // point is not modified like in other SCI32 kernel calls - Common::Rect checkRect( - readSelectorValue(_segMan, curObject, SELECTOR(brLeft)), - readSelectorValue(_segMan, curObject, SELECTOR(brTop)), - readSelectorValue(_segMan, curObject, SELECTOR(brRight)), - readSelectorValue(_segMan, curObject, SELECTOR(brBottom)) - ); + Common::Rect checkRect; + + // At least LSL6 hires passes invalid rectangles which trigger the + // isValidRect assertion in the Rect constructor; this is avoided by + // assigning the properties after construction and then testing the + // rect for validity ourselves here. SSCI does not care about whether + // or not the rects are valid + checkRect.left = readSelectorValue(_segMan, curObject, SELECTOR(brLeft)); + checkRect.top = readSelectorValue(_segMan, curObject, SELECTOR(brTop)); + checkRect.right = readSelectorValue(_segMan, curObject, SELECTOR(brRight)); + checkRect.bottom = readSelectorValue(_segMan, curObject, SELECTOR(brBottom)); + if (!checkRect.isValidRect()) { + return make_reg(0, 0); + } uint16 result = 0; uint16 signal = readSelectorValue(_segMan, curObject, SELECTOR(signal)); diff --git a/engines/sci/graphics/controls32.cpp b/engines/sci/graphics/controls32.cpp index a877d8c276..faf1d7d1a2 100644 --- a/engines/sci/graphics/controls32.cpp +++ b/engines/sci/graphics/controls32.cpp @@ -104,10 +104,7 @@ reg_t GfxControls32::kernelEditText(const reg_t controlObject) { bool dimmed = readSelectorValue(_segMan, controlObject, SELECTOR(dimmed)); editor.bitmap = _gfxText32->createFontBitmap(width, height, editor.textRect, editor.text, editor.foreColor, editor.backColor, editor.skipColor, editor.fontId, alignment, editor.borderColor, dimmed, true); } else { - Common::String title = _segMan->getString(titleObject); - int16 titleBackColor = readSelectorValue(_segMan, controlObject, SELECTOR(titleBack)); - int16 titleForeColor = readSelectorValue(_segMan, controlObject, SELECTOR(titleFore)); - editor.bitmap = _gfxText32->createTitledBitmap(width, height, editor.textRect, editor.text, editor.foreColor, editor.backColor, editor.skipColor, editor.fontId, alignment, editor.borderColor, title, titleForeColor, titleBackColor, titleFontId, true); + error("Titled bitmaps are not known to be used by any game. Please submit a bug report with details about the game you were playing and what you were doing that triggered this error. Thanks!"); } } diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index 6454a1eb32..64ae828a50 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -680,7 +680,7 @@ void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseL int splitCount = splitRects(*rectlist[rectIndex], _planes[innerIndex]->_screenRect, outRects); if (splitCount == 0) { - if (visibleInnerPlane != nullptr) { + if (visibleInnerPlane != nullptr && visibleOuterPlane != nullptr) { // same priority, or relative priority between inner/outer changed if ((visibleOuterPlane->_priority - visibleInnerPlane->_priority) * (outerPlane->_priority - innerPlane->_priority) <= 0) { if (outerPlane->_priority <= innerPlane->_priority) { @@ -697,7 +697,7 @@ void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseL rectlist.add(outRects[i]); } - if (visibleInnerPlane != nullptr) { + if (visibleInnerPlane != nullptr && visibleOuterPlane != nullptr) { // same priority, or relative priority between inner/outer changed if ((visibleOuterPlane->_priority - visibleInnerPlane->_priority) * (outerPlane->_priority - innerPlane->_priority) <= 0) { *rectlist[rectIndex] = outerPlane->_screenRect.findIntersectingRect(innerPlane->_screenRect); @@ -987,7 +987,7 @@ void GfxFrameout::alterVmap(const Palette &palette1, const Palette &palette2, co if (styleRanges[paletteIndex] == style) { int minDiff = 262140; - int minDiffIndex; + int minDiffIndex = paletteIndex; for (int i = 0; i < 236; ++i) { if (styleRanges[i] != style) { @@ -1007,7 +1007,7 @@ void GfxFrameout::alterVmap(const Palette &palette1, const Palette &palette2, co if (style == 1 && styleRanges[paletteIndex] == 0) { int minDiff = 262140; - int minDiffIndex; + int minDiffIndex = paletteIndex; for (int i = 0; i < 236; ++i) { int r = palette2.colors[i].r; diff --git a/engines/sci/graphics/plane32.cpp b/engines/sci/graphics/plane32.cpp index d05e4f79e1..470986fb3c 100644 --- a/engines/sci/graphics/plane32.cpp +++ b/engines/sci/graphics/plane32.cpp @@ -45,8 +45,6 @@ void DrawList::add(ScreenItem *screenItem, const Common::Rect &rect) { uint16 Plane::_nextObjectId = 20000; Plane::Plane(const Common::Rect &gameRect, PlanePictureCodes pictureId) : -_width(g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth), -_height(g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight), _pictureId(pictureId), _mirrored(false), _back(0), @@ -65,8 +63,6 @@ _gameRect(gameRect) { } Plane::Plane(reg_t object) : -_width(g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth), -_height(g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight), _priorityChanged(false), _object(object), _redrawAllCount(g_sci->_gfxFrameout->getScreenCount()), @@ -97,8 +93,6 @@ _moved(0) { Plane::Plane(const Plane &other) : _pictureId(other._pictureId), _mirrored(other._mirrored), -_field_34(other._field_34), _field_38(other._field_38), -_field_3C(other._field_3C), _field_40(other._field_40), _back(other._back), _object(other._object), _priority(other._priority), @@ -116,11 +110,7 @@ void Plane::operator=(const Plane &other) { _mirrored = other._mirrored; _priority = other._priority; _back = other._back; - _width = other._width; - _field_34 = other._field_34; - _height = other._height; _screenRect = other._screenRect; - _field_3C = other._field_3C; _priorityChanged = other._priorityChanged; } @@ -183,7 +173,7 @@ void Plane::addPicInternal(const GuiResourceId pictureId, const Common::Point *p screenItem->_pictureId = pictureId; screenItem->_mirrorX = mirrorX; screenItem->_priority = celObj->_priority; - screenItem->_fixPriority = true; + screenItem->_fixedPriority = true; if (position != nullptr) { screenItem->_position = *position + celObj->_relativePosition; } else { @@ -769,8 +759,6 @@ void Plane::sync(const Plane *other, const Common::Rect &screenRect) { } convertGameRectToPlaneRect(); - _width = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - _height = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; _screenRect = _planeRect; // NOTE: screenRect originally was retrieved through globals // instead of being passed into the function diff --git a/engines/sci/graphics/plane32.h b/engines/sci/graphics/plane32.h index 770a6fa445..c93fb5b64e 100644 --- a/engines/sci/graphics/plane32.h +++ b/engines/sci/graphics/plane32.h @@ -101,16 +101,6 @@ private: static uint16 _nextObjectId; /** - * The dimensions of the plane, in game script - * coordinates. - * TODO: These are never used and are always - * scriptWidth x scriptHeight in SCI engine? The actual - * dimensions of the plane are always in - * gameRect/planeRect. - */ - int16 _width, _height; - - /** * For planes that are used to render picture data, the * resource ID of the picture to be displayed. This * value may also be one of the special @@ -135,10 +125,6 @@ private: */ bool _pictureChanged; - // TODO: Are these ever actually used? - int _field_34, _field_38; // probably a point or ratio - int _field_3C, _field_40; // probably a point or ratio - /** * Converts the dimensions of the game rect used by * scripts to the dimensions of the plane rect used to @@ -275,7 +261,11 @@ public: * given screen rect. */ inline void clipScreenRect(const Common::Rect &screenRect) { - if (_screenRect.intersects(screenRect)) { + // LSL6 hires creates planes with invalid rects; SSCI does not + // care about this, but `Common::Rect::clip` does, so we need to + // check whether or not the rect is actually valid before clipping + // and only clip valid rects + if (_screenRect.isValidRect() && _screenRect.intersects(screenRect)) { _screenRect.clip(screenRect); } else { _screenRect.left = 0; diff --git a/engines/sci/graphics/screen_item32.cpp b/engines/sci/graphics/screen_item32.cpp index c3fdbb6845..fba0fa0422 100644 --- a/engines/sci/graphics/screen_item32.cpp +++ b/engines/sci/graphics/screen_item32.cpp @@ -55,7 +55,7 @@ _useInsetRect(false), _z(0), _celInfo(celInfo), _celObj(nullptr), -_fixPriority(false), +_fixedPriority(false), _position(0, 0), _object(make_reg(0, _nextObjectId++)), _pictureId(-1), @@ -70,7 +70,7 @@ _useInsetRect(false), _z(0), _celInfo(celInfo), _celObj(nullptr), -_fixPriority(false), +_fixedPriority(false), _position(rect.left, rect.top), _object(make_reg(0, _nextObjectId++)), _pictureId(-1), @@ -90,7 +90,7 @@ _useInsetRect(false), _z(0), _celInfo(celInfo), _celObj(nullptr), -_fixPriority(false), +_fixedPriority(false), _position(position), _object(make_reg(0, _nextObjectId++)), _pictureId(-1), @@ -209,10 +209,10 @@ void ScreenItem::setFromObject(SegManager *segMan, const reg_t object, const boo } if (readSelectorValue(segMan, object, SELECTOR(fixPriority))) { - _fixPriority = true; + _fixedPriority = true; _priority = readSelectorValue(segMan, object, SELECTOR(priority)); } else { - _fixPriority = false; + _fixedPriority = false; writeSelectorValue(segMan, object, SELECTOR(priority), _position.y); } @@ -326,6 +326,9 @@ void ScreenItem::calcRects(const Plane &plane) { mulinc(temp, celToScreenX, Ratio()); CelObjPic *celObjPic = dynamic_cast<CelObjPic *>(_celObj); + if (celObjPic == nullptr) { + error("Expected a CelObjPic"); + } temp.translate((celObjPic->_relativePosition.x * scriptToScreenX).toInt() - displaceX, 0); // TODO: This is weird. @@ -369,6 +372,9 @@ void ScreenItem::calcRects(const Plane &plane) { } CelObjPic *celObjPic = dynamic_cast<CelObjPic *>(_celObj); + if (celObjPic == nullptr) { + error("Expected a CelObjPic"); + } temp.translate(celObjPic->_relativePosition.x - (displaceX * scaleX).toInt(), celObjPic->_relativePosition.y - (celObj._displace.y * scaleY).toInt()); // TODO: This is weird. @@ -402,7 +408,7 @@ void ScreenItem::calcRects(const Plane &plane) { _screenRect.top = 0; } - if (!_fixPriority) { + if (!_fixedPriority) { _priority = _z + _position.y; } } else { diff --git a/engines/sci/graphics/screen_item32.h b/engines/sci/graphics/screen_item32.h index 977d80ebad..91f54b48e9 100644 --- a/engines/sci/graphics/screen_item32.h +++ b/engines/sci/graphics/screen_item32.h @@ -132,7 +132,7 @@ public: * in place. Otherwise, the priority of the screen item * is calculated from its y-position + z-index. */ - bool _fixPriority; + bool _fixedPriority; /** * The rendering priority of the screen item, relative @@ -273,7 +273,7 @@ public: typedef StablePointerArray<ScreenItem, 250> ScreenItemListBase; class ScreenItemList : public ScreenItemListBase { - static bool inline sortHelper(const ScreenItem *a, const ScreenItem *b) { + inline static bool sortHelper(const ScreenItem *a, const ScreenItem *b) { return *a < *b; } public: diff --git a/engines/sci/graphics/text32.cpp b/engines/sci/graphics/text32.cpp index 99ffc6e328..d1c223d5d5 100644 --- a/engines/sci/graphics/text32.cpp +++ b/engines/sci/graphics/text32.cpp @@ -48,12 +48,6 @@ GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts) : // Not a typo, the original engine did not initialise height, only width _width(0), _text(""), - _field_20(0), - _field_2C(2), - _field_30(0), - _field_34(0), - _field_38(0), - _field_3C(0), _bitmap(NULL_REG) { _fontId = _defaultFontId; _font = _cache->getFont(_defaultFontId); @@ -61,7 +55,6 @@ GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts) : reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect &rect, const Common::String &text, const uint8 foreColor, const uint8 backColor, const uint8 skipColor, const GuiResourceId fontId, const TextAlign alignment, const int16 borderColor, const bool dimmed, const bool doScaling) { - _field_22 = 0; _borderColor = borderColor; _text = text; _textRect = rect; @@ -111,7 +104,6 @@ reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect } reg_t GfxText32::createFontBitmap(const CelInfo32 &celInfo, const Common::Rect &rect, const Common::String &text, const int16 foreColor, const int16 backColor, const GuiResourceId fontId, const int16 skipColor, const int16 borderColor, const bool dimmed) { - _field_22 = 0; _borderColor = borderColor; _text = text; _textRect = rect; @@ -171,11 +163,6 @@ reg_t GfxText32::createFontBitmap(const CelInfo32 &celInfo, const Common::Rect & return _bitmap; } -reg_t GfxText32::createTitledBitmap(const int16 width, const int16 height, const Common::Rect &textRect, const Common::String &text, const int16 foreColor, const int16 backColor, const int16 skipColor, const GuiResourceId fontId, const TextAlign alignment, const int16 borderColor, Common::String &title, const int16 titleForeColor, const int16 titleBackColor, const GuiResourceId titleFontId, const bool doScaling) { - warning("TODO: createTitledBitmap incomplete !"); - return createFontBitmap(width, height, textRect, text, foreColor, backColor, skipColor, fontId, alignment, borderColor, false, doScaling); -} - void GfxText32::setFont(const GuiResourceId fontId) { // NOTE: In SCI engine this calls FontMgr::BuildFontTable and then a font // table is built on the FontMgr directly; instead, because we already have @@ -558,12 +545,7 @@ Common::Rect GfxText32::getTextSize(const Common::String &text, int16 maxWidth, if (maxWidth >= 0) { if (maxWidth == 0) { - // TODO: This was hardcoded to 192, but guessing - // that it was originally 60% of the scriptWidth - // before the compiler took over. - // Verify this by looking at a game that uses a - // scriptWidth other than 320, like LSL7 - maxWidth = _scaledWidth * (scriptWidth * 0.6) / scriptWidth; + maxWidth = _scaledWidth * 3 / 5; } result.right = maxWidth; diff --git a/engines/sci/graphics/text32.h b/engines/sci/graphics/text32.h index 5768ea0c59..20adb3d7c7 100644 --- a/engines/sci/graphics/text32.h +++ b/engines/sci/graphics/text32.h @@ -304,17 +304,6 @@ private: */ TextAlign _alignment; - int16 _field_20; - - /** - * TODO: Document - */ - int16 _field_22; - - int _field_2C, _field_30, _field_34, _field_38; - - int16 _field_3C; - /** * The position of the text draw cursor. */ @@ -392,11 +381,6 @@ public: */ reg_t createFontBitmap(const CelInfo32 &celInfo, const Common::Rect &rect, const Common::String &text, const int16 foreColor, const int16 backColor, const GuiResourceId fontId, const int16 skipColor, const int16 borderColor, const bool dimmed); - /** - * Creates a font bitmap with a title. - */ - reg_t createTitledBitmap(const int16 width, const int16 height, const Common::Rect &textRect, const Common::String &text, const int16 foreColor, const int16 backColor, const int16 skipColor, const GuiResourceId fontId, const TextAlign alignment, const int16 borderColor, Common::String &title, const int16 titleForeColor, const int16 titleBackColor, const GuiResourceId titleFontId, const bool doScaling); - inline int scaleUpWidth(int value) const { const int scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; return (value * scriptWidth + _scaledWidth - 1) / _scaledWidth; diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp index 3a69b5f03c..cd54c175cc 100644 --- a/engines/scumm/actor.cpp +++ b/engines/scumm/actor.cpp @@ -2479,6 +2479,13 @@ void ScummEngine::setActorRedrawFlags() { _actors[j]->_needRedraw = true; } } else { + if (_game.heversion >= 72) { + for (j = 1; j < _numActors; j++) { + if (_actors[j]->_costume && _actors[j]->_heXmapNum) + _actors[j]->_needRedraw = true; + } + } + for (i = 0; i < _gdi->_numStrips; i++) { int strip = _screenStartStrip + i; if (testGfxAnyUsageBits(strip)) { diff --git a/engines/scumm/he/intern_he.h b/engines/scumm/he/intern_he.h index 72c11c95a1..7f7babc604 100644 --- a/engines/scumm/he/intern_he.h +++ b/engines/scumm/he/intern_he.h @@ -455,9 +455,6 @@ protected: Sprite *_sprite; public: - Moonbase *_moonbase; - -public: ScummEngine_v90he(OSystem *syst, const DetectorResult &dr); ~ScummEngine_v90he(); @@ -553,15 +550,8 @@ protected: byte VAR_NUM_PALETTES; byte VAR_NUM_UNK; -public: // FIXME. TODO. Should be protected. Used by Moonbase byte VAR_U32_VERSION; byte VAR_U32_ARRAY_UNK; - byte VAR_U32_USER_VAR_A; - byte VAR_U32_USER_VAR_B; - byte VAR_U32_USER_VAR_C; - byte VAR_U32_USER_VAR_D; - byte VAR_U32_USER_VAR_E; - byte VAR_U32_USER_VAR_F; }; class ScummEngine_v99he : public ScummEngine_v90he { @@ -585,16 +575,25 @@ protected: }; class ScummEngine_v100he : public ScummEngine_v99he { +friend class AI; + protected: ResType _heResType; int32 _heResId; byte _debugInputBuffer[256]; + +public: + Moonbase *_moonbase; + public: - ScummEngine_v100he(OSystem *syst, const DetectorResult &dr) : ScummEngine_v99he(syst, dr) {} + ScummEngine_v100he(OSystem *syst, const DetectorResult &dr); + ~ScummEngine_v100he(); virtual void resetScumm(); + virtual void setupScummVars(); + protected: virtual void setupOpcodes(); @@ -640,6 +639,14 @@ protected: void o100_getSpriteInfo(); void o100_getWizData(); void o100_getVideoData(); + +protected: + byte VAR_U32_USER_VAR_A; + byte VAR_U32_USER_VAR_B; + byte VAR_U32_USER_VAR_C; + byte VAR_U32_USER_VAR_D; + byte VAR_U32_USER_VAR_E; + byte VAR_U32_USER_VAR_F; }; class ScummEngine_vCUPhe : public Engine { diff --git a/engines/scumm/he/logic/moonbase_logic.cpp b/engines/scumm/he/logic/moonbase_logic.cpp index a32356614f..1b596fc54c 100644 --- a/engines/scumm/he/logic/moonbase_logic.cpp +++ b/engines/scumm/he/logic/moonbase_logic.cpp @@ -23,6 +23,7 @@ #include "scumm/he/intern_he.h" #include "scumm/he/logic_he.h" #include "scumm/he/moonbase/moonbase.h" +#include "scumm/he/moonbase/ai_main.h" namespace Scumm { @@ -32,7 +33,7 @@ namespace Scumm { */ class LogicHEmoonbase : public LogicHE { public: - LogicHEmoonbase(ScummEngine_v90he *vm) : LogicHE(vm) {} + LogicHEmoonbase(ScummEngine_v100he *vm) : LogicHE(vm) { _vm1 = vm; } int versionID(); @@ -48,10 +49,13 @@ private: int op_set_fow_image(int op, int numArgs, int32 *args); void op_ai_test_kludge(int op, int numArgs, int32 *args); - void op_ai_master_control_program(int op, int numArgs, int32 *args); + int op_ai_master_control_program(int op, int numArgs, int32 *args); void op_ai_reset(int op, int numArgs, int32 *args); void op_ai_set_type(int op, int numArgs, int32 *args); void op_ai_clean_up(int op, int numArgs, int32 *args); + +private: + ScummEngine_v100he *_vm1; }; int LogicHEmoonbase::versionID() { @@ -137,14 +141,12 @@ int32 LogicHEmoonbase::dispatch(int op, int numArgs, int32 *args) { break; case OP_SET_FOW_IMAGE: return op_set_fow_image(op, numArgs, args); - break; case OP_AI_TEST_KLUDGE: op_ai_test_kludge(op, numArgs, args); break; case OP_AI_MASTER_CONTROL_PROGRAM: - op_ai_master_control_program(op, numArgs, args); - break; + return op_ai_master_control_program(op, numArgs, args); case OP_AI_RESET: op_ai_reset(op, numArgs, args); break; @@ -185,9 +187,9 @@ void LogicHEmoonbase::op_dos_command(int op, int numArgs, int32 *args) { void LogicHEmoonbase::op_set_fow_sentinel(int32 *args) { debug(2, "op_set_fow_sentinel(%d, %d, %d)", args[0], args[1], args[2]); - _vm->_moonbase->_fowSentinelImage = args[0]; - _vm->_moonbase->_fowSentinelState = args[1]; - _vm->_moonbase->_fowSentinelConditionBits = args[2]; + _vm1->_moonbase->_fowSentinelImage = args[0]; + _vm1->_moonbase->_fowSentinelState = args[1]; + _vm1->_moonbase->_fowSentinelConditionBits = args[2]; } void LogicHEmoonbase::op_set_fow_information(int op, int numArgs, int32 *args) { @@ -201,7 +203,7 @@ void LogicHEmoonbase::op_set_fow_information(int op, int numArgs, int32 *args) { debug(2, "%s", str.c_str()); - _vm->_moonbase->setFOWInfo( + _vm1->_moonbase->setFOWInfo( args[0], // array args[1], // array down dimension args[2], // array across dimension @@ -218,7 +220,7 @@ void LogicHEmoonbase::op_set_fow_information(int op, int numArgs, int32 *args) { int LogicHEmoonbase::op_set_fow_image(int op, int numArgs, int32 *args) { debug(2, "op_set_fow_image(%d)", args[0]); - return _vm->_moonbase->setFOWImage(args[0]) ? 1 : 0; + return _vm1->_moonbase->setFOWImage(args[0]) ? 1 : 0; } void LogicHEmoonbase::op_ai_test_kludge(int op, int numArgs, int32 *args) { @@ -226,27 +228,27 @@ void LogicHEmoonbase::op_ai_test_kludge(int op, int numArgs, int32 *args) { LogicHE::dispatch(op, numArgs, args); } -void LogicHEmoonbase::op_ai_master_control_program(int op, int numArgs, int32 *args) { - warning("STUB: op_ai_master_control_program()"); - LogicHE::dispatch(op, numArgs, args); +int LogicHEmoonbase::op_ai_master_control_program(int op, int numArgs, int32 *args) { + warning("op_ai_master_control_program()"); + return _vm1->_moonbase->_ai->masterControlProgram(numArgs, args); } void LogicHEmoonbase::op_ai_reset(int op, int numArgs, int32 *args) { - warning("STUB: op_ai_reset)"); - LogicHE::dispatch(op, numArgs, args); + warning("op_ai_reset())"); + _vm1->_moonbase->_ai->resetAI(); } void LogicHEmoonbase::op_ai_set_type(int op, int numArgs, int32 *args) { - warning("STUB: op_ai_set_type()"); - LogicHE::dispatch(op, numArgs, args); + warning("op_ai_set_type()"); + _vm1->_moonbase->_ai->setAIType(numArgs, args); } void LogicHEmoonbase::op_ai_clean_up(int op, int numArgs, int32 *args) { - warning("STUB: op_ai_clean_up()"); - LogicHE::dispatch(op, numArgs, args); + warning("op_ai_clean_up()"); + _vm1->_moonbase->_ai->cleanUpAI(); } -LogicHE *makeLogicHEmoonbase(ScummEngine_v90he *vm) { +LogicHE *makeLogicHEmoonbase(ScummEngine_v100he *vm) { return new LogicHEmoonbase(vm); } diff --git a/engines/scumm/he/logic_he.cpp b/engines/scumm/he/logic_he.cpp index 366cd3be26..33f5c40464 100644 --- a/engines/scumm/he/logic_he.cpp +++ b/engines/scumm/he/logic_he.cpp @@ -102,7 +102,7 @@ LogicHE *LogicHE::makeLogicHE(ScummEngine_v90he *vm) { return makeLogicHEbasketball(vm); case GID_MOONBASE: - return makeLogicHEmoonbase(vm); + return makeLogicHEmoonbase((ScummEngine_v100he *)vm); default: return new LogicHE(vm); diff --git a/engines/scumm/he/logic_he.h b/engines/scumm/he/logic_he.h index cd547f1616..5002ee9434 100644 --- a/engines/scumm/he/logic_he.h +++ b/engines/scumm/he/logic_he.h @@ -65,7 +65,7 @@ LogicHE *makeLogicHEfootball2002(ScummEngine_v90he *vm); LogicHE *makeLogicHEsoccer(ScummEngine_v90he *vm); LogicHE *makeLogicHEbaseball2001(ScummEngine_v90he *vm); LogicHE *makeLogicHEbasketball(ScummEngine_v90he *vm); -LogicHE *makeLogicHEmoonbase(ScummEngine_v90he *vm); +LogicHE *makeLogicHEmoonbase(ScummEngine_v100he *vm); } // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_defenseunit.cpp b/engines/scumm/he/moonbase/ai_defenseunit.cpp index f1f0251dc6..ab61297603 100644 --- a/engines/scumm/he/moonbase/ai_defenseunit.cpp +++ b/engines/scumm/he/moonbase/ai_defenseunit.cpp @@ -23,16 +23,24 @@ #include "common/rect.h" #include "common/util.h" #include "scumm/he/intern_he.h" +#include "scumm/he/moonbase/moonbase.h" #include "scumm/he/moonbase/ai_defenseunit.h" #include "scumm/he/moonbase/ai_main.h" namespace Scumm { -DefenseUnit::DefenseUnit() { +DefenseUnit::DefenseUnit(AI *ai) : _ai(ai) { _state = DUS_ON; + + _id = -1; + _distanceTo = 0; + _state = 0; + _radius = 0; + _armor = 0; + _cost = 0; } -DefenseUnit::DefenseUnit(DefenseUnit *inUnit) { +DefenseUnit::DefenseUnit(DefenseUnit *inUnit, AI *ai) : _ai(ai) { _id = inUnit->getID(); _pos.x = inUnit->getPosX(); _pos.y = inUnit->getPosY(); @@ -40,6 +48,7 @@ DefenseUnit::DefenseUnit(DefenseUnit *inUnit) { _state = inUnit->getState(); _radius = inUnit->getRadius(); _armor = inUnit->getArmor(); + _cost = inUnit->getCost(); } DefenseUnit::~DefenseUnit() { @@ -71,14 +80,14 @@ Common::Point *AntiAirUnit::createTargetPos(int index, int distance, int weaponT targetPos->y = getPosY(); } else { ratio = MAX(0, (getRadius() / distance)); - targetPos->x = getPosX() - ratio * (getPosX() - sourceX); - targetPos->y = getPosY() - ratio * (getPosY() - sourceY); + targetPos->x = (int16)(getPosX() - ratio * (getPosX() - sourceX)); + targetPos->y = (int16)(getPosY() - ratio * (getPosY() - sourceY)); } break; case ITEM_EMP: - if (getRadius() + 215 > distance) { //emp radius + if (getRadius() + 215 > distance) { // Emp radius double x1 = static_cast<double>(sourceX); double y1 = static_cast<double>(sourceY); double x2 = static_cast<double>(getPosX()); @@ -87,17 +96,17 @@ Common::Point *AntiAirUnit::createTargetPos(int index, int distance, int weaponT double r2 = static_cast<double>(getRadius() + 3); double d = static_cast<double>(distance); - //formulae for calculating one point of intersection of two circles + // Formulae for calculating one point of intersection of two circles float root = sqrt((((r1 + r2) * (r1 + r2)) - (d * d)) * ((d * d) - ((r2 - r1) * (r2 - r1)))); - int x = ((x1 + x2) / 2) + ((x2 - x1) * (r1 * r1 - r2 * r2)) / (2 * d * d) + ((y2 - y1) / (2 * d * d)) * root; - int y = ((y1 + y2) / 2) + ((y2 - y1) * (r1 * r1 - r2 * r2)) / (2 * d * d) - ((x2 - x1) / (2 * d * d)) * root; + int x = (int)(((x1 + x2) / 2) + ((x2 - x1) * (r1 * r1 - r2 * r2)) / (2 * d * d) + ((y2 - y1) / (2 * d * d)) * root); + int y = (int)(((y1 + y2) / 2) + ((y2 - y1) * (r1 * r1 - r2 * r2)) / (2 * d * d) - ((x2 - x1) / (2 * d * d)) * root); targetPos->x = x; targetPos->y = y; } else { ratio = 1 - (getRadius() / static_cast<float>(distance - 20)); - targetPos->x = sourceX + ratio * (getPosX() - sourceX); - targetPos->y = sourceY + ratio * (getPosY() - sourceY); + targetPos->x = (int16)(sourceX + ratio * (getPosX() - sourceX)); + targetPos->y = (int16)(sourceY + ratio * (getPosY() - sourceY)); } break; @@ -123,14 +132,14 @@ int AntiAirUnit::selectWeapon(int index) { case 2: if (getState() == DUS_OFF) { - if (getPlayerEnergy() > 6) { - if (!_vm->_rnd.getRandomNumber(3)) { + if (_ai->getPlayerEnergy() > 6) { + if (!_ai->_vm->_rnd.getRandomNumber(3)) { return ITEM_VIRUS; } } - if (getPlayerEnergy() > 2) { - if (!_vm->_rnd.getRandomNumber(1)) { + if (_ai->getPlayerEnergy() > 2) { + if (!_ai->_vm->_rnd.getRandomNumber(1)) { return ITEM_SPIKE; } } @@ -169,8 +178,8 @@ Common::Point *ShieldUnit::createTargetPos(int index, int distance, int weaponTy case ITEM_CRAWLER: ratio = MAX(0.0, 1.0 - (static_cast<float>(getRadius()) / static_cast<float>(distance - 20))); { - int maxX = getMaxX(); - int maxY = getMaxY(); + int maxX = _ai->getMaxX(); + int maxY = _ai->getMaxY(); int thisX = (static_cast<int>(sourceX + ratio * (getPosX() - sourceX)) + maxX) % maxX; int thisY = (static_cast<int>(sourceY + ratio * (getPosY() - sourceY)) + maxY) % maxY; targetPos->x = thisX; @@ -179,7 +188,7 @@ Common::Point *ShieldUnit::createTargetPos(int index, int distance, int weaponTy break; case ITEM_EMP: - if (getRadius() + 215 > distance) { //emp radius + if (getRadius() + 215 > distance) { // Emp radius double x1 = static_cast<double>(sourceX); double y1 = static_cast<double>(sourceY); double x2 = static_cast<double>(getPosX()); @@ -188,17 +197,17 @@ Common::Point *ShieldUnit::createTargetPos(int index, int distance, int weaponTy double r2 = static_cast<double>(getRadius() + 10); double d = static_cast<double>(distance); - //formulae for calculating one point of intersection of two circles + // Formulae for calculating one point of intersection of two circles float root = sqrt((((r1 + r2) * (r1 + r2)) - (d * d)) * ((d * d) - ((r2 - r1) * (r2 - r1)))); - int x = ((x1 + x2) / 2) + ((x2 - x1) * (r1 * r1 - r2 * r2)) / (2 * d * d) + ((y2 - y1) / (2 * d * d)) * root; - int y = ((y1 + y2) / 2) + ((y2 - y1) * (r1 * r1 - r2 * r2)) / (2 * d * d) - ((x2 - x1) / (2 * d * d)) * root; + int x = (int)(((x1 + x2) / 2) + ((x2 - x1) * (r1 * r1 - r2 * r2)) / (2 * d * d) + ((y2 - y1) / (2 * d * d)) * root); + int y = (int)(((y1 + y2) / 2) + ((y2 - y1) * (r1 * r1 - r2 * r2)) / (2 * d * d) - ((x2 - x1) / (2 * d * d)) * root); targetPos->x = x; targetPos->y = y; } else { ratio = 1 - (getRadius() / static_cast<float>(distance - 20)); - targetPos->x = sourceX + ratio * (getPosX() - sourceX); - targetPos->y = sourceY + ratio * (getPosY() - sourceY); + targetPos->x = (int16)(sourceX + ratio * (getPosX() - sourceX)); + targetPos->y = (int16)(sourceY + ratio * (getPosY() - sourceY)); } if (distance < getRadius()) { @@ -221,8 +230,8 @@ Common::Point *ShieldUnit::createTargetPos(int index, int distance, int weaponTy int ShieldUnit::selectWeapon(int index) { warning("Shield weapon select"); - int myUnit = getClosestUnit(getPosX(), getPosY(), getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 0); - int dist = getDistance(getPosX(), getPosY(), getHubX(myUnit), getHubY(myUnit)); + int myUnit = _ai->getClosestUnit(getPosX(), getPosY(), _ai->getMaxX(), _ai->getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 0); + int dist = _ai->getDistance(getPosX(), getPosY(), _ai->getHubX(myUnit), _ai->getHubY(myUnit)); if ((dist < (getRadius() - 20)) && (dist > 90)) { return ITEM_SPIKE; @@ -231,7 +240,7 @@ int ShieldUnit::selectWeapon(int index) { switch (index) { case 0: if (getState() == DUS_OFF) { - if (getPlayerEnergy() < 3) { + if (_ai->getPlayerEnergy() < 3) { return ITEM_BOMB; } else { return ITEM_SPIKE; @@ -273,8 +282,8 @@ Common::Point *MineUnit::createTargetPos(int index, int distance, int weaponType case ITEM_EMP: ratio = 1 - (getRadius() / static_cast<float>(distance - 20)); - targetPos->x = sourceX + ratio * (getPosX() - sourceX); - targetPos->y = sourceY + ratio * (getPosY() - sourceY); + targetPos->x = (int16)(sourceX + ratio * (getPosX() - sourceX)); + targetPos->y = (int16)(sourceY + ratio * (getPosY() - sourceY)); break; default: @@ -287,11 +296,11 @@ Common::Point *MineUnit::createTargetPos(int index, int distance, int weaponType } int MineUnit::selectWeapon(int index) { - int myUnit = getClosestUnit(getPosX(), getPosY(), getMaxX(), getCurrentPlayer(), 1, 0, 0, 0); + int myUnit = _ai->getClosestUnit(getPosX(), getPosY(), _ai->getMaxX(), _ai->getCurrentPlayer(), 1, 0, 0, 0); int x = getPosX(); int y = getPosY(); - int dist = getDistance(x, y, getHubX(myUnit), getHubY(myUnit)); + int dist = _ai->getDistance(x, y, _ai->getHubX(myUnit), _ai->getHubY(myUnit)); if ((getState() == DUS_ON) && (dist < 110)) { return ITEM_EMP; @@ -334,34 +343,34 @@ Common::Point *HubUnit::createTargetPos(int index, int distance, int weaponType, int HubUnit::selectWeapon(int index) { warning("Hub weapon select"); - int energy = getPlayerEnergy(); + int energy = _ai->getPlayerEnergy(); if (energy > 6) { //possibly choose crawler - if (getBuildingWorth(getID()) > 21) { + if (_ai->getBuildingWorth(getID()) > 21) { return ITEM_CRAWLER; } } //choose betw/ bomb and cluster - if (getBuildingArmor(getID()) < 1.5) { + if (_ai->getBuildingArmor(getID()) < 1.5) { return ITEM_CLUSTER; } if (energy > 2) { - if (!_vm->_rnd.getRandomNumber(3)) { + if (!_ai->_vm->_rnd.getRandomNumber(3)) { return ITEM_SPIKE; } - if (!_vm->_rnd.getRandomNumber(4)) { + if (!_ai->_vm->_rnd.getRandomNumber(4)) { return ITEM_GUIDED; } - if (!_vm->_rnd.getRandomNumber(4)) { + if (!_ai->_vm->_rnd.getRandomNumber(4)) { return ITEM_MINE; } - if (!_vm->_rnd.getRandomNumber(9)) { + if (!_ai->_vm->_rnd.getRandomNumber(9)) { return ITEM_EMP; } } @@ -449,7 +458,6 @@ int BridgeUnit::selectWeapon(int index) { } } - Common::Point *EnergyUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { Common::Point *targetPos = new Common::Point; @@ -483,22 +491,22 @@ Common::Point *EnergyUnit::createTargetPos(int index, int distance, int weaponTy int EnergyUnit::selectWeapon(int index) { warning("Energy weapon select"); - int energy = getPlayerEnergy(); + int energy = _ai->getPlayerEnergy(); if (energy > 6) { //possibly choose crawler - if (getBuildingWorth(getID()) > 21) { + if (_ai->getBuildingWorth(getID()) > 21) { return ITEM_CRAWLER; } } //choose betw/ bomb and cluster - if (getBuildingArmor(getID()) < 1.5) { + if (_ai->getBuildingArmor(getID()) < 1.5) { return ITEM_CLUSTER; } if (energy > 2) { - if (!_vm->_rnd.getRandomNumber(3)) { + if (!_ai->_vm->_rnd.getRandomNumber(3)) { return ITEM_EMP; } } @@ -539,17 +547,17 @@ Common::Point *OffenseUnit::createTargetPos(int index, int distance, int weaponT int OffenseUnit::selectWeapon(int index) { warning("Offense weapon select"); - int energy = getPlayerEnergy(); + int energy = _ai->getPlayerEnergy(); if (energy > 6) { //possibly choose crawler - if (getBuildingWorth(getID()) > 21) { + if (_ai->getBuildingWorth(getID()) > 21) { return ITEM_CRAWLER; } } //choose betw/ bomb and cluster - if (getBuildingArmor(getID()) < 1.5) { + if (_ai->getBuildingArmor(getID()) < 1.5) { return ITEM_CLUSTER; } @@ -589,13 +597,13 @@ Common::Point *CrawlerUnit::createTargetPos(int index, int distance, int weaponT int CrawlerUnit::selectWeapon(int index) { warning("Crawler weapon select"); - int myUnit = getClosestUnit(getPosX(), getPosY(), getMaxX(), getCurrentPlayer(), 1, 0, 0, 0); - int dist = getDistance(getHubX(myUnit), getHubY(myUnit), getPosX(), getPosY()); + int myUnit = _ai->getClosestUnit(getPosX(), getPosY(), _ai->getMaxX(), _ai->getCurrentPlayer(), 1, 0, 0, 0); + int dist = _ai->getDistance(_ai->getHubX(myUnit), _ai->getHubY(myUnit), getPosX(), getPosY()); int x = getPosX(); int y = getPosY(); - int energy = getPlayerEnergy(); - int terrain = getTerrain(x, y); + int energy = _ai->getPlayerEnergy(); + int terrain = _ai->getTerrain(x, y); if (terrain != TERRAIN_TYPE_WATER) { if ((energy > 2) && (dist < 220)) { @@ -609,7 +617,7 @@ int CrawlerUnit::selectWeapon(int index) { } if (energy > 2) { - if (_vm->_rnd.getRandomNumber(1)) { + if (_ai->_vm->_rnd.getRandomNumber(1)) { return ITEM_MINE; } else { return ITEM_TIME_EXPIRED; @@ -620,61 +628,61 @@ int CrawlerUnit::selectWeapon(int index) { return SKIP_TURN; } -AntiAirUnit::AntiAirUnit() { +AntiAirUnit::AntiAirUnit(AI *ai) : DefenseUnit(ai) { setRadius(190); setArmor(3); setCost(1); } -ShieldUnit::ShieldUnit() { +ShieldUnit::ShieldUnit(AI *ai) : DefenseUnit(ai) { setRadius(170); setArmor(3); setCost(7); } -MineUnit::MineUnit() { +MineUnit::MineUnit(AI *ai) : DefenseUnit(ai) { setRadius(80); setArmor(1); setCost(3); } -HubUnit::HubUnit() { +HubUnit::HubUnit(AI *ai) : DefenseUnit(ai) { setRadius(1); setArmor(5); setCost(7); } -TowerUnit::TowerUnit() { +TowerUnit::TowerUnit(AI *ai) : DefenseUnit(ai) { setRadius(1); setArmor(3); setCost(1); } -BridgeUnit::BridgeUnit() { +BridgeUnit::BridgeUnit(AI *ai) : DefenseUnit(ai) { setRadius(1); setArmor(3); setCost(1); } -EnergyUnit::EnergyUnit() { +EnergyUnit::EnergyUnit(AI *ai) : DefenseUnit(ai) { setRadius(1); setArmor(5); setCost(7); } -OffenseUnit::OffenseUnit() { +OffenseUnit::OffenseUnit(AI *ai) : DefenseUnit(ai) { setRadius(1); setArmor(3); setCost(7); } -CrawlerUnit::CrawlerUnit() { +CrawlerUnit::CrawlerUnit(AI *ai) : DefenseUnit(ai) { setRadius(1); setArmor(3); setCost(7); } -AntiAirUnit::AntiAirUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { +AntiAirUnit::AntiAirUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) { setID(inUnit->getID()); setPos(inUnit->getPosX(), inUnit->getPosY()); setDistanceTo(inUnit->getDistanceTo()); @@ -684,7 +692,7 @@ AntiAirUnit::AntiAirUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { } -ShieldUnit::ShieldUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { +ShieldUnit::ShieldUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) { setID(inUnit->getID()); setPos(inUnit->getPosX(), inUnit->getPosY()); setDistanceTo(inUnit->getDistanceTo()); @@ -693,7 +701,7 @@ ShieldUnit::ShieldUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { setArmor(inUnit->getArmor()); } -MineUnit::MineUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { +MineUnit::MineUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) { setID(inUnit->getID()); setPos(inUnit->getPosX(), inUnit->getPosY()); setDistanceTo(inUnit->getDistanceTo()); @@ -702,7 +710,7 @@ MineUnit::MineUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { setArmor(inUnit->getArmor()); } -HubUnit::HubUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { +HubUnit::HubUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) { setID(inUnit->getID()); setPos(inUnit->getPosX(), inUnit->getPosY()); setDistanceTo(inUnit->getDistanceTo()); @@ -711,7 +719,7 @@ HubUnit::HubUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { setArmor(inUnit->getArmor()); } -TowerUnit::TowerUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { +TowerUnit::TowerUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) { setID(inUnit->getID()); setPos(inUnit->getPosX(), inUnit->getPosY()); setDistanceTo(inUnit->getDistanceTo()); @@ -720,7 +728,7 @@ TowerUnit::TowerUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { setArmor(inUnit->getArmor()); } -BridgeUnit::BridgeUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { +BridgeUnit::BridgeUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) { setID(inUnit->getID()); setPos(inUnit->getPosX(), inUnit->getPosY()); setDistanceTo(inUnit->getDistanceTo()); @@ -729,7 +737,7 @@ BridgeUnit::BridgeUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { setArmor(inUnit->getArmor()); } -EnergyUnit::EnergyUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { +EnergyUnit::EnergyUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) { setID(inUnit->getID()); setPos(inUnit->getPosX(), inUnit->getPosY()); setDistanceTo(inUnit->getDistanceTo()); @@ -738,7 +746,7 @@ EnergyUnit::EnergyUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { setArmor(inUnit->getArmor()); } -OffenseUnit::OffenseUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { +OffenseUnit::OffenseUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) { setID(inUnit->getID()); setPos(inUnit->getPosX(), inUnit->getPosY()); setDistanceTo(inUnit->getDistanceTo()); @@ -747,7 +755,7 @@ OffenseUnit::OffenseUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { setArmor(inUnit->getArmor()); } -CrawlerUnit::CrawlerUnit(DefenseUnit *inUnit) : DefenseUnit(inUnit) { +CrawlerUnit::CrawlerUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) { setID(inUnit->getID()); setPos(inUnit->getPosX(), inUnit->getPosY()); setDistanceTo(inUnit->getDistanceTo()); diff --git a/engines/scumm/he/moonbase/ai_defenseunit.h b/engines/scumm/he/moonbase/ai_defenseunit.h index e658fc31ab..a6085da859 100644 --- a/engines/scumm/he/moonbase/ai_defenseunit.h +++ b/engines/scumm/he/moonbase/ai_defenseunit.h @@ -25,6 +25,8 @@ namespace Scumm { +class AI; + enum { DUT_ANTI_AIR = 1, DUT_SHIELD = 2, @@ -53,9 +55,12 @@ private: int _armor; int _cost; +protected: + AI *_ai; + public: - DefenseUnit(); - DefenseUnit(DefenseUnit *inUnit); + DefenseUnit(AI *ai); + DefenseUnit(DefenseUnit *inUnit, AI *ai); virtual ~DefenseUnit(); @@ -90,8 +95,8 @@ class AntiAirUnit : public DefenseUnit { private: public: - AntiAirUnit(); - AntiAirUnit(DefenseUnit *inUnit); + AntiAirUnit(AI *ai); + AntiAirUnit(DefenseUnit *inUnit, AI *ai); int getType() const { return DUT_ANTI_AIR; } Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); int selectWeapon(int index); @@ -101,8 +106,8 @@ class ShieldUnit : public DefenseUnit { private: public: - ShieldUnit(); - ShieldUnit(DefenseUnit *inUnit); + ShieldUnit(AI *ai); + ShieldUnit(DefenseUnit *inUnit, AI *ai); int getType() const { return DUT_SHIELD; } Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); int selectWeapon(int index); @@ -112,8 +117,8 @@ class MineUnit : public DefenseUnit { private: public: - MineUnit(); - MineUnit(DefenseUnit *inUnit); + MineUnit(AI *ai); + MineUnit(DefenseUnit *inUnit, AI *ai); int getType() const { return DUT_MINE; } Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); int selectWeapon(int index); @@ -123,8 +128,8 @@ class HubUnit : public DefenseUnit { private: public: - HubUnit(); - HubUnit(DefenseUnit *inUnit); + HubUnit(AI *ai); + HubUnit(DefenseUnit *inUnit, AI *ai); int getType() const { return DUT_HUB; } Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); int selectWeapon(int index); @@ -134,8 +139,8 @@ class TowerUnit : public DefenseUnit { private: public: - TowerUnit(); - TowerUnit(DefenseUnit *inUnit); + TowerUnit(AI *ai); + TowerUnit(DefenseUnit *inUnit, AI *ai); int getType() const { return DUT_TOWER; } Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); int selectWeapon(int index); @@ -145,8 +150,8 @@ class BridgeUnit : public DefenseUnit { private: public: - BridgeUnit(); - BridgeUnit(DefenseUnit *inUnit); + BridgeUnit(AI *ai); + BridgeUnit(DefenseUnit *inUnit, AI *ai); int getType() const { return DUT_BRIDGE; } Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); int selectWeapon(int index); @@ -156,8 +161,8 @@ class EnergyUnit : public DefenseUnit { private: public: - EnergyUnit(); - EnergyUnit(DefenseUnit *inUnit); + EnergyUnit(AI *ai); + EnergyUnit(DefenseUnit *inUnit, AI *ai); int getType() const { return DUT_ENERGY; } Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); int selectWeapon(int index); @@ -167,8 +172,8 @@ class OffenseUnit : public DefenseUnit { private: public: - OffenseUnit(); - OffenseUnit(DefenseUnit *inUnit); + OffenseUnit(AI *ai); + OffenseUnit(DefenseUnit *inUnit, AI *ai); int getType() const { return DUT_OFFENSE; } Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); int selectWeapon(int index); @@ -178,8 +183,8 @@ class CrawlerUnit : public DefenseUnit { private: public: - CrawlerUnit(); - CrawlerUnit(DefenseUnit *inUnit); + CrawlerUnit(AI *ai); + CrawlerUnit(DefenseUnit *inUnit, AI *ai); int getType() const { return DUT_CRAWLER; } Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY); int selectWeapon(int index); diff --git a/engines/scumm/he/moonbase/ai_main.cpp b/engines/scumm/he/moonbase/ai_main.cpp index 1a191a1328..9c9ff8bb01 100644 --- a/engines/scumm/he/moonbase/ai_main.cpp +++ b/engines/scumm/he/moonbase/ai_main.cpp @@ -31,8 +31,6 @@ namespace Scumm { -ScummEngine_v90he *_vm; - enum { F_GET_SCUMM_DATA = 0, F_GET_WORLD_DIST = 1, @@ -154,84 +152,80 @@ enum { TREE_DEPTH = 2 }; -AIEntity *AItype[5]; - -int AIstate = STATE_CHOOSE_BEHAVIOR; -int behavior = 2; - -patternList *moveList[5]; +AI::AI(ScummEngine_v100he *vm) : _vm(vm) { + memset(_aiType, 0, sizeof(_aiType)); + _aiState = STATE_CHOOSE_BEHAVIOR; + _behavior = 2; + _energyHogType = 0; -int *storedLaunchAction[5] = {NULL}; - -const int *MCP_params; - -Common::Array<int> lastXCoord[5]; -Common::Array<int> lastYCoord[5]; + memset(_moveList, 0, sizeof(_moveList)); + _mcpParams = 0; +} -void resetAI() { - AIstate = STATE_CHOOSE_BEHAVIOR; +void AI::resetAI() { + _aiState = STATE_CHOOSE_BEHAVIOR; warning("----------------------> Resetting AI"); for (int i = 1; i != 5; i++) { - if (AItype[i]) { - delete AItype[i]; - AItype[i] = NULL; + if (_aiType[i]) { + delete _aiType[i]; + _aiType[i] = NULL; } - AItype[i] = new AIEntity(BRUTAKAS); + _aiType[i] = new AIEntity(BRUTAKAS); } for (int i = 1; i != 5; i++) { - if (moveList[i]) { - delete moveList[i]; - moveList[i] = NULL; + if (_moveList[i]) { + delete _moveList[i]; + _moveList[i] = NULL; } - moveList[i] = new patternList; + _moveList[i] = new patternList; } } -void cleanUpAI() { +void AI::cleanUpAI() { warning("----------------------> Cleaning Up AI"); for (int i = 1; i != 5; i++) { - if (AItype[i]) { - delete AItype[i]; - AItype[i] = NULL; + if (_aiType[i]) { + delete _aiType[i]; + _aiType[i] = NULL; } } for (int i = 1; i != 5; i++) { - if (moveList[i]) { - delete moveList[i]; - moveList[i] = NULL; + if (_moveList[i]) { + delete _moveList[i]; + _moveList[i] = NULL; } } } -void setAIType(const int paramCount, const int *params) { - if (AItype[params[AI_TYPE_PLAYER_NUM]]) { - delete AItype[params[AI_TYPE_PLAYER_NUM]]; - AItype[params[AI_TYPE_PLAYER_NUM]] = NULL; +void AI::setAIType(const int paramCount, const int32 *params) { + if (_aiType[params[AI_TYPE_PLAYER_NUM]]) { + delete _aiType[params[AI_TYPE_PLAYER_NUM]]; + _aiType[params[AI_TYPE_PLAYER_NUM]] = NULL; } - AItype[params[AI_TYPE_PLAYER_NUM]] = new AIEntity(params[AI_TYPE_TYPE]); + _aiType[params[AI_TYPE_PLAYER_NUM]] = new AIEntity(params[AI_TYPE_TYPE]); if (params[AI_TYPE_TYPE] == ENERGY_HOG) { - energyHogType = 1; + _energyHogType = 1; } else { - energyHogType = 0; + _energyHogType = 0; } - warning("AI for player %d is %s", params[AI_TYPE_PLAYER_NUM], AItype[params[AI_TYPE_PLAYER_NUM]]->getNameString()); + warning("AI for player %d is %s", params[AI_TYPE_PLAYER_NUM], _aiType[params[AI_TYPE_PLAYER_NUM]]->getNameString()); } -int masterControlProgram(const int paramCount, const int *params) { +int AI::masterControlProgram(const int paramCount, const int32 *params) { static Tree *myTree; static int index; - MCP_params = params; + _mcpParams = params; static int lastSource[5]; static int lastAngle[5]; @@ -282,7 +276,7 @@ int masterControlProgram(const int paramCount, const int *params) { int timerValue = getTimerValue(3); // If timer has run out - if ((AIstate > STATE_CHOOSE_BEHAVIOR) && ((maxTime) && (timerValue > maxTime))) { + if ((_aiState > STATE_CHOOSE_BEHAVIOR) && ((maxTime) && (timerValue > maxTime))) { if (myTree != NULL) { delete myTree; myTree = NULL; @@ -309,41 +303,43 @@ int masterControlProgram(const int paramCount, const int *params) { launchAction[1] = SKIP_TURN; } - AIstate = STATE_LAUNCH; + _aiState = STATE_LAUNCH; } - static int oldAIstate = 0; + static int old_aiState = 0; - if (oldAIstate != AIstate) { - warning("<<%d>>", AIstate); - oldAIstate = AIstate; + if (old_aiState != _aiState) { + warning("<<%d>>", _aiState); + old_aiState = _aiState; } - switch (AIstate) { + switch (_aiState) { case STATE_CHOOSE_BEHAVIOR: - behavior = chooseBehavior(); - warning("Behavior mode: %d", behavior); + _behavior = chooseBehavior(); + warning("Behavior mode: %d", _behavior); - if (_vm->_rnd.getRandomNumber(99) < AItype[getCurrentPlayer()]->getBehaviorVariation() * AI_VAR_BASE_BEHAVIOR + 1) { + if ((int)_vm->_rnd.getRandomNumber(99) < _aiType[getCurrentPlayer()]->getBehaviorVariation() * AI_VAR_BASE_BEHAVIOR + 1) { if (_vm->_rnd.getRandomNumber(1)) { - behavior--; + _behavior--; - if (behavior < ENERGY_MODE) behavior = DEFENSE_MODE; + if (_behavior < ENERGY_MODE) + _behavior = DEFENSE_MODE; } else { - behavior++; + _behavior++; - if (behavior > DEFENSE_MODE) behavior = ENERGY_MODE; + if (_behavior > DEFENSE_MODE) + _behavior = ENERGY_MODE; } - warning("Alternative behavior: %d", behavior); + warning("Alternative behavior: %d", _behavior); } - if (behavior == ENERGY_MODE) + if (_behavior == ENERGY_MODE) if (!getNumberOfPools()) - behavior = OFFENSE_MODE; + _behavior = OFFENSE_MODE; - if (AItype[getCurrentPlayer()]->getID() == CRAWLER_CHUCKER) - behavior = OFFENSE_MODE; + if (_aiType[getCurrentPlayer()]->getID() == CRAWLER_CHUCKER) + _behavior = OFFENSE_MODE; if (launchAction != NULL) { delete launchAction; @@ -351,16 +347,16 @@ int masterControlProgram(const int paramCount, const int *params) { } index = 0; - AIstate = STATE_CHOOSE_TARGET; + _aiState = STATE_CHOOSE_TARGET; break; case STATE_CHOOSE_TARGET: - target = chooseTarget(behavior); + target = chooseTarget(_behavior); if (!target) target = chooseTarget(OFFENSE_MODE); - if (behavior == ENERGY_MODE) { + if (_behavior == ENERGY_MODE) { int energyPoolScummArray = getEnergyPoolsArray(); targetX = _vm->_moonbase->readFromArray(energyPoolScummArray, target, ENERGY_POOL_X); targetY = _vm->_moonbase->readFromArray(energyPoolScummArray, target, ENERGY_POOL_Y); @@ -372,9 +368,9 @@ int masterControlProgram(const int paramCount, const int *params) { warning("Target (%d, %d) id: %d", targetX, targetY, target); if (getFOW()) - AIstate = STATE_ATTEMPT_SEARCH; + _aiState = STATE_ATTEMPT_SEARCH; else - AIstate = STATE_INIT_APPROACH_TARGET; + _aiState = STATE_INIT_APPROACH_TARGET; break; @@ -404,7 +400,7 @@ int masterControlProgram(const int paramCount, const int *params) { nextBuilding = _vm->_moonbase->readFromArray(unitsArray, 0, unitsCounter); } - _vm->nukeArray(unitsArray); + _vm->_moonbase->deallocateArray(unitsArray); if (!balloonFlag) { launchAction = new int[4]; @@ -424,26 +420,26 @@ int masterControlProgram(const int paramCount, const int *params) { targetX = newTargetPos % getMaxX(); targetY = newTargetPos / getMaxY(); - AIstate = STATE_INIT_ACQUIRE_TARGET; + _aiState = STATE_INIT_ACQUIRE_TARGET; break; } } - AIstate = STATE_INIT_APPROACH_TARGET; + _aiState = STATE_INIT_APPROACH_TARGET; break; case STATE_INIT_APPROACH_TARGET: { int closestOL = getClosestUnit(targetX, targetY, 900, currentPlayer, 1, BUILDING_OFFENSIVE_LAUNCHER, 1); - if (closestOL && (behavior == OFFENSE_MODE)) { - AIstate = STATE_OFFEND_TARGET; + if (closestOL && (_behavior == OFFENSE_MODE)) { + _aiState = STATE_OFFEND_TARGET; break; } } // get closest hub...if attack mode and almost close enough, maybe throw an offense - if ((behavior == OFFENSE_MODE) && (getPlayerEnergy() > 6)) { + if ((_behavior == OFFENSE_MODE) && (getPlayerEnergy() > 6)) { if (!_vm->_rnd.getRandomNumber(2)) { int closestHub = getClosestUnit(targetX, targetY, getMaxX(), currentPlayer, 1, BUILDING_MAIN_BASE, 1); @@ -458,19 +454,19 @@ int masterControlProgram(const int paramCount, const int *params) { targetX = getHubX(closestHub); targetY = getHubY(closestHub); - AIstate = STATE_DEFEND_TARGET; + _aiState = STATE_DEFEND_TARGET; break; } } } } - if ((behavior == OFFENSE_MODE) && (AItype[currentPlayer]->getID() == RANGER) && (getPlayerEnergy() > 2)) { + if ((_behavior == OFFENSE_MODE) && (_aiType[currentPlayer]->getID() == RANGER) && (getPlayerEnergy() > 2)) { int closestHub = getClosestUnit(targetX, targetY, getMaxX(), currentPlayer, 1, BUILDING_MAIN_BASE, 1); int dist = getDistance(targetX, targetY, getHubX(closestHub), getHubY(closestHub)); if (dist < 750) { - AIstate = STATE_OFFEND_TARGET; + _aiState = STATE_OFFEND_TARGET; break; } } @@ -479,21 +475,21 @@ int masterControlProgram(const int paramCount, const int *params) { // If no need to approach, apply appropriate behavior if (retNode == myTree->getBaseNode()) { - switch (behavior) { + switch (_behavior) { case 0: - AIstate = STATE_ENERGIZE_TARGET; + _aiState = STATE_ENERGIZE_TARGET; break; case 1: - AIstate = STATE_OFFEND_TARGET; + _aiState = STATE_OFFEND_TARGET; break; case 2: - AIstate = STATE_DEFEND_TARGET; + _aiState = STATE_DEFEND_TARGET; break; case -1: - AIstate = STATE_LAUNCH; + _aiState = STATE_LAUNCH; break; } @@ -507,8 +503,8 @@ int masterControlProgram(const int paramCount, const int *params) { if (getPlayerEnergy() < 7) { if (!_vm->_rnd.getRandomNumber(3)) { - behavior = DEFENSE_MODE; - AIstate = STATE_CHOOSE_TARGET; + _behavior = DEFENSE_MODE; + _aiState = STATE_CHOOSE_TARGET; } else { if (launchAction == NULL) { launchAction = new int[4]; @@ -520,7 +516,7 @@ int masterControlProgram(const int paramCount, const int *params) { launchAction[1] = SKIP_TURN; } - AIstate = STATE_LAUNCH; + _aiState = STATE_LAUNCH; } delete myTree; @@ -528,7 +524,7 @@ int masterControlProgram(const int paramCount, const int *params) { break; } - AIstate = STATE_CRAWLER_DECISION; + _aiState = STATE_CRAWLER_DECISION; break; // If behavior is offense, possibly just chuck a crawler @@ -537,7 +533,7 @@ int masterControlProgram(const int paramCount, const int *params) { // Brace just here to scope throwCrawler int throwCrawler = 0; - if (behavior == OFFENSE_MODE) { + if (_behavior == OFFENSE_MODE) { if (getPlayerEnergy() > 6) { int crawlerTest = _vm->_rnd.getRandomNumber(9); @@ -546,7 +542,7 @@ int masterControlProgram(const int paramCount, const int *params) { } } - if (AItype[getCurrentPlayer()]->getID() == CRAWLER_CHUCKER) { + if (_aiType[getCurrentPlayer()]->getID() == CRAWLER_CHUCKER) { if (getPlayerEnergy() > 6) { throwCrawler = 1; } else { @@ -557,7 +553,7 @@ int masterControlProgram(const int paramCount, const int *params) { else launchAction[1] = SKIP_TURN; - AIstate = STATE_LAUNCH; + _aiState = STATE_LAUNCH; delete myTree; myTree = NULL; } @@ -585,11 +581,11 @@ int masterControlProgram(const int paramCount, const int *params) { targetX = (targetX + getMaxX()) % getMaxX(); targetY = (targetY + getMaxY()) % getMaxY(); - AIstate = STATE_INIT_ACQUIRE_TARGET; + _aiState = STATE_INIT_ACQUIRE_TARGET; delete myTree; myTree = NULL; } else { - AIstate = STATE_APPROACH_TARGET; + _aiState = STATE_APPROACH_TARGET; } break; } @@ -612,7 +608,7 @@ int masterControlProgram(const int paramCount, const int *params) { delete launchAction; launchAction = NULL; - AIstate = STATE_DEFEND_TARGET; + _aiState = STATE_DEFEND_TARGET; delete myTree; myTree = NULL; break; @@ -625,8 +621,8 @@ int masterControlProgram(const int paramCount, const int *params) { targetX = (targetX + getMaxX()) % getMaxX(); targetY = (targetY + getMaxY()) % getMaxY(); - AIstate = STATE_INIT_ACQUIRE_TARGET; - behavior = -1; + _aiState = STATE_INIT_ACQUIRE_TARGET; + _behavior = -1; delete myTree; myTree = NULL; @@ -644,10 +640,10 @@ int masterControlProgram(const int paramCount, const int *params) { if (launchAction[1] == ITEM_HUB) { index = 0; retNodeFlag = 0; - AIstate = STATE_INIT_ACQUIRE_TARGET; + _aiState = STATE_INIT_ACQUIRE_TARGET; } else { index = 0; - AIstate = STATE_INIT_ACQUIRE_TARGET; + _aiState = STATE_INIT_ACQUIRE_TARGET; } } else { index++; @@ -655,10 +651,10 @@ int masterControlProgram(const int paramCount, const int *params) { launchAction = NULL; } } else { - behavior = DEFENSE_MODE; + _behavior = DEFENSE_MODE; retNodeFlag = 0; index = 0; - AIstate = STATE_CHOOSE_TARGET; + _aiState = STATE_CHOOSE_TARGET; } break; @@ -668,7 +664,7 @@ int masterControlProgram(const int paramCount, const int *params) { if (launchAction != NULL) { if (launchAction[0]) { retNodeFlag = 0; - AIstate = STATE_INIT_ACQUIRE_TARGET; + _aiState = STATE_INIT_ACQUIRE_TARGET; } else { index++; delete launchAction; @@ -683,7 +679,7 @@ int masterControlProgram(const int paramCount, const int *params) { if (launchAction != NULL) { if (launchAction[0]) { retNodeFlag = 0; - AIstate = STATE_INIT_ACQUIRE_TARGET; + _aiState = STATE_INIT_ACQUIRE_TARGET; if (launchAction[LAUNCH_UNIT] != ITEM_BRIDGE) { if (OLflag) { @@ -709,7 +705,7 @@ int masterControlProgram(const int paramCount, const int *params) { myTree = initAcquireTarget(targetX, targetY, &retNode); if (myTree == NULL) { - AIstate = STATE_LAUNCH; + _aiState = STATE_LAUNCH; break; } @@ -719,7 +715,7 @@ int masterControlProgram(const int paramCount, const int *params) { _acquireTarget = 0; - AIstate = STATE_ACQUIRE_TARGET; + _aiState = STATE_ACQUIRE_TARGET; break; case STATE_ACQUIRE_TARGET: { @@ -761,7 +757,7 @@ int masterControlProgram(const int paramCount, const int *params) { assert(launchAction != NULL); delete myTree; myTree = NULL; - AIstate = STATE_LAUNCH; + _aiState = STATE_LAUNCH; } } break; @@ -770,7 +766,7 @@ int masterControlProgram(const int paramCount, const int *params) { break; } - if (AIstate == STATE_LAUNCH) { + if (_aiState == STATE_LAUNCH) { static float randomAttenuation = 1; if (((launchAction[LAUNCH_UNIT] == ITEM_REPAIR) || (launchAction[LAUNCH_UNIT] == ITEM_ANTIAIR) || (launchAction[LAUNCH_UNIT] == ITEM_BRIDGE) || (launchAction[LAUNCH_UNIT] == ITEM_TOWER) || (launchAction[LAUNCH_UNIT] == ITEM_RECLAIMER) || (launchAction[LAUNCH_UNIT] == ITEM_BALLOON) || (launchAction[LAUNCH_UNIT] == ITEM_MINE) || (launchAction[LAUNCH_UNIT] == ITEM_ENERGY) || (launchAction[LAUNCH_UNIT] == ITEM_SHIELD) || (launchAction[LAUNCH_UNIT] == ITEM_OFFENSE) || (launchAction[LAUNCH_UNIT] == ITEM_HUB)) && (getBuildingType(launchAction[LAUNCH_SOURCE_HUB]) == BUILDING_OFFENSIVE_LAUNCHER)) { @@ -821,8 +817,7 @@ int masterControlProgram(const int paramCount, const int *params) { { // ANGLE setting - int angleAdjustment = 0; - angleAdjustment = _vm->_rnd.getRandomNumber(AItype[getCurrentPlayer()]->getAngleVariation() * AI_VAR_BASE_ANGLE) * 3.6; + int angleAdjustment = (int)(_vm->_rnd.getRandomNumber(_aiType[getCurrentPlayer()]->getAngleVariation() * AI_VAR_BASE_ANGLE) * 3.6); //pos or neg choice angleAdjustment *= ((_vm->_rnd.getRandomNumber(1) * 2) - 1); angleAdjustment *= randomAttenuation; @@ -854,7 +849,7 @@ int masterControlProgram(const int paramCount, const int *params) { { // POWER setting int powerRangeFactor = (getMaxPower() - getMinPower()) / 100; - int powerAdjustment = static_cast<float>(_vm->_rnd.getRandomNumber(AItype[getCurrentPlayer()]->getPowerVariation() * AI_VAR_BASE_POWER)) * powerRangeFactor; + int powerAdjustment = static_cast<float>(_vm->_rnd.getRandomNumber(_aiType[getCurrentPlayer()]->getPowerVariation() * AI_VAR_BASE_POWER)) * powerRangeFactor; //pos or neg choice powerAdjustment *= ((_vm->_rnd.getRandomNumber(1) * 2) - 1); powerAdjustment *= randomAttenuation; @@ -904,7 +899,7 @@ int masterControlProgram(const int paramCount, const int *params) { opponentBuilding = _vm->_moonbase->readFromArray(nearbyOpponents, 0, opponentCounter); } - _vm->nukeArray(nearbyOpponents); + _vm->_moonbase->deallocateArray(nearbyOpponents); if (defenseOn && defenseOff) { if (!_vm->_rnd.getRandomNumber(1)) { @@ -936,10 +931,10 @@ int masterControlProgram(const int paramCount, const int *params) { } } - delete launchAction; + delete[] launchAction; launchAction = NULL; - AIstate = STATE_CHOOSE_BEHAVIOR; + _aiState = STATE_CHOOSE_BEHAVIOR; int rSh, rU, rP, rA = 0; rSh = _vm->readVar(_vm->VAR_U32_USER_VAR_A); @@ -951,11 +946,11 @@ int masterControlProgram(const int paramCount, const int *params) { { // Checking for patterns - if ((AItype[currentPlayer]->getID() != CRAWLER_CHUCKER) && - (AItype[currentPlayer]->getID() != ENERGY_HOG) && (getBuildingStackPtr() > 5)) - moveList[currentPlayer]->addPattern(rSh, rU, rP, rA); + if ((_aiType[currentPlayer]->getID() != CRAWLER_CHUCKER) && + (_aiType[currentPlayer]->getID() != ENERGY_HOG) && (getBuildingStackPtr() > 5)) + _moveList[currentPlayer]->addPattern(rSh, rU, rP, rA); - int patternFound = moveList[currentPlayer]->evaluatePattern(rSh, rU, rP, rA); + int patternFound = _moveList[currentPlayer]->evaluatePattern(rSh, rU, rP, rA); if (!_vm->_rnd.getRandomNumber(9)) patternFound = 0; @@ -964,26 +959,26 @@ int masterControlProgram(const int paramCount, const int *params) { warning("------------------------------------------>Eliminating pattern"); if (_vm->_rnd.getRandomNumber(1)) { - behavior--; + _behavior--; - if (behavior < ENERGY_MODE) - behavior = DEFENSE_MODE; + if (_behavior < ENERGY_MODE) + _behavior = DEFENSE_MODE; } else { - behavior++; + _behavior++; - if (behavior > DEFENSE_MODE) - behavior = ENERGY_MODE; + if (_behavior > DEFENSE_MODE) + _behavior = ENERGY_MODE; } - if (behavior == ENERGY_MODE) + if (_behavior == ENERGY_MODE) if (!getNumberOfPools()) - behavior = OFFENSE_MODE; + _behavior = OFFENSE_MODE; _vm->writeVar(_vm->VAR_U32_USER_VAR_A, 0); _vm->writeVar(_vm->VAR_U32_USER_VAR_B, 0); _vm->writeVar(_vm->VAR_U32_USER_VAR_C, 0); _vm->writeVar(_vm->VAR_U32_USER_VAR_D, 0); - AIstate = STATE_CHOOSE_TARGET; + _aiState = STATE_CHOOSE_TARGET; } } @@ -1015,7 +1010,7 @@ int masterControlProgram(const int paramCount, const int *params) { if (selectedUnit) { if (selectedUnit > 0) - _vm->writeVar(_vm->VAR_U32_USER_VAR_E, behavior); + _vm->writeVar(_vm->VAR_U32_USER_VAR_E, _behavior); else _vm->writeVar(_vm->VAR_U32_USER_VAR_E, -999); } @@ -1023,102 +1018,62 @@ int masterControlProgram(const int paramCount, const int *params) { return 1; } -int chooseBehavior() { +int AI::chooseBehavior() { static int dominantMode = 0; - int modeValue[3]; - - const int DEFAULT_SCALE = 5; - if (getBuildingStackPtr() < 5) return OFFENSE_MODE; int currentPlayer = getCurrentPlayer(); - int AIpersonality = AItype[currentPlayer]->getID(); + int AIpersonality = _aiType[currentPlayer]->getID(); switch (AIpersonality) { case BRUTAKAS: - modeValue[ENERGY_MODE] = DEFAULT_SCALE; - modeValue[OFFENSE_MODE] = 2 * DEFAULT_SCALE; - modeValue[DEFENSE_MODE] = 0; dominantMode = OFFENSE_MODE; break; case AGI: - modeValue[ENERGY_MODE] = DEFAULT_SCALE; - modeValue[OFFENSE_MODE] = 0; - modeValue[DEFENSE_MODE] = 2 * DEFAULT_SCALE; dominantMode = DEFENSE_MODE; break; case EL_GATO: - modeValue[ENERGY_MODE] = 2 * DEFAULT_SCALE; - modeValue[OFFENSE_MODE] = DEFAULT_SCALE; - modeValue[DEFENSE_MODE] = 0; dominantMode = ENERGY_MODE; break; case PIXELAHT: - modeValue[ENERGY_MODE] = DEFAULT_SCALE; - modeValue[OFFENSE_MODE] = 0; - modeValue[DEFENSE_MODE] = DEFAULT_SCALE; dominantMode = DEFENSE_MODE; break; case CYBALL: - modeValue[ENERGY_MODE] = 0; - modeValue[OFFENSE_MODE] = 0; - modeValue[DEFENSE_MODE] = 0; dominantMode = ENERGY_MODE; break; case NEEP: - modeValue[ENERGY_MODE] = 0; - modeValue[OFFENSE_MODE] = DEFAULT_SCALE; - modeValue[DEFENSE_MODE] = DEFAULT_SCALE; dominantMode = DEFENSE_MODE; break; case WARCUPINE: - modeValue[ENERGY_MODE] = 5 * DEFAULT_SCALE; - modeValue[OFFENSE_MODE] = DEFAULT_SCALE; - modeValue[DEFENSE_MODE] = 0; dominantMode = ENERGY_MODE; break; case AONE: - modeValue[ENERGY_MODE] = 0; - modeValue[OFFENSE_MODE] = DEFAULT_SCALE; - modeValue[DEFENSE_MODE] = 2 * DEFAULT_SCALE; dominantMode = DEFENSE_MODE; break; case SPANDO: - modeValue[ENERGY_MODE] = DEFAULT_SCALE; - modeValue[OFFENSE_MODE] = DEFAULT_SCALE; - modeValue[DEFENSE_MODE] = DEFAULT_SCALE; dominantMode = ENERGY_MODE; break; case ORBNU_LUNATEK: - modeValue[ENERGY_MODE] = 0; - modeValue[OFFENSE_MODE] = 0; - modeValue[DEFENSE_MODE] = 0; dominantMode = ENERGY_MODE; break; case CRAWLER_CHUCKER: - modeValue[ENERGY_MODE] = 0; - modeValue[OFFENSE_MODE] = 2 * DEFAULT_SCALE; - modeValue[DEFENSE_MODE] = 0; dominantMode = OFFENSE_MODE; break; case ENERGY_HOG: - modeValue[ENERGY_MODE] = 2 * DEFAULT_SCALE; - modeValue[OFFENSE_MODE] = 0; - modeValue[DEFENSE_MODE] = 0; dominantMode = ENERGY_MODE; { int breakFlag = 0; @@ -1133,9 +1088,6 @@ int chooseBehavior() { break; case RANGER: - modeValue[ENERGY_MODE] = 2 * DEFAULT_SCALE; - modeValue[OFFENSE_MODE] = 0; - modeValue[DEFENSE_MODE] = 0; dominantMode = OFFENSE_MODE; //random chance of defense if really early in game, otherwise offense @@ -1146,9 +1098,6 @@ int chooseBehavior() { break; default: //BRUTAKAS - modeValue[ENERGY_MODE] = DEFAULT_SCALE; - modeValue[OFFENSE_MODE] = 2 * DEFAULT_SCALE; - modeValue[DEFENSE_MODE] = 0; dominantMode = OFFENSE_MODE; break; } @@ -1207,7 +1156,7 @@ int chooseBehavior() { energyBuilding = _vm->_moonbase->readFromArray(energyUnits, 0, energyCounter); } - _vm->nukeArray(energyUnits); + _vm->_moonbase->deallocateArray(energyUnits); if (energyCount < poolMaxCount) { int closestHub = getClosestUnit(poolX, poolY, 300, currentPlayer, 1, BUILDING_MAIN_BASE, 1); @@ -1300,7 +1249,7 @@ int chooseBehavior() { defenseBuilding = _vm->_moonbase->readFromArray(defArray, 0, defCounter); } - _vm->nukeArray(defArray); + _vm->_moonbase->deallocateArray(defArray); if (!numDefenders) { int defArray2 = getUnitsWithinRadius(hubX, hubY, 170); @@ -1319,7 +1268,7 @@ int chooseBehavior() { defenseBuilding2 = _vm->_moonbase->readFromArray(defArray, 0, defCounter); } - _vm->nukeArray(defArray2); + _vm->_moonbase->deallocateArray(defArray2); } @@ -1357,7 +1306,7 @@ int chooseBehavior() { } } - _vm->nukeArray(enemyArray); + _vm->_moonbase->deallocateArray(enemyArray); offCon--; } @@ -1406,7 +1355,7 @@ int chooseBehavior() { closeBuilding = _vm->_moonbase->readFromArray(closeBuildingsArray, 0, closeBuildingCounter); } - _vm->nukeArray(closeBuildingsArray); + _vm->_moonbase->deallocateArray(closeBuildingsArray); int defCounter = 0; int defArray = getUnitsWithinRadius(hubX, hubY, 170); @@ -1425,7 +1374,7 @@ int chooseBehavior() { defenseBuilding = _vm->_moonbase->readFromArray(defArray, 0, defCounter); } - _vm->nukeArray(defArray); + _vm->_moonbase->deallocateArray(defArray); if (numDefenders > 2) defCon++; @@ -1463,7 +1412,7 @@ int chooseBehavior() { defCon += 2; } - warning("%s-------------------------------> Energy: %d Offense: %d Defense: %d", AItype[currentPlayer]->getNameString(), eneCon, offCon, defCon); + warning("%s-------------------------------> Energy: %d Offense: %d Defense: %d", _aiType[currentPlayer]->getNameString(), eneCon, offCon, defCon); if (dominantMode == DEFENSE_MODE) if ((defCon <= offCon) && (defCon <= eneCon)) @@ -1489,7 +1438,7 @@ int chooseBehavior() { return -1; } -int chooseTarget(int behavior1) { +int AI::chooseTarget(int behavior) { int numPools = getNumberOfPools(); int currentPlayer = getCurrentPlayer(); @@ -1497,7 +1446,7 @@ int chooseTarget(int behavior1) { int selectionValues[50] = {0}; int selectionDist = 10000000; - if (behavior1 == ENERGY_MODE) { + if (behavior == ENERGY_MODE) { // loop through energy pool array int energyPoolScummArray = getEnergyPoolsArray(); @@ -1524,7 +1473,7 @@ int chooseTarget(int behavior1) { thisPoolUnit = _vm->_moonbase->readFromArray(poolUnitsArray, 0, j); } - _vm->nukeArray(poolUnitsArray); + _vm->_moonbase->deallocateArray(poolUnitsArray); // if enemy collector, put at bottom if (enemyPool) { @@ -1558,7 +1507,7 @@ int chooseTarget(int behavior1) { return selection; } - if (behavior1 == OFFENSE_MODE) { + if (behavior == OFFENSE_MODE) { int returnBuilding = 0; int attackableArray[500]; int nearAttackableArray[500]; @@ -1575,7 +1524,7 @@ int chooseTarget(int behavior1) { if ((getTerrain(getHubX(thisBuilding), getHubY(thisBuilding)) != TERRAIN_TYPE_WATER) || (getPlayerEnergy() > 6)) { if (getClosestUnit(getHubX(thisBuilding), getHubY(thisBuilding), 380, currentPlayer, 1, BUILDING_MAIN_BASE, 1)) { returnBuilding = thisBuilding; - _vm->nukeArray(enemyArray); + _vm->_moonbase->deallocateArray(enemyArray); return returnBuilding; } } else { @@ -1601,7 +1550,7 @@ int chooseTarget(int behavior1) { } } - _vm->nukeArray(enemyArray); + _vm->_moonbase->deallocateArray(enemyArray); if (attackableIndex) { int thisWorth = 0; @@ -1690,7 +1639,7 @@ int chooseTarget(int behavior1) { return returnBuilding; } - if (behavior1 == DEFENSE_MODE) { + if (behavior == DEFENSE_MODE) { int returnBuilding = 0; int savedTally = 0; @@ -1761,7 +1710,7 @@ int chooseTarget(int behavior1) { defenseBuilding = _vm->_moonbase->readFromArray(defenseArray, 0, j); } - _vm->nukeArray(defenseArray); + _vm->_moonbase->deallocateArray(defenseArray); // Calculate if this unit is attackable // get dist to nearest enemy hub, offense @@ -1848,15 +1797,15 @@ int chooseTarget(int behavior1) { return 0; } -Tree *initApproachTarget(int targetX, int targetY, Node **retNode) { +Tree *AI::initApproachTarget(int targetX, int targetY, Node **retNode) { int sourceHub = 0; - if (behavior == 2) + if (_behavior == 2) sourceHub = getClosestUnit(targetX + 10, targetY, getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1); else sourceHub = getClosestUnit(targetX + 10, targetY, getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, MIN_DIST); - Traveller *myTraveller = new Traveller(getHubX(sourceHub), getHubY(sourceHub)); + Traveller *myTraveller = new Traveller(getHubX(sourceHub), getHubY(sourceHub), this); myTraveller->setSourceHub(sourceHub); //target adjustment so that room is allowed for the appropriate shot @@ -1868,17 +1817,17 @@ Tree *initApproachTarget(int targetX, int targetY, Node **retNode) { Traveller::setTargetPosY(targetY + adjY); Traveller::setMaxDist(340); - Tree *myTree = new Tree(myTraveller, TREE_DEPTH); + Tree *myTree = new Tree(myTraveller, TREE_DEPTH, this); *retNode = myTree->aStarSearch_singlePassInit(); return myTree; } -int *approachTarget(Tree *myTree, int &xTarget, int &yTarget, Node **currentNode) { +int *AI::approachTarget(Tree *myTree, int &xTarget, int &yTarget, Node **currentNode) { int *retVal = NULL; *currentNode = NULL; - Node *retNode = myTree->aStarSearch_singlePass(currentNode); + Node *retNode = myTree->aStarSearch_singlePass(); if (*currentNode != NULL) warning("########################################### Got a possible solution"); @@ -1928,13 +1877,13 @@ int *approachTarget(Tree *myTree, int &xTarget, int &yTarget, Node **currentNode int whoseTurn = getCurrentPlayer(); - if ((lastXCoord[whoseTurn]).size() >= MAX_MEMORY) { - (lastXCoord[whoseTurn]).erase((lastXCoord[whoseTurn]).begin()); - (lastYCoord[whoseTurn]).erase((lastYCoord[whoseTurn]).begin()); + if ((_lastXCoord[whoseTurn]).size() >= MAX_MEMORY) { + (_lastXCoord[whoseTurn]).erase((_lastXCoord[whoseTurn]).begin()); + (_lastYCoord[whoseTurn]).erase((_lastYCoord[whoseTurn]).begin()); } - (lastXCoord[whoseTurn]).push_back(retTraveller->getPosX()); - (lastYCoord[whoseTurn]).push_back(retTraveller->getPosY()); + (_lastXCoord[whoseTurn]).push_back(retTraveller->getPosX()); + (_lastYCoord[whoseTurn]).push_back(retTraveller->getPosY()); int temp = static_cast<int>(retTraveller->calcT()); int temp2 = static_cast<int>(retTraveller->getValueG()); @@ -1948,13 +1897,13 @@ int *approachTarget(Tree *myTree, int &xTarget, int &yTarget, Node **currentNode return retVal; } -Tree *initAcquireTarget(int targetX, int targetY, Node **retNode) { +Tree *AI::initAcquireTarget(int targetX, int targetY, Node **retNode) { int sourceHub = getClosestUnit(targetX, targetY, getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, MIN_DIST); warning("My coords (%d): %d %d", sourceHub, getHubX(sourceHub), getHubY(sourceHub)); Sortie::setSourcePos(getHubX(sourceHub), getHubY(sourceHub)); Sortie::setTargetPos(targetX, targetY); - Sortie *myBaseTarget = new Sortie(); + Sortie *myBaseTarget = new Sortie(this); myBaseTarget->setValueG(0); myBaseTarget->setUnitType(ITEM_BOMB); @@ -1968,26 +1917,24 @@ Tree *initAcquireTarget(int targetX, int targetY, Node **retNode) { int thisElement = _vm->_moonbase->readFromArray(unitsArray, 0, 0); - _vm->nukeArray(unitsArray); + _vm->_moonbase->deallocateArray(unitsArray); if (!thisElement) { delete myBaseTarget; return NULL; } - Tree *myTree = new Tree(myBaseTarget, 4); + Tree *myTree = new Tree(myBaseTarget, 4, this); *retNode = myTree->aStarSearch_singlePassInit(); return myTree; } - -int *acquireTarget(int targetX, int targetY, Tree *myTree, int &errorCode) { +int *AI::acquireTarget(int targetX, int targetY, Tree *myTree, int &errorCode) { int currentPlayer = getCurrentPlayer(); int *retVal = NULL; - Node *currentNode = NULL; - Node *retNode = myTree->aStarSearch_singlePass(¤tNode); + Node *retNode = myTree->aStarSearch_singlePass(); if (myTree->IsBaseNode(retNode)) return acquireTarget(targetX, targetY); @@ -2049,7 +1996,7 @@ int *acquireTarget(int targetX, int targetY, Tree *myTree, int &errorCode) { return retVal; } -int *acquireTarget(int targetX, int targetY) { +int *AI::acquireTarget(int targetX, int targetY) { int *retVal = new int[4]; int sourceHub = getClosestUnit(targetX, targetY, getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 110); @@ -2067,7 +2014,7 @@ int *acquireTarget(int targetX, int targetY) { return retVal; } -int *energizeTarget(int &targetX, int &targetY, int index) { +int *AI::energizeTarget(int &targetX, int &targetY, int index) { int n = 10; static int currentPlayer = 0; static int pool = 0; @@ -2106,7 +2053,7 @@ int *energizeTarget(int &targetX, int &targetY, int index) { } if (poolUnitsArray) - _vm->nukeArray(poolUnitsArray); + _vm->_moonbase->deallocateArray(poolUnitsArray); poolUnitsArray = getUnitsWithinRadius(targetX, targetY, 450); assert(poolUnitsArray); @@ -2219,14 +2166,14 @@ int *energizeTarget(int &targetX, int &targetY, int index) { if (nextUnit <= 0) retVal[0] = 0; - _vm->nukeArray(poolUnitsArray); + _vm->_moonbase->deallocateArray(poolUnitsArray); poolUnitsArray = 0; return retVal; } } if (result > 0) { - _vm->nukeArray(poolUnitsArray); + _vm->_moonbase->deallocateArray(poolUnitsArray); poolUnitsArray = 0; targetX = xPos; @@ -2249,7 +2196,7 @@ int *energizeTarget(int &targetX, int &targetY, int index) { } else { int *retVal = new int[4]; retVal[0] = 0; - _vm->nukeArray(poolUnitsArray); + _vm->_moonbase->deallocateArray(poolUnitsArray); poolUnitsArray = 0; return retVal; @@ -2268,12 +2215,12 @@ int *energizeTarget(int &targetX, int &targetY, int index) { j = 0; } } else { - _vm->nukeArray(poolUnitsArray); + _vm->_moonbase->deallocateArray(poolUnitsArray); poolUnitsArray = 0; return NULL; } - _vm->nukeArray(poolUnitsArray); + _vm->_moonbase->deallocateArray(poolUnitsArray); poolUnitsArray = 0; int *retVal = new int[4]; retVal[0] = 0; @@ -2281,7 +2228,7 @@ int *energizeTarget(int &targetX, int &targetY, int index) { return retVal; } -int *offendTarget(int &targetX, int &targetY, int index) { +int *AI::offendTarget(int &targetX, int &targetY, int index) { int *retVal = NULL; int target = getClosestUnit(targetX + 10, targetY, 20, 0, 0, 0, 0); @@ -2297,35 +2244,35 @@ int *offendTarget(int &targetX, int &targetY, int index) { switch (type) { case BUILDING_OFFENSIVE_LAUNCHER: - thisUnit = new OffenseUnit(); + thisUnit = new OffenseUnit(this); break; case BUILDING_TOWER: - thisUnit = new TowerUnit(); + thisUnit = new TowerUnit(this); break; case BUILDING_MAIN_BASE: - thisUnit = new HubUnit(); + thisUnit = new HubUnit(this); break; case BUILDING_ENERGY_COLLECTOR: - thisUnit = new EnergyUnit(); + thisUnit = new EnergyUnit(this); break; case BUILDING_CRAWLER: - thisUnit = new CrawlerUnit(); + thisUnit = new CrawlerUnit(this); break; case BUILDING_BRIDGE: - thisUnit = new BridgeUnit(); + thisUnit = new BridgeUnit(this); break; case BUILDING_SHIELD: - thisUnit = new ShieldUnit(); + thisUnit = new ShieldUnit(this); break; default: - thisUnit = new HubUnit(); + thisUnit = new HubUnit(this); break; } @@ -2399,9 +2346,9 @@ int *offendTarget(int &targetX, int &targetY, int index) { return retVal; } -int *defendTarget(int &targetX, int &targetY, int index) { +int *AI::defendTarget(int &targetX, int &targetY, int index) { int *retVal = NULL; - Defender *thisDefender = new Defender; + Defender *thisDefender = new Defender(this); int defStatus = thisDefender->calculateDefenseUnitPosition(targetX, targetY, index); if (defStatus > 0) { @@ -2451,337 +2398,339 @@ int *defendTarget(int &targetX, int &targetY, int index) { return retVal; } -int getClosestUnit(int x, int y, int radius, int player, int alignment, int unitType, int checkUnitEnabled) { +int AI::getClosestUnit(int x, int y, int radius, int player, int alignment, int unitType, int checkUnitEnabled) { assert((unitType >= 0) && (unitType <= 12)); - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_CLOSEST_UNIT], 7, x, y, radius, player, alignment, unitType, checkUnitEnabled); + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_CLOSEST_UNIT], 7, x, y, radius, player, alignment, unitType, checkUnitEnabled); return retVal; } -int getClosestUnit(int x, int y, int radius, int player, int alignment, int unitType, int checkUnitEnabled, int minDist) { +int AI::getClosestUnit(int x, int y, int radius, int player, int alignment, int unitType, int checkUnitEnabled, int minDist) { assert((unitType >= 0) && (unitType <= 12)); - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_CLOSEST_UNIT], 8, x, y, radius, player, alignment, unitType, checkUnitEnabled, minDist); + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_CLOSEST_UNIT], 8, x, y, radius, player, alignment, unitType, checkUnitEnabled, minDist); return retVal; } -int getDistance(int originX, int originY, int endX, int endY) { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_WORLD_DIST], 4, originX, originY, endX, endY); +int AI::getDistance(int originX, int originY, int endX, int endY) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_WORLD_DIST], 4, originX, originY, endX, endY); return retVal; } -int calcAngle(int originX, int originY, int endX, int endY) { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_WORLD_ANGLE], 5, originX, originY, endX, endY, 0); +int AI::calcAngle(int originX, int originY, int endX, int endY) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_WORLD_ANGLE], 5, originX, originY, endX, endY, 0); return retVal; } -int calcAngle(int originX, int originY, int endX, int endY, int noWrapFlag) { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_WORLD_ANGLE], 5, originX, originY, endX, endY, noWrapFlag); +int AI::calcAngle(int originX, int originY, int endX, int endY, int noWrapFlag) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_WORLD_ANGLE], 5, originX, originY, endX, endY, noWrapFlag); return retVal; } -int getTerrain(int x, int y) { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_TERRAIN_TYPE], 2, x, y); +int AI::getTerrain(int x, int y) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_TERRAIN_TYPE], 2, x, y); return retVal; } -int estimateNextRoundEnergy(int player) { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_ESTIMATE_NEXT_ROUND_ENERGY], 1, player); +int AI::estimateNextRoundEnergy(int player) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_ESTIMATE_NEXT_ROUND_ENERGY], 1, player); return retVal / 10; } -int getHubX(int hub) { +int AI::getHubX(int hub) { assert(hub >= 0 && hub <= 500); - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_HUB_X, hub); + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_HUB_X, hub); return retVal; } -int getHubY(int hub) { +int AI::getHubY(int hub) { assert(hub >= 0 && hub <= 500); - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_HUB_Y, hub); + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_HUB_Y, hub); return retVal; } -int getMaxX() { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_WORLD_X_SIZE); +int AI::getMaxX() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_WORLD_X_SIZE); return retVal; } -int getMaxY() { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_WORLD_Y_SIZE); +int AI::getMaxY() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_WORLD_Y_SIZE); return retVal; } -int getCurrentPlayer() { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_CURRENT_PLAYER); +int AI::getCurrentPlayer() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_CURRENT_PLAYER); assert(retVal != 0); return retVal; } -int getMaxPower() { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_MAX_POWER); +int AI::getMaxPower() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_MAX_POWER); return retVal; } -int getMinPower() { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_MIN_POWER); +int AI::getMinPower() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_MIN_POWER); return retVal; } -int getTerrainSquareSize() { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_TERRAIN_SQUARE_SIZE); +int AI::getTerrainSquareSize() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_TERRAIN_SQUARE_SIZE); return retVal; } -int getBuildingOwner(int building) { +int AI::getBuildingOwner(int building) { assert((building > 0) && (building < 501)); - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_OWNER, building); + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_OWNER, building); return retVal; } -int getBuildingState(int building) { +int AI::getBuildingState(int building) { assert((building > 0) && (building < 501)); - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_STATE, building); + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_STATE, building); return retVal; } -int getBuildingType(int building) { +int AI::getBuildingType(int building) { assert((building > 0) && (building < 501)); - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_TYPE, building); + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_TYPE, building); return retVal; } -int getBuildingArmor(int building) { +int AI::getBuildingArmor(int building) { assert((building > 0) && (building < 501)); - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_ARMOR, building); + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_ARMOR, building); return retVal; } -int getBuildingWorth(int building) { +int AI::getBuildingWorth(int building) { assert((building > 0) && (building < 501)); - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_WORTH, building); + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_WORTH, building); return retVal; } -int getEnergyPoolsArray() { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_ENERGY_POOLS_ARRAY); +int AI::getEnergyPoolsArray() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_ENERGY_POOLS_ARRAY); return retVal; } -int getCoordinateVisibility(int x, int y, int playerNum) { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 4, D_GET_COORDINATE_VISIBILITY, x, y, playerNum); +int AI::getCoordinateVisibility(int x, int y, int playerNum) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 4, D_GET_COORDINATE_VISIBILITY, x, y, playerNum); return retVal; } -int getUnitVisibility(int unit, int playerNum) { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 3, D_GET_UNIT_VISIBILITY, unit, playerNum); +int AI::getUnitVisibility(int unit, int playerNum) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 3, D_GET_UNIT_VISIBILITY, unit, playerNum); return retVal; } -int getEnergyPoolVisibility(int pool, int playerNum) { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 3, D_GET_ENERGY_POOL_VISIBILITY, pool, playerNum); +int AI::getEnergyPoolVisibility(int pool, int playerNum) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 3, D_GET_ENERGY_POOL_VISIBILITY, pool, playerNum); return retVal; } -int getNumberOfPools() { +int AI::getNumberOfPools() { int retVal = 0; - if (AItype[getCurrentPlayer()]->getID() == ENERGY_HOG) { + if (_aiType[getCurrentPlayer()]->getID() == ENERGY_HOG) { retVal = 1; } else { - retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_NUMBER_OF_POOLS); + retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_NUMBER_OF_POOLS); } return retVal; } -int getNumberOfPlayers() { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_NUMBER_OF_PLAYERS); +int AI::getNumberOfPlayers() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_NUMBER_OF_PLAYERS); return retVal; } -int getPlayerEnergy() { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_PLAYER_ENERGY); +int AI::getPlayerEnergy() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_PLAYER_ENERGY); return static_cast<int>(static_cast<float>(retVal) / 10.0); } -int getPlayerMaxTime() { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_PLAYER_MAX_TIME); +int AI::getPlayerMaxTime() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_PLAYER_MAX_TIME); return retVal; } -int getWindXSpeed() { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_WIND_X_SPEED); +int AI::getWindXSpeed() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_WIND_X_SPEED); return retVal; } -int getWindYSpeed() { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_WIND_Y_SPEED); +int AI::getWindYSpeed() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_WIND_Y_SPEED); return retVal; } -int getTotalWindSpeed() { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_TOTAL_WIND_SPEED); +int AI::getTotalWindSpeed() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_TOTAL_WIND_SPEED); return retVal; } -int getWindXSpeedMax() { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_WIND_X_SPEED_MAX); +int AI::getWindXSpeedMax() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_WIND_X_SPEED_MAX); return retVal; } -int getWindYSpeedMax() { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_WIND_Y_SPEED_MAX); +int AI::getWindYSpeedMax() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_WIND_Y_SPEED_MAX); return retVal; } -int getBigXSize() { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_BIG_X_SIZE); +int AI::getBigXSize() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_BIG_X_SIZE); return retVal; } -int getBigYSize() { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_BIG_Y_SIZE); +int AI::getBigYSize() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_BIG_Y_SIZE); return retVal; } -int getEnergyPoolWidth(int pool) { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_ENERGY_POOL_WIDTH, pool); +int AI::getEnergyPoolWidth(int pool) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_ENERGY_POOL_WIDTH, pool); return retVal; } -int getBuildingMaxArmor(int building) { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_MAX_ARMOR, building); +int AI::getBuildingMaxArmor(int building) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_MAX_ARMOR, building); return retVal; } -int getTimerValue(int timerNum) { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_TIMER_VALUE, timerNum); +int AI::getTimerValue(int timerNum) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_TIMER_VALUE, timerNum); return retVal; } -int getLastAttacked(int &x, int &y) { +int AI::getLastAttacked(int &x, int &y) { int currentPlayer = getCurrentPlayer(); - x = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_LAST_ATTACKED_X, currentPlayer); - y = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_LAST_ATTACKED_Y, currentPlayer); + x = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_LAST_ATTACKED_X, currentPlayer); + y = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_LAST_ATTACKED_Y, currentPlayer); if (x || y) return 1; return 0; } -int getPlayerTeam(int player) { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_PLAYER_TEAM, player); +int AI::getPlayerTeam(int player) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_PLAYER_TEAM, player); return retVal; } -int getBuildingTeam(int building) { +int AI::getBuildingTeam(int building) { assert((building >= 1) && (building <= 500)); if (getBuildingOwner(building) == 0) return 0; - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_TEAM, building); + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_TEAM, building); return retVal; } -int getFOW() { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_FOW); +int AI::getFOW() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_FOW); return retVal; } -int getAnimSpeed() { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_ANIM_SPEED); +int AI::getAnimSpeed() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_ANIM_SPEED); return retVal; } -int getBuildingStackPtr() { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_BUILDING_STACK_PTR); +int AI::getBuildingStackPtr() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_BUILDING_STACK_PTR); return retVal; } -int getTurnCounter() { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_SCUMM_DATA], 1, D_GET_TURN_COUNTER); +int AI::getTurnCounter() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_TURN_COUNTER); return retVal; } -int getGroundAltitude(int x, int y) { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_GROUND_ALTITUDE], 2, x, y); +int AI::getGroundAltitude(int x, int y) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_GROUND_ALTITUDE], 2, x, y); return retVal; } -int checkForCordOverlap(int xStart, int yStart, int affectRadius, int simulateFlag) { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_CHECK_FOR_CORD_OVERLAP], 4, xStart, yStart, affectRadius, simulateFlag); +int AI::checkForCordOverlap(int xStart, int yStart, int affectRadius, int simulateFlag) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_CHECK_FOR_CORD_OVERLAP], 4, xStart, yStart, affectRadius, simulateFlag); return retVal; } -int checkForAngleOverlap(int unit, int angle) { +int AI::checkForAngleOverlap(int unit, int angle) { assert(angle > -721); assert(angle < 721); if (!unit) return 0; - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_CHECK_FOR_ANGLE_OVERLAP], 2, unit, angle); + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_CHECK_FOR_ANGLE_OVERLAP], 2, unit, angle); return retVal; } -int checkForUnitOverlap(int x, int y, int radius, int ignoredUnit) { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_CHECK_FOR_UNIT_OVERLAP], 4, x, y, radius, ignoredUnit); +int AI::checkForUnitOverlap(int x, int y, int radius, int ignoredUnit) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_CHECK_FOR_UNIT_OVERLAP], 4, x, y, radius, ignoredUnit); return retVal; } -int checkForEnergySquare(int x, int y) { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_CHECK_FOR_ENERGY_SQUARE], 2, x, y); +int AI::checkForEnergySquare(int x, int y) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_CHECK_FOR_ENERGY_SQUARE], 2, x, y); return retVal; } -int aiChat() { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_AI_CHAT], 0); +int AI::aiChat() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_AI_CHAT], 0); return retVal; } -int getPowerAngleFromPoint(int originX, int originY, int endX, int endY, int threshold, int olFlag) { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_POWER_ANGLE_FROM_POINT], 6, originX, originY, endX, endY, threshold, olFlag); +int AI::getPowerAngleFromPoint(int originX, int originY, int endX, int endY, int threshold, int olFlag) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_POWER_ANGLE_FROM_POINT], 6, originX, originY, endX, endY, threshold, olFlag); return retVal; } -int getPowerAngleFromPoint(int originX, int originY, int endX, int endY, int threshold) { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_POWER_ANGLE_FROM_POINT], 5, originX, originY, endX, endY, threshold); +int AI::getPowerAngleFromPoint(int originX, int originY, int endX, int endY, int threshold) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_POWER_ANGLE_FROM_POINT], 5, originX, originY, endX, endY, threshold); return retVal; } -int checkIfWaterState(int x, int y) { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_CHECK_IF_WATER_STATE], 2, x, y); +int AI::checkIfWaterState(int x, int y) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_CHECK_IF_WATER_STATE], 2, x, y); return retVal; } -int checkIfWaterSquare(int x, int y) { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_CHECK_IF_WATER_SQUARE], 2, x, y); +int AI::checkIfWaterSquare(int x, int y) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_CHECK_IF_WATER_SQUARE], 2, x, y); return retVal; } -int getUnitsWithinRadius(int x, int y, int radius) { +int AI::getUnitsWithinRadius(int x, int y, int radius) { assert(x >= 0); assert(y >= 0); assert(radius >= 0); - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_UNITS_WITHIN_RADIUS], 3, x, y, radius); + debug(0, "getUnitsWithinRadius(%d, %d, %d)", x, y, radius); + + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_UNITS_WITHIN_RADIUS], 3, x, y, radius); return retVal; } -int getLandingPoint(int x, int y, int power, int angle) { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_LANDING_POINT], 4, x, y, power, angle); +int AI::getLandingPoint(int x, int y, int power, int angle) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_LANDING_POINT], 4, x, y, power, angle); return retVal; } -int getEnemyUnitsVisible(int playerNum) { - int retVal = _vm->_moonbase->callScummFunction(MCP_params[F_GET_ENEMY_UNITS_VISIBLE], 1, playerNum); +int AI::getEnemyUnitsVisible(int playerNum) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_ENEMY_UNITS_VISIBLE], 1, playerNum); return retVal; } -float degToRad(float degrees) { +float AI::degToRad(float degrees) { return degrees * M_PI / 180.; } -void limitLocation(int &a, int &b, int c, int d) { +void AI::limitLocation(int &a, int &b, int c, int d) { if (a >= 0) { a = (a % c); } else { @@ -2795,7 +2744,7 @@ void limitLocation(int &a, int &b, int c, int d) { } } -int energyPoolSize(int pool) { +int AI::energyPoolSize(int pool) { int width = getEnergyPoolWidth(pool); switch (width) { @@ -2812,7 +2761,7 @@ int energyPoolSize(int pool) { return 0; } -int getMaxCollectors(int pool) { +int AI::getMaxCollectors(int pool) { int width = getEnergyPoolWidth(pool); switch (width) { @@ -2829,7 +2778,7 @@ int getMaxCollectors(int pool) { return 0; } -int simulateBuildingLaunch(int x, int y, int power, int angle, int numSteps, int isEnergy) { +int AI::simulateBuildingLaunch(int x, int y, int power, int angle, int numSteps, int isEnergy) { static int sXSpeed = 0; static int sYSpeed = 0; static int sZSpeed = 0; @@ -2879,9 +2828,6 @@ int simulateBuildingLaunch(int x, int y, int power, int angle, int numSteps, int sWhichUnit = getClosestUnit(x + 10, y, 30, getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 0, 0); } - int savedUnscaledXLoc = 0; - int savedUnscaledYLoc = 0; - for (int i = 1; i <= numSteps; i++) { unscaledXLoc = sXLoc / SCALE_X; unscaledYLoc = sYLoc / SCALE_Y; @@ -2924,9 +2870,6 @@ int simulateBuildingLaunch(int x, int y, int power, int angle, int numSteps, int if (!isEnergy) cfes = checkForEnergySquare(unscaledXLoc, unscaledYLoc); - savedUnscaledXLoc = unscaledXLoc; - savedUnscaledYLoc = unscaledYLoc; - if (cfco || cfuo || cfes || cfao) { sXSpeed = 0; sYSpeed = 0; @@ -2987,7 +2930,7 @@ int simulateBuildingLaunch(int x, int y, int power, int angle, int numSteps, int return 0; } -int simulateWeaponLaunch(int x, int y, int power, int angle, int numSteps) { +int AI::simulateWeaponLaunch(int x, int y, int power, int angle, int numSteps) { static int sXSpeed = 0; static int sYSpeed = 0; static int sZSpeed = 0; @@ -3102,7 +3045,7 @@ int simulateWeaponLaunch(int x, int y, int power, int angle, int numSteps) { return 0; } -int fakeSimulateWeaponLaunch(int x, int y, int power, int angle) { +int AI::fakeSimulateWeaponLaunch(int x, int y, int power, int angle) { int distance = power * 480 / getMaxPower(); float radAngle = degToRad(angle); int maxX = getMaxX(); @@ -3117,8 +3060,8 @@ int fakeSimulateWeaponLaunch(int x, int y, int power, int angle) { return MAX(1, x + y * maxX); } -int getEnergyHogType() { - return energyHogType; +int AI::getEnergyHogType() { + return _energyHogType; } } // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_main.h b/engines/scumm/he/moonbase/ai_main.h index 4937eced0f..7a38de9458 100644 --- a/engines/scumm/he/moonbase/ai_main.h +++ b/engines/scumm/he/moonbase/ai_main.h @@ -23,15 +23,14 @@ #ifndef SCUMM_HE_MOONBASE_AI_MAIN_H #define SCUMM_HE_MOONBASE_AI_MAIN_H +#include "common/array.h" #include "scumm/he/moonbase/ai_tree.h" namespace Scumm { -class ScummEngine_v90he; - -extern ScummEngine_v90he *_vm; - -typedef Common::Array<int>::iterator intVecItr; +class ScummEngine_v100he; +class AIEntity; +class patternList; enum { TERRAIN_TYPE_GOOD = 0, @@ -84,100 +83,128 @@ enum { MIN_DIST = 108 }; -static int energyHogType = 0; - -void resetAI(); -void cleanUpAI(); -void setAIType(const int paramCount, const int *params); -int masterControlProgram(const int paramCount, const int *params); - -int chooseBehavior(); -int chooseTarget(int behavior); - -Tree *initApproachTarget(int targetX, int targetY, Node **retNode); -int *approachTarget(Tree *myTree, int &x, int &y, Node **currentNode); -Tree *initAcquireTarget(int targetX, int targetY, Node **retNode); -int *acquireTarget(int targetX, int targetY); -int *acquireTarget(int targetX, int targetY, Tree *myTree, int &errorCode); -int *offendTarget(int &targetX, int &targetY, int index); -int *defendTarget(int &targetX, int &targetY, int index); -int *energizeTarget(int &targetX, int &targetY, int index); - -int getClosestUnit(int x, int y, int radius, int player, int alignment, int unitType, int checkUnitEnabled); -int getClosestUnit(int x, int y, int radius, int player, int alignment, int unitType, int checkUnitEnabled, int minDist); - -int getDistance(int originX, int originY, int endX, int endY); -int calcAngle(int originX, int originY, int endX, int endY); -int calcAngle(int originX, int originY, int endX, int endY, int noWrapFlag); -int getTerrain(int x, int y); -int getHubX(int hub); -int getHubY(int hub); -int getMaxX(); -int getMaxY(); -int getCurrentPlayer(); -int getMaxPower(); -int getMinPower(); -int getTerrainSquareSize(); -int getBuildingOwner(int building); -int getBuildingState(int building); -int getBuildingType(int building); -int getBuildingArmor(int building); -int getBuildingWorth(int building); -int getEnergyPoolsArray(); -int getCoordinateVisibility(int x, int y, int playerNum); -int getUnitVisibility(int unit, int playerNum); -int getEnergyPoolVisibility(int pool, int playerNum); -int getNumberOfPools(); -int getNumberOfPlayers(); -int getPlayerEnergy(); -int getPlayerMaxTime(); -int getWindXSpeed(); -int getWindYSpeed(); -int getTotalWindSpeed(); -int getWindXSpeedMax(); -int getWindYSpeedMax(); -int getBigXSize(); -int getBigYSize(); -int getEnergyPoolWidth(int pool); -int getBuildingMaxArmor(int building); -int getTimerValue(int timerNum); -int getLastAttacked(int &x, int &y); -int getPlayerTeam(int player); -int getBuildingTeam(int building); -int getFOW(); -int getAnimSpeed(); -int getBuildingStackPtr(); -int getTurnCounter(); - -int getGroundAltitude(int x, int y); -int checkForCordOverlap(int xStart, int yStart, int affectRadius, int simulateFlag); -int checkForAngleOverlap(int unit, int angle); -int estimateNextRoundEnergy(int player); -int checkForUnitOverlap(int x, int y, int radius, int ignoredUnit); -int checkForEnergySquare(int x, int y); -int aiChat(); - -int simulateBuildingLaunch(int x, int y, int power, int angle, int numSteps, int isEnergy); -int simulateWeaponLaunch(int x, int y, int power, int angle, int numSteps); -int fakeSimulateWeaponLaunch(int x, int y, int power, int angle); - -int getPowerAngleFromPoint(int originX, int originY, int endX, int endY, int threshold, int olFlag); -int getPowerAngleFromPoint(int originX, int originY, int endX, int endY, int threshold); -int checkIfWaterState(int x, int y); -int checkIfWaterSquare(int x, int y); -int getUnitsWithinRadius(int x, int y, int radius); -int getLandingPoint(int x, int y, int power, int angle); -int getEnemyUnitsVisible(int playerNum); - -float degToRad(float degrees); -void limitLocation(int &a, int &b, int c, int d); -int energyPoolSize(int pool); -int getMaxCollectors(int pool); - -int getEnergyHogType(); - -extern Common::Array<int> lastXCoord[]; -extern Common::Array<int> lastYCoord[]; +class AI { +public: + AI(ScummEngine_v100he *vm); + + void resetAI(); + void cleanUpAI(); + void setAIType(const int paramCount, const int32 *params); + int masterControlProgram(const int paramCount, const int32 *params); + +private: + int chooseBehavior(); + int chooseTarget(int behavior); + + Tree *initApproachTarget(int targetX, int targetY, Node **retNode); + int *approachTarget(Tree *myTree, int &x, int &y, Node **currentNode); + Tree *initAcquireTarget(int targetX, int targetY, Node **retNode); + int *acquireTarget(int targetX, int targetY); + int *acquireTarget(int targetX, int targetY, Tree *myTree, int &errorCode); + int *offendTarget(int &targetX, int &targetY, int index); + int *defendTarget(int &targetX, int &targetY, int index); + int *energizeTarget(int &targetX, int &targetY, int index); + +public: + int getClosestUnit(int x, int y, int radius, int player, int alignment, int unitType, int checkUnitEnabled); + int getClosestUnit(int x, int y, int radius, int player, int alignment, int unitType, int checkUnitEnabled, int minDist); + + int getDistance(int originX, int originY, int endX, int endY); + int calcAngle(int originX, int originY, int endX, int endY); + int calcAngle(int originX, int originY, int endX, int endY, int noWrapFlag); + int getTerrain(int x, int y); + + int getHubX(int hub); + int getHubY(int hub); + int getMaxX(); + int getMaxY(); + + int getCurrentPlayer(); + int getMaxPower(); + int getMinPower(); + int getTerrainSquareSize(); + int getBuildingOwner(int building); + int getBuildingState(int building); + int getBuildingType(int building); + int getBuildingArmor(int building); + int getBuildingMaxArmor(int building); + int getBuildingWorth(int building); + int getBuildingTeam(int building); + + int getPlayerEnergy(); + int getPlayerMaxTime(); + int getTimerValue(int timerNum); + int getPlayerTeam(int player); + + int getAnimSpeed(); + + int simulateBuildingLaunch(int x, int y, int power, int angle, int numSteps, int isEnergy); + + int getPowerAngleFromPoint(int originX, int originY, int endX, int endY, int threshold, int olFlag); + int getPowerAngleFromPoint(int originX, int originY, int endX, int endY, int threshold); + int checkIfWaterState(int x, int y); + int getUnitsWithinRadius(int x, int y, int radius); + + float degToRad(float degrees); + + int getEnergyHogType(); + +private: + int getEnergyPoolsArray(); + int getCoordinateVisibility(int x, int y, int playerNum); + int getUnitVisibility(int unit, int playerNum); + int getEnergyPoolVisibility(int pool, int playerNum); + int getNumberOfPools(); + int getNumberOfPlayers(); + int getWindXSpeed(); + int getWindYSpeed(); + int getTotalWindSpeed(); + int getWindXSpeedMax(); + int getWindYSpeedMax(); + int getBigXSize(); + int getBigYSize(); + int getEnergyPoolWidth(int pool); + int getLastAttacked(int &x, int &y); + int getFOW(); + int getBuildingStackPtr(); + int getTurnCounter(); + + int getGroundAltitude(int x, int y); + int checkForCordOverlap(int xStart, int yStart, int affectRadius, int simulateFlag); + int checkForAngleOverlap(int unit, int angle); + int estimateNextRoundEnergy(int player); + int checkForUnitOverlap(int x, int y, int radius, int ignoredUnit); + int checkForEnergySquare(int x, int y); + int aiChat(); + + int simulateWeaponLaunch(int x, int y, int power, int angle, int numSteps); + int fakeSimulateWeaponLaunch(int x, int y, int power, int angle); + + int checkIfWaterSquare(int x, int y); + + int getLandingPoint(int x, int y, int power, int angle); + int getEnemyUnitsVisible(int playerNum); + + void limitLocation(int &a, int &b, int c, int d); + int energyPoolSize(int pool); + int getMaxCollectors(int pool); + +public: + Common::Array<int> _lastXCoord[5]; + Common::Array<int> _lastYCoord[5]; + + ScummEngine_v100he *_vm; + + AIEntity *_aiType[5]; + + int _aiState; + int _behavior; + int _energyHogType; + + patternList *_moveList[5]; + + const int32 *_mcpParams; +}; } // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_node.h b/engines/scumm/he/moonbase/ai_node.h index 0a60dee9ca..0c3ef2f023 100644 --- a/engines/scumm/he/moonbase/ai_node.h +++ b/engines/scumm/he/moonbase/ai_node.h @@ -40,8 +40,8 @@ protected: virtual float calcH() { return 0; } public: - IContainedObject() { _valueG = 0; } - IContainedObject(float inG) { _valueG = inG; } + IContainedObject() { _valueG = 0; _objID = -1; } + IContainedObject(float inG) { _valueG = inG; _objID = -1; } IContainedObject(IContainedObject &sourceContainedObject); virtual ~IContainedObject() {} diff --git a/engines/scumm/he/moonbase/ai_targetacquisition.cpp b/engines/scumm/he/moonbase/ai_targetacquisition.cpp index fb74d8ae3c..02c49dc1a7 100644 --- a/engines/scumm/he/moonbase/ai_targetacquisition.cpp +++ b/engines/scumm/he/moonbase/ai_targetacquisition.cpp @@ -43,28 +43,28 @@ Sortie::~Sortie() { void Sortie::setEnemyDefenses(int enemyDefensesScummArray, int defendX, int defendY) { DefenseUnit *thisUnit; - int currentPlayer = getCurrentPlayer(); + int currentPlayer = _ai->getCurrentPlayer(); for (int i = 0; i < 200; i++) { - int thisElement = _vm->_moonbase->readFromArray(enemyDefensesScummArray, 0, i); + int thisElement = _ai->_vm->_moonbase->readFromArray(enemyDefensesScummArray, 0, i); if (thisElement) { - if (getBuildingOwner(thisElement)) { - if (getPlayerTeam(currentPlayer) != getBuildingTeam(thisElement)) { - int type = getBuildingType(thisElement); + if (_ai->getBuildingOwner(thisElement)) { + if (_ai->getPlayerTeam(currentPlayer) != _ai->getBuildingTeam(thisElement)) { + int type = _ai->getBuildingType(thisElement); switch (type) { case BUILDING_ANTI_AIR: - thisUnit = new AntiAirUnit(); + thisUnit = new AntiAirUnit(_ai); break; case BUILDING_SHIELD: - thisUnit = new ShieldUnit(); + thisUnit = new ShieldUnit(_ai); break; case BUILDING_EXPLOSIVE_MINE: - if (getDistance(getHubX(thisElement), getHubY(thisElement), defendX, defendY) < 90) - thisUnit = new MineUnit(); + if (_ai->getDistance(_ai->getHubX(thisElement), _ai->getHubY(thisElement), defendX, defendY) < 90) + thisUnit = new MineUnit(_ai); else thisUnit = NULL; @@ -81,9 +81,9 @@ void Sortie::setEnemyDefenses(int enemyDefensesScummArray, int defendX, int defe if (thisUnit != NULL) { thisUnit->setID(thisElement); - thisUnit->setPos(getHubX(thisElement), getHubY(thisElement)); + thisUnit->setPos(_ai->getHubX(thisElement), _ai->getHubY(thisElement)); - if (getBuildingState(thisElement)) thisUnit->setState(DUS_OFF); + if (_ai->getBuildingState(thisElement)) thisUnit->setState(DUS_OFF); _enemyDefenses.push_back(thisUnit); } @@ -111,7 +111,7 @@ int Sortie::numChildrenToGen() { IContainedObject *Sortie::createChildObj(int index, int &completionFlag) { float thisDamage; - Sortie *retSortie = new Sortie; + Sortie *retSortie = new Sortie(_ai); int activeDefenses = 0; Common::Array<DefenseUnit *> thisEnemyDefenses; @@ -122,23 +122,23 @@ IContainedObject *Sortie::createChildObj(int index, int &completionFlag) { switch ((*k)->getType()) { case DUT_ANTI_AIR: - temp = new AntiAirUnit(*k); + temp = new AntiAirUnit(*k, _ai); break; case DUT_SHIELD: - temp = new ShieldUnit(*k); + temp = new ShieldUnit(*k, _ai); break; case DUT_MINE: - temp = new MineUnit(*k); + temp = new MineUnit(*k, _ai); break; case DUT_CRAWLER: - temp = new CrawlerUnit(*k); + temp = new CrawlerUnit(*k, _ai); break; default: - temp = new ShieldUnit(*k); + temp = new ShieldUnit(*k, _ai); break; } @@ -155,7 +155,7 @@ IContainedObject *Sortie::createChildObj(int index, int &completionFlag) { retSortie->setUnitType(currentWeapon->getTypeID()); // Calculate distance from target to source hub - int distance = getDistance(currentTarget->getPosX(), currentTarget->getPosY(), getSourcePosX(), getSourcePosY()); + int distance = _ai->getDistance(currentTarget->getPosX(), currentTarget->getPosY(), getSourcePosX(), getSourcePosY()); // Pick correct shot position according to index Common::Point *targetCoords; @@ -169,7 +169,7 @@ IContainedObject *Sortie::createChildObj(int index, int &completionFlag) { // Loop through defensive units, toggling anti-air units and deciding if this weapon will land safely for (Common::Array<DefenseUnit *>::iterator i = thisEnemyDefenses.begin(); i != thisEnemyDefenses.end(); i++) { - distance = getDistance((*i)->getPosX(), (*i)->getPosY(), targetCoords->x, targetCoords->y); + distance = _ai->getDistance((*i)->getPosX(), (*i)->getPosY(), targetCoords->x, targetCoords->y); // Check to see if we're within an active defense's radius if ((distance < (*i)->getRadius()) && ((*i)->getState() == DUS_ON)) { @@ -210,10 +210,10 @@ IContainedObject *Sortie::createChildObj(int index, int &completionFlag) { for (Common::Array<DefenseUnit *>::iterator i = thisEnemyDefenses.begin(); i != thisEnemyDefenses.end(); ) { // Special simulated crawler detonation location used, since it walks a bit if (currentWeapon->getTypeID() == ITEM_CRAWLER) - distance = getDistance((*i)->getPosX(), (*i)->getPosY(), currentTarget->getPosX(), currentTarget->getPosY()); + distance = _ai->getDistance((*i)->getPosX(), (*i)->getPosY(), currentTarget->getPosX(), currentTarget->getPosY()); // Normal detonation location used here else { - distance = getDistance((*i)->getPosX(), (*i)->getPosY(), targetCoords->x, targetCoords->y); + distance = _ai->getDistance((*i)->getPosX(), (*i)->getPosY(), targetCoords->x, targetCoords->y); } if (distance < currentWeapon->getRadius()) { @@ -223,10 +223,10 @@ IContainedObject *Sortie::createChildObj(int index, int &completionFlag) { if ((AAcounter != 3) && (currentWeapon->getTypeID() == ITEM_CLUSTER)) thisDamage = 0; - if (!_vm->_rnd.getRandomNumber(4)) + if (!_ai->_vm->_rnd.getRandomNumber(4)) currentWeapon->setTypeID(ITEM_MINE); - (*i)->setDamage(thisDamage); + (*i)->setDamage((int)thisDamage); // Apply emp effect if (currentWeapon->getTypeID() == ITEM_EMP) { @@ -278,7 +278,8 @@ float Sortie::calcH() { } int Sortie::checkSuccess() { - if (!_enemyDefenses.size()) return SUCCESS; + if (!_enemyDefenses.size()) + return SUCCESS; int targetX = getTargetPosX(); int targetY = getTargetPosY(); @@ -320,10 +321,10 @@ void Sortie::printEnemyDefenses() { } int Defender::calculateDefenseUnitPosition(int targetX, int targetY, int index) { - int currentPlayer = getCurrentPlayer(); + int currentPlayer = _ai->getCurrentPlayer(); //get list of near hubs - int unitsArray = getUnitsWithinRadius(targetX + 5, targetY, 480); + int unitsArray = _ai->getUnitsWithinRadius(targetX + 5, targetY, 480); const int NUM_HUBS = 10; //Order on dist @@ -331,14 +332,14 @@ int Defender::calculateDefenseUnitPosition(int targetX, int targetY, int index) int hubIndex = 0; for (int i = 0; i < 200; i++) { - int thisUnit = _vm->_moonbase->readFromArray(unitsArray, 0, i); + int thisUnit = _ai->_vm->_moonbase->readFromArray(unitsArray, 0, i); if (thisUnit) { - if (((getBuildingType(thisUnit) == BUILDING_MAIN_BASE) || (getBuildingType(thisUnit) == BUILDING_OFFENSIVE_LAUNCHER)) && (getBuildingOwner(thisUnit) == currentPlayer)) { + if (((_ai->getBuildingType(thisUnit) == BUILDING_MAIN_BASE) || (_ai->getBuildingType(thisUnit) == BUILDING_OFFENSIVE_LAUNCHER)) && (_ai->getBuildingOwner(thisUnit) == currentPlayer)) { for (int j = 0; j < NUM_HUBS; j++) { if (hubArray[j]) { - int distCurrent = getDistance(targetX, targetY, getHubX(thisUnit), getHubY(thisUnit)); - int distSaved = getDistance(targetX, targetY, getHubX(hubArray[j]), getHubY(hubArray[j])); + int distCurrent = _ai->getDistance(targetX, targetY, _ai->getHubX(thisUnit), _ai->getHubY(thisUnit)); + int distSaved = _ai->getDistance(targetX, targetY, _ai->getHubX(hubArray[j]), _ai->getHubY(hubArray[j])); if (distCurrent < distSaved) { hubArray[hubIndex] = hubArray[j]; @@ -361,37 +362,37 @@ int Defender::calculateDefenseUnitPosition(int targetX, int targetY, int index) } } - _vm->nukeArray(unitsArray); + _ai->_vm->_moonbase->deallocateArray(unitsArray); //Check if repair is needed - int targetUnit = getClosestUnit(targetX + 5, targetY, 15, currentPlayer, 1, 0, 0, 0); + int targetUnit = _ai->getClosestUnit(targetX + 5, targetY, 15, currentPlayer, 1, 0, 0, 0); - if (targetUnit && (targetUnit != BUILDING_CRAWLER) && (getBuildingTeam(targetUnit) == getPlayerTeam(currentPlayer))) { - int armor = getBuildingArmor(targetUnit); + if (targetUnit && (targetUnit != BUILDING_CRAWLER) && (_ai->getBuildingTeam(targetUnit) == _ai->getPlayerTeam(currentPlayer))) { + int armor = _ai->getBuildingArmor(targetUnit); - if (armor < getBuildingMaxArmor(targetUnit)) { - unitsArray = getUnitsWithinRadius(targetX + 5, targetY, 170); + if (armor < _ai->getBuildingMaxArmor(targetUnit)) { + unitsArray = _ai->getUnitsWithinRadius(targetX + 5, targetY, 170); int defCount = 0; for (int i = 0; i < 200; i++) { - int thisUnit = _vm->_moonbase->readFromArray(unitsArray, 0, i); + int thisUnit = _ai->_vm->_moonbase->readFromArray(unitsArray, 0, i); if (thisUnit) { - if (((getBuildingType(thisUnit) == BUILDING_SHIELD) || (getBuildingType(thisUnit) == BUILDING_ANTI_AIR)) && (getBuildingOwner(thisUnit) == currentPlayer) && (getBuildingState(thisUnit) == 0)) { + if (((_ai->getBuildingType(thisUnit) == BUILDING_SHIELD) || (_ai->getBuildingType(thisUnit) == BUILDING_ANTI_AIR)) && (_ai->getBuildingOwner(thisUnit) == currentPlayer) && (_ai->getBuildingState(thisUnit) == 0)) { defCount++; i = 200; } } } - _vm->nukeArray(unitsArray); + _ai->_vm->_moonbase->deallocateArray(unitsArray); if (defCount) { //repair - int hubUnit = getClosestUnit(targetX, targetY, 480, currentPlayer, 1, BUILDING_MAIN_BASE, 1, 110); + int hubUnit = _ai->getClosestUnit(targetX, targetY, 480, currentPlayer, 1, BUILDING_MAIN_BASE, 1, 110); if (hubUnit && (hubUnit != targetUnit)) { - int powAngle = abs(getPowerAngleFromPoint(getHubX(hubUnit), getHubY(hubUnit), targetX, targetY, 20)); + int powAngle = abs(_ai->getPowerAngleFromPoint(_ai->getHubX(hubUnit), _ai->getHubY(hubUnit), targetX, targetY, 20)); int power = powAngle / 360; int angle = powAngle - (power * 360); @@ -411,30 +412,30 @@ int Defender::calculateDefenseUnitPosition(int targetX, int targetY, int index) //For each hub for (int i = 0; i < MIN(NUM_HUBS, hubIndex); i++) { - int hubX = getHubX(hubArray[i]); - int hubY = getHubY(hubArray[i]); + int hubX = _ai->getHubX(hubArray[i]); + int hubY = _ai->getHubY(hubArray[i]); //get angle to hub int directAngleToHub = 0; //If this hub is the target if ((hubX == targetX) && (hubY == targetY)) { //make the angle seed point at the closest enemy - int enemyUnit = getClosestUnit(hubX, hubY, getMaxX(), currentPlayer, 0, 0, 0); - directAngleToHub = calcAngle(targetX, targetY, getHubX(enemyUnit), getHubY(enemyUnit)); + int enemyUnit = _ai->getClosestUnit(hubX, hubY, _ai->getMaxX(), currentPlayer, 0, 0, 0); + directAngleToHub = _ai->calcAngle(targetX, targetY, _ai->getHubX(enemyUnit), _ai->getHubY(enemyUnit)); } else { - directAngleToHub = calcAngle(targetX, targetY, hubX, hubY); + directAngleToHub = _ai->calcAngle(targetX, targetY, hubX, hubY); } //Number of random chances to land for (int j = 0; j < 3; j++) { //Pick random angle and dist within semicircle (-90 to +90) and (40 to 150) - int randAngle = directAngleToHub + _vm->_rnd.getRandomNumber(179) - 90; - int randDist = _vm->_rnd.getRandomNumber(109) + 40; + int randAngle = directAngleToHub + _ai->_vm->_rnd.getRandomNumber(179) - 90; + int randDist = _ai->_vm->_rnd.getRandomNumber(109) + 40; - int x = targetX + randDist * cos(degToRad(randAngle)); - int y = targetY + randDist * sin(degToRad(randAngle)); + int x = (int)(targetX + randDist * cos(_ai->degToRad(randAngle))); + int y = (int)(targetY + randDist * sin(_ai->degToRad(randAngle))); - int powAngle = getPowerAngleFromPoint(hubX, hubY, x, y, 20); + int powAngle = _ai->getPowerAngleFromPoint(hubX, hubY, x, y, 20); if (powAngle < 0) continue; @@ -443,7 +444,7 @@ int Defender::calculateDefenseUnitPosition(int targetX, int targetY, int index) int angle = powAngle - (power * 360); int coords = 0; - coords = simulateBuildingLaunch(hubX, hubY, power, angle, 100, 0); + coords = _ai->simulateBuildingLaunch(hubX, hubY, power, angle, 100, 0); //if valid, return if (coords > 0) { @@ -451,28 +452,28 @@ int Defender::calculateDefenseUnitPosition(int targetX, int targetY, int index) setSourceX(hubX); setSourceY(hubY); - setTargetX((x + getMaxX()) % getMaxX()); - setTargetY((y + getMaxY()) % getMaxY()); + setTargetX((x + _ai->getMaxX()) % _ai->getMaxX()); + setTargetY((y + _ai->getMaxY()) % _ai->getMaxY()); setSourceUnit(hubArray[i]); - int unitsArray2 = getUnitsWithinRadius(targetX + 5, targetY, 200); + int unitsArray2 = _ai->getUnitsWithinRadius(targetX + 5, targetY, 200); int shieldCount = 0; for (int k = 0; k < 200; k++) { - int thisUnit = _vm->_moonbase->readFromArray(unitsArray2, 0, k); + int thisUnit = _ai->_vm->_moonbase->readFromArray(unitsArray2, 0, k); if (thisUnit) { - if ((getBuildingType(thisUnit) == BUILDING_SHIELD) && (getBuildingOwner(thisUnit) == currentPlayer)) + if ((_ai->getBuildingType(thisUnit) == BUILDING_SHIELD) && (_ai->getBuildingOwner(thisUnit) == currentPlayer)) shieldCount++; - if ((getBuildingType(thisUnit) == BUILDING_BRIDGE) && (getBuildingOwner(thisUnit) == currentPlayer)) { + if ((_ai->getBuildingType(thisUnit) == BUILDING_BRIDGE) && (_ai->getBuildingOwner(thisUnit) == currentPlayer)) { shieldCount--; shieldCount = MAX(-1, shieldCount); } } } - if ((_vm->_rnd.getRandomNumber((int)pow(3.0f, shieldCount + 1) - 1) == 0) && (getPlayerEnergy() > 6)) + if ((_ai->_vm->_rnd.getRandomNumber((int)pow(3.0f, shieldCount + 1) - 1) == 0) && (_ai->getPlayerEnergy() > 6)) setUnit(ITEM_SHIELD); else setUnit(ITEM_ANTIAIR); @@ -480,30 +481,30 @@ int Defender::calculateDefenseUnitPosition(int targetX, int targetY, int index) setPower(power); setAngle(angle); - _vm->nukeArray(unitsArray2); + _ai->_vm->_moonbase->deallocateArray(unitsArray2); return 1; } if (coords < 0) { //drop a bridge for the cord - int yCoord = -coords / getMaxX(); - int xCoord = -coords - (yCoord * getMaxX()); + int yCoord = -coords / _ai->getMaxX(); + int xCoord = -coords - (yCoord * _ai->getMaxX()); - if (checkIfWaterState(xCoord, yCoord)) { - int terrainSquareSize = getTerrainSquareSize(); + if (_ai->checkIfWaterState(xCoord, yCoord)) { + int terrainSquareSize = _ai->getTerrainSquareSize(); xCoord = ((xCoord / terrainSquareSize * terrainSquareSize) + (terrainSquareSize / 2)); yCoord = ((yCoord / terrainSquareSize * terrainSquareSize) + (terrainSquareSize / 2)); int xDist = xCoord - x; int yDist = yCoord - y; - x = xCoord + (terrainSquareSize * 1.414 * (xDist / (abs(xDist) + 1))); - y = yCoord + (terrainSquareSize * 1.414 * (yDist / (abs(yDist) + 1))); + x = (int)(xCoord + (terrainSquareSize * 1.414 * (xDist / (abs(xDist) + 1)))); + y = (int)(yCoord + (terrainSquareSize * 1.414 * (yDist / (abs(yDist) + 1)))); setTargetX(x); setTargetY(y); - int nextUnit = getClosestUnit(x, y, 480, getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 120); - powAngle = getPowerAngleFromPoint(getHubX(nextUnit), getHubY(nextUnit), x, y, 15); + int nextUnit = _ai->getClosestUnit(x, y, 480, _ai->getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 120); + powAngle = _ai->getPowerAngleFromPoint(_ai->getHubX(nextUnit), _ai->getHubY(nextUnit), x, y, 15); powAngle = abs(powAngle); power = powAngle / 360; @@ -527,24 +528,24 @@ int Defender::calculateDefenseUnitPosition(int targetX, int targetY, int index) if (hubIndex == 0) return -3; do { - int sourceHub = hubArray[_vm->_rnd.getRandomNumber(hubIndex - 1)]; + int sourceHub = hubArray[_ai->_vm->_rnd.getRandomNumber(hubIndex - 1)]; - setSourceX(getHubX(sourceHub)); - setSourceY(getHubY(sourceHub)); + setSourceX(_ai->getHubX(sourceHub)); + setSourceY(_ai->getHubY(sourceHub)); setSourceUnit(sourceHub); setUnit(ITEM_HUB); - setPower(_vm->_rnd.getRandomNumber(299) + 200); - setAngle(_vm->_rnd.getRandomNumber(359)); + setPower(_ai->_vm->_rnd.getRandomNumber(299) + 200); + setAngle(_ai->_vm->_rnd.getRandomNumber(359)); count++; if (count > (NUM_HUBS * 3)) break; - coords = simulateBuildingLaunch(getSourceX(), getSourceY(), getPower(), getAngle(), 100, 0); + coords = _ai->simulateBuildingLaunch(getSourceX(), getSourceY(), getPower(), getAngle(), 100, 0); } while (coords <= 0); if (coords > 0) { - setTargetX(coords % getMaxX()); - setTargetY(coords / getMaxX()); + setTargetX(coords % _ai->getMaxX()); + setTargetY(coords / _ai->getMaxX()); } else { setTargetX(0); setTargetY(0); diff --git a/engines/scumm/he/moonbase/ai_targetacquisition.h b/engines/scumm/he/moonbase/ai_targetacquisition.h index cf8f295c70..9afe0f50ab 100644 --- a/engines/scumm/he/moonbase/ai_targetacquisition.h +++ b/engines/scumm/he/moonbase/ai_targetacquisition.h @@ -44,10 +44,10 @@ private: int _unitType; int _shotPosX, _shotPosY; Common::Array<DefenseUnit *> _enemyDefenses; - + AI *_ai; public: - Sortie() {} + Sortie(AI *ai) { _ai = ai; _unitType = 0; _shotPosX = _shotPosY = 0; } virtual ~Sortie(); static void setSourcePos(int x, int y) { @@ -109,8 +109,10 @@ private: int _power; int _angle; int _unit; + AI *_ai; public: + Defender(AI *ai) : _ai(ai) {} void setSourceX(int sourceX) { _sourceX = sourceX; } void setSourceY(int sourceY) { _sourceY = sourceY; } void setTargetX(int targetX) { _targetX = targetX; } diff --git a/engines/scumm/he/moonbase/ai_traveller.cpp b/engines/scumm/he/moonbase/ai_traveller.cpp index 68cf0c0d76..b1c9985b9d 100644 --- a/engines/scumm/he/moonbase/ai_traveller.cpp +++ b/engines/scumm/he/moonbase/ai_traveller.cpp @@ -20,6 +20,8 @@ * */ +#include "scumm/he/intern_he.h" +#include "scumm/he/moonbase/moonbase.h" #include "scumm/he/moonbase/ai_traveller.h" #include "scumm/he/moonbase/ai_main.h" @@ -32,23 +34,39 @@ int Traveller::_maxDist = 0; int Traveller::_numToGen = 0; int Traveller::_sizeAngleStep = 0; -Traveller::Traveller() { +Traveller::Traveller(AI *ai) : _ai(ai) { _waterFlag = 0; setValueG(0); unsetDisabled(); + + _sourceHub = 0; + _angleTo = 0; + _powerTo = 0; + _waterSourceX = 0; + _waterSourceY = 0; + _waterDestX = 0; + _waterDestY = 0; } -Traveller::Traveller(int originX, int originY) { +Traveller::Traveller(int originX, int originY, AI *ai) : _ai(ai) { _waterFlag = 0; setValueG(0); unsetDisabled(); _posX = originX; _posY = originY; + + _sourceHub = 0; + _angleTo = 0; + _powerTo = 0; + _waterSourceX = 0; + _waterSourceY = 0; + _waterDestX = 0; + _waterDestY = 0; } void Traveller::adjustPosX(int offsetX) { - int maxX = getMaxX(); + int maxX = _ai->getMaxX(); int deltaX = _posX + offsetX; if (deltaX < 0) _posX = maxX + deltaX; @@ -57,7 +75,7 @@ void Traveller::adjustPosX(int offsetX) { } void Traveller::adjustPosY(int offsetY) { - int maxY = getMaxX(); + int maxY = _ai->getMaxX(); int deltaY = _posY + offsetY; if (deltaY < 0) _posY = maxY + deltaY; @@ -73,7 +91,7 @@ void Traveller::adjustXY(int offsetX, int offsetY) { float Traveller::calcH() { float retVal = 0; // Calc dist from here to target - retVal = getDistance(_posX, _posY, _targetPosX, _targetPosY); + retVal = _ai->getDistance(_posX, _posY, _targetPosX, _targetPosY); // Divide by _maxDist to get minimum number of jumps to goal retVal /= static_cast<float>(_maxDist); @@ -82,7 +100,7 @@ float Traveller::calcH() { int Traveller::numChildrenToGen() { if (!_numToGen) - _numToGen = getAnimSpeed() + 2; + _numToGen = _ai->getAnimSpeed() + 2; return _numToGen; } @@ -95,7 +113,7 @@ IContainedObject *Traveller::createChildObj(int index, int &completionFlag) { nodeCount++; - Traveller *retTraveller = new Traveller; + Traveller *retTraveller = new Traveller(_ai); static int dir, angle, power; @@ -103,14 +121,14 @@ IContainedObject *Traveller::createChildObj(int index, int &completionFlag) { // Calculate angle between here and target int directAngle = 0; - if (getEnergyHogType()) - directAngle = calcAngle(_posX, _posY, _targetPosX, _targetPosY, 1); + if (_ai->getEnergyHogType()) + directAngle = _ai->calcAngle(_posX, _posY, _targetPosX, _targetPosY, 1); else - directAngle = calcAngle(_posX, _posY, _targetPosX, _targetPosY); + directAngle = _ai->calcAngle(_posX, _posY, _targetPosX, _targetPosY); // Calculate the offset angle for this index if (!_sizeAngleStep) - _sizeAngleStep = 52 - (getAnimSpeed() * 7); + _sizeAngleStep = 52 - (_ai->getAnimSpeed() * 7); dir = _sizeAngleStep * ((static_cast<int>(index / NUM_POWER_STEPS) + 1) >> 1); // Calculate the sign value for the offset for this index @@ -120,15 +138,15 @@ IContainedObject *Traveller::createChildObj(int index, int &completionFlag) { // Calculate power for this index int maxPower = 0; - int directDist = getDistance(_posX, _posY, _targetPosX, _targetPosY); + int directDist = _ai->getDistance(_posX, _posY, _targetPosX, _targetPosY); if (directDist > _maxDist + 120) - maxPower = getMaxPower(); + maxPower = _ai->getMaxPower(); else - maxPower = (static_cast<float>(directDist) / static_cast<float>(_maxDist + 120)) * getMaxPower(); + maxPower = (int)((static_cast<float>(directDist) / static_cast<float>(_maxDist + 120)) * _ai->getMaxPower()); maxPower -= 70; - power = maxPower * (1 - ((index % NUM_POWER_STEPS) * SIZE_POWER_STEP)); + power = (int)(maxPower * (1 - ((index % NUM_POWER_STEPS) * SIZE_POWER_STEP))); } retTraveller->setAngleTo(angle); @@ -139,7 +157,7 @@ IContainedObject *Traveller::createChildObj(int index, int &completionFlag) { int coords = 0; if (!(index % NUM_POWER_STEPS) || (!lastSuccessful)) { - coords = simulateBuildingLaunch(_posX, _posY, power, angle, 10, 0); + coords = _ai->simulateBuildingLaunch(_posX, _posY, power, angle, 10, 0); lastSuccessful = 0; } else { completionState = 1; @@ -156,34 +174,34 @@ IContainedObject *Traveller::createChildObj(int index, int &completionFlag) { completionState = 1; } - int whoseTurn = getCurrentPlayer(); - int maxX = getMaxX(); + int whoseTurn = _ai->getCurrentPlayer(); + int maxX = _ai->getMaxX(); // Check new position to see if landing is clear if (coords > 0) { int yCoord = coords / maxX; int xCoord = coords - (yCoord * maxX); - int terrain = getTerrain(xCoord, yCoord); + int terrain = _ai->getTerrain(xCoord, yCoord); assert(terrain == TERRAIN_TYPE_GOOD); - float pwr = getMinPower() * .3; + float pwr = _ai->getMinPower() * .3; float cosine = cos((static_cast<float>(angle) / 360) * (2 * M_PI)); float sine = sin((static_cast<float>(angle) / 360) * (2 * M_PI)); - int xParam = xCoord + (pwr * cosine); - int yParam = yCoord + (pwr * sine); + int xParam = (int)(xCoord + (pwr * cosine)); + int yParam = (int)(yCoord + (pwr * sine)); if (xParam < 0) - xParam += getMaxX(); - else if (xParam > getMaxX()) - xParam -= getMaxX(); + xParam += _ai->getMaxX(); + else if (xParam > _ai->getMaxX()) + xParam -= _ai->getMaxX(); if (yParam < 0) - yParam += getMaxY(); - else if (yParam > getMaxY()) - yParam -= getMaxY(); + yParam += _ai->getMaxY(); + else if (yParam > _ai->getMaxY()) + yParam -= _ai->getMaxY(); - if (checkIfWaterState(xParam, yParam)) { + if (_ai->checkIfWaterState(xParam, yParam)) { delete retTraveller; return NULL; } @@ -192,7 +210,7 @@ IContainedObject *Traveller::createChildObj(int index, int &completionFlag) { retTraveller->setPosX(xCoord); // Iterate through the previous action list, making sure this one isn't on it - for (intVecItr i = (lastXCoord[whoseTurn]).begin(), j = (lastYCoord[whoseTurn]).begin(); i != (lastXCoord[whoseTurn]).end(); i++, j++) { + for (Common::Array<int>::iterator i = (_ai->_lastXCoord[whoseTurn]).begin(), j = (_ai->_lastYCoord[whoseTurn]).begin(); i != (_ai->_lastXCoord[whoseTurn]).end(); i++, j++) { // Check if this shot is the same as the last time we tried if ((*i == retTraveller->getPosX()) && (*j == retTraveller->getPosY())) { retTraveller->setDisabled(); @@ -208,20 +226,20 @@ IContainedObject *Traveller::createChildObj(int index, int &completionFlag) { int xCoord = -coords - (yCoord * maxX); // If landing fault is because of water, add 1 extra to g and turn on water flag. Also set coords, and adjust power to water fault location - if (checkIfWaterState(xCoord, yCoord)) { - int terrainSquareSize = getTerrainSquareSize(); + if (_ai->checkIfWaterState(xCoord, yCoord)) { + int terrainSquareSize = _ai->getTerrainSquareSize(); xCoord = ((xCoord / terrainSquareSize * terrainSquareSize) + (terrainSquareSize / 2)); yCoord = ((yCoord / terrainSquareSize * terrainSquareSize) + (terrainSquareSize / 2)); int xDist = xCoord - _posX; int yDist = yCoord - _posY; - retTraveller->setPosX(xCoord + (terrainSquareSize * 1.414 * (xDist / (abs(xDist) + 1)))); - retTraveller->setPosY(yCoord + (terrainSquareSize * 1.414 * (yDist / (abs(yDist) + 1)))); + retTraveller->setPosX((int)(xCoord + (terrainSquareSize * 1.414 * (xDist / (abs(xDist) + 1))))); + retTraveller->setPosY((int)(yCoord + (terrainSquareSize * 1.414 * (yDist / (abs(yDist) + 1))))); - int closestHub = getClosestUnit(retTraveller->getPosX(), retTraveller->getPosY(), getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 110); + int closestHub = _ai->getClosestUnit(retTraveller->getPosX(), retTraveller->getPosY(), _ai->getMaxX(), _ai->getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 110); - retTraveller->setWaterSourceX(getHubX(closestHub)); - retTraveller->setWaterSourceY(getHubY(closestHub)); + retTraveller->setWaterSourceX(_ai->getHubX(closestHub)); + retTraveller->setWaterSourceY(_ai->getHubY(closestHub)); retTraveller->setWaterDestX(retTraveller->getPosX()); retTraveller->setWaterDestY(retTraveller->getPosY()); @@ -242,7 +260,7 @@ IContainedObject *Traveller::createChildObj(int index, int &completionFlag) { } int Traveller::checkSuccess() { - if (getDistance(_posX + 1, _posY, _targetPosX, _targetPosY) < _maxDist) + if (_ai->getDistance(_posX + 1, _posY, _targetPosX, _targetPosY) < _maxDist) return SUCCESS; return 0; diff --git a/engines/scumm/he/moonbase/ai_traveller.h b/engines/scumm/he/moonbase/ai_traveller.h index 63a56d77af..20e69eb76c 100644 --- a/engines/scumm/he/moonbase/ai_traveller.h +++ b/engines/scumm/he/moonbase/ai_traveller.h @@ -58,13 +58,14 @@ private: int _waterDestX; int _waterDestY; + AI *_ai; protected: virtual float calcH(); public: - Traveller(); - Traveller(int originX, int originY); + Traveller(AI *ai); + Traveller(int originX, int originY, AI *ai); ~Traveller() {} IContainedObject *duplicate() { return this; } diff --git a/engines/scumm/he/moonbase/ai_tree.cpp b/engines/scumm/he/moonbase/ai_tree.cpp index efbf3e3a99..d18536812b 100644 --- a/engines/scumm/he/moonbase/ai_tree.cpp +++ b/engines/scumm/he/moonbase/ai_tree.cpp @@ -20,36 +20,64 @@ * */ +#include "scumm/he/intern_he.h" + +#include "scumm/he/moonbase/moonbase.h" #include "scumm/he/moonbase/ai_tree.h" #include "scumm/he/moonbase/ai_main.h" namespace Scumm { -Tree::Tree() { +static int compareTreeNodes(const void *a, const void *b) { + if (((const TreeNode *)a)->value < ((const TreeNode *)b)->value) + return -1; + else if (((const TreeNode *)a)->value > ((const TreeNode *)b)->value) + return 1; + else + return 0; +} + +Tree::Tree(AI *ai) : _ai(ai) { pBaseNode = new Node; _maxDepth = MAX_DEPTH; _maxNodes = MAX_NODES; + _currentNode = 0; + _currentChildIndex = 0; + + _currentMap = new Common::SortedArray<TreeNode *>(compareTreeNodes); } -Tree::Tree(IContainedObject *contents) { +Tree::Tree(IContainedObject *contents, AI *ai) : _ai(ai) { pBaseNode = new Node; pBaseNode->setContainedObject(contents); _maxDepth = MAX_DEPTH; _maxNodes = MAX_NODES; + _currentNode = 0; + _currentChildIndex = 0; + + _currentMap = new Common::SortedArray<TreeNode *>(compareTreeNodes); } -Tree::Tree(IContainedObject *contents, int maxDepth) { +Tree::Tree(IContainedObject *contents, int maxDepth, AI *ai) : _ai(ai) { pBaseNode = new Node; pBaseNode->setContainedObject(contents); _maxDepth = maxDepth; _maxNodes = MAX_NODES; + _currentNode = 0; + _currentChildIndex = 0; + + _currentMap = new Common::SortedArray<TreeNode *>(compareTreeNodes); } -Tree::Tree(IContainedObject *contents, int maxDepth, int maxNodes) { +Tree::Tree(IContainedObject *contents, int maxDepth, int maxNodes, AI *ai) : _ai(ai) { pBaseNode = new Node; pBaseNode->setContainedObject(contents); _maxDepth = maxDepth; _maxNodes = maxNodes; + _currentNode = 0; + _currentChildIndex = 0; + + _currentMap = new Common::SortedArray<TreeNode *>(compareTreeNodes); } void Tree::duplicateTree(Node *sourceNode, Node *destNode) { @@ -64,10 +92,13 @@ void Tree::duplicateTree(Node *sourceNode, Node *destNode) { } } -Tree::Tree(const Tree *sourceTree) { +Tree::Tree(const Tree *sourceTree, AI *ai) : _ai(ai) { pBaseNode = new Node(sourceTree->getBaseNode()); _maxDepth = sourceTree->getMaxDepth(); _maxNodes = sourceTree->getMaxNodes(); + _currentMap = new Common::SortedArray<TreeNode *>(compareTreeNodes); + _currentNode = 0; + _currentChildIndex = 0; duplicateTree(sourceTree->getBaseNode(), pBaseNode); } @@ -80,7 +111,6 @@ Tree::~Tree() { while (pNodeItr != NULL) { // If any children are left, move to one of them if (!(pNodeItr->getChildren().empty())) { - //int size = (pNodeItr->getChildren()).size(); pNodeItr = pNodeItr->popChild(); } else { // Delete this node, and move up to the parent for further processing @@ -90,13 +120,12 @@ Tree::~Tree() { pTemp = NULL; } } -} + delete _currentMap; +} Node *Tree::aStarSearch() { - return NULL; -#if 0 - fnpMMap mmfpOpen; + Common::SortedArray<TreeNode *> mmfpOpen(compareTreeNodes); Node *currentNode = NULL; float currentT; @@ -106,24 +135,24 @@ Node *Tree::aStarSearch() { float temp = pBaseNode->getContainedObject()->calcT(); if (static_cast<int>(temp) != SUCCESS) { - - mmfpOpen.insert(fnpMMap::value_type(pBaseNode->getObjectT(), pBaseNode)); + mmfpOpen.insert(new TreeNode(pBaseNode->getObjectT(), pBaseNode)); while (mmfpOpen.size() && (retNode == NULL)) { - currentNode = mmfpOpen.begin()->second; + currentNode = mmfpOpen.front()->node; mmfpOpen.erase(mmfpOpen.begin()); if ((currentNode->getDepth() < _maxDepth) && (Node::getNodeCount() < _maxNodes)) { // Generate nodes - int numChildren = currentNode->generateChildren(); Common::Array<Node *> vChildren = currentNode->getChildren(); for (Common::Array<Node *>::iterator i = vChildren.begin(); i != vChildren.end(); i++) { IContainedObject *pTemp = (*i)->getContainedObject(); currentT = pTemp->calcT(); - if (currentT == SUCCESS) retNode = *i; - else mmfpOpen.insert(fnpMMap::value_type(currentT, (*i))); + if (currentT == SUCCESS) + retNode = *i; + else + mmfpOpen.insert(new TreeNode(currentT, (*i))); } } else { retNode = currentNode; @@ -134,20 +163,18 @@ Node *Tree::aStarSearch() { } return retNode; -#endif } Node *Tree::aStarSearch_singlePassInit() { Node *retNode = NULL; - currentChildIndex = 1; + _currentChildIndex = 1; float temp = pBaseNode->getContainedObject()->calcT(); if (static_cast<int>(temp) != SUCCESS) { - //_currentMap.insert(fnpMMap::value_type(pBaseNode->getObjectT(), pBaseNode)); - //assert(_currentMap.size()); + _currentMap->insert(new TreeNode(pBaseNode->getObjectT(), pBaseNode)); } else { retNode = pBaseNode; } @@ -155,37 +182,35 @@ Node *Tree::aStarSearch_singlePassInit() { return retNode; } -Node *Tree::aStarSearch_singlePass(Node **currentNode) { - currentNode = NULL; +Node *Tree::aStarSearch_singlePass() { + float currentT = 0.0; Node *retNode = NULL; -#if 0 - float currentT; static int maxTime = 0; - if (currentChildIndex == 1) { - maxTime = getPlayerMaxTime(); + if (_currentChildIndex == 1) { + maxTime = _ai->getPlayerMaxTime(); } - if (currentChildIndex) { - if (!(_currentMap.size())) { + if (_currentChildIndex) { + if (!(_currentMap->size())) { retNode = _currentNode; return retNode; } - _currentNode = _currentMap.begin()->second; - _currentMap.erase(_currentMap.begin()); + _currentNode = _currentMap->front()->node; + _currentMap->erase(_currentMap->begin()); } - if ((_currentNode->getDepth() < _maxDepth) && (Node::getNodeCount() < _maxNodes) && ((!maxTime) || (getTimerValue(3) < maxTime))) { + if ((_currentNode->getDepth() < _maxDepth) && (Node::getNodeCount() < _maxNodes) && ((!maxTime) || (_ai->getTimerValue(3) < maxTime))) { // Generate nodes - currentChildIndex = _currentNode->generateChildren(); + _currentChildIndex = _currentNode->generateChildren(); - if (currentChildIndex) { + if (_currentChildIndex) { Common::Array<Node *> vChildren = _currentNode->getChildren(); - if (!vChildren.size() && !_currentMap.size()) { - currentChildIndex = 0; + if (!vChildren.size() && !_currentMap->size()) { + _currentChildIndex = 0; retNode = _currentNode; } @@ -197,11 +222,11 @@ Node *Tree::aStarSearch_singlePass(Node **currentNode) { retNode = *i; i = vChildren.end() - 1; } else { - _currentMap.insert(fnpMMap::value_type(currentT, (*i))); + _currentMap->insert(new TreeNode(currentT, (*i))); } } - if (!(_currentMap.size()) && (currentT != SUCCESS)) { + if (!(_currentMap->size()) && (currentT != SUCCESS)) { assert(_currentNode != NULL); retNode = _currentNode; } @@ -209,7 +234,6 @@ Node *Tree::aStarSearch_singlePass(Node **currentNode) { } else { retNode = _currentNode; } -#endif return retNode; } diff --git a/engines/scumm/he/moonbase/ai_tree.h b/engines/scumm/he/moonbase/ai_tree.h index 1097665a7a..45d4963bc1 100644 --- a/engines/scumm/he/moonbase/ai_tree.h +++ b/engines/scumm/he/moonbase/ai_tree.h @@ -23,16 +23,23 @@ #ifndef SCUMM_HE_MOONBASE_AI_TREE_H #define SCUMM_HE_MOONBASE_AI_TREE_H -#include "common/hash-str.h" +#include "common/array.h" #include "scumm/he/moonbase/ai_node.h" namespace Scumm { -//typedef std::multimap< float, Node *, std::less<float> > fnpMMap; - const int MAX_DEPTH = 100; const int MAX_NODES = 1000000; +class AI; + +struct TreeNode { + float value; + Node *node; + + TreeNode(float v, Node *n) { value = v; node = n; } +}; + class Tree { private: Node *pBaseNode; @@ -40,17 +47,19 @@ private: int _maxDepth; int _maxNodes; - int currentChildIndex; + int _currentChildIndex; - Common::StringMap _currentMap; + Common::SortedArray<TreeNode *> *_currentMap; Node *_currentNode; + AI *_ai; + public: - Tree(); - Tree(IContainedObject *contents); - Tree(IContainedObject *contents, int maxDepth); - Tree(IContainedObject *contents, int maxDepth, int maxNodes); - Tree(const Tree *sourceTree); + Tree(AI *ai); + Tree(IContainedObject *contents, AI *ai); + Tree(IContainedObject *contents, int maxDepth, AI *ai); + Tree(IContainedObject *contents, int maxDepth, int maxNodes, AI *ai); + Tree(const Tree *sourceTree, AI *ai); ~Tree(); void duplicateTree(Node *sourceNode, Node *destNode); @@ -65,7 +74,7 @@ public: Node *aStarSearch(); Node *aStarSearch_singlePassInit(); - Node *aStarSearch_singlePass(Node **currentNode); + Node *aStarSearch_singlePass(); int IsBaseNode(Node *thisNode); }; diff --git a/engines/scumm/he/moonbase/ai_types.cpp b/engines/scumm/he/moonbase/ai_types.cpp index 1674e03777..e134f5ee12 100644 --- a/engines/scumm/he/moonbase/ai_types.cpp +++ b/engines/scumm/he/moonbase/ai_types.cpp @@ -27,6 +27,7 @@ namespace Scumm { AIEntity::AIEntity(int id) { switch (id) { + default: case BRUTAKAS: warning("BRUTAKAS"); _id = id; diff --git a/engines/scumm/he/moonbase/ai_weapon.cpp b/engines/scumm/he/moonbase/ai_weapon.cpp index b13d1efebe..ba50aae4d1 100644 --- a/engines/scumm/he/moonbase/ai_weapon.cpp +++ b/engines/scumm/he/moonbase/ai_weapon.cpp @@ -27,6 +27,7 @@ namespace Scumm { Weapon::Weapon(int typeID) { //, float damage, int radius) switch (typeID) { + default: case ITEM_BOMB: becomeBomb(); break; diff --git a/engines/scumm/he/moonbase/moonbase.cpp b/engines/scumm/he/moonbase/moonbase.cpp index cc25c270e4..15ababd321 100644 --- a/engines/scumm/he/moonbase/moonbase.cpp +++ b/engines/scumm/he/moonbase/moonbase.cpp @@ -22,20 +22,30 @@ #include "scumm/he/intern_he.h" #include "scumm/he/moonbase/moonbase.h" +#include "scumm/he/moonbase/ai_main.h" namespace Scumm { -Moonbase::Moonbase(ScummEngine_v71he *vm) : _vm(vm) { +Moonbase::Moonbase(ScummEngine_v100he *vm) : _vm(vm) { initFOW(); + + _ai = new AI(_vm); } Moonbase::~Moonbase() { + delete _ai; } int Moonbase::readFromArray(int array, int y, int x) { - _vm->VAR(((ScummEngine_v90he *)_vm)->VAR_U32_ARRAY_UNK) = array; + _vm->VAR(_vm->VAR_U32_ARRAY_UNK) = array; - return _vm->readArray(116, y, x); + return _vm->readArray(_vm->VAR_U32_ARRAY_UNK, y, x); +} + +void Moonbase::deallocateArray(int array) { + _vm->VAR(_vm->VAR_U32_ARRAY_UNK) = array; + + return _vm->nukeArray(_vm->VAR_U32_ARRAY_UNK); } int Moonbase::callScummFunction(int scriptNumber, int paramCount,...) { @@ -43,9 +53,21 @@ int Moonbase::callScummFunction(int scriptNumber, int paramCount,...) { va_start(va_params, paramCount); int args[25]; - for (int i = 0; i < paramCount; i++) + memset(args, 0, sizeof(args)); + + Common::String str; + str = Common::String::format("callScummFunction(%d, [", scriptNumber); + + for (int i = 0; i < paramCount; i++) { args[i] = va_arg(va_params, int); + str += Common::String::format("%d ", args[i]); + } + str += "])"; + + debug(0, "%s", str.c_str()); + + va_end(va_params); _vm->runScript(scriptNumber, 0, 1, args); diff --git a/engines/scumm/he/moonbase/moonbase.h b/engines/scumm/he/moonbase/moonbase.h index 7d93661dec..243d53a11d 100644 --- a/engines/scumm/he/moonbase/moonbase.h +++ b/engines/scumm/he/moonbase/moonbase.h @@ -29,12 +29,15 @@ namespace Scumm { +class AI; + class Moonbase { public: - Moonbase(ScummEngine_v71he *vm); + Moonbase(ScummEngine_v100he *vm); ~Moonbase(); int readFromArray(int array, int y, int x); + void deallocateArray(int array); int callScummFunction(int scriptNumber, int paramCount,...); void blitT14WizImage(uint8 *dst, int dstw, int dsth, int dstPitch, const Common::Rect *clipBox, @@ -65,8 +68,10 @@ public: int _fowSentinelState; uint32 _fowSentinelConditionBits; + AI *_ai; + private: - ScummEngine_v71he *_vm; + ScummEngine_v100he *_vm; int _fowFrameBaseNumber; int _fowAnimationFrames; diff --git a/engines/scumm/he/script_v100he.cpp b/engines/scumm/he/script_v100he.cpp index cd807f06b8..2e7b4c4bf5 100644 --- a/engines/scumm/he/script_v100he.cpp +++ b/engines/scumm/he/script_v100he.cpp @@ -356,38 +356,38 @@ void ScummEngine_v100he::o100_actorOps() { // FIXME: check stack parameters debug(0,"o100_actorOps: case 0 UNHANDLED"); break; - case 3: + case 3: // SO_ANIMATION pop(); pop(); pop(); break; - case 4: // SO_ANIMATION_SPEED + case 4: // SO_ANIMATION_SPEED a->setAnimSpeed(pop()); break; - case 6: + case 6: // SO_AT j = pop(); i = pop(); a->putActor(i, j); break; - case 8: + case 8: // SO_BACKGROUND_OFF a->_drawToBackBuf = false; a->_needRedraw = true; a->_needBgReset = true; break; - case 9: + case 9: // SO_BACKGROUND_ON a->drawActorToBackBuf(a->getPos().x, a->getPos().y); break; - case 14: + case 14: // SO_CHARSET a->_charset = pop(); break; - case 18: + case 18: // SO_CLIPPED a->_clipOverride.bottom = pop(); a->_clipOverride.right = pop(); a->_clipOverride.top = pop(); a->_clipOverride.left = pop(); adjustRect(a->_clipOverride); break; - case 22: + case 22: // SO_CONDITION k = getStackList(args, ARRAYSIZE(args)); for (i = 0; i < k; ++i) { a->setUserCondition(args[i] & 0x7F, args[i] & 0x80); @@ -399,7 +399,7 @@ void ScummEngine_v100he::o100_actorOps() { case 27: // SO_DEFAULT a->initActor(0); break; - case 32: + case 32: // SO_ERASE k = pop(); a->setHEFlag(1, k); break; @@ -417,11 +417,11 @@ void ScummEngine_v100he::o100_actorOps() { a->remapActorPaletteColor(i, j); a->_needRedraw = true; break; - case 59: + case 59: // SO_PRIORITY a->_layer = pop(); a->_needRedraw = true; break; - case 63: + case 63: // SO_ROOM_PALETTE a->_hePaletteNum = pop(); a->_needRedraw = true; break; @@ -438,7 +438,7 @@ void ScummEngine_v100he::o100_actorOps() { i = pop(); a->setActorWalkSpeed(i, j); break; - case 78: + case 78: // SO_TALKIE { copyScriptString(string, sizeof(string)); int slot = pop(); @@ -461,7 +461,7 @@ void ScummEngine_v100he::o100_actorOps() { case 89: // SO_NEVER_ZCLIP a->_forceClip = 0; break; - case 128: + case 128: // SO_ACTOR_DEFAULT_CLIPPED _actorClipOverride.bottom = pop(); _actorClipOverride.right = pop(); _actorClipOverride.top = pop(); @@ -517,7 +517,7 @@ void ScummEngine_v100he::o100_actorOps() { case 141: // SO_TALK_COLOR a->_talkColor = pop(); break; - case 142: + case 142: // SO_TALK_CONDITION k = pop(); if (k == 0) k = _rnd.getRandomNumberRng(1, 10); @@ -583,7 +583,7 @@ void ScummEngine_v100he::o100_arrayOps() { writeArray(array, 0, b + c, pop()); } break; - case 130: // + case 130: // SO_COMPLEX_ARRAY_ASSIGNMENT len = getStackList(list, ARRAYSIZE(list)); dim1end = pop(); dim1start = pop(); @@ -1617,13 +1617,13 @@ void ScummEngine_v100he::o100_roomOps() { setPalColor(d, a, b, c); break; - case 129: + case 129: // SO_OBJECT_ORDER b = pop(); a = pop(); swapObjects(a, b); break; - case 130: + case 130: // SO_ROOM_COPY_PALETTE a = pop(); b = pop(); if (_game.features & GF_16BIT_COLOR) @@ -1658,7 +1658,7 @@ void ScummEngine_v100he::o100_roomOps() { setCurrentPalette(a); break; - case 135: + case 135: // SO_ROOM_PALETTE_IN_ROOM b = pop(); a = pop(); setRoomPalette(a, b); @@ -1670,7 +1670,7 @@ void ScummEngine_v100he::o100_roomOps() { _saveLoadFlag = pop(); break; - case 137: + case 137: // SO_ROOM_SAVEGAME_BY_NAME byte buffer[256]; copyScriptString((byte *)buffer, sizeof(buffer)); diff --git a/engines/scumm/he/script_v72he.cpp b/engines/scumm/he/script_v72he.cpp index 31b4887d10..d32eb766cb 100644 --- a/engines/scumm/he/script_v72he.cpp +++ b/engines/scumm/he/script_v72he.cpp @@ -119,7 +119,7 @@ byte *ScummEngine_v72he::defineArray(int array, int type, int dim2start, int dim id = findFreeArrayId(); - debug(9,"defineArray (array %d, dim2start %d, dim2end %d dim1start %d dim1end %d", id, dim2start, dim2end, dim1start, dim1end); + debug(9, "defineArray (array %d, dim2start %d, dim2end %d dim1start %d dim1end %d", id, dim2start, dim2end, dim1start, dim1end); if (array & 0x80000000) { error("Can't define bit variable as array pointer"); @@ -699,13 +699,13 @@ void ScummEngine_v72he::o72_roomOps() { setCurrentPalette(a); break; - case 220: + case 220: // SO_ROOM_COPY_PALETTE a = pop(); b = pop(); copyPalColor(a, b); break; - case 221: + case 221: // SO_ROOM_SAVEGAME_BY_NAME byte buffer[256]; copyScriptString((byte *)buffer, sizeof(buffer)); @@ -718,13 +718,13 @@ void ScummEngine_v72he::o72_roomOps() { _saveTemporaryState = true; break; - case 234: + case 234: // SO_OBJECT_ORDER b = pop(); a = pop(); swapObjects(a, b); break; - case 236: + case 236: // SO_ROOM_PALETTE_IN_ROOM b = pop(); a = pop(); setRoomPalette(a, b); @@ -752,43 +752,43 @@ void ScummEngine_v72he::o72_actorOps() { return; switch (subOp) { - case 21: // HE 80+ + case 21: // SO_CONDITION (HE 80+) k = getStackList(args, ARRAYSIZE(args)); for (i = 0; i < k; ++i) { a->setUserCondition(args[i] & 0x7F, args[i] & 0x80); } break; - case 24: // HE 80+ + case 24: // SO_TALK_CONDITION (HE 80+) k = pop(); if (k == 0) k = _rnd.getRandomNumberRng(1, 10); a->_heNoTalkAnimation = 1; a->setTalkCondition(k); break; - case 43: // HE 90+ + case 43: // SO_PRIORITY (HE 90+) a->_layer = pop(); a->_needRedraw = true; break; - case 64: + case 64: // SO_ACTOR_DEFAULT_CLIPPED _actorClipOverride.bottom = pop(); _actorClipOverride.right = pop(); _actorClipOverride.top = pop(); _actorClipOverride.left = pop(); adjustRect(_actorClipOverride); break; - case 65: // HE 98+ + case 65: // SO_AT (HE 98+) j = pop(); i = pop(); a->putActor(i, j); break; - case 67: // HE 99+ + case 67: // SO_CLIPPED (HE 99+) a->_clipOverride.bottom = pop(); a->_clipOverride.right = pop(); a->_clipOverride.top = pop(); a->_clipOverride.left = pop(); adjustRect(a->_clipOverride); break; - case 68: // HE 90+ + case 68: // // SO_ERASE (HE 90+) k = pop(); a->setHEFlag(1, k); break; @@ -887,10 +887,10 @@ void ScummEngine_v72he::o72_actorOps() { a->_talkPosY = pop(); a->_talkPosX = pop(); break; - case 156: // HE 72+ + case 156: // SO_CHARSET (HE 72+) a->_charset = pop(); break; - case 175: // HE 99+ + case 175: // SO_ROOM_PALETTE (HE 99+) a->_hePaletteNum = pop(); a->_needRedraw = true; break; @@ -907,15 +907,15 @@ void ScummEngine_v72he::o72_actorOps() { case 217: // SO_ACTOR_NEW a->initActor(2); break; - case 218: + case 218: // SO_BACKGROUND_ON a->drawActorToBackBuf(a->getPos().x, a->getPos().y); break; - case 219: + case 219: // SO_BACKGROUND_OFF a->_drawToBackBuf = false; a->_needRedraw = true; a->_needBgReset = true; break; - case 225: + case 225: // SO_TALKIE { copyScriptString(string, sizeof(string)); int slot = pop(); @@ -1077,7 +1077,7 @@ void ScummEngine_v72he::o72_arrayOps() { memcpy(data, string, len); break; - case 126: + case 126: // SO_COMPLEX_ARRAY_ASSIGNMENT len = getStackList(list, ARRAYSIZE(list)); dim1end = pop(); dim1start = pop(); @@ -1101,7 +1101,7 @@ void ScummEngine_v72he::o72_arrayOps() { dim2start++; } break; - case 127: + case 127: // SO_COMPLEX_ARRAY_COPY_OPERATION { int a2_dim1end = pop(); int a2_dim1start = pop(); @@ -1118,7 +1118,7 @@ void ScummEngine_v72he::o72_arrayOps() { copyArray(array, a1_dim2start, a1_dim2end, a1_dim1start, a1_dim1end, array2, a2_dim2start, a2_dim2end, a2_dim1start, a2_dim1end); } break; - case 128: + case 128: // SO_RANGE_ARRAY_ASSIGNMENT b = pop(); c = pop(); dim1end = pop(); @@ -1149,7 +1149,7 @@ void ScummEngine_v72he::o72_arrayOps() { dim2start++; } break; - case 194: + case 194: // SO_FORMATTED_STRING decodeScriptString(string); len = resStrLen(string); data = defineArray(array, kStringArray, 0, 0, 0, len); diff --git a/engines/scumm/he/wiz_he.cpp b/engines/scumm/he/wiz_he.cpp index 0ebfe67245..d730cb2894 100644 --- a/engines/scumm/he/wiz_he.cpp +++ b/engines/scumm/he/wiz_he.cpp @@ -1573,8 +1573,8 @@ uint8 *Wiz::drawWizImage(int resNum, int state, int maskNum, int maskState, int } if (_vm->_game.id == GID_MOONBASE && - ((ScummEngine_v90he *)_vm)->_moonbase->isFOW(resNum, state, conditionBits)) { - ((ScummEngine_v90he *)_vm)->_moonbase->renderFOW(dst, dstPitch, dstType, cw, ch, flags); + ((ScummEngine_v100he *)_vm)->_moonbase->isFOW(resNum, state, conditionBits)) { + ((ScummEngine_v100he *)_vm)->_moonbase->renderFOW(dst, dstPitch, dstType, cw, ch, flags); x1 = 0; y1 = 0; width = rScreen.width(); @@ -1748,7 +1748,7 @@ void Wiz::copyCompositeWizImage(uint8 *dst, uint8 *wizPtr, uint8 *compositeInfoB drawFlags = flags; } - uint srcw1, srch1; + uint srcw1 = 0, srch1 = 0; if (drawFlags & (kWIFFlipX | kWIFFlipY)) { uint8 *wizh = _vm->findWrappedBlock(MKTAG('W','I','Z','H'), wizPtr, subState, 0); assert(wizh); @@ -1814,7 +1814,7 @@ void Wiz::copy555WizImage(uint8 *dst, uint8 *wizd, int dstPitch, int dstType, uint32 compID = READ_LE_UINT32(wizd); if (compID == 0x12340102) { - ((ScummEngine_v90he *)_vm)->_moonbase->blitT14WizImage(dst, dstw, dsth, dstPitch, clipBox, wizd, srcx, srcy, rawROP, paramROP); + ((ScummEngine_v100he *)_vm)->_moonbase->blitT14WizImage(dst, dstw, dsth, dstPitch, clipBox, wizd, srcx, srcy, rawROP, paramROP); } else if (compID == 0x12340802) { warning("Distorion codec"); } else if (compID == 0x12340902) { diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 4adf0c5066..d5727f2a7c 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -885,7 +885,20 @@ ScummEngine_v90he::ScummEngine_v90he(OSystem *syst, const DetectorResult &dr) VAR_U32_VERSION = 0xFF; VAR_U32_ARRAY_UNK = 0xFF; +} +ScummEngine_v90he::~ScummEngine_v90he() { + delete _moviePlay; + delete _sprite; + if (_game.heversion >= 98) { + delete _logicHE; + } + if (_game.heversion >= 99) { + free(_hePalettes); + } +} + +ScummEngine_v100he::ScummEngine_v100he(OSystem *syst, const DetectorResult &dr) : ScummEngine_v99he(syst, dr) { /* Moonbase stuff */ _moonbase = 0; @@ -900,15 +913,7 @@ ScummEngine_v90he::ScummEngine_v90he(OSystem *syst, const DetectorResult &dr) VAR_U32_USER_VAR_F = 0xFF; } -ScummEngine_v90he::~ScummEngine_v90he() { - delete _moviePlay; - delete _sprite; - if (_game.heversion >= 98) { - delete _logicHE; - } - if (_game.heversion >= 99) { - free(_hePalettes); - } +ScummEngine_v100he::~ScummEngine_v100he() { delete _moonbase; } diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index a3fa329728..f9758aec33 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -700,8 +700,6 @@ protected: void ignoreScriptWord() { fetchScriptWord(); } void ignoreScriptByte() { fetchScriptByte(); } void push(int a); - -public: // TODO. FIXME should be protected. Used by Moonbase int pop(); virtual int readVar(uint var); virtual void writeVar(uint var, int value); diff --git a/engines/scumm/scumm_v2.h b/engines/scumm/scumm_v2.h index 763bbc061c..2a9e7a96e6 100644 --- a/engines/scumm/scumm_v2.h +++ b/engines/scumm/scumm_v2.h @@ -78,7 +78,6 @@ protected: void getResultPosIndirect(); virtual void getResultPos(); -public: // TODO. FIXME. Should be protected. Used by Moonbase virtual int readVar(uint var); virtual void writeVar(uint var, int value); diff --git a/engines/scumm/vars.cpp b/engines/scumm/vars.cpp index 5254aa4af2..e5ba4a68ee 100644 --- a/engines/scumm/vars.cpp +++ b/engines/scumm/vars.cpp @@ -340,6 +340,11 @@ void ScummEngine_v90he::setupScummVars() { VAR_NUM_PALETTES = 130; VAR_NUM_UNK = 131; } +} + +void ScummEngine_v100he::setupScummVars() { + ScummEngine_v90he::setupScummVars(); + if (_game.id == GID_MOONBASE) { VAR_U32_USER_VAR_A = 108; VAR_U32_USER_VAR_B = 109; diff --git a/engines/sherlock/fonts.cpp b/engines/sherlock/fonts.cpp index 5a14881f1c..dc7ecd521e 100644 --- a/engines/sherlock/fonts.cpp +++ b/engines/sherlock/fonts.cpp @@ -195,7 +195,7 @@ inline byte Fonts::translateChar(byte c) { } } -void Fonts::writeString(Surface *surface, const Common::String &str, +void Fonts::writeString(BaseSurface *surface, const Common::String &str, const Common::Point &pt, int overrideColor) { Common::Point charPos = pt; diff --git a/engines/sherlock/fonts.h b/engines/sherlock/fonts.h index 3594d466c2..6c805447b3 100644 --- a/engines/sherlock/fonts.h +++ b/engines/sherlock/fonts.h @@ -31,7 +31,7 @@ namespace Sherlock { class SherlockEngine; class ImageFile; -class Surface; +class BaseSurface; class Fonts { private: @@ -44,7 +44,7 @@ protected: static int _widestChar; static uint16 _charCount; - static void writeString(Surface *surface, const Common::String &str, + static void writeString(BaseSurface *surface, const Common::String &str, const Common::Point &pt, int overrideColor = 0); static inline byte translateChar(byte c); diff --git a/engines/sherlock/scalpel/scalpel_inventory.cpp b/engines/sherlock/scalpel/scalpel_inventory.cpp index 6eb8c2c05c..07659b41f2 100644 --- a/engines/sherlock/scalpel/scalpel_inventory.cpp +++ b/engines/sherlock/scalpel/scalpel_inventory.cpp @@ -109,7 +109,7 @@ void ScalpelInventory::drawInventory(InvNewMode mode) { _invMode = (InvMode)((int)mode); if (mode != PLAIN_INVENTORY) { - assert(mode < sizeof(_hotkeysIndexed)); + assert((uint)mode < sizeof(_hotkeysIndexed)); ui._oldKey = _hotkeysIndexed[mode]; } else { ui._oldKey = -1; diff --git a/engines/sherlock/screen.cpp b/engines/sherlock/screen.cpp index d96310abb3..a829ab22e6 100644 --- a/engines/sherlock/screen.cpp +++ b/engines/sherlock/screen.cpp @@ -40,7 +40,9 @@ Screen *Screen::init(SherlockEngine *vm) { return new Scalpel::ScalpelScreen(vm); } -Screen::Screen(SherlockEngine *vm) : Graphics::Screen(), _vm(vm) { +Screen::Screen(SherlockEngine *vm) : BaseSurface(), _vm(vm), + _backBuffer1(vm->getGameID() == GType_RoseTattoo ? 640 : 320, vm->getGameID() == GType_RoseTattoo ? 480 : 200), + _backBuffer2(vm->getGameID() == GType_RoseTattoo ? 640 : 320, vm->getGameID() == GType_RoseTattoo ? 480 : 200) { _transitionSeed = 1; _fadeStyle = false; Common::fill(&_cMap[0], &_cMap[PALETTE_SIZE], 0); @@ -55,6 +57,7 @@ Screen::Screen(SherlockEngine *vm) : Graphics::Screen(), _vm(vm) { _oldFadePercent = 0; _flushScreen = false; + create(_backBuffer1.w, _backBuffer1.h); _backBuffer.create(_backBuffer1, _backBuffer1.getBounds()); } diff --git a/engines/sherlock/screen.h b/engines/sherlock/screen.h index f05a4f0a90..fb44c6dde2 100644 --- a/engines/sherlock/screen.h +++ b/engines/sherlock/screen.h @@ -25,7 +25,6 @@ #include "common/list.h" #include "common/rect.h" -#include "graphics/screen.h" #include "sherlock/image_file.h" #include "sherlock/surface.h" #include "sherlock/resources.h" @@ -39,7 +38,7 @@ namespace Sherlock { class SherlockEngine; -class Screen : virtual public Graphics::Screen, virtual public Surface { +class Screen : public BaseSurface { private: uint32 _transitionSeed; diff --git a/engines/sherlock/surface.cpp b/engines/sherlock/surface.cpp index 47b7d4a780..93bc001149 100644 --- a/engines/sherlock/surface.cpp +++ b/engines/sherlock/surface.cpp @@ -25,19 +25,20 @@ namespace Sherlock { -Surface::Surface() : Graphics::ManagedSurface(), Fonts() { +BaseSurface::BaseSurface() : Graphics::Screen(0, 0), Fonts() { + free(); // Free the 0x0 surface allocated by Graphics::Screen } -Surface::Surface(int width, int height) : Graphics::ManagedSurface(width, height), +BaseSurface::BaseSurface(int width, int height) : Graphics::Screen(width, height), Fonts() { create(width, height); } -void Surface::writeString(const Common::String &str, const Common::Point &pt, uint overrideColor) { +void BaseSurface::writeString(const Common::String &str, const Common::Point &pt, uint overrideColor) { Fonts::writeString(this, str, pt, overrideColor); } -void Surface::writeFancyString(const Common::String &str, const Common::Point &pt, uint overrideColor1, uint overrideColor2) { +void BaseSurface::writeFancyString(const Common::String &str, const Common::Point &pt, uint overrideColor1, uint overrideColor2) { writeString(str, Common::Point(pt.x, pt.y), overrideColor1); writeString(str, Common::Point(pt.x + 1, pt.y), overrideColor1); writeString(str, Common::Point(pt.x + 2, pt.y), overrideColor1); @@ -49,19 +50,19 @@ void Surface::writeFancyString(const Common::String &str, const Common::Point &p writeString(str, Common::Point(pt.x + 1, pt.y + 1), overrideColor2); } -void Surface::SHtransBlitFrom(const ImageFrame &src, const Common::Point &pt, +void BaseSurface::SHtransBlitFrom(const ImageFrame &src, const Common::Point &pt, bool flipped, int overrideColor, int scaleVal) { Common::Point drawPt(pt.x + src.sDrawXOffset(scaleVal), pt.y + src.sDrawYOffset(scaleVal)); SHtransBlitFrom(src._frame, drawPt, flipped, overrideColor, scaleVal); } -void Surface::SHtransBlitFrom(const Graphics::Surface &src, const Common::Point &pt, +void BaseSurface::SHtransBlitFrom(const Graphics::Surface &src, const Common::Point &pt, bool flipped, int overrideColor, int scaleVal) { Common::Rect srcRect(0, 0, src.w, src.h); Common::Rect destRect(pt.x, pt.y, pt.x + src.w * SCALE_THRESHOLD / scaleVal, pt.y + src.h * SCALE_THRESHOLD / scaleVal); - Graphics::ManagedSurface::transBlitFrom(src, srcRect, destRect, TRANSPARENCY, + Graphics::Screen::transBlitFrom(src, srcRect, destRect, TRANSPARENCY, flipped, overrideColor); } diff --git a/engines/sherlock/surface.h b/engines/sherlock/surface.h index 7f946b467f..807fbeb1d1 100644 --- a/engines/sherlock/surface.h +++ b/engines/sherlock/surface.h @@ -25,7 +25,7 @@ #include "common/rect.h" #include "common/platform.h" -#include "graphics/managed_surface.h" +#include "graphics/screen.h" #include "sherlock/fonts.h" #include "sherlock/image_file.h" @@ -35,21 +35,21 @@ namespace Sherlock { #define TRANSPARENCY 255 /** - * Implements a descendent surface that combines both a managed surface and the font + * Implements a base surface that combines both a managed surface and the font * drawing code. It also introduces a series of drawing method stubs that the 3DO * Serrated Scalpel screen overrides to implement sprite doubling */ -class Surface: virtual public Graphics::ManagedSurface, public Fonts { +class BaseSurface: public Graphics::Screen, public Fonts { public: /** * Constructor */ - Surface(); - + BaseSurface(); + /** * Constructor */ - Surface(int width, int height); + BaseSurface(int width, int height); /** * Draws a surface on this surface @@ -95,7 +95,7 @@ public: * Return the width of the surface */ virtual uint16 width() const { return this->w; } - + /** * Return the height of the surface */ @@ -105,13 +105,25 @@ public: * Draws the given string into the back buffer using the images stored in _font */ void writeString(const Common::String &str, const Common::Point &pt, uint overrideColor); - + /** * Draws a fancy version of the given string at the given position */ void writeFancyString(const Common::String &str, const Common::Point &pt, uint overrideColor1, uint overrideColor2); }; +class Surface : public BaseSurface { +protected: + /** + * Override the addDirtyRect from Graphics::Screen, since for standard + * surfaces we don't need dirty rects to be tracked + */ + virtual void addDirtyRect(const Common::Rect &r) {} +public: + Surface() : BaseSurface() {} + Surface(int w, int h) : BaseSurface(w, h) {} +}; + } // End of namespace Sherlock #endif diff --git a/engines/tsage/detection.cpp b/engines/tsage/detection.cpp index fe555f2fdb..584ad87742 100644 --- a/engines/tsage/detection.cpp +++ b/engines/tsage/detection.cpp @@ -83,7 +83,7 @@ public: } virtual const char *getOriginalCopyright() const { - return "(c) Tsunami Media"; + return "(C) Tsunami Media"; } virtual bool hasFeature(MetaEngineFeature f) const { diff --git a/engines/tsage/graphics.cpp b/engines/tsage/graphics.cpp index 58fa5b8094..7b7b41f0aa 100644 --- a/engines/tsage/graphics.cpp +++ b/engines/tsage/graphics.cpp @@ -229,14 +229,16 @@ void Rect::synchronize(Serializer &s) { /*--------------------------------------------------------------------------*/ -GfxSurface::GfxSurface() : Graphics::ManagedSurface(), _bounds(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) { +GfxSurface::GfxSurface() : Graphics::Screen(0, 0), _bounds(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) { + free(); // Free the 0x0 surface allocated by Graphics::Screen _disableUpdates = false; _lockSurfaceCtr = 0; _transColor = -1; _flags = 0; } -GfxSurface::GfxSurface(const GfxSurface &s): Graphics::ManagedSurface() { +GfxSurface::GfxSurface(const GfxSurface &s): Graphics::Screen(0, 0) { + free(); // Free the 0x0 surface allocated by Graphics::Screen _lockSurfaceCtr = 0; operator=(s); diff --git a/engines/tsage/graphics.h b/engines/tsage/graphics.h index 3b395b7625..51636c4119 100644 --- a/engines/tsage/graphics.h +++ b/engines/tsage/graphics.h @@ -28,7 +28,7 @@ #include "common/list.h" #include "common/rect.h" #include "common/system.h" -#include "graphics/managed_surface.h" +#include "graphics/screen.h" namespace TsAGE { @@ -73,13 +73,23 @@ public: enum FrameFlag { FRAME_FLIP_CENTROID_X = 4, FRAME_FLIP_CENTROID_Y = 8 }; -class GfxSurface: virtual public Graphics::ManagedSurface { +/** + * Surface class. This derivces from Graphics::Screen because it has + * logic we'll need for our own Screen class that derives from this one + */ + class GfxSurface: public Graphics::Screen { private: int _lockSurfaceCtr; Graphics::ManagedSurface _rawSurface; bool _disableUpdates; Rect _bounds; + protected: + /** + * Override the addDirtyRect from Graphics::Screen, since for standard + * surfaces we don't need dirty rects to be tracked + */ + virtual void addDirtyRect(const Common::Rect &r) {} public: Common::Point _centroid; int _transColor; diff --git a/engines/tsage/screen.cpp b/engines/tsage/screen.cpp index f11c384797..eaf2067c32 100644 --- a/engines/tsage/screen.cpp +++ b/engines/tsage/screen.cpp @@ -25,10 +25,15 @@ namespace TsAGE { -Screen::Screen(): GfxSurface(), Graphics::Screen() { +Screen::Screen(): GfxSurface() { create(SCREEN_WIDTH, SCREEN_HEIGHT); } +Screen::~Screen() { + // Delete the screen's surface + free(); +} + void Screen::update() { // When dialogs are active, the screen surface may be remapped to // sub-sections of the screen. But for drawing we'll need to temporarily diff --git a/engines/tsage/screen.h b/engines/tsage/screen.h index bf5057e4d6..c5cfee754a 100644 --- a/engines/tsage/screen.h +++ b/engines/tsage/screen.h @@ -36,7 +36,14 @@ namespace TsAGE { #define SCREEN_CENTER_Y 100 #define UI_INTERFACE_Y 168 -class Screen : virtual public Graphics::Screen, virtual public GfxSurface { +class Screen : public GfxSurface { + /** + * Override the addDirtyRect from GfxSurface, since for our screen + * class we need to reintroduce the standard Graphics::Screen implementation + */ + virtual void addDirtyRect(const Common::Rect &r) { + Graphics::Screen::addDirtyRect(r); + } public: /** * Constructor @@ -46,7 +53,7 @@ public: /** * Destructor */ - virtual ~Screen() {} + virtual ~Screen(); /** * Update the screen diff --git a/engines/voyeur/detection.cpp b/engines/voyeur/detection.cpp index 9e5320aac8..7b9fa6722e 100644 --- a/engines/voyeur/detection.cpp +++ b/engines/voyeur/detection.cpp @@ -75,7 +75,7 @@ public: } virtual const char *getOriginalCopyright() const { - return "Voyeur (c) Philips P.O.V. Entertainment Group"; + return "Voyeur (C) Philips P.O.V. Entertainment Group"; } virtual bool hasFeature(MetaEngineFeature f) const; diff --git a/engines/voyeur/files.h b/engines/voyeur/files.h index 8726b38ddf..92b43958d3 100644 --- a/engines/voyeur/files.h +++ b/engines/voyeur/files.h @@ -325,9 +325,13 @@ private: ViewPortAddPtr addFn, ViewPortRestorePtr restoreFn); public: ViewPortResource *_parent; + ViewPortSetupPtr _setupFn; int _pageCount; + ViewPortAddPtr _addFn; int _pageIndex; + ViewPortRestorePtr _restoreFn; int _lastPage; + ScreenMethodPtr _fn1; Common::Rect _bounds; PictureResource *_currentPic; PictureResource *_activePage; @@ -340,10 +344,6 @@ public: int _rectListCount[3]; Common::Rect _clipRect; - ScreenMethodPtr _fn1; - ViewPortSetupPtr _setupFn; - ViewPortAddPtr _addFn; - ViewPortRestorePtr _restoreFn; Common::Rect _fontRect; public: ViewPortResource(BoltFilesState &state, const byte *src); diff --git a/engines/wintermute/detection.cpp b/engines/wintermute/detection.cpp index f77eb5c64d..4e8eab505f 100644 --- a/engines/wintermute/detection.cpp +++ b/engines/wintermute/detection.cpp @@ -85,7 +85,7 @@ public: } virtual const char *getOriginalCopyright() const { - return "Copyright (c) 2011 Jan Nedoma"; + return "Copyright (C) 2011 Jan Nedoma"; } virtual const ADGameDescription *fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const { diff --git a/graphics/module.mk b/graphics/module.mk index 90f6a3199c..7331a56c93 100644 --- a/graphics/module.mk +++ b/graphics/module.mk @@ -13,6 +13,7 @@ MODULE_OBJS := \ fonts/winfont.o \ maccursor.o \ managed_surface.o \ + nine_patch.o \ pixelformat.o \ primitives.o \ scaler.o \ diff --git a/graphics/nine_patch.cpp b/graphics/nine_patch.cpp new file mode 100644 index 0000000000..fea0d8ae66 --- /dev/null +++ b/graphics/nine_patch.cpp @@ -0,0 +1,244 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* This code is based on Nine Patch code by Matthew Leverton + taken from https://github.com/konforce/Allegro-Nine-Patch + + Copyright (C) 2011 Matthew Leverton + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + + +#include "common/array.h" +#include "graphics/transparent_surface.h" +#include "graphics/nine_patch.h" + +namespace Graphics { + +NinePatchSide::~NinePatchSide() { + for (uint i = 0; i < _m.size(); i++) + delete _m[i]; + + _m.clear(); +} + + +bool NinePatchSide::init(Graphics::TransparentSurface *bmp, bool vertical) { + const int len = vertical ? bmp->h : bmp->w; + int i; + int s, t, n, z; + + _m.clear(); + + for (i = 1, s = -1, t = 0, n = 0, z = -1; i < len; ++i) { + int zz; + uint8 r, g, b, a; + uint32 *color = vertical ? (uint32 *)bmp->getBasePtr(0, i) : (uint32 *)bmp->getBasePtr(i, 0); + bmp->format.colorToARGB(*color, a, r, g, b); + + if (i == len - 1) + zz = -1; + else if (r == 0 && g == 0 && b == 0 && a == 255) + zz = 0; + else if (a == 0 || r + g + b + a == 255 * 4) + zz = 1; + else + return false; + + if (z != zz) { + if (s != -1) { + NinePatchMark *mrk = new NinePatchMark; + + mrk->offset = s; + mrk->length = i - s; + if (z == 0) { + mrk->ratio = 1; + t += mrk->length; + } else { + mrk->ratio = 0; + } + _m.push_back(mrk); + } + s = i; + z = zz; + } + } + + _fix = len - 2 - t; + for (i = 0; i < _m.size(); ++i) { + if (_m[i]->ratio) + _m[i]->ratio = _m[i]->length / (float)t; + } + + return true; +} + +void NinePatchSide::calcOffsets(int len) { + uint i, j; + int dest_offset = 0; + int remaining_stretch = len - _fix; + + for (i = 0, j = 0; i < _m.size(); ++i) { + _m[i]->dest_offset = dest_offset; + if (_m[i]->ratio == 0) { + _m[i]->dest_length = _m[i]->length; + } else { + _m[i]->dest_length = (len - _fix) * _m[i]->ratio; + remaining_stretch -= _m[i]->dest_length; + j = i; + } + + dest_offset += _m[i]->dest_length; + } + + if (remaining_stretch) { + _m[j]->dest_length += remaining_stretch; + if (j + 1 < _m.size()) + _m[j + 1]->dest_offset += remaining_stretch; + } +} + +NinePatchBitmap::NinePatchBitmap(Graphics::TransparentSurface *bmp, bool owns_bitmap) { + int i; + uint8 r, g, b, a; + + _bmp = bmp; + _destroy_bmp = owns_bitmap; + _h._m.clear(); + _v._m.clear(); + _cached_dw = 0; + _cached_dh = 0; + _width = bmp->w - 2; + _height = bmp->h - 2; + + if (_width <= 0 || _height <= 0) + goto bad_bitmap; + + /* make sure all four corners are transparent */ +#define _check_pixel(x, y) \ + bmp->format.colorToARGB(*(uint32 *)bmp->getBasePtr(x, y), a, r, g, b); \ + if (a != 0 && r + g + b + a != 4) goto bad_bitmap; + + _check_pixel(0,0); + _check_pixel(bmp->w - 1, 0); + _check_pixel(0, bmp->h - 1); + _check_pixel(bmp->w - 1, bmp->h - 1); +#undef _check_pixel + + _padding.top = _padding.right = _padding.bottom = _padding.left = -1; + + i = 1; + while (i < bmp->w) { + bmp->format.colorToARGB(*(uint32 *)bmp->getBasePtr(i, bmp->h - 1), a, r, g, b); + + if (r + g + b == 0 && a == 1) { + if (_padding.left == -1) + _padding.left = i - 1; + else if (_padding.right != -1) + goto bad_bitmap; + } else if (a == 0 || r + g + b + a == 4) { + if (_padding.left != -1 && _padding.right == -1) + _padding.right = bmp->w - i - 1; + } + ++i; + } + + i = 1; + while (i < bmp->h) { + bmp->format.colorToARGB(*(uint32 *)bmp->getBasePtr(bmp->w - 1, i), a, r, g, b); + + if (r + g + b == 0 && a == 1) { + if (_padding.top == -1) + _padding.top = i - 1; + else if (_padding.bottom != -1) + goto bad_bitmap; + } else if (a == 0 || r + g + b + a == 4) { + if (_padding.top != -1 && _padding.bottom == -1) + _padding.bottom = bmp->h - i - 1; + } + ++i; + } + + if (!_h.init(bmp, false) || !_v.init(bmp, true)) { +bad_bitmap: + _h._m.clear(); + _v._m.clear(); + } +} + +void NinePatchBitmap::blit(Graphics::Surface &target, int dx, int dy, int dw, int dh) { + /* don't draw bitmaps that are smaller than the fixed area */ + if (dw < _h._fix || dh < _v._fix) + return; + + /* if the bitmap is the same size as the origin, then draw it as-is */ + if (dw == _width && dh == _height) { + Common::Rect r(1, 1, dw, dh); + + _bmp->blit(target, dx, dy, Graphics::FLIP_NONE, &r); + return; + } + + /* only recalculate the offsets if they have changed since the last draw */ + if (_cached_dw != dw || _cached_dh != dh) { + _h.calcOffsets(dw); + _v.calcOffsets(dh); + + _cached_dw = dw; + _cached_dh = dh; + } + + /* draw each region */ + for (uint i = 0; i < _v._m.size(); ++i) { + for (uint j = 0; j < _h._m.size(); ++j) { + Common::Rect r(_h._m[j]->offset, _v._m[i]->offset, + _h._m[j]->offset + _h._m[j]->length, _v._m[i]->offset + _v._m[i]->length); + + _bmp->blit(target, dx + _h._m[j]->dest_offset, dy + _v._m[i]->dest_offset, + Graphics::FLIP_NONE, &r, TS_ARGB(255, 255, 255, 255), + _h._m[j]->dest_length, _v._m[i]->dest_length); + } + } +} + +NinePatchBitmap::~NinePatchBitmap() { + if (_destroy_bmp) + delete _bmp; +} + +} // end of namespace Graphics diff --git a/graphics/nine_patch.h b/graphics/nine_patch.h new file mode 100644 index 0000000000..c62de3f6e2 --- /dev/null +++ b/graphics/nine_patch.h @@ -0,0 +1,97 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* This code is based on Nine Patch code by Matthew Leverton + taken from https://github.com/konforce/Allegro-Nine-Patch + + Copyright (C) 2011 Matthew Leverton + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do + so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + */ + +#ifndef GRAPHICS_NINE_PATCH_H +#define GRAPHICS_NINE_PATCH_H + +#include "common/array.h" + +namespace Graphics { + +struct NinePatchMark { + int offset; + int length; + int dest_offset; + int dest_length; + float ratio; +}; + +class NinePatchSide { +public: + Common::Array<NinePatchMark *> _m; + int _fix; + + NinePatchSide() : _fix(0) {} + ~NinePatchSide(); + + bool init(Graphics::TransparentSurface *bmp, bool vertical); + void calcOffsets(int len); +}; + +class NinePatchBitmap { + Graphics::TransparentSurface *_bmp; + NinePatchSide _h, _v; + Common::Rect _padding; + bool _destroy_bmp; + int _width, _height; + int _cached_dw, _cached_dh; + +public: + NinePatchBitmap(Graphics::TransparentSurface *bmp, bool owns_bitmap); + ~NinePatchBitmap(); + + void blit(Graphics::Surface &target, int dx, int dy, int dw, int dh); + + int getWidth() { return _width; } + int getHeight() { return _height; } + int getMinWidth() { return _h._fix; } + int getMinHeight() { return _v._fix; } + Graphics::TransparentSurface *getSource() { return _bmp; } + Common::Rect &getPadding() { return _padding; } +}; + +} // end of namespace Graphics + +#endif // GRAPHICS_NINE_PATCH_H diff --git a/graphics/screen.h b/graphics/screen.h index 29816120f1..b3bb2d3eb2 100644 --- a/graphics/screen.h +++ b/graphics/screen.h @@ -39,7 +39,7 @@ namespace Graphics { * calls, and provides an update that method that blits the affected * areas to the physical screen */ -class Screen : virtual public ManagedSurface { +class Screen : public ManagedSurface { private: /** * List of affected areas of the screen diff --git a/image/codecs/msrle4.cpp b/image/codecs/msrle4.cpp index cd9a8717bc..fc18af8364 100644 --- a/image/codecs/msrle4.cpp +++ b/image/codecs/msrle4.cpp @@ -85,7 +85,7 @@ void MSRLE4Decoder::decode4(Common::SeekableReadStream &stream) { return; } - output = (byte *)_surface->getBasePtr(x, y);; + output = (byte *)_surface->getBasePtr(x, y); } else { // Copy data diff --git a/po/be_BY.po b/po/be_BY.po index 20bc062b5e..84a4435946 100644 --- a/po/be_BY.po +++ b/po/be_BY.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: ScummVM 1.8.0git\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" "POT-Creation-Date: 2016-04-07 08:55+0100\n" -"PO-Revision-Date: 2016-02-21 23:32+0300\n" +"PO-Revision-Date: 2016-05-22 17:05+0300\n" "Last-Translator: Ivan Lukyanov <greencis@mail.ru>\n" "Language-Team: Ivan Lukyanov <greencis@mail.ru>\n" "Language: Belarusian\n" @@ -1054,15 +1054,15 @@ msgstr "Мова графічнага інтэрфейсу ScummVM" #: gui/options.cpp:1235 msgid "Update check:" -msgstr "" +msgstr "Правяраць абнаўленні:" #: gui/options.cpp:1235 msgid "How often to check ScummVM updates" -msgstr "" +msgstr "Як часта правяраць абнаўленні ScummVM" #: gui/options.cpp:1247 msgid "Check now" -msgstr "" +msgstr "Праверыць цяпер" #: gui/options.cpp:1382 msgid "You have to restart ScummVM before your changes will take effect." @@ -1264,19 +1264,22 @@ msgid "" "\n" "Would you like to enable this feature?" msgstr "" +"ScummVM стаў падтрымліваць аўтаматычную праверку\n" +"абнаўленняў, якая патрабуе доступу ў Інтэрнэт.\n" +"\n" +"Ці жадаеце вы ўключыць гэту опцыю?" #: gui/updates-dialog.cpp:55 msgid "(You can always enable it in the options dialog on the Misc tab)" -msgstr "" +msgstr "(Вы заўсёды можаце ўключыць яе ў наладах на закладцы \"Рознае\")" #: gui/updates-dialog.cpp:92 -#, fuzzy msgid "Check for updates automatically" -msgstr "Правяраю абнаўленні..." +msgstr "Аўтаматычна правяраць абнаўленні" #: gui/updates-dialog.cpp:111 msgid "Proceed" -msgstr "" +msgstr "Працягнуць" #: gui/widget.cpp:327 gui/widget.cpp:329 gui/widget.cpp:335 gui/widget.cpp:337 msgid "Clear value" @@ -1407,20 +1410,19 @@ msgstr "Hercules Бурштынавы" #: common/updates.cpp:58 msgid "Daily" -msgstr "" +msgstr "Штодзень" #: common/updates.cpp:60 msgid "Weekly" -msgstr "" +msgstr "Штотыдзень" #: common/updates.cpp:62 msgid "Monthly" -msgstr "" +msgstr "Штомесяц" #: common/updates.cpp:64 -#, fuzzy msgid "<Bad value>" -msgstr "Ачысціць значэнне" +msgstr "<Хібнае значэнне>" #: engines/advancedDetector.cpp:334 #, c-format @@ -2404,23 +2406,26 @@ msgstr "" "меню гульні." #: engines/agi/detection.cpp:177 -#, fuzzy msgid "Use Hercules hires font" -msgstr "Hercules Зялёны" +msgstr "Выкарыстоўваць шрыфт Hercules" #: engines/agi/detection.cpp:178 msgid "Uses Hercules hires font, when font file is available." msgstr "" +"Выкарыстоўвае шрыфт высокага адрознення Hercules, калі даступны файл са " +"шрыфтам." #: engines/agi/detection.cpp:187 msgid "Pause when entering commands" -msgstr "" +msgstr "Паўза падчас уводу каманд" #: engines/agi/detection.cpp:188 msgid "" "Shows a command prompt window and pauses the game (like in SCI) instead of a " "real-time prompt." msgstr "" +"Паказвае акно з радком уводу каманды і ставіць гульню на паўзу (як у SCI) " +"замест уводу ў рэальным часе." #: engines/agi/saveload.cpp:777 engines/avalanche/parser.cpp:1887 #: engines/cge/events.cpp:85 engines/cge2/events.cpp:78 @@ -2712,11 +2717,11 @@ msgstr "" #: engines/mohawk/detection.cpp:168 msgid "Play the Myst fly by movie" -msgstr "" +msgstr "Прайграваць ролік пралёту над Myst" #: engines/mohawk/detection.cpp:169 msgid "The Myst fly by movie was not played by the original engine." -msgstr "" +msgstr "Ролік пралёту над Myst не прайграваўся арыгінальным рухавічком." #. I18N: Option for fast scene switching #: engines/mohawk/dialogs.cpp:96 engines/mohawk/dialogs.cpp:239 @@ -2733,14 +2738,12 @@ msgid "~D~rop Page" msgstr "~В~ыкінуць старонку" #: engines/mohawk/dialogs.cpp:103 -#, fuzzy msgid "Show ~M~ap" -msgstr "П~а~казаць карту" +msgstr "Паказаць ~к~арту" #: engines/mohawk/dialogs.cpp:109 -#, fuzzy msgid "Main Men~u~" -msgstr "~Г~алоўнае меню" +msgstr "Галоўнае мен~ю~" #: engines/mohawk/dialogs.cpp:240 msgid "~W~ater Effect Enabled" @@ -2933,13 +2936,12 @@ msgstr "" "Выкарыстоўваць альтэрнатыўны набор срэбных курсораў замест звычайных залатых" #: engines/scumm/detection.cpp:1335 -#, fuzzy msgid "Show Object Line" -msgstr "Паказваць назвы аб'ектаў" +msgstr "Паказваць радок аб'ектаў" #: engines/scumm/detection.cpp:1336 msgid "Show the names of objects at the bottom of the screen" -msgstr "" +msgstr "Паказваць назвы аб'ектаў унізе экрана" #: engines/scumm/dialogs.cpp:176 #, c-format @@ -3800,12 +3802,14 @@ msgstr "Паказвае назвы аб'ектаў пры навядзенні курсора мышы" #: engines/sword25/detection.cpp:46 msgid "Use English speech" -msgstr "" +msgstr "Выкарыстоўваць англійскую агучку" #: engines/sword25/detection.cpp:47 msgid "" "Use English speech instead of German for every language other than German" msgstr "" +"Выкарыстоўваць англійскую агучку замест нямецкай для ўсіх моў, акрамя " +"нямецкай" #: engines/teenagent/resources.cpp:95 msgid "" diff --git a/po/ru_RU.po b/po/ru_RU.po index 514d094e64..339bc7769b 100644 --- a/po/ru_RU.po +++ b/po/ru_RU.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: ScummVM 1.8.0svn\n" "Report-Msgid-Bugs-To: scummvm-devel@lists.sf.net\n" "POT-Creation-Date: 2016-04-07 08:55+0100\n" -"PO-Revision-Date: 2016-02-21 23:32+0300\n" +"PO-Revision-Date: 2016-05-22 17:04+0300\n" "Last-Translator: Eugene Sandulenko <sev@scummvm.org>\n" "Language-Team: Russian\n" "Language: Russian\n" @@ -1267,17 +1267,17 @@ msgid "" "Would you like to enable this feature?" msgstr "" "ScummVM стал поддерживать автоматическую проверку\n" -"обновлений, которая требует доступа в Интернет\n" +"обновлений, которая требует доступа в Интернет.\n" "\n" -"Хотели бы вы включить эту опцию?" +"Вы хотите включить эту опцию?" #: gui/updates-dialog.cpp:55 msgid "(You can always enable it in the options dialog on the Misc tab)" -msgstr "(Вы можете всегда её включыть в Опциях на закладку Другое)" +msgstr "(Вы всегда можете включить её в настройках на закладке \"Разное\")" #: gui/updates-dialog.cpp:92 msgid "Check for updates automatically" -msgstr "Автоматически проверить обновления" +msgstr "Автоматически проверять обновления" #: gui/updates-dialog.cpp:111 msgid "Proceed" @@ -2424,7 +2424,7 @@ msgid "" "real-time prompt." msgstr "" "Показывает окно со строкой ввода команды и ставит игру на паузу (как в SCI) " -"вместоввода в реальном времени." +"вместо ввода в реальном времени." #: engines/agi/saveload.cpp:777 engines/avalanche/parser.cpp:1887 #: engines/cge/events.cpp:85 engines/cge2/events.cpp:78 @@ -2739,11 +2739,11 @@ msgstr "~В~ыбросить страницу" #: engines/mohawk/dialogs.cpp:103 msgid "Show ~M~ap" -msgstr "П~о~казать карту" +msgstr "Показать ~к~арту" #: engines/mohawk/dialogs.cpp:109 msgid "Main Men~u~" -msgstr "~Г~лавное меню" +msgstr "Главное мен~ю~" #: engines/mohawk/dialogs.cpp:240 msgid "~W~ater Effect Enabled" @@ -2939,7 +2939,7 @@ msgstr "" #: engines/scumm/detection.cpp:1335 msgid "Show Object Line" -msgstr "Показывать линии объектов" +msgstr "Показывать строку объектов" #: engines/scumm/detection.cpp:1336 msgid "Show the names of objects at the bottom of the screen" @@ -3808,7 +3808,7 @@ msgstr "Использовать английскую озвучку" msgid "" "Use English speech instead of German for every language other than German" msgstr "" -"Использовать английскую озвучку вместо немецкой для всех языков кроме " +"Использовать английскую озвучку вместо немецкой для всех языков, кроме " "немецкого" #: engines/teenagent/resources.cpp:95 diff --git a/test/common/array.h b/test/common/array.h index 64354abc00..7473398058 100644 --- a/test/common/array.h +++ b/test/common/array.h @@ -354,3 +354,44 @@ class ArrayTestSuite : public CxxTest::TestSuite } }; + +struct ListElement { + int value; + + ListElement(int v) : value(v) {} +}; + +static int compareInts(const void *a, const void *b) { + return ((ListElement *)a)->value - ((ListElement *)a)->value; +} + +class SortedArrayTestSuite : public CxxTest::TestSuite { +public: + void test_insert() { + Common::SortedArray<ListElement *> container(compareInts); + Common::SortedArray<ListElement *>::iterator iter; + + // Fill the container with some random data + container.insert(new ListElement(1)); + return; + + container.insert(new ListElement(7)); + container.insert(new ListElement(8)); + container.insert(new ListElement(3)); + container.insert(new ListElement(5)); + container.insert(new ListElement(4)); + container.insert(new ListElement(9)); + container.insert(new ListElement(2)); + container.insert(new ListElement(6)); + + // Verify contents are correct + iter = container.begin(); + + for (int i = 1; i < 10; i++) { + TS_ASSERT_EQUALS((*iter)->value, i); + ++iter; + TS_ASSERT_DIFFERS(iter, container.end()); + } + } + +}; diff --git a/test/module.mk b/test/module.mk index 11ee6bd200..e591854ace 100644 --- a/test/module.mk +++ b/test/module.mk @@ -26,6 +26,7 @@ endif #TEST_LDFLAGS += -L/usr/X11R6/lib -lX11 +test: CXXFLAGS += -DCOMPILING_TESTS=1 test: test/runner ./test/runner test/runner: test/runner.cpp $(TEST_LIBS) |