/* 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. * * $URL$ * $Id$ * */ #include "kyra/kyra.h" #include "kyra/kyra_hof.h" #include "kyra/screen.h" #include "kyra/resource.h" #include "kyra/wsamovie.h" #include "kyra/sound.h" #include "kyra/script.h" #include "kyra/script_tim.h" #include "kyra/text_hof.h" #include "kyra/timer.h" #include "kyra/debugger.h" #include "common/system.h" #include "common/config-manager.h" namespace Kyra { const KyraEngine_v2::EngineDesc KyraEngine_HoF::_hofEngineDesc = { // Generic shape related 64, KyraEngine_HoF::_characterFrameTable, // Scene script 8, // Animation script specific 33, // Item specific 175 }; KyraEngine_HoF::KyraEngine_HoF(OSystem *system, const GameFlags &flags) : KyraEngine_v2(system, flags, _hofEngineDesc), _updateFunctor(this, &KyraEngine_HoF::update) { _screen = 0; _text = 0; _seqProcessedString = 0; _activeWSA = 0; _activeText = 0; _seqWsa = 0; _sequences = 0; _sequenceSoundList = 0; _gamePlayBuffer = 0; _cCodeBuffer = _optionsBuffer = _chapterBuffer = 0; _overwriteSceneFacing = false; _mainCharX = _mainCharY = -1; _drawNoShapeFlag = false; _charPalEntry = 0; _itemInHand = -1; _unkSceneScreenFlag1 = false; _noScriptEnter = true; _currentChapter = 0; _newChapterFile = 1; _oldTalkFile = -1; _currentTalkFile = 0; _lastSfxTrack = -1; _handItemSet = -1; _unkHandleSceneChangeFlag = false; _pathfinderFlag = 0; _mouseX = _mouseY = 0; _lastIdleScript = -1; _currentTalkSections.STATim = 0; _currentTalkSections.TLKTim = 0; _currentTalkSections.ENDTim = 0; memset(&_invWsa, 0, sizeof(_invWsa)); _itemAnimData = 0; _demoAnimData = 0; _nextAnimItem = 0; for (int i = 0; i < 15; i++) memset(&_activeItemAnim[i], 0, sizeof(ActiveItemAnim)); _colorCodeFlag1 = 0; _colorCodeFlag2 = -1; _scriptCountDown = 0; _dbgPass = 0; _gamePlayBuffer = 0; _unkBuf500Bytes = 0; _inventorySaved = false; _unkBuf200kByte = 0; memset(&_sceneShapeTable, 0, sizeof(_sceneShapeTable)); _talkObjectList = 0; _shapeDescTable = 0; _gfxBackUpRect = 0; _sceneList = 0; memset(&_sceneAnimMovie, 0, sizeof(_sceneAnimMovie)); memset(&_wsaSlots, 0, sizeof(_wsaSlots)); memset(&_buttonShapes, 0, sizeof(_buttonShapes)); _configTextspeed = 50; _inventoryButtons = _buttonList = 0; _dlgBuffer = 0; _conversationState = new int8*[19]; for (int i = 0; i < 19; i++) _conversationState[i] = new int8[14]; _npcTalkChpIndex = _npcTalkDlgIndex = -1; _mainCharacter.dlgIndex = 0; setDlgIndex(-1); _bookMaxPage = 6; _bookCurPage = 0; _bookNewPage = 0; _bookBkgd = 0; _cauldronState = 0; _cauldronUseCount = 0; memset(_cauldronStateTables, 0, sizeof(_cauldronStateTables)); _menuDirectlyToLoad = false; _menu = 0; _chatIsNote = false; memset(&_npcScriptData, 0, sizeof(_npcScriptData)); } KyraEngine_HoF::~KyraEngine_HoF() { cleanup(); seq_uninit(); delete _screen; delete _text; delete _gui; delete _tim; _text = 0; delete _invWsa.wsa; if (_sequenceSoundList) { for (int i = 0; i < _sequenceSoundListSize; i++) { if (_sequenceSoundList[i]) delete[] _sequenceSoundList[i]; } delete[] _sequenceSoundList; _sequenceSoundList = NULL; } if (_dlgBuffer) delete[] _dlgBuffer; for (int i = 0; i < 19; i++) delete[] _conversationState[i]; delete[] _conversationState; for (Common::Array::iterator i = _timOpcodes.begin(); i != _timOpcodes.end(); ++i) delete *i; _timOpcodes.clear(); } int KyraEngine_HoF::init() { _screen = new Screen_HoF(this, _system); assert(_screen); _screen->setResolution(); KyraEngine::init(); initStaticResource(); _debugger = new Debugger_HoF(this); assert(_debugger); _text = new TextDisplayer_HoF(this, _screen); assert(_text); _gui = new GUI_HoF(this); assert(_gui); _gui->initStaticData(); _tim = new TIMInterpreter(this, _system); assert(_tim); if (_flags.isDemo && !_flags.isTalkie) { _screen->loadFont(_screen->FID_8_FNT, "FONT9P.FNT"); } else { _screen->loadFont(_screen->FID_6_FNT, "6.FNT"); _screen->loadFont(_screen->FID_8_FNT, "8FAT.FNT"); _screen->loadFont(_screen->FID_BOOKFONT_FNT, "BOOKFONT.FNT"); } _screen->loadFont(_screen->FID_GOLDFONT_FNT, "GOLDFONT.FNT"); _screen->setAnimBlockPtr(3504); _screen->setScreenDim(0); if (!_sound->init()) error("Couldn't init sound"); _abortIntroFlag = false; if (_sequenceStrings) { for (int i = 0; i < 33; i++) _sequenceStringsDuration[i] = (int) strlen(_sequenceStrings[i]) * 8; } // No mouse display in demo if (_flags.isDemo && !_flags.isTalkie) return 0; _res->exists("PWGMOUSE.SHP", true); uint8 *shapes = _res->fileData("PWGMOUSE.SHP", 0); assert(shapes); for (int i = 0; i < 2; i++) addShapeToPool(shapes, i, i); delete[] shapes; _screen->setMouseCursor(0, 0, getShapePtr(0)); return 0; } int KyraEngine_HoF::go() { if (_gameToLoad == -1) { if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) seq_showStarcraftLogo(); if (_flags.isDemo && !_flags.isTalkie) { seq_playSequences(kSequenceDemoVirgin, kSequenceDemoFisher); _menuChoice = 4; } else { seq_playSequences(kSequenceVirgin, kSequenceZanfaun); } } else { _menuChoice = 1; } _res->unloadAllPakFiles(); if (_menuChoice != 4) { // load just the pak files needed for ingame _res->loadPakFile(StaticResource::staticDataFilename()); if (_flags.platform == Common::kPlatformPC && _flags.isTalkie) _res->loadFileList("FILEDATA.FDT"); else _res->loadFileList(_ingamePakList, _ingamePakListSize); } _menuDirectlyToLoad = (_menuChoice == 3) ? true : false; _menuDirectlyToLoad &= saveFileLoadable(0); if (_menuChoice & 1) { startup(); if (!quit()) runLoop(); cleanup(); if (_showOutro) seq_playSequences(kSequenceFunters, kSequenceFrash); } return 0; } void KyraEngine_HoF::startup() { _sound->setSoundList(&_soundData[kMusicIngame]); // The track map is exactly the same // for FM-TOWNS and DOS _trackMap = _dosTrackMap; _trackMapSize = _dosTrackMapSize; allocAnimObjects(1, 10, 30); _screen->_curPage = 0; memset(_sceneShapeTable, 0, sizeof(_sceneShapeTable)); _gamePlayBuffer = new uint8[46080]; _unkBuf500Bytes = new uint8[500]; loadMouseShapes(); loadItemShapes(); _screen->setMouseCursor(0, 0, getShapePtr(0)); _screenBuffer = new uint8[64000]; _unkBuf200kByte = new uint8[200000]; loadChapterBuffer(_newChapterFile); loadCCodeBuffer("C_CODE.XXX"); if (_flags.isTalkie) { loadOptionsBuffer("OPTIONS.XXX"); showMessageFromCCode(265, 150, 0); _screen->updateScreen(); openTalkFile(0); _currentTalkFile = 1; openTalkFile(1); } else { _optionsBuffer = _cCodeBuffer; } showMessage(0, 207); _screen->setShapePages(5, 3); _mainCharacter.height = 0x30; _mainCharacter.facing = 4; _mainCharacter.animFrame = 0x12; memset(_sceneAnims, 0, sizeof(_sceneAnims)); for (int i = 0; i < ARRAYSIZE(_sceneAnimMovie); ++i) _sceneAnimMovie[i] = new WSAMovieV2(this, _screen); memset(_wsaSlots, 0, sizeof(_wsaSlots)); for (int i = 0; i < ARRAYSIZE(_wsaSlots); ++i) _wsaSlots[i] = new WSAMovieV2(this, _screen); _screen->_curPage = 0; _talkObjectList = new TalkObject[72]; memset(_talkObjectList, 0, sizeof(TalkObject)*72); _shapeDescTable = new ShapeDesc[55]; memset(_shapeDescTable, 0, sizeof(ShapeDesc)*55); for (int i = 9; i <= 32; ++i) { _shapeDescTable[i-9].width = 30; _shapeDescTable[i-9].height = 55; _shapeDescTable[i-9].xAdd = -15; _shapeDescTable[i-9].yAdd = -50; } for (int i = 19; i <= 24; ++i) { _shapeDescTable[i-9].width = 53; _shapeDescTable[i-9].yAdd = -51; } _gfxBackUpRect = new uint8[_screen->getRectSize(32, 32)]; initItemList(30); loadButtonShapes(); resetItemList(); _characterShapeFile = 1; loadCharacterShapes(_characterShapeFile); initInventoryButtonList(); setupLangButtonShapes(); loadInventoryShapes(); _res->loadFileToBuf("PALETTE.COL", _screen->_currentPalette, 0x300); _screen->loadBitmap("_PLAYFLD.CPS", 3, 3, 0); _screen->copyPage(3, 0); _screen->showMouse(); _screen->hideMouse(); clearAnimObjects(); for (int i = 0; i < 19; ++i) memset(_conversationState[i], -1, sizeof(int8)*14); clearCauldronTable(); memset(_inputColorCode, -1, sizeof(_inputColorCode)); memset(_newSceneDlgState, 0, sizeof(_newSceneDlgState)); for (int i = 0; i < 23; ++i) resetCauldronStateTable(i); _sceneList = new SceneDesc[86]; memset(_sceneList, 0, sizeof(SceneDesc)*86); _sceneListSize = 86; runStartScript(1, 0); loadNPCScript(); if (_gameToLoad == -1) { snd_playWanderScoreViaMap(52, 1); enterNewScene(_mainCharacter.sceneId, _mainCharacter.facing, 0, 0, 1); saveGame(getSavegameFilename(0), "New Game"); } else { loadGame(getSavegameFilename(_gameToLoad)); } _screen->showMouse(); if (_menuDirectlyToLoad) (*_inventoryButtons[0].buttonCallback)(&_inventoryButtons[0]); setNextIdleAnimTimer(); //XXX setWalkspeed(_configWalkspeed); } void KyraEngine_HoF::runLoop() { _screen->updateScreen(); _quitFlag = false; _runFlag = true; while (!_quitFlag && _runFlag) { if (_deathHandler >= 0) { removeHandItem(); delay(5); _drawNoShapeFlag = 0; _gui->optionsButton(0); _deathHandler = -1; } if (_system->getMillis() > _nextIdleAnim) showIdleAnim(); if (queryGameFlag(0x159)) { dinoRide(); resetGameFlag(0x159); } if (queryGameFlag(0x124) && !queryGameFlag(0x125)) { _mainCharacter.animFrame = 32; enterNewScene(39, -1, 0, 0, 0); } if (queryGameFlag(0xD8)) { resetGameFlag(0xD8); if (_mainCharacter.sceneId == 34) { if (queryGameFlag(0xD1)) { initTalkObject(28); npcChatSequence(getTableString(0xFA, _cCodeBuffer, 1), 28, 0x83, 0xFA); deinitTalkObject(28); enterNewScene(35, 4, 0, 0, 0); } else if (queryGameFlag(0xD0)) { initTalkObject(29); npcChatSequence(getTableString(0xFB, _cCodeBuffer, 1), 29, 0x83, 0xFB); deinitTalkObject(29); enterNewScene(33, 6, 0, 0, 0); } } } int inputFlag = checkInput(_buttonList, true); removeInputTop(); update(); if (inputFlag == 198 || inputFlag == 199) { _unk3 = _handItemSet; handleInput(_mouseX, _mouseY); } //if (queryGameFlag(0x1EE) && inputFlag) // sub_13B19(inputFlag); _system->delayMillis(10); } } void KyraEngine_HoF::handleInput(int x, int y) { setNextIdleAnimTimer(); if (_unk5) { _unk5 = 0; return; } if (!_screen->isMouseVisible()) return; if (_unk3 == -2) { snd_playSoundEffect(13); return; } setNextIdleAnimTimer(); if (x <= 6 || x >= 312 || y <= 6 || y >= 135) { bool exitOk = false; assert(_unk3 + 6 >= 0); switch (_unk3 + 6) { case 0: if (_sceneExit1 != 0xFFFF) exitOk = true; break; case 1: if (_sceneExit2 != 0xFFFF) exitOk = true; break; case 2: if (_sceneExit3 != 0xFFFF) exitOk = true; break; case 3: if (_sceneExit4 != 0xFFFF) exitOk = true; break; default: break; } if (exitOk) { inputSceneChange(x, y, 1, 1); return; } } if (checkCharCollision(x, y) && _unk3 >= -1) { runSceneScript2(); return; } else if (pickUpItem(x, y)) { return; } else { int skipHandling = 0; if (checkItemCollision(x, y) == -1) { resetGameFlag(0x1EF); skipHandling = handleInputUnkSub(x, y) ? 1 : 0; if (queryGameFlag(0x1EF)) { resetGameFlag(0x1EF); return; } if (_unk5) { _unk5 = 0; return; } } if (_deathHandler > -1) skipHandling = 1; if (skipHandling) return; if (checkCharCollision(x, y)) { runSceneScript2(); return; } if (_itemInHand >= 0) { if (y > 136) return; dropItem(0, _itemInHand, x, y, 1); } else { if (_unk3 == -2 || y > 135) return; if (!_unk5) { inputSceneChange(x, y, 1, 1); return; } _unk5 = 0; } } } bool KyraEngine_HoF::handleInputUnkSub(int x, int y) { if (y > 143 || _deathHandler > -1 || queryGameFlag(0x164)) return false; if (_handItemSet <= -3 && findItem(_mainCharacter.sceneId, 13) >= 0) { updateCharFacing(); objectChat(getTableString(0xFC, _cCodeBuffer, 1), 0, 0x83, 0xFC); return true; } else { _emc->init(&_sceneScriptState, &_sceneScriptData); _sceneScriptState.regs[1] = x; _sceneScriptState.regs[2] = y; _sceneScriptState.regs[3] = 0; _sceneScriptState.regs[4] = _itemInHand; _emc->start(&_sceneScriptState, 1); while (_emc->isValid(&_sceneScriptState)) _emc->run(&_sceneScriptState); //XXXsys_unkKeyboad (flush? wait? whatever...) if (queryGameFlag(0x1ED)) { _sound->beginFadeOut(); _screen->fadeToBlack(); _showOutro = true; _runFlag = false; } return _sceneScriptState.regs[3] != 0; } } void KyraEngine_HoF::update() { updateInput(); refreshAnimObjectsIfNeed(); updateMouse(); updateSpecialSceneScripts(); _timer->update(); updateItemAnimations(); updateInvWsa(); fadeMessagePalette(); _screen->updateScreen(); } void KyraEngine_HoF::updateWithText() { updateInput(); updateMouse(); fadeMessagePalette(); updateSpecialSceneScripts(); _timer->update(); updateItemAnimations(); updateInvWsa(); restorePage3(); drawAnimObjects(); if (textEnabled() && _chatText) { int pageBackUp = _screen->_curPage; _screen->_curPage = 2; objectChatPrintText(_chatText, _chatObject); _screen->_curPage = pageBackUp; } refreshAnimObjects(0); _screen->updateScreen(); } void KyraEngine_HoF::updateMouse() { int shapeIndex = 0; int type = 0; int xOffset = 0, yOffset = 0; Common::Point mouse = getMousePos(); if (mouse.y <= 145) { if (mouse.x <= 6) { if (_sceneExit4 != 0xFFFF) { type = -3; shapeIndex = 4; xOffset = 1; yOffset = 5; } else { type = -2; } } else if (mouse.x >= 312) { if (_sceneExit2 != 0xFFFF) { type = -5; shapeIndex = 2; xOffset = 7; yOffset = 5; } else { type = -2; } } else if (mouse.y >= 135) { if (_sceneExit3 != 0xFFFF) { type = -4; shapeIndex = 3; xOffset = 5; yOffset = 10; } else { type = -2; } } else if (mouse.y <= 6) { if (_sceneExit1 != 0xFFFF) { type = -6; shapeIndex = 1; xOffset = 5; yOffset = 1; } else { type = -2; } } } for (int i = 0; i < _specialExitCount; ++i) { if (checkSpecialSceneExit(i, mouse.x, mouse.y)) { switch (_specialExitTable[20+i]) { case 0: type = -6; shapeIndex = 1; xOffset = 5; yOffset = 1; break; case 2: type = -5; shapeIndex = 2; xOffset = 7; yOffset = 5; break; case 4: type = -4; shapeIndex = 3; xOffset = 5; yOffset = 7; break; case 6: type = -3; shapeIndex = 4; xOffset = 1; yOffset = 5; break; default: break; } } } if (type == -2) { shapeIndex = 5; xOffset = 5; yOffset = 9; } if (type != 0 && _handItemSet != type && _screen->isMouseVisible()) { _handItemSet = type; _screen->hideMouse(); _screen->setMouseCursor(xOffset, yOffset, getShapePtr(shapeIndex)); _screen->showMouse(); } if (type == 0 && _handItemSet != _itemInHand && _screen->isMouseVisible()) { if ((mouse.y > 145) || (mouse.x > 6 && mouse.x < 312 && mouse.y > 6 && mouse.y < 135)) { _handItemSet = _itemInHand; _screen->hideMouse(); if (_itemInHand == -1) _screen->setMouseCursor(0, 0, getShapePtr(0)); else _screen->setMouseCursor(8, 15, getShapePtr(_itemInHand+64)); _screen->showMouse(); } } } void KyraEngine_HoF::cleanup() { delete[] _inventoryButtons; _inventoryButtons = 0; delete[] _gamePlayBuffer; _gamePlayBuffer = 0; delete[] _unkBuf500Bytes; _unkBuf500Bytes = 0; delete[] _unkBuf200kByte; _unkBuf200kByte = 0; freeSceneShapePtrs(); if (_optionsBuffer != _cCodeBuffer) delete[] _optionsBuffer; _optionsBuffer = 0; delete[] _cCodeBuffer; _cCodeBuffer = 0; delete[] _chapterBuffer; _chapterBuffer = 0; delete[] _talkObjectList; _talkObjectList = 0; delete[] _shapeDescTable; _shapeDescTable = 0; delete[] _gfxBackUpRect; _gfxBackUpRect = 0; for (int i = 0; i < ARRAYSIZE(_sceneAnimMovie); ++i) { delete _sceneAnimMovie[i]; _sceneAnimMovie[i] = 0; } for (int i = 0; i < ARRAYSIZE(_wsaSlots); ++i) { delete _wsaSlots[i]; _wsaSlots[i] = 0; } for (int i = 0; i < ARRAYSIZE(_buttonShapes); ++i) { delete[] _buttonShapes[i]; _buttonShapes[i] = 0; } _emc->unload(&_npcScriptData); } #pragma mark - Localization void KyraEngine_HoF::loadCCodeBuffer(const char *file) { char tempString[13]; strcpy(tempString, file); changeFileExtension(tempString); delete[] _cCodeBuffer; _cCodeBuffer = _res->fileData(tempString, 0); } void KyraEngine_HoF::loadOptionsBuffer(const char *file) { char tempString[13]; strcpy(tempString, file); changeFileExtension(tempString); delete[] _optionsBuffer; _optionsBuffer = _res->fileData(tempString, 0); } void KyraEngine_HoF::loadChapterBuffer(int chapter) { char tempString[14]; static const char *chapterFilenames[] = { "CH1.XXX", "CH2.XXX", "CH3.XXX", "CH4.XXX", "CH5.XXX" }; assert(chapter >= 1 && chapter <= ARRAYSIZE(chapterFilenames)); strcpy(tempString, chapterFilenames[chapter-1]); changeFileExtension(tempString); delete[] _chapterBuffer; _chapterBuffer = _res->fileData(tempString, 0); _currentChapter = chapter; } void KyraEngine_HoF::changeFileExtension(char *buffer) { while (*buffer != '.') ++buffer; ++buffer; strcpy(buffer, _languageExtension[_lang]); } uint8 *KyraEngine_HoF::getTableEntry(uint8 *buffer, int id) { return buffer + READ_LE_UINT16(buffer + (id<<1)); } char *KyraEngine_HoF::getTableString(int id, uint8 *buffer, int decode) { char *string = (char*)getTableEntry(buffer, id); if (decode && _flags.lang != Common::JA_JPN) { decodeString1(string, _internStringBuf); decodeString2(_internStringBuf, _internStringBuf); string = _internStringBuf; } return string; } const char *KyraEngine_HoF::getChapterString(int id) { if (_currentChapter != _newChapterFile) loadChapterBuffer(_newChapterFile); return getTableString(id, _chapterBuffer, 1); } int KyraEngine_HoF::decodeString1(const char *src, char *dst) { static const uint8 decodeTable1[] = { 0x20, 0x65, 0x74, 0x61, 0x69, 0x6E, 0x6F, 0x73, 0x72, 0x6C, 0x68, 0x63, 0x64, 0x75, 0x70, 0x6D }; static const uint8 decodeTable2[] = { 0x74, 0x61, 0x73, 0x69, 0x6F, 0x20, 0x77, 0x62, 0x20, 0x72, 0x6E, 0x73, 0x64, 0x61, 0x6C, 0x6D, 0x68, 0x20, 0x69, 0x65, 0x6F, 0x72, 0x61, 0x73, 0x6E, 0x72, 0x74, 0x6C, 0x63, 0x20, 0x73, 0x79, 0x6E, 0x73, 0x74, 0x63, 0x6C, 0x6F, 0x65, 0x72, 0x20, 0x64, 0x74, 0x67, 0x65, 0x73, 0x69, 0x6F, 0x6E, 0x72, 0x20, 0x75, 0x66, 0x6D, 0x73, 0x77, 0x20, 0x74, 0x65, 0x70, 0x2E, 0x69, 0x63, 0x61, 0x65, 0x20, 0x6F, 0x69, 0x61, 0x64, 0x75, 0x72, 0x20, 0x6C, 0x61, 0x65, 0x69, 0x79, 0x6F, 0x64, 0x65, 0x69, 0x61, 0x20, 0x6F, 0x74, 0x72, 0x75, 0x65, 0x74, 0x6F, 0x61, 0x6B, 0x68, 0x6C, 0x72, 0x20, 0x65, 0x69, 0x75, 0x2C, 0x2E, 0x6F, 0x61, 0x6E, 0x73, 0x72, 0x63, 0x74, 0x6C, 0x61, 0x69, 0x6C, 0x65, 0x6F, 0x69, 0x72, 0x61, 0x74, 0x70, 0x65, 0x61, 0x6F, 0x69, 0x70, 0x20, 0x62, 0x6D }; int size = 0; uint cChar = 0; while ((cChar = *src++) != 0) { if (cChar & 0x80) { cChar &= 0x7F; int index = (cChar & 0x78) >> 3; *dst++ = decodeTable1[index]; ++size; assert(cChar < sizeof(decodeTable2)); cChar = decodeTable2[cChar]; } *dst++ = cChar; ++size; } *dst++ = 0; return size; } void KyraEngine_HoF::decodeString2(const char *src, char *dst) { if (!src || !dst) return; char out = 0; while ((out = *src) != 0) { if (*src == 0x1B) { ++src; out = *src + 0x7F; } *dst++ = out; ++src; } *dst = 0; } #pragma mark - void KyraEngine_HoF::showMessageFromCCode(int id, int16 palIndex, int) { const char *string = getTableString(id, _cCodeBuffer, 1); showMessage(string, palIndex); } void KyraEngine_HoF::showMessage(const char *string, int16 palIndex) { _shownMessage = string; _screen->hideMouse(); _screen->fillRect(0, 190, 319, 199, 0xCF); if (string) { if (palIndex != -1 || _fadeMessagePalette) { palIndex *= 3; memcpy(_messagePal, _screen->_currentPalette + palIndex, 3); memmove(_screen->_currentPalette + 765, _screen->_currentPalette + palIndex, 3); _screen->setScreenPalette(_screen->_currentPalette); } int x = _text->getCenterStringX(string, 0, 320); _text->printText(string, x, 190, 255, 207, 0); setTimer1DelaySecs(7); } _fadeMessagePalette = false; _screen->showMouse(); } void KyraEngine_HoF::showChapterMessage(int id, int16 palIndex) { showMessage(getChapterString(id), palIndex); } void KyraEngine_HoF::updateCommandLineEx(int str1, int str2, int16 palIndex) { char buffer[0x51]; char *src = buffer; strcpy(src, getTableString(str1, _cCodeBuffer, 1)); if (_flags.lang != Common::JA_JPN) { while (*src != 0x20) ++src; ++src; *src = toupper(*src); } strcpy((char*)_unkBuf500Bytes, src); if (str2 > 0) { if (_flags.lang != Common::JA_JPN) strcat((char*)_unkBuf500Bytes, " "); strcat((char*)_unkBuf500Bytes, getTableString(str2, _cCodeBuffer, 1)); } showMessage((char*)_unkBuf500Bytes, palIndex); } void KyraEngine_HoF::fadeMessagePalette() { if (!_fadeMessagePalette) return; bool updatePalette = false; for (int i = 0; i < 3; ++i) { if (_messagePal[i] >= 4) { _messagePal[i] -= 4; updatePalette = true; } else if (_messagePal[i] != 0) { _messagePal[i] = 0; updatePalette = true; } } if (updatePalette) { memcpy(_screen->getPalette(0) + 765, _messagePal, 3); _screen->setScreenPalette(_screen->getPalette(0)); } else { _fadeMessagePalette = false; } } #pragma mark - void KyraEngine_HoF::loadMouseShapes() { _screen->loadBitmap("_MOUSE.CSH", 3, 3, 0); for (int i = 0; i <= 8; ++i) addShapeToPool(_screen->getCPagePtr(3), i, i); } void KyraEngine_HoF::loadItemShapes() { _screen->loadBitmap("_ITEMS.CSH", 3, 3, 0); for (int i = 64; i <= 239; ++i) addShapeToPool(_screen->getCPagePtr(3), i, i-64); _res->loadFileToBuf("_ITEMHT.DAT", _itemHtDat, sizeof(_itemHtDat)); assert(_res->getFileSize("_ITEMHT.DAT") == sizeof(_itemHtDat)); _screen->_curPage = 0; } void KyraEngine_HoF::loadCharacterShapes(int shapes) { char file[10]; strcpy(file, "_ZX.SHP"); _characterShapeFile = shapes; file[2] = '0' + shapes; uint8 *data = _res->fileData(file, 0); for (int i = 9; i <= 32; ++i) addShapeToPool(data, i, i-9); delete[] data; _characterShapeFile = shapes; } void KyraEngine_HoF::loadInventoryShapes() { int curPageBackUp = _screen->_curPage; _screen->_curPage = 2; _screen->loadBitmap("_PLAYALL.CPS", 3, 3, 0); for (int i = 0; i < 10; ++i) addShapeToPool(_screen->encodeShape(_inventoryX[i], _inventoryY[i], 16, 16, 0), 240+i); _screen->_curPage = curPageBackUp; } void KyraEngine_HoF::runStartScript(int script, int unk1) { char filename[14]; strcpy(filename, "_START0X.EMC"); filename[7] = script + '0'; EMCData scriptData; EMCState scriptState; memset(&scriptData, 0, sizeof(EMCData)); memset(&scriptState, 0, sizeof(EMCState)); _emc->load(filename, &scriptData, &_opcodes); _emc->init(&scriptState, &scriptData); scriptState.regs[6] = unk1; _emc->start(&scriptState, 0); while (_emc->isValid(&scriptState)) _emc->run(&scriptState); _emc->unload(&scriptData); } void KyraEngine_HoF::loadNPCScript() { _emc->unload(&_npcScriptData); char filename[12]; strcpy(filename, "_NPC.EMC"); if (_flags.platform != Common::kPlatformPC || _flags.isTalkie) { switch (_lang) { case 0: filename[5] = 'E'; break; case 1: filename[5] = 'F'; break; case 2: filename[5] = 'G'; break; case 3: filename[5] = 'J'; break; default: break; }; } _emc->load(filename, &_npcScriptData, &_opcodes); } #pragma mark - void KyraEngine_HoF::resetScaleTable() { Common::set_to(_scaleTable, _scaleTable + ARRAYSIZE(_scaleTable), 0x100); } void KyraEngine_HoF::setScaleTableItem(int item, int data) { if (item >= 1 || item <= 15) _scaleTable[item-1] = (data << 8) / 100; } int KyraEngine_HoF::getScale(int x, int y) { return _scaleTable[_screen->getLayer(x, y) - 1]; } void KyraEngine_HoF::setDrawLayerTableEntry(int entry, int data) { if (entry >= 1 || entry <= 15) _drawLayerTable[entry-1] = data; } int KyraEngine_HoF::getDrawLayer(int x, int y) { int layer = _screen->getLayer(x, y); layer = _drawLayerTable[layer-1]; if (layer < 0) layer = 0; else if (layer >= 7) layer = 6; return layer; } void KyraEngine_HoF::backUpPage0() { if (_screenBuffer) { _screen->hideMouse(); memcpy(_screenBuffer, _screen->getCPagePtr(0), 64000); _screen->showMouse(); } } void KyraEngine_HoF::restorePage0() { restorePage3(); if (_screenBuffer) _screen->copyBlockToPage(0, 0, 0, 320, 200, _screenBuffer); } void KyraEngine_HoF::updateCharPal(int unk1) { static bool unkVar1 = false; if (!_useCharPal) return; int layer = _screen->getLayer(_mainCharacter.x1, _mainCharacter.y1); int palEntry = _charPalTable[layer]; if (palEntry != _charPalEntry && unk1) { const uint8 *src = &_scenePal[(palEntry << 4) * 3]; uint8 *ptr = _screen->getPalette(0) + 336; for (int i = 0; i < 48; ++i) { *ptr -= (*ptr - *src) >> 1; ++ptr; ++src; } _screen->setScreenPalette(_screen->getPalette(0)); unkVar1 = true; _charPalEntry = palEntry; } else if (unkVar1 || !unk1) { memcpy(_screen->getPalette(0) + 336, &_scenePal[(palEntry << 4) * 3], 48); _screen->setScreenPalette(_screen->getPalette(0)); unkVar1 = false; } } void KyraEngine_HoF::setCharPalEntry(int entry, int value) { if (entry > 15 || entry < 1) entry = 1; if (value > 8 || value < 0) value = 0; _charPalTable[entry] = value; _useCharPal = 1; _charPalEntry = 0; } int KyraEngine_HoF::inputSceneChange(int x, int y, int unk1, int unk2) { bool refreshNPC = false; uint16 curScene = _mainCharacter.sceneId; _pathfinderFlag = 15; if (!_unkHandleSceneChangeFlag) { if (_unk3 == -3) { if (_sceneList[curScene].exit4 != 0xFFFF) { x = 4; y = _sceneEnterY4; _pathfinderFlag = 7; } } else if (_unk3 == -5) { if (_sceneList[curScene].exit2 != 0xFFFF) { x = 316; y = _sceneEnterY2; _pathfinderFlag = 7; } } else if (_unk3 == -6) { if (_sceneList[curScene].exit1 != 0xFFFF) { x = _sceneEnterX1; y = _sceneEnterY1 - 2; _pathfinderFlag = 14; } } else if (_unk3 == -4) { if (_sceneList[curScene].exit3 != 0xFFFF) { x = _sceneEnterX3; y = 147; _pathfinderFlag = 11; } } } int strId = 0; int vocH = _flags.isTalkie ? 131 : -1; if (_pathfinderFlag) { if (findItem(curScene, 13) >= 0 && _unk3 <= -3) { strId = 252; } else if (_itemInHand == 72) { strId = 257; } else if (findItem(curScene, 72) >= 0 && _unk3 <= -3) { strId = 256; } else if (getInventoryItemSlot(72) != -1 && _unk3 <= -3) { strId = 257; } } if (strId) { updateCharFacing(); objectChat(getTableString(strId, _cCodeBuffer, 1), 0, vocH, strId); _pathfinderFlag = 0; return 0; } if (ABS(_mainCharacter.x1 - x) < 4 && ABS(_mainCharacter.y1 - y) < 2) { _pathfinderFlag = 0; return 0; } int curX = _mainCharacter.x1 & ~3; int curY = _mainCharacter.y1 & ~1; int dstX = x & ~3; int dstY = y & ~1; int wayLength = findWay(curX, curY, dstX, dstY, _movFacingTable, 600); _pathfinderFlag = 0; _timer->disable(5); if (wayLength != 0 && wayLength != 0x7D00) refreshNPC = (trySceneChange(_movFacingTable, unk1, unk2) != 0); int charLayer = _screen->getLayer(_mainCharacter.x1, _mainCharacter.y1); if (_layerFlagTable[charLayer] != 0 && !queryGameFlag(0x163)) { if (queryGameFlag(0x164)) { _screen->hideMouse(); _timer->disable(5); runAnimationScript("_ZANBURN.EMC", 0, 1, 1, 0); _deathHandler = 7; snd_playWanderScoreViaMap(0x53, 1); } else { objectChat(getTableString(0xFD, _cCodeBuffer, 1), 0, 0x83, 0xFD); setGameFlag(0x164); _timer->enable(5); _timer->setCountdown(5, 120); } } else if (queryGameFlag(0x164)) { objectChat(getTableString(0xFE, _cCodeBuffer, 1), 0, 0x83, 0xFE); resetGameFlag(0x164); _timer->disable(5); } if (refreshNPC) enterNewSceneUnk2(0); _pathfinderFlag = 0; return refreshNPC; } int KyraEngine_HoF::getCharacterWalkspeed() const { return _timer->getDelay(0); } void KyraEngine_HoF::updateCharAnimFrame(int charId, int *table) { static int unkTable1[] = { 0, 0 }; static const int unkTable2[] = { 17, 0 }; static const int unkTable3[] = { 10, 0 }; static const int unkTable4[] = { 24, 0 }; static const int unkTable5[] = { 19, 0 }; static const int unkTable6[] = { 21, 0 }; static const int unkTable7[] = { 31, 0 }; static const int unkTable8[] = { 26, 0 }; Character *character = &_mainCharacter; ++character->animFrame; int facing = character->facing; if (table) { if (table[0] != table[-1] && table[-1] == table[1]) { facing = getOppositeFacingDirection(table[-1]); table[0] = table[-1]; } } if (!facing) { ++unkTable1[charId]; } else if (facing == 4) { ++unkTable1[charId+1]; } else if (facing == 7 || facing == 1 || facing == 5 || facing == 3) { if (facing == 7 || facing == 1) { if (unkTable1[charId] > 2) facing = 0; } else { if (unkTable1[charId+1] > 2) facing = 4; } unkTable1[charId] = 0; unkTable1[charId+1] = 0; } if (facing == 0) { if (character->animFrame < unkTable8[charId]) character->animFrame = unkTable8[charId]; if (character->animFrame > unkTable7[charId]) character->animFrame = unkTable8[charId]; } else if (facing == 4) { if (character->animFrame < unkTable5[charId]) character->animFrame = unkTable5[charId]; if (character->animFrame > unkTable4[charId]) character->animFrame = unkTable5[charId]; } else { if (character->animFrame > unkTable5[charId]) character->animFrame = unkTable6[charId]; if (character->animFrame == unkTable2[charId]) character->animFrame = unkTable3[charId]; if (character->animFrame > unkTable2[charId]) character->animFrame = unkTable3[charId] + 2; } updateCharacterAnim(charId); } bool KyraEngine_HoF::checkCharCollision(int x, int y) { int scale1 = 0, scale2 = 0, scale3 = 0; int x1 = 0, x2 = 0, y1 = 0, y2 = 0; scale1 = getScale(_mainCharacter.x1, _mainCharacter.y1); scale2 = (scale1 * 24) >> 8; scale3 = (scale1 * 48) >> 8; x1 = _mainCharacter.x1 - (scale2 >> 1); x2 = _mainCharacter.x1 + (scale2 >> 1); y1 = _mainCharacter.y1 - scale3; y2 = _mainCharacter.y1; if (x >= x1 && x <= x2 && y >= y1 && y <= y2) return true; return false; } int KyraEngine_HoF::initAnimationShapes(uint8 *filedata) { const int lastEntry = MIN(_animShapeLastEntry, 31); for (int i = 0; i < lastEntry; ++i) { addShapeToPool(filedata, i+33, i); ShapeDesc *desc = &_shapeDescTable[24+i]; desc->xAdd = _animShapeXAdd; desc->yAdd = _animShapeYAdd; desc->width = _animShapeWidth; desc->height = _animShapeHeight; } return lastEntry; } void KyraEngine_HoF::uninitAnimationShapes(int count, uint8 *filedata) { for (int i = 0; i < count; ++i) remShapeFromPool(i+33); delete[] filedata; setNextIdleAnimTimer(); } void KyraEngine_HoF::setNextIdleAnimTimer() { _nextIdleAnim = _system->getMillis() + _rnd.getRandomNumberRng(10, 15) * 60 * _tickLength; } void KyraEngine_HoF::showIdleAnim() { static const uint8 scriptMinTable[] = { 0x00, 0x05, 0x07, 0x08, 0x00, 0x09, 0x0A, 0x0B, 0xFF, 0x00 }; static const uint8 scriptMaxTable[] = { 0x04, 0x06, 0x07, 0x08, 0x04, 0x09, 0x0A, 0x0B, 0xFF, 0x00 }; if (queryGameFlag(0x159) && _flags.isTalkie) return; static bool scriptAnimation = false; if (!scriptAnimation && _flags.isTalkie) { scriptAnimation = true; randomSceneChat(); } else { scriptAnimation = false; if (_characterShapeFile > 8) return; int scriptMin = scriptMinTable[_characterShapeFile-1]; int scriptMax = scriptMaxTable[_characterShapeFile-1]; int script = 0; if (scriptMin < scriptMax) { do { script = _rnd.getRandomNumberRng(scriptMin, scriptMax); } while (script == _lastIdleScript); } else { script = scriptMin; } runIdleScript(script); _lastIdleScript = script; } } void KyraEngine_HoF::runIdleScript(int script) { if (script < 0 || script >= 12) script = 0; if (_mainCharacter.animFrame != 18) { setNextIdleAnimTimer(); } else { // FIXME: move this to staticres.cpp? static const char *idleScriptFiles[] = { "_IDLHAIR.EMC", "_IDLDUST.EMC", "_IDLLEAN.EMC", "_IDLDIRT.EMC", "_IDLTOSS.EMC", "_IDLNOSE.EMC", "_IDLBRSH.EMC", "_Z3IDLE.EMC", "_Z4IDLE.EMC", "_Z6IDLE.EMC", "_Z7IDLE.EMC", "_Z8IDLE.EMC" }; runAnimationScript(idleScriptFiles[script], 1, 1, 1, 1); } } #pragma mark - void KyraEngine_HoF::backUpGfxRect24x24(int x, int y) { _screen->copyRegionToBuffer(_screen->_curPage, x, y, 24, 24, _gfxBackUpRect); } void KyraEngine_HoF::restoreGfxRect24x24(int x, int y) { _screen->copyBlockToPage(_screen->_curPage, x, y, 24, 24, _gfxBackUpRect); } void KyraEngine_HoF::backUpGfxRect32x32(int x, int y) { _screen->copyRegionToBuffer(_screen->_curPage, x, y, 32, 32, _gfxBackUpRect); } void KyraEngine_HoF::restoreGfxRect32x32(int x, int y) { _screen->copyBlockToPage(_screen->_curPage, x, y, 32, 32, _gfxBackUpRect); } #pragma mark - void KyraEngine_HoF::openTalkFile(int newFile) { debugC(9, kDebugLevelMain | kDebugLevelSound, "KyraEngine_HoF::openTalkFile(%d)", newFile); char talkFilename[16]; if (_oldTalkFile > 0) { sprintf(talkFilename, "CH%dVOC.TLK", _oldTalkFile); _res->unloadPakFile(talkFilename); _oldTalkFile = -1; } if (newFile == 0) { strcpy(talkFilename, "ANYTALK.TLK"); _res->loadPakFile(talkFilename); } else { sprintf(talkFilename, "CH%dVOC.TLK", newFile); _res->loadPakFile(talkFilename); } _oldTalkFile = newFile; } void KyraEngine_HoF::snd_playVoiceFile(int id) { debugC(9, kDebugLevelMain | kDebugLevelSound, "KyraEngine_HoF::snd_playVoiceFile(%d)", id); char vocFile[9]; assert(id >= 0 && id <= 9999999); sprintf(vocFile, "%07d", id); if (_sound->voiceFileIsPresent(vocFile)) { snd_stopVoice(); while (!_sound->voicePlay(vocFile)) { updateWithText(); _system->delayMillis(10); } _speechFile = vocFile; } } void KyraEngine_HoF::snd_loadSoundFile(int id) { debugC(9, kDebugLevelMain | kDebugLevelSound, "KyraEngine_HoF::snd_loadSoundFile(%d)", id); if (id < 0 || !_trackMap) return; assert(id < _trackMapSize); int file = _trackMap[id*2]; _curSfxFile = _curMusicTheme = file; _sound->loadSoundFile(file); } void KyraEngine_HoF::playVoice(int high, int low) { debugC(9, kDebugLevelMain | kDebugLevelSound, "KyraEngine_HoF::playVoice(%d, %d)", high, low); if (!_flags.isTalkie) return; int vocFile = high * 10000 + low * 10; snd_playVoiceFile(vocFile); } void KyraEngine_HoF::snd_playSoundEffect(int track, int volume) { debugC(9, kDebugLevelMain | kDebugLevelSound, "KyraEngine_HoF::snd_playSoundEffect(%d, %d)", track, volume); if (_flags.platform == Common::kPlatformFMTowns || _flags.platform == Common::kPlatformPC98) { if (track == 10) track = _lastSfxTrack; if (track == 10 || track == -1) return; _lastSfxTrack = track; } int16 vocIndex = (int16)READ_LE_UINT16(&_ingameSoundIndex[track * 2]); if (vocIndex != -1) _sound->voicePlay(_ingameSoundList[vocIndex], true); else if (_flags.platform == Common::kPlatformPC) // TODO ?? Maybe there is a way to let users select whether they want // voc, midi or adl sfx (even though it makes no sense to choose anything but voc). KyraEngine::snd_playSoundEffect(track); } #pragma mark - void KyraEngine_HoF::loadInvWsa(const char *filename, int run, int delayTime, int page, int sfx, int sFrame, int flags) { int wsaFlags = 1; if (flags) wsaFlags |= 2; if (!_invWsa.wsa) _invWsa.wsa = new WSAMovieV2(this, _screen); if (!_invWsa.wsa->open(filename, wsaFlags, 0)) error("Couldn't open inventory WSA file '%s'", filename); _invWsa.curFrame = 0; _invWsa.lastFrame = _invWsa.wsa->frames(); _invWsa.x = _invWsa.wsa->xAdd(); _invWsa.y = _invWsa.wsa->yAdd(); _invWsa.w = _invWsa.wsa->width(); _invWsa.h = _invWsa.wsa->height(); _invWsa.x2 = _invWsa.x + _invWsa.w - 1; _invWsa.y2 = _invWsa.y + _invWsa.h - 1; _invWsa.delay = delayTime; _invWsa.page = page; _invWsa.sfx = sfx; _invWsa.specialFrame = sFrame; if (_invWsa.page) _screen->copyRegion(_invWsa.x, _invWsa.y, _invWsa.x, _invWsa.y, _invWsa.w, _invWsa.h, 0, _invWsa.page, Screen::CR_NO_P_CHECK); _invWsa.running = true; _invWsa.timer = _system->getMillis(); if (run) { while (_invWsa.running && !skipFlag() && !_quitFlag) { update(); _system->delayMillis(10); } if (skipFlag()) { resetSkipFlag(); displayInvWsaLastFrame(); } } } void KyraEngine_HoF::closeInvWsa() { _invWsa.wsa->close(); delete _invWsa.wsa; _invWsa.wsa = 0; _invWsa.running = false; } void KyraEngine_HoF::updateInvWsa() { if (!_invWsa.running || !_invWsa.wsa) return; if (_invWsa.timer > _system->getMillis()) return; _invWsa.wsa->setX(0); _invWsa.wsa->setY(0); _invWsa.wsa->setDrawPage(_invWsa.page); _invWsa.wsa->displayFrame(_invWsa.curFrame, 0, 0, 0); if (_invWsa.page) _screen->copyRegion(_invWsa.x, _invWsa.y, _invWsa.x, _invWsa.y, _invWsa.w, _invWsa.h, _invWsa.page, 0, Screen::CR_NO_P_CHECK); _invWsa.timer = _system->getMillis() + _invWsa.delay * _tickLength; ++_invWsa.curFrame; if (_invWsa.curFrame >= _invWsa.lastFrame) displayInvWsaLastFrame(); if (_invWsa.curFrame == _invWsa.specialFrame) snd_playSoundEffect(_invWsa.sfx); if (_invWsa.sfx == -2) { switch (_invWsa.curFrame) { case 9: case 27: case 40: snd_playSoundEffect(0x39); break; case 18: case 34: case 44: snd_playSoundEffect(0x33); break; case 48: snd_playSoundEffect(0x38); break; default: break; } } } void KyraEngine_HoF::displayInvWsaLastFrame() { if (!_invWsa.wsa) return; _invWsa.wsa->setX(0); _invWsa.wsa->setY(0); _invWsa.wsa->setDrawPage(_invWsa.page); _invWsa.wsa->displayFrame(_invWsa.lastFrame-1, 0, 0, 0); if (_invWsa.page) _screen->copyRegion(_invWsa.x, _invWsa.y, _invWsa.x, _invWsa.y, _invWsa.w, _invWsa.h, _invWsa.page, 0, Screen::CR_NO_P_CHECK); closeInvWsa(); int32 countdown = _rnd.getRandomNumberRng(45, 80); _timer->setCountdown(2, countdown * 60); } #pragma mark - void KyraEngine_HoF::setCauldronState(uint8 state, bool paletteFade) { memcpy(_screen->getPalette(2), _screen->getPalette(0), 768); Common::SeekableReadStream *file = _res->getFileStream("_POTIONS.PAL"); if (!file) error("Couldn't load cauldron palette"); file->seek(state*18, SEEK_SET); file->read(_screen->getPalette(2)+723, 18); delete file; file = 0; if (paletteFade) { snd_playSoundEffect((state == 0) ? 0x6B : 0x66); _screen->fadePalette(_screen->getPalette(2), 0x4B, &_updateFunctor); } else { _screen->setScreenPalette(_screen->getPalette(2)); _screen->updateScreen(); } memcpy(_screen->getPalette(0)+723, _screen->getPalette(2)+723, 18); _cauldronState = state; _cauldronUseCount = 0; //if (state == 5) // sub_27149(); } void KyraEngine_HoF::clearCauldronTable() { Common::set_to(_cauldronTable, _cauldronTable+ARRAYSIZE(_cauldronTable), -1); } void KyraEngine_HoF::addFrontCauldronTable(int item) { for (int i = 23; i >= 0; --i) _cauldronTable[i+1] = _cauldronTable[i]; _cauldronTable[0] = item; } void KyraEngine_HoF::cauldronItemAnim(int item) { const int x = 282; const int y = 135; const int mouseDstX = (x + 7) & (~1); const int mouseDstY = (y + 15) & (~1); int mouseX = _mouseX & (~1); int mouseY = _mouseY & (~1); while (mouseY != mouseDstY) { if (mouseY < mouseDstY) mouseY += 2; else if (mouseY > mouseDstY) mouseY -= 2; uint32 waitEnd = _system->getMillis() + _tickLength; setMousePos(mouseX, mouseY); _system->updateScreen(); delayUntil(waitEnd); } while (mouseX != mouseDstX) { if (mouseX < mouseDstX) mouseX += 2; else if (mouseX > mouseDstX) mouseX -= 2; uint32 waitEnd = _system->getMillis() + _tickLength; setMousePos(mouseX, mouseY); _system->updateScreen(); delayUntil(waitEnd); } if (itemIsFlask(item)) { setHandItem(19); delayUntil(_system->getMillis()+_tickLength*30); setHandItem(18); } else { _screen->hideMouse(); backUpGfxRect32x32(x, y); uint8 *shape = getShapePtr(item+64); int curY = y; for (int i = 0; i < 12; i += 2, curY += 2) { restoreGfxRect32x32(x, y); uint32 waitEnd = _system->getMillis() + _tickLength; _screen->drawShape(0, shape, x, curY, 0, 0); _screen->updateScreen(); delayUntil(waitEnd); } snd_playSoundEffect(0x17); for (int i = 16; i > 0; i -= 2, curY += 2) { _screen->setNewShapeHeight(shape, i); restoreGfxRect32x32(x, y); uint32 waitEnd = _system->getMillis() + _tickLength; _screen->drawShape(0, shape, x, curY, 0, 0); _screen->updateScreen(); delayUntil(waitEnd); } restoreGfxRect32x32(x, y); _screen->resetShapeHeight(shape); removeHandItem(); _screen->showMouse(); } } bool KyraEngine_HoF::updateCauldron() { for (int i = 0; i < 23; ++i) { const int16 *curStateTable = _cauldronStateTables[i]; if (*curStateTable == -2) continue; int cauldronState = i; int16 cauldronTable[25]; memcpy(cauldronTable, _cauldronTable, sizeof(cauldronTable)); while (*curStateTable != -2) { int stateValue = *curStateTable++; int j = 0; for (; j < 25; ++j) { int val = cauldronTable[j]; switch (val) { case 68: val = 70; break; case 133: case 167: val = 119; break; case 130: case 143: case 100: val = 12; break; case 132: case 65: case 69: case 74: val = 137; break; case 157: val = 134; break; default: break; } if (val == stateValue) { cauldronTable[j] = -1; j = 26; } } if (j == 25) cauldronState = -1; } if (cauldronState >= 0) { showMessage(0, 0xCF); setCauldronState(cauldronState, true); if (cauldronState == 7) objectChat(getTableString(0xF2, _cCodeBuffer, 1), 0, 0x83, 0xF2); clearCauldronTable(); return true; } } return false; } void KyraEngine_HoF::cauldronRndPaletteFade() { showMessage(0, 0xCF); int index = _rnd.getRandomNumberRng(0x0F, 0x16); Common::SeekableReadStream *file = _res->getFileStream("_POTIONS.PAL"); if (!file) error("Couldn't load cauldron palette"); file->seek(index*18, SEEK_SET); file->read(_screen->getPalette(0)+723, 18); snd_playSoundEffect(0x6A); _screen->fadePalette(_screen->getPalette(0), 0x1E, &_updateFunctor); file->seek(0, SEEK_SET); file->read(_screen->getPalette(0)+723, 18); delete file; _screen->fadePalette(_screen->getPalette(0), 0x1E, &_updateFunctor); } void KyraEngine_HoF::resetCauldronStateTable(int idx) { for (int i = 0; i < 7; ++i) _cauldronStateTables[idx][i] = -2; } bool KyraEngine_HoF::addToCauldronStateTable(int data, int idx) { for (int i = 0; i < 7; ++i) { if (_cauldronStateTables[idx][i] == -2) { _cauldronStateTables[idx][i] = data; return true; } } return false; } void KyraEngine_HoF::listItemsInCauldron() { int itemsInCauldron = 0; for (int i = 0; i < 25; ++i) { if (_cauldronTable[i] != -1) ++itemsInCauldron; else break; } if (!itemsInCauldron) { if (!_cauldronState) objectChat(getTableString(0xF4, _cCodeBuffer, 1), 0, 0x83, 0xF4); else objectChat(getTableString(0xF3, _cCodeBuffer, 1), 0, 0x83, 0xF3); } else { objectChat(getTableString(0xF7, _cCodeBuffer, 1), 0, 0x83, 0xF7); char buffer[80]; for (int i = 0; i < itemsInCauldron-1; ++i) { char *str = buffer; strcpy(str, getTableString(_cauldronTable[i]+54, _cCodeBuffer, 1)); if (_lang == 1) { if (*str == 37) str += 2; } strcpy((char*)_unkBuf500Bytes, "..."); strcat((char*)_unkBuf500Bytes, str); strcat((char*)_unkBuf500Bytes, "..."); objectChat((const char*)_unkBuf500Bytes, 0, 0x83, _cauldronTable[i]+54); } char *str = buffer; strcpy(str, getTableString(_cauldronTable[itemsInCauldron-1]+54, _cCodeBuffer, 1)); if (_lang == 1) { if (*str == 37) str += 2; } strcpy((char*)_unkBuf500Bytes, "..."); strcat((char*)_unkBuf500Bytes, str); strcat((char*)_unkBuf500Bytes, "."); objectChat((const char*)_unkBuf500Bytes, 0, 0x83, _cauldronTable[itemsInCauldron-1]+54); } } #pragma mark - void KyraEngine_HoF::dinoRide() { _mainCharX = _mainCharY = -1; setGameFlag(0x15A); enterNewScene(41, -1, 0, 0, 0); resetGameFlag(0x15A); setGameFlag(0x15B); enterNewScene(39, -1, 0, 0, 0); resetGameFlag(0x15B); setGameFlag(0x16F); setGameFlag(0x15C); enterNewScene(42, -1, 0, 0, 0); resetGameFlag(0x15C); setGameFlag(0x15D); enterNewScene(39, -1, 0, 0, 0); resetGameFlag(0x15D); setGameFlag(0x15E); enterNewScene(40, -1, 0, 0, 0); resetGameFlag(0x15E); _mainCharX = 262; _mainCharY = 28; _mainCharacter.facing = 5; _mainCharacter.animFrame = _characterFrameTable[5]; enterNewScene(39, 4, 0, 0, 0); setHandItem(0x61); _screen->showMouse(); resetGameFlag(0x159); } #pragma mark - void KyraEngine_HoF::playTim(const char *filename) { TIM *tim = _tim->load(filename, &_timOpcodes); if (!tim) return; _tim->resetFinishedFlag(); while (!_quitFlag && !_tim->finished()) { _tim->exec(tim, 0); if (_chatText) updateWithText(); else update(); delay(10); } _tim->unload(tim); } #pragma mark - void KyraEngine_HoF::registerDefaultSettings() { KyraEngine::registerDefaultSettings(); // Most settings already have sensible defaults. This one, however, is // specific to the Kyra engine. ConfMan.registerDefault("walkspeed", 5); } void KyraEngine_HoF::writeSettings() { ConfMan.setInt("talkspeed", ((_configTextspeed-2) * 255) / 95); switch (_lang) { case 1: _flags.lang = Common::FR_FRA; break; case 2: _flags.lang = Common::DE_DEU; break; case 3: _flags.lang = Common::JA_JPN; break; case 0: default: _flags.lang = Common::EN_ANY; break; } ConfMan.set("language", Common::getLanguageCode(_flags.lang)); KyraEngine::writeSettings(); } void KyraEngine_HoF::readSettings() { int talkspeed = ConfMan.getInt("talkspeed"); _configTextspeed = (talkspeed*95)/255 + 2; KyraEngine::readSettings(); } } // end of namespace Kyra