diff options
Diffstat (limited to 'engines')
383 files changed, 42144 insertions, 2133 deletions
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/amazon/amazon_logic.cpp b/engines/access/amazon/amazon_logic.cpp index e78f92cda7..08006fe1b7 100644 --- a/engines/access/amazon/amazon_logic.cpp +++ b/engines/access/amazon/amazon_logic.cpp @@ -185,16 +185,24 @@ void CampScene::mWhileDoOpen() { _vm->_numAnimTimers = 0; _vm->_images.clear(); - if (_vm->_conversation == 2) { - // Cutscene at end of Chapter 6 - Resource *spriteData = _vm->_files->loadFile(28, 37); - _vm->_objectsTable[28] = new SpriteResource(_vm, spriteData); - delete spriteData; - - _vm->_animation->freeAnimationData(); - animResource = _vm->_files->loadFile(28, 38); - _vm->_animation->loadAnimations(animResource); - delete animResource; + if (_vm->isCD()) { + if (_vm->_conversation == 2) { + // Cutscene at end of Chapter 6 + Resource *spriteData = _vm->_files->loadFile(28, 37); + _vm->_objectsTable[28] = new SpriteResource(_vm, spriteData); + delete spriteData; + + _vm->_animation->freeAnimationData(); + animResource = _vm->_files->loadFile(28, 38); + _vm->_animation->loadAnimations(animResource); + delete animResource; + } + } else { + _vm->freeCells(); + _vm->_oldRects.clear(); + _vm->_newRects.clear(); + _vm->_numAnimTimers = 0; + _vm->_images.clear(); } } 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 ef32e96f59..29b58a3f1b 100644 --- a/engines/access/bubble_box.cpp +++ b/engines/access/bubble_box.cpp @@ -165,7 +165,7 @@ void BubbleBox::printBubble(const Common::String &msg) { void BubbleBox::printBubble_v1(const Common::String &msg) { drawBubble(_bubbles.size() - 1); - + // Loop through drawing the lines Common::String s = msg; Common::String line; @@ -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; @@ -369,7 +369,7 @@ void BubbleBox::displayBoxData() { _vm->_screen->drawRect(); _vm->_events->showCursor(); } - + _vm->_events->hideCursor(); int oldPStartY = _boxPStartY; ++_boxPStartY; @@ -474,7 +474,7 @@ int BubbleBox::doBox_v1(int item, int box, int &btnSelected) { --_vm->_screen->_orgX2; --_vm->_screen->_orgY2; _vm->_screen->_lColor = 0xF9; - + // Draw the inner border _vm->_screen->drawBox(); @@ -611,6 +611,7 @@ int BubbleBox::doBox_v1(int item, int box, int &btnSelected) { _vm->_events->showCursor(); warning("TODO: pop values"); _vm->_screen->restoreScreen(); + delete icons; return retval_; } @@ -642,7 +643,9 @@ int BubbleBox::doBox_v1(int item, int box, int &btnSelected) { } } } - + + delete icons; + _vm->_screen->restoreScreen(); _vm->_boxDataStart = _startItem; _vm->_boxSelectYOld = -1; @@ -732,7 +735,7 @@ int BubbleBox::doBox_v1(int item, int box, int &btnSelected) { if (_type != TYPE_3) continue; - + if ((_vm->_events->_mousePos.x < tmpX) || (_vm->_events->_mousePos.x > tmpX + 144)) continue; diff --git a/engines/access/char.cpp b/engines/access/char.cpp index cbe1d5d3d9..f6d3033b1b 100644 --- a/engines/access/char.cpp +++ b/engines/access/char.cpp @@ -44,7 +44,7 @@ CharEntry::CharEntry(const byte *data, AccessEngine *vm) { if (vm->getGameID() == GType_MartianMemorandum) { int lastColor = s.readUint16LE(); _numColors = lastColor - _startColor; - } else + } else _numColors = s.readUint16LE(); // Load cells @@ -131,6 +131,7 @@ void CharManager::loadChar(int charId) { if (ce._animFile._fileNum != -1) { Resource *data = _vm->_files->loadFile(ce._animFile); _vm->_animation->loadAnimations(data); + delete data; } // Load script data 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/inventory.cpp b/engines/access/inventory.cpp index 0a962aa69a..e9874cd8d6 100644 --- a/engines/access/inventory.cpp +++ b/engines/access/inventory.cpp @@ -223,6 +223,7 @@ int InventoryManager::displayInv() { else _vm->_useItem = -1; + free(names); free(inv); return 0; } diff --git a/engines/access/room.cpp b/engines/access/room.cpp index a7192d330f..a41de63bf6 100644 --- a/engines/access/room.cpp +++ b/engines/access/room.cpp @@ -142,7 +142,7 @@ void Room::takePicture() { _vm->_player->_roomNumber = 7; _vm->_room->_function = FN_CLEAR1; return; - } else if (result >= 0) + } else if (result >= 0) _vm->_player->_move = (Direction)(result + 1); _vm->_player->_scrollFlag = false; @@ -715,6 +715,8 @@ void Room::executeCommand(int commandId) { screen.plotImage(spr, _selectCommand + 2, Common::Point(_rMouse[_selectCommand][0], (_vm->getGameID() == GType_MartianMemorandum) ? 184 : 176)); + delete spr; + _vm->_screen->restoreScreen(); _vm->_boxSelect = true; } 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/agi.cpp b/engines/agi/agi.cpp index e566ad12f6..60c8d1f3ef 100644 --- a/engines/agi/agi.cpp +++ b/engines/agi/agi.cpp @@ -403,6 +403,11 @@ AgiEngine::AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc) : AgiBas _lastSaveTime = 0; + _playTimeInSecondsAdjust = 0; + _lastUsedPlayTimeInCycles = 0; + _lastUsedPlayTimeInSeconds = 0; + _passedPlayTimeCycles = 0; + memset(_keyQueue, 0, sizeof(_keyQueue)); _console = nullptr; @@ -418,6 +423,9 @@ AgiEngine::AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc) : AgiBas _inventory = nullptr; _keyHoldMode = false; + + _artificialDelayCurrentRoom = 0; + _artificialDelayCurrentPicture = 0; } void AgiEngine::initialize() { diff --git a/engines/agi/agi.h b/engines/agi/agi.h index 1baf0d912f..79d05c4b1d 100644 --- a/engines/agi/agi.h +++ b/engines/agi/agi.h @@ -20,8 +20,8 @@ * */ -#ifndef AGI_H -#define AGI_H +#ifndef AGI_AGI_H +#define AGI_AGI_H #include "common/scummsys.h" #include "common/error.h" @@ -991,4 +991,4 @@ private: } // End of namespace Agi -#endif /* AGI_H */ +#endif /* AGI_AGI_H */ 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/op_cmd.cpp b/engines/agi/op_cmd.cpp index fed07ea986..8a62fce86c 100644 --- a/engines/agi/op_cmd.cpp +++ b/engines/agi/op_cmd.cpp @@ -662,7 +662,7 @@ void cmdWordToString(AgiGame *state, AgiEngine *vm, uint8 *parameter) { uint16 stringNr = parameter[0]; uint16 wordNr = parameter[1]; - strcpy(state->strings[stringNr], state->_vm->_words->getEgoWord(wordNr)); + Common::strlcpy(state->strings[stringNr], state->_vm->_words->getEgoWord(wordNr), MAX_STRINGLEN); } void cmdOpenDialogue(AgiGame *state, AgiEngine *vm, uint8 *parameter) { @@ -2014,7 +2014,7 @@ void cmdGetString(AgiGame *state, AgiEngine *vm, uint8 *parameter) { // copy string to destination // TODO: not sure if set all the time or only when ENTER is pressed - strcpy(&state->_vm->_game.strings[stringDestNr][0], (char *)textMgr->_inputString); + Common::strlcpy(&state->_vm->_game.strings[stringDestNr][0], (char *)textMgr->_inputString, MAX_STRINGLEN); textMgr->charPos_Pop(); @@ -2102,7 +2102,7 @@ void cmdSetString(AgiGame *state, AgiEngine *vm, uint8 *parameter) { // CM: to avoid crash in Groza (str = 150) if (stringNr > MAX_STRINGS) return; - strcpy(state->strings[stringNr], state->_curLogic->texts[textNr]); + Common::strlcpy(state->strings[stringNr], state->_curLogic->texts[textNr], MAX_STRINGLEN); } void cmdDisplay(AgiGame *state, AgiEngine *vm, uint8 *parameter) { diff --git a/engines/agi/op_test.cpp b/engines/agi/op_test.cpp index 4b215edc63..4505668fd1 100644 --- a/engines/agi/op_test.cpp +++ b/engines/agi/op_test.cpp @@ -231,8 +231,8 @@ uint8 AgiEngine::testCompareStrings(uint8 s1, uint8 s2) { char ms2[MAX_STRINGLEN]; int j, k, l; - strcpy(ms1, _game.strings[s1]); - strcpy(ms2, _game.strings[s2]); + Common::strlcpy(ms1, _game.strings[s1], MAX_STRINGLEN); + Common::strlcpy(ms2, _game.strings[s2], MAX_STRINGLEN); l = strlen(ms1); for (k = 0, j = 0; k < l; k++) { 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/preagi_mickey.cpp b/engines/agi/preagi_mickey.cpp index 620d5e0baf..e1545cdb68 100644 --- a/engines/agi/preagi_mickey.cpp +++ b/engines/agi/preagi_mickey.cpp @@ -1205,7 +1205,7 @@ void MickeyEngine::printStory() { clearScreen(IDA_DEFAULT); for (iRow = 0; iRow < 25; iRow++) { - strcpy(szLine, buffer + pBuf); + Common::strlcpy(szLine, buffer + pBuf, 41); drawStr(iRow, 0, IDA_DEFAULT, szLine); pBuf += strlen(szLine) + 1; } @@ -1213,7 +1213,7 @@ void MickeyEngine::printStory() { clearScreen(IDA_DEFAULT); for (iRow = 0; iRow < 21; iRow++) { - strcpy(szLine, buffer + pBuf); + Common::strlcpy(szLine, buffer + pBuf, 41); drawStr(iRow, 0, IDA_DEFAULT, szLine); pBuf += strlen(szLine) + 1; } diff --git a/engines/agi/preagi_winnie.cpp b/engines/agi/preagi_winnie.cpp index 87ac7c19c6..8fb9daca5e 100644 --- a/engines/agi/preagi_winnie.cpp +++ b/engines/agi/preagi_winnie.cpp @@ -292,7 +292,7 @@ int WinnieEngine::parser(int pc, int index, uint8 *buffer) { } // extract menu string - strcpy(szMenu, (char *)(buffer + pc)); + Common::strlcpy(szMenu, (char *)(buffer + pc), 121); XOR80(szMenu); break; default: diff --git a/engines/agi/saveload.cpp b/engines/agi/saveload.cpp index 0658609cd0..fc4aea3169 100644 --- a/engines/agi/saveload.cpp +++ b/engines/agi/saveload.cpp @@ -118,7 +118,7 @@ int AgiEngine::saveGame(const Common::String &fileName, const Common::String &de out->writeByte(2); // was _game.state, 2 = STATE_RUNNING - strcpy(gameIDstring, _game.id); + Common::strlcpy(gameIDstring, _game.id, 8); out->write(gameIDstring, 8); debugC(5, kDebugLevelMain | kDebugLevelSavegame, "Writing game id (%s, %s)", gameIDstring, _game.id); @@ -689,9 +689,6 @@ int AgiEngine::loadGame(const Common::String &fileName, bool checkId) { } } } - for (i = vtEntries; i < SCREENOBJECTS_MAX; i++) { - memset(&_game.screenObjTable[i], 0, sizeof(ScreenObjEntry)); - } // Fix some pointers in screenObjTable @@ -832,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/agi/text.cpp b/engines/agi/text.cpp index 110ba10632..274a654547 100644 --- a/engines/agi/text.cpp +++ b/engines/agi/text.cpp @@ -457,7 +457,7 @@ void TextMgr::drawMessageBox(const char *textPtr, int16 forcedHeight, int16 want // Caller wants to force specified width/height? set it if (forcedHeight) _messageState.textSize_Height = forcedHeight; - + if (forcedWidth) { if (wantedWidth) _messageState.textSize_Width = wantedWidth; @@ -1207,7 +1207,7 @@ char *TextMgr::stringPrintf(const char *originalText) { } assert(resultString.size() < sizeof(resultPrintfBuffer)); - strcpy(resultPrintfBuffer, resultString.c_str()); + Common::strlcpy(resultPrintfBuffer, resultString.c_str(), 2000); return resultPrintfBuffer; } 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/detection_tables.h b/engines/agos/detection_tables.h index 793d4081cf..90e5a84829 100644 --- a/engines/agos/detection_tables.h +++ b/engines/agos/detection_tables.h @@ -2251,6 +2251,31 @@ static const AGOSGameDescription gameDescriptions[] = { GF_TALKIE }, + // Simon the Sorcerer 2 - Russian DOS CD + { + { + "simon2", + "CD", + + { + { "gsptr30", GAME_BASEFILE, "e26d162e573587f4601b88701292212c", 58851}, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab", 18089}, + { "simon2.gme", GAME_GMEFILE, "9c535d403966750ae98bdaf698375a38", 19687892}, + { "stripped.txt", GAME_STRFILE, "e229f84d46fa83f99b4a7115679f3fb6", 171}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9", 513}, + { NULL, 0, NULL, 0} + }, + Common::RU_RUS, + Common::kPlatformDOS, + ADGF_CD, + GUIO0() + }, + + GType_SIMON2, + GID_SIMON2, + GF_TALKIE + }, + // Simon the Sorcerer 2 - Czech Windows CD { { 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/agos/saveload.cpp b/engines/agos/saveload.cpp index 66a7fa90b3..1ba0f56353 100644 --- a/engines/agos/saveload.cpp +++ b/engines/agos/saveload.cpp @@ -494,7 +494,7 @@ void AGOSEngine_Elvira2::userGame(bool load) { i = userGameGetKey(&b, 128); if (b) { - if (i <= 223) { + if (i <= 23) { if (!confirmOverWrite(window)) { listSaveGames(); continue; diff --git a/engines/agos/string.cpp b/engines/agos/string.cpp index 4f6c62c48a..cc443f2f50 100644 --- a/engines/agos/string.cpp +++ b/engines/agos/string.cpp @@ -126,14 +126,14 @@ const byte *AGOSEngine::getStringPtrByID(uint16 stringId, bool upperCase) { _awaitTwoByteToken = 0; uncompressText(ptr); _textBuffer[_textCount] = 0; - strcpy((char *)dst, (const char *)_textBuffer); + Common::strlcpy((char *)dst, (const char *)_textBuffer, 180); } else { if (stringId < 0x8000) { stringPtr = _stringTabPtr[stringId]; } else { stringPtr = getLocalStringByID(stringId); } - strcpy((char *)dst, (const char *)stringPtr); + Common::strlcpy((char *)dst, (const char *)stringPtr, 180); } // WORKAROUND bug #1538873: The French version of Simon 1 and the @@ -796,7 +796,7 @@ void AGOSEngine_Feeble::printInteractText(uint16 num, const char *string) { if (*string2 == 0x00) { if (w == 0xFFFF) w = pixels; - strcpy(convertedString2, string); + Common::strlcpy(convertedString2, string, 320); break; } while (*string2 != ' ') { diff --git a/engines/agos/string_pn.cpp b/engines/agos/string_pn.cpp index 7a364f3ea9..06c8bbd98e 100644 --- a/engines/agos/string_pn.cpp +++ b/engines/agos/string_pn.cpp @@ -114,7 +114,7 @@ void AGOSEngine_PN::getObjectName(char *v, uint16 x) { } void AGOSEngine_PN::pcl(const char *s) { - strcat(_sb, s); + Common::strlcat(_sb, s, 80); if (strchr(s, '\n') == 0) { for (char *str = _sb; *str; str++) windowPutChar(_windowArray[_curWindow], *str); diff --git a/engines/cge/cge.h b/engines/cge/cge.h index c43358f252..d3f8a93c1d 100644 --- a/engines/cge/cge.h +++ b/engines/cge/cge.h @@ -20,8 +20,8 @@ * */ -#ifndef CGE_H -#define CGE_H +#ifndef CGE_CGE_H +#define CGE_CGE_H #include "common/random.h" #include "common/savefile.h" 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/cge2.h b/engines/cge2/cge2.h index fbe4cb3abc..18f919b5eb 100644 --- a/engines/cge2/cge2.h +++ b/engines/cge2/cge2.h @@ -25,8 +25,8 @@ * Copyright (c) 1994-1997 Janus B. Wisniewski and L.K. Avalon */ -#ifndef CGE2_H -#define CGE2_H +#ifndef CGE2_CGE2_H +#define CGE2_CGE2_H #include "common/random.h" #include "common/savefile.h" @@ -335,4 +335,4 @@ public: } // End of namespace CGE2 -#endif // CGE2_H +#endif // CGE2_CGE2_H 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/cge2/vga13h.cpp b/engines/cge2/vga13h.cpp index 54f5c00d93..8b0d8b6c77 100644 --- a/engines/cge2/vga13h.cpp +++ b/engines/cge2/vga13h.cpp @@ -952,8 +952,9 @@ uint8 Vga::closest(Dac *pal, const uint8 colR, const uint8 colG, const uint8 col } uint8 Vga::closest(Dac *pal, Dac x) { - int exp = (sizeof(long) * 8 - 1); - long D = (1 << exp) - 1; // Maximum value of long. + long D = 0; + D = ~D; + D = (unsigned long)D >> 1; // Maximum value of long. long R = x._r; long G = x._g; long B = x._b; diff --git a/engines/cine/cine.h b/engines/cine/cine.h index 7c0191b4d9..615e36d598 100644 --- a/engines/cine/cine.h +++ b/engines/cine/cine.h @@ -20,8 +20,8 @@ * */ -#ifndef CINE_H -#define CINE_H +#ifndef CINE_CINE_H +#define CINE_CINE_H #include "common/scummsys.h" diff --git a/engines/composer/composer.h b/engines/composer/composer.h index 234494e655..d1a85e975a 100644 --- a/engines/composer/composer.h +++ b/engines/composer/composer.h @@ -20,8 +20,8 @@ * */ -#ifndef COMPOSER_H -#define COMPOSER_H +#ifndef COMPOSER_COMPOSER_H +#define COMPOSER_COMPOSER_H #include "common/ini-file.h" #include "common/random.h" diff --git a/engines/cruise/cell.cpp b/engines/cruise/cell.cpp index b7cef41764..46539463b8 100644 --- a/engines/cruise/cell.cpp +++ b/engines/cruise/cell.cpp @@ -129,15 +129,12 @@ void createTextObject(cellStruct *pObject, int overlayIdx, int messageIdx, int x cellStruct *pNewElement; cellStruct *si = pObject->next; - cellStruct *var_2; while (si) { pObject = si; si = si->next; } - var_2 = si; - pNewElement = (cellStruct *) MemAlloc(sizeof(cellStruct)); memset(pNewElement, 0, sizeof(cellStruct)); @@ -157,11 +154,7 @@ void createTextObject(cellStruct *pObject, int overlayIdx, int messageIdx, int x pNewElement->parentOverlay = parentOvl; pNewElement->gfxPtr = NULL; - if (var_2) { - cx = var_2; - } else { - cx = savePObject; - } + cx = savePObject; pNewElement->prev = cx->prev; cx->prev = pNewElement; diff --git a/engines/cruise/ctp.cpp b/engines/cruise/ctp.cpp index 9515b552e1..4458e39e91 100644 --- a/engines/cruise/ctp.cpp +++ b/engines/cruise/ctp.cpp @@ -307,7 +307,7 @@ int initCt(const char *ctpName) { MemFree(ptr); if (ctpName != currentCtpName) - strcpy(currentCtpName, ctpName); + Common::strlcpy(currentCtpName, ctpName, 40); numberOfWalkboxes = segementSizeTable[6] / 2; // get the number of walkboxes diff --git a/engines/cruise/dataLoader.cpp b/engines/cruise/dataLoader.cpp index 7a1258dbde..2eff82bc61 100644 --- a/engines/cruise/dataLoader.cpp +++ b/engines/cruise/dataLoader.cpp @@ -249,12 +249,19 @@ int loadFile(const char* name, int idx, int destIdx) { int numMaxEntriesInSet = getNumMaxEntiresInSet(ptr); if (destIdx > numMaxEntriesInSet) { + MemFree(ptr); return 0; // exit if limit is reached } - return loadSetEntry(name, ptr, destIdx, idx); + int res = loadSetEntry(name, ptr, destIdx, idx); + MemFree(ptr); + + return res; } case type_FNT: { - return loadFNTSub(ptr, idx); + int res = loadFNTSub(ptr, idx); + MemFree(ptr); + + return res; } case type_SPL: { // Sound file diff --git a/engines/draci/draci.h b/engines/draci/draci.h index 540c288d3d..90afdbcf60 100644 --- a/engines/draci/draci.h +++ b/engines/draci/draci.h @@ -20,8 +20,8 @@ * */ -#ifndef DRACI_H -#define DRACI_H +#ifndef DRACI_DRACI_H +#define DRACI_DRACI_H #include "engines/engine.h" #include "common/random.h" @@ -126,4 +126,4 @@ static inline long scummvm_lround(double val) { return (long)floor(val + 0.5); } } // End of namespace Draci -#endif // DRACI_H +#endif // DRACI_DRACI_H diff --git a/engines/drascula/actors.cpp b/engines/drascula/actors.cpp index a1f2c5386c..b459c4539b 100644 --- a/engines/drascula/actors.cpp +++ b/engines/drascula/actors.cpp @@ -196,10 +196,6 @@ void DrasculaEngine::moveCharacters() { return; } } - - byte *srcSurface = extraSurface; - if (currentChapter == 6 && _lang == kSpanish) - srcSurface = tableSurface; if (characterMoved == 0) { curPos[0] = 0; @@ -218,17 +214,17 @@ void DrasculaEngine::moveCharacters() { curPos[1] = 0; if (currentChapter == 2) copyRect(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5], - srcSurface, screenSurface); + extraSurface, screenSurface); else reduce_hare_chico(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5], - factor_red[curY + curHeight], srcSurface, screenSurface); + factor_red[curY + curHeight], extraSurface, screenSurface); } else if (trackProtagonist == 1) { if (currentChapter == 2) copyRect(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5], - srcSurface, screenSurface); + extraSurface, screenSurface); else reduce_hare_chico(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5], - factor_red[curY + curHeight], srcSurface, screenSurface); + factor_red[curY + curHeight], extraSurface, screenSurface); } else if (trackProtagonist == 2) { if (currentChapter == 2) copyRect(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5], @@ -260,17 +256,17 @@ void DrasculaEngine::moveCharacters() { curPos[1] = 0; if (currentChapter == 2) copyRect(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5], - srcSurface, screenSurface); + extraSurface, screenSurface); else reduce_hare_chico(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5], - factor_red[curY + curHeight], srcSurface, screenSurface); + factor_red[curY + curHeight], extraSurface, screenSurface); } else if (trackProtagonist == 1) { if (currentChapter == 2) copyRect(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5], - srcSurface, screenSurface); + extraSurface, screenSurface); else reduce_hare_chico(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5], - factor_red[curY + curHeight], srcSurface, screenSurface); + factor_red[curY + curHeight], extraSurface, screenSurface); } else if (trackProtagonist == 2) { if (currentChapter == 2) copyRect(curPos[0], curPos[1], curPos[2], curPos[3], curPos[4], curPos[5], diff --git a/engines/drascula/animation.cpp b/engines/drascula/animation.cpp index 0ed2c61e3f..f672ad3b55 100644 --- a/engines/drascula/animation.cpp +++ b/engines/drascula/animation.cpp @@ -1612,7 +1612,7 @@ void DrasculaEngine::animation_6_6() { removeObject(20); loadPic(96, frontSurface); loadPic(97, frontSurface); - loadPic(97, _lang == kSpanish ? tableSurface : extraSurface); + loadPic(97, extraSurface); loadPic(99, backSurface); doBreak = 1; objExit = 104; @@ -2216,7 +2216,7 @@ void DrasculaEngine::activatePendulum() { _roomNumber = 102; loadPic(102, bgSurface, HALF_PAL); loadPic("an_p1.alg", drawSurface3); - loadPic("an_p2.alg", _lang == kSpanish ? tableSurface : extraSurface); + loadPic("an_p2.alg", extraSurface); loadPic("an_p3.alg", frontSurface); copyBackground(0, 171, 0, 0, OBJWIDTH, OBJHEIGHT, backSurface, drawSurface3); diff --git a/engines/drascula/drascula.cpp b/engines/drascula/drascula.cpp index 75a81c20b0..ab91056480 100644 --- a/engines/drascula/drascula.cpp +++ b/engines/drascula/drascula.cpp @@ -359,16 +359,13 @@ Common::Error DrasculaEngine::run() { for (i = 0; i < 25; i++) memcpy(crosshairCursor + i * 40, tableSurface + 225 + (56 + i) * 320, 40); - if (_lang == kSpanish) - loadPic(currentChapter == 6 ? 97 : 974, tableSurface); + if (_lang == kSpanish && currentChapter != 6) + loadPic(974, tableSurface); if (currentChapter != 2) { loadPic(99, cursorSurface); loadPic(99, backSurface); - if (currentChapter == 6 && _lang == kSpanish) - loadPic(95, extraSurface); - else - loadPic(97, extraSurface); + loadPic(97, extraSurface); } memset(iconName, 0, sizeof(iconName)); @@ -598,7 +595,6 @@ bool DrasculaEngine::runCurrentChapter() { if (_rightMouseButton == 1 && _menuScreen) { #endif _rightMouseButton = 0; - delay(100); if (currentChapter == 2) { loadPic(menuBackground, cursorSurface); loadPic(menuBackground, backSurface); @@ -627,7 +623,6 @@ bool DrasculaEngine::runCurrentChapter() { !(currentChapter == 5 && pickedObject == 16)) { #endif _rightMouseButton = 0; - delay(100); characterMoved = 0; if (trackProtagonist == 2) trackProtagonist = 1; @@ -655,12 +650,11 @@ bool DrasculaEngine::runCurrentChapter() { #endif if (_leftMouseButton == 1 && _menuBar) { - delay(100); selectVerbFromBar(); } else if (_leftMouseButton == 1 && takeObject == 0) { - delay(100); if (verify1()) return true; + delay(100); } else if (_leftMouseButton == 1 && takeObject == 1) { if (verify2()) return true; @@ -894,7 +888,7 @@ void DrasculaEngine::pause(int duration) { } int DrasculaEngine::getTime() { - return _system->getMillis() / 20; // originally was 1 + return _system->getMillis() / 10; } void DrasculaEngine::reduce_hare_chico(int xx1, int yy1, int xx2, int yy2, int width, int height, int factor, byte *dir_inicio, byte *dir_fin) { diff --git a/engines/drascula/drascula.h b/engines/drascula/drascula.h index acca2e5915..c879a83db7 100644 --- a/engines/drascula/drascula.h +++ b/engines/drascula/drascula.h @@ -20,8 +20,8 @@ * */ -#ifndef DRASCULA_H -#define DRASCULA_H +#ifndef DRASCULA_DRASCULA_H +#define DRASCULA_DRASCULA_H #include "common/scummsys.h" #include "common/archive.h" @@ -784,4 +784,4 @@ protected: } // End of namespace Drascula -#endif /* DRASCULA_H */ +#endif /* DRASCULA_DRASCULA_H */ diff --git a/engines/drascula/graphics.cpp b/engines/drascula/graphics.cpp index 01bd267158..6bfb2e1823 100644 --- a/engines/drascula/graphics.cpp +++ b/engines/drascula/graphics.cpp @@ -217,6 +217,10 @@ void DrasculaEngine::print_abc(const char *said, int screenX, int screenY) { int letterY = 0, letterX = 0, i; uint len = strlen(said); byte c; + + byte *srcSurface = tableSurface; + if (_lang == kSpanish && currentChapter == 6) + srcSurface = extraSurface; for (uint h = 0; h < len; h++) { c = toupper(said[h]); @@ -241,7 +245,7 @@ void DrasculaEngine::print_abc(const char *said, int screenX, int screenY) { } // for copyRect(letterX, letterY, screenX, screenY, - CHAR_WIDTH, CHAR_HEIGHT, tableSurface, screenSurface); + CHAR_WIDTH, CHAR_HEIGHT, srcSurface, screenSurface); screenX = screenX + CHAR_WIDTH; if (screenX > 317) { diff --git a/engines/drascula/interface.cpp b/engines/drascula/interface.cpp index a09b9da07d..07f192cd4c 100644 --- a/engines/drascula/interface.cpp +++ b/engines/drascula/interface.cpp @@ -123,15 +123,6 @@ void DrasculaEngine::showMenu() { int h, n, x; byte *srcSurface = (currentChapter == 6) ? tableSurface : frontSurface; x = whichObject(); - - // The original uses extraSurface to draw text in draw_abc() in the Spanish version - // while other languages use tableSurface. Here all language use tableSurface for - // chapter 6. However the code in ScummVM was changed to use tableSurface for all - // labguage in draw_abc(). So instead here for the Spanish version we use extraSurface. - // Compared to the original the use of the tableSurface and extraSurface has been swapped - // for the Spanish language all through chapter 6. - if (currentChapter == 6 && _lang == kSpanish) - srcSurface = extraSurface; for (n = 1; n < ARRAYSIZE(inventoryObjects); n++) { h = inventoryObjects[n]; diff --git a/engines/drascula/objects.cpp b/engines/drascula/objects.cpp index 823c073d43..02846abcc9 100644 --- a/engines/drascula/objects.cpp +++ b/engines/drascula/objects.cpp @@ -272,6 +272,8 @@ void DrasculaEngine::updateVisible() { visible[2] = 0; if (_roomNumber == 26 && flags[12] == 1) visible[1] = 0; + if (_roomNumber == 31 && flags[13] == 1) + visible[1] = 0; if (_roomNumber == 35 && flags[14] == 1) visible[2] = 0; if (_roomNumber == 35 && flags[17] == 1) diff --git a/engines/drascula/rooms.cpp b/engines/drascula/rooms.cpp index acfc528b0c..57d4517295 100644 --- a/engines/drascula/rooms.cpp +++ b/engines/drascula/rooms.cpp @@ -1501,7 +1501,7 @@ void DrasculaEngine::update_102() { if (actorFrames[kFramePendulum] <= 4) pendulumSurface = drawSurface3; else if (actorFrames[kFramePendulum] <= 11) - pendulumSurface = _lang == kSpanish ? tableSurface : extraSurface; + pendulumSurface = extraSurface; else pendulumSurface = frontSurface; diff --git a/engines/drascula/sound.cpp b/engines/drascula/sound.cpp index 6c0c171664..62ec796678 100644 --- a/engines/drascula/sound.cpp +++ b/engines/drascula/sound.cpp @@ -44,13 +44,10 @@ void DrasculaEngine::updateVolume(Audio::Mixer::SoundType soundType, int prevVol } void DrasculaEngine::volumeControls() { - byte* srcSurface = tableSurface; - if (currentChapter == 6 && _lang == kSpanish) - srcSurface = extraSurface; - if (_lang == kSpanish) - loadPic(95, srcSurface); + if (_lang == kSpanish && currentChapter != 6) + loadPic(95, tableSurface); - copyRect(1, 56, 73, 63, 177, 97, srcSurface, screenSurface); + copyRect(1, 56, 73, 63, 177, 97, tableSurface, screenSurface); updateScreen(73, 63, 73, 63, 177, 97, screenSurface); setCursor(kCursorCrosshair); @@ -67,11 +64,11 @@ void DrasculaEngine::volumeControls() { updateRoom(); - copyRect(1, 56, 73, 63, 177, 97, srcSurface, screenSurface); + copyRect(1, 56, 73, 63, 177, 97, tableSurface, screenSurface); - copyBackground(183, 56, 82, masterVolumeY, 39, 2 + masterVolume * 4, srcSurface, screenSurface); - copyBackground(183, 56, 138, voiceVolumeY, 39, 2 + voiceVolume * 4, srcSurface, screenSurface); - copyBackground(183, 56, 194, musicVolumeY, 39, 2 + musicVolume * 4, srcSurface, screenSurface); + copyBackground(183, 56, 82, masterVolumeY, 39, 2 + masterVolume * 4, tableSurface, screenSurface); + copyBackground(183, 56, 138, voiceVolumeY, 39, 2 + voiceVolume * 4, tableSurface, screenSurface); + copyBackground(183, 56, 194, musicVolumeY, 39, 2 + musicVolume * 4, tableSurface, screenSurface); updateScreen(); @@ -104,8 +101,8 @@ void DrasculaEngine::volumeControls() { } - if (_lang == kSpanish) - loadPic(currentChapter == 6 ? 95 : 974, srcSurface); + if (_lang == kSpanish && currentChapter != 6) + loadPic(974, tableSurface); selectVerb(kVerbNone); @@ -169,8 +166,8 @@ void DrasculaEngine::MusicFadeout() { void DrasculaEngine::playFile(const char *fname) { Common::SeekableReadStream *stream = _archives.open(fname); if (stream) { - int startOffset = 0; - int soundSize = stream->size() - startOffset; + int startOffset = 32; + int soundSize = stream->size() - 64; if (!strcmp(fname, "3.als") && soundSize == 145166 && _lang != kSpanish) { // WORKAROUND: File 3.als with English speech files has a big silence at diff --git a/engines/drascula/talk.cpp b/engines/drascula/talk.cpp index 3176c5e0f3..cc329b206b 100644 --- a/engines/drascula/talk.cpp +++ b/engines/drascula/talk.cpp @@ -440,12 +440,9 @@ void DrasculaEngine::talk(const char *said, const char *filename) { copyRect(x_talk_izq[face], y_mask_talk, curX + 8, curY - 1, TALK_WIDTH, TALK_HEIGHT, extraSurface, screenSurface); else if (notTowers) { - byte *srcSurface = extraSurface; - if (currentChapter == 6 && _lang == kSpanish) - srcSurface = tableSurface; reduce_hare_chico(x_talk_izq[face], y_mask_talk, curX + (int)((8.0f / 100) * factor_red[MIN(201, curY + curHeight)]), curY, TALK_WIDTH, TALK_HEIGHT, factor_red[MIN(201, curY + curHeight)], - srcSurface, screenSurface); + extraSurface, screenSurface); } updateRefresh(); } else if (trackProtagonist == 1) { @@ -453,11 +450,8 @@ void DrasculaEngine::talk(const char *said, const char *filename) { copyRect(x_talk_dch[face], y_mask_talk, curX + 12, curY, TALK_WIDTH, TALK_HEIGHT, extraSurface, screenSurface); else if (notTowers) { - byte *srcSurface = extraSurface; - if (currentChapter == 6 && _lang == kSpanish) - srcSurface = tableSurface; reduce_hare_chico(x_talk_dch[face], y_mask_talk, curX + (int)((12.0f / 100) * factor_red[MIN(201, curY + curHeight)]), - curY, TALK_WIDTH, TALK_HEIGHT, factor_red[MIN(201, curY + curHeight)], srcSurface, screenSurface); + curY, TALK_WIDTH, TALK_HEIGHT, factor_red[MIN(201, curY + curHeight)], extraSurface, screenSurface); } updateRefresh(); } else if (trackProtagonist == 2) { diff --git a/engines/dreamweb/dreamweb.h b/engines/dreamweb/dreamweb.h index 2e5fb424f8..45bacdba86 100644 --- a/engines/dreamweb/dreamweb.h +++ b/engines/dreamweb/dreamweb.h @@ -20,8 +20,8 @@ * */ -#ifndef DREAMWEB_H -#define DREAMWEB_H +#ifndef DREAMWEB_DREAMWEB_H +#define DREAMWEB_DREAMWEB_H #include "common/error.h" #include "common/keyboard.h" diff --git a/engines/fullpipe/fullpipe.cpp b/engines/fullpipe/fullpipe.cpp index ebaff32550..c2aae9ba88 100644 --- a/engines/fullpipe/fullpipe.cpp +++ b/engines/fullpipe/fullpipe.cpp @@ -24,6 +24,7 @@ #include "common/archive.h" #include "common/config-manager.h" +#include "audio/mixer.h" #include "engines/util.h" @@ -112,6 +113,8 @@ FullpipeEngine::FullpipeEngine(OSystem *syst, const ADGameDescription *gameDesc) _musicLocal = 0; _trackStartDelay = 0; + _sceneTrackHandle = new Audio::SoundHandle(); + memset(_sceneTracks, 0, sizeof(_sceneTracks)); memset(_trackName, 0, sizeof(_trackName)); memset(_sceneTracksCurrentTrack, 0, sizeof(_sceneTracksCurrentTrack)); @@ -192,6 +195,7 @@ FullpipeEngine::~FullpipeEngine() { delete _rnd; delete _console; delete _globalMessageQueueList; + delete _sceneTrackHandle; } void FullpipeEngine::initialize() { diff --git a/engines/fullpipe/fullpipe.h b/engines/fullpipe/fullpipe.h index 7f20a6d6af..fba61aa13b 100644 --- a/engines/fullpipe/fullpipe.h +++ b/engines/fullpipe/fullpipe.h @@ -30,8 +30,6 @@ #include "common/savefile.h" #include "common/system.h" -#include "audio/mixer.h" - #include "graphics/transparent_surface.h" #include "engines/engine.h" @@ -41,6 +39,10 @@ struct ADGameDescription; +namespace Audio { +class SoundHandle; +} + namespace Fullpipe { enum FullpipeGameFeatures { @@ -312,7 +314,7 @@ public: void lift_openLift(); GameVar *_musicGameVar; - Audio::SoundHandle _sceneTrackHandle; + Audio::SoundHandle *_sceneTrackHandle; public: diff --git a/engines/fullpipe/motion.cpp b/engines/fullpipe/motion.cpp index 1a61cb742a..9cf18f3cc2 100644 --- a/engines/fullpipe/motion.cpp +++ b/engines/fullpipe/motion.cpp @@ -855,9 +855,9 @@ Common::Array<MovItem *> *MovGraph::getPaths(StaticANIObject *ani, int x, int y, if (sz > 0) { for (int j = 0; j < sz; j++) _items[idx]->movitems->push_back(movitems[j]); - - delete movitems; } + + delete movitems; } delete movarr; diff --git a/engines/fullpipe/sound.cpp b/engines/fullpipe/sound.cpp index 230d6c39a9..c82c2c414c 100644 --- a/engines/fullpipe/sound.cpp +++ b/engines/fullpipe/sound.cpp @@ -30,6 +30,7 @@ #include "fullpipe/statics.h" #include "common/memstream.h" +#include "audio/mixer.h" #include "audio/audiostream.h" #include "audio/decoders/vorbis.h" #include "audio/decoders/wave.h" @@ -96,12 +97,13 @@ Sound::Sound() { memset(_directSoundBuffers, 0, sizeof(_directSoundBuffers)); _description = 0; _volume = 100; + _handle = new Audio::SoundHandle(); } Sound::~Sound() { freeSound(); - free(_description); + delete _handle; } bool Sound::load(MfcArchive &file, NGIArchive *archive) { @@ -206,14 +208,14 @@ void Sound::setPanAndVolumeByStaticAni() { } void Sound::setPanAndVolume(int vol, int pan) { - g_fp->_mixer->setChannelVolume(_handle, vol / 39); // 0..10000 - g_fp->_mixer->setChannelBalance(_handle, pan / 78); // -10000..10000 + g_fp->_mixer->setChannelVolume(*_handle, vol / 39); // 0..10000 + g_fp->_mixer->setChannelBalance(*_handle, pan / 78); // -10000..10000 } void Sound::play(int flag) { - Audio::SoundHandle handle = getHandle(); + Audio::SoundHandle *handle = getHandle(); - if (g_fp->_mixer->isSoundHandleActive(handle)) + if (g_fp->_mixer->isSoundHandleActive(*handle)) return; byte *soundData = loadData(); @@ -221,7 +223,7 @@ void Sound::play(int flag) { Audio::RewindableAudioStream *wav = Audio::makeWAVStream(dataStream, DisposeAfterUse::YES); Audio::AudioStream *audioStream = new Audio::LoopingAudioStream(wav, (flag == 1) ? 0 : 1); - g_fp->_mixer->playStream(Audio::Mixer::kSFXSoundType, &handle, audioStream); + g_fp->_mixer->playStream(Audio::Mixer::kSFXSoundType, handle, audioStream); } void Sound::freeSound() { @@ -231,11 +233,11 @@ void Sound::freeSound() { } int Sound::getVolume() { - return g_fp->_mixer->getChannelVolume(_handle) * 39; // 0..10000 + return g_fp->_mixer->getChannelVolume(*_handle) * 39; // 0..10000 } void Sound::stop() { - g_fp->_mixer->stopHandle(_handle); + g_fp->_mixer->stopHandle(*_handle); } void FullpipeEngine::setSceneMusicParameters(GameVar *gvar) { @@ -353,7 +355,7 @@ void FullpipeEngine::startSoundStream1(char *trackName) { stopAllSoundStreams(); #ifdef USE_VORBIS - if (_mixer->isSoundHandleActive(_sceneTrackHandle)) + if (_mixer->isSoundHandleActive(*_sceneTrackHandle)) return; Common::File *track = new Common::File(); @@ -363,7 +365,7 @@ void FullpipeEngine::startSoundStream1(char *trackName) { return; } Audio::RewindableAudioStream *ogg = Audio::makeVorbisStream(track, DisposeAfterUse::YES); - _mixer->playStream(Audio::Mixer::kMusicSoundType, &_sceneTrackHandle, ogg); + _mixer->playStream(Audio::Mixer::kMusicSoundType, _sceneTrackHandle, ogg); #endif } diff --git a/engines/fullpipe/sound.h b/engines/fullpipe/sound.h index 14e766f5bb..983f28312b 100644 --- a/engines/fullpipe/sound.h +++ b/engines/fullpipe/sound.h @@ -23,6 +23,10 @@ #ifndef FULLPIPE_SOUND_H #define FULLPIPE_SOUND_H +namespace Audio { +class SoundHandle; +} + namespace Fullpipe { class Sound : public MemoryObject { @@ -31,7 +35,7 @@ class Sound : public MemoryObject { int _directSoundBuffer; int _directSoundBuffers[7]; byte *_soundData; - Audio::SoundHandle _handle; + Audio::SoundHandle *_handle; int _volume; public: @@ -45,7 +49,7 @@ public: virtual bool load(MfcArchive &file) { assert(0); return false; } // Disable base class void updateVolume(); int getId() const { return _id; } - Audio::SoundHandle getHandle() const { return _handle; } + Audio::SoundHandle *getHandle() const { return _handle; } void play(int flag); void freeSound(); diff --git a/engines/fullpipe/statics.cpp b/engines/fullpipe/statics.cpp index 8ee3b14d0c..36fbb73037 100644 --- a/engines/fullpipe/statics.cpp +++ b/engines/fullpipe/statics.cpp @@ -1576,6 +1576,9 @@ Movement::Movement(Movement *src, int *oldIdxs, int newSize, StaticANIObject *an _m2x = 0; _m2y = 0; + _counter = 0; + _counterMax = 0; + _field_78 = 0; _framePosOffsets = 0; _field_84 = 0; 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/scumm/he/logic/moonbase.cpp b/engines/gnap/debugger.h index 29a0dde7a2..ac83cc5504 100644 --- a/engines/scumm/he/logic/moonbase.cpp +++ b/engines/gnap/debugger.h @@ -20,33 +20,35 @@ * */ -#include "scumm/he/intern_he.h" -#include "scumm/he/logic_he.h" +#ifndef GNAP_DEBUGGER_H +#define GNAP_DEBUGGER_H -namespace Scumm { +#include "common/scummsys.h" +#include "gui/debugger.h" -/** - * Logic code for: - * Moonbase Commander - */ -class LogicHEmoonbase : public LogicHE { +namespace Gnap { + +class GnapEngine; + +class Debugger : public GUI::Debugger { +private: + GnapEngine *_vm; public: - LogicHEmoonbase(ScummEngine_v90he *vm) : LogicHE(vm) {} + /* + * 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); - int versionID(); +public: + Debugger(GnapEngine *vm); + virtual ~Debugger() {} }; -int LogicHEmoonbase::versionID() { - if (_vm->_game.features & GF_DEMO) - return -100; - else if (strcmp(_vm->_game.variant, "1.1") == 0) - return 110; - else - return 100; -} - -LogicHE *makeLogicHEmoonbase(ScummEngine_v90he *vm) { - return new LogicHEmoonbase(vm); -} +} // End of namespace Gnap -} // End of namespace Scumm +#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/gob/gob.cpp b/engines/gob/gob.cpp index d995f26d9f..b51a6382e6 100644 --- a/engines/gob/gob.cpp +++ b/engines/gob/gob.cpp @@ -26,6 +26,7 @@ #include "base/plugins.h" #include "common/config-manager.h" #include "audio/mididrv.h" +#include "audio/mixer.h" #include "gui/gui-manager.h" #include "gui/dialog.h" diff --git a/engines/gob/inter_playtoons.cpp b/engines/gob/inter_playtoons.cpp index 45f573efcd..13d24dc05d 100644 --- a/engines/gob/inter_playtoons.cpp +++ b/engines/gob/inter_playtoons.cpp @@ -41,7 +41,6 @@ #include "gob/video.h" #include "gob/videoplayer.h" #include "gob/save/saveload.h" -#include "gob/sound/sound.h" namespace Gob { diff --git a/engines/gob/inter_v4.cpp b/engines/gob/inter_v4.cpp index 656ca6f5c3..d379d5ab11 100644 --- a/engines/gob/inter_v4.cpp +++ b/engines/gob/inter_v4.cpp @@ -205,7 +205,7 @@ void Inter_v4::o4_playVmdOrMusic() { return; } else if (props.lastFrame == -9) { _vm->_sound->bgStop(); - _vm->_sound->bgSetPlayMode(BackgroundAtmosphere::kPlayModeRandom); + _vm->_sound->bgSetPlayMode(Sound::kPlayModeRandom); _vm->_sound->bgPlay(file.c_str(), "SND", SOUND_SND, props.palStart); return; } else if (props.lastFrame < 0) { diff --git a/engines/gob/pregob/onceupon/onceupon.cpp b/engines/gob/pregob/onceupon/onceupon.cpp index a6e4da75e7..50910e77bd 100644 --- a/engines/gob/pregob/onceupon/onceupon.cpp +++ b/engines/gob/pregob/onceupon/onceupon.cpp @@ -30,8 +30,6 @@ #include "gob/anifile.h" #include "gob/aniobject.h" -#include "gob/sound/sound.h" - #include "gob/pregob/txtfile.h" #include "gob/pregob/gctfile.h" diff --git a/engines/gob/pregob/pregob.h b/engines/gob/pregob/pregob.h index 021cf2b3d6..108771a63a 100644 --- a/engines/gob/pregob/pregob.h +++ b/engines/gob/pregob/pregob.h @@ -29,14 +29,14 @@ #include "gob/util.h" #include "gob/aniobject.h" -#include "gob/sound/sounddesc.h" - #include "gob/pregob/txtfile.h" namespace Gob { class GobEngine; +class ANIFile; class Surface; +class SoundDesc; class GCTFile; diff --git a/engines/gob/sound/bgatmosphere.cpp b/engines/gob/sound/bgatmosphere.cpp index 21fb70278a..c7be1be96a 100644 --- a/engines/gob/sound/bgatmosphere.cpp +++ b/engines/gob/sound/bgatmosphere.cpp @@ -23,6 +23,7 @@ #include "common/array.h" #include "gob/sound/bgatmosphere.h" +#include "gob/sound/sound.h" #include "gob/sound/sounddesc.h" namespace Gob { @@ -30,7 +31,7 @@ namespace Gob { BackgroundAtmosphere::BackgroundAtmosphere(Audio::Mixer &mixer) : SoundMixer(mixer, Audio::Mixer::kMusicSoundType), _rnd("gobBA") { - _playMode = kPlayModeLinear; + _playMode = Sound::kPlayModeLinear; _queuePos = -1; _shaded = false; _shadable = true; @@ -56,7 +57,7 @@ void BackgroundAtmosphere::stopBA() { SoundMixer::stop(0); } -void BackgroundAtmosphere::setPlayMode(PlayMode mode) { +void BackgroundAtmosphere::setPlayMode(Sound::BackgroundPlayMode mode) { _playMode = mode; } @@ -100,11 +101,11 @@ void BackgroundAtmosphere::getNextQueuePos() { switch (_playMode) { - case kPlayModeLinear: + case Sound::kPlayModeLinear: _queuePos = (_queuePos + 1) % _queue.size(); break; - case kPlayModeRandom: + case Sound::kPlayModeRandom: _queuePos = _rnd.getRandomNumber(_queue.size() - 1); break; diff --git a/engines/gob/sound/bgatmosphere.h b/engines/gob/sound/bgatmosphere.h index c838a2c2bb..138b65a1c1 100644 --- a/engines/gob/sound/bgatmosphere.h +++ b/engines/gob/sound/bgatmosphere.h @@ -27,6 +27,7 @@ #include "common/mutex.h" #include "common/random.h" +#include "gob/sound/sound.h" #include "gob/sound/soundmixer.h" namespace Audio { @@ -39,18 +40,13 @@ class SoundDesc; class BackgroundAtmosphere : private SoundMixer { public: - enum PlayMode { - kPlayModeLinear, - kPlayModeRandom - }; - BackgroundAtmosphere(Audio::Mixer &mixer); ~BackgroundAtmosphere(); void playBA(); void stopBA(); - void setPlayMode(PlayMode mode); + void setPlayMode(Sound::BackgroundPlayMode mode); void queueSample(SoundDesc &sndDesc); void queueClear(); @@ -60,7 +56,7 @@ public: void unshade(); private: - PlayMode _playMode; + Sound::BackgroundPlayMode _playMode; Common::Array<SoundDesc *> _queue; int _queuePos; diff --git a/engines/gob/sound/sound.cpp b/engines/gob/sound/sound.cpp index 22dfe9d3c3..000eafa031 100644 --- a/engines/gob/sound/sound.cpp +++ b/engines/gob/sound/sound.cpp @@ -28,6 +28,7 @@ #include "gob/game.h" #include "gob/inter.h" +#include "gob/sound/bgatmosphere.h" #include "gob/sound/pcspeaker.h" #include "gob/sound/soundblaster.h" #include "gob/sound/adlplayer.h" @@ -717,7 +718,7 @@ void Sound::bgStop() { _bgatmos->queueClear(); } -void Sound::bgSetPlayMode(BackgroundAtmosphere::PlayMode mode) { +void Sound::bgSetPlayMode(Sound::BackgroundPlayMode mode) { if (!_bgatmos) return; diff --git a/engines/gob/sound/sound.h b/engines/gob/sound/sound.h index 6ebc323b18..f1fd46d24b 100644 --- a/engines/gob/sound/sound.h +++ b/engines/gob/sound/sound.h @@ -23,12 +23,13 @@ #ifndef GOB_SOUND_SOUND_H #define GOB_SOUND_SOUND_H +#include "common/str.h" #include "gob/sound/sounddesc.h" -#include "gob/sound/bgatmosphere.h" namespace Gob { class GobEngine; +class BackgroundAtmosphere; class PCSpeaker; class SoundBlaster; class ADLPlayer; @@ -39,6 +40,11 @@ class CDROM; class Sound { public: + enum BackgroundPlayMode { + kPlayModeLinear, + kPlayModeRandom + }; + static const int kSoundsCount = 60; Sound(GobEngine *vm); @@ -135,7 +141,7 @@ public: void bgPlay(const char *base, const char *ext, SoundType type, int count); void bgStop(); - void bgSetPlayMode(BackgroundAtmosphere::PlayMode mode); + void bgSetPlayMode(BackgroundPlayMode mode); void bgShade(); void bgUnshade(); diff --git a/engines/gob/videoplayer.cpp b/engines/gob/videoplayer.cpp index e97848d27e..bbf4ef4162 100644 --- a/engines/gob/videoplayer.cpp +++ b/engines/gob/videoplayer.cpp @@ -21,6 +21,8 @@ */ +#include "video/coktel_decoder.h" + #include "gob/videoplayer.h" #include "gob/global.h" #include "gob/dataio.h" diff --git a/engines/gob/videoplayer.h b/engines/gob/videoplayer.h index 02ed510ec5..1c39ecf2fa 100644 --- a/engines/gob/videoplayer.h +++ b/engines/gob/videoplayer.h @@ -29,11 +29,14 @@ #include "common/str.h" #include "graphics/surface.h" -#include "video/coktel_decoder.h" #include "gob/util.h" #include "gob/draw.h" +namespace Video { +class CoktelDecoder; +} + namespace Gob { class GobEngine; 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/groovie.h b/engines/groovie/groovie.h index d442d39cb2..c2994d20cc 100644 --- a/engines/groovie/groovie.h +++ b/engines/groovie/groovie.h @@ -20,8 +20,8 @@ * */ -#ifndef GROOVIE_H -#define GROOVIE_H +#ifndef GROOVIE_GROOVIE_H +#define GROOVIE_GROOVIE_H #include "groovie/debug.h" #include "groovie/font.h" @@ -132,4 +132,4 @@ private: } // End of namespace Groovie -#endif // GROOVIE_H +#endif // GROOVIE_GROOVIE_H 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/hugo/hugo.h b/engines/hugo/hugo.h index 27dfea8725..85209afe06 100644 --- a/engines/hugo/hugo.h +++ b/engines/hugo/hugo.h @@ -20,8 +20,8 @@ * */ -#ifndef HUGO_H -#define HUGO_H +#ifndef HUGO_HUGO_H +#define HUGO_HUGO_H #include "engines/engine.h" @@ -341,4 +341,4 @@ private: } // End of namespace Hugo -#endif // Hugo_H +#endif // HUGO_HUGO_H diff --git a/engines/kyra/items_lok.cpp b/engines/kyra/items_lok.cpp index 55a23b2a1a..3a2e631744 100644 --- a/engines/kyra/items_lok.cpp +++ b/engines/kyra/items_lok.cpp @@ -844,8 +844,6 @@ void KyraEngine_LoK::updatePlayerItemsForScene() { uint8 item = _currentCharacter->inventoryItems[i]; if (item >= 29 && item < 33) { ++item; - if (item > 33) - item = 33; _currentCharacter->inventoryItems[i] = item; redraw = true; } diff --git a/engines/kyra/resource.h b/engines/kyra/resource.h index 3ab08a4c7c..c3ebf6e5fe 100644 --- a/engines/kyra/resource.h +++ b/engines/kyra/resource.h @@ -203,7 +203,6 @@ enum KyraResources { k1ConfigStrings, k1AudioTracks, - k1AudioTracks2, k1AudioTracksIntro, k1CreditsStrings, diff --git a/engines/kyra/staticres.cpp b/engines/kyra/staticres.cpp index 1a2e2c093c..e99321ddcb 100644 --- a/engines/kyra/staticres.cpp +++ b/engines/kyra/staticres.cpp @@ -39,7 +39,7 @@ namespace Kyra { -#define RESFILE_VERSION 87 +#define RESFILE_VERSION 88 namespace { bool checkKyraDat(Common::SeekableReadStream *file) { @@ -805,19 +805,11 @@ void KyraEngine_LoK::initStaticResource() { } // audio resource assignment - int size1, size2; - const char *const *soundfiles1 = _staticres->loadStrings(k1AudioTracks, size1); - const char *const *soundfiles2 = _staticres->loadStrings(k1AudioTracks2, size2); - int soundFilesSize = size1 + size2; + int soundFilesSize; + const char *const *soundFiles = _staticres->loadStrings(k1AudioTracks, soundFilesSize); int soundFilesIntroSize = 0; int cdaTableSize = 0; - const char **soundFiles = 0; - if (soundFilesSize) { - soundFiles = new const char*[soundFilesSize]; - for (int i = 0; i < soundFilesSize; i++) - soundFiles[i] = (i < size1) ? soundfiles1[i] : soundfiles2[i - size1]; - } const char *const *soundFilesIntro = _staticres->loadStrings(k1AudioTracksIntro, soundFilesIntroSize); const int32 *cdaTable = (const int32 *)_staticres->loadRawData(k1TownsCDATable, cdaTableSize); diff --git a/engines/lab/anim.cpp b/engines/lab/anim.cpp index 1190f0323b..2dc580735e 100644 --- a/engines/lab/anim.cpp +++ b/engines/lab/anim.cpp @@ -208,6 +208,8 @@ void Anim::diffNextFrame(bool onlyDiffData) { _vm->updateEvents(); _vm->waitTOF(); } + + _waitForEffect = false; } _size -= 8; @@ -217,7 +219,9 @@ void Anim::diffNextFrame(bool onlyDiffData) { _diffFile->skip(2); // Sound effects embedded in animations are started here. These are - // usually animation-specific, like door opening sounds, and are not looped + // usually animation-specific, like door opening sounds, and are not looped. + // The engine should wait for all such sounds to end. + _waitForEffect = true; _vm->_music->playSoundEffect(_sampleSpeed, _size, false, _diffFile); break; @@ -233,6 +237,8 @@ void Anim::diffNextFrame(bool onlyDiffData) { if (drawOnScreen) didTOF = true; } + + _waitForEffect = false; } _isPlaying = false; 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/lab/processroom.cpp b/engines/lab/processroom.cpp index 5093e8ef85..68e6e63c1d 100644 --- a/engines/lab/processroom.cpp +++ b/engines/lab/processroom.cpp @@ -254,16 +254,9 @@ void LabEngine::doActions(const ActionList &actionList) { _music->loadSoundEffect(action->_messages[0], true, false); break; - case kActionShowDiff: { - bool curWait = _anim->_waitForEffect; - // Pause the engine until the sound is finished - _anim->_waitForEffect = true; + case kActionShowDiff: _graphics->readPict(action->_messages[0], true); - - // Restore the previous value of _waitForEffect - _anim->_waitForEffect = curWait; break; - } case kActionShowDiffLooping: // used in scene 44 (heart of the labyrinth, minotaur) _graphics->readPict(action->_messages[0], false); diff --git a/engines/lastexpress/entities/gendarmes.cpp b/engines/lastexpress/entities/gendarmes.cpp index b628b8dfe7..1b51dd2006 100644 --- a/engines/lastexpress/entities/gendarmes.cpp +++ b/engines/lastexpress/entities/gendarmes.cpp @@ -174,7 +174,7 @@ IMPLEMENT_FUNCTION_IISS(9, Gendarmes, doCompartment, CarIndex, EntityPosition) strcat((char *)¶meters1->seq1, (char *)¶ms->seq1); strcat((char *)¶meters1->seq2, (char *)¶ms->seq1); - strcat((char *)¶meters1->seq3, (char *)¶ms->seq1); + Common::strlcat((char *)¶meters1->seq3, (char *)¶ms->seq1, 9); // Beware, seq3 is smaller than seq1 if ((getEntities()->isInsideCompartment(kEntityPlayer, (CarIndex)params->param1, (EntityPosition)params->param2) || getEntities()->isInsideCompartment(kEntityPlayer, (CarIndex)params->param1, (EntityPosition)parameters2->param7) diff --git a/engines/lastexpress/lastexpress.h b/engines/lastexpress/lastexpress.h index b4098f3860..b33784b1e8 100644 --- a/engines/lastexpress/lastexpress.h +++ b/engines/lastexpress/lastexpress.h @@ -20,8 +20,8 @@ * */ -#ifndef LASTEXPRESS_H -#define LASTEXPRESS_H +#ifndef LASTEXPRESS_LASTEXPRESS_H +#define LASTEXPRESS_LASTEXPRESS_H #include "lastexpress/debug.h" #include "lastexpress/eventhandler.h" @@ -146,4 +146,4 @@ private: } // End of namespace LastExpress -#endif // LASTEXPRESS_H +#endif // LASTEXPRESS_LASTEXPRESS_H diff --git a/engines/lastexpress/sound/entry.cpp b/engines/lastexpress/sound/entry.cpp index 697e6e1269..7308214551 100644 --- a/engines/lastexpress/sound/entry.cpp +++ b/engines/lastexpress/sound/entry.cpp @@ -366,7 +366,7 @@ void SoundEntry::saveLoadWithSerializer(Common::Serializer &s) { assert(_name1.size() <= 16); assert(_name2.size() <= 16); - if (_name2.matchString("NISSND?") && (_status.status & kFlagType9) != kFlag3) { + if (_name2.matchString("NISSND?") && ((_status.status & kFlagType9) != kFlag3)) { s.syncAsUint32LE(_status.status); s.syncAsUint32LE(_type); s.syncAsUint32LE(_blockCount); // field_8; diff --git a/engines/lure/game.cpp b/engines/lure/game.cpp index 38ca0ba54f..84cc91ec9a 100644 --- a/engines/lure/game.cpp +++ b/engines/lure/game.cpp @@ -538,7 +538,7 @@ void Game::handleRightClickMenu() { hotspot = res.getHotspot(room.hotspotId()); assert(hotspot); strings.getString(hotspot->nameId, statusLine); - strcat(statusLine, stringList.getString(S_FOR)); + Common::strlcat(statusLine, stringList.getString(S_FOR), MAX_DESC_SIZE); statusLine += strlen(statusLine); itemId = PopupMenu::ShowItems(GET, player->roomNumber()); @@ -549,7 +549,7 @@ void Game::handleRightClickMenu() { hotspot = res.getHotspot(room.hotspotId()); assert(hotspot); strings.getString(hotspot->nameId, statusLine); - strcat(statusLine, stringList.getString(S_TO)); + Common::strlcat(statusLine, stringList.getString(S_TO), MAX_DESC_SIZE); breakFlag = GetTellActions(); break; @@ -559,7 +559,7 @@ void Game::handleRightClickMenu() { case DRINK: hasItems = (res.numInventoryItems() != 0); if (!hasItems) - strcat(statusLine, stringList.getString(S_ACTION_NOTHING)); + Common::strlcat(statusLine, stringList.getString(S_ACTION_NOTHING), MAX_DESC_SIZE); statusLine += strlen(statusLine); room.update(); @@ -579,9 +579,9 @@ void Game::handleRightClickMenu() { assert(useHotspot); strings.getString(useHotspot->nameId, statusLine); if (action == GIVE) - strcat(statusLine, stringList.getString(S_TO)); + Common::strlcat(statusLine, stringList.getString(S_TO), MAX_DESC_SIZE); else - strcat(statusLine, stringList.getString(S_ON)); + Common::strlcat(statusLine, stringList.getString(S_ON), MAX_DESC_SIZE); statusLine += strlen(statusLine); } else if ((action == DRINK) || (action == EXAMINE)) @@ -762,11 +762,11 @@ bool Game::GetTellActions() { // Second parameter action = (Action) commands[_numTellCommands * 3]; if (action == ASK) - strcat(statusLine, stringList.getString(S_FOR)); + Common::strlcat(statusLine, stringList.getString(S_FOR), MAX_DESC_SIZE); else if (action == GIVE) - strcat(statusLine, stringList.getString(S_TO)); + Common::strlcat(statusLine, stringList.getString(S_TO), MAX_DESC_SIZE); else if (action == USE) - strcat(statusLine, stringList.getString(S_ON)); + Common::strlcat(statusLine, stringList.getString(S_ON), MAX_DESC_SIZE); else { // All other commads don't need a second parameter ++paramIndex; diff --git a/engines/lure/hotspots.cpp b/engines/lure/hotspots.cpp index fbf93e1e14..29e5d2832e 100644 --- a/engines/lure/hotspots.cpp +++ b/engines/lure/hotspots.cpp @@ -1898,8 +1898,8 @@ void Hotspot::doStatus(HotspotData *hotspot) { endAction(); strings.getString(room.roomNumber(), buffer); - strcat(buffer, "\n\n"); - strcat(buffer, stringList.getString(S_YOU_ARE_CARRYING)); + Common::strlcat(buffer, "\n\n", MAX_DESC_SIZE); + Common::strlcat(buffer, stringList.getString(S_YOU_ARE_CARRYING), MAX_DESC_SIZE); // Scan through the list and add in any items assigned to the player HotspotDataList &list = res.hotspotData(); @@ -1909,25 +1909,25 @@ void Hotspot::doStatus(HotspotData *hotspot) { if (rec.roomNumber == PLAYER_ID) { if (numItems++ == 0) - strcat(buffer, ": "); + Common::strlcat(buffer, ": ", MAX_DESC_SIZE); else - strcat(buffer, ", "); + Common::strlcat(buffer, ", ", MAX_DESC_SIZE); strings.getString(rec.nameId, buffer + strlen(buffer)); } } // If there were no items, add in the word 'nothing' if (numItems == 0) - strcat(buffer, stringList.getString(S_INV_NOTHING)); + Common::strlcat(buffer, stringList.getString(S_INV_NOTHING), MAX_DESC_SIZE); // If the player has money, add it in uint16 numGroats = res.fieldList().numGroats(); if (numGroats > 0) { - strcat(buffer, "\n\n"); - strcat(buffer, stringList.getString(S_YOU_HAVE)); - sprintf(buffer + strlen(buffer), "%d", numGroats); - strcat(buffer, " "); - strcat(buffer, stringList.getString((numGroats == 1) ? S_GROAT : S_GROATS)); + Common::strlcat(buffer, "\n\n", MAX_DESC_SIZE); + Common::strlcat(buffer, stringList.getString(S_YOU_HAVE), MAX_DESC_SIZE); + snprintf(buffer + strlen(buffer), MAX_DESC_SIZE - strlen(buffer), "%d", numGroats); + Common::strlcat(buffer, " ", MAX_DESC_SIZE); + Common::strlcat(buffer, stringList.getString((numGroats == 1) ? S_GROAT : S_GROATS), MAX_DESC_SIZE); // Make sure we're not overrunning } // Display the dialog diff --git a/engines/lure/lure.h b/engines/lure/lure.h index af00197c3f..71ce2d3cff 100644 --- a/engines/lure/lure.h +++ b/engines/lure/lure.h @@ -20,8 +20,8 @@ * */ -#ifndef LURE_H -#define LURE_H +#ifndef LURE_LURE_H +#define LURE_LURE_H #include "engines/engine.h" #include "common/rect.h" diff --git a/engines/lure/scripts.cpp b/engines/lure/scripts.cpp index 3df119a9da..f7dc06033a 100644 --- a/engines/lure/scripts.cpp +++ b/engines/lure/scripts.cpp @@ -926,8 +926,8 @@ uint16 Script::execute(uint16 startOffset) { opcode >>= 1; if (gDebugLevel >= ERROR_DETAILED) - strcat(debugInfo, (opcode > S_OPCODE_RANDOM) ? "INVALID" : - scriptOpcodes[opcode]); + Common::strlcat(debugInfo, (opcode > S_OPCODE_RANDOM) ? "INVALID" : + scriptOpcodes[opcode], MAX_DESC_SIZE); if (hasParam) { // Flag to read next two bytes as active parameter @@ -1087,7 +1087,7 @@ uint16 Script::execute(uint16 startOffset) { else if (scriptMethodNames[param] == NULL) strcat(debugInfo, " UNKNOWN METHOD"); else { strcat(debugInfo, " "); - strcat(debugInfo, scriptMethodNames[param]); + Common::strlcat(debugInfo, scriptMethodNames[param], MAX_DESC_SIZE); } // Any params diff --git a/engines/made/database.cpp b/engines/made/database.cpp index 3eab31acc2..0020cb398c 100644 --- a/engines/made/database.cpp +++ b/engines/made/database.cpp @@ -40,6 +40,7 @@ namespace Made { */ Object::Object() : _objData(NULL), _freeData(false) { + _objSize = 0; } Object::~Object() { diff --git a/engines/made/made.cpp b/engines/made/made.cpp index f1539297ee..a29aa2512f 100644 --- a/engines/made/made.cpp +++ b/engines/made/made.cpp @@ -58,11 +58,24 @@ MadeEngine::MadeEngine(OSystem *syst, const MadeGameDescription *gameDesc) : Eng const GameSettings *g; + _eventNum = 0; + _eventMouseX = _eventMouseY = 0; + _eventKey = 0; + _autoStopSound = false; + _soundEnergyIndex = 0; + _soundEnergyArray = 0; + _musicBeatStart = 0; + _cdTimeStart = 0; + + _gameId = -1; + const char *gameid = ConfMan.get("gameid").c_str(); for (g = madeSettings; g->gameid; ++g) if (!scumm_stricmp(g->gameid, gameid)) _gameId = g->id; + assert(_gameId != -1); + _rnd = new Common::RandomSource("made"); _console = new MadeConsole(this); @@ -85,6 +98,8 @@ MadeEngine::MadeEngine(OSystem *syst, const MadeGameDescription *gameDesc) : Eng _music = nullptr; + _soundRate = 0; + // Set default sound frequency switch (getGameID()) { case GID_RODNEY: diff --git a/engines/made/pmvplayer.cpp b/engines/made/pmvplayer.cpp index 453e2a4872..0beb132b93 100644 --- a/engines/made/pmvplayer.cpp +++ b/engines/made/pmvplayer.cpp @@ -223,7 +223,10 @@ bool PmvPlayer::play(const char *filename) { //delete _audioStream; delete _fd; - _surface->free(); + + if(_surface) + _surface->free(); + delete _surface; return !_aborted; diff --git a/engines/made/redreader.cpp b/engines/made/redreader.cpp index f92ffd8dd8..a0aaf7be43 100644 --- a/engines/made/redreader.cpp +++ b/engines/made/redreader.cpp @@ -102,7 +102,7 @@ int LzhDecompressor::decompress(Common::SeekableReadStream &source, byte *dest, int bufsize; byte* buffer; - buffer = (byte *) malloc(DICSIZ); + buffer = (byte *)calloc(DICSIZ, 1); _source = &source; _compSize = sourceLen; diff --git a/engines/made/resource.cpp b/engines/made/resource.cpp index f8e763e74e..a9734ed47d 100644 --- a/engines/made/resource.cpp +++ b/engines/made/resource.cpp @@ -43,6 +43,7 @@ Resource::~Resource() { PictureResource::PictureResource() : _picture(NULL), _picturePalette(NULL) { _hasPalette = false; + _paletteColorCount = 0; } PictureResource::~PictureResource() { @@ -182,6 +183,9 @@ void PictureResource::loadChunked(byte *source, int size) { /* AnimationResource */ AnimationResource::AnimationResource() { + _flags = 0; + _width = 0; + _height = 0; } AnimationResource::~AnimationResource() { diff --git a/engines/made/screen.cpp b/engines/made/screen.cpp index edccb68953..33edb3834c 100644 --- a/engines/made/screen.cpp +++ b/engines/made/screen.cpp @@ -91,6 +91,8 @@ Screen::Screen(MadeEngine *vm) : _vm(vm) { _currentFontNum = 0; _fontDrawCtx.clipRect = Common::Rect(320, 200); _fontDrawCtx.destSurface = _backgroundScreen; + _outlineColor = 0; + _dropShadowColor = 0; clearChannels(); } diff --git a/engines/made/sound.cpp b/engines/made/sound.cpp index ad49031e7b..62559efa84 100644 --- a/engines/made/sound.cpp +++ b/engines/made/sound.cpp @@ -155,6 +155,7 @@ void decompressSound(byte *source, byte *dest, uint16 chunkSize, uint16 chunkCou }; soundEnergyItem.position = 0; + memset(deltaSoundBuffer, 0, 1024); if (soundEnergyArray) soundEnergyArray->clear(); @@ -237,6 +238,7 @@ void decompressSound(byte *source, byte *dest, uint16 chunkSize, uint16 chunkCou break; default: + delete[] soundBuffer; return; } @@ -247,6 +249,9 @@ void decompressSound(byte *source, byte *dest, uint16 chunkSize, uint16 chunkCou // soundBuffer. soundBuffer[workChunkSize] = soundBuffer[workChunkSize - 1]; + for (i = 0; i < chunkSize; i++) + soundBuffer[i] = 0; + if (deltaType == 1) { for (i = 0; i < chunkSize - 1; i += 2) { l = i / 2; 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/mads.cpp b/engines/mads/mads.cpp index 29bcd10094..5776d813cf 100644 --- a/engines/mads/mads.cpp +++ b/engines/mads/mads.cpp @@ -58,6 +58,7 @@ MADSEngine::MADSEngine(OSystem *syst, const MADSGameDescription *gameDesc) : _resources = nullptr; _sound = nullptr; _audio = nullptr; + _screen = nullptr; } MADSEngine::~MADSEngine() { diff --git a/engines/mads/menu_views.h b/engines/mads/menu_views.h index c203248ad9..e22b6223a7 100644 --- a/engines/mads/menu_views.h +++ b/engines/mads/menu_views.h @@ -8,20 +8,12 @@ * 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. -<<<<<<< HEAD - -======= * ->>>>>>> master * 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. -<<<<<<< HEAD - -======= * ->>>>>>> master * 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. 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/mohawk/mohawk.h b/engines/mohawk/mohawk.h index 6fa733e38e..ac91dca971 100644 --- a/engines/mohawk/mohawk.h +++ b/engines/mohawk/mohawk.h @@ -20,8 +20,8 @@ * */ -#ifndef MOHAWK_H -#define MOHAWK_H +#ifndef MOHAWK_MOHAWK_H +#define MOHAWK_MOHAWK_H #include "common/scummsys.h" #include "common/array.h" diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp index e2bc88ebf6..633b67f7e9 100644 --- a/engines/mohawk/myst.cpp +++ b/engines/mohawk/myst.cpp @@ -604,7 +604,8 @@ void MohawkEngine_Myst::changeToCard(uint16 card, TransitionType transition) { _gfx->runTransition(transition, Common::Rect(544, 333), 10, 0); } else { _gfx->copyBackBufferToScreen(Common::Rect(544, 333)); - _needsUpdate = true; + _system->updateScreen(); + _needsUpdate = false; } } diff --git a/engines/mortevielle/mortevielle.cpp b/engines/mortevielle/mortevielle.cpp index 90d366ed7e..81b2edb57d 100644 --- a/engines/mortevielle/mortevielle.cpp +++ b/engines/mortevielle/mortevielle.cpp @@ -145,6 +145,7 @@ MortevielleEngine::MortevielleEngine(OSystem *system, const MortevielleGameDescr _endGame = false; _loseGame = false; _txxFileFl = false; + _is = 0; } MortevielleEngine::~MortevielleEngine() { diff --git a/engines/neverhood/diskplayerscene.cpp b/engines/neverhood/diskplayerscene.cpp index 96a935851c..e79f4c9d77 100644 --- a/engines/neverhood/diskplayerscene.cpp +++ b/engines/neverhood/diskplayerscene.cpp @@ -22,6 +22,7 @@ #include "neverhood/diskplayerscene.h" #include "neverhood/mouse.h" +#include "neverhood/smackerplayer.h" namespace Neverhood { diff --git a/engines/neverhood/diskplayerscene.h b/engines/neverhood/diskplayerscene.h index 2ae85b9a0b..dba10f3a46 100644 --- a/engines/neverhood/diskplayerscene.h +++ b/engines/neverhood/diskplayerscene.h @@ -26,11 +26,11 @@ #include "neverhood/neverhood.h" #include "neverhood/resourceman.h" #include "neverhood/scene.h" -#include "neverhood/smackerplayer.h" namespace Neverhood { class DiskplayerScene; +class SmackerPlayer; class AsDiskplayerSceneKey : public AnimatedSprite { public: diff --git a/engines/neverhood/menumodule.cpp b/engines/neverhood/menumodule.cpp index 0f2a421d83..e58dd31f03 100644 --- a/engines/neverhood/menumodule.cpp +++ b/engines/neverhood/menumodule.cpp @@ -23,6 +23,8 @@ #include "common/config-manager.h" #include "common/translation.h" +#include "audio/mixer.h" + #include "gui/saveload.h" #include "neverhood/menumodule.h" diff --git a/engines/neverhood/modules/module1300.cpp b/engines/neverhood/modules/module1300.cpp index 60ff0411a6..65bd353576 100644 --- a/engines/neverhood/modules/module1300.cpp +++ b/engines/neverhood/modules/module1300.cpp @@ -23,6 +23,7 @@ #include "neverhood/diskplayerscene.h" #include "neverhood/gamemodule.h" #include "neverhood/menumodule.h" +#include "neverhood/smackerplayer.h" #include "neverhood/modules/module1000_sprites.h" #include "neverhood/modules/module1200_sprites.h" #include "neverhood/modules/module1300.h" diff --git a/engines/neverhood/modules/module1300.h b/engines/neverhood/modules/module1300.h index 4a0ca6c062..8164a51d0d 100644 --- a/engines/neverhood/modules/module1300.h +++ b/engines/neverhood/modules/module1300.h @@ -26,10 +26,11 @@ #include "neverhood/neverhood.h" #include "neverhood/module.h" #include "neverhood/scene.h" -#include "neverhood/smackerplayer.h" namespace Neverhood { +class SmackerPlayer; + class Module1300 : public Module { public: Module1300(NeverhoodEngine *vm, Module *parentModule, int which); diff --git a/engines/neverhood/modules/module1300_sprites.h b/engines/neverhood/modules/module1300_sprites.h index 6f4faaa234..bf9f72a5a7 100644 --- a/engines/neverhood/modules/module1300_sprites.h +++ b/engines/neverhood/modules/module1300_sprites.h @@ -26,7 +26,6 @@ #include "neverhood/neverhood.h" #include "neverhood/module.h" #include "neverhood/scene.h" -#include "neverhood/smackerplayer.h" namespace Neverhood { diff --git a/engines/neverhood/modules/module2800.cpp b/engines/neverhood/modules/module2800.cpp index ab22390c7d..63d507d8fd 100644 --- a/engines/neverhood/modules/module2800.cpp +++ b/engines/neverhood/modules/module2800.cpp @@ -23,6 +23,7 @@ #include "neverhood/diskplayerscene.h" #include "neverhood/gamemodule.h" #include "neverhood/scene.h" +#include "neverhood/smackerplayer.h" #include "neverhood/modules/module1000_sprites.h" #include "neverhood/modules/module1200_sprites.h" #include "neverhood/modules/module1700_sprites.h" diff --git a/engines/neverhood/navigationscene.h b/engines/neverhood/navigationscene.h index 8e286effb9..e1dabfea3d 100644 --- a/engines/neverhood/navigationscene.h +++ b/engines/neverhood/navigationscene.h @@ -26,6 +26,7 @@ #include "neverhood/neverhood.h" #include "neverhood/resourceman.h" #include "neverhood/scene.h" +#include "neverhood/smackerplayer.h" namespace Neverhood { diff --git a/engines/neverhood/neverhood.cpp b/engines/neverhood/neverhood.cpp index c6cff86c72..0dc271997b 100644 --- a/engines/neverhood/neverhood.cpp +++ b/engines/neverhood/neverhood.cpp @@ -24,6 +24,8 @@ #include "common/config-manager.h" #include "common/textconsole.h" +#include "audio/mixer.h" + #include "base/plugins.h" #include "base/version.h" diff --git a/engines/neverhood/neverhood.h b/engines/neverhood/neverhood.h index 9eac4ffc44..4c5f9c3303 100644 --- a/engines/neverhood/neverhood.h +++ b/engines/neverhood/neverhood.h @@ -20,8 +20,8 @@ * */ -#ifndef NEVERHOOD_H -#define NEVERHOOD_H +#ifndef NEVERHOOD_NEVERHOOD_H +#define NEVERHOOD_NEVERHOOD_H #include "common/scummsys.h" #include "common/events.h" @@ -149,4 +149,4 @@ private: } // End of namespace Neverhood -#endif /* NEVERHOOD_H */ +#endif /* NEVERHOOD_NEVERHOOD_H */ diff --git a/engines/neverhood/scene.cpp b/engines/neverhood/scene.cpp index 1a8e74da38..8ed988c0fc 100644 --- a/engines/neverhood/scene.cpp +++ b/engines/neverhood/scene.cpp @@ -22,6 +22,7 @@ #include "neverhood/console.h" #include "neverhood/scene.h" +#include "neverhood/smackerplayer.h" namespace Neverhood { diff --git a/engines/neverhood/scene.h b/engines/neverhood/scene.h index 98a7fa5090..1d1faf28bc 100644 --- a/engines/neverhood/scene.h +++ b/engines/neverhood/scene.h @@ -31,13 +31,13 @@ #include "neverhood/klaymen.h" #include "neverhood/module.h" #include "neverhood/palette.h" -#include "neverhood/smackerplayer.h" #include "neverhood/sprite.h" #include "neverhood/staticdata.h" namespace Neverhood { class Console; +class SmackerPlayer; class Scene : public Entity { public: diff --git a/engines/neverhood/screen.cpp b/engines/neverhood/screen.cpp index cc735c4c16..5cc7998210 100644 --- a/engines/neverhood/screen.cpp +++ b/engines/neverhood/screen.cpp @@ -21,6 +21,7 @@ */ #include "graphics/palette.h" +#include "video/smk_decoder.h" #include "neverhood/screen.h" namespace Neverhood { diff --git a/engines/neverhood/screen.h b/engines/neverhood/screen.h index 82ce90b245..91bbe12c66 100644 --- a/engines/neverhood/screen.h +++ b/engines/neverhood/screen.h @@ -25,11 +25,14 @@ #include "common/array.h" #include "graphics/surface.h" -#include "video/smk_decoder.h" #include "neverhood/neverhood.h" #include "neverhood/microtiles.h" #include "neverhood/graphics.h" +namespace Video { + class SmackerDecoder; +} + namespace Neverhood { struct RenderItem { diff --git a/engines/neverhood/smackerscene.cpp b/engines/neverhood/smackerscene.cpp index 2b43579130..50677d7d5c 100644 --- a/engines/neverhood/smackerscene.cpp +++ b/engines/neverhood/smackerscene.cpp @@ -21,6 +21,7 @@ */ #include "neverhood/smackerscene.h" +#include "neverhood/smackerplayer.h" namespace Neverhood { diff --git a/engines/neverhood/sound.cpp b/engines/neverhood/sound.cpp index b15bea4a64..db22b72289 100644 --- a/engines/neverhood/sound.cpp +++ b/engines/neverhood/sound.cpp @@ -21,10 +21,17 @@ */ #include "common/memstream.h" -#include "graphics/palette.h" +#include "audio/mixer.h" #include "neverhood/sound.h" +#include "neverhood/resource.h" #include "neverhood/resourceman.h" +// Convert volume from percent to 0..255 +#define VOLUME(volume) (Audio::Mixer::kMaxChannelVolume / 100 * (volume)) + +// Convert panning from percent (50% equals center) to -127..0..+127 +#define PANNING(panning) (254 / 100 * (panning) - 127) + namespace Neverhood { SoundResource::SoundResource(NeverhoodEngine *vm) @@ -583,6 +590,11 @@ AudioResourceManSoundItem::AudioResourceManSoundItem(NeverhoodEngine *vm, uint32 _volume(100), _panning(50) { _vm->_res->queryResource(_fileHash, _resourceHandle); + _soundHandle = new Audio::SoundHandle(); +} + +AudioResourceManSoundItem::~AudioResourceManSoundItem() { + delete _soundHandle; } void AudioResourceManSoundItem::loadSound() { @@ -594,22 +606,22 @@ void AudioResourceManSoundItem::loadSound() { } void AudioResourceManSoundItem::unloadSound() { - if (_vm->_mixer->isSoundHandleActive(_soundHandle)) - _vm->_mixer->stopHandle(_soundHandle); + if (_vm->_mixer->isSoundHandleActive(*_soundHandle)) + _vm->_mixer->stopHandle(*_soundHandle); _vm->_res->unloadResource(_resourceHandle); _data = NULL; } void AudioResourceManSoundItem::setVolume(int16 volume) { _volume = MIN<int16>(volume, 100); - if (_isPlaying && _vm->_mixer->isSoundHandleActive(_soundHandle)) - _vm->_mixer->setChannelVolume(_soundHandle, VOLUME(_volume)); + if (_isPlaying && _vm->_mixer->isSoundHandleActive(*_soundHandle)) + _vm->_mixer->setChannelVolume(*_soundHandle, VOLUME(_volume)); } void AudioResourceManSoundItem::setPan(int16 pan) { _panning = MIN<int16>(pan, 100); - if (_isPlaying && _vm->_mixer->isSoundHandleActive(_soundHandle)) - _vm->_mixer->setChannelVolume(_soundHandle, PANNING(_panning)); + if (_isPlaying && _vm->_mixer->isSoundHandleActive(*_soundHandle)) + _vm->_mixer->setChannelVolume(*_soundHandle, PANNING(_panning)); } void AudioResourceManSoundItem::playSound(bool looping) { @@ -619,7 +631,7 @@ void AudioResourceManSoundItem::playSound(bool looping) { const byte *shiftValue = _resourceHandle.extData(); Common::MemoryReadStream *stream = new Common::MemoryReadStream(_data, _resourceHandle.size(), DisposeAfterUse::NO); NeverhoodAudioStream *audioStream = new NeverhoodAudioStream(22050, *shiftValue, looping, DisposeAfterUse::YES, stream); - _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle, + _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, _soundHandle, audioStream, -1, VOLUME(_volume), PANNING(_panning)); debug(1, "playing sound %08X", _fileHash); _isPlaying = true; @@ -627,13 +639,13 @@ void AudioResourceManSoundItem::playSound(bool looping) { } void AudioResourceManSoundItem::stopSound() { - if (_vm->_mixer->isSoundHandleActive(_soundHandle)) - _vm->_mixer->stopHandle(_soundHandle); + if (_vm->_mixer->isSoundHandleActive(*_soundHandle)) + _vm->_mixer->stopHandle(*_soundHandle); _isPlaying = false; } bool AudioResourceManSoundItem::isPlaying() { - return _vm->_mixer->isSoundHandleActive(_soundHandle); + return _vm->_mixer->isSoundHandleActive(*_soundHandle); } AudioResourceManMusicItem::AudioResourceManMusicItem(NeverhoodEngine *vm, uint32 fileHash) @@ -641,6 +653,11 @@ AudioResourceManMusicItem::AudioResourceManMusicItem(NeverhoodEngine *vm, uint32 _volume(100), _panning(50), _start(false), _isFadingIn(false), _isFadingOut(false), _isPlaying(false), _fadeVolume(0), _fadeVolumeStep(0) { + _soundHandle = new Audio::SoundHandle(); +} + +AudioResourceManMusicItem::~AudioResourceManMusicItem() { + delete _soundHandle; } void AudioResourceManMusicItem::playMusic(int16 fadeVolumeStep) { @@ -658,7 +675,7 @@ void AudioResourceManMusicItem::playMusic(int16 fadeVolumeStep) { } void AudioResourceManMusicItem::stopMusic(int16 fadeVolumeStep) { - if (_vm->_mixer->isSoundHandleActive(_soundHandle)) { + if (_vm->_mixer->isSoundHandleActive(*_soundHandle)) { if (fadeVolumeStep != 0) { if (_isFadingIn) _isFadingIn = false; @@ -667,7 +684,7 @@ void AudioResourceManMusicItem::stopMusic(int16 fadeVolumeStep) { _isFadingOut = true; _fadeVolumeStep = fadeVolumeStep; } else { - _vm->_mixer->stopHandle(_soundHandle); + _vm->_mixer->stopHandle(*_soundHandle); } _isPlaying = false; } @@ -677,8 +694,8 @@ void AudioResourceManMusicItem::unloadMusic() { if (_isFadingOut) { _canRestart = true; } else { - if (_vm->_mixer->isSoundHandleActive(_soundHandle)) - _vm->_mixer->stopHandle(_soundHandle); + if (_vm->_mixer->isSoundHandleActive(*_soundHandle)) + _vm->_mixer->stopHandle(*_soundHandle); _isPlaying = false; _terminate = true; } @@ -686,8 +703,8 @@ void AudioResourceManMusicItem::unloadMusic() { void AudioResourceManMusicItem::setVolume(int16 volume) { _volume = MIN<int16>(volume, 100); - if (_isPlaying && _vm->_mixer->isSoundHandleActive(_soundHandle)) - _vm->_mixer->setChannelVolume(_soundHandle, VOLUME(_volume)); + if (_isPlaying && _vm->_mixer->isSoundHandleActive(*_soundHandle)) + _vm->_mixer->setChannelVolume(*_soundHandle, VOLUME(_volume)); } void AudioResourceManMusicItem::restart() { @@ -698,33 +715,33 @@ void AudioResourceManMusicItem::restart() { void AudioResourceManMusicItem::update() { - if (_start && !_vm->_mixer->isSoundHandleActive(_soundHandle)) { + if (_start && !_vm->_mixer->isSoundHandleActive(*_soundHandle)) { ResourceHandle resourceHandle; _vm->_res->queryResource(_fileHash, resourceHandle); Common::SeekableReadStream *stream = _vm->_res->createStream(_fileHash); const byte *shiftValue = resourceHandle.extData(); NeverhoodAudioStream *audioStream = new NeverhoodAudioStream(22050, *shiftValue, true, DisposeAfterUse::YES, stream); - _vm->_mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, + _vm->_mixer->playStream(Audio::Mixer::kMusicSoundType, _soundHandle, audioStream, -1, VOLUME(_isFadingIn ? _fadeVolume : _volume), PANNING(_panning)); _start = false; _isPlaying = true; } - if (_vm->_mixer->isSoundHandleActive(_soundHandle)) { + if (_vm->_mixer->isSoundHandleActive(*_soundHandle)) { if (_isFadingIn) { _fadeVolume += _fadeVolumeStep; if (_fadeVolume >= _volume) { _fadeVolume = _volume; _isFadingIn = false; } - _vm->_mixer->setChannelVolume(_soundHandle, VOLUME(_fadeVolume)); + _vm->_mixer->setChannelVolume(*_soundHandle, VOLUME(_fadeVolume)); } if (_isFadingOut) { _fadeVolume -= _fadeVolumeStep; if (_fadeVolume < 0) _fadeVolume = 0; - _vm->_mixer->setChannelVolume(_soundHandle, VOLUME(_fadeVolume)); + _vm->_mixer->setChannelVolume(*_soundHandle, VOLUME(_fadeVolume)); if (_fadeVolume == 0) { _isFadingOut = false; stopMusic(0); diff --git a/engines/neverhood/sound.h b/engines/neverhood/sound.h index 24947f0191..e5e4ec9216 100644 --- a/engines/neverhood/sound.h +++ b/engines/neverhood/sound.h @@ -24,23 +24,22 @@ #define NEVERHOOD_SOUND_H #include "audio/audiostream.h" -#include "audio/mixer.h" #include "common/array.h" -#include "graphics/surface.h" -#include "neverhood/neverhood.h" -#include "neverhood/resource.h" +#include "neverhood/resourceman.h" -namespace Neverhood { +namespace Common { +class SeekableReadStream; +} -// Convert volume from percent to 0..255 -#define VOLUME(volume) (Audio::Mixer::kMaxChannelVolume / 100 * (volume)) +namespace Audio { +class SoundHandle; +} -// Convert panning from percent (50% equals center) to -127..0..+127 -#define PANNING(panning) (254 / 100 * (panning) - 127) +namespace Neverhood { +class NeverhoodEngine; class AudioResourceManSoundItem; class AudioResourceManMusicItem; -class AudioResourceMan; class SoundResource { public: @@ -214,6 +213,7 @@ private: class AudioResourceManSoundItem { public: AudioResourceManSoundItem(NeverhoodEngine *vm, uint32 fileHash); + ~AudioResourceManSoundItem(); void loadSound(); void unloadSound(); void setVolume(int16 volume); @@ -230,12 +230,13 @@ protected: bool _isPlaying; int16 _volume; int16 _panning; - Audio::SoundHandle _soundHandle; + Audio::SoundHandle *_soundHandle; }; class AudioResourceManMusicItem { public: AudioResourceManMusicItem(NeverhoodEngine *vm, uint32 fileHash); + ~AudioResourceManMusicItem(); void playMusic(int16 fadeVolumeStep); void stopMusic(int16 fadeVolumeStep); void unloadMusic(); @@ -259,7 +260,7 @@ protected: bool _isFadingOut; int16 _fadeVolume; int16 _fadeVolumeStep; - Audio::SoundHandle _soundHandle; + Audio::SoundHandle *_soundHandle; }; class AudioResourceMan { 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 50a5b38d8d..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() { @@ -163,7 +165,7 @@ int16 Program::findLocal(const char* name) { int16 Program::addLocal(const char *name, int16 value, int16 min, int16 max) { assert(_numLocals < NUM_LOCALS); - strcpy(_localNames[_numLocals], name); + Common::strlcpy(_localNames[_numLocals], name, 10); _locals[_numLocals].setRange(min, max); _locals[_numLocals].setValue(value); @@ -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.cpp b/engines/parallaction/parallaction.cpp index 2b75e78582..bbe759dffe 100644 --- a/engines/parallaction/parallaction.cpp +++ b/engines/parallaction/parallaction.cpp @@ -60,6 +60,7 @@ Parallaction::Parallaction(OSystem *syst, const PARALLACTIONGameDescription *gam DebugMan.addDebugChannel(kDebugMenu, "menu", "Menu debug level"); DebugMan.addDebugChannel(kDebugInventory, "inventory", "Inventory debug level"); + _screenWidth = 0; _screenHeight = 0; _screenSize = 0; _gameType = 0; @@ -86,6 +87,7 @@ Parallaction::Parallaction(OSystem *syst, const PARALLACTIONGameDescription *gam _inventory = 0; _currentLocationIndex = 0; _numLocations = 0; + _language = 0; } Parallaction::~Parallaction() { @@ -208,7 +210,7 @@ void Parallaction::allocateLocationSlot(const char *name) { error("No more location slots available. Please report this immediately to ScummVM team"); if (_currentLocationIndex == -1) { - strcpy(_locationNames[_numLocations], name); + Common::strlcpy(_locationNames[_numLocations], name, 10); _currentLocationIndex = _numLocations; _numLocations++; diff --git a/engines/parallaction/parallaction.h b/engines/parallaction/parallaction.h index 6ea50584f8..c4839897ef 100644 --- a/engines/parallaction/parallaction.h +++ b/engines/parallaction/parallaction.h @@ -20,8 +20,8 @@ * */ -#ifndef PARALLACTION_H -#define PARALLACTION_H +#ifndef PARALLACTION_PARALLACTION_H +#define PARALLACTION_PARALLACTION_H #include "common/str.h" #include "common/stack.h" diff --git a/engines/parallaction/parallaction_br.cpp b/engines/parallaction/parallaction_br.cpp index 1e1c0b0a3d..9f045cb397 100644 --- a/engines/parallaction/parallaction_br.cpp +++ b/engines/parallaction/parallaction_br.cpp @@ -320,7 +320,7 @@ void Parallaction_br::changeLocation() { freeLocation(false); // load new location - strcpy(_location._name, _newLocationName.c_str()); + Common::strlcpy(_location._name, _newLocationName.c_str(), 100); parseLocation(_location._name); if (_location._startPosition.x != -1000) { 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/pegasus/neighborhood/mars/mars.cpp b/engines/pegasus/neighborhood/mars/mars.cpp index df5a75541c..7c4a8a98ba 100644 --- a/engines/pegasus/neighborhood/mars/mars.cpp +++ b/engines/pegasus/neighborhood/mars/mars.cpp @@ -1950,7 +1950,7 @@ void Mars::pickedUpItem(Item *item) { } void Mars::dropItemIntoRoom(Item *item, Hotspot *dropSpot) { - if (dropSpot->getObjectID() == kAttackRobotHotSpotID) { + if (dropSpot && dropSpot->getObjectID() == kAttackRobotHotSpotID) { _attackingItem = (InventoryItem *)item; startExtraSequence(kMars48RobotDefends, kExtraCompletedFlag, kFilterNoInput); loadLoopSound2(""); diff --git a/engines/pegasus/pegasus.h b/engines/pegasus/pegasus.h index d88545a4d1..57ae910def 100644 --- a/engines/pegasus/pegasus.h +++ b/engines/pegasus/pegasus.h @@ -23,8 +23,8 @@ * */ -#ifndef PEGASUS_H -#define PEGASUS_H +#ifndef PEGASUS_PEGASUS_H +#define PEGASUS_PEGASUS_H #include "common/list.h" #include "common/macresman.h" diff --git a/engines/prince/prince.cpp b/engines/prince/prince.cpp index b39d26e056..f1fd5a25d3 100644 --- a/engines/prince/prince.cpp +++ b/engines/prince/prince.cpp @@ -1543,20 +1543,18 @@ void PrinceEngine::showAnim(Anim &anim) { // make_special_shadow if ((anim._flags & 0x80)) { - if (animSurface) { - DrawNode newDrawNode; - newDrawNode.posX = x; - newDrawNode.posY = y + animSurface->h - anim._shadowBack; - newDrawNode.posZ = Hero::kHeroShadowZ; - newDrawNode.width = 0; - newDrawNode.height = 0; - newDrawNode.scaleValue = _scaleValue; - newDrawNode.originalRoomSurface = nullptr; - newDrawNode.data = this; - newDrawNode.drawFunction = &Hero::showHeroShadow; - newDrawNode.s = animSurface; - _drawNodeList.push_back(newDrawNode); - } + DrawNode newDrawNode; + newDrawNode.posX = x; + newDrawNode.posY = y + animSurface->h - anim._shadowBack; + newDrawNode.posZ = Hero::kHeroShadowZ; + newDrawNode.width = 0; + newDrawNode.height = 0; + newDrawNode.scaleValue = _scaleValue; + newDrawNode.originalRoomSurface = nullptr; + newDrawNode.data = this; + newDrawNode.drawFunction = &Hero::showHeroShadow; + newDrawNode.s = animSurface; + _drawNodeList.push_back(newDrawNode); } //ShowFrameCodeShadow diff --git a/engines/prince/prince.h b/engines/prince/prince.h index 6dce044a41..82fcb152fa 100644 --- a/engines/prince/prince.h +++ b/engines/prince/prince.h @@ -20,8 +20,8 @@ * */ -#ifndef PRINCE_H -#define PRINCE_H +#ifndef PRINCE_PRINCE_H +#define PRINCE_PRINCE_H #include "common/random.h" #include "common/system.h" diff --git a/engines/queen/queen.h b/engines/queen/queen.h index c00e1b3a70..789025c264 100644 --- a/engines/queen/queen.h +++ b/engines/queen/queen.h @@ -20,8 +20,8 @@ * */ -#ifndef QUEEN_H -#define QUEEN_H +#ifndef QUEEN_QUEEN_H +#define QUEEN_QUEEN_H #include "engines/engine.h" #include "common/random.h" diff --git a/engines/saga/interface.cpp b/engines/saga/interface.cpp index cb09d53762..b08534c7fa 100644 --- a/engines/saga/interface.cpp +++ b/engines/saga/interface.cpp @@ -1170,7 +1170,7 @@ void Interface::processStatusTextInput(Common::KeyState keystate) { _statusTextInputPos--; _statusTextInputString[_statusTextInputPos] = 0; default: - if (_statusTextInputPos >= STATUS_TEXT_INPUT_MAX) { + if (_statusTextInputPos > STATUS_TEXT_INPUT_MAX) { break; } if (Common::isAlnum(keystate.ascii) || (keystate.ascii == ' ')) { @@ -2299,6 +2299,9 @@ void Interface::drawPanelButtonText(InterfacePanel *panel, PanelButton *panelBut break; } if (_vm->getGameId() == GID_ITE) { + if (textId > kTextEnterProtectAnswer) + error("This should not happen. Please report to ScummVM Team how you achieved this error."); + text = _vm->getTextString(textId); textFont = kKnownFontMedium; textShadowKnownColor = kKnownColorVerbTextShadow; diff --git a/engines/saga/isomap.cpp b/engines/saga/isomap.cpp index 77680178c1..e50378b9c0 100644 --- a/engines/saga/isomap.cpp +++ b/engines/saga/isomap.cpp @@ -97,6 +97,23 @@ IsoMap::IsoMap(SagaEngine *vm) : _vm(vm) { _viewScroll.x = (128 - 8) * 16; _viewScroll.y = (128 - 8) * 16 - 64; _viewDiff = 1; + _platformHeight = 0; + _queueCount = _readCount = 0; + + for (int i = 0; i < SAGA_DRAGON_SEARCH_DIAMETER; i++) + for (int j = 0; j < SAGA_DRAGON_SEARCH_DIAMETER; j++) + _dragonSearchArray.cell[i][j].visited = _dragonSearchArray.cell[i][j].direction = 0; + + for (int i = 0; i < SAGA_SEARCH_DIAMETER; i++) + for (int j = 0; j < SAGA_SEARCH_DIAMETER; j++) + _searchArray.cell[i][j].visited = _searchArray.cell[i][j].direction = 0; + + for (int i = 0; i < SAGA_SEARCH_QUEUE_SIZE; i++) { + memset(&_dragonSearchArray.queue[i], 0, sizeof(DragonTilePoint)); + memset(&_searchArray.queue[i], 0, sizeof(TilePoint)); + } + + memset(&_tileMap, 0, sizeof(TileMapData)); } void IsoMap::loadImages(const ByteArray &resourceData) { diff --git a/engines/saga/puzzle.cpp b/engines/saga/puzzle.cpp index 099bf79e6b..2c9a02beec 100644 --- a/engines/saga/puzzle.cpp +++ b/engines/saga/puzzle.cpp @@ -86,6 +86,11 @@ Puzzle::Puzzle(SagaEngine *vm) : _vm(vm), _solved(false), _active(false) { _hintBox.setWidth(240); _hintBox.setHeight(30); + _hintNextRqState = kRQNoHint; + _hintGiver = 0; + _hintSpeaker = 0; + _slidePointX = _slidePointY = 0; + initPieceInfo( 0, 268, 18, 0, 0, 0 + PUZZLE_X_OFFSET, 0 + PUZZLE_Y_OFFSET, 0, 3, Point(0, 1), Point(0, 62), Point(15, 31), Point(0, 0), Point(0, 0), Point(0,0)); initPieceInfo( 1, 270, 52, 0, 0, 0 + PUZZLE_X_OFFSET, 32 + PUZZLE_Y_OFFSET, 0, 4, diff --git a/engines/saga/saga.h b/engines/saga/saga.h index 06cb411e5a..422eaa530d 100644 --- a/engines/saga/saga.h +++ b/engines/saga/saga.h @@ -20,8 +20,8 @@ * */ -#ifndef SAGA_H -#define SAGA_H +#ifndef SAGA_SAGA_H +#define SAGA_SAGA_H #include "engines/engine.h" diff --git a/engines/saga/saveload.cpp b/engines/saga/saveload.cpp index e659e09ce8..2d798bb0d6 100644 --- a/engines/saga/saveload.cpp +++ b/engines/saga/saveload.cpp @@ -56,7 +56,7 @@ SaveFileData *SagaEngine::getSaveFile(uint idx) { return &_saveFiles[_saveFilesCount - idx - 1]; } else { if (!emptySlot.name[0]) - strcpy(emptySlot.name, getTextString(kTextNewSave)); + Common::strlcpy(emptySlot.name, getTextString(kTextNewSave), SAVE_TITLE_SIZE); return (idx == 0) ? &emptySlot : &_saveFiles[_saveFilesCount - idx]; } diff --git a/engines/saga/scene.cpp b/engines/saga/scene.cpp index efd4c371b1..5cb4b55899 100644 --- a/engines/saga/scene.cpp +++ b/engines/saga/scene.cpp @@ -969,9 +969,8 @@ void Scene::processSceneResources(SceneResourceDataArray &resourceList) { case SAGA_OBJECT: break; case SAGA_BG_IMAGE: // Scene background resource - if (_bg.loaded) { + if (_bg.loaded) error("Scene::processSceneResources() Multiple background resources encountered"); - } debug(3, "Loading background resource."); @@ -987,9 +986,9 @@ void Scene::processSceneResources(SceneResourceDataArray &resourceList) { memcpy(_bg.pal, palPointer, sizeof(_bg.pal)); break; case SAGA_BG_MASK: // Scene background mask resource - if (_bgMask.loaded) { + if (_bgMask.loaded) error("Scene::ProcessSceneResources(): Duplicate background mask resource encountered"); - } + debug(3, "Loading BACKGROUND MASK resource."); _vm->decodeBGImage(resourceData, _bgMask.buffer, &_bgMask.w, &_bgMask.h, true); _bgMask.loaded = true; @@ -1014,47 +1013,38 @@ void Scene::processSceneResources(SceneResourceDataArray &resourceList) { _actionMap->load(resourceData); break; case SAGA_ISO_IMAGES: - if (!(_sceneDescription.flags & kSceneFlagISO)) { + if (!(_sceneDescription.flags & kSceneFlagISO)) error("Scene::ProcessSceneResources(): not Iso mode"); - } debug(3, "Loading isometric images resource."); _vm->_isoMap->loadImages(resourceData); break; case SAGA_ISO_MAP: - if (!(_sceneDescription.flags & kSceneFlagISO)) { + if (!(_sceneDescription.flags & kSceneFlagISO)) error("Scene::ProcessSceneResources(): not Iso mode"); - } debug(3, "Loading isometric map resource."); - _vm->_isoMap->loadMap(resourceData); break; case SAGA_ISO_PLATFORMS: - if (!(_sceneDescription.flags & kSceneFlagISO)) { + if (!(_sceneDescription.flags & kSceneFlagISO)) error("Scene::ProcessSceneResources(): not Iso mode"); - } debug(3, "Loading isometric platforms resource."); - _vm->_isoMap->loadPlatforms(resourceData); break; case SAGA_ISO_METATILES: - if (!(_sceneDescription.flags & kSceneFlagISO)) { + if (!(_sceneDescription.flags & kSceneFlagISO)) error("Scene::ProcessSceneResources(): not Iso mode"); - } debug(3, "Loading isometric metatiles resource."); - _vm->_isoMap->loadMetaTiles(resourceData); break; case SAGA_ANIM: { uint16 animId = resource->resourceType - 14; - debug(3, "Loading animation resource animId=%i", animId); - _vm->_anim->load(animId, resourceData); } break; @@ -1063,9 +1053,8 @@ void Scene::processSceneResources(SceneResourceDataArray &resourceList) { loadSceneEntryList(resourceData); break; case SAGA_ISO_MULTI: - if (!(_sceneDescription.flags & kSceneFlagISO)) { + if (!(_sceneDescription.flags & kSceneFlagISO)) error("Scene::ProcessSceneResources(): not Iso mode"); - } debug(3, "Loading isometric multi resource."); 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/detection_tables.h b/engines/sci/detection_tables.h index 2ae9802d35..c01613268a 100644 --- a/engines/sci/detection_tables.h +++ b/engines/sci/detection_tables.h @@ -349,6 +349,19 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformDOS, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Conquests of Camelot - English Atari ST + // Game version 1.019.000 + // Floppy: INT#10.12.90 + // Executable reports "1.002.038" + {"camelot", "", { + {"resource.map", 0, "0f80a11867be91a158823887a49cf443", 7290}, + {"resource.001", 0, "162f66c42e4146ee63f78fba6f1a6757", 596773}, + {"resource.002", 0, "162f66c42e4146ee63f78fba6f1a6757", 724615}, + {"resource.003", 0, "162f66c42e4146ee63f78fba6f1a6757", 713351}, + {"resource.004", 0, "162f66c42e4146ee63f78fba6f1a6757", 718766}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Conquests of Camelot - English DOS // SCI interpreter version 0.000.685 {"camelot", "", { @@ -939,6 +952,22 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Hoyle 1 - English Atari ST + // Game version 1.000.104, SCI interpreter version 1.002.024 + {"hoyle1", "", { + {"resource.001", 0, "e0dd44069a62a463fd124974b915f10d", 518127}, + {"resource.map", 0, "0af9a3dcd72a091960de070432e1f524", 4386}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + + // Hoyle 1 - English Atari ST + // Game version 1.000.108, SCI interpreter version 1.002.026 + {"hoyle1", "", { + {"resource.map", 0, "ed8355f84752e49ffa1f0cf9eca4b28e", 4140}, + {"resource.001", 0, "e0dd44069a62a463fd124974b915f10d", 517454}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Hoyle 2 - English DOS // SCI interpreter version 0.000.572 {"hoyle2", "", { @@ -982,6 +1011,15 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Hoyle 2 - English Atari ST + // Game version 1.001.017 + // Executable scanning reports "1.002.034" + {"hoyle2", "", { + {"resource.map", 0, "13c8cc977598b6ad61d24c6296a090fd", 1356}, + {"resource.001", 0, "8f2dd70abe01112eca464cda818b5eb6", 216280}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Hoyle 2 - English Macintosh // Executable scanning reports "x.yyy.zzz" {"hoyle2", "", { @@ -1977,6 +2015,17 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Larry 2 - English Atari ST + // Game version 1.001.006 + // Executable reports "1.000.159" 1988-12-02 12:22 p.m. + {"lsl2", "", { + {"resource.map", 0, "2fc3ce7da1346e4dadfee18606d814fc", 4758}, + {"resource.001", 0, "4a24443a25e2b1492462a52809605dc2", 477342}, + {"resource.002", 0, "4a24443a25e2b1492462a52809605dc2", 406698}, + {"resource.003", 0, "4a24443a25e2b1492462a52809605dc2", 592433}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Larry 2 - English DOS Non-Interactive Demo // Executable scanning reports "x.yyy.zzz" // SCI interpreter version 0.000.409 @@ -2034,6 +2083,17 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformDOS, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Larry 2 - English Atari ST (Kixx) + // Game version 1.002.000 + // Executable reports "1.001.008" 1989-01-12 16:30 + {"lsl2", "", { + {"resource.map", 0, "2c9c3b0923e3764f5ab999bcb71c2d47", 4758}, + {"resource.001", 0, "4a24443a25e2b1492462a52809605dc2", 477625}, + {"resource.002", 0, "4a24443a25e2b1492462a52809605dc2", 406935}, + {"resource.003", 0, "4a24443a25e2b1492462a52809605dc2", 592533}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Larry 3 - English Amiga (from www.back2roots.org) // Executable scanning reports "1.002.032" // SCI interpreter version 0.000.685 @@ -2048,6 +2108,19 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Larry 3 - English Atari ST + // Game version 1.021, 1990-01-27 + // Int#6.26.90 + // Executable scanning reports "1.002.026" + {"lsl3", "", { + {"resource.map", 0, "0b6bd3e039682830a51c5755c06591db", 5916}, + {"resource.001", 0, "f18441027154292836b973c655fa3175", 456722}, + {"resource.002", 0, "f18441027154292836b973c655fa3175", 578024}, + {"resource.003", 0, "f18441027154292836b973c655fa3175", 506807}, + {"resource.004", 0, "f18441027154292836b973c655fa3175", 513651}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Larry 3 - English DOS (supplied by ssburnout in bug report #3049193) // 1.021 8x5.25" (label: Int#5.15.90) {"lsl3", "", { @@ -2916,6 +2989,17 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformDOS, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Police Quest 2 - English Atari ST + // Game version 1.002.011 DS 1989-07-21 + // Executable reports "1.002.003" + {"pq2", "", { + {"resource.map", 0, "28a6f471c7900c2c92da40eecb615d9d", 4584}, + {"resource.001", 0, "77f02def3094af804fd2371db25b7100", 509525}, + {"resource.002", 0, "77f02def3094af804fd2371db25b7100", 546000}, + {"resource.003", 0, "77f02def3094af804fd2371db25b7100", 591851}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Police Quest 2 - English DOS (from FRG) // SCI interpreter version 0.000.395 {"pq2", "", { @@ -2935,6 +3019,17 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformDOS, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Police Quest 2 - English Atari ST + // Game version 1.001.006 1989-01-16 13:30 + // Executable reports "1.001.009" + {"pq2", "", { + {"resource.map", 0, "8e1161c684b342742d30f938a4839a4b", 4518}, + {"resource.001", 0, "77f02def3094af804fd2371db25b7100", 506563}, + {"resource.002", 0, "77f02def3094af804fd2371db25b7100", 541261}, + {"resource.003", 0, "77f02def3094af804fd2371db25b7100", 587511}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Police Quest 2 - Japanese PC-98 (also includes english language) // Executable scanning reports "x.yyy.zzz" // SCI interpreter version unknown @@ -3204,6 +3299,19 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformDOS, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Quest for Glory 1 / Hero's Quest - English Atari ST + // Game version 1.137 + // Executable reports "1.002.028" + {"qfg1", "", { + {"resource.map", 0, "2a794066ad161acbedac8fa14e46905d", 6438}, + {"resource.000", 0, "40332d3ebfc70a4b6a6a0443c2763287", 79204}, + {"resource.001", 0, "f7fc269d3db146830d6427d3e02d4187", 473547}, + {"resource.002", 0, "e64004e020fdf1813be52b639b08be89", 635687}, + {"resource.003", 0, "f0af87c60ec869946da442833aa5afa8", 640438}, + {"resource.004", 0, "f0af87c60ec869946da442833aa5afa8", 644452}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Quest for Glory 1 / Hero's Quest - English DOS Demo // Executable scanning reports "0.000.685" {"qfg1", "Demo", { @@ -3283,6 +3391,7 @@ static const struct ADGameDescription SciGameDescriptions[] = { Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK, GUIO4(GUIO_NOSPEECH, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, // Quest for Glory 2 - English Amiga + // Game version 1.109 // Executable scanning reports "1.003.004" // SCI interpreter version 0.001.010 {"qfg2", "", { @@ -3705,6 +3814,18 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::EN_ANY, Common::kPlatformAmiga, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Space Quest 3 - English Atari ST + // Game version 1.0Q 1989-27-03 17:00 + // Int#1.002.002 + // Executable reports "1.002.001" + {"sq3", "", { + {"resource.map", 0, "c36e322805949affd882a75803a6a54e", 5484}, + {"resource.001", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 485146}, + {"resource.002", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 720227}, + {"resource.003", 0, "ceeda7202b96e5c85ecaa88a40a540fc", 688524}, + AD_LISTEND}, + Common::EN_ANY, Common::kPlatformAtariST, 0, GUIO5(GUIO_NOSPEECH, GAMEOPTION_EGA_UNDITHER, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Space Quest 3 - German Amiga (also includes english language) // Executable scanning reports "1.004.006" // SCI interpreter version 0.000.453 (just a guess) @@ -4235,6 +4356,16 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::FR_FRA, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Torin's Passage - Russian Windows CD (SoftClub official translate) + // SCI interpreter version 2.100.002 + // VERSION file "1.0" + { "torin", "",{ + { "resource.aud", 0, "f66df699be5ed011b16b3f816cee8a04", 210583510 }, + { "ressci.000", 0, "e672da099fb1663b87c78abc6c8ba2a4", 130622695 }, + { "resmap.000", 0, "643859f8f2be8e7701611e29b3b65208", 9799 }, + AD_LISTEND }, + Common::RU_RUS, Common::kPlatformWindows, ADGF_UNSTABLE | ADGF_CD, GUIO4(GUIO_NOASPECT, GAMEOPTION_PREFER_DIGITAL_SFX, GAMEOPTION_ORIGINAL_SAVELOAD, GAMEOPTION_FB01_MIDI) }, + // Torin's Passage - English Macintosh {"torin", "", { {"Data1", 0, "63887e33cc282c92dc1f916f54aea8eb", 700786}, 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/script_patches.cpp b/engines/sci/engine/script_patches.cpp index 8039c5f282..116ffdd5a2 100644 --- a/engines/sci/engine/script_patches.cpp +++ b/engines/sci/engine/script_patches.cpp @@ -2934,6 +2934,84 @@ static const uint16 qfg3PatchChiefPriority[] = { PATCH_END }; +// There are 3 points that can't be achieved in the game. They should've been +// awarded for telling Rakeesh and Kreesha (room 285) about the Simabni +// initiation. +// However the array of posibble messages the hero can tell in that room +// (local 156) is missing the "Tell about Initiation" message (#31) which +// awards these points. +// This patch adds the message to that array, thus allowing the hero to tell +// that message (after completing the initiation) and gain the 3 points. +// A side effect of increasing the local156 array is that the next local +// array is shifted and shrinks in size from 4 words to 3. The patch changes +// the 2 locations in the script that reference that array, to point to the new +// location ($aa --> $ab). It is safe to shrink the 2nd array to 3 words +// because only the first element in it is ever used. +// +// Note: You have to re-enter the room in case a saved game was loaded from a +// previous version of ScummVM and that saved game was made inside that room. +// +// Applies to: English, French, German, Italian, Spanish and the GOG release. +// Responsible method: heap in script 285 +// Fixes bug #7086 +static const uint16 qfg3SignatureMissingPoints1[] = { + // local[$9c] = [0 -41 -76 1 -30 -77 -33 -34 -35 -36 -37 -42 -80 999] + // local[$aa] = [0 0 0 0] + SIG_UINT16(0x0000), // 0 START MARKER + SIG_MAGICDWORD, + SIG_UINT16(0xFFD7), // -41 "Greet" + SIG_UINT16(0xFFB4), // -76 "Say Good-bye" + SIG_UINT16(0x0001), // 1 "Tell about Tarna" + SIG_UINT16(0xFFE2), // -30 "Tell about Simani" + SIG_UINT16(0xFFB3), // -77 "Tell about Prisoner" + SIG_UINT16(0xFFDF), // -33 "Dispelled Leopard Lady" + SIG_UINT16(0xFFDE), // -34 "Tell about Leopard Lady" + SIG_UINT16(0xFFDD), // -35 "Tell about Leopard Lady" + SIG_UINT16(0xFFDC), // -36 "Tell about Leopard Lady" + SIG_UINT16(0xFFDB), // -37 "Tell about Village" + SIG_UINT16(0xFFD6), // -42 "Greet" + SIG_UINT16(0xFFB0), // -80 "Say Good-bye" + SIG_UINT16(0x03E7), // 999 END MARKER + SIG_ADDTOOFFSET(+2), // local[$aa][0] + SIG_END +}; + +static const uint16 qfg3PatchMissingPoints1[] = { + PATCH_ADDTOOFFSET(+14), + PATCH_UINT16(0xFFE1), // -31 "Tell about Initiation" + PATCH_UINT16(0xFFDE), // -34 "Tell about Leopard Lady" + PATCH_UINT16(0xFFDD), // -35 "Tell about Leopard Lady" + PATCH_UINT16(0xFFDC), // -36 "Tell about Leopard Lady" + PATCH_UINT16(0xFFDB), // -37 "Tell about Village" + PATCH_UINT16(0xFFD6), // -42 "Greet" + PATCH_UINT16(0xFFB0), // -80 "Say Good-bye" + PATCH_UINT16(0x03E7), // 999 END MARKER + PATCH_GETORIGINALBYTE(+28), // local[$aa][0].low + PATCH_GETORIGINALBYTE(+29), // local[$aa][0].high + PATCH_END +}; + +static const uint16 qfg3SignatureMissingPoints2a[] = { + SIG_MAGICDWORD, + 0x35, 0x00, // ldi 0 + 0xb3, 0xaa, // sali local[$aa] + SIG_END +}; + +static const uint16 qfg3SignatureMissingPoints2b[] = { + SIG_MAGICDWORD, + 0x36, // push + 0x5b, 0x02, 0xaa, // lea local[$aa] + SIG_END +}; + +static const uint16 qfg3PatchMissingPoints2[] = { + PATCH_ADDTOOFFSET(+3), + 0xab, // local[$aa] ==> local[$ab] + PATCH_END +}; + + // Partly WORKAROUND: // During combat, the game is not properly throttled. That's because the game uses // an inner loop for combat and does not iterate through the main loop. @@ -2995,14 +3073,17 @@ static const uint16 qfg3PatchCombatSpeedThrottling2[] = { // script, description, signature patch static const SciScriptPatcherEntry qfg3Signatures[] = { - { true, 944, "import dialog continuous calls", 1, qfg3SignatureImportDialog, qfg3PatchImportDialog }, - { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialog, qfg3PatchWooDialog }, - { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialogAlt, qfg3PatchWooDialogAlt }, - { true, 52, "export character save bug", 2, qfg3SignatureExportChar, qfg3PatchExportChar }, - { true, 54, "import character from QfG1 bug", 1, qfg3SignatureImportQfG1Char, qfg3PatchImportQfG1Char }, - { true, 640, "chief in hut priority fix", 1, qfg3SignatureChiefPriority, qfg3PatchChiefPriority }, - { true, 550, "combat speed throttling script", 1, qfg3SignatureCombatSpeedThrottling1, qfg3PatchCombatSpeedThrottling1 }, - { true, 550, "combat speed throttling heap", 1, qfg3SignatureCombatSpeedThrottling2, qfg3PatchCombatSpeedThrottling2 }, + { true, 944, "import dialog continuous calls", 1, qfg3SignatureImportDialog, qfg3PatchImportDialog }, + { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialog, qfg3PatchWooDialog }, + { true, 440, "dialog crash when asking about Woo", 1, qfg3SignatureWooDialogAlt, qfg3PatchWooDialogAlt }, + { true, 52, "export character save bug", 2, qfg3SignatureExportChar, qfg3PatchExportChar }, + { true, 54, "import character from QfG1 bug", 1, qfg3SignatureImportQfG1Char, qfg3PatchImportQfG1Char }, + { true, 640, "chief in hut priority fix", 1, qfg3SignatureChiefPriority, qfg3PatchChiefPriority }, + { true, 285, "missing points for telling about initiation heap", 1, qfg3SignatureMissingPoints1, qfg3PatchMissingPoints1 }, + { true, 285, "missing points for telling about initiation script", 1, qfg3SignatureMissingPoints2a, qfg3PatchMissingPoints2 }, + { true, 285, "missing points for telling about initiation script", 1, qfg3SignatureMissingPoints2b, qfg3PatchMissingPoints2 }, + { true, 550, "combat speed throttling script", 1, qfg3SignatureCombatSpeedThrottling1, qfg3PatchCombatSpeedThrottling1 }, + { true, 550, "combat speed throttling heap", 1, qfg3SignatureCombatSpeedThrottling2, qfg3PatchCombatSpeedThrottling2 }, SCI_SIGNATUREENTRY_TERMINATOR }; 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/sci/sci.h b/engines/sci/sci.h index 7df3d38163..c49a516d01 100644 --- a/engines/sci/sci.h +++ b/engines/sci/sci.h @@ -20,8 +20,8 @@ * */ -#ifndef SCI_H -#define SCI_H +#ifndef SCI_SCI_H +#define SCI_SCI_H #include "engines/engine.h" #include "common/macresman.h" @@ -460,4 +460,4 @@ const char *getSciVersionDesc(SciVersion version); } // End of namespace Sci -#endif // SCI_H +#endif // SCI_SCI_H 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/detection.cpp b/engines/scumm/detection.cpp index 0867b20fc3..4c9d1221aa 100644 --- a/engines/scumm/detection.cpp +++ b/engines/scumm/detection.cpp @@ -186,6 +186,11 @@ Common::String ScummEngine_v70he::generateFilename(const int room) const { } if (_filenamePattern.genMethod == kGenHEPC || _filenamePattern.genMethod == kGenHEIOS) { + if (id == '3' && _game.id == GID_MOONBASE) { + result = Common::String::format("%s.u32", _filenamePattern.pattern); + break; + } + // For HE >= 98, we already called snprintf above. if (_game.heversion < 98 || room < 0) result = Common::String::format("%s.he%c", _filenamePattern.pattern, id); diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h index 5a994cb699..bb3e7f6ec3 100644 --- a/engines/scumm/detection_tables.h +++ b/engines/scumm/detection_tables.h @@ -245,9 +245,11 @@ static const GameSettings gameVariantsTable[] = { {"monkey", "CD", 0, GID_MONKEY, 5, 0, MDT_ADLIB, GF_AUDIOTRACKS, UNK, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, {"monkey", "FM-TOWNS", 0, GID_MONKEY, 5, 0, MDT_TOWNS, GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO4(GUIO_NOSPEECH, GUIO_NOMIDI, GUIO_MIDITOWNS, GUIO_NOASPECT)}, {"monkey", "SEGA", 0, GID_MONKEY, 5, 0, MDT_NONE, GF_AUDIOTRACKS, Common::kPlatformSegaCD, GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI)}, + {"monkey", "SE Talkie", 0, GID_MONKEY, 5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, GF_AUDIOTRACKS, UNK, GUIO0()}, {"monkey2", "", 0, GID_MONKEY2, 5, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO1(GUIO_NOSPEECH)}, {"monkey2", "FM-TOWNS", 0, GID_MONKEY2, 5, 0, MDT_PCSPK | MDT_TOWNS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, Common::kPlatformFMTowns, GUIO5(GUIO_NOSPEECH, GUIO_MIDITOWNS, GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_NOASPECT)}, + {"monkey2", "SE Talkie",0, GID_MONKEY2, 5, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO0()}, {"atlantis", "", 0, GID_INDY4, 5, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO0()}, {"atlantis", "Steam", "steam", GID_INDY4, 5, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO0()}, diff --git a/engines/scumm/he/intern_he.h b/engines/scumm/he/intern_he.h index 370f54c1d8..7f7babc604 100644 --- a/engines/scumm/he/intern_he.h +++ b/engines/scumm/he/intern_he.h @@ -184,8 +184,11 @@ protected: }; #ifdef ENABLE_HE +class Moonbase; + class ScummEngine_v71he : public ScummEngine_v70he { friend class Wiz; + friend class Moonbase; protected: bool _skipProcessActors; @@ -423,6 +426,7 @@ protected: class ScummEngine_v90he : public ScummEngine_v80he { friend class LogicHE; + friend class Moonbase; friend class MoviePlayer; friend class Sprite; @@ -433,7 +437,7 @@ protected: byte filename[260]; int32 status; int32 flags; - int32 unk2; + int32 number; int32 wizResNum; }; @@ -571,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(); @@ -626,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 new file mode 100644 index 0000000000..1b596fc54c --- /dev/null +++ b/engines/scumm/he/logic/moonbase_logic.cpp @@ -0,0 +1,255 @@ +/* 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 "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 { + +/** + * Logic code for: + * Moonbase Commander + */ +class LogicHEmoonbase : public LogicHE { +public: + LogicHEmoonbase(ScummEngine_v100he *vm) : LogicHE(vm) { _vm1 = vm; } + + int versionID(); + + int32 dispatch(int op, int numArgs, int32 *args); + +private: + void op_create_multi_state_wiz(int op, int numArgs, int32 *args); + void op_load_multi_channel_wiz(int op, int numArgs, int32 *args); + void op_wiz_from_multi_channel_wiz(int op, int numArgs, int32 *args); + void op_dos_command(int op, int numArgs, int32 *args); + void op_set_fow_sentinel(int32 *args); + void op_set_fow_information(int op, int numArgs, int32 *args); + int op_set_fow_image(int op, int numArgs, int32 *args); + + void op_ai_test_kludge(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() { + if (_vm->_game.features & GF_DEMO) + return -100; + else if (strcmp(_vm->_game.variant, "1.1") == 0) + return 110; + else + return 100; +} + +#define OP_CREATE_MULTI_STATE_WIZ 100 +#define OP_LOAD_MULTI_CHANNEL_WIZ 101 +#define OP_WIZ_FROM_MULTI_CHANNEL_WIZ 102 +#define OP_DOS_COMMAND 103 +#define OP_SET_FOW_SENTINEL 104 +#define OP_SET_FOW_INFORMATION 105 +#define OP_SET_FOW_IMAGE 106 + +#define OP_AI_TEST_KLUDGE 10000 +#define OP_AI_MASTER_CONTROL_PROGRAM 10001 +#define OP_AI_RESET 10002 +#define OP_AI_SET_TYPE 10003 +#define OP_AI_CLEAN_UP 10004 + +#define OP_NET_REMOTE_START_SCRIPT 1492 +#define OP_NET_DO_INIT_ALL 1493 +#define OP_NET_DO_INIT_PROVIDER 1494 +#define OP_NET_DO_INIT_SESSION 1495 +#define OP_NET_DO_INIT_USER 1496 +#define OP_NET_QUERY_PROVIDERS 1497 +#define OP_NET_GET_PROVIDER_NAME 1498 +#define OP_NET_SET_PROVIDER 1499 +#define OP_NET_CLOSE_PROVIDER 1500 +#define OP_NET_QUERY_SESSIONS 1501 +#define OP_NET_GET_SESSION_NAME 1502 +#define OP_NET_CREATE_SESSION 1503 +#define OP_NET_JOIN_SESSION 1504 +#define OP_NET_END_SESSION 1505 +#define OP_NET_ADD_USER 1506 +#define OP_NET_REMOVE_USER 1507 +#define OP_NET_WHO_SENT_THIS 1508 +#define OP_NET_REMOTE_SEND_ARRAY 1509 +#define OP_NET_WHO_AM_I 1510 +#define OP_NET_REMOTE_START_FUNCTION 1511 +#define OP_NET_GET_PLAYER_LONG_NAME 1512 +#define OP_NET_GET_PLAYER_SHORT_NAME 1513 +#define OP_NET_SET_PROVIDER_BY_NAME 1516 +#define OP_NET_HOST_TCPIP_GAME 1517 +#define OP_NET_JOIN_TCPIP_GAME 1518 +#define OP_NET_SET_FAKE_LAG 1555 +#define OP_NET_GET_HOST_NAME 1556 +#define OP_NET_GET_IP_FROM_NAME 1557 +#define OP_NET_GET_SESSION_PLAYER_COUNT 1558 +#define OP_NET_DISABLE_SESSION_PLAYER_JOIN 1559 +#define OP_NET_START_QUERY_SESSIONS 1560 +#define OP_NET_UPDATE_QUERY_SESSIONS 1561 +#define OP_NET_STOP_QUERY_SESSIONS 1562 +#define OP_NET_DESTROY_PLAYER 1563 +#define OP_NET_ENABLE_SESSION_PLAYER_JOIN 1564 +#define OP_NET_SET_AI_PLAYER_COUNT 1565 + + +int32 LogicHEmoonbase::dispatch(int op, int numArgs, int32 *args) { + switch (op) { + case OP_CREATE_MULTI_STATE_WIZ: + op_create_multi_state_wiz(op, numArgs, args); + break; + case OP_LOAD_MULTI_CHANNEL_WIZ: + op_load_multi_channel_wiz(op, numArgs, args); + break; + case OP_WIZ_FROM_MULTI_CHANNEL_WIZ: + op_wiz_from_multi_channel_wiz(op, numArgs, args); + break; + case OP_DOS_COMMAND: + op_dos_command(op, numArgs, args); + break; + case OP_SET_FOW_SENTINEL: + op_set_fow_sentinel(args); + break; + case OP_SET_FOW_INFORMATION: + op_set_fow_information(op, numArgs, args); + break; + case OP_SET_FOW_IMAGE: + return op_set_fow_image(op, numArgs, args); + + case OP_AI_TEST_KLUDGE: + op_ai_test_kludge(op, numArgs, args); + break; + case OP_AI_MASTER_CONTROL_PROGRAM: + return op_ai_master_control_program(op, numArgs, args); + case OP_AI_RESET: + op_ai_reset(op, numArgs, args); + break; + case OP_AI_SET_TYPE: + op_ai_set_type(op, numArgs, args); + break; + case OP_AI_CLEAN_UP: + op_ai_clean_up(op, numArgs, args); + break; + + default: + LogicHE::dispatch(op, numArgs, args); + } + + return 0; +} + +void LogicHEmoonbase::op_create_multi_state_wiz(int op, int numArgs, int32 *args) { + warning("STUB: op_create_multi_state_wiz()"); + LogicHE::dispatch(op, numArgs, args); +} + +void LogicHEmoonbase::op_load_multi_channel_wiz(int op, int numArgs, int32 *args) { + warning("STUB: op_load_multi_channel_wiz()"); + LogicHE::dispatch(op, numArgs, args); +} + +void LogicHEmoonbase::op_wiz_from_multi_channel_wiz(int op, int numArgs, int32 *args) { + warning("STUB: op_wiz_from_multi_channel_wiz()"); + LogicHE::dispatch(op, numArgs, args); +} + +void LogicHEmoonbase::op_dos_command(int op, int numArgs, int32 *args) { + warning("STUB: op_dos_command()"); + LogicHE::dispatch(op, numArgs, args); +} + +void LogicHEmoonbase::op_set_fow_sentinel(int32 *args) { + debug(2, "op_set_fow_sentinel(%d, %d, %d)", args[0], args[1], 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) { + Common::String str; + + str = Common::String::format("op_set_fow_information(%d", args[0]); + for (int i = 1; i < numArgs; i++) { + str += Common::String::format(", %d", args[i]); + } + str += ")"; + + debug(2, "%s", str.c_str()); + + _vm1->_moonbase->setFOWInfo( + args[0], // array + args[1], // array down dimension + args[2], // array across dimension + args[3], // logical view X coordinate + args[4], // logical view Y coordinate + args[5], // screen draw clip rect x1 + args[6], // screen draw clip rect y1 + args[7], // screen draw clip rect x2 + args[8], // screen draw clip rect y2 + args[9], // techinque + args[10] // frame + ); +} + +int LogicHEmoonbase::op_set_fow_image(int op, int numArgs, int32 *args) { + debug(2, "op_set_fow_image(%d)", args[0]); + return _vm1->_moonbase->setFOWImage(args[0]) ? 1 : 0; +} + +void LogicHEmoonbase::op_ai_test_kludge(int op, int numArgs, int32 *args) { + warning("STUB: op_ai_test_kludge()"); + 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("op_ai_reset())"); + _vm1->_moonbase->_ai->resetAI(); +} + +void LogicHEmoonbase::op_ai_set_type(int op, int numArgs, int32 *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("op_ai_clean_up()"); + _vm1->_moonbase->_ai->cleanUpAI(); +} + +LogicHE *makeLogicHEmoonbase(ScummEngine_v100he *vm) { + return new LogicHEmoonbase(vm); +} + +} // End of namespace Scumm 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 new file mode 100644 index 0000000000..ab61297603 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_defenseunit.cpp @@ -0,0 +1,767 @@ +/* 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/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(AI *ai) : _ai(ai) { + _state = DUS_ON; + + _id = -1; + _distanceTo = 0; + _state = 0; + _radius = 0; + _armor = 0; + _cost = 0; +} + +DefenseUnit::DefenseUnit(DefenseUnit *inUnit, AI *ai) : _ai(ai) { + _id = inUnit->getID(); + _pos.x = inUnit->getPosX(); + _pos.y = inUnit->getPosY(); + _distanceTo = inUnit->getDistanceTo(); + _state = inUnit->getState(); + _radius = inUnit->getRadius(); + _armor = inUnit->getArmor(); + _cost = inUnit->getCost(); +} + +DefenseUnit::~DefenseUnit() { +} + +Common::Point *AntiAirUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + float ratio; + int radius; + Common::Point *targetPos = new Common::Point; + + if (!distance) distance = 1; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CRAWLER: + radius = getRadius(); + + if ((distance < radius) || (getState() == DUS_OFF)) { + targetPos->x = getPosX(); + targetPos->y = getPosY(); + } else { + ratio = MAX(0, (getRadius() / distance)); + targetPos->x = (int16)(getPosX() - ratio * (getPosX() - sourceX)); + targetPos->y = (int16)(getPosY() - ratio * (getPosY() - sourceY)); + } + + break; + + case ITEM_EMP: + if (getRadius() + 215 > distance) { // Emp radius + double x1 = static_cast<double>(sourceX); + double y1 = static_cast<double>(sourceY); + double x2 = static_cast<double>(getPosX()); + double y2 = static_cast<double>(getPosY()); + double r1 = static_cast<double>(215); + double r2 = static_cast<double>(getRadius() + 3); + double d = static_cast<double>(distance); + + // 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 = (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 = (int16)(sourceX + ratio * (getPosX() - sourceX)); + targetPos->y = (int16)(sourceY + ratio * (getPosY() - sourceY)); + } + + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int AntiAirUnit::selectWeapon(int index) { + switch (index) { + case 0: + return ITEM_CLUSTER; + break; + + case 1: + return ITEM_EMP; + break; + + case 2: + if (getState() == DUS_OFF) { + if (_ai->getPlayerEnergy() > 6) { + if (!_ai->_vm->_rnd.getRandomNumber(3)) { + return ITEM_VIRUS; + } + } + + if (_ai->getPlayerEnergy() > 2) { + if (!_ai->_vm->_rnd.getRandomNumber(1)) { + return ITEM_SPIKE; + } + } + + return ITEM_BOMB; + } + + return ITEM_CLUSTER; + break; + + default: + return ITEM_CLUSTER; + break; + } +} + +Common::Point *ShieldUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + float ratio; + Common::Point *targetPos = new Common::Point; + + if (getState() == DUS_OFF) { + targetPos->x = getPosX(); + targetPos->y = getPosY(); + } else { + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CRAWLER: + ratio = MAX(0.0, 1.0 - (static_cast<float>(getRadius()) / static_cast<float>(distance - 20))); + { + 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; + targetPos->y = thisY; + } + break; + + case ITEM_EMP: + if (getRadius() + 215 > distance) { // Emp radius + double x1 = static_cast<double>(sourceX); + double y1 = static_cast<double>(sourceY); + double x2 = static_cast<double>(getPosX()); + double y2 = static_cast<double>(getPosY()); + double r1 = static_cast<double>(215); + double r2 = static_cast<double>(getRadius() + 10); + double d = static_cast<double>(distance); + + // 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 = (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 = (int16)(sourceX + ratio * (getPosX() - sourceX)); + targetPos->y = (int16)(sourceY + ratio * (getPosY() - sourceY)); + } + + if (distance < getRadius()) { + targetPos->x = getPosX(); + targetPos->y = getPosY(); + } + + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + } + + return targetPos; +} + +int ShieldUnit::selectWeapon(int index) { + warning("Shield weapon select"); + + 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; + } + + switch (index) { + case 0: + if (getState() == DUS_OFF) { + if (_ai->getPlayerEnergy() < 3) { + return ITEM_BOMB; + } else { + return ITEM_SPIKE; + } + } + + return ITEM_EMP; + break; + + case 1: + if (dist < getRadius() + 150) { + return ITEM_EMP; + } else { + return ITEM_CRAWLER; + } + + break; + + default: + return ITEM_EMP; + break; + } +} + +Common::Point *MineUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + float ratio; + Common::Point *targetPos = new Common::Point; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_EMP: + ratio = 1 - (getRadius() / static_cast<float>(distance - 20)); + targetPos->x = (int16)(sourceX + ratio * (getPosX() - sourceX)); + targetPos->y = (int16)(sourceY + ratio * (getPosY() - sourceY)); + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int MineUnit::selectWeapon(int index) { + int myUnit = _ai->getClosestUnit(getPosX(), getPosY(), _ai->getMaxX(), _ai->getCurrentPlayer(), 1, 0, 0, 0); + int x = getPosX(); + int y = getPosY(); + + int dist = _ai->getDistance(x, y, _ai->getHubX(myUnit), _ai->getHubY(myUnit)); + + if ((getState() == DUS_ON) && (dist < 110)) { + return ITEM_EMP; + } else { + return ITEM_BOMB; + } +} + + +Common::Point *HubUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + Common::Point *targetPos = new Common::Point; + + if (!distance) distance = 1; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CRAWLER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int HubUnit::selectWeapon(int index) { + warning("Hub weapon select"); + + int energy = _ai->getPlayerEnergy(); + + if (energy > 6) { + //possibly choose crawler + if (_ai->getBuildingWorth(getID()) > 21) { + return ITEM_CRAWLER; + } + } + + //choose betw/ bomb and cluster + if (_ai->getBuildingArmor(getID()) < 1.5) { + return ITEM_CLUSTER; + } + + if (energy > 2) { + if (!_ai->_vm->_rnd.getRandomNumber(3)) { + return ITEM_SPIKE; + } + + if (!_ai->_vm->_rnd.getRandomNumber(4)) { + return ITEM_GUIDED; + } + + if (!_ai->_vm->_rnd.getRandomNumber(4)) { + return ITEM_MINE; + } + + if (!_ai->_vm->_rnd.getRandomNumber(9)) { + return ITEM_EMP; + } + } + + return ITEM_BOMB; +} + + +Common::Point *TowerUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + Common::Point *targetPos = new Common::Point; + + if (!distance) distance = 1; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_SPIKE: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int TowerUnit::selectWeapon(int index) { + switch (index) { + case 0: + return ITEM_SPIKE; + break; + + default: + return ITEM_SPIKE; + break; + } +} + + +Common::Point *BridgeUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + Common::Point *targetPos = new Common::Point; + + if (!distance) distance = 1; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int BridgeUnit::selectWeapon(int index) { + switch (index) { + case 0: + return ITEM_BOMB; + break; + + case 1: + return ITEM_CLUSTER; + break; + + default: + return ITEM_BOMB; + break; + } +} + +Common::Point *EnergyUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + Common::Point *targetPos = new Common::Point; + + if (!distance) distance = 1; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CRAWLER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int EnergyUnit::selectWeapon(int index) { + warning("Energy weapon select"); + + int energy = _ai->getPlayerEnergy(); + + if (energy > 6) { + //possibly choose crawler + if (_ai->getBuildingWorth(getID()) > 21) { + return ITEM_CRAWLER; + } + } + + //choose betw/ bomb and cluster + if (_ai->getBuildingArmor(getID()) < 1.5) { + return ITEM_CLUSTER; + } + + if (energy > 2) { + if (!_ai->_vm->_rnd.getRandomNumber(3)) { + return ITEM_EMP; + } + } + + return ITEM_BOMB; +} + +Common::Point *OffenseUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + Common::Point *targetPos = new Common::Point; + + if (!distance) distance = 1; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CRAWLER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int OffenseUnit::selectWeapon(int index) { + warning("Offense weapon select"); + + int energy = _ai->getPlayerEnergy(); + + if (energy > 6) { + //possibly choose crawler + if (_ai->getBuildingWorth(getID()) > 21) { + return ITEM_CRAWLER; + } + } + + //choose betw/ bomb and cluster + if (_ai->getBuildingArmor(getID()) < 1.5) { + return ITEM_CLUSTER; + } + + return ITEM_BOMB; +} + +Common::Point *CrawlerUnit::createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) { + Common::Point *targetPos = new Common::Point; + + if (!distance) + distance = 1; + + switch (weaponType) { + case ITEM_BOMB: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CLUSTER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + case ITEM_CRAWLER: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + + default: + targetPos->x = getPosX(); + targetPos->y = getPosY(); + break; + } + + return targetPos; +} + +int CrawlerUnit::selectWeapon(int index) { + warning("Crawler weapon select"); + 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 = _ai->getPlayerEnergy(); + int terrain = _ai->getTerrain(x, y); + + if (terrain != TERRAIN_TYPE_WATER) { + if ((energy > 2) && (dist < 220)) { + return ITEM_RECLAIMER; + } else { + return ITEM_BOMB; + } + } else { + if (energy > 6) { + return ITEM_CRAWLER; + } + + if (energy > 2) { + if (_ai->_vm->_rnd.getRandomNumber(1)) { + return ITEM_MINE; + } else { + return ITEM_TIME_EXPIRED; + } + } + } + + return SKIP_TURN; +} + +AntiAirUnit::AntiAirUnit(AI *ai) : DefenseUnit(ai) { + setRadius(190); + setArmor(3); + setCost(1); +} + +ShieldUnit::ShieldUnit(AI *ai) : DefenseUnit(ai) { + setRadius(170); + setArmor(3); + setCost(7); +} + +MineUnit::MineUnit(AI *ai) : DefenseUnit(ai) { + setRadius(80); + setArmor(1); + setCost(3); +} + +HubUnit::HubUnit(AI *ai) : DefenseUnit(ai) { + setRadius(1); + setArmor(5); + setCost(7); +} + +TowerUnit::TowerUnit(AI *ai) : DefenseUnit(ai) { + setRadius(1); + setArmor(3); + setCost(1); +} + +BridgeUnit::BridgeUnit(AI *ai) : DefenseUnit(ai) { + setRadius(1); + setArmor(3); + setCost(1); +} + +EnergyUnit::EnergyUnit(AI *ai) : DefenseUnit(ai) { + setRadius(1); + setArmor(5); + setCost(7); +} + +OffenseUnit::OffenseUnit(AI *ai) : DefenseUnit(ai) { + setRadius(1); + setArmor(3); + setCost(7); +} + +CrawlerUnit::CrawlerUnit(AI *ai) : DefenseUnit(ai) { + setRadius(1); + setArmor(3); + setCost(7); +} + +AntiAirUnit::AntiAirUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); + +} + +ShieldUnit::ShieldUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +MineUnit::MineUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +HubUnit::HubUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +TowerUnit::TowerUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +BridgeUnit::BridgeUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +EnergyUnit::EnergyUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +OffenseUnit::OffenseUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +CrawlerUnit::CrawlerUnit(DefenseUnit *inUnit, AI *ai) : DefenseUnit(inUnit, ai) { + setID(inUnit->getID()); + setPos(inUnit->getPosX(), inUnit->getPosY()); + setDistanceTo(inUnit->getDistanceTo()); + setState(inUnit->getState()); + setRadius(inUnit->getRadius()); + setArmor(inUnit->getArmor()); +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_defenseunit.h b/engines/scumm/he/moonbase/ai_defenseunit.h new file mode 100644 index 0000000000..a6085da859 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_defenseunit.h @@ -0,0 +1,195 @@ +/* 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 SCUMM_HE_MOONBASE_AI_DEFENCEUNIT_H +#define SCUMM_HE_MOONBASE_AI_DEFENCEUNIT_H + +namespace Scumm { + +class AI; + +enum { + DUT_ANTI_AIR = 1, + DUT_SHIELD = 2, + DUT_MINE = 3, + DUT_HUB = 4, + DUT_TOWER = 5, + DUT_BRIDGE = 6, + DUT_ENERGY = 7, + DUT_OFFENSE = 8, + DUT_CRAWLER = 9 +}; + +enum { + DUS_ON = 1, + DUS_OFF = 2, + DUS_DESTROYED = 3 +}; + +class DefenseUnit { +private: + int _id; + Common::Point _pos; + int _distanceTo; + int _state; + int _radius; + int _armor; + int _cost; + +protected: + AI *_ai; + +public: + DefenseUnit(AI *ai); + DefenseUnit(DefenseUnit *inUnit, AI *ai); + + virtual ~DefenseUnit(); + + void setID(int id) { _id = id; } + void setDistanceTo(int distanceTo) { _distanceTo = distanceTo; } + void setState(int state) { _state = state; } + void setRadius(int radius) { _radius = radius; } + void setArmor(int armor) { _armor = armor; } + void setDamage(int damage) { _armor -= damage; } + void setPos(int x, int y) { + _pos.x = x; + _pos.y = y; + } + void setCost(int cost) { _cost = cost; } + + int getID() const { return _id; } + int getDistanceTo() const { return _distanceTo; } + int getState() const { return _state; } + int getRadius() const { return _radius; } + int getArmor() const { return _armor; } + int getPosX() const { return _pos.x; } + int getPosY() const { return _pos.y; } + int getCost() const { return _cost; } + + virtual int getType() const = 0; + + virtual Common::Point *createTargetPos(int index, int distance, int weaponType, int sourceX, int sourceY) = 0; + virtual int selectWeapon(int index) = 0; +}; + +class AntiAirUnit : public DefenseUnit { +private: + +public: + 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); +}; + +class ShieldUnit : public DefenseUnit { +private: + +public: + 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); +}; + +class MineUnit : public DefenseUnit { +private: + +public: + 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); +}; + +class HubUnit : public DefenseUnit { +private: + +public: + 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); +}; + +class TowerUnit : public DefenseUnit { +private: + +public: + 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); +}; + +class BridgeUnit : public DefenseUnit { +private: + +public: + 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); +}; + +class EnergyUnit : public DefenseUnit { +private: + +public: + 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); +}; + +class OffenseUnit : public DefenseUnit { +private: + +public: + 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); +}; + +class CrawlerUnit : public DefenseUnit { +private: + +public: + 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); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_main.cpp b/engines/scumm/he/moonbase/ai_main.cpp new file mode 100644 index 0000000000..9c9ff8bb01 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_main.cpp @@ -0,0 +1,3067 @@ +/* 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 "scumm/he/intern_he.h" + +#include "scumm/he/moonbase/moonbase.h" +#include "scumm/he/moonbase/ai_main.h" +#include "scumm/he/moonbase/ai_traveller.h" +#include "scumm/he/moonbase/ai_targetacquisition.h" +#include "scumm/he/moonbase/ai_types.h" +#include "scumm/he/moonbase/ai_pattern.h" + +namespace Scumm { + +enum { + F_GET_SCUMM_DATA = 0, + F_GET_WORLD_DIST = 1, + F_GET_WORLD_ANGLE = 2, + F_GET_TERRAIN_TYPE = 3, + F_GET_CLOSEST_UNIT = 4, + F_SIMULATE_BUILDING_LAUNCH = 5, + F_GET_POWER_ANGLE_FROM_POINT = 6, + F_CHECK_IF_WATER_STATE = 7, + F_GET_UNITS_WITHIN_RADIUS = 8, + F_GET_LANDING_POINT = 9, + F_GET_ENEMY_UNITS_VISIBLE = 10, + F_CHECK_IF_WATER_SQUARE = 11, + F_GET_GROUND_ALTITUDE = 12, + F_CHECK_FOR_CORD_OVERLAP = 13, + F_CHECK_FOR_ANGLE_OVERLAP = 14, + F_ESTIMATE_NEXT_ROUND_ENERGY = 15, + F_CHECK_FOR_UNIT_OVERLAP = 16, + F_GET_COORDINATE_VISIBILITY = 17, + F_CHECK_FOR_ENERGY_SQUARE = 18, + F_AI_CHAT = 19 +}; + +enum { + D_GET_HUB_X = 1, + D_GET_HUB_Y = 2, + D_GET_WORLD_X_SIZE = 3, + D_GET_WORLD_Y_SIZE = 4, + D_GET_CURRENT_PLAYER = 5, + D_GET_MAX_POWER = 6, + D_GET_MIN_POWER = 7, + D_GET_TERRAIN_SQUARE_SIZE = 8, + D_GET_BUILDING_OWNER = 9, + D_GET_BUILDING_STATE = 10, + D_GET_BUILDING_TYPE = 11, + D_DEBUG_BREAK = 12, + D_GET_ENERGY_POOLS_ARRAY = 13, + D_GET_COORDINATE_VISIBILITY = 14, + D_GET_UNIT_VISIBILITY = 15, + D_GET_ENERGY_POOL_VISIBILITY = 16, + D_GET_NUMBER_OF_POOLS = 17, + D_GET_NUMBER_OF_PLAYERS = 18, + D_GET_BUILDING_ARMOR = 19, + D_GET_BUILDING_WORTH = 20, + D_GET_PLAYER_ENERGY = 21, + D_GET_PLAYER_MAX_TIME = 22, + D_GET_WIND_X_SPEED = 23, + D_GET_WIND_Y_SPEED = 24, + D_GET_TOTAL_WIND_SPEED = 25, + D_GET_WIND_X_SPEED_MAX = 26, + D_GET_WIND_Y_SPEED_MAX = 27, + D_GET_BIG_X_SIZE = 28, + D_GET_BIG_Y_SIZE = 29, + D_GET_ENERGY_POOL_WIDTH = 30, + D_GET_BUILDING_MAX_ARMOR = 31, + D_GET_TIMER_VALUE = 32, + D_GET_LAST_ATTACKED_X = 33, + D_GET_LAST_ATTACKED_Y = 34, + D_PRINT_DEBUG_TIMER = 35, + D_GET_PLAYER_TEAM = 36, + D_GET_BUILDING_TEAM = 37, + D_GET_FOW = 38, + D_GET_ANIM_SPEED = 39, + D_GET_BUILDING_STACK_PTR = 40, + D_GET_TURN_COUNTER = 41 +}; + +enum { + AI_TYPE_PLAYER_NUM = 0, + AI_TYPE_TYPE = 1 +}; + +enum { + ENERGY_MODE = 0, + OFFENSE_MODE = 1, + DEFENSE_MODE = 2 +}; + +enum { + LAUNCH_SOURCE_HUB = 0, + LAUNCH_UNIT = 1, + LAUNCH_ANGLE = 2, + LAUNCH_POWER = 3, + + MAX_LAUNCH_POWER = 560, + MAX_FIRING_DISTANCE = 560 +}; + +enum { + SCALE_X = 50, + SCALE_Y = 50, + SCALE_Z = 50, + + GRAVITY_CONSTANT = (MAX_LAUNCH_POWER *MAX_LAUNCH_POWER) / MAX_FIRING_DISTANCE, + + HEIGHT_LOW = 20, + + NODE_RADIUS = 7, + NODE_DIAMETER = NODE_RADIUS * 2 + 2, + NODE_DETECT_RADIUS = NODE_RADIUS - 1, + + BUILDING_HUB_RADIUS = 16 +}; + +enum { + STATE_CHOOSE_BEHAVIOR = 0, + STATE_CHOOSE_TARGET = 1, + STATE_ATTEMPT_SEARCH = 2, + STATE_INIT_APPROACH_TARGET = 3, + STATE_APPROACH_TARGET = 4, + STATE_INIT_ACQUIRE_TARGET = 5, + STATE_ACQUIRE_TARGET = 6, + STATE_ENERGIZE_TARGET = 7, + STATE_OFFEND_TARGET = 8, + STATE_DEFEND_TARGET = 9, + STATE_LAUNCH = 10, + STATE_CRAWLER_DECISION = 11, + + TREE_DEPTH = 2 +}; + +AI::AI(ScummEngine_v100he *vm) : _vm(vm) { + memset(_aiType, 0, sizeof(_aiType)); + _aiState = STATE_CHOOSE_BEHAVIOR; + _behavior = 2; + _energyHogType = 0; + + memset(_moveList, 0, sizeof(_moveList)); + _mcpParams = 0; +} + +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; + } + + _aiType[i] = new AIEntity(BRUTAKAS); + } + + for (int i = 1; i != 5; i++) { + if (_moveList[i]) { + delete _moveList[i]; + _moveList[i] = NULL; + } + + _moveList[i] = new patternList; + } +} + +void AI::cleanUpAI() { + warning("----------------------> Cleaning Up AI"); + + for (int i = 1; i != 5; i++) { + 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; + } + } +} + +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]); + + if (params[AI_TYPE_TYPE] == ENERGY_HOG) { + _energyHogType = 1; + } else { + _energyHogType = 0; + } + + warning("AI for player %d is %s", params[AI_TYPE_PLAYER_NUM], _aiType[params[AI_TYPE_PLAYER_NUM]]->getNameString()); +} + +int AI::masterControlProgram(const int paramCount, const int32 *params) { + static Tree *myTree; + + static int index; + + _mcpParams = params; + + static int lastSource[5]; + static int lastAngle[5]; + static int lastPower[5]; + + + static int sourceHub; + static int target; + + static int targetX; + static int targetY; + + static int _acquireTarget = 0; + + static int *launchAction = NULL; + static int *currentLaunchAction = NULL; + + static int OLflag = 0; + static int TAflag = 0; + + + Node *retNode; + static int retNodeFlag; + + // Memory cleanup in case of quit during game + if (_vm->readVar(_vm->VAR_U32_USER_VAR_F)) { + if (myTree != NULL) { + delete myTree; + myTree = NULL; + } + + if (launchAction != NULL) { + delete launchAction; + launchAction = NULL; + } + + if (currentLaunchAction != NULL) { + delete currentLaunchAction; + currentLaunchAction = NULL; + } + + return 1; + } + + int currentPlayer = getCurrentPlayer(); + + int maxTime = getPlayerMaxTime(); + int timerValue = getTimerValue(3); + + // If timer has run out + if ((_aiState > STATE_CHOOSE_BEHAVIOR) && ((maxTime) && (timerValue > maxTime))) { + if (myTree != NULL) { + delete myTree; + myTree = NULL; + } + + if (launchAction != NULL) { + delete launchAction; + launchAction = NULL; + } + + launchAction = new int[4]; + + if (currentLaunchAction != NULL) { + launchAction[LAUNCH_SOURCE_HUB] = currentLaunchAction[LAUNCH_SOURCE_HUB]; + launchAction[LAUNCH_UNIT] = currentLaunchAction[LAUNCH_UNIT]; + launchAction[LAUNCH_ANGLE] = currentLaunchAction[LAUNCH_ANGLE]; + launchAction[LAUNCH_POWER] = currentLaunchAction[LAUNCH_POWER]; + delete currentLaunchAction; + currentLaunchAction = NULL; + } else { + if (!_vm->_rnd.getRandomNumber(1)) + launchAction[1] = ITEM_TIME_EXPIRED; + else + launchAction[1] = SKIP_TURN; + } + + _aiState = STATE_LAUNCH; + } + + static int old_aiState = 0; + + if (old_aiState != _aiState) { + warning("<<%d>>", _aiState); + old_aiState = _aiState; + } + + switch (_aiState) { + case STATE_CHOOSE_BEHAVIOR: + _behavior = chooseBehavior(); + warning("Behavior mode: %d", _behavior); + + if ((int)_vm->_rnd.getRandomNumber(99) < _aiType[getCurrentPlayer()]->getBehaviorVariation() * AI_VAR_BASE_BEHAVIOR + 1) { + if (_vm->_rnd.getRandomNumber(1)) { + _behavior--; + + if (_behavior < ENERGY_MODE) + _behavior = DEFENSE_MODE; + } else { + _behavior++; + + if (_behavior > DEFENSE_MODE) + _behavior = ENERGY_MODE; + } + + warning("Alternative behavior: %d", _behavior); + } + + if (_behavior == ENERGY_MODE) + if (!getNumberOfPools()) + _behavior = OFFENSE_MODE; + + if (_aiType[getCurrentPlayer()]->getID() == CRAWLER_CHUCKER) + _behavior = OFFENSE_MODE; + + if (launchAction != NULL) { + delete launchAction; + launchAction = NULL; + } + + index = 0; + _aiState = STATE_CHOOSE_TARGET; + break; + + case STATE_CHOOSE_TARGET: + target = chooseTarget(_behavior); + + if (!target) + target = chooseTarget(OFFENSE_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); + } else { + targetX = getHubX(target); + targetY = getHubY(target); + } + + warning("Target (%d, %d) id: %d", targetX, targetY, target); + + if (getFOW()) + _aiState = STATE_ATTEMPT_SEARCH; + else + _aiState = STATE_INIT_APPROACH_TARGET; + + break; + + case STATE_ATTEMPT_SEARCH: + if (!getCoordinateVisibility(targetX, targetY, currentPlayer)) { + int closestHub = getClosestUnit(targetX, targetY, getMaxX(), currentPlayer, 1, BUILDING_MAIN_BASE, 1, 0); + int targetAngle = calcAngle(getHubX(closestHub), getHubY(closestHub), targetX, targetY); + int testX = static_cast<int>(getHubX(closestHub) + (500 * cos(degToRad(targetAngle))) + getMaxX()) % getMaxX(); + int testY = static_cast<int>(getHubY(closestHub) + (500 * sin(degToRad(targetAngle))) + getMaxY()) % getMaxY(); + + int balloonFlag = 0; + + int unitsArray = getUnitsWithinRadius(testX, testY, 500); + int unitsCounter = 0; + + //see if any balloons are already in the area + int nextBuilding = _vm->_moonbase->readFromArray(unitsArray, 0, unitsCounter); + + while (nextBuilding) { + if (((getBuildingType(nextBuilding) == BUILDING_BALLOON) || (getBuildingType(nextBuilding) == BUILDING_TOWER)) && (getBuildingTeam(nextBuilding) == getPlayerTeam(currentPlayer))) { + balloonFlag = 1; + nextBuilding = 0; + continue; + } + + unitsCounter++; + nextBuilding = _vm->_moonbase->readFromArray(unitsArray, 0, unitsCounter); + } + + _vm->_moonbase->deallocateArray(unitsArray); + + if (!balloonFlag) { + launchAction = new int[4]; + launchAction[LAUNCH_SOURCE_HUB] = closestHub; + + if (getPlayerEnergy() > 3) { + launchAction[LAUNCH_UNIT] = ITEM_BALLOON; + launchAction[LAUNCH_POWER] = getMaxPower(); + } else { + launchAction[LAUNCH_UNIT] = ITEM_TOWER; + launchAction[LAUNCH_POWER] = getMinPower(); + } + + launchAction[LAUNCH_ANGLE] = targetAngle + (_vm->_rnd.getRandomNumber(89) - 45); + + int newTargetPos = abs(fakeSimulateWeaponLaunch(getHubX(closestHub), getHubY(closestHub), launchAction[LAUNCH_POWER], launchAction[LAUNCH_ANGLE])); + targetX = newTargetPos % getMaxX(); + targetY = newTargetPos / getMaxY(); + + _aiState = STATE_INIT_ACQUIRE_TARGET; + break; + } + } + + _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; + break; + } + } + + // get closest hub...if attack mode and almost close enough, maybe throw an offense + if ((_behavior == OFFENSE_MODE) && (getPlayerEnergy() > 6)) { + if (!_vm->_rnd.getRandomNumber(2)) { + int closestHub = getClosestUnit(targetX, targetY, getMaxX(), currentPlayer, 1, BUILDING_MAIN_BASE, 1); + + int dist = getDistance(targetX, targetY, getHubX(closestHub), getHubY(closestHub)); + + if ((dist > 470) && (dist < 900)) { + int closestOL = getClosestUnit(targetX, targetY, 900, currentPlayer, 1, BUILDING_OFFENSIVE_LAUNCHER, 0); + + if (!closestOL) { + // Launch an OL + OLflag = 1; + targetX = getHubX(closestHub); + targetY = getHubY(closestHub); + + _aiState = STATE_DEFEND_TARGET; + break; + } + } + } + } + + 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; + break; + } + } + + myTree = initApproachTarget(targetX, targetY, &retNode); + + // If no need to approach, apply appropriate behavior + if (retNode == myTree->getBaseNode()) { + switch (_behavior) { + case 0: + _aiState = STATE_ENERGIZE_TARGET; + break; + + case 1: + _aiState = STATE_OFFEND_TARGET; + break; + + case 2: + _aiState = STATE_DEFEND_TARGET; + break; + + case -1: + _aiState = STATE_LAUNCH; + break; + } + + delete myTree; + myTree = NULL; + break; + } + + delete retNode; + retNode = NULL; + + if (getPlayerEnergy() < 7) { + if (!_vm->_rnd.getRandomNumber(3)) { + _behavior = DEFENSE_MODE; + _aiState = STATE_CHOOSE_TARGET; + } else { + if (launchAction == NULL) { + launchAction = new int[4]; + } + + if (!_vm->_rnd.getRandomNumber(2)) { + launchAction[1] = ITEM_TIME_EXPIRED; + } else { + launchAction[1] = SKIP_TURN; + } + + _aiState = STATE_LAUNCH; + } + + delete myTree; + myTree = NULL; + break; + } + + _aiState = STATE_CRAWLER_DECISION; + break; + + // If behavior is offense, possibly just chuck a crawler + case STATE_CRAWLER_DECISION: + { + // Brace just here to scope throwCrawler + int throwCrawler = 0; + + if (_behavior == OFFENSE_MODE) { + if (getPlayerEnergy() > 6) { + int crawlerTest = _vm->_rnd.getRandomNumber(9); + + if (!crawlerTest) + throwCrawler = 1; + } + } + + if (_aiType[getCurrentPlayer()]->getID() == CRAWLER_CHUCKER) { + if (getPlayerEnergy() > 6) { + throwCrawler = 1; + } else { + launchAction = new int[4]; + + if (!_vm->_rnd.getRandomNumber(1)) + launchAction[1] = ITEM_TIME_EXPIRED; + else + launchAction[1] = SKIP_TURN; + + _aiState = STATE_LAUNCH; + delete myTree; + myTree = NULL; + } + } + + if (throwCrawler) { + sourceHub = getClosestUnit(targetX, targetY, getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1); + int powAngle = getPowerAngleFromPoint(getHubX(sourceHub), getHubY(sourceHub), targetX, targetY, 15); + powAngle = abs(powAngle); + int power = powAngle / 360; + int angle = powAngle - (power * 360); + + launchAction = new int[4]; + launchAction[0] = sourceHub; + launchAction[1] = ITEM_CRAWLER; + warning("CRAWLER DECISION is launching a crawler"); + launchAction[2] = angle; + launchAction[3] = power; + retNodeFlag = 0; + + // Need to update target so acquire can work + int targetCoords = fakeSimulateWeaponLaunch(getHubX(launchAction[LAUNCH_SOURCE_HUB]), getHubY(launchAction[LAUNCH_SOURCE_HUB]), launchAction[LAUNCH_POWER], launchAction[LAUNCH_ANGLE]); + targetX = targetCoords % getMaxX(); + targetY = targetCoords / getMaxX(); + targetX = (targetX + getMaxX()) % getMaxX(); + targetY = (targetY + getMaxY()) % getMaxY(); + + _aiState = STATE_INIT_ACQUIRE_TARGET; + delete myTree; + myTree = NULL; + } else { + _aiState = STATE_APPROACH_TARGET; + } + break; + } + + // ApproachTarget returns NULL if target is already reachable + case STATE_APPROACH_TARGET: + { + int x, y; + Node *currentNode = NULL; + launchAction = approachTarget(myTree, x, y, ¤tNode); + } + + if (launchAction != NULL) { + if (launchAction[0] == -1) { + warning("Creating fake target approach hub"); + TAflag = 1; + int closestHub = getClosestUnit(targetX, targetY, getMaxX(), currentPlayer, 1, BUILDING_MAIN_BASE, 1); + targetX = getHubX(closestHub); + targetY = getHubY(closestHub); + + delete launchAction; + launchAction = NULL; + _aiState = STATE_DEFEND_TARGET; + delete myTree; + myTree = NULL; + break; + } + + // Need to update target so acquire can work + int targetCoords = fakeSimulateWeaponLaunch(getHubX(launchAction[LAUNCH_SOURCE_HUB]), getHubY(launchAction[LAUNCH_SOURCE_HUB]), launchAction[LAUNCH_POWER], launchAction[LAUNCH_ANGLE]); + targetX = targetCoords % getMaxX(); + targetY = targetCoords / getMaxX(); + targetX = (targetX + getMaxX()) % getMaxX(); + targetY = (targetY + getMaxY()) % getMaxY(); + + _aiState = STATE_INIT_ACQUIRE_TARGET; + _behavior = -1; + + delete myTree; + myTree = NULL; + } + + break; + + case STATE_ENERGIZE_TARGET: + launchAction = energizeTarget(targetX, targetY, index); + + if (launchAction != NULL) { + if (launchAction[0]) { + assert(launchAction[0] > 0); + + if (launchAction[1] == ITEM_HUB) { + index = 0; + retNodeFlag = 0; + _aiState = STATE_INIT_ACQUIRE_TARGET; + } else { + index = 0; + _aiState = STATE_INIT_ACQUIRE_TARGET; + } + } else { + index++; + delete launchAction; + launchAction = NULL; + } + } else { + _behavior = DEFENSE_MODE; + retNodeFlag = 0; + index = 0; + _aiState = STATE_CHOOSE_TARGET; + } + break; + + case STATE_OFFEND_TARGET: + launchAction = offendTarget(targetX, targetY, index); + + if (launchAction != NULL) { + if (launchAction[0]) { + retNodeFlag = 0; + _aiState = STATE_INIT_ACQUIRE_TARGET; + } else { + index++; + delete launchAction; + launchAction = NULL; + } + } + break; + + case STATE_DEFEND_TARGET: + launchAction = defendTarget(targetX, targetY, index); + + if (launchAction != NULL) { + if (launchAction[0]) { + retNodeFlag = 0; + _aiState = STATE_INIT_ACQUIRE_TARGET; + + if (launchAction[LAUNCH_UNIT] != ITEM_BRIDGE) { + if (OLflag) { + OLflag = 0; + launchAction[LAUNCH_UNIT] = ITEM_OFFENSE; + } + + if (TAflag) { + TAflag = 0; + warning("replacing defense unit %d with a hub", launchAction[LAUNCH_UNIT]); + launchAction[LAUNCH_UNIT] = ITEM_HUB; + } + } + } else { + index++; + delete launchAction; + launchAction = NULL; + } + } + break; + + case STATE_INIT_ACQUIRE_TARGET: + myTree = initAcquireTarget(targetX, targetY, &retNode); + + if (myTree == NULL) { + _aiState = STATE_LAUNCH; + break; + } + + // This next line is a questionable fix + if (retNode == myTree->getBaseNode()) + retNodeFlag = 1; + + _acquireTarget = 0; + + _aiState = STATE_ACQUIRE_TARGET; + break; + + case STATE_ACQUIRE_TARGET: { + // Here to scope tempLaunchAction + int *tempLaunchAction = NULL; + + int errCod = 0; + + _acquireTarget++; + + if (!retNodeFlag) { + tempLaunchAction = acquireTarget(targetX, targetY, myTree, errCod); + } else { + warning("NOT acquiring target!!!!!!!"); + _acquireTarget = 101; + } + + if (tempLaunchAction != NULL) { + if (launchAction != NULL) { + delete launchAction; + launchAction = NULL; + } + + launchAction = tempLaunchAction; + } + + // If no hubs are available for launching...turn must be skipped + if (launchAction != NULL) { + if (launchAction[LAUNCH_SOURCE_HUB] == 0) { + launchAction[LAUNCH_UNIT] = SKIP_TURN; + } + } + + if ((tempLaunchAction != NULL) || (errCod == 1) || (_acquireTarget > 100)) { + if (tempLaunchAction == NULL) { + warning("\nABORTING acquire target!!!!!"); + } + + assert(launchAction != NULL); + delete myTree; + myTree = NULL; + _aiState = STATE_LAUNCH; + } + } + break; + + default: + break; + } + + 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)) { + if (getPlayerEnergy() > 2) { + launchAction[LAUNCH_UNIT] = ITEM_GUIDED; + } else { + launchAction[LAUNCH_UNIT] = ITEM_BOMB; + } + } + + if ((lastSource[currentPlayer] == launchAction[LAUNCH_SOURCE_HUB]) && (lastAngle[currentPlayer] == launchAction[LAUNCH_ANGLE]) && (lastPower[currentPlayer] == launchAction[LAUNCH_POWER])) { + randomAttenuation -= .2f; + randomAttenuation = MAX(randomAttenuation, 0.0f); + warning("Attenuating..."); + } else { + randomAttenuation = 1; + } + + lastSource[currentPlayer] = launchAction[LAUNCH_SOURCE_HUB]; + lastAngle[currentPlayer] = launchAction[LAUNCH_ANGLE]; + lastPower[currentPlayer] = launchAction[LAUNCH_POWER]; + + _vm->writeVar(_vm->VAR_U32_USER_VAR_A, launchAction[LAUNCH_SOURCE_HUB]); + int energy = getPlayerEnergy(); + warning("Computer's energy: %d", energy); + + // Check if there's enough energy to launch this item + int n = (launchAction[1] / 6); + int energyRequired = (1 + n * n + n); + + if (((energy - energyRequired) < 0) || (!launchAction[LAUNCH_SOURCE_HUB])) { + _vm->writeVar(_vm->VAR_U32_USER_VAR_B, SKIP_TURN); + } else { + assert((launchAction[LAUNCH_UNIT] >= 0) && (launchAction[LAUNCH_UNIT] <= 18)); + + if ((launchAction[LAUNCH_UNIT] < 0) || (launchAction[LAUNCH_UNIT] > 18)) launchAction[LAUNCH_UNIT] = 0; + + _vm->writeVar(_vm->VAR_U32_USER_VAR_B, launchAction[LAUNCH_UNIT]); + } + + if (launchAction[LAUNCH_UNIT] == ITEM_BOMB) { + if (energy > 2) { + if (!_vm->_rnd.getRandomNumber(4)) { + launchAction[LAUNCH_UNIT] = ITEM_GUIDED; + } + } + } + + { + // ANGLE setting + 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; + + int safeAngle = 0; + int lu = launchAction[LAUNCH_UNIT]; + + if ((lu == ITEM_ANTIAIR) || (lu == ITEM_TOWER) || (lu == ITEM_ENERGY) || (lu == ITEM_SHIELD) || (lu == ITEM_OFFENSE) || (lu == ITEM_HUB)) { + for (int i = 1; i < 90; i++) { + assert((launchAction[LAUNCH_ANGLE] < 1000) && (angleAdjustment < 360)); + + if (checkForAngleOverlap(launchAction[LAUNCH_SOURCE_HUB], launchAction[LAUNCH_ANGLE] + angleAdjustment)) { + angleAdjustment += (i % 2 ? i : -i); + } else { + safeAngle = 1; + i = 90; + } + } + } else { + safeAngle = 1; + } + + if (!safeAngle) angleAdjustment = 0; + + warning("Angle adjustment = %d", angleAdjustment); + _vm->writeVar(_vm->VAR_U32_USER_VAR_C, launchAction[LAUNCH_ANGLE] + angleAdjustment); + } + + { + // POWER setting + int powerRangeFactor = (getMaxPower() - getMinPower()) / 100; + 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; + + warning("Power adjustment = %d", powerAdjustment); + int newPower = MIN(getMaxPower(), launchAction[LAUNCH_POWER] + powerAdjustment); + newPower = MAX(getMinPower(), launchAction[LAUNCH_POWER] + powerAdjustment); + _vm->writeVar(_vm->VAR_U32_USER_VAR_D, newPower); + + assert(_vm->readVar(_vm->VAR_U32_USER_VAR_B) != -1); + + if (launchAction[LAUNCH_UNIT] != SKIP_TURN) { + if ((launchAction[LAUNCH_SOURCE_HUB] > 0) && (launchAction[LAUNCH_SOURCE_HUB] <= 500)) { + if (getBuildingState(launchAction[LAUNCH_SOURCE_HUB]) != 0) { + _vm->writeVar(_vm->VAR_U32_USER_VAR_B, SKIP_TURN); + } + } else { + _vm->writeVar(_vm->VAR_U32_USER_VAR_B, SKIP_TURN); + } + } + + if ((launchAction[LAUNCH_UNIT] == SKIP_TURN) || (launchAction[LAUNCH_POWER] == 0)) { + _vm->writeVar(_vm->VAR_U32_USER_VAR_D, -1); + } + } + + + if ((launchAction[LAUNCH_SOURCE_HUB] > 0) && (launchAction[LAUNCH_SOURCE_HUB] <= 500)) { + int nearbyOpponents = getUnitsWithinRadius(getHubX(launchAction[LAUNCH_SOURCE_HUB]), getHubY(launchAction[LAUNCH_SOURCE_HUB]), 180); + int opponentCounter = 0; + int opponentBuilding = _vm->_moonbase->readFromArray(nearbyOpponents, 0, opponentCounter); + int defenseOn = 0; + int defenseOff = 0; + + while (opponentBuilding) { + if (getBuildingOwner(opponentBuilding)) { + if ((getBuildingType(opponentBuilding) == BUILDING_ANTI_AIR) && (getBuildingTeam(opponentBuilding) != getPlayerTeam(currentPlayer))) { + if (getBuildingState(opponentBuilding) == 0) { + defenseOn = 1; + } else { + defenseOff = 1; + } + } + } + + opponentCounter++; + opponentBuilding = _vm->_moonbase->readFromArray(nearbyOpponents, 0, opponentCounter); + } + + _vm->_moonbase->deallocateArray(nearbyOpponents); + + if (defenseOn && defenseOff) { + if (!_vm->_rnd.getRandomNumber(1)) { + _vm->writeVar(_vm->VAR_U32_USER_VAR_B, ITEM_TIME_EXPIRED); + } else { + _vm->writeVar(_vm->VAR_U32_USER_VAR_B, SKIP_TURN); + } + } else { + if (defenseOn) { + _vm->writeVar(_vm->VAR_U32_USER_VAR_B, ITEM_CLUSTER); + int temp = _vm->_rnd.getRandomNumber(2); + int tempPower = 0; + + switch (temp) { + case 0: + tempPower = getMinPower(); + break; + + case 1: + tempPower = getMaxPower(); + break; + + default: + tempPower = launchAction[LAUNCH_POWER]; + } + + _vm->writeVar(_vm->VAR_U32_USER_VAR_D, tempPower); + } + } + } + + delete[] launchAction; + launchAction = NULL; + + _aiState = STATE_CHOOSE_BEHAVIOR; + + int rSh, rU, rP, rA = 0; + rSh = _vm->readVar(_vm->VAR_U32_USER_VAR_A); + rU = _vm->readVar(_vm->VAR_U32_USER_VAR_B); + rP = _vm->readVar(_vm->VAR_U32_USER_VAR_D); + rA = _vm->readVar(_vm->VAR_U32_USER_VAR_C); + + warning("su: %d unit: %d power: %d angle: %d", rSh, rU, rP, rA); + + { + // Checking for patterns + 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); + + if (!_vm->_rnd.getRandomNumber(9)) + patternFound = 0; + + if (patternFound) { + warning("------------------------------------------>Eliminating pattern"); + + if (_vm->_rnd.getRandomNumber(1)) { + _behavior--; + + if (_behavior < ENERGY_MODE) + _behavior = DEFENSE_MODE; + } else { + _behavior++; + + if (_behavior > DEFENSE_MODE) + _behavior = ENERGY_MODE; + } + + if (_behavior == ENERGY_MODE) + if (!getNumberOfPools()) + _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; + } + } + + if ((rSh > 0) && (rSh < 501)) { + if ((rU == ITEM_ANTIAIR) || (rU == ITEM_TOWER) || (rU == ITEM_ENERGY) || (rU == ITEM_SHIELD) || (rU == ITEM_OFFENSE) || (rU == ITEM_HUB)) { + int tryCount = 0; + + while (checkForAngleOverlap(rSh, rA) && tryCount < 25) { + rA = _vm->_rnd.getRandomNumber(359); + tryCount++; + } + + if (tryCount < 25) { + _vm->writeVar(_vm->VAR_U32_USER_VAR_C, rA); + } else { + _vm->writeVar(_vm->VAR_U32_USER_VAR_B, SKIP_TURN); + } + } + } + } else { + _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); + } + + // Sending behavior to SCUMM side for profiling + int selectedUnit = _vm->readVar(_vm->VAR_U32_USER_VAR_B); + + if (selectedUnit) { + if (selectedUnit > 0) + _vm->writeVar(_vm->VAR_U32_USER_VAR_E, _behavior); + else + _vm->writeVar(_vm->VAR_U32_USER_VAR_E, -999); + } + + return 1; +} + +int AI::chooseBehavior() { + static int dominantMode = 0; + + if (getBuildingStackPtr() < 5) + return OFFENSE_MODE; + + int currentPlayer = getCurrentPlayer(); + + int AIpersonality = _aiType[currentPlayer]->getID(); + + switch (AIpersonality) { + case BRUTAKAS: + dominantMode = OFFENSE_MODE; + break; + + case AGI: + dominantMode = DEFENSE_MODE; + break; + + case EL_GATO: + dominantMode = ENERGY_MODE; + break; + + case PIXELAHT: + dominantMode = DEFENSE_MODE; + break; + + case CYBALL: + dominantMode = ENERGY_MODE; + break; + + case NEEP: + dominantMode = DEFENSE_MODE; + break; + + case WARCUPINE: + dominantMode = ENERGY_MODE; + break; + + case AONE: + dominantMode = DEFENSE_MODE; + break; + + case SPANDO: + dominantMode = ENERGY_MODE; + break; + + case ORBNU_LUNATEK: + dominantMode = ENERGY_MODE; + break; + + case CRAWLER_CHUCKER: + dominantMode = OFFENSE_MODE; + break; + + case ENERGY_HOG: + dominantMode = ENERGY_MODE; + { + int breakFlag = 0; + + for (int i = 500; i > 0; i--) + if ((getBuildingOwner(i) == currentPlayer) && (getBuildingType(i) == BUILDING_ENERGY_COLLECTOR)) + breakFlag = 1; + + if (!breakFlag) + return ENERGY_MODE; + } + break; + + case RANGER: + dominantMode = OFFENSE_MODE; + + //random chance of defense if really early in game, otherwise offense + if (_vm->_rnd.getRandomNumber(1) || getTurnCounter() > 4) + return OFFENSE_MODE; + + return DEFENSE_MODE; + break; + + default: //BRUTAKAS + dominantMode = OFFENSE_MODE; + break; + } + + // get a list of all the visible enemy units + int eneCon = 0; + int offCon = 0; + int defCon = 0; + + // energy mode + { + warning("Starting Energy Behavior Selection"); + int minEnergy = 8; + int maxEnergy = 14; + + if (dominantMode == ENERGY_MODE) { + eneCon = 3; + maxEnergy = 21; + } else { + eneCon = 5; + } + + + // loop through energy pool array + int energyPoolScummArray = getEnergyPoolsArray(); + int numPools = getNumberOfPools(); + + // Prevent energy from being chosen if not enough energy to create + int energyAmount = getPlayerEnergy(); + + if ((energyAmount < 7)) + numPools = 0; + + for (int i = 1; i <= numPools; i++) { + int poolX = _vm->_moonbase->readFromArray(energyPoolScummArray, i, ENERGY_POOL_X); + int poolY = _vm->_moonbase->readFromArray(energyPoolScummArray, i, ENERGY_POOL_Y); + + int radius = energyPoolSize(i) / 2; + int poolMaxCount = getMaxCollectors(i); + + int energyCount = 0; + int energyUnits = getUnitsWithinRadius(poolX, poolY, radius + 30); + int energyCounter = 0; + int energyBuilding = _vm->_moonbase->readFromArray(energyUnits, 0, energyCounter); + + + while (energyBuilding) { + energyCounter++; + + if ((getBuildingType(energyBuilding) == BUILDING_ENERGY_COLLECTOR) && (getBuildingOwner(energyBuilding) != currentPlayer)) + energyCount = poolMaxCount; + + if ((getBuildingType(energyBuilding) == BUILDING_ENERGY_COLLECTOR) && (getBuildingOwner(energyBuilding) == currentPlayer)) + energyCount++; + + energyBuilding = _vm->_moonbase->readFromArray(energyUnits, 0, energyCounter); + } + + _vm->_moonbase->deallocateArray(energyUnits); + + if (energyCount < poolMaxCount) { + int closestHub = getClosestUnit(poolX, poolY, 300, currentPlayer, 1, BUILDING_MAIN_BASE, 1); + + if (closestHub) { + eneCon = MIN(1, eneCon); + } else { + int secondClosestHub = getClosestUnit(poolX, poolY, 480, currentPlayer, 1, BUILDING_MAIN_BASE, 1); + + if (secondClosestHub) + eneCon = MIN(2, eneCon); + else + eneCon = MIN(3, eneCon); + } + } + } + + int totalEnergy = estimateNextRoundEnergy(currentPlayer); + + if (totalEnergy < minEnergy) + eneCon--; + + if (totalEnergy > maxEnergy) + eneCon += 2; + + if ((totalEnergy > 34) || (!numPools)) + eneCon = 10; + + if (dominantMode == ENERGY_MODE) + eneCon--; + } + + + // offense mode + { + warning("Starting Offense Behavior Selection"); + + if (dominantMode == OFFENSE_MODE) offCon = 3; + else offCon = 5; + + int enemyArray = getEnemyUnitsVisible(currentPlayer); + int enemyX = 0; + int enemyY = 0; + int hubX = 0; + int hubY = 0; + + int nearEnemyHub = 0; + + // cycle through the array of buildings + for (int i = 0; i < 500; i++) { + if (int thisBuilding = _vm->_moonbase->readFromArray(enemyArray, i, 0)) { + enemyX = getHubX(thisBuilding); + enemyY = getHubY(thisBuilding); + int closestHub = getClosestUnit(enemyX, enemyY, 970, currentPlayer, 1, BUILDING_MAIN_BASE, 1); + int closestOL = getClosestUnit(enemyX, enemyY, 970, currentPlayer, 1, BUILDING_MAIN_BASE, 1); + int dist = 0; + + if (closestOL) { + hubX = getHubX(closestOL); + hubY = getHubY(closestOL); + nearEnemyHub = 1; + } + + if (closestHub) { + hubX = getHubX(closestHub); + hubY = getHubY(closestHub); + dist = getDistance(hubX, hubY, enemyX, enemyY); + + if (dist < 480) + nearEnemyHub = 1; + } + + + if (closestHub || closestOL) { + int numDefenders = 0; + int defArray = getUnitsWithinRadius(enemyX, enemyY, 170); + int defCounter = 0; + int defenseBuilding = _vm->_moonbase->readFromArray(defArray, 0, defCounter); + + while (defenseBuilding) { + defCounter++; + + if (((getBuildingType(defenseBuilding) == BUILDING_ANTI_AIR) || + (getBuildingType(defenseBuilding) == BUILDING_SHIELD)) && + (getBuildingOwner(defenseBuilding) != currentPlayer)) { + if (getBuildingState(defenseBuilding) == 0) + numDefenders++; + } + + defenseBuilding = _vm->_moonbase->readFromArray(defArray, 0, defCounter); + } + + _vm->_moonbase->deallocateArray(defArray); + + if (!numDefenders) { + int defArray2 = getUnitsWithinRadius(hubX, hubY, 170); + defCounter = 0; + int defenseBuilding2 = _vm->_moonbase->readFromArray(defArray2, 0, defCounter); + + while (defenseBuilding2) { + defCounter++; + + if (((getBuildingType(defenseBuilding2) == BUILDING_ANTI_AIR) || + (getBuildingType(defenseBuilding2) == BUILDING_SHIELD)) && + (getBuildingOwner(defenseBuilding2) != currentPlayer)) + if (getBuildingState(defenseBuilding2) == 0) + numDefenders++; + + defenseBuilding2 = _vm->_moonbase->readFromArray(defArray, 0, defCounter); + } + + _vm->_moonbase->deallocateArray(defArray2); + + } + + if ((!numDefenders) && (nearEnemyHub)) { + if (thisBuilding > 495) + offCon = 1 + _vm->_rnd.getRandomNumber(1); + else + offCon = MIN(offCon, 2); + } else { + if (thisBuilding > 495) { + if (nearEnemyHub) { + offCon = MIN(offCon, 2); + break; + } else { + offCon = MIN(offCon, 3); + break; + } + } else { + offCon = MIN(offCon, 4); + } + } + } + + if (getBuildingType(thisBuilding) == BUILDING_CRAWLER) { + if (getClosestUnit(enemyX, enemyY, 350, currentPlayer, 1, 0, 0)) { + int closestHub1 = getClosestUnit(enemyX, enemyY, 460, currentPlayer, 1, BUILDING_MAIN_BASE, 1); + int closestMine = getClosestUnit(enemyX, enemyY, 80, 0, 0, BUILDING_EXPLOSIVE_MINE, 1); + + if (closestHub1 && !closestMine) + offCon = -1; + } + } + } else { + break; + } + } + + _vm->_moonbase->deallocateArray(enemyArray); + offCon--; + } + + // defense mode + { + warning("Starting Defense Behavior Selection"); + + if (dominantMode == DEFENSE_MODE) + defCon = 3; + else + defCon = 5; + + int numDefenders = 0; + int openFlag = 0; + + int mainHubX = getHubX(0); + int mainHubY = getHubY(0); + int mainHub = getClosestUnit(mainHubX + 10, mainHubY, 20, currentPlayer, 1, BUILDING_MAIN_BASE, 0); + + int damageFlag = 0; + + // cycle through the array of buildings + for (int i = 500; i >= 1; i--) { + if ((i < 497) && (defCon < 3)) + break; + + if (getBuildingOwner(i) == currentPlayer) { + int type = getBuildingType(i); + int hubX = getHubX(i); + int hubY = getHubY(i); + + if (type == BUILDING_MAIN_BASE || type == BUILDING_ENERGY_COLLECTOR || type == BUILDING_OFFENSIVE_LAUNCHER) { + int nearEnemy = 0; + int closeBuildingsArray = getUnitsWithinRadius(hubX, hubY, 480); + int closeBuildingCounter = 0; + int closeBuilding = _vm->_moonbase->readFromArray(closeBuildingsArray, 0, closeBuildingCounter); + + while (closeBuilding) { + closeBuildingCounter++; + + if ((getBuildingOwner(closeBuilding) != currentPlayer) && (getBuildingType(closeBuilding) == BUILDING_MAIN_BASE)) { + nearEnemy = 1; + break; + } + + closeBuilding = _vm->_moonbase->readFromArray(closeBuildingsArray, 0, closeBuildingCounter); + } + + _vm->_moonbase->deallocateArray(closeBuildingsArray); + + int defCounter = 0; + int defArray = getUnitsWithinRadius(hubX, hubY, 170); + int defenseBuilding = _vm->_moonbase->readFromArray(defArray, 0, defCounter); + numDefenders = 0; + + while (defenseBuilding) { + defCounter++; + + if (((getBuildingType(defenseBuilding) == BUILDING_ANTI_AIR) || (getBuildingType(defenseBuilding) == BUILDING_SHIELD)) && (getBuildingOwner(defenseBuilding) == currentPlayer)) { + //main base has enemies near, but is defended + if (getBuildingState(defenseBuilding) == 0) + numDefenders++; + } + + defenseBuilding = _vm->_moonbase->readFromArray(defArray, 0, defCounter); + } + + _vm->_moonbase->deallocateArray(defArray); + + if (numDefenders > 2) + defCon++; + + if (numDefenders < 2) + if (dominantMode == DEFENSE_MODE) + openFlag = 1; + + if (!numDefenders) { + if (nearEnemy) { + if (i == mainHub) { + defCon = 1; + break; + } else { + defCon = MIN(defCon, 2); + } + } else { + if (i == mainHub) + defCon = MIN(defCon, 3); + else + defCon = MIN(defCon, 4); + } + } + + if (getBuildingArmor(i) < getBuildingMaxArmor(i)) + damageFlag = 1; + } + } + } + + if (damageFlag && (defCon > 1)) + defCon--; + + if (!openFlag && defCon == 3) + defCon += 2; + } + + warning("%s-------------------------------> Energy: %d Offense: %d Defense: %d", _aiType[currentPlayer]->getNameString(), eneCon, offCon, defCon); + + if (dominantMode == DEFENSE_MODE) + if ((defCon <= offCon) && (defCon <= eneCon)) + return DEFENSE_MODE; + + if (dominantMode == OFFENSE_MODE) + if ((offCon <= eneCon) && (offCon <= defCon)) + return OFFENSE_MODE; + + if (dominantMode == ENERGY_MODE) + if ((eneCon <= offCon) && (eneCon <= defCon)) + return ENERGY_MODE; + + if ((defCon <= offCon) && (defCon <= eneCon)) + return DEFENSE_MODE; + + if ((offCon <= eneCon) && (offCon <= defCon)) + return OFFENSE_MODE; + + if ((eneCon <= offCon) && (eneCon <= defCon)) + return ENERGY_MODE; + + return -1; +} + +int AI::chooseTarget(int behavior) { + int numPools = getNumberOfPools(); + int currentPlayer = getCurrentPlayer(); + + int selection = 0; + int selectionValues[50] = {0}; + int selectionDist = 10000000; + + if (behavior == ENERGY_MODE) { + // loop through energy pool array + int energyPoolScummArray = getEnergyPoolsArray(); + + for (int i = 1; i <= numPools; i++) { + // check # units on pool + int numPoolSpots = _vm->_moonbase->readFromArray(energyPoolScummArray, i, ENERGY_POOL_UNITS_ON); + + if (numPoolSpots == 0) { + // put this one in the middle + warning("Empty pool #%d", i); + selectionValues[i] = 2; + } else { + // get units w/in radius of pool + int poolUnitsArray = getUnitsWithinRadius(_vm->_moonbase->readFromArray(energyPoolScummArray, i, ENERGY_POOL_X), _vm->_moonbase->readFromArray(energyPoolScummArray, i, ENERGY_POOL_Y), 50); + int enemyPool = 0; + int j = 1; + int thisPoolUnit = _vm->_moonbase->readFromArray(poolUnitsArray, 0, j); + + while (thisPoolUnit) { + if (getBuildingOwner(thisPoolUnit) != currentPlayer) + enemyPool = 1; + + j++; + thisPoolUnit = _vm->_moonbase->readFromArray(poolUnitsArray, 0, j); + } + + _vm->_moonbase->deallocateArray(poolUnitsArray); + + // if enemy collector, put at bottom + if (enemyPool) { + selectionValues[i] = 1; + } else if (numPoolSpots < getMaxCollectors(i)) { + selectionValues[i] = 3; + } else { + // this pool is filled + selectionValues[i] = 0; + } + } + + if (selectionValues[i] > selectionValues[selection]) { + selection = i; + } else if (selectionValues[i] == selectionValues[selection]) { + int poolX = _vm->_moonbase->readFromArray(energyPoolScummArray, i, ENERGY_POOL_X); + int poolY = _vm->_moonbase->readFromArray(energyPoolScummArray, i, ENERGY_POOL_Y); + + int closestHub = getClosestUnit(poolX, poolY, getMaxX(), currentPlayer, 1, BUILDING_MAIN_BASE, 1, 100); + int thisDist = getDistance(poolX, poolY, getHubX(closestHub), getHubY(closestHub)); + + if (thisDist < selectionDist) { + selection = i; + selectionDist = thisDist; + } + } + + } + + warning("Pool selected: %d dist: %d", selection, selectionDist); + return selection; + } + + if (behavior == OFFENSE_MODE) { + int returnBuilding = 0; + int attackableArray[500]; + int nearAttackableArray[500]; + int attackableIndex = 0; + int nearAttackableIndex = 0; + + int enemyArray = getEnemyUnitsVisible(currentPlayer); + + for (int i = 500; i >= 1; i--) { + int thisBuilding = _vm->_moonbase->readFromArray(enemyArray, i - 1, 0); + + if (thisBuilding) { + if (getBuildingType(thisBuilding) == BUILDING_CRAWLER) { + 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->_moonbase->deallocateArray(enemyArray); + return returnBuilding; + } + } else { + continue; + } + } + + int enemyX = getHubX(thisBuilding); + int enemyY = getHubY(thisBuilding); + int closestHub = getClosestUnit(enemyX, enemyY, 930, currentPlayer, 1, BUILDING_MAIN_BASE, 1); + + int dist = getDistance(enemyX, enemyY, getHubX(closestHub), getHubY(closestHub)); + + if (getBuildingType(thisBuilding) != BUILDING_BALLOON) { + if (dist < 470) { + attackableArray[attackableIndex] = thisBuilding; + attackableIndex++; + } else { + nearAttackableArray[nearAttackableIndex] = thisBuilding; + nearAttackableIndex++; + } + } + } + } + + _vm->_moonbase->deallocateArray(enemyArray); + + if (attackableIndex) { + int thisWorth = 0; + int savedWorth = 1; + int closestSavedShield = 0; + int closestSavedAntiAir = 0; + + for (int i = 0; i < attackableIndex; i++) { + thisWorth = getBuildingWorth(attackableArray[i]); + + if (thisWorth == savedWorth) { + int closestShield = getClosestUnit(getHubX(attackableArray[i]), getHubY(attackableArray[i]), 180, currentPlayer, 0, BUILDING_SHIELD, 1); + int closestAntiAir = getClosestUnit(getHubX(attackableArray[i]), getHubY(attackableArray[i]), 180, currentPlayer, 0, BUILDING_ANTI_AIR, 1); + + if (closestSavedShield > closestShield) { + savedWorth = thisWorth; + closestSavedShield = closestShield; + closestSavedAntiAir = closestAntiAir; + returnBuilding = attackableArray[i]; + } else { + if (closestSavedAntiAir > closestAntiAir) { + savedWorth = thisWorth; + closestSavedShield = closestShield; + closestSavedAntiAir = closestAntiAir; + returnBuilding = attackableArray[i]; + } + } + } + + if (thisWorth > savedWorth) { + savedWorth = thisWorth; + returnBuilding = attackableArray[i]; + } + } + } else { + if (nearAttackableIndex) { + int thisWorth = 0; + int savedWorth = 1; + int closestSavedShield = 0; + int closestSavedAntiAir = 0; + + for (int i = 0; i < nearAttackableIndex; i++) { + thisWorth = getBuildingWorth(nearAttackableArray[i]); + + if (thisWorth == savedWorth) { + int closestShield = getClosestUnit(getHubX(nearAttackableArray[i]), getHubY(nearAttackableArray[i]), 180, currentPlayer, 0, BUILDING_SHIELD, 1); + int closestAntiAir = getClosestUnit(getHubX(nearAttackableArray[i]), getHubY(nearAttackableArray[i]), 180, currentPlayer, 0, BUILDING_ANTI_AIR, 1); + + if (closestSavedShield > closestShield) { + savedWorth = thisWorth; + closestSavedShield = closestShield; + closestSavedAntiAir = closestAntiAir; + returnBuilding = nearAttackableArray[i]; + } else { + if (closestSavedAntiAir > closestAntiAir) { + savedWorth = thisWorth; + closestSavedShield = closestShield; + closestSavedAntiAir = closestAntiAir; + returnBuilding = nearAttackableArray[i]; + } + } + } + + if (thisWorth > savedWorth) { + savedWorth = thisWorth; + returnBuilding = nearAttackableArray[i]; + } + } + } + } + + if (!returnBuilding) { + for (int i = 500; i > 496; i--) { + if (getBuildingOwner(i)) { + if (getBuildingTeam(i) != getPlayerTeam(currentPlayer)) { + returnBuilding = i; + i = 0; + } + } + } + } + + warning("Attack target: %d", returnBuilding); + + assert(returnBuilding); + return returnBuilding; + } + + if (behavior == DEFENSE_MODE) { + int returnBuilding = 0; + + int savedTally = 0; + int savedDamage; + float savedNumDefenses = 0; + int savedWorth = 0; + + float numDefenses = 0; + int tally = 0; + int attackable = 0; + int attacked = 0; + int damage = 0; + + int type = 0; + int worth = 0; + + + int attackedX = 0; + int attackedY = 0; + int attackedUnit = 0; + + if (getLastAttacked(attackedX, attackedY)) { + (void)getClosestUnit(attackedX + 10, attackedY, 50, currentPlayer, 1, 0, 0); // Unused? + } + + // loop through own units + for (int i = 1; i <= 500; i++) { + numDefenses = 0; + attackable = 0; + attacked = 0; + damage = 0; + type = 0; + worth = 0; + + int owner = getBuildingOwner(i); + + if (owner == currentPlayer) { + type = getBuildingType(i); + + // if current unit in [hub, offense, energy, tower] + if ((type == BUILDING_MAIN_BASE) || (type == BUILDING_ENERGY_COLLECTOR) || (type == BUILDING_OFFENSIVE_LAUNCHER) || (type == BUILDING_TOWER)) { + worth = getBuildingWorth(i); + + // Calculate current defenses + int x = getHubX(i); + int y = getHubY(i); + assert(x >= 0); + assert(y >= 0); + + int defenseArray = getUnitsWithinRadius(x, y, 180); + int j = 0; + // cycle through the defense units close to the target building + int defenseBuilding = _vm->_moonbase->readFromArray(defenseArray, 0, j); + + // loop on all defenses w/in 180 + while (defenseBuilding != 0) { + int defenseType = getBuildingType(defenseBuilding); + + // sub for each + if ((defenseType == BUILDING_ANTI_AIR) || (defenseType == BUILDING_SHIELD)) { + if (getBuildingState(defenseBuilding) == 0) + numDefenses += 1; + else + numDefenses += .5; + } + + j++; + defenseBuilding = _vm->_moonbase->readFromArray(defenseArray, 0, j); + } + + _vm->_moonbase->deallocateArray(defenseArray); + + // Calculate if this unit is attackable + // get dist to nearest enemy hub, offense + int closestHub = getClosestUnit(x, y, getMaxX(), getCurrentPlayer(), 0, BUILDING_MAIN_BASE, 0); + int numStridesToHub = getDistance(getHubX(closestHub), getHubY(closestHub), x, y) / 480; + closestHub = getClosestUnit(x, y, getMaxX(), getCurrentPlayer(), 0, BUILDING_OFFENSIVE_LAUNCHER, 0); + int numStridesToOL = getDistance(getHubX(closestHub), getHubY(closestHub), x, y) / 800; + + // sub for each stride away + if (!numStridesToOL || !numStridesToHub) + attackable = 1; + + // Check if this unit was just attacked + if (attackedUnit == i) + attacked = 1; + + if (!numDefenses) { + tally = 1; + + if (attackable) { + tally = 4; + + if (attacked) { + tally = 5; + } + } + } else { + if (attackable) { + tally = 2; + + if (attacked) { + tally = 3; + } + } + } + + // Check if this unit is damaged + int saveFlag = 0; + + if (tally > savedTally) { + saveFlag = 1; + } else { + if (tally == savedTally) { + if (worth > savedWorth) { + saveFlag = 1; + + if (numDefenses > savedNumDefenses) { + saveFlag = 0; + } + } + + if (damage > savedDamage) { + saveFlag = 1; + + if (numDefenses > savedNumDefenses) { + saveFlag = 0; + } + } + + if (numDefenses < savedNumDefenses) { + saveFlag = 1; + } + + if (numDefenses >= 3) { + saveFlag = 0; + } + } + } + + if (saveFlag) { + savedTally = tally; + savedWorth = worth; + savedDamage = damage; + savedNumDefenses = numDefenses; + returnBuilding = i; + } + } + } + } + + return returnBuilding; + } + + return 0; +} + +Tree *AI::initApproachTarget(int targetX, int targetY, Node **retNode) { + int sourceHub = 0; + + 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), this); + myTraveller->setSourceHub(sourceHub); + + //target adjustment so that room is allowed for the appropriate shot + int tempAngle = calcAngle(getHubX(sourceHub), getHubY(sourceHub), targetX, targetY); + int adjX = -120 * cos(degToRad(tempAngle)); + int adjY = -120 * sin(degToRad(tempAngle)); + + Traveller::setTargetPosX(targetX + adjX); + Traveller::setTargetPosY(targetY + adjY); + Traveller::setMaxDist(340); + + Tree *myTree = new Tree(myTraveller, TREE_DEPTH, this); + *retNode = myTree->aStarSearch_singlePassInit(); + + return myTree; +} + +int *AI::approachTarget(Tree *myTree, int &xTarget, int &yTarget, Node **currentNode) { + int *retVal = NULL; + + *currentNode = NULL; + Node *retNode = myTree->aStarSearch_singlePass(); + + if (*currentNode != NULL) + warning("########################################### Got a possible solution"); + + if (myTree->IsBaseNode(retNode)) { + warning("########################################### Returning Base Node"); + retVal = new int[4]; + retVal[0] = -1; + return retVal; + } + + if (retNode == NULL) { + return retVal; + } else { + retVal = new int[4]; + + Traveller *retTraveller = reinterpret_cast<Traveller *>(retNode->getFirstStep()->getContainedObject()); + + // set launching hub, item to launch, angle of launch, power of launch + // if water flag is set, launch bridge instead of hub + retVal[0] = static_cast<Traveller *>(myTree->getBaseNode()->getContainedObject())->getSourceHub(); + + if (retTraveller->getWaterFlag()) { + int powAngle = getPowerAngleFromPoint(retTraveller->getWaterSourceX(), + retTraveller->getWaterSourceY(), + retTraveller->getWaterDestX(), + retTraveller->getWaterDestY(), + 15); + + powAngle = abs(powAngle); + int power = powAngle / 360; + int angle = powAngle - (power * 360); + + retVal[0] = getClosestUnit(retTraveller->getWaterSourceX() + 10, retTraveller->getWaterSourceY(), getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 0); + + retVal[1] = ITEM_BRIDGE; + retVal[2] = angle; + retVal[3] = power; + + warning("Target Bridge Coords: <%d, %d> ", retTraveller->getWaterDestX(), retTraveller->getWaterDestY()); + } else { + retVal[1] = ITEM_HUB; + retVal[2] = retTraveller->getAngleTo(); + retVal[3] = retTraveller->getPowerTo(); + } + + + int whoseTurn = getCurrentPlayer(); + + 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()); + + int temp = static_cast<int>(retTraveller->calcT()); + int temp2 = static_cast<int>(retTraveller->getValueG()); + int x = static_cast<int>(retTraveller->getPosX()); + int y = static_cast<int>(retTraveller->getPosY()); + warning("Target Coords: <%d, %d> G-value: %d T-value: %d", x, y, temp2, temp); + xTarget = x; + yTarget = y; + } + + return retVal; +} + +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(this); + myBaseTarget->setValueG(0); + + myBaseTarget->setUnitType(ITEM_BOMB); + myBaseTarget->setShotPos(-1, -1); + + int unitsArray = getUnitsWithinRadius(targetX + 7, targetY, 211); + + warning("Target Coords: <%d, %d> Source Coords: <%d, %d>", targetX, targetY, getHubX(sourceHub) , getHubY(sourceHub)); + + myBaseTarget->setEnemyDefenses(unitsArray, targetX, targetY); + + int thisElement = _vm->_moonbase->readFromArray(unitsArray, 0, 0); + + _vm->_moonbase->deallocateArray(unitsArray); + + if (!thisElement) { + delete myBaseTarget; + return NULL; + } + + Tree *myTree = new Tree(myBaseTarget, 4, this); + *retNode = myTree->aStarSearch_singlePassInit(); + + return myTree; +} + +int *AI::acquireTarget(int targetX, int targetY, Tree *myTree, int &errorCode) { + int currentPlayer = getCurrentPlayer(); + int *retVal = NULL; + + Node *retNode = myTree->aStarSearch_singlePass(); + + if (myTree->IsBaseNode(retNode)) + return acquireTarget(targetX, targetY); + + if (retNode == NULL) { + errorCode = 0; + return retVal; + } + + Sortie *thisSortie = static_cast<Sortie *>(retNode->getFirstStep()->getContainedObject()); + int unitToShoot = thisSortie->getUnitType(); + + if (unitToShoot < 0) { + errorCode = 1; + return retVal; + } + + if (unitToShoot == ITEM_CRAWLER) { + warning("target acquisition is launching a crawler"); + } + + int shotTargetX = thisSortie->getShotPosX(); + int shotTargetY = thisSortie->getShotPosY(); + int theTarget = getClosestUnit(shotTargetX + 5, shotTargetY, getMaxX(), 0, 0, 0, 0, 0); + + + int sourceOL = 0; + int sourceX = thisSortie->getSourcePosX(); + int sourceY = thisSortie->getSourcePosY(); + + int sourceHub = getClosestUnit(sourceX + 5, sourceY, getMaxX(), currentPlayer, 1, BUILDING_MAIN_BASE, 1, 0); + + sourceOL = getClosestUnit(sourceX, sourceY, 900, currentPlayer, 1, BUILDING_OFFENSIVE_LAUNCHER, 1, 110); + + if (sourceOL) { + sourceHub = sourceOL; + sourceX = getHubX(sourceOL); + sourceY = getHubY(sourceOL); + } + + if (!sourceHub) sourceHub = getClosestUnit(sourceX + 5, sourceY, getMaxX(), currentPlayer, 1, BUILDING_MAIN_BASE, 1, 0); + + int powAngle = getPowerAngleFromPoint(sourceX, sourceY, shotTargetX, shotTargetY, 15, sourceOL); + warning("The source (%d: <%d, %d>) The target (%d: <%d, %d>)", sourceHub, sourceX, sourceY, theTarget, shotTargetX, shotTargetY); + + powAngle = abs(powAngle); + int power = powAngle / 360; + int angle = powAngle - (power * 360); + + retVal = new int[4]; + + retVal[0] = sourceHub; + retVal[1] = unitToShoot; + retVal[2] = angle; + retVal[3] = power; + + warning("Unit to shoot: %d", unitToShoot); + + return retVal; +} + +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); + + if (!sourceHub) + sourceHub = getClosestUnit(targetX, targetY, getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 0); + + int directAngle = calcAngle(getHubX(sourceHub), getHubY(sourceHub), targetX, targetY); + int directDistance = getDistance(getHubX(sourceHub), getHubY(sourceHub), targetX, targetY); + + retVal[0] = sourceHub; + retVal[1] = ITEM_OFFENSE; + retVal[2] = directAngle; + retVal[3] = MAX(MIN(getMaxPower() * directDistance / 500, getMaxPower()), getMinPower()); + + return retVal; +} + +int *AI::energizeTarget(int &targetX, int &targetY, int index) { + int n = 10; + static int currentPlayer = 0; + static int pool = 0; + static int radius = 0; + static int poolUnitsArray = 0; + static int j = 0; + static int k = 0; + static int sameUnit = 0; + static int nextUnit = 0; + static int attempt = 0; + + if (!index) { + warning("index is 0!"); + currentPlayer = getCurrentPlayer(); + pool = 0; + + // get the pool that's closest to the target coords + for (int i = 1; i <= getNumberOfPools(); i++) { + int poolX = _vm->_moonbase->readFromArray(getEnergyPoolsArray(), i, ENERGY_POOL_X); + int poolY = _vm->_moonbase->readFromArray(getEnergyPoolsArray(), i, ENERGY_POOL_Y); + + if ((poolX == targetX) && (poolY == targetY)) { + pool = i; + } + } + + // calculate the appropriate radius + radius = energyPoolSize(pool) / 2; + + // create test points + k = 0; + j = 0; + nextUnit = 0; + sameUnit = 0; + attempt = 0; + } + + if (poolUnitsArray) + _vm->_moonbase->deallocateArray(poolUnitsArray); + + poolUnitsArray = getUnitsWithinRadius(targetX, targetY, 450); + assert(poolUnitsArray); + + // 0 is for energy collectors, 1 is for circumnavigating hubs + if (k < 2) { + if (!sameUnit) { + sameUnit = 1; + attempt = 0; + nextUnit = _vm->_moonbase->readFromArray(poolUnitsArray, 0, j); + j++; + } + + if (nextUnit) { + if ((getBuildingType(nextUnit) == BUILDING_MAIN_BASE) && (getBuildingOwner(nextUnit) == currentPlayer)) { + int minAngle = 0; + int hubToPoolAngle = 0; + int testAngle = 0; + int testDist = 0; + static int xPos = 0; + static int yPos = 0; + static int newAttempt = 1; + + if (sameUnit) { + if (k == 0) { + int poolToHubAngle = calcAngle(targetX, targetY, getHubX(nextUnit), getHubY(nextUnit)); + minAngle = poolToHubAngle - 45; + } else { + hubToPoolAngle = calcAngle(getHubX(nextUnit), getHubY(nextUnit), targetX, targetY); + } + } + + if (attempt < n) { + static int power = 0; + static int angle = 0; + + if (newAttempt) { + newAttempt = 0; + + if (k == 0) { + testAngle = (_vm->_rnd.getRandomNumber(89) + minAngle) % 360; + testDist = radius; + + xPos = targetX + testDist * cos(degToRad(testAngle)); + yPos = targetY + testDist * sin(degToRad(testAngle)); + } else { + switch (_vm->_rnd.getRandomNumber(1)) { + case 0: + testAngle = (hubToPoolAngle + (45 + _vm->_rnd.getRandomNumber(19))) % 360; + break; + + default: + testAngle = (hubToPoolAngle + (315 - _vm->_rnd.getRandomNumber(19))) % 360; + break; + } + + testDist = ((((n - attempt) / n) * .5) + .5) * (getDistance(getHubX(nextUnit), getHubY(nextUnit), targetX, targetY) / .8); + xPos = getHubX(nextUnit) + testDist * cos(degToRad(testAngle)); + yPos = getHubY(nextUnit) + testDist * sin(degToRad(testAngle)); + } + + // check if points are good + int powAngle = getPowerAngleFromPoint(getHubX(nextUnit), getHubY(nextUnit), xPos, yPos, 15); + powAngle = abs(powAngle); + + power = powAngle / 360; + angle = powAngle - (power * 360); + } + + int result = 0; + result = simulateBuildingLaunch(getHubX(nextUnit), getHubY(nextUnit), power, angle, 10, 1); + + if (result) { + newAttempt = 1; + + if (result > 0) { + xPos = (xPos + getMaxX()) % getMaxX(); + yPos = (yPos + getMaxY()) % getMaxY(); + + result = 1; + } else { + // Drop a bridge for the cord + int yCoord = -result / getMaxX(); + int xCoord = -result - (yCoord * getMaxX()); + + if (checkIfWaterState(xCoord, yCoord)) { + int terrainSquareSize = getTerrainSquareSize(); + xCoord = ((xCoord / terrainSquareSize * terrainSquareSize) + (terrainSquareSize / 2)); + yCoord = ((yCoord / terrainSquareSize * terrainSquareSize) + (terrainSquareSize / 2)); + + int xDist = xCoord - xPos; + int yDist = yCoord - yPos; + xPos = xCoord + (terrainSquareSize * 1.414 * (xDist / (abs(xDist) + 1))); + yPos = yCoord + (terrainSquareSize * 1.414 * (yDist / (abs(yDist) + 1))); + + nextUnit = getClosestUnit(xPos, yPos, 480, getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 120); + int powAngle = getPowerAngleFromPoint(getHubX(nextUnit), getHubY(nextUnit), xPos, yPos, 15); + + powAngle = abs(powAngle); + power = powAngle / 360; + angle = powAngle - (power * 360); + + int *retVal = new int[4]; + + retVal[0] = nextUnit; + retVal[1] = ITEM_BRIDGE; + retVal[2] = angle; + retVal[3] = power; + + if (nextUnit <= 0) + retVal[0] = 0; + + _vm->_moonbase->deallocateArray(poolUnitsArray); + poolUnitsArray = 0; + return retVal; + } + } + + if (result > 0) { + _vm->_moonbase->deallocateArray(poolUnitsArray); + poolUnitsArray = 0; + + targetX = xPos; + targetY = yPos; + + int *retVal = new int[4]; + + retVal[0] = nextUnit; + + if (k == 0) { + retVal[1] = ITEM_ENERGY; + } else { + retVal[1] = ITEM_HUB; + } + + retVal[2] = angle; + retVal[3] = power; + return retVal; + } + } else { + int *retVal = new int[4]; + retVal[0] = 0; + _vm->_moonbase->deallocateArray(poolUnitsArray); + poolUnitsArray = 0; + + return retVal; + } + + attempt++; + } else { + sameUnit = 0; + } + } else { + sameUnit = 0; + } + } else { + sameUnit = 0; + k++; + j = 0; + } + } else { + _vm->_moonbase->deallocateArray(poolUnitsArray); + poolUnitsArray = 0; + return NULL; + } + + _vm->_moonbase->deallocateArray(poolUnitsArray); + poolUnitsArray = 0; + int *retVal = new int[4]; + retVal[0] = 0; + + return retVal; +} + +int *AI::offendTarget(int &targetX, int &targetY, int index) { + int *retVal = NULL; + + int target = getClosestUnit(targetX + 10, targetY, 20, 0, 0, 0, 0); + + if (!target) + target = getClosestUnit(targetX + 10, targetY, 0, 0, 0, 0, 0); + + warning("The target inside the offendTarget routine is: %d", target); + int type = getBuildingType(target); + int unit = 0; + + DefenseUnit *thisUnit; + + switch (type) { + case BUILDING_OFFENSIVE_LAUNCHER: + thisUnit = new OffenseUnit(this); + break; + + case BUILDING_TOWER: + thisUnit = new TowerUnit(this); + break; + + case BUILDING_MAIN_BASE: + thisUnit = new HubUnit(this); + break; + + case BUILDING_ENERGY_COLLECTOR: + thisUnit = new EnergyUnit(this); + break; + + case BUILDING_CRAWLER: + thisUnit = new CrawlerUnit(this); + break; + + case BUILDING_BRIDGE: + thisUnit = new BridgeUnit(this); + break; + + case BUILDING_SHIELD: + thisUnit = new ShieldUnit(this); + break; + + default: + thisUnit = new HubUnit(this); + break; + } + + thisUnit->setPos(targetX, targetY); + thisUnit->setID(target); + + int sourceHub = getClosestUnit(targetX, targetY, getMaxX(), getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 110); + int sourceOL = 0; + sourceOL = getClosestUnit(targetX, targetY, 900, getCurrentPlayer(), 1, BUILDING_OFFENSIVE_LAUNCHER, 1, 110); + + unit = thisUnit->selectWeapon(_vm->_rnd.getRandomNumber(4)); + + if (sourceOL) { + if ((unit == ITEM_BOMB) || (unit == ITEM_CLUSTER) || (unit == ITEM_GUIDED) || (unit == ITEM_EMP) || (unit == ITEM_SPIKE) || (unit == ITEM_CRAWLER) || (unit == ITEM_VIRUS)) { + sourceHub = sourceOL; + } + } + + if (!sourceHub) { + retVal = new int[4]; + + retVal[1] = SKIP_TURN; + return retVal; + } + + + if ((thisUnit->getType() == BUILDING_CRAWLER) && (unit == SKIP_TURN)) { + retVal = new int[4]; + retVal[1] = unit; + delete thisUnit; + return retVal; + } + + if (unit == ITEM_CRAWLER) { + warning("******** offense is launching a crawler ********"); + warning("The defensive unit is: %d", unit); + } + + Common::Point *targetCoords; + int dist = getDistance(getHubX(sourceHub), getHubY(sourceHub), targetX, targetY); + targetCoords = thisUnit->createTargetPos(0, dist, unit, getHubX(sourceHub), getHubY(sourceHub)); + + int powAngle = getPowerAngleFromPoint(getHubX(sourceHub), getHubY(sourceHub), targetCoords->x, targetCoords->y, 15, sourceOL); + powAngle = abs(powAngle); + int power = powAngle / 360; + int angle = powAngle % 360; + + if (unit == ITEM_MINE) + power -= 30; + + targetX = targetCoords->x; + targetY = targetCoords->y; + + if (targetX < 0) + targetX = (targetX + getMaxX()) % getMaxX(); + + if (targetY < 0) + targetY = (targetY + getMaxY()) % getMaxY(); + + assert(targetX >= 0 && targetY >= 0); + delete targetCoords; + delete thisUnit; + + retVal = new int[4]; + + retVal[0] = sourceHub; + retVal[1] = unit; + retVal[2] = angle; + retVal[3] = power; + + return retVal; +} + +int *AI::defendTarget(int &targetX, int &targetY, int index) { + int *retVal = NULL; + Defender *thisDefender = new Defender(this); + int defStatus = thisDefender->calculateDefenseUnitPosition(targetX, targetY, index); + + if (defStatus > 0) { + targetX = thisDefender->getTargetX(); + targetY = thisDefender->getTargetY(); + retVal = new int[4]; + + retVal[0] = thisDefender->getSourceUnit(); + retVal[1] = thisDefender->getUnit(); + retVal[2] = thisDefender->getAngle(); + retVal[3] = thisDefender->getPower(); + } + + if (defStatus == 0) { + retVal = new int[4]; + retVal[0] = 0; + } + + if (defStatus == -1) { + if (thisDefender->getTargetX() || thisDefender->getTargetY()) { + targetX = thisDefender->getTargetX(); + targetY = thisDefender->getTargetY(); + } + + retVal = new int[4]; + retVal[0] = thisDefender->getSourceUnit(); + retVal[1] = thisDefender->getUnit(); + retVal[2] = thisDefender->getAngle(); + retVal[3] = thisDefender->getPower(); + } + + if (defStatus == -3) { + retVal = new int[4]; + retVal[0] = 0; + retVal[1] = SKIP_TURN; + retVal[2] = 0; + retVal[3] = 0; + } + + assert(targetX >= 0 && targetY >= 0); + + if (retVal[1] == ITEM_CRAWLER) { + warning("defend target is launching a crawler"); + } + + delete thisDefender; + return retVal; +} + +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(_mcpParams[F_GET_CLOSEST_UNIT], 7, x, y, radius, player, alignment, unitType, checkUnitEnabled); + return retVal; +} + +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(_mcpParams[F_GET_CLOSEST_UNIT], 8, x, y, radius, player, alignment, unitType, checkUnitEnabled, minDist); + return retVal; +} + +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 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 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 AI::getTerrain(int x, int y) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_TERRAIN_TYPE], 2, x, y); + return retVal; +} + +int AI::estimateNextRoundEnergy(int player) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_ESTIMATE_NEXT_ROUND_ENERGY], 1, player); + return retVal / 10; +} + +int AI::getHubX(int hub) { + assert(hub >= 0 && hub <= 500); + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_HUB_X, hub); + return retVal; +} + +int AI::getHubY(int hub) { + assert(hub >= 0 && hub <= 500); + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_HUB_Y, hub); + return retVal; +} + +int AI::getMaxX() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_WORLD_X_SIZE); + return retVal; +} + +int AI::getMaxY() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_WORLD_Y_SIZE); + return retVal; +} + +int AI::getCurrentPlayer() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_CURRENT_PLAYER); + assert(retVal != 0); + return retVal; +} + +int AI::getMaxPower() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_MAX_POWER); + return retVal; +} + +int AI::getMinPower() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_MIN_POWER); + return retVal; +} + +int AI::getTerrainSquareSize() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_TERRAIN_SQUARE_SIZE); + return retVal; +} + +int AI::getBuildingOwner(int building) { + assert((building > 0) && (building < 501)); + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_OWNER, building); + return retVal; +} + +int AI::getBuildingState(int building) { + assert((building > 0) && (building < 501)); + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_STATE, building); + return retVal; +} + +int AI::getBuildingType(int building) { + assert((building > 0) && (building < 501)); + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_TYPE, building); + return retVal; +} + +int AI::getBuildingArmor(int building) { + assert((building > 0) && (building < 501)); + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_ARMOR, building); + return retVal; +} + +int AI::getBuildingWorth(int building) { + assert((building > 0) && (building < 501)); + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_WORTH, building); + return retVal; +} + +int AI::getEnergyPoolsArray() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_ENERGY_POOLS_ARRAY); + return retVal; +} + +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 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 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 AI::getNumberOfPools() { + int retVal = 0; + + if (_aiType[getCurrentPlayer()]->getID() == ENERGY_HOG) { + retVal = 1; + } else { + retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_NUMBER_OF_POOLS); + } + + return retVal; +} + +int AI::getNumberOfPlayers() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_NUMBER_OF_PLAYERS); + return retVal; +} + +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 AI::getPlayerMaxTime() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_PLAYER_MAX_TIME); + return retVal; +} + +int AI::getWindXSpeed() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_WIND_X_SPEED); + return retVal; +} + +int AI::getWindYSpeed() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_WIND_Y_SPEED); + return retVal; +} + +int AI::getTotalWindSpeed() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_TOTAL_WIND_SPEED); + return retVal; +} + +int AI::getWindXSpeedMax() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_WIND_X_SPEED_MAX); + return retVal; +} + +int AI::getWindYSpeedMax() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_WIND_Y_SPEED_MAX); + return retVal; +} + +int AI::getBigXSize() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_BIG_X_SIZE); + return retVal; +} + +int AI::getBigYSize() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_BIG_Y_SIZE); + return retVal; +} + +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 AI::getBuildingMaxArmor(int building) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_MAX_ARMOR, building); + return retVal; +} + +int AI::getTimerValue(int timerNum) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_TIMER_VALUE, timerNum); + return retVal; +} + +int AI::getLastAttacked(int &x, int &y) { + int currentPlayer = getCurrentPlayer(); + 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 AI::getPlayerTeam(int player) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_PLAYER_TEAM, player); + return retVal; +} + +int AI::getBuildingTeam(int building) { + assert((building >= 1) && (building <= 500)); + + if (getBuildingOwner(building) == 0) return 0; + + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 2, D_GET_BUILDING_TEAM, building); + return retVal; +} + +int AI::getFOW() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_FOW); + return retVal; +} + +int AI::getAnimSpeed() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_ANIM_SPEED); + return retVal; +} + +int AI::getBuildingStackPtr() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_BUILDING_STACK_PTR); + return retVal; +} + +int AI::getTurnCounter() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_SCUMM_DATA], 1, D_GET_TURN_COUNTER); + return retVal; +} + +int AI::getGroundAltitude(int x, int y) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_GROUND_ALTITUDE], 2, x, y); + return retVal; +} + +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 AI::checkForAngleOverlap(int unit, int angle) { + assert(angle > -721); + assert(angle < 721); + + if (!unit) return 0; + + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_CHECK_FOR_ANGLE_OVERLAP], 2, unit, angle); + return retVal; +} + +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 AI::checkForEnergySquare(int x, int y) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_CHECK_FOR_ENERGY_SQUARE], 2, x, y); + return retVal; +} + +int AI::aiChat() { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_AI_CHAT], 0); + return retVal; +} + +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 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 AI::checkIfWaterState(int x, int y) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_CHECK_IF_WATER_STATE], 2, x, y); + return retVal; +} + +int AI::checkIfWaterSquare(int x, int y) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_CHECK_IF_WATER_SQUARE], 2, x, y); + return retVal; +} + +int AI::getUnitsWithinRadius(int x, int y, int radius) { + assert(x >= 0); + assert(y >= 0); + assert(radius >= 0); + + 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 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 AI::getEnemyUnitsVisible(int playerNum) { + int retVal = _vm->_moonbase->callScummFunction(_mcpParams[F_GET_ENEMY_UNITS_VISIBLE], 1, playerNum); + return retVal; +} + +float AI::degToRad(float degrees) { + return degrees * M_PI / 180.; +} + +void AI::limitLocation(int &a, int &b, int c, int d) { + if (a >= 0) { + a = (a % c); + } else { + a = (c - (abs(a) % c)); + } + + if (b >= 0) { + b = (b % d); + } else { + b = (d - (abs(b) % d)); + } +} + +int AI::energyPoolSize(int pool) { + int width = getEnergyPoolWidth(pool); + + switch (width) { + case 126: + return 115; + + case 116: + return 100; + + case 63: + return 60; + } + + return 0; +} + +int AI::getMaxCollectors(int pool) { + int width = getEnergyPoolWidth(pool); + + switch (width) { + case 126: + return 4; + + case 116: + return 3; + + case 63: + return 2; + } + + return 0; +} + +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; + static int sXLoc = 0; + static int sYLoc = 0; + static int sZLoc = 0; + static int sFrictionCount = 0; + static int sWhichRadius = 0; + static int sWhichUnit = 0; + + int gWindXSpeed = getWindXSpeed(); + int gWindYSpeed = getWindYSpeed(); + int gTotalWindSpeed = getTotalWindSpeed(); + int gWindXSpeedMax = getWindXSpeedMax(); + int gWindYSpeedMax = getWindYSpeedMax(); + int bigXSize = getBigXSize(); + int bigYSize = getBigYSize(); + + int groundAltitude = 0; + int totalSpeed = 0; + int resultingPoint = 0; + int unscaledXLoc = 0; + int unscaledYLoc = 0; + int terrainType = 0; + int passedBeyondUnit = 0; + int currentDist = 0; + + + if (!numSteps) + numSteps = 1; + + if (!sXSpeed && !sYSpeed) { + sZSpeed = (static_cast<int>(.70711 * power)) ; + sXSpeed = (static_cast<int>(cos(degToRad(angle)) * sZSpeed)) ; + sYSpeed = (static_cast<int>(sin(degToRad(angle)) * sZSpeed)) ; + + sZSpeed *= SCALE_Z; + + sZLoc = (getGroundAltitude(x, y) + HEIGHT_LOW + 10) * SCALE_Z; + + sXLoc = x * SCALE_X; + sYLoc = y * SCALE_Y; + + sFrictionCount = 0; + sWhichRadius = NODE_DETECT_RADIUS + 1; + + sWhichUnit = getClosestUnit(x + 10, y, 30, getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 0, 0); + } + + for (int i = 1; i <= numSteps; i++) { + unscaledXLoc = sXLoc / SCALE_X; + unscaledYLoc = sYLoc / SCALE_Y; + + groundAltitude = getGroundAltitude(unscaledXLoc, unscaledYLoc); + groundAltitude *= SCALE_Z; + + sZLoc += sZSpeed / SCALE_Z; + + resultingPoint = MAX(1, unscaledXLoc + unscaledYLoc * getMaxX()); + + if (sZLoc <= groundAltitude) { + terrainType = getTerrain(unscaledXLoc, unscaledYLoc); + + sXSpeed = 0; + sYSpeed = 0; + sFrictionCount = 0; + + if (terrainType == TERRAIN_TYPE_GOOD) + return resultingPoint; + else + return 0 - resultingPoint; + } else { + if (checkIfWaterState(unscaledXLoc, unscaledYLoc)) { + sXSpeed = 0; + sYSpeed = 0; + sFrictionCount = 0; + + return 0 - resultingPoint; + } else { + int cfco = 0; + int cfuo = 0; + int cfes = 0; + int cfao = 0; + cfao = checkForAngleOverlap(sWhichUnit, angle); + + cfco = checkForCordOverlap(unscaledXLoc, unscaledYLoc, sWhichRadius, 1); + cfuo = checkForUnitOverlap(unscaledXLoc, unscaledYLoc, sWhichRadius, sWhichUnit); + + if (!isEnergy) + cfes = checkForEnergySquare(unscaledXLoc, unscaledYLoc); + + if (cfco || cfuo || cfes || cfao) { + sXSpeed = 0; + sYSpeed = 0; + sFrictionCount = 0; + + return 0 - resultingPoint; + } else { + sFrictionCount++; + + if (sFrictionCount == 10) { + sFrictionCount = 0; + + if (!gWindXSpeed) + sXSpeed = sXSpeed * .95; + + if (!gWindYSpeed) + sYSpeed = sYSpeed * .95; + } + + if (passedBeyondUnit) { + totalSpeed = getDistance(0, 0, sXSpeed, sYSpeed); + + if (totalSpeed > gTotalWindSpeed) { + if (gWindXSpeed > 0) { + if (sXSpeed < gWindXSpeedMax) + sXSpeed += gWindXSpeed; + } else { + if (sXSpeed > gWindXSpeedMax) + sXSpeed += gWindXSpeed; + } + + if (gWindYSpeed > 0) { + if (sYSpeed < gWindYSpeedMax) + sYSpeed += gWindYSpeed; + } else { + if (sYSpeed > gWindYSpeedMax) + sYSpeed += gWindYSpeed; + } + } + } else { + currentDist = getDistance(unscaledXLoc, unscaledYLoc, x, y); + + if (currentDist > BUILDING_HUB_RADIUS + NODE_DIAMETER) + passedBeyondUnit = 1; + } + + sXLoc += sXSpeed; + sYLoc += sYSpeed; + + limitLocation(sXLoc, sYLoc, bigXSize, bigYSize); + + sZSpeed -= GRAVITY_CONSTANT; + } + } + } + } + + return 0; +} + +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; + static int sXLoc = 0; + static int sYLoc = 0; + static int sZLoc = 0; + static int sFrictionCount = 0; + + int gWindXSpeed = getWindXSpeed(); + int gWindYSpeed = getWindYSpeed(); + int gTotalWindSpeed = getTotalWindSpeed(); + int gWindXSpeedMax = getWindXSpeedMax(); + int gWindYSpeedMax = getWindYSpeedMax(); + int bigXSize = getBigXSize(); + int bigYSize = getBigYSize(); + + int groundAltitude = 0; + int totalSpeed = 0; + int resultingPoint = 0; + int unscaledXLoc = 0; + int unscaledYLoc = 0; + int terrainType = 0; + int passedBeyondUnit = 0; + int currentDist = 0; + + if (!numSteps) numSteps = 1; + + if (!sXSpeed && !sYSpeed) { + sZSpeed = (static_cast<int>(.70711 * power)) ; + sXSpeed = (static_cast<int>(cos(degToRad(angle)) * sZSpeed)) ; + sYSpeed = (static_cast<int>(sin(degToRad(angle)) * sZSpeed)) ; + + sZSpeed *= SCALE_Z; + + sZLoc = (getGroundAltitude(x, y) + HEIGHT_LOW + 10) * SCALE_Z; + + sXLoc = x * SCALE_X; + sYLoc = y * SCALE_Y; + + sFrictionCount = 0; + } + + for (int i = 1; i <= numSteps; i++) { + unscaledXLoc = sXLoc / SCALE_X; + unscaledYLoc = sYLoc / SCALE_Y; + + groundAltitude = getGroundAltitude(unscaledXLoc, unscaledYLoc); + groundAltitude *= SCALE_Z; + sZLoc += sZSpeed / SCALE_Z; + resultingPoint = MAX(1, unscaledXLoc + unscaledYLoc * getMaxX()); + + if (sZLoc <= groundAltitude) { + terrainType = getTerrain(unscaledXLoc, unscaledYLoc); + + sXSpeed = 0; + sYSpeed = 0; + sFrictionCount = 0; + + if (terrainType == TERRAIN_TYPE_GOOD) + return resultingPoint; + else + return 0 - resultingPoint; + } else { + sFrictionCount++; + + if (sFrictionCount == 10) { + sFrictionCount = 0; + + if (!gWindXSpeed) + sXSpeed = sXSpeed * .95; + + if (!gWindYSpeed) + sYSpeed = sYSpeed * .95; + } + + if (passedBeyondUnit) { + totalSpeed = getDistance(0, 0, sXSpeed, sYSpeed); + + if (totalSpeed > gTotalWindSpeed) { + if (gWindXSpeed > 0) { + if (sXSpeed < gWindXSpeedMax) + sXSpeed += gWindXSpeed; + } else { + if (sXSpeed > gWindXSpeedMax) + sXSpeed += gWindXSpeed; + } + + if (gWindYSpeed > 0) { + if (sYSpeed < gWindYSpeedMax) + sYSpeed += gWindYSpeed; + } else { + if (sYSpeed > gWindYSpeedMax) + sYSpeed += gWindYSpeed; + } + } + } else { + currentDist = getDistance(unscaledXLoc, unscaledYLoc, x, y); + + if (currentDist > BUILDING_HUB_RADIUS + NODE_DIAMETER) + passedBeyondUnit = 1; + } + + sXLoc += sXSpeed; + sYLoc += sYSpeed; + + limitLocation(sXLoc, sYLoc, bigXSize, bigYSize); + + sZSpeed -= GRAVITY_CONSTANT; + } + } + + return 0; +} + +int AI::fakeSimulateWeaponLaunch(int x, int y, int power, int angle) { + int distance = power * 480 / getMaxPower(); + float radAngle = degToRad(angle); + int maxX = getMaxX(); + int maxY = getMaxY(); + + x += distance * cos(radAngle); + y += distance * sin(radAngle); + + x = (x + maxX) % maxX; + y = (y + maxY) % maxY; + + return MAX(1, x + y * maxX); +} + +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 new file mode 100644 index 0000000000..7a38de9458 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_main.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 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_v100he; +class AIEntity; +class patternList; + +enum { + TERRAIN_TYPE_GOOD = 0, + TERRAIN_TYPE_SLOPE = 1, + TERRAIN_TYPE_WATER = 2, + MAX_MEMORY = 3 +}; + +enum { + ITEM_BOMB = 0, + ITEM_CLUSTER = 1, + ITEM_REPAIR = 2, + ITEM_ANTIAIR = 3, + ITEM_BRIDGE = 4, + ITEM_TOWER = 5, + ITEM_GUIDED = 6, + ITEM_EMP = 7, + ITEM_SPIKE = 8, + ITEM_RECLAIMER = 9, + ITEM_BALLOON = 10, + ITEM_MINE = 11, + ITEM_CRAWLER = 12, + ITEM_VIRUS = 13, + ITEM_ENERGY = 14, + ITEM_SHIELD = 15, + ITEM_OFFENSE = 16, + ITEM_HUB = 17, + ITEM_TIME_EXPIRED = 18, + SKIP_TURN = -999 +}; + +enum BuildingTypes { + BUILDING_ENERGY_COLLECTOR = 3, + BUILDING_MAIN_BASE = 4, + BUILDING_BRIDGE = 5, + BUILDING_TOWER = 6, + BUILDING_EXPLOSIVE_MINE = 7, + BUILDING_SHIELD = 8, + BUILDING_ANTI_AIR = 9, + BUILDING_OFFENSIVE_LAUNCHER = 10, + BUILDING_BALLOON = 11, + BUILDING_CRAWLER = 12 +}; + +enum { + ENERGY_POOL_X = 45, + ENERGY_POOL_Y = 46, + ENERGY_POOL_UNITS_ON = 47, + + MIN_DIST = 108 +}; + +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 + +#endif diff --git a/engines/scumm/he/moonbase/ai_node.cpp b/engines/scumm/he/moonbase/ai_node.cpp new file mode 100644 index 0000000000..083a156a31 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_node.cpp @@ -0,0 +1,153 @@ +/* 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 "scumm/he/moonbase/ai_node.h" + +namespace Scumm { + +IContainedObject::IContainedObject(IContainedObject &sourceContainedObject) { + _objID = sourceContainedObject.getObjID(); + _valueG = sourceContainedObject.getG(); +} + +int Node::_nodeCount = 0; + +Node::Node() { + _parent = NULL; + _depth = 0; + _nodeCount++; + _contents = NULL; +} + +Node::Node(Node *sourceNode) { + _parent = NULL; + _children = sourceNode->getChildren(); + + _depth = sourceNode->getDepth(); + + _contents = sourceNode->getContainedObject()->duplicate(); +} + +Node::~Node() { + if (_contents != NULL) { + delete _contents; + _contents = NULL; + } + + _nodeCount--; +} + +int Node::generateChildren() { + int numChildren = _contents->numChildrenToGen(); + + int numChildrenGenerated = numChildren; + int errorCode = -1; + static int i = 0; + + while (i < numChildren) { + Node *tempNode = new Node; + _children.push_back(tempNode); + tempNode->setParent(this); + tempNode->setDepth(_depth + 1); + + int completionFlag; + + IContainedObject *thisContObj = _contents->createChildObj(i, completionFlag); + assert(!(thisContObj != NULL && completionFlag == 0)); + + if (!completionFlag) { + _children.pop_back(); + delete tempNode; + return 0; + } + + i++; + + if (thisContObj != NULL) { + tempNode->setContainedObject(thisContObj); + } else { + _children.pop_back(); + delete tempNode; + numChildrenGenerated--; + } + } + + i = 0; + + if (numChildrenGenerated > 0) + return numChildrenGenerated; + + return errorCode; +} + + +int Node::generateNextChild() { + int numChildren = _contents->numChildrenToGen(); + + static int i = 0; + + Node *tempNode = new Node; + _children.push_back(tempNode); + tempNode->setParent(this); + tempNode->setDepth(_depth + 1); + + int compFlag; + IContainedObject *thisContObj = _contents->createChildObj(i, compFlag); + + if (thisContObj != NULL) { + tempNode->setContainedObject(thisContObj); + } else { + _children.pop_back(); + delete tempNode; + } + + ++i; + + if (i > numChildren) + i = 0; + + return i; +} + +Node *Node::popChild() { + Node *temp; + + temp = _children.back(); + _children.pop_back(); + return temp; +} + +Node *Node::getFirstStep() { + Node *currentNode = this; + + if (currentNode->getParent() == NULL) + return currentNode; + + while (currentNode->getParent()->getParent() != NULL) + currentNode = currentNode->getParent(); + + assert(currentNode->getDepth() == 1); + + return currentNode; +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_node.h b/engines/scumm/he/moonbase/ai_node.h new file mode 100644 index 0000000000..0c3ef2f023 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_node.h @@ -0,0 +1,103 @@ +/* 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 SCUMM_HE_MOONBASE_AI_NODE_H +#define SCUMM_HE_MOONBASE_AI_NODE_H + +#include "common/array.h" + +namespace Scumm { + +const float SUCCESS = -1; +const float FAILURE = 1e20f; + +class IContainedObject { +private: + int _objID; + float _valueG; + +protected: + virtual float getG() const { return _valueG; } + virtual float calcH() { return 0; } + +public: + IContainedObject() { _valueG = 0; _objID = -1; } + IContainedObject(float inG) { _valueG = inG; _objID = -1; } + IContainedObject(IContainedObject &sourceContainedObject); + virtual ~IContainedObject() {} + + virtual IContainedObject *duplicate() = 0; + + void setValueG(float inG) { _valueG = inG; } + float getValueG() { return _valueG; } + + int getObjID() const { return _objID; } + void setObjID(int inputObjID) { _objID = inputObjID; } + + virtual int numChildrenToGen() = 0; + virtual IContainedObject *createChildObj(int index, int &completionFlag) = 0; + + virtual int checkSuccess() = 0; + virtual float calcT() { return getG(); } + + float returnG() const { return getG(); } +}; + +class Node { +private: + Node *_parent; + Common::Array<Node *> _children; + + int _depth; + static int _nodeCount; + + IContainedObject *_contents; + +public: + Node(); + Node(Node *sourceNode); + ~Node(); + + void setParent(Node *parentPtr) { _parent = parentPtr; } + Node *getParent() const { return _parent; } + + void setDepth(int depth) { _depth = depth; } + int getDepth() const { return _depth; } + + static int getNodeCount() { return _nodeCount; } + + void setContainedObject(IContainedObject *value) { _contents = value; } + IContainedObject *getContainedObject() { return _contents; } + + Common::Array<Node *> getChildren() const { return _children; } + int generateChildren(); + int generateNextChild(); + Node *popChild(); + + float getObjectT() { return _contents->calcT(); } + + Node *getFirstStep(); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_pattern.h b/engines/scumm/he/moonbase/ai_pattern.h new file mode 100644 index 0000000000..ae1fa6b55e --- /dev/null +++ b/engines/scumm/he/moonbase/ai_pattern.h @@ -0,0 +1,162 @@ +/* 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 SCUMM_HE_MOONBASE_AI_PATTERN_H +#define SCUMM_HE_MOONBASE_AI_PATTERN_H + +namespace Scumm { + +const int NO_PATTERN = 0; +const int PATTERN_FOUND = 1; + +class patternInstance { +private: + int _sourceHub; + int _unit; + int _power; + int _angle; + +public: + patternInstance() { + _sourceHub = 0; + _unit = 0; + _power = 0; + _angle = 0; + } + + patternInstance(int sh, int unit, int power, int angle) { + setSourceHub(sh); + setUnit(unit); + setPower(power); + setAngle(angle); + } + + void setSourceHub(int sh) { _sourceHub = sh; } + void setUnit(int unit) { _unit = unit; } + + void setPower(int power) { + if (power < 300) + _power = 1; + else if (power < 480) + _power = 2; + else + _power = 3; + } + + void setAngle(int angle) { + int tempAngle = angle % 360; + + if ((tempAngle >= 0) && (tempAngle < 90)) + _angle = 1; + + if ((tempAngle >= 90) && (tempAngle < 180)) + _angle = 2; + + if ((tempAngle >= 180) && (tempAngle < 270)) + _angle = 3; + + if ((tempAngle >= 270)) + _angle = 4; + } + + int getSourceHub() const { return _sourceHub; } + int getUnit() const { return _unit; } + int getPowerIndex() const { return _power; } + int getAngleIndex() const { return _angle; } + + static int comparePatterns(patternInstance *p1, patternInstance *p2) { + if (p1->getSourceHub() != p2->getSourceHub()) + return 0; + + if (p1->getUnit() != p2->getUnit()) + return 0; + + if (p1->getUnit() == -999) + return 0; + + int temp = abs(p1->getPowerIndex() - p2->getPowerIndex()); + + if (temp > 1) + return 0; + + temp = abs(p1->getAngleIndex() - p2->getAngleIndex()); + + if (temp > 1 && temp < 3) + return 0; + + return 1; + } +}; + +class patternList { +private: + patternInstance *theList[10]; + int listIndex; + +public: + patternList() { + for (int i = 0; i < 10; i++) { + theList[i] = new patternInstance(); + } + + listIndex = 0; + } + ~patternList() { + for (int i = 0; i < 10; i++) { + delete theList[i]; + } + } + + void addPattern(int sh, int unit, int power, int angle) { + theList[listIndex]->setSourceHub(sh); + theList[listIndex]->setUnit(unit); + theList[listIndex]->setPower(power); + theList[listIndex]->setAngle(angle); + + listIndex++; + + if (listIndex > 9) + listIndex = 0; + } + + int evaluatePattern(int sh, int unit, int power, int angle) { + patternInstance *patternToMatch = new patternInstance(sh, unit, power, angle); + int matchCount = 0; + + for (int i = 0; i < 9; i++) { + if (patternInstance::comparePatterns(theList[i], patternToMatch)) { + matchCount++; + } + } + + delete patternToMatch; + + if (matchCount > 2) + return PATTERN_FOUND; + + return NO_PATTERN; + } +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_targetacquisition.cpp b/engines/scumm/he/moonbase/ai_targetacquisition.cpp new file mode 100644 index 0000000000..02c49dc1a7 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_targetacquisition.cpp @@ -0,0 +1,557 @@ +/* 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 "scumm/he/intern_he.h" +#include "scumm/he/moonbase/moonbase.h" + +#include "scumm/he/moonbase/ai_targetacquisition.h" +#include "scumm/he/moonbase/ai_main.h" +#include "scumm/he/moonbase/ai_weapon.h" + +namespace Scumm { + +int Sortie::_sSourceX = 0; +int Sortie::_sSourceY = 0; + +int Sortie::_sTargetX = 0; +int Sortie::_sTargetY = 0; + +Sortie::~Sortie() { + for (Common::Array<DefenseUnit *>::iterator k = _enemyDefenses.begin(); k != _enemyDefenses.end(); k++) { + delete *k; + } +} + +void Sortie::setEnemyDefenses(int enemyDefensesScummArray, int defendX, int defendY) { + DefenseUnit *thisUnit; + int currentPlayer = _ai->getCurrentPlayer(); + + for (int i = 0; i < 200; i++) { + int thisElement = _ai->_vm->_moonbase->readFromArray(enemyDefensesScummArray, 0, i); + + if (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(_ai); + break; + + case BUILDING_SHIELD: + thisUnit = new ShieldUnit(_ai); + break; + + case BUILDING_EXPLOSIVE_MINE: + if (_ai->getDistance(_ai->getHubX(thisElement), _ai->getHubY(thisElement), defendX, defendY) < 90) + thisUnit = new MineUnit(_ai); + else + thisUnit = NULL; + + break; + + case BUILDING_CRAWLER: + thisUnit = NULL; + break; + + default: + thisUnit = NULL; + break; + } + + if (thisUnit != NULL) { + thisUnit->setID(thisElement); + thisUnit->setPos(_ai->getHubX(thisElement), _ai->getHubY(thisElement)); + + if (_ai->getBuildingState(thisElement)) thisUnit->setState(DUS_OFF); + + _enemyDefenses.push_back(thisUnit); + } + } + } + } else { + i = 200; + } + } +} + +int *Sortie::getShotPos() const { + int *retVal = new int[2]; + + retVal[0] = _shotPosX; + retVal[1] = _shotPosY; + + return retVal; +} + +int Sortie::numChildrenToGen() { + int retVal = MAX<uint>(_enemyDefenses.size(), 1) * NUM_SHOT_POSITIONS * NUM_WEAPONS; + return retVal; +} + +IContainedObject *Sortie::createChildObj(int index, int &completionFlag) { + float thisDamage; + Sortie *retSortie = new Sortie(_ai); + int activeDefenses = 0; + + Common::Array<DefenseUnit *> thisEnemyDefenses; + + // Copy the defensive unit list from the parent + for (Common::Array<DefenseUnit *>::iterator k = _enemyDefenses.begin(); k != _enemyDefenses.end(); k++) { + DefenseUnit *temp; + + switch ((*k)->getType()) { + case DUT_ANTI_AIR: + temp = new AntiAirUnit(*k, _ai); + break; + + case DUT_SHIELD: + temp = new ShieldUnit(*k, _ai); + break; + + case DUT_MINE: + temp = new MineUnit(*k, _ai); + break; + + case DUT_CRAWLER: + temp = new CrawlerUnit(*k, _ai); + break; + + default: + temp = new ShieldUnit(*k, _ai); + break; + } + + thisEnemyDefenses.push_back(temp); + } + + // Calculate the current target from the index + DefenseUnit *currentTarget = *(thisEnemyDefenses.begin() + static_cast<int>(index / (NUM_WEAPONS * NUM_SHOT_POSITIONS))); + + assert(currentTarget); + + // Pick correct weapon according to index + Weapon *currentWeapon = new Weapon(currentTarget->selectWeapon(index % NUM_WEAPONS)); + retSortie->setUnitType(currentWeapon->getTypeID()); + + // Calculate distance from target to source hub + int distance = _ai->getDistance(currentTarget->getPosX(), currentTarget->getPosY(), getSourcePosX(), getSourcePosY()); + + // Pick correct shot position according to index + Common::Point *targetCoords; + targetCoords = currentTarget->createTargetPos((static_cast<int>(index / NUM_WEAPONS) % NUM_SHOT_POSITIONS), distance, currentWeapon->getTypeID(), getSourcePosX(), getSourcePosY()); + retSortie->setShotPos(targetCoords->x, targetCoords->y); + + // Set the g value based on cost of the weapon + retSortie->setValueG(getG() + currentWeapon->getCost()); + + int AAcounter = 3; + + // 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 = _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)) { + activeDefenses++; + + // Turn off this anti-air and drop the coverage count + if (((*i)->getType() == DUT_ANTI_AIR)) { + (*i)->setState(DUS_OFF); + + if (currentWeapon->getTypeID() == ITEM_CLUSTER) + AAcounter--; + else + AAcounter = 0; + } + + // Essentially disable this weapon choice, due to its impact with a shield, or untriggered anti-air + if (((*i)->getType() == DUT_SHIELD) || !AAcounter) { + retSortie->setValueG(1000); + i = thisEnemyDefenses.end() - 1; + } + } else { + // Turn on any anti-airs that were off the previous turn + if (((*i)->getType() == DUT_ANTI_AIR) && ((*i)->getState() == DUS_OFF)) + (*i)->setState(DUS_ON); + } + } + + // Turn on all the non-anti-air units in preparation for emp's and the next turn + for (Common::Array<DefenseUnit *>::iterator i = thisEnemyDefenses.begin(); i != thisEnemyDefenses.end(); i++) { + if ((*i)->getType() != DUT_ANTI_AIR) { + (*i)->setState(DUS_ON); + } + } + + // If this weapon is still valid + if (retSortie->getValueG() < 1000) { + // Apply emp effects and damage to all units in range of weapon + 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 = _ai->getDistance((*i)->getPosX(), (*i)->getPosY(), currentTarget->getPosX(), currentTarget->getPosY()); + // Normal detonation location used here + else { + distance = _ai->getDistance((*i)->getPosX(), (*i)->getPosY(), targetCoords->x, targetCoords->y); + } + + if (distance < currentWeapon->getRadius()) { + // Apply damage + thisDamage = currentWeapon->getDamage(); + + if ((AAcounter != 3) && (currentWeapon->getTypeID() == ITEM_CLUSTER)) + thisDamage = 0; + + if (!_ai->_vm->_rnd.getRandomNumber(4)) + currentWeapon->setTypeID(ITEM_MINE); + + (*i)->setDamage((int)thisDamage); + + // Apply emp effect + if (currentWeapon->getTypeID() == ITEM_EMP) { + (*i)->setState(DUS_OFF); + } + + // Remove destroyed defenses + if ((*i)->getArmor() <= 0) { + delete *i; + i = thisEnemyDefenses.erase(i); + } else { + i++; + } + } else { + i++; + } + } + } + + retSortie->setEnemyDefenses(thisEnemyDefenses); + + delete targetCoords; + delete currentWeapon; + return retSortie; +} + +float Sortie::calcH() { + float retValue = 0; + Common::Array<DefenseUnit *> thisEnemyDefenses = getEnemyDefenses(); + + for (Common::Array<DefenseUnit *>::iterator i = thisEnemyDefenses.begin(); i != thisEnemyDefenses.end(); i++) { + if ((*i)->getState() == DUS_ON) { + switch ((*i)->getType()) { + case DUT_ANTI_AIR: + retValue += 1; + + case DUT_MINE: + retValue += 1; + break; + + case DUT_SHIELD: + retValue += 1; + break; + } + } + } + + return retValue; +} + +int Sortie::checkSuccess() { + if (!_enemyDefenses.size()) + return SUCCESS; + + int targetX = getTargetPosX(); + int targetY = getTargetPosY(); + + int targetCheck = 0; + + for (Common::Array<DefenseUnit *>::iterator i = _enemyDefenses.begin(); i != _enemyDefenses.end(); i++) { + if (((*i)->getState() == DUS_ON) && ((*i)->getType() != DUT_HUB)) { + return 0; + } + + if (((*i)->getPosX() == targetX) && ((*i)->getPosY() == targetY)) targetCheck = 1; + } + + if (!targetCheck) + return SUCCESS; + + // If shot pos == target pos return SUCCESS; + if ((targetX == getShotPosX()) && (getTargetPosY() == getShotPosY())) { + return SUCCESS; + } + + return 0; +} + +float Sortie::calcT() { + return (checkSuccess() != SUCCESS) ? (getG() + calcH()) : SUCCESS; +} + +IContainedObject *Sortie::duplicate() { + return this; +} + + +void Sortie::printEnemyDefenses() { + for (Common::Array<DefenseUnit *>::iterator i = _enemyDefenses.begin(); i != _enemyDefenses.end(); i++) { + warning("Unit %d - Type: %d, Armor: %d, Status: %d", (*i)->getID(), (*i)->getType(), static_cast<int>((*i)->getArmor()), (*i)->getState()); + } +} + +int Defender::calculateDefenseUnitPosition(int targetX, int targetY, int index) { + int currentPlayer = _ai->getCurrentPlayer(); + + //get list of near hubs + int unitsArray = _ai->getUnitsWithinRadius(targetX + 5, targetY, 480); + + const int NUM_HUBS = 10; + //Order on dist + int hubArray[NUM_HUBS] = { 0 }; + int hubIndex = 0; + + for (int i = 0; i < 200; i++) { + int thisUnit = _ai->_vm->_moonbase->readFromArray(unitsArray, 0, i); + + if (thisUnit) { + 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 = _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]; + hubArray[j] = thisUnit; + hubIndex++; + j = 100; + } + } else { + hubArray[j] = thisUnit; + hubIndex++; + j = 100; + } + } + } + } + + if (hubIndex >= NUM_HUBS) { + hubIndex = NUM_HUBS; + i = 200; + } + } + + _ai->_vm->_moonbase->deallocateArray(unitsArray); + + //Check if repair is needed + int targetUnit = _ai->getClosestUnit(targetX + 5, targetY, 15, currentPlayer, 1, 0, 0, 0); + + if (targetUnit && (targetUnit != BUILDING_CRAWLER) && (_ai->getBuildingTeam(targetUnit) == _ai->getPlayerTeam(currentPlayer))) { + int armor = _ai->getBuildingArmor(targetUnit); + + if (armor < _ai->getBuildingMaxArmor(targetUnit)) { + unitsArray = _ai->getUnitsWithinRadius(targetX + 5, targetY, 170); + int defCount = 0; + + for (int i = 0; i < 200; i++) { + int thisUnit = _ai->_vm->_moonbase->readFromArray(unitsArray, 0, i); + + if (thisUnit) { + if (((_ai->getBuildingType(thisUnit) == BUILDING_SHIELD) || (_ai->getBuildingType(thisUnit) == BUILDING_ANTI_AIR)) && (_ai->getBuildingOwner(thisUnit) == currentPlayer) && (_ai->getBuildingState(thisUnit) == 0)) { + defCount++; + i = 200; + } + } + } + + _ai->_vm->_moonbase->deallocateArray(unitsArray); + + if (defCount) { + //repair + int hubUnit = _ai->getClosestUnit(targetX, targetY, 480, currentPlayer, 1, BUILDING_MAIN_BASE, 1, 110); + + if (hubUnit && (hubUnit != targetUnit)) { + int powAngle = abs(_ai->getPowerAngleFromPoint(_ai->getHubX(hubUnit), _ai->getHubY(hubUnit), targetX, targetY, 20)); + int power = powAngle / 360; + int angle = powAngle - (power * 360); + + setTargetX(targetX); + setTargetY(targetY); + + setSourceUnit(hubUnit); + setUnit(ITEM_REPAIR); + setPower(power); + setAngle(angle); + + return 1; + } + } + } + } + + //For each hub + for (int i = 0; i < MIN(NUM_HUBS, hubIndex); 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 = _ai->getClosestUnit(hubX, hubY, _ai->getMaxX(), currentPlayer, 0, 0, 0); + directAngleToHub = _ai->calcAngle(targetX, targetY, _ai->getHubX(enemyUnit), _ai->getHubY(enemyUnit)); + } else { + 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 + _ai->_vm->_rnd.getRandomNumber(179) - 90; + int randDist = _ai->_vm->_rnd.getRandomNumber(109) + 40; + + int x = (int)(targetX + randDist * cos(_ai->degToRad(randAngle))); + int y = (int)(targetY + randDist * sin(_ai->degToRad(randAngle))); + + int powAngle = _ai->getPowerAngleFromPoint(hubX, hubY, x, y, 20); + + if (powAngle < 0) + continue; + + int power = powAngle / 360; + int angle = powAngle - (power * 360); + + int coords = 0; + coords = _ai->simulateBuildingLaunch(hubX, hubY, power, angle, 100, 0); + + //if valid, return + if (coords > 0) { + //warning("The prospective launching hub for this defensive unit is: %d", hubArray[i]); + + setSourceX(hubX); + setSourceY(hubY); + setTargetX((x + _ai->getMaxX()) % _ai->getMaxX()); + setTargetY((y + _ai->getMaxY()) % _ai->getMaxY()); + setSourceUnit(hubArray[i]); + + int unitsArray2 = _ai->getUnitsWithinRadius(targetX + 5, targetY, 200); + int shieldCount = 0; + + for (int k = 0; k < 200; k++) { + int thisUnit = _ai->_vm->_moonbase->readFromArray(unitsArray2, 0, k); + + if (thisUnit) { + if ((_ai->getBuildingType(thisUnit) == BUILDING_SHIELD) && (_ai->getBuildingOwner(thisUnit) == currentPlayer)) + shieldCount++; + + if ((_ai->getBuildingType(thisUnit) == BUILDING_BRIDGE) && (_ai->getBuildingOwner(thisUnit) == currentPlayer)) { + shieldCount--; + shieldCount = MAX(-1, shieldCount); + } + } + } + + if ((_ai->_vm->_rnd.getRandomNumber((int)pow(3.0f, shieldCount + 1) - 1) == 0) && (_ai->getPlayerEnergy() > 6)) + setUnit(ITEM_SHIELD); + else + setUnit(ITEM_ANTIAIR); + + setPower(power); + setAngle(angle); + + _ai->_vm->_moonbase->deallocateArray(unitsArray2); + return 1; + } + + if (coords < 0) { + //drop a bridge for the cord + int yCoord = -coords / _ai->getMaxX(); + int xCoord = -coords - (yCoord * _ai->getMaxX()); + + 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 = (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 = _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; + angle = powAngle - (power * 360); + + setSourceUnit(nextUnit); + setUnit(ITEM_BRIDGE); + setPower(power); + setAngle(angle); + + return 1; + } + } + } + } + + // Else create new hub + int count = 0; + int coords = 0; + + if (hubIndex == 0) return -3; + + do { + int sourceHub = hubArray[_ai->_vm->_rnd.getRandomNumber(hubIndex - 1)]; + + setSourceX(_ai->getHubX(sourceHub)); + setSourceY(_ai->getHubY(sourceHub)); + setSourceUnit(sourceHub); + setUnit(ITEM_HUB); + setPower(_ai->_vm->_rnd.getRandomNumber(299) + 200); + setAngle(_ai->_vm->_rnd.getRandomNumber(359)); + count++; + + if (count > (NUM_HUBS * 3)) break; + + coords = _ai->simulateBuildingLaunch(getSourceX(), getSourceY(), getPower(), getAngle(), 100, 0); + } while (coords <= 0); + + if (coords > 0) { + setTargetX(coords % _ai->getMaxX()); + setTargetY(coords / _ai->getMaxX()); + } else { + setTargetX(0); + setTargetY(0); + } + + return -1; +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_targetacquisition.h b/engines/scumm/he/moonbase/ai_targetacquisition.h new file mode 100644 index 0000000000..9afe0f50ab --- /dev/null +++ b/engines/scumm/he/moonbase/ai_targetacquisition.h @@ -0,0 +1,152 @@ +/* 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 SCUMM_HE_MOONBASE_AI_TARGETACQUISITION_H +#define SCUMM_HE_MOONBASE_AI_TARGETACQUISITION_H + +#include "scumm/he/moonbase/ai_defenseunit.h" +#include "scumm/he/moonbase/ai_node.h" +#include "scumm/he/moonbase/ai_tree.h" + +namespace Scumm { + +const int NUM_IMPT_UNITS = 3; +const int NUM_SHOT_POSITIONS = 1; +const int NUM_WEAPONS = 3; + +class Sortie : public IContainedObject { +private: + static int _sSourceX; + static int _sSourceY; + + static int _sTargetX; + static int _sTargetY; + + int _unitType; + int _shotPosX, _shotPosY; + Common::Array<DefenseUnit *> _enemyDefenses; + AI *_ai; + +public: + Sortie(AI *ai) { _ai = ai; _unitType = 0; _shotPosX = _shotPosY = 0; } + virtual ~Sortie(); + + static void setSourcePos(int x, int y) { + _sSourceX = x; + _sSourceY = y; + } + static void setTargetPos(int x, int y) { + _sTargetX = x; + _sTargetY = y; + } + + void setUnitType(int unitType) { _unitType = unitType; } + + void setShotPosX(int shotPosX) { _shotPosX = shotPosX; } + void setShotPosY(int shotPosY) { _shotPosY = shotPosY; } + void setShotPos(int shotPosX, int shotPosY) { + _shotPosX = shotPosX; + _shotPosY = shotPosY; + } + + void setEnemyDefenses(Common::Array<DefenseUnit *> enemyDefenses) { + _enemyDefenses = enemyDefenses; + } + void setEnemyDefenses(int enemyDefensesScummArray, int defendX, int defendY); + + void printEnemyDefenses(); + + static int getSourcePosX() { return _sSourceX; } + static int getSourcePosY() { return _sSourceY; } + static int getTargetPosX() { return _sTargetX; } + static int getTargetPosY() { return _sTargetY; } + + int getUnitType() const { return _unitType; } + + int getShotPosX() const { return _shotPosX; } + int getShotPosY() const { return _shotPosY; } + int *getShotPos() const; + + Common::Array<DefenseUnit *> getEnemyDefenses() const { return _enemyDefenses; } + + virtual IContainedObject *duplicate(); + + virtual int numChildrenToGen(); + virtual IContainedObject *createChildObj(int, int &completionFlag); + + + virtual float calcH(); + virtual int checkSuccess(); + virtual float calcT(); +}; + +class Defender { +private: + int _sourceX; + int _sourceY; + int _targetX; + int _targetY; + int _sourceUnit; + 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; } + void setTargetY(int targetY) { _targetY = targetY; } + void setSourceUnit(int sourceUnit) { _sourceUnit = sourceUnit; } + void setPower(int power) { _power = power; } + void setAngle(int angle) { _angle = angle; } + void setUnit(int unit) { _unit = unit; } + + int getSourceX() const { return _sourceX; } + int getSourceY() const { return _sourceY; } + int getTargetX() const { return _targetX; } + int getTargetY() const { return _targetY; } + int getSourceUnit() const { return _sourceUnit; } + int getPower() const { return _power; } + int getAngle() const { return _angle; } + int getUnit() const { return _unit; } + + int calculateDefenseUnitPosition(int targetX, int targetY, int index); +}; + +class defenseUnitCompare { +public: + bool operator()(DefenseUnit *x, DefenseUnit *y) { + //disabled units go at the end + if (x->getState() == DUS_OFF) { + warning("OFF"); + return 0; + } + + return x->getDistanceTo() < y->getDistanceTo(); + } +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_traveller.cpp b/engines/scumm/he/moonbase/ai_traveller.cpp new file mode 100644 index 0000000000..b1c9985b9d --- /dev/null +++ b/engines/scumm/he/moonbase/ai_traveller.cpp @@ -0,0 +1,277 @@ +/* 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 "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" + +namespace Scumm { + +int Traveller::_targetPosX = 0; +int Traveller::_targetPosY = 0; +int Traveller::_maxDist = 0; + +int Traveller::_numToGen = 0; +int Traveller::_sizeAngleStep = 0; + +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, 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 = _ai->getMaxX(); + int deltaX = _posX + offsetX; + + if (deltaX < 0) _posX = maxX + deltaX; + else if (deltaX > maxX) _posX = deltaX - maxX; + else _posX = deltaX; +} + +void Traveller::adjustPosY(int offsetY) { + int maxY = _ai->getMaxX(); + int deltaY = _posY + offsetY; + + if (deltaY < 0) _posY = maxY + deltaY; + else if (deltaY > maxY) _posY = deltaY - maxY; + else _posY = deltaY; +} + +void Traveller::adjustXY(int offsetX, int offsetY) { + adjustPosX(offsetX); + adjustPosY(offsetY); +} + +float Traveller::calcH() { + float retVal = 0; + // Calc dist from here to target + retVal = _ai->getDistance(_posX, _posY, _targetPosX, _targetPosY); + // Divide by _maxDist to get minimum number of jumps to goal + retVal /= static_cast<float>(_maxDist); + + return retVal * 2.0; +} + +int Traveller::numChildrenToGen() { + if (!_numToGen) + _numToGen = _ai->getAnimSpeed() + 2; + + return _numToGen; +} + +IContainedObject *Traveller::createChildObj(int index, int &completionFlag) { + static int nodeCount = 0; + static int completionState = 1; + + if (!index) nodeCount = 0; + + nodeCount++; + + Traveller *retTraveller = new Traveller(_ai); + + static int dir, angle, power; + + if (completionState) { + // Calculate angle between here and target + int directAngle = 0; + + if (_ai->getEnergyHogType()) + directAngle = _ai->calcAngle(_posX, _posY, _targetPosX, _targetPosY, 1); + else + directAngle = _ai->calcAngle(_posX, _posY, _targetPosX, _targetPosY); + + // Calculate the offset angle for this index + if (!_sizeAngleStep) + _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 + int orientation = dir * (((static_cast<int>(index / NUM_POWER_STEPS) % 2) << 1) - 1); + // Add the offset angle to the direct angle to target + angle = orientation + directAngle; + + // Calculate power for this index + int maxPower = 0; + int directDist = _ai->getDistance(_posX, _posY, _targetPosX, _targetPosY); + + if (directDist > _maxDist + 120) + maxPower = _ai->getMaxPower(); + else + maxPower = (int)((static_cast<float>(directDist) / static_cast<float>(_maxDist + 120)) * _ai->getMaxPower()); + + maxPower -= 70; + power = (int)(maxPower * (1 - ((index % NUM_POWER_STEPS) * SIZE_POWER_STEP))); + } + + retTraveller->setAngleTo(angle); + retTraveller->setPowerTo(power); + + // Set this object's position to the new one determined by the power and angle from above + static int lastSuccessful = 0; + int coords = 0; + + if (!(index % NUM_POWER_STEPS) || (!lastSuccessful)) { + coords = _ai->simulateBuildingLaunch(_posX, _posY, power, angle, 10, 0); + lastSuccessful = 0; + } else { + completionState = 1; + lastSuccessful = 0; + } + + if (!coords) { + completionFlag = 0; + completionState = 0; + delete retTraveller; + return NULL; + } else { + completionFlag = 1; + completionState = 1; + } + + 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 = _ai->getTerrain(xCoord, yCoord); + assert(terrain == TERRAIN_TYPE_GOOD); + + 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 = (int)(xCoord + (pwr * cosine)); + int yParam = (int)(yCoord + (pwr * sine)); + + if (xParam < 0) + xParam += _ai->getMaxX(); + else if (xParam > _ai->getMaxX()) + xParam -= _ai->getMaxX(); + + if (yParam < 0) + yParam += _ai->getMaxY(); + else if (yParam > _ai->getMaxY()) + yParam -= _ai->getMaxY(); + + if (_ai->checkIfWaterState(xParam, yParam)) { + delete retTraveller; + return NULL; + } + + retTraveller->setPosY(yCoord); + retTraveller->setPosX(xCoord); + + // Iterate through the previous action list, making sure this one isn't on it + 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(); + delete retTraveller; + return NULL; + } + } + + retTraveller->setValueG(getG() + 7 + (dir * DIRECTION_WEIGHT)); + lastSuccessful = 1; + } else { + int yCoord = -coords / maxX; + 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 (_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((int)(xCoord + (terrainSquareSize * 1.414 * (xDist / (abs(xDist) + 1))))); + retTraveller->setPosY((int)(yCoord + (terrainSquareSize * 1.414 * (yDist / (abs(yDist) + 1))))); + + int closestHub = _ai->getClosestUnit(retTraveller->getPosX(), retTraveller->getPosY(), _ai->getMaxX(), _ai->getCurrentPlayer(), 1, BUILDING_MAIN_BASE, 1, 110); + + retTraveller->setWaterSourceX(_ai->getHubX(closestHub)); + retTraveller->setWaterSourceY(_ai->getHubY(closestHub)); + retTraveller->setWaterDestX(retTraveller->getPosX()); + retTraveller->setWaterDestY(retTraveller->getPosY()); + + retTraveller->setPowerTo(power); + retTraveller->setAngleTo(angle); + + retTraveller->setValueG(getG() + 10 + (dir * DIRECTION_WEIGHT)); + retTraveller->enableWaterFlag(); + } else { + // If not, set G to highest value + retTraveller->setDisabled(); + delete retTraveller; + return NULL; + } + } + + return retTraveller; +} + +int Traveller::checkSuccess() { + if (_ai->getDistance(_posX + 1, _posY, _targetPosX, _targetPosY) < _maxDist) + return SUCCESS; + + return 0; +} + +float Traveller::calcT() { + assert(!_disabled); + + if (_disabled) return FAILURE; + + return (checkSuccess() != SUCCESS) ? (getG() + calcH()) : SUCCESS; +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_traveller.h b/engines/scumm/he/moonbase/ai_traveller.h new file mode 100644 index 0000000000..20e69eb76c --- /dev/null +++ b/engines/scumm/he/moonbase/ai_traveller.h @@ -0,0 +1,123 @@ +/* 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 SCUMM_HE_MOONBASE_AI_TRAVELER_H +#define SCUMM_HE_MOONBASE_AI_TRAVELER_H + +#include "scumm/he/moonbase/ai_node.h" + +namespace Scumm { + +const int NUM_TO_GEN = 9; + +const int NUM_POWER_STEPS = 3; +const double SIZE_POWER_STEP = .15; +const int SIZE_ANGLE_STEP = 45; +const int VARIATION_EXTENT = 3; +const int DIRECTION_WEIGHT = 5; + +class Traveller : public IContainedObject { +private: + static int _targetPosX; + static int _targetPosY; + static int _maxDist; + + static int _numToGen; + static int _sizeAngleStep; + + int _sourceHub; + + int _posX; + int _posY; + int _angleTo; + int _powerTo; + + int _disabled; + int _waterFlag; + int _waterSourceX; + int _waterSourceY; + int _waterDestX; + int _waterDestY; + + AI *_ai; + +protected: + virtual float calcH(); + +public: + Traveller(AI *ai); + Traveller(int originX, int originY, AI *ai); + ~Traveller() {} + + IContainedObject *duplicate() { return this; } + + static void setTargetPosX(int posX) { _targetPosX = posX; } + static void setTargetPosY(int posY) { _targetPosY = posY; } + static void setMaxDist(int maxDist) { _maxDist = maxDist; } + + void setSourceHub(int sourceHub) { _sourceHub = sourceHub; } + + void setPosX(int posX) { _posX = posX; } + void setPosY(int posY) { _posY = posY; } + void setAngleTo(int angleTo) { _angleTo = angleTo; } + void setPowerTo(int powerTo) { _powerTo = powerTo; } + + void setWaterSourceX(int waterSourceX) { _waterSourceX = waterSourceX; } + void setWaterSourceY(int waterSourceY) { _waterSourceY = waterSourceY; } + + void setWaterDestX(int waterDestX) { _waterDestX = waterDestX; } + void setWaterDestY(int waterDestY) { _waterDestY = waterDestY; } + + int getSourceHub() const { return _sourceHub; } + + int getPosX() const { return _posX; } + int getPosY() const { return _posY; } + int getAngleTo() const { return _angleTo; } + int getPowerTo() const { return _powerTo; } + + int getWaterSourceX() const { return _waterSourceX; } + int getWaterSourceY() const { return _waterSourceY; } + int getWaterDestX() const { return _waterDestX; } + int getWaterDestY() const { return _waterDestY; } + + void setDisabled() { _disabled = 1; } + void unsetDisabled() { _disabled = 0; } + int getDisabled() { return _disabled; } + + void adjustPosX(int offsetX); + void adjustPosY(int offsetY); + void adjustXY(int offsetX, int offsetY); + + void enableWaterFlag() { _waterFlag = 1; } + void disableWaterFlag() { _waterFlag = 0; } + int getWaterFlag() const { return _waterFlag; } + + virtual int numChildrenToGen(); + virtual IContainedObject *createChildObj(int, int &); + + virtual int checkSuccess(); + virtual float calcT(); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_tree.cpp b/engines/scumm/he/moonbase/ai_tree.cpp new file mode 100644 index 0000000000..d18536812b --- /dev/null +++ b/engines/scumm/he/moonbase/ai_tree.cpp @@ -0,0 +1,245 @@ +/* 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 "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 { + +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, 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, 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, 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) { + Common::Array<Node *> vUnvisited = sourceNode->getChildren(); + + while (vUnvisited.size()) { + Node *newNode = new Node(*(vUnvisited.end())); + newNode->setParent(destNode); + (destNode->getChildren()).push_back(newNode); + duplicateTree(*(vUnvisited.end()), newNode); + vUnvisited.pop_back(); + } +} + +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); +} + +Tree::~Tree() { + // Delete all nodes + Node *pNodeItr = pBaseNode; + + // Depth first traversal of nodes to delete them + while (pNodeItr != NULL) { + // If any children are left, move to one of them + if (!(pNodeItr->getChildren().empty())) { + pNodeItr = pNodeItr->popChild(); + } else { + // Delete this node, and move up to the parent for further processing + Node *pTemp = pNodeItr; + pNodeItr = pNodeItr->getParent(); + delete pTemp; + pTemp = NULL; + } + } + + delete _currentMap; +} + +Node *Tree::aStarSearch() { + Common::SortedArray<TreeNode *> mmfpOpen(compareTreeNodes); + + Node *currentNode = NULL; + float currentT; + + Node *retNode = NULL; + + float temp = pBaseNode->getContainedObject()->calcT(); + + if (static_cast<int>(temp) != SUCCESS) { + mmfpOpen.insert(new TreeNode(pBaseNode->getObjectT(), pBaseNode)); + + while (mmfpOpen.size() && (retNode == NULL)) { + currentNode = mmfpOpen.front()->node; + mmfpOpen.erase(mmfpOpen.begin()); + + if ((currentNode->getDepth() < _maxDepth) && (Node::getNodeCount() < _maxNodes)) { + // Generate nodes + 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(new TreeNode(currentT, (*i))); + } + } else { + retNode = currentNode; + } + } + } else { + retNode = pBaseNode; + } + + return retNode; +} + + +Node *Tree::aStarSearch_singlePassInit() { + Node *retNode = NULL; + + _currentChildIndex = 1; + + float temp = pBaseNode->getContainedObject()->calcT(); + + if (static_cast<int>(temp) != SUCCESS) { + _currentMap->insert(new TreeNode(pBaseNode->getObjectT(), pBaseNode)); + } else { + retNode = pBaseNode; + } + + return retNode; +} + +Node *Tree::aStarSearch_singlePass() { + float currentT = 0.0; + Node *retNode = NULL; + + static int maxTime = 0; + + if (_currentChildIndex == 1) { + maxTime = _ai->getPlayerMaxTime(); + } + + if (_currentChildIndex) { + if (!(_currentMap->size())) { + retNode = _currentNode; + return retNode; + } + + _currentNode = _currentMap->front()->node; + _currentMap->erase(_currentMap->begin()); + } + + if ((_currentNode->getDepth() < _maxDepth) && (Node::getNodeCount() < _maxNodes) && ((!maxTime) || (_ai->getTimerValue(3) < maxTime))) { + // Generate nodes + _currentChildIndex = _currentNode->generateChildren(); + + if (_currentChildIndex) { + Common::Array<Node *> vChildren = _currentNode->getChildren(); + + if (!vChildren.size() && !_currentMap->size()) { + _currentChildIndex = 0; + retNode = _currentNode; + } + + for (Common::Array<Node *>::iterator i = vChildren.begin(); i != vChildren.end(); i++) { + IContainedObject *pTemp = (*i)->getContainedObject(); + currentT = pTemp->calcT(); + + if (currentT == SUCCESS) { + retNode = *i; + i = vChildren.end() - 1; + } else { + _currentMap->insert(new TreeNode(currentT, (*i))); + } + } + + if (!(_currentMap->size()) && (currentT != SUCCESS)) { + assert(_currentNode != NULL); + retNode = _currentNode; + } + } + } else { + retNode = _currentNode; + } + + return retNode; +} + +int Tree::IsBaseNode(Node *thisNode) { + return (thisNode == pBaseNode); +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_tree.h b/engines/scumm/he/moonbase/ai_tree.h new file mode 100644 index 0000000000..45d4963bc1 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_tree.h @@ -0,0 +1,84 @@ +/* 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 SCUMM_HE_MOONBASE_AI_TREE_H +#define SCUMM_HE_MOONBASE_AI_TREE_H + +#include "common/array.h" +#include "scumm/he/moonbase/ai_node.h" + +namespace Scumm { + +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; + + int _maxDepth; + int _maxNodes; + + int _currentChildIndex; + + Common::SortedArray<TreeNode *> *_currentMap; + Node *_currentNode; + + AI *_ai; + +public: + 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); + + Node *getBaseNode() const { return pBaseNode; } + void setMaxDepth(int maxDepth) { _maxDepth = maxDepth; } + int getMaxDepth() const { return _maxDepth; } + + void setMaxNodes(int maxNodes) { _maxNodes = maxNodes; } + int getMaxNodes() const { return _maxNodes; } + + Node *aStarSearch(); + + Node *aStarSearch_singlePassInit(); + Node *aStarSearch_singlePass(); + + int IsBaseNode(Node *thisNode); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_types.cpp b/engines/scumm/he/moonbase/ai_types.cpp new file mode 100644 index 0000000000..e134f5ee12 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_types.cpp @@ -0,0 +1,176 @@ +/* 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/textconsole.h" +#include "scumm/he/moonbase/ai_types.h" + +namespace Scumm { + +AIEntity::AIEntity(int id) { + switch (id) { + default: + case BRUTAKAS: + warning("BRUTAKAS"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "BRUTAKAS"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_SMALL; + _angleVariation = AI_VAR_SMALL; + _powerVariation = AI_VAR_SMALL; + break; + + case AGI: + warning("Agi"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "Agi"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_MEDIUM; + _angleVariation = AI_VAR_MEDIUM; + _powerVariation = AI_VAR_LARGE; + break; + + case EL_GATO: + warning("El Gato de la Noche"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "El Gato de la Noche"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_SMALL; + _angleVariation = AI_VAR_SMALL; + _powerVariation = AI_VAR_MEDIUM; + break; + + case PIXELAHT: + warning("Pixelaht"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "Pixelaht"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_LARGE; + _angleVariation = AI_VAR_MEDIUM; + _powerVariation = AI_VAR_SMALL; + break; + + case CYBALL: + warning("cYbaLL"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "cYbaLL"); + _behaviorVariation = AI_VAR_LARGE; + _targetVariation = AI_VAR_LARGE; + _angleVariation = AI_VAR_SMALL; + _powerVariation = AI_VAR_SMALL; + break; + + case NEEP: + warning("Neep! Neep!"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "Neep! Neep!"); + _behaviorVariation = AI_VAR_MEDIUM; + _targetVariation = AI_VAR_SMALL; + _angleVariation = AI_VAR_SMALL; + _powerVariation = AI_VAR_LARGE; + break; + + case WARCUPINE: + warning("WARcupine"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "WARcupine"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_SMALL; + _angleVariation = AI_VAR_LARGE; + _powerVariation = AI_VAR_MEDIUM; + break; + + case AONE: + warning("aone"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "aone"); + _behaviorVariation = AI_VAR_MEDIUM; + _targetVariation = AI_VAR_MEDIUM; + _angleVariation = AI_VAR_MEDIUM; + _powerVariation = AI_VAR_MEDIUM; + break; + + case SPANDO: + warning("S p a n d o"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "S p a n d o"); + _behaviorVariation = AI_VAR_LARGE; + _targetVariation = AI_VAR_LARGE; + _angleVariation = AI_VAR_SMALL; + _powerVariation = AI_VAR_SMALL; + break; + + case ORBNU_LUNATEK: + warning("Bonur J Lunatek"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "Bonur J Lunatek"); + _behaviorVariation = AI_VAR_HUGE; + _targetVariation = AI_VAR_HUGE; + _angleVariation = AI_VAR_HUGE; + _powerVariation = AI_VAR_HUGE; + break; + + case CRAWLER_CHUCKER: + warning("Le Chuckre des Crawlres"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "Le Chuckre des Crawlres"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_MEDIUM; + _angleVariation = AI_VAR_MEDIUM; + _powerVariation = AI_VAR_LARGE; + break; + + case ENERGY_HOG: + warning("Energy Hog"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "Energy Hog\n"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_SMALL; + _angleVariation = AI_VAR_SMALL; + _powerVariation = AI_VAR_SMALL; + break; + + case RANGER: + warning("Ranger"); + _id = id; + _nameString = new char[64]; + strcpy(_nameString, "Ranger\n"); + _behaviorVariation = AI_VAR_SMALL; + _targetVariation = AI_VAR_SMALL; + _angleVariation = AI_VAR_SMALL; + _powerVariation = AI_VAR_SMALL; + break; + } +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_types.h b/engines/scumm/he/moonbase/ai_types.h new file mode 100644 index 0000000000..e2de87d653 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_types.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. + * + */ + +#ifndef SCUMM_HE_MOONBASE_AI_TYPES_H +#define SCUMM_HE_MOONBASE_AI_TYPES_H + +namespace Scumm { + +enum { + AGI = 1, + AONE = 2, + BRUTAKAS = 3, + CYBALL = 4, + EL_GATO = 5, + NEEP = 6, + ORBNU_LUNATEK = 7, + PIXELAHT = 8, + SPANDO = 9, + WARCUPINE = 10 +}; + +enum { + CRAWLER_CHUCKER = 11, + ENERGY_HOG = 12, + RANGER = 13 +}; + +enum { + AI_VAR_NONE = -1, + AI_VAR_SMALL = 0, + AI_VAR_MEDIUM = 1, + AI_VAR_LARGE = 2, + AI_VAR_HUGE = 5 +}; + +enum { + AI_VAR_BASE_BEHAVIOR = 10, + AI_VAR_BASE_TARGET = 10, + AI_VAR_BASE_ANGLE = 2, + AI_VAR_BASE_POWER = 5 +}; + +class AIEntity { +private: + int _id; + char *_nameString; + int _behaviorVariation; + int _targetVariation; + int _angleVariation; + int _powerVariation; + +public: + AIEntity(int id); + ~AIEntity() { + if (_nameString) { + delete _nameString; + _nameString = 0; + } + } + + int getID() const { return _id; } + char *getNameString() const { return _nameString; } + int getBehaviorVariation() const { return _behaviorVariation; } + int getTargetVariation() const { return _targetVariation; } + int getAngleVariation() const { return _angleVariation; } + int getPowerVariation() const { return _powerVariation; } + + void setID(int id) { _id = id; } + void setNameString(char *nameString) { _nameString = nameString; } + void setBehaviorVariation(int behaviorVariation) { _behaviorVariation = behaviorVariation; } + void setTargetVariation(int targetVariation) { _targetVariation = targetVariation; } + void setAngleVariation(int angleVariation) { _angleVariation = angleVariation; } + void setPowerVariation(int powerVariation) { _powerVariation = powerVariation; } +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/ai_weapon.cpp b/engines/scumm/he/moonbase/ai_weapon.cpp new file mode 100644 index 0000000000..ba50aae4d1 --- /dev/null +++ b/engines/scumm/he/moonbase/ai_weapon.cpp @@ -0,0 +1,88 @@ +/* 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 "scumm/he/moonbase/ai_weapon.h" +#include "scumm/he/moonbase/ai_main.h" + +namespace Scumm { + +Weapon::Weapon(int typeID) { //, float damage, int radius) + switch (typeID) { + default: + case ITEM_BOMB: + becomeBomb(); + break; + + case ITEM_CLUSTER: + becomeCluster(); + break; + + case ITEM_CRAWLER: + becomeCrawler(); + break; + + case ITEM_EMP: + becomeEMP(); + break; + + case ITEM_SPIKE: + becomeSpike(); + break; + } +} + +void Weapon::becomeBomb() { + _typeID = ITEM_BOMB; + _damage = 3; + _radius = 30; + _cost = 1; +} + +void Weapon::becomeCluster() { + _typeID = ITEM_CLUSTER; + _damage = 1.5; + _radius = 20; + _cost = 1; +} + +void Weapon::becomeCrawler() { + _typeID = ITEM_CRAWLER; + _damage = 4; + _radius = 180; + _cost = 7; +} + +void Weapon::becomeEMP() { + _typeID = ITEM_EMP; + _damage = .1f; + _radius = 215; + _cost = 3; +} + +void Weapon::becomeSpike() { + _typeID = ITEM_SPIKE; + _damage = 6; + _radius = 180; + _cost = 3; +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/ai_weapon.h b/engines/scumm/he/moonbase/ai_weapon.h new file mode 100644 index 0000000000..55c710ccdf --- /dev/null +++ b/engines/scumm/he/moonbase/ai_weapon.h @@ -0,0 +1,59 @@ +/* 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 SCUMM_HE_MOONBASE_AI_WEAPON_H +#define SCUMM_HE_MOONBASE_AI_WEAPON_H + +namespace Scumm { + +class Weapon { +private: + int _typeID; + float _damage; + int _radius; + int _cost; + +public: + Weapon() {} + Weapon(int typeID); + virtual ~Weapon() {} + + void setTypeID(int typeID) { _typeID = typeID; } + void setDamage(float damage) { _damage = damage; } + void setRadius(int radius) { _radius = radius; } + void setCost(int cost) { _cost = cost; } + + int getTypeID() { return _typeID; } + float getDamage() { return _damage; } + int getRadius() { return _radius; } + int getCost() { return _cost; } + + void becomeBomb(); + void becomeCluster(); + void becomeCrawler(); + void becomeEMP(); + void becomeSpike(); +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/he/moonbase/moonbase.cpp b/engines/scumm/he/moonbase/moonbase.cpp new file mode 100644 index 0000000000..15ababd321 --- /dev/null +++ b/engines/scumm/he/moonbase/moonbase.cpp @@ -0,0 +1,223 @@ +/* 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 "scumm/he/intern_he.h" +#include "scumm/he/moonbase/moonbase.h" +#include "scumm/he/moonbase/ai_main.h" + +namespace Scumm { + +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(_vm->VAR_U32_ARRAY_UNK) = array; + + 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,...) { + va_list va_params; + va_start(va_params, paramCount); + int args[25]; + + 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); + + return _vm->pop(); +} + + +void Moonbase::blitT14WizImage(uint8 *dst, int dstw, int dsth, int dstPitch, const Common::Rect *clipBox, + uint8 *wizd, int x, int y, int rawROP, int paramROP) { + bool premulAlpa = false; + + if (rawROP == 1) + premulAlpa = true; + + Common::Rect clippedDstRect(dstw, dsth); + if (clipBox) { + Common::Rect clip(clipBox->left, clipBox->top, clipBox->right, clipBox->bottom); + if (clippedDstRect.intersects(clip)) { + clippedDstRect.clip(clip); + } else { + return; + } + } + + int width = READ_LE_UINT16(wizd + 0x8 + 0); + int height = READ_LE_UINT16(wizd + 0x8 + 2); + + Common::Rect srcLimitsRect(width, height); + Common::Rect dstOperation(x, y, x + width, y + height); + if (!clippedDstRect.intersects(dstOperation)) + return; + Common::Rect clippedRect = clippedDstRect.findIntersectingRect(dstOperation); + + int cx = clippedRect.right - clippedRect.left; + int cy = clippedRect.bottom - clippedRect.top; + + int sx = ((clippedRect.left - x) + srcLimitsRect.left); + int sy = ((clippedRect.top - y) + srcLimitsRect.top); + + dst += clippedRect.top * dstPitch + clippedRect.left * 2; + + int headerSize = READ_LE_UINT32(wizd + 0x4); + uint8 *dataPointer = wizd + 0x8 + headerSize; + + for (int i = 0; i < sy; i++) { + uint16 lineSize = READ_LE_UINT16(dataPointer + 0); + + dataPointer += lineSize; + } + + for (int i = 0; i < cy; i++) { + uint16 lineSize = READ_LE_UINT16(dataPointer + 0); + uint8 *singlesOffset = READ_LE_UINT16(dataPointer + 2) + dataPointer; + uint8 *quadsOffset = READ_LE_UINT16(dataPointer + 4) + dataPointer; + + int pixels = 0; + byte *dst1 = dst; + byte *codes = dataPointer + 6; + + while (1) { + int code = *codes - 2; + codes++; + + if (code <= 0) { // quad or single + uint8 *src; + int cnt; + if (code == 0) { // quad + src = quadsOffset; + quadsOffset += 8; + cnt = 4; // 4 pixels + } else { // single + src = singlesOffset; + singlesOffset += 2; + cnt = 1; + } + + for (int c = 0; c < cnt; c++) { + if (pixels >= sx) { + if (rawROP == 1) { // MMX_PREMUL_ALPHA_COPY + WRITE_LE_UINT16(dst1, READ_LE_UINT16(src)); + } else if (rawROP == 2) { // MMX_ADDITIVE + uint16 color = READ_LE_UINT16(src); + uint16 orig = READ_LE_UINT16(dst1); + + uint32 r = MIN<uint32>(0x7c00, (orig & 0x7c00) + (color & 0x7c00)); + uint32 g = MIN<uint32>(0x03e0, (orig & 0x03e0) + (color & 0x03e0)); + uint32 b = MIN<uint32>(0x001f, (orig & 0x001f) + (color & 0x001f)); + WRITE_LE_UINT16(dst1, (r | g | b)); + } else if (rawROP == 5) { // MMX_CHEAP_50_50 + uint16 color = (READ_LE_UINT16(src) >> 1) & 0x3DEF; + uint16 orig = (READ_LE_UINT16(dst1) >> 1) & 0x3DEF; + WRITE_LE_UINT16(dst1, (color + orig)); + } + dst1 += 2; + } + src += 2; + pixels++; + } + } else { // skip + if ((code & 1) == 0) { + code >>= 1; + + for (int j = 0; j < code; j++) { + if (pixels >= sx) + dst1 += 2; + pixels++; + } + } else { // special case + if (pixels >= sx) { + int alpha = code >> 1; + uint16 color = READ_LE_UINT16(singlesOffset); + uint32 orig = READ_LE_UINT16(dst1); + + if (!premulAlpa) { + WRITE_LE_UINT16(dst1, color); // ENABLE_PREMUL_ALPHA = 0 + } else { + if (alpha > 32) { + alpha -= 32; + + uint32 oR = orig & 0x7c00; + uint32 oG = orig & 0x03e0; + uint32 oB = orig & 0x1f; + uint32 dR = ((((color & 0x7c00) - oR) * alpha) >> 5) + oR; + uint32 dG = ((((color & 0x3e0) - oG) * alpha) >> 5) + oG; + uint32 dB = ((((color & 0x1f) - oB) * alpha) >> 5) + oB; + + WRITE_LE_UINT16(dst1, (dR & 0x7c00) | (dG & 0x3e0) | (dB & 0x1f)); + } else { + uint32 pix = ((orig << 16) | orig) & 0x3e07c1f; + pix = (((pix * alpha) & 0xffffffff) >> 5) & 0x3e07c1f; + pix = ((pix >> 16) + pix + color) & 0xffff; + WRITE_LE_UINT16(dst1, pix); + } + } + + dst1 += 2; + } + singlesOffset += 2; + pixels++; + } + } + + if (pixels >= cx + sx) + break; + } + + dataPointer += lineSize; + dst += dstPitch; + } +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/moonbase/moonbase.h b/engines/scumm/he/moonbase/moonbase.h new file mode 100644 index 0000000000..243d53a11d --- /dev/null +++ b/engines/scumm/he/moonbase/moonbase.h @@ -0,0 +1,111 @@ +/* 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 SCUMM_HE_MOONBASE_MOONBASE_H +#define SCUMM_HE_MOONBASE_MOONBASE_H + +#ifdef ENABLE_HE + +#include "common/winexe_pe.h" + +namespace Scumm { + +class AI; + +class Moonbase { +public: + 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, + uint8 *wizd, int srcx, int srcy, int rawROP, int paramROP); + + // FOW Stuff + bool isFOW(int resNum, int state, uint32 conditionBits) { + return resNum == _fowSentinelImage && state == _fowSentinelState && conditionBits == _fowSentinelConditionBits; + } + + void initFOW(); + void releaseFOWResources(); + + bool setFOWImage(int id); + + void setFOWInfo(int fowInfoArray, int downDim, int acrossDim, int viewX, int viewY, int clipX1, + int clipY1, int clipX2, int clipY2, int technique, int nFrame); + + + void renderFOW(uint8 *destSurface, int dstPitch, int dstType, int dstw, int dsth, int flags); + +private: + int readFOWVisibilityArray(int array, int y, int x); + void renderFOWState(uint8 *destSurface, int dstPitch, int dstType, int dstw, int dsth, int x, int y, int srcw, int srch, int state, int flags); + +public: + int _fowSentinelImage; + int _fowSentinelState; + uint32 _fowSentinelConditionBits; + + AI *_ai; + +private: + ScummEngine_v100he *_vm; + + int _fowFrameBaseNumber; + int _fowAnimationFrames; + int _fowCurrentFOWFrame; + + int32 _fowTileW; + int32 _fowTileH; + + uint8 *_fowImage; + int _fowClipX1; + int _fowClipY1; + int _fowClipX2; + int _fowClipY2; + + int _fowDrawX; + int _fowDrawY; + + int _fowVtx1; + int _fowVty1; + int _fowMvx; + int _fowMvy; + int _fowVw; + int _fowVh; + + bool _fowBlackMode; + + int _fowRenderTable[32768]; + + Common::PEResources _exe; + Common::String _fileName; +}; + +} // End of namespace Scumm + +#endif // ENABLE_HE + +#endif // SCUMM_HE_MOONBASE_H diff --git a/engines/scumm/he/moonbase/moonbase_fow.cpp b/engines/scumm/he/moonbase/moonbase_fow.cpp new file mode 100644 index 0000000000..48c2219926 --- /dev/null +++ b/engines/scumm/he/moonbase/moonbase_fow.cpp @@ -0,0 +1,415 @@ +/* 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 "scumm/he/intern_he.h" +#include "scumm/he/moonbase/moonbase.h" + +namespace Scumm { + +#define FOW_ANIM_FRAME_COUNT 38 + +void Moonbase::initFOW() { + _fowSentinelImage = -1; + _fowSentinelState = -1; + _fowSentinelConditionBits = 0; + + _fowFrameBaseNumber = 0; + _fowAnimationFrames = 1; + _fowCurrentFOWFrame = 0; + + _fowTileW = 0; + _fowTileH = 0; + + _fowImage = nullptr; + _fowClipX1 = 0; + _fowClipY1 = 0; + _fowClipX2 = 0; + _fowClipY2 = 0; + + _fowDrawX = 0; + _fowDrawY = 0; + + _fowVtx1 = 0; + _fowVty1 = 0; + _fowMvx = 0; + _fowMvy = 0; + _fowVw = 0; + _fowVh = 0; + + _fowBlackMode = true; + + memset(_fowRenderTable, 0, 32768); +} + +void Moonbase::releaseFOWResources() { + if (_fowImage) { + free(_fowImage); + _fowImage = 0; + } +} + +bool Moonbase::setFOWImage(int image) { + releaseFOWResources(); + + if (!_fowImage) { + Common::String fowImageFilename(ConfMan.get("MOONX_FOWImageFilename").c_str()); + +#if 0 // TODO + if (!fowImageFilename.empty()) { + void *wiz = loadWizFromFilename(fowImageFilename); + + if (wiz) { + captureFOWImageFromLocation(wiz, file.size()); + free(wiz); + } + } +#endif + + if (!_fowImage && image < 0) { + int resId; + + // PIECES BUBBLES CIRCLES SIMPLE* WEDGEY BUBBLE2 + // WEDGE2 SPIKEY ANGLES SMOOTHED WUZZY SYS7-BEVELED + if (image >= -12 && image <= -1) + resId = 210 - image; // 211-222 range + else + resId = 214; // default, SIMPLE + + if (_fileName.empty()) { // We are running for the first time + _fileName = _vm->generateFilename(-3); + + if (!_exe.loadFromEXE(_fileName)) + error("Cannot open file %s", _fileName.c_str()); + } + + Common::SeekableReadStream *stream = _exe.getResource(Common::kPERCData, resId); + + if (stream->size()) { + _fowImage = (uint8 *)malloc(stream->size()); + + stream->read(_fowImage, stream->size()); + } + + delete stream; + } + + if (!_fowImage && image > 0) + _fowImage = _vm->getResourceAddress(rtImage, image); + + if (!_fowImage) + return false; + } + + int nStates = _vm->_wiz->getWizImageStates(_fowImage); + + if (nStates > FOW_ANIM_FRAME_COUNT) { + releaseFOWResources(); + return false; + } + + _fowAnimationFrames = (nStates + FOW_ANIM_FRAME_COUNT - 1) / FOW_ANIM_FRAME_COUNT; + + _vm->_wiz->getWizImageDim(_fowImage, (nStates - 1), _fowTileW, _fowTileH); + _fowBlackMode = !_vm->_wiz->isWizPixelNonTransparent(_fowImage, nStates - 1, 0, 0, 0); + + if (ConfMan.hasKey("EnableFOWRects")) + _fowBlackMode = (ConfMan.getInt("EnableFOWRects") == 1); + + return true; +} + +enum FOWElement { + FOW_EMPTY = 0, + FOW_SOLID = 1, + + FF_L = 0x01, + FF_R = 0x02, + FF_T = 0x04, + FF_B = 0x08, + FF_T_L = 0x10, + FF_T_R = 0x20, + FF_B_L = 0x40, + FF_B_R = 0x80, + FF_Q_A = (FF_L | FF_T | FF_T_L), + FF_Q_B = (FF_R | FF_T | FF_T_R), + FF_Q_C = (FF_L | FF_B | FF_B_L), + FF_Q_D = (FF_R | FF_B | FF_B_R) +}; + +int Moonbase::readFOWVisibilityArray(int array, int y, int x) { + if (readFromArray(array, y, x) > 0) + return FOW_EMPTY; + + return FOW_SOLID; +} + +void Moonbase::setFOWInfo(int fowInfoArray, int downDim, int acrossDim, int viewX, int viewY, int clipX1, + int clipY1, int clipX2, int clipY2, int technique, int nFrame) { + if (!_fowImage) + return; + + _fowDrawX = clipX1; + _fowDrawY = clipY1; + + _fowClipX1 = clipX1; + _fowClipY1 = clipY1; + _fowClipX2 = clipX2; + _fowClipY2 = clipY2; + + // Figure out the number of tiles are involved + int view_W = (clipX2 - clipX1) + 1; + int view_H = (clipY2 - clipY1) + 1; + + int tw = _fowTileW; + int th = _fowTileH; + + int dw = acrossDim; + int dh = downDim; + + int dlw = dw * tw; + int dlh = dh * th; + + _fowMvx = (0 <= viewX) ? (viewX % dlw) : (dlw - (-viewX % dlw)); + _fowMvy = (0 <= viewY) ? (viewY % dlh) : (dlh - (-viewY % dlh)); + + _fowVtx1 = _fowMvx / tw; + _fowVty1 = _fowMvy / th; + + _fowVw = (((_fowMvx + view_W + tw - 1) / tw) - _fowVtx1) + 1; + _fowVh = (((_fowMvy + view_H + th - 1) / th) - _fowVty1) + 1; + + // Build the connectivity table + int t = (_fowVty1 - 1); if (t >= dh) { t = 0; } else if (t < 0) { t = (dh - 1); } + int m = (_fowVty1 + 0); if (m >= dh) { m = 0; } else if (m < 0) { m = (dh - 1); } + int b = (_fowVty1 + 1); if (b >= dh) { b = 0; } else if (b < 0) { b = (dh - 1); } + + int il = (_fowVtx1 - 1); if (il >= dh) { il = 0; } else if (il < 0) { il = (dw - 1); } + int ic = (_fowVtx1 + 0); if (ic >= dh) { ic = 0; } else if (ic < 0) { ic = (dw - 1); } + int ir = (_fowVtx1 + 1); if (ir >= dh) { ir = 0; } else if (ir < 0) { ir = (dw - 1); } + + int dataOffset = (_fowVw * 3); + int dataOffset2 = (dataOffset * 2); + int *pOutterRenderTableA = _fowRenderTable; + int *pOutterRenderTableB = pOutterRenderTableA + dataOffset; + + for (int ay = 0; ay < _fowVh; ay++) { + int l = il; + int c = ic; + int r = ir; + + int *pRenderTableA = pOutterRenderTableA; + int *pRenderTableB = pOutterRenderTableB; + + pOutterRenderTableA += dataOffset2; + pOutterRenderTableB += dataOffset2; + + for (int ax = 0; ax < _fowVw; ax++) { + int visibility = readFOWVisibilityArray(fowInfoArray, m, c); + + if (visibility == FOW_EMPTY) { + int bits = 0; + + if (readFOWVisibilityArray(fowInfoArray, t, l) != 0) bits |= FF_T_L; + if (readFOWVisibilityArray(fowInfoArray, t, c) != 0) bits |= FF_T; + if (readFOWVisibilityArray(fowInfoArray, t, r) != 0) bits |= FF_T_R; + if (readFOWVisibilityArray(fowInfoArray, m, l) != 0) bits |= FF_L; + if (readFOWVisibilityArray(fowInfoArray, m, r) != 0) bits |= FF_R; + if (readFOWVisibilityArray(fowInfoArray, b, l) != 0) bits |= FF_B_L; + if (readFOWVisibilityArray(fowInfoArray, b, c) != 0) bits |= FF_B; + if (readFOWVisibilityArray(fowInfoArray, b, r) != 0) bits |= FF_B_R; + + if (bits) { + *pRenderTableA++ = 1; + *pRenderTableB++ = 1; + + // Quadrant (A) + if (bits & FF_Q_A) { + *pRenderTableA++ = ( + ((FF_L & bits) ? 1 : 0) | + ((FF_T & bits) ? 2 : 0) | + ((FF_T_L & bits) ? 4 : 0) + ) + 0; + } else { + *pRenderTableA++ = 0; + } + + // Quadrant (B) + if (bits & FF_Q_B) { + *pRenderTableA++ = ( + ((FF_R & bits) ? 1 : 0) | + ((FF_T & bits) ? 2 : 0) | + ((FF_T_R & bits) ? 4 : 0) + ) + 8; + } else { + *pRenderTableA++ = 0; + } + + // Quadrant (C) + if (bits & FF_Q_C) { + *pRenderTableB++ = ( + ((FF_L & bits) ? 1 : 0) | + ((FF_B & bits) ? 2 : 0) | + ((FF_B_L & bits) ? 4 : 0) + ) + 16; + } else { + *pRenderTableB++ = 0; + } + + // Quadrant (D) + if (bits & FF_Q_D) { + *pRenderTableB++ = ( + ((FF_R & bits) ? 1 : 0) | + ((FF_B & bits) ? 2 : 0) | + ((FF_B_R & bits) ? 4 : 0) + ) + 24; + } else { + *pRenderTableB++ = 0; + } + } else { + *pRenderTableA++ = 0; + *pRenderTableB++ = 0; + } + } else { + if (_fowBlackMode) { + *pRenderTableA++ = 2; + *pRenderTableB++ = 2; + } else { + *pRenderTableA++ = 1; + *pRenderTableA++ = 33; + *pRenderTableA++ = 34; + + *pRenderTableB++ = 1; + *pRenderTableB++ = 35; + *pRenderTableB++ = 36; + } + } + + if (++l >= dw) { l = 0; } + if (++c >= dw) { c = 0; } + if (++r >= dw) { r = 0; } + } + + if (++t >= dh) { t = 0; } + if (++m >= dh) { m = 0; } + if (++b >= dh) { b = 0; } + } + + _fowCurrentFOWFrame = (nFrame >= 0) ? (nFrame % _fowAnimationFrames) : ((-nFrame) % _fowAnimationFrames); + _fowFrameBaseNumber = (_fowCurrentFOWFrame * FOW_ANIM_FRAME_COUNT); +} + +void Moonbase::renderFOWState(uint8 *destSurface, int dstPitch, int dstType, int dstw, int dsth, int x, int y, int srcw, int srch, int state, int flags) { + int32 spotx, spoty; + + _vm->_wiz->getWizImageSpot(_fowImage, state, spotx, spoty); + Common::Rect r(_fowClipX1, _fowClipY1, _fowClipX2, _fowClipY2); + + _vm->_wiz->drawWizImageEx(destSurface, _fowImage, 0, dstPitch, dstType, dstw, dsth, x - spotx, y - spoty, srcw, srch, state, &r, flags, 0, 0, 16, 0, 0); +} + +static void blackRect_16bpp(uint8 *destSurface, int dstPitch, int dstw, int dsth, int x1, int y1, int x2, int y2) { + byte *dst = destSurface + dstPitch * y1 + x1 * 2; + int h = y2 - y1; + int w = ((x2 - x1) + 1) * 2; + + while ( --h >= 0 ) { + memset(dst, 0, w); + dst += dstPitch; + } +} + +void Moonbase::renderFOW(uint8 *destSurface, int dstPitch, int dstType, int dstw, int dsth, int flags) { + if (!_fowImage) + return; + + const int *pOutterRenderTable = _fowRenderTable; + int ixPos = ((_fowVtx1 * _fowTileW) - _fowMvx) + _fowDrawX; + int yPos = ((_fowVty1 * _fowTileH) - _fowMvy) + _fowDrawY; + int dataOffset = _fowVw * 3; + int halfTileHeight = _fowTileH / 2; + int cx2 = MIN(_fowClipX2, (dstw - 1)); + int cy2 = MIN(_fowClipY2, (dsth - 1)); + + for (int ry = 0; ry < _fowVh; ry++) { + int real_yPos = yPos; + + for (int i = 0; i < 2; i++) { + const int *pRenderTable = pOutterRenderTable; + pOutterRenderTable += dataOffset; + + int xPos = ixPos; + + for (int rx = 0; rx < _fowVw; rx++) { + int nState = *pRenderTable++; + + if (nState != 0) { + if (nState == 2) { + int countLeft = (_fowVw - rx); + int count = 0; + + for (; count < countLeft; count++) { + if (*(pRenderTable + count) != 2) + break; + + pRenderTable++; + rx++; + } + count++; + + int x1 = xPos; + int y1 = real_yPos; + + xPos += _fowTileW * count; + int x2 = (xPos - 1); + int y2 = ((y1 + halfTileHeight) - 1); + + x1 = MAX(0, x1); + y1 = MAX(0, y1); + x2 = MIN(x2, cx2); + y2 = MIN(y2, cy2); + + if ((x2 >= x1) && (y2 >= y1) && (x1 <= _fowClipX2) && (y1 <= _fowClipY2)) + blackRect_16bpp(destSurface, dstPitch, dstw, dsth, x1, y1, x2, y2); + } else { + int subState; + + if ((subState = *pRenderTable++) != 0) + renderFOWState(destSurface, dstPitch, dstType, dstw, dsth, xPos, yPos, _fowTileW, _fowTileH, (subState + _fowFrameBaseNumber), flags); + + if ((subState = *pRenderTable++) != 0) + renderFOWState(destSurface, dstPitch, dstType, dstw, dsth, xPos, yPos, _fowTileW, _fowTileH, (subState + _fowFrameBaseNumber), flags); + + xPos += _fowTileW; + } + } else { + xPos += _fowTileW; + } + } + real_yPos += halfTileHeight; + } + yPos += _fowTileH; + } +} + +} // End of namespace Scumm diff --git a/engines/scumm/he/script_v100he.cpp b/engines/scumm/he/script_v100he.cpp index afc6633ef6..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); @@ -549,20 +549,20 @@ void ScummEngine_v100he::o100_arrayOps() { debug(9,"o100_arrayOps: array %d case %d", array, subOp); switch (subOp) { - case 35: + case 35: // SO_FORMATTED_STRING decodeScriptString(string); len = resStrLen(string); data = defineArray(array, kStringArray, 0, 0, 0, len); memcpy(data, string, len); break; - case 77: // SO_ASSIGN_STRING + case 77: // SO_STRING copyScriptString(string, sizeof(string)); len = resStrLen(string); data = defineArray(array, kStringArray, 0, 0, 0, len); memcpy(data, string, len); break; - case 128: // SO_ASSIGN_2DIM_LIST + case 128: // SO_ASSIGN_2DIM_LIST len = getStackList(list, ARRAYSIZE(list)); id = readVar(array); if (id == 0) @@ -572,7 +572,7 @@ void ScummEngine_v100he::o100_arrayOps() { writeArray(array, c, len, list[len]); } break; - case 129: // SO_ASSIGN_INT_LIST + case 129: // SO_ASSIGN_INT_LIST b = pop(); c = pop(); id = readVar(array); @@ -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(); @@ -607,7 +607,7 @@ void ScummEngine_v100he::o100_arrayOps() { dim2start++; } break; - case 131: + case 131: // SO_COMPLEX_ARRAY_COPY_OPERATION { int a2_dim1end = pop(); int a2_dim1start = pop(); @@ -624,44 +624,76 @@ void ScummEngine_v100he::o100_arrayOps() { copyArray(array, a1_dim2start, a1_dim2end, a1_dim1start, a1_dim1end, array2, a2_dim2start, a2_dim2end, a2_dim1start, a2_dim1end); } break; - case 132: - // TODO: Used by room 2 script 2180 in Moonbase Commander - fetchScriptWord(); - fetchScriptWord(); - type = pop(); - pop(); - pop(); - pop(); - pop(); - pop(); - pop(); - pop(); - pop(); - dim1end = pop(); - dim1start = pop(); - dim2end = pop(); - dim2start = pop(); - id = readVar(array); - if (id == 0) { - defineArray(array, kDwordArray, dim2start, dim2end, dim1start, dim1end); - } - switch (type) { - case 1: - break; - case 2: - break; - case 3: - break; - case 4: - break; - case 5: + case 132: // SO_COMPLEX_ARRAY_MATH_OPERATION + { + // Used by room 2 script 2180 in Moonbase Commander (modify-line-of-sight) + int array2 = fetchScriptWord(); + int array1 = fetchScriptWord(); + type = pop(); + int a1_dim1end = pop(); + int a1_dim1start = pop(); + int a1_dim2end = pop(); + int a1_dim2start = pop(); + int a2_dim1end = pop(); + int a2_dim1start = pop(); + int a2_dim2end = pop(); + int a2_dim2start = pop(); + dim1end = pop(); + dim1start = pop(); + dim2end = pop(); + dim2start = pop(); + + int a12_num = a1_dim2end - a1_dim2start + 1; + int a11_num = a1_dim1end - a1_dim1start + 1; + int a22_num = a2_dim2end - a2_dim2start + 1; + int a21_num = a2_dim1end - a2_dim1start + 1; + int d12_num = dim2end - dim2start + 1; + int d11_num = dim1end - dim1start + 1; + + id = readVar(array); + if (id == 0) { + defineArray(array, kDwordArray, dim2start, dim2end, dim1start, dim1end); + } + if (a12_num != a22_num || a12_num != d12_num || a11_num != a21_num || a11_num != d11_num) { + error("Operation size mismatch (%d vs %d)(%d vs %d)", a12_num, a22_num, a11_num, a21_num); + } + + for (; a1_dim2start <= a1_dim2end; ++a1_dim2start, ++a2_dim2start, ++dim2start) { + int a2dim1 = a2_dim1start; + int a1dim1 = a1_dim1start; + int dim1 = dim1start; + for (; a1dim1 <= a1_dim1end; ++a1dim1, ++a2dim1, ++dim1) { + int val1 = readArray(array1, a1_dim2start, a1dim1); + int val2 = readArray(array2, a2_dim2start, a2dim1); + int res; + + switch (type) { + case 1: // Addition + res = val2 + val1; + break; + case 2: // Subtraction + res = val2 - val1; + break; + case 3: // Binary AND + res = val2 & val1; + break; + case 4: // Binary OR + res = val2 | val1; + break; + case 5: // Binary XOR + res = val2 ^ val1; + break; + default: + error("o100_arrayOps: case 132 unknown type %d)", type); + } + writeArray(array, dim2start, dim1, res); + } + } + + warning("STUB: o100_arrayOps: case 132 type %d", type); break; - default: - error("o100_arrayOps: case 132 unknown type %d)", type); } - debug(0, "o100_arrayOps: case 132 type %d", type); - break; - case 133: + case 133: // SO_RANGE_ARRAY_ASSIGNMENT b = pop(); c = pop(); dim1end = pop(); @@ -910,10 +942,10 @@ void ScummEngine_v100he::o100_setSpriteGroupInfo() { byte subOp = fetchScriptByte(); switch (subOp) { - case 0: + case 0: // SO_INIT _curSpriteGroupId = pop(); break; - case 6: + case 6: // SO_MOVE value2 = pop(); value1 = pop(); if (!_curSpriteGroupId) @@ -921,7 +953,7 @@ void ScummEngine_v100he::o100_setSpriteGroupInfo() { _sprite->setGroupPosition(_curSpriteGroupId, value1, value2); break; - case 18: + case 18: // SO_CLIPPED value4 = pop(); value3 = pop(); value2 = pop(); @@ -931,10 +963,10 @@ void ScummEngine_v100he::o100_setSpriteGroupInfo() { _sprite->setGroupBounds(_curSpriteGroupId, value1, value2, value3, value4); break; - case 38: + case 38: // SO_GROUP type = pop() - 1; switch (type) { - case 0: + case 0: // SPRGRPOP_MOVE value2 = pop(); value1 = pop(); if (!_curSpriteGroupId) @@ -942,48 +974,48 @@ void ScummEngine_v100he::o100_setSpriteGroupInfo() { _sprite->moveGroupMembers(_curSpriteGroupId, value1, value2); break; - case 1: + case 1: // SPRGRPOP_ORDER value1 = pop(); if (!_curSpriteGroupId) break; _sprite->setGroupMembersPriority(_curSpriteGroupId, value1); break; - case 2: + case 2: // SPRGRPOP_NEW_GROUP value1 = pop(); if (!_curSpriteGroupId) break; _sprite->setGroupMembersGroup(_curSpriteGroupId, value1); break; - case 3: + case 3: // SPRGRPOP_UPDATE_TYPE value1 = pop(); if (!_curSpriteGroupId) break; _sprite->setGroupMembersUpdateType(_curSpriteGroupId, value1); break; - case 4: + case 4: // SPRGRPOP_NEW if (!_curSpriteGroupId) break; _sprite->setGroupMembersResetSprite(_curSpriteGroupId); break; - case 5: + case 5: // SPRGRPOP_ANIMATION_SPEED value1 = pop(); if (!_curSpriteGroupId) break; _sprite->setGroupMembersAnimationSpeed(_curSpriteGroupId, value1); break; - case 6: + case 6: // SPRGRPOP_ANIMATION_TYPE value1 = pop(); if (!_curSpriteGroupId) break; _sprite->setGroupMembersAutoAnimFlag(_curSpriteGroupId, value1); break; - case 7: + case 7: // SPRGRPOP_SHADOW value1 = pop(); if (!_curSpriteGroupId) break; @@ -994,14 +1026,14 @@ void ScummEngine_v100he::o100_setSpriteGroupInfo() { error("o100_setSpriteGroupInfo subOp 38: Unknown case %d", subOp); } break; - case 40: + case 40: // SO_IMAGE value1 = pop(); if (!_curSpriteGroupId) break; _sprite->setGroupImage(_curSpriteGroupId, value1); break; - case 49: + case 49: // SO_AT value2 = pop(); value1 = pop(); if (!_curSpriteGroupId) @@ -1009,51 +1041,51 @@ void ScummEngine_v100he::o100_setSpriteGroupInfo() { _sprite->moveGroup(_curSpriteGroupId, value1, value2); break; - case 52: + case 52: // SO_NAME copyScriptString(string, sizeof(string)); break; - case 53: + case 53: // SO_NEW if (!_curSpriteGroupId) break; _sprite->resetGroup(_curSpriteGroupId); break; - case 54: + case 54: // SO_NEW_GENERAL_PROPERTY // dummy case pop(); pop(); break; - case 59: + case 59: // SO_PRIORITY value1 = pop(); if (!_curSpriteGroupId) break; _sprite->setGroupPriority(_curSpriteGroupId, value1); break; - case 60: + case 60: // SO_PROPERTY type = pop(); value1 = pop(); if (!_curSpriteGroupId) break; switch (type) { - case 0: + case 0: // SPRGRPPROP_XMUL _sprite->setGroupXMul(_curSpriteGroupId, value1); break; - case 1: + case 1: // SPRGRPPROP_XDIV _sprite->setGroupXDiv(_curSpriteGroupId, value1); break; - case 2: + case 2: // SPRGRPPROP_YMUL _sprite->setGroupYMul(_curSpriteGroupId, value1); break; - case 3: + case 3: // SPRGRPPROP_YDIV _sprite->setGroupYDiv(_curSpriteGroupId, value1); break; default: error("o100_setSpriteGroupInfo subOp 60: Unknown case %d", subOp); } break; - case 89: + case 89: // SO_NEVER_ZCLIP if (!_curSpriteGroupId) break; @@ -1110,6 +1142,7 @@ void ScummEngine_v100he::o100_resourceRoutines() { break; case 128: // TODO: Clear Heap + warning("STUB: o100_resourceRoutines: clear Heap"); break; case 129: // Dummy case @@ -1310,36 +1343,36 @@ void ScummEngine_v100he::o100_wizImageOps() { if (_wizParams.img.resNum) _wiz->processWizImage(&_wizParams); break; - case 128: - _wizParams.field_239D = pop(); - _wizParams.field_2399 = pop(); - _wizParams.field_23A5 = pop(); - _wizParams.field_23A1 = pop(); - copyScriptString(_wizParams.string2, sizeof(_wizParams.string2)); + case 128: // Font create _wizParams.processMode = 15; + _wizParams.fontProperties.bgColor = pop(); + _wizParams.fontProperties.fgColor = pop(); + _wizParams.fontProperties.size = pop(); + _wizParams.fontProperties.style = pop(); + copyScriptString(_wizParams.fontProperties.fontName, sizeof(_wizParams.fontProperties.fontName)); break; case 129: _wizParams.processMode = 14; break; - case 130: + case 130: // Font render _wizParams.processMode = 16; - _wizParams.field_23AD = pop(); - _wizParams.field_23A9 = pop(); - copyScriptString(_wizParams.string1, sizeof(_wizParams.string1)); + _wizParams.fontProperties.yPos = pop(); + _wizParams.fontProperties.xPos = pop(); + copyScriptString(_wizParams.fontProperties.string, sizeof(_wizParams.fontProperties.string)); break; case 131: _wizParams.processMode = 13; break; - case 133: + case 133: // Render ellipse _wizParams.processMode = 17; - _wizParams.field_23CD = pop(); - _wizParams.field_23C9 = pop(); - _wizParams.field_23C5 = pop(); - _wizParams.field_23C1 = pop(); - _wizParams.field_23BD = pop(); - _wizParams.field_23B9 = pop(); - _wizParams.field_23B5 = pop(); - _wizParams.field_23B1 = pop(); + _wizParams.ellipseProperties.color = pop(); + _wizParams.ellipseProperties.lod = pop(); + _wizParams.ellipseProperties.ky = pop(); + _wizParams.ellipseProperties.kx = pop(); + _wizParams.ellipseProperties.qy = pop(); + _wizParams.ellipseProperties.qx = pop(); + _wizParams.ellipseProperties.py = pop(); + _wizParams.ellipseProperties.px = pop(); break; case 134: _wizParams.processFlags |= kWPFFillColor | kWPFClipBox2; @@ -1584,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) @@ -1625,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); @@ -1637,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)); @@ -1781,14 +1814,14 @@ void ScummEngine_v100he::o100_setSpriteInfo() { byte subOp = fetchScriptByte(); switch (subOp) { - case 0: + case 0: // SO_INIT _curMaxSpriteId = pop(); _curSpriteId = pop(); if (_curSpriteId > _curMaxSpriteId) SWAP(_curSpriteId, _curMaxSpriteId); break; - case 2: + case 2: // SO_ANGLE args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -1799,7 +1832,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteAngle(spriteId, args[0]); break; - case 3: + case 3: // SO_ANIMATION args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -1810,7 +1843,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteFlagAutoAnim(spriteId, args[0]); break; - case 4: + case 4: // SO_ANIMATION_SPEED args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -1821,7 +1854,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteAnimSpeed(spriteId, args[0]); break; - case 6: + case 6: // SO_AT args[1] = pop(); args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) @@ -1833,7 +1866,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpritePosition(spriteId, args[0], args[1]); break; - case 7: + case 7: // SO_AT_IMAGE args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -1844,7 +1877,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteSourceImage(spriteId, args[0]); break; - case 16: + case 16: // SO_CLASS n = getStackList(args, ARRAYSIZE(args)); if (_curSpriteId != 0 && _curMaxSpriteId != 0 && n != 0) { int *p = &args[n - 1]; @@ -1867,7 +1900,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { } while (--n); } break; - case 32: + case 32: // SO_ERASE args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -1878,7 +1911,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteFlagEraseType(spriteId, args[0]); break; - case 38: + case 38: // SO_GROUP args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -1889,7 +1922,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteGroup(spriteId, args[0]); break; - case 40: + case 40: // SO_IMAGE args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -1900,7 +1933,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteImage(spriteId, args[0]); break; - case 48: + case 48: // SO_MASK args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -1911,7 +1944,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteMaskImage(spriteId, args[0]); break; - case 49: + case 49: // SO_MOVE args[1] = pop(); args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) @@ -1923,10 +1956,10 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->moveSprite(spriteId, args[0], args[1]); break; - case 52: + case 52: // SO_NAME copyScriptString(string, sizeof(string)); break; - case 53: + case 53: // SO_NEW if (_curSpriteId > _curMaxSpriteId) break; spriteId = _curSpriteId; @@ -1936,7 +1969,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->resetSprite(spriteId); break; - case 54: + case 54: // SO_NEW_GENERAL_PROPERTY args[1] = pop(); args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) @@ -1948,7 +1981,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteGeneralProperty(spriteId, args[0], args[1]); break; - case 57: + case 57: // SO_PALETTE args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -1959,7 +1992,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpritePalette(spriteId, args[0]); break; - case 59: + case 59: // SO_PRIORITY args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -1970,7 +2003,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpritePriority(spriteId, args[0]); break; - case 60: + case 60: // SO_PROPERTY args[1] = pop(); args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) @@ -1997,13 +2030,14 @@ void ScummEngine_v100he::o100_setSpriteInfo() { _sprite->setSpriteFlagRemapPalette(spriteId, args[0]); break; default: + warning("Unknown sprite property %d for sprite %d", args[0], spriteId); break; } break; - case 61: + case 61: // SO_RESTART _sprite->resetTables(true); break; - case 65: + case 65: // SO_SCALE args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -2014,7 +2048,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteScale(spriteId, args[0]); break; - case 70: + case 70: // SO_SHADOW args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -2025,7 +2059,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteShadow(spriteId, args[0]); break; - case 73: + case 73: // SO_STATE args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -2036,7 +2070,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteImageState(spriteId, args[0]); break; - case 74: + case 74: // SO_STEP_DIST args[1] = pop(); args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) @@ -2048,7 +2082,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteDist(spriteId, args[0], args[1]); break; - case 75: + case 75: // SO_STEP_DIST_X args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -2061,7 +2095,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { _sprite->setSpriteDist(spriteId, args[0], tmp[1]); } break; - case 76: + case 76: // SO_STEP_DIST_Y args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -2074,7 +2108,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { _sprite->setSpriteDist(spriteId, tmp[0], args[0]); } break; - case 82: + case 82: // SO_UPDATE args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -2085,7 +2119,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteFlagUpdateType(spriteId, args[0]); break; - case 83: + case 83: // SO_VARIABLE args[1] = pop(); args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) @@ -2097,7 +2131,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { for (; spriteId <= _curMaxSpriteId; spriteId++) _sprite->setSpriteUserValue(spriteId, args[0], args[1]); break; - case 88: + case 88: // SO_IMAGE_ZCLIP args[0] = pop(); if (_curSpriteId > _curMaxSpriteId) break; @@ -2106,9 +2140,9 @@ void ScummEngine_v100he::o100_setSpriteInfo() { spriteId++; for (; spriteId <= _curMaxSpriteId; spriteId++) - _sprite->setSpriteField84(spriteId, args[0]); + _sprite->setSpriteZBuffer(spriteId, args[0]); break; - case 89: + case 89: // SO_NEVER_ZCLIP if (_curSpriteId > _curMaxSpriteId) break; spriteId = _curSpriteId; @@ -2116,7 +2150,7 @@ void ScummEngine_v100he::o100_setSpriteInfo() { spriteId++; for (; spriteId <= _curMaxSpriteId; spriteId++) - _sprite->setSpriteField84(spriteId, 0); + _sprite->setSpriteZBuffer(spriteId, 0); break; default: error("o100_setSpriteInfo: Unknown case %d", subOp); @@ -2235,40 +2269,43 @@ void ScummEngine_v100he::o100_videoOps() { byte subOp = fetchScriptByte(); switch (subOp) { - case 0: + case 0: // SO_INIT memset(_videoParams.filename, 0, sizeof(_videoParams.filename)); _videoParams.status = 0; _videoParams.flags = 0; - _videoParams.unk2 = pop(); + _videoParams.number = pop(); _videoParams.wizResNum = 0; + + if (_videoParams.number != 1 && _videoParams.number != -1) + warning("o100_videoOps: number: %d", _videoParams.number); break; - case 19: + case 19: // SO_CLOSE _videoParams.status = 19; break; - case 40: + case 40: // SO_IMAGE _videoParams.wizResNum = pop(); if (_videoParams.wizResNum) _videoParams.flags |= 2; break; - case 47: + case 47: // SO_LOAD copyScriptString(_videoParams.filename, sizeof(_videoParams.filename)); _videoParams.status = 47; break; - case 67: + case 67: // SO_SET_FLAGS _videoParams.flags |= pop(); break; - case 92: - if (_videoParams.status == 47) { + case 92: // SO_END + if (_videoParams.status == 47) { // SO_LOAD // Start video if (_videoParams.flags == 0) _videoParams.flags = 4; - if (_videoParams.flags == 2) { + if (_videoParams.flags & 2) { VAR(119) = _moviePlay->load(convertFilePath(_videoParams.filename), _videoParams.flags, _videoParams.wizResNum); } else { VAR(119) = _moviePlay->load(convertFilePath(_videoParams.filename), _videoParams.flags); } - } else if (_videoParams.status == 19) { + } else if (_videoParams.status == 19) { // SO_CLOSE // Stop video _moviePlay->close(); } @@ -2448,49 +2485,52 @@ void ScummEngine_v100he::o100_getSpriteGroupInfo() { byte subOp = fetchScriptByte(); + warning("o100_getSpriteGroupInfo, subop %d", subOp); + switch (subOp) { - case 5: + case 5: // SO_ARRAY spriteGroupId = pop(); if (spriteGroupId) push(getGroupSpriteArray(spriteGroupId)); else push(0); break; - case 40: + case 40: // SO_IMAGE spriteGroupId = pop(); if (spriteGroupId) push(_sprite->getGroupDstResNum(spriteGroupId)); else push(0); break; - case 54: + case 54: // SO_NEW_GENERAL_PROPERTY // TODO: U32 related pop(); pop(); push(0); + warning("STUB: o100_getSpriteGroupInfo, subop 54"); break; - case 59: + case 59: // SO_PRIORITY spriteGroupId = pop(); if (spriteGroupId) push(_sprite->getGroupPriority(spriteGroupId)); else push(0); break; - case 60: + case 60: // SO_PROPERTY type = pop(); spriteGroupId = pop(); if (spriteGroupId) { switch (type) { - case 0: + case 0: // SPRGRPPROP_XMUL push(_sprite->getGroupXMul(spriteGroupId)); break; - case 1: + case 1: // SPRGRPPROP_XDIV push(_sprite->getGroupXDiv(spriteGroupId)); break; - case 2: + case 2: // SPRGRPPROP_YMUL push(_sprite->getGroupYMul(spriteGroupId)); break; - case 3: + case 3: // SPRGRPPROP_YDIV push(_sprite->getGroupYDiv(spriteGroupId)); break; default: @@ -2500,7 +2540,7 @@ void ScummEngine_v100he::o100_getSpriteGroupInfo() { push(0); } break; - case 85: + case 85: // SO_XPOS spriteGroupId = pop(); if (spriteGroupId) { _sprite->getGroupPosition(spriteGroupId, tx, ty); @@ -2509,7 +2549,7 @@ void ScummEngine_v100he::o100_getSpriteGroupInfo() { push(0); } break; - case 86: + case 86: // SO_YPOS spriteGroupId = pop(); if (spriteGroupId) { _sprite->getGroupPosition(spriteGroupId, tx, ty); diff --git a/engines/scumm/he/script_v70he.cpp b/engines/scumm/he/script_v70he.cpp index a911487738..b91943c685 100644 --- a/engines/scumm/he/script_v70he.cpp +++ b/engines/scumm/he/script_v70he.cpp @@ -71,6 +71,7 @@ void ScummEngine_v70he::o70_startSound() { value = pop(); _heSndSoundId = pop(); _sound->addSoundToQueue(_heSndSoundId, 0, 0, 8); + break; case 56: _heSndFlags |= 16; break; 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/script_v90he.cpp b/engines/scumm/he/script_v90he.cpp index f65d2f6077..f63973e3f1 100644 --- a/engines/scumm/he/script_v90he.cpp +++ b/engines/scumm/he/script_v90he.cpp @@ -285,29 +285,29 @@ void ScummEngine_v90he::o90_wizImageOps() { _wizParams.processMode = 13; break; case 142: // HE99+ - _wizParams.field_239D = pop(); - _wizParams.field_2399 = pop(); - _wizParams.field_23A5 = pop(); - _wizParams.field_23A1 = pop(); - copyScriptString(_wizParams.string2, sizeof(_wizParams.string2)); _wizParams.processMode = 15; + _wizParams.fontProperties.bgColor = pop(); + _wizParams.fontProperties.fgColor = pop(); + _wizParams.fontProperties.size = pop(); + _wizParams.fontProperties.style = pop(); + copyScriptString(_wizParams.fontProperties.fontName, sizeof(_wizParams.fontProperties.fontName)); break; case 143: // HE99+ _wizParams.processMode = 16; - _wizParams.field_23AD = pop(); - _wizParams.field_23A9 = pop(); - copyScriptString(_wizParams.string1, sizeof(_wizParams.string1)); + _wizParams.fontProperties.yPos = pop(); + _wizParams.fontProperties.xPos = pop(); + copyScriptString(_wizParams.fontProperties.string, sizeof(_wizParams.fontProperties.string)); break; case 189: // HE99+ _wizParams.processMode = 17; - _wizParams.field_23CD = pop(); - _wizParams.field_23C9 = pop(); - _wizParams.field_23C5 = pop(); - _wizParams.field_23C1 = pop(); - _wizParams.field_23BD = pop(); - _wizParams.field_23B9 = pop(); - _wizParams.field_23B5 = pop(); - _wizParams.field_23B1 = pop(); + _wizParams.ellipseProperties.color = pop(); + _wizParams.ellipseProperties.lod = pop(); + _wizParams.ellipseProperties.ky = pop(); + _wizParams.ellipseProperties.kx = pop(); + _wizParams.ellipseProperties.qy = pop(); + _wizParams.ellipseProperties.qx = pop(); + _wizParams.ellipseProperties.py = pop(); + _wizParams.ellipseProperties.px = pop(); break; case 196: // HE99+ _wizParams.processMode = 14; @@ -1412,7 +1412,7 @@ void ScummEngine_v90he::o90_videoOps() { memset(_videoParams.filename, 0, sizeof(_videoParams.filename)); _videoParams.status = 0; _videoParams.flags = 0; - _videoParams.unk2 = pop(); + _videoParams.number = pop(); _videoParams.wizResNum = 0; break; case 14: diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp index 9a456b86c0..8670116c68 100644 --- a/engines/scumm/he/sound_he.cpp +++ b/engines/scumm/he/sound_he.cpp @@ -51,10 +51,12 @@ SoundHE::SoundHE(ScummEngine *parent, Audio::Mixer *mixer) _heMusicTracks(0) { memset(_heChannel, 0, sizeof(_heChannel)); + _heSoundChannels = new Audio::SoundHandle[8](); } SoundHE::~SoundHE() { free(_heMusic); + delete[] _heSoundChannels; } void SoundHE::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlags) { @@ -470,6 +472,10 @@ void SoundHE::processSoundOpcodes(int sound, byte *codePtr, int *soundVars) { if (arg == 2) { val = getSoundVar(sound, val); } + if (!val) { + val = 1; // Safeguard for division by zero + warning("Incorrect value 0 for processSoundOpcodes() kludge DIV"); + } val = getSoundVar(sound, var) / val; setSoundVar(sound, var, val); break; diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h index 323858a7c9..e0324d0753 100644 --- a/engines/scumm/he/sound_he.h +++ b/engines/scumm/he/sound_he.h @@ -44,7 +44,7 @@ protected: HEMusic *_heMusic; int16 _heMusicTracks; - Audio::SoundHandle _heSoundChannels[8]; + Audio::SoundHandle *_heSoundChannels; public: // Used by createSound() struct { diff --git a/engines/scumm/he/sprite_he.cpp b/engines/scumm/he/sprite_he.cpp index 245a986531..e3f04dfcf0 100644 --- a/engines/scumm/he/sprite_he.cpp +++ b/engines/scumm/he/sprite_he.cpp @@ -386,7 +386,7 @@ int Sprite::getSpriteGeneralProperty(int spriteId, int type) { case 0x7B: return _spriteTable[spriteId].imgFlags; case 0x7D: - return _spriteTable[spriteId].field_90; + return _spriteTable[spriteId].conditionBits; case 0x7E: return _spriteTable[spriteId].animProgress; default: @@ -739,26 +739,24 @@ void Sprite::setSpriteResetClass(int spriteId) { _spriteTable[spriteId].classFlags = 0; } -void Sprite::setSpriteField84(int spriteId, int value) { +void Sprite::setSpriteZBuffer(int spriteId, int value) { assertRange(1, spriteId, _varNumSprites, "sprite"); - _spriteTable[spriteId].field_84 = value; + _spriteTable[spriteId].zbufferImage = value; } void Sprite::setSpriteGeneralProperty(int spriteId, int type, int value) { - debug(0, "setSpriteGeneralProperty: spriteId %d type 0x%x", spriteId, type); + debug(6, "setSpriteGeneralProperty: spriteId %d type 0x%x value 0x%x", spriteId, type, value); assertRange(1, spriteId, _varNumSprites, "sprite"); int32 delay; - // XXX U32 related check - switch (type) { case 0x7B: _spriteTable[spriteId].imgFlags = value; _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw; break; case 0x7D: - _spriteTable[spriteId].field_90 = value; + _spriteTable[spriteId].conditionBits = value; _spriteTable[spriteId].flags |= kSFChanged | kSFNeedRedraw; break; case 0x7E: @@ -797,9 +795,9 @@ void Sprite::resetSprite(int spriteId) { _spriteTable[spriteId].sourceImage = 0; _spriteTable[spriteId].maskImage = 0; _spriteTable[spriteId].priority = 0; - _spriteTable[spriteId].field_84 = 0; + _spriteTable[spriteId].zbufferImage = 0; _spriteTable[spriteId].imgFlags = 0; - _spriteTable[spriteId].field_90 = 0; + _spriteTable[spriteId].conditionBits = 0; if (_vm->_game.heversion >= 100) { _spriteTable[spriteId].flags &= ~kSFMarkDirty; @@ -816,7 +814,7 @@ void Sprite::setSpriteImage(int spriteId, int imageNum) { origResWizStates = _spriteTable[spriteId].imageStateCount; _spriteTable[spriteId].image = imageNum; - _spriteTable[spriteId].field_74 = 0; + _spriteTable[spriteId].animIndex = 0; _spriteTable[spriteId].imageState = 0; if (_spriteTable[spriteId].image) { @@ -1292,7 +1290,7 @@ void Sprite::processImages(bool arg) { wiz.spriteId = spi->id; wiz.spriteGroup = spi->group; - wiz.field_23EA = spi->field_90; + wiz.conditionBits = spi->conditionBits; spi->curImageState = wiz.img.state = imageState; spi->curImage = wiz.img.resNum = image; wiz.processFlags = kWPFNewState | kWPFSetPos; @@ -1339,9 +1337,9 @@ void Sprite::processImages(bool arg) { } if (spr_flags & kSFRemapPalette) wiz.img.flags |= kWIFRemapPalette; - if (spi->field_84) { + if (spi->zbufferImage) { wiz.processFlags |= 0x200000; - wiz.img.field_390 = spi->field_84; + wiz.img.zbuffer = spi->zbufferImage; wiz.img.zorder = spi->priority; } if (spi->sourceImage) { @@ -1419,14 +1417,14 @@ void Sprite::saveOrLoadSpriteData(Serializer *s) { MKLINE(SpriteInfo, curAngle, sleInt32, VER(48)), MKLINE(SpriteInfo, curScale, sleInt32, VER(48)), MKLINE(SpriteInfo, curImgFlags, sleInt32, VER(48)), - MKLINE(SpriteInfo, field_74, sleInt32, VER(48)), + MKLINE(SpriteInfo, animIndex, sleInt32, VER(48)), MKLINE(SpriteInfo, animSpeed, sleInt32, VER(48)), MKLINE(SpriteInfo, sourceImage, sleInt32, VER(48)), MKLINE(SpriteInfo, maskImage, sleInt32, VER(48)), - MKLINE(SpriteInfo, field_84, sleInt32, VER(48)), + MKLINE(SpriteInfo, zbufferImage, sleInt32, VER(48)), MKLINE(SpriteInfo, classFlags, sleInt32, VER(48)), MKLINE(SpriteInfo, imgFlags, sleInt32, VER(48)), - MKLINE(SpriteInfo, field_90, sleInt32, VER(48)), + MKLINE(SpriteInfo, conditionBits, sleInt32, VER(48)), MKEND() }; diff --git a/engines/scumm/he/sprite_he.h b/engines/scumm/he/sprite_he.h index e31ccbf790..3ea6bb9f84 100644 --- a/engines/scumm/he/sprite_he.h +++ b/engines/scumm/he/sprite_he.h @@ -72,14 +72,14 @@ struct SpriteInfo { int32 curAngle; int32 curScale; int32 curImgFlags; - int32 field_74; + int32 animIndex; int32 animSpeed; int32 sourceImage; int32 maskImage; - int32 field_84; + int32 zbufferImage; int32 classFlags; int32 imgFlags; - int32 field_90; + int32 conditionBits; }; struct SpriteGroup { @@ -182,7 +182,7 @@ public: void setSpriteAnimSpeed(int spriteId, int value); void setSpriteSetClass(int spriteId, int classId, int toggle); void setSpriteResetClass(int spriteId); - void setSpriteField84(int spriteId, int value); + void setSpriteZBuffer(int spriteId, int value); void setSpriteGeneralProperty(int spriteId, int type, int value); void moveGroupMembers(int spriteGroupId, int value1, int value2); diff --git a/engines/scumm/he/wiz_he.cpp b/engines/scumm/he/wiz_he.cpp index 9a59609651..d730cb2894 100644 --- a/engines/scumm/he/wiz_he.cpp +++ b/engines/scumm/he/wiz_he.cpp @@ -31,6 +31,7 @@ #include "scumm/scumm.h" #include "scumm/util.h" #include "scumm/he/wiz_he.h" +#include "scumm/he/moonbase/moonbase.h" namespace Scumm { @@ -976,7 +977,7 @@ void Wiz::decompressRawWizImage(uint8 *dst, int dstPitch, int dstType, const uin } } -int Wiz::isWizPixelNonTransparent(const uint8 *data, int x, int y, int w, int h, uint8 bitDepth) { +int Wiz::isPixelNonTransparent(const uint8 *data, int x, int y, int w, int h, uint8 bitDepth) { if (x < 0 || x >= w || y < 0 || y >= h) { return 0; } @@ -1422,19 +1423,19 @@ void Wiz::displayWizImage(WizImage *pwi) { wi->state = pwi->state; wi->flags = pwi->flags; wi->shadow = 0; - wi->field_390 = 0; + wi->zbuffer = 0; wi->palette = 0; ++_imagesNum; } else if (pwi->flags & kWIFIsPolygon) { drawWizPolygon(pwi->resNum, pwi->state, pwi->x1, pwi->flags, 0, 0, 0); } else { const Common::Rect *r = NULL; - drawWizImage(pwi->resNum, pwi->state, 0, 0, pwi->x1, pwi->y1, 0, 0, 0, r, pwi->flags, 0, _vm->getHEPaletteSlot(0)); + drawWizImage(pwi->resNum, pwi->state, 0, 0, pwi->x1, pwi->y1, 0, 0, 0, r, pwi->flags, 0, _vm->getHEPaletteSlot(0), 0); } } -uint8 *Wiz::drawWizImage(int resNum, int state, int maskNum, int maskState, int x1, int y1, int zorder, int shadow, int field_390, const Common::Rect *clipBox, int flags, int dstResNum, const uint8 *palPtr) { - debug(3, "drawWizImage(resNum %d, state %d maskNum %d maskState %d x1 %d y1 %d flags 0x%X zorder %d shadow %d field_390 %d dstResNum %d)", resNum, state, maskNum, maskState, x1, y1, flags, zorder, shadow, field_390, dstResNum); +uint8 *Wiz::drawWizImage(int resNum, int state, int maskNum, int maskState, int x1, int y1, int zorder, int shadow, int zbuffer, const Common::Rect *clipBox, int flags, int dstResNum, const uint8 *palPtr, uint32 conditionBits) { + debug(7, "drawWizImage(resNum %d, state %d maskNum %d maskState %d x1 %d y1 %d flags 0x%X zorder %d shadow %d zbuffer %d dstResNum %d conditionBits: 0x%x)", resNum, state, maskNum, maskState, x1, y1, flags, zorder, shadow, zbuffer, dstResNum, conditionBits); uint8 *dataPtr; uint8 *dst = NULL; @@ -1454,10 +1455,7 @@ uint8 *Wiz::drawWizImage(int resNum, int state, int maskNum, int maskState, int uint32 comp = READ_LE_UINT32(wizh + 0x0); uint32 width = READ_LE_UINT32(wizh + 0x4); uint32 height = READ_LE_UINT32(wizh + 0x8); - debug(3, "wiz_header.comp = %d wiz_header.w = %d wiz_header.h = %d", comp, width, height); - - uint8 *wizd = _vm->findWrappedBlock(MKTAG('W','I','Z','D'), dataPtr, state, 0); - assert(wizd); + debug(7, "wiz_header.comp = %d wiz_header.w = %d wiz_header.h = %d", comp, width, height); uint8 *mask = NULL; if (maskNum) { @@ -1574,58 +1572,259 @@ uint8 *Wiz::drawWizImage(int resNum, int state, int maskNum, int maskState, int transColor = (trns == NULL) ? _vm->VAR(_vm->VAR_WIZ_TCOLOR) : -1; } + if (_vm->_game.id == GID_MOONBASE && + ((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(); + height = rScreen.height(); + } else { + drawWizImageEx(dst, dataPtr, mask, dstPitch, dstType, cw, ch, x1, y1, width, height, + state, &rScreen, flags, palPtr, transColor, _vm->_bytesPerPixel, xmapPtr, conditionBits); + } + + if (!(flags & kWIFBlitToMemBuffer) && dstResNum == 0) { + Common::Rect rImage(x1, y1, x1 + width, y1 + height); + if (rImage.intersects(rScreen)) { + rImage.clip(rScreen); + if (!(flags & kWIFBlitToFrontVideoBuffer) && (flags & (kWIFBlitToFrontVideoBuffer | kWIFMarkBufferDirty))) { + ++rImage.bottom; + _vm->markRectAsDirty(kMainVirtScreen, rImage); + } else { + _vm->restoreBackgroundHE(rImage); + } + } + } + + return dst; +} + +void Wiz::drawWizImageEx(uint8 *dst, uint8 *dataPtr, uint8 *maskPtr, int dstPitch, int dstType, + int dstw, int dsth, int srcx, int srcy, int srcw, int srch, int state, const Common::Rect *rect, + int flags, const uint8 *palPtr, int transColor, uint8 bitDepth, const uint8 *xmapPtr, uint32 conditionBits) { + uint8 *wizh = _vm->findWrappedBlock(MKTAG('W','I','Z','H'), dataPtr, state, 0); + assert(wizh); + uint32 comp = READ_LE_UINT32(wizh + 0x0); + uint32 width = READ_LE_UINT32(wizh + 0x4); + uint32 height = READ_LE_UINT32(wizh + 0x8); + debug(7, "wiz_header.comp = %d wiz_header.w = %d wiz_header.h = %d", comp, width, height); + + uint8 *wizd = _vm->findWrappedBlock(MKTAG('W','I','Z','D'), dataPtr, state, 0); + assert(wizd); + switch (comp) { case 0: - copyRawWizImage(dst, wizd, dstPitch, dstType, cw, ch, x1, y1, width, height, &rScreen, flags, palPtr, transColor, _vm->_bytesPerPixel); + copyRawWizImage(dst, wizd, dstPitch, dstType, dstw, dsth, srcx, srcy, srcw, srch, rect, flags, palPtr, transColor, bitDepth); break; case 1: - if (flags & 0x80) { + if (flags & kWIFZPlaneOn) { dst = _vm->getMaskBuffer(0, 0, 1); dstPitch /= _vm->_bytesPerPixel; - copyWizImageWithMask(dst, wizd, dstPitch, cw, ch, x1, y1, width, height, &rScreen, 0, 2); - } else if (flags & 0x100) { + copyWizImageWithMask(dst, wizd, dstPitch, dstw, dsth, srcx, srcy, srcw, srch, rect, 0, 2); + } else if (flags & kWIFZPlaneOff) { dst = _vm->getMaskBuffer(0, 0, 1); dstPitch /= _vm->_bytesPerPixel; - copyWizImageWithMask(dst, wizd, dstPitch, cw, ch, x1, y1, width, height, &rScreen, 0, 1); + copyWizImageWithMask(dst, wizd, dstPitch, dstw, dsth, srcx, srcy, srcw, srch, rect, 0, 1); } else { - copyWizImage(dst, wizd, dstPitch, dstType, cw, ch, x1, y1, width, height, &rScreen, flags, palPtr, xmapPtr, _vm->_bytesPerPixel); + copyWizImage(dst, wizd, dstPitch, dstType, dstw, dsth, srcx, srcy, srcw, srch, rect, flags, palPtr, xmapPtr, bitDepth); } break; #ifdef USE_RGB_COLOR case 2: - if (maskNum) { - copyMaskWizImage(dst, wizd, mask, dstPitch, dstType, cw, ch, x1, y1, width, height, &rScreen, flags, palPtr); + if (maskPtr) { + copyMaskWizImage(dst, wizd, maskPtr, dstPitch, dstType, dstw, dsth, srcx, srcy, srcw, srch, rect, flags, palPtr); } else { - copyRaw16BitWizImage(dst, wizd, dstPitch, dstType, cw, ch, x1, y1, width, height, &rScreen, flags, transColor); + copyRaw16BitWizImage(dst, wizd, dstPitch, dstType, dstw, dsth, srcx, srcy, srcw, srch, rect, flags, transColor); } break; case 4: - // TODO: Unknown image type + copyCompositeWizImage(dst, dataPtr, wizd, maskPtr, dstPitch, dstType, dstw, dsth, srcx, srcy, srcw, srch, state, rect, flags, palPtr, transColor, bitDepth, xmapPtr, conditionBits); break; case 5: - copy16BitWizImage(dst, wizd, dstPitch, dstType, cw, ch, x1, y1, width, height, &rScreen, flags, xmapPtr); + copy16BitWizImage(dst, wizd, dstPitch, dstType, dstw, dsth, srcx, srcy, srcw, srch, rect, flags, xmapPtr); + break; + case 9: + copy555WizImage(dst, wizd, dstPitch, dstType, dstw, dsth, srcx, srcy, rect, conditionBits); break; #endif default: - error("drawWizImage: Unhandled wiz compression type %d", comp); + error("drawWizImageEx: Unhandled wiz compression type %d", comp); } +} - if (!(flags & kWIFBlitToMemBuffer) && dstResNum == 0) { - Common::Rect rImage(x1, y1, x1 + width, y1 + height); - if (rImage.intersects(rScreen)) { - rImage.clip(rScreen); - if (!(flags & kWIFBlitToFrontVideoBuffer) && (flags & (kWIFBlitToFrontVideoBuffer | kWIFMarkBufferDirty))) { - ++rImage.bottom; - _vm->markRectAsDirty(kMainVirtScreen, rImage); - } else { - _vm->restoreBackgroundHE(rImage); +#ifdef USE_RGB_COLOR + +void Wiz::copyCompositeWizImage(uint8 *dst, uint8 *wizPtr, uint8 *compositeInfoBlockPtr, uint8 *maskPtr, int dstPitch, int dstType, + int dstw, int dsth, int srcx, int srcy, int srcw, int srch, int state, const Common::Rect *clipBox, + int flags, const uint8 *palPtr, int transColor, uint8 bitDepth, const uint8 *xmapPtr, uint32 conditionBits) { + + uint8 *nestedBlockHeader = _vm->heFindResource(MKTAG('N','E','S','T'), wizPtr); + assert(nestedBlockHeader); + + uint8 *nestedWizHeader = _vm->heFindResource(MKTAG('M','U','L','T'), nestedBlockHeader); + assert(nestedWizHeader); + + uint16 layerCount = READ_LE_UINT16(compositeInfoBlockPtr); + compositeInfoBlockPtr += 2; + + uint16 defaultSubConditionBits = (conditionBits & kWMSBReservedBits); + + conditionBits &= ~kWMSBReservedBits; + + for (uint layerCounter = 0; layerCounter < layerCount; layerCounter++) { + int cmdSize = READ_LE_UINT16(compositeInfoBlockPtr); + uint8 *cmdPtr = compositeInfoBlockPtr + 2; + + compositeInfoBlockPtr += (cmdSize + 2); + uint32 layerCmdDataBits = READ_LE_UINT32(cmdPtr); + cmdPtr += 4; + + uint32 subConditionBits; + + if (layerCmdDataBits & kWCFConditionBits) { + uint32 layerConditionBits = READ_LE_UINT32(cmdPtr); + cmdPtr += 4; + + subConditionBits = (layerConditionBits & kWMSBReservedBits); + layerConditionBits &= ~kWMSBReservedBits; + + if (subConditionBits == 0) + subConditionBits = defaultSubConditionBits; + + uint32 conditionType = (layerConditionBits & kWSPCCTBits); + layerConditionBits &= ~kWSPCCTBits; + + switch (conditionType) { + case kWSPCCTAnd: + if (layerConditionBits != (layerConditionBits & conditionBits)) + continue; + break; + + case kWSPCCTNot: + if (layerConditionBits & conditionBits) + continue; + break; + + case kWSPCCTOr: + default: + if (!(layerConditionBits & conditionBits)) + continue; + break; } + } else { + subConditionBits = defaultSubConditionBits; } + + uint16 subState; + if (layerCmdDataBits & kWCFSubState) { + subState = READ_LE_UINT16(cmdPtr); + cmdPtr += 2; + } else { + subState = 0; + } + + int16 xPos; + if (layerCmdDataBits & kWCFXDelta) { + xPos = (int16)READ_LE_UINT16(cmdPtr); + cmdPtr += 2; + } else { + xPos = 0; + } + + int16 yPos; + if (layerCmdDataBits & kWCFYDelta) { + yPos = (int16)READ_LE_UINT16(cmdPtr); + cmdPtr += 2; + } else { + yPos = 0; + } + + uint32 drawFlags; + if (layerCmdDataBits & kWCFDrawFlags) { + drawFlags = READ_LE_UINT32(cmdPtr); + cmdPtr += 4; + } else { + drawFlags = flags; + } + + uint srcw1 = 0, srch1 = 0; + if (drawFlags & (kWIFFlipX | kWIFFlipY)) { + uint8 *wizh = _vm->findWrappedBlock(MKTAG('W','I','Z','H'), wizPtr, subState, 0); + assert(wizh); + srcw1 = READ_LE_UINT32(wizh + 0x4); + srch1 = READ_LE_UINT32(wizh + 0x8); + } + + if (drawFlags & kWIFFlipX) + xPos = (srcw - (xPos + srcw1)); + + if (drawFlags & kWIFFlipY) + yPos = (srch - (yPos + srch1)); + + if (layerCmdDataBits & kWCFSubConditionBits) { + subConditionBits = READ_LE_UINT32(cmdPtr); + cmdPtr += 4; + } + + drawWizImageEx(dst, nestedWizHeader, maskPtr, dstPitch, dstType, dstw, dsth, srcx + xPos, srcy + yPos, srcw, srch, + subState, clipBox, drawFlags, palPtr, transColor, bitDepth, xmapPtr, subConditionBits); + } +} + +void Wiz::copy555WizImage(uint8 *dst, uint8 *wizd, int dstPitch, int dstType, + int dstw, int dsth, int srcx, int srcy, const Common::Rect *clipBox, uint32 conditionBits) { + + int rawROP = conditionBits & kWMSBRopMask; + int paramROP = (conditionBits & kWMSBReservedBits) >> kWMSBRopParamRShift; + + switch (rawROP) { + default: + case 1: + rawROP = 1; + // MMX_PREMUL_ALPHA_COPY + break; + + case 2: + //warning("T14: MMX_ADDITIVE"); + break; + + case 3: + warning("T14: MMX_SUBTRACTIVE"); + break; + + case 4: + warning("T14: MMX_CONSTANT_ALPHA"); + break; + + case 5: + //warning("T14: MMX_CHEAP_50_50"); + break; + + case 6: + warning("T14: COPY"); + break; + + case 7: + warning("T14: CHEAP_50_50"); + break; + } + + + uint32 compID = READ_LE_UINT32(wizd); + + if (compID == 0x12340102) { + ((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) { + error("Unsupported Distortion"); } - return dst; } +#endif + struct PolygonDrawData { struct PolygonArea { int32 xmin; @@ -1747,7 +1946,7 @@ void Wiz::captureWizPolygon(int resNum, int maskNum, int maskState, int id1, int assert(maskNum); const Common::Rect *r = NULL; - const uint8 *src = drawWizImage(maskNum, maskState, 0, 0, 0, 0, 0, 0, 0, r, kWIFBlitToMemBuffer, 0, 0); + const uint8 *src = drawWizImage(maskNum, maskState, 0, 0, 0, 0, 0, 0, 0, r, kWIFBlitToMemBuffer, 0, 0, 0); getWizImageDim(maskNum, maskState, srcw, srch); dstw = wp->bound.width(); @@ -1815,7 +2014,7 @@ void Wiz::drawWizPolygonTransform(int resNum, int state, Common::Point *wp, int debug(0, "drawWizPolygonTransform() unhandled flag 0x800000"); } - srcWizBuf = drawWizImage(resNum, state, 0, 0, 0, 0, 0, shadow, 0, r, flags, 0, _vm->getHEPaletteSlot(palette)); + srcWizBuf = drawWizImage(resNum, state, 0, 0, 0, 0, 0, shadow, 0, r, flags, 0, _vm->getHEPaletteSlot(palette), 0); } else { assert(_vm->_bytesPerPixel == 1); uint8 *dataPtr = _vm->getResourceAddress(rtImage, resNum); @@ -1826,7 +2025,7 @@ void Wiz::drawWizPolygonTransform(int resNum, int state, Common::Point *wp, int } } else { if (getWizImageData(resNum, state, 0) != 0) { - srcWizBuf = drawWizImage(resNum, state, 0, 0, 0, 0, 0, shadow, 0, r, kWIFBlitToMemBuffer, 0, _vm->getHEPaletteSlot(palette)); + srcWizBuf = drawWizImage(resNum, state, 0, 0, 0, 0, 0, shadow, 0, r, kWIFBlitToMemBuffer, 0, _vm->getHEPaletteSlot(palette), 0); } else { uint8 *dataPtr = _vm->getResourceAddress(rtImage, resNum); assert(dataPtr); @@ -2001,7 +2200,7 @@ void Wiz::flushWizBuffer() { drawWizPolygon(pwi->resNum, pwi->state, pwi->x1, pwi->flags, pwi->shadow, 0, pwi->palette); } else { const Common::Rect *r = NULL; - drawWizImage(pwi->resNum, pwi->state, 0, 0, pwi->x1, pwi->y1, pwi->zorder, pwi->shadow, pwi->field_390, r, pwi->flags, 0, _vm->getHEPaletteSlot(pwi->palette)); + drawWizImage(pwi->resNum, pwi->state, 0, 0, pwi->x1, pwi->y1, pwi->zorder, pwi->shadow, pwi->zbuffer, r, pwi->flags, 0, _vm->getHEPaletteSlot(pwi->palette), 0); } } _imagesNum = 0; @@ -2023,7 +2222,7 @@ void Wiz::loadWizCursor(int resId, int palette) { const Common::Rect *r = NULL; _cursorImage = true; - uint8 *cursor = drawWizImage(resId, 0, 0, 0, 0, 0, 0, 0, 0, r, kWIFBlitToMemBuffer, 0, _vm->getHEPaletteSlot(palette)); + uint8 *cursor = drawWizImage(resId, 0, 0, 0, 0, 0, 0, 0, 0, r, kWIFBlitToMemBuffer, 0, _vm->getHEPaletteSlot(palette), 0); _cursorImage = false; int32 cw, ch; @@ -2073,10 +2272,10 @@ void Wiz::displayWizComplexImage(const WizParameters *params) { if (params->processFlags & kWPFShadow) { shadow = params->img.shadow; } - int field_390 = 0; - if (params->processFlags & 0x200000) { - field_390 = params->img.field_390; - debug(0, "displayWizComplexImage() unhandled flag 0x200000"); + int zbuffer = 0; + if (params->processFlags & kWPFZBuffer) { + zbuffer = params->img.zbuffer; + debug(0, "displayWizComplexImage() unhandled flag kWPFZBuffer"); } const Common::Rect *r = NULL; if (params->processFlags & kWPFClipBox) { @@ -2104,19 +2303,19 @@ void Wiz::displayWizComplexImage(const WizParameters *params) { pwi->state = state; pwi->flags = flags; pwi->shadow = shadow; - pwi->field_390 = field_390; + pwi->zbuffer = zbuffer; pwi->palette = palette; ++_imagesNum; } else { if (sourceImage != 0) { - drawWizImage(params->sourceImage, 0, params->img.resNum, state, po_x, po_y, params->img.zorder, shadow, field_390, r, flags, dstResNum, _vm->getHEPaletteSlot(palette)); + drawWizImage(params->sourceImage, 0, params->img.resNum, state, po_x, po_y, params->img.zorder, shadow, zbuffer, r, flags, dstResNum, _vm->getHEPaletteSlot(palette), 0); } else if (params->processFlags & (kWPFScaled | kWPFRotate)) { drawWizComplexPolygon(params->img.resNum, state, po_x, po_y, shadow, rotationAngle, scale, r, flags, dstResNum, palette); } else { if (flags & kWIFIsPolygon) { drawWizPolygon(params->img.resNum, state, po_x, flags, shadow, dstResNum, palette); } else { - drawWizImage(params->img.resNum, state, 0, 0, po_x, po_y, params->img.zorder, shadow, field_390, r, flags, dstResNum, _vm->getHEPaletteSlot(palette)); + drawWizImage(params->img.resNum, state, 0, 0, po_x, po_y, params->img.zorder, shadow, zbuffer, r, flags, dstResNum, _vm->getHEPaletteSlot(palette), params->conditionBits); } } } @@ -2501,6 +2700,10 @@ void Wiz::processWizImage(const WizParameters *params) { void Wiz::getWizImageDim(int resNum, int state, int32 &w, int32 &h) { uint8 *dataPtr = _vm->getResourceAddress(rtImage, resNum); assert(dataPtr); + getWizImageDim(dataPtr, state, w, h); +} + +void Wiz::getWizImageDim(uint8 *dataPtr, int state, int32 &w, int32 &h) { uint8 *wizh = _vm->findWrappedBlock(MKTAG('W','I','Z','H'), dataPtr, state, 0); assert(wizh); w = READ_LE_UINT32(wizh + 0x4); @@ -2510,6 +2713,10 @@ void Wiz::getWizImageDim(int resNum, int state, int32 &w, int32 &h) { void Wiz::getWizImageSpot(int resId, int state, int32 &x, int32 &y) { uint8 *dataPtr = _vm->getResourceAddress(rtImage, resId); assert(dataPtr); + getWizImageSpot(dataPtr, state, x, y); +} + +void Wiz::getWizImageSpot(uint8 *dataPtr, int state, int32 &x, int32 &y) { uint8 *spotPtr = _vm->findWrappedBlock(MKTAG('S','P','O','T'), dataPtr, state, 0); if (spotPtr) { x = READ_LE_UINT32(spotPtr + 0); @@ -2547,6 +2754,11 @@ int Wiz::getWizImageData(int resNum, int state, int type) { int Wiz::getWizImageStates(int resNum) { const uint8 *dataPtr = _vm->getResourceAddress(rtImage, resNum); assert(dataPtr); + + return getWizImageStates(dataPtr); +} + +int Wiz::getWizImageStates(const uint8 *dataPtr) { if (READ_BE_UINT32(dataPtr) == MKTAG('M','U','L','T')) { const byte *offs, *wrap; @@ -2565,9 +2777,14 @@ int Wiz::getWizImageStates(int resNum) { } int Wiz::isWizPixelNonTransparent(int resNum, int state, int x, int y, int flags) { - int ret = 0; uint8 *data = _vm->getResourceAddress(rtImage, resNum); assert(data); + + return isWizPixelNonTransparent(data, state, x, y, flags); +} + +int Wiz::isWizPixelNonTransparent(uint8 *data, int state, int x, int y, int flags) { + int ret = 0; uint8 *wizh = _vm->findWrappedBlock(MKTAG('W','I','Z','H'), data, state, 0); assert(wizh); int c = READ_LE_UINT32(wizh + 0x0); @@ -2591,19 +2808,20 @@ int Wiz::isWizPixelNonTransparent(int resNum, int state, int x, int y, int flags } break; case 1: - ret = isWizPixelNonTransparent(wizd, x, y, w, h, 1); + ret = isPixelNonTransparent(wizd, x, y, w, h, 1); break; #ifdef USE_RGB_COLOR case 2: ret = getRawWizPixelColor(wizd, x, y, w, h, 2, _vm->VAR(_vm->VAR_WIZ_TCOLOR)) != _vm->VAR(_vm->VAR_WIZ_TCOLOR) ? 1 : 0; break; - case 4: - // TODO: Unknown image type - ret = 1; - debug(0, "isWizPixelNonTransparent: Unhandled wiz compression type %d", c); + case 4: { + uint16 color = 0xffff; + copyCompositeWizImage((byte *)&color, data, wizd, 0, 2, kDstMemory, 1, 1, -x, -y, w, h, state, 0, 0, 0, 0, 2, 0, 0); + ret = color != 0xffff; break; + } case 5: - ret = isWizPixelNonTransparent(wizd, x, y, w, h, 2); + ret = isPixelNonTransparent(wizd, x, y, w, h, 2); break; #endif default: @@ -2641,8 +2859,7 @@ uint16 Wiz::getWizPixelColor(int resNum, int state, int x, int y) { color = getRawWizPixelColor(wizd, x, y, w, h, 2, _vm->VAR(_vm->VAR_WIZ_TCOLOR)); break; case 4: - // TODO: Unknown image type - debug(0, "getWizPixelColor: Unhandled wiz compression type %d", c); + copyCompositeWizImage((byte *)&color, data, wizd, 0, 2, kDstMemory, 1, 1, -x, -y, w, h, state, 0, 0, 0, 0, 2, 0, 0); break; case 5: color = getWizPixelColor(wizd, x, y, w, h, 2, _vm->VAR(_vm->VAR_WIZ_TCOLOR)); diff --git a/engines/scumm/he/wiz_he.h b/engines/scumm/he/wiz_he.h index 8db438a074..692ad76db5 100644 --- a/engines/scumm/he/wiz_he.h +++ b/engines/scumm/he/wiz_he.h @@ -43,10 +43,32 @@ struct WizImage { int state; int flags; int shadow; - int field_390; + int zbuffer; int palette; }; +struct FontProperties { + byte string[4096]; + byte fontName[4096]; + int fgColor; + int bgColor; + int style; + int size; + int xPos; + int yPos; +}; + +struct EllipseProperties { + int px; + int py; + int qx; + int qy; + int kx; + int ky; + int lod; + int color; +}; + struct WizParameters { int field_0; byte filename[260]; @@ -77,27 +99,13 @@ struct WizParameters { int remapNum; int dstResNum; uint16 fillColor; - byte string1[4096]; - byte string2[4096]; - int field_2399; - int field_239D; - int field_23A1; - int field_23A5; - int field_23A9; - int field_23AD; - int field_23B1; - int field_23B5; - int field_23B9; - int field_23BD; - int field_23C1; - int field_23C5; - int field_23C9; - int field_23CD; + FontProperties fontProperties; + EllipseProperties ellipseProperties; Common::Rect box2; - int field_23DE; + int blendFlags; int spriteId; int spriteGroup; - int field_23EA; + int conditionBits; WizImage img; }; @@ -109,6 +117,9 @@ enum WizImageFlags { kWIFMarkBufferDirty = 0x10, kWIFBlitToMemBuffer = 0x20, kWIFIsPolygon = 0x40, + kWIFZPlaneOn = 0x80, + kWIFZPlaneOff = 0x100, + kWIFUseShadow = 0x200, kWIFFlipX = 0x400, kWIFFlipY = 0x800 }; @@ -130,7 +141,31 @@ enum WizProcessFlags { kWPFFillColor = 0x20000, kWPFClipBox2 = 0x40000, kWPFMaskImg = 0x80000, - kWPFParams = 0x100000 + kWPFParams = 0x100000, + kWPFZBuffer = 0x200000 +}; + +enum WizCompositeFlags { + kWCFConditionBits = 0x01, + kWCFSubState = 0x02, + kWCFXDelta = 0x04, + kWCFYDelta = 0x08, + kWCFDrawFlags = 0x10, + kWCFSubConditionBits = 0x20 +}; + +enum WizSpcConditionTypes { + kWSPCCTBits = 0xc0000000, + kWSPCCTOr = 0x00000000, + kWSPCCTAnd = 0x40000000, + kWSPCCTNot = 0x80000000 +}; + +enum WizMoonSystemBits { + kWMSBRopMask = 0xff, + kWMSBRopParamMask = 0xff00, + kWMSBReservedBits = (kWMSBRopMask | kWMSBRopParamMask), + kWMSBRopParamRShift = 8 }; enum { @@ -185,14 +220,19 @@ public: void remapWizImagePal(const WizParameters *params); void getWizImageDim(int resNum, int state, int32 &w, int32 &h); + void getWizImageDim(uint8 *dataPtr, int state, int32 &w, int32 &h); int getWizImageStates(int resnum); + int getWizImageStates(const uint8 *ptr); int isWizPixelNonTransparent(int resnum, int state, int x, int y, int flags); + int isWizPixelNonTransparent(uint8 *data, int state, int x, int y, int flags); + int isPixelNonTransparent(const uint8 *data, int x, int y, int w, int h, uint8 bitdepth); uint16 getWizPixelColor(int resnum, int state, int x, int y); int getWizImageData(int resNum, int state, int type); void flushWizBuffer(); void getWizImageSpot(int resId, int state, int32 &x, int32 &y); + void getWizImageSpot(uint8 *data, int state, int32 &x, int32 &y); void loadWizCursor(int resId, int palette); void captureWizImage(int resNum, const Common::Rect& r, bool frontBuffer, int compType); @@ -202,7 +242,8 @@ public: void displayWizImage(WizImage *pwi); void processWizImage(const WizParameters *params); - uint8 *drawWizImage(int resNum, int state, int maskNum, int maskState, int x1, int y1, int zorder, int shadow, int field_390, const Common::Rect *clipBox, int flags, int dstResNum, const uint8 *palPtr); + uint8 *drawWizImage(int resNum, int state, int maskNum, int maskState, int x1, int y1, int zorder, int shadow, int zbuffer, const Common::Rect *clipBox, int flags, int dstResNum, const uint8 *palPtr, uint32 conditionBits); + void drawWizImageEx(uint8 *dst, uint8 *src, uint8 *mask, int dstPitch, int dstType, int dstw, int dsth, int srcx, int srcy, int srcw, int srch, int state, const Common::Rect *rect, int flags, const uint8 *palPtr, int transColor, uint8 bitDepth, const uint8 *xmapPtr, uint32 conditionBits); void drawWizPolygon(int resNum, int state, int id, int flags, int shadow, int dstResNum, int palette); void drawWizComplexPolygon(int resNum, int state, int po_x, int po_y, int shadow, int angle, int zoom, const Common::Rect *r, int flags, int dstResNum, int palette); void drawWizPolygonTransform(int resNum, int state, Common::Point *wp, int flags, int shadow, int dstResNum, int palette); @@ -210,6 +251,12 @@ public: #ifdef USE_RGB_COLOR static void copyMaskWizImage(uint8 *dst, const uint8 *src, const uint8 *mask, int dstPitch, int dstType, int dstw, int dsth, int srcx, int srcy, int srcw, int srch, const Common::Rect *rect, int flags, const uint8 *palPtr); + + void copyCompositeWizImage(uint8 *dst, uint8 *wizPtr, uint8 *wizd, uint8 *maskPtr, int dstPitch, int dstType, + int dstw, int dsth, int srcx, int srcy, int srcw, int srch, int state, const Common::Rect *clipBox, + int flags, const uint8 *palPtr, int transColor, uint8 bitDepth, const uint8 *xmapPtr, uint32 conditionBits); + void copy555WizImage(uint8 *dst, uint8 *wizd, int dstPitch, int dstType, + int dstw, int dsth, int srcx, int srcy, const Common::Rect *clipBox, uint32 conditionBits); #endif static void copyAuxImage(uint8 *dst1, uint8 *dst2, const uint8 *src, int dstw, int dsth, int srcx, int srcy, int srcw, int srch, uint8 bitdepth); @@ -230,7 +277,6 @@ public: template<int type> static void write8BitColor(uint8 *dst, const uint8 *src, int dstType, const uint8 *palPtr, const uint8 *xmapPtr, uint8 bitDepth); static void writeColor(uint8 *dstPtr, int dstType, uint16 color); - int isWizPixelNonTransparent(const uint8 *data, int x, int y, int w, int h, uint8 bitdepth); uint16 getWizPixelColor(const uint8 *data, int x, int y, int w, int h, uint8 bitDepth, uint16 color); uint16 getRawWizPixelColor(const uint8 *data, int x, int y, int w, int h, uint8 bitDepth, uint16 color); void computeWizHistogram(uint32 *histogram, const uint8 *data, const Common::Rect& rCapt); diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp index 12047635a0..6ef7e4d7f4 100644 --- a/engines/scumm/input.cpp +++ b/engines/scumm/input.cpp @@ -24,6 +24,7 @@ #include "common/events.h" #include "common/system.h" #include "common/translation.h" +#include "audio/mixer.h" #include "scumm/debugger.h" #include "scumm/dialogs.h" diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk index 416a8f7ef9..04611ba1b1 100644 --- a/engines/scumm/module.mk +++ b/engines/scumm/module.mk @@ -136,9 +136,19 @@ MODULE_OBJS += \ he/logic/basketball.o \ he/logic/football.o \ he/logic/funshop.o \ - he/logic/moonbase.o \ + he/logic/moonbase_logic.o \ he/logic/puttrace.o \ - he/logic/soccer.o + he/logic/soccer.o \ + he/moonbase/ai_defenseunit.o \ + he/moonbase/ai_main.o \ + he/moonbase/ai_node.o \ + he/moonbase/ai_targetacquisition.o \ + he/moonbase/ai_traveller.o \ + he/moonbase/ai_tree.o \ + he/moonbase/ai_types.o \ + he/moonbase/ai_weapon.o \ + he/moonbase/moonbase.o \ + he/moonbase/moonbase_fow.o endif # This module can be built as a plugin diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h index e986ae4b47..68e4887b00 100644 --- a/engines/scumm/scumm-md5.h +++ b/engines/scumm/scumm-md5.h @@ -1,5 +1,5 @@ /* - This file was generated by the md5table tool on Mon Mar 28 09:52:54 2016 + This file was generated by the md5table tool on Sat Apr 30 14:24:41 2016 DO NOT EDIT MANUALLY! */ @@ -532,6 +532,7 @@ static const MD5Table md5table[] = { { "bf8b52fdd9a69c67f34e8e9fec72661c", "farm", "HE 71", "Demo", -1, Common::EN_ANY, Common::kPlatformWindows }, { "bfdf584b01503f0762baded581f6a0a2", "SoccerMLS", "", "", -1, Common::EN_ANY, Common::kPlatformWindows }, { "c0039ad982999c92d0de81910d640fa0", "freddi", "HE 71", "", 26159, Common::NL_NLD, Common::kPlatformWindows }, + { "c0c9de81fb965e6cbe77f6e5631ca705", "monkey", "SE Talkie", "Unofficial SE Talkie v1.02", 9135, Common::EN_ANY, Common::kPlatformDOS }, { "c13225cb1bbd3bc9fe578301696d8021", "monkey", "SEGA", "", -1, Common::EN_ANY, Common::kPlatformSegaCD }, { "c20848f53c2d48bfacdc840993843765", "freddi2", "HE 80", "Demo", -1, Common::NL_NLD, Common::kPlatformUnknown }, { "c225bec1b6c0798a2b8c89ac226dc793", "pajama", "HE 101", "", -1, Common::EN_ANY, Common::kPlatformWii }, @@ -672,6 +673,7 @@ static const MD5Table md5table[] = { { "f3c5d9bf3f091bd1f18dc1013fba5396", "atlantis", "Steam", "Steam", 638976, Common::EN_ANY, Common::kPlatformWindows }, { "f3d55aea441e260e9e9c7d2a187097e0", "puttzoo", "", "Demo", 14337, Common::EN_ANY, Common::kPlatformWindows }, { "f40a7f495f59188ca57a9d1d50301bb6", "puttputt", "HE 60", "Demo", -1, Common::EN_ANY, Common::kPlatformMacintosh }, + { "f4d20ab4ce19743a646cb48bd93aee72", "monkey2", "SE Talkie", "Unofficial SE Talkie v0.2", 10835, Common::EN_ANY, Common::kPlatformDOS }, { "f5228b0cc1c19e6ea8268ba2eeb61f60", "freddi", "HE 73", "Demo", -1, Common::FR_FRA, Common::kPlatformWindows }, { "f73883f13b5a302749a5bad31d909780", "tentacle", "", "CD", -1, Common::DE_DEU, Common::kPlatformMacintosh }, { "f7635a0e2ab82c9a0f9ace5f232a488f", "catalog", "HE 72", "Demo", -1, Common::EN_ANY, Common::kPlatformWindows }, diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index e7118616ba..d5727f2a7c 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -66,6 +66,7 @@ #include "scumm/players/player_v5m.h" #include "scumm/resource.h" #include "scumm/he/resource_he.h" +#include "scumm/he/moonbase/moonbase.h" #include "scumm/scumm_v0.h" #include "scumm/scumm_v8.h" #include "scumm/sound.h" @@ -733,7 +734,7 @@ ScummEngine_v0::ScummEngine_v0(OSystem *syst, const DetectorResult &dr) VAR_ACTIVE_VERB = 0xFF; if (strcmp(dr.fp.pattern, "maniacdemo.d64") == 0 ) - _game.features |= GF_DEMO; + _game.features |= GF_DEMO; } ScummEngine_v6::ScummEngine_v6(OSystem *syst, const DetectorResult &dr) @@ -874,7 +875,7 @@ ScummEngine_v90he::ScummEngine_v90he(OSystem *syst, const DetectorResult &dr) memset(_videoParams.filename, 0, sizeof(_videoParams.filename)); _videoParams.status = 0; _videoParams.flags = 0; - _videoParams.unk2 = 0; + _videoParams.number = 0; _videoParams.wizResNum = 0; VAR_NUM_SPRITE_GROUPS = 0xFF; @@ -897,6 +898,25 @@ ScummEngine_v90he::~ScummEngine_v90he() { } } +ScummEngine_v100he::ScummEngine_v100he(OSystem *syst, const DetectorResult &dr) : ScummEngine_v99he(syst, dr) { + /* Moonbase stuff */ + _moonbase = 0; + + if (_game.id == GID_MOONBASE) + _moonbase = new Moonbase(this); + + VAR_U32_USER_VAR_A = 0xFF; + VAR_U32_USER_VAR_B = 0xFF; + VAR_U32_USER_VAR_C = 0xFF; + VAR_U32_USER_VAR_D = 0xFF; + VAR_U32_USER_VAR_E = 0xFF; + VAR_U32_USER_VAR_F = 0xFF; +} + +ScummEngine_v100he::~ScummEngine_v100he() { + delete _moonbase; +} + ScummEngine_vCUPhe::ScummEngine_vCUPhe(OSystem *syst, const DetectorResult &dr) : Engine(syst){ _syst = syst; _game = dr.game; diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index 6e0adc3ff3..f9758aec33 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -20,8 +20,8 @@ * */ -#ifndef SCUMM_H -#define SCUMM_H +#ifndef SCUMM_SCUMM_H +#define SCUMM_SCUMM_H #include "engines/engine.h" @@ -704,6 +704,7 @@ protected: virtual int readVar(uint var); virtual void writeVar(uint var, int value); +protected: void beginCutscene(int *args); void endCutscene(); void abortCutscene(); diff --git a/engines/scumm/scumm_v2.h b/engines/scumm/scumm_v2.h index e438008c1e..2a9e7a96e6 100644 --- a/engines/scumm/scumm_v2.h +++ b/engines/scumm/scumm_v2.h @@ -77,9 +77,11 @@ protected: void getResultPosIndirect(); virtual void getResultPos(); + virtual int readVar(uint var); virtual void writeVar(uint var, int value); +protected: virtual int getActiveObject(); void ifStateCommon(byte type); void ifNotStateCommon(byte type); diff --git a/engines/scumm/scumm_v6.h b/engines/scumm/scumm_v6.h index 73268f6f33..83b9f2f4f8 100644 --- a/engines/scumm/scumm_v6.h +++ b/engines/scumm/scumm_v6.h @@ -119,7 +119,10 @@ protected: ArrayHeader *getArray(int array); byte *defineArray(int array, int type, int dim2, int dim1); int findFreeArrayId(); +public: // FIXME. TODO void nukeArray(int array); + +protected: virtual int readArray(int array, int index, int base); virtual void writeArray(int array, int index, int base, int value); void shuffleArray(int num, int minIdx, int maxIdx); diff --git a/engines/scumm/smush/smush_player.cpp b/engines/scumm/smush/smush_player.cpp index 2ca2579b54..42ee0115c7 100644 --- a/engines/scumm/smush/smush_player.cpp +++ b/engines/scumm/smush/smush_player.cpp @@ -25,6 +25,8 @@ #include "common/system.h" #include "common/util.h" +#include "audio/mixer.h" + #include "graphics/cursorman.h" #include "graphics/palette.h" @@ -242,9 +244,15 @@ SmushPlayer::SmushPlayer(ScummEngine_v7 *scumm) { _paused = false; _pauseStartTime = 0; _pauseTime = 0; + + + _IACTchannel = new Audio::SoundHandle(); + _compressedFileSoundHandle = new Audio::SoundHandle(); } SmushPlayer::~SmushPlayer() { + delete _IACTchannel; + delete _compressedFileSoundHandle; } void SmushPlayer::init(int32 speed) { @@ -271,8 +279,8 @@ void SmushPlayer::init(int32 speed) { vs->pitch = vs->w; _vm->_gdi->_numStrips = vs->w / 8; - _vm->_mixer->stopHandle(_compressedFileSoundHandle); - _vm->_mixer->stopHandle(_IACTchannel); + _vm->_mixer->stopHandle(*_compressedFileSoundHandle); + _vm->_mixer->stopHandle(*_IACTchannel); _IACTpos = 0; _vm->_smixer->stop(); } @@ -470,7 +478,7 @@ void SmushPlayer::handleIACT(int32 subSize, Common::SeekableReadStream &b) { if (!_IACTstream) { _IACTstream = Audio::makeQueuingAudioStream(22050, true); - _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_IACTchannel, _IACTstream); + _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, _IACTchannel, _IACTstream); } _IACTstream->queueBuffer(output_data, 0x1000, DisposeAfterUse::YES, Audio::FLAG_STEREO | Audio::FLAG_16BITS); @@ -1091,7 +1099,7 @@ void SmushPlayer::seekSan(const char *file, int32 pos, int32 contFrame) { } void SmushPlayer::tryCmpFile(const char *filename) { - _vm->_mixer->stopHandle(_compressedFileSoundHandle); + _vm->_mixer->stopHandle(*_compressedFileSoundHandle); _compressedFileMode = false; const char *i = strrchr(filename, '.'); @@ -1110,7 +1118,7 @@ void SmushPlayer::tryCmpFile(const char *filename) { strcpy(fname + (i - filename), ".ogg"); if (file->open(fname)) { _compressedFileMode = true; - _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_compressedFileSoundHandle, Audio::makeVorbisStream(file, DisposeAfterUse::YES)); + _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, _compressedFileSoundHandle, Audio::makeVorbisStream(file, DisposeAfterUse::YES)); return; } #endif @@ -1119,7 +1127,7 @@ void SmushPlayer::tryCmpFile(const char *filename) { strcpy(fname + (i - filename), ".mp3"); if (file->open(fname)) { _compressedFileMode = true; - _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, &_compressedFileSoundHandle, Audio::makeMP3Stream(file, DisposeAfterUse::YES)); + _vm->_mixer->playStream(Audio::Mixer::kSFXSoundType, _compressedFileSoundHandle, Audio::makeMP3Stream(file, DisposeAfterUse::YES)); return; } #endif @@ -1185,12 +1193,12 @@ void SmushPlayer::play(const char *filename, int32 speed, int32 offset, int32 st // the sound. Synt to time instead. now = _vm->_system->getMillis() - _pauseTime; elapsed = now - _startTime; - } else if (_vm->_mixer->isSoundHandleActive(_compressedFileSoundHandle)) { + } else if (_vm->_mixer->isSoundHandleActive(*_compressedFileSoundHandle)) { // Compressed SMUSH files. - elapsed = _vm->_mixer->getSoundElapsedTime(_compressedFileSoundHandle); - } else if (_vm->_mixer->isSoundHandleActive(_IACTchannel)) { + elapsed = _vm->_mixer->getSoundElapsedTime(*_compressedFileSoundHandle); + } else if (_vm->_mixer->isSoundHandleActive(*_IACTchannel)) { // Curse of Monkey Island SMUSH files. - elapsed = _vm->_mixer->getSoundElapsedTime(_IACTchannel); + elapsed = _vm->_mixer->getSoundElapsedTime(*_IACTchannel); } else { // For other SMUSH files, we don't necessarily have any // one channel to sync against, so we have to use @@ -1245,8 +1253,8 @@ void SmushPlayer::play(const char *filename, int32 speed, int32 offset, int32 st break; if (_vm->shouldQuit() || _vm->_saveLoadFlag || _vm->_smushVideoShouldFinish) { _smixer->stop(); - _vm->_mixer->stopHandle(_compressedFileSoundHandle); - _vm->_mixer->stopHandle(_IACTchannel); + _vm->_mixer->stopHandle(*_compressedFileSoundHandle); + _vm->_mixer->stopHandle(*_IACTchannel); _IACTpos = 0; break; } diff --git a/engines/scumm/smush/smush_player.h b/engines/scumm/smush/smush_player.h index b0d6e6a9f0..f1dffef7c7 100644 --- a/engines/scumm/smush/smush_player.h +++ b/engines/scumm/smush/smush_player.h @@ -24,9 +24,9 @@ #define SCUMM_SMUSH_PLAYER_H #include "common/util.h" -#include "scumm/sound.h" namespace Audio { +class SoundHandle; class QueuingAudioStream; } @@ -65,10 +65,10 @@ private: bool _skipNext; uint32 _frame; - Audio::SoundHandle _IACTchannel; + Audio::SoundHandle *_IACTchannel; Audio::QueuingAudioStream *_IACTstream; - Audio::SoundHandle _compressedFileSoundHandle; + Audio::SoundHandle *_compressedFileSoundHandle; bool _compressedFileMode; byte _IACToutput[4096]; int32 _IACTpos; diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp index f66452e99c..33b7c3108d 100644 --- a/engines/scumm/sound.cpp +++ b/engines/scumm/sound.cpp @@ -97,12 +97,17 @@ Sound::Sound(ScummEngine *parent, Audio::Mixer *mixer) _loomSteamCD.balance = 0; _isLoomSteam = _vm->_game.id == GID_LOOM && Common::File::exists("CDDA.SOU"); + + _loomSteamCDAudioHandle = new Audio::SoundHandle(); + _talkChannelHandle = new Audio::SoundHandle(); } Sound::~Sound() { stopCDTimer(); stopCD(); free(_offsetTable); + delete _loomSteamCDAudioHandle; + delete _talkChannelHandle; } void Sound::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlags) { @@ -425,7 +430,7 @@ void Sound::processSfxQueues() { if (_talk_sound_mode & 1) startTalkSound(_talk_sound_a1, _talk_sound_b1, 1); if (_talk_sound_mode & 2) - startTalkSound(_talk_sound_a2, _talk_sound_b2, 2, &_talkChannelHandle); + startTalkSound(_talk_sound_a2, _talk_sound_b2, 2, _talkChannelHandle); _talk_sound_mode = 0; } @@ -439,7 +444,7 @@ void Sound::processSfxQueues() { } else if (_vm->_game.heversion >= 60) { finished = !isSoundRunning(1); } else { - finished = !_mixer->isSoundHandleActive(_talkChannelHandle); + finished = !_mixer->isSoundHandleActive(*_talkChannelHandle); } if ((uint) act < 0x80 && ((_vm->_game.version == 8) || (_vm->_game.version <= 7 && !_vm->_string[0].no_talk_anim))) { @@ -675,7 +680,7 @@ void Sound::stopTalkSound() { } else if (_vm->_game.heversion >= 60) { stopSound(1); } else { - _mixer->stopHandle(_talkChannelHandle); + _mixer->stopHandle(*_talkChannelHandle); } _sfxMode &= ~2; } @@ -1060,7 +1065,7 @@ void Sound::playCDTrackInternal(int track, int numLoops, int startFrame, int dur g_system->getAudioCDManager()->play(track, numLoops, startFrame, duration); } else { // Stop any currently playing track - _mixer->stopHandle(_loomSteamCDAudioHandle); + _mixer->stopHandle(*_loomSteamCDAudioHandle); Common::File *cddaFile = new Common::File(); if (cddaFile->open("CDDA.SOU")) { @@ -1068,7 +1073,7 @@ void Sound::playCDTrackInternal(int track, int numLoops, int startFrame, int dur Audio::Timestamp end = Audio::Timestamp(0, startFrame + duration, 75); Audio::SeekableAudioStream *stream = makeCDDAStream(cddaFile, DisposeAfterUse::YES); - _mixer->playStream(Audio::Mixer::kMusicSoundType, &_loomSteamCDAudioHandle, + _mixer->playStream(Audio::Mixer::kMusicSoundType, _loomSteamCDAudioHandle, Audio::makeLoopingAudioStream(stream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops)); } else { delete cddaFile; @@ -1080,14 +1085,14 @@ void Sound::stopCD() { if (!_isLoomSteam) g_system->getAudioCDManager()->stop(); else - _mixer->stopHandle(_loomSteamCDAudioHandle); + _mixer->stopHandle(*_loomSteamCDAudioHandle); } int Sound::pollCD() const { if (!_isLoomSteam) return g_system->getAudioCDManager()->isPlaying(); else - return _mixer->isSoundHandleActive(_loomSteamCDAudioHandle); + return _mixer->isSoundHandleActive(*_loomSteamCDAudioHandle); } void Sound::updateCD() { @@ -1100,7 +1105,7 @@ AudioCDManager::Status Sound::getCDStatus() { return g_system->getAudioCDManager()->getStatus(); else { AudioCDManager::Status info = _loomSteamCD; - info.playing = _mixer->isSoundHandleActive(_loomSteamCDAudioHandle); + info.playing = _mixer->isSoundHandleActive(*_loomSteamCDAudioHandle); return info; } } diff --git a/engines/scumm/sound.h b/engines/scumm/sound.h index 8c11c7b5b2..7fdb16371c 100644 --- a/engines/scumm/sound.h +++ b/engines/scumm/sound.h @@ -26,10 +26,14 @@ #include "common/scummsys.h" #include "common/str.h" #include "audio/mididrv.h" -#include "audio/mixer.h" #include "backends/audiocd/audiocd.h" #include "scumm/saveload.h" +namespace Audio { +class Mixer; +class SoundHandle; +} + namespace Scumm { class ScummEngine; @@ -81,12 +85,12 @@ protected: int16 _currentCDSound; int16 _currentMusic; - Audio::SoundHandle _loomSteamCDAudioHandle; + Audio::SoundHandle *_loomSteamCDAudioHandle; bool _isLoomSteam; AudioCDManager::Status _loomSteamCD; public: - Audio::SoundHandle _talkChannelHandle; // Handle of mixer channel actor is talking on + Audio::SoundHandle *_talkChannelHandle; // Handle of mixer channel actor is talking on bool _soundsPaused; byte _sfxMode; diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp index 3049fbcf62..e6054918fa 100644 --- a/engines/scumm/string.cpp +++ b/engines/scumm/string.cpp @@ -23,6 +23,7 @@ #include "common/config-manager.h" +#include "audio/mixer.h" #include "scumm/actor.h" #include "scumm/charset.h" @@ -662,7 +663,7 @@ void ScummEngine::CHARSET_1() { // Special case for HE games } else if (_game.id == GID_LOOM && !ConfMan.getBool("subtitles") && (_sound->pollCD())) { // Special case for Loom (CD), since it only uses CD audio.for sound - } else if (!ConfMan.getBool("subtitles") && (!_haveActorSpeechMsg || _mixer->isSoundHandleActive(_sound->_talkChannelHandle))) { + } else if (!ConfMan.getBool("subtitles") && (!_haveActorSpeechMsg || _mixer->isSoundHandleActive(*_sound->_talkChannelHandle))) { // Subtitles are turned off, and there is a voice version // of this message -> don't print it. } else { diff --git a/engines/scumm/vars.cpp b/engines/scumm/vars.cpp index a6be5c3f3a..e5ba4a68ee 100644 --- a/engines/scumm/vars.cpp +++ b/engines/scumm/vars.cpp @@ -341,6 +341,19 @@ void ScummEngine_v90he::setupScummVars() { 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; + VAR_U32_USER_VAR_C = 110; + VAR_U32_USER_VAR_D = 111; + VAR_U32_USER_VAR_E = 112; + VAR_U32_USER_VAR_F = 113; + } +} #endif #ifdef ENABLE_SCUMM_7_8 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/image_file.cpp b/engines/sherlock/image_file.cpp index 1247a7f8d0..c53e537bb8 100644 --- a/engines/sherlock/image_file.cpp +++ b/engines/sherlock/image_file.cpp @@ -302,12 +302,6 @@ ImageFile3DO::ImageFile3DO(Common::SeekableReadStream &stream, bool isRoomData) } } -ImageFile3DO::~ImageFile3DO() { - // already done in ImageFile destructor - //for (uint idx = 0; idx < size(); ++idx) - // (*this)[idx]._frame.free(); -} - void ImageFile3DO::load(Common::SeekableReadStream &stream, bool isRoomData) { uint32 headerId = 0; @@ -380,7 +374,7 @@ void ImageFile3DO::loadAnimationFile(Common::SeekableReadStream &stream) { if (streamLeft < celDataSize) error("load3DOAnimationFile: expected cel data, not enough bytes"); - // + // // Load data for frame and decompress it byte *data = new byte[celDataSize]; stream.read(data, celDataSize); @@ -683,7 +677,7 @@ void ImageFile3DO::load3DOCelRoomData(Common::SeekableReadStream &stream) { if (ccbFlags & 0x200) // bit 9 ccbFlags_compressed = true; - + // PRE0 first 3 bits define how many bits per encoded pixel are used ccbPRE0_bitsPerPixel = imagefile3DO_cel_bitsPerPixelLookupTable[ccbPRE0 & 0x07]; if (!ccbPRE0_bitsPerPixel) @@ -713,7 +707,7 @@ void ImageFile3DO::load3DOCelRoomData(Common::SeekableReadStream &stream) { stream.read(celDataPtr, celDataSize); streamLeft -= celDataSize; - + // Set up frame { ImageFrame imageFrame; diff --git a/engines/sherlock/image_file.h b/engines/sherlock/image_file.h index 778332b726..0ae7cc1dcb 100644 --- a/engines/sherlock/image_file.h +++ b/engines/sherlock/image_file.h @@ -95,7 +95,7 @@ public: ImageFile(); ImageFile(const Common::String &name, bool skipPal = false, bool animImages = false); ImageFile(Common::SeekableReadStream &stream, bool skipPal = false); - ~ImageFile(); + virtual ~ImageFile(); static void setVm(SherlockEngine *vm); }; @@ -155,7 +155,6 @@ private: public: ImageFile3DO(const Common::String &name, ImageFile3DOType imageFile3DOType); ImageFile3DO(Common::SeekableReadStream &stream, bool isRoomData = false); - ~ImageFile3DO(); static void setVm(SherlockEngine *vm); }; 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/sherlock.h b/engines/sherlock/sherlock.h index d3b2d0cac8..0b4333ea3a 100644 --- a/engines/sherlock/sherlock.h +++ b/engines/sherlock/sherlock.h @@ -20,8 +20,8 @@ * */ -#ifndef SHERLOCK_HOLMES_H -#define SHERLOCK_HOLMES_H +#ifndef SHERLOCK_SHERLOCK_H +#define SHERLOCK_SHERLOCK_H #include "common/scummsys.h" #include "common/array.h" 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/sherlock/tattoo/tattoo_fixed_text.h b/engines/sherlock/tattoo/tattoo_fixed_text.h index 7dbe13bbb3..eb636cdada 100644 --- a/engines/sherlock/tattoo/tattoo_fixed_text.h +++ b/engines/sherlock/tattoo/tattoo_fixed_text.h @@ -233,7 +233,7 @@ public: virtual const Common::String getActionMessage(FixedTextActionId actionId, int messageIndex); }; -} // End of namespace Scalpel +} // End of namespace Tattoo } // End of namespace Sherlock diff --git a/engines/sherlock/tattoo/tattoo_journal.cpp b/engines/sherlock/tattoo/tattoo_journal.cpp index 8e1a61d36e..4d4f37f8d5 100644 --- a/engines/sherlock/tattoo/tattoo_journal.cpp +++ b/engines/sherlock/tattoo/tattoo_journal.cpp @@ -50,7 +50,7 @@ void TattooJournal::show() { Screen &screen = *_vm->_screen; TattooUserInterface &ui = *(TattooUserInterface *)_vm->_ui; byte palette[PALETTE_SIZE]; - + Common::Point oldScroll = screen._currentScroll; screen._currentScroll = Common::Point(0, 0); @@ -66,7 +66,7 @@ void TattooJournal::show() { // Set screen to black, and set background screen._backBuffer1.SHblitFrom((*_journalImages)[0], Common::Point(0, 0)); - screen.empty(); + screen.clear(); screen.setPalette(palette); if (_journal.empty()) { @@ -87,12 +87,12 @@ void TattooJournal::show() { handleKeyboardEvents(); highlightJournalControls(true); - + handleButtons(); if (_wait) events.wait(2); - + } while (!_vm->shouldQuit() && !_exitJournal); // Clear events @@ -275,7 +275,7 @@ void TattooJournal::handleButtons() { if (frameCounter >= _scrollingTimer) { // Set next scrolling time _scrollingTimer = frameCounter + 6; - + // Handle different scrolling actions switch (_selector) { case JH_SCROLL_LEFT: @@ -355,13 +355,13 @@ void TattooJournal::handleButtons() { _savedIndex = _index; _savedSub = _sub; _savedPage = _page; - + bool drawResult = drawJournal(dir + 2, 1000 * LINES_PER_PAGE); if (!drawResult) { _index = _savedIndex; _sub = _savedSub; _page = _savedPage; - + drawFrame(); drawJournal(0, 0); notFound = true; @@ -539,7 +539,7 @@ void TattooJournal::drawControls(int mode) { _oldSelector = 100; switch (mode) { - case 0: + case 0: highlightJournalControls(false); break; case 1: @@ -548,7 +548,7 @@ void TattooJournal::drawControls(int mode) { default: break; } - + _oldSelector = savedSelector; } @@ -558,7 +558,7 @@ void TattooJournal::highlightJournalControls(bool slamIt) { Common::Point mousePos = events.mousePos(); Common::Rect r(JOURNAL_BAR_WIDTH, BUTTON_SIZE + screen.fontHeight() + 13); r.moveTo((SHERLOCK_SCREEN_WIDTH - r.width()) / 2, SHERLOCK_SCREEN_HEIGHT - r.height()); - + if ((events._pressed || events._released) && _selector == JH_THUMBNAIL) { if (events._released) _selector = JH_NONE; @@ -576,7 +576,7 @@ void TattooJournal::highlightJournalControls(bool slamIt) { _selector = JH_NONE; if (bounds.contains(mousePos)) _selector = (mousePos.x - r.left) / (r.width() / 3); - + else if (events._pressed && mousePos.y >= (r.top + screen.fontHeight() + 10) && mousePos.y < (r.top + screen.fontHeight() + 10 + BUTTON_SIZE)) { if (mousePos.x >= r.left && mousePos.x < (r.left + BUTTON_SIZE)) @@ -628,7 +628,7 @@ void TattooJournal::highlightJournalControls(bool slamIt) { color = (_selector == JH_SAVE) ? COMMAND_HIGHLIGHTED : INFO_TOP; else color = INFO_BOTTOM; - screen.gPrint(Common::Point(xp - screen.stringWidth(FIXED(SaveJournal)) / 2, r.top + 5), + screen.gPrint(Common::Point(xp - screen.stringWidth(FIXED(SaveJournal)) / 2, r.top + 5), color, "%s", FIXED(SaveJournal)); // Draw the horizontal scrollbar @@ -701,7 +701,7 @@ void TattooJournal::drawScrollBar() { raised = _selector != JH_SCROLL_LEFT; screen._backBuffer1.fillRect(Common::Rect(r.left, r.top + screen.fontHeight() + 12, r.left + BUTTON_SIZE, r.top + screen.fontHeight() + BUTTON_SIZE + 9), INFO_MIDDLE); - ui.drawDialogRect(screen._backBuffer1, Common::Rect(r.left + 3, r.top + screen.fontHeight() + 10, r.left + 3 + BUTTON_SIZE, + ui.drawDialogRect(screen._backBuffer1, Common::Rect(r.left + 3, r.top + screen.fontHeight() + 10, r.left + 3 + BUTTON_SIZE, r.top + screen.fontHeight() + 10 + BUTTON_SIZE), raised); color = (_page > 1) ? INFO_BOTTOM + 2 : INFO_BOTTOM; @@ -716,15 +716,15 @@ void TattooJournal::drawScrollBar() { // Draw the Scroll Right button raised = _selector != JH_SCROLL_RIGHT; - screen._backBuffer1.fillRect(Common::Rect(r.right - BUTTON_SIZE - 1, r.top + screen.fontHeight() + 12, + screen._backBuffer1.fillRect(Common::Rect(r.right - BUTTON_SIZE - 1, r.top + screen.fontHeight() + 12, r.right - 5, r.top + screen.fontHeight() + BUTTON_SIZE + 9), INFO_MIDDLE); ui.drawDialogRect(screen._backBuffer1, Common::Rect(r.right - BUTTON_SIZE - 3, r.top + screen.fontHeight() + 10, r.right - 3, r.top + screen.fontHeight() + BUTTON_SIZE + 9), raised); color = _down ? INFO_BOTTOM + 2 : INFO_BOTTOM; - screen._backBuffer1.vLine(r.right - 1 - BUTTON_SIZE + BUTTON_SIZE / 2, r.top + screen.fontHeight() + 10 + BUTTON_SIZE / 2, + screen._backBuffer1.vLine(r.right - 1 - BUTTON_SIZE + BUTTON_SIZE / 2, r.top + screen.fontHeight() + 10 + BUTTON_SIZE / 2, r.top + screen.fontHeight() + 10 + BUTTON_SIZE / 2, color); - screen._backBuffer1.vLine(r.right - 2 - BUTTON_SIZE + BUTTON_SIZE / 2, r.top + screen.fontHeight() + 9 + BUTTON_SIZE / 2, + screen._backBuffer1.vLine(r.right - 2 - BUTTON_SIZE + BUTTON_SIZE / 2, r.top + screen.fontHeight() + 9 + BUTTON_SIZE / 2, r.top + screen.fontHeight() + 11 + BUTTON_SIZE / 2, color); screen._backBuffer1.vLine(r.right - 3 - BUTTON_SIZE + BUTTON_SIZE / 2, r.top + screen.fontHeight() + 8 + BUTTON_SIZE / 2, r.top + screen.fontHeight() + 12 + BUTTON_SIZE / 2, color); @@ -776,14 +776,14 @@ int TattooJournal::getFindName(bool printError) { drawControls(1); disableControls(); - + // Backup the area under the text entry Surface bgSurface(r.width() - 6, screen.fontHeight()); - bgSurface.SHblitFrom(screen._backBuffer1, Common::Point(0, 0), Common::Rect(r.left + 3, cursorY, + bgSurface.SHblitFrom(screen._backBuffer1, Common::Point(0, 0), Common::Rect(r.left + 3, cursorY, r.right - 3, cursorY + screen.fontHeight())); if (printError) { - screen.gPrint(Common::Point(r.left + (r.width() - screen.stringWidth(FIXED(TextNotFound))) / 2, cursorY), + screen.gPrint(Common::Point(r.left + (r.width() - screen.stringWidth(FIXED(TextNotFound))) / 2, cursorY), INFO_TOP, "%s", FIXED(TextNotFound)); } else { // If there was a name already entered, copy it to name and display it @@ -977,7 +977,7 @@ void TattooJournal::saveJournal() { int line = 0; // Copy all of the talk files entries into one big string - do { + do { if (_lines[line].hasPrefix("@")) { text += Common::String(_lines[line].c_str() + 1); if ((line + 1) < (int)_lines.size() && _lines[line + 1].hasPrefix("@")) @@ -992,7 +992,7 @@ void TattooJournal::saveJournal() { // which show up as a blank line with the next line starting // with a '@'. We have to add a line break here because the '@' handler // previously assumes that they're always following a blank line - + if ((_lines[line].empty() || _lines[line] == " ") && (line + 1) < (int)_lines.size() && _lines[line + 1].hasPrefix("@")) text += "\n"; @@ -1005,7 +1005,7 @@ void TattooJournal::saveJournal() { do { if (text.size() > 80) { const char *msgP = text.c_str() + 80; - + if (Common::String(text.c_str(), msgP).contains("\n")) { // The 80 characters contain a carriage return, // so we can print out that line @@ -1013,7 +1013,7 @@ void TattooJournal::saveJournal() { file->writeString(Common::String(text.c_str(), cr)); text = Common::String(cr + 1); } else { - // Move backwards to find a word break + // Move backwards to find a word break while (*msgP != ' ') --msgP; diff --git a/engines/sherlock/tattoo/tattoo_people.h b/engines/sherlock/tattoo/tattoo_people.h index e0d53c67dd..c844d86e19 100644 --- a/engines/sherlock/tattoo/tattoo_people.h +++ b/engines/sherlock/tattoo/tattoo_people.h @@ -273,9 +273,8 @@ public: virtual void setListenSequence(int speaker, int sequenceNum = 1); }; -} // End of namespace Scalpel +} // End of namespace Tattoo } // End of namespace Sherlock - #endif diff --git a/engines/sky/control.cpp b/engines/sky/control.cpp index dfdd765120..9f4b6c21c6 100644 --- a/engines/sky/control.cpp +++ b/engines/sky/control.cpp @@ -167,7 +167,7 @@ ControlStatus::~ControlStatus() { void ControlStatus::setToText(const char *newText) { char tmpLine[256]; - strcpy(tmpLine, newText); + Common::strlcpy(tmpLine, newText, 256); if (_textData) { _statusText->flushForRedraw(); free(_textData); @@ -324,7 +324,11 @@ void Control::initPanel() { } void Control::buttonControl(ConResource *pButton) { - char autoSave[] = "Restore Autosave"; + char autoSave[50] = "Restore Autosave"; + + if (Common::parseLanguage(ConfMan.get("language")) == Common::RU_RUS) + strncpy(autoSave, "Zarpyzit/ abtocoxpahehie", 50); + if (pButton == NULL) { free(_textSprite); _textSprite = NULL; @@ -398,7 +402,8 @@ void Control::animClick(ConResource *pButton) { void Control::drawMainPanel() { memset(_screenBuf, 0, GAME_SCREEN_WIDTH * FULL_SCREEN_HEIGHT); _system->copyRectToScreen(_screenBuf, GAME_SCREEN_WIDTH, 0, 0, GAME_SCREEN_WIDTH, FULL_SCREEN_HEIGHT); - _controlPanel->drawToScreen(NO_MASK); + if (_controlPanel) + _controlPanel->drawToScreen(NO_MASK); _exitButton->drawToScreen(NO_MASK); _savePanButton->drawToScreen(NO_MASK); _restorePanButton->drawToScreen(NO_MASK); @@ -525,8 +530,13 @@ void Control::doControlPanel() { } uint16 Control::handleClick(ConResource *pButton) { - char quitDos[] = "Quit to DOS?"; - char restart[] = "Restart?"; + char quitDos[50] = "Quit to DOS?"; + char restart[50] = "Restart?"; + + if (Common::parseLanguage(ConfMan.get("language")) == Common::RU_RUS) { + strncpy(quitDos, "B[uti b DOC?", 50); + strncpy(restart, "Hobaq irpa?", 50); + } switch (pButton->_onClick) { case DO_NOTHING: @@ -1562,8 +1572,13 @@ void Control::showGameQuitMsg() { screenData = _skyScreen->giveCurrent(); - _skyText->displayText(_quitTexts[SkyEngine::_systemVars.language * 2 + 0], textBuf1, true, 320, 255); - _skyText->displayText(_quitTexts[SkyEngine::_systemVars.language * 2 + 1], textBuf2, true, 320, 255); + if (Common::parseLanguage(ConfMan.get("language")) == Common::RU_RUS) { + _skyText->displayText(_quitTexts[8 * 2 + 0], textBuf1, true, 320, 255); + _skyText->displayText(_quitTexts[8 * 2 + 1], textBuf2, true, 320, 255); + } else { + _skyText->displayText(_quitTexts[SkyEngine::_systemVars.language * 2 + 0], textBuf1, true, 320, 255); + _skyText->displayText(_quitTexts[SkyEngine::_systemVars.language * 2 + 1], textBuf2, true, 320, 255); + } uint8 *curLine1 = textBuf1 + sizeof(DataFileHeader); uint8 *curLine2 = textBuf2 + sizeof(DataFileHeader); uint8 *targetLine = screenData + GAME_SCREEN_WIDTH * 80; @@ -1584,7 +1599,7 @@ void Control::showGameQuitMsg() { free(textBuf2); } -char Control::_quitTexts[16][35] = { +char Control::_quitTexts[18][35] = { "Game over player one", "BE VIGILANT", "Das Spiel ist aus.", @@ -1600,7 +1615,9 @@ char Control::_quitTexts[16][35] = { "Fim de jogo para o jogador um", "BE VIGILANT", "Game over player one", - "BE VIGILANT" + "BE VIGILANT", + "Irpa okohseha, irpok 1", + "JYD\x96 JDITELEH" }; uint8 Control::_crossImg[594] = { diff --git a/engines/sky/control.h b/engines/sky/control.h index 44591f299d..2089c74363 100644 --- a/engines/sky/control.h +++ b/engines/sky/control.h @@ -292,7 +292,7 @@ private: ControlStatus *_statusBar; - static char _quitTexts[16][35]; + static char _quitTexts[18][35]; static uint8 _crossImg[594]; }; diff --git a/engines/sky/sky.h b/engines/sky/sky.h index 56833004ac..374302b8d3 100644 --- a/engines/sky/sky.h +++ b/engines/sky/sky.h @@ -20,8 +20,8 @@ * */ -#ifndef SKY_H -#define SKY_H +#ifndef SKY_SKY_H +#define SKY_SKY_H #include "common/error.h" diff --git a/engines/sky/skydefs.h b/engines/sky/skydefs.h index 167b7dade2..ed07a5e2cd 100644 --- a/engines/sky/skydefs.h +++ b/engines/sky/skydefs.h @@ -45,6 +45,7 @@ namespace Sky { #define SKY_ITALIAN 5 #define SKY_PORTUGUESE 6 #define SKY_SPANISH 7 +#define SKY_RUSSIAN 8 #define ST_COLLISION_BIT 5 diff --git a/engines/sword1/resman.cpp b/engines/sword1/resman.cpp index 0a0324a67c..2f8b37d21c 100644 --- a/engines/sword1/resman.cpp +++ b/engines/sword1/resman.cpp @@ -327,13 +327,12 @@ Common::File *ResMan::resFile(uint32 id) { Clu *closeClu = _openCluStart; _openCluStart = _openCluStart->nextOpen; - if (closeClu) { - if (closeClu->file) - closeClu->file->close(); - delete closeClu->file; - closeClu->file = NULL; - closeClu->nextOpen = NULL; - } + if (closeClu->file) + closeClu->file->close(); + delete closeClu->file; + closeClu->file = NULL; + closeClu->nextOpen = NULL; + _openClus--; } } diff --git a/engines/sword1/sword1.h b/engines/sword1/sword1.h index eb24ef70fa..8dab522fd0 100644 --- a/engines/sword1/sword1.h +++ b/engines/sword1/sword1.h @@ -20,8 +20,8 @@ * */ -#ifndef SWORD1_H -#define SWORD1_H +#ifndef SWORD1_SWORD1_H +#define SWORD1_SWORD1_H #include "engines/engine.h" #include "common/error.h" @@ -150,4 +150,4 @@ private: } // End of namespace Sword1 -#endif //BSSWORD1_H +#endif // SWORD1_SWORD1_H diff --git a/engines/sword2/screen.cpp b/engines/sword2/screen.cpp index 0cb951fdfc..40baf67e46 100644 --- a/engines/sword2/screen.cpp +++ b/engines/sword2/screen.cpp @@ -1296,7 +1296,7 @@ void Screen::setPsxScrCache(byte *psxScrCache, uint8 level) { } byte *Screen::getPsxScrCache(uint8 level) { - if (level > 3) { + if (level > 2) { level = 0; } @@ -1307,7 +1307,7 @@ byte *Screen::getPsxScrCache(uint8 level) { } bool Screen::getPsxScrCacheStatus(uint8 level) { - if (level > 3) { + if (level > 2) { level = 0; } diff --git a/engines/sword2/sword2.h b/engines/sword2/sword2.h index ef5c2b215e..65d1a955fb 100644 --- a/engines/sword2/sword2.h +++ b/engines/sword2/sword2.h @@ -22,8 +22,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#ifndef SWORD2_H -#define SWORD2_H +#ifndef SWORD2_SWORD2_H +#define SWORD2_SWORD2_H #define FRAMES_PER_SECOND 12 diff --git a/engines/sword25/gfx/image/vectorimage.cpp b/engines/sword25/gfx/image/vectorimage.cpp index a678fdccad..5d35a4f47e 100644 --- a/engines/sword25/gfx/image/vectorimage.cpp +++ b/engines/sword25/gfx/image/vectorimage.cpp @@ -217,6 +217,7 @@ Common::Rect CalculateBoundingBox(const VectorImageElement &vectorImageElement) VectorImage::VectorImage(const byte *pFileData, uint fileSize, bool &success, const Common::String &fname) : _pixelData(0), _fname(fname) { success = false; + _bgColor = 0; // Create bitstream object // In the following the file data will be readout of the bitstream object. diff --git a/engines/sword25/gfx/renderobject.cpp b/engines/sword25/gfx/renderobject.cpp index c109e49aa8..92d39c252d 100644 --- a/engines/sword25/gfx/renderobject.cpp +++ b/engines/sword25/gfx/renderobject.cpp @@ -71,19 +71,18 @@ RenderObject::RenderObject(RenderObjectPtr<RenderObject> parentPtr, TYPES type, _version(++_nextGlobalVersion), _isSolid(false) { - // Renderobject registrieren, abhängig vom Handle-Parameter entweder mit beliebigem oder vorgegebenen Handle. if (handle == 0) _handle = RenderObjectRegistry::instance().registerObject(this); else _handle = RenderObjectRegistry::instance().registerObject(this, handle); if (_handle == 0) - return; + error("Failed to initialize RenderObject()"); updateAbsolutePos(); - // Dieses Objekt zu den Kindern der Elternobjektes hinzufügen, falls nicht Wurzel (ParentPtr ungültig) und dem - // selben RenderObjektManager zuweisen. + // Add this item to the children of the parent object, if not root (ParentPtr is invalid), + // assign to the same RenderObjectManager. if (_parentPtr.isValid()) { _managerPtr = _parentPtr->getManager(); _parentPtr->addObject(this->getHandle()); @@ -100,24 +99,22 @@ RenderObject::RenderObject(RenderObjectPtr<RenderObject> parentPtr, TYPES type, } RenderObject::~RenderObject() { - // Objekt aus dem Elternobjekt entfernen. + // Remove object from its parent. if (_parentPtr.isValid()) _parentPtr->detatchChildren(this->getHandle()); deleteAllChildren(); - // Objekt deregistrieren. RenderObjectRegistry::instance().deregisterObject(this); } void RenderObject::preRender(RenderObjectQueue *renderQueue) { - // Objektänderungen validieren validateObject(); if (!_visible) return; - // Falls notwendig, wird die Renderreihenfolge der Kinderobjekte aktualisiert. + // If necessary, update the children rendering order of the updated objects. if (_childChanged) { sortRenderObjects(); _childChanged = false; @@ -149,7 +146,7 @@ bool RenderObject::render(RectangleList *updateRects, const Common::Array<int> & if (needRender) doRender(updateRects); - // Dann müssen die Kinder gezeichnet werden + // Draw all children RENDEROBJECT_ITER it = _children.begin(); for (; it != _children.end(); ++it) if (!(*it)->render(updateRects, updateRectsMinZ)) @@ -159,7 +156,6 @@ bool RenderObject::render(RectangleList *updateRects, const Common::Array<int> & } void RenderObject::validateObject() { - // Die Veränderungen in den Objektvariablen aufheben _oldBbox = _bbox; _oldVisible = _visible; _oldX = _x; @@ -169,15 +165,14 @@ void RenderObject::validateObject() { } bool RenderObject::updateObjectState() { - // Falls sich das Objekt verändert hat, muss der interne Zustand neu berechnet werden und evtl. Update-Regions für den nächsten Frame - // registriert werden. + // If the object has changed, the internal state must be recalculated and possibly + // update Regions be registered for the next frame. if ((calcBoundingBox() != _oldBbox) || (_visible != _oldVisible) || (_x != _oldX) || (_y != _oldY) || (_z != _oldZ) || _refreshForced) { - // Renderrang des Objektes neu bestimmen, da sich dieser verändert haben könnte if (_parentPtr.isValid()) _parentPtr->signalChildChange(); @@ -200,12 +195,10 @@ bool RenderObject::updateObjectState() { } void RenderObject::updateBoxes() { - // Bounding-Box aktualisieren _bbox = calcBoundingBox(); } Common::Rect RenderObject::calcBoundingBox() const { - // Die Bounding-Box mit der Objektgröße initialisieren. Common::Rect bbox(0, 0, _width, _height); // Die absolute Position der Bounding-Box berechnen. @@ -247,8 +240,6 @@ int32 RenderObject::calcAbsoluteZ() const { } void RenderObject::deleteAllChildren() { - // Es ist nicht notwendig die Liste zu iterieren, da jedes Kind für sich DetatchChildren an diesem Objekt aufruft und sich somit - // selber entfernt. Daher muss immer nur ein beliebiges Element (hier das letzte) gelöscht werden, bis die Liste leer ist. while (!_children.empty()) { RenderObjectPtr<RenderObject> curPtr = _children.back(); curPtr.erase(); @@ -261,10 +252,10 @@ bool RenderObject::addObject(RenderObjectPtr<RenderObject> pObject) { return false; } - // Objekt in die Kinderliste einfügen. + // Insert Object in the children list. _children.push_back(pObject); - // Sicherstellen, dass vor dem nächsten Rendern die Renderreihenfolge aktualisiert wird. + // Make sure that before the next render the channel order is updated. if (_parentPtr.isValid()) _parentPtr->signalChildChange(); diff --git a/engines/sword25/package/packagemanager.cpp b/engines/sword25/package/packagemanager.cpp index 457dda6268..87bedcc9c1 100644 --- a/engines/sword25/package/packagemanager.cpp +++ b/engines/sword25/package/packagemanager.cpp @@ -82,7 +82,7 @@ Common::String PackageManager::ensureSpeechLang(const Common::String &fileName) return fileName; Common::String newFileName = "/speech/en"; - int fileIdx = 9; + uint fileIdx = 9; while (fileIdx < fileName.size() && fileName[fileIdx] != '/') ++fileIdx; if (fileIdx < fileName.size()) diff --git a/engines/sword25/sword25.h b/engines/sword25/sword25.h index 72391cf9d8..c08f20e3b3 100644 --- a/engines/sword25/sword25.h +++ b/engines/sword25/sword25.h @@ -20,8 +20,8 @@ * */ -#ifndef SWORD25_H -#define SWORD25_H +#ifndef SWORD25_SWORD25_H +#define SWORD25_SWORD25_H #include "common/scummsys.h" #include "engines/engine.h" diff --git a/engines/teenagent/music.cpp b/engines/teenagent/music.cpp index 5d66c3c90c..795b8f7312 100644 --- a/engines/teenagent/music.cpp +++ b/engines/teenagent/music.cpp @@ -36,7 +36,7 @@ static const uint32 noteToPeriod[3][12] = { {214, 201, 189, 179, 170, 160, 151, 143, 135, 127, 120, 113} }; -MusicPlayer::MusicPlayer(TeenAgentEngine *vm) : Paula(false, 44100, 5000), _vm(vm), _id(0) { +MusicPlayer::MusicPlayer(TeenAgentEngine *vm) : Paula(false, 44100, 5000), _vm(vm), _id(0), _currRow(0) { } MusicPlayer::~MusicPlayer() { @@ -55,7 +55,7 @@ bool MusicPlayer::load(int id) { Common::StackLock lock(_mutex); // Load the samples - sampleCount = stream->readByte(); + byte sampleCount = stream->readByte(); debugC(0, kDebugMusic, "sampleCount = %d", sampleCount); diff --git a/engines/teenagent/music.h b/engines/teenagent/music.h index e1630cc845..4b1b683a30 100644 --- a/engines/teenagent/music.h +++ b/engines/teenagent/music.h @@ -74,7 +74,6 @@ private: size = 0; } } _samples[256]; - byte sampleCount; Common::Array<Row> _rows; uint _currRow; diff --git a/engines/teenagent/objects.h b/engines/teenagent/objects.h index f923ae52ab..1f8a82a66e 100644 --- a/engines/teenagent/objects.h +++ b/engines/teenagent/objects.h @@ -165,7 +165,7 @@ struct Object { //19 Common::String name, description; - Object(): _base(NULL) {} + Object(): _base(NULL) { id = 0; actorOrientation = 0; enabled = 0; } void dump(int level = 0) const; void setName(const Common::String &newName); void load(byte *addr); @@ -205,7 +205,7 @@ struct Walkbox { Rect rect; byte sideHint[4]; - Walkbox() : _base(NULL) {} + Walkbox() : _base(NULL) { memset(this, 0, sizeof(Walkbox)); } void dump(int level = 0) const; void load(byte *src); void save() const; diff --git a/engines/teenagent/scene.cpp b/engines/teenagent/scene.cpp index 6e1cef31bc..c250269844 100644 --- a/engines/teenagent/scene.cpp +++ b/engines/teenagent/scene.cpp @@ -71,6 +71,9 @@ Scene::Scene(TeenAgentEngine *vm) : _vm(vm), intro(false), _id(0), ons(0), varia.close(); loadObjectData(); + + _onsCount = 0; + _messageColor = 0; } Scene::~Scene() { @@ -314,7 +317,7 @@ void Scene::loadOns() { uint16 addr = _vm->res->dseg.get_word(dsAddr_onsAnimationTablePtr + (_id - 1) * 2); debugC(0, kDebugScene, "ons index: %04x", addr); - onsCount = 0; + _onsCount = 0; byte b; byte onId[16]; while ((b = _vm->res->dseg.get_byte(addr)) != 0xff) { @@ -323,15 +326,15 @@ void Scene::loadOns() { if (b == 0) continue; - onId[onsCount++] = b; + onId[_onsCount++] = b; } delete[] ons; ons = NULL; - if (onsCount > 0) { - ons = new Surface[onsCount]; - for (uint32 i = 0; i < onsCount; ++i) { + if (_onsCount > 0) { + ons = new Surface[_onsCount]; + for (uint32 i = 0; i < _onsCount; ++i) { Common::ScopedPtr<Common::SeekableReadStream> s(_vm->res->ons.getStream(onId[i])); if (s) { ons[i].load(*s, Surface::kTypeOns); @@ -498,7 +501,7 @@ bool Scene::processEvent(const Common::Event &event) { events.clear(); sounds.clear(); currentEvent.clear(); - messageColor = textColorMark; + _messageColor = textColorMark; for (int i = 0; i < 4; ++i) customAnimation[i].free(); _vm->playMusic(4); @@ -651,7 +654,7 @@ bool Scene::render(bool tickGame, bool tickMark, uint32 messageDelta) { bool gotAnyAnimation = false; if (ons != NULL && debugFeatures.feature[DebugFeatures::kShowOns]) { - for (uint32 i = 0; i < onsCount; ++i) { + for (uint32 i = 0; i < _onsCount; ++i) { Surface *s = ons + i; if (s != NULL) s->render(surface); @@ -821,7 +824,7 @@ bool Scene::render(bool tickGame, bool tickMark, uint32 messageDelta) { } if (visible) { - _vm->res->font7.render(surface, messagePos.x, messagePos.y, message, messageColor); + _vm->res->font7.render(surface, messagePos.x, messagePos.y, message, _messageColor); busy = true; } } @@ -1005,7 +1008,7 @@ bool Scene::processEventQueue() { warning("no animation in slot %u", messageSlot); } messagePos = messagePosition(message, p); - messageColor = currentEvent.color; + _messageColor = currentEvent.color; if (messageFirstFrame) currentEvent.clear(); // async message, clearing event @@ -1153,7 +1156,7 @@ bool Scene::processEventQueue() { } if (events.empty()) { - messageColor = textColorMark; + _messageColor = textColorMark; hideActor = false; } @@ -1232,7 +1235,7 @@ void Scene::displayMessage(const Common::String &str, byte color, const Common:: debugC(0, kDebugScene, "displayMessage: %s", str.c_str()); message = str; messagePos = (pos.x | pos.y) ? pos : messagePosition(str, position); - messageColor = color; + _messageColor = color; messageTimer = messageDuration(message); } @@ -1251,7 +1254,7 @@ void Scene::clear() { void Scene::clearMessage() { message.clear(); messageTimer = 0; - messageColor = textColorMark; + _messageColor = textColorMark; messageFirstFrame = 0; messageLastFrame = 0; messageAnimation = NULL; diff --git a/engines/teenagent/scene.h b/engines/teenagent/scene.h index 07b304ed97..40f910a3aa 100644 --- a/engines/teenagent/scene.h +++ b/engines/teenagent/scene.h @@ -194,7 +194,7 @@ private: SurfaceList on; bool onEnabled; Surface *ons; - uint32 onsCount; + uint32 _onsCount; Animation actorAnimation, animation[4], customAnimation[4]; Common::Rect actorAnimationPosition, animationPosition[4]; @@ -214,7 +214,7 @@ private: Common::String message; Common::Point messagePos; - byte messageColor; + byte _messageColor; uint messageTimer; byte messageFirstFrame; byte messageLastFrame; diff --git a/engines/teenagent/teenagent.cpp b/engines/teenagent/teenagent.cpp index 4dc785754c..2d10b82f51 100644 --- a/engines/teenagent/teenagent.cpp +++ b/engines/teenagent/teenagent.cpp @@ -71,6 +71,13 @@ TeenAgentEngine::TeenAgentEngine(OSystem *system, const ADGameDescription *gd) res = new Resources(); console = 0; + scene = 0; + inventory = 0; + _sceneBusy = false; + _dstObject = 0; + _musicStream = 0; + _markDelay = 0; + _gameDelay = 0; } TeenAgentEngine::~TeenAgentEngine() { diff --git a/engines/teenagent/teenagent.h b/engines/teenagent/teenagent.h index 234bfb1c57..438f06d189 100644 --- a/engines/teenagent/teenagent.h +++ b/engines/teenagent/teenagent.h @@ -20,8 +20,8 @@ * */ -#ifndef TEENAGENT_ENGINE_H -#define TEENAGENT_ENGINE_H +#ifndef TEENAGENT_TEENAGENT_H +#define TEENAGENT_TEENAGENT_H #include "engines/engine.h" diff --git a/engines/testbed/testbed.h b/engines/testbed/testbed.h index 0f70e1191f..5ea05feba2 100644 --- a/engines/testbed/testbed.h +++ b/engines/testbed/testbed.h @@ -20,8 +20,8 @@ * */ -#ifndef TESTBED_H -#define TESTBED_H +#ifndef TESTBED_TESTBED_H +#define TESTBED_TESTBED_H #include "common/array.h" @@ -72,4 +72,4 @@ private: } // End of namespace Testbed -#endif // TESTBED_H +#endif // TESTBED_TESTBED_H diff --git a/engines/tinsel/bmv.cpp b/engines/tinsel/bmv.cpp index 22c3798fc6..cfe97e6ec1 100644 --- a/engines/tinsel/bmv.cpp +++ b/engines/tinsel/bmv.cpp @@ -567,8 +567,8 @@ void BMVPlayer::PlayBMV(CORO_PARAM, SCNHANDLE hFileStem, int myEscape) { assert(!bMovieOn); - strcpy(szMovieFile, (char *)LockMem(hFileStem)); - strcat(szMovieFile, BMOVIE_EXTENSION); + Common::strlcpy(szMovieFile, (char *)LockMem(hFileStem), 14); + Common::strlcat(szMovieFile, BMOVIE_EXTENSION, 14); assert(strlen(szMovieFile) <= 12); diff --git a/engines/tinsel/detection.cpp b/engines/tinsel/detection.cpp index 2fde6e788a..c44f1f4ef3 100644 --- a/engines/tinsel/detection.cpp +++ b/engines/tinsel/detection.cpp @@ -235,7 +235,7 @@ const ADGameDescription *TinselMetaEngine::fallbackDetect(const FileMap &allFile for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++) { // Get the next filename, stripping off any '1' suffix character char tempFilename[50]; - strcpy(tempFilename, fileDesc->fileName); + Common::strlcpy(tempFilename, fileDesc->fileName, 50); char *pOne = strchr(tempFilename, '1'); if (pOne) { do { @@ -275,7 +275,7 @@ const ADGameDescription *TinselMetaEngine::fallbackDetect(const FileMap &allFile for (fileDesc = g->desc.filesDescriptions; fileDesc->fileName; fileDesc++) { // Get the next filename, stripping off any '1' suffix character char tempFilename[50]; - strcpy(tempFilename, fileDesc->fileName); + Common::strlcpy(tempFilename, fileDesc->fileName, 50); char *pOne = strchr(tempFilename, '1'); if (pOne) { do { diff --git a/engines/tinsel/dialogs.cpp b/engines/tinsel/dialogs.cpp index a84dad942c..b5d090ec15 100644 --- a/engines/tinsel/dialogs.cpp +++ b/engines/tinsel/dialogs.cpp @@ -1671,10 +1671,10 @@ static void Select(int i, bool force) { #else // Current description with cursor appended if (cd.box[i].boxText != NULL) { - strcpy(g_sedit, cd.box[i].boxText); - strcat(g_sedit, sCursor); + Common::strlcpy(g_sedit, cd.box[i].boxText, SG_DESC_LEN+2); + Common::strlcat(g_sedit, sCursor, SG_DESC_LEN+2); } else { - strcpy(g_sedit, sCursor); + Common::strlcpy(g_sedit, sCursor, SG_DESC_LEN+2); } #endif @@ -3676,13 +3676,13 @@ extern void HideConversation(bool bHide) { ConstructInventory(FULL); else { // Move it all back on-screen - for (i = 0; g_objArray[i] && i < MAX_WCOMP; i++) { + for (i = 0; i < MAX_WCOMP && g_objArray[i]; i++) { MultiAdjustXY(g_objArray[i], -2 * SCREEN_WIDTH, 0); } // Don't flash if items changed. If they have, will be redrawn anyway. if (TinselV2 || !g_ItemsChanged) { - for (i = 0; g_iconArray[i] && i < MAX_ICONS; i++) { + for (i = 0; i < MAX_ICONS && g_iconArray[i]; i++) { MultiAdjustXY(g_iconArray[i], -2*SCREEN_WIDTH, 0); } } @@ -3739,10 +3739,10 @@ extern void HideConversation(bool bHide) { deltay = g_InvD[INV_CONV].inventoryY - deltay; // Move it all - for (i = 0; g_objArray[i] && i < MAX_WCOMP; i++) { + for (i = 0; i < MAX_WCOMP && g_objArray[i]; i++) { MultiMoveRelXY(g_objArray[i], x - center, deltay); } - for (i = 0; g_iconArray[i] && i < MAX_ICONS; i++) { + for (i = 0; i < MAX_ICONS && g_iconArray[i]; i++) { MultiMoveRelXY(g_iconArray[i], x - center, deltay); } g_InvD[INV_CONV].inventoryX += x - center; @@ -3771,10 +3771,10 @@ extern void HideConversation(bool bHide) { y = 0; if (x || y) { - for (i = 0; g_objArray[i] && i < MAX_WCOMP; i++) { + for (i = 0; i < MAX_WCOMP && g_objArray[i]; i++) { MultiMoveRelXY(g_objArray[i], x, y); } - for (i = 0; g_iconArray[i] && i < MAX_ICONS; i++) { + for (i = 0; i < MAX_ICONS && g_iconArray[i]; i++) { MultiMoveRelXY(g_iconArray[i], x, y); } g_InvD[INV_CONV].inventoryX += x; @@ -3786,10 +3786,10 @@ extern void HideConversation(bool bHide) { */ if (MultiLowest(g_RectObject) > SCREEN_BOX_HEIGHT2 - SysVar(SV_CONV_MINY)) { y = (SCREEN_BOX_HEIGHT2 - SysVar(SV_CONV_MINY)) - MultiLowest(g_RectObject); - for (i = 0; g_objArray[i] && i < MAX_WCOMP; i++) { + for (i = 0; i < MAX_WCOMP && g_objArray[i]; i++) { MultiMoveRelXY(g_objArray[i], 0, y); } - for (i = 0; g_iconArray[i] && i < MAX_ICONS; i++) { + for (i = 0; i < MAX_ICONS && g_iconArray[i]; i++) { MultiMoveRelXY(g_iconArray[i], 0, y); } g_InvD[INV_CONV].inventoryY += y; @@ -4617,9 +4617,9 @@ extern void Xmovement(int x) { GetAniPosition(g_objArray[0], &g_InvD[g_ino].inventoryX, &aniY); g_InvD[g_ino].inventoryX +=x; MultiSetAniX(g_objArray[0], g_InvD[g_ino].inventoryX); - for (i = 1; g_objArray[i] && i < MAX_WCOMP; i++) + for (i = 1; i < MAX_WCOMP && g_objArray[i]; i++) MultiMoveRelXY(g_objArray[i], x, 0); - for (i = 0; g_iconArray[i] && i < MAX_ICONS; i++) + for (i = 0; i < MAX_ICONS && g_iconArray[i]; i++) MultiMoveRelXY(g_iconArray[i], x, 0); break; @@ -4665,9 +4665,9 @@ extern void Ymovement(int y) { GetAniPosition(g_objArray[0], &aniX, &g_InvD[g_ino].inventoryY); g_InvD[g_ino].inventoryY +=y; MultiSetAniY(g_objArray[0], g_InvD[g_ino].inventoryY); - for (i = 1; g_objArray[i] && i < MAX_WCOMP; i++) + for (i = 1; i < MAX_WCOMP && g_objArray[i]; i++) MultiMoveRelXY(g_objArray[i], 0, y); - for (i = 0; g_iconArray[i] && i < MAX_ICONS; i++) + for (i = 0; i < MAX_ICONS && g_iconArray[i]; i++) MultiMoveRelXY(g_iconArray[i], 0, y); break; diff --git a/engines/tinsel/handle.cpp b/engines/tinsel/handle.cpp index 62d244e449..9ffd477c4a 100644 --- a/engines/tinsel/handle.cpp +++ b/engines/tinsel/handle.cpp @@ -258,7 +258,7 @@ void LoadExtraGraphData(SCNHANDLE start, SCNHANDLE next) { } void SetCdPlaySceneDetails(int fileNum, const char *fileName) { - strcpy(g_szCdPlayFile, fileName); + Common::strlcpy(g_szCdPlayFile, fileName, 100); } void SetCdPlayHandle(int fileNum) { diff --git a/engines/tinsel/saveload.cpp b/engines/tinsel/saveload.cpp index 88cd80b78a..226cbb51c0 100644 --- a/engines/tinsel/saveload.cpp +++ b/engines/tinsel/saveload.cpp @@ -563,7 +563,7 @@ static void DoSave() { while (1) { Common::String fname = _vm->getSavegameFilename(ano); - strcpy(tmpName, fname.c_str()); + Common::strlcpy(tmpName, fname.c_str(), FNAMELEN); for (i = 0; i < g_numSfiles; i++) if (!strcmp(g_savedFiles[i].name, tmpName)) diff --git a/engines/tinsel/tinsel.h b/engines/tinsel/tinsel.h index c83bc80ead..9e4ce61b04 100644 --- a/engines/tinsel/tinsel.h +++ b/engines/tinsel/tinsel.h @@ -20,8 +20,8 @@ * */ -#ifndef TINSEL_H -#define TINSEL_H +#ifndef TINSEL_TINSEL_H +#define TINSEL_TINSEL_H #include "common/scummsys.h" #include "common/system.h" @@ -251,4 +251,4 @@ void CdHasChanged(); } // End of namespace Tinsel -#endif /* TINSEL_H */ +#endif /* TINSEL_TINSEL_H */ diff --git a/engines/toltecs/movie.cpp b/engines/toltecs/movie.cpp index b64903ec6d..b26408fadc 100644 --- a/engines/toltecs/movie.cpp +++ b/engines/toltecs/movie.cpp @@ -45,7 +45,7 @@ enum ChunkTypes { kChunkStopSubtitles = 8 }; -MoviePlayer::MoviePlayer(ToltecsEngine *vm) : _vm(vm), _isPlaying(false), _lastPrefetchOfs(0), _framesPerSoundChunk(0), _endPos(0) { +MoviePlayer::MoviePlayer(ToltecsEngine *vm) : _vm(vm), _isPlaying(false), _lastPrefetchOfs(0), _framesPerSoundChunk(0), _endPos(0), _audioStream(0) { } MoviePlayer::~MoviePlayer() { diff --git a/engines/toltecs/resource.cpp b/engines/toltecs/resource.cpp index 468ae0272f..6dbb9c2843 100644 --- a/engines/toltecs/resource.cpp +++ b/engines/toltecs/resource.cpp @@ -31,6 +31,7 @@ namespace Toltecs { /* ArchiveReader */ ArchiveReader::ArchiveReader() { + _offsets = 0; } ArchiveReader::~ArchiveReader() { diff --git a/engines/toltecs/sprite.cpp b/engines/toltecs/sprite.cpp index f29f64dcfe..be4be5d9e3 100644 --- a/engines/toltecs/sprite.cpp +++ b/engines/toltecs/sprite.cpp @@ -84,6 +84,7 @@ public: _yerror = _sprite->yerror; _origHeight = _sprite->origHeight; _scalerStatus = 0; + _xerror = 0; } SpriteReaderStatus readPacket(PixelPacket &packet) { SpriteReaderStatus status = kSrsPixelsLeft; @@ -135,6 +136,8 @@ public: _yerror = _sprite->yerror; _origHeight = _sprite->origHeight; _scalerStatus = 0; + _sourcep = 0; + _xerror = 0; } SpriteReaderStatus readPacket(PixelPacket &packet) { SpriteReaderStatus status; diff --git a/engines/toltecs/toltecs.h b/engines/toltecs/toltecs.h index 7a04f6e8da..ece82f4a1a 100644 --- a/engines/toltecs/toltecs.h +++ b/engines/toltecs/toltecs.h @@ -20,8 +20,8 @@ * */ -#ifndef TOLTECS_H -#define TOLTECS_H +#ifndef TOLTECS_TOLTECS_H +#define TOLTECS_TOLTECS_H #include "common/scummsys.h" #include "common/endian.h" @@ -231,4 +231,4 @@ public: } // End of namespace Toltecs -#endif /* TOLTECS_H */ +#endif /* TOLTECS_TOLTECS_H */ diff --git a/engines/tony/gfxcore.cpp b/engines/tony/gfxcore.cpp index 2a32926c53..27145d7c4b 100644 --- a/engines/tony/gfxcore.cpp +++ b/engines/tony/gfxcore.cpp @@ -1733,13 +1733,6 @@ void RMGfxSourceBuffer8AA::drawAA(RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *pri g /= 5; b /= 5; - if (r > 0x1f) - r = 0x1f; - if (g > 0x3f) - g = 0x3f; - if (b > 0x1f) - b = 0x1f; - mybuf[0] = (r << 11) | (g << 5) | b; } } @@ -1774,13 +1767,6 @@ void RMGfxSourceBuffer8AA::drawAA(RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *pri g /= 6; b /= 6; - if (r > 0x1f) - r = 0x1f; - if (g > 0x3f) - g = 0x3f; - if (b > 0x1f) - b = 0x1f; - mybuf[0] = (r << 11) | (g << 5) | b; } } diff --git a/engines/tony/mpal/loadmpc.cpp b/engines/tony/mpal/loadmpc.cpp index 8d030f1e52..01892a40e6 100644 --- a/engines/tony/mpal/loadmpc.cpp +++ b/engines/tony/mpal/loadmpc.cpp @@ -331,7 +331,7 @@ static const byte *parseItem(const byte *lpBuf, LpMpalItem lpmiItem) { byte len = *lpBuf; lpBuf++; - memcpy(lpmiItem->_lpszDescribe, lpBuf, MIN((byte)127, len)); + memcpy(lpmiItem->_lpszDescribe, lpBuf, MIN((byte)MAX_DESCRIBE_SIZE, len)); lpBuf += len; if (len >= MAX_DESCRIBE_SIZE) diff --git a/engines/tony/mpal/mpal.cpp b/engines/tony/mpal/mpal.cpp index 89cc28130d..9172843781 100644 --- a/engines/tony/mpal/mpal.cpp +++ b/engines/tony/mpal/mpal.cpp @@ -367,12 +367,18 @@ MpalHandle resLoad(uint32 dwId) { temp = (byte *)globalAlloc(GMEM_FIXED | GMEM_ZEROINIT, nSizeComp); nBytesRead = GLOBALS._hMpr.read(temp, nSizeComp); - if (nBytesRead != nSizeComp) + if (nBytesRead != nSizeComp) { + globalDestroy(temp); + globalDestroy(h); return NULL; + } lzo1x_decompress(temp, nSizeComp, buf, &nBytesRead); - if (nBytesRead != nSizeDecomp) + if (nBytesRead != nSizeDecomp) { + globalDestroy(temp); + globalDestroy(h); return NULL; + } globalDestroy(temp); globalUnlock(h); @@ -526,8 +532,10 @@ static LpItem getItemData(uint32 nOrdItem) { globalFree(hDat); // Check if we've got to the end of the file - if (i != 0xABCD) + if (i != 0xABCD) { + globalDestroy(ret); return NULL; + } return ret; } @@ -1413,36 +1421,51 @@ bool mpalInit(const char *lpszMpcFileName, const char *lpszMprFileName, if (bCompress) { // Get the compressed size and read the data in uint32 dwSizeComp = hMpc.readUint32LE(); - if (hMpc.err()) + if (hMpc.err()) { + globalDestroy(lpMpcImage); return false; + } cmpbuf = (byte *)globalAlloc(GMEM_FIXED, dwSizeComp); - if (cmpbuf == NULL) + if (cmpbuf == NULL) { + globalDestroy(lpMpcImage); return false; + } nBytesRead = hMpc.read(cmpbuf, dwSizeComp); - if (nBytesRead != dwSizeComp) + if (nBytesRead != dwSizeComp) { + globalDestroy(cmpbuf); + globalDestroy(lpMpcImage); return false; + } // Decompress the data lzo1x_decompress(cmpbuf, dwSizeComp, lpMpcImage, &nBytesRead); - if (nBytesRead != dwSizeDecomp) + if (nBytesRead != dwSizeDecomp) { + globalDestroy(cmpbuf); + globalDestroy(lpMpcImage); return false; + } globalDestroy(cmpbuf); } else { // If the file is not compressed, we directly read in the data nBytesRead = hMpc.read(lpMpcImage, dwSizeDecomp); - if (nBytesRead != dwSizeDecomp) + if (nBytesRead != dwSizeDecomp) { + globalDestroy(lpMpcImage); return false; + } } // Close the file hMpc.close(); // Process the data - if (parseMpc(lpMpcImage) == false) + if (parseMpc(lpMpcImage) == false) { + globalDestroy(lpMpcImage); + return false; + } globalDestroy(lpMpcImage); diff --git a/engines/tony/tony.h b/engines/tony/tony.h index 40bace8db8..fc47e1a1bf 100644 --- a/engines/tony/tony.h +++ b/engines/tony/tony.h @@ -20,8 +20,8 @@ * */ -#ifndef TONY_H -#define TONY_H +#ifndef TONY_TONY_H +#define TONY_TONY_H #include "common/scummsys.h" #include "common/system.h" @@ -242,4 +242,4 @@ extern TonyEngine *g_vm; } // End of namespace Tony -#endif /* TONY_H */ +#endif /* TONY_TONY_H */ diff --git a/engines/touche/touche.h b/engines/touche/touche.h index 33c415d9dc..c76532b302 100644 --- a/engines/touche/touche.h +++ b/engines/touche/touche.h @@ -20,8 +20,8 @@ * */ -#ifndef TOUCHE_ENGINE_H -#define TOUCHE_ENGINE_H +#ifndef TOUCHE_TOUCHE_H +#define TOUCHE_TOUCHE_H #include "common/array.h" #include "common/endian.h" 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/ringworld2/ringworld2_scenes2.cpp b/engines/tsage/ringworld2/ringworld2_scenes2.cpp index bd8a0cdd0d..6b44ecc514 100644 --- a/engines/tsage/ringworld2/ringworld2_scenes2.cpp +++ b/engines/tsage/ringworld2/ringworld2_scenes2.cpp @@ -1440,7 +1440,7 @@ void Scene2425::postInit(SceneObjectList *OwnerList) { case 2425: _sceneMode = 10; R2_GLOBALS._player.setPosition(Common::Point(280, 150)); - _action->signal(); + signal(); break; case 2455: _sceneMode = 2428; 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/tsage/tsage.h b/engines/tsage/tsage.h index dd077e526f..ca29d68243 100644 --- a/engines/tsage/tsage.h +++ b/engines/tsage/tsage.h @@ -20,8 +20,8 @@ * */ -#ifndef TSAGE_H -#define TSAGE_H +#ifndef TSAGE_TSAGE_H +#define TSAGE_TSAGE_H #include "engines/engine.h" #include "gui/debugger.h" diff --git a/engines/tucker/tucker.h b/engines/tucker/tucker.h index 2ab94dedbc..1be4682508 100644 --- a/engines/tucker/tucker.h +++ b/engines/tucker/tucker.h @@ -20,8 +20,8 @@ * */ -#ifndef TUCKER_ENGINE_H -#define TUCKER_ENGINE_H +#ifndef TUCKER_TUCKER_H +#define TUCKER_TUCKER_H #include "common/file.h" #include "common/util.h" 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/wage/debugger.cpp b/engines/wage/debugger.cpp index 7d01b0b85e..d34aca7d49 100644 --- a/engines/wage/debugger.cpp +++ b/engines/wage/debugger.cpp @@ -56,7 +56,7 @@ static int strToInt(const char *s) { } bool Debugger::Cmd_ListScenes(int argc, const char **argv) { - int currentScene; + int currentScene = 0; for (uint i = 1; i < _engine->_world->_orderedScenes.size(); i++) { // #0 is STORAGE@ if (_engine->_world->_player->_currentScene == _engine->_world->_orderedScenes[i]) diff --git a/engines/wage/design.cpp b/engines/wage/design.cpp index 907a1ec435..eda28df159 100644 --- a/engines/wage/design.cpp +++ b/engines/wage/design.cpp @@ -45,20 +45,22 @@ * */ +#include "graphics/managed_surface.h" #include "graphics/primitives.h" -#include "wage/wage.h" + +#include "wage/macwindowmanager.h" #include "wage/design.h" namespace Wage { struct PlotData { - Graphics::Surface *surface; + Graphics::ManagedSurface *surface; Patterns *patterns; uint fillType; int thickness; Design *design; - PlotData(Graphics::Surface *s, Patterns *p, int f, int t, Design *d) : + PlotData(Graphics::ManagedSurface *s, Patterns *p, int f, int t, Design *d) : surface(s), patterns(p), fillType(f), thickness(t), design(d) {} }; @@ -83,7 +85,7 @@ Design::~Design() { delete _surface; } -void Design::paint(Graphics::Surface *surface, Patterns &patterns, int x, int y) { +void Design::paint(Graphics::ManagedSurface *surface, Patterns &patterns, int x, int y) { bool needRender = false; if (_surface == NULL) { @@ -96,11 +98,10 @@ void Design::paint(Graphics::Surface *surface, Patterns &patterns, int x, int y) } _bounds->debugPrint(4, "Calculated bounds:"); - _surface = new Graphics::Surface; + _surface = new Graphics::ManagedSurface; _surface->create(_bounds->width(), _bounds->height(), Graphics::PixelFormat::createFormatCLUT8()); - Common::Rect r(0, 0, _bounds->width(), _bounds->height()); - _surface->fillRect(r, kColorGreen); + _surface->clear(kColorGreen); needRender = true; } @@ -133,16 +134,10 @@ void Design::paint(Graphics::Surface *surface, Patterns &patterns, int x, int y) if (_bounds->width() && _bounds->height()) { const int padding = 3; - for (int i = padding; i < _bounds->height() - 2 * padding; i++) { - const byte *src = (const byte *)_surface->getBasePtr(padding, i); - byte *dst = (byte *)surface->getBasePtr(x + padding, y+i); - for (int j = padding; j < _bounds->width() - 2 * padding; j++) { - if (*src != kColorGreen) - *dst = *src; - src++; - dst++; - } - } + Common::Rect from(padding, padding, _bounds->width() - 2 * padding, _bounds->height() - 2 * padding); + Common::Rect to(from); + to.moveTo(x, y); + surface->transBlitFrom(*_surface, from, to, kColorGreen); } } @@ -215,9 +210,9 @@ void drawPixel(int x, int y, int color, void *data) { if (p->thickness == 1) { p->design->adjustBounds(x, y); } else { - int x1 = x - p->thickness / 2; + int x1 = x; int x2 = x1 + p->thickness; - int y1 = y - p->thickness / 2; + int y1 = y; int y2 = y1 + p->thickness; for (y = y1; y < y2; y++) @@ -240,9 +235,9 @@ void drawPixel(int x, int y, int color, void *data) { color : kColorWhite; } } else { - int x1 = x - p->thickness / 2; + int x1 = x; int x2 = x1 + p->thickness; - int y1 = y - p->thickness / 2; + int y1 = y; int y2 = y1 + p->thickness; for (y = y1; y < y2; y++) @@ -269,7 +264,7 @@ void drawPixelPlain(int x, int y, int color, void *data) { *((byte *)p->surface->getBasePtr(x, y)) = (byte)color; } -void Design::drawRect(Graphics::Surface *surface, Common::ReadStream &in, +void Design::drawRect(Graphics::ManagedSurface *surface, Common::ReadStream &in, Patterns &patterns, byte fillType, byte borderThickness, byte borderFillType) { int16 y1 = in.readSint16BE(); int16 x1 = in.readSint16BE(); @@ -298,7 +293,7 @@ void Design::drawRect(Graphics::Surface *surface, Common::ReadStream &in, } } -void Design::drawRoundRect(Graphics::Surface *surface, Common::ReadStream &in, +void Design::drawRoundRect(Graphics::ManagedSurface *surface, Common::ReadStream &in, Patterns &patterns, byte fillType, byte borderThickness, byte borderFillType) { int16 y1 = in.readSint16BE(); int16 x1 = in.readSint16BE(); @@ -324,7 +319,7 @@ void Design::drawRoundRect(Graphics::Surface *surface, Common::ReadStream &in, Graphics::drawRoundRect(r, arc / 2, kColorBlack, false, drawPixel, &pd); } -void Design::drawPolygon(Graphics::Surface *surface, Common::ReadStream &in, +void Design::drawPolygon(Graphics::ManagedSurface *surface, Common::ReadStream &in, Patterns &patterns, byte fillType, byte borderThickness, byte borderFillType) { byte ignored = in.readSint16BE(); // ignored @@ -401,7 +396,7 @@ void Design::drawPolygon(Graphics::Surface *surface, Common::ReadStream &in, free(ypoints); } -void Design::drawOval(Graphics::Surface *surface, Common::ReadStream &in, +void Design::drawOval(Graphics::ManagedSurface *surface, Common::ReadStream &in, Patterns &patterns, byte fillType, byte borderThickness, byte borderFillType) { int16 y1 = in.readSint16BE(); int16 x1 = in.readSint16BE(); @@ -419,7 +414,7 @@ void Design::drawOval(Graphics::Surface *surface, Common::ReadStream &in, Graphics::drawEllipse(x1, y1, x2-1, y2-1, kColorBlack, false, drawPixel, &pd); } -void Design::drawBitmap(Graphics::Surface *surface, Common::SeekableReadStream &in) { +void Design::drawBitmap(Graphics::ManagedSurface *surface, Common::SeekableReadStream &in) { int numBytes = in.readSint16BE(); int y1 = in.readSint16BE(); int x1 = in.readSint16BE(); @@ -427,7 +422,7 @@ void Design::drawBitmap(Graphics::Surface *surface, Common::SeekableReadStream & int x2 = in.readSint16BE(); int w = x2 - x1; int h = y2 - y1; - Graphics::Surface tmp; + Graphics::ManagedSurface tmp; tmp.create(w, h, Graphics::PixelFormat::createFormatCLUT8()); @@ -507,11 +502,11 @@ void Design::drawBitmap(Graphics::Surface *surface, Common::SeekableReadStream & tmp.free(); } -void Design::drawRect(Graphics::Surface *surface, Common::Rect &rect, int thickness, int color, Patterns &patterns, byte fillType) { +void Design::drawRect(Graphics::ManagedSurface *surface, Common::Rect &rect, int thickness, int color, Patterns &patterns, byte fillType) { drawRect(surface, rect.left, rect.top, rect.right, rect.bottom, thickness, color, patterns, fillType); } -void Design::drawRect(Graphics::Surface *surface, int x1, int y1, int x2, int y2, int thickness, int color, Patterns &patterns, byte fillType) { +void Design::drawRect(Graphics::ManagedSurface *surface, int x1, int y1, int x2, int y2, int thickness, int color, Patterns &patterns, byte fillType) { PlotData pd(surface, &patterns, fillType, thickness, nullptr); Graphics::drawLine(x1, y1, x2, y1, kColorBlack, drawPixel, &pd); @@ -521,32 +516,32 @@ void Design::drawRect(Graphics::Surface *surface, int x1, int y1, int x2, int y2 } -void Design::drawFilledRect(Graphics::Surface *surface, Common::Rect &rect, int color, Patterns &patterns, byte fillType) { +void Design::drawFilledRect(Graphics::ManagedSurface *surface, Common::Rect &rect, int color, Patterns &patterns, byte fillType) { PlotData pd(surface, &patterns, fillType, 1, nullptr); for (int y = rect.top; y <= rect.bottom; y++) Graphics::drawHLine(rect.left, rect.right, y, color, drawPixel, &pd); } -void Design::drawFilledRoundRect(Graphics::Surface *surface, Common::Rect &rect, int arc, int color, Patterns &patterns, byte fillType) { +void Design::drawFilledRoundRect(Graphics::ManagedSurface *surface, Common::Rect &rect, int arc, int color, Patterns &patterns, byte fillType) { PlotData pd(surface, &patterns, fillType, 1, nullptr); Graphics::drawRoundRect(rect, arc, color, true, drawPixel, &pd); } -void Design::drawHLine(Graphics::Surface *surface, int x1, int x2, int y, int thickness, int color, Patterns &patterns, byte fillType) { +void Design::drawHLine(Graphics::ManagedSurface *surface, int x1, int x2, int y, int thickness, int color, Patterns &patterns, byte fillType) { PlotData pd(surface, &patterns, fillType, thickness, nullptr); Graphics::drawHLine(x1, x2, y, color, drawPixel, &pd); } -void Design::drawVLine(Graphics::Surface *surface, int x, int y1, int y2, int thickness, int color, Patterns &patterns, byte fillType) { +void Design::drawVLine(Graphics::ManagedSurface *surface, int x, int y1, int y2, int thickness, int color, Patterns &patterns, byte fillType) { PlotData pd(surface, &patterns, fillType, thickness, nullptr); Graphics::drawVLine(x, y1, y2, color, drawPixel, &pd); } -FloodFill::FloodFill(Graphics::Surface *surface, byte color1, byte color2) { +FloodFill::FloodFill(Graphics::ManagedSurface *surface, byte color1, byte color2) { _surface = surface; _color1 = color1; _color2 = color2; diff --git a/engines/wage/design.h b/engines/wage/design.h index e8f42f4e04..9b0231ca96 100644 --- a/engines/wage/design.h +++ b/engines/wage/design.h @@ -48,10 +48,11 @@ #ifndef WAGE_DESIGN_H #define WAGE_DESIGN_H -#include "graphics/surface.h" #include "common/memstream.h" #include "common/rect.h" +#include "wage/macwindowmanager.h" + namespace Wage { class Design { @@ -67,14 +68,14 @@ public: return _bounds; } - void paint(Graphics::Surface *canvas, Patterns &patterns, int x, int y); + void paint(Graphics::ManagedSurface *canvas, Patterns &patterns, int x, int y); bool isPointOpaque(int x, int y); - static void drawRect(Graphics::Surface *surface, Common::Rect &rect, int thickness, int color, Patterns &patterns, byte fillType); - static void drawRect(Graphics::Surface *surface, int x1, int y1, int x2, int y2, int thickness, int color, Patterns &patterns, byte fillType); - static void drawFilledRect(Graphics::Surface *surface, Common::Rect &rect, int color, Patterns &patterns, byte fillType); - static void drawFilledRoundRect(Graphics::Surface *surface, Common::Rect &rect, int arc, int color, Patterns &patterns, byte fillType); - static void drawHLine(Graphics::Surface *surface, int x1, int x2, int y, int thickness, int color, Patterns &patterns, byte fillType); - static void drawVLine(Graphics::Surface *surface, int x, int y1, int y2, int thickness, int color, Patterns &patterns, byte fillType); + static void drawRect(Graphics::ManagedSurface *surface, Common::Rect &rect, int thickness, int color, Patterns &patterns, byte fillType); + static void drawRect(Graphics::ManagedSurface *surface, int x1, int y1, int x2, int y2, int thickness, int color, Patterns &patterns, byte fillType); + static void drawFilledRect(Graphics::ManagedSurface *surface, Common::Rect &rect, int color, Patterns &patterns, byte fillType); + static void drawFilledRoundRect(Graphics::ManagedSurface *surface, Common::Rect &rect, int arc, int color, Patterns &patterns, byte fillType); + static void drawHLine(Graphics::ManagedSurface *surface, int x1, int x2, int y, int thickness, int color, Patterns &patterns, byte fillType); + static void drawVLine(Graphics::ManagedSurface *surface, int x, int y1, int y2, int thickness, int color, Patterns &patterns, byte fillType); bool isBoundsCalculation() { return _boundsCalculationMode; } void adjustBounds(int16 x, int16 y); @@ -83,32 +84,32 @@ private: byte *_data; int _len; Common::Rect *_bounds; - Graphics::Surface *_surface; + Graphics::ManagedSurface *_surface; bool _boundsCalculationMode; private: void render(Patterns &patterns); - void drawRect(Graphics::Surface *surface, Common::ReadStream &in, + void drawRect(Graphics::ManagedSurface *surface, Common::ReadStream &in, Patterns &patterns, byte fillType, byte borderThickness, byte borderFillType); - void drawRoundRect(Graphics::Surface *surface, Common::ReadStream &in, + void drawRoundRect(Graphics::ManagedSurface *surface, Common::ReadStream &in, Patterns &patterns, byte fillType, byte borderThickness, byte borderFillType); - void drawPolygon(Graphics::Surface *surface, Common::ReadStream &in, + void drawPolygon(Graphics::ManagedSurface *surface, Common::ReadStream &in, Patterns &patterns, byte fillType, byte borderThickness, byte borderFillType); - void drawOval(Graphics::Surface *surface, Common::ReadStream &in, + void drawOval(Graphics::ManagedSurface *surface, Common::ReadStream &in, Patterns &patterns, byte fillType, byte borderThickness, byte borderFillType); - void drawBitmap(Graphics::Surface *surface, Common::SeekableReadStream &in); + void drawBitmap(Graphics::ManagedSurface *surface, Common::SeekableReadStream &in); }; class FloodFill { public: - FloodFill(Graphics::Surface *surface, byte color1, byte color2); + FloodFill(Graphics::ManagedSurface *surface, byte color1, byte color2); ~FloodFill(); void addSeed(int x, int y); void fill(); private: Common::List<Common::Point *> _queue; - Graphics::Surface *_surface; + Graphics::ManagedSurface *_surface; byte _color1, _color2; byte *_visited; int _w, _h; diff --git a/engines/wage/dialog.cpp b/engines/wage/dialog.cpp index 263570bddc..d9bb3e6a61 100644 --- a/engines/wage/dialog.cpp +++ b/engines/wage/dialog.cpp @@ -49,6 +49,7 @@ #include "common/events.h" #include "wage/wage.h" +#include "wage/macwindowmanager.h" #include "wage/design.h" #include "wage/gui.h" #include "wage/dialog.h" @@ -88,11 +89,11 @@ Dialog::~Dialog() { } const Graphics::Font *Dialog::getDialogFont() { - return _gui->getFont("Chicago-12", Graphics::FontManager::kBigGUIFont); + return _gui->_wm.getFont("Chicago-12", Graphics::FontManager::kBigGUIFont); } void Dialog::paint() { - Design::drawFilledRect(&_gui->_screen, _bbox, kColorWhite, _gui->_patterns, kPatternSolid); + Design::drawFilledRect(&_gui->_screen, _bbox, kColorWhite, _gui->_wm.getPatterns(), kPatternSolid); _font->drawString(&_gui->_screen, _text, _bbox.left + 24, _bbox.top + 16, _bbox.width(), kColorBlack); static int boxOutline[] = { 1, 0, 0, 1, 1 }; @@ -114,7 +115,7 @@ void Dialog::paint() { Common::Rect bb(button->bounds.left + 5, button->bounds.top + 5, button->bounds.right - 5, button->bounds.bottom - 5); - Design::drawFilledRect(&_gui->_screen, bb, kColorBlack, _gui->_patterns, kPatternSolid); + Design::drawFilledRect(&_gui->_screen, bb, kColorBlack, _gui->_wm.getPatterns(), kPatternSolid); color = kColorWhite; } @@ -137,7 +138,7 @@ void Dialog::drawOutline(Common::Rect &bounds, int *spec, int speclen) { for (int i = 0; i < speclen; i++) if (spec[i] != 0) Design::drawRect(&_gui->_screen, bounds.left + i, bounds.top + i, bounds.right - i, bounds.bottom - i, - 1, kColorBlack, _gui->_patterns, kPatternSolid); + 1, kColorBlack, _gui->_wm.getPatterns(), kPatternSolid); } int Dialog::run() { @@ -145,7 +146,7 @@ int Dialog::run() { Common::Rect r(_bbox); _tempSurface.copyRectToSurface(_gui->_screen.getBasePtr(_bbox.left, _bbox.top), _gui->_screen.pitch, 0, 0, _bbox.width() + 1, _bbox.height() + 1); - _gui->pushArrowCursor(); + _gui->_wm.pushArrowCursor(); while (!shouldQuit) { Common::Event event; @@ -189,7 +190,7 @@ int Dialog::run() { _gui->_screen.copyRectToSurface(_tempSurface.getBasePtr(0, 0), _tempSurface.pitch, _bbox.left, _bbox.top, _bbox.width() + 1, _bbox.height() + 1); g_system->copyRectToScreen(_gui->_screen.getBasePtr(r.left, r.top), _gui->_screen.pitch, r.left, r.top, r.width() + 1, r.height() + 1); - _gui->popCursor(); + _gui->_wm.popCursor(); return _pressedButton; } diff --git a/engines/wage/dialog.h b/engines/wage/dialog.h index c5878acc95..ec99fc06b2 100644 --- a/engines/wage/dialog.h +++ b/engines/wage/dialog.h @@ -74,7 +74,7 @@ public: private: Gui *_gui; - Graphics::Surface _tempSurface; + Graphics::ManagedSurface _tempSurface; Common::Rect _bbox; Common::String _text; diff --git a/engines/wage/entities.cpp b/engines/wage/entities.cpp index adb053868a..43ac6c8cc7 100644 --- a/engines/wage/entities.cpp +++ b/engines/wage/entities.cpp @@ -52,6 +52,7 @@ #include "wage/world.h" #include "common/memstream.h" +#include "graphics/managed_surface.h" namespace Wage { @@ -134,20 +135,20 @@ Scene::~Scene() { delete _textBounds; } -void Scene::paint(Graphics::Surface *surface, int x, int y) { +void Scene::paint(Graphics::ManagedSurface *surface, int x, int y) { Common::Rect r(x + 5, y + 5, _design->getBounds()->width() + x - 10, _design->getBounds()->height() + y - 10); surface->fillRect(r, kColorWhite); - _design->paint(surface, ((WageEngine *)g_engine)->_world->_patterns, x, y); + _design->paint(surface, *((WageEngine *)g_engine)->_world->_patterns, x, y); for (ObjList::const_iterator it = _objs.begin(); it != _objs.end(); ++it) { debug(2, "paining Obj: %s, index: %d, type: %d", (*it)->_name.c_str(), (*it)->_index, (*it)->_type); - (*it)->_design->paint(surface, ((WageEngine *)g_engine)->_world->_patterns, x, y); + (*it)->_design->paint(surface, *((WageEngine *)g_engine)->_world->_patterns, x, y); } for (ChrList::const_iterator it = _chrs.begin(); it != _chrs.end(); ++it) { debug(2, "paining Chr: %s", (*it)->_name.c_str()); - (*it)->_design->paint(surface, ((WageEngine *)g_engine)->_world->_patterns, x, y); + (*it)->_design->paint(surface, *((WageEngine *)g_engine)->_world->_patterns, x, y); } } @@ -202,6 +203,22 @@ const char *Scene::getFontName() { return "Unknown"; } +Designed *Scene::lookUpEntity(int x, int y) { + for (ObjList::const_iterator it = _objs.end(); it != _objs.begin(); ) { + it--; + if ((*it)->_design->isPointOpaque(x, y)) + return *it; + } + + for (ChrList::const_iterator it = _chrs.end(); it != _chrs.begin(); ) { + it--; + if ((*it)->_design->isPointOpaque(x, y)) + return *it; + } + + return nullptr; +} + Obj::Obj() : _currentOwner(NULL), _currentScene(NULL) { _index = 0; _namePlural = false; diff --git a/engines/wage/entities.h b/engines/wage/entities.h index 33cf087322..9e706f0d58 100644 --- a/engines/wage/entities.h +++ b/engines/wage/entities.h @@ -49,7 +49,7 @@ #define WAGE_ENTITIES_H namespace Graphics { - struct Surface; + class ManagedSurface; } namespace Wage { @@ -322,11 +322,13 @@ public: Scene(Common::String name, Common::SeekableReadStream *data); ~Scene(); + Designed *lookUpEntity(int x, int y); + Common::Rect *getTextBounds() { return _textBounds == NULL ? NULL : new Common::Rect(*_textBounds); } - void paint(Graphics::Surface *screen, int x, int y); + void paint(Graphics::ManagedSurface *screen, int x, int y); const char *getFontName(); }; diff --git a/engines/wage/gui-console.cpp b/engines/wage/gui-console.cpp index ab5df637ec..8b6fe43a17 100644 --- a/engines/wage/gui-console.cpp +++ b/engines/wage/gui-console.cpp @@ -45,6 +45,7 @@ * */ +#include "common/events.h" #include "common/timer.h" #include "common/unzip.h" #include "graphics/cursorman.h" @@ -54,7 +55,8 @@ #include "wage/wage.h" #include "wage/design.h" #include "wage/entities.h" -#include "wage/menu.h" +#include "wage/macwindow.h" +#include "wage/macmenu.h" #include "wage/gui.h" #include "wage/world.h" @@ -66,7 +68,7 @@ const Graphics::Font *Gui::getConsoleFont() { snprintf(fontName, 128, "%s-%d", scene->getFontName(), scene->_fontSize); - return getFont(fontName, Graphics::FontManager::kConsoleFont); + return _wm.getFont(fontName, Graphics::FontManager::kConsoleFont); } void Gui::clearOutput() { @@ -114,7 +116,7 @@ enum { void Gui::flowText(Common::String &str) { Common::StringArray wrappedLines; - int textW = _consoleTextArea.width() - kConWPadding * 2; + int textW = _consoleWindow->getInnerDimensions().width() - kConWPadding * 2; const Graphics::Font *font = getConsoleFont(); font->wordWrapText(str, textW, wrappedLines); @@ -142,7 +144,7 @@ void Gui::flowText(Common::String &str) { draw(); } -void Gui::renderConsole(Graphics::Surface *g, Common::Rect &r) { +void Gui::renderConsole(Graphics::ManagedSurface *g, const Common::Rect &r) { bool fullRedraw = _consoleFullRedraw; bool textReflow = false; int surfW = r.width() + kConWOverlap * 2; @@ -150,7 +152,6 @@ void Gui::renderConsole(Graphics::Surface *g, Common::Rect &r) { Common::Rect boundsR(kConWOverlap - kConOverscan, kConHOverlap - kConOverscan, r.width() + kConWOverlap + kConOverscan, r.height() + kConHOverlap + kConOverscan); - Common::Rect fullR(0, 0, surfW, surfH); if (_console.w != surfW || _console.h != surfH) { if (_console.w != surfW) @@ -163,7 +164,7 @@ void Gui::renderConsole(Graphics::Surface *g, Common::Rect &r) { } if (fullRedraw) - _console.fillRect(fullR, kColorWhite); + _console.clear(kColorWhite); const Graphics::Font *font = getConsoleFont(); @@ -197,7 +198,7 @@ void Gui::renderConsole(Graphics::Surface *g, Common::Rect &r) { color = kColorWhite; Common::Rect trect(0, y1, _console.w, y1 + _consoleLineHeight); - Design::drawFilledRect(&_console, trect, kColorBlack, _patterns, kPatternSolid); + Design::drawFilledRect(&_console, trect, kColorBlack, _wm.getPatterns(), kPatternSolid); } if (line == _selectionStartY || line == _selectionEndY) { @@ -224,7 +225,7 @@ void Gui::renderConsole(Graphics::Surface *g, Common::Rect &r) { else trect.left = rectW; - Design::drawFilledRect(&_console, trect, kColorBlack, _patterns, kPatternSolid); + Design::drawFilledRect(&_console, trect, kColorBlack, _wm.getPatterns(), kPatternSolid); font->drawString(&_console, beg, x1, y1, textW, color1); font->drawString(&_console, end, x1 + rectW - kConWPadding - kConWOverlap, y1, textW, color2); @@ -243,7 +244,7 @@ void Gui::renderConsole(Graphics::Surface *g, Common::Rect &r) { int rectW2 = rectW1 + font->getStringWidth(mid); Common::Rect trect(rectW1, y1, rectW2, y1 + _consoleLineHeight); - Design::drawFilledRect(&_console, trect, kColorBlack, _patterns, kPatternSolid); + Design::drawFilledRect(&_console, trect, kColorBlack, _wm.getPatterns(), kPatternSolid); font->drawString(&_console, beg, x1, y1, textW, kColorBlack); font->drawString(&_console, mid, x1 + rectW1 - kConWPadding - kConWOverlap, y1, textW, kColorWhite); @@ -280,17 +281,13 @@ void Gui::renderConsole(Graphics::Surface *g, Common::Rect &r) { rr.bottom = _screen.h - 1; g->copyRectToSurface(_console, xcon, ycon, boundsR); - g_system->copyRectToScreen(g->getBasePtr(rr.left, rr.top), g->pitch, rr.left, rr.top, rr.width(), rr.height()); } void Gui::drawInput() { if (!_screen.getPixels()) return; - if (_sceneIsActive) { - _sceneIsActive = false; - _bordersDirty = true; - } + _wm.setActive(_consoleWindow->getId()); _out.pop_back(); _lines.pop_back(); @@ -302,17 +299,17 @@ void Gui::drawInput() { if (_engine->_inputText.contains('\n')) { _consoleDirty = true; } else { - int x = kConWPadding + _consoleTextArea.left; - int y = _cursorY + _consoleTextArea.top; + int x = kConWPadding + _consoleWindow->getInnerDimensions().left; + int y = _cursorY + _consoleWindow->getInnerDimensions().top; - Common::Rect r(x, y, x + _consoleTextArea.width() - kConWPadding, y + font->getFontHeight()); + Common::Rect r(x, y, x + _consoleWindow->getInnerDimensions().width() - kConWPadding, y + font->getFontHeight()); _screen.fillRect(r, kColorWhite); undrawCursor(); font->drawString(&_screen, _out[_inputTextLineNum], x, y, _screen.w, kColorBlack); - g_system->copyRectToScreen(_screen.getBasePtr(x, y), _screen.pitch, x, y, _consoleTextArea.width(), font->getFontHeight()); + g_system->copyRectToScreen(_screen.getBasePtr(x, y), _screen.pitch, x, y, _consoleWindow->getInnerDimensions().width(), font->getFontHeight()); } _cursorX = font->getStringWidth(_out[_inputTextLineNum]) + kConHPadding; @@ -427,4 +424,140 @@ void Gui::enableNewGameMenus() { _menu->enableCommand(kMenuFile, kMenuActionQuit, true); } +bool Gui::processConsoleEvents(WindowClick click, Common::Event &event) { + if (click == kBorderScrollUp || click == kBorderScrollDown) { + if (event.type == Common::EVENT_LBUTTONDOWN) { + int consoleHeight = _consoleWindow->getInnerDimensions().height(); + int textFullSize = _lines.size() * _consoleLineHeight + consoleHeight; + float scrollPos = (float)_scrollPos / textFullSize; + float scrollSize = (float)consoleHeight / textFullSize; + + _consoleWindow->setScroll(scrollPos, scrollSize); + + return true; + } else if (event.type == Common::EVENT_LBUTTONUP) { + int oldScrollPos = _scrollPos; + + switch (click) { + case kBorderScrollUp: + _scrollPos = MAX<int>(0, _scrollPos - _consoleLineHeight); + undrawCursor(); + _cursorY -= (_scrollPos - oldScrollPos); + _consoleDirty = true; + _consoleFullRedraw = true; + break; + case kBorderScrollDown: + _scrollPos = MIN<int>((_lines.size() - 2) * _consoleLineHeight, _scrollPos + _consoleLineHeight); + undrawCursor(); + _cursorY -= (_scrollPos - oldScrollPos); + _consoleDirty = true; + _consoleFullRedraw = true; + break; + default: + return false; + } + + return true; + } + + return false; + } + + if (click == kBorderResizeButton) { + _consoleDirty = true; + _consoleFullRedraw = true; + + return true; + } + + if (click == kBorderInner) { + if (event.type == Common::EVENT_LBUTTONDOWN) { + startMarking(event.mouse.x, event.mouse.y); + + return true; + } else if (event.type == Common::EVENT_LBUTTONUP) { + if (_inTextSelection) { + _inTextSelection = false; + + if (_selectionEndY == -1 || + (_selectionEndX == _selectionStartX && _selectionEndY == _selectionStartY)) { + _selectionStartY = _selectionEndY = -1; + _consoleFullRedraw = true; + _menu->enableCommand(kMenuEdit, kMenuActionCopy, false); + } else { + _menu->enableCommand(kMenuEdit, kMenuActionCopy, true); + + bool cutAllowed = false; + + if (_selectionStartY == _selectionEndY && _selectionStartY == (int)_lines.size() - 1) + cutAllowed = true; + + _menu->enableCommand(kMenuEdit, kMenuActionCut, cutAllowed); + _menu->enableCommand(kMenuEdit, kMenuActionClear, cutAllowed); + } + } + + return true; + } else if (event.type == Common::EVENT_MOUSEMOVE) { + if (_inTextSelection) { + updateTextSelection(event.mouse.x, event.mouse.y); + return true; + } + } + + return false; + } + + return false; +} + +int Gui::calcTextX(int x, int textLine) { + const Graphics::Font *font = getConsoleFont(); + + if ((uint)textLine >= _lines.size()) + return 0; + + Common::String str = _lines[textLine]; + + x -= _consoleWindow->getInnerDimensions().left; + + for (int i = str.size(); i >= 0; i--) { + if (font->getStringWidth(str) < x) { + return i; + } + + str.deleteLastChar(); + } + + return 0; +} + +int Gui::calcTextY(int y) { + y -= _consoleWindow->getInnerDimensions().top; + + if (y < 0) + y = 0; + + const int firstLine = _scrollPos / _consoleLineHeight; + int textLine = (y - _scrollPos % _consoleLineHeight) / _consoleLineHeight + firstLine; + + return textLine; +} + +void Gui::startMarking(int x, int y) { + _selectionStartY = calcTextY(y); + _selectionStartX = calcTextX(x, _selectionStartY); + + _selectionEndY = -1; + + _inTextSelection = true; +} + +void Gui::updateTextSelection(int x, int y) { + _selectionEndY = calcTextY(y); + _selectionEndX = calcTextX(x, _selectionEndY); + + _consoleFullRedraw = true; +} + } // End of namespace Wage diff --git a/engines/wage/gui.cpp b/engines/wage/gui.cpp index 50b8b00861..310e5734b7 100644 --- a/engines/wage/gui.cpp +++ b/engines/wage/gui.cpp @@ -46,73 +46,40 @@ */ #include "common/timer.h" -#include "common/unzip.h" +#include "common/system.h" #include "graphics/cursorman.h" -#include "graphics/fonts/bdf.h" -#include "graphics/palette.h" #include "graphics/primitives.h" #include "wage/wage.h" #include "wage/design.h" #include "wage/entities.h" +#include "wage/gui.h" #include "wage/macwindow.h" #include "wage/macwindowmanager.h" -#include "wage/menu.h" -#include "wage/gui.h" +#include "wage/macmenu.h" #include "wage/world.h" namespace Wage { -static const byte palette[] = { - 0, 0, 0, // Black - 0x80, 0x80, 0x80, // Gray - 0xff, 0xff, 0xff, // White - 0x00, 0xff, 0x00, // Green - 0x00, 0x7f, 0x00 // Green2 -}; - -static byte fillPatterns[][8] = { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, // kPatternSolid - { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }, // kPatternStripes - { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 }, // kPatternCheckers - { 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa } // kPatternCheckers2 -}; - -static const byte macCursorArrow[] = { - 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 2, 0, 2, 3, 3, 3, 3, 3, 3, 3, 3, - 2, 0, 0, 2, 3, 3, 3, 3, 3, 3, 3, - 2, 0, 0, 0, 2, 3, 3, 3, 3, 3, 3, - 2, 0, 0, 0, 0, 2, 3, 3, 3, 3, 3, - 2, 0, 0, 0, 0, 0, 2, 3, 3, 3, 3, - 2, 0, 0, 0, 0, 0, 0, 2, 3, 3, 3, - 2, 0, 0, 0, 0, 0, 0, 0, 2, 3, 3, - 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, - 2, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, - 2, 0, 0, 2, 0, 0, 2, 3, 3, 3, 3, - 2, 0, 2, 3, 2, 0, 0, 2, 3, 3, 3, - 2, 2, 3, 3, 2, 0, 0, 2, 3, 3, 3, - 2, 3, 3, 3, 3, 2, 0, 0, 2, 3, 3, - 3, 3, 3, 3, 3, 2, 0, 0, 2, 3, 3, - 3, 3, 3, 3, 3, 3, 2, 2, 2, 3, 3 -}; - -static const byte macCursorBeam[] = { - 0, 0, 3, 3, 3, 0, 0, 3, 3, 3, 3, - 3, 3, 0, 3, 0, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 0, 3, 0, 3, 3, 3, 3, 3, 3, - 0, 0, 3, 3, 3, 0, 0, 3, 3, 3, 3, +static const MenuData menuSubItems[] = { + { kMenuHighLevel, "File", 0, 0, false }, + { kMenuHighLevel, "Edit", 0, 0, false }, + { kMenuFile, "New", kMenuActionNew, 0, false }, + { kMenuFile, "Open...", kMenuActionOpen, 0, false }, + { kMenuFile, "Close", kMenuActionClose, 0, true }, + { kMenuFile, "Save", kMenuActionSave, 0, false }, + { kMenuFile, "Save as...", kMenuActionSaveAs, 0, true }, + { kMenuFile, "Revert", kMenuActionRevert, 0, false }, + { kMenuFile, "Quit", kMenuActionQuit, 0, true }, + + { kMenuEdit, "Undo", kMenuActionUndo, 'Z', false }, + { kMenuEdit, NULL, 0, 0, false }, + { kMenuEdit, "Cut", kMenuActionCut, 'K', false }, + { kMenuEdit, "Copy", kMenuActionCopy, 'C', false }, + { kMenuEdit, "Paste", kMenuActionPaste, 'V', false }, + { kMenuEdit, "Clear", kMenuActionClear, 'B', false }, + + { 0, NULL, 0, 0, false } }; static void cursorTimerHandler(void *refCon) { @@ -127,8 +94,8 @@ static void cursorTimerHandler(void *refCon) { if (!gui->_screen.getPixels()) return; - x += gui->_consoleTextArea.left; - y += gui->_consoleTextArea.top; + x += gui->_consoleWindow->getInnerDimensions().left; + y += gui->_consoleWindow->getInnerDimensions().top; gui->_screen.vLine(x, y, y + kCursorHeight, gui->_cursorState ? kColorBlack : kColorWhite); @@ -143,22 +110,25 @@ static void cursorTimerHandler(void *refCon) { gui->_cursorDirty = true; } +static bool sceneWindowCallback(WindowClick click, Common::Event &event, void *gui); +static bool consoleWindowCallback(WindowClick click, Common::Event &event, void *gui); +static void menuCommandsCallback(int action, Common::String &text, void *data); + + Gui::Gui(WageEngine *engine) { _engine = engine; _scene = NULL; _sceneDirty = true; _consoleDirty = true; - _bordersDirty = true; - _menuDirty = true; _cursorDirty = false; _consoleFullRedraw = true; _screen.create(g_system->getWidth(), g_system->getHeight(), Graphics::PixelFormat::createFormatCLUT8()); + _wm.setScreen(&_screen); + _scrollPos = 0; _consoleLineHeight = 8; // Dummy value which makes sense _consoleNumLines = 24; // Dummy value - _builtInFonts = false; - _sceneIsActive = false; _cursorX = 0; _cursorY = 0; @@ -171,31 +141,39 @@ Gui::Gui(WageEngine *engine) { _inputTextLineNum = 0; - g_system->getPaletteManager()->setPalette(palette, 0, 4); + g_system->getTimerManager()->installTimerProc(&cursorTimerHandler, 200000, this, "wageCursor"); - CursorMan.replaceCursorPalette(palette, 0, 4); - CursorMan.replaceCursor(macCursorArrow, 11, 16, 1, 1, 3); - _cursorIsArrow = true; - CursorMan.showMouse(true); + _menu = _wm.addMenu(); - for (int i = 0; i < ARRAYSIZE(fillPatterns); i++) - _patterns.push_back(fillPatterns[i]); + _menu->setCommandsCallback(menuCommandsCallback, this); - loadFonts(); + _menu->addStaticMenus(menuSubItems); + _menu->addMenuSubItem(kMenuAbout, _engine->_world->getAboutMenuItemName(), kMenuActionAbout); - g_system->getTimerManager()->installTimerProc(&cursorTimerHandler, 200000, this, "wageCursor"); + _commandsMenuId = _menu->addMenuItem(_engine->_world->_commandsMenuName.c_str()); + regenCommandsMenu(); - _menu = new Menu(this); + if (!_engine->_world->_weaponMenuDisabled) { + _weaponsMenuId = _menu->addMenuItem(_engine->_world->_weaponsMenuName.c_str()); - _sceneWindowId = _wm.add(false); - _consoleWindowId = _wm.add(true); + regenWeaponsMenu(); + } else { + _weaponsMenuId = -1; + } + + _menu->calcDimensions(); + + _sceneWindow = _wm.addWindow(false, false, false); + _sceneWindow->setCallback(sceneWindowCallback, this); + + _consoleWindow = _wm.addWindow(true, true, true); + _consoleWindow->setCallback(consoleWindowCallback, this); } Gui::~Gui() { _screen.free(); _console.free(); g_system->getTimerManager()->removeTimerProc(&cursorTimerHandler); - delete _menu; } void Gui::undrawCursor() { @@ -205,63 +183,34 @@ void Gui::undrawCursor() { _cursorOff = false; } -const Graphics::Font *Gui::getFont(const char *name, Graphics::FontManager::FontUsage fallback) { - const Graphics::Font *font = 0; - - if (!_builtInFonts) { - font = FontMan.getFontByName(name); - - if (!font) - warning("Cannot load font %s", name); - } - - if (_builtInFonts || !font) - font = FontMan.getFontByUsage(fallback); - - return font; -} - -const Graphics::Font *Gui::getTitleFont() { - return getFont("Chicago-12", Graphics::FontManager::kBigGUIFont); -} - -void Gui::drawDesktop() { - // Draw desktop - Common::Rect r(0, 0, _screen.w - 1, _screen.h - 1); - Design::drawFilledRoundRect(&_screen, r, kDesktopArc, kColorBlack, _patterns, kPatternCheckers); - g_system->copyRectToScreen(_screen.getPixels(), _screen.pitch, 0, 0, _screen.w, _screen.h); -} - void Gui::draw() { if (_engine->_isGameOver) { - if (_menuDirty) { - drawDesktop(); - _menu->render(); - } - - _menuDirty = false; + _wm.draw(); return; } - if (_scene != _engine->_world->_player->_currentScene) + if (!_engine->_world->_player->_currentScene) + return; + + if (_scene != _engine->_world->_player->_currentScene) { _sceneDirty = true; - if (_sceneDirty || _bordersDirty) - drawDesktop(); + _scene = _engine->_world->_player->_currentScene; - if (_sceneIsActive) { - drawConsole(); - drawScene(); - } else { - drawScene(); - drawConsole(); + _sceneWindow->setDimensions(*_scene->_designBounds); + _sceneWindow->setTitle(_scene->_name); + _consoleWindow->setDimensions(*_scene->_textBounds); + + _wm.setFullRefresh(true); } - if (_menuDirty) - _menu->render(); + drawScene(); + drawConsole(); - if (_cursorDirty) { + _wm.draw(); + + if (_cursorDirty && _cursorRect.left < _screen.w && _cursorRect.bottom < _screen.h) { g_system->copyRectToScreen(_screen.getBasePtr(_cursorRect.left, _cursorRect.top), _screen.pitch, _cursorRect.left, _cursorRect.top, _cursorRect.width(), _cursorRect.height()); @@ -270,438 +219,141 @@ void Gui::draw() { _sceneDirty = false; _consoleDirty = false; - _bordersDirty = false; - _menuDirty = false; _consoleFullRedraw = false; } void Gui::drawScene() { - if (!_sceneDirty && !_bordersDirty) + if (!_sceneDirty) return; - _scene = _engine->_world->_player->_currentScene; - - MacWindow *w = _wm.getWindow(_sceneWindowId); - - w->setDimensions(*_scene->_designBounds); + _scene->paint(_sceneWindow->getSurface(), 0, 0); + _sceneWindow->setDirty(true); _sceneDirty = true; _consoleDirty = true; - _menuDirty = true; + _menu->setDirty(true); _consoleFullRedraw = true; - - _scene->paint(&_screen, _scene->_designBounds->left, _scene->_designBounds->top); - - _sceneArea.left = _scene->_designBounds->left + kBorderWidth - 2; - _sceneArea.top = _scene->_designBounds->top + kBorderWidth - 2; - _sceneArea.setWidth(_scene->_designBounds->width() - 2 * kBorderWidth); - _sceneArea.setHeight(_scene->_designBounds->height() - 2 * kBorderWidth); - - _consoleTextArea.left = _scene->_textBounds->left + kBorderWidth - 2; - _consoleTextArea.top = _scene->_textBounds->top + kBorderWidth - 2; - _consoleTextArea.setWidth(_scene->_textBounds->width() - 2 * kBorderWidth); - _consoleTextArea.setHeight(_scene->_textBounds->height() - 2 * kBorderWidth); - - paintBorder(&_screen, _sceneArea, kWindowScene); -} - -// Render console -void Gui::drawConsole() { - if (!_consoleDirty && !_consoleFullRedraw && !_bordersDirty && !_sceneDirty) - return; - - renderConsole(&_screen, _consoleTextArea); - paintBorder(&_screen, _consoleTextArea, kWindowConsole); -} - -void Gui::drawBox(Graphics::Surface *g, int x, int y, int w, int h) { - Common::Rect r(x, y, x + w + 1, y + h + 1); - - g->fillRect(r, kColorWhite); - g->frameRect(r, kColorBlack); } -void Gui::fillRect(Graphics::Surface *g, int x, int y, int w, int h, int color) { - Common::Rect r(x, y, x + w, y + h); - - g->fillRect(r, color); -} - -#define ARROW_W 12 -#define ARROW_H 6 -const int arrowPixels[ARROW_H][ARROW_W] = { - {0,0,0,0,0,1,1,0,0,0,0,0}, - {0,0,0,0,1,1,1,1,0,0,0,0}, - {0,0,0,1,1,1,1,1,1,0,0,0}, - {0,0,1,1,1,1,1,1,1,1,0,0}, - {0,1,1,1,1,1,1,1,1,1,1,0}, - {1,1,1,1,1,1,1,1,1,1,1,1}}; - -static void drawPixelInverted(int x, int y, int color, void *data) { - Graphics::Surface *surface = (Graphics::Surface *)data; +static bool sceneWindowCallback(WindowClick click, Common::Event &event, void *g) { + Gui *gui = (Gui *)g; - if (x >= 0 && x < surface->w && y >= 0 && y < surface->h) { - byte *p = (byte *)surface->getBasePtr(x, y); - - *p = *p == kColorWhite ? kColorBlack : kColorWhite; - } + return gui->processSceneEvents(click, event); } -void Gui::paintBorder(Graphics::Surface *g, Common::Rect &r, WindowType windowType, int highlightedPart, float scrollPos, float scrollSize) { - bool active = false, scrollable = false, closeable = false, drawTitle = false; - const int size = kBorderWidth; - int x = r.left - size; - int y = r.top - size; - int width = r.width() + 2 * size; - int height = r.height() + 2 * size; - - switch (windowType) { - case kWindowScene: - active = _sceneIsActive; - scrollable = false; - closeable = _sceneIsActive; - drawTitle = true; - break; - case kWindowConsole: - active = !_sceneIsActive; - scrollable = true; - closeable = !_sceneIsActive; - drawTitle = false; - break; - } - - drawBox(g, x, y, size, size); - drawBox(g, x + width - size - 1, y, size, size); - drawBox(g, x + width - size - 1, y + height - size - 1, size, size); - drawBox(g, x, y + height - size - 1, size, size); - drawBox(g, x + size, y + 2, width - 2 * size - 1, size - 4); - drawBox(g, x + size, y + height - size + 1, width - 2 * size - 1, size - 4); - drawBox(g, x + 2, y + size, size - 4, height - 2 * size - 1); - drawBox(g, x + width - size + 1, y + size, size - 4, height - 2 * size - 1); - - if (active) { - fillRect(g, x + size, y + 5, width - 2 * size - 1, 8); - fillRect(g, x + size, y + height - 13, width - 2 * size - 1, 8); - fillRect(g, x + 5, y + size, 8, height - 2 * size - 1); - if (!scrollable) { - fillRect(g, x + width - 13, y + size, 8, height - 2 * size - 1); - } else { - int x1 = x + width - 15; - int y1 = y + size + 1; - - for (int yy = 0; yy < ARROW_H; yy++) { - for (int xx = 0; xx < ARROW_W; xx++) - g->hLine(x1 + xx, y1 + yy, x1 + xx, (arrowPixels[yy][xx] != 0 ? kColorBlack : kColorWhite)); - } - - fillRect(g, x + width - 13, y + size + ARROW_H, 8, height - 2 * size - 1 - ARROW_H * 2); - - y1 += height - 2 * size - ARROW_H - 2; - for (int yy = 0; yy < ARROW_H; yy++) { - for (int xx = 0; xx < ARROW_W; xx++) - g->hLine(x1 + xx, y1 + yy, x1 + xx, (arrowPixels[ARROW_H - yy - 1][xx] != 0 ? kColorBlack : kColorWhite)); - } - - if (highlightedPart == kBorderScrollUp || highlightedPart == kBorderScrollDown) { - int rx1 = x + width - kBorderWidth + 2; - int ry1 = y + size + r.height() * scrollPos; - int rx2 = rx1 + size - 4; - int ry2 = ry1 + r.height() * scrollSize; - Common::Rect rr(rx1, ry1, rx2, ry2); - - Graphics::drawFilledRect(rr, kColorBlack, drawPixelInverted, g); - } - } - if (closeable) { - if (highlightedPart == kBorderCloseButton) { - fillRect(g, x + 6, y + 6, 6, 6); - } else { - drawBox(g, x + 5, y + 5, 7, 7); - } - } - } - - if (drawTitle) { - const Graphics::Font *font = getTitleFont(); - int yOff = _builtInFonts ? 3 : 1; +bool Gui::processSceneEvents(WindowClick click, Common::Event &event) { + if (click == kBorderInner && event.type == Common::EVENT_LBUTTONUP) { + Designed *obj = _scene->lookUpEntity(event.mouse.x - _sceneWindow->getDimensions().left, + event.mouse.y - _sceneWindow->getDimensions().top); - int w = font->getStringWidth(_scene->_name) + 10; - int maxWidth = width - size * 2 - 7; - if (w > maxWidth) - w = maxWidth; - drawBox(g, x + (width - w) / 2, y, w, size); - font->drawString(g, _scene->_name, x + (width - w) / 2 + 5, y + yOff, w, kColorBlack); - } + if (obj != nullptr) + _engine->processTurn(NULL, obj); - if (x < 0) { - width += x; - x = 0; - } - if (y < 0) { - height += y; - y = 0; + return true; } - if (x + width > _screen.w) - width = _screen.w - x; - if (y + height > _screen.h) - height = _screen.h - y; - g_system->copyRectToScreen(g->getBasePtr(x, y), g->pitch, x, y, width, height); + return false; } -void Gui::loadFonts() { - Common::Archive *dat; - - dat = Common::makeZipArchive("wage.dat"); - - if (!dat) { - warning("Could not find wage.dat. Falling back to built-in fonts"); - _builtInFonts = true; - +// Render console +void Gui::drawConsole() { + if (!_consoleDirty && !_consoleFullRedraw && !_sceneDirty) return; - } - Common::ArchiveMemberList list; - dat->listMembers(list); - - for (Common::ArchiveMemberList::iterator it = list.begin(); it != list.end(); ++it) { - Common::SeekableReadStream *stream = dat->createReadStreamForMember((*it)->getName()); - - Graphics::BdfFont *font = Graphics::BdfFont::loadFont(*stream); - - delete stream; - - Common::String fontName = (*it)->getName(); - - // Trim the .bdf extension - for (int i = fontName.size() - 1; i >= 0; --i) { - if (fontName[i] == '.') { - while ((uint)i < fontName.size()) { - fontName.deleteLastChar(); - } - break; - } - } - - FontMan.assignFontToName(fontName, font); - - debug(2, " %s", fontName.c_str()); - } + renderConsole(_consoleWindow->getSurface(), Common::Rect(kBorderWidth - 2, kBorderWidth - 2, + _consoleWindow->getDimensions().width(), _consoleWindow->getDimensions().height())); + _consoleWindow->setDirty(true); +} - _builtInFonts = false; +static bool consoleWindowCallback(WindowClick click, Common::Event &event, void *g) { + Gui *gui = (Gui *)g; - delete dat; + return gui->processConsoleEvents(click, event); } +//////////////// +// Menu stuff +//////////////// void Gui::regenCommandsMenu() { - _menu->regenCommandsMenu(); + _menu->createSubMenuFromString(_commandsMenuId, _engine->_world->_commandsMenu.c_str()); } void Gui::regenWeaponsMenu() { - _menu->regenWeaponsMenu(); -} - -void Gui::processMenuShortCut(byte flags, uint16 ascii) { - _menu->processMenuShortCut(flags, ascii); -} - -void Gui::mouseMove(int x, int y) { - if (_menu->_menuActivated) { - if (_menu->mouseMove(x, y)) - _menuDirty = true; - + if (_engine->_world->_weaponMenuDisabled) return; - } - if (_inTextSelection) { - updateTextSelection(x, y); - return; - } + _menu->clearSubMenu(_weaponsMenuId); - if (_consoleTextArea.contains(x, y)) { - if (_cursorIsArrow) { - CursorMan.replaceCursor(macCursorBeam, 11, 16, 3, 8, 3); - _cursorIsArrow = false; - } - } else if (_cursorIsArrow == false) { - CursorMan.replaceCursor(macCursorArrow, 11, 16, 1, 1, 3); - _cursorIsArrow = true; - } -} - -void Gui::pushArrowCursor() { - CursorMan.pushCursor(macCursorArrow, 11, 16, 1, 1, 3); -} - -void Gui::popCursor() { - CursorMan.popCursor(); -} - -static int isInBorder(Common::Rect &rect, int x, int y) { - if (x >= rect.left - kBorderWidth && x < rect.left && y >= rect.top - kBorderWidth && y < rect.top) - return kBorderCloseButton; - - if (x >= rect.right && x < rect.right + kBorderWidth) { - if (y < rect.top - kBorderWidth) - return kBorderNone; - - if (y >= rect.bottom + kBorderWidth) - return kBorderNone; + Chr *player = _engine->_world->_player; + ObjArray *weapons = player->getWeapons(true); - if (y >= rect.top + rect.height() / 2) - return kBorderScrollDown; + bool empty = true; - return kBorderScrollUp; - } + for (uint i = 0; i < weapons->size(); i++) { + Obj *obj = (*weapons)[i]; + if (obj->_type == Obj::REGULAR_WEAPON || + obj->_type == Obj::THROW_WEAPON || + obj->_type == Obj::MAGICAL_OBJECT) { + Common::String command(obj->_operativeVerb); + command += " "; + command += obj->_name; - return kBorderNone; -} + _menu->addMenuSubItem(_weaponsMenuId, command.c_str(), kMenuActionCommand, 0, 0, true); -Designed *Gui::mouseUp(int x, int y) { - if (_menu->_menuActivated) { - if (_menu->mouseRelease(x, y)) { - _sceneDirty = true; - _consoleDirty = true; - _bordersDirty = true; - _menuDirty = true; + empty = false; } - - return NULL; } + delete weapons; - if (_inTextSelection) { - _inTextSelection = false; - - if (_selectionEndY == -1 || - (_selectionEndX == _selectionStartX && _selectionEndY == _selectionStartY)) { - _selectionStartY = _selectionEndY = -1; - _consoleFullRedraw = true; - _menu->enableCommand(kMenuEdit, kMenuActionCopy, false); - } else { - _menu->enableCommand(kMenuEdit, kMenuActionCopy, true); - - bool cutAllowed = false; - - if (_selectionStartY == _selectionEndY && _selectionStartY == (int)_lines.size() - 1) - cutAllowed = true; - - _menu->enableCommand(kMenuEdit, kMenuActionCut, cutAllowed); - _menu->enableCommand(kMenuEdit, kMenuActionClear, cutAllowed); - } - } - - int borderClick; - - if (_sceneArea.contains(x, y)) { - if (!_sceneIsActive) { - _sceneIsActive = true; - _bordersDirty = true; - } - - for (ObjList::const_iterator it = _scene->_objs.end(); it != _scene->_objs.begin(); ) { - it--; - if ((*it)->_design->isPointOpaque(x - _sceneArea.left + kBorderWidth, y - _sceneArea.top + kBorderWidth)) - return *it; - } - - for (ChrList::const_iterator it = _scene->_chrs.end(); it != _scene->_chrs.begin(); ) { - it--; - if ((*it)->_design->isPointOpaque(x - _sceneArea.left + kBorderWidth, y - _sceneArea.top + kBorderWidth)) - return *it; - } - } else if (_consoleTextArea.contains(x, y)) { - if (_sceneIsActive) { - _sceneIsActive = false; - _bordersDirty = true; - } - } else if ((borderClick = isInBorder(_consoleTextArea, x, y)) != kBorderNone) { - _bordersDirty = true; - int _oldScrollPos = _scrollPos; - - switch (borderClick) { - case kBorderScrollUp: - _scrollPos = MAX<int>(0, _scrollPos - _consoleLineHeight); - undrawCursor(); - _cursorY -= (_scrollPos - _oldScrollPos); - _consoleDirty = true; - _consoleFullRedraw = true; - break; - case kBorderScrollDown: - _scrollPos = MIN<int>((_lines.size() - 2) * _consoleLineHeight, _scrollPos + _consoleLineHeight); - undrawCursor(); - _cursorY -= (_scrollPos - _oldScrollPos); - _consoleDirty = true; - _consoleFullRedraw = true; - break; - } - } - - return NULL; + if (empty) + _menu->addMenuSubItem(_weaponsMenuId, "You have no weapons", 0, 0, 0, false); } -void Gui::mouseDown(int x, int y) { - int borderClick; - - if (_menu->mouseClick(x, y)) { - _menuDirty = true; - } else if (_consoleTextArea.contains(x, y)) { - startMarking(x, y); - } else if ((borderClick = isInBorder(_consoleTextArea, x, y)) != kBorderNone) { - int textFullSize = _lines.size() * _consoleLineHeight + _consoleTextArea.height(); - float scrollPos = (float)_scrollPos / textFullSize; - float scrollSize = (float)_consoleTextArea.height() / textFullSize; - - paintBorder(&_screen, _consoleTextArea, kWindowConsole, borderClick, scrollPos, scrollSize); - } -} - -int Gui::calcTextX(int x, int textLine) { - const Graphics::Font *font = getConsoleFont(); - - if ((uint)textLine >= _lines.size()) - return 0; - - Common::String str = _lines[textLine]; - - x -= _consoleTextArea.left; - - for (int i = str.size(); i >= 0; i--) { - if (font->getStringWidth(str) < x) { - return i; - } - - str.deleteLastChar(); - } - - return 0; +bool Gui::processEvent(Common::Event &event) { + return _wm.processEvent(event); } -int Gui::calcTextY(int y) { - y -= _consoleTextArea.top; - - if (y < 0) - y = 0; +void menuCommandsCallback(int action, Common::String &text, void *data) { + Gui *g = (Gui *)data; - const int firstLine = _scrollPos / _consoleLineHeight; - int textLine = (y - _scrollPos % _consoleLineHeight) / _consoleLineHeight + firstLine; - - return textLine; + g->executeMenuCommand(action, text); } -void Gui::startMarking(int x, int y) { - _selectionStartY = calcTextY(y); - _selectionStartX = calcTextX(x, _selectionStartY); - - _selectionEndY = -1; +void Gui::executeMenuCommand(int action, Common::String &text) { + switch(action) { + case kMenuActionAbout: + case kMenuActionNew: + case kMenuActionOpen: + case kMenuActionClose: + case kMenuActionSave: + case kMenuActionSaveAs: + case kMenuActionRevert: + case kMenuActionQuit: + + case kMenuActionUndo: + actionUndo(); + break; + case kMenuActionCut: + actionCut(); + break; + case kMenuActionCopy: + actionCopy(); + break; + case kMenuActionPaste: + actionPaste(); + break; + case kMenuActionClear: + actionClear(); + break; - _inTextSelection = true; -} + case kMenuActionCommand: + _engine->processTurn(&text, NULL); + break; -void Gui::updateTextSelection(int x, int y) { - _selectionEndY = calcTextY(y); - _selectionEndX = calcTextX(x, _selectionEndY); + default: + warning("Unknown action: %d", action); - _consoleFullRedraw = true; + } } } // End of namespace Wage diff --git a/engines/wage/gui.h b/engines/wage/gui.h index 11e001b274..ba1bb5ef3b 100644 --- a/engines/wage/gui.h +++ b/engines/wage/gui.h @@ -50,8 +50,8 @@ #include "common/str-array.h" #include "graphics/font.h" -#include "graphics/fontman.h" -#include "graphics/surface.h" +#include "graphics/managed_surface.h" +#include "common/events.h" #include "common/rect.h" #include "wage/macwindow.h" @@ -60,27 +60,13 @@ namespace Wage { class Menu; +class Scene; +class WageEngine; enum { - kMenuHeight = 20, - kMenuLeftMargin = 7, - kMenuSpacing = 13, - kMenuPadding = 16, - kMenuDropdownPadding = 14, - kMenuDropdownItemHeight = 16, - kMenuItemHeight = 20, - kDesktopArc = 7, - kComponentsPadding = 10, kCursorHeight = 12 }; -enum { - kPatternSolid = 1, - kPatternStripes = 2, - kPatternCheckers = 3, - kPatternCheckers2 = 4 -}; - class Gui { public: Gui(WageEngine *engine); @@ -89,17 +75,12 @@ public: void draw(); void appendText(const char *str); void clearOutput(); - void mouseMove(int x, int y); - void mouseDown(int x, int y); - Designed *mouseUp(int x, int y); + bool processEvent(Common::Event &event); + void drawInput(); void setSceneDirty() { _sceneDirty = true; } - const Graphics::Font *getFont(const char *name, Graphics::FontManager::FontUsage fallback); void regenCommandsMenu(); void regenWeaponsMenu(); - void processMenuShortCut(byte flags, uint16 ascii); - void pushArrowCursor(); - void popCursor(); void actionCopy(); void actionPaste(); @@ -110,19 +91,15 @@ public: void disableAllMenus(); void enableNewGameMenus(); - bool builtInFonts() { return _builtInFonts; } + bool processSceneEvents(WindowClick click, Common::Event &event); + bool processConsoleEvents(WindowClick click, Common::Event &event); + void executeMenuCommand(int action, Common::String &text); private: void drawScene(); void drawConsole(); void undrawCursor(); - void drawDesktop(); - void paintBorder(Graphics::Surface *g, Common::Rect &r, WindowType windowType, int highlightedPart = kBorderNone, - float scrollPos = 0.0, float scrollSize = 0.0); - void renderConsole(Graphics::Surface *g, Common::Rect &r); - void drawBox(Graphics::Surface *g, int x, int y, int w, int h); - void fillRect(Graphics::Surface *g, int x, int y, int w, int h, int color = kColorBlack); - void loadFonts(); + void renderConsole(Graphics::ManagedSurface *g, const Common::Rect &r); void flowText(Common::String &str); const Graphics::Font *getConsoleFont(); const Graphics::Font *getTitleFont(); @@ -132,29 +109,27 @@ private: void updateTextSelection(int x, int y); public: - Graphics::Surface _screen; + Graphics::ManagedSurface _screen; int _cursorX, _cursorY; bool _cursorState; - Common::Rect _consoleTextArea; - bool _builtInFonts; WageEngine *_engine; - Patterns _patterns; - bool _cursorDirty; Common::Rect _cursorRect; bool _cursorOff; - bool _menuDirty; + Scene *_scene; + + MacWindowManager _wm; + MacWindow *_sceneWindow; + MacWindow *_consoleWindow; private: - Graphics::Surface _console; + Graphics::ManagedSurface _console; Menu *_menu; - Scene *_scene; bool _sceneDirty; bool _consoleDirty; - bool _bordersDirty; Common::StringArray _out; Common::StringArray _lines; @@ -163,10 +138,6 @@ private: uint _consoleNumLines; bool _consoleFullRedraw; - Common::Rect _sceneArea; - bool _sceneIsActive; - bool _cursorIsArrow; - bool _inTextSelection; int _selectionStartX; int _selectionStartY; @@ -178,9 +149,8 @@ private: int _inputTextLineNum; - MacWindowManager _wm; - int _sceneWindowId; - int _consoleWindowId; + int _commandsMenuId; + int _weaponsMenuId; }; } // End of namespace Wage diff --git a/engines/wage/menu.cpp b/engines/wage/macmenu.cpp index 12ef8c2219..ed5f5070ff 100644 --- a/engines/wage/menu.cpp +++ b/engines/wage/macmenu.cpp @@ -48,15 +48,25 @@ #include "common/system.h" #include "common/keyboard.h" -#include "wage/wage.h" -#include "wage/entities.h" -#include "wage/design.h" -#include "wage/gui.h" -#include "wage/menu.h" -#include "wage/world.h" +#include "graphics/primitives.h" +#include "graphics/font.h" + +#include "wage/macwindowmanager.h" +#include "wage/macwindow.h" +#include "wage/macmenu.h" namespace Wage { +enum { + kMenuHeight = 20, + kMenuLeftMargin = 7, + kMenuSpacing = 13, + kMenuPadding = 16, + kMenuDropdownPadding = 14, + kMenuDropdownItemHeight = 16, + kMenuItemHeight = 20 +}; + struct MenuSubItem { Common::String text; int action; @@ -79,66 +89,65 @@ struct MenuItem { MenuItem(const char *n) : name(n) {} }; -struct MenuData { - int menunum; - const char *title; - int action; - byte shortcut; - bool enabled; -} static const menuSubItems[] = { - { kMenuFile, "New", kMenuActionNew, 0, false }, - { kMenuFile, "Open...", kMenuActionOpen, 0, false }, - { kMenuFile, "Close", kMenuActionClose, 0, true }, - { kMenuFile, "Save", kMenuActionSave, 0, false }, - { kMenuFile, "Save as...", kMenuActionSaveAs, 0, true }, - { kMenuFile, "Revert", kMenuActionRevert, 0, false }, - { kMenuFile, "Quit", kMenuActionQuit, 0, true }, - - { kMenuEdit, "Undo", kMenuActionUndo, 'Z', false }, - { kMenuEdit, NULL, 0, 0, false }, - { kMenuEdit, "Cut", kMenuActionCut, 'K', false }, - { kMenuEdit, "Copy", kMenuActionCopy, 'C', false }, - { kMenuEdit, "Paste", kMenuActionPaste, 'V', false }, - { kMenuEdit, "Clear", kMenuActionClear, 'B', false }, - - { 0, NULL, 0, 0, false } -}; +Menu::Menu(int id, const Common::Rect &bounds, MacWindowManager *wm) + : BaseMacWindow(id, false, wm) { + _font = getMenuFont(); -Menu::Menu(Gui *gui) : _gui(gui) { - assert(_gui->_engine); - assert(_gui->_engine->_world); + _screen.create(bounds.width(), bounds.height(), Graphics::PixelFormat::createFormatCLUT8()); - _font = getMenuFont(); + _bbox.left = 0; + _bbox.top = 0; + _bbox.right = _screen.w; + _bbox.bottom = kMenuHeight; + + _menuActivated = false; + _activeItem = -1; + _activeSubItem = -1; + + _ccallback = NULL; + _cdata = NULL; + + _tempSurface.create(_screen.w, _font->getFontHeight(), Graphics::PixelFormat::createFormatCLUT8()); +} + +Menu::~Menu() { + for (uint i = 0; i < _items.size(); i++) { + for (uint j = 0; j < _items[i]->subitems.size(); j++) + delete _items[i]->subitems[j]; + delete _items[i]; + } +} - MenuItem *about = new MenuItem(_gui->_builtInFonts ? "\xa9" : "\xf0"); // (c) Symbol as the most resembling apple +void Menu::addStaticMenus(const MenuData *data) { + MenuItem *about = new MenuItem(_wm->hasBuiltInFonts() ? "\xa9" : "\xf0"); // (c) Symbol as the most resembling apple _items.push_back(about); - _items[0]->subitems.push_back(new MenuSubItem(_gui->_engine->_world->getAboutMenuItemName(), kMenuActionAbout)); - MenuItem *file = new MenuItem("File"); - _items.push_back(file); + for (int i = 0; data[i].menunum; i++) { + const MenuData *m = &data[i]; - MenuItem *edit = new MenuItem("Edit"); - _items.push_back(edit); + if (m->menunum == kMenuHighLevel) { + MenuItem *item = new MenuItem(m->title); + _items.push_back(item); - for (int i = 0; menuSubItems[i].menunum; i++) { - const MenuData *m = &menuSubItems[i]; + continue; + } _items[m->menunum]->subitems.push_back(new MenuSubItem(m->title, m->action, 0, m->shortcut, m->enabled)); } +} - _commands = new MenuItem(_gui->_engine->_world->_commandsMenuName.c_str()); - _items.push_back(_commands); - regenCommandsMenu(); - - _weapons = NULL; +int Menu::addMenuItem(const char *name) { + MenuItem *i = new MenuItem(name); + _items.push_back(i); - if (!_gui->_engine->_world->_weaponMenuDisabled) { - _weapons = new MenuItem(_gui->_engine->_world->_weaponsMenuName.c_str()); - _items.push_back(_weapons); + return _items.size() - 1; +} - regenWeaponsMenu(); - } +void Menu::addMenuSubItem(int id, const char *text, int action, int style, char shortcut, bool enabled) { + _items[id]->subitems.push_back(new MenuSubItem(text, action, style, shortcut, enabled)); +} +void Menu::calcDimensions() { // Calculate menu dimensions int y = 1; int x = 18; @@ -150,47 +159,29 @@ Menu::Menu(Gui *gui) : _gui(gui) { _items[i]->bbox.left = x - kMenuLeftMargin; _items[i]->bbox.top = y; _items[i]->bbox.right = x + w + kMenuSpacing - kMenuLeftMargin; - _items[i]->bbox.bottom = y + _font->getFontHeight() + (_gui->_builtInFonts ? 3 : 2); + _items[i]->bbox.bottom = y + _font->getFontHeight() + (_wm->hasBuiltInFonts() ? 3 : 2); } calcMenuBounds(_items[i]); x += w + kMenuSpacing; } - - _bbox.left = 0; - _bbox.top = 0; - _bbox.right = _gui->_screen.w - 1; - _bbox.bottom = kMenuHeight - 1; - - _menuActivated = false; - _activeItem = -1; - _activeSubItem = -1; - - _screenCopy.create(_gui->_screen.w, _gui->_screen.h, Graphics::PixelFormat::createFormatCLUT8()); - _tempSurface.create(_gui->_screen.w, _font->getFontHeight(), Graphics::PixelFormat::createFormatCLUT8()); -} - -Menu::~Menu() { - for (uint i = 0; i < _items.size(); i++) { - for (uint j = 0; j < _items[i]->subitems.size(); j++) - delete _items[i]->subitems[j]; - delete _items[i]; - } } -void Menu::regenCommandsMenu() { - for (uint j = 0; j < _commands->subitems.size(); j++) - delete _commands->subitems[j]; +void Menu::clearSubMenu(int id) { + MenuItem *menu = _items[id]; - _commands->subitems.clear(); + for (uint j = 0; j < menu->subitems.size(); j++) + delete menu->subitems[j]; - createCommandsMenu(_commands); - calcMenuBounds(_commands); + menu->subitems.clear(); } -void Menu::createCommandsMenu(MenuItem *menu) { - Common::String string(_gui->_engine->_world->_commandsMenu); +void Menu::createSubMenuFromString(int id, const char *str) { + clearSubMenu(id); + + MenuItem *menu = _items[id]; + Common::String string(str); Common::String item; @@ -253,45 +244,12 @@ void Menu::createCommandsMenu(MenuItem *menu) { item.clear(); } -} - -void Menu::regenWeaponsMenu() { - if (_gui->_engine->_world->_weaponMenuDisabled) - return; - - for (uint j = 0; j < _weapons->subitems.size(); j++) - delete _weapons->subitems[j]; - - _weapons->subitems.clear(); - - createWeaponsMenu(_weapons); - calcMenuBounds(_weapons); -} - -void Menu::createWeaponsMenu(MenuItem *menu) { - Chr *player = _gui->_engine->_world->_player; - ObjArray *weapons = player->getWeapons(true); - - for (uint i = 0; i < weapons->size(); i++) { - Obj *obj = (*weapons)[i]; - if (obj->_type == Obj::REGULAR_WEAPON || - obj->_type == Obj::THROW_WEAPON || - obj->_type == Obj::MAGICAL_OBJECT) { - Common::String command(obj->_operativeVerb); - command += " "; - command += obj->_name; - - menu->subitems.push_back(new MenuSubItem(command.c_str(), kMenuActionCommand, 0, 0, true)); - } - } - delete weapons; - if (menu->subitems.empty()) - menu->subitems.push_back(new MenuSubItem("You have no weapons", 0, 0, 0, false)); + calcMenuBounds(menu); } const Graphics::Font *Menu::getMenuFont() { - return _gui->getFont("Chicago-12", Graphics::FontManager::kBigGUIFont); + return _wm->getFont("Chicago-12", Graphics::FontManager::kBigGUIFont); } const char *Menu::getAcceleratorString(MenuSubItem *item, const char *prefix) { @@ -299,7 +257,7 @@ const char *Menu::getAcceleratorString(MenuSubItem *item, const char *prefix) { *res = 0; if (item->shortcut != 0) - sprintf(res, "%s%c%c", prefix, (_gui->_builtInFonts ? '^' : '\x11'), item->shortcut); + sprintf(res, "%s%c%c", prefix, (_wm->hasBuiltInFonts() ? '^' : '\x11'), item->shortcut); return res; } @@ -338,14 +296,35 @@ void Menu::calcMenuBounds(MenuItem *menu) { menu->subbbox.bottom = y2; } -void Menu::render() { +static void drawPixelPlain(int x, int y, int color, void *data) { + Graphics::ManagedSurface *surface = (Graphics::ManagedSurface *)data; + + if (x >= 0 && x < surface->w && y >= 0 && y < surface->h) + *((byte *)surface->getBasePtr(x, y)) = (byte)color; +} + +static void drawFilledRoundRect(Graphics::ManagedSurface *surface, Common::Rect &rect, int arc, int color) { + Graphics::drawRoundRect(rect, arc, color, true, drawPixelPlain, surface); +} + +bool Menu::draw(Graphics::ManagedSurface *g, bool forceRedraw) { Common::Rect r(_bbox); - Design::drawFilledRoundRect(&_gui->_screen, r, kDesktopArc, kColorWhite, _gui->_patterns, kPatternSolid); + if (!_contentIsDirty && !forceRedraw) + return false; + + _contentIsDirty = false; + + _screen.clear(kColorGreen); + + drawFilledRoundRect(&_screen, r, kDesktopArc, kColorWhite); r.top = 7; - Design::drawFilledRect(&_gui->_screen, r, kColorWhite, _gui->_patterns, kPatternSolid); + _screen.fillRect(r, kColorWhite); r.top = kMenuHeight - 1; - Design::drawFilledRect(&_gui->_screen, r, kColorBlack, _gui->_patterns, kPatternSolid); + r.bottom++; + _screen.fillRect(r, kColorGreen); + r.bottom--; + _screen.fillRect(r, kColorBlack); for (uint i = 0; i < _items.size(); i++) { int color = kColorBlack; @@ -355,19 +334,24 @@ void Menu::render() { Common::Rect hbox = it->bbox; hbox.left -= 1; - hbox.right += 2; + hbox.right += 3; + hbox.bottom += 1; - Design::drawFilledRect(&_gui->_screen, hbox, kColorBlack, _gui->_patterns, kPatternSolid); + _screen.fillRect(hbox, kColorBlack); color = kColorWhite; if (!it->subitems.empty()) renderSubmenu(it); } - _font->drawString(&_gui->_screen, it->name, it->bbox.left + kMenuLeftMargin, it->bbox.top + (_gui->_builtInFonts ? 2 : 1), it->bbox.width(), color); + _font->drawString(&_screen, it->name, it->bbox.left + kMenuLeftMargin, it->bbox.top + (_wm->hasBuiltInFonts() ? 2 : 1), it->bbox.width(), color); } - g_system->copyRectToScreen(_gui->_screen.getPixels(), _gui->_screen.pitch, 0, 0, _gui->_screen.w, kMenuHeight); + g->transBlitFrom(_screen, kColorGreen); + + g_system->copyRectToScreen(g->getPixels(), g->pitch, 0, 0, g->w, g->h); + + return true; } void Menu::renderSubmenu(MenuItem *menu) { @@ -376,10 +360,12 @@ void Menu::renderSubmenu(MenuItem *menu) { if (r->width() == 0 || r->height() == 0) return; - Design::drawFilledRect(&_gui->_screen, *r, kColorWhite, _gui->_patterns, kPatternSolid); - Design::drawRect(&_gui->_screen, *r, 1, kColorBlack, _gui->_patterns, kPatternSolid); - Design::drawVLine(&_gui->_screen, r->right + 1, r->top + 3, r->bottom + 1, 1, kColorBlack, _gui->_patterns, kPatternSolid); - Design::drawHLine(&_gui->_screen, r->left + 3, r->right + 1, r->bottom + 1, 1, kColorBlack, _gui->_patterns, kPatternSolid); + _screen.fillRect(*r, kColorWhite); + _screen.frameRect(*r, kColorBlack); + _screen.vLine(r->right, r->top + 3, r->bottom + 1, kColorBlack); + _screen.vLine(r->right + 1, r->top + 3, r->bottom + 1, kColorBlack); + _screen.hLine(r->left + 3, r->bottom, r->right + 1, kColorBlack); + _screen.hLine(r->left + 3, r->bottom + 1, r->right + 1, kColorBlack); int x = r->left + kMenuDropdownPadding; int y = r->top + 1; @@ -391,13 +377,13 @@ void Menu::renderSubmenu(MenuItem *menu) { int color = kColorBlack; if (i == (uint)_activeSubItem && !text.empty() && menu->subitems[i]->enabled) { color = kColorWhite; - Common::Rect trect(r->left, y - (_gui->_builtInFonts ? 1 : 0), r->right, y + _font->getFontHeight()); + Common::Rect trect(r->left, y - (_wm->hasBuiltInFonts() ? 1 : 0), r->right, y + _font->getFontHeight()); - Design::drawFilledRect(&_gui->_screen, trect, kColorBlack, _gui->_patterns, kPatternSolid); + _screen.fillRect(trect, kColorBlack); } if (!text.empty()) { - Graphics::Surface *s = &_gui->_screen; + Graphics::ManagedSurface *s = &_screen; int tx = x, ty = y; if (!menu->subitems[i]->enabled) { @@ -406,7 +392,7 @@ void Menu::renderSubmenu(MenuItem *menu) { ty = 0; accelX -= x; - _tempSurface.fillRect(Common::Rect(0, 0, _tempSurface.w, _tempSurface.h), kColorGreen); + _tempSurface.clear(kColorGreen); } _font->drawString(s, text, tx, ty, r->width(), color); @@ -419,8 +405,8 @@ void Menu::renderSubmenu(MenuItem *menu) { // fake it here for (int ii = 0; ii < _tempSurface.h; ii++) { const byte *src = (const byte *)_tempSurface.getBasePtr(0, ii); - byte *dst = (byte *)_gui->_screen.getBasePtr(x, y+ii); - byte pat = _gui->_patterns[kPatternCheckers2 - 1][ii % 8]; + byte *dst = (byte *)_screen.getBasePtr(x, y+ii); + byte pat = _wm->getPatterns()[kPatternCheckers2 - 1][ii % 8]; for (int j = 0; j < r->width(); j++) { if (*src != kColorGreen && (pat & (1 << (7 - (x + j) % 8)))) *dst = *src; @@ -430,20 +416,51 @@ void Menu::renderSubmenu(MenuItem *menu) { } } } else { // Delimiter - Design::drawHLine(&_gui->_screen, r->left + 1, r->right - 1, y + kMenuDropdownItemHeight / 2, 1, kColorBlack, _gui->_patterns, kPatternStripes); + bool flip = r->left & 2; + byte *ptr = (byte *)_screen.getBasePtr(r->left + 1, y + kMenuDropdownItemHeight / 2); + for (int xx = r->left + 1; xx <= r->right - 1; xx++, ptr++) { + *ptr = flip ? kColorBlack : kColorWhite; + flip = !flip; + } } y += kMenuDropdownItemHeight; } - g_system->copyRectToScreen(_gui->_screen.getBasePtr(r->left, r->top), _gui->_screen.pitch, r->left, r->top, r->width() + 3, r->height() + 3); + _contentIsDirty = true; + //g_system->copyRectToScreen(_screen.getBasePtr(r->left, r->top), _screen.pitch, r->left, r->top, r->width() + 2, r->height() + 2); +} + +bool Menu::processEvent(Common::Event &event) { + switch (event.type) { + case Common::EVENT_KEYDOWN: + return keyEvent(event); + case Common::EVENT_LBUTTONDOWN: + return mouseClick(event.mouse.x, event.mouse.y); + case Common::EVENT_LBUTTONUP: + return mouseRelease(event.mouse.x, event.mouse.y); + case Common::EVENT_MOUSEMOVE: + return mouseMove(event.mouse.x, event.mouse.y); + default: + return false; + } +} + +bool Menu::keyEvent(Common::Event &event) { + if (event.type != Common::EVENT_KEYDOWN) + return false; + + if (event.kbd.flags & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_META)) { + if (event.kbd.ascii >= 0x20 && event.kbd.ascii <= 0x7f) { + return processMenuShortCut(event.kbd.flags, event.kbd.ascii); + } + } + + return false; } bool Menu::mouseClick(int x, int y) { if (_bbox.contains(x, y)) { - if (!_menuActivated) - _screenCopy.copyFrom(_gui->_screen); - for (uint i = 0; i < _items.size(); i++) if (_items[i]->bbox.contains(x, y)) { if ((uint)_activeItem == i) @@ -454,14 +471,15 @@ bool Menu::mouseClick(int x, int y) { r.right += 3; r.bottom += 3; - _gui->_screen.copyRectToSurface(_screenCopy, r.left, r.top, r); - g_system->copyRectToScreen(_gui->_screen.getBasePtr(r.left, r.top), _gui->_screen.pitch, r.left, r.top, r.width() + 1, r.height() + 1); + _wm->setFullRefresh(true); } _activeItem = i; _activeSubItem = -1; _menuActivated = true; + _contentIsDirty = true; + return true; } } else if (_menuActivated && _items[_activeItem]->subbbox.contains(x, y)) { @@ -472,11 +490,13 @@ bool Menu::mouseClick(int x, int y) { _activeSubItem = numSubItem; renderSubmenu(_items[_activeItem]); + _contentIsDirty = true; } } else if (_menuActivated && _activeItem != -1) { _activeSubItem = -1; renderSubmenu(_items[_activeItem]); + _contentIsDirty = true; } return false; @@ -495,77 +515,49 @@ bool Menu::mouseRelease(int x, int y) { _menuActivated = false; if (_activeItem != -1 && _activeSubItem != -1 && _items[_activeItem]->subitems[_activeSubItem]->enabled) - executeCommand(_items[_activeItem]->subitems[_activeSubItem]); + (*_ccallback)(_items[_activeItem]->subitems[_activeSubItem]->action, + _items[_activeItem]->subitems[_activeSubItem]->text, _cdata); _activeItem = -1; _activeSubItem = -1; + _wm->setFullRefresh(true); + return true; } return false; } -void Menu::executeCommand(MenuSubItem *subitem) { - switch(subitem->action) { - case kMenuActionAbout: - case kMenuActionNew: - case kMenuActionOpen: - case kMenuActionClose: - case kMenuActionSave: - case kMenuActionSaveAs: - case kMenuActionRevert: - case kMenuActionQuit: - - case kMenuActionUndo: - _gui->actionUndo(); - break; - case kMenuActionCut: - _gui->actionCut(); - break; - case kMenuActionCopy: - _gui->actionCopy(); - break; - case kMenuActionPaste: - _gui->actionPaste(); - break; - case kMenuActionClear: - _gui->actionClear(); - break; - - case kMenuActionCommand: - _gui->_engine->processTurn(&subitem->text, NULL); - break; - - default: - warning("Unknown action: %d", subitem->action); - - } -} - -void Menu::processMenuShortCut(byte flags, uint16 ascii) { +bool Menu::processMenuShortCut(byte flags, uint16 ascii) { ascii = tolower(ascii); if (flags & (Common::KBD_CTRL | Common::KBD_META)) { for (uint i = 0; i < _items.size(); i++) for (uint j = 0; j < _items[i]->subitems.size(); j++) if (_items[i]->subitems[j]->enabled && tolower(_items[i]->subitems[j]->shortcut) == ascii) { - executeCommand(_items[i]->subitems[j]); - break; + (*_ccallback)(_items[i]->subitems[j]->action, _items[i]->subitems[j]->text, _cdata); + return true; } } + + return false; } void Menu::enableCommand(int menunum, int action, bool state) { for (uint i = 0; i < _items[menunum]->subitems.size(); i++) if (_items[menunum]->subitems[i]->action == action) _items[menunum]->subitems[i]->enabled = state; + + _contentIsDirty = true; } void Menu::disableAllMenus() { for (uint i = 1; i < _items.size(); i++) // Leave About menu on for (uint j = 0; j < _items[i]->subitems.size(); j++) _items[i]->subitems[j]->enabled = false; + + _contentIsDirty = true; } } // End of namespace Wage diff --git a/engines/wage/menu.h b/engines/wage/macmenu.h index 3550356bc6..e73e4c48a9 100644 --- a/engines/wage/menu.h +++ b/engines/wage/macmenu.h @@ -45,8 +45,8 @@ * */ -#ifndef WAGE_MENU_H -#define WAGE_MENU_H +#ifndef WAGE_MACMENU_H +#define WAGE_MACMENU_H namespace Wage { @@ -64,6 +64,7 @@ enum { }; enum { + kMenuHighLevel = -1, kMenuAbout = 0, kMenuFile = 1, kMenuEdit = 2, @@ -90,29 +91,43 @@ enum { kMenuActionCommand }; -class Menu { +struct MenuData { + int menunum; + const char *title; + int action; + byte shortcut; + bool enabled; +}; + +class Menu : public BaseMacWindow { public: - Menu(Gui *gui); + Menu(int id, const Common::Rect &bounds, MacWindowManager *wm); ~Menu(); - void render(); - bool mouseClick(int x, int y); - bool mouseRelease(int x, int y); - bool mouseMove(int x, int y); + void setCommandsCallback(void (*callback)(int, Common::String &, void *), void *data) { _ccallback = callback; _cdata = data; } + + void addStaticMenus(const MenuData *data); + void calcDimensions(); + + int addMenuItem(const char *name); + void addMenuSubItem(int id, const char *text, int action, int style = 0, char shortcut = 0, bool enabled = true); + void createSubMenuFromString(int id, const char *string); + void clearSubMenu(int id); + + bool draw(Graphics::ManagedSurface *g, bool forceRedraw = false); + bool processEvent(Common::Event &event); - void regenCommandsMenu(); - void regenWeaponsMenu(); - void processMenuShortCut(byte flags, uint16 ascii); void enableCommand(int menunum, int action, bool state); void disableAllMenus(); - bool _menuActivated; + void setActive(bool active) { _menuActivated = active; } + bool hasAllFocus() { return _menuActivated; } + Common::Rect _bbox; private: - Gui *_gui; - Graphics::Surface _screenCopy; - Graphics::Surface _tempSurface; + Graphics::ManagedSurface _screen; + Graphics::ManagedSurface _tempSurface; private: const Graphics::Font *getMenuFont(); @@ -120,18 +135,25 @@ private: int calculateMenuWidth(MenuItem *menu); void calcMenuBounds(MenuItem *menu); void renderSubmenu(MenuItem *menu); - void createCommandsMenu(MenuItem *menu); - void createWeaponsMenu(MenuItem *menu); - void executeCommand(MenuSubItem *subitem); + + bool keyEvent(Common::Event &event); + bool mouseClick(int x, int y); + bool mouseRelease(int x, int y); + bool mouseMove(int x, int y); + + bool processMenuShortCut(byte flags, uint16 ascii); Common::Array<MenuItem *> _items; - MenuItem *_weapons; - MenuItem *_commands; const Graphics::Font *_font; + bool _menuActivated; + int _activeItem; int _activeSubItem; + + void (*_ccallback)(int action, Common::String &text, void *data); + void *_cdata; }; } // End of namespace Wage diff --git a/engines/wage/macwindow.cpp b/engines/wage/macwindow.cpp index 3abfdf6017..8903936061 100644 --- a/engines/wage/macwindow.cpp +++ b/engines/wage/macwindow.cpp @@ -45,26 +45,49 @@ * */ +#include "graphics/font.h" #include "graphics/primitives.h" +#include "common/events.h" -#include "wage/wage.h" -#include "wage/gui.h" #include "wage/macwindow.h" +#include "wage/macwindowmanager.h" namespace Wage { -MacWindow::MacWindow(bool scrollable) : _scrollable(scrollable) { +BaseMacWindow::BaseMacWindow(int id, bool editable, MacWindowManager *wm) : + _id(id), _editable(editable), _wm(wm) { + _callback = 0; + _dataPtr = 0; + + _contentIsDirty = true; + + _type = kWindowUnknown; +} + +MacWindow::MacWindow(int id, bool scrollable, bool resizable, bool editable, MacWindowManager *wm) : + BaseMacWindow(id, editable, wm), _scrollable(scrollable), _resizable(resizable) { _active = false; _borderIsDirty = true; _highlightedPart = kBorderNone; _scrollPos = _scrollSize = 0.0; + + _beingDragged = false; + _beingResized = false; + + _draggedX = _draggedY = 0; + + _type = kWindowWindow; } MacWindow::~MacWindow() { } +const Graphics::Font *MacWindow::getTitleFont() { + return _wm->getFont("Chicago-12", Graphics::FontManager::kBigGUIFont); +} + void MacWindow::setActive(bool active) { if (active == _active) return; @@ -80,37 +103,53 @@ void MacWindow::resize(int w, int h) { _surface.free(); _surface.create(w, h, Graphics::PixelFormat::createFormatCLUT8()); _borderSurface.free(); - _borderSurface.create(w + 2 * kBorderWidth, h + 2 * kBorderWidth, Graphics::PixelFormat::createFormatCLUT8()); + _borderSurface.create(w, h, Graphics::PixelFormat::createFormatCLUT8()); + _composeSurface.free(); + _composeSurface.create(w, h, Graphics::PixelFormat::createFormatCLUT8()); _dims.setWidth(w); _dims.setHeight(h); - _borderDims.setWidth(w + 2 * kBorderWidth); - _borderDims.setHeight(h + 2 * kBorderWidth); - move(_dims.left, _dims.top); // Update _borderDims position + updateInnerDims(); + + _contentIsDirty = true; + _borderIsDirty = true; } void MacWindow::move(int x, int y) { + if (_dims.left == x && _dims.top == y) + return; + _dims.moveTo(x, y); - _borderDims.moveTo(x - kBorderWidth, y - kBorderWidth); + updateInnerDims(); + + _contentIsDirty = true; } void MacWindow::setDimensions(const Common::Rect &r) { resize(r.width(), r.height()); - move(r.left, r.top); + _dims.moveTo(r.left, r.top); + updateInnerDims(); + + _contentIsDirty = true; } -void MacWindow::draw(Graphics::Surface *g, bool forceRedraw) { +bool MacWindow::draw(Graphics::ManagedSurface *g, bool forceRedraw) { + if (!_borderIsDirty && !_contentIsDirty && !forceRedraw) + return false; + if (_borderIsDirty || forceRedraw) drawBorder(); -} -const Graphics::Font *MacWindow::getTitleFont() { - return ((WageEngine *)g_engine)->_gui->getFont("Chicago-12", Graphics::FontManager::kBigGUIFont); -} + _contentIsDirty = false; -bool MacWindow::builtInFonts() { - return ((WageEngine *)g_engine)->_gui->builtInFonts(); + // Compose + _composeSurface.blitFrom(_surface, Common::Rect(0, 0, _surface.w - 2, _surface.h - 2), Common::Point(2, 2)); + _composeSurface.transBlitFrom(_borderSurface, kColorGreen); + + g->transBlitFrom(_composeSurface, _composeSurface.getBounds(), Common::Point(_dims.left - 2, _dims.top - 2), kColorGreen2); + + return true; } #define ARROW_W 12 @@ -124,7 +163,7 @@ const int arrowPixels[ARROW_H][ARROW_W] = { {1,1,1,1,1,1,1,1,1,1,1,1}}; static void drawPixelInverted(int x, int y, int color, void *data) { - Graphics::Surface *surface = (Graphics::Surface *)data; + Graphics::ManagedSurface *surface = (Graphics::ManagedSurface *)data; if (x >= 0 && x < surface->w && y >= 0 && y < surface->h) { byte *p = (byte *)surface->getBasePtr(x, y); @@ -133,10 +172,15 @@ static void drawPixelInverted(int x, int y, int color, void *data) { } } +void MacWindow::updateInnerDims() { + _innerDims = _dims; + _innerDims.grow(-kBorderWidth); +} + void MacWindow::drawBorder() { _borderIsDirty = false; - bool active = _active, scrollable = _scrollable, closeable = _active, drawTitle = _title.empty(); + bool active = _active, scrollable = _scrollable, closeable = _active, drawTitle = !_title.empty(); const int size = kBorderWidth; int x = 0; int y = 0; @@ -144,7 +188,11 @@ void MacWindow::drawBorder() { int height = _borderSurface.h; Graphics::ManagedSurface *g = &_borderSurface; - g->fillRect(_borderDims, kColorGreen2); + // We draw rect with outer kColorGreen2 and inner kColorGreen, so on 2 passes we cut out + // scene by external shape of the border + int sz = kBorderWidth / 2; + g->clear(kColorGreen2); + g->fillRect(Common::Rect(sz, sz, width - sz, height - sz), kColorGreen); drawBox(g, x, y, size, size); drawBox(g, x + width - size - 1, y, size, size); @@ -156,11 +204,11 @@ void MacWindow::drawBorder() { drawBox(g, x + width - size + 1, y + size, size - 4, height - 2 * size - 1); if (active) { - fillRect(g, x + size, y + 5, width - 2 * size - 1, 8); - fillRect(g, x + size, y + height - 13, width - 2 * size - 1, 8); - fillRect(g, x + 5, y + size, 8, height - 2 * size - 1); + fillRect(g, x + size, y + 5, width - 2 * size - 1, 8, kColorBlack); + fillRect(g, x + size, y + height - 13, width - 2 * size - 1, 8, kColorBlack); + fillRect(g, x + 5, y + size, 8, height - 2 * size - 1, kColorBlack); if (!scrollable) { - fillRect(g, x + width - 13, y + size, 8, height - 2 * size - 1); + fillRect(g, x + width - 13, y + size, 8, height - 2 * size - 1, kColorBlack); } else { int x1 = x + width - 15; int y1 = y + size + 1; @@ -170,7 +218,7 @@ void MacWindow::drawBorder() { g->hLine(x1 + xx, y1 + yy, x1 + xx, (arrowPixels[yy][xx] != 0 ? kColorBlack : kColorWhite)); } - fillRect(g, x + width - 13, y + size + ARROW_H, 8, height - 2 * size - 1 - ARROW_H * 2); + fillRect(g, x + width - 13, y + size + ARROW_H, 8, height - 2 * size - 1 - ARROW_H * 2, kColorBlack); y1 += height - 2 * size - ARROW_H - 2; for (int yy = 0; yy < ARROW_H; yy++) { @@ -190,7 +238,7 @@ void MacWindow::drawBorder() { } if (closeable) { if (_highlightedPart == kBorderCloseButton) { - fillRect(g, x + 6, y + 6, 6, 6); + fillRect(g, x + 6, y + 6, 6, 6, kColorBlack); } else { drawBox(g, x + 5, y + 5, 7, 7); } @@ -199,7 +247,7 @@ void MacWindow::drawBorder() { if (drawTitle) { const Graphics::Font *font = getTitleFont(); - int yOff = builtInFonts() ? 3 : 1; + int yOff = _wm->hasBuiltInFonts() ? 3 : 1; int w = font->getStringWidth(_title) + 10; int maxWidth = width - size * 2 - 7; @@ -210,6 +258,24 @@ void MacWindow::drawBorder() { } } +void MacWindow::setHighlight(WindowClick highlightedPart) { + if (_highlightedPart == highlightedPart) + return; + + _highlightedPart = highlightedPart; + _borderIsDirty = true; + } + + void MacWindow::setScroll(float scrollPos, float scrollSize) { + if (_scrollPos == scrollPos && _scrollSize == scrollSize) + return; + + _scrollPos = scrollPos; + _scrollSize = scrollSize; + _borderIsDirty = true; + } + + void MacWindow::drawBox(Graphics::ManagedSurface *g, int x, int y, int w, int h) { Common::Rect r(x, y, x + w + 1, y + h + 1); @@ -223,4 +289,88 @@ void MacWindow::fillRect(Graphics::ManagedSurface *g, int x, int y, int w, int h g->fillRect(r, color); } +WindowClick MacWindow::isInBorder(int x, int y) { + if (_innerDims.contains(x, y)) + return kBorderInner; + + if (x >= _innerDims.left - kBorderWidth && x < _innerDims.left && y >= _innerDims.top - kBorderWidth && y < _innerDims.top) + return kBorderCloseButton; + + if (_resizable) + if (x >= _innerDims.right && x < _innerDims.right + kBorderWidth && y >= _innerDims.bottom && y < _innerDims.bottom + kBorderWidth) + return kBorderResizeButton; + + if (_scrollable && x >= _innerDims.right && x < _innerDims.right + kBorderWidth) { + if (y < _innerDims.top - kBorderWidth) + return kBorderBorder; + + if (y >= _innerDims.bottom + kBorderWidth) + return kBorderBorder; + + if (y >= _innerDims.top + _innerDims.height() / 2) + return kBorderScrollDown; + + return kBorderScrollUp; + } + + return kBorderBorder; +} + +bool MacWindow::processEvent(Common::Event &event) { + WindowClick click = isInBorder(event.mouse.x, event.mouse.y); + + switch (event.type) { + case Common::EVENT_MOUSEMOVE: + if (_beingDragged) { + _dims.translate(event.mouse.x - _draggedX, event.mouse.y - _draggedY); + updateInnerDims(); + + _draggedX = event.mouse.x; + _draggedY = event.mouse.y; + + _wm->setFullRefresh(true); + } + + if (_beingResized) { + resize(MAX(kBorderWidth * 4, _dims.width() + event.mouse.x - _draggedX), + MAX(kBorderWidth * 4, _dims.height() + event.mouse.y - _draggedY)); + + _draggedX = event.mouse.x; + _draggedY = event.mouse.y; + + _wm->setFullRefresh(true); + (*_callback)(click, event, _dataPtr); + } + break; + case Common::EVENT_LBUTTONDOWN: + setHighlight(click); + + if (click == kBorderBorder) { + _beingDragged = true; + + _draggedX = event.mouse.x; + _draggedY = event.mouse.y; + } + + if (click == kBorderResizeButton) { + _beingResized = true; + + _draggedX = event.mouse.x; + _draggedY = event.mouse.y; + } + + break; + case Common::EVENT_LBUTTONUP: + _beingDragged = false; + _beingResized = false; + + setHighlight(kBorderNone); + break; + default: + return false; + } + + return (*_callback)(click, event, _dataPtr); +} + } // End of namespace Wage diff --git a/engines/wage/macwindow.h b/engines/wage/macwindow.h index 0d7d49f06d..4c6e9efeff 100644 --- a/engines/wage/macwindow.h +++ b/engines/wage/macwindow.h @@ -52,55 +52,106 @@ namespace Wage { +class MacWindowManager; + enum WindowType { - kWindowScene, - kWindowConsole + kWindowUnknown, + kWindowWindow, + kWindowMenu }; enum { kBorderWidth = 17 }; -enum BorderHighlight { +enum WindowClick { kBorderNone = 0, kBorderScrollUp, kBorderScrollDown, - kBorderCloseButton + kBorderCloseButton, + kBorderInner, + kBorderBorder, + kBorderResizeButton }; -class MacWindow { +class BaseMacWindow { public: - MacWindow(bool scrollable); - ~MacWindow(); + BaseMacWindow(int id, bool editable, MacWindowManager *wm); + virtual ~BaseMacWindow() {} + + const Common::Rect &getDimensions() { return _dims; } + int getId() { return _id; } + WindowType getType() { return _type; } + bool isEditable() { return _editable; } + Graphics::ManagedSurface *getSurface() { return &_surface; } + virtual void setActive(bool active) = 0; + void setDirty(bool dirty) { _contentIsDirty = dirty; } + + virtual bool draw(Graphics::ManagedSurface *g, bool forceRedraw = false) = 0; + virtual bool processEvent(Common::Event &event) = 0; + + virtual bool hasAllFocus() = 0; + + void setCallback(bool (*callback)(WindowClick, Common::Event &, void *), void *data) { _callback = callback; _dataPtr = data; } + +protected: + int _id; + WindowType _type; + + bool _editable; + + Graphics::ManagedSurface _surface; + bool _contentIsDirty; + + Common::Rect _dims; + + bool (*_callback)(WindowClick, Common::Event &, void *); + void *_dataPtr; + + MacWindowManager *_wm; +}; + +class MacWindow : public BaseMacWindow { +public: + MacWindow(int id, bool scrollable, bool resizable, bool editable, MacWindowManager *wm); + virtual ~MacWindow(); void move(int x, int y); void resize(int w, int h); void setDimensions(const Common::Rect &r); - void draw(Graphics::Surface *g, bool forceRedraw = false); + const Common::Rect &getInnerDimensions() { return _innerDims; } + + bool draw(Graphics::ManagedSurface *g, bool forceRedraw = false); + void setActive(bool active); - Graphics::ManagedSurface *getSurface() { return &_surface; } void setTitle(Common::String &title) { _title = title; } - void setHighlight(BorderHighlight highlightedPart) { _highlightedPart = highlightedPart; } - void setScroll(float scrollPos, float scrollSize) { _scrollPos = scrollPos; _scrollSize = scrollSize; } + void setHighlight(WindowClick highlightedPart); + void setScroll(float scrollPos, float scrollSize); + bool processEvent(Common::Event &event); + bool hasAllFocus() { return _beingDragged || _beingResized; } private: void drawBorder(); void drawBox(Graphics::ManagedSurface *g, int x, int y, int w, int h); - void fillRect(Graphics::ManagedSurface *g, int x, int y, int w, int h, int color = kColorBlack); + void fillRect(Graphics::ManagedSurface *g, int x, int y, int w, int h, int color); const Graphics::Font *getTitleFont(); - bool builtInFonts(); + void updateInnerDims(); + WindowClick isInBorder(int x, int y); private: - Graphics::ManagedSurface _surface; Graphics::ManagedSurface _borderSurface; + Graphics::ManagedSurface _composeSurface; bool _scrollable; + bool _resizable; bool _active; bool _borderIsDirty; - BorderHighlight _highlightedPart; + bool _beingDragged, _beingResized; + int _draggedX, _draggedY; + + WindowClick _highlightedPart; float _scrollPos, _scrollSize; - Common::Rect _dims; - Common::Rect _borderDims; + Common::Rect _innerDims; Common::String _title; }; diff --git a/engines/wage/macwindowmanager.cpp b/engines/wage/macwindowmanager.cpp index 14c3f236e8..5cc54d648a 100644 --- a/engines/wage/macwindowmanager.cpp +++ b/engines/wage/macwindowmanager.cpp @@ -45,38 +45,127 @@ * */ -#include "common/list.h" #include "common/array.h" +#include "common/events.h" +#include "common/list.h" +#include "common/unzip.h" +#include "common/system.h" +#include "common/stream.h" -#include "graphics/surface.h" +#include "graphics/cursorman.h" +#include "graphics/fonts/bdf.h" +#include "graphics/managed_surface.h" +#include "graphics/palette.h" +#include "graphics/primitives.h" -#include "wage/wage.h" -#include "wage/macwindow.h" #include "wage/macwindowmanager.h" +#include "wage/macwindow.h" +#include "wage/macmenu.h" namespace Wage { +static const byte palette[] = { + 0, 0, 0, // Black + 0x80, 0x80, 0x80, // Gray + 0xff, 0xff, 0xff, // White + 0x00, 0xff, 0x00, // Green + 0x00, 0xcf, 0x00 // Green2 +}; + +static byte fillPatterns[][8] = { { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, // kPatternSolid + { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 }, // kPatternStripes + { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 }, // kPatternCheckers + { 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa } // kPatternCheckers2 +}; + +static const byte macCursorArrow[] = { + 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 0, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 0, 0, 2, 3, 3, 3, 3, 3, 3, 3, + 2, 0, 0, 0, 2, 3, 3, 3, 3, 3, 3, + 2, 0, 0, 0, 0, 2, 3, 3, 3, 3, 3, + 2, 0, 0, 0, 0, 0, 2, 3, 3, 3, 3, + 2, 0, 0, 0, 0, 0, 0, 2, 3, 3, 3, + 2, 0, 0, 0, 0, 0, 0, 0, 2, 3, 3, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, + 2, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, + 2, 0, 0, 2, 0, 0, 2, 3, 3, 3, 3, + 2, 0, 2, 3, 2, 0, 0, 2, 3, 3, 3, + 2, 2, 3, 3, 2, 0, 0, 2, 3, 3, 3, + 2, 3, 3, 3, 3, 2, 0, 0, 2, 3, 3, + 3, 3, 3, 3, 3, 2, 0, 0, 2, 3, 3, + 3, 3, 3, 3, 3, 3, 2, 2, 2, 3, 3 +}; + +static const byte macCursorBeam[] = { + 0, 0, 3, 3, 3, 0, 0, 3, 3, 3, 3, + 3, 3, 0, 3, 0, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 0, 3, 0, 3, 3, 3, 3, 3, 3, + 0, 0, 3, 3, 3, 0, 0, 3, 3, 3, 3, +}; + MacWindowManager::MacWindowManager() { + _screen = 0; _lastId = 0; _activeWindow = -1; + + _menu = 0; + + _fullRefresh = true; + + _builtInFonts = true; + + for (int i = 0; i < ARRAYSIZE(fillPatterns); i++) + _patterns.push_back(fillPatterns[i]); + + loadFonts(); + + g_system->getPaletteManager()->setPalette(palette, 0, ARRAYSIZE(palette) / 3); + + CursorMan.replaceCursorPalette(palette, 0, ARRAYSIZE(palette) / 3); + CursorMan.replaceCursor(macCursorArrow, 11, 16, 1, 1, 3); + _cursorIsArrow = true; + CursorMan.showMouse(true); } MacWindowManager::~MacWindowManager() { - for (uint i = 0; i < _lastId; i++) + for (int i = 0; i < _lastId; i++) delete _windows[i]; } -int MacWindowManager::add(bool scrollable) { - MacWindow *w = new MacWindow(scrollable); +MacWindow *MacWindowManager::addWindow(bool scrollable, bool resizable, bool editable) { + MacWindow *w = new MacWindow(_lastId, scrollable, resizable, editable, this); _windows.push_back(w); _windowStack.push_back(w); - _activeWindow = _lastId; + setActive(_lastId); _lastId++; - return _activeWindow; + return w; +} + +Menu *MacWindowManager::addMenu() { + _menu = new Menu(_lastId, _screen->getBounds(), this); + + _windows.push_back(_menu); + + _lastId++; + + return _menu; } void MacWindowManager::setActive(int id) { @@ -86,6 +175,8 @@ void MacWindowManager::setActive(int id) { if (_activeWindow != -1) _windows[_activeWindow]->setActive(false); + _activeWindow = id; + _windows[id]->setActive(true); _windowStack.remove(_windows[id]); @@ -94,11 +185,196 @@ void MacWindowManager::setActive(int id) { _fullRefresh = true; } -void MacWindowManager::draw(Graphics::Surface *g) { - for (Common::List<MacWindow *>::const_iterator it = _windowStack.begin(); it != _windowStack.end(); it++) - (*it)->draw(g, _fullRefresh); +struct PlotData { + Graphics::ManagedSurface *surface; + Patterns *patterns; + uint fillType; + int thickness; + + PlotData(Graphics::ManagedSurface *s, Patterns *p, int f, int t) : + surface(s), patterns(p), fillType(f), thickness(t) {} +}; + +static void drawPixel(int x, int y, int color, void *data) { + PlotData *p = (PlotData *)data; + + if (p->fillType > p->patterns->size()) + return; + + byte *pat = p->patterns->operator[](p->fillType - 1); + + if (p->thickness == 1) { + if (x >= 0 && x < p->surface->w && y >= 0 && y < p->surface->h) { + uint xu = (uint)x; // for letting compiler optimize it + uint yu = (uint)y; + + *((byte *)p->surface->getBasePtr(xu, yu)) = + (pat[yu % 8] & (1 << (7 - xu % 8))) ? + color : kColorWhite; + } + } else { + int x1 = x; + int x2 = x1 + p->thickness; + int y1 = y; + int y2 = y1 + p->thickness; + + for (y = y1; y < y2; y++) + for (x = x1; x < x2; x++) + if (x >= 0 && x < p->surface->w && y >= 0 && y < p->surface->h) { + uint xu = (uint)x; // for letting compiler optimize it + uint yu = (uint)y; + *((byte *)p->surface->getBasePtr(xu, yu)) = + (pat[yu % 8] & (1 << (7 - xu % 8))) ? + color : kColorWhite; + } + } +} + +void MacWindowManager::drawDesktop() { + Common::Rect r(_screen->getBounds()); + + PlotData pd(_screen, &_patterns, kPatternCheckers, 1); + + Graphics::drawRoundRect(r, kDesktopArc, kColorBlack, true, drawPixel, &pd); + + g_system->copyRectToScreen(_screen->getPixels(), _screen->pitch, 0, 0, _screen->w, _screen->h); +} + +void MacWindowManager::draw() { + assert(_screen); + + if (_fullRefresh) + drawDesktop(); + + for (Common::List<BaseMacWindow *>::const_iterator it = _windowStack.begin(); it != _windowStack.end(); it++) { + BaseMacWindow *w = *it; + if (w->draw(_screen, _fullRefresh)) { + w->setDirty(false); + + Common::Rect clip(w->getDimensions().left - 2, w->getDimensions().top - 2, w->getDimensions().right - 2, w->getDimensions().bottom - 2); + clip.clip(_screen->getBounds()); + + g_system->copyRectToScreen(_screen->getBasePtr(clip.left, clip.top), _screen->pitch, clip.left, clip.top, clip.width(), clip.height()); + } + } + + // Menu is drawn on top of everything and always + if (_menu) + _menu->draw(_screen, _fullRefresh); _fullRefresh = false; } +bool MacWindowManager::processEvent(Common::Event &event) { + // Menu gets events first fir shortcuts and menu bar + if (_menu && _menu->processEvent(event)) + return true; + + if (event.type != Common::EVENT_MOUSEMOVE && event.type != Common::EVENT_LBUTTONDOWN && + event.type != Common::EVENT_LBUTTONUP) + return false; + + if (_windows[_activeWindow]->isEditable() && _windows[_activeWindow]->getType() == kWindowWindow && + ((MacWindow *)_windows[_activeWindow])->getInnerDimensions().contains(event.mouse.x, event.mouse.y)) { + if (_cursorIsArrow) { + CursorMan.replaceCursor(macCursorBeam, 11, 16, 3, 8, 3); + _cursorIsArrow = false; + } + } else { + if (_cursorIsArrow == false) { + CursorMan.replaceCursor(macCursorArrow, 11, 16, 1, 1, 3); + _cursorIsArrow = true; + } + } + + for (Common::List<BaseMacWindow *>::const_iterator it = _windowStack.end(); it != _windowStack.begin();) { + it--; + BaseMacWindow *w = *it; + + if (w->hasAllFocus() || w->getDimensions().contains(event.mouse.x, event.mouse.y)) { + if (event.type == Common::EVENT_LBUTTONDOWN || event.type == Common::EVENT_LBUTTONUP) + setActive(w->getId()); + + return w->processEvent(event); + } + } + + return false; +} + +////////////////////// +// Font stuff +////////////////////// +void MacWindowManager::loadFonts() { + Common::Archive *dat; + + dat = Common::makeZipArchive("classicmacfonts.dat"); + + if (!dat) { + warning("Could not find classicmacfonts.dat. Falling back to built-in fonts"); + _builtInFonts = true; + + return; + } + + Common::ArchiveMemberList list; + dat->listMembers(list); + + for (Common::ArchiveMemberList::iterator it = list.begin(); it != list.end(); ++it) { + Common::SeekableReadStream *stream = dat->createReadStreamForMember((*it)->getName()); + + Graphics::BdfFont *font = Graphics::BdfFont::loadFont(*stream); + + delete stream; + + Common::String fontName = (*it)->getName(); + + // Trim the .bdf extension + for (int i = fontName.size() - 1; i >= 0; --i) { + if (fontName[i] == '.') { + while ((uint)i < fontName.size()) { + fontName.deleteLastChar(); + } + break; + } + } + + FontMan.assignFontToName(fontName, font); + + debug(2, " %s", fontName.c_str()); + } + + _builtInFonts = false; + + delete dat; +} + +const Graphics::Font *MacWindowManager::getFont(const char *name, Graphics::FontManager::FontUsage fallback) { + const Graphics::Font *font = 0; + + if (!_builtInFonts) { + font = FontMan.getFontByName(name); + + if (!font) + warning("Cannot load font %s", name); + } + + if (_builtInFonts || !font) + font = FontMan.getFontByUsage(fallback); + + return font; +} + +///////////////// +// Cursor stuff +///////////////// +void MacWindowManager::pushArrowCursor() { + CursorMan.pushCursor(macCursorArrow, 11, 16, 1, 1, 3); +} + +void MacWindowManager::popCursor() { + CursorMan.popCursor(); +} + + } // End of namespace Wage diff --git a/engines/wage/macwindowmanager.h b/engines/wage/macwindowmanager.h index b15c7737c6..13f85cddd4 100644 --- a/engines/wage/macwindowmanager.h +++ b/engines/wage/macwindowmanager.h @@ -48,30 +48,92 @@ #ifndef WAGE_MACWINDOWMANAGER_H #define WAGE_MACWINDOWMANAGER_H +#include "common/array.h" +#include "common/list.h" +#include "common/events.h" +#include "common/archive.h" + +#include "graphics/fontman.h" + +namespace Graphics { +class ManagedSurface; +} + namespace Wage { +enum { + kDesktopArc = 7 +}; + +enum { + kColorBlack = 0, + kColorGray = 1, + kColorWhite = 2, + kColorGreen = 3, + kColorGreen2 = 4 +}; + +enum { + kPatternSolid = 1, + kPatternStripes = 2, + kPatternCheckers = 3, + kPatternCheckers2 = 4 +}; + +class BaseMacWindow; class MacWindow; +class Menu; + +typedef Common::Array<byte *> Patterns; class MacWindowManager { public: MacWindowManager(); ~MacWindowManager(); - int add(bool scrollable); + void setScreen(Graphics::ManagedSurface *screen) { _screen = screen; } + bool hasBuiltInFonts() { return _builtInFonts; } + const Graphics::Font *getFont(const char *name, Graphics::FontManager::FontUsage fallback); + + MacWindow *addWindow(bool scrollable, bool resizable, bool editable); + Menu *addMenu(); void setActive(int id); - void draw(Graphics::Surface *g); + void setFullRefresh(bool redraw) { _fullRefresh = true; } - MacWindow *getWindow(int id) { return _windows[id]; } + void draw(); + + bool processEvent(Common::Event &event); + + BaseMacWindow *getWindow(int id) { return _windows[id]; } + + Patterns &getPatterns() { return _patterns; } + void drawFilledRoundRect(Graphics::ManagedSurface *surface, Common::Rect &rect, int arc, int color); + + void pushArrowCursor(); + void popCursor(); + +private: + void drawDesktop(); + void loadFonts(); private: - Common::List<MacWindow *> _windowStack; - Common::Array<MacWindow *> _windows; + Graphics::ManagedSurface *_screen; + + Common::List<BaseMacWindow *> _windowStack; + Common::Array<BaseMacWindow *> _windows; int _lastId; int _activeWindow; bool _fullRefresh; + + Patterns _patterns; + + Menu *_menu; + + bool _builtInFonts; + bool _cursorIsArrow; }; } // End of namespace Wage diff --git a/engines/wage/module.mk b/engines/wage/module.mk index d65934fc2b..e150d5f27e 100644 --- a/engines/wage/module.mk +++ b/engines/wage/module.mk @@ -9,9 +9,9 @@ MODULE_OBJS := \ entities.o \ gui.o \ gui-console.o \ + macmenu.o \ macwindow.o \ macwindowmanager.o \ - menu.o \ randomhat.o \ script.o \ sound.o \ diff --git a/engines/wage/wage.cpp b/engines/wage/wage.cpp index 3a52aed1c4..567e2768d8 100644 --- a/engines/wage/wage.cpp +++ b/engines/wage/wage.cpp @@ -86,6 +86,7 @@ WageEngine::WageEngine(OSystem *syst, const ADGameDescription *desc) : Engine(sy _offer = NULL; _resManager = NULL; + _debugger = NULL; debug("WageEngine::WageEngine()"); } @@ -148,24 +149,14 @@ void WageEngine::processEvents() { Common::Event event; while (_eventMan->pollEvent(event)) { + if (_gui->processEvent(event)) + continue; + switch (event.type) { case Common::EVENT_QUIT: if (saveDialog()) _shouldQuit = true; break; - case Common::EVENT_MOUSEMOVE: - _gui->mouseMove(event.mouse.x, event.mouse.y); - break; - case Common::EVENT_LBUTTONDOWN: - _gui->mouseDown(event.mouse.x, event.mouse.y); - break; - case Common::EVENT_LBUTTONUP: - { - Designed *obj = _gui->mouseUp(event.mouse.x, event.mouse.y); - if (obj != NULL) - processTurn(NULL, obj); - } - break; case Common::EVENT_KEYDOWN: switch (event.kbd.keycode) { case Common::KEYCODE_BACKSPACE: @@ -189,13 +180,6 @@ void WageEngine::processEvents() { break; } - if (event.kbd.flags & (Common::KBD_ALT | Common::KBD_CTRL | Common::KBD_META)) { - if (event.kbd.ascii >= 0x20 && event.kbd.ascii <= 0x7f) { - _gui->processMenuShortCut(event.kbd.flags, event.kbd.ascii); - } - break; - } - if (event.kbd.ascii >= 0x20 && event.kbd.ascii <= 0x7f) { _inputText += (char)event.kbd.ascii; _gui->drawInput(); @@ -236,7 +220,6 @@ void WageEngine::gameOver() { _gui->disableAllMenus(); _gui->enableNewGameMenus(); - _gui->_menuDirty = true; } bool WageEngine::saveDialog() { diff --git a/engines/wage/wage.h b/engines/wage/wage.h index 87009c2350..eb50a2e3dd 100644 --- a/engines/wage/wage.h +++ b/engines/wage/wage.h @@ -103,14 +103,6 @@ enum { // the current limitation is 32 debug levels (1 << 31 is the last one) }; -enum { - kColorBlack = 0, - kColorGray = 1, - kColorWhite = 2, - kColorGreen = 3, - kColorGreen2 = 4 -}; - Common::String readPascalString(Common::SeekableReadStream *in); Common::Rect *readRect(Common::SeekableReadStream *in); const char *getIndefiniteArticle(const Common::String &word); @@ -118,8 +110,6 @@ const char *prependGenderSpecificPronoun(int gender); const char *getGenderSpecificPronoun(int gender, bool capitalize); bool isStorageScene(const Common::String &name); -typedef Common::Array<byte *> Patterns; - class WageEngine : public Engine { friend class Dialog; public: diff --git a/engines/wage/world.cpp b/engines/wage/world.cpp index 954a425b7b..53fc1b4742 100644 --- a/engines/wage/world.cpp +++ b/engines/wage/world.cpp @@ -73,6 +73,8 @@ World::World(WageEngine *engine) { _weaponMenuDisabled = true; _engine = engine; + + _patterns = new Patterns; } World::~World() { @@ -88,8 +90,10 @@ World::~World() { for (uint i = 0; i < _orderedScenes.size(); i++) delete _orderedScenes[i]; - for (uint i = 0; i < _patterns.size(); i++) - free(_patterns[i]); + for (uint i = 0; i < _patterns->size(); i++) + free(_patterns->operator[](i)); + + delete _patterns; delete _globalScript; @@ -261,7 +265,7 @@ bool World::loadWorld(Common::MacResManager *resMan) { byte *pattern = (byte *)malloc(8); res->read(pattern, 8); - _patterns.push_back(pattern); + _patterns->push_back(pattern); } delete res; @@ -274,7 +278,7 @@ bool World::loadWorld(Common::MacResManager *resMan) { byte *pattern = (byte *)malloc(8); res->read(pattern, 8); - _patterns.push_back(pattern); + _patterns->push_back(pattern); } } delete res; diff --git a/engines/wage/world.h b/engines/wage/world.h index 355d660c8d..468bedbc59 100644 --- a/engines/wage/world.h +++ b/engines/wage/world.h @@ -48,6 +48,8 @@ #ifndef WAGE_WORLD_H #define WAGE_WORLD_H +#include "wage/macwindowmanager.h" + namespace Wage { class Sound; @@ -85,7 +87,7 @@ public: ObjArray _orderedObjs; ChrArray _orderedChrs; Common::Array<Sound *> _orderedSounds; - Patterns _patterns; + Patterns *_patterns; Scene *_storageScene; Chr *_player; //List<MoveListener> moveListeners; diff --git a/engines/wintermute/base/base_engine.cpp b/engines/wintermute/base/base_engine.cpp index 2166a3e070..4ce334aceb 100644 --- a/engines/wintermute/base/base_engine.cpp +++ b/engines/wintermute/base/base_engine.cpp @@ -84,7 +84,7 @@ void BaseEngine::LOG(bool res, const char *fmt, ...) { va_end(va); if (instance()._gameRef) { - instance()._gameRef->LOG("%s", buff); + instance()._gameRef->LOG(res, "%s", buff); } else { debugCN(kWintermuteDebugLog, "%02d:%02d:%02d: %s\n", hours, mins, secs, buff); } diff --git a/engines/wintermute/base/sound/base_sound_manager.cpp b/engines/wintermute/base/sound/base_sound_manager.cpp index f1e0c3b1f9..74c0086817 100644 --- a/engines/wintermute/base/sound/base_sound_manager.cpp +++ b/engines/wintermute/base/sound/base_sound_manager.cpp @@ -100,15 +100,14 @@ BaseSoundBuffer *BaseSoundMgr::addSound(const Common::String &filename, Audio::M BaseSoundBuffer *sound; Common::String useFilename = filename; + useFilename.toLowercase(); // try to switch WAV to OGG file (if available) - AnsiString ext = PathUtil::getExtension(filename); - if (StringUtil::compareNoCase(ext, "wav")) { - AnsiString path = PathUtil::getDirectoryName(filename); - AnsiString name = PathUtil::getFileNameWithoutExtension(filename); - - AnsiString newFile = PathUtil::combine(path, name + "ogg"); - if (BaseFileManager::getEngineInstance()->hasFile(newFile)) { - useFilename = newFile; + if (useFilename.hasSuffix(".wav")) { + Common::String oggFilename = useFilename; + oggFilename.erase(oggFilename.size() - 4); + oggFilename = oggFilename + ".ogg"; + if (BaseFileManager::getEngineInstance()->hasFile(oggFilename)) { + useFilename = oggFilename; } } 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/engines/wintermute/ui/ui_window.cpp b/engines/wintermute/ui/ui_window.cpp index 9f3cdeaaa3..34341d1db2 100644 --- a/engines/wintermute/ui/ui_window.cpp +++ b/engines/wintermute/ui/ui_window.cpp @@ -139,13 +139,12 @@ bool UIWindow::display(int offsetX, int offsetY) { _shieldButton->setListener(this, _shieldButton, 0); _shieldButton->_parent = this; } - if (_shieldButton) { - _shieldButton->_posX = _shieldButton->_posY = 0; - _shieldButton->setWidth(_gameRef->_renderer->getWidth()); - _shieldButton->setHeight(_gameRef->_renderer->getHeight()); - _shieldButton->display(); - } + _shieldButton->_posX = _shieldButton->_posY = 0; + _shieldButton->setWidth(_gameRef->_renderer->getWidth()); + _shieldButton->setHeight(_gameRef->_renderer->getHeight()); + + _shieldButton->display(); } if (!_visible) { diff --git a/engines/wintermute/wintermute.h b/engines/wintermute/wintermute.h index f8f5fc7deb..f61a809b22 100644 --- a/engines/wintermute/wintermute.h +++ b/engines/wintermute/wintermute.h @@ -20,8 +20,8 @@ * */ -#ifndef WINTERMUTE_H -#define WINTERMUTE_H +#ifndef WINTERMUTE_WINTERMUTE_H +#define WINTERMUTE_WINTERMUTE_H #include "engines/engine.h" #include "engines/advancedDetector.h" |