diff options
author | Marisa-Chan | 2014-06-13 21:43:04 +0700 |
---|---|---|
committer | Marisa-Chan | 2014-06-13 21:43:04 +0700 |
commit | 45589950c0fb1a449351e6a00ef10d42290d8bae (patch) | |
tree | 44e4eedcb7e69d5fc386155b000ed038af07251d /engines/avalanche | |
parent | 48360645dcd5f8fddb135b6e31ae5cae4be8d77f (diff) | |
parent | 5c005ad3a3f1df0bc968c85c1cf0fc48e36ab0b2 (diff) | |
download | scummvm-rg350-45589950c0fb1a449351e6a00ef10d42290d8bae.tar.gz scummvm-rg350-45589950c0fb1a449351e6a00ef10d42290d8bae.tar.bz2 scummvm-rg350-45589950c0fb1a449351e6a00ef10d42290d8bae.zip |
Merge remote-tracking branch 'upstream/master' into zvision
Conflicts:
engines/zvision/animation/rlf_animation.cpp
engines/zvision/animation_control.h
engines/zvision/core/console.cpp
engines/zvision/core/events.cpp
engines/zvision/cursors/cursor.cpp
engines/zvision/cursors/cursor_manager.cpp
engines/zvision/cursors/cursor_manager.h
engines/zvision/fonts/truetype_font.cpp
engines/zvision/graphics/render_manager.cpp
engines/zvision/graphics/render_manager.h
engines/zvision/inventory/inventory_manager.h
engines/zvision/inventory_manager.h
engines/zvision/meta_animation.h
engines/zvision/module.mk
engines/zvision/scripting/actions.cpp
engines/zvision/scripting/control.h
engines/zvision/scripting/controls/animation_control.cpp
engines/zvision/scripting/controls/animation_control.h
engines/zvision/scripting/controls/input_control.cpp
engines/zvision/scripting/controls/lever_control.cpp
engines/zvision/scripting/controls/timer_node.cpp
engines/zvision/scripting/controls/timer_node.h
engines/zvision/scripting/puzzle.h
engines/zvision/scripting/scr_file_handling.cpp
engines/zvision/scripting/script_manager.cpp
engines/zvision/scripting/script_manager.h
engines/zvision/sidefx.cpp
engines/zvision/sound/zork_raw.cpp
engines/zvision/sound/zork_raw.h
engines/zvision/video/video.cpp
engines/zvision/video/zork_avi_decoder.h
engines/zvision/zvision.cpp
engines/zvision/zvision.h
Diffstat (limited to 'engines/avalanche')
45 files changed, 4341 insertions, 1033 deletions
diff --git a/engines/avalanche/animation.cpp b/engines/avalanche/animation.cpp index ef30faa87c..451b4a1c68 100644 --- a/engines/avalanche/animation.cpp +++ b/engines/avalanche/animation.cpp @@ -8,12 +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. - + * * 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. @@ -48,6 +48,37 @@ const int32 Animation::kCatacombMap[8][8] = { AnimationType::AnimationType(Animation *anim) { _anim = anim; + + _xLength = 0; + _yLength = 0; + for (int i = 0; i < 24; i++) { + _mani[i] = nullptr; + _sil[i] = nullptr; + } + _frameNum = 0; + _seq = 0; + _characterId = 0; + _count = 0; + _facingDir = kDirNone; + _stepNum = 0; + _x = 0; + _y = 0; + _moveX = 0; + _moveY = 0; + _quick = false; + _visible = false; + _homing = false; + _doCheck = false; + _homingX = 0; + _homingY = 0; + _speedX = 0; + _speedY = 0; + _vanishIfStill = false; + _callEachStepFl = false; + _eachStepProc = Animation::kProcNone; + _fgBubbleCol = kColorWhite; + _bgBubbleCol = kColorBlack; + _id = 177; } /** @@ -217,7 +248,7 @@ void AnimationType::walk() { break; case kMagicUnfinished: { bounce(); - Common::String tmpStr = Common::String::format("%c%cSorry.%cThis place is not available yet!", + Common::String tmpStr = Common::String::format("%c%cSorry.%cThis place is not available yet!", kControlBell, kControlCenter, kControlRoman); _anim->_vm->_dialogs->displayText(tmpStr); } @@ -370,6 +401,13 @@ Animation::Animation(AvalancheEngine *vm) { for (int16 i = 0; i < kSpriteNumbMax; i++) { _sprites[i] = new AnimationType(this); } + + _direction = kDirNone; + _oldDirection = kDirNone; + _arrowTriggered = false; + _geidaSpin = 0; + _geidaTime = 0; + _sayWhat = 0; } Animation::~Animation() { @@ -726,7 +764,7 @@ void Animation::catacombMove(byte ped) { spr1->init(5, true); // ...Load Geida. appearPed(1, geidaPed(ped)); spr1->_callEachStepFl = true; - spr1->_eachStepProc = kProcGeida; + spr1->_eachStepProc = kProcFollowAvvy; } } @@ -754,7 +792,7 @@ void Animation::callSpecial(uint16 which) { _vm->_magics[11]._data = 5; _vm->_magics[3]._operation = kMagicBounce; // Now works as planned! stopWalking(); - _vm->_dialogs->displayScrollChain('q', 26); + _vm->_dialogs->displayScrollChain('Q', 26); _vm->_userMovesAvvy = true; break; case 3: // _vm->special 3: Room 71: triggers dart. @@ -777,22 +815,20 @@ void Animation::callSpecial(uint16 which) { } break; case 4: // This is the ghost room link. - _vm->fadeOut(); - _sprites[0]->turn(kDirRight); // you'll see this after we get back from bootstrap + _sprites[0]->turn(kDirRight); // You'll see this after we get back. _vm->_timer->addTimer(1, Timer::kProcGhostRoomPhew, Timer::kReasonGhostRoomPhew); - //_vm->_enid->backToBootstrap(3); TODO: Replace it with proper ScummVM-friendly function(s)! Do not remove until then! + _vm->_ghostroom->run(); break; case 5: if (_vm->_friarWillTieYouUp) { // _vm->special 5: Room 42: touched tree, and get tied up. _vm->_magics[4]._operation = kMagicBounce; // Boundary effect is now working again. - _vm->_dialogs->displayScrollChain('q', 35); + _vm->_dialogs->displayScrollChain('Q', 35); _sprites[0]->remove(); - //tr[1].vanishifstill:=true; AnimationType *spr1 = _sprites[1]; _vm->_background->draw(-1, -1, 1); - _vm->_dialogs->displayScrollChain('q', 36); + _vm->_dialogs->displayScrollChain('Q', 36); _vm->_tiedUp = true; _vm->_friarWillTieYouUp = false; spr1->walkTo(2); @@ -825,7 +861,7 @@ void Animation::callSpecial(uint16 which) { case 8: // _vm->special 8: leave du Lustie's room. if (_vm->_geidaFollows && !_vm->_lustieIsAsleep) { AnimationType *spr1 = _sprites[1]; - _vm->_dialogs->displayScrollChain('q', 63); + _vm->_dialogs->displayScrollChain('Q', 63); spr1->turn(kDirDown); spr1->stopWalk(); spr1->_callEachStepFl = false; // Geida @@ -848,9 +884,9 @@ void Animation::callSpecial(uint16 which) { if ((_vm->_catacombX == 4) && (_vm->_catacombY == 1)) { // Into Geida's room. if (_vm->_objects[kObjectKey - 1]) - _vm->_dialogs->displayScrollChain('q', 62); + _vm->_dialogs->displayScrollChain('Q', 62); else { - _vm->_dialogs->displayScrollChain('q', 61); + _vm->_dialogs->displayScrollChain('Q', 61); return; } } @@ -1031,7 +1067,7 @@ void Animation::arrowProcs(byte tripnum) { void Animation::grabAvvy(byte tripnum) { // For Friar Tuck, in Nottingham. AnimationType *tripSpr = _sprites[tripnum]; - AnimationType *avvy = _sprites[tripnum]; + AnimationType *avvy = _sprites[0]; int16 tox = avvy->_x + 17; int16 toy = avvy->_y - 1; @@ -1085,7 +1121,7 @@ void Animation::spin(Direction dir, byte &tripnum) { } } -void Animation::geidaProcs(byte tripnum) { +void Animation::follow(byte tripnum) { AnimationType *tripSpr = _sprites[tripnum]; AnimationType *avvy = _sprites[0]; @@ -1096,14 +1132,14 @@ void Animation::geidaProcs(byte tripnum) { } if (tripSpr->_y < (avvy->_y - 2)) { - // Geida is further from the screen than Avvy. + // The following NPC is further from the screen than Avvy. spin(kDirDown, tripnum); tripSpr->_moveY = 1; tripSpr->_moveX = 0; takeAStep(tripnum); return; } else if (tripSpr->_y > (avvy->_y + 2)) { - // Avvy is further from the screen than Geida. + // Avvy is further from the screen than the following NPC. spin(kDirUp, tripnum); tripSpr->_moveY = -1; tripSpr->_moveX = 0; @@ -1169,8 +1205,9 @@ void Animation::drawSprites() { * @remarks Originally called 'trippancy_link' */ void Animation::animLink() { - if (_vm->_menu->isActive() || _vm->_seeScroll) + if (_vm->_dropdown->isActive() || !_vm->_animationsEnabled) return; + for (int16 i = 0; i < kSpriteNumbMax; i++) { AnimationType *curSpr = _sprites[i]; if (curSpr->_quick && curSpr->_visible) @@ -1199,8 +1236,10 @@ void Animation::animLink() { case kProcGrabAvvy : grabAvvy(i); break; - case kProcGeida : - geidaProcs(i); + case kProcFollowAvvy : + follow(i); + break; + default: break; } } @@ -1208,7 +1247,7 @@ void Animation::animLink() { if (_mustExclaim) { _mustExclaim = false; - _vm->_dialogs->displayScrollChain('x', _sayWhat); + _vm->_dialogs->displayScrollChain('X', _sayWhat); } } @@ -1291,7 +1330,7 @@ void Animation::handleMoveKey(const Common::Event &event) { if (!_vm->_userMovesAvvy) return; - if (_vm->_menu->_activeMenuItem._activeNow) + if (_vm->_dropdown->_activeMenuItem._activeNow) _vm->_parser->tryDropdown(); else { switch (event.kbd.keycode) { @@ -1360,6 +1399,69 @@ void Animation::handleMoveKey(const Common::Event &event) { } } +/** +* Draws a part of the lightning bolt for thunder(). +* @remarks Originally called 'zl' +*/ +void Animation::drawLightning(int16 x1, int16 y1, int16 x2, int16 y2) { + _vm->_graphics->drawLine(x1, y1 - 1, x2, y2 - 1, 1, 3, kColorBlue); + _vm->_graphics->drawLine(x1, y1, x2, y2, 1, 1, kColorLightcyan); +} + +/** +* Plays the actual thunder animation when Avvy (the player) swears too much. +* @remarks Originally called 'zonk' +*/ +void Animation::thunder() { + _vm->_graphics->setBackgroundColor(kColorYellow); + + _vm->_graphics->saveScreen(); + + int x = _vm->_animation->_sprites[0]->_x + _vm->_animation->_sprites[0]->_xLength / 2; + int y = _vm->_animation->_sprites[0]->_y; + + for (int i = 0; i < 256; i++) { + _vm->_sound->playNote(270 - i, 1); + + drawLightning(640, 0, 0, y / 4); + drawLightning(0, y / 4, 640, y / 2); + drawLightning(640, y / 2, x, y); + _vm->_graphics->refreshScreen(); + + _vm->_sound->playNote(2700 - 10 * i, 5); + _vm->_system->delayMillis(5); + _vm->_sound->playNote(270 - i, 1); + + _vm->_graphics->restoreScreen(); + _vm->_sound->playNote(2700 - 10 * i, 5); + _vm->_system->delayMillis(5); + } + + _vm->_graphics->restoreScreen(); + _vm->_graphics->removeBackup(); + + _vm->_graphics->setBackgroundColor(kColorBlack); +} + +/** +* Makes the screen wobble. +*/ +void Animation::wobble() { + _vm->_graphics->saveScreen(); + + for (int i = 0; i < 26; i++) { + _vm->_graphics->shiftScreen(); + _vm->_graphics->refreshScreen(); + _vm->_system->delayMillis(i * 7); + + _vm->_graphics->restoreScreen(); + _vm->_system->delayMillis(i * 7); + } + + _vm->_graphics->restoreScreen(); + _vm->_graphics->removeBackup(); +} + void Animation::setDirection(Direction dir) { _direction = dir; } @@ -1394,6 +1496,7 @@ int Animation::getAvvyClothes() { } void Animation::resetVariables() { + setDirection(kDirUp); _geidaSpin = 0; _geidaTime = 0; _arrowTriggered = false; diff --git a/engines/avalanche/animation.h b/engines/avalanche/animation.h index 33f6ab02a6..d1ee4a3ebd 100644 --- a/engines/avalanche/animation.h +++ b/engines/avalanche/animation.h @@ -8,12 +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. - + * * 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. @@ -97,13 +97,13 @@ public: static const byte kSpriteNumbMax = 5; // current max no. of sprites enum Proc { - kProcFollowAvvyY = 1, + kProcNone = 0, + kProcFollowAvvyY, kProcBackAndForth, kProcFaceAvvy, kProcArrow, - kProcSpludwick, // Unused kProcGrabAvvy, - kProcGeida // Spludwick uses it as well for homing! TODO: Unify it with kProcSpludwick. + kProcFollowAvvy }; AnimationType *_sprites[kSpriteNumbMax]; @@ -124,6 +124,12 @@ public: void handleMoveKey(const Common::Event &event); void hideInCupboard(); + // These 2 functions are responsible for playing the thunder animation when the player swears too much. + void drawLightning(int16 x1, int16 y1, int16 x2, int16 y2); + void thunder(); + + void wobble(); + void setDirection(Direction dir); void setOldDirection(Direction dir); Direction getDirection(); @@ -156,11 +162,11 @@ private: void followAvalotY(byte tripnum); void backAndForth(byte tripnum); void faceAvvy(byte tripnum); - + // Movements for Homing NPCs: Spludwick and Geida. void spin(Direction dir, byte &tripnum); void takeAStep(byte &tripnum); - void geidaProcs(byte tripnum); + void follow(byte tripnum); void drawSprites(); }; diff --git a/engines/avalanche/avalanche.cpp b/engines/avalanche/avalanche.cpp index 4f3868768a..6cfe4dfdb6 100644 --- a/engines/avalanche/avalanche.cpp +++ b/engines/avalanche/avalanche.cpp @@ -8,12 +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. - + * * 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 + * 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. @@ -41,17 +41,26 @@ AvalancheEngine::AvalancheEngine(OSystem *syst, const AvalancheGameDescription * TimeDate time; _system->getTimeAndDate(time); _rnd->setSeed(time.tm_sec + time.tm_min + time.tm_hour); - - // Needed because of Lucerna::load_also() - for (int i = 0; i < 31; i++) { - for (int j = 0; j < 2; j++) - _also[i][j] = nullptr; - } - - _totalTime = 0; _showDebugLines = false; - memset(_fxPal, 0, 16 * 16 * 3); + _clock = nullptr; + _graphics = nullptr; + _parser = nullptr; + _dialogs = nullptr; + _background = nullptr; + _sequence = nullptr; + _timer = nullptr; + _animation = nullptr; + _dropdown = nullptr; + _closing = nullptr; + _sound = nullptr; + _nim = nullptr; + _ghostroom = nullptr; + _help = nullptr; + _highscore = nullptr; + + _platform = gd->desc.platform; + initVariables(); } AvalancheEngine::~AvalancheEngine() { @@ -62,15 +71,18 @@ AvalancheEngine::~AvalancheEngine() { delete _parser; delete _clock; - delete _pingo; delete _dialogs; delete _background; delete _sequence; delete _timer; delete _animation; - delete _menu; + delete _dropdown; delete _closing; delete _sound; + delete _nim; + delete _ghostroom; + delete _help; + delete _highscore; for (int i = 0; i < 31; i++) { for (int j = 0; j < 2; j++) { @@ -82,20 +94,80 @@ AvalancheEngine::~AvalancheEngine() { } } +void AvalancheEngine::initVariables() { + for (int i = 0; i < 31; i++) { + _also[i][0] = nullptr; + _also[i][1] = nullptr; + } + + memset(_fxPal, 0, 16 * 16 * 3); + + for (int i = 0; i < 15; i++) { + _peds[i]._direction = kDirNone; + _peds[i]._x = 0; + _peds[i]._y = 0; + _magics[i]._operation = kMagicNothing; + _magics[i]._data = 0; + } + + for (int i = 0; i < 7; i++) { + _portals[i]._operation = kMagicNothing; + _portals[i]._data = 0; + } + + for (int i = 0; i < 30; i++) { + _fields[i]._x1 = 0; + _fields[i]._y1 = 0; + _fields[i]._x2 = 0; + _fields[i]._y2 = 0; + } + + _fieldNum = 0; + _cp = 0; + _ledStatus = 177; + _alive = false; + _subjectNum = 0; + _him = kPeoplePardon; + _her = kPeoplePardon; + _it = Parser::kPardon; + _roomCycles = 0; + _doingSpriteRun = false; + _isLoaded = false; + _soundFx = true; + _holdTheDawn = false; + + _lineNum = 0; + for (int i = 0; i < 50; i++) + _lines[i]._color = kColorWhite; + _dropsOk = false; + _cheat = false; + _letMeOut = false; + _thinks = 2; + _thinkThing = true; + _animationsEnabled = true; + _currentMouse = 177; + _holdLeftMouse = false; + + resetVariables(); +} + Common::ErrorCode AvalancheEngine::initialize() { _graphics = new GraphicManager(this); _parser = new Parser(this); _clock = new Clock(this); - _pingo = new Pingo(this); _dialogs = new Dialogs(this); _background = new Background(this); _sequence = new Sequence(this); _timer = new Timer(this); _animation = new Animation(this); - _menu = new Menu(this); + _dropdown = new DropDownMenu(this); _closing = new Closing(this); _sound = new SoundHandler(this); + _nim = new Nim(this); + _ghostroom = new GhostRoom(this); + _help = new Help(this); + _highscore = new HighScore(this); _graphics->init(); _dialogs->init(); @@ -124,13 +196,14 @@ const char *AvalancheEngine::getCopyrightString() const { void AvalancheEngine::synchronize(Common::Serializer &sz) { _animation->synchronize(sz); _parser->synchronize(sz); + _nim->synchronize(sz); _sequence->synchronize(sz); _background->synchronize(sz); sz.syncAsByte(_carryNum); for (int i = 0; i < kObjectNum; i++) sz.syncAsByte(_objects[i]); - sz.syncAsSint16LE(_dnascore); + sz.syncAsSint16LE(_score); sz.syncAsSint32LE(_money); sz.syncAsByte(_room); if (sz.isSaving()) @@ -166,17 +239,17 @@ void AvalancheEngine::synchronize(Common::Serializer &sz) { sz.syncAsByte(_arrowInTheDoor); if (sz.isSaving()) { - uint16 like2drinkSize = _favouriteDrink.size(); + uint16 like2drinkSize = _favoriteDrink.size(); sz.syncAsUint16LE(like2drinkSize); for (uint16 i = 0; i < like2drinkSize; i++) { - char actChr = _favouriteDrink[i]; + char actChr = _favoriteDrink[i]; sz.syncAsByte(actChr); } - uint16 favourite_songSize = _favouriteSong.size(); - sz.syncAsUint16LE(favourite_songSize); - for (uint16 i = 0; i < favourite_songSize; i++) { - char actChr = _favouriteSong[i]; + uint16 favoriteSongSize = _favoriteSong.size(); + sz.syncAsUint16LE(favoriteSongSize); + for (uint16 i = 0; i < favoriteSongSize; i++) { + char actChr = _favoriteSong[i]; sz.syncAsByte(actChr); } @@ -194,23 +267,23 @@ void AvalancheEngine::synchronize(Common::Serializer &sz) { sz.syncAsByte(actChr); } } else { - if (!_favouriteDrink.empty()) - _favouriteDrink.clear(); + if (!_favoriteDrink.empty()) + _favoriteDrink.clear(); uint16 like2drinkSize = 0; char actChr = ' '; sz.syncAsUint16LE(like2drinkSize); for (uint16 i = 0; i < like2drinkSize; i++) { sz.syncAsByte(actChr); - _favouriteDrink += actChr; + _favoriteDrink += actChr; } - if (!_favouriteSong.empty()) - _favouriteSong.clear(); - uint16 favourite_songSize = 0; - sz.syncAsUint16LE(favourite_songSize); - for (uint16 i = 0; i < favourite_songSize; i++) { + if (!_favoriteSong.empty()) + _favoriteSong.clear(); + uint16 favoriteSongSize = 0; + sz.syncAsUint16LE(favoriteSongSize); + for (uint16 i = 0; i < favoriteSongSize; i++) { sz.syncAsByte(actChr); - _favouriteSong += actChr; + _favoriteSong += actChr; } if (!_worstPlaceOnEarth.empty()) @@ -263,11 +336,11 @@ void AvalancheEngine::synchronize(Common::Serializer &sz) { sz.syncAsByte(_timer->_times[i]._action); sz.syncAsByte(_timer->_times[i]._reason); } - + } -bool AvalancheEngine::canSaveGameStateCurrently() { // TODO: Refine these!!! - return (!_seeScroll && _alive); +bool AvalancheEngine::canSaveGameStateCurrently() { + return (_animationsEnabled && _alive); } Common::Error AvalancheEngine::saveGameState(int slot, const Common::String &desc) { @@ -297,6 +370,8 @@ bool AvalancheEngine::saveGame(const int16 slot, const Common::String &desc) { f->writeSint16LE(t.tm_mon); f->writeSint16LE(t.tm_year); + _totalTime += getTimeInSeconds() - _startTime; + Common::Serializer sz(NULL, f); synchronize(sz); f->finalize(); @@ -309,8 +384,8 @@ Common::String AvalancheEngine::getSaveFileName(const int slot) { return Common::String::format("%s.%03d", _targetName.c_str(), slot); } -bool AvalancheEngine::canLoadGameStateCurrently() { // TODO: Refine these!!! - return (!_seeScroll); +bool AvalancheEngine::canLoadGameStateCurrently() { + return (_animationsEnabled); } Common::Error AvalancheEngine::loadGameState(int slot) { @@ -329,7 +404,7 @@ bool AvalancheEngine::loadGame(const int16 slot) { // Check version. We can't restore from obsolete versions. byte saveVersion = f->readByte(); - if (saveVersion != kSavegameVersion) { + if (saveVersion > kSavegameVersion) { warning("Savegame of incompatible version!"); delete f; return false; @@ -352,14 +427,15 @@ bool AvalancheEngine::loadGame(const int16 slot) { t.tm_mon = f->readSint16LE(); t.tm_year = f->readSint16LE(); - resetVariables(); + resetAllVariables(); Common::Serializer sz(f, NULL); synchronize(sz); delete f; _isLoaded = true; - _seeScroll = true; // This prevents display of the new sprites before the new picture is loaded. + + _animationsEnabled = false; if (_holdTheDawn) { _holdTheDawn = false; @@ -368,7 +444,7 @@ bool AvalancheEngine::loadGame(const int16 slot) { _background->release(); minorRedraw(); - _menu->setup(); + _dropdown->setup(); setRoom(kPeopleAvalot, _room); _alive = true; refreshObjectList(); @@ -377,9 +453,9 @@ bool AvalancheEngine::loadGame(const int16 slot) { _animation->animLink(); _background->update(); - Common::String tmpStr = Common::String::format("%cLoaded: %c%s.ASG%c%c%c%s%c%csaved on %s.", - kControlItalic, kControlRoman, description.c_str(), kControlCenter, kControlNewLine, - kControlNewLine, _roomnName.c_str(), kControlNewLine, kControlNewLine, + Common::String tmpStr = Common::String::format("%cLoaded: %c%s.ASG%c%c%c%s%c%csaved on %s.", + kControlItalic, kControlRoman, description.c_str(), kControlCenter, kControlNewLine, + kControlNewLine, _roomnName.c_str(), kControlNewLine, kControlNewLine, expandDate(t.tm_mday, t.tm_mon, t.tm_year).c_str()); _dialogs->displayText(tmpStr); @@ -416,6 +492,12 @@ Common::String AvalancheEngine::expandDate(int d, int m, int y) { return day + ' ' + month + ' ' + intToStr(y + 1900); } +uint32 AvalancheEngine::getTimeInSeconds() { + TimeDate time; + _system->getTimeAndDate(time); + return time.tm_hour * 3600 + time.tm_min * 60 + time.tm_sec; +} + void AvalancheEngine::updateEvents() { Common::Event event; @@ -456,75 +538,9 @@ Common::Error AvalancheEngine::run() { do { runAvalot(); - -#if 0 - switch (_storage._operation) { - case kRunShootemup: - run("seu.avx", kJsb, kBflight, kNormal); - break; - case kRunDosshell: - dosShell(); - break; - case kRunGhostroom: - run("g-room.avx", kJsb, kNoBflight, kNormal); - break; - case kRunGolden: - run("golden.avx", kJsb, kBflight, kMusical); - break; - } -#endif - } while (!_letMeOut && !shouldQuit()); return Common::kNoError; } -#if 0 -void AvalancheEngine::run(Common::String what, bool withJsb, bool withBflight, Elm how) { - // Probably there'll be no need of this function, as all *.AVX-es will become classes. - warning("STUB: run(%s)", what.c_str()); -} - -Common::String AvalancheEngine::elmToStr(Elm how) { - switch (how) { - case kNormal: - case kMusical: - return Common::String("jsb"); - case kRegi: - return Common::String("REGI"); - case kElmpoyten: - return Common::String("ELMPOYTEN"); - // Useless, but silent a warning - default: - return Common::String(""); - } -} - -// Same as keypressed1(). -void AvalancheEngine::flushBuffer() { - warning("STUB: flushBuffer()"); -} - -void AvalancheEngine::dosShell() { - warning("STUB: dosShell()"); -} - -// Needed in dos_shell(). TODO: Remove later. -Common::String AvalancheEngine::commandCom() { - warning("STUB: commandCom()"); - return ("STUB: commandCom()"); -} - -// Needed for run_avalot()'s errors. TODO: Remove later. -void AvalancheEngine::explain(byte error) { - warning("STUB: explain()"); -} - -// Needed later. -void AvalancheEngine::quit() { - cursorOn(); -} - -#endif - } // End of namespace Avalanche diff --git a/engines/avalanche/avalanche.h b/engines/avalanche/avalanche.h index cc9a34d82b..6eb5e675cc 100644 --- a/engines/avalanche/avalanche.h +++ b/engines/avalanche/avalanche.h @@ -8,12 +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. - + * * 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 + * 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. @@ -32,15 +32,21 @@ #include "avalanche/graphics.h" #include "avalanche/parser.h" #include "avalanche/avalot.h" -#include "avalanche/pingo.h" #include "avalanche/dialogs.h" #include "avalanche/background.h" #include "avalanche/sequence.h" #include "avalanche/timer.h" #include "avalanche/animation.h" -#include "avalanche/menu.h" +#include "avalanche/dropdown.h" #include "avalanche/closing.h" #include "avalanche/sound.h" +#include "avalanche/nim.h" +#include "avalanche/clock.h" +#include "avalanche/ghostroom.h" +#include "avalanche/help.h" +#include "avalanche/shootemup.h" +#include "avalanche/mainmenu.h" +#include "avalanche/highscore.h" #include "common/serializer.h" @@ -55,9 +61,11 @@ class RandomSource; namespace Avalanche { -struct AvalancheGameDescription; +struct AvalancheGameDescription { + ADGameDescription desc; +}; -static const int kSavegameVersion = 1; +static const int kSavegameVersion = 2; enum Pitch { kPitchInvalid, @@ -73,15 +81,18 @@ public: Clock *_clock; GraphicManager *_graphics; Parser *_parser; - Pingo *_pingo; Dialogs *_dialogs; Background *_background; Sequence *_sequence; Timer *_timer; Animation *_animation; - Menu *_menu; + DropDownMenu *_dropdown; Closing *_closing; SoundHandler *_sound; + Nim *_nim; + GhostRoom *_ghostroom; + Help *_help; + HighScore *_highscore; OSystem *_system; @@ -109,6 +120,7 @@ public: Common::Error loadGameState(int slot); bool loadGame(const int16 slot); Common::String expandDate(int d, int m, int y); + uint32 getTimeInSeconds(); void updateEvents(); bool getEvent(Common::Event &event); // A wrapper around _eventMan->pollEvent(), so we can use it in Scrolls::normscroll() for example. @@ -122,43 +134,6 @@ private: AvalancheConsole *_console; Common::Platform _platform; -#if 0 - struct { - byte _operation; - uint16 _skellern; - byte _contents[1000]; - } _storage; - - static const int16 kRunShootemup = 1, kRunDosshell = 2, kRunGhostroom = 3, kRunGolden = 4; - static const int16 kReset = 0; - - static const bool kJsb = true, kNoJsb = false, kBflight = true, kNoBflight = false; - - // From bootstrp: - enum Elm {kNormal, kMusical, kElmpoyten, kRegi}; - - Common::String _argsWithNoFilename; - byte _originalMode; - byte *_old1c; - Common::String _segofs; - int32 _soundcard, _speed, _baseaddr, _irq, _dma; - bool _zoomy; - - void run(Common::String what, bool withJsb, bool withBflight, Elm how); - void bFlightOn(); - void bFlightOff(); - Common::String elmToStr(Elm how); - bool keyPressed(); - void flushBuffer(); - void dosShell(); - void bFlight(); - Common::String commandCom(); - void explain(byte error); - void cursorOff(); - void cursorOn(); - void quit(); -#endif - public: // For Thinkabout: static const bool kThing = true; @@ -167,7 +142,6 @@ public: static const char kSpludwicksOrder[3]; static const uint16 kNotes[12]; - static const TuneType kTune; bool _holdLeftMouse; @@ -178,7 +152,7 @@ public: // Former DNA structure byte _carryNum; // How many objects you're carrying... bool _objects[kObjectNum]; // ...and which ones they are. - int16 _dnascore; // your score, of course + int16 _score; // your score, of course int32 _money; // your current amount of dosh Room _room; // your current room bool _wonNim; // Have you *won* Nim? (That's harder.) @@ -207,8 +181,9 @@ public: bool _standingOnDais; // In room 71, inside Cardiff Castle. bool _takenPen; // Have you taken the pen (in Cardiff?) bool _arrowInTheDoor; // Did the arrow hit the wall? - Common::String _favouriteDrink, _favouriteSong, _worstPlaceOnEarth, _spareEvening; // Personalisation str's - uint32 _totalTime; // Your total time playing this game, in ticks. + Common::String _favoriteDrink, _favoriteSong, _worstPlaceOnEarth, _spareEvening; // Personalisation str's + uint32 _startTime; // When did you start playing this session? + uint32 _totalTime; // Your total time playing this game, in seconds. Updated only at saving and loading. byte _jumpStatus; // Fixes how high you're jumping. bool _mushroomGrowing; // Is the mushroom growing in 42? bool _crapulusWillTell; // Will Crapulus tell you about Spludwick being away? @@ -237,7 +212,7 @@ public: bool _letMeOut; byte _thinks; bool _thinkThing; - bool _seeScroll; // TODO: maybe this means we're interacting with the toolbar / a scroll? + bool _animationsEnabled; // If set to TRUE, it stops the animation system working. This prevents display of the new sprites before the new picture is loaded or during the display of a scroll. Original name: seescroll. char _objectList[10]; // Called .free() for them in ~Gyro(). @@ -255,13 +230,15 @@ public: byte _subjectNum; // The same thing. People _him, _her; byte _it; - uint32 _roomTime; // Set to 0 when you enter a room, added to in every loop. + uint32 _roomCycles; // Set to 0 when you enter a room, added to in every loop. Cycles since you've been in this room. bool _doingSpriteRun; // Only set to True if we're doing a sprite_run at this moment. This stops the trippancy system from moving any of the sprites. - bool _isLoaded; // Is it a loaded gamestate? bool _soundFx; + bool _isLoaded; // Is it a loaded gamestate? + void callVerb(VerbCode id); + void loadBackground(byte num); void loadRoom(byte num); void thinkAbout(byte object, bool type); // Hey!!! Get it and put it!!! void incScore(byte num); // Add on no. of points @@ -274,9 +251,9 @@ public: void gameOver(); uint16 bearing(byte whichPed); // Returns the bearing from ped 'whichped' to Avvy, in degrees. - // There are two kinds of redraw: Major and Minor. Minor is what happens when you load a game, etc. Major redraws EVERYTHING. + // There are two kinds of redraw: Major and Minor. Minor is what happens when you load a game, etc. + // Major was replaced with GraphicManager::refreshScreen(), it redraws EVERYTHING. void minorRedraw(); - void majorRedraw(); void spriteRun(); @@ -284,7 +261,7 @@ public: void newGame(); // This sets up the DNA for a completely new game. bool getFlag(char x); bool decreaseMoney(uint16 amount); // Called pennycheck in the original. - + Common::String getName(People whose); Common::String getItem(byte which); // Called get_better in the original. Common::String f5Does(); // This procedure determines what f5 does. @@ -316,6 +293,7 @@ private: Common::String readAlsoStringFromFile(Common::File &file); void runAvalot(); void init(); + void initVariables(); void setup(); void scram(Common::String &str); void unScramble(); @@ -332,6 +310,7 @@ private: void checkClick(); void fixFlashers(); void loadAlso(byte num); + void resetAllVariables(); void resetVariables(); }; diff --git a/engines/avalanche/avalot.cpp b/engines/avalanche/avalot.cpp index 8ef41a2c93..0ffe7d3f9d 100644 --- a/engines/avalanche/avalot.cpp +++ b/engines/avalanche/avalot.cpp @@ -8,12 +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. - + * * 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 + * 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. @@ -76,11 +76,6 @@ namespace Avalanche { const char AvalancheEngine::kSpludwicksOrder[3] = {kObjectOnion, kObjectInk, kObjectMushroom}; const uint16 AvalancheEngine::kNotes[12] = {196, 220, 247, 262, 294, 330, 350, 392, 440, 494, 523, 587}; -const TuneType AvalancheEngine::kTune = { - kPitchHigher, kPitchHigher, kPitchLower, kPitchSame, kPitchHigher, kPitchHigher, kPitchLower, kPitchHigher, kPitchHigher, kPitchHigher, - kPitchLower, kPitchHigher, kPitchHigher, kPitchSame, kPitchHigher, kPitchLower, kPitchLower, kPitchLower, kPitchLower, kPitchHigher, - kPitchHigher, kPitchLower, kPitchLower, kPitchLower, kPitchLower, kPitchSame, kPitchLower, kPitchHigher, kPitchSame, kPitchLower, kPitchHigher -}; Room AvalancheEngine::_whereIs[29] = { // The Lads @@ -116,83 +111,6 @@ Room AvalancheEngine::_whereIs[29] = { kRoomWiseWomans // The Wise Woman. }; -Clock::Clock(AvalancheEngine *vm) { - _vm = vm; - _oldHour = _oldHourAngle = _oldMinute = 17717; -} - -void Clock::update() { // TODO: Move variables from Gyro to here (or at least somewhere nearby), rename them. - TimeDate t; - _vm->_system->getTimeAndDate(t); - _hour = t.tm_hour; - _minute = t.tm_min; - _second = t.tm_sec; - - _hourAngle = (_hour % 12) * 30 + _minute / 2; - - if (_oldHour != _hour) { - plotHands(); - chime(); - } - - if (_oldMinute != _minute) - plotHands(); - - if ((_hour == 0) && (_oldHour != 0) && (_oldHour != 17717)) { - Common::String tmpStr = Common::String::format("Good morning!%c%cYes, it's just past " \ - "midnight. Are you having an all-night Avvy session? Glad you like the game that much!", - kControlNewLine, kControlNewLine); - _vm->_dialogs->displayText(tmpStr); - } - _oldHour = _hour; - _oldHourAngle = _hourAngle; - _oldMinute = _minute; -} - -Common::Point Clock::calcHand(uint16 angle, uint16 length, Color color) { - if (angle > 900) { - return(Common::Point(177, 177)); - } - - return(_vm->_graphics->drawScreenArc(kCenterX, kCenterY, 449 - angle, 450 - angle, length, color)); -} - -void Clock::drawHand(const Common::Point &endPoint, Color color) { - if (endPoint.x == 177) - return; - - _vm->_graphics->drawScreenLine(kCenterX, kCenterY, endPoint.x, endPoint.y, color); -} - -void Clock::plotHands() { - _clockHandHour = calcHand(_oldHourAngle, 14, kColorYellow); - _clockHandMinute = calcHand(_oldMinute * 6, 17, kColorYellow); - drawHand(_clockHandHour, kColorBrown); - drawHand(_clockHandMinute, kColorBrown); - - _clockHandHour = calcHand(_hourAngle, 14, kColorBrown); - _clockHandMinute = calcHand(_minute * 6, 17, kColorBrown); - drawHand(_clockHandHour, kColorYellow); - drawHand(_clockHandMinute, kColorYellow); -} - -void Clock::chime() { - if ((_oldHour == 17717) || (!_vm->_soundFx)) // Too high - must be first time around - return; - - byte hour = _hour % 12; - if (hour == 0) - hour = 12; - - _vm->_graphics->loadMouse(kCurWait); - - for (int i = 1; i <= hour; i++) { - for (int j = 1; j <= 3; j++) - _vm->_sound->playNote((i % 3) * 64 + 140 - j * 30, 50 - j * 12); - if (i != hour) - _vm->_system->delayMillis(100); - } -} void AvalancheEngine::handleKeyDown(Common::Event &event) { @@ -272,7 +190,6 @@ void AvalancheEngine::setup() { _animation->resetAnims(); - drawToolbar(); _dialogs->setReadyLight(2); fadeIn(); @@ -281,7 +198,11 @@ void AvalancheEngine::setup() { _animation->_sprites[0]->_speedX = kWalk; _animation->updateSpeed(); - _menu->init(); + _dropdown->init(); + + _graphics->drawSoundLight(_sound->_soundFl); + + drawToolbar(); int16 loadSlot = ConfMan.instance().getInt("save_slot"); if (loadSlot >= 0) { @@ -290,27 +211,31 @@ void AvalancheEngine::setup() { loadGame(loadSlot); } else { - _isLoaded = false; // Set to true in _vm->loadGame(). + // We don't need the MainMenu during the whole game, only at the beginning of it. + MainMenu *mainmenu = new MainMenu(this); + mainmenu->run(); + delete mainmenu; + if (_letMeOut) + return; + newGame(); - _soundFx = !_soundFx; - fxToggle(); thinkAbout(kObjectMoney, kThing); - _dialogs->displayScrollChain('q', 83); // Info on the game, etc. + _dialogs->displayScrollChain('Q', 83); // Info on the game, etc. } } void AvalancheEngine::runAvalot() { setup(); - do { + while (!_letMeOut && !shouldQuit()) { uint32 beginLoop = _system->getMillis(); updateEvents(); // The event handler. _clock->update(); - _menu->update(); + _dropdown->update(); _background->update(); _animation->animLink(); checkClick(); @@ -322,9 +247,7 @@ void AvalancheEngine::runAvalot() { uint32 delay = _system->getMillis() - beginLoop; if (delay <= 55) _system->delayMillis(55 - delay); // Replaces slowdown(); 55 comes from 18.2 Hz (B Flight). - } while (!_letMeOut && !shouldQuit()); - - warning("STUB: run()"); + }; _closing->exitGame(); } @@ -335,13 +258,6 @@ void AvalancheEngine::init() { _also[i][j] = nullptr; } -#if 0 - if (_vm->_enhanced->atbios) - atkey = "f1"; - else - atkey = "alt-"; -#endif - _letMeOut = false; _currentMouse = 177; _dropsOk = true; @@ -498,9 +414,7 @@ void AvalancheEngine::loadAlso(byte num) { } } -void AvalancheEngine::loadRoom(byte num) { - CursorMan.showMouse(false); - +void AvalancheEngine::loadBackground(byte num) { Common::String filename = Common::String::format("place%d.avd", num); Common::File file; if (!file.open(filename)) @@ -522,9 +436,15 @@ void AvalancheEngine::loadRoom(byte num) { _graphics->refreshBackground(); file.close(); +} + +void AvalancheEngine::loadRoom(byte num) { + CursorMan.showMouse(false); + loadBackground(num); loadAlso(num); - _background->load(num); + _background->loadSprites(num); + CursorMan.showMouse(true); } @@ -542,7 +462,7 @@ void AvalancheEngine::findPeople(byte room) { void AvalancheEngine::exitRoom(byte x) { _sound->stopSound(); _background->release(); - _seeScroll = true; // This stops the trippancy system working over the length of this procedure. + _animationsEnabled = false; switch (x) { case kRoomSpludwicks: @@ -565,21 +485,20 @@ void AvalancheEngine::exitRoom(byte x) { } _interrogation = 0; // Leaving the room cancels all the questions automatically. - _seeScroll = false; // Now it can work again! + _animationsEnabled = true; _lastRoom = _room; if (_room != kRoomMap) _lastRoomNotMap = _room; } - /** * Only when entering a NEW town! Not returning to the last one, * but choosing another from the map. * @remarks Originally called 'new_town' */ void AvalancheEngine::enterNewTown() { - _menu->setup(); + _dropdown->setup(); switch (_room) { case kRoomOutsideNottsPub: // Entry into Nottingham. @@ -613,11 +532,11 @@ void AvalancheEngine::putGeidaAt(byte whichPed, byte ped) { spr1->init(5, false); // load Geida _animation->appearPed(1, whichPed); spr1->_callEachStepFl = true; - spr1->_eachStepProc = Animation::kProcGeida; + spr1->_eachStepProc = Animation::kProcFollowAvvy; } void AvalancheEngine::enterRoom(Room roomId, byte ped) { - _seeScroll = true; // This stops the trippancy system working over the length of this procedure. + _animationsEnabled = false; findPeople(roomId); _room = roomId; @@ -634,12 +553,13 @@ void AvalancheEngine::enterRoom(Room roomId, byte ped) { if (_geidaFollows) _whereIs[kPeopleGeida - 150] = roomId; - _roomTime = 0; - + _roomCycles = 0; if ((_lastRoom == kRoomMap) && (_lastRoomNotMap != _room)) enterNewTown(); + _animation->updateSpeed(); + switch (roomId) { case kRoomYours: if (_avvyInBed) { @@ -697,7 +617,7 @@ void AvalancheEngine::enterRoom(Room roomId, byte ped) { } spr1->_callEachStepFl = true; - spr1->_eachStepProc = Animation::kProcGeida; + spr1->_eachStepProc = Animation::kProcFollowAvvy; } else _whereIs[kPeopleSpludwick - 150] = kRoomNowhere; break; @@ -806,12 +726,12 @@ void AvalancheEngine::enterRoom(Room roomId, byte ped) { _graphics->zoomOut(_peds[ped - 1]._x, _peds[ped - 1]._y); if ((_objects[kObjectWine - 1]) && (_wineState != 3)) { - _dialogs->displayScrollChain('q', 9); // Don't want to waste the wine! + _dialogs->displayScrollChain('Q', 9); // Don't want to waste the wine! _objects[kObjectWine - 1] = false; refreshObjectList(); } - _dialogs->displayScrollChain('q', 69); + _dialogs->displayScrollChain('Q', 69); break; case kRoomCatacombs: @@ -994,14 +914,13 @@ void AvalancheEngine::enterRoom(Room roomId, byte ped) { case kRoomDucks: _npcFacing = 1; // Duck. - break; + break; default: break; } - _seeScroll = false; // Now it can work again! - _isLoaded = false; + _animationsEnabled = true; } void AvalancheEngine::thinkAbout(byte object, bool type) { @@ -1036,7 +955,7 @@ void AvalancheEngine::drawToolbar() { } void AvalancheEngine::drawScore() { - uint16 score = _dnascore; + uint16 score = _score; int8 numbers[3] = {0, 0, 0}; for (int i = 0; i < 2; i++) { byte divisor = 1; @@ -1060,17 +979,16 @@ void AvalancheEngine::drawScore() { _scoreToDisplay[i] = numbers[i]; } -void AvalancheEngine::incScore(byte num) { +void AvalancheEngine::incScore(byte num) { for (int i = 1; i <= num; i++) { - _dnascore++; + _score++; if (_soundFx) { for (int j = 1; j <= 97; j++) - // Length os 2 is a guess, the original doesn't have a delay specified - _sound->playNote(177 + _dnascore * 3, 2); + // Length of 2 is a guess, the original doesn't have a delay specified + _sound->playNote(177 + _score * 3, 2); } } - warning("STUB: points()"); drawScore(); } @@ -1195,7 +1113,7 @@ void AvalancheEngine::checkClick() { _graphics->loadMouse(kCurIBeam); //I-beam else if ((340 <= cursorPos.y) && (cursorPos.y <= 399)) _graphics->loadMouse(kCurScrewDriver); // screwdriver - else if (!_menu->isActive()) { // Dropdown can handle its own pointers. + else if (!_dropdown->isActive()) { // Dropdown can handle its own pointers. if (_holdLeftMouse) { _graphics->loadMouse(kCurCrosshair); // Mark's crosshairs guideAvvy(cursorPos); // Normally, if you click on the picture, you're guiding Avvy around. @@ -1206,7 +1124,7 @@ void AvalancheEngine::checkClick() { if (_holdLeftMouse) { if ((0 <= cursorPos.y) && (cursorPos.y <= 21)) { // Click on the dropdown menu. if (_dropsOk) - _menu->update(); + _dropdown->update(); } else if ((317 <= cursorPos.y) && (cursorPos.y <= 339)) { // Click on the command line. _parser->_inputTextPos = (cursorPos.x - 23) / 8; if (_parser->_inputTextPos > _parser->_inputText.size() + 1) @@ -1229,7 +1147,7 @@ void AvalancheEngine::checkClick() { _parser->_thing += 49; _parser->_person = kPeoplePardon; } else { - _parser->_person = (People) _thinks; + _parser->_person = (People)_thinks; _parser->_thing = _parser->kPardon; } callVerb(kVerbCodeExam); @@ -1246,7 +1164,7 @@ void AvalancheEngine::checkClick() { _animation->_sprites[0]->_speedX = kRun; _animation->updateSpeed(); } else if ((396 <= cursorPos.x) && (cursorPos.x <= 483)) - fxToggle(); + _sound->toggleSound(); else if ((535 <= cursorPos.x) && (cursorPos.x <= 640)) _mouseText.insertChar(kControlNewLine, 0); } else if (!_dropsOk) @@ -1255,7 +1173,14 @@ void AvalancheEngine::checkClick() { } void AvalancheEngine::errorLed() { - warning("STUB: errorled()"); + _dialogs->setReadyLight(0); + _graphics->drawErrorLight(true); + for (int i = 177; i >= 1; i--) { + _sound->playNote(177 + (i * 177177) / 999, 1); + _system->delayMillis(1); + } + _graphics->drawErrorLight(false); + _dialogs->setReadyLight(2); } /** @@ -1307,9 +1232,9 @@ void AvalancheEngine::fadeOut() { void AvalancheEngine::fadeIn() { if (_holdTheDawn || !_fxHidden) return; - + _fxHidden = false; - + byte pal[3]; for (int i = 15; i >= 0; i--) { for (int j = 0; j < 16; j++) { @@ -1337,7 +1262,6 @@ void AvalancheEngine::drawDirection() { // It's data is loaded in load_digits(). CursorMan.showMouse(true); } - void AvalancheEngine::gameOver() { _userMovesAvvy = false; @@ -1366,17 +1290,13 @@ void AvalancheEngine::minorRedraw() { fadeIn(); } -void AvalancheEngine::majorRedraw() { - warning("STUB: major_redraw()"); -} - uint16 AvalancheEngine::bearing(byte whichPed) { AnimationType *avvy = _animation->_sprites[0]; PedType *curPed = &_peds[whichPed]; if (avvy->_x == curPed->_x) return 0; - + int16 deltaX = avvy->_x - curPed->_x; int16 deltaY = avvy->_y - curPed->_y; uint16 result = (uint16)(atan((float)(deltaY / deltaX)) * 180 / M_PI); @@ -1387,7 +1307,7 @@ uint16 AvalancheEngine::bearing(byte whichPed) { } } -/** +/** * @remarks Originally called 'sprite_run' */ void AvalancheEngine::spriteRun() { @@ -1409,12 +1329,11 @@ Common::String AvalancheEngine::intToStr(int32 num) { } void AvalancheEngine::resetVariables() { - _animation->setDirection(kDirUp); _carryNum = 0; for (int i = 0; i < kObjectNum; i++) _objects[i] = false; - _dnascore = 0; + _score = 0; _money = 0; _room = kRoomNowhere; _saveNum = 0; @@ -1448,16 +1367,16 @@ void AvalancheEngine::resetVariables() { _standingOnDais = false; _takenPen = false; _arrowInTheDoor = false; - _favouriteDrink = ""; - _favouriteSong = ""; + _favoriteDrink = ""; + _favoriteSong = ""; _worstPlaceOnEarth = ""; _spareEvening = ""; _totalTime = 0; _jumpStatus = 0; _mushroomGrowing = false; _spludwickAtHome = false; - _lastRoom = 0; - _lastRoomNotMap = 0; + _lastRoom = kRoomDummy; + _lastRoomNotMap = kRoomDummy; _crapulusWillTell = false; _enterCatacombsFromLustiesRoom = false; _teetotal = false; @@ -1477,12 +1396,18 @@ void AvalancheEngine::resetVariables() { _takenMushroom = false; _givenPenToAyles = false; _askedDogfoodAboutNim = false; + _startTime = getTimeInSeconds(); +} +void AvalancheEngine::resetAllVariables() { + resetVariables(); _parser->resetVariables(); + _nim->resetVariables(); _animation->resetVariables(); _sequence->resetVariables(); _background->resetVariables(); - _menu->resetVariables(); + _dropdown->resetVariables(); + _timer->resetVariables(); } void AvalancheEngine::newGame() { @@ -1497,12 +1422,12 @@ void AvalancheEngine::newGame() { avvy->init(0, true); _alive = true; - resetVariables(); + resetAllVariables(); _dialogs->setBubbleStateNatural(); _spareEvening = "answer a questionnaire"; - _favouriteDrink = "beer"; + _favoriteDrink = "beer"; _money = 30; // 2/6 _animation->setDirection(kDirStopped); _parser->_wearing = kObjectClothes; @@ -1514,7 +1439,7 @@ void AvalancheEngine::newGame() { _thinkThing = true; _thinks = 2; refreshObjectList(); - _seeScroll = false; + _animationsEnabled = true; avvy->appear(300, 117, kDirRight); // Needed to initialize Avalot. //for (gd = 0; gd <= 30; gd++) for (gm = 0; gm <= 1; gm++) also[gd][gm] = nil; @@ -1527,10 +1452,12 @@ void AvalancheEngine::newGame() { _doingSpriteRun = false; _avvyInBed = true; + _isLoaded = false; + enterRoom(kRoomYours, 1); avvy->_visible = false; drawScore(); - _menu->setup(); + _dropdown->setup(); _clock->update(); spriteRun(); } @@ -1558,16 +1485,18 @@ Common::String AvalancheEngine::getName(People whose) { static const char lads[17][20] = { "Avalot", "Spludwick", "Crapulus", "Dr. Duck", "Malagauche", "Friar Tuck", "Robin Hood", "Cwytalot", "du Lustie", "the Duke of Cardiff", - "Dogfood", "A trader", "Ibythneth", "Ayles", "Port", + "Dogfood", "A trader", "Ibythneth", "Ayles", "Port", "Spurge", "Jacques" }; static const char lasses[4][15] = {"Arkata", "Geida", "\0xB1", "the Wise Woman"}; - if (whose < kPeopleArkata) + if (whose <= kPeopleJacques) return Common::String(lads[whose - kPeopleAvalot]); - else + else if ((whose >= kPeopleArkata) && (whose <= kPeopleWisewoman)) return Common::String(lasses[whose - kPeopleArkata]); + else + error("getName() - Unexpected character id %d", (byte) whose); } Common::String AvalancheEngine::getItem(byte which) { @@ -1674,6 +1603,9 @@ void AvalancheEngine::flipRoom(Room room, byte ped) { if (_room == kRoomLustiesRoom) _enterCatacombsFromLustiesRoom = true; + if (room > kRoomMap) + return; + enterRoom(room, ped); _animation->appearPed(0, ped - 1); _enterCatacombsFromLustiesRoom = false; @@ -1735,10 +1667,10 @@ void AvalancheEngine::openDoor(Room whither, byte ped, byte magicnum) { } void AvalancheEngine::setRoom(People persId, Room roomId) { - _whereIs[persId - kPeopleAvalot] = roomId; + _whereIs[persId - kPeopleAvalot] = roomId; } Room AvalancheEngine::getRoom(People persId) { - return _whereIs[persId - kPeopleAvalot]; + return _whereIs[persId - kPeopleAvalot]; } } // End of namespace Avalanche diff --git a/engines/avalanche/avalot.h b/engines/avalanche/avalot.h index ab78f5c385..04b945fd20 100644 --- a/engines/avalanche/avalot.h +++ b/engines/avalanche/avalot.h @@ -8,12 +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. - + * * 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 + * 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. @@ -35,33 +35,9 @@ namespace Avalanche { class AvalancheEngine; -class Clock { -public: - Clock(AvalancheEngine *vm); - - void update(); - -private: - static const int kCenterX = 510; - static const int kCenterY = 183; - - AvalancheEngine *_vm; - - uint16 _hour, _minute, _second, _hourAngle, _oldHour, _oldMinute, _oldHourAngle; - Common::Point _clockHandHour, _clockHandMinute; - - Common::Point calcHand(uint16 angle, uint16 length, Color color); - void drawHand(const Common::Point &endPoint, Color color); - void plotHands(); - void chime(); -}; - static const byte kObjectNum = 18; // always preface with a # static const int16 kCarryLimit = 12; // carry limit -static const int16 kNumlockCode = 32; // Code for Num Lock -static const int16 kMouseSize = 134; - struct PedType { int16 _x, _y; Direction _direction; @@ -80,8 +56,6 @@ struct LineType : public FieldType { Color _color; }; -typedef int8 TuneType[31]; - struct QuasipedType { byte _whichPed; Color _textColor; @@ -90,15 +64,6 @@ struct QuasipedType { People _who; }; -#if 0 -struct Sundry { // Things which must be saved over a backtobootstrap, outside DNA. - Common::String _qEnidFilename; - bool _qSoundFx; - byte _qThinks; - bool _qThinkThing; -}; -#endif - } // End of namespace Avalanche #endif // AVALANCHE_AVALOT_H diff --git a/engines/avalanche/background.cpp b/engines/avalanche/background.cpp index c84c049c8f..f1ba659a55 100644 --- a/engines/avalanche/background.cpp +++ b/engines/avalanche/background.cpp @@ -8,12 +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. - + * * 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. @@ -37,6 +37,7 @@ const int16 Background::kOnDisk = -1; Background::Background(AvalancheEngine *vm) { _vm = vm; _spriteNum = 0; + _nextBell = 0; } Background::~Background() { @@ -47,28 +48,28 @@ Background::~Background() { * @remarks Originally called 'pics_link' */ void Background::update() { - if (_vm->_menu->isActive()) + if (_vm->_dropdown->isActive()) return; // No animation when the menus are up. switch (_vm->_room) { case kRoomOutsideArgentPub: - if ((_vm->_roomTime % 12) == 0) - draw(-1, -1, (_vm->_roomTime / 12) % 4); + if ((_vm->_roomCycles % 12) == 0) + draw(-1, -1, (_vm->_roomCycles / 12) % 4); break; case kRoomBrummieRoad: - if ((_vm->_roomTime % 2) == 0) - draw(-1, -1, (_vm->_roomTime / 2) % 4); + if ((_vm->_roomCycles % 2) == 0) + draw(-1, -1, (_vm->_roomCycles / 2) % 4); break; case kRoomBridge: - if ((_vm->_roomTime % 2) == 0) - draw(-1, -1, 3 + (_vm->_roomTime / 2) % 4); + if ((_vm->_roomCycles % 2) == 0) + draw(-1, -1, 3 + (_vm->_roomCycles / 2) % 4); break; case kRoomYours: - if ((!_vm->_avvyIsAwake) && ((_vm->_roomTime % 4) == 0)) - draw(-1, -1, (_vm->_roomTime / 12) % 2); + if ((!_vm->_avvyIsAwake) && ((_vm->_roomCycles % 4) == 0)) + draw(-1, -1, (_vm->_roomCycles / 12) % 2); break; case kRoomArgentPub: - if (((_vm->_roomTime % 7) == 1) && (_vm->_malagauche != 177)) { + if (((_vm->_roomCycles % 7) == 1) && (_vm->_malagauche != 177)) { // Malagauche cycle. _vm->_malagauche++; switch (_vm->_malagauche) { @@ -92,7 +93,7 @@ void Background::update() { } } - switch (_vm->_roomTime % 200) { + switch (_vm->_roomCycles % 200) { case 179: case 197: draw(-1, -1, 4); // Dogfood's drinking cycle. @@ -108,7 +109,7 @@ void Background::update() { _vm->_npcFacing = 177; // Impossible value for this. break; default: - if (_vm->_roomTime % 200 <= 178) { // Normally. + if (_vm->_roomCycles % 200 <= 178) { // Normally. byte direction = 1; uint16 angle = _vm->bearing(1); if (((angle >= 1) && (angle <= 90)) || ((angle >= 358) && (angle <= 360))) @@ -126,8 +127,8 @@ void Background::update() { } break; case kRoomWestHall: - if ((_vm->_roomTime % 3) == 0) { - switch ((_vm->_roomTime / 3) % 6) { + if ((_vm->_roomCycles % 3) == 0) { + switch ((_vm->_roomCycles / 3) % 6) { case 4: draw(-1, -1, 0); break; @@ -147,7 +148,7 @@ void Background::update() { if (!(_vm->_lustieIsAsleep)) { byte direction = 0; uint16 angle = _vm->bearing(1); - if ((_vm->_roomTime % 45) > 42) + if ((_vm->_roomCycles % 45) > 42) direction = 4; // du Lustie blinks. // Bearing of Avvy from du Lustie. else if ((angle <= 45) || ((angle >= 315) && (angle <= 360))) @@ -164,8 +165,8 @@ void Background::update() { } break; case kRoomAylesOffice: - if ((!_vm->_aylesIsAwake) && (_vm->_roomTime % 14 == 0)) { - switch ((_vm->_roomTime / 14) % 2) { + if ((!_vm->_aylesIsAwake) && (_vm->_roomCycles % 14 == 0)) { + switch ((_vm->_roomCycles / 14) % 2) { case 0: draw(-1, -1, 0); // Frame 2: EGA. break; @@ -177,7 +178,7 @@ void Background::update() { break; case kRoomRobins: if (_vm->_tiedUp) { - switch (_vm->_roomTime % 54) { + switch (_vm->_roomCycles % 54) { case 20: draw(-1, -1, 3); // Frame 4: Avalot blinks. break; @@ -198,7 +199,7 @@ void Background::update() { else if ((angle >= 181) && (angle <= 314)) direction = 8; // Right. - if ((_vm->_roomTime % 60) > 57) + if ((_vm->_roomCycles % 60) > 57) direction--; // Blinks. if (direction != _vm->_npcFacing) { // Port. @@ -206,7 +207,7 @@ void Background::update() { _vm->_npcFacing = direction; } - switch (_vm->_roomTime % 50) { + switch (_vm->_roomCycles % 50) { case 45 : draw(-1, -1, 8); // Spurge blinks. break; @@ -217,8 +218,8 @@ void Background::update() { break; } case kRoomDucks: { - if ((_vm->_roomTime % 3) == 0) // The fire flickers. - draw(-1, -1, (_vm->_roomTime / 3) % 3); + if ((_vm->_roomCycles % 3) == 0) // The fire flickers. + draw(-1, -1, (_vm->_roomCycles / 3) % 3); // Bearing of Avvy from Duck. byte direction = 0; @@ -230,7 +231,7 @@ void Background::update() { else if ((angle >= 181) && (angle <= 314)) direction = 8; // Right. - if ((_vm->_roomTime % 45) > 42) + if ((_vm->_roomCycles % 45) > 42) direction++; // Duck blinks. if (direction != _vm->_npcFacing) { // Duck. @@ -245,12 +246,12 @@ void Background::update() { if ((_vm->_bellsAreRinging) && (_vm->getFlag('B'))) { // They're ringing the bells. - switch (_vm->_roomTime % 4) { + switch (_vm->_roomCycles % 4) { case 1: if (_nextBell < 5) _nextBell = 12; _nextBell--; - // CHECKME: 2 is a guess. No length in the original? + // CHECKME: 2 is a guess. No length in the original? _vm->_sound->playNote(_vm->kNotes[_nextBell], 2); break; case 2: @@ -260,7 +261,7 @@ void Background::update() { } } -void Background::load(byte number) { +void Background::loadSprites(byte number) { Common::File f; _filename = _filename.format("chunk%d.avd", number); if (!f.open(_filename)) @@ -278,24 +279,24 @@ void Background::load(byte number) { sprite._type = (PictureType)(f.readByte()); sprite._x = f.readSint16LE(); sprite._y = f.readSint16LE(); - sprite._xl = f.readSint16LE(); - sprite._yl = f.readSint16LE(); + sprite._width = f.readSint16LE(); + sprite._height = f.readSint16LE(); sprite._size = f.readSint32LE(); bool natural = f.readByte(); bool memorize = f.readByte(); if (memorize) { _sprites[i]._x = sprite._x; - _sprites[i]._xl = sprite._xl; + _sprites[i]._width = sprite._width; _sprites[i]._y = sprite._y; - _sprites[i]._yl = sprite._yl; + _sprites[i]._height = sprite._height; _sprites[i]._type = sprite._type; if (natural) _vm->_graphics->getNaturalPicture(_sprites[i]); else { _sprites[i]._size = sprite._size; - _sprites[i]._picture = _vm->_graphics->loadPictureRaw(f, _sprites[i]._xl * 8, _sprites[i]._yl + 1); + _sprites[i]._picture = _vm->_graphics->loadPictureRaw(f, _sprites[i]._width * 8, _sprites[i]._height + 1); } } else _sprites[i]._x = kOnDisk; @@ -334,11 +335,11 @@ void Background::draw(int16 destX, int16 destY, byte sprId) { sprite._type = (PictureType)(f.readByte()); sprite._x = f.readSint16LE(); sprite._y = f.readSint16LE(); - sprite._xl = f.readSint16LE(); - sprite._yl = f.readSint16LE(); + sprite._width = f.readSint16LE(); + sprite._height = f.readSint16LE(); sprite._size = f.readSint32LE(); f.skip(2); // Natural and Memorize are used in Load() - sprite._picture = _vm->_graphics->loadPictureRaw(f, sprite._xl * 8, sprite._yl + 1); + sprite._picture = _vm->_graphics->loadPictureRaw(f, sprite._width * 8, sprite._height + 1); if (destX < 0) { destX = sprite._x * 8; diff --git a/engines/avalanche/background.h b/engines/avalanche/background.h index 34d7a9a2cc..e994d9eae9 100644 --- a/engines/avalanche/background.h +++ b/engines/avalanche/background.h @@ -8,12 +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. - + * * 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. @@ -40,7 +40,7 @@ enum PictureType {kEga, kBgi, kNaturalImage}; struct SpriteType { PictureType _type; int16 _x, _y; - int16 _xl, _yl; + int16 _width, _height; int32 _size; Graphics::Surface _picture; }; @@ -51,7 +51,7 @@ public: ~Background(); void update(); - void load(byte number); + void loadSprites(byte number); void release(); // Setting the destination to negative coordinates means the picture should be drawn to it's original position. diff --git a/engines/avalanche/clock.cpp b/engines/avalanche/clock.cpp new file mode 100644 index 0000000000..6d398d9921 --- /dev/null +++ b/engines/avalanche/clock.cpp @@ -0,0 +1,116 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* +* This code is based on the original source code of Lord Avalot d'Argent version 1.3. +* Copyright (c) 1994-1995 Mike: Mark and Thomas Thurman. +*/ + +#include "avalanche/clock.h" +#include "avalanche/avalanche.h" + +namespace Avalanche { + +Clock::Clock(AvalancheEngine *vm) { + _vm = vm; + // Magic value to determine if we just created the instance + _oldHour = _oldHourAngle = _oldMinute = 17717; + _hour = _minute = _second = 0; + _hourAngle = 0; +} + +void Clock::update() { + TimeDate t; + _vm->_system->getTimeAndDate(t); + _hour = t.tm_hour; + _minute = t.tm_min; + _second = t.tm_sec; + + _hourAngle = (_hour % 12) * 30 + _minute / 2; + + if (_oldHour != _hour) { + plotHands(); + chime(); + } + + if (_oldMinute != _minute) + plotHands(); + + if ((_hour == 0) && (_oldHour != 0) && (_oldHour != 17717)) { + Common::String tmpStr = Common::String::format("Good morning!%c%cYes, it's just past " \ + "midnight. Are you having an all-night Avvy session? Glad you like the game that much!", + kControlNewLine, kControlNewLine); + _vm->_dialogs->displayText(tmpStr); + } + _oldHour = _hour; + _oldHourAngle = _hourAngle; + _oldMinute = _minute; +} + +Common::Point Clock::calcHand(uint16 angle, uint16 length, Color color) { + if (angle > 900) { + return(Common::Point(177, 177)); + } + + return(_vm->_graphics->drawScreenArc(kCenterX, kCenterY, 449 - angle, 450 - angle, length, color)); +} + +void Clock::drawHand(const Common::Point &endPoint, Color color) { + if (endPoint.x == 177) + return; + + _vm->_graphics->drawScreenLine(kCenterX, kCenterY, endPoint.x, endPoint.y, color); +} + +void Clock::plotHands() { + _clockHandHour = calcHand(_oldHourAngle, 14, kColorYellow); + _clockHandMinute = calcHand(_oldMinute * 6, 17, kColorYellow); + drawHand(_clockHandHour, kColorBrown); + drawHand(_clockHandMinute, kColorBrown); + + _clockHandHour = calcHand(_hourAngle, 14, kColorBrown); + _clockHandMinute = calcHand(_minute * 6, 17, kColorBrown); + drawHand(_clockHandHour, kColorYellow); + drawHand(_clockHandMinute, kColorYellow); +} + +void Clock::chime() { + // Too high - must be first time around + // Mute - skip the sound generation + if ((_oldHour == 17717) || (!_vm->_soundFx)) + return; + + byte hour = _hour % 12; + if (hour == 0) + hour = 12; + + _vm->_graphics->loadMouse(kCurWait); + + for (int i = 1; i <= hour; i++) { + for (int j = 1; j <= 3; j++) + _vm->_sound->playNote((i % 3) * 64 + 140 - j * 30, 50 - j * 12); + if (i != hour) + _vm->_system->delayMillis(100); + } +} + +} // End of namespace Avalanche diff --git a/engines/avalanche/pingo.h b/engines/avalanche/clock.h index 72fdb54c2a..85ea508a80 100644 --- a/engines/avalanche/pingo.h +++ b/engines/avalanche/clock.h @@ -8,12 +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. - + * * 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. @@ -21,39 +21,40 @@ */ /* - * This code is based on the original source code of Lord Avalot d'Argent version 1.3. - * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. - */ - -/* PINGO Full-screen sub-parts of the game. */ +* This code is based on the original source code of Lord Avalot d'Argent version 1.3. +* Copyright (c) 1994-1995 Mike: Mark and Thomas Thurman. +*/ -#ifndef AVALANCHE_PINGO_H -#define AVALANCHE_PINGO_H +#ifndef AVALANCHE_CLOCK_H +#define AVALANCHE_CLOCK_H -#include "common/str.h" +#include "common/rect.h" +#include "avalanche/enums.h" namespace Avalanche { class AvalancheEngine; -class Pingo { +class Clock { public: - Pingo(AvalancheEngine *vm); + Clock(AvalancheEngine *vm); - void bossKey(); - void copy02(); - void copy03(); - void copyPage(byte frp, byte top); - void wobble(); - void zonk(); - void winningPic(); + void update(); private: + static const int kCenterX = 510; + static const int kCenterY = 183; + AvalancheEngine *_vm; - void dPlot(int16 x, int16 y, Common::String z); - void zl(int16 x1, int16 y1, int16 x2, int16 y2); + uint16 _hour, _minute, _second, _hourAngle, _oldHour, _oldMinute, _oldHourAngle; + Common::Point _clockHandHour, _clockHandMinute; + + Common::Point calcHand(uint16 angle, uint16 length, Color color); + void drawHand(const Common::Point &endPoint, Color color); + void plotHands(); + void chime(); }; -} // End of namespace Avalanche. +} // End of namespace Avalanche -#endif // AVALANCHE_PINGO_H +#endif // AVALANCHE_CLOCK_H diff --git a/engines/avalanche/closing.cpp b/engines/avalanche/closing.cpp index 1cb2e84218..552b71b563 100644 --- a/engines/avalanche/closing.cpp +++ b/engines/avalanche/closing.cpp @@ -8,12 +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. - + * * 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. diff --git a/engines/avalanche/closing.h b/engines/avalanche/closing.h index 25217e347e..6e65deb310 100644 --- a/engines/avalanche/closing.h +++ b/engines/avalanche/closing.h @@ -8,12 +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. - + * * 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. diff --git a/engines/avalanche/configure.engine b/engines/avalanche/configure.engine new file mode 100644 index 0000000000..28d6a558db --- /dev/null +++ b/engines/avalanche/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 avalanche "Lord Avalot d'Argent" no diff --git a/engines/avalanche/console.cpp b/engines/avalanche/console.cpp index 656cc1907c..d4923affc1 100644 --- a/engines/avalanche/console.cpp +++ b/engines/avalanche/console.cpp @@ -8,12 +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. - + * * 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 + * 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. @@ -31,7 +31,7 @@ namespace Avalanche { AvalancheConsole::AvalancheConsole(AvalancheEngine *vm) : GUI::Debugger(), _vm(vm) { - DCmd_Register("magic_lines", WRAP_METHOD(AvalancheConsole, Cmd_MagicLines)); + registerCmd("magic_lines", WRAP_METHOD(AvalancheConsole, Cmd_MagicLines)); } AvalancheConsole::~AvalancheConsole() { @@ -42,7 +42,7 @@ AvalancheConsole::~AvalancheConsole() { */ bool AvalancheConsole::Cmd_MagicLines(int argc, const char **argv) { if (argc != 1) { - DebugPrintf("Usage: %s\n", argv[0]); + debugPrintf("Usage: %s\n", argv[0]); return true; } @@ -50,5 +50,4 @@ bool AvalancheConsole::Cmd_MagicLines(int argc, const char **argv) { return false; } - } // End of namespace Avalanche diff --git a/engines/avalanche/console.h b/engines/avalanche/console.h index 166515d913..b5b5fb63fc 100644 --- a/engines/avalanche/console.h +++ b/engines/avalanche/console.h @@ -8,12 +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. - + * * 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 + * 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. diff --git a/engines/avalanche/detection.cpp b/engines/avalanche/detection.cpp index 428e71f35a..028f167e70 100644 --- a/engines/avalanche/detection.cpp +++ b/engines/avalanche/detection.cpp @@ -8,12 +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. - + * * 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 + * 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. @@ -35,10 +35,6 @@ namespace Avalanche { -struct AvalancheGameDescription { - ADGameDescription desc; -}; - uint32 AvalancheEngine::getFeatures() const { return _gameDescription->desc.flags; } @@ -133,7 +129,7 @@ SaveStateList AvalancheMetaEngine::listSaves(const char *target) const { // Check version. byte saveVersion = file->readByte(); - if (saveVersion != kSavegameVersion) { + if (saveVersion > kSavegameVersion) { warning("Savegame of incompatible version!"); delete file; continue; diff --git a/engines/avalanche/dialogs.cpp b/engines/avalanche/dialogs.cpp index e5acd9cae2..f95440900b 100644 --- a/engines/avalanche/dialogs.cpp +++ b/engines/avalanche/dialogs.cpp @@ -8,12 +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. - + * * 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 + * 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. @@ -34,6 +34,12 @@ namespace Avalanche { +const Dialogs::TuneType Dialogs::kTune = { + kPitchHigher, kPitchHigher, kPitchLower, kPitchSame, kPitchHigher, kPitchHigher, kPitchLower, kPitchHigher, kPitchHigher, kPitchHigher, + kPitchLower, kPitchHigher, kPitchHigher, kPitchSame, kPitchHigher, kPitchLower, kPitchLower, kPitchLower, kPitchLower, kPitchHigher, + kPitchHigher, kPitchLower, kPitchLower, kPitchLower, kPitchLower, kPitchSame, kPitchLower, kPitchHigher, kPitchSame, kPitchLower, kPitchHigher +}; + // A quasiped defines how people who aren't sprites talk. For example, quasiped // "A" is Dogfood. The rooms aren't stored because I'm leaving that to context. const QuasipedType Dialogs::kQuasipeds[16] = { @@ -59,6 +65,19 @@ const QuasipedType Dialogs::kQuasipeds[16] = { Dialogs::Dialogs(AvalancheEngine *vm) { _vm = vm; _noError = true; + + _aboutBox = false; + _talkX = 0; + _talkY = 0; + _maxLineNum = 0; + _scReturn = false; + _currentFont = kFontStyleRoman; + _param = 0; + _useIcon = 0; + _scrollBells = 0; + _underScroll = 0; + _shadowBoxX = 0; + _shadowBoxY = 0; } void Dialogs::init() { @@ -74,6 +93,7 @@ void Dialogs::setReadyLight(byte state) { if (_vm->_ledStatus == state) return; // Already like that! + // TODO: Implement different patterns for green color. Color color = kColorBlack; switch (state) { case 0: @@ -85,9 +105,7 @@ void Dialogs::setReadyLight(byte state) { color = kColorGreen; break; // Hit a key } - warning("STUB: Dialogs::setReadyLight()"); - CursorMan.showMouse(false); _vm->_graphics->drawReadyLight(color); CursorMan.showMouse(true); _vm->_ledStatus = state; @@ -139,7 +157,7 @@ void Dialogs::scrollModeNormal() { Common::String e = "(c) 1994"; setReadyLight(3); - _vm->_seeScroll = true; + _vm->_animationsEnabled = false; _vm->_graphics->loadMouse(kCurFletch); _vm->_graphics->saveScreen(); @@ -156,7 +174,8 @@ void Dialogs::scrollModeNormal() { (event.kbd.keycode == Common::KEYCODE_PLUS)))) { escape = true; break; - } + } else if (event.type == Common::EVENT_KEYDOWN) + _vm->errorLed(); } } @@ -177,7 +196,6 @@ void Dialogs::scrollModeNormal() { break; } while (!((mrelease > 0) || (buttona1()) || (buttonb1()))); - if (mrelease == 0) { inkey(); if (aboutscroll) { @@ -198,10 +216,8 @@ void Dialogs::scrollModeNormal() { #endif setReadyLight(0); - _vm->_seeScroll = false; + _vm->_animationsEnabled = true; _vm->_holdLeftMouse = false; // Used in Lucerna::checkclick(). - - warning("STUB: Scrolls::scrollModeNormal()"); } /** @@ -258,7 +274,7 @@ bool Dialogs::theyMatch(TuneType &played) { byte mistakes = 0; for (unsigned int i = 0; i < sizeof(played); i++) { - if (played[i] != _vm->kTune[i]) + if (played[i] != kTune[i]) mistakes++; } @@ -272,7 +288,7 @@ bool Dialogs::theyMatch(TuneType &played) { */ void Dialogs::scrollModeMusic() { setReadyLight(3); - _vm->_seeScroll = true; + _vm->_animationsEnabled = false; CursorMan.showMouse(false); _vm->_graphics->loadMouse(kCurFletch); @@ -281,7 +297,7 @@ void Dialogs::scrollModeMusic() { played[i] = kPitchInvalid; int8 lastOne = -1, thisOne = -1; // Invalid values. - _vm->_seeScroll = true; + _vm->_animationsEnabled = false; _vm->_graphics->saveScreen(); _vm->_graphics->showScroll(); @@ -306,7 +322,7 @@ void Dialogs::scrollModeMusic() { || (event.kbd.keycode == Common::KEYCODE_u) || (event.kbd.keycode == Common::KEYCODE_i) || (event.kbd.keycode == Common::KEYCODE_o) || (event.kbd.keycode == Common::KEYCODE_p) || (event.kbd.keycode == Common::KEYCODE_LEFTBRACKET) || (event.kbd.keycode == Common::KEYCODE_RIGHTBRACKET))) { - byte value; + byte value = 0; switch (event.kbd.keycode) { case Common::KEYCODE_q: value = 0; @@ -345,6 +361,7 @@ void Dialogs::scrollModeMusic() { value = 11; break; default: + error("cannot happen"); break; } @@ -362,7 +379,7 @@ void Dialogs::scrollModeMusic() { else store(kPitchHigher, played); } - + if (theyMatch(played)) { setReadyLight(0); _vm->_timer->addTimer(8, Timer::kProcJacquesWakesUp, Timer::kReasonJacquesWakingUp); @@ -374,7 +391,7 @@ void Dialogs::scrollModeMusic() { _vm->_graphics->restoreScreen(); _vm->_graphics->removeBackup(); - _vm->_seeScroll = false; + _vm->_animationsEnabled = true; CursorMan.showMouse(true); } @@ -437,7 +454,7 @@ void Dialogs::drawScroll(DialogFunctionType modeFunc) { mx -= lx; my -= ly + 2; - bool centre = false; + bool center = false; byte iconIndent = 0; switch (_useIcon) { @@ -463,11 +480,11 @@ void Dialogs::drawScroll(DialogFunctionType modeFunc) { if (!_scroll[i].empty()) switch (_scroll[i][_scroll[i].size() - 1]) { case kControlCenter: - centre = true; + center = true; _scroll[i].deleteLastChar(); break; case kControlLeftJustified: - centre = false; + center = false; _scroll[i].deleteLastChar(); break; case kControlQuestion: @@ -479,7 +496,7 @@ void Dialogs::drawScroll(DialogFunctionType modeFunc) { break; } - if (centre) + if (center) say(320 - _scroll[i].size() * 4 + iconIndent, my, _scroll[i]); else say(mx + iconIndent, my, _scroll[i]); @@ -489,7 +506,7 @@ void Dialogs::drawScroll(DialogFunctionType modeFunc) { _underScroll = (my + 3) * 2; // Multiplying because of the doubled screen height. ringBell(); - + _vm->_dropsOk = false; dodgem(); @@ -497,7 +514,7 @@ void Dialogs::drawScroll(DialogFunctionType modeFunc) { unDodgem(); _vm->_dropsOk = true; - + resetScrollDriver(); } @@ -584,7 +601,7 @@ Common::String Dialogs::displayMoney() { else result = Common::String::format("%d/%d", _vm->_money / 12, _vm->_money % 12); } else { // L, s & d - result = Common::String::format("\x9C%d.%d.%d", _vm->_money / 240, (_vm->_money / 12) % 20, + result = Common::String::format("\x9C%d.%d.%d", _vm->_money / 240, (_vm->_money / 12) % 20, _vm->_money % 12); } if (_vm->_money > 12) { @@ -623,13 +640,10 @@ void Dialogs::solidify(byte n) { /** * @remarks Originally called 'calldriver' - * Display text by calling the dialog driver. It unifies the function of the original + * Display text by calling the dialog driver. It unifies the function of the original * 'calldriver' and 'display' by using Common::String instead of a private buffer. */ void Dialogs::displayText(Common::String text) { -// bool was_virtual; // Was the mouse cursor virtual on entry to this proc? - warning("STUB: Scrolls::calldrivers()"); - _vm->_sound->stopSound(); setReadyLight(0); @@ -689,6 +703,7 @@ void Dialogs::displayText(Common::String text) { if (_param == 0) setBubbleStateNatural(); else if ((1 <= _param) && (_param <= 9)) { + assert(_param - 1 < _vm->_animation->kSpriteNumbMax); AnimationType *spr = _vm->_animation->_sprites[_param - 1]; if ((_param > _vm->_animation->kSpriteNumbMax) || (!spr->_quick)) { // Not valid. _vm->errorLed(); @@ -699,10 +714,11 @@ void Dialogs::displayText(Common::String text) { // Quasi-peds. (This routine performs the same // thing with QPs as triptype.chatter does with the // sprites.) + assert(_param - 10 < 16); PedType *quasiPed = &_vm->_peds[kQuasipeds[_param - 10]._whichPed]; _talkX = quasiPed->_x; _talkY = quasiPed->_y; // Position. - + _vm->_graphics->setDialogColor(kQuasipeds[_param - 10]._backgroundColor, kQuasipeds[_param - 10]._textColor); } else { _vm->errorLed(); // Not valid. @@ -717,7 +733,7 @@ void Dialogs::displayText(Common::String text) { return; break; - // CHECME: The whole kControlNegative block seems completely unused, as the only use (the easter egg check) is a false positive + // CHECME: The whole kControlNegative block seems completely unused, as the only use (the easter egg check) is a false positive case kControlNegative: switch (_param) { case 1: @@ -729,10 +745,10 @@ void Dialogs::displayText(Common::String text) { } break; case 3: - displayText(_vm->_favouriteDrink + kControlToBuffer); + displayText(_vm->_favoriteDrink + kControlToBuffer); break; case 4: - displayText(_vm->_favouriteSong + kControlToBuffer); + displayText(_vm->_favoriteSong + kControlToBuffer); break; case 5: displayText(_vm->_worstPlaceOnEarth + kControlToBuffer); @@ -748,7 +764,7 @@ void Dialogs::displayText(Common::String text) { case 10: switch (_vm->_boxContent) { case 0: // Sixpence. - displayScrollChain('q', 37); // You find the sixpence. + displayScrollChain('Q', 37); // You find the sixpence. _vm->_money += 6; _vm->_boxContent = _vm->_parser->kNothing; _vm->incScore(2); @@ -762,7 +778,7 @@ void Dialogs::displayText(Common::String text) { break; case 11: for (int j = 0; j < kObjectNum; j++) { - if (_vm->_objects[j]) + if (_vm->_objects[j]) displayText(_vm->getItem(j) + ", " + kControlToBuffer); } break; @@ -802,6 +818,8 @@ void Dialogs::displayText(Common::String text) { } } } + + setReadyLight(2); } void Dialogs::setTalkPos(int16 x, int16 y) { @@ -815,7 +833,7 @@ int16 Dialogs::getTalkPosX() { bool Dialogs::displayQuestion(Common::String question) { displayText(question + kControlNewLine + kControlQuestion); - + if (_scReturn && (_vm->_rnd->getRandomNumber(1) == 0)) { // Half-and-half chance. Common::String tmpStr = Common::String::format("...Positive about that?%cI%c%c%c", kControlRegister, kControlIcon, kControlNewLine, kControlQuestion); displayText(tmpStr); // Be annoying! @@ -859,7 +877,7 @@ void Dialogs::loadFont() { * @remarks Originally called 'musical_scroll' */ void Dialogs::displayMusicalScroll() { - Common::String tmpStr = Common::String::format("To play the harp...%c%cUse these keys:%c%cQ W E R T Y U I O P [ ]%c%cOr press Enter to stop playing.%c", + Common::String tmpStr = Common::String::format("To play the harp...%c%cUse these keys:%c%cQ W E R T Y U I O P [ ]%c%cOr press Enter to stop playing.%c", kControlNewLine, kControlNewLine, kControlNewLine, kControlInsertSpaces, kControlNewLine, kControlNewLine, kControlToBuffer); displayText(tmpStr); @@ -891,7 +909,7 @@ void Dialogs::displayScrollChain(char block, byte point, bool report, bool bubbl bool error = false; - indexfile.seek((toupper(block) - 65) * 2); + indexfile.seek((toupper(block) - 'A') * 2); uint16 idx_offset = indexfile.readUint16LE(); if (idx_offset == 0) error = true; @@ -938,7 +956,7 @@ void Dialogs::displayScrollChain(char block, byte point, bool report, bool bubbl */ void Dialogs::speak(byte who, byte subject) { if (subject == 0) { // No subject. - displayScrollChain('s', who, false, true); + displayScrollChain('S', who, false, true); return; } @@ -992,7 +1010,7 @@ void Dialogs::talkTo(byte whom) { switch (whom) { case kPeopleSpludwick: if ((_vm->_lustieIsAsleep) & (!_vm->_objects[kObjectPotion - 1])) { - displayScrollChain('q', 68); + displayScrollChain('Q', 68); _vm->_objects[kObjectPotion - 1] = true; _vm->refreshObjectList(); _vm->incScore(3); @@ -1004,64 +1022,64 @@ void Dialogs::talkTo(byte whom) { case 1: // Fallthrough is intended. case 2: { Common::String objStr = _vm->getItem(AvalancheEngine::kSpludwicksOrder[_vm->_givenToSpludwick]); - Common::String tmpStr = Common::String::format("Can you get me %s, please?%c2%c", + Common::String tmpStr = Common::String::format("Can you get me %s, please?%c2%c", objStr.c_str(), kControlRegister, kControlSpeechBubble); displayText(tmpStr); } return; case 3: - displayScrollChain('q', 30); // Need any help with the game? + displayScrollChain('Q', 30); // Need any help with the game? return; } } else { - displayScrollChain('q', 42); // Haven't talked to Crapulus. Go and talk to him. + displayScrollChain('Q', 42); // Haven't talked to Crapulus. Go and talk to him. return; } break; case kPeopleIbythneth: if (_vm->_givenBadgeToIby) { - displayScrollChain('q', 33); // Thanks a lot! + displayScrollChain('Q', 33); // Thanks a lot! return; // And leave the proc. } break; // Or... just continue, 'cos he hasn't got it. case kPeopleDogfood: if (_vm->_wonNim) { // We've won the game. - displayScrollChain('q', 6); // "I'm Not Playing!" + displayScrollChain('Q', 6); // "I'm Not Playing!" return; // Zap back. } else _vm->_askedDogfoodAboutNim = true; break; case kPeopleAyles: if (!_vm->_aylesIsAwake) { - displayScrollChain('q', 43); // He's fast asleep! + displayScrollChain('Q', 43); // He's fast asleep! return; } else if (!_vm->_givenPenToAyles) { - displayScrollChain('q', 44); // Can you get me a pen, Avvy? + displayScrollChain('Q', 44); // Can you get me a pen, Avvy? return; } break; case kPeopleJacques: - displayScrollChain('q', 43); + displayScrollChain('Q', 43); return; case kPeopleGeida: if (_vm->_givenPotionToGeida) _vm->_geidaFollows = true; else { - displayScrollChain('u', 17); + displayScrollChain('U', 17); return; } break; case kPeopleSpurge: if (!_vm->_sittingInPub) { - displayScrollChain('q', 71); // Try going over and sitting down. + displayScrollChain('Q', 71); // Try going over and sitting down. return; } else { if (_vm->_spurgeTalkCount < 5) _vm->_spurgeTalkCount++; if (_vm->_spurgeTalkCount > 1) { // no. 1 falls through - displayScrollChain('q', 70 + _vm->_spurgeTalkCount); + displayScrollChain('Q', 70 + _vm->_spurgeTalkCount); return; } } @@ -1069,7 +1087,7 @@ void Dialogs::talkTo(byte whom) { } // On a subject. Is there any reason to block it? } else if ((whom == kPeopleAyles) && (!_vm->_aylesIsAwake)) { - displayScrollChain('q', 43); // He's fast asleep! + displayScrollChain('Q', 43); // He's fast asleep! return; } @@ -1094,12 +1112,12 @@ void Dialogs::talkTo(byte whom) { speak(whom, _vm->_subjectNum); if (!_noError) - displayScrollChain('n', whom); // File not found! + displayScrollChain('N', whom); // File not found! if ((_vm->_subjectNum == 0) && ((whom + 149) == kPeopleCrapulus)) { // Crapulus: get the badge - first time only _vm->_objects[kObjectBadge - 1] = true; _vm->refreshObjectList(); - displayScrollChain('q', 1); // Circular from Cardiff. + displayScrollChain('Q', 1); // Circular from Cardiff. _vm->_talkedToCrapulus = true; _vm->setRoom(kPeopleCrapulus, kRoomDummy); // Crapulus walks off. @@ -1164,7 +1182,9 @@ void Dialogs::sayThanks(byte thing) { Common::String tmpStr = personSpeaks(); tmpStr += Common::String::format("Hey, thanks!%c(But now, you've lost it!)", kControlSpeechBubble); displayText(tmpStr); - _vm->_objects[thing] = false; + + if (thing < kObjectNum) + _vm->_objects[thing] = false; } /** diff --git a/engines/avalanche/dialogs.h b/engines/avalanche/dialogs.h index 43e6a4fec6..4b50a61732 100644 --- a/engines/avalanche/dialogs.h +++ b/engines/avalanche/dialogs.h @@ -8,12 +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. - + * * 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 + * 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. @@ -70,6 +70,9 @@ private: kFontStyleItalic }; + typedef int8 TuneType[31]; + + static const TuneType kTune; static const int16 kHalfIconWidth = 19; static const QuasipedType kQuasipeds[16]; diff --git a/engines/avalanche/menu.cpp b/engines/avalanche/dropdown.cpp index bba8e862a9..97adfc2581 100644 --- a/engines/avalanche/menu.cpp +++ b/engines/avalanche/dropdown.cpp @@ -8,12 +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. - + * * 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. @@ -28,43 +28,43 @@ /* Original name: DROPDOWN A customized version of Oopmenu (qv). */ #include "avalanche/avalanche.h" -#include "avalanche/menu.h" +#include "avalanche/dropdown.h" namespace Avalanche { -void HeadType::init(char trig, char altTrig, Common::String title, byte pos, MenuFunc setupFunc, MenuFunc chooseFunc, Menu *menu) { +void HeadType::init(char trig, char altTrig, Common::String title, byte pos, MenuFunc setupFunc, MenuFunc chooseFunc, DropDownMenu *menu) { _trigger = trig; _altTrigger = altTrig; _title = title; _position = pos; - _xpos = _position * _menu->kSpacing + _menu->kIndent; - _xright = (_position + 1) * _menu->kSpacing + _menu->kIndent; + _xpos = _position * _dropdown->kSpacing + _dropdown->kIndent; + _xright = (_position + 1) * _dropdown->kSpacing + _dropdown->kIndent; _setupFunc = setupFunc; _chooseFunc = chooseFunc; - _menu = menu; + _dropdown = menu; } void HeadType::draw() { CursorMan.showMouse(false); - _menu->drawMenuText(_xpos, 1, _trigger, _title, true, false); + _dropdown->drawMenuText(_xpos, 1, _trigger, _title, true, false); CursorMan.showMouse(true); } void HeadType::highlight() { CursorMan.showMouse(false); - _menu->_vm->_sound->stopSound(); - _menu->drawMenuText(_xpos, 1, _trigger, _title, true, true); + _dropdown->_vm->_sound->stopSound(); + _dropdown->drawMenuText(_xpos, 1, _trigger, _title, true, true); - _menu->_activeMenuItem._left = _xpos; - _menu->_activeMenuItem._activeNow = true; - _menu->_activeMenuItem._activeNum = _position; - _menu->_menuActive = true; + _dropdown->_activeMenuItem._left = _xpos; + _dropdown->_activeMenuItem._activeNow = true; + _dropdown->_activeMenuItem._activeNum = _position; + _dropdown->_menuActive = true; // Force reload and redraw of cursor. - _menu->_vm->_currentMouse = 177; - + _dropdown->_vm->_currentMouse = 177; + } bool HeadType::parseAltTrigger(char key) { @@ -73,12 +73,12 @@ bool HeadType::parseAltTrigger(char key) { return false; } -void MenuItem::init(Menu *menu) { - _menu = menu; +void MenuItem::init(DropDownMenu *menu) { + _dropdown = menu; _activeNow = false; _activeNum = 1; - _menu->_menuActive = false; + _dropdown->_menuActive = false; } void MenuItem::reset() { @@ -113,8 +113,8 @@ void MenuItem::displayOption(byte y, bool highlit) { else backgroundColor = kColorLightgray; - _menu->_vm->_graphics->drawMenuBlock((_flx1 + 1) * 8, 3 + (y + 1) * 10, (_flx2 + 1) * 8, 13 + (y + 1) * 10, backgroundColor); - _menu->drawMenuText(_left, 4 + (y + 1) * 10, _options[y]._trigger, text, _options[y]._valid, highlit); + _dropdown->_vm->_graphics->drawMenuBlock((_flx1 + 1) * 8, 3 + (y + 1) * 10, (_flx2 + 1) * 8, 13 + (y + 1) * 10, backgroundColor); + _dropdown->drawMenuText(_left, 4 + (y + 1) * 10, _options[y]._trigger, text, _options[y]._valid, highlit); } void MenuItem::display() { @@ -125,15 +125,15 @@ void MenuItem::display() { _flx2 = _left + _width; _fly = 15 + _optionNum * 10; _activeNow = true; - _menu->_menuActive = true; + _dropdown->_menuActive = true; - _menu->_vm->_graphics->drawMenuItem((_flx1 + 1) * 8, 12, (_flx2 + 1) * 8, _fly); + _dropdown->_vm->_graphics->drawMenuItem((_flx1 + 1) * 8, 12, (_flx2 + 1) * 8, _fly); displayOption(0, true); for (int y = 1; y < _optionNum; y++) displayOption(y, false); - _menu->_vm->_currentMouse = 177; + _dropdown->_vm->_currentMouse = 177; CursorMan.showMouse(true); // 4 = fletch } @@ -141,12 +141,12 @@ void MenuItem::display() { void MenuItem::wipe() { CursorMan.showMouse(false); - _menu->drawMenuText(_menu->_menuBar._menuItems[_menu->_activeMenuItem._activeNum]._xpos, 1, - _menu->_menuBar._menuItems[_menu->_activeMenuItem._activeNum]._trigger, - _menu->_menuBar._menuItems[_menu->_activeMenuItem._activeNum]._title, true, false); + _dropdown->drawMenuText(_dropdown->_menuBar._menuItems[_dropdown->_activeMenuItem._activeNum]._xpos, 1, + _dropdown->_menuBar._menuItems[_dropdown->_activeMenuItem._activeNum]._trigger, + _dropdown->_menuBar._menuItems[_dropdown->_activeMenuItem._activeNum]._title, true, false); _activeNow = false; - _menu->_menuActive = false; + _dropdown->_menuActive = false; _firstlix = false; CursorMan.showMouse(true); @@ -191,7 +191,7 @@ void MenuItem::select(byte which) { if (_choiceNum > _optionNum) _choiceNum = 0; // Off the top, I suppose. - (_menu->*_menu->_menuBar._menuItems[_activeNum]._chooseFunc)(); + (_dropdown->*_dropdown->_menuBar._menuItems[_activeNum]._chooseFunc)(); } void MenuItem::parseKey(char c) { @@ -204,29 +204,34 @@ void MenuItem::parseKey(char c) { } } if (!found) - _menu->_vm->_sound->blip(); + _dropdown->_vm->_sound->blip(); +} + +MenuBar::MenuBar() { + _menuNum = 0; + _dropdown = nullptr; } -void MenuBar::init(Menu *menu) { - _menu = menu; +void MenuBar::init(DropDownMenu *menu) { + _dropdown = menu; _menuNum = 0; } void MenuBar::createMenuItem(char trig, Common::String title, char altTrig, MenuFunc setupFunc, MenuFunc chooseFunc) { - _menuItems[_menuNum].init(trig, altTrig, title, _menuNum, setupFunc, chooseFunc, _menu); + _menuItems[_menuNum].init(trig, altTrig, title, _menuNum, setupFunc, chooseFunc, _dropdown); _menuNum++; } void MenuBar::draw() { - _menu->_vm->_graphics->drawMenuBar(kMenuBackgroundColor); + _dropdown->_vm->_graphics->drawMenuBar(kMenuBackgroundColor); - byte savecp = _menu->_vm->_cp; - _menu->_vm->_cp = 3; + byte savecp = _dropdown->_vm->_cp; + _dropdown->_vm->_cp = 3; for (int i = 0; i < _menuNum; i++) _menuItems[i].draw(); - _menu->_vm->_cp = savecp; + _dropdown->_vm->_cp = savecp; } void MenuBar::parseAltTrigger(char c) { @@ -239,13 +244,13 @@ void MenuBar::parseAltTrigger(char c) { } void MenuBar::setupMenuItem(byte which) { - if (_menu->_activeMenuItem._activeNow) { - _menu->_activeMenuItem.wipe(); // Get rid of menu. - if (_menu->_activeMenuItem._activeNum == _menuItems[which]._position) + if (_dropdown->_activeMenuItem._activeNow) { + _dropdown->_activeMenuItem.wipe(); // Get rid of menu. + if (_dropdown->_activeMenuItem._activeNum == _menuItems[which]._position) return; // Clicked on own highlight. } _menuItems[which].highlight(); - (_menu->*_menuItems[which]._setupFunc)(); + (_dropdown->*_menuItems[which]._setupFunc)(); } void MenuBar::chooseMenuItem(int16 x) { @@ -257,13 +262,16 @@ void MenuBar::chooseMenuItem(int16 x) { } } -Menu::Menu(AvalancheEngine *vm) { +DropDownMenu::DropDownMenu(AvalancheEngine *vm) { _vm = vm; _activeMenuItem.init(this); _menuBar.init(this); + + _menuActive = false; + _lastPerson = kPeopleNone; } -void Menu::findWhatYouCanDoWithIt() { +void DropDownMenu::findWhatYouCanDoWithIt() { switch (_vm->_thinks) { case kObjectWine: case kObjectPotion: @@ -291,7 +299,7 @@ void Menu::findWhatYouCanDoWithIt() { } } -void Menu::drawMenuText(int16 x, int16 y, char trigger, Common::String text, bool valid, bool highlighted) { +void DropDownMenu::drawMenuText(int16 x, int16 y, char trigger, Common::String text, bool valid, bool highlighted) { Color fontColor; Color backgroundColor; if (highlighted) { @@ -334,11 +342,11 @@ void Menu::drawMenuText(int16 x, int16 y, char trigger, Common::String text, boo _vm->_graphics->refreshScreen(); } -void Menu::bleep() { +void DropDownMenu::bleep() { _vm->_sound->playNote(177, 7); } -void Menu::parseKey(char r, char re) { +void DropDownMenu::parseKey(char r, char re) { #if 0 switch (r) { case 0: @@ -387,24 +395,24 @@ void Menu::parseKey(char r, char re) { warning("STUB: Dropdown::parseKey()"); // To be implemented properly later! Don't remove the comment above! } -Common::String Menu::selectGender(byte x) { +Common::String DropDownMenu::selectGender(byte x) { if (x < 175) return "im"; else return "er"; } -void Menu::setupMenuGame() { +void DropDownMenu::setupMenuGame() { _activeMenuItem.reset(); _activeMenuItem.setupOption("Help...", 'H', "f1", true); - _activeMenuItem.setupOption("Boss Key", 'B', "alt-B", false); - _activeMenuItem.setupOption("Untrash screen", 'U', "ctrl-f7", true); + _activeMenuItem.setupOption("Boss Key", 'B', "alt-B", true); + _activeMenuItem.setupOption("Untrash screen", 'U', "ctrl-f7", false); _activeMenuItem.setupOption("Score and rank", 'S', "f9", true); _activeMenuItem.setupOption("About Avvy...", 'A', "shift-f10", true); _activeMenuItem.display(); } -void Menu::setupMenuFile() { +void DropDownMenu::setupMenuFile() { _activeMenuItem.reset(); _activeMenuItem.setupOption("New game", 'N', "f4", true); _activeMenuItem.setupOption("Load...", 'L', "^f3", true); @@ -415,7 +423,7 @@ void Menu::setupMenuFile() { _activeMenuItem.display(); } -void Menu::setupMenuAction() { +void DropDownMenu::setupMenuAction() { _activeMenuItem.reset(); Common::String f5Does = _vm->f5Does(); @@ -441,7 +449,7 @@ void Menu::setupMenuAction() { _activeMenuItem.display(); } -void Menu::setupMenuPeople() { +void DropDownMenu::setupMenuPeople() { if (!people.empty()) people.clear(); @@ -457,7 +465,7 @@ void Menu::setupMenuPeople() { _activeMenuItem.display(); } -void Menu::setupMenuObjects() { +void DropDownMenu::setupMenuObjects() { _activeMenuItem.reset(); for (int i = 0; i < kObjectNum; i++) { if (_vm->_objects[i]) @@ -466,7 +474,7 @@ void Menu::setupMenuObjects() { _activeMenuItem.display(); } -void Menu::setupMenuWith() { +void DropDownMenu::setupMenuWith() { _activeMenuItem.reset(); if (_vm->_thinkThing) { @@ -523,7 +531,7 @@ void Menu::setupMenuWith() { _activeMenuItem.display(); } -void Menu::runMenuGame() { +void DropDownMenu::runMenuGame() { // Help, boss, untrash screen. switch (_activeMenuItem._choiceNum) { case 0: @@ -533,7 +541,7 @@ void Menu::runMenuGame() { _vm->callVerb(kVerbCodeBoss); break; case 2: - _vm->majorRedraw(); + _vm->_graphics->refreshScreen(); break; case 3: _vm->callVerb(kVerbCodeScore); @@ -544,7 +552,7 @@ void Menu::runMenuGame() { } } -void Menu::runMenuFile() { +void DropDownMenu::runMenuFile() { // New game, load, save, save as, DOS shell, about, quit. switch (_activeMenuItem._choiceNum) { case 0: @@ -571,7 +579,7 @@ void Menu::runMenuFile() { } } -void Menu::runMenuAction() { +void DropDownMenu::runMenuAction() { // Get up, pause game, open door, look, inventory, walk/run. switch (_activeMenuItem._choiceNum) { case 0: { @@ -608,16 +616,16 @@ void Menu::runMenuAction() { } } -void Menu::runMenuObjects() { +void DropDownMenu::runMenuObjects() { _vm->thinkAbout(_vm->_objectList[_activeMenuItem._choiceNum], AvalancheEngine::kThing); } -void Menu::runMenuPeople() { +void DropDownMenu::runMenuPeople() { _vm->thinkAbout(people[_activeMenuItem._choiceNum], AvalancheEngine::kPerson); _lastPerson = (People)people[_activeMenuItem._choiceNum]; } -void Menu::runMenuWith() { +void DropDownMenu::runMenuWith() { _vm->_parser->_thing = _vm->_thinks; if (_vm->_thinkThing) { @@ -656,21 +664,21 @@ void Menu::runMenuWith() { _vm->callVerb((VerbCode)(byte)_verbStr[_activeMenuItem._choiceNum]); } -void Menu::setup() { +void DropDownMenu::setup() { _menuBar.init(this); _activeMenuItem.init(this); - _menuBar.createMenuItem('F', "File", '!', &Avalanche::Menu::setupMenuFile, &Avalanche::Menu::runMenuFile); - _menuBar.createMenuItem('G', "Game", 34, &Avalanche::Menu::setupMenuGame, &Avalanche::Menu::runMenuGame); - _menuBar.createMenuItem('A', "Action", 30, &Avalanche::Menu::setupMenuAction, &Avalanche::Menu::runMenuAction); - _menuBar.createMenuItem('O', "Objects", 24, &Avalanche::Menu::setupMenuObjects, &Avalanche::Menu::runMenuObjects); - _menuBar.createMenuItem('P', "People", 25, &Avalanche::Menu::setupMenuPeople, &Avalanche::Menu::runMenuPeople); - _menuBar.createMenuItem('W', "With", 17, &Avalanche::Menu::setupMenuWith, &Avalanche::Menu::runMenuWith); + _menuBar.createMenuItem('F', "File", '!', &Avalanche::DropDownMenu::setupMenuFile, &Avalanche::DropDownMenu::runMenuFile); + _menuBar.createMenuItem('G', "Game", 34, &Avalanche::DropDownMenu::setupMenuGame, &Avalanche::DropDownMenu::runMenuGame); + _menuBar.createMenuItem('A', "Action", 30, &Avalanche::DropDownMenu::setupMenuAction, &Avalanche::DropDownMenu::runMenuAction); + _menuBar.createMenuItem('O', "Objects", 24, &Avalanche::DropDownMenu::setupMenuObjects, &Avalanche::DropDownMenu::runMenuObjects); + _menuBar.createMenuItem('P', "People", 25, &Avalanche::DropDownMenu::setupMenuPeople, &Avalanche::DropDownMenu::runMenuPeople); + _menuBar.createMenuItem('W', "With", 17, &Avalanche::DropDownMenu::setupMenuWith, &Avalanche::DropDownMenu::runMenuWith); _menuBar.draw(); } -void Menu::update() { // TODO: Optimize it ASAP!!! It really needs it... +void DropDownMenu::update() { _vm->_graphics->saveScreen(); Common::Point cursorPos = _vm->getMousePos(); @@ -761,7 +769,7 @@ void Menu::update() { // TODO: Optimize it ASAP!!! It really needs it... _vm->_graphics->removeBackup(); } -char Menu::getThingChar(byte which) { +char DropDownMenu::getThingChar(byte which) { static const char thingsChar[] = "WMBParCLguKeSnIohn"; // V=Vinegar char result; @@ -778,17 +786,19 @@ char Menu::getThingChar(byte which) { return result; } -byte Menu::getNameChar(People whose) { +byte DropDownMenu::getNameChar(People whose) { static const char ladChar[] = "ASCDMTRwLfgeIyPu"; static const char lassChar[] = "kG\0xB1o"; - if (whose < kPeopleArkata) + if (whose <= kPeopleJacques) return ladChar[whose - kPeopleAvalot]; - else + else if ((whose >= kPeopleArkata) && (whose <= kPeopleWisewoman)) return lassChar[whose - kPeopleArkata]; + else + error("getName() - Unexpected character id %d", (byte) whose); } -Common::String Menu::getThing(byte which) { +Common::String DropDownMenu::getThing(byte which) { static const char things[kObjectNum][20] = { "Wine", "Money-bag", "Bodkin", "Potion", "Chastity belt", "Crossbow bolt", "Crossbow", "Lute", "Pilgrim's badge", "Mushroom", "Key", @@ -820,15 +830,15 @@ Common::String Menu::getThing(byte which) { return result; } -bool Menu::isActive() { +bool DropDownMenu::isActive() { return _menuActive; } -void Menu::init() { +void DropDownMenu::init() { _menuActive = false; } -void Menu::resetVariables() { +void DropDownMenu::resetVariables() { _lastPerson = kPeoplePardon; } } // End of namespace Avalanche. diff --git a/engines/avalanche/menu.h b/engines/avalanche/dropdown.h index a7ec8bf2db..417b775c3d 100644 --- a/engines/avalanche/menu.h +++ b/engines/avalanche/dropdown.h @@ -8,12 +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. - + * * 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. @@ -27,17 +27,17 @@ /* Original name: DROPDOWN A customized version of Oopmenu (qv). */ -#ifndef AVALANCHE_MENU_H -#define AVALANCHE_MENU_H +#ifndef AVALANCHE_DROPDOWN_H +#define AVALANCHE_DROPDOWN_H #include "common/str.h" namespace Avalanche { class AvalancheEngine; -class Menu; +class DropDownMenu; -typedef void (Menu::*MenuFunc)(); +typedef void (DropDownMenu::*MenuFunc)(); static const Color kMenuBackgroundColor = kColorLightgray; static const Color kMenuBorderColor = kColorBlack; @@ -49,13 +49,13 @@ public: int16 _xpos, _xright; MenuFunc _setupFunc, _chooseFunc; - void init(char trig, char alTtrig, Common::String title, byte pos, MenuFunc setupFunc, MenuFunc chooseFunc, Menu *menu); + void init(char trig, char alTtrig, Common::String title, byte pos, MenuFunc setupFunc, MenuFunc chooseFunc, DropDownMenu *menu); void draw(); void highlight(); bool parseAltTrigger(char key); private: - Menu *_menu; + DropDownMenu *_dropdown; }; struct OptionType { @@ -75,7 +75,7 @@ public: byte _activeNum; // And if so, which is it? byte _choiceNum; // Your choice? - void init(Menu *menu); + void init(DropDownMenu *menu); void reset(); void setupOption(Common::String title, char trigger, Common::String shortcut, bool valid); void display(); @@ -88,7 +88,7 @@ private: byte _optionNum; byte _highlightNum; - Menu *_menu; + DropDownMenu *_dropdown; void displayOption(byte y, bool highlit); void moveHighlight(int8 inc); @@ -102,20 +102,21 @@ public: HeadType _menuItems[8]; byte _menuNum; - void init(Menu *menu); + MenuBar(); + void init(DropDownMenu *menu); void createMenuItem(char trig, Common::String title, char altTrig, MenuFunc setupFunc, MenuFunc chooseFunc); void draw(); void chooseMenuItem(int16 x); private: - Menu *_menu; + DropDownMenu *_dropdown; void setupMenuItem(byte which); // CHECKME: Useless function void parseAltTrigger(char c); }; -class Menu { +class DropDownMenu { public: friend class HeadType; friend class MenuItem; @@ -124,7 +125,7 @@ public: MenuItem _activeMenuItem; MenuBar _menuBar; - Menu(AvalancheEngine *vm); + DropDownMenu(AvalancheEngine *vm); void update(); void setup(); // Standard menu bar. @@ -178,4 +179,4 @@ private: } // End of namespace Avalanche. -#endif // AVALANCHE_MENU_H +#endif // AVALANCHE_DROPDOWN_H diff --git a/engines/avalanche/enums.h b/engines/avalanche/enums.h index 604c62de84..0ba39321bc 100644 --- a/engines/avalanche/enums.h +++ b/engines/avalanche/enums.h @@ -8,12 +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. - + * * 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 + * 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. @@ -29,6 +29,11 @@ #define AVALANCHE_ENUMS_H namespace Avalanche { + +enum MonsterType { kMonsterTypeGhost, kMonsterTypeGlerk }; + +enum Flavour { kFlavourEga, kFlavourBgi, kFlavourNatural, kFlavourTwo, kFlavourOne }; + enum Color { kColorBlack = 0, kColorBlue, kColorGreen, kColorCyan, kColorRed, kColorMagenta = 5, kColorBrown, kColorLightgray, kColorDarkgray, kColorLightblue, @@ -81,13 +86,13 @@ enum VerbCode { kVerbCodeRestart = 31, kVerbCodeEat = 32, kVerbCodeListen = 33, kVerbCodeBuy = 34, kVerbCodeAttack = 35, kVerbCodePasswd = 36, kVerbCodeDir = 37, kVerbCodeDie = 38, kVerbCodeScore = 39, kVerbCodePut = 40, kVerbCodeKiss = 41, kVerbCodeClimb = 42, kVerbCodeJump = 43, kVerbCodeHiscores = 44, kVerbCodeWake = 45, - kVerbCodeHello = 46, kVerbCodeThanks = 47, + kVerbCodeHello = 46, kVerbCodeThanks = 47, kVerbCodeSmartAlec = 249, kVerbCodeExpletive = 253, kVerbCodePardon = 254 }; enum MouseCursor { - kCurUpArrow = 0, kCurScrewDriver = 1, kCurRightArrow = 2, kCurFletch = 3, kCurWait = 4, kCurHand = 5, - kCurCrosshair = 6, kCurIBeam = 7 + kCurUpArrow = 0, kCurScrewDriver = 1, kCurRightArrow = 2, kCurFletch = 3, kCurWait = 4, kCurHand = 5, + kCurCrosshair = 6, kCurIBeam = 7, kCurHelp = 8 }; // Magic/portal constants: @@ -127,7 +132,6 @@ static const int16 kScreenHeight = 200; static const int16 kWalk = 3; static const int16 kRun = 5; - } // End of namespace Avalanche #endif // AVALANCHE_ENUMS_H diff --git a/engines/avalanche/ghostroom.cpp b/engines/avalanche/ghostroom.cpp new file mode 100644 index 0000000000..047a3670c2 --- /dev/null +++ b/engines/avalanche/ghostroom.cpp @@ -0,0 +1,398 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* +* This code is based on the original source code of Lord Avalot d'Argent version 1.3. +* Copyright (c) 1994-1995 Mike: Mark and Thomas Thurman. +*/ + +#include "avalanche/avalanche.h" +#include "avalanche/ghostroom.h" + +#include "common/random.h" + +namespace Avalanche { + +const int8 GhostRoom::kAdjustment[5] = { 7, 0, 7, 7, 7 }; +const byte GhostRoom::kWaveOrder[5] = { 4, 0, 1, 2, 3 }; +const byte GhostRoom::kGlerkFade[26] = { 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1, 0 }; +const byte GhostRoom::kGreldetFade[18] = { 0, 1, 2, 3, 4, 5, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0 }; + +GhostRoom::GhostRoom(AvalancheEngine *vm) { + _vm = vm; + + _glerkStage = 0; + _batX = 0; + _batY = 0; + _batCount = 0; + _aarghCount = 0; + _greldetX = _greldetY = 0; + _greldetCount = 0; + _redGreldet = false; + _wasLoaded = false; + + _ghost = nullptr; + _glerk = nullptr; +} + +GhostRoom::~GhostRoom() { + for (int i = 0; i < 2; i++) + _eyes[i].free(); + + _exclamation.free(); + + for (int i = 0; i < 3; i++) + _bat[i].free(); + + for (int i = 0; i < 6; i++) + _aargh[i].free(); + + for (int i = 0; i < 5; i++) + _greenEyes[i].free(); + + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 6; j++) + _greldet[j][i].free(); + } + + if (_wasLoaded) { + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 2; j++) { + for (int y = 0; y < 66; y++) { + delete[] _ghost[i][j][y]; + } + delete[] _ghost[i][j]; + } + delete[] _ghost[i]; + } + delete[] _ghost; + + for (int i = 0; i < 6; i++) { + for (int j = 0; j < 4; j++) { + for (int y = 0; y < 35; y++) { + delete[] _glerk[i][j][y]; + } + delete[] _glerk[i][j]; + } + delete[] _glerk[i]; + } + delete[] _glerk; + } +} + +void GhostRoom::wait(uint16 howLong) { + for (int i = 0; i < howLong; i++) { + Common::Event event; + _vm->getEvent(event); + if (event.type == Common::EVENT_KEYDOWN) + _vm->_sound->playNote(6177, 1); + _vm->_system->delayMillis(1); + } +} + +void GhostRoom::doBat() { + _batCount++; + + int8 dx = 0; + int8 iy = 0; + byte batImage = 0; + if ((_batCount % 2) == 1) { + if ((1 <= _batCount) && (_batCount <= 90)) { + dx = 2; + iy = 1; + batImage = 0; + } else if ((91 <= _batCount) && (_batCount <= 240)) { + dx = 1; + iy = 1; + batImage = 1; + } else if((241 <= _batCount) && (_batCount <= 260)) { + dx = 1; + iy = 4; + batImage = 2; + } + + if ((_batCount == 91) || (_batCount == 241)) // When the bat changes, blank out the old one. + _vm->_graphics->drawFilledRectangle(Common::Rect(_batX + _bat[batImage].w, _batY, _batX + _bat[batImage - 1].w, _batY + _bat[batImage - 1].h), kColorBlack); + + _vm->_graphics->drawFilledRectangle(Common::Rect(_batX, _batY, _batX + _bat[batImage].w, _batY + iy), kColorBlack); + _vm->_graphics->drawFilledRectangle(Common::Rect(_batX + _bat[batImage].w - dx, _batY, _batX + _bat[batImage].w, _batY + _bat[batImage].h), kColorBlack); + + _batX -= dx; + _batY++; + _vm->_graphics->ghostDrawPicture(_bat[batImage], _batX, _batY); + } +} + +void GhostRoom::bigGreenEyes(byte how) { + _vm->_graphics->ghostDrawPicture(_greenEyes[how], 330, 103); + _vm->_graphics->ghostDrawPicture(_greenEyes[how], 376, 103); + _vm->_graphics->refreshScreen(); +} + +ChunkBlock GhostRoom::readChunkBlock(Common::File &file) { + ChunkBlock cb; + cb._flavour = (Flavour)file.readByte(); + cb._x = file.readSint16LE(); + cb._y = file.readSint16LE(); + cb._width = file.readSint16LE(); + cb._height = file.readSint16LE(); + cb._size = file.readSint32LE(); + return cb; +} + +void GhostRoom::loadPictures() { + Common::File file; + + if (!file.open("spooky.avd")) + error("AVALANCHE: GhostRoom: File not found: spooky.avd"); + + file.seek(44); + + // Initializing ghost's array. + _ghost = new byte***[5]; + for (int i = 0; i < 5; i++) { + _ghost[i] = new byte**[2]; + for (int j = 0; j < 2; j++) { + _ghost[i][j] = new byte*[66]; + for (int y = 0; y < 66; y++) { + _ghost[i][j][y] = new byte[26]; + for (int x = 0; x < 26; x++) + _ghost[i][j][y][x] = 0; + } + } + } + + // Read in the pictures of the ghost. + for (int i = 0; i < 5; i++) { + ChunkBlock cb = readChunkBlock(file); + for (int j = 0; j < 2; j++) { + for (int y = 0; y <= cb._height; y++) + file.read(_ghost[i][j][y], cb._width / 8); + } + } + + for (int i = 0; i < 2; i++) + _eyes[i] = _vm->_graphics->ghostLoadPicture(file, dummyCoord); + + _exclamation = _vm->_graphics->ghostLoadPicture(file, dummyCoord); + + // Actually this function not just loads, but also draws the images, but they are part of the background + // and they are need to be drawn only once. + _vm->_graphics->ghostDrawBackgroundItems(file); + + for (int i = 0; i < 3; i++) + _bat[i] = _vm->_graphics->ghostLoadPicture(file, dummyCoord); + + // Initializing glerk's array. + _glerk = new byte***[6]; + for (int i = 0; i < 6; i++) { + _glerk[i] = new byte**[4]; + for (int j = 0; j < 4; j++) { + _glerk[i][j] = new byte*[35]; + for (int y = 0; y < 35; y++) { + _glerk[i][j][y] = new byte[9]; + for (int x = 0; x < 9; x++) + _glerk[i][j][y][x] = 0; + } + } + } + + // Read in the pictures of the "glerk". + for (int i = 0; i < 6; i++) { + ChunkBlock cb = readChunkBlock(file); + for (int j = 0; j < 4; j++) { + for (int y = 0; y <= cb._height; y++) + file.read(_glerk[i][j][y], cb._width / 8); + } + } + + for (int i = 0; i < 6; i++) + _aargh[i] = _vm->_graphics->ghostLoadPicture(file, _aarghWhere[i]); + + for (int i = 0; i < 5; i++) + _greenEyes[i] = _vm->_graphics->ghostLoadPicture(file, dummyCoord); + + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 6; j++) + _greldet[j][i] = _vm->_graphics->ghostLoadPicture(file, dummyCoord); + } + + file.close(); +} + +void GhostRoom::run() { + CursorMan.showMouse(false); + _vm->_graphics->saveScreen(); + _vm->fadeOut(); + _vm->_graphics->blackOutScreen(); + _vm->fadeIn(); + + // Only load the pictures if it's our first time walking into the room. + // After that we simply use the already loaded images. + if (!_wasLoaded) { + loadPictures(); + _wasLoaded = true; + } + + // Avvy walks over: + _glerkStage = 0; + _batX = 277; + _batY = 40; + _batCount = 0; + + for (int x = 500; x >= 217; x--) { + // The floating eyeballs: + int xBound = x % 30; + if ((22 <= xBound) && (xBound <= 27)) { + if (xBound == 27) + _vm->_graphics->drawFilledRectangle(Common::Rect(x, 135, x + 17, 137), kColorBlack); + _vm->_graphics->ghostDrawPicture(_eyes[0], x, 136); + _vm->_graphics->drawDot(x + 16, 137, kColorBlack); + } else { + if (xBound == 21) + _vm->_graphics->drawFilledRectangle(Common::Rect(x, 137, x + 18, 139), kColorBlack); + _vm->_graphics->ghostDrawPicture(_eyes[0], x, 135); + _vm->_graphics->drawDot(x + 16, 136, kColorBlack); // Eyes would leave a trail 1 pixel high behind them. + } + + // Plot the Glerk: + if ((x % 10) == 0) { + if (_glerkStage > 25) + break; + + _vm->_graphics->ghostDrawMonster(_glerk[kGlerkFade[_glerkStage]], 456, 14, kMonsterTypeGlerk); + _glerkStage++; + } + + doBat(); + + _vm->_graphics->refreshScreen(); + + wait(15); + } + + // Blank out the Glerk's space. + _vm->_graphics->drawFilledRectangle(Common::Rect(456, 14, 531, 51), kColorBlack); + _vm->_graphics->refreshScreen(); + + + // Here comes the descending ghost: + for (int y = -64; y <= 103; y++) { + _vm->_graphics->ghostDrawMonster(_ghost[1 + (abs(y / 7) % 2) * 3], 0, y, kMonsterTypeGhost); + if (y > 0) + _vm->_graphics->drawFilledRectangle(Common::Rect(0, y - 1, 26 * 8 + 1, y + 1), kColorBlack); + _vm->_graphics->refreshScreen(); + + wait(27); + } + + // Then it waves: + _aarghCount = -15; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 5; j++) { + _vm->_graphics->drawFilledRectangle(Common::Rect(0, 96, 26 * 8, 170), kColorBlack); + _vm->_graphics->ghostDrawMonster(_ghost[kWaveOrder[j]], 0, 96 + kAdjustment[j], kMonsterTypeGhost); + + _aarghCount++; + + if (_aarghCount >= 0) { + for (int k = 0; k <= _aarghCount; k++) + _vm->_graphics->ghostDrawPicture(_aargh[k], _aarghWhere[k].x, _aarghWhere[k].y); + } + + _vm->_graphics->refreshScreen(); + + wait(177); + } + } + + // The exclamation mark appears: + _vm->_graphics->ghostDrawPicture(_exclamation, 246, 127); + _vm->_graphics->refreshScreen(); + wait(777); + + // Erase "aargh": + _vm->_graphics->drawFilledRectangle(Common::Rect(172, 78, 348, 112), kColorBlack); + _vm->_graphics->refreshScreen(); + + for (int i = 4; i >= 0; i--) { + wait(377); + bigGreenEyes(i); + } + + // Erase the exclamation mark: + _vm->_graphics->drawFilledRectangle(Common::Rect(246, 127, 252, 134), kColorBlack); + _vm->_graphics->refreshScreen(); + + // Avvy hurries back: + _glerkStage = 0; + _greldetCount = 18; + _redGreldet = false; + + for (int x = 217; x <= 479; x++) { + // The floating eyeballs again: + int xBound = x % 30; + if ((22 <= xBound) && (xBound <= 27)) { + if (xBound == 22) + _vm->_graphics->drawFilledRectangle(Common::Rect(x + 22, 134, x + 39, 138), kColorBlack); + _vm->_graphics->ghostDrawPicture(_eyes[1], x + 23, 136); + _vm->_graphics->drawDot(x + 22, 137, kColorBlack); + } else { + if (xBound == 28) + _vm->_graphics->drawFilledRectangle(Common::Rect(x + 22, 135, x + 39, 139), kColorBlack); + _vm->_graphics->ghostDrawPicture(_eyes[1], x + 23, 135); + _vm->_graphics->drawDot(x + 22, 136, kColorBlack); // Eyes would leave a trail 1 pixel high behind them. + } + + // Plot the Green Eyes: + if ((x % 53) == 5) { + bigGreenEyes(_glerkStage); + _glerkStage++; + } + + // Plot the Greldet: + if (_greldetCount == 18) { + _greldetX = _vm->_rnd->getRandomNumber(599); + _greldetY = _vm->_rnd->getRandomNumber(79); + _greldetCount = 0; + _redGreldet = !_redGreldet; + } + + _vm->_graphics->ghostDrawPicture(_greldet[kGreldetFade[_greldetCount]][_redGreldet], _greldetX, _greldetY); + _greldetCount++; + + _vm->_graphics->refreshScreen(); + + wait(10); + } + + CursorMan.showMouse(true); + + _vm->fadeOut(); + _vm->_graphics->restoreScreen(); + _vm->_graphics->removeBackup(); + _vm->_animation->animLink(); + _vm->fadeIn(); +} + +} // End of namespace Avalanche diff --git a/engines/avalanche/ghostroom.h b/engines/avalanche/ghostroom.h new file mode 100644 index 0000000000..ca1e8ac806 --- /dev/null +++ b/engines/avalanche/ghostroom.h @@ -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. + * + */ + +/* +* This code is based on the original source code of Lord Avalot d'Argent version 1.3. +* Copyright (c) 1994-1995 Mike: Mark and Thomas Thurman. +*/ + +#ifndef AVALANCHE_GHOSTROOM_H +#define AVALANCHE_GHOSTROOM_H + +#include "common/scummsys.h" +#include "graphics/surface.h" + +namespace Avalanche { +class AvalancheEngine; + +struct ChunkBlock { + Flavour _flavour; + int16 _x, _y; + int16 _width, _height; + int32 _size; +}; + +class GhostRoom { +public: + GhostRoom(AvalancheEngine *vm); + ~GhostRoom(); + + void run(); + ChunkBlock readChunkBlock(Common::File &file); + +private: + AvalancheEngine *_vm; + + static const int8 kAdjustment[5]; + static const byte kWaveOrder[5]; + static const byte kGlerkFade[26]; + static const byte kGreldetFade[18]; + + Common::Point dummyCoord; + byte ****_ghost;// [5][2][66][26] + Graphics::Surface _eyes[2]; + Graphics::Surface _exclamation; + Graphics::Surface _bat[3]; + byte ****_glerk; // [6][4][35][9] + Graphics::Surface _aargh[6]; + Common::Point _aarghWhere[6]; + Graphics::Surface _greenEyes[5]; + Graphics::Surface _greldet[6][2]; + + int16 _batX, _batY; + uint16 _batCount; + byte _glerkStage; + int8 _aarghCount; + int16 _greldetX, _greldetY; + byte _greldetCount; + bool _redGreldet; + bool _wasLoaded; + + void loadPictures(); + void wait(uint16 howLong); + void doBat(); + void bigGreenEyes(byte how); +}; + +} // End of namespace Avalanche + +#endif // AVALANCHE_GHOSTROOM_H diff --git a/engines/avalanche/graphics.cpp b/engines/avalanche/graphics.cpp index 25b01d65f3..60c23594d3 100644 --- a/engines/avalanche/graphics.cpp +++ b/engines/avalanche/graphics.cpp @@ -8,12 +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. - + * * 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 + * 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. @@ -36,19 +36,20 @@ namespace Avalanche { const byte GraphicManager::kEgaPaletteIndex[16] = {0, 1, 2, 3, 4, 5, 20, 7, 56, 57, 58, 59, 60, 61, 62, 63}; const MouseHotspotType GraphicManager::kMouseHotSpots[9] = { - {8,0}, // 0 - up-arrow - {0,0}, // 1 - screwdriver + {8,0}, // 0 - up-arrow + {0,0}, // 1 - screwdriver {15,6}, // 2 - right-arrow - {0,0}, // 3 - fletch - {8,7}, // 4 - hourglass - {4,0}, // 5 - TTHand - {8,5}, // 6 - Mark's crosshairs - {8,7}, // 7 - I-beam - {0,0} // 8 - question mark + {0,0}, // 3 - fletch + {8,7}, // 4 - hourglass + {4,0}, // 5 - TTHand + {8,5}, // 6 - Mark's crosshairs + {8,7}, // 7 - I-beam + {0,0} // 8 - question mark }; GraphicManager::GraphicManager(AvalancheEngine *vm) { _vm = vm; + setDialogColor(kColorBlack, kColorWhite); } GraphicManager::~GraphicManager() { @@ -126,7 +127,6 @@ void GraphicManager::loadMouse(byte which) { cursor.create(16, 32, Graphics::PixelFormat::createFormatCLUT8()); cursor.fillRect(Common::Rect(0, 0, 16, 32), 255); - // The AND mask. f.seek(kMouseSize * 2 * which + 134); @@ -198,7 +198,7 @@ void GraphicManager::drawToolbar() { Common::Point GraphicManager::drawArc(Graphics::Surface &surface, int16 x, int16 y, int16 stAngle, int16 endAngle, uint16 radius, Color color) { Common::Point endPoint; - const float convfac = M_PI / 180.0; + const float convfac = (float)M_PI / 180.0f; int32 xRadius = radius; int32 yRadius = radius * kScreenWidth / (8 * kScreenHeight); // Just don't ask why... @@ -231,7 +231,7 @@ Common::Point GraphicManager::drawArc(Graphics::Surface &surface, int16 x, int16 uint16 numOfPixels = (uint16)floor(sqrt(3.0) * sqrt(pow(double(xRadius), 2) + pow(double(yRadius), 2)) + 0.5); // Calculate the angle precision required. - float delta = 90.0 / numOfPixels; + float delta = 90.0f / numOfPixels; // Always just go over the first 90 degrees. Could be optimized a // bit if startAngle and endAngle lie in the same quadrant, left as an @@ -281,6 +281,14 @@ Common::Point GraphicManager::drawArc(Graphics::Surface &surface, int16 x, int16 return endPoint; } +void GraphicManager::drawDot(int x, int y, Color color) { + *(byte *)_surface.getBasePtr(x, y) = color; +} + +void GraphicManager::drawLine(int x1, int y1, int x2, int y2, int penX, int penY, Color color) { + _surface.drawThickLine(x1, y1, x2, y2, penX, penY, color); +} + Common::Point GraphicManager::drawScreenArc(int16 x, int16 y, int16 stAngle, int16 endAngle, uint16 radius, Color color) { return drawArc(_surface, x, y, stAngle, endAngle, radius, color); } @@ -344,6 +352,25 @@ void GraphicManager::drawNormalText(const Common::String text, FontType font, by drawText(_surface, text, font, fontHeight, x, y, color); } +/** + * Draws text double the size of the normal. + */ +void GraphicManager::drawBigText(Graphics::Surface &surface, const Common::String text, FontType font, byte fontHeight, int16 x, int16 y, Color color) { + for (uint i = 0; i < text.size(); i++) { + for (int j = 0; j < fontHeight; j++) { + byte pixel = font[(byte)text[i]][j]; + byte pixelBit = 0; + for (int bit = 0; bit < 16; bit++) { + if ((bit % 2) == 0) + pixelBit = (pixel >> (bit / 2)) & 1; + for (int k = 0; k < 2; k++) + if (pixelBit) + *(byte *)surface.getBasePtr(x + i * 16 + 16 - bit, y + j * 2 + k) = color; + } + } + } +} + void GraphicManager::drawScrollText(const Common::String text, FontType font, byte fontHeight, int16 x, int16 y, Color color) { drawText(_scrolls, text, font, fontHeight, x, y, color); } @@ -451,6 +478,424 @@ void GraphicManager::drawDebugLines() { } } +void GraphicManager::drawRectangle(Common::Rect rect, Color color) { + _surface.frameRect(rect, color); +} + +void GraphicManager::drawFilledRectangle(Common::Rect rect, Color color) { + _surface.fillRect(rect, color); +} + +void GraphicManager::blackOutScreen() { + _vm->_graphics->drawFilledRectangle(Common::Rect(0, 0, 640, 200), kColorBlack); +} + +void GraphicManager::nimLoad() { + Common::File file; + Common::String filename = "nim.avd"; + + if (!file.open(filename)) + error("AVALANCHE: Scrolls: File not found: %s", filename.c_str()); + + file.seek(41); + + _nimStone = loadPictureSign(file, 7, 23); + for (int i = 0; i < 3; i++) + _nimInitials[i] = loadPictureSign(file, 7, 23); + _nimLogo = loadPictureSign(file, 30, 37); + + file.close(); +} + +void GraphicManager::nimDrawStone(int x, int y) { + drawPicture(_surface, _nimStone, x, y); +} + +void GraphicManager::nimDrawInitials() { + for (int i = 0; i < 3; i++) + drawPicture(_surface, _nimInitials[i], 0, 75 + i * 35); +} + +void GraphicManager::nimDrawLogo() { + drawPicture(_surface, _nimLogo, 392, 5); +} + +void GraphicManager::nimFree() { + _nimStone.free(); + for (int i = 0; i < 3; i++) + _nimInitials[i].free(); + _nimLogo.free(); +} + +void GraphicManager::ghostDrawMonster(byte ***picture, uint16 destX, int16 destY, MonsterType type) { + uint16 height = 0; + uint16 width = 0; + // Only for the Ghost: + const byte kPlaneToUse[4] = { 0, 0, 0, 1 }; + int yStart = 0; + + // Constants from the original code: + switch (type) { + case kMonsterTypeGhost: + height = 66; + width = 208; // 26 * 8 + + // We have to mess around with the coords and the sizes since + // the ghost isn't always placed fully on the screen. + if (destY < 0) { + yStart = abs(destY); + height -= yStart; + destY = 0; + } + break; + case kMonsterTypeGlerk: + height = 35; + width = 72; // 9 * 8 + break; + default: + break; + } + + Graphics::Surface monsterPicture; + monsterPicture.create(width, height, Graphics::PixelFormat::createFormatCLUT8()); + + for (int y = 0; y < height; y++) { + for (int plane = 0; plane < 4; plane++) { + for (uint16 x = 0; x < width / 8; x++) { + byte pixel = 0; + + switch (type) { + case kMonsterTypeGhost: + pixel = picture[kPlaneToUse[plane]][y + yStart][x]; + break; + case kMonsterTypeGlerk: + pixel = picture[plane][y][x]; + break; + default: + break; + } + + for (int bit = 0; bit < 8; bit++) { + byte pixelBit = (pixel >> bit) & 1; + *(byte *)monsterPicture.getBasePtr(x * 8 + 7 - bit, y) += (pixelBit << plane); + } + } + } + } + + drawPicture(_surface, monsterPicture, destX, destY); + + monsterPicture.free(); +} + +/** + * With the use of the second argument, it replaces get_meg_aargh as well. + * @remarks Originally called 'get_me' and was located in Ghostroom. + */ +Graphics::Surface GraphicManager::ghostLoadPicture(Common::File &file, Common::Point &coord) { + ChunkBlock cb = _vm->_ghostroom->readChunkBlock(file); + + coord.x = cb._x; + coord.y = cb._y; + + Graphics::Surface picture = loadPictureGraphic(file); + + skipDifference(cb._size, picture, file); + + return picture; +} + +void GraphicManager::ghostDrawPicture(const Graphics::Surface &picture, uint16 destX, uint16 destY) { + drawPicture(_surface, picture, destX, destY); +} + +/** + * Loads and puts 3 images (in this order: cobweb, Mark's signature, open door) into the background at the beginning of the ghostroom scene. + * @remarks Originally called 'plain_grab' and was located in Ghostroom. It was originally called 3 times. I unified these in one function, used a for cycle. + */ +void GraphicManager::ghostDrawBackgroundItems(Common::File &file) { + for (int num = 0; num < 3; num++) { + ChunkBlock cb = _vm->_ghostroom->readChunkBlock(file); + + int width = cb._width; + int height = cb._height + 1; + + Graphics::Surface picture; + picture.create(width, height, Graphics::PixelFormat::createFormatCLUT8()); + + // Load the picture according to it's type. + switch (cb._flavour) { + case kFlavourOne: // There is only one plane. + for (uint16 y = 0; y < height; y++) { + for (uint16 x = 0; x < width; x += 8) { + byte pixel = file.readByte(); + for (int i = 0; i < 8; i++) { + byte pixelBit = (pixel >> i) & 1; + *(byte *)picture.getBasePtr(x + 7 - i, y) = (pixelBit << 3); + } + } + } + break; + case kFlavourEga: + picture = loadPictureRaw(file, width, height); + break; + default: + break; + } + + drawPicture(_surface, picture, cb._x, cb._y); + + picture.free(); + } + refreshScreen(); +} + +/** +* @remarks Originally called 'plot_button' +*/ +void GraphicManager::helpDrawButton(int y, byte which) { + if (y > 200) { + _vm->_graphics->setBackgroundColor(kColorGreen); + _vm->_system->delayMillis(10); + _vm->_graphics->setBackgroundColor(kColorBlack); + return; + } + + Common::File file; + + if (!file.open("buttons.avd")) + error("AVALANCHE: Help: File not found: buttons.avd"); + + file.seek(which * 930); // 930 is the size of one button. + + Graphics::Surface button = loadPictureGraphic(file); + + int x = 0; + if (y == -177) { + x = 229; + y = 5; + } + else + x = 470; + + _vm->_graphics->drawPicture(_surface, button, x, y); + + button.free(); + file.close(); +} + +/** + * @remarks Originally called 'light' + */ +void GraphicManager::helpDrawHighlight(byte which, Color color) { + if (which == 177) // Dummy value for "no button at all". + return; + + which &= 31; + drawRectangle(Common::Rect(466, 38 + which * 27, 556, 63 + which * 27), color); +} + +void GraphicManager::helpDrawBigText(const Common::String text, int16 x, int16 y, Color color) { + drawBigText(_surface, text, _vm->_font, 8, x, y, color); +} + +/** + * @remarks Originally called 'titles' + */ +void GraphicManager::seuDrawTitle() { + Common::File file; + + if (!file.open("shoot1.avd")) + error("AVALANCHE: ShootEmUp: File not found: shoot1.avd"); + + const uint16 width = 320; + const uint16 height = 200; + + Graphics::Surface picture = loadPictureRaw(file, width, height); + + Graphics::Surface doubledPicture; + doubledPicture.create(width * 2, height, Graphics::PixelFormat::createFormatCLUT8()); + + // These cycles are for doubling the picture's width. + for (int x = (width * 2) - 2 ; x >= 0; x -= 2) { + for (int y = 0; y < height; y++) { + *(byte *)doubledPicture.getBasePtr(x, y) = *(byte *)doubledPicture.getBasePtr(x + 1, y) = *(byte *)picture.getBasePtr(x / 2, y); + } + } + + drawPicture(_surface, doubledPicture, 0, 0); + refreshScreen(); + + picture.free(); + doubledPicture.free(); + + file.close(); +} + +void GraphicManager::seuLoad() { + Common::File file; + + if (!file.open("notts.avd")) + error("AVALANCHE: ShootEmUp: File not found: notts.avd"); + + for (int i = 0; i < 99; i++) { + int size = file.readUint16LE(); + _seuPictures[i] = loadPictureGraphic(file); + skipDifference(size, _seuPictures[i], file); + } + + file.close(); +} + +void GraphicManager::seuFree() { + for (int i = 0; i < 99; i++) + _seuPictures[i].free(); +} + +/** + * @remarks Originally called 'display' and it also replaces 'display_const' + */ +void GraphicManager::seuDrawPicture(int x, int y, byte which) { + drawPicture(_surface, _seuPictures[which], x, y); +} + +/** + * @remarks Originally called 'cameo_display' + */ +void GraphicManager::seuDrawCameo(int destX, int destY, byte w1, byte w2) { + // First we make the pixels of the previous sprite (cameo) blank: + uint16 maxX = _seuPictures[w2].w; + uint16 maxY = _seuPictures[w2].h; + + if (destX + maxX > _surface.w) + maxX = _surface.w - destX; + + if (destY + maxY > _surface.h) + maxY = _surface.h - destY; + + for (uint16 y = 0; y < maxY; y++) { + for (uint16 x = 0; x < maxX; x++) { + if (*(const byte *)_seuPictures[w2].getBasePtr(x, y) != 0) + *(byte *)_surface.getBasePtr(x + destX, y + destY) = 0; + } + } + + // Then we draw the desired sprite: + drawPicture(_surface, _seuPictures[w1], destX, destY); +} + +uint16 GraphicManager::seuGetPicWidth(int which) { + return _seuPictures[which].w; +} + +uint16 GraphicManager::seuGetPicHeight(int which) { + return _seuPictures[which].h; +} + +void GraphicManager::menuRefreshScreen() { + g_system->copyRectToScreen(_menu.getPixels(), _menu.pitch, 0, 0, kScreenWidth, kMenuScreenHeight); + g_system->updateScreen(); +} + +void GraphicManager::menuInitialize() { + initGraphics(kScreenWidth, kMenuScreenHeight, true); + _menu.create(kScreenWidth, kMenuScreenHeight, Graphics::PixelFormat::createFormatCLUT8()); +} + +void GraphicManager::menuFree() { + _menu.free(); +} + +void GraphicManager::menuRestoreScreen() { + initGraphics(kScreenWidth, 2 * kScreenHeight, true); +} + +void GraphicManager::menuLoadPictures() { + _menu.fillRect(Common::Rect(0, 0, kScreenWidth, kMenuScreenHeight), kColorBlack); + + Common::File file; + + if (!file.open("menu.avd")) + error("AVALANCHE: MainMenu: File not found: menu.avd"); + + int height = 33; + int width = 9 * 8; + + for (int plane = 0; plane < 4; plane++) { + // The icons themselves: + int n = 0; + for (uint16 y = 70; y < 70 + height * 6; y++) { + for (uint16 x = 48; x < 48 + width; x += 8) { + if (n < 1773) { // Magic value deciphered from the original code. + byte pixel = file.readByte(); + n++; + for (int i = 0; i < 8; i++) { + byte pixelBit = (pixel >> i) & 1; + *(byte *)_menu.getBasePtr(x + 7 - i, y) += (pixelBit << plane); + } + } + } + } + // The right borders of the menuboxes: + for (int a = 0; a < 33; a++) { + byte pixel = file.readByte(); + for (int b = 0; b < 6; b++) { + for (int i = 0; i < 8; i++) { + byte pixelBit = (pixel >> i) & 1; + *(byte *)_menu.getBasePtr(584 + 7 - i, 70 + b * 33 + a) += (pixelBit << plane); + } + } + } + } + + for (int i = 0; i < 6; i++) { + _menu.fillRect(Common::Rect(114, 73 + i * 33, 584, 100 + i * 33), kColorLightgray); + _menu.fillRect(Common::Rect(114, 70 + i * 33, 584, 73 + i * 33), kColorWhite); + _menu.fillRect(Common::Rect(114, 100 + i * 33, 584, 103 + i * 33), kColorDarkgray); + } + + file.close(); + + // The title on the top of the screen: + if (!file.open("mainmenu.avd")) + error("AVALANCHE: MainMenu: File not found: mainmenu.avd"); + + Graphics::Surface title = loadPictureRaw(file, 640, 59); + drawPicture(_menu, title, 0, 0); + title.free(); + + file.close(); +} + +void GraphicManager::menuDrawBigText(FontType font, uint16 x, uint16 y, Common::String text, Color color) { + drawBigText(_menu, text, font, 14, x, y, color); +} + +void GraphicManager::menuDrawIndicator(int x) { // TODO: Implement striped pattern for the indicator. + if (x > 0) + _menu.fillRect(Common::Rect(x - 1, 330, x, 337), kColorBlack); + _menu.fillRect(Common::Rect(x, 330, x + 1, 337), kColorWhite); + menuRefreshScreen(); +} + +/** + * This function is for skipping the difference between a stored 'size' value associated with a picture + * and the actual size of the pictures when reading them from files for Ghostroom and Shoot em' up. + * It's needed bacuse the original code loaded the pictures to arrays first and only used the useful parts + * of these arrays when drawing the images, but in the ScummVM version, we only read the + * useful parts from the files, so we have to skip these differences between readings. + */ +void GraphicManager::skipDifference(int size, const Graphics::Surface &picture, Common::File &file) { + int bytesPerRow = (picture.w / 8); + if ((picture.w % 8) > 0) + bytesPerRow += 1; + int loadedBytes = picture.h * bytesPerRow * 4 + 4; + // * 4 is for the four planes, + 4 is for the reading of the width and the height at loadPictureGraphic's beginning. + + int bytesToSkip = size - loadedBytes; + file.skip(bytesToSkip); +} + /** * This function mimics Pascal's getimage(). */ @@ -469,6 +914,8 @@ Graphics::Surface GraphicManager::loadPictureGraphic(Common::File &file) { byte pixel = file.readByte(); for (int bit = 0; bit < 8; bit++) { byte pixelBit = (pixel >> bit) & 1; + // If the picture's width is not a multiple of 8, and we get over the boundary with the 'x' cycle, pixelBit is surely == 0. + // Otherwise, it doesn't cause trouble, since addign 0 doesn't have an effect at all. if (pixelBit != 0) *(byte *)picture.getBasePtr(x + 7 - bit, y) += (pixelBit << plane); } @@ -503,6 +950,52 @@ Graphics::Surface GraphicManager::loadPictureRaw(Common::File &file, uint16 widt return picture; } +Graphics::Surface GraphicManager::loadPictureSign(Common::File &file, uint16 width, uint16 height) { + // I know it looks very similar to the other loadPicture methods, but in truth it's the combination of the two. + width *= 8; + + Graphics::Surface picture; // We make a Surface object for the picture itself. + picture.create(width, height, Graphics::PixelFormat::createFormatCLUT8()); + + // Produce the picture. We read it in row-by-row, and every row has 4 planes. + for (int y = 0; y < height; y++) { + for (int8 plane = 0; plane < 4; plane++) { // The planes are in the "right" order. + for (uint16 x = 0; x < width; x += 8) { + byte pixel = file.readByte(); + for (int bit = 0; bit < 8; bit++) { + byte pixelBit = (pixel >> bit) & 1; + *(byte *)picture.getBasePtr(x + 7 - bit, y) += (pixelBit << plane); + } + } + } + } + + return picture; +} + +/** +* Shifts the whole screen down by one line and fills the gap with black. +*/ +void GraphicManager::shiftScreen() { + for (uint16 y = _surface.h - 1; y > 1; y--) + memcpy(_surface.getBasePtr(0, y), _surface.getBasePtr(0, y - 1), _surface.w); + + _surface.drawLine(0, 0, _surface.w, 0, kColorBlack); +} + +void GraphicManager::drawWinningPic() { + Common::File file; + + if (!file.open("finale.avd")) + error("AVALANCHE: Timer: File not found: finale.avd"); + + Graphics::Surface winning = loadPictureRaw(file, 640, 200); + drawPicture(_surface, winning, 0, 0); + + winning.free(); + file.close(); +} + void GraphicManager::clearAlso() { _magics.fillRect(Common::Rect(0, 0, 640, 200), 0); _magics.frameRect(Common::Rect(0, 45, 640, 161), 15); @@ -572,7 +1065,7 @@ void GraphicManager::drawPicture(Graphics::Surface &target, const Graphics::Surf if (destX + maxX > target.w) maxX = target.w - destX; - + if (destY + maxY > target.h) maxY = target.h - destY; @@ -590,7 +1083,27 @@ void GraphicManager::drawCursor(byte pos) { } void GraphicManager::drawReadyLight(Color color) { - _surface.fillRect(Common::Rect(419, 195, 438, 197), color); + _surface.fillRect(Common::Rect(419, 195, 439, 198), color); + _scrolls.fillRect(Common::Rect(419, 195, 439, 198), color); +} + +void GraphicManager::drawSoundLight(bool state) { + Color color = kColorBlack; + if (state) + color = kColorCyan; + else + color = kColorBlack; + _surface.fillRect(Common::Rect(419, 175, 439, 178), color); +} + +void GraphicManager::drawErrorLight(bool state) { + Color color = kColorBlack; + if (state) + color = kColorRed; + else + color = kColorBlack; + _surface.fillRect(Common::Rect(419, 184, 439, 187), color); + refreshScreen(); } /** @@ -603,28 +1116,10 @@ void GraphicManager::drawSign(Common::String fn, int16 xl, int16 yl, int16 y) { if (!file.open(filename)) error("AVALANCHE: Scrolls: File not found: %s", filename.c_str()); - // I know it looks very similar to the loadPicture methods, but in truth it's the combination of the two. - uint16 width = xl * 8; - uint16 height = yl; - Graphics::Surface sign; // We make a Surface object for the picture itself. - sign.create(width, height, Graphics::PixelFormat::createFormatCLUT8()); - - // Produce the picture. We read it in row-by-row, and every row has 4 planes. - for (int yy = 0; yy < height; yy++) { - for (int8 plane = 0; plane < 4; plane++) { // The planes are in the "right" order. - for (uint16 xx = 0; xx < width; xx += 8) { - byte pixel = file.readByte(); - for (int bit = 0; bit < 8; bit++) { - byte pixelBit = (pixel >> bit) & 1; - if (pixelBit != 0) - *(byte *)sign.getBasePtr(xx + 7 - bit, yy) += (pixelBit << plane); - } - } - } - } - - drawPicture(_scrolls, sign, kScreenWidth / 2 - width / 2, y); + sign = loadPictureSign(file, xl, yl); + uint16 width = xl * 8; + drawPicture(_scrolls, sign, kScreenWidth / 2 - width / 2, y); // x coord: center the picture. file.close(); } @@ -668,7 +1163,7 @@ void GraphicManager::prepareBubble(int xc, int xw, int my, Common::Point points[ drawTriangle(points, _talkBackgroundColor); } -/** +/** * Set the background of the text to the desired color. */ void GraphicManager::wipeChar(int x, int y, Color color) { @@ -733,10 +1228,10 @@ void GraphicManager::showScroll() { void GraphicManager::getNaturalPicture(SpriteType &sprite) { sprite._type = kNaturalImage; // We simply read from the screen and later, in drawSprite() we draw it right back. - sprite._size = sprite._xl * 8 * sprite._yl + 1; - sprite._picture.create(sprite._xl * 8, sprite._yl + 1, Graphics::PixelFormat::createFormatCLUT8()); - for (uint16 y = 0; y < sprite._yl + 1; y++) { - for (uint16 x = 0; x < sprite._xl * 8; x++) + sprite._size = sprite._width * 8 * sprite._height + 1; + sprite._picture.create(sprite._width * 8, sprite._height + 1, Graphics::PixelFormat::createFormatCLUT8()); + for (uint16 y = 0; y < sprite._height + 1; y++) { + for (uint16 x = 0; x < sprite._width * 8; x++) *(byte *)sprite._picture.getBasePtr(x, y) = *(byte *)_vm->_graphics->_surface.getBasePtr(sprite._x * 8 + x, sprite._y + y); } } @@ -759,9 +1254,12 @@ void GraphicManager::setDialogColor(Color bg, Color text) { _talkFontColor = text; } -// Original name background() -void GraphicManager::setBackgroundColor(Color x) { - warning("STUB: setBackgroundColor()"); +/** +* Changes the black color of the palette to the selected one. +* @remarks Originally called 'background' +*/ +void GraphicManager::setBackgroundColor(Color newColor) { + g_system->getPaletteManager()->setPalette(_egaPalette[kEgaPaletteIndex[newColor]], kColorBlack, 1); } } // End of namespace Avalanche diff --git a/engines/avalanche/graphics.h b/engines/avalanche/graphics.h index 4af6d4e8db..bd8fc6c8ff 100644 --- a/engines/avalanche/graphics.h +++ b/engines/avalanche/graphics.h @@ -8,12 +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. - + * * 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 + * 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. @@ -38,6 +38,7 @@ namespace Avalanche { class AvalancheEngine; class AnimationType; struct SpriteType; +struct ChunkBlock; typedef byte FontType[256][16]; typedef byte ManiType[2049]; @@ -58,6 +59,11 @@ public: void loadDigits(); void loadMouse(byte which); + void drawRectangle(Common::Rect rect, Color color); + void drawFilledRectangle(Common::Rect rect, Color color); + void blackOutScreen(); + void drawDot(int x, int y, Color color); + void drawLine(int x1, int y1, int x2, int y2, int penX, int penY, Color color); Common::Point drawScreenArc(int16 x, int16 y, int16 stAngle, int16 endAngle, uint16 radius, Color color); void drawPieSlice(int16 x, int16 y, int16 stAngle, int16 endAngle, uint16 radius, Color color); void drawTriangle(Common::Point *p, Color color); @@ -77,22 +83,66 @@ public: void drawChar(byte ander, int x, int y, Color color); void drawDebugLines(); + // For the mini-game "Nim". + void nimLoad(); + void nimDrawStone(int x, int y); + void nimDrawInitials(); + void nimDrawLogo(); + void nimFree(); + + // Used in wobble() + void shiftScreen(); + + // Used in winning() + void drawWinningPic(); + + // Ghostroom's functions: + void ghostDrawMonster(byte ***picture, uint16 destX, int16 destY, MonsterType type); + Graphics::Surface ghostLoadPicture(Common::File &file, Common::Point &coord); + void ghostDrawPicture(const Graphics::Surface &picture, uint16 destX, uint16 destY); + void ghostDrawBackgroundItems(Common::File &file); + + // Help's function: + void helpDrawButton(int y, byte which); + void helpDrawHighlight(byte which, Color color); + void helpDrawBigText(const Common::String text, int16 x, int16 y, Color color); + + // Shoot em' up's functions: + void seuDrawTitle(); + void seuLoad(); + void seuFree(); + void seuDrawPicture(int x, int y, byte which); + void seuDrawCameo(int destX, int destY, byte w1, byte w2); + uint16 seuGetPicWidth(int which); + uint16 seuGetPicHeight(int which); + + // Main Menu's functions: + // The main menu uses a different screen height (350) from the game itself (200 * 2) + // so it needs it's own graphic functions on that matter. + void menuRefreshScreen(); + void menuInitialize(); + void menuFree(); + void menuRestoreScreen(); + void menuLoadPictures(); + void menuDrawBigText(FontType font, uint16 x, uint16 y, Common::String text, Color color); + void menuDrawIndicator(int x); + void clearAlso(); void clearTextBar(); void setAlsoLine(int x1, int y1, int x2, int y2, Color color); byte getAlsoColor(int x1, int y1, int x2, int y2); byte getScreenColor(Common::Point pos); - // The caller has to .free() the returned Surfaces!!! - // Further information about these two: http://www.shikadi.net/moddingwiki/Raw_EGA_data + // Further information about this: http://www.shikadi.net/moddingwiki/Raw_EGA_data Graphics::Surface loadPictureRaw(Common::File &file, uint16 width, uint16 height); void drawSprite(AnimationType *sprite, byte picnum, int16 x, int16 y); - void drawPicture(Graphics::Surface &target, const Graphics::Surface picture, uint16 destX, uint16 destY); void drawThinkPic(Common::String filename, int id); void drawToolbar(); void drawCursor(byte pos); void drawReadyLight(Color color); + void drawSoundLight(bool state); + void drawErrorLight(bool state); void drawSign(Common::String name, int16 xl, int16 yl, int16 y); void drawIcon(int16 x, int16 y, byte which); void drawScreenLine(int16 x, int16 y, int16 x2, int16 y2, Color color); @@ -100,7 +150,7 @@ public: void refreshScreen(); void loadBackground(Common::File &file); void refreshBackground(); - void setBackgroundColor(Color x); + void setBackgroundColor(Color newColor); void setDialogColor(Color bg, Color text); void zoomOut(int16 x, int16 y); @@ -108,14 +158,16 @@ public: void getNaturalPicture(SpriteType &sprite); void saveScreen(); - void removeBackup(); void restoreScreen(); + void removeBackup(); private: + static const int16 kMouseSize = 134; static const uint16 kBackgroundWidth = kScreenWidth; static const byte kEgaPaletteIndex[16]; static const byte kBackgroundHeight = 8 * 12080 / kScreenWidth; // With 640 width it's 151. // The 8 = number of bits in a byte, and 12080 comes from Lucerna::load(). + static const uint16 kMenuScreenHeight = 350; Graphics::Surface _background; Graphics::Surface _backup; @@ -125,15 +177,32 @@ private: Graphics::Surface _screen; // Only used in refreshScreen() to make it more optimized. (No recreation of it at every call of the function.) Graphics::Surface _scrolls; Graphics::Surface _surface; + Graphics::Surface _menu; + + // For the mini-game "Nim". + Graphics::Surface _nimStone; + Graphics::Surface _nimInitials[3]; + Graphics::Surface _nimLogo; + + // For the mini-game "Shoot em' up". + Graphics::Surface _seuPictures[99]; + byte _egaPalette[64][3]; AvalancheEngine *_vm; + void skipDifference(int size, const Graphics::Surface &picture, Common::File &file); + + // Further information about these two: http://www.shikadi.net/moddingwiki/Raw_EGA_data Graphics::Surface loadPictureGraphic(Common::File &file); // Reads Graphic-planar EGA data. + Graphics::Surface loadPictureSign(Common::File &file, uint16 width, uint16 height); // Reads a tricky type of picture used for the "game over"/"about" scrolls and in the mini-game Nim. + void drawText(Graphics::Surface &surface, const Common::String text, FontType font, byte fontHeight, int16 x, int16 y, Color color); + void drawBigText(Graphics::Surface &surface, const Common::String text, FontType font, byte fontHeight, int16 x, int16 y, Color color); + void drawPicture(Graphics::Surface &target, const Graphics::Surface picture, uint16 destX, uint16 destY); + // Taken from Free Pascal's Procedure InternalEllipseDefault. Used to replace Pascal's procedure arc. // Returns the end point of the arc. (Needed in Clock.) - // TODO: Make it more accurate later. Common::Point drawArc(Graphics::Surface &surface, int16 x, int16 y, int16 stAngle, int16 endAngle, uint16 radius, Color color); }; diff --git a/engines/avalanche/help.cpp b/engines/avalanche/help.cpp new file mode 100644 index 0000000000..4d08e3d58b --- /dev/null +++ b/engines/avalanche/help.cpp @@ -0,0 +1,270 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* +* This code is based on the original source code of Lord Avalot d'Argent version 1.3. +* Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. +*/ + +/* Original name: HELPER The help system unit. */ + +#include "avalanche/avalanche.h" +#include "avalanche/help.h" + +#include "common/scummsys.h" + +namespace Avalanche { + +Help::Help(AvalancheEngine *vm) { + _vm = vm; + + for (int i = 0; i < 10; i++) { + _buttons[i]._trigger = Common::KEYCODE_INVALID; + _buttons[i]._whither = 0; + } + _highlightWas = 0; + _buttonNum = 0; + _holdLeft = false; +} + +/** + * Loads and draws the chosen page of the help. + * @remarks Originally called 'getme' + */ +void Help::switchPage(byte which) { + // Help icons are 80x20. + + _highlightWas = 177; // Forget where the highlight was. + + Common::File file; + + if (!file.open("help.avd")) + error("AVALANCHE: Help: File not found: help.avd"); + + file.seek(which * 2); + uint16 offset = file.readUint16LE(); + file.seek(offset); + + Common::String title = getLine(file); + + _vm->_graphics->drawFilledRectangle(Common::Rect(0, 0, 640, 200), kColorBlue); + _vm->_graphics->drawFilledRectangle(Common::Rect(8, 40, 450, 200), kColorWhite); + + byte index = file.readByte(); + _vm->_graphics->helpDrawButton(-177, index); + + // Plot the title: + _vm->_graphics->drawNormalText(title, _vm->_font, 8, 629 - 8 * title.size(), 26, kColorBlack); + _vm->_graphics->drawNormalText(title, _vm->_font, 8, 630 - 8 * title.size(), 25, kColorCyan); + + _vm->_graphics->helpDrawBigText("help!", 549, 1, kColorBlack); + _vm->_graphics->helpDrawBigText("help!", 550, 0, kColorCyan); + + byte y = 0; + do { + Common::String line = getLine(file); + if (!line.empty()) { + if (line.compareTo(Common::String('!')) == 0) // End of the help text is signalled with a '!'. + break; + if (line[0] == '\\') { + line.deleteChar(0); + _vm->_graphics->drawNormalText(line, _vm->_font, 8, 16, 41 + y * 10, kColorRed); + } + else + _vm->_graphics->drawNormalText(line, _vm->_font, 8, 16, 41 + y * 10, kColorBlack); + } + y++; + } while (true); + + // We are now at the end of the text. Next we must read the icons: + y = 0; + _buttonNum = 0; + while (!file.eos()) { + int trigger = file.readByte(); + + if (trigger == 177) + break; + switch (trigger) { + case 254: // Escape + trigger = 27; + break; + case 214: // PageUp + trigger = 280; + break; + case 216: // PageDown + trigger = 281; + break; + default: // A - Z + // The characters are stored in the file in uppercase, but we need the lowercase versions for KeyCode: + trigger = tolower(trigger); + break; + } + + _buttons[y]._trigger = Common::KeyCode(trigger); + index = file.readByte(); + if (_buttons[y]._trigger != Common::KEYCODE_INVALID) + _vm->_graphics->helpDrawButton(13 + (y + 1) * 27, index); + _buttons[y]._whither = file.readByte(); // This is the position to jump to. + + Common::String text = ""; + switch (_buttons[y]._trigger) { + case Common::KEYCODE_ESCAPE: + text = Common::String("Esc"); + break; + case Common::KEYCODE_PAGEUP: + text = Common::String(24); + break; + case Common::KEYCODE_PAGEDOWN: + text = Common::String(25); + break; + default: + text = Common::String(toupper(_buttons[y]._trigger)); + break; + } + + _vm->_graphics->helpDrawBigText(text, 589 - (text.size() * 8), 18 + (y + 1) * 27, kColorBlack); + _vm->_graphics->helpDrawBigText(text, 590 - (text.size() * 8), 17 + (y + 1) * 27, kColorCyan); + + y++; + _buttonNum++; + } + + _vm->_graphics->refreshScreen(); + + file.close(); +} + +Common::String Help::getLine(Common::File &file) { + Common::String line; + byte length = file.readByte(); + for (int i = 0; i < length; i++) { + char c = file.readByte(); + line += (c ^ 177); + } + return line; +} + +bool Help::handleMouse(const Common::Event &event) { + Common::Point mousePos; + mousePos.x = event.mouse.x; + mousePos.y = event.mouse.y / 2; + + int index = -1; + + if (event.type == Common::EVENT_LBUTTONUP) { // Clicked *somewhere*... + _holdLeft = false; + + if ((mousePos.x < 470) || (mousePos.x > 550) || (((mousePos.y - 13) % 27) > 20)) + index = -1; + else // Clicked on a button. + index = ((mousePos.y - 13) / 27) - 1; + } else { // LBUTTONDOWN or MOUSEMOVE + int highlightIs = 0; + + // Decide which button we are hovering the cursor over: + if ((mousePos.x > 470) && (mousePos.x <= 550) && (((mousePos.y - 13) % 27) <= 20)) { // No click, so highlight. + highlightIs = (mousePos.y - 13) / 27 - 1; + if ((highlightIs < 0) || (5 < highlightIs)) + highlightIs = 177; // In case of silly values. + } else + highlightIs = 177; + + Color highlightColor = kColorLightblue; + // If we clicked on a button or we are holding down the button, we have to highlight it with cyan: + if (((highlightIs != 177) && (event.type == Common::EVENT_LBUTTONDOWN)) || _holdLeft) { + _holdLeft = true; + highlightColor = kColorLightcyan; + } + + // Erase the previous highlight only if it's needed: + if (_highlightWas != highlightIs) + _vm->_graphics->helpDrawHighlight(_highlightWas, kColorBlue); + + // Highlight the current one with the proper color: + if ((highlightIs != 177) && (_buttons[highlightIs]._trigger != Common::KEYCODE_INVALID)) { + _highlightWas = highlightIs; + _vm->_graphics->helpDrawHighlight(highlightIs, highlightColor); + } + } + + if ((index >= 0) && (_buttons[index]._trigger != Common::KEYCODE_INVALID)) { + if (_buttons[index]._trigger == Common::KEYCODE_ESCAPE) + return true; + else { + _vm->fadeOut(); + switchPage(_buttons[index]._whither); + _vm->fadeIn(); + return false; + } + } + + return false; +} + +bool Help::handleKeyboard(const Common::Event &event) { + if (event.kbd.keycode == Common::KEYCODE_ESCAPE) + return true; + + for (int i = 0; i < _buttonNum; i++) { + if (_buttons[i]._trigger == event.kbd.keycode) { + _vm->fadeOut(); + switchPage(_buttons[i]._whither); + _vm->fadeIn(); + return false; + } + } + + return false; +} + +/** + * @remarks Originally called 'boot_help' + */ +void Help::run() { + _vm->_graphics->saveScreen(); + _vm->fadeOut(); + switchPage(0); + _vm->fadeIn(); + + _vm->_graphics->loadMouse(kCurHelp); + + // Originally it was the body of 'continue_help': + bool close = false; + while (!_vm->shouldQuit() && !close) { + Common::Event event; + _vm->getEvent(event); + if (event.type == Common::EVENT_KEYDOWN) + close = handleKeyboard(event); + else if ((event.type == Common::EVENT_LBUTTONDOWN) || (event.type == Common::EVENT_LBUTTONUP) || (event.type == Common::EVENT_MOUSEMOVE)) + close = handleMouse(event); + + _vm->_graphics->refreshScreen(); + } + // End of 'continue_help'. + + _vm->fadeOut(); + _vm->_graphics->restoreScreen(); + _vm->_graphics->removeBackup(); + _vm->fadeIn(); +} + +} // End of namespace Avalanche diff --git a/engines/avalanche/help.h b/engines/avalanche/help.h new file mode 100644 index 0000000000..7543d87047 --- /dev/null +++ b/engines/avalanche/help.h @@ -0,0 +1,65 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* +* This code is based on the original source code of Lord Avalot d'Argent version 1.3. +* Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. +*/ + +/* Original name: HELPER The help system unit. */ + +#ifndef AVALANCHE_HELP_H +#define AVALANCHE_HELP_H + +namespace Avalanche { +class AvalancheEngine; + +class Help { +public: + Help(AvalancheEngine *vm); + + void run(); + +private: + struct Button { + Common::KeyCode _trigger; + byte _whither; + }; + + AvalancheEngine *_vm; + + Button _buttons[10]; + byte _highlightWas; + byte _buttonNum; // How many buttons do we have on the screen at the moment? + bool _holdLeft; // Is the left mouse button is still being held? + + void switchPage(byte which); + Common::String getLine(Common::File &file); // It was a nested function in getMe(). + + // These two return true if we have to leave the Help: + bool handleMouse(const Common::Event &event); + bool handleKeyboard(const Common::Event &event); +}; + +} // End of namespace Avalanche + +#endif // AVALANCHE_HELP_H diff --git a/engines/avalanche/highscore.cpp b/engines/avalanche/highscore.cpp new file mode 100644 index 0000000000..5f47aeb894 --- /dev/null +++ b/engines/avalanche/highscore.cpp @@ -0,0 +1,110 @@ +/* ScummVM - Graphic Adventure Engine +* +* ScummVM is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the COPYRIGHT +* file distributed with this source distribution. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +*/ + +/* +* This code is based on the original source code of Lord Avalot d'Argent version 1.3. +* Copyright (c) 1994-1995 Mike: Mark and Thomas Thurman. +*/ + +#include "avalanche/avalanche.h" +#include "avalanche/highscore.h" + +#include "common/savefile.h" + +namespace Avalanche { + +HighScore::HighScore(AvalancheEngine *vm) { + _vm = vm; +} + +void HighScore::displayHighScores() { + warning("STUB: HighScore::displayHighScores("); +} + +void HighScore::saveHighScores() { + int firstSmaller = 0; + while ((firstSmaller < 12) && (_data[firstSmaller]._score >= _vm->_score)) + firstSmaller++; + + if (firstSmaller < 12) { + // Shift all the lower scores down a space: + for (int i = firstSmaller; i < 11; i++) + _data[i + 1] = _data[i]; + // Set the new high score: + _data[firstSmaller]._name = "Player"; // TODO: Come up with something for that. In the original it wasn't implemented at all... + _data[firstSmaller]._rank = _vm->_parser->rank(); + _data[firstSmaller]._score = _vm->_score; + } + + Common::OutSaveFile *f = g_system->getSavefileManager()->openForSaving("scores.avd"); + if (!f) { + warning("Can't create file 'scores.avd', high scores are not saved."); + return; + } + Common::Serializer sz(NULL, f); + syncHighScores(sz); + f->finalize(); + delete f; +} + +void HighScore::loadHighScroes() { + Common::File file; + if (!file.exists("scores.avd")) { + produceDefaultHighScores(); + } else { + Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading("scores.avd"); + if (!f) + return; + Common::Serializer sz(f, NULL); + syncHighScores(sz); + delete f; + } +} + +void HighScore::produceDefaultHighScores() { + for (int i = 0; i < 12; i++) { + _data[i]._score = 32 - (i + 1) * 2; + _data[i]._rank = "..."; + } + _data[0]._name = "Mike"; + _data[1]._name = "Liz"; + _data[2]._name = "Thomas"; + _data[3]._name = "Mark"; + _data[4]._name = "Mandy"; + _data[5]._name = "Andrew"; + _data[6]._name = "Lucy Tryphena"; + _data[7]._name = "Tammy the dog"; + _data[8]._name = "Avaricius"; + _data[9]._name = "Spellchick"; + _data[10]._name = "Caddelli"; + _data[11]._name = "Spludwick"; +} + +void HighScore::syncHighScores(Common::Serializer &sz) { + for (int i = 0; i < 12; i++) { + sz.syncString(_data[i]._name); + sz.syncAsUint16LE(_data[i]._score); + sz.syncString(_data[i]._rank); + } +} + +} // End of namespace Avalanche diff --git a/engines/avalanche/highscore.h b/engines/avalanche/highscore.h new file mode 100644 index 0000000000..de7ec36ed5 --- /dev/null +++ b/engines/avalanche/highscore.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. +* +*/ + +/* +* This code is based on the original source code of Lord Avalot d'Argent version 1.3. +* Copyright (c) 1994-1995 Mike: Mark and Thomas Thurman. +*/ + +#ifndef AVALANCHE_HIGHSCORE_H +#define AVALANCHE_HIGHSCORE_H + +namespace Avalanche { +class AvalancheEngine; + +struct HighScoreData { + Common::String _name; + uint16 _score; + Common::String _rank; +}; + +class HighScore { +public: + HighScore(AvalancheEngine *vm); + + void displayHighScores(); + void saveHighScores(); + void loadHighScroes(); + +private: + AvalancheEngine *_vm; + + HighScoreData _data[12]; + + void produceDefaultHighScores(); + void syncHighScores(Common::Serializer &sz); +}; + +} // End of namespace Avalanche + +#endif // AVALANCHE_HIGHSCORE_H diff --git a/engines/avalanche/mainmenu.cpp b/engines/avalanche/mainmenu.cpp new file mode 100644 index 0000000000..543684556c --- /dev/null +++ b/engines/avalanche/mainmenu.cpp @@ -0,0 +1,116 @@ +/* ScummVM - Graphic Adventure Engine +* +* ScummVM is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the COPYRIGHT +* file distributed with this source distribution. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +*/ + +/* +* This code is based on the original source code of Lord Avalot d'Argent version 1.3. +* Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. +*/ + +#include "avalanche/avalanche.h" +#include "avalanche/mainmenu.h" + +namespace Avalanche { + +MainMenu::MainMenu(AvalancheEngine *vm) { + _vm = vm; + + _registrant = Common::String(""); +} + +void MainMenu::run() { + CursorMan.showMouse(false); + _vm->_graphics->menuInitialize(); + _vm->_graphics->menuLoadPictures(); + loadRegiInfo(); + loadFont(); + + option(1, "Play the game."); + option(2, "Read the background."); + option(3, "Preview... perhaps..."); + option(4, "View the documentation."); + option(5, "Registration info."); + option(6, "Exit back to DOS."); + centre(274, _registrant); + centre(301, "Make your choice, or wait for the demo."); + + _vm->_graphics->menuRefreshScreen(); + + wait(); +} + +void MainMenu::loadFont() { + Common::File file; + if (!file.open("avalot.fnt")) + error("AVALANCHE: Scrolls: File not found: avalot.fnt"); + for (int16 i = 0; i < 256; i++) + file.read(_font[i], 16); + file.close(); +} + +void MainMenu::loadRegiInfo() { + _registrant = "(Unregistered evaluation copy.)"; + warning("STUB: MainMenu::loadRegiInfo()"); +} + +void MainMenu::option(byte which, Common::String what) { + _vm->_graphics->menuDrawBigText(_font, 127, 39 + which * 33, Common::String(which + 48) + ')', kColorBlack); + _vm->_graphics->menuDrawBigText(_font, 191, 39 + which * 33, what, kColorBlack); +} + +void MainMenu::centre(int16 y, Common::String text) { + _vm->_graphics->menuDrawBigText(_font, 320 - text.size() * 8, y, text, kColorLightgray); +} + +void MainMenu::wait() { + int x = 0; + while (!_vm->shouldQuit()) { + _vm->_graphics->menuDrawIndicator(x); + _vm->_system->delayMillis(40); + x++; + if (x == 641) + x = 0; + Common::Event event; + _vm->getEvent(event); + if (event.type == Common::EVENT_KEYDOWN) { + switch (event.kbd.keycode) { + case Common::KEYCODE_SPACE: + case Common::KEYCODE_RETURN: + case Common::KEYCODE_1: // Falltroughs are inteded. + // Play the game + _vm->_graphics->menuFree(); + _vm->_graphics->menuRestoreScreen(); + CursorMan.showMouse(true); + return; + case Common::KEYCODE_ESCAPE: + case Common::KEYCODE_6: // Falltroughs are inteded. + // Exit back to DOS + _vm->_letMeOut = true; + _vm->_graphics->menuFree(); + return; + default: + break; + } + } + } +} + +} // End of namespace Avalanche diff --git a/engines/avalanche/mainmenu.h b/engines/avalanche/mainmenu.h new file mode 100644 index 0000000000..e973e0ccf3 --- /dev/null +++ b/engines/avalanche/mainmenu.h @@ -0,0 +1,55 @@ +/* ScummVM - Graphic Adventure Engine +* +* ScummVM is the legal property of its developers, whose names +* are too numerous to list here. Please refer to the COPYRIGHT +* file distributed with this source distribution. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +*/ + +/* +* This code is based on the original source code of Lord Avalot d'Argent version 1.3. +* Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. +*/ + +#ifndef AVALANCHE_MAINMENU_H +#define AVALANCHE_MAINMENU_H + +namespace Avalanche { +class AvalancheEngine; + +class MainMenu { +public: + MainMenu(AvalancheEngine *vm); + + void run(); + +private: + AvalancheEngine *_vm; + + Common::String _registrant; + FontType _font; + + void loadFont(); + void loadRegiInfo(); + void option(byte which, Common::String what); + void centre(int16 y, Common::String text); + void wait(); +}; + +} // End of namespace Avalanche + +#endif // AVALANCHE_MAINMENU_H diff --git a/engines/avalanche/module.mk b/engines/avalanche/module.mk index 9c1205df02..919ff0334f 100644 --- a/engines/avalanche/module.mk +++ b/engines/avalanche/module.mk @@ -9,14 +9,20 @@ MODULE_OBJS = \ console.o \ detection.o \ graphics.o \ - menu.o \ + dropdown.o \ parser.o \ - pingo.o \ dialogs.o \ sequence.o \ sound.o \ - timer.o - + timer.o \ + nim.o \ + clock.o \ + ghostroom.o \ + help.o \ + shootemup.o \ + mainmenu.o \ + highscore.o + # This module can be built as a plugin ifeq ($(ENABLE_AVALANCHE), DYNAMIC_PLUGIN) PLUGIN := 1 diff --git a/engines/avalanche/nim.cpp b/engines/avalanche/nim.cpp new file mode 100644 index 0000000000..79be16d3c3 --- /dev/null +++ b/engines/avalanche/nim.cpp @@ -0,0 +1,575 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +#include "avalanche/avalanche.h" +#include "avalanche/nim.h" + +namespace Avalanche { + +const char * const Nim::kNames[2] = {"Avalot", "Dogfood"}; + +Nim::Nim(AvalancheEngine *vm) { + _vm = vm; + + resetVariables(); +} + +void Nim::resetVariables() { + _playedNim = 0; + _turns = 0; + _dogfoodsTurn = false; + _stonesLeft = 0; + _clicked = false; + _row = 0; + _number = 0; + _squeak = false; + _lmo = false; + + for (int i = 0; i < 3; i++) { + _old[i] = 0; + _stones[i] = 0; + _inAp[i] = 0; + _r[i] = 0; + } +} + +void Nim::synchronize(Common::Serializer &sz) { + if (sz.isLoading() && sz.getVersion() < 2) + return; + + sz.syncAsByte(_playedNim); +} + +void Nim::playNim() { + if (_vm->_wonNim) { // Already won the game. + _vm->_dialogs->displayScrollChain('Q', 6); + return; + } + + if (!_vm->_askedDogfoodAboutNim) { + _vm->_dialogs->displayScrollChain('Q', 84); + return; + } + + _vm->_dialogs->displayScrollChain('Q', 3); + _playedNim++; + + _vm->_graphics->saveScreen(); + _vm->fadeOut(); + + CursorMan.showMouse(false); + setup(); + board(); + //CursorMan.showMouse(true); + + do { + + startMove(); + if (_dogfoodsTurn) + dogFood(); + else { + CursorMan.showMouse(true); + takeSome(); + CursorMan.showMouse(false); + } + _stones[_row] -= _number; + showChanges(); + } while (_stonesLeft != 0); + + endOfGame(); // Winning sequence is A1, B3, B1, C1, C1, btw. + + _vm->fadeOut(); + _vm->_graphics->restoreScreen(); + _vm->_graphics->removeBackup(); + _vm->fadeIn(); + CursorMan.showMouse(true); + + if (_dogfoodsTurn) { + // Dogfood won - as usual. + if (_playedNim == 1) // Your first game. + _vm->_dialogs->displayScrollChain('Q', 4); // Goody! Play me again? + else + _vm->_dialogs->displayScrollChain('Q', 5); // Oh, look at that! I've won again! + _vm->decreaseMoney(4); // And you've just lost 4d! + } else { + // You won - strange! + _vm->_dialogs->displayScrollChain('Q', 7); + _vm->_objects[kObjectLute - 1] = true; + _vm->refreshObjectList(); + _vm->_wonNim = true; + _vm->_background->draw(-1, -1, 0); // Show the settle with no lute on it. + + // 7 points for winning! + _vm->incScore(7); + } + + if (_playedNim == 1) { + // 3 points for playing your 1st game. + _vm->incScore(3); + } +} + +void Nim::chalk(int x, int y, Common::String text) { + const Color greys[] = { kColorBlack, kColorDarkgray, kColorLightgray, kColorWhite }; + + for (int i = 0; i < 4; i++) { + _vm->_graphics->drawNormalText(text, _vm->_font, 8, x - i, y, greys[i]); + _vm->_graphics->refreshScreen(); + int freq = i * 100 * text.size(); + if (freq == 0) + _vm->_system->delayMillis(3); + else + _vm->_sound->playNote(freq, 3); + _vm->_system->delayMillis(30); + } +} + +void Nim::setup() { + _vm->fadeIn(); + _vm->_graphics->nimLoad(); + + _vm->_graphics->blackOutScreen(); + // Upper left rectangle. + _vm->_graphics->drawRectangle(Common::Rect(10, 5, 381, 71), kColorRed); + _vm->_graphics->drawFilledRectangle(Common::Rect(11, 6, 380, 70), kColorBrown); + // Bottom right rectangle. + _vm->_graphics->drawRectangle(Common::Rect(394, 50, 635, 198), kColorRed); + _vm->_graphics->drawFilledRectangle(Common::Rect(395, 51, 634, 197), kColorBrown); + + _vm->_graphics->nimDrawLogo(); + _vm->_graphics->nimDrawInitials(); + + _vm->_graphics->drawNormalText("SCOREBOARD:", _vm->_font, 8, 475, 45, kColorWhite); + _vm->_graphics->drawNormalText("Turn:", _vm->_font, 8, 420, 55, kColorYellow); + _vm->_graphics->drawNormalText("Player:", _vm->_font, 8, 490, 55, kColorYellow); + _vm->_graphics->drawNormalText("Move:", _vm->_font, 8, 570, 55, kColorYellow); + + chalk(27, 7, "Take pieces away with:"); + chalk(77, 17, "1) the mouse (click leftmost)"); + chalk(53, 27, "or 2) the keyboard:"); + chalk(220, 27, Common::String(24) + '/' + 25 + ": choose row,"); + chalk(164, 37, Common::String("+/- or ") + 27 + '/' + 26 + ": more/fewer,"); + chalk(204, 47, "Enter: take stones."); + + _vm->_graphics->refreshScreen(); + + for (int i = 0; i < 3; i++) + _stones[i] = i + 3; + _stonesLeft = 12; + + _turns = 0; + _dogfoodsTurn = true; + + _row = 0; + _number = 1; + for (int i = 0; i < 3; i++) + _old[i] = 0; +} + +void Nim::board() { + _vm->_graphics->drawFilledRectangle(Common::Rect(57, 72, 393, 200), kColorBlack); + for (int i = 0; i < 3; i++) + for (int j = 0; j < _stones[i]; j++) + _vm->_graphics->nimDrawStone(64 + j * 8 * 8, 75 + i * 35); + // It's practically the body of the Pascal function "plotstone()", reimplemented. + // It's the only place where we use it, so there's no reason to keep it separated as a function. + _vm->_graphics->refreshScreen(); +} + +void Nim::startMove() { + _turns++; + Common::String turnsStr = Common::String::format("%d", _turns); + int y = 55 + _turns * 10; + _dogfoodsTurn = !_dogfoodsTurn; + chalk(433, y, turnsStr); + chalk(493, y, kNames[_dogfoodsTurn]); + for (int i = 0; i < 3; i++) + _old[i] = _stones[i]; +} + +void Nim::showChanges() { + chalk(573, 55 + _turns * 10, Common::String('A' + _row) + Common::String('0' + _number)); + board(); + _stonesLeft -= _number; +} + +void Nim::blip() { + _vm->_sound->playNote(1771, 3); +} + +void Nim::findNextUp() { + while (_stones[_row] == 0) { + _row--; + if (_row < 0) + _row = 2; + } + + if (_number > _stones[_row]) + _number = _stones[_row]; +} + +void Nim::findNextDown() { + while (_stones[_row] == 0) { + _row++; + if (_row > 2) + _row = 0; + } + + if (_number > _stones[_row]) + _number = _stones[_row]; +} + +bool Nim::checkInput() { + while (!_vm->shouldQuit()) { + _vm->_graphics->refreshScreen(); + Common::Event event; + while (_vm->getEvent(event)) { + if (event.type == Common::EVENT_LBUTTONUP) { + Common::Point cursorPos = _vm->getMousePos(); + + int8 newRow = (cursorPos.y / 2 - 38) / 35 - 1; + if ((newRow < 0) || (newRow > 2)) { + blip(); + return false; + } + + int8 newNum = _stones[newRow] - ((cursorPos.x - 64) / 64); + if ((newNum < 1) || (newNum > _stones[newRow])) { + blip(); + return false; + } + + _number = newNum; + _row = newRow; + + return true; + } else if (event.type == Common::EVENT_KEYDOWN) { + switch (event.kbd.keycode) { + case Common::KEYCODE_LEFT: + case Common::KEYCODE_KP_PLUS: + if (_stones[_row] > _number) + _number++; + return false; + case Common::KEYCODE_RIGHT: + case Common::KEYCODE_KP_MINUS: + if (_number > 1) + _number--; + return false; + case Common::KEYCODE_1: + _number = 1; + return false; + case Common::KEYCODE_2: + if (_stones[_row] >= 2) + _number = 2; + return false; + case Common::KEYCODE_3: + if (_stones[_row] >= 3) + _number = 3; + else + _number = _stones[_row]; + return false; + case Common::KEYCODE_4: + if (_stones[_row] >= 4) + _number = 4; + else + _number = _stones[_row]; + return false; + case Common::KEYCODE_5: + if (_stones[_row] == 5) + _number = 5; + else + _number = _stones[_row]; + return false; + case Common::KEYCODE_HOME: + _number = _stones[_row]; + return false; + case Common::KEYCODE_END: + _number = 1; + return false; + case Common::KEYCODE_UP: + _row--; + if (_row < 0) + _row = 2; + findNextUp(); + return false; + case Common::KEYCODE_DOWN: + _row++; + if (_row > 2) + _row = 0; + findNextDown(); + return false; + case Common::KEYCODE_a: + if (_stones[0] != 0) { + _row = 0; + if (_number > _stones[_row]) + _number = _stones[_row]; + } + return false; + case Common::KEYCODE_b: + if (_stones[1] != 0) { + _row = 1; + if (_number > _stones[_row]) + _number = _stones[_row]; + } + return false; + case Common::KEYCODE_c: + if (_stones[2] != 0) { + _row = 2; + if (_number > _stones[_row]) + _number = _stones[_row]; + } + return false; + case Common::KEYCODE_PAGEUP: + _row = 0; + findNextDown(); + return false; + case Common::KEYCODE_PAGEDOWN: + _row = 2; + findNextUp(); + return false; + case Common::KEYCODE_RETURN: + return true; + default: + break; + } + } + } + } + return false; +} + +void Nim::takeSome() { + _number = 1; + + do { + byte sr; + do { + sr = _stones[_row]; + if (sr == 0) { + if (_row == 2) + _row = 0; + else + _row++; + _number = 1; + } + } while (sr == 0); + + if (_number > sr) + _number = sr; + + int x1 = 63 + (_stones[_row] - _number) * 64; + int y1 = 38 + 35 * (_row + 1); + int x2 = 55 + _stones[_row] * 64; + int y2 = 64 + 35 * (_row + 1); + _vm->_graphics->drawRectangle(Common::Rect(x1, y1, x2, y2), kColorBlue); // Draw the selection rectangle. + _vm->_graphics->refreshScreen(); + + bool confirm = false; + do { + confirm = checkInput(); + + if (!confirm) { + _vm->_graphics->drawRectangle(Common::Rect(x1, y1, x2, y2), kColorBlack); // Erase the previous selection. + x1 = 63 + (_stones[_row] - _number) * 64; + y1 = 38 + 35 * (_row + 1); + x2 = 55 + _stones[_row] * 64; + y2 = 64 + 35 * (_row + 1); + _vm->_graphics->drawRectangle(Common::Rect(x1, y1, x2, y2), kColorBlue); // Draw the new one. + _vm->_graphics->refreshScreen(); + } + } while (!confirm); + + return; + + } while (true); +} + +void Nim::endOfGame() { + chalk(595, 55 + _turns * 10, "Wins!"); + _vm->_graphics->drawNormalText("- - - Press any key... - - -", _vm->_font, 8, 100, 190, kColorWhite); + + Common::Event event; + bool escape = false; + while (!_vm->shouldQuit() && !escape) { + _vm->_graphics->refreshScreen(); + while (_vm->getEvent(event)) { + if ((event.type == Common::EVENT_LBUTTONUP) || (event.type == Common::EVENT_KEYDOWN)) { + escape = true; + break; + } + } + } + + _vm->_graphics->nimFree(); +} + +bool Nim::find(byte x) { + bool ret = false; + for (int i = 0; i < 3; i++) { + if (_stones[i] == x) { + ret = true; + _inAp[i] = true; + } + } + return ret; +} + +void Nim::findAp(byte start, byte stepSize) { + byte thisOne = 0; + byte matches = 0; + + for (int i = 0; i < 3; i++) + _inAp[i] = 0; // Blank 'em all! + + for (int i = 0; i < 3; i++) { + if (find(start + i * stepSize)) + matches++; + else + thisOne = i; + } + + // Now... Matches must be 0, 1, 2, or 3. + // 0 / 1 mean there are no A.P.s here, so we'll keep looking, + // 2 means there is a potential A.P.that we can create (ideal!), and + // 3 means that we're already in an A.P. (Trouble!) + + byte ooo = 0; // Odd one out. + + switch (matches) { + case 2: + for (int i = 0; i < 3; i++) { // Find which one didn't fit the A.P. + if (!_inAp[i]) + ooo = i; + } + + if (_stones[ooo] > (start + thisOne * stepSize)) { // Check if it's possible! + // Create an A.P. + _row = ooo; // Already calculated. + // Start + thisone * stepsize will give the amount we SHOULD have here. + _number = _stones[_row] - (start + thisOne * stepSize); + _lmo = true; + return; + } + break; + case 3: // We're actually IN an A.P! Trouble! Oooh dear. + // Take 1 from the largest pile. + _row = _r[2]; + _number = 1; + _lmo = true; + return; + default: + break; + } +} + +void Nim::dogFood() { + _lmo = false; + byte live = 0; + byte sr[3]; + + for (int i = 0; i < 3; i++) { + if (_stones[i] > 0) { + _r[live] = i; + sr[live] = _stones[i]; + live++; + } + } + + switch (live) { + case 1: // Only one is free - so take 'em all! + _row = _r[0]; + _number = _stones[_r[0]]; + return; + case 2: // Two are free - make them equal! + if (sr[0] > sr[1]) { // T > b + _row = _r[0]; + _number = sr[0] - sr[1]; + } + else if (sr[0] < sr[1]) { // B > t + _row = _r[1]; + _number = sr[1] - sr[0]; + } + else { // B = t... oh no, we've lost! + _row = _r[0]; + _number = 1; + } + return; + case 3: { + // Ho hum... this'll be difficult! + // There are three possible courses of action when we have 3 lines left: + // 1) Look for 2 equal lines, then take the odd one out. + // 2) Look for A.P.s, and capitalise on them. + // 3) Go any old where. + const byte other[3][2] = { { 2, 3 }, { 1, 3 }, { 1, 2 } }; + + for (int i = 0; i < 3; i++) { // Look for 2 equal lines. + if (_stones[other[i][0]] == _stones[other[i][1]]) { + _row = i; // This row. + _number = _stones[i]; // All of 'em. + return; + } + } + + bool sorted; + do { + sorted = true; + for (int i = 0; i < 2; i++) { + if (sr[i] > sr[i + 1]) { + byte temp = sr[i + 1]; + sr[i + 1] = sr[i]; + sr[i] = temp; + + temp = _r[i + 1]; + _r[i + 1] = _r[i]; + _r[i] = temp; + + sorted = false; + } + } + } while (!sorted); + + // Now we look for A.P.s... + for (int i = 1; i <= 3; i++) { + findAp(i, 1); // There are 3 "1"s. + if (_lmo) + return; // Cut - out. + } + findAp(1, 2); // Only "2" possible. + if (_lmo) + return; + + // A.P.search must have failed - use the default move. + _row = _r[2]; + _number = 1; + return; + } + default: + break; + } +} + +} // End of namespace Avalanche diff --git a/engines/avalanche/nim.h b/engines/avalanche/nim.h new file mode 100644 index 0000000000..3c2f0455cf --- /dev/null +++ b/engines/avalanche/nim.h @@ -0,0 +1,79 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* + * This code is based on the original source code of Lord Avalot d'Argent version 1.3. + * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. + */ + +#ifndef AVALANCHE_NIM_H +#define AVALANCHE_NIM_H + +namespace Avalanche { + +class Nim { +public: + Nim(AvalancheEngine *vm); + void resetVariables(); + void synchronize(Common::Serializer &sz); + void playNim(); + +private: + AvalancheEngine *_vm; + + static const char * const kNames[2]; + + byte _old[3]; + byte _stones[3]; + byte _turns; + bool _dogfoodsTurn; + byte _stonesLeft; + bool _clicked; + int8 _row; + byte _number; + bool _squeak; + byte _playedNim; // How many times you've played Nim. + + // Inner variables for dogFood(), find() and findAp(). + bool _inAp[3]; + bool _lmo; // Let Me Out! + byte _r[3]; + + void chalk(int x, int y, Common::String text); + void setup(); + void board(); + void startMove(); + void showChanges(); + void blip(); + void findNextUp(); // Inner function for checkInput(). + void findNextDown(); // Same as above. + bool checkInput(); // It returns TRUE if the player confirmed his selection of stones either by pressing RETURN or by clicking on a stone. + void takeSome(); + void endOfGame(); + bool find(byte x); // This gives TRUE if there's a pile with x stones in. + void findAp(byte start, byte stepSize); + void dogFood(); // AI procedure to play the game. +}; + +} // End of namespace Avalanche + +#endif // AVALANCHE_NIM_H diff --git a/engines/avalanche/parser.cpp b/engines/avalanche/parser.cpp index fc176c78b0..220186ca5e 100644 --- a/engines/avalanche/parser.cpp +++ b/engines/avalanche/parser.cpp @@ -8,12 +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. - + * * 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 + * 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. @@ -27,6 +27,7 @@ #include "avalanche/avalanche.h" #include "avalanche/parser.h" +#include "avalanche/nim.h" #include "gui/saveload.h" @@ -37,6 +38,21 @@ const char *Parser::kVersionNum = "1.30"; Parser::Parser(AvalancheEngine *vm) { _vm = vm; + + _verb = kVerbCodePardon; + _thing = kPardon; + _person = kPeopleNone; + _polite = false; + _inputTextPos = 0; + _quote = false; + _cursorState = false; + _weirdWord = false; + _wearing = kNothing; + _thing2 = 0; + _sworeNum = 0; + _alcoholLevel = 0; + + _boughtOnion = false; } void Parser::init() { @@ -365,8 +381,8 @@ void Parser::init() { void Parser::handleInputText(const Common::Event &event) { byte inChar = event.kbd.ascii; warning("STUB: Parser::handleInputText()"); -// if (_vm->_menu->_activeMenuItem._activeNow) { -// _vm->_menu->parseKey(inChar, _vm->_enhanced->extd); +// if (_vm->_dropdown->_activeMenuItem._activeNow) { +// _vm->_dropdown->parseKey(inChar, _vm->_enhanced->extd); // } else { if (_inputText.size() < 76) { if ((inChar == '"') || (inChar == '`')) { @@ -386,7 +402,7 @@ void Parser::handleInputText(const Common::Event &event) { } void Parser::handleBackspace() { - if (_vm->_menu->_activeMenuItem._activeNow) + if (_vm->_dropdown->_activeMenuItem._activeNow) return; if (_inputTextPos > 0) { @@ -400,7 +416,7 @@ void Parser::handleBackspace() { } void Parser::handleReturn() { - if (_vm->_menu->_activeMenuItem._activeNow) + if (_vm->_dropdown->_activeMenuItem._activeNow) tryDropdown(); else if (!_inputText.empty()) { _inputTextBackup = _inputText; @@ -452,7 +468,7 @@ void Parser::handleFunctionKey(const Common::Event &event) { break; case Common::KEYCODE_F7: if (event.kbd.flags & Common::KBD_CTRL) - _vm->majorRedraw(); + _vm->_graphics->refreshScreen(); else _vm->callVerb(kVerbCodeOpen); break; @@ -506,10 +522,17 @@ void Parser::cursorOff() { _cursorState = false; } +/** + * Asks the parsekey proc in Dropdown if it knows it. + */ void Parser::tryDropdown() { - warning("STUB: Parser::tryDropdown()"); // TODO: Implement at the same time with Dropdown's keyboard handling. + // TODO: Implement at the same time with Dropdown's keyboard handling. + warning("STUB: Parser::tryDropdown()"); } +/** + * Returns the index of the first appearance of crit in src. + */ int16 Parser::getPos(const Common::String &crit, const Common::String &src) { if (src.contains(crit)) return strstr(src.c_str(),crit.c_str()) - src.c_str(); @@ -579,7 +602,7 @@ Common::String Parser::rank() { }; for (int i = 0; i < 8; i++) { - if ((_vm->_dnascore >= ranks[i]._score) && (_vm->_dnascore < ranks[i + 1]._score)) + if ((_vm->_score >= ranks[i]._score) && (_vm->_score < ranks[i + 1]._score)) return Common::String(ranks[i]._title); } return ""; @@ -588,8 +611,12 @@ Common::String Parser::rank() { Common::String Parser::totalTime() { uint16 h, m, s; - h = (uint16)(_vm->_totalTime / 65535); - s = (uint16)(_vm->_totalTime % 65535); + uint32 curTime = _vm->getTimeInSeconds() - _vm->_startTime; + if (_vm->_isLoaded) + curTime += _vm->_totalTime; + + h = (uint16)(curTime / 3600); + s = (uint16)(curTime % 3600); m = s / 60; s = s % 60; @@ -605,6 +632,9 @@ void Parser::cheatParse(Common::String codes) { warning("STUB: Parser::cheatParse()"); } +/** + * Strips punctuation from word. + */ void Parser::stripPunctuation(Common::String &word) { const char punct[] = "~`!@#$%^&*()_+-={}[]:\"|;'\\,./<>?"; @@ -692,13 +722,13 @@ void Parser::storeInterrogation(byte interrogation) { case 1: _inputText.toLowercase(); _vm->_dialogs->sayIt(_inputText); - _vm->_favouriteDrink = _inputText; + _vm->_favoriteDrink = _inputText; _vm->_cardiffQuestionNum = 2; break; case 2: properNouns(); _vm->_dialogs->sayIt(_inputText); - _vm->_favouriteSong = _inputText; + _vm->_favoriteSong = _inputText; _vm->_cardiffQuestionNum = 3; break; case 3: @@ -713,7 +743,7 @@ void Parser::storeInterrogation(byte interrogation) { if (!_vm->_spareEvening.empty()) _vm->_spareEvening.clear(); _vm->_spareEvening = _inputText; - _vm->_dialogs->displayScrollChain('z', 5); // His closing statement... + _vm->_dialogs->displayScrollChain('Z', 5); // His closing statement... _vm->_animation->_sprites[1]->walkTo(3); // The end of the drawbridge _vm->_animation->_sprites[1]->_vanishIfStill = true; // Then go away! _vm->_magics[1]._operation = kMagicNothing; @@ -729,8 +759,6 @@ void Parser::storeInterrogation(byte interrogation) { _vm->_timer->cardiffSurvey(); } - - void Parser::parse() { // First parsing - word identification if (!_thats.empty()) @@ -743,7 +771,6 @@ void Parser::parse() { _person = kPeoplePardon; clearWords(); - // A cheat mode attempt. if (_inputText[0] == '.') { cheatParse(_inputText); @@ -906,7 +933,7 @@ void Parser::parse() { _polite = true; } - if ((!unkString.empty()) && (_verb != kVerbCodeExam) && (_verb != kVerbCodeTalk) && + if ((!unkString.empty()) && (_verb != kVerbCodeExam) && (_verb != kVerbCodeTalk) && (_verb != kVerbCodeSave) && (_verb != kVerbCodeLoad) && (_verb != kVerbCodeDir)) { Common::String tmpStr = Common::String::format("Sorry, but I have no idea what \"%s\" means. Can you rephrase it?", unkString.c_str()); _vm->_dialogs->displayText(tmpStr); @@ -928,6 +955,9 @@ void Parser::parse() { } } +/** + * Examine a standard object-thing + */ void Parser::examineObject() { if (_thing != _vm->_thinks) _vm->thinkAbout(_thing, AvalancheEngine::kThing); @@ -937,29 +967,29 @@ void Parser::examineObject() { switch (_vm->_wineState) { case 1: // Normal examine wine scroll - _vm->_dialogs->displayScrollChain('t', 1); + _vm->_dialogs->displayScrollChain('T', 1); break; case 2: // Bad wine - _vm->_dialogs->displayScrollChain('d', 6); + _vm->_dialogs->displayScrollChain('D', 6); break; case 3: // Vinegar - _vm->_dialogs->displayScrollChain('d', 7); + _vm->_dialogs->displayScrollChain('D', 7); break; } break; case kObjectOnion: if (_vm->_rottenOnion) // Yucky onion - _vm->_dialogs->displayScrollChain('q', 21); + _vm->_dialogs->displayScrollChain('Q', 21); else // Normal onion - _vm->_dialogs->displayScrollChain('t', 18); + _vm->_dialogs->displayScrollChain('T', 18); break; default: // Ordinarily - _vm->_dialogs->displayScrollChain('t', _thing); + _vm->_dialogs->displayScrollChain('T', _thing); } } @@ -992,7 +1022,7 @@ void Parser::exampers() { // He's asleep. _vm->_dialogs->displayScrollChain('Q', 65); else - _vm->_dialogs->displayScrollChain('p', newPerson); + _vm->_dialogs->displayScrollChain('P', newPerson); if ((_person == kPeopleAyles) && !_vm->_aylesIsAwake) _vm->_dialogs->displayScrollChain('Q', 13); @@ -1010,15 +1040,20 @@ bool Parser::isHolding() { // Also object if ((51 <= _thing) && (_thing <= 99)) return true; + if (_thing == 0) + return false; bool holdingResult = false; - if (_thing > 100) + if (_thing >= 100) _vm->_dialogs->displayText("Be reasonable!"); - else if (!_vm->_objects[_thing - 1]) - // Verbs that need "_thing" to be in the inventory. - _vm->_dialogs->displayText("You're not holding it, Avvy."); - else + else if (_thing <= kObjectNum) { + if (!_vm->_objects[_thing - 1]) + // Verbs that need "_thing" to be in the inventory. + _vm->_dialogs->displayText("You're not holding it, Avvy."); + else + holdingResult = true; + } else holdingResult = true; return holdingResult; @@ -1053,8 +1088,10 @@ void Parser::examine() { examineObject(); else if ((50 <= _thing) && (_thing <= 100)) { // Also _thing + int id = _thing - 50; + assert(id < 31); openBox(true); - _vm->_dialogs->displayText(*_vm->_also[_thing - 50][1]); + _vm->_dialogs->displayText(*_vm->_also[id][1]); openBox(false); } } @@ -1107,7 +1144,7 @@ void Parser::swallow() { return; } _vm->_dialogs->displayScrollChain('U', 1); - _vm->_pingo->wobble(); + _vm->_animation->wobble(); _vm->_dialogs->displayScrollChain('U', 2); _vm->_objects[kObjectWine - 1] = false; _vm->refreshObjectList(); @@ -1116,7 +1153,7 @@ void Parser::swallow() { case 2: case 3: // You can't drink it! - _vm->_dialogs->displayScrollChain('d', 8); + _vm->_dialogs->displayScrollChain('D', 8); break; } break; @@ -1153,6 +1190,9 @@ void Parser::swallow() { } } +/** + * this lists the other people in the room. + */ void Parser::peopleInRoom() { // First compute the number of people in the room. byte numPeople = 0; @@ -1190,42 +1230,45 @@ void Parser::peopleInRoom() { _vm->_dialogs->displayText(tmpStr + " here."); } +/** + * This is called when you say "look". + */ void Parser::lookAround() { _vm->_dialogs->displayText(*_vm->_also[0][1]); switch (_vm->_room) { case kRoomSpludwicks: if (_vm->_avariciusTalk > 0) - _vm->_dialogs->displayScrollChain('q', 23); + _vm->_dialogs->displayScrollChain('Q', 23); else peopleInRoom(); break; case kRoomRobins: if (_vm->_tiedUp) - _vm->_dialogs->displayScrollChain('q', 38); + _vm->_dialogs->displayScrollChain('Q', 38); if (_vm->_mushroomGrowing) - _vm->_dialogs->displayScrollChain('q', 55); + _vm->_dialogs->displayScrollChain('Q', 55); break; case kRoomInsideCardiffCastle: if (!_vm->_takenPen) - _vm->_dialogs->displayScrollChain('q', 49); + _vm->_dialogs->displayScrollChain('Q', 49); break; case kRoomLustiesRoom: if (_vm->_lustieIsAsleep) - _vm->_dialogs->displayScrollChain('q', 65); + _vm->_dialogs->displayScrollChain('Q', 65); break; case kRoomCatacombs: switch (_vm->_catacombY * 256 + _vm->_catacombX) { case 258 : // Inside art gallery. - _vm->_dialogs->displayScrollChain('q', 80); + _vm->_dialogs->displayScrollChain('Q', 80); break; case 514 : // Outside ditto. - _vm->_dialogs->displayScrollChain('q', 81); + _vm->_dialogs->displayScrollChain('Q', 81); break; case 260 : // Outside Geida's room. - _vm->_dialogs->displayScrollChain('q', 82); + _vm->_dialogs->displayScrollChain('Q', 82); break; } break; @@ -1248,7 +1291,7 @@ void Parser::openDoor() { break; case kRoomSpludwicks: if (_thing == 61) { - _vm->_dialogs->displayScrollChain('q', 85); + _vm->_dialogs->displayScrollChain('Q', 85); return; } break; @@ -1266,7 +1309,7 @@ void Parser::openDoor() { switch (portal->_operation) { case kMagicExclaim: _vm->_animation->_sprites[0]->bounce(); - _vm->_dialogs->displayScrollChain('x', portal->_data); + _vm->_dialogs->displayScrollChain('X', portal->_data); break; case kMagicTransport: _vm->flipRoom((Room)((portal->_data) >> 8), portal->_data & 0x0F); @@ -1294,6 +1337,9 @@ void Parser::openDoor() { _vm->_dialogs->displayText("Door? What door?"); } +/** + * Called when you call kVerbCodeput. + */ void Parser::putProc() { if (!isHolding()) return; @@ -1322,7 +1368,7 @@ void Parser::putProc() { // Put onion into vinegar! Yes! _vm->_onionInVinegar = true; _vm->incScore(7); - _vm->_dialogs->displayScrollChain('u', 9); + _vm->_dialogs->displayScrollChain('U', 9); } } } else @@ -1400,6 +1446,7 @@ void Parser::goToCauldron() { /** * Check is it's possible to give something to Spludwick + * The result of this function is whether or not he says "Hey, thanks!". * @remarks Originally called 'give2spludwick' */ bool Parser::giveToSpludwick() { @@ -1412,10 +1459,10 @@ bool Parser::giveToSpludwick() { case kObjectOnion: _vm->_objects[kObjectOnion - 1] = false; if (_vm->_rottenOnion) - _vm->_dialogs->displayScrollChain('q', 22); + _vm->_dialogs->displayScrollChain('Q', 22); else { _vm->_givenToSpludwick++; - _vm->_dialogs->displayScrollChain('q', 20); + _vm->_dialogs->displayScrollChain('Q', 20); goToCauldron(); _vm->incScore(3); } @@ -1425,13 +1472,13 @@ bool Parser::giveToSpludwick() { _vm->_objects[kObjectInk - 1] = false; _vm->refreshObjectList(); _vm->_givenToSpludwick++; - _vm->_dialogs->displayScrollChain('q', 24); + _vm->_dialogs->displayScrollChain('Q', 24); goToCauldron(); _vm->incScore(3); break; case kObjectMushroom: _vm->_objects[kObjectMushroom - 1] = false; - _vm->_dialogs->displayScrollChain('q', 25); + _vm->_dialogs->displayScrollChain('Q', 25); _vm->incScore(5); _vm->_givenToSpludwick++; goToCauldron(); @@ -1479,6 +1526,9 @@ void Parser::already() { _vm->_dialogs->displayText("You're already standing!"); } +/** + * Called when you ask Avvy to stand. + */ void Parser::standUp() { switch (_vm->_room) { case kRoomYours: @@ -1486,9 +1536,9 @@ void Parser::standUp() { if (_vm->_avvyIsAwake && _vm->_avvyInBed) { // But he's in bed. if (_vm->_teetotal) { - _vm->_dialogs->displayScrollChain('d', 12); + _vm->_dialogs->displayScrollChain('D', 12); _vm->_graphics->setBackgroundColor(kColorBlack); - _vm->_dialogs->displayScrollChain('d', 14); + _vm->_dialogs->displayScrollChain('D', 14); } _vm->_animation->_sprites[0]->_visible = true; _vm->_userMovesAvvy = true; @@ -1543,7 +1593,7 @@ void Parser::getProc(char thing) { _vm->_dialogs->displayText(tmpStr); } } else - _vm->_dialogs->displayScrollChain('q', 57); + _vm->_dialogs->displayScrollChain('Q', 57); break; case kRoomInsideCardiffCastle: switch (thing) { @@ -1564,15 +1614,15 @@ void Parser::getProc(char thing) { _vm->_dialogs->displayText("Taken."); } } else if (_vm->_standingOnDais) - _vm->_dialogs->displayScrollChain('q', 53); + _vm->_dialogs->displayScrollChain('Q', 53); else - _vm->_dialogs->displayScrollChain('q', 51); + _vm->_dialogs->displayScrollChain('Q', 51); break; case kObjectBolt: - _vm->_dialogs->displayScrollChain('q', 52); + _vm->_dialogs->displayScrollChain('Q', 52); break; default: - _vm->_dialogs->displayScrollChain('q', 57); + _vm->_dialogs->displayScrollChain('Q', 57); } break; case kRoomRobins: @@ -1585,10 +1635,10 @@ void Parser::getProc(char thing) { _vm->refreshObjectList(); _vm->incScore(3); } else - _vm->_dialogs->displayScrollChain('q', 57); + _vm->_dialogs->displayScrollChain('Q', 57); break; default: - _vm->_dialogs->displayScrollChain('q', 57); + _vm->_dialogs->displayScrollChain('Q', 57); } } @@ -1605,7 +1655,7 @@ void Parser::giveGeidaTheLute() { _vm->_objects[kObjectLute - 1] = false; _vm->refreshObjectList(); // She plays it. - _vm->_dialogs->displayScrollChain('q', 64); + _vm->_dialogs->displayScrollChain('Q', 64); _vm->_timer->addTimer(1, Timer::kProcGiveLuteToGeida, Timer::kReasonGeidaSings); //_vm->_enid->backToBootstrap(4); TODO: Replace it with proper ScummVM-friendly function(s)! Do not remove until then! @@ -1619,7 +1669,7 @@ void Parser::playHarp() { } void Parser::winSequence() { - _vm->_dialogs->displayScrollChain('q', 78); + _vm->_dialogs->displayScrollChain('Q', 78); _vm->_sequence->startWinSeq(); _vm->_timer->addTimer(30, Timer::kProcWinning, Timer::kReasonWinning); } @@ -1644,6 +1694,10 @@ void Parser::doThat() { // "Slip" object _thing -= 49; + if (_vm->_tiedUp) { + _vm->_dialogs->displayText("You better stay quiet now!"); + return; + } if ((_verb != kVerbCodeLoad) && (_verb != kVerbCodeSave) && (_verb != kVerbCodeQuit) && (_verb != kVerbCodeInfo) && (_verb != kVerbCodeHelp) && (_verb != kVerbCodeLarrypass) && (_verb != kVerbCodePhaon) && (_verb != kVerbCodeBoss) && (_verb != kVerbCodeCheat) && (_verb != kVerbCodeRestart) @@ -1653,7 +1707,7 @@ void Parser::doThat() { "Try restarting, or restoring a saved game!"); return; } - if (!_vm->_avvyIsAwake && (_verb != kVerbCodeDie) && (_verb != kVerbCodeExpletive) && (_verb != kVerbCodeWake)) { + if (!_vm->_avvyIsAwake && (_verb != kVerbCodeWake)) { _vm->_dialogs->displayText("Talking in your sleep? Try waking up!"); return; } @@ -1666,7 +1720,7 @@ void Parser::doThat() { case kVerbCodeOpen: openDoor(); break; - case kVerbCodePause: { + case kVerbCodePause: { // Note that the original game doesn't care about the "O.K." box neither, it accepts // clicks from everywhere on the screen to continue. Just like my code. Common::String tmpStr = Common::String::format("Game paused.%c%c%cPress Enter, Esc, or click the mouse on the `O.K.\" " \ @@ -1758,7 +1812,7 @@ void Parser::doThat() { break; case kPeopleIbythneth: if (_thing == kObjectBadge) { - _vm->_dialogs->displayScrollChain('q', 32); // Thanks! Wow! + _vm->_dialogs->displayScrollChain('Q', 32); // Thanks! Wow! _vm->incScore(3); _vm->_objects[kObjectBadge - 1] = false; _vm->_objects[kObjectHabit - 1] = true; @@ -1772,7 +1826,7 @@ void Parser::doThat() { if (_vm->_aylesIsAwake) { if (_thing == kObjectPen) { _vm->_objects[kObjectPen - 1] = false; - _vm->_dialogs->displayScrollChain('q', 54); + _vm->_dialogs->displayScrollChain('Q', 54); _vm->_objects[kObjectInk - 1] = true; _vm->_givenPenToAyles = true; _vm->refreshObjectList(); @@ -1787,7 +1841,7 @@ void Parser::doThat() { case kObjectPotion: _vm->_objects[kObjectPotion - 1] = false; // She drinks it. - _vm->_dialogs->displayScrollChain('u', 16); + _vm->_dialogs->displayScrollChain('U', 16); _vm->incScore(2); _vm->_givenPotionToGeida = true; _vm->refreshObjectList(); @@ -1806,7 +1860,7 @@ void Parser::doThat() { winSequence(); else // That Geida woman! - _vm->_dialogs->displayScrollChain('q', 77); + _vm->_dialogs->displayScrollChain('Q', 77); break; default: _vm->_dialogs->sayThanks(_thing - 1); @@ -1836,7 +1890,7 @@ void Parser::doThat() { if (savegameId < 0) // dialog aborted, nothing to load return; - + _vm->loadGame(savegameId); } break; @@ -1940,45 +1994,7 @@ void Parser::doThat() { // They just typed "play"... switch (_vm->_room) { case kRoomArgentPub: - // ...in the pub, => play Nim. - warning("STUB: Parser::doThat() - case kVerbCodeplay - play_nim()"); - // play_nim(); - - // The following parts are copied from play_nim(). - // The player automatically wins the game everytime he wins, until I implement the mini-game. - if (_vm->_wonNim) { // Already won the game. - _vm->_dialogs->displayScrollChain('Q', 6); - return; - } - - if (!_vm->_askedDogfoodAboutNim) { - _vm->_dialogs->displayScrollChain('q', 84); - return; - } - - _vm->_dialogs->displayScrollChain('Q', 3); - _playedNim++; - - // You won - strange! - - // You won! Give us a lute! - _vm->_dialogs->displayScrollChain('Q', 7); - _vm->_objects[kObjectLute - 1] = true; - _vm->refreshObjectList(); - _vm->_wonNim = true; - // Show the settle with no lute on it. - _vm->_background->draw(-1, -1, 0); - // 7 points for winning! - _vm->incScore(7); - - if (_playedNim == 1) - // 3 points for playing your 1st game. - _vm->incScore(3); - - // A warning to the player that there should have been a mini-game. TODO: Remove it later!!! - _vm->_dialogs->displayText(Common::String("P.S.: There should have been the mini-game called \"Nim\", " \ - "but I haven't implemented it yet: you win and get the lute automatically.") - + kControlNewLine + kControlNewLine + "Peter (uruk)"); + _vm->_nim->playNim(); // ...in the pub, => play Nim. break; case kRoomMusicRoom: playHarp(); @@ -2005,8 +2021,7 @@ void Parser::doThat() { break; case 55: if (_vm->_room == kRoomArgentPub) - // play_nim(); - warning("STUB: Parser::doThat() - case kVerbCodeplay - play_nim()"); + _vm->_nim->playNim(); else _vm->_dialogs->displayText(kWhat); break; @@ -2027,8 +2042,7 @@ void Parser::doThat() { } break; case kVerbCodeHelp: - // boot_help(); - warning("STUB: Parser::doThat() - case kVerbCodehelp"); + _vm->_help->run(); break; case kVerbCodeLarrypass: _vm->_dialogs->displayText("Wrong game!"); @@ -2037,8 +2051,7 @@ void Parser::doThat() { _vm->_dialogs->displayText("Hello, Phaon!"); break; case kVerbCodeBoss: - // bosskey(); - warning("STUB: Parser::doThat() - case kVerbCodeboss"); + bossKey(); break; case kVerbCodePee: if (_vm->getFlag('P')) { @@ -2057,13 +2070,13 @@ void Parser::doThat() { break; case kVerbCodeMagic: if (_vm->_avariciusTalk > 0) - _vm->_dialogs->displayScrollChain('q', 19); + _vm->_dialogs->displayScrollChain('Q', 19); else { if ((_vm->_room == kRoomSpludwicks) & (_vm->_animation->inField(1))) { // Avaricius appears! - _vm->_dialogs->displayScrollChain('q', 17); + _vm->_dialogs->displayScrollChain('Q', 17); if (_vm->getRoom(kPeopleSpludwick) == kRoomSpludwicks) - _vm->_dialogs->displayScrollChain('q', 18); + _vm->_dialogs->displayScrollChain('Q', 18); else { Avalanche::AnimationType *spr = _vm->_animation->_sprites[1]; // Avaricius @@ -2097,7 +2110,7 @@ void Parser::doThat() { } break; default: { - _vm->_pingo->zonk(); + _vm->_animation->thunder(); Common::String tmpStr = Common::String::format("A crack of lightning shoots from the sky, and fries you." \ "%c%c(`Such is the anger of the gods, Avvy!\")", kControlNewLine, kControlNewLine); _vm->_dialogs->displayText(tmpStr); @@ -2217,7 +2230,7 @@ void Parser::doThat() { case kRoomNottsPub: // Can't sell to southerners. - _vm->_dialogs->displayScrollChain('n', 15); + _vm->_dialogs->displayScrollChain('N', 15); break; default: // Can't buy that. @@ -2293,7 +2306,7 @@ void Parser::doThat() { break; case kVerbCodeScore: { Common::String tmpStr = Common::String::format("Your score is %d,%c%cout of a possible 128.%c%c " \ - "This gives you a rank of %s.%c%c%s", _vm->_dnascore, kControlCenter, kControlNewLine, kControlNewLine, + "This gives you a rank of %s.%c%c%s", _vm->_score, kControlCenter, kControlNewLine, kControlNewLine, kControlNewLine, rank().c_str(), kControlNewLine, kControlNewLine, totalTime().c_str()); _vm->_dialogs->displayText(tmpStr); } @@ -2354,7 +2367,7 @@ void Parser::doThat() { // Picture of Avvy, awake in bed. _vm->_background->draw(-1, -1, 2); if (_vm->_teetotal) - _vm->_dialogs->displayScrollChain('d', 13); + _vm->_dialogs->displayScrollChain('D', 13); } else _vm->_dialogs->displayText("You're already awake, Avvy!"); break; @@ -2411,6 +2424,25 @@ void Parser::doThat() { } } +void Parser::bossKey() { + _vm->_graphics->saveScreen(); + _vm->_graphics->blackOutScreen(); + _vm->_graphics->loadMouse(kCurUpArrow); + _vm->loadBackground(98); + _vm->_graphics->drawNormalText("Graph/Histo/Draw/Sample: \"JANJUN93.GRA\": (W3-AB3)", _vm->_font, 8, 120, 169, kColorDarkgray); + _vm->_graphics->drawNormalText("Press any key or click the mouse to return.", _vm->_font, 8, 144, 182, kColorDarkgray); + _vm->_graphics->refreshScreen(); + Common::Event event; + _vm->getEvent(event); + while ((!_vm->shouldQuit()) && (event.type != Common::EVENT_KEYDOWN) && (event.type != Common::EVENT_LBUTTONDOWN)){ + _vm->getEvent(event); + _vm->_graphics->refreshScreen(); + } + _vm->_graphics->restoreScreen(); + _vm->_graphics->removeBackup(); + _vm->loadBackground(_vm->_room); +} + void Parser::verbOpt(byte verb, Common::String &answer, char &ansKey) { // kVerbCodegive isn't dealt with by this procedure, but by ddm__with. switch (verb) { @@ -2452,10 +2484,9 @@ void Parser::doVerb(VerbCode id) { } void Parser::resetVariables() { - _wearing = 0; + _wearing = kNothing; _sworeNum = 0; _alcoholLevel = 0; - _playedNim = 0; _boughtOnion = false; } @@ -2463,7 +2494,10 @@ void Parser::synchronize(Common::Serializer &sz) { sz.syncAsByte(_wearing); sz.syncAsByte(_sworeNum); sz.syncAsByte(_alcoholLevel); - sz.syncAsByte(_playedNim); + if (sz.isLoading() && sz.getVersion() < 2) { + int dummy; + sz.syncAsByte(dummy); + } sz.syncAsByte(_boughtOnion); } diff --git a/engines/avalanche/parser.h b/engines/avalanche/parser.h index 261e5ecefe..6133c41442 100644 --- a/engines/avalanche/parser.h +++ b/engines/avalanche/parser.h @@ -8,12 +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. - + * * 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 + * 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. @@ -34,17 +34,16 @@ #include "common/str.h" #include "common/serializer.h" - namespace Avalanche { class AvalancheEngine; class Parser { public: - static const byte kPardon = 254; // Didn't understand / wasn't given. static const int16 kParserWordsNum = 277; // How many words does the parser know? + static const int16 kFirstPassword = 88; // words[kFirstPassword] should equal "TIROS". + static const byte kPardon = 254; // Didn't understand / wasn't given. static const byte kNothing = 250; - static const byte kMoved = 0; // This word was moved. (Usually because it was the subject of conversation.) - static const int16 kFirstPassword = 88; // words[kFirstPassword] should equal "TIROS". + static const byte kMoved = 0; // This word was moved. (Usually because it was the subject of conversation.) struct VocabEntry { byte _number; @@ -63,23 +62,21 @@ public: byte _thing; People _person; bool _polite; - Common::String _inputText; // Original name: current + Common::String _inputText; Common::String _inputTextBackup; - byte _inputTextPos; // Original name: curpos - bool _quote; // 66 or 99 next? + byte _inputTextPos; + bool _quote; bool _cursorState; bool _weirdWord; byte _wearing; // what you're wearing Parser(AvalancheEngine *vm); - void init(); void parse(); void doThat(); void verbOpt(byte verb, Common::String &answer, char &ansKey); void drink(); - void handleInputText(const Common::Event &event); void handleBackspace(); void handleReturn(); @@ -87,10 +84,10 @@ public: void plotText(); void cursorOn(); void cursorOff(); - void tryDropdown(); // This asks the parsekey proc in Dropdown if it knows it. - int16 getPos(const Common::String &crit, const Common::String &src); // Returns the index of the first appearance of crit in src. + void tryDropdown(); + int16 getPos(const Common::String &crit, const Common::String &src); void doVerb(VerbCode id); - + Common::String rank(); void resetVariables(); void synchronize(Common::Serializer &sz); @@ -107,27 +104,23 @@ private: Common::String _thats; byte _thing2; - byte _sworeNum; // number of times you've sworn + byte _sworeNum; // number of times you've sworn byte _alcoholLevel; // Your blood alcohol level. - byte _playedNim; // How many times you've played Nim. - bool _boughtOnion; // Have you bought an onion yet? + bool _boughtOnion; // Have you bought an onion yet? byte wordNum(Common::String word); void replace(Common::String oldChars, byte newChar); - - Common::String rank(); Common::String totalTime(); - void clearWords(); void cheatParse(Common::String codes); - void stripPunctuation(Common::String &word); // Strips punctuation from word. - void displayWhat(byte target, bool animate, bool &ambiguous); // << It's an adjective! + void stripPunctuation(Common::String &word); + void displayWhat(byte target, bool animate, bool &ambiguous); bool doPronouns(); void properNouns(); - void lookAround(); // This is called when you say "look". + void lookAround(); void openDoor(); void storeInterrogation(byte interrogation); - void examineObject(); // Examine a standard object-thing + void examineObject(); bool isPersonHere(); void exampers(); bool isHolding(); @@ -135,19 +128,20 @@ private: void examine(); void inventory(); void swallow(); - void peopleInRoom(); // This lists the other people in the room. - void putProc(); // Called when you call kVerbCodeput. + void peopleInRoom(); + void putProc(); void notInOrder(); void goToCauldron(); - bool giveToSpludwick(); // The result of this fn is whether or not he says "Hey, thanks!". + bool giveToSpludwick(); void cardiffClimbing(); void already(); - void standUp(); // Called when you ask Avvy to stand. + void standUp(); void getProc(char thing); void giveGeidaTheLute(); void playHarp(); void winSequence(); void wipeText(); + void bossKey(); }; } // End of namespace Avalanche diff --git a/engines/avalanche/pingo.cpp b/engines/avalanche/pingo.cpp deleted file mode 100644 index 433924f594..0000000000 --- a/engines/avalanche/pingo.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -/* - * This code is based on the original source code of Lord Avalot d'Argent version 1.3. - * Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. - */ - -/* PINGO Full-screen sub-parts of the game. */ - -#include "avalanche/avalanche.h" -#include "avalanche/pingo.h" - -namespace Avalanche { - -Pingo::Pingo(AvalancheEngine *vm) { - _vm = vm; -} - -void Pingo::dPlot(int16 x, int16 y, Common::String z) { - warning("STUB: Pingo::dPlot()"); -} - -void Pingo::bossKey() { - warning("STUB: Pingo::bossKey()"); -} - -void Pingo::copy02() { // taken from Wobble (below) - warning("STUB: Pingo::copy02()"); -} - -void Pingo::copy03() { // taken from Wobble (below) - warning("STUB: Pingo::copy03()"); -} - -void Pingo::copyPage(byte frp, byte top) { // taken from Copy02 (above) - warning("STUB: Pingo::copyPage()"); -} - -void Pingo::wobble() { - warning("STUB: Pingo::wobble()"); -} - -void Pingo::zl(int16 x1, int16 y1, int16 x2, int16 y2) { - warning("STUB: Pingo::zl()"); -} - -void Pingo::zonk() { - warning("STUB: Pingo::zonk()"); -} - -void Pingo::winningPic() { - Common::File f; - _vm->fadeOut(); - - if (!f.open("finale.avd")) - error("AVALANCHE: File not found: finale.avd"); - -#if 0 - for (int bit = 0; bit <= 3; bit++) { - port[0x3c4] = 2; - port[0x3ce] = 4; - port[0x3c5] = 1 << bit; - port[0x3cf] = bit; - blockread(f, mem[0xa000 * 0], 16000); - } -#endif - - f.close(); - - warning("STUB: Pingo::winningPic()"); - - _vm->fadeIn(); - -#if 0 - do { - _vm->check(); - } while (!(keypressed() || (mrelease > 0))); - while (keypressed()) - char r = readkey(); - major_redraw(); -#endif - - warning("STUB: Pingo::winningPic()"); -} - -} // End of namespace Avalanche. diff --git a/engines/avalanche/sequence.cpp b/engines/avalanche/sequence.cpp index 10fa7f0a00..d63532a457 100644 --- a/engines/avalanche/sequence.cpp +++ b/engines/avalanche/sequence.cpp @@ -8,12 +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. - + * * 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. @@ -34,6 +34,8 @@ namespace Avalanche { Sequence::Sequence(AvalancheEngine *vm) { _vm = vm; + + resetVariables(); } void Sequence::resetVariables() { diff --git a/engines/avalanche/sequence.h b/engines/avalanche/sequence.h index d3c1b54963..8062118059 100644 --- a/engines/avalanche/sequence.h +++ b/engines/avalanche/sequence.h @@ -8,12 +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. - + * * 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. diff --git a/engines/avalanche/shootemup.cpp b/engines/avalanche/shootemup.cpp new file mode 100644 index 0000000000..e5e44ed934 --- /dev/null +++ b/engines/avalanche/shootemup.cpp @@ -0,0 +1,693 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* +* This code is based on the original source code of Lord Avalot d'Argent version 1.3. +* Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. +*/ + +#include "avalanche/avalanche.h" +#include "avalanche/shootemup.h" + +#include "common/random.h" + +namespace Avalanche { + +const byte ShootEmUp::kStocks = 27; +const byte ShootEmUp::kAvvyShoots = 86; +const byte ShootEmUp::kFacingRight = 87; +const byte ShootEmUp::kFacingLeft = 93; +const long int ShootEmUp::kFlag = -20047; +const byte ShootEmUp::kFrameDelayMax = 2; +const byte ShootEmUp::kAvvyY = 150; +const byte ShootEmUp::kShooting[7] = { 86, 79, 80, 81, 80, 79, 86 }; +const byte ShootEmUp::kTimesASecond = 18; +const byte ShootEmUp::kFlashTime = 20; // If flash_time is <= this, the word "time" will flash. Should be about 20. +const byte ShootEmUp::kLeftMargin = 10; +const int16 ShootEmUp::kRightMargin = 605; + +ShootEmUp::ShootEmUp(AvalancheEngine *vm) { + _vm = vm; + + _time = 120; + for (int i = 0; i < 7; i++) + _stockStatus[i] = 0; + for (int i = 0; i < 99; i++) { + _sprites[i]._ix = 0; + _sprites[i]._iy = 0; + _sprites[i]._x = kFlag; + _sprites[i]._y = 0; + _sprites[i]._p = 0; + _sprites[i]._timeout = 0; + _sprites[i]._cameo = false; + _sprites[i]._cameoFrame = 0; + _sprites[i]._missile = false; + _sprites[i]._wipe = false; + } + _rectNum = 0; + _avvyWas = 320; + _avvyPos = 320; + _avvyAnim = 1; + _avvyFacing = kFacingLeft; + _altWasPressedBefore = false; + _throwNext = 73; + _firing = false; + for (int i = 0; i < 4; i++) { + _running[i]._x = kFlag; + _running[i]._y = 0; + _running[i]._frame = 0; + _running[i]._tooHigh = 0; + _running[i]._lowest = 0; + _running[i]._ix = 0; + _running[i]._iy = 0; + _running[i]._frameDelay = 0; + } + for (int i = 0; i < 7; i++) + _hasEscaped[i] = false; + _count321 = 255; // Counting down. + _howManyHaveEscaped = 0; + _escapeCount = 0; + _escaping = false; + _timeThisSecond = 0; + _cp = false; + _wasFacing = 0; + _score = 0; + _escapeStock = 0; + _gotOut = false; +} + +uint16 ShootEmUp::run() { + CursorMan.showMouse(false); + _vm->_graphics->saveScreen(); + _vm->fadeOut(); + _vm->_graphics->seuDrawTitle(); + _vm->fadeIn(); + + _vm->_graphics->seuLoad(); + + // Should we show the instructions? + while (!_vm->shouldQuit()) { + Common::Event event; + _vm->getEvent(event); + if (event.type == Common::EVENT_KEYDOWN) { + if ((event.kbd.keycode == Common::KEYCODE_i) || (event.kbd.keycode == Common::KEYCODE_F1)) + instructions(); + break; // We don't show the instructions and simply go on with the minigame if not I or F1 was pressed. + } + } + + setup(); + + while ((_time != 0) && (!_vm->shouldQuit())) { + uint32 beginLoop = _vm->_system->getMillis(); + + blankIt(); + hitPeople(); + plotThem(); + moveThem(); + moveAvvy(); + bumpFolk(); + peopleRunning(); + animate(); + escapeCheck(); + collisionCheck(); + updateTime(); + check321(); + readKbd(); + + _cp = !_cp; + + _vm->_graphics->refreshScreen(); + + uint32 delay = _vm->_system->getMillis() - beginLoop; + if (delay <= 55) + _vm->_system->delayMillis(55 - delay); // Replaces slowdown(); 55 comes from 18.2 Hz (B Flight). + }; + + _vm->fadeOut(); + _vm->_graphics->restoreScreen(); + _vm->_graphics->removeBackup(); + _vm->fadeIn(); + CursorMan.showMouse(true); + + return _score; +} + +bool ShootEmUp::overlap(uint16 a1x, uint16 a1y, uint16 a2x, uint16 a2y, uint16 b1x, uint16 b1y, uint16 b2x, uint16 b2y) { + // By De Morgan's law: + return (a2x >= b1x) && (b2x >= a1x) && (a2y >= b1y) && (b2y >= a1y); +} + +byte ShootEmUp::getStockNumber(byte index) { + while (_hasEscaped[index]) { + index++; + if (index == 7) + index = 0; + } + return index; +} + +void ShootEmUp::blankIt() { + for (int i = 0; i < _rectNum; i++) + _vm->_graphics->drawFilledRectangle(_rectangles[i], kColorBlack); + _rectNum = 0; +} + +void ShootEmUp::moveThem() { + for (int i = 0; i < 99; i++) { + if (_sprites[i]._x != kFlag) { + _sprites[i]._x += _sprites[i]._ix; + _sprites[i]._y += _sprites[i]._iy; + } + } +} + +void ShootEmUp::blank(Common::Rect rect) { + _rectangles[_rectNum++] = rect; +} + +void ShootEmUp::plotThem() { + for (int i = 0; i < 99; i++) { + if (_sprites[i]._x != kFlag) { + if (_sprites[i]._cameo) { + _vm->_graphics->seuDrawCameo(_sprites[i]._x, _sprites[i]._y, _sprites[i]._p, _sprites[i]._cameoFrame); + if (!_cp) { + _sprites[i]._cameoFrame += 2; + _sprites[i]._p += 2; + } + } else + _vm->_graphics->seuDrawPicture(_sprites[i]._x, _sprites[i]._y, _sprites[i]._p); + + if (_sprites[i]._wipe) + blank(Common::Rect(_sprites[i]._x, _sprites[i]._y, _sprites[i]._x + _vm->_graphics->seuGetPicWidth(_sprites[i]._p), _sprites[i]._y + _vm->_graphics->seuGetPicHeight(_sprites[i]._p))); + + if (_sprites[i]._timeout > 0) { + _sprites[i]._timeout--; + if (_sprites[i]._timeout == 0) + _sprites[i]._x = kFlag; + } + } + } +} + +void ShootEmUp::define(int16 x, int16 y, int8 p, int8 ix, int8 iy, int16 time, bool isAMissile, bool doWeWipe) { + for (int i = 0; i < 99; i++) { + if (_sprites[i]._x == kFlag) { + _sprites[i]._x = x; + _sprites[i]._y = y; + _sprites[i]._p = p; + _sprites[i]._ix = ix; + _sprites[i]._iy = iy; + _sprites[i]._timeout = time; + _sprites[i]._cameo = false; + _sprites[i]._missile = isAMissile; + _sprites[i]._wipe = doWeWipe; + return; + } + } +} + +void ShootEmUp::defineCameo(int16 x, int16 y, int8 p, int16 time) { + for (int i = 0; i < 99; i++) { + if (_sprites[i]._x == kFlag) { + _sprites[i]._x = x; + _sprites[i]._y = y; + _sprites[i]._p = p; + _sprites[i]._ix = 0; + _sprites[i]._iy = 0; + _sprites[i]._timeout = time; + _sprites[i]._cameo = true; + _sprites[i]._cameoFrame = p + 1; + _sprites[i]._missile = false; + _sprites[i]._wipe = false; + return; + } + } +} + +void ShootEmUp::showStock(byte index) { + if (_escaping && (index == _escapeStock)) { + _vm->_graphics->seuDrawPicture(index * 90 + 20, 30, kStocks + 2); + return; + } + + if (_stockStatus[index] > 5) + return; + + _vm->_graphics->seuDrawPicture(index * 90 + 20, 30, kStocks + _stockStatus[index]); + _stockStatus[index] = 1 - _stockStatus[index]; +} + +void ShootEmUp::drawNumber(int number, int size, int x) { + for (int i = 0; i < size - 1; i++) { + int divisor = 10; + for (int j = 0; j < size - 2 - i; j++) + divisor *= 10; + char value = number / divisor; + _vm->_graphics->seuDrawPicture(x + i * 10, 0, value); + number -= value * divisor; + } + _vm->_graphics->seuDrawPicture(x + (size - 1) * 10, 0, number % 10); +} + +void ShootEmUp::showScore() { + drawNumber(_score, 5, 40); +} + +void ShootEmUp::showTime() { + drawNumber(_time, 3, 140); +} + +void ShootEmUp::gain(int8 howMuch) { + if ((_score + howMuch) < 0) // howMuch can be negative! + _score = 0; + else + _score += howMuch; + + showScore(); +} + +void ShootEmUp::newEscape() { + _escapeCount = _vm->_rnd->getRandomNumber(17) * 20; + _escaping = false; +} + +void ShootEmUp::nextPage() { + _vm->_graphics->drawNormalText("Press a key for next page >", _vm->_font, 8, 400, 190, kColorWhite); + _vm->_graphics->refreshScreen(); + + while (!_vm->shouldQuit()) { + Common::Event event; + _vm->getEvent(event); + if (event.type == Common::EVENT_KEYDOWN) { + break; + } + } + + _vm->_graphics->blackOutScreen(); +} + +void ShootEmUp::instructions() { + _vm->_graphics->blackOutScreen(); + _vm->_graphics->seuDrawPicture(25, 25, kFacingRight); + _vm->_graphics->drawNormalText("< Avvy, our hero, needs your help - you must move him around.", _vm->_font, 8, 60, 35, kColorWhite); + _vm->_graphics->drawNormalText("(He''s too terrified to move himself!)", _vm->_font, 8, 80, 45, kColorWhite); + _vm->_graphics->drawNormalText("Your task is to prevent the people in the stocks from escaping", _vm->_font, 8, 0, 75, kColorWhite); + _vm->_graphics->drawNormalText("by pelting them with rotten fruit, eggs and bread. The keys are:", _vm->_font, 8, 0, 85, kColorWhite); + _vm->_graphics->drawNormalText("LEFT SHIFT", _vm->_font, 8, 80, 115, kColorWhite); + _vm->_graphics->drawNormalText("Move left.", _vm->_font, 8, 200, 115, kColorWhite); + _vm->_graphics->drawNormalText("RIGHT SHIFT", _vm->_font, 8, 72, 135, kColorWhite); + _vm->_graphics->drawNormalText("Move right.", _vm->_font, 8, 200, 135, kColorWhite); + _vm->_graphics->drawNormalText("ALT", _vm->_font, 8, 136, 155, kColorWhite); + _vm->_graphics->drawNormalText("Throw something.", _vm->_font, 8, 200, 155, kColorWhite); + + nextPage(); + + _vm->_graphics->seuDrawPicture(25, 35, kStocks); + _vm->_graphics->drawNormalText("This man is in the stocks. Your job is to stop him getting out.", _vm->_font, 8, 80, 35, kColorWhite); + _vm->_graphics->drawNormalText("UNFORTUNATELY... the locks on the stocks are loose, and every", _vm->_font, 8, 88, 45, kColorWhite); + _vm->_graphics->drawNormalText("so often, someone will discover this and try to get out.", _vm->_font, 8, 88, 55, kColorWhite); + _vm->_graphics->seuDrawPicture(25, 85, kStocks + 2); + _vm->_graphics->drawNormalText("< Someone who has found a way out!", _vm->_font, 8, 80, 85, kColorWhite); + _vm->_graphics->drawNormalText("You MUST IMMEDIATELY hit people smiling like this, or they", _vm->_font, 8, 88, 95, kColorWhite); + _vm->_graphics->drawNormalText("will disappear and lose you points.", _vm->_font, 8, 88, 105, kColorWhite); + _vm->_graphics->seuDrawPicture(25, 125, kStocks + 5); + _vm->_graphics->seuDrawPicture(25, 155, kStocks + 4); + _vm->_graphics->drawNormalText("< Oh dear!", _vm->_font, 8, 80, 125, kColorWhite); + + nextPage(); + + _vm->_graphics->drawNormalText("Your task is made harder by:", _vm->_font, 8, 0, 35, kColorWhite); + _vm->_graphics->seuDrawPicture(25, 55, 48); + _vm->_graphics->drawNormalText("< Yokels. These people will run in front of you. If you hit", _vm->_font, 8, 60, 55, kColorWhite); + _vm->_graphics->drawNormalText("them, you will lose MORE points than you get hitting people", _vm->_font, 8, 68, 65, kColorWhite); + _vm->_graphics->drawNormalText("in the stocks. So BEWARE!", _vm->_font, 8, 68, 75, kColorWhite); + _vm->_graphics->drawNormalText("Good luck with the game!", _vm->_font, 8, 80, 125, kColorWhite); + + nextPage(); +} + +void ShootEmUp::setup() { + _vm->_graphics->blackOutScreen(); + + newEscape(); + + for (int i = 0; i < 7; i++) { + _stockStatus[i] = _vm->_rnd->getRandomNumber(1); + showStock(i); + } + + // Set up status line: + _vm->_graphics->seuDrawPicture(0, 0, 16); // Score: + showScore(); // Value of score (00000 here). + _vm->_graphics->seuDrawPicture(110, 0, 19); // Time: + showTime(); // Value of time. + + _vm->_graphics->refreshScreen(); + + // From the original core cycle: + initRunner(20, 70, 48, 54, _vm->_rnd->getRandomNumber(4) + 1, _vm->_rnd->getRandomNumber(3) - 2); + initRunner(600, 70, 48, 54, _vm->_rnd->getRandomNumber(4) + 1, _vm->_rnd->getRandomNumber(3) - 2); + initRunner(600, 100, 61, 67, (-(int8)_vm->_rnd->getRandomNumber(4)) + 1, _vm->_rnd->getRandomNumber(3) - 2); + initRunner(20, 100, 61, 67, (-(int8)_vm->_rnd->getRandomNumber(4)) + 1, _vm->_rnd->getRandomNumber(3) - 2); +} + +void ShootEmUp::initRunner(int16 x, int16 y, byte f1, byte f2, int8 ix, int8 iy) { + for (int i = 0; i < 4; i++) { + if (_running[i]._x == kFlag) { + _running[i]._x = x; + _running[i]._y = y; + _running[i]._frame = f1; + _running[i]._tooHigh = f2; + _running[i]._lowest = f1; + _running[i]._ix = ix; + _running[i]._iy = iy; + if ((ix == 0) && (iy == 0)) + _running[i]._ix = 2; // To stop them running on the spot! + _running[i]._frameDelay = kFrameDelayMax; + return; + } + } +} + +void ShootEmUp::moveAvvy() { + if (_avvyWas < _avvyPos) + _avvyFacing = kFacingRight; + else if (_avvyWas > _avvyPos) + _avvyFacing = kFacingLeft; + + if (!_firing) { + if (_avvyWas == _avvyPos) + _avvyAnim = 1; + else { + _avvyAnim++; + if (_avvyAnim == 6) + _avvyAnim = 0; + } + } + + if (_avvyFacing == kAvvyShoots) + define(_avvyPos, kAvvyY, kShooting[_avvyAnim], 0, 0, 1, false, true); + else + define(_avvyPos, kAvvyY, _avvyAnim + _avvyFacing, 0, 0, 1, false, true); + + _avvyWas = _avvyPos; + + if (_avvyFacing == kAvvyShoots) { + if (_avvyAnim == 6) { + _avvyFacing = _wasFacing; + _avvyAnim = 0; + _firing = false; + } else + _avvyAnim++; + } +} + +void ShootEmUp::readKbd() { + Common::Event event; + _vm->getEvent(event); + + if ((event.type == Common::EVENT_KEYUP) && ((event.kbd.keycode == Common::KEYCODE_LALT) || (event.kbd.keycode == Common::KEYCODE_RALT))) { + // Don't let the player fire continuously by holding down one of the ALT keys. + _altWasPressedBefore = false; + return; + } + + if (_firing) // So you can't stack up shots while the shooting animation plays. + return; + + if (event.type == Common::EVENT_KEYDOWN) { + switch (event.kbd.keycode) { + case Common::KEYCODE_LALT: // Alt was pressed - shoot! + case Common::KEYCODE_RALT: // Falltrough is intended. + if (_altWasPressedBefore || (_count321 != 0)) + return; + + _altWasPressedBefore = true; + _firing = true; + define(_avvyPos + 27, kAvvyY + 5, _throwNext, 0, -2, 53, true, true); + _throwNext++; + if (_throwNext == 79) + _throwNext = 73; + _avvyAnim = 0; + _wasFacing = _avvyFacing; + _avvyFacing = kAvvyShoots; + return; + case Common::KEYCODE_RSHIFT: // Right shift: move right. + _avvyPos += 5; + if (_avvyPos > kRightMargin) + _avvyPos = kRightMargin; + return; + case Common::KEYCODE_LSHIFT: // Left shift: move left. + _avvyPos -= 5; + if (_avvyPos < kLeftMargin) + _avvyPos = kLeftMargin; + return; + default: + break; + } + } +} + +void ShootEmUp::animate() { + if (_vm->_rnd->getRandomNumber(9) == 1) + showStock(getStockNumber(_vm->_rnd->getRandomNumber(5))); + for (int i = 0; i < 7; i++) { + if (_stockStatus[i] > 5) { + _stockStatus[i]--; + if (_stockStatus[i] == 8) { + _stockStatus[i] = 0; + showStock(i); + } + } + } +} + +void ShootEmUp::collisionCheck() { + for (int i = 0; i < 99; i++) { + if ((_sprites[i]._x != kFlag) && (_sprites[i]._missile) && + (_sprites[i]._y < 60) && (_sprites[i]._timeout == 1)) { + int distFromSide = (_sprites[i]._x - 20) % 90; + int thisStock = (_sprites[i]._x - 20) / 90; + if ((!_hasEscaped[thisStock]) && (distFromSide > 17) && (distFromSide < 34)) { + _vm->_sound->playNote(999, 3); + _vm->_system->delayMillis(3); + define(_sprites[i]._x + 20, _sprites[i]._y, 25 + _vm->_rnd->getRandomNumber(1), 3, 1, 12, false, true); // Well done! + define(thisStock * 90 + 20, 30, 30, 0, 0, 7, false, false); // Face of man + defineCameo(thisStock * 90 + 20 + 10, 35, 40, 7); // Splat! + define(thisStock * 90 + 20 + 20, 50, 33 + _vm->_rnd->getRandomNumber(4), 0, 2, 9, false, true); // Oof! + _stockStatus[thisStock] = 17; + gain(3); // Score for hitting a face. + + if (_escaping && (_escapeStock = thisStock)) { // Hit the escaper. + _vm->_sound->playNote(1777, 1); + _vm->_system->delayMillis(1); + gain(5); // Bonus for hitting escaper. + _escaping = false; + newEscape(); + } + } else { + define(_sprites[i]._x, _sprites[i]._y, 82 + _vm->_rnd->getRandomNumber(2), 2, 2, 17, false, true); // Missed! + if ((!_hasEscaped[thisStock]) && (distFromSide > 3) && (distFromSide < 43)) { + define(thisStock * 90 + 20, 30, 29, 0, 0, 7, false, false); // Face of man + if (distFromSide > 35) + defineCameo(_sprites[i]._x - 27, 35, 40, 7); // Splat! + else + defineCameo(_sprites[i]._x - 7, 35, 40, 7); + _stockStatus[thisStock] = 17; + } + } + } + } +} + +void ShootEmUp::turnAround(byte who, bool randomX) { + if (randomX) { + int8 ix = (_vm->_rnd->getRandomNumber(4) + 1); + if (_running[who]._ix > 0) + _running[who]._ix = -(ix); + else + _running[who]._ix = ix; + } else + _running[who]._ix = -(_running[who]._ix); + + _running[who]._iy = -(_running[who]._iy); +} + +void ShootEmUp::bumpFolk() { + for (int i = 0; i < 4; i++) { + if (_running[i]._x != kFlag) { + for (int j = i + 1; j < 4; j++) { + bool overlaps = overlap(_running[i]._x, _running[i]._y, _running[i]._x + 17, _running[i]._y + 24, + _running[j]._x, _running[j]._y, _running[j]._x + 17, _running[j]._y + 24); + if ((_running[i]._x != kFlag) && overlaps) { + turnAround(i, false); // Opp. directions. + turnAround(j, false); + } + } + } + } +} + +void ShootEmUp::peopleRunning() { + if (_count321 != 0) + return; + + for (int i = 0; i < 4; i++) { + if (_running[i]._x != kFlag) { + if (((_running[i]._y + _running[i]._iy) <= 53) || ((_running[i]._y + _running[i]._iy) >= 120)) + _running[i]._iy = -(_running[i]._iy); + + byte frame = 0; + if (_running[i]._ix < 0) + frame = _running[i]._frame - 1; + else + frame = _running[i]._frame + 6; + define(_running[i]._x, _running[i]._y, frame, 0, 0, 1, false, true); + + if (_running[i]._frameDelay == 0) { + _running[i]._frame++; + if (_running[i]._frame == _running[i]._tooHigh) + _running[i]._frame = _running[i]._lowest; + _running[i]._frameDelay = kFrameDelayMax; + _running[i]._y += _running[i]._iy; + } else + _running[i]._frameDelay--; + + if (((_running[i]._x + _running[i]._ix) <= 0) || ((_running[i]._x + _running[i]._ix) >= 620)) + turnAround(i, true); + + _running[i]._x += _running[i]._ix; + } + } +} + +void ShootEmUp::updateTime() { + if (_count321 != 0) + return; + + _timeThisSecond++; + + if (_timeThisSecond < kTimesASecond) + return; + + _time--; + showTime(); + _timeThisSecond = 0; + + if (_time <= kFlashTime) { + int timeMode = 0; + if ((_time % 2) == 1) + timeMode = 19; // Normal 'Time:' + else + timeMode = 85; // Flash 'Time:' + + _vm->_graphics->seuDrawPicture(110, 0, timeMode); + } +} + +void ShootEmUp::hitPeople() { + if (_count321 != 0) + return; + + for (int i = 0; i < 99; i++) { + if ((_sprites[i]._missile) && (_sprites[i]._x != kFlag)) { + for (int j = 0; j < 4; j++) { + + bool overlaps = overlap(_sprites[i]._x, _sprites[i]._y, _sprites[i]._x + 7, _sprites[i]._y + 10, + _running[j]._x, _running[j]._y, _running[j]._x + 17, _running[j]._y + 24); + + if ((_running[j]._x != kFlag) && (overlaps)) { + _vm->_sound->playNote(7177, 1); + _sprites[i]._x = kFlag; + gain(-5); + define(_running[j]._x + 20, _running[j]._y + 3, 33 + _vm->_rnd->getRandomNumber(5), 1, 3, 9, false, true); // Oof! + define(_sprites[i]._x, _sprites[i]._y, 82, 1, 0, 17, false, true); // Oops! + } + } + } + } +} + +void ShootEmUp::escapeCheck() { + if (_count321 != 0) + return; + + if (_escapeCount > 0) { + _escapeCount--; + return; + } + + // Escape_count = 0; now what ? + + if (_escaping) { + if (_gotOut) { + newEscape(); + _escaping = false; + _vm->_graphics->seuDrawPicture(_escapeStock * 90 + 20, 30, kStocks + 4); + } else { + _vm->_graphics->seuDrawPicture(_escapeStock * 90 + 20, 30, kStocks + 5); + _escapeCount = 20; + _gotOut = true; + define(_escapeStock * 90 + 20, 50, 24, 0, 2, 17, false, true); // Escaped! + gain(-10); + _hasEscaped[_escapeStock] = true; + + _howManyHaveEscaped++; + + if (_howManyHaveEscaped == 7) { + _vm->_graphics->seuDrawPicture(266, 90, 23); + _time = 0; + } + } + } else { + _escapeStock = getStockNumber(_vm->_rnd->getRandomNumber(6)); + _escaping = true; + _gotOut = false; + _vm->_graphics->seuDrawPicture(_escapeStock * 90 + 20, 30, kStocks + 2); // Smiling! + _escapeCount = 200; + } +} + +void ShootEmUp::check321() { + if (_count321 == 0) + return; + + _count321--; + + switch (_count321) { + case 84: + define(320, 60, 15, 2, 1, 94, false, true); + break; + case 169: + define(320, 60, 14, 0, 1, 94, false, true); + break; + case 254: + define(320, 60, 13, -2, 1, 94, false, true); + define(0, 100, 17, 2, 0, 254, false, true); + break; + default: + break; + } +} + +} // End of namespace Avalanche diff --git a/engines/avalanche/shootemup.h b/engines/avalanche/shootemup.h new file mode 100644 index 0000000000..3cdcc1d5cd --- /dev/null +++ b/engines/avalanche/shootemup.h @@ -0,0 +1,134 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +/* +* This code is based on the original source code of Lord Avalot d'Argent version 1.3. +* Copyright (c) 1994-1995 Mike, Mark and Thomas Thurman. +*/ + +#ifndef AVALANCHE_SHOOTEMUP_H +#define AVALANCHE_SHOOTEMUP_H + +namespace Avalanche { +class AvalancheEngine; + +class ShootEmUp { +public: + ShootEmUp(AvalancheEngine *vm); + + uint16 run(); + +private: + struct Sprite { + int8 _ix, _iy; + int16 _x, _y; + int8 _p; + int16 _timeout; + bool _cameo; + byte _cameoFrame; + bool _missile; + bool _wipe; + }; + + struct Runner { + int16 _x, _y; + byte _frame; + byte _tooHigh; + byte _lowest; + int8 _ix, _iy; + byte _frameDelay; + }; + + static const byte kStocks; + static const byte kAvvyShoots; + static const byte kFacingRight; + static const byte kFacingLeft; + static const long int kFlag; + static const byte kFrameDelayMax; + static const byte kAvvyY; + static const byte kShooting[7]; + static const byte kTimesASecond; + static const byte kFlashTime; + static const byte kLeftMargin; + static const int16 kRightMargin; + + AvalancheEngine *_vm; + + uint16 _score; + byte _time; + byte _stockStatus[7]; + Sprite _sprites[99]; + byte _rectNum; // Original: 'rsize' + Common::Rect _rectangles[99]; + uint16 _avvyWas; + uint16 _avvyPos; + byte _avvyAnim; + byte _avvyFacing; + bool _altWasPressedBefore; + byte _throwNext; + bool _firing; + Runner _running[4]; + bool _hasEscaped[7]; + byte _count321; + byte _howManyHaveEscaped; + uint16 _escapeCount; + bool _escaping; + byte _timeThisSecond; + bool _cp; + byte _wasFacing; + byte _escapeStock; + bool _gotOut; + + bool overlap(uint16 a1x, uint16 a1y, uint16 a2x, uint16 a2y, uint16 b1x, uint16 b1y, uint16 b2x, uint16 b2y); + byte getStockNumber(byte index); + void blankIt(); + void moveThem(); + void blank(Common::Rect rect); + void plotThem(); + void define(int16 x, int16 y, int8 p, int8 ix, int8 iy, int16 time, bool isAMissile, bool doWeWipe); + void defineCameo(int16 x, int16 y, int8 p, int16 time); + void showStock(byte index); + void drawNumber(int number, int size, int x); + void showScore(); + void showTime(); + void gain(int8 howMuch); + void newEscape(); + void nextPage(); // Internal function of 'instructions' in the original. + void instructions(); + void setup(); + void initRunner(int16 xx, int16 yy, byte f1, byte f2, int8 ixx, int8 iyy); + void moveAvvy(); + void readKbd(); + void animate(); + void collisionCheck(); + void turnAround(byte who, bool randomX); + void bumpFolk(); + void peopleRunning(); + void updateTime(); + void hitPeople(); + void escapeCheck(); + void check321(); +}; + +} // End of namespace Avalanche + +#endif // AVALANCHE_SHOOTEMUP_H diff --git a/engines/avalanche/sound.cpp b/engines/avalanche/sound.cpp index c324df4713..0223bead48 100644 --- a/engines/avalanche/sound.cpp +++ b/engines/avalanche/sound.cpp @@ -8,12 +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. - + * * 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. @@ -51,6 +51,7 @@ void SoundHandler::stopSound() { */ void SoundHandler::toggleSound() { _soundFl = !_soundFl; + _vm->_graphics->drawSoundLight(_soundFl); } void SoundHandler::syncVolume() { diff --git a/engines/avalanche/sound.h b/engines/avalanche/sound.h index 25b6b267d3..a67016a206 100644 --- a/engines/avalanche/sound.h +++ b/engines/avalanche/sound.h @@ -8,12 +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. - + * * 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. diff --git a/engines/avalanche/timer.cpp b/engines/avalanche/timer.cpp index 4e90c7fe48..a9753b3cce 100644 --- a/engines/avalanche/timer.cpp +++ b/engines/avalanche/timer.cpp @@ -8,12 +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. - + * * 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. @@ -35,12 +35,7 @@ namespace Avalanche { Timer::Timer(AvalancheEngine *vm) { _vm = vm; - for (int i = 0; i < 7; i++) { - _times[i]._timeLeft = 0; - _times[i]._action = 0; - _times[i]._reason = 0; - } - _timerLost = false; + resetVariables(); } /** @@ -48,22 +43,20 @@ Timer::Timer(AvalancheEngine *vm) { * @remarks Originally called 'set_up_timer' */ void Timer::addTimer(int32 duration, byte action, byte reason) { - if ((_vm->_isLoaded == false) || (_timerLost == true)) { - byte i = 0; - while ((i < 7) && (_times[i]._timeLeft != 0)) - i++; - - if (i == 7) - return; // Oh dear... No timer left - - // Everything's OK here! - _times[i]._timeLeft = duration; - _times[i]._action = action; - _times[i]._reason = reason; - } else { - _vm->_isLoaded = false; - return; + byte i = 0; + while ((i < 7) && (_times[i]._timeLeft != 0)) { + if (_times[i]._reason == reason) // We only add a timer if it's not already in the array. + return; + i++; } + + if (i == 7) + return; // Oh dear... No timer left + + // Everything's OK here! + _times[i]._timeLeft = duration; + _times[i]._action = action; + _times[i]._reason = reason; } /** @@ -71,7 +64,7 @@ void Timer::addTimer(int32 duration, byte action, byte reason) { * @remarks Originally called 'one_tick' */ void Timer::updateTimer() { - if (_vm->_menu->isActive()) + if (_vm->_dropdown->isActive()) return; for (int i = 0; i < 7; i++) { @@ -208,8 +201,8 @@ void Timer::updateTimer() { } } } - _vm->_roomTime++; // Cycles since you've been in this room. - _vm->_totalTime++; // Total amount of time for this game. + + _vm->_roomCycles++; // Cycles since you've been in this room. } void Timer::loseTimer(byte which) { @@ -218,7 +211,6 @@ void Timer::loseTimer(byte which) { _times[i]._timeLeft = 0; // Cancel this one! } - _timerLost = true; } void Timer::openDrawbridge() { @@ -232,7 +224,7 @@ void Timer::openDrawbridge() { } void Timer::avariciusTalks() { - _vm->_dialogs->displayScrollChain('q', _vm->_avariciusTalk); + _vm->_dialogs->displayScrollChain('Q', _vm->_avariciusTalk); _vm->_avariciusTalk++; if (_vm->_avariciusTalk < 17) @@ -275,25 +267,25 @@ void Timer::stairs() { void Timer::cardiffSurvey() { if (_vm->_cardiffQuestionNum == 0) { _vm->_cardiffQuestionNum++; - _vm->_dialogs->displayScrollChain('q', 27); + _vm->_dialogs->displayScrollChain('Q', 27); } - _vm->_dialogs->displayScrollChain('z', _vm->_cardiffQuestionNum); + _vm->_dialogs->displayScrollChain('Z', _vm->_cardiffQuestionNum); _vm->_interrogation = _vm->_cardiffQuestionNum; addTimer(182, kProcCardiffSurvey, kReasonCardiffsurvey); } void Timer::cardiffReturn() { - _vm->_dialogs->displayScrollChain('q', 28); + _vm->_dialogs->displayScrollChain('Q', 28); cardiffSurvey(); // Add end of question. } void Timer::cwytalotInHerts() { - _vm->_dialogs->displayScrollChain('q', 29); + _vm->_dialogs->displayScrollChain('Q', 29); } void Timer::getTiedUp() { - _vm->_dialogs->displayScrollChain('q', 34); // ...Trouble! + _vm->_dialogs->displayScrollChain('Q', 34); // ...Trouble! _vm->_userMovesAvvy = false; _vm->_beenTiedUp = true; _vm->_animation->stopWalking(); @@ -320,31 +312,32 @@ void Timer::hangAround() { avvy->init(7, true); // Robin Hood _vm->setRoom(kPeopleRobinHood, kRoomRobins); _vm->_animation->appearPed(0, 1); - _vm->_dialogs->displayScrollChain('q', 39); + _vm->_dialogs->displayScrollChain('Q', 39); avvy->walkTo(6); addTimer(55, kProcHangAround2, kReasonHangingAround); } void Timer::hangAround2() { - _vm->_dialogs->displayScrollChain('q', 40); + _vm->_dialogs->displayScrollChain('Q', 40); AnimationType *spr = _vm->_animation->_sprites[1]; spr->_vanishIfStill = false; spr->walkTo(3); _vm->setRoom(kPeopleFriarTuck, kRoomRobins); - _vm->_dialogs->displayScrollChain('q', 41); + _vm->_dialogs->displayScrollChain('Q', 41); _vm->_animation->_sprites[0]->remove(); spr->remove(); // Get rid of Robin Hood and Friar Tuck. - addTimer(1, kProcAfterTheShootemup, kReasonHangingAround); - // Immediately call the following proc (when you have a chance). + addTimer(1, kProcAfterTheShootemup, kReasonHangingAround); // Immediately call the following proc (when you have a chance). _vm->_tiedUp = false; - // _vm->_enid->backToBootstrap(1); Call the shoot-'em-up. TODO: Replace it with proper ScummVM-friendly function(s)! Do not remove until then! + // We don't need the ShootEmUp during the whole game, it's only playable once. + ShootEmUp *shootemup = new ShootEmUp(_vm); + _shootEmUpScore = shootemup->run(); + delete shootemup; } void Timer::afterTheShootemup() { - // Only placed this here to replace the minigame. TODO: Remove it when the shoot em' up is implemented! _vm->flipRoom(_vm->_room, 1); _vm->_animation->_sprites[0]->init(0, true); // Avalot. @@ -353,29 +346,17 @@ void Timer::afterTheShootemup() { _vm->_objects[kObjectCrossbow - 1] = true; _vm->refreshObjectList(); - // Same as the added line above: TODO: Remove it later!!! - _vm->_dialogs->displayText(Common::String("P.S.: There should have been the mini-game called \"shoot em' up\", " \ - "but I haven't implemented it yet: you get the crossbow automatically.") + kControlNewLine + kControlNewLine + "Peter (uruk)"); - -#if 0 - byte shootscore, gain; - - shootscore = mem[storage_seg * storage_ofs]; - gain = (shootscore + 5) / 10; // Rounding up. - - display(string("\6Your score was ") + strf(shootscore) + '.' + "\r\rYou gain (" + - strf(shootscore) + " 0xF6 10) = " + strf(gain) + " points."); + byte gain = (_shootEmUpScore + 5) / 10; // Rounding up. + _vm->_dialogs->displayText(Common::String::format("%cYour score was %d.%c%cYou gain (%d \xf6 10) = %d points.", kControlItalic, _shootEmUpScore, kControlNewLine, kControlNewLine, _shootEmUpScore, gain)); if (gain > 20) { - display("But we won't let you have more than 20 points!"); - points(20); + _vm->_dialogs->displayText("But we won't let you have more than 20 points!"); + _vm->incScore(20); } else - points(gain); -#endif + _vm->incScore(gain); - warning("STUB: Timer::after_the_shootemup()"); - _vm->_dialogs->displayScrollChain('q', 70); + _vm->_dialogs->displayScrollChain('Q', 70); } void Timer::jacquesWakesUp() { @@ -431,7 +412,7 @@ void Timer::naughtyDuke() { // This is when the Duke comes in and takes your mon void Timer::naughtyDuke2() { AnimationType *spr = _vm->_animation->_sprites[1]; - _vm->_dialogs->displayScrollChain('q', 48); // "Ha ha, it worked again!" + _vm->_dialogs->displayScrollChain('Q', 48); // "Ha ha, it worked again!" spr->walkTo(0); // Walk to the door. spr->_vanishIfStill = true; // Then go away! @@ -484,14 +465,14 @@ void Timer::jump() { _vm->_arrowInTheDoor = false; // You've got it. _vm->_objects[kObjectBolt - 1] = true; _vm->refreshObjectList(); - _vm->_dialogs->displayScrollChain('q', 50); + _vm->_dialogs->displayScrollChain('Q', 50); _vm->incScore(3); } } } void Timer::crapulusSaysSpludOut() { - _vm->_dialogs->displayScrollChain('q', 56); + _vm->_dialogs->displayScrollChain('Q', 56); _vm->_crapulusWillTell = false; } @@ -500,7 +481,7 @@ void Timer::buyDrinks() { _vm->_malagauche = 0; _vm->_dialogs->displayScrollChain('D', _vm->_drinking); // Display message about it. - _vm->_pingo->wobble(); // Do the special effects. + _vm->_animation->wobble(); // Do the special effects. _vm->_dialogs->displayScrollChain('D', 1); // That'll be thruppence. if (_vm->decreaseMoney(3)) // Pay 3d. _vm->_dialogs->displayScrollChain('D', 3); // Tell 'em you paid up. @@ -586,7 +567,7 @@ void Timer::robinHoodAndGeida() { } void Timer::robinHoodAndGeidaTalk() { - _vm->_dialogs->displayScrollChain('q', 66); + _vm->_dialogs->displayScrollChain('Q', 66); AnimationType *avvy = _vm->_animation->_sprites[0]; AnimationType *spr = _vm->_animation->_sprites[1]; @@ -605,7 +586,7 @@ void Timer::avalotReturns() { spr->remove(); avvy->init(0, true); _vm->_animation->appearPed(0, 0); - _vm->_dialogs->displayScrollChain('q', 67); + _vm->_dialogs->displayScrollChain('Q', 67); _vm->_userMovesAvvy = true; } @@ -636,21 +617,43 @@ void Timer::arkataShouts() { if (_vm->_teetotal) return; - _vm->_dialogs->displayScrollChain('q', 76); + _vm->_dialogs->displayScrollChain('Q', 76); addTimer(160, kProcArkataShouts, kReasonArkataShouts); } +/** + * @remarks Contains the content of the function 'winning_pic', originally located in PINGO. + */ void Timer::winning() { - _vm->_dialogs->displayScrollChain('q', 79); - _vm->_pingo->winningPic(); + _vm->_dialogs->displayScrollChain('Q', 79); + + // This was originally located in winning_pic: + CursorMan.showMouse(false); + _vm->_graphics->saveScreen(); + _vm->fadeOut(); + _vm->_graphics->drawWinningPic(); + _vm->_graphics->refreshScreen(); + _vm->fadeIn(); + + // Waiting for a keypress or a left mouseclick: + Common::Event event; + bool escape = false; + while (!_vm->shouldQuit() && !escape) { + _vm->_graphics->refreshScreen(); + while (_vm->getEvent(event)) { + if ((event.type == Common::EVENT_LBUTTONUP) || (event.type == Common::EVENT_KEYDOWN)) { + escape = true; + break; + } + } + } - warning("STUB: Timer::winning()"); -#if 0 - do { - _vm->checkclick(); - } while (!(_vm->mrelease == 0)); -#endif - // TODO: To be implemented with Pingo::winningPic(). + _vm->fadeOut(); + _vm->_graphics->restoreScreen(); + _vm->_graphics->removeBackup(); + _vm->fadeIn(); + CursorMan.showMouse(true); + // winning_pic's end. _vm->callVerb(kVerbCodeScore); _vm->_dialogs->displayText(" T H E E N D "); @@ -690,4 +693,14 @@ void Timer::giveLuteToGeida() { // Moved here from Acci. _vm->_sequence->startGeidaLuteSeq(); } +void Timer::resetVariables() { + for (int i = 0; i < 7; i++) { + _times[i]._timeLeft = 0; + _times[i]._action = 0; + _times[i]._reason = 0; + } + + _shootEmUpScore = 0; +} + } // End of namespace Avalanche. diff --git a/engines/avalanche/timer.h b/engines/avalanche/timer.h index 6cd894b0a5..ad6ac0eae6 100644 --- a/engines/avalanche/timer.h +++ b/engines/avalanche/timer.h @@ -8,12 +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. - + * * 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. @@ -119,10 +119,10 @@ public: }; TimerType _times[7]; - bool _timerLost; // Is the timer "lost"? (Because of using loseTimer()) Timer(AvalancheEngine *vm); + void resetVariables(); void addTimer(int32 duration, byte action, byte reason); void updateTimer(); void loseTimer(byte which); @@ -170,7 +170,7 @@ public: private: AvalancheEngine *_vm; - + byte _shootEmUpScore; }; } // End of namespace Avalanche. |