diff options
67 files changed, 20178 insertions, 2371 deletions
@@ -94,8 +94,9 @@ add_engine gob "Gobli*ns" yes add_engine groovie "Groovie" yes "groovie2" add_engine groovie2 "Groovie 2 games" no add_engine hugo "Hugo Trilogy" yes -add_engine kyra "Legend of Kyrandia" yes "lol" +add_engine kyra "Legend of Kyrandia" yes "lol eob" add_engine lol "Lands of Lore" yes +add_engine eob "Eye of the Beholder" no add_engine lastexpress "The Last Express" no add_engine lure "Lure of the Temptress" yes add_engine made "MADE" yes diff --git a/engines/engines.mk b/engines/engines.mk index 5280bf92d7..9939506b86 100644 --- a/engines/engines.mk +++ b/engines/engines.mk @@ -87,6 +87,10 @@ MODULES += engines/kyra ifdef ENABLE_LOL DEFINES += -DENABLE_LOL endif + +ifdef ENABLE_EOB +DEFINES += -DENABLE_EOB +endif endif ifdef ENABLE_LASTEXPRESS diff --git a/engines/kyra/chargen.cpp b/engines/kyra/chargen.cpp new file mode 100644 index 0000000000..4a0e035483 --- /dev/null +++ b/engines/kyra/chargen.cpp @@ -0,0 +1,1495 @@ +/* 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. + * + */ + +#ifdef ENABLE_EOB + +#include "kyra/eobcommon.h" +#include "kyra/resource.h" +#include "kyra/sound_intern.h" + +namespace Kyra { + +class CharacterGenerator { +public: + CharacterGenerator(EobCoreEngine *vm, Screen_Eob *screen); + ~CharacterGenerator(); + + void start(EobCharacter *characters, uint8 ***faceShapes); + +private: + void init(); + void initButtonsFromList(int first, int numButtons); + void initButton(int index, int x, int y, int w, int h, int keyCode); + void checkForCompleteParty(); + void toggleSpecialButton(int index, int bodyCustom, int pageNum); + void processSpecialButton(int index); + void highlightBoxFrame(int index); + int viewDeleteCharacter(); + void createPartyMember(); + int raceSexMenu(); + int classMenu(int raceSex); + int alignmentMenu(int cClass); + void updateMagicShapes(); + void generateStats(int index); + void modifyMenu(); + void statsAndFacesMenu(); + void faceSelectMenu(); + int getNextFreeFaceShape(int shpIndex, int charSex, int step, int8 *selectedPortraits); + void processFaceMenuSelection(int index); + void printStats(int index, int mode); + void processNameInput(int index, int len, int textColor); + int rollDice(); + int modifyStat(int index, int8 *stat1, int8 *stat2); + int getMaxHp(int cclass, int constitution, int level1, int level2, int level3); + int getMinHp(int cclass, int constitution, int level1, int level2, int level3); + void finish(); + + uint8 **_chargenMagicShapes; + uint8 **_chargenButtonLabels; + int _activeBox; + int _magicShapesBox; + int _updateBoxIndex; + int _updateBoxColorIndex; + int _updateBoxShapesIndex; + int _lastUpdateBoxShapesIndex; + uint32 _chargenBoxTimer; + uint32 _chargenMagicShapeTimer; + int8 _chargenSelectedPortraits[4]; + int8 _chargenSelectedPortraits2[4]; + + uint16 _chargenMinStats[7]; + uint16 _chargenMaxStats[7]; + + const char *const *_chargenStrings1; + const char *const *_chargenStrings2; + const char *const *_chargenStatStrings; + const char *const *_chargenRaceSexStrings; + const char *const *_chargenClassStrings; + const char *const *_chargenAlignmentStrings; + const char *const *_chargenEnterGameStrings; + + const uint8 *_chargenStartLevels; + const uint8 *_chargenClassMinStats; + const uint8 *_chargenRaceMinStats; + const uint16 *_chargenRaceMaxStats; + + static const EobChargenButtonDef _chargenButtonDefs[]; + static const CreatePartyModButton _chargenModButtons[]; + static const EobRect8 _chargenButtonBodyCoords[]; + static const EobRect16 _chargenPortraitBoxFrames[]; + static const int16 _chargenBoxX[]; + static const int16 _chargenBoxY[]; + static const int16 _chargenNameFieldX[]; + static const int16 _chargenNameFieldY[]; + + static const int32 _classMenuMasks[]; + static const int32 _alignmentMenuMasks[]; + + static const int16 _raceModifiers[]; + + EobCharacter *_characters; + uint8 **_faceShapes; + + EobCoreEngine *_vm; + Screen_Eob *_screen; +}; + +void EobCoreEngine::startCharacterGeneration() { + CharacterGenerator(this, _screen).start(_characters, &_faceShapes); +} + +CharacterGenerator::CharacterGenerator(EobCoreEngine *vm, Screen_Eob *screen) : _vm(vm), _screen(screen), + _characters(0), _faceShapes(0), _chargenMagicShapes(0), _chargenButtonLabels(0), _updateBoxIndex(-1), + _chargenBoxTimer(0), _chargenMagicShapeTimer(0), _updateBoxColorIndex(0), _updateBoxShapesIndex(0), + _lastUpdateBoxShapesIndex(0), _magicShapesBox(6), _activeBox(0) { + + _chargenStatStrings = _vm->_chargenStatStrings; + _chargenRaceSexStrings = _vm->_chargenRaceSexStrings; + _chargenClassStrings = _vm->_chargenClassStrings; + _chargenAlignmentStrings = _vm->_chargenAlignmentStrings; + + memset(_chargenSelectedPortraits, -1, sizeof(_chargenSelectedPortraits)); + memset(_chargenSelectedPortraits2, 0, sizeof(_chargenSelectedPortraits2)); + memset(_chargenMinStats, 0, sizeof(_chargenMinStats)); + memset(_chargenMaxStats, 0, sizeof(_chargenMaxStats)); + + int temp; + _chargenStrings1 = _vm->staticres()->loadStrings(kEobBaseChargenStrings1, temp); + _chargenStrings2 = _vm->staticres()->loadStrings(kEobBaseChargenStrings2, temp); + _chargenStartLevels = _vm->staticres()->loadRawData(kEobBaseChargenStartLevels, temp); + _chargenEnterGameStrings = _vm->staticres()->loadStrings(kEobBaseChargenEnterGameStrings, temp); + _chargenClassMinStats = _vm->staticres()->loadRawData(kEobBaseChargenClassMinStats, temp); + _chargenRaceMinStats = _vm->staticres()->loadRawData(kEobBaseChargenRaceMinStats, temp); + _chargenRaceMaxStats = _vm->staticres()->loadRawDataBe16(kEobBaseChargenRaceMaxStats, temp); +} + +CharacterGenerator::~CharacterGenerator() { + if (_chargenMagicShapes) { + for (int i = 0; i < 10; i++) + delete[] _chargenMagicShapes[i]; + delete[] _chargenMagicShapes; + } + + if (_chargenButtonLabels) { + for (int i = 0; i < 10; i++) + delete[] _chargenButtonLabels[i]; + delete[] _chargenButtonLabels; + } +} + +void CharacterGenerator::start(EobCharacter *characters, uint8 ***faceShapes) { + if (!characters && !faceShapes) + return; + + _characters = characters; + _faceShapes = *faceShapes; + + _vm->sound()->playTrack(0); + + _vm->delay(_vm->_tickLength); + + init(); + + _screen->setScreenDim(2); + + checkForCompleteParty(); + initButtonsFromList(0, 5); + + _vm->sound()->playTrack(_vm->game() == GI_EOB1 ? 20 : 13); + _activeBox = 0; + + for (bool loop = true; loop && (!_vm->shouldQuit()); ) { + highlightBoxFrame(_activeBox + 6); + _vm->sound()->process(); + int inputFlag = _vm->checkInput(_vm->_activeButtons, false, 0); + _vm->removeInputTop(); + + if (inputFlag) { + if (inputFlag == _vm->_keyMap[Common::KEYCODE_LEFT] || inputFlag == _vm->_keyMap[Common::KEYCODE_RIGHT]) + _activeBox ^= 1; + else if (inputFlag == _vm->_keyMap[Common::KEYCODE_UP] || inputFlag == _vm->_keyMap[Common::KEYCODE_DOWN]) + _activeBox ^= 2; + highlightBoxFrame(-1); + } + + if (inputFlag & 0x8000) { + inputFlag = (inputFlag & 0x0f) - 1; + if (inputFlag == 4) { + loop = false; + } else { + _activeBox = inputFlag; + inputFlag = 43; + } + } + + if (inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN] || inputFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP5]) { + highlightBoxFrame(-1); + if (_characters[_activeBox].name[0]) { + int b = _activeBox; + if (viewDeleteCharacter()) + loop = false; + if (b != _activeBox && !_characters[_activeBox].name[0]) + createPartyMember(); + } else { + createPartyMember(); + } + + initButtonsFromList(0, 5); + checkForCompleteParty(); + } + + if (loop == false) { + for (int i = 0; i < 4; i++) { + if (!_characters[i].name[0]) + loop = true; + } + } + } + + if (!_vm->shouldQuit()) { + processSpecialButton(15); + finish(); + } + + _vm->sound()->playTrack(15); + + *faceShapes = _faceShapes; +} + +void CharacterGenerator::init() { + _screen->loadEobBitmap("CHARGENA", 3, 3); + if (_faceShapes) { + for (int i = 0; i < 44; i++) + delete[] _faceShapes[i]; + delete[] _faceShapes; + } + + _faceShapes = new uint8*[44]; + for (int i = 0; i < 44; i++) + _faceShapes[i] = _screen->encodeShape((i % 10) << 2, (i / 10) << 5, 4, 32, true); + _screen->_curPage = 0; + + _screen->loadEobCpsFileToPage("CHARGEN", 0, 3, 3, 0); + _screen->loadEobBitmap("CHARGENB", 3, 3); + if (_chargenMagicShapes) { + for (int i = 0; i < 10; i++) + delete[] _chargenMagicShapes[i]; + delete[] _chargenMagicShapes; + } + + _chargenMagicShapes = new uint8*[10]; + for (int i = 0; i < 10; i++) + _chargenMagicShapes[i] = _screen->encodeShape(i << 2, 0, 4, 32, true); + + _chargenButtonLabels = new uint8*[17]; + for (int i = 0; i < 17; i++) { + const CreatePartyModButton *c = &_chargenModButtons[i]; + _chargenButtonLabels[i] = c->labelW? _screen->encodeShape(c->encodeLabelX, c->encodeLabelY, c->labelW, c->labelH, true) : 0; + } + + _screen->copyPage(3, 2); + _screen->_curPage = 0; + _screen->copyRegion(144, 64, 0, 0, 180, 128, 0, 2, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); +} + +void CharacterGenerator::initButtonsFromList(int first, int numButtons) { + _vm->gui_resetButtonList(); + + for (int i = 0; i < numButtons; i++) { + const EobChargenButtonDef *e = &_chargenButtonDefs[first + i]; + initButton(i, e->x, e->y, e->w, e->h, e->keyCode); + } + + _vm->gui_notifyButtonListChanged(); +} + +void CharacterGenerator::initButton(int index, int x, int y, int w, int h, int keyCode) { + Button *b = 0; + int cnt = 1; + + if (_vm->_activeButtons) { + Button *n = _vm->_activeButtons; + while (n->nextButton) { + ++cnt; + n = n->nextButton; + } + + ++cnt; + b = n->nextButton = &_vm->_activeButtonData[cnt]; + } else { + b = &_vm->_activeButtonData[0]; + _vm->_activeButtons = b; + } + + *b = Button(); + b->flags = 0x1100; + b->data0Val2 = 12; + b->data1Val2 = b->data2Val2 = 15; + b->data3Val2 = 8; + + b->index = index + 1; + b->x = x << 3; + b->y = y; + b->width = w; + b->height = h; + b->keyCode = keyCode; + b->keyCode2 = keyCode + 0x100; +} + +void CharacterGenerator::checkForCompleteParty() { + _screen->copyRegion(0, 0, 160, 0, 160, 128, 2, 2, Screen::CR_NO_P_CHECK); + int cp = _screen->setCurPage(2); + _screen->printShadedText(_chargenStrings1[8], 168, 16, 15, 0); + _screen->setCurPage(cp); + _screen->copyRegion(160, 0, 144, 64, 160, 128, 2, 0, Screen::CR_NO_P_CHECK); + + int numChars = 0; + for (int i = 0; i < 4; i++) { + if (_characters[i].name[0]) + numChars++; + } + + if (numChars == 4) { + _screen->setCurPage(2); + _screen->printShadedText(_chargenStrings1[0], 168, 61, 15, 0); + _screen->setCurPage(0); + _screen->copyRegion(168, 61, 152, 125, 136, 40, 2, 0, Screen::CR_NO_P_CHECK); + toggleSpecialButton(15, 0, 0); + } else { + toggleSpecialButton(14, 0, 0); + } + + _screen->updateScreen(); +} + +void CharacterGenerator::toggleSpecialButton(int index, int bodyCustom, int pageNum) { + if (index >= 17) + return; + + const CreatePartyModButton *c = &_chargenModButtons[index]; + const EobRect8 *p = &_chargenButtonBodyCoords[c->bodyIndex + bodyCustom]; + + int x2 = 20; + int y2 = 0; + + if (pageNum) { + x2 = c->destX + 2; + y2 = c->destY - 64; + } + + _screen->copyRegion(p->x << 3, p->y, x2 << 3, y2, p->w << 3, p->h, 2, 2, Screen::CR_NO_P_CHECK); + if (c->labelW) + _screen->drawShape(2, _chargenButtonLabels[index], (x2 << 3) + c->labelX, y2 + c->labelY, 0); + + if (pageNum == 2) + return; + + _screen->copyRegion(160, 0, c->destX << 3, c->destY, p->w << 3, p->h, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); +} + +void CharacterGenerator::processSpecialButton(int index) { + toggleSpecialButton(index, 1, 0); + _vm->sound()->playSoundEffect(76); + _vm->_system->delayMillis(80); + toggleSpecialButton(index, 0, 0); +} + +void CharacterGenerator::highlightBoxFrame(int index) { + static const uint8 colorTable[] = { 0x0F, 0xB0, 0xB2, 0xB4, 0xB6, + 0xB8, 0xBA, 0xBC, 0x0C, 0xBC, 0xBA, 0xB8, 0xB6, 0xB4, 0xB2, 0xB0, 0x00 + }; + + if (_updateBoxIndex == index) { + if (_updateBoxIndex == -1) + return; + + if (_vm->_system->getMillis() <= _chargenBoxTimer) + return; + + if (!colorTable[_updateBoxColorIndex]) + _updateBoxColorIndex = 0; + + const EobRect16 *r = &_chargenPortraitBoxFrames[_updateBoxIndex]; + _screen->drawBox(r->x1, r->y1, r->x2, r->y2, colorTable[_updateBoxColorIndex++]); + _screen->updateScreen(); + + _chargenBoxTimer = _vm->_system->getMillis() + _vm->_tickLength; + + } else { + if (_updateBoxIndex != -1) { + const EobRect16 *r = &_chargenPortraitBoxFrames[_updateBoxIndex]; + _screen->drawBox(r->x1, r->y1, r->x2, r->y2, 12); + _screen->updateScreen(); + } + + _updateBoxColorIndex = 0; + _updateBoxIndex = index; + _chargenBoxTimer = _vm->_system->getMillis(); + } +} + +int CharacterGenerator::viewDeleteCharacter() { + initButtonsFromList(0, 7); + _vm->removeInputTop(); + + highlightBoxFrame(-1); + printStats(_activeBox, 2); + + int res = 0; + for (bool loop = true; loop && _characters[_activeBox].name[0] && !_vm->shouldQuit(); ) { + highlightBoxFrame(_activeBox + 6); + _vm->sound()->process(); + + int inputFlag = _vm->checkInput(_vm->_activeButtons, false, 0); + int cbx = _activeBox; + _vm->removeInputTop(); + + if (inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN] || inputFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP5] || inputFlag == _vm->_keyMap[Common::KEYCODE_ESCAPE]) { + processSpecialButton(9); + res = 0; + loop = false; + } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_LEFT] || inputFlag == _vm->_keyMap[Common::KEYCODE_RIGHT]) { + cbx ^= 1; + } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_UP] || inputFlag == _vm->_keyMap[Common::KEYCODE_DOWN]) { + cbx ^= 2; + } + + if (inputFlag & 0x8000) { + inputFlag = (inputFlag & 0x0f) - 1; + if (inputFlag == 4) { + res = 1; + loop = false; + } else if (inputFlag == 5) { + processSpecialButton(9); + res = 0; + loop = false; + } else if (inputFlag == 6) { + if (_characters[_activeBox].name[0]) { + processSpecialButton(16); + _characters[_activeBox].name[0] = 0; + processNameInput(_activeBox, 1, 12); + processFaceMenuSelection(_activeBox + 50); + } + } else { + cbx = inputFlag; + } + } + + if (loop == false) + highlightBoxFrame(-1); + + if (!_characters[cbx].name[0]) + loop = false; + + if (cbx != _activeBox) { + _activeBox = cbx; + highlightBoxFrame(-1); + if (loop) + printStats(_activeBox, 2); + } + } + + return res; +} + +void CharacterGenerator::createPartyMember() { + _screen->setScreenDim(2); + _chargenBoxTimer = 0; + + for (int i = 0; i != 3 && !_vm->shouldQuit(); i++) { + bool bck = false; + + switch (i) { + case 0: + _characters[_activeBox].raceSex = raceSexMenu(); + break; + case 1: + _characters[_activeBox].cClass = classMenu(_characters[_activeBox].raceSex); + if (_characters[_activeBox].cClass == _vm->_keyMap[Common::KEYCODE_ESCAPE]) + bck = true; + break; + case 2: + _characters[_activeBox].alignment = alignmentMenu(_characters[_activeBox].cClass); + if (_characters[_activeBox].alignment == _vm->_keyMap[Common::KEYCODE_ESCAPE]) + bck = true; + break; + default: + break; + } + + if (bck) + i -= 2; + }; + + if (!_vm->shouldQuit()) { + generateStats(_activeBox); + statsAndFacesMenu(); + + for (_characters[_activeBox].name[0] = 0; _characters[_activeBox].name[0] == 0 && !_vm->shouldQuit(); ) { + processFaceMenuSelection(_chargenMinStats[6]); + printStats(_activeBox, 0); + _screen->printShadedText(_chargenStrings2[11], 149, 100, 9, 0); + if (!_vm->shouldQuit()) + processNameInput(_activeBox, _vm->_gui->getTextInput(_characters[_activeBox].name, 24, 100, 10, 15, 0, 8), 2); + } + } +} + +int CharacterGenerator::raceSexMenu() { + _screen->drawBox(_chargenBoxX[_activeBox], _chargenBoxY[_activeBox], _chargenBoxX[_activeBox] + 32, _chargenBoxY[_activeBox] + 33, 12); + _screen->copyRegion(0, 0, 144, 64, 160, 128, 2, 0, Screen::CR_NO_P_CHECK); + _screen->printShadedText(_chargenStrings2[8], 147, 67, 9, 0); + _vm->removeInputTop(); + + _vm->_gui->setupMenu(1, 0, _chargenRaceSexStrings, -1, 0, 0); + int16 res = -1; + + while (res == -1 && !_vm->shouldQuit()) { + res = _vm->_gui->handleMenu(1, _chargenRaceSexStrings, 0, -1, 0); + updateMagicShapes(); + } + + return res; +} + +int CharacterGenerator::classMenu(int raceSex) { + int32 itemsMask = -1; + + for (int i = 0; i < 4; i++) { + // check for evil characters + if (_characters[i].name[0] && _characters[i].alignment > 5) + itemsMask = 0xFFFB; + } + + _vm->removeInputTop(); + updateMagicShapes(); + + _screen->copyRegion(0, 0, 144, 64, 160, 128, 2, 0, Screen::CR_NO_P_CHECK); + _screen->printShadedText(_chargenStrings2[9], 147, 67, 9, 0); + + toggleSpecialButton(5, 0, 0); + + itemsMask &=_classMenuMasks[raceSex / 2]; + _vm->_gui->setupMenu(2, 15, _chargenClassStrings, itemsMask, 0, 0); + + _vm->_mouseX = _vm->_mouseY = 0; + int16 res = -1; + + while (res == -1 && !_vm->shouldQuit()) { + updateMagicShapes(); + + int in = _vm->checkInput(0, false, 0) & 0xff; + Common::Point mp = _vm->getMousePos(); + + if (in == _vm->_keyMap[Common::KEYCODE_ESCAPE] || _vm->_gui->_menuLastInFlags == _vm->_keyMap[Common::KEYCODE_ESCAPE] || _vm->_gui->_menuLastInFlags == _vm->_keyMap[Common::KEYCODE_b]) { + res = _vm->_keyMap[Common::KEYCODE_ESCAPE]; + } else if (_vm->posWithinRect(mp.x, mp.y, 264, 171, 303, 187)) { + if (in == 199 || in == 201) + res = _vm->_keyMap[Common::KEYCODE_ESCAPE]; + else + _vm->removeInputTop(); + } else { + res = _vm->_gui->handleMenu(2, _chargenClassStrings, 0, itemsMask, 0); + } + } + + _vm->removeInputTop(); + + if (res == _vm->_keyMap[Common::KEYCODE_ESCAPE]) + processSpecialButton(5); + + return res; +} + +int CharacterGenerator::alignmentMenu(int cClass) { + int32 itemsMask = -1; + + for (int i = 0; i < 4; i++) { + // check for paladins + if (_characters[i].name[0] && _characters[i].cClass == 2) + itemsMask = 0xFE3F; + } + + _vm->removeInputTop(); + updateMagicShapes(); + + _screen->copyRegion(0, 0, 144, 64, 160, 128, 2, 0, Screen::CR_NO_P_CHECK); + _screen->printShadedText(_chargenStrings2[10], 147, 67, 9, 0); + + toggleSpecialButton(5, 0, 0); + + itemsMask &=_alignmentMenuMasks[cClass]; + _vm->_gui->setupMenu(3, 9, _chargenAlignmentStrings, itemsMask, 0, 0); + + _vm->_mouseX = _vm->_mouseY = 0; + int16 res = -1; + + while (res == -1 && !_vm->shouldQuit()) { + updateMagicShapes(); + + int in = _vm->checkInput(0, false, 0) & 0xff; + Common::Point mp = _vm->getMousePos(); + + if (in == _vm->_keyMap[Common::KEYCODE_ESCAPE] || _vm->_gui->_menuLastInFlags == _vm->_keyMap[Common::KEYCODE_ESCAPE] || _vm->_gui->_menuLastInFlags == _vm->_keyMap[Common::KEYCODE_b]) { + res = _vm->_keyMap[Common::KEYCODE_ESCAPE]; + } else if (_vm->posWithinRect(mp.x, mp.y, 264, 171, 303, 187)) { + if (in == 199 || in == 201) + res = _vm->_keyMap[Common::KEYCODE_ESCAPE]; + else + _vm->removeInputTop(); + } else { + res = _vm->_gui->handleMenu(3, _chargenAlignmentStrings, 0, itemsMask, 0); + } + } + + _vm->removeInputTop(); + + if (res == _vm->_keyMap[Common::KEYCODE_ESCAPE]) + processSpecialButton(5); + + return res; +} + +void CharacterGenerator::updateMagicShapes() { + _vm->sound()->process(); + + if (_magicShapesBox != _activeBox) { + _chargenMagicShapeTimer = 0; + _magicShapesBox = _activeBox; + } + + if (_chargenMagicShapeTimer < _vm->_system->getMillis()) { + if (++_updateBoxShapesIndex > 9) + _updateBoxShapesIndex = 0; + _chargenMagicShapeTimer = _vm->_system->getMillis() + 2 * _vm->_tickLength; + } + + if (_updateBoxShapesIndex == _lastUpdateBoxShapesIndex) + return; + + _screen->copyRegion(_activeBox << 5, 128, 288, 128, 32, 32, 2, 2, Screen::CR_NO_P_CHECK); + _screen->drawShape(2, _chargenMagicShapes[_updateBoxShapesIndex], 288, 128, 0); + _screen->copyRegion(288, 128, _chargenBoxX[_activeBox], _chargenBoxY[_activeBox] + 1, 32, 32, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + + _lastUpdateBoxShapesIndex = _updateBoxShapesIndex; +} + +void CharacterGenerator::generateStats(int index) { + EobCharacter *c = &_characters[index]; + + for (int i = 0; i < 3; i++) { + c->level[i] = _chargenStartLevels[(c->cClass << 2) + i]; + c->experience[i] = (_vm->game() == GI_EOB2 ? 69000 : 5000) / _chargenStartLevels[(c->cClass << 2) + 3]; + } + + int rc = c->raceSex >> 1; + for (int i = 0; i < 6; i++) { + _chargenMinStats[i] = MAX(_chargenClassMinStats[c->cClass * 6 + i], _chargenRaceMinStats[rc * 6 + i]); + _chargenMaxStats[i] = _chargenRaceMaxStats[rc * 6 + i]; + } + + if (_vm->_charClassModUnk[c->cClass]) + _chargenMaxStats[0] = 18; + + uint16 sv[6]; + for (int i = 0; i < 6; i++) { + sv[i] = MAX<uint16>(rollDice() + _raceModifiers[rc * 6 + i], _chargenMinStats[i]); + if (!i && sv[i] == 18) + sv[i] |= (uint16)(_vm->rollDice(1, 100) << 8); + if (sv[i] > _chargenMaxStats[i]) + sv[i] = _chargenMaxStats[i]; + } + + c->strengthCur = c->strengthMax = sv[0] & 0xff; + c->strengthExtCur = c->strengthExtMax = sv[0] >> 8; + c->intelligenceCur = c->intelligenceMax = sv[1] & 0xff; + c->wisdomCur = c->wisdomMax = sv[2] & 0xff; + c->dexterityCur = c->dexterityMax = sv[3] & 0xff; + c->constitutionCur = c->constitutionMax = sv[4] & 0xff; + c->charismaCur = c->charismaMax = sv[5] & 0xff; + c->armorClass = 10 + _vm->getDexterityArmorClassModifier(sv[3] & 0xff); + c->hitPointsCur = 0; + + for (int l = 0; l < 3; l++) { + for (int i = 0; i < c->level[l]; i++) + c->hitPointsCur += _vm->generateCharacterHitpointsByLevel(index, 1 << l); + } + + c->hitPointsMax = c->hitPointsCur; +} + +void CharacterGenerator::modifyMenu() { + _vm->removeInputTop(); + printStats(_activeBox, 3); + + EobCharacter *c = &_characters[_activeBox]; + int8 hpLO = c->hitPointsCur; + + for (int i = 0; i >= 0 && i < 7; ) { + switch (i) { + case 0: + i = modifyStat(i, &c->strengthCur, &c->strengthExtCur); + break; + case 1: + i = modifyStat(i, &c->intelligenceCur, 0); + break; + case 2: + i = modifyStat(i, &c->wisdomCur, 0); + break; + case 3: + i = modifyStat(i, &c->dexterityCur, 0); + break; + case 4: + i = modifyStat(i, &c->constitutionCur, 0); + break; + case 5: + i = modifyStat(i, &c->charismaCur, 0); + break; + case 6: + hpLO = c->hitPointsCur; + i = modifyStat(i, &hpLO, 0); + c->hitPointsCur = hpLO; + break; + default: + break; + } + + if (i == -2 || _vm->shouldQuit()) + break; + else if (i < 0) + i = 6; + i %= 7; + + printStats(_activeBox, 3); + } + + printStats(_activeBox, 1); +} + +void CharacterGenerator::statsAndFacesMenu() { + faceSelectMenu(); + printStats(_activeBox, 1); + initButtonsFromList(27, 4); + _vm->removeInputTop(); + int in = 0; + + while (!in && !_vm->shouldQuit()) { + updateMagicShapes(); + in = _vm->checkInput(_vm->_activeButtons, false, 0); + _vm->removeInputTop(); + + if (in == 0x8001) { + processSpecialButton(4); + updateMagicShapes(); + generateStats(_activeBox); + in = -1; + } else if (in == 0x8002) { + processSpecialButton(7); + modifyMenu(); + in = -1; + } else if (in == 0x8003) { + processSpecialButton(8); + faceSelectMenu(); + in = -1; + } else if (in == 0x8004 || in == _vm->_keyMap[Common::KEYCODE_KP5]) { + processSpecialButton(6); + in = 1; + } else { + in = 0; + } + + if (in & 0x8000) { + printStats(_activeBox, 1); + initButtonsFromList(27, 4); + in = 0; + } + } + + highlightBoxFrame(6 + _activeBox); + highlightBoxFrame(-1); +} + +void CharacterGenerator::faceSelectMenu() { + int8 sp[4]; + memcpy(sp, _chargenSelectedPortraits2, sizeof(sp)); + _vm->removeInputTop(); + initButtonsFromList(21, 6); + + int charSex = _characters[_activeBox].raceSex % 2; + int8 shp = charSex ? 26 : 0; + + printStats(_activeBox, 4); + toggleSpecialButton(12, 0, 0); + toggleSpecialButton(13, 0, 0); + highlightBoxFrame(-1); + + shp = getNextFreeFaceShape(shp, charSex, 1, _chargenSelectedPortraits); + + int res = -1; + int box = 1; + + while (res == -1 && !_vm->shouldQuit()) { + int8 shpOld = shp; + + for (int i = 0; i < 4; i++) { + sp[i] = shp; + _screen->drawShape(0, _faceShapes[sp[i]], 176 + (i << 5), 66, 0); + shp = getNextFreeFaceShape(shp + 1, charSex, 1, _chargenSelectedPortraits); + } + + shp = shpOld; + int in = 0; + + while (!in && !_vm->shouldQuit()) { + updateMagicShapes(); + in = _vm->checkInput(_vm->_activeButtons, false, 0); + _vm->removeInputTop(); + + highlightBoxFrame(box + 10); + + if (in == 0x8002 || in == _vm->_keyMap[Common::KEYCODE_RIGHT]) { + processSpecialButton(13); + in = 2; + } else if (in > 0x8002 && in < 0x8007) { + box = (in & 7) - 3; + in = 3; + } else if (in == 0x8001 || in == _vm->_keyMap[Common::KEYCODE_LEFT]) { + processSpecialButton(12); + in = 1; + } else if (in == _vm->_keyMap[Common::KEYCODE_RETURN] || in == _vm->_keyMap[Common::KEYCODE_KP5]) { + in = 3; + } else if (in & 0x8000) { + in &= 0xff; + } else { + in = 0; + } + } + + highlightBoxFrame(-1); + + if (in == 1) + shp = getNextFreeFaceShape(shp - 1, charSex, -1, _chargenSelectedPortraits); + else if (in == 2) + shp = getNextFreeFaceShape(shp + 1, charSex, 1, _chargenSelectedPortraits); + else if (in == 3) + res = box; + } + + if (!_vm->shouldQuit()) { + highlightBoxFrame(-1); + updateMagicShapes(); + + _chargenSelectedPortraits[_activeBox] = sp[res]; + _characters[_activeBox].portrait = sp[res]; + _characters[_activeBox].faceShape = _faceShapes[sp[res]]; + + printStats(_activeBox, 1); + } +} + +int CharacterGenerator::getNextFreeFaceShape(int shpIndex, int charSex, int step, int8 *selectedPortraits) { + int shpCur = ((shpIndex < 0) ? 43 : shpIndex) % 44; + bool notUsable = false; + + do { + notUsable = false; + for (int i = 0; i < 4; i++) { + if (_characters[i].name[0] && selectedPortraits[i] == shpCur) + notUsable = true; + } + + if ((charSex && (shpCur < 26)) || (!charSex && (shpCur > 28))) + notUsable = true; + + if (notUsable) { + shpCur += step; + shpCur = ((shpCur < 0) ? 43 : shpCur) % 44; + } + } while (notUsable); + + return shpCur; +} + +void CharacterGenerator::processFaceMenuSelection(int index) { + highlightBoxFrame(-1); + if (index <= 48) + _screen->drawShape(0, _characters[_activeBox].faceShape, _chargenBoxX[_activeBox], _chargenBoxY[_activeBox] + 1, 0); + else + toggleSpecialButton(index - 50, 0, 0); +} + +void CharacterGenerator::printStats(int index, int mode) { + _screen->copyRegion(0, 0, 160, 0, 160, 128, 2, 2, Screen::CR_NO_P_CHECK); + _screen->_curPage = 2; + + EobCharacter *c = &_characters[index]; + + if (mode != 4) + _screen->drawShape(2, c->faceShape, 224, 2, 0); + + _screen->printShadedText(c->name, 160 + ((20 - strlen(c->name)) << 2), 35, 15, 0); + _screen->printShadedText(_chargenRaceSexStrings[c->raceSex], 160 + ((20 - strlen(_chargenRaceSexStrings[c->raceSex])) << 2), 45, 15, 0); + _screen->printShadedText(_chargenClassStrings[c->cClass], 160 + ((20 - strlen(_chargenClassStrings[c->cClass])) << 2), 54, 15, 0); + + for (int i = 0; i < 6; i++) + _screen->printShadedText(_chargenStatStrings[i], 163, (i + 8) << 3, 15, 0); + + _screen->printShadedText(_chargenStrings1[2], 248, 64, 15, 0); + + char str[22]; + snprintf(str, 22, _chargenStrings1[3], _vm->getCharStrength(c->strengthCur, c->strengthExtCur), c->intelligenceCur, c->wisdomCur, c->dexterityCur, c->constitutionCur, c->charismaCur); + _screen->printShadedText(str, 192, 64, 15, 0); + + snprintf(str, 22, _chargenStrings1[4], c->armorClass, c->hitPointsMax); + _screen->printShadedText(str, 280, 64, 15, 0); + + const char *lvlStr = c->level[2] ? _chargenStrings1[7] : (c->level[1] ? _chargenStrings1[6] : _chargenStrings1[5]); + snprintf(str, 22, lvlStr, c->level[0], c->level[1], c->level[2]); + _screen->printShadedText(str, 280, 80, 15, 0); + + switch (mode) { + case 1: + toggleSpecialButton(4, 0, 2); + toggleSpecialButton(7, 0, 2); + toggleSpecialButton(8, 0, 2); + toggleSpecialButton(6, 0, 2); + break; + + case 2: + toggleSpecialButton(16, 0, 2); + toggleSpecialButton(9, 0, 2); + break; + + case 3: + toggleSpecialButton(10, 0, 2); + toggleSpecialButton(11, 0, 2); + toggleSpecialButton(9, 0, 2); + break; + + default: + break; + } + + _screen->copyRegion(160, 0, 144, 64, 160, 128, 2, 0, Screen::CR_NO_P_CHECK); + + if (mode != 3) + _screen->updateScreen(); + + _screen->_curPage = 0; +} + +void CharacterGenerator::processNameInput(int index, int len, int textColor) { + Screen::FontId of = _screen->setFont(Screen::FID_6_FNT); + + // WORKAROUND for bug in original code: + len = strlen(_characters[index].name); + + int xOffs = (60 - _screen->getFontWidth() * len) >> 1; + _screen->printText(_chargenStrings1[1], _chargenNameFieldX[index], _chargenNameFieldY[index], 12, 12); + _screen->printText(_characters[index].name, _chargenNameFieldX[index] + xOffs, _chargenNameFieldY[index], textColor, 0); + _screen->updateScreen(); + _screen->setFont(of); +} + +int CharacterGenerator::rollDice() { + int res = 0; + int min = 10; + + for (int i = 0; i < 4; i++) { + int d = _vm->rollDice(1, 6, 0); + res += d; + if (d < min) + min = d; + } + + res -= min; + return res; +} + +int CharacterGenerator::modifyStat(int index, int8 *stat1, int8 *stat2) { + uint8 *s1 = (uint8*) stat1; + uint8 *s2 = (uint8*) stat2; + + initButtonsFromList(31, 10); + Button *b = _vm->gui_getButton(_vm->_activeButtons, index + 1); + + printStats(_activeBox, 3); + _vm->removeInputTop(); + + char statStr[6]; + if (index) + snprintf(statStr, 6, "%d", *s1); + else + snprintf(statStr, 6, "%s", _vm->getCharStrength(*s1, *s2)); + + _screen->copyRegion(b->x - 112, b->y - 64, b->x + 32, b->y, 40, b->height, 2, 0, Screen::CR_NO_P_CHECK); + _screen->printShadedText(statStr, b->x + 32, b->y, 6, 0); + _screen->updateScreen(); + + EobCharacter *c = &_characters[_activeBox]; + + int ci = index; + uint8 v2 = s2 ? *s2 : 0; + + if (index == 6) { + _chargenMaxStats[6] = getMaxHp(c->cClass, c->constitutionCur, c->level[0], c->level[1], c->level[2]); + _chargenMinStats[6] = getMinHp(c->cClass, c->constitutionCur, c->level[0], c->level[1], c->level[2]); + } + + for (bool loop = true; loop && !_vm->shouldQuit(); ) { + uint8 v1 = *s1; + updateMagicShapes(); + int inputFlag = _vm->checkInput(_vm->_activeButtons, false, 0); + _vm->removeInputTop(); + + if (inputFlag == _vm->_keyMap[Common::KEYCODE_LEFT] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP4] || inputFlag == _vm->_keyMap[Common::KEYCODE_MINUS] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP_MINUS] || inputFlag == 0x8009) { + processSpecialButton(11); + v1--; + + } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_RIGHT] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP6] || inputFlag == _vm->_keyMap[Common::KEYCODE_PLUS] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP_PLUS] || inputFlag == 0x8008) { + processSpecialButton(10); + v1++; + + } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_UP] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP8]) { + ci = --ci % 7; + loop = false; + + } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_DOWN] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP2]) { + ci = ++ci % 7; + loop = false; + + } else if (inputFlag == _vm->_keyMap[Common::KEYCODE_o] || inputFlag == _vm->_keyMap[Common::KEYCODE_KP5] || inputFlag == _vm->_keyMap[Common::KEYCODE_ESCAPE] || inputFlag == 0x800A) { + processSpecialButton(9); + loop = false; + ci = -2; + + } else if (inputFlag & 0x8000) { + inputFlag = (inputFlag & 0x0f) - 1; + if (index != inputFlag) { + ci = inputFlag; + loop = false; + } + } + + if (v1 == *s1) + continue; + + if (!index) { + while (v1 > 18) { + v1--; + v2++; + } + while (v2 > 0 && v1 < 18) { + v1++; + v2--; + } + + v1 = CLIP<uint8>(v1, _chargenMinStats[index], _chargenMaxStats[index] & 0xff); + v2 = (v1 == 18 && _chargenMaxStats[index] >= 19) ? CLIP<uint8>(v2, 0, 100) : 0; + if (s2) + *s2 = v2; + else + error("CharacterGenerator::modifyStat:..."); + } else { + v1 = CLIP<uint8>(v1, _chargenMinStats[index], _chargenMaxStats[index]); + } + + *s1 = v1; + + if (index == 6) + _characters[_activeBox].hitPointsMax = v1; + + if (index) + snprintf(statStr, 6, "%d", *s1); + else + snprintf(statStr, 6, "%s", _vm->getCharStrength(*s1, *s2)); + + _screen->copyRegion(b->x - 112, b->y - 64, b->x + 32, b->y, 40, b->height, 2, 0, Screen::CR_NO_P_CHECK); + _screen->printShadedText(statStr, b->x + 32, b->y, 6, 0); + _screen->updateScreen(); + + if (index == 4) { + int oldVal = c->hitPointsCur; + _chargenMaxStats[6] = getMaxHp(c->cClass, c->constitutionCur, c->level[0], c->level[1], c->level[2]); + _chargenMinStats[6] = getMinHp(c->cClass, c->constitutionCur, c->level[0], c->level[1], c->level[2]); + c->hitPointsMax = c->hitPointsCur = CLIP<int16>(c->hitPointsCur, _chargenMinStats[6], _chargenMaxStats[6]); + + if (c->hitPointsCur != oldVal) { + snprintf(statStr, 6, "%d", c->hitPointsCur); + _screen->copyRegion(120, 72, 264, 136, 40, 8, 2, 0, Screen::CR_NO_P_CHECK); + _screen->printShadedText(statStr, 264, 136, 15, 0); + _screen->updateScreen(); + } + + } else if (index == 3) { + int oldVal = c->armorClass; + c->armorClass = _vm->getDexterityArmorClassModifier(v1) + 10; + + if (c->armorClass != oldVal) { + snprintf(statStr, 6, "%d", c->armorClass); + _screen->copyRegion(120, 64, 264, 128, 40, 8, 2, 0, Screen::CR_NO_P_CHECK); + _screen->printShadedText(statStr, 264, 128, 15, 0); + _screen->updateScreen(); + } + } + + if (loop = false) { + if (index) + snprintf(statStr, 6, "%d", *s1); + else + snprintf(statStr, 6, "%s", _vm->getCharStrength(*s1, *s2)); + _screen->printText(statStr, b->x + 32, b->y, 15, 0); + _screen->updateScreen(); + } + } + + return ci; +} + +int CharacterGenerator::getMaxHp(int cclass, int constitution, int level1, int level2, int level3) { + int res = 0; + constitution = _vm->getClassAndConstHitpointsModifier(cclass, constitution); + + int m = _vm->getClassHpIncreaseType(cclass, 0); + if (m != -1) + res = _vm->getModifiedHpLimits(m, constitution, level1, false); + + m = _vm->getClassHpIncreaseType(cclass, 1); + if (m != -1) + res += _vm->getModifiedHpLimits(m, constitution, level2, false); + + m = _vm->getClassHpIncreaseType(cclass, 2); + if (m != -1) + res += _vm->getModifiedHpLimits(m, constitution, level3, false); + + res /= _vm->_numLevelsPerClass[cclass]; + + return res; +} + +int CharacterGenerator::getMinHp(int cclass, int constitution, int level1, int level2, int level3) { + int res = 0; + constitution = _vm->getClassAndConstHitpointsModifier(cclass, constitution); + + int m = _vm->getClassHpIncreaseType(cclass, 0); + if (m != -1) + res = _vm->getModifiedHpLimits(m, constitution, level1, true); + + m = _vm->getClassHpIncreaseType(cclass, 1); + if (m != -1) + res += _vm->getModifiedHpLimits(m, constitution, level2, true); + + m = _vm->getClassHpIncreaseType(cclass, 2); + if (m != -1) + res += _vm->getModifiedHpLimits(m, constitution, level3, true); + + res /= _vm->_numLevelsPerClass[cclass]; + + return res; +} + +void CharacterGenerator::finish() { + _screen->copyRegion(0, 0, 160, 0, 160, 128, 2, 2, Screen::CR_NO_P_CHECK); + int cp = _screen->setCurPage(2); + _screen->printShadedText(_chargenEnterGameStrings[0], 168, 32, 15, 0); + _screen->setCurPage(cp); + _screen->copyRegion(160, 0, 144, 64, 160, 128, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + + if (_vm->game() == GI_EOB1) { + static const int8 classDefaultItemsList[] = { 1, 17, 2, 17, 46, -1, 4, -1, 5, -1, 6, 2, 7, -1, 8, -1, 9, 21, 10, 2, 31, 2 }; + static const int8 classDefaultItemsListIndex[] = { 4, 8, 0, -1, 4, 3, 0, -1, 4, 10, 0, 8, 3, 6, 1, -1, 2, 7, 0, -1, + 4, 5, 0, -1, 4, 7, 0, 8, 4, 5, 0, 8, 4, 6, 8, 8, 4, 6, 5, 8, 3, 6, 5, -1, 2, 7, 5, 0, 4, 6, 7, 0, 4, 3, 7, 0, 2, 6, 7, 1 }; + + _characters[0].inventory[2] = _vm->duplicateItem(35); + + for (int i = 0; i < 4; i++) { + EobCharacter *c = &_characters[i]; + c->flags = 1; + c->food = 100; + c->id = i; + c->inventory[3] = _vm->duplicateItem(10); + + for (int ii = 0; ii < 4; ii++) { + int l = classDefaultItemsListIndex[(c->cClass << 2) + ii] << 1; + if (classDefaultItemsList[l] == -1) + continue; + + int d = classDefaultItemsList[l]; + int slot = classDefaultItemsList[l + 1]; + + if (slot < 0) { + slot = 0; + if (c->inventory[slot]) + slot++; + if (c->inventory[slot]) + slot++; + } + + if (slot != 2 && c->inventory[slot]) + continue; + + if (d == 5 && (c->raceSex >> 1) == 3) + d = 36; + + if (slot == 2) { + while (c->inventory[slot]) + slot++; + } + + c->inventory[slot] = _vm->duplicateItem(d); + } + + _vm->recalcArmorClass(i); + } + + } else { + static const uint8 classDefaultItemsListIndex[] = { 0, 0, 0, 1, 2, 3, 0, 0, 0, 0, 3, 2, 0, 0, 2 }; + static const int8 itemList0[] = { 3, 36, 0, 17, -1, 0, 0, 56, 1, 17, 31, 0, 1, 23, 1, 17, 31, 1, 1 }; + static const int8 itemList1[] = { 1, 2, 0, 17, -1, 0, 0 }; + static const int8 itemList2[] = { 2, 56, 1, 17, 31, 0, 1, 23, 1, 17, 31, 0, 1 }; + static const int8 itemList3[] = { 2, 1, 1, 17, 31, 1, 1, 1, 0, 17, 31, 2, 1 }; + static const int8 *itemList[] = { itemList0, itemList1, itemList2, itemList3 }; + + for (int i = 0; i < 4; i++) { + EobCharacter *c = &_characters[i]; + c->flags = 1; + c->food = 100; + c->id = i; + const int8 *df = itemList[classDefaultItemsListIndex[c->cClass]]; + int v1 = _vm->rollDice(1, *df++, -1); + + df = &df[v1 * 6]; + for (int ii = 0; ii < 2; ii++) { + if (df[0] == -1) + break; + _vm->createInventoryItem(c, df[0], df[1], df[2]); + df = &df[3]; + } + + uint16 m = _vm->_classModifierFlags[c->cClass]; + v1 = _vm->rollDice(1, 2, -1); + + int ival = 0; + int itype = 0; + + if (m & 0x31) { + if ((c->raceSex >> 1) == 3) { + itype = 22; + ival = 1; + } else { + if (v1 == 0) { + itype = 5; + ival = 1; + } else { + itype = 34; + } + } + } else if (m & 0x04) { + itype = 26; + if (v1 != 0) + ival = 1; + } else if (m & 0x08) { + ival = 1; + itype = ((c->raceSex >> 1) == 3) ? 22 : 5; + } else { + if (v1 == 0) { + itype = 3; + } else { + itype = 4; + ival = 1; + } + } + + _vm->createInventoryItem(c, itype, ival, 0); + _vm->createInventoryItem(c, 10, -1, 2); + _vm->createInventoryItem(c, 10, -1, 2); + _vm->createInventoryItem(c, 24, 2, 2); + + if (_vm->_classModifierFlags[c->cClass] & 2) { + _vm->createInventoryItem(c, 7, -1, 1); + _vm->createInventoryItem(c, 21, 4, 2); + _vm->createInventoryItem(c, 21, 13, 2); + } + + if (_vm->_classModifierFlags[c->cClass] & 0x14) { + if (c->cClass == 2) + _vm->createInventoryItem(c, 27, -1, 1); + else + _vm->createInventoryItem(c, 8, -1, 1); + + _vm->createInventoryItem(c, 20, 49, 1); + } + + if (_vm->_classModifierFlags[c->cClass] & 8) + _vm->createInventoryItem(c, 6, -1, 1); + + if (i == 0) + _vm->createInventoryItem(c, 93, -1, 2); + + _vm->recalcArmorClass(i); + } + } + + for (int i = 0; i < 4; i++) { + if (_vm->_classModifierFlags[_characters[i].cClass] & 2) + _characters[i].mageSpellsAvailabilityFlags = (_vm->game() == GI_EOB2) ? 0x81CB6 : 0x26C; + + if (_vm->_classModifierFlags[_characters[i].cClass] & 0x14 && _vm->game() == GI_EOB2) { + // Cleric: Turn Undead + _characters[i].clericSpells[0] = 29; + } + } + + for (int i = 0; i < 4; i++) { + EobCharacter *c = &_characters[i]; + c->strengthMax = c->strengthCur; + c->strengthExtMax = c->strengthExtCur; + c->intelligenceMax = c->intelligenceCur; + c->wisdomMax = c->wisdomCur; + c->dexterityMax = c->dexterityCur; + c->constitutionMax = c->constitutionCur; + c->charismaMax = c->charismaCur; + c->hitPointsMax = c->hitPointsCur; + } + + _vm->gui_resetButtonList(); + + if (_faceShapes) { + for (int i = 0; i < 44; i++) { + bool del = true; + for (int ii = 0; ii < 4; ii++) { + if (_characters[ii].faceShape == _faceShapes[i]) + del = false; + } + if (del) + delete[] _faceShapes[i]; + } + delete[] _faceShapes; + _faceShapes = 0; + } + + if (_chargenMagicShapes) { + for (int i = 0; i < 10; i++) + delete[] _chargenMagicShapes[i]; + delete[] _chargenMagicShapes; + _chargenMagicShapes = 0; + } + + if (_chargenButtonLabels) { + for (int i = 0; i < 10; i++) + delete[] _chargenButtonLabels[i]; + delete[] _chargenButtonLabels; + _chargenButtonLabels = 0; + } +} + +const EobChargenButtonDef CharacterGenerator::_chargenButtonDefs[] = { + { 0x01, 0x37, 0x31, 0x32, 0x70 }, + { 0x09, 0x37, 0x31, 0x32, 0x71 }, + { 0x01, 0x77, 0x31, 0x32, 0x72 }, + { 0x09, 0x77, 0x31, 0x32, 0x73 }, + { 0x03, 0xB5, 0x53, 0x10, 0x1A }, + { 0x21, 0xAC, 0x26, 0x10, 0x19 }, + { 0x1C, 0xAC, 0x26, 0x10, 0x21 }, + { 0x21, 0xAC, 0x26, 0x10, 0x32 }, + { 0x13, 0x50, 0x9A, 0x08, 0x00 }, + { 0x13, 0x58, 0x9A, 0x08, 0x00 }, + { 0x13, 0x60, 0x9A, 0x08, 0x00 }, + { 0x13, 0x68, 0x9A, 0x08, 0x00 }, + { 0x13, 0x70, 0x9A, 0x08, 0x00 }, + { 0x13, 0x78, 0x9A, 0x08, 0x00 }, + { 0x13, 0x80, 0x9A, 0x08, 0x00 }, + { 0x13, 0x88, 0x9A, 0x08, 0x00 }, + { 0x13, 0x90, 0x9A, 0x08, 0x00 }, + { 0x13, 0x98, 0x9A, 0x08, 0x00 }, + { 0x13, 0xA0, 0x9A, 0x08, 0x00 }, + { 0x13, 0xA8, 0x9A, 0x08, 0x00 }, + { 0x13, 0xB0, 0x9A, 0x08, 0x00 }, + { 0x12, 0x42, 0x20, 0x10, 0x00 }, + { 0x12, 0x52, 0x20, 0x10, 0x00 }, + { 0x16, 0x42, 0x20, 0x20, 0x00 }, + { 0x1A, 0x42, 0x20, 0x20, 0x00 }, + { 0x1E, 0x42, 0x20, 0x20, 0x00 }, + { 0x22, 0x42, 0x20, 0x20, 0x00 }, + { 0x1C, 0x9C, 0x26, 0x10, 0x14 }, + { 0x21, 0x9C, 0x26, 0x10, 0x34 }, + { 0x1C, 0xAC, 0x26, 0x10, 0x22 }, + { 0x21, 0xAC, 0x26, 0x10, 0x26 }, + { 0x12, 0x80, 0x35, 0x08, 0x00 }, + { 0x12, 0x88, 0x35, 0x08, 0x00 }, + { 0x12, 0x90, 0x35, 0x08, 0x00 }, + { 0x12, 0x98, 0x35, 0x08, 0x00 }, + { 0x12, 0xA0, 0x35, 0x08, 0x00 }, + { 0x12, 0xA8, 0x35, 0x08, 0x00 }, + { 0x1D, 0x88, 0x35, 0x08, 0x00 }, + { 0x1B, 0xAC, 0x15, 0x10, 0x0D }, + { 0x1E, 0xAC, 0x15, 0x10, 0x0C }, + { 0x21, 0xAC, 0x25, 0x10, 0x19 } +}; + +const CreatePartyModButton CharacterGenerator::_chargenModButtons[] = { + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x40 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0A, 0x40 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x80 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0A, 0x80 }, + { 0x00, 0xC0, 0x04, 0x05, 0x07, 0x05, 0x08, 0x1C, 0x9C }, + { 0x04, 0xC0, 0x03, 0x05, 0x0A, 0x05, 0x08, 0x21, 0xAC }, + { 0x07, 0xC0, 0x03, 0x05, 0x0B, 0x05, 0x08, 0x21, 0xAC }, + { 0x0A, 0xC0, 0x04, 0x05, 0x06, 0x05, 0x08, 0x21, 0x9C }, + { 0x18, 0xC0, 0x03, 0x05, 0x09, 0x05, 0x08, 0x1C, 0xAC }, + { 0x0E, 0xC0, 0x02, 0x05, 0x0F, 0x05, 0x08, 0x21, 0xAC }, + { 0x10, 0xC0, 0x01, 0x05, 0x09, 0x05, 0x04, 0x1B, 0xAC }, + { 0x11, 0xC0, 0x01, 0x01, 0x09, 0x07, 0x04, 0x1E, 0xAC }, + { 0x12, 0xC0, 0x03, 0x07, 0x07, 0x04, 0x06, 0x12, 0x42 }, + { 0x15, 0xC0, 0x03, 0x07, 0x07, 0x04, 0x06, 0x12, 0x52 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x03, 0xB5 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x03, 0xB5 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x1C, 0xAC } +}; + +const EobRect8 CharacterGenerator::_chargenButtonBodyCoords[] = { + { 0x00, 0x80, 0x04, 0x20 }, + { 0x04, 0x80, 0x04, 0x20 }, + { 0x08, 0x80, 0x04, 0x20 }, + { 0x0C, 0x80, 0x04, 0x20 }, + { 0x0E, 0xA0, 0x03, 0x10 }, + { 0x0B, 0xA0, 0x03, 0x10 }, + { 0x10, 0x80, 0x04, 0x10 }, + { 0x10, 0x90, 0x04, 0x10 }, + { 0x11, 0xA0, 0x05, 0x10 }, + { 0x11, 0xB0, 0x05, 0x10 }, + { 0x16, 0xA0, 0x05, 0x10 }, + { 0x16, 0xB0, 0x05, 0x10 }, + { 0x00, 0xA0, 0x0B, 0x10 }, + { 0x14, 0x80, 0x0B, 0x10 }, + { 0x14, 0x90, 0x0B, 0x10 } +}; + +const EobRect16 CharacterGenerator::_chargenPortraitBoxFrames[] = { + { 0x00B7, 0x0001, 0x00F7, 0x0034 }, + { 0x00FF, 0x0001, 0x013F, 0x0034 }, + { 0x00B7, 0x0035, 0x00F7, 0x0068 }, + { 0x00FF, 0x0035, 0x013F, 0x0068 }, + { 0x00B7, 0x0069, 0x00F7, 0x009C }, + { 0x00FF, 0x0069, 0x013F, 0x009C }, + { 0x0010, 0x003F, 0x0030, 0x0060 }, + { 0x0050, 0x003F, 0x0070, 0x0060 }, + { 0x0010, 0x007F, 0x0030, 0x00A0 }, + { 0x0050, 0x007F, 0x0070, 0x00A0 }, + { 0x00B0, 0x0042, 0x00D0, 0x0061 }, + { 0x00D0, 0x0042, 0x00F0, 0x0061 }, + { 0x00F0, 0x0042, 0x0110, 0x0061 }, + { 0x0110, 0x0042, 0x0130, 0x0061 }, + { 0x0004, 0x0018, 0x0024, 0x0039 }, + { 0x00A3, 0x0018, 0x00C3, 0x0039 }, + { 0x0004, 0x0040, 0x0024, 0x0061 }, + { 0x00A3, 0x0040, 0x00C3, 0x0061 }, + { 0x0004, 0x0068, 0x0024, 0x0089 }, + { 0x00A3, 0x0068, 0x00C3, 0x0089 } +}; + +const int16 CharacterGenerator::_chargenBoxX[] = { 0x10, 0x50, 0x10, 0x50 }; +const int16 CharacterGenerator::_chargenBoxY[] = { 0x3F, 0x3F, 0x7F, 0x7F }; +const int16 CharacterGenerator::_chargenNameFieldX[] = { 0x02, 0x42, 0x02, 0x42 }; +const int16 CharacterGenerator::_chargenNameFieldY[] = { 0x6B, 0x6B, 0xAB, 0xAB }; + +const int32 CharacterGenerator::_classMenuMasks[] = { + 0x003F, 0x07BB, 0x77FB, 0x00F1, 0x08F1, 0x00B1 +}; + +const int32 CharacterGenerator::_alignmentMenuMasks[] = { + 0x01FF, 0x0007, 0x0001, 0x01FF, 0x01FF, 0x01FE, 0x01FF, 0x01FE, + 0x01FF, 0x01FE, 0x01FE, 0x01FE, 0x01FF, 0x0007, 0x01FF +}; + +const int16 CharacterGenerator::_raceModifiers[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, -1, 0, 1, -1, 0, 0, 0, -1, 0, 0, 1, 0, 0 +}; + +} // End of namespace Kyra + +#endif // ENABLE_EOB diff --git a/engines/kyra/detection.cpp b/engines/kyra/detection.cpp index 68eb08210e..44fbc0fe68 100644 --- a/engines/kyra/detection.cpp +++ b/engines/kyra/detection.cpp @@ -17,12 +17,15 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * */ #include "kyra/kyra_lok.h" #include "kyra/lol.h" #include "kyra/kyra_hof.h" #include "kyra/kyra_mr.h" +#include "kyra/eob1.h" +#include "kyra/eob2.h" #include "common/config-manager.h" #include "common/system.h" @@ -129,6 +132,14 @@ bool KyraMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGame *engine = new Kyra::LoLEngine(syst, flags); break; #endif // ENABLE_LOL +#ifdef ENABLE_EOB + case Kyra::GI_EOB1: + *engine = new Kyra::EobEngine(syst, flags); + break; + case Kyra::GI_EOB2: + *engine = new Kyra::DarkMoonEngine(syst, flags); + break; +#endif // ENABLE_EOB default: res = false; warning("Kyra engine: unknown gameID"); diff --git a/engines/kyra/detection_tables.h b/engines/kyra/detection_tables.h index ebf7c8eee7..50a154dcf3 100644 --- a/engines/kyra/detection_tables.h +++ b/engines/kyra/detection_tables.h @@ -57,6 +57,9 @@ namespace { #define LOL_DEMO_FLAGS FLAGS(true, true, false, false, false, false, false, false, Kyra::GI_LOL) #define LOL_KYRA2_DEMO_FLAGS FLAGS(true, false, false, false, false, false, false, false, Kyra::GI_KYRA2) +#define EOB_FLAGS FLAGS(false, false, false, false, false, false, false, false, Kyra::GI_EOB1) +#define EOB2_FLAGS FLAGS(false, false, false, false, false, false, false, false, Kyra::GI_EOB2) + const KYRAGameDescription adGameDescs[] = { /* disable these targets until they get supported { @@ -1439,6 +1442,73 @@ const KYRAGameDescription adGameDescs[] = { LOL_KYRA2_DEMO_FLAGS }, #endif // ENABLE_LOL +#ifdef ENABLE_EOB + + { + { + "eob", + 0, + { + { "EOBDATA2.PAK", 0, "feaf0345086b3a1d931352f4b0ad8feb", -1 }, + { 0, 0, 0, 0 } + }, + Common::EN_ANY, + Common::kPlatformPC, + ADGF_NO_FLAGS, + GUIO3(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK) + }, + EOB_FLAGS + }, + + { + { + "eob", + 0, + { + { "SHINDIA.CPS", 0, "383b0c7ba0903eae5d04cad28ce90aaf", -1 }, + { 0, 0, 0, 0 } + }, + Common::DE_DEU, + Common::kPlatformPC, + ADGF_NO_FLAGS, + GUIO3(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK) + }, + EOB_FLAGS + }, + + { + { + "eob2", + 0, + { + { "LEVEL15.INF", 0, "10f19eab75c73d0476dc58bcf70fff7a", -1 }, + { 0, 0, 0, 0 } + }, + Common::EN_ANY, + Common::kPlatformPC, + ADGF_NO_FLAGS, + GUIO3(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK) + }, + EOB2_FLAGS + }, + + { + { + "eob2", + 0, + { + { "LEVEL15.INF", 0, "ce54243ad1ca4447f521340428da2c91", -1 }, + { 0, 0, 0, 0 } + }, + Common::DE_DEU, + Common::kPlatformPC, + ADGF_NO_FLAGS, + GUIO3(GUIO_NOSPEECH, GUIO_MIDIADLIB, GUIO_MIDIPCSPK) + }, + EOB2_FLAGS + }, +#endif // ENABLE_EOB + { AD_TABLE_END_MARKER, FLAGS(0, 0, 0, 0, 0, 0, 0, 0, 0) } }; @@ -1449,6 +1519,10 @@ const PlainGameDescriptor gameList[] = { #ifdef ENABLE_LOL { "lol", "Lands of Lore: The Throne of Chaos" }, #endif // ENABLE_LOL +#ifdef ENABLE_EOB + { "eob", "Eye of the Beholder" }, + { "eob2", "Eye of the Beholder II: The Legend of Darkmoon" }, +#endif // ENABLE_EOB { 0, 0 } }; diff --git a/engines/kyra/eob1.cpp b/engines/kyra/eob1.cpp new file mode 100644 index 0000000000..993d06a56b --- /dev/null +++ b/engines/kyra/eob1.cpp @@ -0,0 +1,242 @@ +/* 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. + * + */ + +#ifdef ENABLE_EOB + +#include "kyra/eob1.h" +#include "kyra/resource.h" + +namespace Kyra { + +EobEngine::EobEngine(OSystem *system, const GameFlags &flags) : EobCoreEngine(system, flags) { + _numSpells = 53; +} + +EobEngine::~EobEngine() { + delete[] _itemsOverlay; +} + +Common::Error EobEngine::init() { + Common::Error err = EobCoreEngine::init(); + if (err.getCode() != Common::kNoError) + return err; + + initStaticResource(); + + _itemsOverlay = _res->fileData("ITEMRMP.VGA", 0); + + _bkgColor_1 = 132; + _color1_1 = 135; + _color2_1 = 130; + _color4 = 133; + _color5 = 133; + _color6 = 180; + _color7 = 177; + _color8 = 184; + + _color14 = _color8; + _color13 = _color7; + _color12 = _color6; + + _screen->modifyScreenDim(7, 0x01, 0xB3, 0x22, 0x12); + _screen->modifyScreenDim(9, 0x01, 0x7D, 0x26, 0x3F); + _screen->modifyScreenDim(12, 0x01, 0x04, 0x14, 0xA0); + + _scriptTimersCount = 1; + + return Common::kNoError; +} + +void EobEngine::startupNew() { + _currentLevel = 1; + _currentSub = 0; + loadLevel(1, 0); + _currentBlock = 490; + _currentDirection = 0; + setHandItem(0); +} + +void EobEngine::startupLoad() { + updateHandItemCursor(); + loadLevel(_currentLevel, _currentSub); + _saveLoadMode = 0; +} + +void EobEngine::npcSequence(int npcIndex) { + + +} + +void EobEngine::updateUsedCharacterHandItem(int charIndex, int slot) { + EobItem *itm = &_items[_characters[charIndex].inventory[slot]]; + if (itm->type == 48) { + int charges = itm->flags & 0x3f; + if (--charges) + --itm->flags; + else + deleteInventoryItem(charIndex, slot); + } else if (itm->type == 34 || itm->type == 35) { + deleteInventoryItem(charIndex, slot); + } +} + +void EobEngine::replaceMonster(int unit, uint16 block, int pos, int dir, int type, int shpIndex, int mode, int h2, int randItem, int fixedItem) { + if (_levelBlockProperties[block].flags & 7) + return; + + for (int i = 0; i < 30; i++) { + if (_monsters[i].hitPointsCur <= 0) { + initMonster(i, unit, block, pos, dir, type, shpIndex, mode, h2, randItem, fixedItem);; + break; + } + } +} + +void EobEngine::loadDoorShapes(int doorType1, int shapeId1, int doorType2, int shapeId2) { + _screen->loadEobBitmap("DOOR", 5, 3); + _screen->_curPage = 2; + + if (doorType1 != 0xff) { + for (int i = 0; i < 3; i++) { + const uint8 *enc = &_doorShapeEncodeDefs[(doorType1 * 3 + i) << 2]; + _doorShapes[shapeId1 + i] = _screen->encodeShape(enc[0], enc[1], enc[2], enc[3]); + enc = &_doorSwitchShapeEncodeDefs[(doorType1 * 3 + i) << 2]; + _doorSwitches[shapeId1 + i].shp = _screen->encodeShape(enc[0], enc[1], enc[2], enc[3]); + _doorSwitches[shapeId1 + i].x = _doorSwitchCoords[doorType1 << 1]; + _doorSwitches[shapeId1 + i].y = _doorSwitchCoords[(doorType1 << 1) + 1]; + } + } + + if (doorType2 != 0xff) { + for (int i = 0; i < 3; i++) { + const uint8 *enc = &_doorShapeEncodeDefs[(doorType2 * 3 + i) << 2]; + _doorShapes[shapeId2 + i] = _screen->encodeShape(enc[0], enc[1], enc[2], enc[3]); + enc = &_doorSwitchShapeEncodeDefs[(doorType2 * 3 + i) << 2]; + _doorSwitches[shapeId2 + i].shp = _screen->encodeShape(enc[0], enc[1], enc[2], enc[3]); + _doorSwitches[shapeId2 + i].x = _doorSwitchCoords[doorType2 << 1]; + _doorSwitches[shapeId2 + i].y = _doorSwitchCoords[(doorType2 << 1) + 1]; + } + } + + _screen->_curPage = 0; +} + +void EobEngine::drawDoorIntern(int type, int index, int x, int y, int w, int wall, int mDim, int16 y1, int16 y2) { + int shapeIndex = type + 2 - mDim; + uint8 *shp = _doorShapes[shapeIndex]; + + int d1 = 0; + int d2 = 0; + int v = 0; + const ScreenDim *td = _screen->getScreenDim(5); + + switch (_currentLevel) { + case 4: + case 5: + case 6: + y = _dscDoorY2[mDim] - shp[1]; + d1 = _dscDoorCoordsExt[index << 1] >> 3; + d2 = _dscDoorCoordsExt[(index << 1) + 1] >> 3; + if (_shpDmX1 > d1) + d1 = _shpDmX1; + if (_shpDmX2 < d2) + d2 = _shpDmX2; + _screen->modifyScreenDim(5, d1, td->sy, d2 - d1, td->h); + v = ((wall < 30) ? (wall - _dscDoorScaleOffs[wall]) * _dscDoorScaleMult3[mDim] : _dscDoorScaleMult4[mDim]) * -1; + v -= (shp[2] << 3); + drawBlockObject(0, 2, shp, x + v, y, 5); + v += (shp[2] << 3); + drawBlockObject(1, 2, shp, x - v, y, 5); + if (_wllShapeMap[wall] == -1) + drawBlockObject(0, 2, _doorSwitches[shapeIndex].shp, _doorSwitches[shapeIndex].x + w - v, _doorSwitches[shapeIndex].y, 5); + break; + + case 7: + case 8: + case 9: + y = _dscDoorY3[mDim] - _doorShapes[shapeIndex + 3][1]; + d1 = x - (_doorShapes[shapeIndex + 3][2] << 2); + x -= (shp[2] << 2); + drawBlockObject(0, 2, _doorShapes[shapeIndex + 3], d1, y, 5); + scaleLevelShapesDim(index, y1, y2, 5); + y = _dscDoorY3[mDim] - ((wall < 30) ? (wall - _dscDoorScaleOffs[wall]) * _dscDoorScaleMult1[mDim] : _dscDoorScaleMult2[mDim]); + drawBlockObject(0, 2, shp, x, y, 5); + if (_wllShapeMap[wall] == -1) + drawBlockObject(0, 2, _doorSwitches[shapeIndex].shp, _doorSwitches[shapeIndex].x + w, _doorSwitches[shapeIndex].y, 5); + break; + + case 10: + case 11: + v = ((wall < 30) ? (wall - _dscDoorScaleOffs[wall]) * _dscDoorScaleMult5[mDim] : _dscDoorScaleMult6[mDim]) * -1; + x -= (shp[2] << 2); + y = _dscDoorY3[mDim] + v; + drawBlockObject(0, 2, shp, x, y + v, 5); + v >>= 4; + y = _dscDoorY5[mDim]; + drawBlockObject(0, 2, _doorShapes[shapeIndex + 3], x, y - v, 5); + if (_wllShapeMap[wall] == -1) + drawBlockObject(0, 2, _doorSwitches[shapeIndex].shp, _doorSwitches[shapeIndex].x + w, _doorSwitches[shapeIndex].y, 5); + break; + + default: + y = (_currentLevel == 12 ? _dscDoorY6[mDim] : _dscDoorY1[mDim]) - shp[1]; + x -= (shp[2] << 2); + y -= (wall >= 30 ? _dscDoorScaleMult2[mDim] : (wall - _dscDoorScaleOffs[wall]) * _dscDoorScaleMult1[mDim]); + drawBlockObject(0, 2, shp, x, y, 5); + + if (_wllShapeMap[wall] == -1) + drawBlockObject(0, 2, _doorSwitches[shapeIndex].shp, _doorSwitches[shapeIndex].x + w, _doorSwitches[shapeIndex].y, 5); + break; + } +} + +uint32 EobEngine::convertSpellFlagToEob2Format(uint32 flag, int ignoreInvisibility) { + uint32 res = 0; + if (flag & 0x01) + res |= 0x20; + if (flag & 0x02) + res |= 0x400; + if (flag & 0x04) + res |= 0x80; + if (flag & 0x08) + res |= 0x40; + if (ignoreInvisibility) + res |= 0x100; + return res; +} + +uint32 EobEngine::convertCharacterEffectFlagToEob2Format(uint32 flag) { + uint32 res = 0; + if (flag & 0x02) + res |= 0x08; + if (flag & 0x04) + res |= 0x40; + if (flag & 0x80) + res |= 0x2000; + if (flag & 0x100) + res |= 0x4000; + return res; +} + +} // End of namespace Kyra + +#endif // ENABLE_EOB diff --git a/engines/kyra/eob1.h b/engines/kyra/eob1.h new file mode 100644 index 0000000000..27a060f65c --- /dev/null +++ b/engines/kyra/eob1.h @@ -0,0 +1,96 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifdef ENABLE_EOB + +#ifndef KYRA_EOB1_H +#define KYRA_EOB1_H + +#include "kyra/eobcommon.h" + +namespace Kyra { + +class EobEngine : public EobCoreEngine { +friend class GUI_Eob; +public: + EobEngine(OSystem *system, const GameFlags &flags); + ~EobEngine(); + +private: + // Init / Release + Common::Error init(); + void initStaticResource(); + void initSpells(); + + // Main Menu + int mainMenu(); + int mainMenuLoop(); + + // Main loop + void startupNew(); + void startupLoad(); + + // Intro/Outro + void seq_playOpeningCredits(); + void seq_playIntro(); + void seq_playFinale(); + + // characters + void npcSequence(int npcIndex); + + //const char *const *_npc1Strings; + //const char *const *_npc2Strings; + + // items + void updateUsedCharacterHandItem(int charIndex, int slot); + + // Monsters + void replaceMonster(int unit, uint16 block, int d, int dir, int type, int shpIndex, int mode, int h2, int randItem, int fixedItem); + + // Level + void loadDoorShapes(int doorType1, int shapeId1, int doorType2, int shapeId2); + void drawDoorIntern(int type, int index, int x, int y, int w, int wall, int mDim, int16 y1, int16 y2); + + const int16 *_dscDoorCoordsExt; + const uint8 *_dscDoorScaleMult4; + const uint8 *_dscDoorScaleMult5; + const uint8 *_dscDoorScaleMult6; + const uint8 *_dscDoorY3; + const uint8 *_dscDoorY4; + const uint8 *_dscDoorY5; + const uint8 *_dscDoorY6; + + const uint8 *_doorShapeEncodeDefs; + const uint8 *_doorSwitchShapeEncodeDefs; + const uint8 *_doorSwitchCoords; + + // Misc + uint32 convertSpellFlagToEob2Format(uint32 flag, int ignoreInvisibility); + uint32 convertCharacterEffectFlagToEob2Format(uint32 flag); +}; + + +} // End of namespace Kyra + +#endif + +#endif // ENABLE_EOB diff --git a/engines/kyra/eob2.cpp b/engines/kyra/eob2.cpp new file mode 100644 index 0000000000..120ad157d0 --- /dev/null +++ b/engines/kyra/eob2.cpp @@ -0,0 +1,437 @@ +/* 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. + * + */ + +#ifdef ENABLE_EOB + +#include "kyra/eob2.h" +#include "kyra/resource.h" +#include "kyra/sound.h" + +namespace Kyra { + +DarkMoonEngine::DarkMoonEngine(OSystem *system, const GameFlags &flags) : EobCoreEngine(system, flags) { + _seqIntro = _seqFinale = 0; + _shapesIntro = _shapesFinale = 0; + _dscDoorType5Offs = 0; + _numSpells = 70; +} + +DarkMoonEngine::~DarkMoonEngine() { + delete[] _seqIntro; + delete[] _seqFinale; + delete[] _shapesIntro; + delete[] _shapesFinale; +} + +Common::Error DarkMoonEngine::init() { + Common::Error err = EobCoreEngine::init(); + if (err.getCode() != Common::kNoError) + return err; + + initStaticResource(); + + _monsterProps = new EobMonsterProperty[10]; + + _bkgColor_1 = 183; + _color1_1 = 186; + _color2_1 = 181; + _color4 = 133; + _color5 = 184; + _color6 = 183; + _color7 = 181; + _color8 = 186; + _color12 = 180; + _color13 = 177; + _color14 = 182; + + return Common::kNoError; +} + +void DarkMoonEngine::startupNew() { + _currentLevel = 4; + _currentSub = 0; + loadLevel(4, 0); + _currentBlock = 171; + _currentDirection = 2; + setHandItem(0); +} + +void DarkMoonEngine::startupLoad() { + updateHandItemCursor(); + loadLevel(_currentLevel, _currentSub); + _saveLoadMode = 0; +} + +void DarkMoonEngine::npcSequence(int npcIndex) { + _screen->loadEobBitmap("OUTTAKE", 5, 3); + _screen->copyRegion(0, 0, 0, 0, 176, 120, 0, 6, Screen::CR_NO_P_CHECK); + const uint8 *shpDef = &_npcShpData[npcIndex << 3]; + + for (int i = npcIndex; i != 255; i = shpDef[7]) { + shpDef = &_npcShpData[i << 3]; + _screen->_curPage = 2; + const uint8 *shp = _screen->encodeShape(READ_LE_UINT16(shpDef), shpDef[2], shpDef[3], shpDef[4]); + _screen->_curPage = 0; + _screen->drawShape(0, shp, 88 + shpDef[5] - (shp[2] << 2), 104 + shpDef[6] - shp[1], 5); + delete[] shp; + } + + Common::SeekableReadStream *s = _res->createReadStream("TEXT.DAT"); + _screen->loadFileDataToPage(s, 5, 32000); + delete s; + + gui_drawBox(0, 121, 320, 79, _color1_1, _color2_1, _bkgColor_1); + _txt->setupField(9, true); + _txt->resetPageBreakString(); + + if (npcIndex == 0) { + snd_playSoundEffect(57); + if (npcJoinDialogue(0, 1, 3, 2)) + setScriptFlag(0x40); + } else if (npcIndex == 1) { + snd_playSoundEffect(53); + gui_drawDialogueBox(); + + _txt->printDialogueText(4, 0); + int r = runDialogue(-1, 0, _npc1Strings[0], _npc1Strings[1]) - 1; + + if (r == 0) { + _sound->playTrack(0); + delay(3 * _tickLength); + snd_playSoundEffect(91); + npcJoinDialogue(1, 5, 6, 7); + } else if (r == 1) { + setScriptFlag(0x20); + } + + } else if (npcIndex == 2) { + snd_playSoundEffect(55); + gui_drawDialogueBox(); + + _txt->printDialogueText(8, 0); + int r = runDialogue(-1, 0, _npc2Strings[0], _npc2Strings[1]) - 1; + + if (r == 0) { + if (rollDice(1, 2, -1)) + _txt->printDialogueText(9, _okStrings[0]); + else + npcJoinDialogue(2, 102, 103, 104); + setScriptFlag(8); + } else if (r == 1) { + _currentDirection = 0; + } + } + + _txt->removePageBreakFlag(); + gui_restorePlayField(); +} + +void DarkMoonEngine::updateUsedCharacterHandItem(int charIndex, int slot) { + EobItem *itm = &_items[_characters[charIndex].inventory[slot]]; + if (itm->type == 48 || itm->type == 62) { + if (itm->value == 5) + return; + int charges = itm->flags & 0x3f; + if (--charges) + --itm->flags; + else + deleteInventoryItem(charIndex, slot); + } else if (itm->type == 26 || itm->type == 34 || itm->type == 35) { + deleteInventoryItem(charIndex, slot); + } +} + +void DarkMoonEngine::generateMonsterPalettes(const char *file, int16 monsterIndex) { + int cp = _screen->setCurPage(2); + _screen->loadEobBitmap(file, 3, 3); + uint8 tmpPal[16]; + uint8 newPal[16]; + + for (int i = 0; i < 6; i++) { + int dci = monsterIndex + i; + memcpy(tmpPal, _monsterShapes[dci] + 4, 16); + int colx = 302 + 3 * i; + + for (int ii = 0; ii < 16; ii++) { + uint8 col = _screen->getPagePixel(_screen->_curPage, colx, 184 + ii); + + int iii = 0; + for (; iii < 16; iii++) { + if (tmpPal[iii] == col) { + newPal[ii] = iii; + break; + } + } + + if (iii == 16) + newPal[ii] = 0; + } + + for (int ii = 1; ii < 3; ii++) { + memcpy(tmpPal, _monsterShapes[dci] + 4, 16); + + for (int iii = 0; iii < 16; iii++) { + uint8 col = _screen->getPagePixel(_screen->_curPage, colx + ii, 184 + iii); + if (newPal[iii]) + tmpPal[newPal[iii]] = col; + } + + int c = i; + if (monsterIndex >= 18) + c += 6; + + c = (c << 1) + (ii - 1); + memcpy(_monsterPalettes[c], tmpPal, 16); + } + } + + _screen->setCurPage(cp); +} + +void DarkMoonEngine::loadMonsterDecoration(const char *file, int16 monsterIndex) { + char filename[13]; + snprintf(filename, 13, "%s.dcr", file); + + Common::SeekableReadStream *s = _res->createReadStream(filename); + if (!s) + return; + + int len = s->readUint16LE(); + + for (int i = 0; i < len; i++) { + for (int ii = 0; ii < 6; ii++) { + uint8 dc[6]; + s->read(dc, 6); + if (!dc[2] || !dc[3]) + continue; + + SpriteDecoration *m = &_monsterDecorations[i * 6 + ii + monsterIndex]; + + m->shp = _screen->encodeShape(dc[0], dc[1], dc[2], dc[3]); + m->x = dc[4]; + m->y = dc[5]; + } + } + + delete s; +} + +void DarkMoonEngine::replaceMonster(int unit, uint16 block, int pos, int dir, int type, int shpIndex, int mode, int h2, int randItem, int fixedItem) { + uint8 flg = _levelBlockProperties[block].flags & 7; + + if (flg == 7 || _currentBlock == block || (flg && (_monsterProps[type].u30 || pos == 4))) + return; + + for (int i = 0; i < 30; i++) { + if (_monsters[i].block != block) + continue; + if (_monsters[i].pos == 4 || _monsterProps[_monsters[i].type].u30) + return; + } + + int index = -1; + int maxDist = 0; + + for (int i = 0; i < 30; i++) { + if (_monsters[i].hitPointsCur <= 0) { + index = i; + break; + } + + if (_monsters[i].flags & 0x40) + continue; + + int dist = getBlockDistance(_monsters[i].block, _currentBlock); + + if (dist > maxDist) { + maxDist = dist; + index = i; + } + } + + if (index == -1) + return; + + if (_monsters[index].hitPointsCur > 0) + killMonster(&_monsters[index], false); + + initMonster(index, unit, block, pos, dir, type, shpIndex, mode, h2, randItem, fixedItem); +} + +const uint8 *DarkMoonEngine::loadDoorShapes(const char *filename, int doorIndex, const uint8 *shapeDefs) { + _screen->loadEobBitmap(filename, 3, 3); + for (int i = 0; i < 3; i++) { + _doorShapes[doorIndex * 3 + i] = _screen->encodeShape(READ_LE_UINT16(shapeDefs), READ_LE_UINT16(shapeDefs + 2), READ_LE_UINT16(shapeDefs + 4), READ_LE_UINT16(shapeDefs + 6)); + shapeDefs += 8; + } + + for (int i = 0; i < 2; i++) { + _doorSwitches[doorIndex * 3 + i].shp = _screen->encodeShape(READ_LE_UINT16(shapeDefs), READ_LE_UINT16(shapeDefs + 2), READ_LE_UINT16(shapeDefs + 4), READ_LE_UINT16(shapeDefs + 6)); + shapeDefs += 8; + _doorSwitches[doorIndex * 3 + i].x = *shapeDefs; + shapeDefs += 2; + _doorSwitches[doorIndex * 3 + i]. y= *shapeDefs; + shapeDefs += 2; + } + _screen->_curPage = 0; + return shapeDefs; +} + +void DarkMoonEngine::drawDoorIntern(int type, int, int x, int y, int w, int wall, int mDim, int16, int16) { + int shapeIndex = type * 3 + 2 - mDim; + uint8 *shp = _doorShapes[shapeIndex]; + + if ((_doorType[type] == 0) || (_doorType[type] == 1)) { + y = _dscDoorY1[mDim] - shp[1]; + x -= (shp[2] << 2); + + if (_doorType[type] == 1) { + drawBlockObject(0, 2, shp, x, y, 5); + shp = _doorShapes[3 + shapeIndex]; + } + + y -= ((wall - _dscDoorScaleOffs[wall]) * _dscDoorScaleMult1[mDim]); + + if (_specialWallTypes[wall] == 5) + y -= _dscDoorType5Offs[shapeIndex]; + + } else if (_doorType[type] == 2) { + x -= (shp[2] << 2); + y = _dscDoorY2[mDim] - ((wall - _dscDoorScaleOffs[wall]) * _dscDoorScaleMult3[mDim]); + } + + drawBlockObject(0, 2, shp, x, y, 5); + + if (_wllShapeMap[wall] == -1 && !_noDoorSwitch[type]) + drawBlockObject(0, 2, _doorSwitches[shapeIndex].shp, _doorSwitches[shapeIndex].x + w, _doorSwitches[shapeIndex].y, 5); +} + +void DarkMoonEngine::drawLightningColumn() { + int f = rollDice(1, 2, -1); + int y = 0; + + for (int i = 0; i < 6; i++) { + f ^= 1; + drawBlockObject(f, 2, _lightningColumnShape, 72, y, 5); + y += 64; + } +} + +int DarkMoonEngine::resurrectionSelectDialogue() { + int cnt = 0; + const char *namesList[10]; + memset(namesList, 0, 10 * sizeof(const char*)); + int8 indexList[10]; + + for (int i = 0; i < 6; i++) { + if (!testCharacter(i, 1)) + continue; + if (_characters[i].hitPointsCur != -10) + continue; + + namesList[cnt] = _characters[i].name; + indexList[cnt++] = i; + } + + for (int i = 0; i < 6; i++) { + if (!testCharacter(i, 1)) + continue; + + for (int ii = 0; ii < 27; ii++) { + uint16 inv = _characters[i].inventory[ii]; + if (!inv) + continue; + + if (_items[inv].type != 33) + continue; + + namesList[cnt] = _npcPreset[_items[inv].value - 1].name; + indexList[cnt++] = -_items[inv].value; + } + } + + if (_itemInHand) { + if (_items[_itemInHand].type == 33) { + namesList[cnt] = _npcPreset[_items[_itemInHand].value - 1].name; + indexList[cnt++] = -_items[_itemInHand].value; + } + } + + namesList[cnt] = _abortStrings[0]; + indexList[cnt++] = 99; + + int r = indexList[runDialogue(-1, 1, namesList[0], namesList[1], namesList[2], namesList[3], namesList[4], namesList[5], namesList[6], namesList[7], namesList[8]) - 1]; + if (r == 99) + return 0; + + if (r < 0) { + r = -r; + if (prepareForNewPartyMember(33, r)) + initNpc(r - 1); + } else { + _characters[r].hitPointsCur = 1; + } + + return 1; +} + +int DarkMoonEngine::charSelectDialogue() { + int cnt = 0; + const char *namesList[7]; + memset(namesList, 0, 7 * sizeof(const char*)); + + for (int i = 0; i < 6; i++) { + if (!testCharacter(i, 3)) + continue; + namesList[cnt++] = _characters[i].name; + } + + namesList[cnt++] = _abortStrings[0]; + + int r = runDialogue(-1, 1, namesList[0], namesList[1], namesList[2], namesList[3], namesList[4], namesList[5], namesList[6], 0) - 1; + if (r == cnt - 1) + return 99; + + for (cnt = 0; cnt < 6; cnt++) { + if (!testCharacter(cnt, 3)) + continue; + if (--r < 0) + break; + } + return cnt; +} + +void DarkMoonEngine::characterLevelGain(int charIndex) { + EobCharacter *c = &_characters[charIndex]; + int s = _numLevelsPerClass[c->cClass]; + for (int i = 0; i < s; i++) { + uint32 er = getRequiredExperience(c->cClass, i, c->level[i] + 1); + if (er == 0xffffffff) + continue; + + increaseCharacterExperience(charIndex, er - c->experience[i]); + } +} + +} // End of namespace Kyra + +#endif // ENABLE_EOB diff --git a/engines/kyra/eob2.h b/engines/kyra/eob2.h new file mode 100644 index 0000000000..a36ec0e466 --- /dev/null +++ b/engines/kyra/eob2.h @@ -0,0 +1,119 @@ +/* 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. + * + */ + +#ifdef ENABLE_EOB + +#ifndef KYRA_EOB2_H +#define KYRA_EOB2_H + +#include "kyra/eobcommon.h" + +namespace Kyra { + +class DarkmoonSequenceHelper; + +struct EobSequenceStep { + uint8 command; + uint8 obj; + int16 x1; + uint8 y1; + uint8 delay; + uint8 pal; + uint8 x2; + uint8 y2; + uint8 w; + uint8 h; +}; + +class DarkMoonEngine : public EobCoreEngine { +friend class GUI_Eob; +friend class DarkmoonSequenceHelper; +public: + DarkMoonEngine(OSystem *system, const GameFlags &flags); + ~DarkMoonEngine(); + +private: + // Init / Release + Common::Error init(); + void initStaticResource(); + void initSpells(); + + // Main Menu + int mainMenu(); + int mainMenuLoop(); + + // Main loop + void startupNew(); + void startupLoad(); + + // Intro/Outro + void seq_playIntro(); + void seq_playFinale(); + void seq_playCredits(DarkmoonSequenceHelper *sq, const uint8 *data, int sd, int backupPage, int tempPage, int speed); + + const char * const*_introStrings; + const char * const *_cpsFilesIntro; + const EobSequenceStep **_seqIntro; + const EobShapeDef **_shapesIntro; + + const char * const*_finaleStrings; + const uint8 *_creditsData; + const char * const *_cpsFilesFinale; + const EobSequenceStep **_seqFinale; + const EobShapeDef **_shapesFinale; + + static const char *_palFilesIntro[]; + static const char *_palFilesFinale[]; + + // characters + void npcSequence(int npcIndex); + + const uint8 *_npcShpData; + const char *const *_npc1Strings; + const char *const *_npc2Strings; + + // items + void updateUsedCharacterHandItem(int charIndex, int slot); + + // Monsters + void generateMonsterPalettes(const char *file, int16 monsterIndex); + void loadMonsterDecoration(const char *file, int16 monsterIndex); + void replaceMonster(int unit, uint16 block, int d, int dir, int type, int shpIndex, int mode, int h2, int randItem, int fixedItem); + + // Level + const uint8 *loadDoorShapes(const char *filename, int doorIndex, const uint8 *shapeDefs); + void drawDoorIntern(int type, int, int x, int y, int w, int wall, int mDim, int16, int16); + + const uint8 *_dscDoorType5Offs; + + // misc + void drawLightningColumn(); + int resurrectionSelectDialogue(); + int charSelectDialogue(); + void characterLevelGain(int charIndex); +}; + +} // End of namespace Kyra + +#endif + +#endif // ENABLE_EOB diff --git a/engines/kyra/eobcommon.cpp b/engines/kyra/eobcommon.cpp new file mode 100644 index 0000000000..d82a3dd012 --- /dev/null +++ b/engines/kyra/eobcommon.cpp @@ -0,0 +1,1845 @@ +/* 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. + * + */ + +#ifdef ENABLE_EOB + +#include "common/config-manager.h" + +#include "audio/mididrv.h" +#include "audio/mixer.h" + +#include "kyra/loleobbase.h" +#include "kyra/resource.h" +#include "kyra/sound_intern.h" +#include "kyra/script_eob.h" +#include "kyra/timer.h" + +namespace Kyra { + +EobCoreEngine::EobCoreEngine(OSystem *system, const GameFlags &flags) : LolEobBaseEngine(system, flags), _numLargeItemShapes(flags.gameID == GI_EOB1 ? 14 : 11), + _numSmallItemShapes(flags.gameID == GI_EOB1 ? 23 : 26), _numThrownItemShapes(flags.gameID == GI_EOB1 ? 12 : 9), _numItemIconShapes(flags.gameID == GI_EOB1 ? 89 : 112), + _teleporterWallId(flags.gameID == GI_EOB1 ? 52 : 44) { + _screen = 0; + _gui = 0; + + //_runLoopUnk2 = 0; + //_runLoopTimerUnk = 0; + _playFinale = false; + _runFlag = true; + _saveLoadMode = 0; + _updateHandItemCursor = false; + + _largeItemShapes = _smallItemShapes = _thrownItemShapes = _spellShapes = _firebeamShapes = _itemIconShapes = + _wallOfForceShapes = _teleporterShapes = _sparkShapes = _compassShapes = 0; + _redSplatShape = _greenSplatShape = _deadCharShape = _disabledCharGrid = _blackBoxSmallGrid = + _weaponSlotGrid = _blackBoxWideGrid = _lightningColumnShape = 0; + _tempIconShape = 0; + + _monsterDustStrings = 0; + _monsterDistAttType10 = 0; + _monsterDistAttSfx10 = 0; + _monsterDistAttType17 = 0; + _monsterDistAttSfx17 = 0; + + _faceShapes = 0; + _characters = 0; + _items = 0; + _itemTypes = 0; + _itemNames = 0; + _itemInHand = -1; + _numItems = _numItemNames = 0; + + _castScrollSlot = 0; + _currentSub = 0; + + _itemsOverlay = 0; + + _partyEffectFlags = 0; + _lastUsedItem = 0; + + _levelDecorationRects = 0; + _doorSwitches = 0; + _monsterProps = 0; + _monsterDecorations = 0; + _monsterOvl1 = _monsterOvl2 = 0; + _monsters = 0; + _dstMonsterIndex = 0; + _inflictMonsterDamageUnk = 0; + + _teleporterPulse = 0; + + _dscShapeCoords = 0; + _dscItemPosIndex = 0; + _dscItemShpX = 0; + _dscItemScaleIndex = 0; + _dscItemTileIndex = 0; + _dscItemShapeMap = 0; + _dscDoorScaleOffs = 0; + _dscDoorScaleMult1 = 0; + _dscDoorScaleMult2 = 0; + _dscDoorScaleMult3 = 0; + _dscDoorY1 = 0; + + _color9 = 17; + _color10 = 23; + _color11 = 20; + + _exchangeCharacterId = -1; + _charExchangeSwap = 0; + _hpBarGraphs = true; + + memset(_dialogueLastBitmap, 0, 13); + _dlgUnk1 = 0; + _moveCounter = 0; + _partyResting = false; + + _flyingObjects = 0; + + _inf = 0; + _stepCounter = 0; + _stepsUntilScriptCall = 0; + _scriptTimersMode = 3; + _currentDirection = 0; + + _openBookSpellLevel = 0; + _openBookSpellSelectedItem = 0; + _openBookSpellListOffset = 0; + _openBookChar = _openBookCharBackup = 0; + _openBookType = _openBookTypeBackup = 0; + _openBookSpellList = 0; + _openBookAvailableSpells = 0; + _activeSpellCaster = 0; + _activeSpellCasterPos = 0; + _activeSpell = 0; + _returnAfterSpellCallback = false; + _spells = 0; + _spellAnimBuffer = 0; + _clericSpellOffset = 0; +} + +EobCoreEngine::~EobCoreEngine() { + releaseItemsAndDecorationsShapes(); + releaseTempData(); + + if (_faceShapes) { + for (int i = 0; i < 44; i++) { + if (_characters) { + for (int ii = 0; ii < 6; ii++) { + if (_characters[ii].faceShape == _faceShapes[i]) + _characters[ii].faceShape = 0; + } + } + delete[] _faceShapes[i]; + _faceShapes[i] = 0; + } + delete[] _faceShapes; + } + + if (_characters) { + for (int i = 0; i < 6; i++) + delete[] _characters[i].faceShape; + } + + delete[] _characters; + delete[] _items; + delete[] _itemTypes; + if (_itemNames) { + for (int i = 0; i < 130; i++) + delete _itemNames[i]; + } + delete[] _itemNames; + delete[] _flyingObjects; + + delete[] _monsterOvl1; + delete[] _monsterOvl2; + delete[] _monsters; + + if (_monsterDecorations) { + releaseMonsterShapes(0, 36); + delete[] _monsterShapes; + delete[] _monsterDecorations; + + for (int i = 0; i < 24; i++) + delete[] _monsterPalettes[i]; + delete[] _monsterPalettes; + } + + delete[] _monsterProps; + + if (_doorSwitches) { + releaseDoorShapes(); + delete[] _doorSwitches; + } + + releaseDecorations(); + delete[] _levelDecorationRects; + + delete[] _spells; + delete[] _spellAnimBuffer; + + delete _gui; + _gui = 0; + delete _inf; + delete _timer; + _timer = 0; +} + +Common::Error EobCoreEngine::init() { + // In EOB the timer proc is directly invoked via interrupt 0x1c, 18.2 times per second. + // This makes a tick length of 54.94. + _tickLength = 55; + + _screen = new Screen_Eob(this, _system); + + assert(_screen); + _screen->setResolution(); + + // Setup mixer + _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume")); + _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume")); + _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, ConfMan.getInt("speech_volume")); + + //MidiDriverType midiDriver = MidiDriver::detectDevice(MDT_PCSPK | MDT_ADLIB); + _sound = new SoundAdLibPC(this, _mixer); + assert(_sound); + + if (_sound) + _sound->updateVolumeSettings(); + + _res = new Resource(this); + assert(_res); + _res->reset(); + + if (!screen()->init()) + error("screen()->init() failed"); + + if (ConfMan.hasKey("save_slot")) { + _gameToLoad = ConfMan.getInt("save_slot"); + if (!saveFileLoadable(_gameToLoad)) + _gameToLoad = -1; + } + + setupKeyMap(); + _gui = new GUI_Eob(this); + _txt = new TextDisplayer_Eob(this, _screen); + _inf = new EobInfProcessor(this, _screen); + + _screen->loadFont(Screen::FID_6_FNT, "FONT6.FNT"); + _screen->loadFont(Screen::FID_8_FNT, "FONT8.FNT"); + + _activeButtons = 0; + + _staticres = new StaticResource(this); + assert(_staticres); + if (!_staticres->init()) + error("_staticres->init() failed"); + + Common::Error err = LolEobBaseEngine::init(); + if (err.getCode() != Common::kNoError) + return err; + + initButtonData(); + initStaticResource(); + initSpells(); + + _timer = new TimerManager(this, _system); + assert(_timer); + setupTimers(); + + _wllVmpMap[1] = 1; + _wllVmpMap[2] = 2; + memset(&_wllVmpMap[3], 3, 20); + _wllVmpMap[23] = 4; + _wllVmpMap[24] = 5; + + memcpy(_wllWallFlags, _wllFlagPreset, _wllFlagPresetSize); + + memset(&_specialWallTypes[3], 1, 5); + memset(&_specialWallTypes[13], 1, 5); + _specialWallTypes[8] = _specialWallTypes[18] = 6; + + memset(&_wllShapeMap[3], -1, 5); + memset(&_wllShapeMap[13], -1, 5); + + _wllVcnOffset = 16; + + _monsters = new EobMonsterInPlay[30]; + memset(_monsters, 0, 30 * sizeof(EobMonsterInPlay)); + + _characters = new EobCharacter[6]; + memset(_characters, 0, sizeof(EobCharacter) * 6); + + _items = new EobItem[600]; + memset(_items, 0, sizeof(EobItem) * 600); + + _itemNames = new char*[130]; + for (int i = 0; i < 130; i++) { + _itemNames[i] = new char[35]; + memset(_itemNames[i], 0, 35); + } + + _flyingObjects = new EobFlyingObject[10]; + memset(_flyingObjects, 0, 10 * sizeof(EobFlyingObject)); + + _spellAnimBuffer = new uint8[4096]; + memset(_spellAnimBuffer, 0, 4096); + + memset(_doorType, 0, sizeof(_doorType)); + memset(_noDoorSwitch, 0, sizeof(_noDoorSwitch)); + + _monsterShapes = new uint8*[36]; + memset(_monsterShapes, 0, 36 * sizeof(uint8*)); + _monsterDecorations = new SpriteDecoration[36]; + memset(_monsterDecorations, 0, 36 * sizeof(SpriteDecoration)); + _monsterPalettes = new uint8*[24]; + for (int i = 0; i < 24; i++) + _monsterPalettes[i] = new uint8[16]; + + _doorSwitches = new SpriteDecoration[6]; + memset(_doorSwitches, 0, 6 * sizeof(SpriteDecoration)); + + _monsterOvl1 = new uint8[16]; + _monsterOvl2 = new uint8[16]; + memset(_monsterOvl1, 15, 16 * sizeof(uint8)); + memset(_monsterOvl2, 13, 16 * sizeof(uint8)); + _monsterOvl1[0] = _monsterOvl2[0] = 0; + + return Common::kNoError; +} + +Common::Error EobCoreEngine::go() { + _txt->removePageBreakFlag(); + _screen->loadPalette("palette.col", _screen->getPalette(0)); + _screen->setScreenPalette(_screen->getPalette(0)); + _screen->setFont(Screen::FID_8_FNT); + + loadItemsAndDecorationsShapes(); + _screen->setMouseCursor(0, 0, _itemIconShapes[0]); + _screen->showMouse(); + + //initPlayBuffers + + loadItemDefs(); + + int action = 0; + + if (_gameToLoad != -1) { + if (loadGameState(_gameToLoad).getCode() != Common::kNoError) + error("Couldn't load game slot %d on startup", _gameToLoad); + _gameToLoad = -1; + } else { + action = mainMenu(); + } + + if (action == -1) { + // load game + _saveLoadMode = -1; + startupLoad(); + } else if (action == -2) { + // new game + startCharacterGeneration(); + startupNew(); + } else if (action == -3) { + // transfer party + } + + if (!shouldQuit() && action > -3) { + runLoop(); + + if (_playFinale) + seq_playFinale(); + } + + return Common::kNoError; +} + +void EobCoreEngine::runLoop() { + _envAudioTimer = _system->getMillis() + (rollDice(1, 10, 3) * 18 * _tickLength); + /// + // startupSub1 + // + // + _updateFlags = 0; + //_unkCharacterId = 0; + _flashShapeTimer = 0; + _drawSceneTimer = _system->getMillis(); + //_unkBBBBBBBBBBBBBBBB = 1; + gui_setPlayFieldButtons(); + // + + _screen->_curPage = 0; + gui_drawPlayField(0); + + _screen->setFont(Screen::FID_6_FNT); + + _screen->_curPage = 0; + gui_drawAllCharPortraitsWithStats(); + + drawScene(1); + + _screen->setScreenDim(7); + + //_runLoopUnk2 = _currentBlock; + _runFlag = true; + + while (!shouldQuit() && _runFlag) { + //_runLoopUnk2 = _currentBlock; + updateCharacterEvents(true); + checkInput(_activeButtons, true); + removeInputTop(); + + if (_updateHandItemCursor) { + _updateHandItemCursor = false; + setHandItem(_itemInHand); + } + + _timer->update(); + updateScriptTimers(); + + if (_sceneUpdateRequired) + drawScene(1); + + if (_envAudioTimer >= _system->getMillis()) + continue; + + _envAudioTimer = _system->getMillis() + (rollDice(1, 10, 3) * 18 * _tickLength); + snd_processEnvironmentalSoundEffect(rollDice(1, 2, -1) ? 27 : 28, _currentBlock + rollDice(1, 12, -1)); + updateEnvironmentalSfx(0); + } +} + +bool EobCoreEngine::updateCharacterEvents(bool a) { + int numChars = 0; + for (int i = 0; i < 6; i++) + numChars += testCharacter(i, 13); + + if (numChars) + return false; + + if (!a) + return true; + + + gui_drawAllCharPortraitsWithStats(); + + /// TODO + /// if (checkScriptFlag(0x10)) + /// j_dranThoseFools() + + /// TODO + + /// TODO + + + return false; +} + +void EobCoreEngine::loadItemsAndDecorationsShapes() { + releaseItemsAndDecorationsShapes(); + _screen->setCurPage(2); + + _screen->loadBitmap("ITEML1.CPS", 5, 3, 0); + _largeItemShapes = new const uint8*[_numLargeItemShapes]; + int div = (_flags.gameID == GI_EOB1) ? 3 : 8; + int mul = (_flags.gameID == GI_EOB1) ? 64 : 24; + + for (int i = 0; i < _numLargeItemShapes; i++) + _largeItemShapes[i] = _screen->encodeShape((i / div) << 3, (i % div) * mul, 8, 24); + + _screen->loadBitmap("ITEMS1.CPS", 5, 3, 0); + _smallItemShapes = new const uint8*[_numSmallItemShapes]; + for (int i = 0; i < _numSmallItemShapes; i++) + _smallItemShapes[i] = _screen->encodeShape((i / div) << 2, (i % div) * mul, 4, 24); + + _screen->loadBitmap("THROWN.CPS", 5, 3, 0); + _thrownItemShapes = new const uint8*[_numThrownItemShapes]; + for (int i = 0; i < _numThrownItemShapes; i++) + _thrownItemShapes[i] = _screen->encodeShape((i / div) << 2, (i % div) * mul, 4, 24); + + _spellShapes = new const uint8*[4]; + for (int i = 0; i < 4; i++) + _spellShapes[i] = _screen->encodeShape(8, i << 5, 6, 32); + + _firebeamShapes = new const uint8*[3]; + _firebeamShapes[0] = _screen->encodeShape(16, 0, 4, 24); + _firebeamShapes[1] = _screen->encodeShape(16, 24, 4, 24); + _firebeamShapes[2] = _screen->encodeShape(16, 48, 3, 24); + _redSplatShape = _screen->encodeShape(16, _flags.gameID == GI_EOB1 ? 144 : 72, 5, 24); + _greenSplatShape = _screen->encodeShape(16, _flags.gameID == GI_EOB1 ? 168 : 96, 5, 16); + + _screen->loadBitmap("ITEMICN.CPS", 5, 3, 0); + _itemIconShapes = new const uint8*[_numItemIconShapes]; + for (int i = 0; i < _numItemIconShapes; i++) + _itemIconShapes[i] = _screen->encodeShape((i % 0x14) << 1, (i / 0x14) << 4, 2, 0x10); + _tempIconShape = new uint8[300]; + + _screen->loadBitmap("DECORATE.CPS", 5, 3, 0); + + if (_flags.gameID == GI_EOB2) { + _lightningColumnShape = _screen->encodeShape(18, 88, 4, 64); + _wallOfForceShapes = new const uint8*[6]; + for (int i = 0; i < 6; i++) + _wallOfForceShapes[i] = _screen->encodeShape(_wallOfForceShapeDefs[(i << 2)], _wallOfForceShapeDefs[(i << 2) + 1], _wallOfForceShapeDefs[(i << 2) + 2], _wallOfForceShapeDefs[(i << 2) + 3]); + } + + _teleporterShapes = new const uint8*[6]; + for (int i = 0; i < 6; i++) + _teleporterShapes[i] = _screen->encodeShape(_teleporterShapeDefs[(i << 2)], _teleporterShapeDefs[(i << 2) + 1], _teleporterShapeDefs[(i << 2) + 2], _teleporterShapeDefs[(i << 2) + 3]); + _sparkShapes = new const uint8*[3]; + _sparkShapes[0] = _screen->encodeShape(29, 0, 2, 16); + _sparkShapes[1] = _screen->encodeShape(31, 0, 2, 16); + _sparkShapes[2] = _screen->encodeShape(33, 0, 2, 16); + _deadCharShape = _screen->encodeShape(0, 88, 4, 32); + _disabledCharGrid = _screen->encodeShape(4, 88, 4, 32); + _blackBoxSmallGrid = _screen->encodeShape(9, 88, 2, 8); + _weaponSlotGrid = _screen->encodeShape(8, 88, 4, 16); + _blackBoxWideGrid = _screen->encodeShape(8, 104, 4, 8); + + static const uint8 dHeight[] = { 17, 10, 10 }; + static const uint8 dY[] = { 120, 137, 147 }; + + _compassShapes = new const uint8*[12]; + for (int y = 0; y < 3; y++) { + for (int x = 0; x < 4; x++) + _compassShapes[(y << 2) + x] = _screen->encodeShape(x * 3, dY[y], 3, dHeight[y]); + } +} + +void EobCoreEngine::releaseItemsAndDecorationsShapes() { + if (_largeItemShapes) { + for (int i = 0; i < _numLargeItemShapes; i++) { + if (_largeItemShapes[i]) + delete[] _largeItemShapes[i]; + } + delete[] _largeItemShapes; + } + + if (_smallItemShapes) { + for (int i = 0; i < _numSmallItemShapes; i++) { + if (_smallItemShapes[i]) + delete[] _smallItemShapes[i]; + } + delete[] _smallItemShapes; + } + + if (_thrownItemShapes) { + for (int i = 0; i < _numThrownItemShapes; i++) { + if (_thrownItemShapes[i]) + delete[] _thrownItemShapes[i]; + } + delete[] _thrownItemShapes; + } + + if (_spellShapes) { + for (int i = 0; i < 4; i++) { + if (_spellShapes[i]) + delete [] _spellShapes[i]; + } + delete[] _spellShapes; + } + + if (_itemIconShapes) { + for (int i = 0; i < _numItemIconShapes; i++) { + if (_itemIconShapes[i]) + delete[] _itemIconShapes[i]; + } + delete[] _itemIconShapes; + } + delete[] _tempIconShape; + + if (_sparkShapes) { + for (int i = 0; i < 3; i++) { + if (_sparkShapes[i]) + delete[] _sparkShapes[i]; + } + delete[] _sparkShapes; + } + + if (_wallOfForceShapes) { + for (int i = 0; i < 6; i++) { + if (_wallOfForceShapes[i]) + delete[] _wallOfForceShapes[i]; + } + delete[] _wallOfForceShapes; + } + + if (_teleporterShapes) { + for (int i = 0; i < 6; i++) { + if (_teleporterShapes[i]) + delete[] _teleporterShapes[i]; + } + delete[] _teleporterShapes; + } + + if (_compassShapes) { + for (int i = 0; i < 12; i++) { + if (_compassShapes[i]) + delete[] _compassShapes[i]; + } + delete[] _compassShapes; + } + + if (_firebeamShapes) { + for (int i = 0; i < 3; i++) { + if (_firebeamShapes[i]) + delete[] _firebeamShapes[i]; + } + delete []_firebeamShapes; + } + + delete[] _deadCharShape; + delete[] _disabledCharGrid; + delete[] _blackBoxSmallGrid; + delete[] _weaponSlotGrid; + delete[] _blackBoxWideGrid; + delete[] _lightningColumnShape; +} + +void EobCoreEngine::setHandItem(Item itemIndex) { + if (itemIndex == -1) + return; + + if (_screen->curDimIndex() == 7 && itemIndex) { + printFullItemName(itemIndex); + _txt->printMessage(_takenStrings[0]); + } + + _itemInHand = itemIndex; + int icon = _items[_itemInHand].icon; + const uint8 *shp = _itemIconShapes[icon]; + + if (icon && (_items[_itemInHand].flags & 0x80) && ((_flags.gameID == GI_EOB2 && (_partyEffectFlags & 2)) || (_flags.gameID == GI_EOB1 && (_partyEffectFlags & 0x10000)))) { + memcpy(_tempIconShape, shp, 300); + if (_flags.gameID == GI_EOB1) + _screen->replaceShapePalette(_tempIconShape, &_itemsOverlay[icon << 4]); + else + _screen->applyShapeOverlay(_tempIconShape, 3); + shp = _tempIconShape; + } + + int mouseOffs = itemIndex ? 8 : 0; + _screen->setMouseCursor(mouseOffs, mouseOffs, shp); +} + +int EobCoreEngine::getDexterityArmorClassModifier(int dexterity) { + static const int mod[] = { 5, 5, 5, 4, 3, 2, 1, 0, 0, + 0, 0, 0, 0, 0, 0, -1, -2, -3, -4, -4, -5, -5, -5, -6, -6 }; + return mod[dexterity]; +} + +int EobCoreEngine::generateCharacterHitpointsByLevel(int charIndex, int levelIndex) { + EobCharacter *c = &_characters[charIndex]; + int m = getClassAndConstHitpointsModifier(c->cClass, c->constitutionCur); + + int h = 0; + + for (int i = 0; i < 3; i++) { + if (!(levelIndex & (1 << i))) + continue; + + int d = getClassHpIncreaseType(c->cClass, i); + + if (c->level[i] <= _hpIncrPerLevel[6 + i]) + h += rollDice(1, (d >= 0) ? _hpIncrPerLevel[d] : 0); + else + h += _hpIncrPerLevel[12 + i]; + + h += m; + } + + h /= _numLevelsPerClass[c->cClass]; + + if (h < 1) + h = 1; + + return h; +} + +int EobCoreEngine::getClassAndConstHitpointsModifier(int cclass, int constitution) { + int res = _hpConstModifiers[constitution]; + + if (res <= 2 || (_classModifierFlags[cclass] & 0x31)) + return res; + + return 2; +} + +int EobCoreEngine::getClassHpIncreaseType(int cclass, int levelIndex) { + return _classHpIncreaseType[cclass * 3 + levelIndex]; +} + +int EobCoreEngine::getModifiedHpLimits(int hpModifier, int constModifier, int level, bool mode) { + int s = _hpIncrPerLevel[6 + hpModifier] > level ? level : _hpIncrPerLevel[6 + hpModifier]; + int res = s; + + if (!mode) + res *= (hpModifier >= 0 ? _hpIncrPerLevel[hpModifier] : 0); + + if (level > s) { + s = level - s; + res += (s * _hpIncrPerLevel[12 + hpModifier]); + } + + if (!mode || (constModifier > 0)) + res += (level * constModifier); + + return res; +} + +const char *EobCoreEngine::getCharStrength(int str, int strExt) { + if (strExt) { + if (strExt == 100) + strExt = 0; + snprintf(_strenghtStr, 6, "%d/%02d", str, strExt); + } else { + snprintf(_strenghtStr, 6, "%d", str); + } + + return _strenghtStr; +} + +int EobCoreEngine::testCharacter(int index, int flags) { + if (index == -1) + return 0; + + EobCharacter *c = &_characters[index]; + int res = 1; + + if (flags & 1) + res &= (c->flags & 1); + + if (flags & 2) + res &= ((c->hitPointsCur <= -10) || (c->flags & 8)) ? 0 : 1; + + if (flags & 4) + res &= ((c->hitPointsCur <= 0) || (c->flags & 8)) ? 0 : 1; + + if (flags & 8) + res &= (c->flags & 12) ? 0 : 1; + + if (flags & 0x20) + res &= (c->flags & 4) ? 0 : 1; + + if (flags & 0x10) + res &= (c->flags & 2) ? 0 : 1; + + if (flags & 0x40) + res &= (c->food <= 0) ? 0 : 1; + + return res; +} + +int EobCoreEngine::getNextValidCharIndex(int curCharIndex, int searchStep) { + do { + curCharIndex += searchStep; + if (curCharIndex < 0) + curCharIndex = 5; + if (curCharIndex > 5) + curCharIndex = 0; + } while (!testCharacter(curCharIndex, 1)); + + return curCharIndex; +} + +void EobCoreEngine::recalcArmorClass(int index) { + EobCharacter *c = &_characters[index]; + int acm = getDexterityArmorClassModifier(c->dexterityCur); + c->armorClass = 10 + acm; + + static uint8 slot[] = { 17, 0, 1, 18 }; + for (int i = 0; i < 4; i++) { + int itm = c->inventory[slot[i]]; + if (!itm) + continue; + + if (i == 2) { + if (!validateWeaponSlotItem(index, 1)) + continue; + } + + int tp = _items[itm].type; + + if (!(_itemTypes[tp].allowedClasses & _classModifierFlags[c->cClass]) || (_itemTypes[tp].extraProperties & 0x7f) || (i >= 1 && i <= 2 && tp != 27 && !(_flags.gameID == GI_EOB2 && tp == 57))) + continue; + + c->armorClass += _itemTypes[tp].armorClass; + c->armorClass -= _items[itm].value; + } + + if (!_items[c->inventory[17]].value) { + int8 m1 = 0; + int8 m2 = 0; + + if (c->inventory[25]) { + if (!(_itemTypes[_items[c->inventory[25]].type].extraProperties & 0x7f)) + m1 = _items[c->inventory[25]].value; + } + + if (c->inventory[26]) { + if (!(_itemTypes[_items[c->inventory[26]].type].extraProperties & 0x7f)) + m2 = _items[c->inventory[26]].value; + } + + c->armorClass -= MAX(m1, m2); + } + + if (c->effectsRemainder[0] > 0) { + if (c->armorClass <= (acm + 6)) + c->effectsRemainder[0] = 0; + else + c->armorClass = (acm + 6); + } + + // shield + if ((c->effectFlags & 8) && (c->armorClass > 4)) + c->armorClass = 4; + + // magical vestment + if (c->effectFlags & 0x4000) { + int8 m1 = 5; + + if (getCharacterClericPaladinLevel(index) > 5) + m1 += ((getCharacterClericPaladinLevel(index) - 5) / 3); + + if (c->armorClass > m1) + c->armorClass = m1; + } + + if (c->armorClass < -10) + c->armorClass = -10; +} + +int EobCoreEngine::validateWeaponSlotItem(int index, int slot) { + EobCharacter *c = &_characters[index]; + int itm1 = c->inventory[0]; + int r = itemUsableByCharacter(index, itm1); + int tp1 = _items[itm1].type; + + if (!slot) + return (!itm1 || r) ? 1 : 0; + + int itm2 = c->inventory[1]; + r = itemUsableByCharacter(index, itm2); + int tp2 = _items[itm2].type; + + if (itm1 && _itemTypes[tp1].requiredHands == 2) + return 0; + + if (!itm2) + return 1; + + int f = (_itemTypes[tp2].extraProperties & 0x7f); + if (f <= 0 || f > 3) + return r; + + if (_itemTypes[tp2].requiredHands) + return 0; + + return r; +} + +int EobCoreEngine::getCharacterClericPaladinLevel(int index) { + if (_castScrollSlot) + return 9; + + if (index == -1) + return (_currentLevel < 7) ? 5 : 9; + + int l = getLevelIndexForHpIncType(2, _characters[index].cClass); + if (l > -1) + return _characters[index].level[l]; + + l = getLevelIndexForHpIncType(4, _characters[index].cClass); + if (l > -1) { + if (_characters[index].level[l] > 8) + return _characters[index].level[l] - 8; + } + + return 1; +} + +int EobCoreEngine::getCharacterMageLevel(int index) { + if (_castScrollSlot) + return 9; + + if (index == -1) + return (_currentLevel < 7) ? 5 : 9; + + int l = getLevelIndexForHpIncType(1, _characters[index].cClass); + return (l > -1) ? _characters[index].level[l] : 1; +} + +int EobCoreEngine::getLevelIndexForHpIncType(int hpIncType, int cClass) { + if (getClassHpIncreaseType(cClass, 0) == hpIncType) + return 0; + + if (getClassHpIncreaseType(cClass, 1) == hpIncType) + return 1; + + if (getClassHpIncreaseType(cClass, 2) == hpIncType) + return 2; + + return -1; +} + +int EobCoreEngine::countCharactersWithSpecificItems(int16 itemType, int16 itemValue) { + int res = 0; + for (int i = 0; i < 6; i++) { + if (!testCharacter(i, 1)) + continue; + if (checkCharacterInventoryForItem(i, itemType, itemValue) != -1) + res++; + } + return res; +} + +int EobCoreEngine::checkCharacterInventoryForItem(int character, int16 itemType, int16 itemValue) { + for (int i = 0; i < 27; i++) { + uint16 inv = _characters[character].inventory[i]; + if (!inv) + continue; + if (_items[inv].type != itemType && itemType != -1) + continue; + if (_items[inv].value == itemValue || itemValue == -1) + return i; + } + return -1; +} + +void EobCoreEngine::modifyCharacterHitpoints(int character, int16 points) { + if (!testCharacter(character, 3)) + return; + + EobCharacter *c = &_characters[character]; + c->hitPointsCur += points; + if (c->hitPointsCur > c->hitPointsMax) + c->hitPointsCur = c->hitPointsMax; + + gui_drawHitpoints(character); + gui_drawCharPortraitWithStats(character); +} + +void EobCoreEngine::neutralizePoison(int character) { + _characters[character].flags &= ~2; + _characters[character].effectFlags &= ~0x2000; + deleteCharEventTimer(character, -34); + gui_drawCharPortraitWithStats(character); +} + +void EobCoreEngine::initNpc(int npcIndex) { + EobCharacter *c = _characters; + int i = 0; + for (; i < 6; i++) { + if (!(_characters[i].flags & 1)) { + c = &_characters[i]; + break; + } + } + + delete[] c->faceShape; + memcpy(c, &_npcPreset[npcIndex], sizeof(EobCharacter)); + recalcArmorClass(i); + + for (i = 0; i < 25; i++) { + if (!c->inventory[i]) + continue; + c->inventory[i] = duplicateItem(c->inventory[i]); + } + + _screen->loadEobBitmap(_flags.gameID == GI_EOB2 ? "OUTPORTS" : "OUTTAKE", 3, 3); + _screen->_curPage = 2; + c->faceShape = _screen->encodeShape(npcIndex << 2, _flags.gameID == GI_EOB2 ? 0 : 160, 4, 32, true); + _screen->_curPage = 0; +} + +int EobCoreEngine::npcJoinDialogue(int npcIndex, int queryJoinTextId, int confirmJoinTextId, int noJoinTextId) { + gui_drawDialogueBox(); + _txt->printDialogueText(queryJoinTextId, 0); + + int r = runDialogue(-1, 0, _yesNoStrings[0], _yesNoStrings[1]) - 1; + if (r == 0) { + if (confirmJoinTextId == -1) { + char tmp[35]; + snprintf(tmp, 35, _npcJoinStrings[0], _npcPreset[npcIndex].name); + _txt->printDialogueText(tmp, true); + } else { + _txt->printDialogueText(confirmJoinTextId, _okStrings[0]); + } + + if (prepareForNewPartyMember(33, npcIndex + 1)) + initNpc(npcIndex); + + } else if (r == 1) { + _txt->printDialogueText(noJoinTextId, _okStrings[0]); + } + + return r ^ 1; +} + +int EobCoreEngine::prepareForNewPartyMember(int16 itemType, int16 itemValue) { + int numChars = 0; + for (int i = 0; i < 6; i++) + numChars += (_characters[i].flags & 1); + + if (numChars < 6) { + deletePartyItem(itemType, itemValue); + } else { + gui_drawDialogueBox(); + _txt->printDialogueText(_npcMaxStrings[0]); + int r = runDialogue(-1, 1, _characters[0].name, _characters[1].name, _characters[2].name, _characters[3].name, + _characters[4].name, _characters[5].name, _abortStrings[0], 0, 0) - 1; + + if (r == 6) + return 0; + + deletePartyItem(itemType, itemValue); + removeCharacterFromParty(r); + } + + return 1; +} + +void EobCoreEngine::removeCharacterFromParty(int charIndex) { + EobCharacter *c = &_characters[charIndex]; + c->flags = 0; + + for (int i = 0; i < 27; i++) { + if (i == 16 || !c->inventory[i]) + continue; + + setItemPosition((Item*)&_levelBlockProperties[_currentBlock & 0x3ff].drawObjects, _currentBlock, c->inventory[i], _dropItemDirIndex[(_currentDirection << 2) + rollDice(1, 2, -1)]); + c->inventory[i] = 0; + } + + while (c->inventory[16]) + setItemPosition((Item*)&_levelBlockProperties[_currentBlock & 0x3ff].drawObjects, _currentBlock, getQueuedItem(&c->inventory[16], 0, -1), _dropItemDirIndex[(_currentDirection << 2) + rollDice(1, 2, -1)]); + + c->inventory[16] = 0; + + if (_updateCharNum == charIndex) + _updateCharNum = 0; + + setupCharacterTimers(); +} + +void EobCoreEngine::increasePartyExperience(int16 points) { + int cnt = 0; + for (int i = 0; i < 6; i++) { + if (testCharacter(i, 3)) + cnt++; + } + + if (cnt <= 0) + return; + + points /= cnt; + + for (int i = 0; i < 6; i++) { + if (!testCharacter(i, 3)) + continue; + increaseCharacterExperience(i, points); + } +} + +void EobCoreEngine::increaseCharacterExperience(int charIndex, int32 points) { + int cl = _characters[charIndex].cClass; + points /= _numLevelsPerClass[cl]; + + for (int i = 0; i < 3; i++) { + if (getClassHpIncreaseType(cl, i) == -1) + continue; + _characters[charIndex].experience[i] += points; + + uint32 er = getRequiredExperience(cl, i, _characters[charIndex].level[i] + 1); + if (er == 0xffffffff) + continue; + + if (_characters[charIndex].experience[i] >= er) + increaseCharacterLevel(charIndex, i); + } +} + +uint32 EobCoreEngine::getRequiredExperience(int cClass, int levelIndex, int level) { + cClass = getClassHpIncreaseType(cClass, levelIndex); + if (cClass == -1) + return 0xffffffff; + + const uint32 *tbl = _expRequirementTables[cClass]; + return tbl[level - 1]; +} + +void EobCoreEngine::increaseCharacterLevel(int charIndex, int levelIndex) { + _characters[charIndex].level[levelIndex]++; + int hpInc = generateCharacterHitpointsByLevel(charIndex, levelIndex); + _characters[charIndex].hitPointsCur += hpInc; + _characters[charIndex].hitPointsMax += hpInc; + + gui_drawCharPortraitWithStats(charIndex); + _txt->printMessage(_levelGainStrings[0], -1, _characters[charIndex].name); + snd_playSoundEffect(23); +} + +void EobCoreEngine::setWeaponSlotStatus(int charIndex, int mode, int slot) { + if (mode == 0 || mode == 2) + _characters[charIndex].disabledSlots ^= (1 << slot); + else if (mode != 1) + return; + + _characters[charIndex].slotStatus[slot] = 0; + gui_drawCharPortraitWithStats(charIndex); +} + +void EobCoreEngine::setupDialogueButtons(int presetfirst, int numStr, const char *str1, ...) { + _dialogueNumButtons = numStr; + _dialogueButtonString[0] = str1; + _dialogueHighlightedButton = 0; + + va_list args; + va_start(args, str1); + const char **s5p = va_arg(args, const char**); + va_end(args); + for (int i = 1; i < numStr; i++) { + if (s5p[i - 1]) + _dialogueButtonString[i] = s5p[i - 1]; + else + _dialogueNumButtons = numStr = i; + } + + static const uint16 prsX[] = { 59, 166, 4, 112, 220, 4, 112, 220, 4, 112, 220, 4, 112, 220 }; + static const uint8 prsY[] = { 0, 0, 0, 0, 0, 12, 12, 12, 24, 24, 24, 36, 36, 36 }; + + const ScreenDim *dm = screen()->_curDim; + int yOffs = (_txt->lineCount() + 1) * _screen->getFontHeight() + dm->sy + 4; + + _dialogueButtonPosX = &prsX[presetfirst]; + _dialogueButtonPosY = &prsY[presetfirst]; + _dialogueButtonYoffs = yOffs; + + drawDialogueButtons(); + + if (!shouldQuit()) + removeInputTop(); +} + +void EobCoreEngine::initDialogueSequence() { + _dlgUnk1 = -1; + _txt->setWaitButtonMode(0); + _dialogueField = true; + + _dialogueLastBitmap[0] = 0; + + _txt->resetPageBreakString(); + gui_updateControls(); + + _sound->playTrack(0); + Common::SeekableReadStream *s = _res->createReadStream("TEXT.DAT"); + _screen->loadFileDataToPage(s, 5, 32000); + _txt->setupField(9, 0); + delete s; +} + +void EobCoreEngine::restoreAfterDialogueSequence() { + _txt->allowPageBreak(false); + _dialogueField = false; + + _dialogueLastBitmap[0] = 0; + + gui_restorePlayField(); + _screen->setScreenDim(7); + + if (_flags.gameID == GI_EOB2) + _sound->playTrack(2); + + _sceneUpdateRequired = true; +} + +void EobCoreEngine::drawSequenceBitmap(const char *file, int destRect, int x1, int y1, int flags) { + static const uint8 frameX[] = { 1, 0 }; + static const uint8 frameY[] = { 8, 0 }; + static const uint8 frameW[] = { 20, 40 }; + static const uint8 frameH[] = { 96, 121 }; + + int page = ((flags & 2) || destRect) ? 0 : 6; + + if (scumm_stricmp(_dialogueLastBitmap, file)) { + if (!destRect) { + if (!(flags & 1)) { + _screen->loadEobCpsFileToPage("BORDER", 0, 3, 3, 2); + _screen->copyRegion(0, 0, 0, 0, 184, 121, 2, page, Screen::CR_NO_P_CHECK); + } else { + _screen->copyRegion(0, 0, 0, 0, 184, 121, 0, page, Screen::CR_NO_P_CHECK); + } + + if (!page) + _screen->copyRegion(0, 0, 0, 0, 184, 121, 2, 6, Screen::CR_NO_P_CHECK); + } + + _screen->loadEobCpsFileToPage(file, 0, 3, 3, 2); + strcpy(_dialogueLastBitmap, file); + } + + if (flags & 2) + _screen->crossFadeRegion(x1 << 3, y1, frameX[destRect] << 3, frameY[destRect], frameW[destRect] << 3, frameH[destRect], 2, page); + else + _screen->copyRegion(x1 << 3, y1, frameX[destRect] << 3, frameY[destRect], frameW[destRect] << 3, frameH[destRect], 2, page, Screen::CR_NO_P_CHECK); + + if (page == 6) + _screen->copyRegion(0, 0, 0, 0, 184, 121, 6, 0, Screen::CR_NO_P_CHECK); + + _screen->updateScreen(); +} + +int EobCoreEngine::runDialogue(int dialogueTextId, int style, const char *button1, ...) { + if (dialogueTextId != -1) + txt()->printDialogueText(dialogueTextId, 0); + + va_list args; + va_start(args, button1); + if (style) + setupDialogueButtons(2, 9, button1, args); + else + setupDialogueButtons(0, 2, button1, args); + va_end(args); + + int res = 0; + while (res == 0 && !shouldQuit()) + res = processDialogue(); + + gui_drawDialogueBox(); + + return res; +} + +void EobCoreEngine::delay(uint32 millis, bool, bool) { + while (millis && !shouldQuit() && !skipFlag()) { + updateInput(); + uint32 step = MIN<uint32>(millis, (_tickLength / 5)); + _system->delayMillis(step); + millis -= step; + } +} + +void EobCoreEngine::displayParchment(int id) { + _txt->setWaitButtonMode(1); + _txt->resetPageBreakString(); + gui_updateControls(); + + if (id >= 0) { + // display text + Common::SeekableReadStream *s = _res->createReadStream("TEXT.DAT"); + _screen->loadFileDataToPage(s, 5, 32000); + gui_drawBox(0, 0, 176, 175, _color1_1, _color2_1, _bkgColor_1); + _txt->setupField(12, 1); + if (_flags.gameID == GI_EOB2) + id++; + _txt->printDialogueText(id, _okStrings[0]); + + } else { + // display bitmap + id = -id - 1; + static const uint8 x[] = { 0, 20, 0 }; + static const uint8 y[] = { 0, 0, 96 }; + drawSequenceBitmap("MAP", 0, x[id], y[id], 0); + + removeInputTop(); + while (!shouldQuit()) { + delay(_tickLength); + if (checkInput(0) & 0xff) + break; + removeInputTop(); + } + removeInputTop(); + } + + restoreAfterDialogueSequence(); +} + +void EobCoreEngine::useSlotWeapon(int charIndex, int slotIndex, int item) { + EobCharacter *c = &_characters[charIndex]; + int tp = item ? _items[item].type : 0; + + if (c->effectFlags & 0x40) + removeCharacterEffect(10, charIndex, 1); // remove invisibility effect + + int ep = _itemTypes[tp].extraProperties & 0x7f; + int8 inflict = 0; + + if (ep == 1) { + inflict = closeDistanceAttack(charIndex, item); + if (!inflict) + inflict = -1; + snd_playSoundEffect(32); + } else if (ep == 2) { + inflict = thrownAttack(charIndex, slotIndex, item); + } else if (ep == 3) { + inflict = bowAttack(charIndex, item); + gui_drawCharPortraitWithStats(charIndex); + } + + if (inflict > 0) { + if (_items[item].flags & 8) { + c->hitPointsCur += inflict; + gui_drawCharPortraitWithStats(charIndex); + } + + if (_items[item].flags & 0x10) + c->inventory[slotIndex] = 0; + + inflictMonsterDamage(&_monsters[_dstMonsterIndex], inflict, true); + } + + c->disabledSlots ^= (1 << slotIndex); + c->slotStatus[slotIndex] = inflict; + + gui_drawCharPortraitWithStats(charIndex); + setCharEventTimer(charIndex, 18, inflict >= -2 ? slotIndex + 2 : slotIndex, 1); +} + +int EobCoreEngine::closeDistanceAttack(int charIndex, int item) { + if (charIndex > 1) + return -3; + + uint16 d = calcNewBlockPosition(_currentBlock, _currentDirection); + int r = getClosestMonsterPos(charIndex, d); + + if (r == -1) { + uint8 w = _specialWallTypes[_levelBlockProperties[d].walls[_sceneDrawVarDown]]; + if (w == 0xff) { + if (_flags.gameID == GI_EOB1) { + _levelBlockProperties[d].walls[_sceneDrawVarDown]++; + _levelBlockProperties[d].walls[_sceneDrawVarDown ^ 2]++; + + } else { + for (int i = 0; i < 4; i++) { + if (_specialWallTypes[_levelBlockProperties[d].walls[i]] == 0xff) + _levelBlockProperties[d].walls[i]++; + } + } + _sceneUpdateRequired = true; + + } else if ((_flags.gameID == GI_EOB1) || (_flags.gameID == GI_EOB2 && w != 8 && w != 9)) { + return -1; + } + + return (_flags.gameID == GI_EOB2 && ((_itemTypes[_items[item].type].allowedClasses & 4) || !item)) ? -5 : -2; + + } else { + if (_monsters[r].flags & 0x20) { + killMonster(&_monsters[r], 1); + _txt->printMessage(_monsterDustStrings[0]); + return -2; + } + + if (!characterAttackHitTest(charIndex, r, item, 1)) + return -1; + + uint16 flg = 0x100; + + if ((_flags.gameID == GI_EOB1 && _items[item].type > 51 && _items[item].type < 57) || (_flags.gameID == GI_EOB2 && isMagicWeapon(item))) + flg |= 1; + + _dstMonsterIndex = r; + return calcCloseDistanceMonsterDamage(&_monsters[r], charIndex, item, 1, flg, 5, 3); + } + + return 0; +} + +int EobCoreEngine::thrownAttack(int charIndex, int slotIndex, int item) { + int d = charIndex > 3 ? charIndex - 2 : charIndex; + if (!launchObject(charIndex, item, _currentBlock, _dropItemDirIndex[(_currentDirection << 2) + d], _currentDirection, _items[item].type)) + return 0; + + snd_playSoundEffect(11); + _characters[charIndex].inventory[slotIndex] = 0; + reloadWeaponSlot(charIndex, slotIndex, -1, 0); + _sceneUpdateRequired = true; + return 0; +} + +int EobCoreEngine::bowAttack(int charIndex, int item) { + return 0; +} + +void EobCoreEngine::inflictMonsterDamage(EobMonsterInPlay *m, int damage, bool giveExperience) { + m->hitPointsCur -= damage; + m->flags = (m->flags & 0xf7) | 1; + + if (_monsterProps[m->type].flags & 0x2000) { + inflictMonsterDamage_s1(m); + checkSceneUpdateNeed(m->block); + m->hitPointsCur = 0; + } else { + if (checkSceneUpdateNeed(m->block)) { + m->flags |= 2; + if (_inflictMonsterDamageUnk) + return; + flashMonsterShape(m); + } + } + + if (m->hitPointsCur <= 0) + killMonster(m, giveExperience); + else if (getBlockDistance(m->block, _currentBlock) < 4) + m->dest = _currentBlock; +} + +void EobCoreEngine::calcAndInflictMonsterDamage(EobMonsterInPlay *m, int times, int pips, int offs, int flags, int b, int damageType) { + int dmg = calcCloseDistanceMonsterDamage(m, times, pips, offs, flags, b, damageType); + if (dmg > 0) + inflictMonsterDamage(m, dmg, flags & 0x800 ? true : false); +} + +void EobCoreEngine::calcAndInflictCharacterDamage(int charIndex, int times, int itemOrPips, int useStrModifierOrBase, int flg, int a, int damageType) { + int dmg = calcCharacterDamage(charIndex, times, itemOrPips, useStrModifierOrBase, flg, a, damageType); + if (dmg) + inflictCharacterDamage(charIndex, dmg); +} + +int EobCoreEngine::calcCharacterDamage(int charIndex, int times, int itemOrPips, int useStrModifierOrBase, int flg, int a, int damageType) { + int s = (flg & 0x100) ? calcDamageModifers(times, 0, itemOrPips, _items[itemOrPips].type, useStrModifierOrBase) : rollDice(times, itemOrPips, useStrModifierOrBase); + EobCharacter *c = &_characters[charIndex]; + + if (a != 5) { + if (checkUnkConstModifiers(c, _charClassModUnk[c->cClass], c->level[0], a, c->raceSex)) + s = recalcDamageModifier(damageType, s); + } + + if ((flg & 0x110) == 0x110) { + if (!calcDamageCheckItemType(_items[itemOrPips].type)) + s = 1; + } + + if (flg & 4) { + if (checkInventoryForRings(charIndex, 3)) + s = 0; + } + + if (flg & 0x400) { + if (c->effectFlags & 0x2000) + s = 0; + else + _txt->printMessage(_characterStatusStrings8[0], -1, c->name); + } + + return s; +} + +void EobCoreEngine::inflictCharacterDamage(int charIndex, int damage) { + EobCharacter *c = &_characters[charIndex]; + if (!testCharacter(charIndex, 3)) + return; + + if (c->effectsRemainder[3]) + c->effectsRemainder[3] = (damage < c->effectsRemainder[3]) ? (c->effectsRemainder[3] - damage) : 0; + + c->hitPointsCur -= damage; + c->damageTaken = damage; + + if (c->hitPointsCur > -10) { + snd_playSoundEffect(21); + } else { + c->hitPointsCur = -10; + c->flags &= 1; + c->food = 0; + removeAllCharacterEffects(charIndex); + snd_playSoundEffect(22); + } + + if (c->effectsRemainder[0]) { + c->effectsRemainder[0] = (damage < c->effectsRemainder[0]) ? (c->effectsRemainder[0] - damage) : 0; + if (!c->effectsRemainder[0]) + removeCharacterEffect(1, charIndex, 1); + } + + if (_currentControlMode) + gui_drawFaceShape(charIndex); + else + gui_drawCharPortraitWithStats(charIndex); + + if (c->hitPointsCur <= 0 && _updateFlags == 1 && charIndex == _openBookChar) { + Button b; + clickedSpellbookAbort(&b); + } + + setCharEventTimer(charIndex, 18, 6, 1); +} + +bool EobCoreEngine::characterAttackHitTest(int charIndex, int monsterIndex, int item, int attackType) { + if (charIndex < 0) + return true; + + int p = item ? (_flags.gameID == GI_EOB1 ? _items[item].type : (_itemTypes[_items[item].type].extraProperties & 0x7f)) : 0; + + if (_monsters[monsterIndex].flags & 0x20) + return true;// EOB 2 only ? + + int t = _monsters[monsterIndex].type; + int d = (p < 1 || p > 3) ? 0 : _items[item].value; + + if (_flags.gameID == GI_EOB2) { + if ((p > 0 && p < 4) || !item ){ + if (((_monsterProps[t].statusFlags & 0x200) && (d <= 0)) || ((_monsterProps[t].statusFlags & 0x1000) && (d <= 1))) + return false; + } + } + + d += (attackType ? getStrHitChanceModifier(charIndex) : getDexHitChanceModifier(charIndex)); + + int m = getMonsterAcHitChanceModifier(charIndex, _monsterProps[t].armorClass) - d; + int s = rollDice(1, 20); + + _monsters[monsterIndex].flags |= 1; + + if (_flags.gameID == GI_EOB1) { + if (_partyEffectFlags & 0x30) + s++; + if (_characters[charIndex].effectFlags & 0x40) + s++; + } else if ((_partyEffectFlags & 0x8400) || (_characters[charIndex].effectFlags & 0x1000)) { + s++; + } + + s = CLIP(s, 1, 20); + + return s < m ? false : true; +} + +bool EobCoreEngine::monsterAttackHitTest(EobMonsterInPlay *m, int charIndex) { + int tp = m->type; + EobMonsterProperty *p = &_monsterProps[tp]; + + int r = rollDice(1, 20); + if (r != 20) { + if (_characters[charIndex].effectFlags & 0x800) + r -= 2; + if (_characters[charIndex].effectFlags & 0x10) + r -= 2; + if (_partyEffectFlags & 0x8000) + r--; + } + + return ((r == 20) || (r >= (p->hitChance - _characters[charIndex].armorClass))); +} + +bool EobCoreEngine::flyingObjectMonsterHit(EobFlyingObject *fo, int monsterIndex) { + if (fo->attackerId != -1) { + if (!characterAttackHitTest(fo->attackerId, monsterIndex, fo->item, 0)) + return false; + } + calcAndInflictMonsterDamage(&_monsters[monsterIndex], fo->attackerId, fo->item, 0, (fo->attackerId == -1) ? 0x110: 0x910, 5, 3); + return true; +} + +bool EobCoreEngine::flyingObjectPartyHit(EobFlyingObject *fo) { + int ps = _dscItemPosIndex[(_currentDirection << 2) + (_items[fo->item].pos & 3)]; + bool res = false; + + bool b = ((_currentDirection == fo->direction || _currentDirection == (fo->direction ^ 2)) && ps > 2); + int s = ps << 1; + if (ps > 2) + s += rollDice(1, 2, -1); + + static const int8 charId[] = { 0, -1, 1, -1, 2, 4, 3, 5 }; + + for (int i = 0; i < 2; i++) { + int c = charId[s]; + s ^= 1; + if (!testCharacter(c, 3)) + continue; + calcAndInflictCharacterDamage(c, -1, fo->item, 0, 0x110, 5, 3); + res = true; + if (ps < 2 || b == 0) + break; + } + + return res; +} + +void EobCoreEngine::monsterCloseAttack(EobMonsterInPlay *m) { + int first = _monsterCloseAttDstTable1[(_currentDirection << 2) + m->dir] * 12; + int v = (m->pos == 4) ? rollDice(1, 2, -1) : _monsterCloseAttChkTable2[(m->dir << 2) + m->pos]; + if (!v) + first += 6; + + int last = first + 6; + for (int i = first; i < last; i++) { + int c = _monsterCloseAttDstTable2[i]; + if (!testCharacter(c, 3)) + continue; + + // Character Invisibility + if ((_characters[c].effectFlags & 0x140) && (rollDice(1, 20) >= 5)) + continue; + + int dmg = 0; + for (int ii = 0; ii < _monsterProps[m->type].attacksPerRound; ii++) { + if (!monsterAttackHitTest(m, c)) + continue; + dmg += rollDice(_monsterProps[m->type].dmgDc[ii].times, _monsterProps[m->type].dmgDc[ii].pips, _monsterProps[m->type].dmgDc[ii].base); + } + + if (dmg > 0) { + if ((_monsterProps[m->type].flags & 0x80) && rollDice(1, 4, -1) != 3) { + int slot = rollDice(1, 27, -1); + for (int iii = 0; iii < 27; iii++) { + Item itm = _characters[c].inventory[slot]; + if (!itm || !(_itemTypes[_items[itm].type].extraProperties & 0x80)) { + if (++slot == 27) + slot = 0; + continue; + } + + _characters[c].inventory[slot] = 0; + _txt->printMessage(_itemExtraStrings[(_characters[c].raceSex & 1) ^ 1], -1, _characters[c].name); + printFullItemName(itm); + _txt->printMessage(_itemExtraStrings[2]); + } + gui_drawCharPortraitWithStats(c); + } + + inflictCharacterDamage(c, dmg); + + if (_monsterProps[m->type].flags & 0x10) { + statusAttack(c, 2, _monsterSpecAttStrings[_flags.gameID == GI_EOB1 ? 3 : 2], 0, 1, 8, 1); + _characters[c].effectFlags &= ~0x2000; + } + + if (_monsterProps[m->type].flags & 0x20) + statusAttack(c, 4, _monsterSpecAttStrings[_flags.gameID == GI_EOB1 ? 4 : 3], 2, 5, 9, 1); + + if (_monsterProps[m->type].flags & 0x8000) + statusAttack(c, 8, _monsterSpecAttStrings[4], 2, 0, 0, 1); + + } + + if (!(_monsterProps[m->type].flags & 0x4000)) + return; + } +} + +void EobCoreEngine::monsterSpellCast(EobMonsterInPlay *m, int type) { + launchMagicObject(-1, type, m->block, m->pos, m->dir); + snd_processEnvironmentalSoundEffect(_spells[_magicFlightObjectProperties[type << 2]].sound, m->block); +} + +void EobCoreEngine::statusAttack(int charIndex, int attackStatusFlags, const char *attackStatusString, int a, uint32 effectDuration, int restoreEvent, int noRefresh) { + EobCharacter *c = &_characters[charIndex]; + if ((c->flags & attackStatusFlags) && noRefresh) + return; + if (!testCharacter(charIndex, 3)) + return; + + if (a != 5 && specialAttackConstTest(charIndex, a)) + return; + + if (attackStatusFlags & 8) { + removeAllCharacterEffects(charIndex); + c->flags = (c->flags & 1) | 8; + } else { + c->flags |= attackStatusFlags; + } + + if ((attackStatusFlags & 0x0c) && (_openBookChar == charIndex) && _updateFlags) { + Button b; + clickedSpellbookAbort(&b); + } + + if (effectDuration) + setCharEventTimer(charIndex, effectDuration * 546, restoreEvent, 1); + + gui_drawCharPortraitWithStats(charIndex); + _txt->printMessage(_characterStatusStrings13[0], -1, c->name, attackStatusString); +} + +int EobCoreEngine::calcCloseDistanceMonsterDamage(EobMonsterInPlay *m, int times, int pips, int offs, int flags, int b, int damageType) { + int s = flags & 0x100 ? calcDamageModifers(times, m, pips, _items[pips].type, offs) : rollDice(times, pips, offs); + EobMonsterProperty *p = &_monsterProps[m->type]; + + if (b == 5) { + if (checkUnkConstModifiers(m, 0, p->level, b, 6)) + s = recalcDamageModifier(damageType, s); + } + + if ((flags & 0x110) == 0x110) { + if (!calcDamageCheckItemType(_items[pips].type)) + s = 1; + } + + if ((flags & 0x100) && ((_flags.gameID == GI_EOB2 && (p->statusFlags & 0x100)) || (_flags.gameID == GI_EOB1 && (p->flags & 4))) && (!(_itemTypes[_items[pips].type].allowedClasses & 4 /* bug in original code ??*/))) + s >>= 1; + + if (p->statusFlags & 0x2000) { + if (flags & 0x100) { + if (_items[pips].value < 3) + s >>= 2; + if (_items[pips].value == 3) + s >>= 1; + if (s == 0) + s = _items[pips].value; + + } else { + s >>= 1; + } + } + + if (flags & 1) { + if (checkMonsterDamageEvasion(m)) + s = 0; + } + + if (_flags.gameID == GI_EOB1) + return s; + + static const uint16 damageImmunityFlags[] = { 0x01, 0x10, 0x02, 0x20, 0x80, 0x400, 0x20, 0x800, 0x40, 0x80, 0x400, 0x40 }; + for (int i = 0; i < 12; i += 2) { + if ((flags & damageImmunityFlags[i]) && (p->statusFlags & damageImmunityFlags[i + 1])) + s = 0; + } + + return s; +} + +int EobCoreEngine::calcDamageModifers(int charIndex, EobMonsterInPlay *m, int item, int itemType, int useStrModifier) { + int s = (useStrModifier && (charIndex != -1)) ? getStrDamageModifier(charIndex) : 0; + if (item) { + EobItemType *p = &_itemTypes[itemType]; + int t = m ? m->type : 0; + s += ((m && (_monsterProps[t].flags & 1)) ? rollDice(p->dmgNumDiceL, p->dmgNumPipsL, p->dmgIncS /* bug in original code ? */) : + rollDice(p->dmgNumDiceS, p->dmgNumPipsS, p->dmgIncS)); + s += _items[item].value; + } else { + s += rollDice(1, 2); + } + + return (s < 0) ? 0 : s; +} + +bool EobCoreEngine::checkUnkConstModifiers(void *target, int hpModifier, int level, int b, int race) { + static const int8 constMod[] = { 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5 }; + + if (b == 5) + return false; + + int s = getConstModifierTableValue(hpModifier, level, b); + if (((race == 3 || race == 5) && (b == 4 || b == 1 || b == 0)) || (race == 4 && (b == 4 || b == 1))) { + EobCharacter *c = (EobCharacter*)target; + s -= constMod[c->constitutionCur]; + } + + return rollDice(1, 20) < s ? false : true; +} + +bool EobCoreEngine::specialAttackConstTest(int charIndex, int b) { + return checkUnkConstModifiers(&_characters[charIndex], _charClassModUnk[_characters[charIndex].cClass], _characters[charIndex].level[0], b, _characters[charIndex].raceSex >> 1); +} + +int EobCoreEngine::getConstModifierTableValue(int hpModifier, int level, int b) { + const uint8 *tbl = _constModTables[hpModifier]; + if (_constModLevelIndex[hpModifier] < level) + level = _constModLevelIndex[hpModifier]; + level /= _constModDiv[hpModifier]; + level += (_constModExt[hpModifier] * b); + + return tbl[level]; +} + +bool EobCoreEngine::calcDamageCheckItemType(int itemType) { + itemType = _itemTypes[itemType].extraProperties & 0x7f; + return (itemType == 2 || itemType == 3) ? true : false; +} + +int EobCoreEngine::recalcDamageModifier(int damageType, int dmgModifier) { + if (damageType == 3) + return 0; + + if (damageType == 0 || damageType == 1) + return dmgModifier >> 1; + + return dmgModifier; +} + +bool EobCoreEngine::checkMonsterDamageEvasion(EobMonsterInPlay *m) { + return rollDice(1, 100) < _monsterProps[m->type].dmgModifierEvade ? true : false; +} + +int EobCoreEngine::getStrHitChanceModifier(int charIndex) { + static const int8 strExtLimit[] = { 1, 51, 76, 91, 100 }; + static const int8 strExtMod[] = { 1, 2, 2, 2, 3 }; + static const int8 strMod[] = { -4, -3, -3, -2, -2, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 3, 3, 4, 4, 5, 6, 7 }; + + int r = strMod[_characters[charIndex].strengthCur - 1]; + if (_characters[charIndex].strengthExtCur) { + for (int i = 0; i < 5; i++) { + if (_characters[charIndex].strengthExtCur >= strExtLimit[i]) + r = strExtMod[i]; + } + } + + return r; +} + +int EobCoreEngine::getStrDamageModifier(int charIndex) { + static const int8 strExtLimit[] = { 1, 51, 76, 91, 100 }; + static const int8 strExtMod[] = { 3, 3, 4, 5, 6 }; + static const int8 strMod[] = { -3, -2, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 7, 8, 9, 10, 11, 12, 14 }; + + int r = strMod[_characters[charIndex].strengthCur - 1]; + if (_characters[charIndex].strengthExtCur) { + for (int i = 0; i < 5; i++) { + if (_characters[charIndex].strengthExtCur >= strExtLimit[i]) + r = strExtMod[i]; + } + } + + return r; +} + +int EobCoreEngine::getDexHitChanceModifier(int charIndex) { + static const int8 dexMod[] = { -5, -4, -3, -2, -1, 0, 0, 0, 0, 0, 0, 1, 2, 2, 3, 3, 4, 4, 4 }; + return dexMod[_characters[charIndex].dexterityCur - 1]; +} + +int EobCoreEngine::getMonsterAcHitChanceModifier(int charIndex, int monsterAc) { + static const uint8 mod1[] = { 1, 3, 3, 2 }; + static const uint8 mod2[] = { 1, 1, 2, 1 }; + + int l = _characters[charIndex].level[0] - 1; + int cm = _charClassModUnk[_characters[charIndex].cClass]; + + return (20 - ((l / mod1[cm]) * mod2[cm])) - monsterAc; +} + +void EobCoreEngine::inflictMonsterDamage_s1(EobMonsterInPlay *m) { + +} + +void EobCoreEngine::snd_playSoundEffect(int id, int volume) { + if (id < 1 || id > 119 || shouldQuit()) + return; + + _sound->playSoundEffect(id, volume); +} + +} // End of namespace Kyra + +#endif // ENABLE_EOB diff --git a/engines/kyra/eobcommon.h b/engines/kyra/eobcommon.h new file mode 100644 index 0000000000..201c1dfa11 --- /dev/null +++ b/engines/kyra/eobcommon.h @@ -0,0 +1,977 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef KYRA_EOBCOMMON_H +#define KYRA_EOBCOMMON_H + +#if defined(ENABLE_EOB) || defined(ENABLE_LOL) +#include "kyra/loleobbase.h" +#endif // (ENABLE_EOB || ENABLE_LOL) + +#ifdef ENABLE_EOB + +namespace Kyra { + +struct EobShapeDef { + int16 index; + uint8 x, y, w, h; +}; + +struct CreatePartyModButton { + uint8 encodeLabelX; + uint8 encodeLabelY; + uint8 labelW; + uint8 labelH; + uint8 labelX; + uint8 labelY; + uint8 bodyIndex; + uint8 destX; + uint8 destY; +}; + +struct EobRect8 { + uint8 x; + uint8 y; + uint8 w; + uint8 h; +}; + +struct EobRect16 { + int16 x1; + int16 y1; + uint16 x2; + uint16 y2; +}; + +struct EobChargenButtonDef { + uint8 x; + uint8 y; + uint8 w; + uint8 h; + uint8 keyCode; +}; + +struct EobGuiButtonDef { + uint8 keyCode; + uint8 keyCode2; + uint16 flags; + uint16 x; + uint8 y; + uint16 w; + uint8 h; + Button::Callback buttonCallback; + uint16 arg; +}; + +struct EobCharacter { + uint8 id; + uint8 flags; + char name[11]; + int8 strengthCur; + int8 strengthMax; + int8 strengthExtCur; + int8 strengthExtMax; + int8 intelligenceCur; + int8 intelligenceMax; + int8 wisdomCur; + int8 wisdomMax; + int8 dexterityCur; + int8 dexterityMax; + int8 constitutionCur; + int8 constitutionMax; + int8 charismaCur; + int8 charismaMax; + int16 hitPointsCur; + int16 hitPointsMax; + int8 armorClass; + uint8 disabledSlots; + uint8 raceSex; + uint8 cClass; + uint8 alignment; + int8 portrait; + uint8 food; + uint8 level[3]; + uint32 experience[3]; + uint8 *faceShape; + + int8 mageSpells[80]; + int8 clericSpells[80]; + uint32 mageSpellsAvailabilityFlags; + + Item inventory[27]; + uint32 timers[10]; + int8 events[10]; + uint8 effectsRemainder[4]; + uint32 effectFlags; + uint8 damageTaken; + int8 slotStatus[5]; +}; + +struct EobItem { + uint8 nameUnid; + uint8 nameId; + uint8 flags; + int8 icon; + int8 type; + int8 pos; + int16 block; + Item next; + Item prev; + uint8 level; + int8 value; +}; + +struct EobItemType { + uint16 invFlags; + uint16 handFlags; + int8 armorClass; + int8 allowedClasses; + int8 requiredHands; + int8 dmgNumDiceS; + int8 dmgNumPipsS; + int8 dmgIncS; + int8 dmgNumDiceL; + int8 dmgNumPipsL; + int8 dmgIncL; + uint8 unk1; + uint16 extraProperties; +}; + +struct SpriteDecoration { + uint8 *shp; + uint8 x; + uint8 y; +}; + +struct EobMonsterProperty { + int8 armorClass; + int8 hitChance; + uint8 level; + uint8 hpDcTimes; + uint8 hpDcPips; + uint8 hpDcBase; + uint8 attacksPerRound; + struct DmgDc { + uint8 times; + uint8 pips; + int8 base; + } dmgDc[3]; + uint16 statusFlags; + uint16 flags; + int32 u22; + int32 experience; + + uint8 u30; + uint8 sound1; + uint8 sound2; + uint8 numRemoteAttacks; + uint8 remoteWeaponChangeMode; + uint8 numRemoteWeapons; + + int8 remoteWeapons[5]; + + uint8 u41; + uint8 dmgModifierEvade; + + uint8 decorations[3]; +}; + +struct EobMonsterInPlay { + uint8 type; + uint8 unit; + uint16 block; + uint8 pos; + int8 dir; + uint8 animStep; + uint8 shpIndex; + int8 mode; + int8 f_9; + int8 curAttackFrame; + uint8 f_b; + int16 hitPointsMax; + int16 hitPointsCur; + uint16 dest; + uint16 randItem; + uint16 fixedItem; + uint8 flags; + uint8 idleAnimState; + uint8 curRemoteWeapon; + uint8 numRemoteAttacks; + int8 palette; + uint8 directionChanged; + uint8 stepsTillRemoteAttack; + uint8 sub; +}; + +struct ScriptTimer { + uint16 func; + uint16 ticks; + uint32 next; +}; + +struct EobFlyingObject { + uint8 enable; + uint8 objectType; + int16 attackerId; + Item item; + uint16 curBlock; + uint16 u2; + uint8 u1; + uint8 direction; + uint8 distance; + int8 callBackIndex; + uint8 curPos; + uint8 flags; + uint8 unused; +}; + +class EobInfProcessor; + +class EobCoreEngine : public LolEobBaseEngine { +friend class TextDisplayer_Eob; +friend class GUI_Eob; +friend class EobInfProcessor; +friend class DarkmoonSequenceHelper; +friend class CharacterGenerator; +public: + EobCoreEngine(OSystem *system, const GameFlags &flags); + virtual ~EobCoreEngine(); + + Screen *screen() { return _screen; } + GUI *gui() const { return _gui; } + +protected: + // Startup + virtual Common::Error init(); + Common::Error go(); + + // Main Menu, Intro, Finale + virtual int mainMenu() = 0; + virtual void seq_playFinale() = 0; + bool _playFinale; + + //Init + void loadItemsAndDecorationsShapes(); + void releaseItemsAndDecorationsShapes(); + + void initButtonData(); + void initStaticResource(); + virtual void initSpells(); + + const uint8 **_largeItemShapes; + const uint8 **_smallItemShapes; + const uint8 **_thrownItemShapes; + const int _numLargeItemShapes; + const int _numSmallItemShapes; + const int _numThrownItemShapes; + const int _numItemIconShapes; + + const uint8 **_spellShapes; + const uint8 **_firebeamShapes; + const uint8 *_redSplatShape; + const uint8 *_greenSplatShape; + const uint8 **_wallOfForceShapes; + const uint8 **_teleporterShapes; + const uint8 **_sparkShapes; + const uint8 *_deadCharShape; + const uint8 *_disabledCharGrid; + const uint8 *_blackBoxSmallGrid; + const uint8 *_weaponSlotGrid; + const uint8 *_blackBoxWideGrid; + const uint8 *_lightningColumnShape; + + uint8 *_tempIconShape; + uint8 *_itemsOverlay; + + static const uint8 _teleporterShapeDefs[]; + static const uint8 _wallOfForceShapeDefs[]; + + const char *const *_mainMenuStrings; + + // Main loop + virtual void startupNew() = 0; + virtual void startupLoad() = 0; + void runLoop(); + void update() { screen()->updateScreen(); } + bool updateCharacterEvents(bool a); + + bool _runFlag; + //int _runLoopUnk2; + + // Create Party + void startCharacterGeneration(); + + uint8 **_faceShapes; + + static const int8 _classHpIncreaseType[]; + static const uint8 _hpIncrPerLevel[]; + static const uint8 _numLevelsPerClass[]; + static const int16 _hpConstModifiers[]; + static const uint8 _charClassModUnk[]; + + const uint8 *_classModifierFlags; + + // timers + void setupTimers(); + void setCharEventTimer(int charIndex, uint32 countdown, int evnt, int updateExistingTimer); + void deleteCharEventTimer(int charIndex, int evnt); + void setupCharacterTimers(); + + void timerProcessMonsters(int timerNum); + void timerSpecialCharacterUpdate(int timerNum); + void timerProcessFlyingObjects(int timerNum); + void timerProcessCharacterExchange(int timerNum); + void timerUpdateTeleporters(int timerNum); + void timerUpdateFoodStatus(int timerNum); + void timerUpdateMonsterIdleAnim(int timerNum); + + uint8 getClock2Timer(int index) { return index < _numClock2Timers ? _clock2Timers[index] : 0; } + uint8 getNumClock2Timers() { return _numClock2Timers; } + + static const uint8 _clock2Timers[]; + static const uint8 _numClock2Timers; + + // Mouse + void setHandItem(Item itemIndex); + void updateHandItemCursor() { _updateHandItemCursor = true; } + bool _updateHandItemCursor; + + // Characters + int getDexterityArmorClassModifier(int dexterity); + int generateCharacterHitpointsByLevel(int charIndex, int levelIndex); + int getClassAndConstHitpointsModifier(int cclass, int constitution); + int getClassHpIncreaseType(int cclass, int levelIndex); + int getModifiedHpLimits(int hpModifier, int constModifier, int level, bool mode); + const char *getCharStrength(int str, int strExt); + int testCharacter(int index, int flags); + int getNextValidCharIndex(int curCharIndex, int searchStep); + + void recalcArmorClass(int index); + int validateWeaponSlotItem(int index, int slot); + int getCharacterClericPaladinLevel(int index); + int getCharacterMageLevel(int index); + int getLevelIndexForHpIncType(int unk, int cClass); + + int countCharactersWithSpecificItems(int16 itemType, int16 itemValue); + int checkCharacterInventoryForItem(int character, int16 itemType, int16 itemValue); + void modifyCharacterHitpoints(int character, int16 points); + void neutralizePoison(int character); + + virtual void npcSequence(int npcIndex) = 0; + void initNpc(int npcIndex); + int npcJoinDialogue(int npcIndex, int queryJoinTextId, int confirmJoinTextId, int noJoinTextId); + int prepareForNewPartyMember(int16 itemType, int16 itemValue); + void removeCharacterFromParty(int charIndex); + + void increasePartyExperience(int16 points); + void increaseCharacterExperience(int charIndex, int32 points); + uint32 getRequiredExperience(int cClass, int levelIndex, int level); + void increaseCharacterLevel(int charIndex, int levelIndex); + + void setWeaponSlotStatus(int charIndex, int mode, int slot); + + EobCharacter *_characters; + char _strenghtStr[6]; + int _castScrollSlot; + int _exchangeCharacterId; + + const char *const *_levelGainStrings; + const uint32 *_expRequirementTables[6]; + + const uint8 *_constModTables[6]; + const uint8 *_constModLevelIndex; + const uint8 *_constModDiv; + const uint8 *_constModExt; + + const EobCharacter *_npcPreset; + bool _partyResting; + + // Items + void loadItemDefs(); + Item duplicateItem(Item itemIndex); + void setItemPosition(Item *itemQueue, int block, Item item, int pos); + void createInventoryItem(EobCharacter *c, Item itemIndex, int itemValue, int preferedInventorySlot); + int deleteInventoryItem(int charIndex, int slot); + void deleteBlockItem(uint16 block, int type); + int validateInventorySlotForItem(Item item, int charIndex, int slot); + void deletePartyItem(Item itemType, int16 itemValue); + virtual void updateUsedCharacterHandItem(int charIndex, int slot) = 0; + int itemUsableByCharacter(int charIndex, Item item); + int countQueuedItems(Item itemQueue, int16 id, int16 type, int count, int includeFlyingItems); + int getQueuedItem(Item *items, int pos, int id); + void printFullItemName(Item item); + void identifyQueuedItems(Item itemQueue); + void drawItemIconShape(int pageNum, Item itemId, int x, int y); + bool isMagicWeapon(Item itemIndex); + bool checkInventoryForRings(int charIndex, int itemValue); + void eatItemInHand(int charIndex); + + bool launchObject(int charIndex, Item item, uint16 startBlock, int startPos, int dir, int type); + void launchMagicObject(int charIndex, int type, uint16 startBlock, int startPos, int dir); + bool updateObjectFlight(EobFlyingObject *fo, int block, int pos); + bool updateFlyingObjectHitTest(EobFlyingObject *fo, int block, int pos); + void updateFlyingObject_s3(EobFlyingObject *fo); + void endObjectFlight(EobFlyingObject *fo); + void checkFlyingObjects(); + + void reloadWeaponSlot(int charIndex, int slotIndex, int itemType, int arrowOrDagger); + + EobItem *_items; + uint16 _numItems; + EobItemType *_itemTypes; + char **_itemNames; + uint16 _numItemNames; + uint32 _partyEffectFlags; + Item _lastUsedItem; + + const uint16 *_slotValidationFlags; + + EobFlyingObject *_flyingObjects; + const uint8 *_drawObjPosIndex; + const uint8 *_flightObjFlipIndex; + const int8 *_flightObjShpMap; + const int8 *_flightObjSclIndex; + + // Monsters + void loadMonsterShapes(const char *filename, int monsterIndex, bool hasDecorations, int encodeTableIndex); + void releaseMonsterShapes(int first, int num); + virtual void generateMonsterPalettes(const char *file, int16 monsterIndex) {} + virtual void loadMonsterDecoration(const char *file, int16 monsterIndex) {} + const uint8 *loadMonsterProperties(const uint8 *data); + const uint8 *loadActiveMonsterData(const uint8 *data, int level); + void initMonster(int index, int unit, uint16 block, int pos, int dir, int type, int shpIndex, int mode, int i, int randItem, int fixedItem); + void placeMonster(EobMonsterInPlay *m, uint16 block, int dir); + virtual void replaceMonster(int b, uint16 block, int pos, int dir, int type, int shpIndex, int mode, int h2, int randItem, int fixedItem) = 0; + void killMonster(EobMonsterInPlay *m, bool giveExperience); + int countSpecificMonsters(int type); + void updateAttackingMonsterFlags(); + + const int8 *getMonsterBlockPositions(uint16 block); + int getClosestMonsterPos(int charIndex, int block); + + bool blockHasMonsters(uint16 block); + bool isMonsterOnPos(EobMonsterInPlay *m, uint16 block, int pos, int checkPos4); + const int16 *findBlockMonsters(uint16 block, int pos, int dir, int blockDamage, int singleTargetCheckAdjacent); + + void drawBlockObject(int flipped, int page, const uint8 *shape, int x, int y, int sd, uint8 *ovl = 0); + void drawMonsterShape(const uint8 *shape, int x, int y, int flipped, int flags, int palIndex); + void flashMonsterShape(EobMonsterInPlay *m); + void updateAllMonsterShapes(); + void drawBlockItems(int index); + void drawDoor(int index); + virtual void drawDoorIntern(int type, int index, int x, int y, int w, int wall, int mDim, int16 y1, int16 y2) = 0; + void drawMonsters(int index); + void drawWallOfForce(int index); + void drawFlyingObjects(int index); + void drawTeleporter(int index); + + void updateMonsters(int unit); + void updateMonsterDest(EobMonsterInPlay *m); + void updateMonsterDest2(EobMonsterInPlay *m); + void turnFriendlyMonstersHostile(); + int getNextMonsterDirection(int curBlock, int destBlock); + int getNextMonsterPos(EobMonsterInPlay *m, int block); + int findFreeMonsterPos(int block, int size); + void updateMoveMonster(EobMonsterInPlay *m); + bool updateMonsterTryDistanceAttack(EobMonsterInPlay *m); + bool updateMonsterTryCloseAttack(EobMonsterInPlay *m, int block); + void walkMonster(EobMonsterInPlay *m, int destBlock); + bool walkMonsterNextStep(EobMonsterInPlay *m, int destBlock, int direction); + void updateMonsterFollowPath(EobMonsterInPlay *m, int turnSteps); + void updateMonstersStraying(EobMonsterInPlay *m, int a); + void updateMonsters_mode710(EobMonsterInPlay *m); + void setBlockMonsterDirection(int block, int dir); + + uint8 *_monsterOvl1; + uint8 *_monsterOvl2; + + SpriteDecoration *_monsterDecorations; + EobMonsterProperty *_monsterProps; + + EobMonsterInPlay *_monsters; + + const int8 *_monsterStepTable0; + const int8 *_monsterStepTable1; + const int8 *_monsterStepTable2; + const int8 *_monsterStepTable3; + const uint8 *_monsterCloseAttPosTable1; + const uint8 *_monsterCloseAttPosTable2; + const int8 *_monsterCloseAttUnkTable; + const uint8 *_monsterCloseAttChkTable1; + const uint8 *_monsterCloseAttChkTable2; + const uint8 *_monsterCloseAttDstTable1; + const uint8 *_monsterCloseAttDstTable2; + + const uint8 *_monsterProximityTable; + const uint8 *_findBlockMonstersTable; + const char *const *_monsterDustStrings; + + const uint8 *_monsterDistAttType10; + const uint8 *_monsterDistAttSfx10; + const uint8 *_monsterDistAttType17; + const uint8 *_monsterDistAttSfx17; + const char *const *_monsterSpecAttStrings; + + const int8 *_monsterFrmOffsTable1; + const int8 *_monsterFrmOffsTable2; + + const uint16 *_encodeMonsterShpTable; + const uint8 _teleporterWallId; + + const int8 *_monsterDirChangeTable; + + // Level + void loadLevel(int level, int func); + const char *initLevelData(int func); + void addLevelItems(); + void loadVcnData(const char *file, const char */*nextFile*/); + void loadBlockProperties(const char *mazFile); + void loadDecorations(const char *cpsFile, const char *decFile); + void assignWallsAndDecorations(int wallIndex, int vmpIndex, int decDataIndex, int specialType, int flags); + void releaseDecorations(); + void releaseDoorShapes(); + void toggleWallState(int wall, int flags); + virtual void loadDoorShapes(int doorType1, int shapeId1, int doorType2, int shapeId2) {} + virtual const uint8 *loadDoorShapes(const char *filename, int doorIndex, const uint8*shapeDefs) { return (const uint8*)filename; } + + void drawScene(int update); + void drawSceneShapes(); + void drawDecorations(int index); + + int calcNewBlockPositionAndTestPassability(uint16 curBlock, uint16 direction); + void notifyBlockNotPassable(); + void moveParty(uint16 block); + + int clickedDoorSwitch(uint16 block, uint16 direction); + int clickedDoorPry(uint16 block, uint16 direction); + int clickedDoorNoPry(uint16 block, uint16 direction); + int clickedNiche(uint16 block, uint16 direction); + + int specialWallAction(int block, int direction); + + void openDoor(int block); + void closeDoor(int block); + + int16 _doorType[2]; + int16 _noDoorSwitch[2]; + + EobRect8 *_levelDecorationRects; + SpriteDecoration *_doorSwitches; + + int8 _currentSub; + char _curGfxFile[13]; + + uint32 _drawSceneTimer; + uint32 _flashShapeTimer; + uint32 _envAudioTimer; + uint16 _teleporterPulse; + + const int16 *_dscShapeCoords; + + const uint8 *_dscItemPosIndex; + const int16 *_dscItemShpX; + const uint8 *_dscItemScaleIndex; + const uint8 *_dscItemTileIndex; + const uint8 *_dscItemShapeMap; + + const uint8 *_dscDoorScaleOffs; + const uint8 *_dscDoorScaleMult1; + const uint8 *_dscDoorScaleMult2; + const uint8 *_dscDoorScaleMult3; + const uint8 *_dscDoorY1; + + const uint8 *_wllFlagPreset; + int _wllFlagPresetSize; + const uint8 *_teleporterShapeCoords; + + // Script + void runLevelScript(int block, int flags); + void setScriptFlag(int flag); + bool checkScriptFlag(int flag); + + const uint8 *initScriptTimers(const uint8 *pos); + void updateScriptTimers(); + + EobInfProcessor *_inf; + int _stepCounter; + int _stepsUntilScriptCall; + ScriptTimer _scriptTimers[5]; + int _scriptTimersCount; + uint8 _scriptTimersMode; + + // Gui + void gui_drawPlayField(int pageNum); + void gui_restorePlayField(); + void gui_drawAllCharPortraitsWithStats(); + void gui_drawCharPortraitWithStats(int index); + void gui_drawFaceShape(int index); + void gui_drawWeaponSlot(int charIndex, int slot); + void gui_drawWeaponSlotStatus(int x, int y, int status); + void gui_drawHitpoints(int index); + void gui_drawFoodStatusGraph(int index); + void gui_drawHorizontalBarGraph(int x, int y, int w, int h, int32 curVal, int32 maxVal, int col1, int col2); + void gui_drawCharPortraitStatusFrame(int index); + void gui_drawInventoryItem(int slot, int special, int pageNum); + void gui_drawCompass(bool force); + void gui_drawDialogueBox(); + void gui_drawSpellbook(); + void gui_drawSpellbookScrollArrow(int x, int y, int direction); + void gui_updateSlotAfterScrollUse(); + void gui_updateControls(); + void gui_toggleButtons(); + void gui_setPlayFieldButtons(); + void gui_setInventoryButtons(); + void gui_setStatsListButtons(); + void gui_setSwapCharacterButtons(); + void gui_setCastOnWhomButtons(); + void gui_initButton(int index, int x = -1, int y = -1, int val = -1); + Button *gui_getButton(Button *buttonList, int index); + + int clickedInventoryNextPage(Button *button); + int clickedPortraitRestore(Button *button); + int clickedCharPortraitDefault(Button *button); + int clickedCamp(Button *button); + int clickedSceneDropPickupItem(Button *button); + int clickedCharPortrait2(Button *button); + int clickedWeaponSlot(Button *button); + int clickedCharNameLabelRight(Button *button); + int clickedInventorySlot(Button *button); + int clickedEatItem(Button *button); + int clickedInventoryPrevChar(Button *button); + int clickedInventoryNextChar(Button *button); + int clickedSpellbookTab(Button *button); + int clickedSpellbookList(Button *button); + int clickedCastSpellOnCharacter(Button *button); + int clickedUpArrow(Button *button); + int clickedDownArrow(Button *button); + int clickedLeftArrow(Button *button); + int clickedRightArrow(Button *button); + int clickedTurnLeftArrow(Button *button); + int clickedTurnRightArrow(Button *button); + int clickedAbortCharSwitch(Button *button); + int clickedSceneThrowItem(Button *button); + int clickedSceneSpecial(Button *button); + int clickedSpellbookAbort(Button *button); + int clickedSpellbookScroll(Button *button); + int clickedUnk(Button *button); + + void gui_processCharPortraitClick(int index); + void gui_processWeaponSlotClickLeft(int charIndex, int slotIndex); + void gui_processWeaponSlotClickRight(int charIndex, int slotIndex); + void gui_processInventorySlotClick(int slot); + + static const int16 _buttonList1[]; + int _buttonList1Size; + static const int16 _buttonList2[]; + int _buttonList2Size; + static const int16 _buttonList3[]; + int _buttonList3Size; + static const int16 _buttonList4[]; + int _buttonList4Size; + static const int16 _buttonList5[]; + int _buttonList5Size; + static const int16 _buttonList6[]; + int _buttonList6Size; + static const int16 _buttonList7[]; + int _buttonList7Size; + static const int16 _buttonList8[]; + int _buttonList8Size; + + const EobGuiButtonDef *_buttonDefs; + const char *const *_characterGuiStringsHp; + const char *const *_characterGuiStringsWp; + const char *const *_characterGuiStringsWr; + const char *const *_characterGuiStringsSt; + const char *const *_characterGuiStringsIn; + + const char *const *_characterStatusStrings7; + const char *const *_characterStatusStrings8; + const char *const *_characterStatusStrings9; + const char *const *_characterStatusStrings12; + const char *const *_characterStatusStrings13; + + const uint16 *_inventorySlotsX; + const uint8 *_inventorySlotsY; + const uint8 **_compassShapes; + uint8 _charExchangeSwap; + bool _hpBarGraphs; + + // text + void setupDialogueButtons(int presetfirst, int numStr, const char *str1, ...); + void initDialogueSequence(); + void restoreAfterDialogueSequence(); + void drawSequenceBitmap(const char *file, int destRect, int x1, int y1, int flags); + int runDialogue(int dialogueTextId, int style, const char *button1, ...); + + char _dialogueLastBitmap[13]; + int _dlgUnk1; + int _moveCounter; + + uint8 _color4; + uint8 _color5; + uint8 _color6; + uint8 _color7; + uint8 _color8; + uint8 _color9; + uint8 _color10; + uint8 _color11; + uint8 _color12; + uint8 _color13; + uint8 _color14; + + const char *const *_chargenStatStrings; + const char *const *_chargenRaceSexStrings; + const char *const *_chargenClassStrings; + const char *const *_chargenAlignmentStrings; + + const char *const *_pryDoorStrings; + const char *const *_warningStrings; + const char *const *_itemExtraStrings; + const char *const *_itemSuffixStrings; + const char *const *_takenStrings; + const char *const *_potionEffectStrings; + + const char *const *_yesNoStrings; + const char *const *_npcMaxStrings; + const char *const *_okStrings; + const char *const *_npcJoinStrings; + const char *const *_cancelStrings; + const char *const *_abortStrings; + + // misc + void delay(uint32 millis, bool doUpdate = false, bool isMainLoop = false); + void displayParchment(int id); + + virtual void drawLightningColumn() {} + virtual int resurrectionSelectDialogue() { return -1; } + virtual int charSelectDialogue() { return -1; } + virtual void characterLevelGain(int charIndex) {} + + Common::Error loadGameState(int slot); + Common::Error saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail); + + void *generateMonsterTempData(LevelTempData *tmp); + void *generateFlyingObjectTempData(LevelTempData *tmp); + void restoreMonsterTempData(LevelTempData *tmp); + void restoreFlyingObjectTempData(LevelTempData *tmp); + void releaseMonsterTempData(LevelTempData *tmp); + void releaseFlyingObjectTempData(LevelTempData *tmp); + + int _saveLoadMode; + + Screen_Eob *_screen; + GUI_Eob *_gui; + + // fight + void useSlotWeapon(int charIndex, int slotIndex, int item); + int closeDistanceAttack(int charIndex, int item); + int thrownAttack(int charIndex, int slotIndex, int item); + int bowAttack(int charIndex, int item); + + void inflictMonsterDamage(EobMonsterInPlay *m, int damage, bool giveExperience); + void calcAndInflictMonsterDamage(EobMonsterInPlay *m, int times, int pips, int offs, int flags, int b, int damageType); + void calcAndInflictCharacterDamage(int charIndex, int times, int itemOrPips, int useStrModifierOrBase, int flg, int a, int damageType); + int calcCharacterDamage(int charIndex, int times, int itemOrPips, int useStrModifierOrBase, int flg, int a, int damageType) ; + void inflictCharacterDamage(int charIndex, int damage); + + bool characterAttackHitTest(int charIndex, int monsterIndex, int item, int attackType); + bool monsterAttackHitTest(EobMonsterInPlay *m, int charIndex); + bool flyingObjectMonsterHit(EobFlyingObject *fo, int monsterIndex); + bool flyingObjectPartyHit(EobFlyingObject *fo); + + void monsterCloseAttack(EobMonsterInPlay *m); + void monsterSpellCast(EobMonsterInPlay *m, int type); + void statusAttack(int charIndex, int attackStatusFlags, const char *attackStatusString, int a, uint32 effectDuration, int restoreEvent, int noRefresh); + + int calcCloseDistanceMonsterDamage(EobMonsterInPlay *m, int times, int pips, int offs, int flags, int b, int damageType); + int calcDamageModifers(int charIndex, EobMonsterInPlay *m, int item, int itemType, int useStrModifier); + bool checkUnkConstModifiers(void *target, int hpModifier, int level, int b, int race); + bool specialAttackConstTest(int charIndex, int b); + int getConstModifierTableValue(int hpModifier, int level, int b); + bool calcDamageCheckItemType(int itemType); + int recalcDamageModifier(int damageType, int dmgModifier); + bool checkMonsterDamageEvasion(EobMonsterInPlay *m); + int getStrHitChanceModifier(int charIndex); + int getStrDamageModifier(int charIndex); + int getDexHitChanceModifier(int charIndex); + int getMonsterAcHitChanceModifier(int charIndex, int monsterAc); + void inflictMonsterDamage_s1(EobMonsterInPlay *m); + + int _dstMonsterIndex; + int _inflictMonsterDamageUnk; + int16 _foundMonstersArray[5]; + + // magic + void useMagicBookOrSymbol(int charIndex, int type); + void useMagicScroll(int charIndex, int type, int weaponSlot); + void usePotion(int charIndex, int weaponSlot); + + void castSpell(int spell, int weaponSlot); + void removeCharacterEffect(int spell, int charIndex, int showWarning); + void removeAllCharacterEffects(int charIndex); + void castOnWhomDialogue(); + void startSpell(int spell); + + void sparkEffectDefensive(int charIndex); + void sparkEffectOffensive(); + void setSpellEventTimer(int spell, int timerBaseFactor, int timerLength, int timerLevelFactor, int updateExistingTimer); + + bool magicObjectHit(EobFlyingObject *fo, int dcTimes, int dcPips, int dcOffs, int level); + + void spellCallback_start_empty() {} + bool spellCallback_end_empty(EobFlyingObject *fo) { return true; } + void spellCallback_start_armor(); + void spellCallback_start_burningHands(); + void spellCallback_start_detectMagic(); + bool spellCallback_end_detectMagic(EobFlyingObject *fo); + void spellCallback_start_magicMissile(); + bool spellCallback_end_magicMissile(EobFlyingObject *fo); + void spellCallback_start_shockingGrasp(); + bool spellCallback_end_shockingGraspFlameBlade(EobFlyingObject *fo); + void spellCallback_start_improvedIdentify(); + void spellCallback_start_melfsAcidArrow(); + bool spellCallback_end_melfsAcidArrow(EobFlyingObject *fo); + void spellCallback_start_dispelMagic(); + void spellCallback_start_fireball(); + bool spellCallback_end_fireball(EobFlyingObject *fo); + void spellCallback_start_flameArrow(); + bool spellCallback_end_flameArrow(EobFlyingObject *fo); + void spellCallback_start_holdPerson(); + bool spellCallback_end_holdPerson(EobFlyingObject *fo); + void spellCallback_start_lightningBolt(); + bool spellCallback_end_lightningBolt(EobFlyingObject *fo); + void spellCallback_start_vampiricTouch(); + bool spellCallback_end_vampiricTouch(EobFlyingObject *fo); + void spellCallback_start_fear(); + void spellCallback_start_iceStorm(); + bool spellCallback_end_iceStorm(EobFlyingObject *fo); + void spellCallback_start_removeCurse(); + void spellCallback_start_coneOfCold(); + void spellCallback_start_holdMonster(); + bool spellCallback_end_holdMonster(EobFlyingObject *fo); + void spellCallback_start_wallOfForce(); + void spellCallback_start_disintegrate(); + void spellCallback_start_fleshToStone(); + void spellCallback_start_stoneToFlesh(); + void spellCallback_start_trueSeeing(); + bool spellCallback_end_trueSeeing(EobFlyingObject *fo); + void spellCallback_start_slayLiving(); + void spellCallback_start_powerWordStun(); + void spellCallback_start_causeLightWounds(); + void spellCallback_start_cureLightWounds(); + void spellCallback_start_aid(); + bool spellCallback_end_aid(EobFlyingObject *fo); + void spellCallback_start_flameBlade(); + void spellCallback_start_slowPoison(); + bool spellCallback_end_slowPoison(EobFlyingObject *fo); + void spellCallback_start_createFood(); + void spellCallback_start_removeParalysis(); + void spellCallback_start_causeSeriousWounds(); + void spellCallback_start_cureSeriousWounds(); + void spellCallback_start_neutralizePoison(); + void spellCallback_start_causeCriticalWounds(); + void spellCallback_start_cureCriticalWounds(); + void spellCallback_start_flameStrike(); + bool spellCallback_end_flameStrike(EobFlyingObject *fo); + void spellCallback_start_raiseDead(); + void spellCallback_start_harm(); + void spellCallback_start_heal(); + void spellCallback_start_layOnHands(); + void spellCallback_start_turnUndead(); + bool spellCallback_end_unk1Passive(EobFlyingObject *fo); + bool spellCallback_end_unk2Passive(EobFlyingObject *fo); + bool spellCallback_end_deathSpellPassive(EobFlyingObject *fo); + bool spellCallback_end_disintegratePassive(EobFlyingObject *fo); + bool spellCallback_end_causeCriticalWoundsPassive(EobFlyingObject *fo); + bool spellCallback_end_fleshToStonePassive(EobFlyingObject *fo); + + int8 _openBookSpellLevel; + int8 _openBookSpellSelectedItem; + int8 _openBookSpellListOffset; + uint8 _openBookChar; + uint8 _openBookType; + uint8 _openBookCharBackup; + uint8 _openBookTypeBackup; + const char *const *_openBookSpellList; + int8 *_openBookAvailableSpells; + uint8 _activeSpellCaster; + uint8 _activeSpellCasterPos; + uint8 _activeSpell; + bool _returnAfterSpellCallback; + + typedef void (EobCoreEngine::*SpellStartCallback)(); + typedef bool (EobCoreEngine::*SpellEndCallback)(EobFlyingObject *fo); + + struct EobSpell { + const char *name; + SpellStartCallback startCallback; + uint16 flags; + const uint16 *timingPara; + SpellEndCallback endCallback; + uint8 sound; + uint32 effectFlags; + uint16 damageFlags; + }; + + EobSpell *_spells; + int _numSpells; + + const char *const *_bookNumbers; + const char *const *_mageSpellList; + int _mageSpellListSize; + int _clericSpellOffset; + const char *const *_clericSpellList; + const char *const *_spellNames; + const char *const *_magicStrings1; + const char *const *_magicStrings2; + const char *const *_magicStrings3; + const char *const *_magicStrings4; + const char *const *_magicStrings5; + const char *const *_magicStrings6; + const char *const *_magicStrings7; + const char *const *_magicStrings8; + + uint8 *_spellAnimBuffer; + + const uint8 *_sparkEffectDefSteps; + const uint8 *_sparkEffectDefSubSteps; + const uint8 *_sparkEffectDefShift; + const uint8 *_sparkEffectDefAdd; + const uint8 *_sparkEffectDefX; + const uint8 *_sparkEffectDefY; + const uint32 *_sparkEffectOfFlags1; + const uint32 *_sparkEffectOfFlags2; + const uint8 *_sparkEffectOfShift; + const uint8 *_sparkEffectOfX; + const uint8 *_sparkEffectOfY; + + const uint8 *_magicFlightObjectProperties; + + // sound + void snd_playSoundEffect(int id, int volume=0xFF); +}; + +} // End of namespace Kyra + +#endif // ENABLE_EOB + +#endif
\ No newline at end of file diff --git a/engines/kyra/gui.h b/engines/kyra/gui.h index 6e9606f1de..04fc9b5523 100644 --- a/engines/kyra/gui.h +++ b/engines/kyra/gui.h @@ -40,10 +40,10 @@ struct Button { typedef Common::Functor1<Button *, int> CallbackFunctor; typedef Common::SharedPtr<CallbackFunctor> Callback; - Button() : nextButton(0), index(0), keyCode(0), keyCode2(0), data0Val1(0), data1Val1(0), data2Val1(0), flags(0), + Button() : nextButton(0), index(0), keyCode(0), keyCode2(0), data0Val1(0), data1Val1(0), data2Val1(0), data3Val1(0), flags(0), data0ShapePtr(0), data1ShapePtr(0), data2ShapePtr(0), data0Callback(), data1Callback(), data2Callback(), dimTableIndex(0), x(0), y(0), width(0), height(0), data0Val2(0), data0Val3(0), data1Val2(0), data1Val3(0), - data2Val2(0), data2Val3(0), flags2(0), mouseWheel(0), buttonCallback(), arg(0) {} + data2Val2(0), data2Val3(0), data3Val2(0), data3Val3(0), flags2(0), mouseWheel(0), buttonCallback(), arg(0) {} Button *nextButton; uint16 index; @@ -54,6 +54,7 @@ struct Button { byte data0Val1; byte data1Val1; byte data2Val1; + byte data3Val1; uint16 flags; @@ -78,6 +79,9 @@ struct Button { uint8 data2Val2; uint8 data2Val3; + uint8 data3Val2; + uint8 data3Val3; + uint16 flags2; int8 mouseWheel; diff --git a/engines/kyra/gui_eob.cpp b/engines/kyra/gui_eob.cpp new file mode 100644 index 0000000000..9aafb76189 --- /dev/null +++ b/engines/kyra/gui_eob.cpp @@ -0,0 +1,2165 @@ +/* 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. + * + */ + +#if defined(ENABLE_EOB) || defined(ENABLE_LOL) + +#include "kyra/eobcommon.h" +#include "kyra/gui_eob.h" +#include "kyra/timer.h" +#include "kyra/util.h" + +#include "common/system.h" + +namespace Kyra { + +void LolEobBaseEngine::removeInputTop() { + if (!_eventList.empty()) { + if (_eventList.begin()->event.type == Common::EVENT_LBUTTONDOWN) + _mouseClick = 1; + else if (_eventList.begin()->event.type == Common::EVENT_RBUTTONDOWN) + _mouseClick = 2; + else + _mouseClick = 0; + + _eventList.erase(_eventList.begin()); + } +} + +void LolEobBaseEngine::gui_drawBox(int x, int y, int w, int h, int frameColor1, int frameColor2, int fillColor) { + w--; h--; + if (fillColor != -1) + screen()->fillRect(x + 1, y + 1, x + w - 1, y + h - 1, fillColor); + + screen()->drawClippedLine(x + 1, y, x + w, y, frameColor2); + screen()->drawClippedLine(x + w, y, x + w, y + h - 1, frameColor2); + screen()->drawClippedLine(x, y, x, y + h, frameColor1); + screen()->drawClippedLine(x, y + h, x + w, y + h, frameColor1); +} + +void LolEobBaseEngine::gui_drawHorizontalBarGraph(int x, int y, int w, int h, int32 cur, int32 max, int col1, int col2) { + if (max < 1) + return; + if (cur < 0) + cur = 0; + + int32 e = MIN(cur, max); + + if (!--w) + return; + if (!--h) + return; + + int32 t = (e * w) / max; + + if (!t && e) + t++; + + if (t) + screen()->fillRect(x, y, x + t - 1, y + h, col1); + + if (t < w && col2) + screen()->fillRect(x + t, y, x + w, y + h, col2); +} + +void LolEobBaseEngine::gui_initButtonsFromList(const int16 *list) { + while (*list != -1) + gui_initButton(*list++); +} + +void LolEobBaseEngine::gui_resetButtonList() { + for (uint i = 0; i < ARRAYSIZE(_activeButtonData); ++i) + _activeButtonData[i].nextButton = 0; + + gui_notifyButtonListChanged(); + _activeButtons = 0; +} + +void LolEobBaseEngine::gui_notifyButtonListChanged() { + if (gui()) { + if (!_buttonListChanged && !_preserveEvents) + removeInputTop(); + _buttonListChanged = true; + } +} + +bool LolEobBaseEngine::clickedShape(int shapeIndex) { + if (_clickedSpecialFlag != 0x40) + return true; + + for (; shapeIndex; shapeIndex = _levelDecorationProperties[shapeIndex].next) { + if (_flags.gameID != GI_LOL) + shapeIndex--; + + uint16 s = _levelDecorationProperties[shapeIndex].shapeIndex[1]; + + if (s == 0xffff) + continue; + + int w = _flags.gameID == GI_LOL ? _levelDecorationShapes[s][3] : (_levelDecorationShapes[s][2] << 3); + int h = _levelDecorationShapes[s][_flags.gameID == GI_LOL ? 2 : 1]; + int x = _levelDecorationProperties[shapeIndex].shapeX[1] + _clickedShapeXOffs; + int y = _levelDecorationProperties[shapeIndex].shapeY[1] + _clickedShapeYOffs; + + if (_levelDecorationProperties[shapeIndex].flags & 1) { + if (_flags.gameID == GI_LOL) + w <<= 1; + else + x = 176 - x - w; + } + + if (posWithinRect(_mouseX, _mouseY, x - 4, y - 4, x + w + 8, y + h + 8)) + return true; + } + + return false; +} + +#ifdef ENABLE_EOB + +Button *EobCoreEngine::gui_getButton(Button *buttonList, int index) { + while (buttonList) { + if (buttonList->index == index) + return buttonList; + buttonList = buttonList->nextButton; + } + + return 0; +} + +void EobCoreEngine::gui_drawPlayField(int pageNum) { + _screen->loadEobCpsFileToPage("PLAYFLD", 0, 5, 3, 2); + int cp = _screen->setCurPage(2); + gui_drawCompass(true); + + if (pageNum && !_sceneDrawPage1) + drawScene(0); + + _screen->setCurPage(cp); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + + _screen->loadEobCpsFileToPage("INVENT", 0, 5, 3, 2); +} + +void EobCoreEngine::gui_restorePlayField() { + loadVcnData(0, 0); + _screen->_curPage = 0; + gui_drawPlayField(1); + gui_drawAllCharPortraitsWithStats(); +} + +void EobCoreEngine::gui_drawAllCharPortraitsWithStats() { + for (int i = 0; i < 6; i++) + gui_drawCharPortraitWithStats(i); +} + +void EobCoreEngine::gui_drawCharPortraitWithStats(int index) { + if (!testCharacter(index, 1)) + return; + + static const uint16 charPortraitPosX[] = { 8, 80, 184, 256 }; + static const uint16 charPortraitPosY[] = { 2, 54, 106 }; + + EobCharacter *c = &_characters[index]; + int txtCol1 = 12; + int txtCol2 = 15; + + if ((_flags.gameID == GI_EOB1 && c->flags & 6) || (_flags.gameID == GI_EOB2 && c->flags & 0x0e)) { + txtCol1 = 8; + txtCol2 = 6; + } + + char tmpStr[10]; + + if (_currentControlMode == 0) { + int x2 = charPortraitPosX[index & 1]; + int y2 = charPortraitPosY[index >> 1]; + Screen::FontId cf = _screen->setFont(Screen::FID_6_FNT); + + _screen->copyRegion(176, 168, x2 , y2, 64, 24, 2, 2, Screen::CR_NO_P_CHECK); + _screen->copyRegion(240, 168, x2, y2 + 24, 64, 26, 2, 2, Screen::CR_NO_P_CHECK); + int cp = _screen->setCurPage(2); + + if (index == _exchangeCharacterId) + _screen->printText(_characterGuiStringsSt[0], x2 + 2, y2 + 2, 8, _bkgColor_1); + else + _screen->printText(c->name, x2 + 2, y2 + 2, txtCol1, _bkgColor_1); + + gui_drawFaceShape(index); + gui_drawWeaponSlot(index, 0); + gui_drawWeaponSlot(index, 1); + gui_drawHitpoints(index); + + if (testCharacter(index, 2)) + gui_drawCharPortraitStatusFrame(index); + + if (c->damageTaken > 0) { + _screen->drawShape(2, _redSplatShape, x2 + 13, y2 + 30, 0); + sprintf(tmpStr, "%d", c->damageTaken); + _screen->printText(tmpStr, x2 + 34 - (strlen(tmpStr) * 3), y2 + 42, 15, 0); + } + + _screen->setCurPage(cp); + _screen->setFont(cf); + + if (!cp) { + _screen->copyRegion(x2, y2, charPortraitPosX[2 + (index & 1)], y2, 64, 50, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + } + } else if ((_currentControlMode == 1 || _currentControlMode == 2) && index == _updateCharNum) { + _screen->copyRegion(176, 0, 0, 0, 144, 168, 2, 2, Screen::CR_NO_P_CHECK); + _screen->_curPage = 2; + gui_drawFaceShape(index); + _screen->printShadedText(c->name, 219, 6, txtCol2, _bkgColor_1); + gui_drawHitpoints(index); + gui_drawFoodStatusGraph(index); + + if (_currentControlMode == 1) { + if (c->hitPointsCur == -10) + _screen->printShadedText(_characterGuiStringsSt[1], 247, 158, 6, _color6); + else if (c->hitPointsCur < 1) + _screen->printShadedText(_characterGuiStringsSt[2], 226, 158, 6, _color6); + else if (c->effectFlags & (_flags.gameID == GI_EOB1 ? 0x80 : 0x2000)) + _screen->printShadedText(_characterGuiStringsSt[3], 220, 158, 6, _color6); + else if (c->flags & 2) + _screen->printShadedText(_characterGuiStringsSt[4], 235, 158, 6, _color6); + else if (c->flags & 4) + _screen->printShadedText(_characterGuiStringsSt[5], 232, 158, 6, _color6); + else if (c->flags & 8) + _screen->printShadedText(_characterGuiStringsSt[6], 232, 158, 6, _color6); + + for (int i = 0; i < 27; i++) + gui_drawInventoryItem(i, 0, 2); + gui_drawInventoryItem(16, 1, 2); + + } else { + static const uint16 cm2X1[] = { 179, 272, 301 }; + static const uint16 cm2Y1[] = { 36, 51, 51 }; + static const uint16 cm2X2[] = { 271, 300, 318 }; + static const uint16 cm2Y2[] = { 165, 165, 147 }; + + for (int i = 0; i < 3; i++) + _screen->fillRect(cm2X1[i], cm2Y1[i], cm2X2[i], cm2Y2[i], _color6); + + _screen->printShadedText(_characterGuiStringsIn[0], 183, 42, 15, _color6); + _screen->printText(_chargenClassStrings[c->cClass], 183, 55, 12, _color6); + _screen->printText(_chargenAlignmentStrings[c->alignment], 183, 62, 12, _color6); + _screen->printText(_chargenRaceSexStrings[c->raceSex], 183, 69, 12, _color6); + + for (int i = 0; i < 6; i++) + _screen->printText(_chargenStatStrings[6 + i], 183, 82 + i * 7, 12, _color6); + + _screen->printText(_characterGuiStringsIn[1], 183, 124, 12, _color6); + _screen->printText(_characterGuiStringsIn[2], 239, 138, 12, _color6); + _screen->printText(_characterGuiStringsIn[3], 278, 138, 12, _color6); + + _screen->printText(getCharStrength(c->strengthCur, c->strengthExtCur), 275, 82, 15, _color6); + sprintf(tmpStr, "%d", c->intelligenceCur); + _screen->printText(tmpStr, 275, 89, 15, _color6); + sprintf(tmpStr, "%d", c->wisdomCur); + _screen->printText(tmpStr, 275, 96, 15, _color6); + sprintf(tmpStr, "%d", c->dexterityCur); + _screen->printText(tmpStr, 275, 103, 15, _color6); + sprintf(tmpStr, "%d", c->constitutionCur); + _screen->printText(tmpStr, 275, 110, 15, _color6); + sprintf(tmpStr, "%d", c->charismaCur); + _screen->printText(tmpStr, 275, 117, 15, _color6); + sprintf(tmpStr, "%d", c->armorClass); + _screen->printText(tmpStr, 275, 124, 15, _color6); + + for (int i = 0; i < 3; i++) { + int t = getClassHpIncreaseType(c->cClass, i); + if (t == -1) + continue; + + _screen->printText(_chargenClassStrings[t + 15], 180, 145 + 7 * i, 12, _color6); + sprintf(tmpStr, "%d", c->experience[i]); + _screen->printText(tmpStr, 251 - strlen(tmpStr) * 3, 145 + 7 * i, 15, _color6); + sprintf(tmpStr, "%d", c->level[i]); + _screen->printText(tmpStr, 286 - strlen(tmpStr) * 3, 145 + 7 * i, 15, _color6); + } + } + + _screen->_curPage = 0; + _screen->copyRegion(176, 0, 176, 0, 144, 168, 2, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(0, 0, 176, 0, 144, 168, 2, 2, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + } +} + +void EobCoreEngine::gui_drawFaceShape(int index) { + if (!testCharacter(index, 1)) + return; + + static const uint8 xCoords[] = { 8, 80 }; + static const uint8 yCoords[] = { 11, 63, 115 }; + + int x = xCoords[index & 1]; + int y = yCoords[index >> 1]; + + if (!_screen->_curPage) + x += 176; + + if (_currentControlMode) { + if (_updateCharNum != index) + return; + + x = 181; + y = 3; + } + + EobCharacter *c = &_characters[index]; + + if (c->hitPointsCur == -10) { + _screen->drawShape(_screen->_curPage, _deadCharShape, x, y, 0); + return; + } + + if (_flags.gameID == GI_EOB1) { + if (c->effectFlags & 4) { + _screen->fillRect(x, y, x + 31, y + 31, 12); + return; + } + } else { + if (c->effectFlags & 0x140) { + _screen->setFadeTableIndex(1); + _screen->setShapeFadeMode(1, true); + } + + if (c->flags & 2) { + _screen->setFadeTableIndex(0); + _screen->setShapeFadeMode(1, true); + } + + if (c->flags & 8) { + _screen->setFadeTableIndex(2); + _screen->setShapeFadeMode(1, true); + } + } + + _screen->drawShape(_screen->_curPage, c->faceShape, x, y, 0); + + if (c->hitPointsCur < 1) + _screen->drawShape(_screen->_curPage, _disabledCharGrid, x, y, 0); + + //if ((c->flags & 2) || (c->flags & 8) || (c->effectFlags & 0x140)) { + _screen->setFadeTableIndex(4); + _screen->setShapeFadeMode(1, false); + //} +} + +void EobCoreEngine::gui_drawWeaponSlot(int charIndex, int slot) { + static const uint8 xCoords[] = { 40, 112 }; + static const uint8 yCoords[] = { 11, 27, 63, 79, 115, 131 }; + + int x = xCoords[charIndex & 1]; + int y = yCoords[(charIndex & 6) + slot]; + + if (!_screen->_curPage) + x += 176; + + int itm = _characters[charIndex].inventory[slot]; + gui_drawBox(x, y, 31, 16, _color1_1, _color2_1, _bkgColor_1); + + if (_characters[charIndex].slotStatus[slot]) { + gui_drawWeaponSlotStatus(x, y, _characters[charIndex].slotStatus[slot]); + return; + } + + if (itm) + drawItemIconShape(_screen->_curPage, itm, x + 8, y); + else if (!slot && checkScriptFlag(0x8000)) + _screen->drawShape(_screen->_curPage, _itemIconShapes[103], x + 8, y, 0); + else + _screen->drawShape(_screen->_curPage, _itemIconShapes[85], x + 8, y, 0); + + if ((_characters[charIndex].disabledSlots & (1 << slot)) || !validateWeaponSlotItem(charIndex, slot) || (_characters[charIndex].hitPointsCur <= 0) || (_characters[charIndex].flags & 0x0c)) + _screen->drawShape(_screen->_curPage, _weaponSlotGrid, x, y, 0); +} + +void EobCoreEngine::gui_drawWeaponSlotStatus(int x, int y, int status) { + char tmpStr[6]; + char tmpStr2[6]; + tmpStr2[0] = 0; + + if (status > -3 || status == -5) + _screen->drawShape(_screen->_curPage, _greenSplatShape, x - 1, y, 0); + else + gui_drawBox(x, y, 31, 16, _color9, _color10, _color11); + + switch (status + 5) { + case 0: + strcpy(tmpStr, _characterGuiStringsWp[2]); + break; + case 1: + strcpy(tmpStr, _characterGuiStringsWr[2]); + strcpy(tmpStr2, _characterGuiStringsWr[3]); + break; + case 2: + strcpy(tmpStr, _characterGuiStringsWr[0]); + strcpy(tmpStr2, _characterGuiStringsWr[1]); + break; + case 3: + strcpy(tmpStr, _characterGuiStringsWp[1]); + break; + case 4: + strcpy(tmpStr, _characterGuiStringsWp[0]); + break; + default: + snprintf(tmpStr, 6, "%d", status); + break; + } + + if (tmpStr2[0]) { + _screen->printText(tmpStr, x + (16 - strlen(tmpStr) * 3), y + 2, 15, 0); + _screen->printText(tmpStr2, x + (16 - strlen(tmpStr) * 3), y + 9, 15, 0); + } else { + _screen->printText(tmpStr, x + (16 - strlen(tmpStr) * 3), y + 5, 15, 0); + } +} + +void EobCoreEngine::gui_drawHitpoints(int index) { + if (!testCharacter(index, 1)) + return; + + if (_currentControlMode && (index != _updateCharNum)) + return; + + static const uint8 xCoords[] = { 23, 95 }; + static const uint8 yCoords[] = { 46, 98, 150 }; + static const uint8 barColor[] = { 3, 5, 8 }; + + int x = xCoords[index & 1]; + int y = yCoords[index >> 1]; + int w = 38; + int h = 3; + + if (!_screen->_curPage) + x += 176; + + if (_currentControlMode) { + x = 250; + y = 16; + w = 51; + h = 5; + } + + EobCharacter *c = &_characters[index]; + + if (_hpBarGraphs) { + int bgCur = c->hitPointsCur + 10; + int bgMax = c->hitPointsMax + 10; + int col = ((bgMax / 3) > bgCur) ? 1 : 0; + if (bgCur <= 10) + col = 2; + + if (!_currentControlMode) + _screen->printText(_characterGuiStringsHp[0], x - 13, y - 1, 12, 0); + + + gui_drawHorizontalBarGraph(x, y, w, h, bgCur, bgMax, barColor[col], _color5); + + } else { + char tmpString[12]; + snprintf(tmpString, 12, _characterGuiStringsHp[1], c->hitPointsCur, c->hitPointsMax); + + if (!_currentControlMode) { + x -= 13; + y -= 1; + } + + _screen->printText(tmpString, x, y, 12, _bkgColor_1); + } +} + +void EobCoreEngine::gui_drawFoodStatusGraph(int index) { + if (!_currentControlMode) + return; + + if (!testCharacter(index, 1)) + return; + + EobCharacter *c = &_characters[index]; + if (!(c->flags & 1)) + return; + + if (index != _updateCharNum) + return; + + uint8 col = c->food < 20 ? 8 : (c->food < 33 ? 5 : 3); + gui_drawHorizontalBarGraph(250, 25, 51, 5, c->food, 100, col, _color5); +} + +void EobCoreEngine::gui_drawHorizontalBarGraph(int x, int y, int w, int h, int32 curVal, int32 maxVal, int col1, int col2) { + gui_drawBox(x - 1, y - 1, w + 3, h + 2, _color2_1, _color1_1, -1); + LolEobBaseEngine::gui_drawHorizontalBarGraph(x, y, w + 2, h, curVal, maxVal, col1, col2); +} + +void EobCoreEngine::gui_drawCharPortraitStatusFrame(int index) { + uint8 boxColor = ((_partyEffectFlags & 0x20000) | (_partyEffectFlags & 0xffff)) ? 4 : 6; + + static const uint8 xCoords[] = { 8, 80 }; + static const uint8 yCoords[] = { 2, 54, 106 }; + int x = xCoords[index & 1]; + int y = yCoords[index >> 1]; + + if (!_screen->_curPage) + x += 176; + + EobCharacter *c = &_characters[index]; + + int v8 = (_flags.gameID == GI_EOB2 && ((c->effectFlags & 0x4818) || c->effectsRemainder[0] || c->effectsRemainder[1] || ((_partyEffectFlags & 0x20000) | (_partyEffectFlags & 0xffff)))) || + (_flags.gameID == GI_EOB1 && ((c->effectFlags & 0x302) || c->effectsRemainder[0] || c->effectsRemainder[1])) ? 1 : 0; + int vA = (_flags.gameID == GI_EOB2 && ((((c->effectFlags & 0x3000) | (c->effectFlags & 0x10000)) || (_partyEffectFlags & 0x8420)))) || + (_flags.gameID == GI_EOB1 && ((c->effectFlags & 0x4c8) || _partyEffectFlags & 300000))? 1 : 0; + + if (v8 || vA) { + if (v8 && !vA) { + _screen->drawBox(x, y, x + 63, y + 49, boxColor); + return; + } + + if (vA && !v8) { + _screen->drawBox(x, y, x + 63, y + 49, 5); + return; + } + + int iX= x; + int iY= y; + + for (int i = 0; i < 64; i += 16) { + x = iX + i; + if (v8) { + _screen->drawClippedLine(x, y, x + 7, y, boxColor); + _screen->drawClippedLine(x + 8, y + 49, x + 15, y + 49, boxColor); + } + if (vA) { + _screen->drawClippedLine(x + 8, y, x + 15, y, 5); + _screen->drawClippedLine(x, y + 49, x + 7, y + 49, 5); + } + } + + x = iX; + + for (int i = 1; i < 48; i += 12) { + y = iY + i - 1; + + if (vA) { + _screen->drawClippedLine(x, y + 1, x, y + 6, 5); + _screen->drawClippedLine(x + 63, y + 7, x + 63, y + 12, 5); + } + if (v8) { + _screen->drawClippedLine(x, y + 7, x, y + 12, boxColor); + _screen->drawClippedLine(x + 63, y + 1, x + 63, y + 6, boxColor); + } + } + + } else { + _screen->drawClippedLine(x, y, x + 62, y, _color1_1); + _screen->drawClippedLine(x, y + 49, x + 62, y + 49, _color2_1); + _screen->drawClippedLine(x - 1, y, x - 1, y + 50, 12); + _screen->drawClippedLine(x + 63, y, x + 63, y + 50, 12); + } +} + +void EobCoreEngine::gui_drawInventoryItem(int slot, int special, int pageNum) { + int x = _inventorySlotsX[slot]; + int y = _inventorySlotsY[slot]; + + int item = _characters[_updateCharNum].inventory[slot]; + int cp = _screen->setCurPage(pageNum); + + if (special) { + int wh = (slot == 25 || slot == 26) ? 10 : 18; + gui_drawBox(x - 1, y - 1, wh, wh, _color1_1, _color2_1, slot == 16 ? -1 : _bkgColor_1); + + if (slot == 16) { + _screen->fillRect(227, 65, 238, 69, 12); + int cnt = countQueuedItems(_characters[_updateCharNum].inventory[slot], -1, -1, 1, 1); + x = cnt >= 10 ? 227 : 233; + char str[3]; + snprintf(str, 3, "%d", cnt); + _screen->printText(str, x, 65, 15, 0); + } + } + + if (slot != 16 && item) { + if (slot == 25 || slot == 26) { + x -= 4; + y -= 4; + } + drawItemIconShape(pageNum, item, x, y); + } + _screen->_curPage = cp; + _screen->updateScreen(); +} + +void EobCoreEngine::gui_drawCompass(bool force) { + if (_currentDirection == _compassDirection && !force) + return; + + static const uint8 shpX[2][3] = { { 0x70, 0x4D, 0x95 }, { 0x72, 0x4F, 0x97 } }; + static const uint8 shpY[2][3] = { { 0x7F, 0x9A, 0x9A }, { 0x83, 0x9E, 0x9E } }; + int g = _flags.gameID == GI_EOB1 ? 0 : 1; + + for (int i = 0; i < 3; i++) + _screen->drawShape(_screen->_curPage, _compassShapes[(i << 2) + _currentDirection], shpX[g][i], shpY[g][i], 0); + + _compassDirection = _currentDirection; +} + +void EobCoreEngine::gui_drawDialogueBox() { + gui_drawBox(0, 121, 320, 79, _color1_1, _color2_1, _bkgColor_1); + txt()->clearCurDim(); +} + +void EobCoreEngine::gui_drawSpellbook() { + _screen->setCurPage(2); + int numTab = (_flags.gameID == GI_EOB1) ? 5 : 6; + _screen->copyRegion(64, 121, 64, 121, 112, 56, 0, 2, Screen::CR_NO_P_CHECK); + + for (int i = 0; i < numTab; i++) { + int col1 = _color14; + int col2 = _color13; + int col3 = _color12; + + if (i == _openBookSpellLevel) { + col1 = _color1_1; + col2 = _color2_1; + col3 = _bkgColor_1; + } + + if (_flags.gameID == GI_EOB1) { + gui_drawBox(i * 21 + 71, 122, 21, 9, col1, col2, col3); + _screen->printText(_magicStrings7[i], i * 21 + 73, 123, 12, 0); + } else { + gui_drawBox(i * 18 + 68, 121, 18, 9, col1, col2, col3); + char val[3]; + sprintf(val, "%d", i + 1); + _screen->printText(val, i * 18 + 75, 123, 12, 0); + } + } + + if (_flags.gameID == GI_EOB1) + gui_drawBox(71, 131, 105, 44, _color1_1, _color2_1, _bkgColor_1); + else { + gui_drawBox(68, 130, 108, 47, _color1_1, _color2_1, _bkgColor_1); + gui_drawBox(68, 168, 78, 9, _color8, _color7, _color6); + gui_drawBox(146, 168, 14, 9, _color8, _color7, _color6); + gui_drawBox(160, 168, 16, 9, _color8, _color7, _color6); + gui_drawSpellbookScrollArrow(150, 169, 0); + gui_drawSpellbookScrollArrow(165, 169, 1); + } + + int textCol1 = 15; + int textCol2 = 8; + int textXa = 74; + int textXs = 71; + int textY = 170; + int col3 = _bkgColor_1; + + if (_flags.gameID == GI_EOB1) { + textCol2 = 11; + textXa = textXs = 73; + textY = 168; + } + + for (int i = 0; i < 7; i++) { + int d = _openBookAvailableSpells[_openBookSpellLevel * 10 + _openBookSpellListOffset + i]; + if (_openBookSpellSelectedItem == i) { + if (d >= 0 && i < 6 && (i + _openBookSpellListOffset) < 9) { + _screen->printText(_openBookSpellList[d], textXs, 132 + 6 * i, textCol1, textCol2); + } else if (i == 6) { + if (_flags.gameID == GI_EOB2) + _screen->fillRect(69, 169, 144, 175, textCol2); + _screen->printText(_magicStrings1[0], textXa, textY, textCol1, textCol2); + } + } else { + if (d >= 0 && i < 6 && (i + _openBookSpellListOffset) < 9) + _screen->printText(_openBookSpellList[d], textXs, 132 + 6 * i, textCol1, col3); + else + _screen->printText(_magicStrings1[0], textXa, textY, 12, _color6); + } + } + + if (_characters[_openBookChar].disabledSlots & 4) { + static const uint8 xpos[] = { 0x44, 0x62, 0x80, 0x90 }; + static const uint8 ypos[] = { 0x82, 0x92, 0x98 }; + for (int yc = 0; yc < 3; yc++) { + for (int xc = 0; xc < 4; xc++) + _screen->drawShape(_screen->_curPage, _weaponSlotGrid, xpos[xc], ypos[yc], 0); + } + } + + if (_openBookAvailableSpells[_openBookSpellLevel * 10 + 6] <= 0) + _screen->drawShape(2, _blackBoxWideGrid, 146, 168, 0); + + _screen->setCurPage(0); + _screen->copyRegion(64, 121, 64, 121, 112, 56, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); +} + +void EobCoreEngine::gui_drawSpellbookScrollArrow(int x, int y, int direction) { + static const uint8 x1[] = { 0, 2, 1, 0, 2, 2 }; + static const uint8 x2[] = { 2, 4, 5, 6, 4, 4 }; + if (direction) { + _screen->setPagePixel(_screen->_curPage, x + 3, y + 5, 12); + for (int i = 1; i < 6; i++) + _screen->drawClippedLine(x + x1[i], (5 - i) + y, x + x2[i], (5 - i) + y, 12); + } else { + _screen->setPagePixel(_screen->_curPage, x + 3, y, 12); + for (int i = 1; i < 6; i++) + _screen->drawClippedLine(x + x1[i], y + i, x + x2[i], y + i, 12); + } +} + +void EobCoreEngine::gui_updateSlotAfterScrollUse() { + _characters[_openBookChar].disabledSlots ^= (1 << (--_castScrollSlot)); + setCharEventTimer(_openBookChar, 18, _castScrollSlot + 2, 1); + gui_drawCharPortraitWithStats(_openBookChar); + _openBookChar = _openBookCharBackup; + _openBookType = _openBookTypeBackup; + _castScrollSlot = 0; + gui_toggleButtons(); +} + +void EobCoreEngine::gui_updateControls() { + Button b; + if (_currentControlMode) + clickedPortraitRestore(&b); + if (_updateFlags) + clickedSpellbookAbort(&b); +} + +void EobCoreEngine::gui_toggleButtons() { + if (_currentControlMode == 0) + gui_setPlayFieldButtons(); + else if (_currentControlMode == 1) + gui_setInventoryButtons(); + else if (_currentControlMode == 2) + gui_setStatsListButtons(); +} + +void EobCoreEngine::gui_setPlayFieldButtons() { + gui_resetButtonList(); + gui_initButtonsFromList(_updateFlags ? _buttonList2 : _buttonList1); +} + +void EobCoreEngine::gui_setInventoryButtons() { + gui_resetButtonList(); + gui_initButtonsFromList(_updateFlags ? _buttonList5 : _buttonList3); +} + +void EobCoreEngine::gui_setStatsListButtons() { + gui_resetButtonList(); + gui_initButtonsFromList(_updateFlags ? _buttonList6 : _buttonList4); +} + +void EobCoreEngine::gui_setSwapCharacterButtons() { + gui_resetButtonList(); + gui_initButtonsFromList(_buttonList7); +} + +void EobCoreEngine::gui_setCastOnWhomButtons() { + gui_resetButtonList(); + gui_initButtonsFromList(_buttonList8); +} + +void EobCoreEngine::gui_initButton(int index, int, int, int) { + Button *b = 0; + int cnt = 1; + + if (_flags.gameID == GI_EOB1 && index > 92) + return; + + if (_activeButtons) { + Button *n = _activeButtons; + while (n->nextButton) { + ++cnt; + n = n->nextButton; + } + + ++cnt; + b = n->nextButton = &_activeButtonData[cnt]; + } else { + b = &_activeButtonData[0]; + _activeButtons = b; + } + + *b = Button(); + b->data0Val2 = 12; + b->data1Val2 = b->data2Val2 = 15; + b->data3Val2 = 8; + + b->index = index + 1; + + const EobGuiButtonDef *d = &_buttonDefs[index]; + + if (_flags.gameID == GI_EOB1) { + // EOB1 spellbook modifications + if (index > 61 && index < 67) + d = &_buttonDefs[index + 33]; + if (index == 88) + d = &_buttonDefs[index + 12]; + } + + b->x = d->x; + b->y = d->y; + b->width = d->w; + b->height = d->h; + + // EOB1 spellbook modifications + if (_flags.gameID == GI_EOB1 && ((index > 66 && index < 73) || (index > 76 && index < 79))) + b->y++; + + b->flags = d->flags; + b->keyCode = d->keyCode; + b->keyCode2 = d->keyCode2; + b->arg = d->arg; + b->buttonCallback = d->buttonCallback; +} + +int EobCoreEngine::clickedCharPortraitDefault(Button *button) { + if (!testCharacter(button->arg, 1)) + return 1; + + gui_processCharPortraitClick(button->arg); + return 0; +} + +int EobCoreEngine::clickedCamp(Button *button) { + return button->arg; +} + +int EobCoreEngine::clickedSceneDropPickupItem(Button *button) { + uint16 block = _currentBlock; + if (button->arg > 1) { + block = calcNewBlockPosition(_currentBlock, _currentDirection); + int f = _wllWallFlags[_levelBlockProperties[block].walls[_sceneDrawVarDown]]; + if (!(f & 0x0b)) + return 1; + } + int d = _dropItemDirIndex[(_currentDirection << 2) + button->arg]; + + if (_itemInHand) { + setItemPosition((Item*)&_levelBlockProperties[block & 0x3ff].drawObjects, block, _itemInHand, d); + setHandItem(0); + runLevelScript(block, 4); + } else { + d = getQueuedItem((Item*)&_levelBlockProperties[block].drawObjects, d, -1); + if (!d) + return 1; + setHandItem(d); + runLevelScript(block, 8); + } + + _sceneUpdateRequired = true; + return 1; +} + +int EobCoreEngine::clickedCharPortrait2(Button *button) { + if (!_gui->_progress) { + if (!testCharacter(button->arg, 1)) + return button->index; + } + + _currentControlMode = 1; + if (!_gui->_progress) + _updateCharNum = button->arg; + + _screen->copyRegion(176, 0, 0, 0, 144, 168, 0, 5, Screen::CR_NO_P_CHECK); + gui_drawCharPortraitWithStats(_updateCharNum); + gui_setInventoryButtons(); + + return button->index; +} + +int EobCoreEngine::clickedWeaponSlot(Button *button) { + if (!testCharacter(button->arg, 1)) + return 1; + + static const uint8 sY[] = { 24, 24, 80, 80, 136, 136 }; + int slot = sY[button->arg] > _mouseY ? 0 : 1; + + if ((_gui->_flagsMouseLeft & 0x7f) == 1) + gui_processWeaponSlotClickLeft(button->arg, slot); + else + gui_processWeaponSlotClickRight(button->arg, slot); + + return 1; +} + +int EobCoreEngine::clickedCharNameLabelRight(Button *button) { + if (!testCharacter(button->arg, 1)) + return button->index; + + if (_updateFlags) { + Button b; + clickedSpellbookAbort(&b); + } + + if (_exchangeCharacterId == -1) { + _exchangeCharacterId = button->arg; + gui_setSwapCharacterButtons(); + gui_drawCharPortraitWithStats(_exchangeCharacterId); + enableTimer(0); + } else { + int d = _exchangeCharacterId; + _exchangeCharacterId = -1; + + EobCharacter temp; + memcpy(&temp, &_characters[d], sizeof(EobCharacter)); + memcpy(&_characters[d], &_characters[button->arg], sizeof(EobCharacter)); + memcpy(&_characters[button->arg], &temp, sizeof(EobCharacter)); + + _timer->disable(0); + gui_drawCharPortraitWithStats(d); + gui_processCharPortraitClick(button->arg); + gui_drawCharPortraitWithStats(button->arg); + gui_setPlayFieldButtons(); + setupCharacterTimers(); + } + + return button->index; +} + +int EobCoreEngine::clickedInventorySlot(Button *button) { + gui_processInventorySlotClick(button->arg); + return button->index; +} + +int EobCoreEngine::clickedEatItem(Button *button) { + eatItemInHand(_updateCharNum); + return button->index; +} + +int EobCoreEngine::clickedInventoryPrevChar(Button *button) { + if (_gui->_progress == 1) + _updateCharNum = 0; + else if (_gui->_progress == 2) + _updateCharNum = 1; + else + _updateCharNum = getNextValidCharIndex(_updateCharNum, -1); + + gui_drawCharPortraitWithStats(_updateCharNum); + return button->index; +} + +int EobCoreEngine::clickedInventoryNextChar(Button *button) { + int oldVal = _updateCharNum; + int v = button->arg == 2 ? 2 : 0; + + if (_gui->_progress == 1) + _updateCharNum = v + 2; + else if (_gui->_progress == 2) + _updateCharNum = v + 3; + else + _updateCharNum = getNextValidCharIndex(_updateCharNum, 1); + + if (!testCharacter(_updateCharNum, 1)) { + _updateCharNum = oldVal; + return 1; + } + + gui_drawCharPortraitWithStats(_updateCharNum); + return button->index; +} + +int EobCoreEngine::clickedSpellbookTab(Button *button) { + _openBookSpellLevel = button->arg; + _openBookSpellListOffset = 0; + + for (_openBookSpellSelectedItem = 0; _openBookSpellSelectedItem < 6; _openBookSpellSelectedItem++) { + if (_openBookAvailableSpells[_openBookSpellLevel * 10 + _openBookSpellSelectedItem] > 0) + break; + } + + gui_drawSpellbook(); + + _characters[_openBookChar].slotStatus[3] = _openBookSpellLevel; + _characters[_openBookChar].slotStatus[2] = _openBookSpellSelectedItem; + _characters[_openBookChar].slotStatus[4] = _openBookSpellListOffset; + + return button->index; +} + +int EobCoreEngine::clickedSpellbookList(Button *button) { + int listIndex = button->arg; + bool spellLevelAvailable = false; + + if (listIndex == 6) { + for (int i = 0; i < 10; i++) { + if (_openBookAvailableSpells[_openBookSpellLevel * 10 + i] > 0) { + spellLevelAvailable = true; + break; + } + } + if (!spellLevelAvailable) + return button->index; + + int v = (_gui->_progress == 1) ? -1 : ((_gui->_progress == 2) ? 1 : 0); + + _openBookSpellSelectedItem += _openBookSpellListOffset; + if (_openBookSpellSelectedItem == 12 || (_openBookSpellSelectedItem == 6 && _openBookSpellListOffset == 0)) + _openBookSpellSelectedItem = 9; + + do { + _openBookSpellSelectedItem += v; + int s = (_openBookSpellSelectedItem >= 0) ? _openBookSpellSelectedItem : 9; + _openBookSpellSelectedItem = (s <= 9) ? s : 0; + } while (_openBookAvailableSpells[_openBookSpellLevel * 10 + _openBookSpellSelectedItem] <= 0 && _openBookSpellSelectedItem != 9); + + if (_openBookSpellSelectedItem >= 6) { + _openBookSpellListOffset = 6; + if (_openBookSpellSelectedItem == 9) + _openBookSpellSelectedItem = 6; + else + _openBookSpellSelectedItem -= 6; + } else { + _openBookSpellListOffset = 0; + } + + if (_openBookSpellListOffset == 6 && _openBookAvailableSpells[_openBookSpellLevel * 10 + 6] <= 0) + _openBookSpellListOffset = 0; + + gui_drawSpellbook(); + + } else { + if (listIndex == 7 || _openBookAvailableSpells[_openBookSpellLevel * 10 + _openBookSpellListOffset + listIndex] > 0) { + if (listIndex < 6) { + if (_openBookSpellListOffset + listIndex < 9) + _openBookSpellSelectedItem= listIndex; + else if (listIndex != 7) + return button->index; + } else if (listIndex != 7) { + return button->index; + } + + if (_openBookSpellSelectedItem < 6 && ((_openBookSpellSelectedItem + _openBookSpellListOffset) < 9)) { + if (_characters[_openBookChar].disabledSlots & 4) + return button->index; + + gui_drawSpellbook(); + + int s = _openBookAvailableSpells[_openBookSpellLevel * 10 + _openBookSpellListOffset + _openBookSpellSelectedItem]; + if (_openBookType == 1) + s += _mageSpellListSize; + + castSpell(s, 0); + + } else if ((_openBookSpellSelectedItem == 6 && listIndex == 7) || (_openBookSpellSelectedItem != 6 && listIndex == 6) ) { + Button b; + clickedSpellbookAbort(&b); + } + } + } + + _characters[_openBookChar].slotStatus[2] = _openBookSpellSelectedItem; + _characters[_openBookChar].slotStatus[4] = _openBookSpellListOffset; + return button->index; +} + +int EobCoreEngine::clickedCastSpellOnCharacter(Button *button) { + _activeSpellCaster = button->arg; + + if (_activeSpellCaster == 255) { + _txt->printMessage(_magicStrings3[1]); + snd_playSoundEffect(79); + if (_castScrollSlot) { + gui_updateSlotAfterScrollUse(); + } else { + gui_toggleButtons(); + gui_drawSpellbook(); + } + } else { + if (_characters[_activeSpellCaster].flags & 1) + startSpell(_activeSpell); + } + + return button->index; +} + +int EobCoreEngine::clickedInventoryNextPage(Button *button) { + if (_currentControlMode == 2) { + gui_setInventoryButtons(); + _currentControlMode = 1; + } else { + gui_setStatsListButtons(); + _currentControlMode = 2; + } + + gui_drawCharPortraitWithStats(_updateCharNum); + return button->index; +} + +int EobCoreEngine::clickedPortraitRestore(Button *button) { + _currentControlMode = 0; + _screen->_curPage = 2; + _screen->copyRegion(0, 0, 0, 0, 144, 168, 5, _screen->_curPage, Screen::CR_NO_P_CHECK); + gui_drawAllCharPortraitsWithStats(); + _screen->_curPage = 0; + _screen->copyRegion(0, 0, 176, 0, 144, 168, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + gui_setPlayFieldButtons(); + return button->index; +} + +int EobCoreEngine::clickedUpArrow(Button *button) { + int b = calcNewBlockPositionAndTestPassability(_currentBlock, _currentDirection); + + if (b == -1) { + notifyBlockNotPassable(); + } else { + moveParty(b); + _sceneDefaultUpdate = 1; + } + + return button->index; +} + +int EobCoreEngine::clickedDownArrow(Button *button) { + int b = calcNewBlockPositionAndTestPassability(_currentBlock, (_currentDirection + 2) & 3); + + if (b == -1) { + notifyBlockNotPassable(); + } else { + moveParty(b); + _sceneDefaultUpdate = 1; + } + + return button->index; +} + +int EobCoreEngine::clickedLeftArrow(Button *button) { + int b = calcNewBlockPositionAndTestPassability(_currentBlock, (_currentDirection - 1) & 3); + + if (b == -1) { + notifyBlockNotPassable(); + } else { + moveParty(b); + _sceneDefaultUpdate = 1; + } + + return button->index; +} + +int EobCoreEngine::clickedRightArrow(Button *button) { + int b = calcNewBlockPositionAndTestPassability(_currentBlock, (_currentDirection + 1) & 3); + + if (b == -1) { + notifyBlockNotPassable(); + } else { + moveParty(b); + _sceneDefaultUpdate = 1; + } + + return button->index; +} + +int EobCoreEngine::clickedTurnLeftArrow(Button *button) { + _currentDirection = (_currentDirection - 1) & 3; + //_keybControlUnk = -1; + _sceneDefaultUpdate = 1; + _sceneUpdateRequired = true; + return button->index; +} + +int EobCoreEngine::clickedTurnRightArrow(Button *button) { + _currentDirection = (_currentDirection + 1) & 3; + //_keybControlUnk = -1; + _sceneDefaultUpdate = 1; + _sceneUpdateRequired = true; + return button->index; +} + +int EobCoreEngine::clickedAbortCharSwitch(Button *button) { + _timer->disable(0); + int c = _exchangeCharacterId; + _exchangeCharacterId = -1; + gui_drawCharPortraitWithStats(c); + gui_setPlayFieldButtons(); + return button->index; +} + +int EobCoreEngine::clickedSceneThrowItem(Button *button) { + if (!_itemInHand) + return button->index; + + if (launchObject(_updateCharNum, _itemInHand, _currentBlock, _dropItemDirIndex[(_currentDirection << 2) + button->arg], _currentDirection, _items[_itemInHand].type)) { + setHandItem(0); + _sceneUpdateRequired = true; + } + + return button->index; +} + +int EobCoreEngine::clickedSceneSpecial(Button *button) { + _clickedSpecialFlag = 0x40; + return specialWallAction(calcNewBlockPosition(_currentBlock, _currentDirection), _currentDirection); +} + +int EobCoreEngine::clickedSpellbookAbort(Button *button) { + _updateFlags = 0; + _screen->copyRegion(0, 0, 64, 121, 112, 56, 10, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + gui_drawCompass(true); + gui_toggleButtons(); + return button->index; +} + +int EobCoreEngine::clickedSpellbookScroll(Button *button) { + if (_openBookAvailableSpells[_openBookSpellLevel * 10] > 0) { + _openBookSpellListOffset ^= 6; + _openBookSpellSelectedItem = 0; + } else { + _openBookSpellListOffset = 6; + } + + _characters[_openBookChar].slotStatus[2] = _openBookSpellSelectedItem; + _characters[_openBookChar].slotStatus[4] = _openBookSpellListOffset; + + gui_drawSpellbook(); + + return button->index; +} + +int EobCoreEngine::clickedUnk(Button *button) { + return button->index; +} + +void EobCoreEngine::gui_processCharPortraitClick(int index) { + if (index == _updateCharNum) + return; + + int a = _updateCharNum; + _updateCharNum = index; + + gui_drawCharPortraitWithStats(a); + gui_drawCharPortraitWithStats(index); +} + +void EobCoreEngine::gui_processWeaponSlotClickLeft(int charIndex, int slotIndex) { + int itm = _characters[charIndex].inventory[slotIndex]; + if (_items[itm].flags & 0x20) + return; + + int ih = _itemInHand; + int t = _items[ih].type; + uint16 v = (ih) ? _itemTypes[t].invFlags : 0xffff; + + if (v & _slotValidationFlags[slotIndex]) { + setHandItem(itm); + _characters[charIndex].inventory[slotIndex] = ih; + gui_drawCharPortraitWithStats(charIndex); + } + + recalcArmorClass(charIndex); +} + +void EobCoreEngine::gui_processWeaponSlotClickRight(int charIndex, int slotIndex) { + const char * const *strs = &_itemExtraStrings[_flags.gameID == GI_EOB1 ? 17 : (_flags.lang == Common::DE_DEU ? 26 : 22)]; + + if (!testCharacter(charIndex, 0x0d)) + return; + + uint16 itm = _characters[charIndex].inventory[slotIndex]; + int wslot = slotIndex < 2 ? slotIndex : -1; + + if (slotIndex < 2 && (!validateWeaponSlotItem(charIndex, slotIndex) || (!_currentControlMode && (_characters[charIndex].disabledSlots & (1 << slotIndex))))) + return; + + if (!itemUsableByCharacter(charIndex, itm)) + _txt->printMessage(strs[0], -1, _characters[charIndex].name); + + if (!itm && slotIndex > 1) + return; + + int8 tp = _items[itm].type; + int8 vl = _items[itm].value; + uint8 ep = _itemTypes[tp].extraProperties & 0x7f; + + switch (ep) { + case 0: + case 16: + // Item automatically used when worn + _txt->printMessage(strs[1]); + break; + + case 1: + case 2: + case 3: + // Weapons + if (!_currentControlMode) + useSlotWeapon(charIndex, slotIndex, itm); + break; + + case 4: + case 8: + case 12: + case 13: + case 15: + // Item not used that way + _txt->printMessage(strs[2]); + break; + + case 5: + case 6: + // Cleric holy symbol / mage spell book + if (!_currentControlMode) + useMagicBookOrSymbol(charIndex, ep == 6 ? 1 : 0); + break; + + case 7: + // Food ration + /* don't do anything if mouse control is enabled */ + //eatItemInHand(charIndex); + break; + + case 10: + if (_flags.gameID == GI_EOB1) + vl += _clericSpellOffset; + case 9: + // Mage/Cleric Scroll + if (!_currentControlMode) + useMagicScroll(charIndex, vl, wslot); + break; + + case 11: + // Letters, Notes, Maps + displayParchment(vl); + break; + + case 14: + // Potion + usePotion(charIndex, wslot); + break; + + case 18: + ep = ep; + break; + + case 19: + // eob2 horn + ep = ep; + break; + + case 20: + if (vl == 1) + inflictCharacterDamage(charIndex, 200); + else + useMagicScroll(charIndex, 55, wslot); + deleteInventoryItem(charIndex, wslot); + break; + + default: + break; + } + + if (ep == 1 && charIndex >= 2) + return; + + _lastUsedItem = itm; + runLevelScript(calcNewBlockPosition(_currentBlock, _currentDirection), 0x100); + _lastUsedItem = 0; +} + +void EobCoreEngine::gui_processInventorySlotClick(int slot) { + int itm = _characters[_updateCharNum].inventory[slot]; + int ih = _itemInHand; + if (!validateInventorySlotForItem(ih, _updateCharNum, slot)) + return; + + if (slot == 16) { + if (ih) { + setItemPosition(&_characters[_updateCharNum].inventory[16], -2, ih, 0); + gui_drawInventoryItem(slot, 1, 0); + setHandItem(0); + + } else { + itm = getQueuedItem(&_characters[_updateCharNum].inventory[16], 0, -1); + gui_drawInventoryItem(slot, 1, 0); + setHandItem(itm); + } + + } else { + setHandItem(itm); + _characters[_updateCharNum].inventory[slot] = ih; + gui_drawInventoryItem(slot, 1, 0); + recalcArmorClass(_updateCharNum); + } +} + +GUI_Eob::GUI_Eob(EobCoreEngine *vm) : GUI(vm), _vm(vm), _screen(vm->_screen) { + _scrollUpFunctor = _scrollDownFunctor = BUTTON_FUNCTOR(GUI_Eob, this, 0); + + _redrawButtonFunctor = BUTTON_FUNCTOR(GUI, this, &GUI::redrawButtonCallback); + _redrawShadedButtonFunctor = BUTTON_FUNCTOR(GUI, this, &GUI::redrawShadedButtonCallback); + + _specialProcessButton = _backupButtonList = 0; + _flagsMouseLeft = _flagsMouseRight = _flagsModifier = 0; + _backupButtonList = 0; + _progress = 0; + _prcButtonUnk3 = 1; + _cflag = 0xffff; + + _menuLineSpacing = 0; + _menuUnk1 = 0; + _menuLastInFlags = 0; + _menuCur = 0; + _menuNumItems = 0; +} + +void GUI_Eob::processButton(Button *button) { + if (!button->data0Val1 && !button->data2Val1 && !button->data1Val1) + return; + + if ((button->flags & 0x18) == 0x18) + return; + + int sd = button->dimTableIndex; + const ScreenDim *dm = _screen->getScreenDim(sd); + + int fx = button->x; + if (fx < 0) + fx += (dm->w << 3); + + int sx = fx + (dm->sx << 3); + + int fy = button->y; + if (fy < 0) + fy += dm->h; + + int sy = fy + dm->sy; + + uint16 fw = button->width; + uint16 fh = button->height; + + uint8 col1 = button->data1Val1; + uint8 col2 = button->data1Val3; + + int fx2 = sx + fw - 1; + int fy2 = sy + fh - 1; + + if (button->flags2 & 1) { + if (button->data1Val1 == 1) { + if (button->data0Val1 == 1) { + _screen->drawShape(_screen->_curPage, button->data1ShapePtr, fx, fy, sd); + } else if (button->data0Val1 == 2) { + if (!(button->flags2 & 4)) + _screen->printText((const char*) button->data1ShapePtr, sx, sy, col1, col2); + } else if (button->data0Val1 == 3) { + // nullsub (at least EOBII) + } else if (button->data0Val1 == 4) { + if (button->data1Callback) + (*button->data1Callback.get())(button); + } + } else if (button->data1Val1 == 2) { + if (!(button->flags2 & 4)) + _screen->drawBox(sx, sy, fx2, fy2, col1); + } else if (button->data1Val1 == 3) { + // nullsub (at least EOBII) + } else if (button->data1Val1 == 4) { + if (button->data1Callback) + (*button->data1Callback.get())(button); + } + } + + if (button->flags2 & 4) { + if (button->data2Val1 == 1) { + if (button->data0Val1 == 1) { + _screen->drawShape(_screen->_curPage, button->data2ShapePtr, fx, fy, sd); + } else if (button->data0Val1 == 2) { + if (button->flags2 & 1) + _screen->printText((const char*) button->data2ShapePtr, sx, sy, button->data3Val2, button->data3Val3); + else + _screen->printText((const char*) button->data2ShapePtr, sx, sy, button->data2Val2, button->data2Val3); + } else if (button->data0Val1 == 3) { + // nullsub (at least EOBII) + } else if (button->data0Val1 == 4) { + if (button->data2Callback) + (*button->data2Callback.get())(button); + } + } else if (button->data2Val1 == 2) { + _screen->drawBox(sx, sy, fx2, fy2, (button->flags2 & 1) ? button->data3Val2 : button->data2Val2); + } else if (button->data2Val1 == 3) { + // nullsub (at least EOBII) + } else if (button->data2Val1 == 4) { + if (button->data2Callback) + (*button->data2Callback.get())(button); + } + } + + if (!(button->flags2 & 5)) { + if (button->data0Val1 == 1) { + _screen->drawShape(_screen->_curPage, button->data0ShapePtr, fx, fy, sd); + } else if (button->data0Val1 == 2) { + _screen->printText((const char*) button->data0ShapePtr, sx, sy, button->data0Val2, button->data0Val3); + } else if (button->data0Val1 == 3) { + // nullsub (at least EOBII) + } else if (button->data0Val1 == 4) { + if (button->data0Callback) + (*button->data0Callback.get())(button); + } else if (button->data0Val1 == 5) { + _screen->drawBox(sx, sy, fx2, fy2, button->data0Val2); + } else { + if (!button->data0Val1) { + if(button->data1Val1 == 2 || button->data2Val1 == 2) { + _screen->drawBox(sx, sy, fx2, fy2, button->data0Val2); + } else { + // nullsub (at least EOBII) + } + } + } + } +} + +int GUI_Eob::processButtonList(Kyra::Button *buttonList, uint16 inputFlags, int8 mouseWheel) { + _progress = 0; + uint16 in = inputFlags & 0xff; + uint16 buttonReleaseFlag = 0; + bool clickEvt = false; + + _flagsMouseLeft = (_vm->_mouseClick == 1) ? 2 : 4; + _flagsMouseRight = (_vm->_mouseClick == 2) ? 2 : 4; + _vm->_mouseClick = 0; + + if (in >= 199 && in <= 202) { + buttonReleaseFlag = (inputFlags & 0x800) ? 3 : 1; + if (in < 201) + _flagsMouseLeft = buttonReleaseFlag; + else + _flagsMouseRight = buttonReleaseFlag; + + //////////////////////////// + if (!buttonList && !(inputFlags & 0x800)) + return inputFlags & 0xff; + //////////////////////////// + + inputFlags = 0; + clickEvt = true; + } else { + inputFlags &= 0xff; + } + + uint16 result = 0; + bool runLoop = true; + + if (!buttonList) + return inputFlags; + + if (_vm->_buttonListChanged || (buttonList != _backupButtonList)) { + _backupButtonList = buttonList; + _flagsModifier = 0; + + while (runLoop) { + processButton(buttonList); + _flagsModifier |= (buttonList->flags & 0xAA04); + + // UNUSED + //if (buttonList->flags2 & 0x20) { + // if (_processButtonListExtraCallback) + // this->*_processButtonListExtraCallback(buttonList); + //} + + if (buttonList->nextButton) + buttonList = buttonList->nextButton; + else + runLoop = false; + } + + _vm->_buttonListChanged = false; + + _specialProcessButton = 0; + _prcButtonUnk3 = 1; + _cflag = 0xffff; + } + + int sd = 0; + const ScreenDim *dm = _screen->getScreenDim(sd); + + int x1 = dm->sx << 3; + int y1 = dm->sy; + int w1 = dm->w << 3; + int h1 = dm->h; + + uint16 v8 = 0; + uint16 v18 = 0; + uint16 v16 = 0; + + if (_specialProcessButton) + buttonList = _specialProcessButton; + + while (runLoop) { + if (buttonList->flags & 8) { + buttonList = buttonList->nextButton; + runLoop = buttonList ? true : false; + continue; + } + + int vc = 0; + int v6 = 0; + uint16 iFlag = buttonList->index | 0x8000; + uint16 flgs2 = buttonList->flags2; + uint16 flgs = buttonList->flags; + + if (flgs2 & 1) + flgs2 |= 8; + else + flgs2 &= 0xfff7; + + if (flgs2 & 4) + flgs2 |= 0x10; + else + flgs2 &= 0xffef; + + uint16 vL = 0; + uint16 vR = 0; + + if (inputFlags) { + if (buttonList->keyCode == in) { + _progress = 1; + _flagsMouseLeft = 1; + flgs2 ^= 1; + result = iFlag; + v6 = 1; + } else if (buttonList->keyCode2 == in) { + _progress = 2; + _flagsMouseRight = 1; + result = iFlag; + v6 = 1; + } + } else if (_flagsModifier || clickEvt) { + vL = flgs & 0xf00; + vR = flgs & 0xf000; + + if (_prcButtonUnk3) { + if (sd != buttonList->dimTableIndex) { + sd = buttonList->dimTableIndex; + dm = _screen->getScreenDim(sd); + x1 = dm->sx << 3; + y1 = dm->sy; + w1 = dm->w << 3; + h1 = dm->h; + } + + int x2 = x1; + if (buttonList->x < 0) + x2 += w1; + x2 += buttonList->x; + + int y2 = y1; + if (buttonList->y < 0) + y2 += h1; + y2 += buttonList->y; + + if (_vm->_mouseX >= x2 && _vm->_mouseX <= (x2 + buttonList->width) && _vm->_mouseY >= y2 && _vm->_mouseY <= (y2 + buttonList->height)) { + flgs2 |= 2; + + if (vL) { + switch (_flagsMouseLeft - 1) { + case 0: + v18 = 1; + + if ((flgs & 4) && buttonList->data2Val1) { + flgs2 |= 4; + vc = 1; + } else { + flgs2 &= 0xfffb; + } + + if (flgs & 0x100) { + v6 = 1; + if (!(flgs & 1)) { + flgs2 ^= 1; + result = iFlag; + } + } + + if (flgs & 0x40) { + _specialProcessButton = buttonList; + v8 = 1; + } + + _cflag = flgs; + break; + + case 1: + if (flgs != _cflag) + break; + + if ((flgs & 4) && buttonList->data2Val1) { + flgs2 |= 4; + vc = 1; + } else { + flgs2 &= 0xfffb; + } + + if (!(flgs & 0x200)) + break; + + v6 = 1; + + if (flgs & 1) + break; + + flgs2 |= 1; + result = iFlag; + break; + + case 2: + if (_cflag != flgs) + break; + + if (flgs & 0x400) { + v6 = 1; + if (flgs & 1) { + flgs2 ^= 1; + result = iFlag; + } + } + + if ((flgs & 2) && (flgs2 & 1)) + flgs2 &= 0xfffe; + break; + + case 3: + if ((flgs & 4) || (!buttonList->data2Val1)) + flgs2 &= 0xfffb; + else + flgs2 |= 4; + + if (flgs & 0x800) { + v6 = 1; + break; + } + + if ((flgs & 2) && (flgs2 & 1)) + flgs2 &= 0xfffe; + break; + + default: + break; + } + } + + if (vR && !v6 && !vc) { + switch (_flagsMouseRight - 1) { + case 0: + v18 = 1; + + if ((flgs & 4) && buttonList->data2Val1) + flgs2 |= 4; + else + flgs2 &= 0xfffb; + + if (flgs & 0x1000) { + v6 = 1; + if (!(flgs & 1)) { + flgs2 ^= 1; + result = iFlag; + } + } + + if (flgs & 0x40) { + _specialProcessButton = buttonList; + v8 = 1; + } + + _cflag = flgs; + break; + + case 1: + if (flgs != _cflag) + break; + + if ((flgs & 4) && buttonList->data2Val1) + flgs2 |= 4; + else + flgs2 &= 0xfffb; + + if (!(flgs & 0x2000)) + break; + + v6 = 1; + + if (flgs & 1) + break; + + flgs2 |= 1; + result = iFlag; + break; + case 2: + if (_cflag != flgs) + break; + + if (flgs & 0x4000) { + v6 = 1; + if (flgs & 1) { + flgs2 ^= 1; + result = iFlag; + } + } + + if ((flgs & 2) && (flgs2 & 1)) + flgs2 &= 0xfffe; + break; + + case 3: + if ((flgs & 4) || (!buttonList->data2Val1)) + flgs2 &= 0xfffb; + else + flgs2 |= 4; + + if (flgs & 0x8000) { + v6 = 1; + break; + } + + if ((flgs & 2) && (flgs2 & 1)) + flgs2 &= 0xfffe; + break; + + default: + break; + } + } + } else { // if (_vm->_mouseX >= x2 && _vm->_mouseX <= (x2 + buttonList->width)....) + flgs2 &= 0xfff9; + + if ((flgs & 0x40) && (!(flgs & 0x80)) && _specialProcessButton && !v8) { + static const uint16 flagsTable[] = { 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000 }; + + if (vL) { + v16 = flagsTable[_flagsMouseLeft - 1]; + if (v16 & flgs) + v6 = 1; + } + + if (vR && !v6) { + v16 = flagsTable[_flagsMouseRight + 3]; + if (v16 & flgs) + v6 = 1; + } + + if (!v6) { + _specialProcessButton = 0; + _prcButtonUnk3 = 1; + } + } + + if ((flgs & 2) && (flgs2 & 1)) + flgs2 &= 0xfffe; + } // end if (_vm->_mouseX >= x2 && _vm->_mouseX <= (x2 + buttonList->width)....) + } // end if (_prcButtonUnk3) + } // end if (_flagsModifier || clickEvt) + + buttonList->flags = flgs; + buttonList->flags2 = flgs2; + + bool f21 = (flgs2 & 8) ? true : false; + bool f22 = (flgs2 & 1) ? true : false; + bool f23 = (flgs2 & 0x10) ? true : false; + bool f24 = (flgs2 & 4) ? true : false; + + if (f21 != f22 || f23 != f24) + processButton(buttonList); + + if (v6 && buttonList->buttonCallback) + runLoop = ((*buttonList->buttonCallback.get())(buttonList)) ? false : true; + + if ((flgs2 & 2) && (flgs & 0x20)) + runLoop = false; + + if (_specialProcessButton && ((vL && _flagsMouseLeft == 3) || (vR && _flagsMouseRight == 3))) { + _specialProcessButton = 0; + _prcButtonUnk3 = 1; + runLoop = false; + } + + if (_specialProcessButton && !v8) + runLoop = false; + + buttonList = buttonList->nextButton; + if (!buttonList) + runLoop = false; + }; + + if ((_flagsMouseLeft == 1 || _flagsMouseRight == 1) && !v18) + _cflag = 0xffff; + + if (!result) + result = inputFlags; + + return result; +} + +void GUI_Eob::setupMenu(int sd, int maxItem, const char *const *strings, int32 menuItemsMask, int unk, int lineSpacing) { + initMenuItemsMask(sd, maxItem, menuItemsMask, unk); + + const ScreenDim *dm = _screen->getScreenDim(19 + sd); + int x = (_screen->_curDim->sx + dm->sx) << 3; + int y = _screen->_curDim->sy + dm->sy; + + int v = getMenuItem(_menuCur, menuItemsMask, unk); + + for (int i = 0; i < _menuNumItems; i++) { + int item = getMenuItem(i, menuItemsMask, unk); + int ty = y + i * (lineSpacing + _screen->getFontHeight()); + _screen->printShadedText(strings[item], x, ty, dm->unkA, 0); + if (item == v) + _screen->printText(strings[item], x, ty, dm->unkC, 0); + } + + _screen->updateScreen(); + _menuLineSpacing = lineSpacing; + _menuUnk1 = 0; + _menuLastInFlags = 0; + _vm->removeInputTop(); +} + +int GUI_Eob::handleMenu(int sd, const char *const *strings, void *b, int32 menuItemsMask, int unk) { + const ScreenDim *dm = _screen->getScreenDim(19 + sd); + int h = _menuNumItems - 1; + int currentItem = _menuCur % _menuNumItems; + int newItem = currentItem; + int result = -1; + int lineH = (_menuLineSpacing + _screen->getFontHeight()); + int lineS1 = _menuLineSpacing >> 1; + int x = (_screen->_curDim->sx + dm->sx) << 3; + int y = _screen->_curDim->sy + dm->sy; + + int inFlag = _vm->checkInput(0, false, 0) & 0x8ff; + _vm->removeInputTop(); + Common::Point mousePos = _vm->getMousePos(); + + int x1 = (_screen->_curDim->sx << 3) + (dm->sx * _screen->getFontWidth()); + int y1 = _screen->_curDim->sy + dm->sy - lineS1; + int x2 = x1 + (dm->w * _screen->getFontWidth()) - 1; + int y2 = y1 + _menuNumItems * lineH - 1; + if (_vm->posWithinRect(mousePos.x, mousePos.y, x1, y1, x2, y2)) + newItem = (mousePos.y - y1) / lineH; + + if (inFlag == 199 || inFlag == 201) { + if (_vm->posWithinRect(_vm->_mouseX, _vm->_mouseY, x1, y1, x2, y2)) + result = newItem = (_vm->_mouseY - y1) / lineH; + } else if (inFlag == _vm->_keyMap[Common::KEYCODE_RETURN] || inFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inFlag == _vm->_keyMap[Common::KEYCODE_KP5]) { + result = newItem; + } else if (inFlag == _vm->_keyMap[Common::KEYCODE_HOME] || inFlag == _vm->_keyMap[Common::KEYCODE_KP7] || inFlag == _vm->_keyMap[Common::KEYCODE_PAGEUP] || inFlag == _vm->_keyMap[Common::KEYCODE_KP9]) { + newItem = 0; + } else if (inFlag == _vm->_keyMap[Common::KEYCODE_END] || inFlag == _vm->_keyMap[Common::KEYCODE_KP1] || inFlag == _vm->_keyMap[Common::KEYCODE_PAGEDOWN] || inFlag == _vm->_keyMap[Common::KEYCODE_KP3]) { + newItem = h; + } else if (inFlag == _vm->_keyMap[Common::KEYCODE_UP] || inFlag == _vm->_keyMap[Common::KEYCODE_KP8]) { + if (--newItem < 0) + newItem = h; + } else if (inFlag == _vm->_keyMap[Common::KEYCODE_DOWN] || inFlag == _vm->_keyMap[Common::KEYCODE_KP2]) { + if (++newItem > h) + newItem = 0; + } else { + _menuLastInFlags = inFlag; + } + + if (newItem != currentItem) { + _screen->printText(strings[getMenuItem(currentItem, menuItemsMask, unk)], x, y + currentItem * lineH , dm->unkA, 0); + _screen->printText(strings[getMenuItem(newItem, menuItemsMask, unk)], x, y + newItem * lineH , dm->unkC, 0); + _screen->updateScreen(); + } + + if (result != -1) { + result = getMenuItem(result, menuItemsMask, unk); + menuFlashSelection(strings[result], x, y + newItem * lineH, dm->unkA, dm->unkC, 0); + } + + _menuCur = newItem; + + return result; +} + +void GUI_Eob::initMenuItemsMask(int menuId, int maxItem, int32 menuItemsMask, int unk) { + if (menuItemsMask == -1) { + _menuNumItems = _screen->getScreenDim(19 + menuId)->h; + _menuCur = _screen->getScreenDim(19 + menuId)->unk8; + return; + } + + _menuNumItems = 0; + + for (int i = 0; i < maxItem; i++) { + if (menuItemsMask & (1 << (i + unk))) + _menuNumItems++; + } + + _menuCur = 0; +} + +int GUI_Eob::getMenuItem(int index, int32 menuItemsMask, int unk) { + if (menuItemsMask == -1) + return index; + + int res = 0; + int i = index; + + for (; i; res++) { + if (menuItemsMask & (1 << (res + unk))) + i--; + } + + while (!(menuItemsMask & (1 << (res + unk)))) + res++; + + return res; +} + +void GUI_Eob::menuFlashSelection(const char *str, int x, int y, int color1, int color2, int color3) { + for (int i = 0; i < 3; i++) { + _screen->printText(str, x, y, color2, color3); + _screen->updateScreen(); + _vm->_system->delayMillis(32); + _screen->printText(str, x, y, color1, color3); + _screen->updateScreen(); + _vm->_system->delayMillis(32); + } +} + +int GUI_Eob::getTextInput(char *dest, int x, int y, int destMaxLen, int textColor1, int textColor2, int cursorColor) { + uint8 cursorState = 1; + char sufx[] = " "; + + int len = strlen(dest); + if (len > destMaxLen) { + len = destMaxLen; + dest[destMaxLen] = 0; + } + + int pos = len; + if (len >= destMaxLen) + pos--; + + _screen->copyRegion((x - 1) << 3, y, 0, 191, (destMaxLen + 2) << 3, 9, 0, 2, Screen::CR_NO_P_CHECK); + _screen->printShadedText(dest, x << 3, y, textColor1, textColor2); + + uint32 next = _vm->_system->getMillis() + 2 * _vm->_tickLength; + sufx[0] = (pos < len) ? dest[pos] : 32; + _screen->printText(sufx, (x + pos) << 3, y, textColor1, cursorColor); + + int in = 0; + + do { + in = 0; + _keyPressed.reset(); + + while (!in) { + if (next <= _vm->_system->getMillis()) { + if (cursorState) { + _screen->copyRegion((pos + 1) << 3, 191, (x + pos) << 3, y, 8, 9, 2, 0, Screen::CR_NO_P_CHECK); + _screen->printShadedText(sufx, (x + pos) << 3, y, textColor1, textColor2); + } else { + _screen->printText(sufx, (x + pos) << 3, y, textColor1, cursorColor); + } + + _screen->updateScreen(); + cursorState ^= 1; + next = _vm->_system->getMillis() + 2 * _vm->_tickLength; + } + + _vm->updateInput(); + for (Common::List<KyraEngine_v1::Event>::const_iterator evt = _vm->_eventList.begin(); evt != _vm->_eventList.end(); ++evt) { + if (evt->event.type == Common::EVENT_KEYDOWN) { + _keyPressed = evt->event.kbd; + in = _keyPressed.ascii; + } + } + _vm->removeInputTop(); + } + + if (_keyPressed.keycode == Common::KEYCODE_BACKSPACE) { + if (pos >= len && len > 0) { + dest[--len] = 0; + pos--; + + } else if (pos > 0) { + for (int i = pos; i < destMaxLen; i++) + dest[i - 1] = dest[i]; + dest[--len] = 0; + pos--; + } + + } else if (_keyPressed.keycode == Common::KEYCODE_LEFT || _keyPressed.keycode == Common::KEYCODE_KP4) { + if (pos > 0) + pos--; + + } else if (_keyPressed.keycode == Common::KEYCODE_RIGHT || _keyPressed.keycode == Common::KEYCODE_KP6) { + if (pos < len && pos < (destMaxLen - 1)) + pos++; + + } else if (in > 31 && in < 126) { + if (!(in == 32 && pos == 0)) { + if (in >= 97 && in <= 122) + in -=32; + + if (pos < len) { + for (int i = destMaxLen - 1; i >= pos; i--) + dest[i + 1] = dest[i]; + + dest[pos++] = in; + + if (len == destMaxLen) + dest[len] = 0; + + } else { + if (pos == destMaxLen) { + pos--; + len--; + } + + dest[pos++] = in; + dest[pos] = 0; + } + + if (++len > destMaxLen) + len = destMaxLen; + + if (pos > (destMaxLen - 1)) + pos = (destMaxLen - 1); + } + } + + _screen->copyRegion(0, 191, (x - 1) << 3, y, (destMaxLen + 2) << 3, 9, 2, 0, Screen::CR_NO_P_CHECK); + _screen->printShadedText(dest, x << 3, y, textColor1, textColor2); + sufx[0] = (pos < len) ? dest[pos] : 32; + + if (cursorState) + _screen->printText(sufx, (x + pos) << 3, y, textColor1, cursorColor); + else + _screen->printShadedText(sufx, (x + pos) << 3, y, textColor1, textColor2); + _screen->updateScreen(); + + } while (_keyPressed.keycode != Common::KEYCODE_RETURN && _keyPressed.keycode != Common::KEYCODE_ESCAPE); + + return _keyPressed.keycode == Common::KEYCODE_ESCAPE ? -1 : len; +} + +#endif // ENABLE_EOB + +} // End of namespace Kyra + +#endif // defined(ENABLE_EOB) || defined(ENABLE_LOL) + diff --git a/engines/kyra/gui_eob.h b/engines/kyra/gui_eob.h new file mode 100644 index 0000000000..82e9476fe0 --- /dev/null +++ b/engines/kyra/gui_eob.h @@ -0,0 +1,130 @@ +/* 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. + * + */ + +#if defined(ENABLE_EOB) || defined(ENABLE_LOL) + +#ifndef KYRA_GUI_EOB_H +#define KYRA_GUI_EOB_H + +#include "kyra/gui.h" + +#ifdef ENABLE_EOB + +namespace Kyra { + +class DarkMoonEngine; +class Screen_Eob; + +class GUI_Eob : public GUI { + friend class EobCoreEngine; + friend class CharacterGenerator; +public: + GUI_Eob(EobCoreEngine *vm); + + void initStaticData() {} + + // button specific + void processButton(Button *button); + int processButtonList(Button *buttonList, uint16 inputFlags, int8 mouseWheel); + + int redrawShadedButtonCallback(Button *button) { return 0; } + int redrawButtonCallback(Button *button) { return 0; } + + void setupMenu(int sd, int maxItem, const char *const *strings, int32 menuItemsMask, int unk, int lineSpacing); + int handleMenu(int sd, const char *const *strings, void *b, int32 menuItemsMask, int unk); + int getMenuItem(int index, int32 menuItemsMask, int unk); + void menuFlashSelection(const char *str, int x, int y, int color1, int color2, int color3); + + int getTextInput(char *dest, int x, int y, int destMaxLen, int textColor1, int textColor2, int cursorColor); + + //int runMenu(Menu &menu); + + // utilities for thumbnail creation + void createScreenThumbnail(Graphics::Surface &dst) {} + +private: + void initMenuItemsMask(int menuId, int maxItem, int32 menuItemsMask, int unk); + + //void backupPage0(); + //void restorePage0(); + + //void setupSavegameNames(Menu &menu, int num); + //void printMenuText(const char *str, int x, int y, uint8 c0, uint8 c1, uint8 flags); + + //int getMenuCenterStringX(const char *str, int x1, int x2); + + int getInput(); + + Button *getButtonListData() { return _menuButtons; } + Button *getScrollUpButton() { return &_scrollUpButton; } + Button *getScrollDownButton() { return &_scrollDownButton; } + + Button::Callback getScrollUpButtonHandler() const { return _scrollUpFunctor; } + Button::Callback getScrollDownButtonHandler() const { return _scrollDownFunctor; } + + uint8 defaultColor1() const { return 0xFE; } + uint8 defaultColor2() const { return 0x00; } + + const char *getMenuTitle(const Menu &menu) { return 0; } + const char *getMenuItemTitle(const MenuItem &menuItem) { return 0; } + const char *getMenuItemLabel(const MenuItem &menuItem) { return 0; } + + Button _menuButtons[10]; + Button _scrollUpButton; + Button _scrollDownButton; + //Menu _mainMenu, _gameOptions, _audioOptions, _choiceMenu, _loadMenu, _saveMenu, _deleteMenu, _savenameMenu, _deathMenu; + //Menu *_currentMenu, *_lastMenu, *_newMenu; + //int _menuResult; + //char *_saveDescription; + + EobCoreEngine *_vm; + Screen_Eob *_screen; + + bool _pressFlag; + + Button *_specialProcessButton; + Button *_backupButtonList; + uint16 _flagsMouseLeft; + uint16 _flagsMouseRight; + uint16 _flagsModifier; + uint16 _progress; + uint16 _prcButtonUnk3; /// ALWAYS 1?? REMOVE ?? + uint16 _cflag; + + Button::Callback _scrollUpFunctor; + Button::Callback _scrollDownFunctor; + + int _menuLineSpacing; + int _menuUnk1; + int _menuLastInFlags; + + int _menuCur; + int _menuNumItems; +}; + +} // End of namespace Kyra + +#endif // ENABLE_EOB + +#endif + +#endif // ENABLE_EOB || ENABLE_LOL diff --git a/engines/kyra/gui_lol.cpp b/engines/kyra/gui_lol.cpp index 5bef3cd5b5..08ce0bdadd 100644 --- a/engines/kyra/gui_lol.cpp +++ b/engines/kyra/gui_lol.cpp @@ -32,6 +32,7 @@ #include "common/savefile.h" #include "common/system.h" #include "common/config-manager.h" + #include "graphics/scaler.h" #include "base/version.h" @@ -213,9 +214,9 @@ void LoLEngine::gui_displayCharInventory(int charNum) { } if (_flags.use16ColorMode) - gui_drawBarGraph(154, 66 + i * 8, 34, 5, b, e, 0x88, 0); + gui_drawHorizontalBarGraph(154, 66 + i * 8, 34, 5, b, e, 0x88, 0); else - gui_drawBarGraph(154, 64 + i * 10, 34, 5, b, e, 132, 0); + gui_drawHorizontalBarGraph(154, 64 + i * 10, 34, 5, b, e, 132, 0); } _screen->drawClippedLine(14, 120, 194, 120, 1); @@ -343,31 +344,6 @@ void LoLEngine::gui_drawCharInventoryItem(int itemIndex) { _screen->drawShape(_screen->_curPage, getItemIconShapePtr(i), x + 1, y + 1, 0, 0); } -void LoLEngine::gui_drawBarGraph(int x, int y, int w, int h, int32 cur, int32 max, int col1, int col2) { - if (max < 1) - return; - if (cur < 0) - cur = 0; - - int32 e = MIN(cur, max); - - if (!--w) - return; - if (!--h) - return; - - int32 t = (e * w) / max; - - if (!t && e) - t++; - - if (t) - _screen->fillRect(x, y, x + t - 1, y + h, col1); - - if (t < w && col2) - _screen->fillRect(x + t, y, x + w, y + h, col2); -} - void LoLEngine::gui_drawAllCharPortraitsWithStats() { int numChars = countActiveCharacters(); if (!numChars) @@ -460,17 +436,6 @@ void LoLEngine::gui_drawCharPortraitWithStats(int charNum) { _screen->setFont(tmpFid); } -void LoLEngine::gui_drawBox(int x, int y, int w, int h, int frameColor1, int frameColor2, int fillColor) { - w--; h--; - if (fillColor != -1) - _screen->fillRect(x + 1, y + 1, x + w - 1, y + h - 1, fillColor); - - _screen->drawClippedLine(x + 1, y, x + w, y, frameColor2); - _screen->drawClippedLine(x + w, y, x + w, y + h - 1, frameColor2); - _screen->drawClippedLine(x, y, x, y + h, frameColor1); - _screen->drawClippedLine(x, y + h, x + w, y + h, frameColor1); -} - void LoLEngine::gui_drawCharFaceShape(int charNum, int x, int y, int pageNum) { if (_characters[charNum].curFaceFrame < 7 && _characters[charNum].tempFaceFrame) _characters[charNum].curFaceFrame = _characters[charNum].tempFaceFrame; @@ -853,19 +818,6 @@ void LoLEngine::gui_triggerEvent(int eventType) { _preserveEvents = true; } -void LoLEngine::removeInputTop() { - if (!_eventList.empty()) { - if (_eventList.begin()->event.type == Common::EVENT_LBUTTONDOWN) - _gui->_mouseClick = 1; - else if (_eventList.begin()->event.type == Common::EVENT_RBUTTONDOWN) - _gui->_mouseClick = 2; - else - _gui->_mouseClick = 0; - - _eventList.erase(_eventList.begin()); - } -} - void LoLEngine::gui_enableDefaultPlayfieldButtons() { gui_resetButtonList(); gui_initButtonsFromList(_buttonList1); @@ -911,19 +863,6 @@ void LoLEngine::gui_enableCharInventoryButtons(int charNum) { gui_setFaceFramesControlButtons(21, 0); } -void LoLEngine::gui_resetButtonList() { - for (uint i = 0; i < ARRAYSIZE(_activeButtonData); ++i) - _activeButtonData[i].nextButton = 0; - - gui_notifyButtonListChanged(); - _activeButtons = 0; -} - -void LoLEngine::gui_initButtonsFromList(const int16 *list) { - while (*list != -1) - gui_initButton(*list++); -} - void LoLEngine::gui_setFaceFramesControlButtons(int index, int xOffs) { int c = countActiveCharacters(); for (int i = 0; i < c; i++) @@ -1009,14 +948,6 @@ void LoLEngine::gui_initButton(int index, int x, int y, int val) { b->buttonCallback = _buttonCallbacks[index]; } -void LoLEngine::gui_notifyButtonListChanged() { - if (_gui) { - if (!_gui->_buttonListChanged && !_preserveEvents) - removeInputTop(); - _gui->_buttonListChanged = true; - } -} - int LoLEngine::clickedUpArrow(Button *button) { if (button->arg && !_floatingCursorsEnabled) return 0; @@ -1333,7 +1264,6 @@ int LoLEngine::clickedExitCharInventory(Button *button) { int LoLEngine::clickedSceneDropItem(Button *button) { static const uint8 offsX[] = { 0x40, 0xC0, 0x40, 0xC0 }; static const uint8 offsY[] = { 0x40, 0x40, 0xC0, 0xC0 }; - static const uint8 dirIndex[] = { 0, 1, 2, 3, 1, 3, 0, 2, 3, 2, 1, 0, 2, 0, 3, 1 }; if ((_updateFlags & 1) || !_itemInHand) return 0; @@ -1348,7 +1278,7 @@ int LoLEngine::clickedSceneDropItem(Button *button) { uint16 x = 0; uint16 y = 0; - int i = dirIndex[(_currentDirection << 2) + button->arg]; + int i = _dropItemDirIndex[(_currentDirection << 2) + button->arg]; calcCoordinates(x, y, block, offsX[i], offsY[i]); setItemPosition(_itemInHand, x, y, 0, 1); @@ -1917,9 +1847,7 @@ GUI_LoL::GUI_LoL(LoLEngine *vm) : GUI(vm), _vm(vm), _screen(vm->_screen) { _specialProcessButton = _backUpButtonList = 0; _flagsModifier = 0; - _mouseClick = 0; _sliderSfx = 11; - _buttonListChanged = false; _savegameList = 0; _savegameListSize = 0; } @@ -2015,18 +1943,18 @@ int GUI_LoL::processButtonList(Button *buttonList, uint16 inputFlag, int8 mouseW if (!buttonList) return inputFlag & 0x7FFF; - if (_backUpButtonList != buttonList || _buttonListChanged) { + if (_backUpButtonList != buttonList || _vm->_buttonListChanged) { _specialProcessButton = 0; _flagsModifier = 0; - if (_mouseClick == 1) + if (_vm->_mouseClick == 1) _flagsModifier |= 0x200; - if (_mouseClick == 2) + if (_vm->_mouseClick == 2) _flagsModifier |= 0x2000; - _mouseClick = 0; + _vm->_mouseClick = 0; _backUpButtonList = buttonList; - _buttonListChanged = false; + _vm->_buttonListChanged = false; while (buttonList) { processButton(buttonList); diff --git a/engines/kyra/gui_lol.h b/engines/kyra/gui_lol.h index af487402f6..9c0c4e2bfe 100644 --- a/engines/kyra/gui_lol.h +++ b/engines/kyra/gui_lol.h @@ -162,9 +162,7 @@ private: Button *_specialProcessButton; Button *_backUpButtonList; - bool _buttonListChanged; uint16 _flagsModifier; - uint8 _mouseClick; int _savegameOffset; int _sliderSfx; diff --git a/engines/kyra/items_eob.cpp b/engines/kyra/items_eob.cpp new file mode 100644 index 0000000000..4f6982abff --- /dev/null +++ b/engines/kyra/items_eob.cpp @@ -0,0 +1,614 @@ +/* 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. + * + */ + +#ifdef ENABLE_EOB + +#include "kyra/eobcommon.h" +#include "kyra/resource.h" +#include "kyra/sound.h" + +namespace Kyra { + +void EobCoreEngine::loadItemDefs() { + Common::SeekableReadStream *s = _res->createReadStream("item.dat"); + _numItems = s->readUint16LE(); + + for (int i = 0; i < 600; i++) + _items[i].block = -1; + + for (int i = 0; i < _numItems; i++) { + _items[i].nameUnid = s->readByte(); + _items[i].nameId = s->readByte(); + _items[i].flags = s->readByte(); + _items[i].icon = s->readSByte(); + _items[i].type = s->readSByte(); + _items[i].pos = s->readSByte(); + _items[i].block = s->readSint16LE(); + _items[i].next = s->readSint16LE(); + _items[i].prev = s->readSint16LE(); + _items[i].level = s->readSByte(); + _items[i].value = s->readSByte(); + } + + _numItemNames = s->readUint16LE(); + for (int i = 0; i < _numItemNames; i++) + s->read(_itemNames[i], 35); + + delete s; + + s = _res->createReadStream("itemtype.dat"); + uint16 numTypes = s->readUint16LE(); + + _itemTypes = new EobItemType[numTypes]; + memset(_itemTypes, 0, sizeof(EobItemType) * numTypes); + + for (int i = 0; i < numTypes; i++) { + _itemTypes[i].invFlags = s->readUint16LE(); + _itemTypes[i].handFlags = s->readUint16LE(); + _itemTypes[i].armorClass = s->readSByte(); + _itemTypes[i].allowedClasses = s->readSByte(); + _itemTypes[i].requiredHands = s->readSByte(); + _itemTypes[i].dmgNumDiceS = s->readSByte(); + _itemTypes[i].dmgNumPipsS = s->readSByte(); + _itemTypes[i].dmgIncS = s->readSByte(); + _itemTypes[i].dmgNumDiceL = s->readSByte(); + _itemTypes[i].dmgNumPipsL = s->readSByte(); + _itemTypes[i].dmgIncL = s->readSByte(); + _itemTypes[i].unk1 = s->readByte(); + _itemTypes[i].extraProperties = s->readUint16LE(); + } + + delete s; +} + +Kyra::Item EobCoreEngine::duplicateItem(Item itemIndex) { + EobItem *itm = &_items[itemIndex]; + + if (itm->block == -1) + return 0; + + Item i = 1; + bool foundSlot = false; + + for (; i < 600; i++) { + if (_items[i].block == -1) { + foundSlot = true; + break; + } + } + + if (!foundSlot) + return 0; + + memcpy(&_items[i], &_items[itemIndex], sizeof(EobItem)); + return i; +} + +void EobCoreEngine::setItemPosition(Item *itemQueue, int block, Item item, int pos) { + if (!item) + return; + + EobItem *itm = &_items[item]; + itm->pos = pos; + itm->block = block; + itm->level = block < 0 ? 0xff : _currentLevel; + + if (!*itemQueue) { + *itemQueue = itm->next = itm->prev = item; + } else { + EobItem *itmQ = &_items[*itemQueue]; + EobItem *itmQN = &_items[itmQ->next]; + itm->prev = itmQN->prev; + itm->next = itmQ->next; + *itemQueue = itmQN->prev = itmQ->next = item; + } +} + +void EobCoreEngine::createInventoryItem(EobCharacter *c, Item itemIndex, int itemValue, int preferedInventorySlot) { + if (itemIndex <= 0) + return; + + itemIndex = duplicateItem(itemIndex); + _items[itemIndex].flags |= 0x40; + + if (itemValue != -1) + _items[itemIndex].value = itemValue; + + if (itemValue && ((_itemTypes[_items[itemIndex].type].extraProperties & 0x7f) < 4)) + _items[itemIndex].flags |= 0x80; + + if (c->inventory[preferedInventorySlot]) { + for (int i = 2; i < 16; i++) { + if (!c->inventory[i]) { + c->inventory[i] = itemIndex; + return; + } + } + } else { + c->inventory[preferedInventorySlot] = itemIndex; + } +} + +int EobCoreEngine::deleteInventoryItem(int charIndex, int slot) { + int itm = (slot == -1) ? _itemInHand : _characters[charIndex].inventory[slot]; + _items[itm].block = -1; + + if (slot == -1) { + setHandItem(0); + } else { + _characters[charIndex].inventory[slot] = 0; + + if (_currentControlMode == 1) + gui_drawInventoryItem(slot, 1, 0); + + if (_currentControlMode == 0) + gui_drawCharPortraitWithStats(charIndex); + } + + return _items[itm].value; +} + +void EobCoreEngine::deleteBlockItem(uint16 block, int type) { + uint16 itm = _levelBlockProperties[block].drawObjects; + if (!itm) + return; + + for (uint16 i2 = itm, i = 0; itm != i2 || !i; i++ ) { + if (type == _items[itm].type || type == -1) { + _items[itm].block = -1; + _items[itm].level = 0; + uint16 i3 = itm; + itm = _items[itm].prev; + _items[i3].prev = _items[i3].next = 0; + } else { + uint16 i3 = itm; + itm = _items[itm].prev; + _items[i3].prev = _items[i3].next = 0; + setItemPosition((Item*)&_levelBlockProperties[block].drawObjects, block, i3, _items[i3].pos); + } + } +} + +int EobCoreEngine::validateInventorySlotForItem(Item item, int charIndex, int slot) { + if (item < 0) + return 0; + + int offset = (_flags.gameID == GI_EOB1) ? 11 : (_flags.lang == Common::DE_DEU ? 16 : 12); + + if (slot == 17 && item && !itemUsableByCharacter(charIndex, item)) { + _txt->printMessage(_itemExtraStrings[offset], -1, _characters[charIndex].name); + return 0; + } + + int itm = _characters[charIndex].inventory[slot]; + int ex = _itemTypes[_items[itm].type].extraProperties & 0x7f; + + if (slot < 2 && _items[itm].flags & 0x20 && ex > 0 && ex < 4) { + _txt->printMessage(_itemExtraStrings[offset + 1], -1, _characters[charIndex].name); + return 0; + } + + uint16 v = item ? _itemTypes[_items[item].type].invFlags : 0xffff; + if (v & _slotValidationFlags[slot]) + return 1; + + _txt->printMessage(_itemExtraStrings[offset + (_flags.gameID == GI_EOB1 ? 1 : 2)]); + return 0; +} + +void EobCoreEngine::deletePartyItem(Item itemType, int16 itemValue) { + for (int i = 0; i < 6; i++) { + if (!testCharacter(i, 1)) + continue; + + EobCharacter *c = &_characters[i]; + int slot = checkCharacterInventoryForItem(i, itemType, itemValue); + + if (slot == -1) + continue; + + int itm = c->inventory[slot]; + _items[itm].block = -1; + c->inventory[slot] = 0; + + if (_currentControlMode == 0 && slot < 2 && i < 5) + gui_drawWeaponSlot(i, slot); + + if (_currentControlMode == 1 && i == _updateCharNum) + gui_drawInventoryItem(slot, 1, 0); + } +} + +int EobCoreEngine::itemUsableByCharacter(int charIndex, Item item) { + if (!item) + return 1; + + return (_itemTypes[_items[item].type].allowedClasses & _classModifierFlags[_characters[charIndex].cClass]); +} + +int EobCoreEngine::countQueuedItems(Item itemQueue, int16 id, int16 type, int count, int includeFlyingItems) { + uint16 o1 = itemQueue; + uint16 o2 = o1; + + if (!o1) + return 0; + + int res = 0; + + for (bool forceLoop = true; o1 != o2 || forceLoop; o1 = _items[o1].prev) { + EobItem *itm = &_items[o1]; + forceLoop = false; + if (id != -1 || type != -1) { + if ((id != -1 || (id == -1 && type != itm->type)) && (type != -1) || (id != o1)) + continue; + } + + if (!includeFlyingItems) { + if (itm->pos > 3 && itm->pos < 8) + continue; + } + + if (!count) + return o1; + + res++; + } + + return res; +} + +int EobCoreEngine::getQueuedItem(Item *items, int pos, int id) { + Item o1 = *items; + Item o2 = o1; + + if (!o1) + return 0; + + EobItem *itm = &_items[o1]; + + for (bool forceLoop = true; o1 != o2 || forceLoop; o1 = itm->prev) { + itm = &_items[o1]; + forceLoop = false; + if ((id != -1 || (id == -1 && itm->pos != pos)) && id != o1) + continue; + + Item n = itm->next; + Item p = itm->prev; + _items[n].prev = p; + _items[p].next = n; + itm->next = itm->prev = itm->block = 0; + itm->level = 0; + if (o1 == *items) + *items = p; + if (o1 == *items) + *items = 0; + + return o1; + } + + return 0; +} + +void EobCoreEngine::printFullItemName(Item item) { + EobItem *itm = &_items[item]; + const char *nameUnid = _itemNames[itm->nameUnid]; + const char *nameId = _itemNames[itm->nameId]; + uint8 f = _itemTypes[itm->type].extraProperties & 0x7f; + int8 v = itm->value; + + const char *tstr2 = 0; + const char *tstr3 = 0; + int e = 0; + + char tmpString[61]; + + if ((itm->flags & 0x40) && !strlen(nameId)) { + switch (f) { + case 0: + case 1: + case 2: + case 3: + if (v == 0) + strcpy(tmpString, nameUnid); + else if (v < 0) + sprintf(tmpString, _itemExtraStrings[3], v, nameUnid); + else + sprintf(tmpString, _itemExtraStrings[4], v, nameUnid); + break; + + case 9: + tstr2 = _itemExtraStrings[5]; + tstr3 = _spells[v].name; + e = 1; + break; + + case 10: + tstr2 = _itemExtraStrings[6]; + tstr3 = _spells[_flags.gameID == GI_EOB1 ? (_clericSpellOffset + v) : v].name; + e = 1; + break; + + case 14: + tstr2 = _itemExtraStrings[8]; + tstr3 = _itemSuffixStrings[8]; + break; + + case 16: + tstr2 = _itemExtraStrings[7]; + tstr3 = _itemSuffixStrings[v + 6]; + e = 0; + break; + + case 18: + if (v == 5) { + tstr2 = _itemExtraStrings[_flags.lang == Common::EN_ANY ? 9 : 10]; + e = 1; + } else { + tstr2 = _itemExtraStrings[9]; + e = 0; + } + tstr3 = _itemSuffixStrings[v + (_flags.lang == Common::EN_ANY ? 11 : 15)]; + break; + + default: + strcpy(tmpString, nameUnid); + break; + } + + + if (tstr3) { + if (!tstr2) { + sprintf(tmpString, _itemExtraStrings[_flags.lang == Common::EN_ANY ? 10 : 11], tstr3); + } else { + if (e == 1) { + if (tstr2 == _itemExtraStrings[12]) + sprintf(tmpString, _itemExtraStrings[_flags.lang == Common::EN_ANY ? 11 : 14], tstr2, tstr3); + else + sprintf(tmpString, _itemExtraStrings[_flags.gameID == GI_EOB1 ? 10 : (_flags.lang == Common::EN_ANY ? 11 : 13)], tstr2, tstr3); + } else { + sprintf(tmpString, _itemExtraStrings[_flags.gameID == GI_EOB1 ? 10 : (_flags.lang == Common::EN_ANY ? 11 : 15)], tstr2, tstr3); + } + } + } + } else { + strcpy(tmpString, (itm->flags & 0x40) ? nameId : nameUnid); + } + + _txt->printMessage(tmpString); +} + +void EobCoreEngine::identifyQueuedItems(Item itemQueue) { + if (!itemQueue) + return; + + Item first = itemQueue; + do { + _items[itemQueue].flags |= 0x40; + itemQueue = _items[itemQueue].prev; + + } while (first != itemQueue); +} + +void EobCoreEngine::drawItemIconShape(int pageNum, Item itemId, int x, int y) { + int icn = _items[itemId].icon; + bool applyBluePal = ((_partyEffectFlags & 2) && (_items[itemId].flags & 0x80)) ? true : false; + + if (applyBluePal) { + _screen->setFadeTableIndex(3); + _screen->setShapeFadeMode(1, true); + } + + _screen->drawShape(pageNum, _itemIconShapes[icn], x, y, 0); + + if (applyBluePal) { + _screen->setFadeTableIndex(4); + _screen->setShapeFadeMode(1, false); + } +} + +bool EobCoreEngine::isMagicWeapon(Item itemIndex) { + return (_items[itemIndex].type > 10 && _items[itemIndex].type < 18); +} + +bool EobCoreEngine::checkInventoryForRings(int charIndex, int itemValue) { + for (int i = 25; i <= 26; i++) { + int itm = _characters[charIndex].inventory[i]; + if (itm && _items[itm].type == 47 && _items[itm].value == itemValue) + return true; + } + return false; +} + +void EobCoreEngine::eatItemInHand(int charIndex) { + EobCharacter *c = &_characters[charIndex]; + if (!testCharacter(charIndex, 5)) { + _txt->printMessage(_warningStrings[1], -1, c->name); + } else if (_itemInHand && _items[_itemInHand].type != 31) { + _txt->printMessage(_warningStrings[3]); + } else if (_items[_itemInHand].value == -1) { + _txt->printMessage(_warningStrings[2]); + snd_playSoundEffect(79); + } else { + c->food += _items[_itemInHand].value; + if (c->food > 100) + c->food = 100; + + _items[_itemInHand].block = -1; + setHandItem(0); + gui_drawFoodStatusGraph(charIndex); + _screen->updateScreen(); + snd_playSoundEffect(9); + } +} + +bool EobCoreEngine::launchObject(int charIndex, Item item, uint16 startBlock, int startPos, int dir, int type) { + EobFlyingObject *t = _flyingObjects; + int slot = 0; + for (; slot < 10; slot++) { + if (!t->enable) + break; + t++; + } + + if (slot == 10) + return false; + + setItemPosition((Item*)&_levelBlockProperties[startBlock].drawObjects, startBlock, item, startPos | 4); + + t->enable = 1; + t->u2 = 1; + t->flags = 0; + t->direction = dir; + t->distance = 12; + t->curBlock = startBlock; + t->curPos = startPos; + t->item = item; + t->objectType = type; + t->attackerId = charIndex; + t->callBackIndex = 0; + + snd_playSoundEffect(type == 7 ? 26 : 11); + return true; +} + +void EobCoreEngine::launchMagicObject(int charIndex, int type, uint16 startBlock, int startPos, int dir) { + EobFlyingObject *t = _flyingObjects; + int slot = 0; + for (; slot < 10; slot++) { + if (!t->enable) + break; + t++; + } + + if (slot == 10) + return; + + t->enable = 2; + t->u2 = 1; + t->flags = _magicFlightObjectProperties[(type << 2) + 2]; + t->direction = dir; + t->distance = _magicFlightObjectProperties[(type << 2) + 1]; + t->curBlock = startBlock; + t->curPos = startPos; + t->item = type; + t->objectType = _magicFlightObjectProperties[(type << 2) + 3]; + t->attackerId = charIndex; + t->u2 = 1; + t->callBackIndex = _magicFlightObjectProperties[type << 2]; + _sceneUpdateRequired = true; +} + +bool EobCoreEngine::updateObjectFlight(EobFlyingObject *fo, int block, int pos) { + uint8 wallFlags = _wllWallFlags[_levelBlockProperties[block].walls[fo->direction ^ 2]]; + if (fo->enable == 1) { + if ((wallFlags & 1) || (fo->u2) || ((wallFlags & 2) && (_dscItemShapeMap[_items[fo->item].icon] >= 15))) { + getQueuedItem((Item*)&_levelBlockProperties[fo->curBlock].drawObjects, 0, fo->item); + setItemPosition((Item*)&_levelBlockProperties[block].drawObjects, block, fo->item, pos | 4); + fo->curBlock = block; + fo->curPos = pos; + fo->distance--; + return true; + + } else { + _clickedSpecialFlag = 0x10; + specialWallAction(block, fo->direction); + return false; + } + + } else { + if (!(wallFlags & 1) && (fo->curBlock != block)) + return false; + fo->curBlock = block; + fo->curPos = pos; + if (fo->distance != 255) + fo->distance--; + } + return true; +} + +bool EobCoreEngine::updateFlyingObjectHitTest(EobFlyingObject *fo, int block, int pos) { + if (fo->u2 && (fo->curBlock != _currentBlock || fo->attackerId >= 0) && (!blockHasMonsters(block) || fo->attackerId < 0)) + return false; + + if (fo->enable == 2) { + if (fo->callBackIndex) + return (this->*_spells[fo->callBackIndex].endCallback)(fo); + } + + if (blockHasMonsters(block)) { + for (int i = 0; i < 30; i++) { + if (!isMonsterOnPos(&_monsters[i], block, pos, 1)) + continue; + if (flyingObjectMonsterHit(fo, i)) + return true; + } + + } else if (block == _currentBlock) { + return flyingObjectPartyHit(fo); + } + + return false; +} + +void EobCoreEngine::updateFlyingObject_s3(EobFlyingObject *fo) { + +} + +void EobCoreEngine::endObjectFlight(EobFlyingObject *fo) { + if (fo->enable == 1) { + _items[fo->item].pos &= 3; + runLevelScript(fo->curBlock, 4); + updateEnvironmentalSfx(18); + } + memset(fo, 0, sizeof(EobFlyingObject)); +} + +void EobCoreEngine::checkFlyingObjects() { + for (int i = 0; i < 10; i++) { + EobFlyingObject *fo = &_flyingObjects[i]; + if (!fo->enable) + continue; + if (updateFlyingObjectHitTest(fo, fo->curBlock, fo->curPos)) + endObjectFlight(fo); + } +} + +void EobCoreEngine::reloadWeaponSlot(int charIndex, int slotIndex, int itemType, int arrowOrDagger) { + if (arrowOrDagger && _characters[charIndex].inventory[16]) { + _characters[charIndex].inventory[slotIndex] = getQueuedItem(&_characters[charIndex].inventory[16], 0, -1); + } else { + for (int i = 24; i >= 22; i--) { + if (!_characters[charIndex].inventory[i]) + continue; + if (_items[_characters[charIndex].inventory[i]].type == itemType && itemType != -1) + continue; + _characters[charIndex].inventory[slotIndex] = _characters[charIndex].inventory[i]; + _characters[charIndex].inventory[i] = 0; + return; + } + } +} + +} // End of namespace Kyra + +#endif // ENABLE_EOB + diff --git a/engines/kyra/kyra_hof.cpp b/engines/kyra/kyra_hof.cpp index 34bde7fe5c..df595b6ffa 100644 --- a/engines/kyra/kyra_hof.cpp +++ b/engines/kyra/kyra_hof.cpp @@ -171,7 +171,7 @@ KyraEngine_HoF::~KyraEngine_HoF() { delete[] _conversationState[i]; delete[] _conversationState; - for (Common::Array<const TIMOpcode *>::iterator i = _timOpcodes.begin(); i != _timOpcodes.end(); ++i) + for (Common::Array<const TIMOpcode*>::iterator i = _timOpcodes.begin(); i != _timOpcodes.end(); ++i) delete *i; _timOpcodes.clear(); } diff --git a/engines/kyra/kyra_hof.h b/engines/kyra/kyra_hof.h index 916cac0c9d..a6add505cb 100644 --- a/engines/kyra/kyra_hof.h +++ b/engines/kyra/kyra_hof.h @@ -656,7 +656,7 @@ protected: int t2_resetChat(const TIM *tim, const uint16 *param); int t2_playSoundEffect(const TIM *tim, const uint16 *param); - Common::Array<const TIMOpcode *> _timOpcodes; + Common::Array<const TIMOpcode*> _timOpcodes; // sound int _oldTalkFile; diff --git a/engines/kyra/kyra_mr.cpp b/engines/kyra/kyra_mr.cpp index 39ed0d038a..4e07c8f343 100644 --- a/engines/kyra/kyra_mr.cpp +++ b/engines/kyra/kyra_mr.cpp @@ -183,7 +183,7 @@ KyraEngine_MR::~KyraEngine_MR() { delete[] _sceneStrings; delete[] _talkObjectList; - for (Common::Array<const Opcode *>::iterator i = _opcodesDialog.begin(); i != _opcodesDialog.end(); ++i) + for (Common::Array<const Opcode*>::iterator i = _opcodesDialog.begin(); i != _opcodesDialog.end(); ++i) delete *i; _opcodesDialog.clear(); diff --git a/engines/kyra/kyra_mr.h b/engines/kyra/kyra_mr.h index 473c0371dc..38ff271037 100644 --- a/engines/kyra/kyra_mr.h +++ b/engines/kyra/kyra_mr.h @@ -463,7 +463,7 @@ private: void npcChatSequence(const char *str, int object, int vocHigh, int vocLow); - Common::Array<const Opcode *> _opcodesDialog; + Common::Array<const Opcode*> _opcodesDialog; int o3d_updateAnim(EMCState *script); int o3d_delay(EMCState *script); diff --git a/engines/kyra/kyra_v1.cpp b/engines/kyra/kyra_v1.cpp index dbdcda22d5..f5ef0d09ac 100644 --- a/engines/kyra/kyra_v1.cpp +++ b/engines/kyra/kyra_v1.cpp @@ -211,7 +211,7 @@ Common::Error KyraEngine_v1::init() { } KyraEngine_v1::~KyraEngine_v1() { - for (Common::Array<const Opcode *>::iterator i = _opcodes.begin(); i != _opcodes.end(); ++i) + for (Common::Array<const Opcode*>::iterator i = _opcodes.begin(); i != _opcodes.end(); ++i) delete *i; _opcodes.clear(); _keyMap.clear(); @@ -354,14 +354,14 @@ int KyraEngine_v1::checkInput(Button *buttonList, bool mainLoop, int eventFlag) } void KyraEngine_v1::setupKeyMap() { - struct KeyMapEntry { + struct KeyCodeMapEntry { Common::KeyCode kcScummVM; int16 kcDOS; int16 kcPC98; }; #define KC(x) Common::KEYCODE_##x - static const KeyMapEntry keys[] = { + static const KeyCodeMapEntry keys[] = { { KC(SPACE), 61, 53 }, { KC(RETURN), 43, 29 }, { KC(UP), 96, 68 }, @@ -377,13 +377,48 @@ void KyraEngine_v1::setupKeyMap() { { KC(KP7), 91, 67 }, { KC(PAGEUP), 101, 69 }, { KC(KP9), 101, 69 }, + { KC(END), 93, 0/*unknown*/ }, + { KC(KP1), 93, 0/*unknown*/ }, + { KC(PAGEDOWN), 103, 0/*unknown*/ }, + { KC(KP3), 103, 0/*unknown*/ }, { KC(F1), 112, 99 }, { KC(F2), 113, 100 }, { KC(F3), 114, 101 }, + { KC(F4), 115, 102 }, + { KC(F5), 116, 103 }, + { KC(F6), 117, 104 }, + { KC(a), 31, 31 }, + { KC(b), 50, 50 }, + { KC(c), 48, 48 }, + { KC(d), 33, 33 }, + { KC(e), 19, 19 }, + { KC(f), 34, 34 }, + { KC(i), 24, 24 }, + { KC(k), 38, 38 }, + { KC(m), 52, 52 }, + { KC(n), 51, 51 }, { KC(o), 25, 25 }, + { KC(p), 26, 26 }, { KC(r), 20, 20 }, + { KC(s), 32, 32 }, + { KC(w), 18, 18 }, + { KC(y), 22, 22 }, + { KC(z), 46, 46 }, + { KC(1), 2, 0/*unknown*/ }, + { KC(2), 3, 0/*unknown*/ }, + { KC(3), 4, 0/*unknown*/ }, + { KC(4), 5, 0/*unknown*/ }, + { KC(5), 6, 0/*unknown*/ }, + { KC(6), 7, 0/*unknown*/ }, + { KC(7), 8, 0/*unknown*/ }, { KC(SLASH), 55, 55 }, { KC(ESCAPE), 110, 1 }, + { KC(MINUS), 12, 0/*unknown*/ }, + { KC(KP_MINUS), 105, 0/*unknown*/ }, + { KC(PLUS), 13, 0/*unknown*/ }, + { KC(KP_PLUS), 106, 0/*unknown*/ }, + { KC(COMMA), 53, 0/*unknown*/ }, + { KC(PERIOD), 54, 0/*unknown*/ } }; #undef KC diff --git a/engines/kyra/kyra_v1.h b/engines/kyra/kyra_v1.h index 24a3b35418..40f9074106 100644 --- a/engines/kyra/kyra_v1.h +++ b/engines/kyra/kyra_v1.h @@ -323,7 +323,7 @@ protected: // opcode virtual void setupOpcodeTable() = 0; - Common::Array<const Opcode *> _opcodes; + Common::Array<const Opcode*> _opcodes; int o1_queryGameFlag(EMCState *script); int o1_setGameFlag(EMCState *script); diff --git a/engines/kyra/kyra_v2.h b/engines/kyra/kyra_v2.h index 56391d151a..7896748b96 100644 --- a/engines/kyra/kyra_v2.h +++ b/engines/kyra/kyra_v2.h @@ -252,7 +252,7 @@ protected: virtual void uninitAnimationShapes(int count, uint8 *filedata) = 0; // Shapes - typedef Common::HashMap<int, uint8 *> ShapeMap; + typedef Common::HashMap<int, uint8*> ShapeMap; ShapeMap _gameShapes; uint8 *getShapePtr(int index) const; diff --git a/engines/kyra/lol.cpp b/engines/kyra/lol.cpp index 120900537b..6cb3b51370 100644 --- a/engines/kyra/lol.cpp +++ b/engines/kyra/lol.cpp @@ -42,10 +42,9 @@ namespace Kyra { const char *const LoLEngine::kKeymapName = "lol"; -LoLEngine::LoLEngine(OSystem *system, const GameFlags &flags) : KyraEngine_v1(system, flags) { +LoLEngine::LoLEngine(OSystem *system, const GameFlags &flags) : LolEobBaseEngine(system, flags) { _screen = 0; _gui = 0; - _txt = 0; _tim = 0; _lang = 0; @@ -97,15 +96,11 @@ LoLEngine::LoLEngine(OSystem *system, const GameFlags &flags) : KyraEngine_v1(sy memset(_inventory, 0, sizeof(_inventory)); memset(_charStatusFlags, 0, sizeof(_charStatusFlags)); _inventoryCurItem = 0; - _currentControlMode = 0; - _specialSceneFlag = 0; _lastCharInventory = -1; _emcLastItem = -1; _itemIconShapes = _itemShapes = _gameShapes = _thrownShapes = _effectShapes = _fireballShapes = _healShapes = _healiShapes = 0; _levelShpList = _levelDatList = 0; - _monsterShapes = _monsterPalettes = 0; - _monsterShapesEx = 0; _gameShapeMap = 0; memset(_monsterAnimType, 0, 3); _healOverlay = 0; @@ -116,10 +111,8 @@ LoLEngine::LoLEngine(OSystem *system, const GameFlags &flags) : KyraEngine_v1(sy _charSelection = -1; _characters = 0; _spellProperties = 0; - _updateFlags = 0; _selectedSpell = 0; - _updateCharNum = _updatePortraitSpeechAnimDuration = _portraitSpeechAnimMode = _resetPortraitAfterSpeechAnim = _textColorFlag = _needSceneRestore = 0; - _fadeText = false; + _updateCharNum = _portraitSpeechAnimMode = _textColorFlag = 0; _palUpdateTimer = _updatePortraitNext = 0; _lampStatusTimer = 0xffffffff; @@ -128,66 +121,42 @@ LoLEngine::LoLEngine(OSystem *system, const GameFlags &flags) : KyraEngine_v1(sy _lastButtonShape = 0; _buttonPressTimer = 0; _selectedCharacter = 0; - _suspendScript = _sceneUpdateRequired = false; + _suspendScript = false; _scriptDirection = 0; - _currentDirection = 0; - _currentBlock = 0; - _compassDirection = _compassDirectionIndex = -1; + _compassDirectionIndex = -1; _compassStep = 0; - memset(_visibleBlockIndex, 0, sizeof(_visibleBlockIndex)); _smoothScrollModeNormal = 1; - _wllVmpMap = _specialWallTypes = _wllBuffer4 = _wllWallFlags = 0; - _wllShapeMap = 0; - _lvlShapeTop = _lvlShapeBottom = _lvlShapeLeftRight = 0; - _levelBlockProperties = 0; + _wllAutomapData = 0; + _sceneXoffset = 112; + _sceneShpDim = 13; _monsters = 0; _monsterProperties = 0; - _lvlBlockIndex = _lvlShapeIndex = 0; + _lvlShapeIndex = 0; _partyAwake = true; - _vcnBlocks = 0; - _vcnShift = 0; - _vcnExpTable = 0; - _vmpPtr = 0; - _vcfBlocks = 0; _transparencyTable2 = 0; _transparencyTable1 = 0; - _levelShapeProperties = 0; - _levelShapes = 0; _specialGuiShape = 0; _specialGuiShapeX = _specialGuiShapeY = _specialGuiShapeMirrorFlag = 0; - _blockDrawingBuffer = 0; - _sceneWindowBuffer = 0; - memset(_doorShapes, 0, sizeof(_doorShapes)); memset(_characterFaceShapes, 0, sizeof(_characterFaceShapes)); _lampEffect = _brightness = _lampOilStatus = 0; _lampStatusSuspended = false; - _blockBrightness = 0; _tempBuffer5120 = 0; _flyingObjects = 0; _monsters = 0; _lastMouseRegion = 0; - _objectLastDirection = _monsterStepCounter = _monsterStepMode = 0; + _objectLastDirection = 0; _monsterCurBlock = 0; _seqWindowX1 = _seqWindowY1 = _seqWindowX2 = _seqWindowY2 = _seqTrigger = 0; _spsWindowX = _spsWindowY = _spsWindowW = _spsWindowH = 0; - _dscUnk1 = 0; - _dscShapeIndex = 0; + _dscWalls = 0; _dscOvlMap = 0; _dscShapeScaleW = 0; - _dscShapeScaleH = 0; - _dscShapeX = 0; + _dscShapeScaleH = 0; _dscShapeY = 0; - _dscTileIndex = 0; - _dscUnk2 = 0; - _dscDoorShpIndex = 0; - _dscDim1 = 0; - _dscDim2 = 0; - _dscBlockMap = _dscDoor1 = _dscShapeOvlIndex = 0; - _dscBlockIndex = 0; - _dscDimMap = 0; + _dscShapeOvlIndex = 0; _dscDoorMonsterX = _dscDoorMonsterY = 0; _dscDoor4 = 0; @@ -198,29 +167,24 @@ LoLEngine::LoLEngine(OSystem *system, const GameFlags &flags) : KyraEngine_v1(sy _curMusicTheme = -1; _curMusicFileExt = 0; _curMusicFileIndex = -1; - _environmentSfx = _environmentSfxVol = _envSfxDistThreshold = 0; _envSfxUseQueue = false; _envSfxNumTracksInQueue = 0; memset(_envSfxQueuedTracks, 0, sizeof(_envSfxQueuedTracks)); memset(_envSfxQueuedBlocks, 0, sizeof(_envSfxQueuedBlocks)); - _sceneDrawVarDown = _sceneDrawVarRight = _sceneDrawVarLeft = _wllProcessFlag = 0; _partyPosX = _partyPosY = 0; _shpDmX = _shpDmY = _dmScaleW = _dmScaleH = 0; _floatingCursorControl = _currentFloatingCursor = 0; memset(_activeTim, 0, sizeof(_activeTim)); - memset(_openDoorState, 0, sizeof(_openDoorState)); memset(&_activeSpell, 0, sizeof(_activeSpell)); - _activeVoiceFileTotalTime = 0; _pageBuffer1 = _pageBuffer2 = 0; memset(_charStatsTemp, 0, sizeof(_charStatsTemp)); _compassBroken = _drainMagic = 0; - _dialogueField = false; _buttonData = 0; _activeButtons = 0; @@ -228,8 +192,6 @@ LoLEngine::LoLEngine(OSystem *system, const GameFlags &flags) : KyraEngine_v1(sy _preserveEvents = false; _buttonList1 = _buttonList2 = _buttonList3 = _buttonList4 = _buttonList5 = _buttonList6 = _buttonList7 = _buttonList8 = 0; - memset(_lvlTempData, 0, sizeof(_lvlTempData)); - _mapOverlay = 0; _automapShapes = 0; _defaultLegendData = 0; @@ -250,6 +212,7 @@ LoLEngine::LoLEngine(OSystem *system, const GameFlags &flags) : KyraEngine_v1(sy LoLEngine::~LoLEngine() { setupPrologueData(false); + releaseTempData(); #ifdef ENABLE_KEYMAPPER _eventMan->getKeymapper()->cleanupGameKeymaps(); @@ -264,8 +227,6 @@ LoLEngine::~LoLEngine() { _gui = 0; delete _tim; _tim = 0; - delete _txt; - _txt = 0; delete[] _itemsInPlay; delete[] _itemProperties; @@ -322,64 +283,47 @@ LoLEngine::~LoLEngine() { delete[] _healiShapes; } - for (int i = 0; i < 3; i++) - releaseMonsterShapes(i); + if (_monsterDecorationShapes) { + for (int i = 0; i < 3; i++) + releaseMonsterShapes(i); - delete[] _monsterShapes; - delete[] _monsterPalettes; - delete[] _monsterShapesEx; + delete[] _monsterShapes; + _monsterShapes = 0; + delete[] _monsterPalettes; + _monsterPalettes = 0; + delete[] _monsterDecorationShapes; + _monsterDecorationShapes = 0; + } + + for (int i = 0; i < 6; i++) { + delete[] _doorShapes[i]; + _doorShapes[i] = 0; + } delete[] _automapShapes; - for (Common::Array<const TIMOpcode *>::iterator i = _timIntroOpcodes.begin(); i != _timIntroOpcodes.end(); ++i) + for (Common::Array<const TIMOpcode*>::iterator i = _timIntroOpcodes.begin(); i != _timIntroOpcodes.end(); ++i) delete *i; _timIntroOpcodes.clear(); - for (Common::Array<const TIMOpcode *>::iterator i = _timOutroOpcodes.begin(); i != _timOutroOpcodes.end(); ++i) + for (Common::Array<const TIMOpcode*>::iterator i = _timOutroOpcodes.begin(); i != _timOutroOpcodes.end(); ++i) delete *i; _timOutroOpcodes.clear(); - for (Common::Array<const TIMOpcode *>::iterator i = _timIngameOpcodes.begin(); i != _timIngameOpcodes.end(); ++i) + for (Common::Array<const TIMOpcode*>::iterator i = _timIngameOpcodes.begin(); i != _timIngameOpcodes.end(); ++i) delete *i; _timIngameOpcodes.clear(); - - delete[] _wllVmpMap; - delete[] _wllShapeMap; - delete[] _specialWallTypes; - delete[] _wllBuffer4; - delete[] _wllWallFlags; - delete[] _lvlShapeTop; - delete[] _lvlShapeBottom; - delete[] _lvlShapeLeftRight; + delete[] _wllAutomapData; delete[] _tempBuffer5120; delete[] _flyingObjects; delete[] _monsters; - delete[] _levelBlockProperties; delete[] _monsterProperties; - delete[] _levelFileData; - delete[] _vcnExpTable; - delete[] _vcnBlocks; - delete[] _vcnShift; - delete[] _vmpPtr; - delete[] _vcfBlocks; delete[] _transparencyTable2; delete[] _transparencyTable1; - delete[] _levelShapeProperties; - delete[] _blockDrawingBuffer; - delete[] _sceneWindowBuffer; delete[] _lightningProps; - if (_levelShapes) { - for (int i = 0; i < 400; i++) - delete[] _levelShapes[i]; - delete[] _levelShapes; - } - - for (int i = 0; i < 2; i++) - delete[] _doorShapes[i]; - delete _lvlShpFileHandle; if (_ingameSoundList) { @@ -388,16 +332,6 @@ LoLEngine::~LoLEngine() { delete[] _ingameSoundList; } - for (int i = 0; i < 29; i++) { - if (_lvlTempData[i]) { - delete[] _lvlTempData[i]->wallsXorData; - delete[] _lvlTempData[i]->flags; - delete[] _lvlTempData[i]->monsters; - delete[] _lvlTempData[i]->flyingObjects; - delete _lvlTempData[i]; - } - } - for (int i = 0; i < 3; i++) { for (int ii = 0; ii < 40; ii++) delete[] _characterFaceShapes[ii][i]; @@ -409,7 +343,7 @@ LoLEngine::~LoLEngine() { delete[] _mapCursorOverlay; delete[] _mapOverlay; - for (Common::Array<const SpellProc *>::iterator i = _spellProcs.begin(); i != _spellProcs.end(); ++i) + for (Common::Array<const SpellProc*>::iterator i = _spellProcs.begin(); i != _spellProcs.end(); ++i) delete *i; _spellProcs.clear(); @@ -440,13 +374,18 @@ Common::Error LoLEngine::init() { KyraEngine_v1::init(); initStaticResource(); - _envSfxDistThreshold = _sound->getSfxType() == Sound::kAdLib ? 15 : 3; - _gui = new GUI_LoL(this); assert(_gui); _gui->initStaticData(); _txt = new TextDisplayer_LoL(this, _screen); + _dialogueButtonLabelCol1 = 144; + _dialogueButtonLabelCol2 = 254; + _dialogueButtonW = 74; + _dialogueButtonH = 9; + _bkgColor_1 = -1; + _color1_1 = 136; + _color2_1 = 251; _screen->setAnimBlockPtr(10000); _screen->setScreenDim(0); @@ -465,41 +404,15 @@ Common::Error LoLEngine::init() { if (!_sound->init()) error("Couldn't init sound"); - _wllVmpMap = new uint8[80]; - memset(_wllVmpMap, 0, 80); - _wllShapeMap = new int8[80]; - memset(_wllShapeMap, 0, 80); - _specialWallTypes = new uint8[80]; - memset(_specialWallTypes, 0, 80); - _wllBuffer4 = new uint8[80]; - memset(_wllBuffer4, 0, 80); - _wllWallFlags = new uint8[80]; - memset(_wllWallFlags, 0, 80); - _lvlShapeTop = new int16[18]; - memset(_lvlShapeTop, 0, 18 * sizeof(int16)); - _lvlShapeBottom = new int16[18]; - memset(_lvlShapeBottom, 0, 18 * sizeof(int16)); - _lvlShapeLeftRight = new int16[36]; - memset(_lvlShapeLeftRight, 0, 36 * sizeof(int16)); - _levelShapeProperties = new LevelShapeProperty[100]; - memset(_levelShapeProperties, 0, 100 * sizeof(LevelShapeProperty)); - _levelShapes = new uint8 *[400]; - memset(_levelShapes, 0, 400 * sizeof(uint8 *)); - _blockDrawingBuffer = new uint16[1320]; - memset(_blockDrawingBuffer, 0, 1320 * sizeof(uint16)); - _sceneWindowBuffer = new uint8[21120]; - memset(_sceneWindowBuffer, 0, 21120); - - _levelBlockProperties = new LevelBlockProperty[1025]; - memset(_levelBlockProperties, 0, 1025 * sizeof(LevelBlockProperty)); - _monsters = new MonsterInPlay[30]; - memset(_monsters, 0, 30 * sizeof(MonsterInPlay)); - _monsterProperties = new MonsterProperty[5]; - memset(_monsterProperties, 0, 5 * sizeof(MonsterProperty)); - - _vcnExpTable = new uint8[128]; - for (int i = 0; i < 128; i++) - _vcnExpTable[i] = i & 0x0f; + LolEobBaseEngine::init(); + + _wllAutomapData = new uint8[80]; + memset(_wllAutomapData, 0, 80); + + _monsters = new LolMonsterInPlay[30]; + memset(_monsters, 0, 30 * sizeof(LolMonsterInPlay)); + _monsterProperties = new LolMonsterProperty[5]; + memset(_monsterProperties, 0, 5 * sizeof(LolMonsterProperty)); _tempBuffer5120 = new uint8[5120]; memset(_tempBuffer5120, 0, 5120); @@ -509,25 +422,26 @@ Common::Error LoLEngine::init() { memset(_globalScriptVars, 0, sizeof(_globalScriptVars)); - _levelFileData = 0; _lvlShpFileHandle = 0; _sceneDrawPage1 = 2; _sceneDrawPage2 = 6; - _monsterShapes = new uint8 *[48]; - memset(_monsterShapes, 0, 48 * sizeof(uint8 *)); - _monsterPalettes = new uint8 *[48]; - memset(_monsterPalettes, 0, 48 * sizeof(uint8 *)); + _clickedShapeXOffs = 136; + _clickedShapeYOffs = 8; + _clickedSpecialFlag = 0x40; - _monsterShapesEx = new uint8 *[576]; - memset(_monsterShapesEx, 0, 576 * sizeof(uint8 *)); + _monsterShapes = new uint8*[48]; + memset(_monsterShapes, 0, 48 * sizeof(uint8*)); + _monsterPalettes = new uint8*[48]; + memset(_monsterPalettes, 0, 48 * sizeof(uint8*)); + _monsterDecorationShapes = new uint8*[576]; + memset(_monsterDecorationShapes, 0, 576 * sizeof(uint8*)); memset(&_scriptData, 0, sizeof(EMCData)); - _hasTempDataFlags = 0; _activeMagicMenu = -1; - _automapShapes = new const uint8 *[109]; + _automapShapes = new const uint8*[109]; _mapOverlay = new uint8[256]; memset(_availableSpells, -1, 8); @@ -687,7 +601,7 @@ void LoLEngine::loadItemIconShapes() { _screen->loadBitmap("ITEMICN.SHP", 3, 3, 0); const uint8 *shp = _screen->getCPagePtr(3); _numItemIconShapes = READ_LE_UINT16(shp); - _itemIconShapes = new uint8 *[_numItemIconShapes]; + _itemIconShapes = new uint8*[_numItemIconShapes]; for (int i = 0; i < _numItemIconShapes; i++) _itemIconShapes[i] = _screen->makeShapeCopy(shp, i); @@ -697,7 +611,7 @@ void LoLEngine::loadItemIconShapes() { _screen->loadBitmap("GAMESHP.SHP", 3, 3, 0); shp = _screen->getCPagePtr(3); _numGameShapes = READ_LE_UINT16(shp); - _gameShapes = new uint8 *[_numGameShapes]; + _gameShapes = new uint8*[_numGameShapes]; for (int i = 0; i < _numGameShapes; i++) _gameShapes[i] = _screen->makeShapeCopy(shp, i); } @@ -717,12 +631,6 @@ void LoLEngine::setMouseCursorToItemInHand() { _screen->setMouseCursor(o, o, getItemIconShapePtr(_itemInHand)); } -bool LoLEngine::posWithinRect(int mouseX, int mouseY, int x1, int y1, int x2, int y2) { - if (mouseX < x1 || mouseX > x2 || mouseY < y1 || mouseY > y2) - return false; - return true; -} - void LoLEngine::checkFloatingPointerRegions() { if (!_floatingCursorsEnabled) return; @@ -877,42 +785,42 @@ void LoLEngine::startup() { _screen->loadBitmap("ITEMSHP.SHP", 3, 3, 0); const uint8 *shp = _screen->getCPagePtr(3); _numItemShapes = READ_LE_UINT16(shp); - _itemShapes = new uint8 *[_numItemShapes]; + _itemShapes = new uint8*[_numItemShapes]; for (int i = 0; i < _numItemShapes; i++) _itemShapes[i] = _screen->makeShapeCopy(shp, i); _screen->loadBitmap("THROWN.SHP", 3, 3, 0); shp = _screen->getCPagePtr(3); _numThrownShapes = READ_LE_UINT16(shp); - _thrownShapes = new uint8 *[_numThrownShapes]; + _thrownShapes = new uint8*[_numThrownShapes]; for (int i = 0; i < _numThrownShapes; i++) _thrownShapes[i] = _screen->makeShapeCopy(shp, i); _screen->loadBitmap("ICE.SHP", 3, 3, 0); shp = _screen->getCPagePtr(3); _numEffectShapes = READ_LE_UINT16(shp); - _effectShapes = new uint8 *[_numEffectShapes]; + _effectShapes = new uint8*[_numEffectShapes]; for (int i = 0; i < _numEffectShapes; i++) _effectShapes[i] = _screen->makeShapeCopy(shp, i); _screen->loadBitmap("FIREBALL.SHP", 3, 3, 0); shp = _screen->getCPagePtr(3); _numFireballShapes = READ_LE_UINT16(shp); - _fireballShapes = new uint8 *[_numFireballShapes]; + _fireballShapes = new uint8*[_numFireballShapes]; for (int i = 0; i < _numFireballShapes; i++) _fireballShapes[i] = _screen->makeShapeCopy(shp, i); _screen->loadBitmap("HEAL.SHP", 3, 3, 0); shp = _screen->getCPagePtr(3); _numHealShapes = READ_LE_UINT16(shp); - _healShapes = new uint8 *[_numHealShapes]; + _healShapes = new uint8*[_numHealShapes]; for (int i = 0; i < _numHealShapes; i++) _healShapes[i] = _screen->makeShapeCopy(shp, i); _screen->loadBitmap("HEALI.SHP", 3, 3, 0); shp = _screen->getCPagePtr(3); _numHealiShapes = READ_LE_UINT16(shp); - _healiShapes = new uint8 *[_numHealiShapes]; + _healiShapes = new uint8*[_numHealiShapes]; for (int i = 0; i < _numHealiShapes; i++) _healiShapes[i] = _screen->makeShapeCopy(shp, i); @@ -969,10 +877,9 @@ void LoLEngine::runLoop() { enableSysTimer(2); - bool _runFlag = true; _flagsTable[73] |= 0x08; - while (!shouldQuit() && _runFlag) { + while (!shouldQuit()) { if (_gameToLoad != -1) { // FIXME: Instead of throwing away the error returned by // loadGameState, we should use it / augment it. @@ -1784,44 +1691,6 @@ void LoLEngine::fadeText() { _fadeText = false; } -void LoLEngine::transformRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPage, int dstPage) { - uint16 *p1 = (uint16 *)_tempBuffer5120; - uint16 *p2 = (uint16 *)(_tempBuffer5120 + 640); - - for (int i = 0; i < w; i++) - p1[i] = i; - - for (int i = 0; i < h; i++) - p2[i] = i; - - for (int i = 0; i < w; i++) - SWAP(p1[_rnd.getRandomNumberRng(0, w - 1)], p1[i]); - - for (int i = 0; i < h; i++) - SWAP(p2[_rnd.getRandomNumberRng(0, h - 1)], p2[i]); - - for (int i = 0; i < h; i++) { - int i2 = i; - - for (int ii = 0; ii < w; ii++) { - int dx1 = x1 + p1[ii]; - int dy1 = y1 + p2[i2]; - int dx2 = x2 + p1[ii]; - int dy2 = y2 + p2[i2]; - - if (++i2 == h) - i2 = 0; - - _screen->setPagePixel(dstPage, dx2, dy2, _screen->getPagePixel(srcPage, dx1, dy1)); - } - - if (!dstPage && (i & 5) == 5) { - updateInput(); - _screen->updateScreen(); - } - } -} - void LoLEngine::setPaletteBrightness(const Palette &srcPal, int brightness, int modifier) { generateBrightnessPalette(srcPal, _screen->getPalette(1), brightness, modifier); _screen->fadePalette(_screen->getPalette(1), 5, 0); @@ -2040,7 +1909,46 @@ int LoLEngine::playCharacterScriptChat(int charId, int mode, int restorePortrait return 1; } -void LoLEngine::giveItemToMonster(MonsterInPlay *monster, Item item) { +void LoLEngine::setupDialogueButtons(int numStr, const char *s1, const char *s2, const char *s3) { + screen()->setScreenDim(5); + + if (numStr == 1 && speechEnabled()) { + _dialogueNumButtons = 0; + _dialogueButtonString[0] = _dialogueButtonString[1] = _dialogueButtonString[2] = 0; + } else { + _dialogueNumButtons = numStr; + _dialogueButtonString[0] = s1; + _dialogueButtonString[1] = s2; + _dialogueButtonString[2] = s3; + _dialogueHighlightedButton = 0; + + const ScreenDim *d = screen()->getScreenDim(5); + + static uint16 posX[3]; + static uint8 posY[3]; + + memset(posY, d->sy + d->h - 9, 3); + + _dialogueButtonPosX = posX; + _dialogueButtonPosY = posY; + + if (numStr == 1) { + posX[0] = posX[1] = posX[2] = d->sx + d->w - (_dialogueButtonW + 3); + } else { + int xOffs = d->w / numStr; + posX[0] = d->sx + (xOffs >> 1) - 37; + posX[1] = posX[0] + xOffs; + posX[2] = posX[1] + xOffs; + } + + drawDialogueButtons(); + } + + if (!shouldQuit()) + removeInputTop(); +} + +void LoLEngine::giveItemToMonster(LolMonsterInPlay *monster, Item item) { uint16 *c = &monster->assignedItems; while (*c) c = &_itemsInPlay[*c].nextAssignedObject; @@ -2049,7 +1957,7 @@ void LoLEngine::giveItemToMonster(MonsterInPlay *monster, Item item) { } const uint16 *LoLEngine::getCharacterOrMonsterStats(int id) { - return (id & 0x8000) ? (const uint16 *)_monsters[id & 0x7fff].properties->fightingStats : _characters[id].defaultModifiers; + return (id & 0x8000) ? (const uint16*)_monsters[id & 0x7fff].properties->fightingStats : _characters[id].defaultModifiers; } uint16 *LoLEngine::getCharacterOrMonsterItemsMight(int id) { @@ -2073,21 +1981,6 @@ void LoLEngine::delay(uint32 millis, bool doUpdate, bool) { } } -int LoLEngine::rollDice(int times, int pips) { - if (times <= 0 || pips <= 0) - return 0; - - int res = 0; - while (times--) - res += _rnd.getRandomNumberRng(1, pips); - - return res; -} - -void LoLEngine::updateEnvironmentalSfx(int soundId) { - snd_processEnvironmentalSoundEffect(soundId, _currentBlock); -} - // spells int LoLEngine::castSpell(int charNum, int spellType, int spellLevel) { @@ -2502,7 +2395,7 @@ int LoLEngine::processMagicIce(int charNum, int spellLevel) { int might = rollDice(iceDamageMin[spellLevel], iceDamageMax[spellLevel]) + iceDamageAdd[spellLevel]; int dmg = calcInflictableDamagePerItem(charNum, 0, might, 3, 2); - MonsterInPlay *m = &_monsters[o & 0x7fff]; + LolMonsterInPlay *m = &_monsters[o & 0x7fff]; if (m->hitPoints <= dmg) { increaseExperience(charNum, 2, m->hitPoints); o = m->nextAssignedObject; @@ -2581,7 +2474,7 @@ int LoLEngine::processMagicFireball(int charNum, int spellLevel) { while (o & 0x8000) { static const uint8 fireballDamage[] = { 20, 40, 80, 100 }; int dmg = calcInflictableDamagePerItem(charNum, o, fireballDamage[spellLevel], 4, 1); - MonsterInPlay *m = &_monsters[o & 0x7fff]; + LolMonsterInPlay *m = &_monsters[o & 0x7fff]; o = m->nextAssignedObject; _envSfxUseQueue = true; inflictDamage(m->id | 0x8000, dmg, charNum, 2, 4); @@ -2746,7 +2639,7 @@ int LoLEngine::processMagicHandOfFate(int spellLevel) { uint16 o = _levelBlockProperties[b1].assignedObjects; while (o & 0x8000) { uint16 o2 = o; - MonsterInPlay *m = &_monsters[o & 0x7fff]; + LolMonsterInPlay *m = &_monsters[o & 0x7fff]; o = findObject(o)->nextAssignedObject; int nX = 0; int nY = 0; @@ -3015,7 +2908,7 @@ int LoLEngine::processMagicVaelansCube() { uint16 o = _levelBlockProperties[bl].assignedObjects; while (o & 0x8000) { - MonsterInPlay *m = &_monsters[o & 0x7fff]; + LolMonsterInPlay *m = &_monsters[o & 0x7fff]; if (m->properties->flags & 0x1000) { inflictDamage(o, 100, 0xffff, 0, 0x80); v = 1; @@ -3495,7 +3388,7 @@ int LoLEngine::calcInflictableDamage(int16 attacker, int16 target, int hitType) } int LoLEngine::inflictDamage(uint16 target, int damage, uint16 attacker, int skill, int flags) { - MonsterInPlay *m = 0; + LolMonsterInPlay *m = 0; LoLCharacter *c = 0; if (target & 0x8000) { @@ -3712,7 +3605,7 @@ void LoLEngine::checkForPartyDeath() { } } -void LoLEngine::applyMonsterAttackSkill(MonsterInPlay *monster, int16 target, int16 damage) { +void LoLEngine::applyMonsterAttackSkill(LolMonsterInPlay *monster, int16 target, int16 damage) { if (rollDice(1, 100) > monster->properties->attackSkillChance) return; @@ -3782,7 +3675,7 @@ void LoLEngine::applyMonsterAttackSkill(MonsterInPlay *monster, int16 target, in } } -void LoLEngine::applyMonsterDefenseSkill(MonsterInPlay *monster, int16 attacker, int flags, int skill, int damage) { +void LoLEngine::applyMonsterDefenseSkill(LolMonsterInPlay *monster, int16 attacker, int flags, int skill, int damage) { if (rollDice(1, 100) > monster->properties->defenseSkillChance) return; @@ -4041,7 +3934,7 @@ uint16 LoLEngine::getNearestMonsterFromCharacterForBlock(uint16 block, int charN int o = _levelBlockProperties[block].assignedObjects; while (o & 0x8000) { - MonsterInPlay *m = &_monsters[o & 0x7fff]; + LolMonsterInPlay *m = &_monsters[o & 0x7fff]; if (m->mode >= 13) { o = m->nextAssignedObject; continue; @@ -4107,7 +4000,7 @@ void LoLEngine::displayAutomap() { _currentMapLevel = _currentLevel; uint8 *tmpWll = new uint8[80]; - memcpy(tmpWll, _wllBuffer4, 80); + memcpy(tmpWll, _wllAutomapData, 80); _screen->loadBitmap("parch.cps", 2, 2, &_screen->getPalette(3)); _screen->loadBitmap("autobut.shp", 3, 5, 0); @@ -4188,7 +4081,7 @@ void LoLEngine::displayAutomap() { _screen->fadeToBlack(10); loadLevelWallData(_currentLevel, false); - memcpy(_wllBuffer4, tmpWll, 80); + memcpy(_wllAutomapData, tmpWll, 80); delete[] tmpWll; restoreBlockTempData(_currentLevel); addLevelItems(); @@ -4229,7 +4122,7 @@ bool LoLEngine::updateAutoMapIntern(uint16 block, uint16 x, uint16 y, int16 xOff uint16 b = block + blockPosTable[6 + xOffs]; if (fx != -1) { - if (_wllBuffer4[_levelBlockProperties[b].walls[fx]] & 0xc0) + if (_wllAutomapData[_levelBlockProperties[b].walls[fx]] & 0xc0) return false; } @@ -4237,13 +4130,13 @@ bool LoLEngine::updateAutoMapIntern(uint16 block, uint16 x, uint16 y, int16 xOff b = block + blockPosTable[9 + yOffs]; if (fy != -1) { - if (_wllBuffer4[_levelBlockProperties[b].walls[fy]] & 0xc0) + if (_wllAutomapData[_levelBlockProperties[b].walls[fy]] & 0xc0) return false; } b = block + blockPosTable[6 + xOffs] + blockPosTable[9 + yOffs]; - if ((fx != -1) && (fy != -1) && (_wllBuffer4[_levelBlockProperties[b].walls[fx]] & 0xc0) && (_wllBuffer4[_levelBlockProperties[b].walls[fy]] & 0xc0)) + if ((fx != -1) && (fy != -1) && (_wllAutomapData[_levelBlockProperties[b].walls[fx]] & 0xc0) && (_wllAutomapData[_levelBlockProperties[b].walls[fy]] & 0xc0)) return false; _levelBlockProperties[b].flags |= 7; @@ -4252,7 +4145,7 @@ bool LoLEngine::updateAutoMapIntern(uint16 block, uint16 x, uint16 y, int16 xOff } void LoLEngine::loadMapLegendData(int level) { - uint16 *legendData = (uint16 *)_tempBuffer5120; + uint16 *legendData= (uint16*)_tempBuffer5120; for (int i = 0; i < 32; i++) { legendData[i * 6] = 0xffff; legendData[i * 6 + 5] = 0xffff; @@ -4306,7 +4199,7 @@ void LoLEngine::drawMapPage(int pageNum) { for (; bl < 1024; bl++) { uint8 *w = _levelBlockProperties[bl].walls; - if ((_levelBlockProperties[bl].flags & 7) == 7 && (!(_wllBuffer4[w[0]] & 0xc0)) && (!(_wllBuffer4[w[2]] & 0xc0)) && (!(_wllBuffer4[w[1]] & 0xc0))&& (!(_wllBuffer4[w[3]] & 0xc0))) { + if ((_levelBlockProperties[bl].flags & 7) == 7 && (!(_wllAutomapData[w[0]] & 0xc0)) && (!(_wllAutomapData[w[2]] & 0xc0)) && (!(_wllAutomapData[w[1]] & 0xc0))&& (!(_wllAutomapData[w[3]] & 0xc0))) { uint16 b0 = calcNewBlockPosition(bl, 0); uint16 b2 = calcNewBlockPosition(bl, 2); uint16 b1 = calcNewBlockPosition(bl, 1); @@ -4323,25 +4216,25 @@ void LoLEngine::drawMapPage(int pageNum) { // draw north wall drawMapBlockWall(b3, w31, sx, sy, 3); drawMapShape(w31, sx, sy, 3); - if (_wllBuffer4[w31] & 0xc0) + if (_wllAutomapData[w31] & 0xc0) _screen->copyBlockAndApplyOverlay(_screen->_curPage, sx, sy, _screen->_curPage, sx, sy, 1, 6, 0, _mapOverlay); // draw west wall drawMapBlockWall(b1, w13, sx, sy, 1); drawMapShape(w13, sx, sy, 1); - if (_wllBuffer4[w13] & 0xc0) + if (_wllAutomapData[w13] & 0xc0) _screen->copyBlockAndApplyOverlay(_screen->_curPage, sx + 6, sy, _screen->_curPage, sx + 6, sy, 1, 6, 0, _mapOverlay); // draw east wall drawMapBlockWall(b0, w02, sx, sy, 0); drawMapShape(w02, sx, sy, 0); - if (_wllBuffer4[w02] & 0xc0) + if (_wllAutomapData[w02] & 0xc0) _screen->copyBlockAndApplyOverlay(_screen->_curPage, sx, sy, _screen->_curPage, sx, sy, 7, 1, 0, _mapOverlay); //draw south wall drawMapBlockWall(b2, w20, sx, sy, 2); drawMapShape(w20, sx, sy, 2); - if (_wllBuffer4[w20] & 0xc0) + if (_wllAutomapData[w20] & 0xc0) _screen->copyBlockAndApplyOverlay(_screen->_curPage, sx, sy + 5, _screen->_curPage, sx, sy + 5, 7, 1, 0, _mapOverlay); } @@ -4362,7 +4255,7 @@ void LoLEngine::drawMapPage(int pageNum) { sx = mapGetStartPosX(); sy = mapGetStartPosY(); - uint16 *legendData = (uint16 *)_tempBuffer5120; + uint16 *legendData = (uint16*)_tempBuffer5120; uint8 yOffset = _flags.use16ColorMode ? 4 : 0; for (int ii = 0; ii < 32; ii++) { @@ -4508,7 +4401,7 @@ void LoLEngine::redrawMapCursor() { } void LoLEngine::drawMapBlockWall(uint16 block, uint8 wall, int x, int y, int direction) { - if (((1 << direction) & _levelBlockProperties[block].flags) || ((_wllBuffer4[wall] & 0x1f) != 13)) + if (((1 << direction) & _levelBlockProperties[block].flags) || ((_wllAutomapData[wall] & 0x1f) != 13)) return; int cp = _screen->_curPage; @@ -4518,7 +4411,7 @@ void LoLEngine::drawMapBlockWall(uint16 block, uint8 wall, int x, int y, int dir } void LoLEngine::drawMapShape(uint8 wall, int x, int y, int direction) { - int l = _wllBuffer4[wall] & 0x1f; + int l = _wllAutomapData[wall] & 0x1f; if (l == 0x1f) return; @@ -4611,48 +4504,7 @@ void LoLEngine::printMapExitButtonText() { _screen->setCurPage(cp); } -void LoLEngine::generateTempData() { - int l = _currentLevel - 1; - if (_lvlTempData[l]) { - delete[] _lvlTempData[l]->wallsXorData; - delete[] _lvlTempData[l]->flags; - delete[] _lvlTempData[l]->monsters; - delete[] _lvlTempData[l]->flyingObjects; - delete _lvlTempData[l]; - } - - _lvlTempData[l] = new LevelTempData; - _lvlTempData[l]->wallsXorData = new uint8[4096]; - _lvlTempData[l]->flags = new uint8[1024]; - _lvlTempData[l]->monsters = new MonsterInPlay[30]; - _lvlTempData[l]->flyingObjects = new FlyingObject[8]; - - Common::String filename = Common::String::format("LEVEL%d.CMZ", _currentLevel); - - _screen->loadBitmap(filename.c_str(), 15, 15, 0); - const uint8 *p = _screen->getCPagePtr(14); - uint16 len = READ_LE_UINT16(p + 4); - p += 6; - - memset(_lvlTempData[l]->wallsXorData, 0, 4096); - memset(_lvlTempData[l]->flags, 0, 1024); - uint8 *d = _lvlTempData[l]->wallsXorData; - uint8 *df = _lvlTempData[l]->flags; - - for (int i = 0; i < 1024; i++) { - for (int ii = 0; ii < 4; ii++) - *d++ = p[i * len + ii] ^ _levelBlockProperties[i].walls[ii]; - *df++ = _levelBlockProperties[i].flags; - } - - memcpy(_lvlTempData[l]->monsters, _monsters, sizeof(MonsterInPlay) * 30); - memcpy(_lvlTempData[l]->flyingObjects, _flyingObjects, sizeof(FlyingObject) * 8); - - _lvlTempData[l]->monsterDifficulty =_monsterDifficulty; - - _hasTempDataFlags |= (1 << l); -} } // End of namespace Kyra diff --git a/engines/kyra/lol.h b/engines/kyra/lol.h index eb2f6cf2d7..20519b78fb 100644 --- a/engines/kyra/lol.h +++ b/engines/kyra/lol.h @@ -25,7 +25,7 @@ #ifndef KYRA_LOL_H #define KYRA_LOL_H -#include "kyra/kyra_v1.h" +#include "kyra/loleobbase.h" #include "kyra/script_tim.h" #include "kyra/script.h" #include "kyra/gui_lol.h" @@ -86,15 +86,7 @@ struct SpellProperty { uint16 flags; }; -struct LevelBlockProperty { - uint8 walls[4]; - uint16 assignedObjects; - uint16 drawObjects; - uint8 direction; - uint8 flags; -}; - -struct MonsterProperty { +struct LolMonsterProperty { uint8 shapeIndex; uint8 maxWidth; uint16 fightingStats[9]; @@ -116,7 +108,7 @@ struct MonsterProperty { uint8 sounds[3]; }; -struct MonsterInPlay { +struct LolMonsterInPlay { uint16 nextAssignedObject; uint16 nextDrawObject; uint8 flyingHeight; @@ -142,7 +134,7 @@ struct MonsterInPlay { int16 hitPoints; uint8 speedTick; uint8 type; - MonsterProperty *properties; + LolMonsterProperty *properties; uint8 numDistAttacks; uint8 curDistWeapon; int8 distAttackTick; @@ -179,15 +171,6 @@ struct ItemProperty { uint8 unkD; }; -struct LevelShapeProperty { - uint16 shapeIndex[10]; - uint8 scaleFlag[10]; - int16 shapeX[10]; - int16 shapeY[10]; - int8 next; - uint8 flags; -}; - struct CompassDef { uint8 shapeIndex; int8 x; @@ -195,7 +178,7 @@ struct CompassDef { uint8 flags; }; -struct ButtonDef { +struct LoLButtonDef { uint16 buttonflags; uint16 keyCode; uint16 keyCode2; @@ -207,12 +190,6 @@ struct ButtonDef { uint16 screenDim; }; -struct OpenDoorState { - uint16 block; - int8 wall; - int8 state; -}; - struct ActiveSpell { uint8 spell; const SpellProperty *p; @@ -245,14 +222,6 @@ struct FlyingObjectShape { uint8 flipFlags; }; -struct LevelTempData { - uint8 *wallsXorData; - uint8 *flags; - MonsterInPlay *monsters; - FlyingObject *flyingObjects; - uint8 monsterDifficulty; -}; - struct MapLegendData { uint8 shapeIndex; bool enable; @@ -296,7 +265,7 @@ struct MistOfDoomAnimData { uint8 sound; }; -class LoLEngine : public KyraEngine_v1 { +class LoLEngine : public LolEobBaseEngine { friend class GUI_LoL; friend class TextDisplayer_LoL; friend class TIMInterpreter_LoL; @@ -305,7 +274,7 @@ friend class Debugger_LoL; friend class HistoryPlayer; public: LoLEngine(OSystem *system, const GameFlags &flags); - ~LoLEngine(); + virtual ~LoLEngine(); virtual void initKeymap(); @@ -348,13 +317,11 @@ private: // main loop void runLoop(); void update(); - void updateEnvironmentalSfx(int soundId); // mouse void setMouseCursorToIcon(int icon); void setMouseCursorToItemInHand(); uint8 *getItemIconShapePtr(int index); - bool posWithinRect(int mouseX, int mouseY, int x1, int y1, int x2, int y2); void checkFloatingPointerRegions(); int _floatingCursorControl; @@ -443,11 +410,7 @@ private: // timers void setupTimers(); - void enableTimer(int id); - void enableSysTimer(int sysTimer); - void disableSysTimer(int sysTimer); - void timerProcessDoors(int timerNum); void timerProcessMonsters(int timerNum); void timerSpecialCharacterUpdate(int timerNum); void timerProcessFlyingObjects(int timerNum); @@ -457,6 +420,9 @@ private: void timerUpdateLampState(int timerNum); void timerFadeMessageText(int timerNum); + uint8 getClock2Timer(int index) { return index < _numClock2Timers ? _clock2Timers[index] : 0; } + uint8 getNumClock2Timers() { return _numClock2Timers; } + static const uint8 _clock2Timers[]; static const uint8 _numClock2Timers; @@ -470,7 +436,7 @@ private: int snd_updateCharacterSpeech(); void snd_stopSpeech(bool setFlag); void snd_playSoundEffect(int track, int volume); - void snd_processEnvironmentalSoundEffect(int soundId, int block); + bool snd_processEnvironmentalSoundEffect(int soundId, int block); void snd_queueEnvironmentalSoundEffect(int soundId, int block); void snd_playQueuedEffects(); void snd_loadSoundFile(int track); @@ -479,21 +445,17 @@ private: int _lastSpeechId; int _lastSpeaker; - uint32 _activeVoiceFileTotalTime; int _lastSfxTrack; int _lastMusicTrack; int _curMusicFileIndex; char _curMusicFileExt; - int _environmentSfx; - int _environmentSfxVol; - int _envSfxDistThreshold; bool _envSfxUseQueue; int _envSfxNumTracksInQueue; uint16 _envSfxQueuedTracks[10]; uint16 _envSfxQueuedBlocks[10]; int _nextSpeechId; int _nextSpeaker; - typedef Common::List<Audio::SeekableAudioStream *> SpeechList; + typedef Common::List<Audio::SeekableAudioStream*> SpeechList; SpeechList _speechList; int _curTlkFile; @@ -502,9 +464,7 @@ private: int _ingameSoundListSize; const uint8 *_musicTrackMap; - int _musicTrackMapSize; const uint16 *_ingameSoundIndex; - int _ingameSoundIndexSize; const uint8 *_ingameGMSoundIndex; int _ingameGMSoundIndexSize; const uint8 *_ingameMT32SoundIndex; @@ -519,7 +479,6 @@ private: void gui_drawScene(int pageNum); void gui_drawAllCharPortraitsWithStats(); void gui_drawCharPortraitWithStats(int charNum); - void gui_drawBox(int x, int y, int w, int h, int frameColor1, int frameColor2, int fillColor); void gui_drawCharFaceShape(int charNum, int x, int y, int pageNum); void gui_highlightPortraitFrame(int charNum); void gui_drawLiveMagicBar(int x, int y, int curPoints, int unk, int maxPoints, int w, int h, int col1, int col2, int flag); @@ -534,7 +493,6 @@ private: void gui_printCharacterStats(int index, int redraw, int value); void gui_changeCharacterStats(int charNum); void gui_drawCharInventoryItem(int itemIndex); - void gui_drawBarGraph(int x, int y, int w, int h, int32 curVal, int32 maxVal, int col1, int col2); int gui_enableControls(); int gui_disableControls(int controlMode); @@ -548,37 +506,27 @@ private: int _lastButtonShape; uint32 _buttonPressTimer; int _selectedCharacter; - int _compassDirection; int _compassStep; int _compassDirectionIndex; uint32 _compassTimer; int _charInventoryUnk; const CompassDef *_compassDefs; - int _compassDefsSize; void gui_updateInput(); void gui_triggerEvent(int eventType); - void removeInputTop(); void gui_enableDefaultPlayfieldButtons(); void gui_enableSequenceButtons(int x, int y, int w, int h, int enableFlags); void gui_specialSceneRestoreButtons(); void gui_enableCharInventoryButtons(int charNum); - void gui_resetButtonList(); - void gui_initButtonsFromList(const int16 *list); void gui_setFaceFramesControlButtons(int index, int xOffs); void gui_initCharInventorySpecialButtons(int charNum); void gui_initMagicScrollButtons(); void gui_initMagicSubmenu(int charNum); void gui_initButton(int index, int x = -1, int y = -1, int val = -1); - void gui_notifyButtonListChanged(); - Common::Array<Button::Callback> _buttonCallbacks; - Button *_activeButtons; - Button _activeButtonData[70]; - ButtonDef _sceneWindowButton; - bool _preserveEvents; + LoLButtonDef _sceneWindowButton; int clickedUpArrow(Button *button); int clickedDownArrow(Button *button); @@ -613,45 +561,34 @@ private: int clickedLamp(Button *button); int clickedStatusIcon(Button *button); - const ButtonDef *_buttonData; - int _buttonDataSize; + Common::Array<Button::Callback> _buttonCallbacks; + const LoLButtonDef *_buttonData; const int16 *_buttonList1; - int _buttonList1Size; const int16 *_buttonList2; - int _buttonList2Size; const int16 *_buttonList3; - int _buttonList3Size; const int16 *_buttonList4; - int _buttonList4Size; const int16 *_buttonList5; - int _buttonList5Size; const int16 *_buttonList6; - int _buttonList6Size; const int16 *_buttonList7; - int _buttonList7Size; const int16 *_buttonList8; - int _buttonList8Size; // text int characterSays(int track, int charId, bool redraw); int playCharacterScriptChat(int charId, int mode, int restorePortrait, char *str, EMCState *script, const uint16 *paramList, int16 paramIndex); + void setupDialogueButtons(int numStr, const char *s1, const char *s2, const char *s3); TextDisplayer_LoL *_txt; + TextDisplayer_Eob *txt() { return _txt; } // emc scripts void runInitScript(const char *filename, int optionalFunc); void runInfScript(const char *filename); void runLevelScript(int block, int flags); void runLevelScriptCustom(int block, int flags, int charNum, int item, int reg3, int reg4); - bool checkSceneUpdateNeed(int func); EMCData _scriptData; bool _suspendScript; uint16 _scriptDirection; - uint16 _currentDirection; - uint16 _currentBlock; - bool _sceneUpdateRequired; - int16 _visibleBlockIndex[18]; int16 _globalScriptVars[24]; // emc opcode @@ -702,7 +639,7 @@ private: int olol_checkEquippedItemScriptFlags(EMCState *script); int olol_setDoorState(EMCState *script); int olol_updateBlockAnimations(EMCState *script); - int olol_mapShapeToBlock(EMCState *script); + int olol_assignLevelDecorationShape(EMCState *script); int olol_resetBlockShapeAssignment(EMCState *script); int olol_copyRegion(EMCState *script); int olol_initMonster(EMCState *script); @@ -716,7 +653,7 @@ private: int olol_battleHitSkillTest(EMCState *script); int olol_inflictDamage(EMCState *script); int olol_moveMonster(EMCState *script); - int olol_dialogueBox(EMCState *script); + int olol_setupDialogueButtons(EMCState *script); int olol_giveTakeMoney(EMCState *script); int olol_checkMoney(EMCState *script); int olol_setScriptTimer(EMCState *script); @@ -792,7 +729,7 @@ private: int olol_assignCustomSfx(EMCState *script); int olol_findAssignedMonster(EMCState *script); int olol_checkBlockForMonster(EMCState *script); - int olol_transformRegion(EMCState *script); + int olol_crossFadeRegion(EMCState *script); int olol_calcCoordinatesAddDirectionOffset(EMCState *script); int olol_resetPortraitsAndDisableSysTimer(EMCState *script); int olol_enableSysTimer(EMCState *script); @@ -820,7 +757,7 @@ private: int olol_shakeScene(EMCState *script); int olol_gasExplosion(EMCState *script); int olol_calcNewBlockPosition(EMCState *script); - int olol_fadeScene(EMCState *script); + int olol_crossFadeScene(EMCState *script); int olol_updateDrawPage2(EMCState *script); int olol_setMouseCursor(EMCState *script); int olol_characterSays(EMCState *script); @@ -834,14 +771,14 @@ private: // tim opcode void setupOpcodeTable(); - Common::Array<const TIMOpcode *> _timIntroOpcodes; + Common::Array<const TIMOpcode*> _timIntroOpcodes; int tlol_setupPaletteFade(const TIM *tim, const uint16 *param); int tlol_loadPalette(const TIM *tim, const uint16 *param); int tlol_setupPaletteFadeEx(const TIM *tim, const uint16 *param); int tlol_processWsaFrame(const TIM *tim, const uint16 *param); int tlol_displayText(const TIM *tim, const uint16 *param); - Common::Array<const TIMOpcode *> _timOutroOpcodes; + Common::Array<const TIMOpcode*> _timOutroOpcodes; int tlol_fadeInScene(const TIM *tim, const uint16 *param); int tlol_unusedResourceFunc(const TIM *tim, const uint16 *param); int tlol_fadeInPalette(const TIM *tim, const uint16 *param); @@ -850,7 +787,7 @@ private: int tlol_delayForChat(const TIM *tim, const uint16 *param); int tlol_fadeOutSound(const TIM *tim, const uint16 *param); - Common::Array<const TIMOpcode *> _timIngameOpcodes; + Common::Array<const TIMOpcode*> _timIngameOpcodes; int tlol_initSceneWindowDialogue(const TIM *tim, const uint16 *param); int tlol_restoreAfterSceneWindowDialogue(const TIM *tim, const uint16 *param); int tlol_giveItem(const TIM *tim, const uint16 *param); @@ -900,7 +837,6 @@ private: void createTransparencyTables(); void updateSequenceBackgroundAnimations(); - bool _dialogueField; uint8 **_itemIconShapes; int _numItemIconShapes; uint8 **_itemShapes; @@ -913,7 +849,6 @@ private: int _numEffectShapes; const int8 *_gameShapeMap; - int _gameShapeMapSize; uint8 *_characterFaceShapes[40][3]; @@ -942,19 +877,13 @@ private: LoLCharacter *_characters; uint16 _activeCharsXpos[3]; - int _updateFlags; - int _updateCharNum; - int _updatePortraitSpeechAnimDuration; + int _portraitSpeechAnimMode; - int _resetPortraitAfterSpeechAnim; int _textColorFlag; - bool _fadeText; - int _needSceneRestore; uint32 _palUpdateTimer; uint32 _updatePortraitNext; int _loadLevelFlag; - int _hasTempDataFlags; int _activeMagicMenu; uint16 _scriptCharacterCycle; int _charStatsTemp[5]; @@ -963,15 +892,10 @@ private: int _charDefaultsSize; const uint16 *_charDefsMan; - int _charDefsManSize; const uint16 *_charDefsWoman; - int _charDefsWomanSize; const uint16 *_charDefsKieran; - int _charDefsKieranSize; const uint16 *_charDefsAkshel; - int _charDefsAkshelSize; const int32 *_expRequirements; - int _expRequirementsSize; // lamp void resetLampStatus(); @@ -983,16 +907,14 @@ private: int _lampOilStatus; uint32 _lampStatusTimer; bool _lampStatusSuspended; - uint8 _blockBrightness; // level void loadLevel(int index); void addLevelItems(); - void loadLevelWallData(int index, bool mapShapes); + void loadLevelWallData(int fileIndex, bool mapShapes); void assignBlockObject(LevelBlockProperty *l, uint16 item); - int assignLevelShapes(int index); - uint8 *getLevelShapes(int index); - void restoreBlockTempData(int index); + int assignLevelDecorationShapes(int index); + uint8 *getLevelDecorationShapes(int index); void restoreTempDataAdjustMonsterStrength(int index); void loadBlockProperties(const char *cmzFile); void loadLevelShpDat(const char *shpFile, const char *datFile, bool flag); @@ -1006,17 +928,7 @@ private: void drawScene(int pageNum); - void generateBlockDrawingBuffer(); - void generateVmpTileData(int16 startBlockX, uint8 startBlockY, uint8 wllVmpIndex, int16 vmpOffset, uint8 numBlocksX, uint8 numBlocksY); - void generateVmpTileDataFlipped(int16 startBlockX, uint8 startBlockY, uint8 wllVmpIndex, int16 vmpOffset, uint8 numBlocksX, uint8 numBlocksY); - bool hasWall(int index); - void assignVisibleBlocks(int block, int direction); - - void drawVcnBlocks(); void drawSceneShapes(); - void setLevelShapesDim(int index, int16 &x1, int16 &x2, int dim); - void scaleLevelShapesDim(int index, int16 &y1, int16 &y2, int dim); - void drawLevelModifyScreenDim(int dim, int16 x1, int16 y1, int16 x2, int16 y2); void drawDecorations(int index); void drawBlockEffects(int index, int type); void drawSpecialGuiShape(int pageNum); @@ -1033,27 +945,17 @@ private: void updateCompass(); void moveParty(uint16 direction, int unk1, int unk2, int buttonShape); - bool checkBlockPassability(uint16 block, uint16 direction); void notifyBlockNotPassable(int scrollFlag); + virtual bool checkBlockPassability(uint16 block, uint16 direction); - uint16 calcNewBlockPosition(uint16 curBlock, uint16 direction); uint16 calcBlockIndex(uint16 x, uint16 y); void calcCoordinates(uint16 &x, uint16 &y, int block, uint16 xOffs, uint16 yOffs); void calcCoordinatesForSingleCharacter(int charNum, uint16 &x, uint16 &y); void calcCoordinatesAddDirectionOffset(uint16 &x, uint16 &y, int direction); - int clickedWallShape(uint16 block, uint16 direction); - int clickedLeverOn(uint16 block, uint16 direction); - int clickedLeverOff(uint16 block, uint16 direction); - int clickedWallOnlyScript(uint16 block); int clickedDoorSwitch(uint16 block, uint16 direction); int clickedNiche(uint16 block, uint16 direction); - bool clickedShape(int shapeIndex); - void processDoorSwitch(uint16 block, int unk); - void openCloseDoor(uint16 block, int openClose); - void completeDoorOperations(); - void movePartySmoothScrollBlocked(int speed); void movePartySmoothScrollUp(int speed); void movePartySmoothScrollDown(int speed); @@ -1069,79 +971,40 @@ private: int smoothScrollDrawSpecialGuiShape(int pageNum); - OpenDoorState _openDoorState[3]; int _blockDoor; int _smoothScrollModeNormal; const uint8 *_scrollXTop; - int _scrollXTopSize; const uint8 *_scrollYTop; - int _scrollYTopSize; const uint8 *_scrollXBottom; - int _scrollXBottomSize; const uint8 *_scrollYBottom; - int _scrollYBottomSize; int _nextScriptFunc; - uint8 _currentLevel; - int _sceneDefaultUpdate; - int _lvlBlockIndex; int _lvlShapeIndex; bool _partyAwake; - uint8 *_vcnBlocks; - uint8 *_vcnShift; - uint8 *_vcnExpTable; - uint16 *_vmpPtr; - uint8 *_vcfBlocks; - uint16 *_blockDrawingBuffer; - uint8 *_sceneWindowBuffer; - LevelShapeProperty *_levelShapeProperties; - uint8 **_levelShapes; uint8 *_specialGuiShape; uint16 _specialGuiShapeX; uint16 _specialGuiShapeY; uint16 _specialGuiShapeMirrorFlag; - char _lastBlockDataFile[12]; char _lastOverridePalFile[12]; char *_lastOverridePalFilePtr; int _lastSpecialColor; int _lastSpecialColorWeight; - int _sceneDrawVarDown; - int _sceneDrawVarRight; - int _sceneDrawVarLeft; - int _wllProcessFlag; - uint8 *_transparencyTable2; uint8 *_transparencyTable1; int _loadSuppFilesFlag; - - uint8 *_wllVmpMap; - int8 *_wllShapeMap; - uint8 *_specialWallTypes; - uint8 *_wllBuffer4; - uint8 *_wllWallFlags; - - int16 *_lvlShapeTop; - int16 *_lvlShapeBottom; - int16 *_lvlShapeLeftRight; - - LevelBlockProperty *_levelBlockProperties; - LevelBlockProperty *_visibleBlocks[18]; + uint8 *_wllAutomapData; uint16 _partyPosX; uint16 _partyPosY; Common::SeekableReadStream *_lvlShpFileHandle; - uint16 _lvlShpNum; - uint16 _levelFileDataSize; - LevelShapeProperty *_levelFileData; - uint8 *_doorShapes[2]; int _shpDmX; int _shpDmY; uint16 _dmScaleW; @@ -1154,55 +1017,20 @@ private: uint8 *_tempBuffer5120; const char * const *_levelDatList; - int _levelDatListSize; const char * const *_levelShpList; - int _levelShpListSize; - const int8 *_dscUnk1; - int _dscUnk1Size; - const int8 *_dscShapeIndex; - int _dscShapeIndexSize; + const int8 *_dscWalls; + const uint8 *_dscOvlMap; - int _dscOvlMapSize; + const uint8 *_dscShapeOvlIndex; const uint16 *_dscShapeScaleW; - int _dscShapeScaleWSize; const uint16 *_dscShapeScaleH; - int _dscShapeScaleHSize; - const int16 *_dscShapeX; - int _dscShapeXSize; const int8 *_dscShapeY; - int _dscShapeYSize; - const uint8 *_dscTileIndex; - int _dscTileIndexSize; - const uint8 *_dscUnk2; - int _dscUnk2Size; - const uint8 *_dscDoorShpIndex; - int _dscDoorShpIndexSize; - const int8 *_dscDim1; - int _dscDim1Size; - const int8 *_dscDim2; - int _dscDim2Size; - const uint8 *_dscBlockMap; - int _dscBlockMapSize; - const uint8 *_dscDimMap; - int _dscDimMapSize; + const uint16 *_dscDoorMonsterScaleTable; - int _dscDoorMonsterScaleTableSize; const uint16 *_dscDoor4; - int _dscDoor4Size; - const uint8 *_dscShapeOvlIndex; - int _dscShapeOvlIndexSize; - const int8 *_dscBlockIndex; - int _dscBlockIndexSize; - const uint8 *_dscDoor1; - int _dscDoor1Size; const int16 *_dscDoorMonsterX; - int _dscDoorMonsterXSize; const int16 *_dscDoorMonsterY; - int _dscDoorMonsterYSize; - - int _sceneDrawPage1; - int _sceneDrawPage2; // items void giveCredits(int credits, int redraw); @@ -1239,8 +1067,7 @@ private: Item _itemInHand; Item _inventory[48]; Item _inventoryCurItem; - int _currentControlMode; - int _specialSceneFlag; + int _lastCharInventory; uint16 _charStatusFlags[3]; int _emcLastItem; @@ -1250,34 +1077,27 @@ private: EMCData _itemScript; const uint8 *_charInvIndex; - int _charInvIndexSize; const uint8 *_charInvDefs; - int _charInvDefsSize; const uint16 *_inventorySlotDesc; - int _inventorySlotDescSize; const uint16 *_itemCost; - int _itemCostSize; const uint8 *_stashSetupData; - int _stashSetupDataSize; const int8 *_sceneItemOffs; - int _sceneItemOffsSize; const FlyingObjectShape *_flyingItemShapes; - int _flyingItemShapesSize; // monsters void loadMonsterShapes(const char *file, int monsterIndex, int b); void releaseMonsterShapes(int monsterIndex); int deleteMonstersFromBlock(int block); - void setMonsterMode(MonsterInPlay *monster, int mode); - bool updateMonsterAdjustBlocks(MonsterInPlay *monster); - void placeMonster(MonsterInPlay *monster, uint16 x, uint16 y); + void setMonsterMode(LolMonsterInPlay *monster, int mode); + bool updateMonsterAdjustBlocks(LolMonsterInPlay *monster); + void placeMonster(LolMonsterInPlay *monster, uint16 x, uint16 y); int calcMonsterDirection(uint16 x1, uint16 y1, uint16 x2, uint16 y2); - void setMonsterDirection(MonsterInPlay *monster, int dir); - void monsterDropItems(MonsterInPlay *monster); + void setMonsterDirection(LolMonsterInPlay *monster, int dir); + void monsterDropItems(LolMonsterInPlay *monster); void removeAssignedObjectFromBlock(LevelBlockProperty *l, uint16 id); void removeDrawObjectFromBlock(LevelBlockProperty *l, uint16 id); void assignMonsterToBlock(uint16 *assignedBlockObjects, uint16 id); - void giveItemToMonster(MonsterInPlay *monster, Item item); + void giveItemToMonster(LolMonsterInPlay *monster, Item item); int checkBlockBeforeObjectPlacement(uint16 x, uint16 y, uint16 objectWidth, uint16 testFlag, uint16 wallFlag); int checkBlockForWallsAndSufficientSpace(int block, int x, int y, int objectWidth, int testFlag, int wallFlag); int calcMonsterSkillLevel(int id, int a); @@ -1288,7 +1108,7 @@ private: void drawBlockObjects(int blockArrayIndex); void drawMonster(uint16 id); - int getMonsterCurFrame(MonsterInPlay *m, uint16 dirFlags); + int getMonsterCurFrame(LolMonsterInPlay *m, uint16 dirFlags); void reassignDrawObjects(uint16 direction, uint16 itemIndex, LevelBlockProperty *l, bool flag); void redrawSceneItem(); int calcItemMonsterPosition(ItemInPlay *i, uint16 direction); @@ -1298,47 +1118,35 @@ private: uint8 *drawItemOrMonster(uint8 *shape, uint8 *monsterPalette, int x, int y, int fineX, int fineY, int flags, int tblValue, bool vflip); int calcDrawingLayerParameters(int srcX, int srcY, int &x2, int &y2, uint16 &w, uint16 &h, uint8 *shape, int vflip); - void updateMonster(MonsterInPlay *monster); - void moveMonster(MonsterInPlay *monster); - void walkMonster(MonsterInPlay *monster); - bool chasePartyWithDistanceAttacks(MonsterInPlay *monster); - void chasePartyWithCloseAttacks(MonsterInPlay *monster); - int walkMonsterCalcNextStep(MonsterInPlay *monster); - int getMonsterDistance(uint16 block1, uint16 block2); + void updateMonster(LolMonsterInPlay *monster); + void moveMonster(LolMonsterInPlay *monster); + void walkMonster(LolMonsterInPlay *monster); + bool chasePartyWithDistanceAttacks(LolMonsterInPlay *monster); + void chasePartyWithCloseAttacks(LolMonsterInPlay *monster); + int walkMonsterCalcNextStep(LolMonsterInPlay *monster); int checkForPossibleDistanceAttack(uint16 monsterBlock, int direction, int distance, uint16 curBlock); - int walkMonsterCheckDest(int x, int y, MonsterInPlay *monster, int unk); + int walkMonsterCheckDest(int x, int y, LolMonsterInPlay *monster, int unk); void getNextStepCoords(int16 monsterX, int16 monsterY, int &newX, int &newY, uint16 direction); - void rearrangeAttackingMonster(MonsterInPlay *monster); - void moveStrayingMonster(MonsterInPlay *monster); - void killMonster(MonsterInPlay *monster); - - MonsterInPlay *_monsters; - MonsterProperty *_monsterProperties; - uint8 **_monsterShapes; - uint8 **_monsterPalettes; - uint8 **_monsterShapesEx; + void rearrangeAttackingMonster(LolMonsterInPlay *monster); + void moveStrayingMonster(LolMonsterInPlay *monster); + void killMonster(LolMonsterInPlay *monster); + + LolMonsterInPlay *_monsters; + LolMonsterProperty *_monsterProperties; + uint8 **_monsterDecorationShapes; uint8 _monsterAnimType[3]; uint16 _monsterCurBlock; int _objectLastDirection; - int _monsterStepCounter; - int _monsterStepMode; const uint16 *_monsterModifiers; - int _monsterModifiersSize; const int8 *_monsterShiftOffs; - int _monsterShiftOffsSize; const uint8 *_monsterDirFlags; - int _monsterDirFlagsSize; const uint8 *_monsterScaleX; - int _monsterScaleXSize; const uint8 *_monsterScaleY; - int _monsterScaleYSize; const uint16 *_monsterScaleWH; - int _monsterScaleWHSize; // misc void delay(uint32 millis, bool doUpdate = false, bool isMainLoop = false); - int rollDice(int times, int pips); uint8 _compassBroken; uint8 _drainMagic; @@ -1349,7 +1157,7 @@ private: // spells typedef Common::Functor1Mem<ActiveSpell *, int, LoLEngine> SpellProc; - Common::Array<const SpellProc *> _spellProcs; + Common::Array<const SpellProc*> _spellProcs; typedef void (LoLEngine::*SpellProcCallback)(WSAMovie_v2 *, int, int); int castSpell(int charNum, int spellType, int spellLevel); @@ -1398,7 +1206,7 @@ private: int8 _availableSpells[8]; int _selectedSpell; const SpellProperty *_spellProperties; - int _spellPropertiesSize; + //int _spellPropertiesSize; int _subMenuIndex; LightningProperty *_lightningProps; @@ -1420,13 +1228,9 @@ private: static const MistOfDoomAnimData _mistAnimData[]; const uint8 *_updateSpellBookCoords; - int _updateSpellBookCoordsSize; const uint8 *_updateSpellBookAnimData; - int _updateSpellBookAnimDataSize; const uint8 *_healShapeFrames; - int _healShapeFramesSize; const int16 *_fireBallCoords; - int _fireBallCoordsSize; // fight int battleHitSkillTest(int16 attacker, int16 target, int skill); @@ -1437,8 +1241,8 @@ private: int calcInflictableDamagePerItem(int16 attacker, int16 target, uint16 itemMight, int index, int hitType); void checkForPartyDeath(); - void applyMonsterAttackSkill(MonsterInPlay *monster, int16 target, int16 damage); - void applyMonsterDefenseSkill(MonsterInPlay *monster, int16 attacker, int flags, int skill, int damage); + void applyMonsterAttackSkill(LolMonsterInPlay *monster, int16 target, int16 damage); + void applyMonsterDefenseSkill(LolMonsterInPlay *monster, int16 attacker, int flags, int skill, int damage); int removeCharacterItem(int charNum, int itemFlags); int paralyzePoisonCharacter(int charNum, int typeFlag, int immunityFlags, int hitChance, int redraw); void paralyzePoisonAllCharacters(int typeFlag, int immunityFlags, int hitChance); @@ -1478,7 +1282,6 @@ private: uint8 *_mapOverlay; const uint8 **_automapShapes; const uint16 *_autoMapStrings; - int _autoMapStringsSize; MapLegendData *_defaultLegendData; uint8 *_mapCursorOverlay; uint8 _automapTopLeftX; @@ -1495,10 +1298,15 @@ private: Common::Error loadGameState(int slot); Common::Error saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail); - Graphics::Surface *generateSaveThumbnail() const; + void *generateMonsterTempData(LevelTempData *tmp); + void *generateFlyingObjectTempData(LevelTempData *tmp); + void restoreBlockTempData(int levelIndex); + void restoreMonsterTempData(LevelTempData *tmp); + void restoreFlyingObjectTempData(LevelTempData *tmp); + void releaseMonsterTempData(LevelTempData *tmp); + void releaseFlyingObjectTempData(LevelTempData *tmp); - void generateTempData(); - LevelTempData *_lvlTempData[29]; + Graphics::Surface *generateSaveThumbnail() const; }; class HistoryPlayer { diff --git a/engines/kyra/loleobbase.cpp b/engines/kyra/loleobbase.cpp new file mode 100644 index 0000000000..05b683b830 --- /dev/null +++ b/engines/kyra/loleobbase.cpp @@ -0,0 +1,354 @@ +/* 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. + * + */ + +#if defined(ENABLE_EOB) || defined(ENABLE_LOL) + +#include "kyra/loleobbase.h" +#include "kyra/sound.h" + +#include "common/system.h" + +namespace Kyra { + +LolEobBaseEngine::LolEobBaseEngine(OSystem *system, const GameFlags &flags) : KyraEngine_v1(system, flags) { + _txt = 0; + _mouseClick = 0; + _preserveEvents = _buttonListChanged = false; + + _sceneXoffset = 0; + _sceneShpDim = 5; + _activeButtons = 0; + + _currentLevel = 0; + + _vmpPtr = 0; + _vcnBlocks = 0; + _vcfBlocks = 0; + _vcnShift = 0; + _vcnExpTable = 0; + _vmpPtr = 0; + _blockBrightness = _wllVcnOffset = 0; + _blockDrawingBuffer = 0; + _sceneWindowBuffer = 0; + _monsterShapes = _monsterPalettes = 0; + + _doorShapes = 0; + + _levelDecorationProperties = 0; + _levelDecorationData = 0; + _levelDecorationShapes = 0; + _decorationCount = 0; + _mappedDecorationsCount = 0; + memset(_visibleBlockIndex, 0, sizeof(_visibleBlockIndex)); + + _lvlShapeTop = _lvlShapeBottom = _lvlShapeLeftRight = 0; + _levelBlockProperties = 0; + _hasTempDataFlags = 0; + + _wllVmpMap = _specialWallTypes = _wllWallFlags = 0; + _wllShapeMap = 0; + + _sceneDrawVarDown = _sceneDrawVarRight = _sceneDrawVarLeft = _wllProcessFlag = 0; + + _currentBlock = 0; + _currentDirection = 0; + _compassDirection = -1; + _updateFlags = _clickedSpecialFlag = 0; + _sceneDefaultUpdate = 0; + _sceneUpdateRequired = false; + + _clickedShapeXOffs = _clickedShapeYOffs = 0; + + _dscShapeX = 0; + _dscTileIndex = 0; + _dscUnk2 = 0; + _dscDim1 = 0; + _dscDim2 = 0; + _dscBlockMap = 0; + _dscBlockIndex = 0; + _dscShapeIndex = 0; + _dscDimMap = 0; + _dscDoorShpIndex = 0; + _dscDoorY2 = 0; + + _shpDmX1 = _shpDmX2 = 0; + + memset(_openDoorState, 0, sizeof(_openDoorState)); + memset(_dialogueButtonString, 0, 3 * sizeof(const char *)); + _dialogueButtonPosX = 0; + _dialogueButtonPosY = 0; + _dialogueNumButtons = _dialogueButtonYoffs = _dialogueHighlightedButton = 0; + _currentControlMode = 0; + _specialSceneFlag = 0; + _dialogueButtonLabelCol1 = 9; + _dialogueButtonLabelCol2 = 15; + _dialogueButtonW = 95; + _dialogueButtonH = 9; + + _updateCharNum = -1; + _activeVoiceFileTotalTime = 0; + _updatePortraitSpeechAnimDuration = _resetPortraitAfterSpeechAnim = _needSceneRestore = 0; + _fadeText = false; + + memset(_lvlTempData, 0, sizeof(_lvlTempData)); + + _dialogueField = false; + + _environmentSfx = _environmentSfxVol = _envSfxDistThreshold = 0; + _monsterStepCounter = _monsterStepMode = 0; +} + +LolEobBaseEngine::~LolEobBaseEngine() { + delete[] _wllVmpMap; + delete[] _wllShapeMap; + delete[] _specialWallTypes; + delete[] _wllWallFlags; + + delete[] _vmpPtr; + delete[] _vcnExpTable; + delete[] _vcnBlocks; + delete[] _vcfBlocks; + delete[] _vcnShift; + delete[] _blockDrawingBuffer; + delete[] _sceneWindowBuffer; + + delete[] _lvlShapeTop; + delete[] _lvlShapeBottom; + delete[] _lvlShapeLeftRight; + + delete[] _doorShapes; + + delete[] _levelDecorationShapes; + delete[] _levelDecorationData; + delete[] _levelDecorationProperties; + delete[] _levelBlockProperties; + + delete _txt; + _txt = 0; +} + +Common::Error LolEobBaseEngine::init() { + _levelDecorationProperties = new LevelDecorationProperty[100]; + memset(_levelDecorationProperties, 0, 100 * sizeof(LevelDecorationProperty)); + _levelDecorationShapes = new uint8*[400]; + memset(_levelDecorationShapes, 0, 400 * sizeof(uint8*)); + _levelBlockProperties = new LevelBlockProperty[1025]; + memset(_levelBlockProperties, 0, 1025 * sizeof(LevelBlockProperty)); + + _wllVmpMap = new uint8[255]; + memset(_wllVmpMap, 0, 255); + _wllShapeMap = new int8[255]; + memset(_wllShapeMap, 0, 255); + _specialWallTypes = new uint8[255]; + memset(_specialWallTypes, 0, 255); + _wllWallFlags = new uint8[255]; + memset(_wllWallFlags, 0, 255); + + _blockDrawingBuffer = new uint16[1320]; + memset(_blockDrawingBuffer, 0, 1320 * sizeof(uint16)); + _sceneWindowBuffer = new uint8[21120]; + memset(_sceneWindowBuffer, 0, 21120); + + _lvlShapeTop = new int16[18]; + memset(_lvlShapeTop, 0, 18 * sizeof(int16)); + _lvlShapeBottom = new int16[18]; + memset(_lvlShapeBottom, 0, 18 * sizeof(int16)); + _lvlShapeLeftRight = new int16[36]; + memset(_lvlShapeLeftRight, 0, 36 * sizeof(int16)); + + _vcnExpTable = new uint8[128]; + for (int i = 0; i < 128; i++) + _vcnExpTable[i] = i & 0x0f; + + _doorShapes = new uint8*[6]; + memset(_doorShapes, 0, 6 * sizeof(uint8*)); + + initStaticResource(); + + _envSfxDistThreshold = (_sound->getSfxType() == Sound::kAdLib || _sound->getSfxType() == Sound::kPCSpkr) ? 15 : 3; + + return Common::kNoError; +} + +bool LolEobBaseEngine::posWithinRect(int mouseX, int mouseY, int x1, int y1, int x2, int y2) { + if (mouseX < x1 || mouseX > x2 || mouseY < y1 || mouseY > y2) + return false; + return true; +} + +void LolEobBaseEngine::drawDialogueButtons() { + int cp = screen()->setCurPage(0); + Screen::FontId of = screen()->setFont(gameFlags().use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT); + + for (int i = 0; i < _dialogueNumButtons; i++) { + int x = _dialogueButtonPosX[i]; + if (gameFlags().use16ColorMode) { + gui_drawBox(x, ((_dialogueButtonYoffs + _dialogueButtonPosY[i]) & ~7) - 1, 74, 10, 0xee, 0xcc, -1); + screen()->printText(_dialogueButtonString[i], (x + 37 - (screen()->getTextWidth(_dialogueButtonString[i])) / 2) & ~3, + ((_dialogueButtonYoffs + _dialogueButtonPosY[i]) + 2) & ~7, _dialogueHighlightedButton == i ? 0xc1 : 0xe1, 0); + } else { + gui_drawBox(x, (_dialogueButtonYoffs + _dialogueButtonPosY[i]), _dialogueButtonW, _dialogueButtonH, _color1_1, _color2_1, _bkgColor_1); + screen()->printText(_dialogueButtonString[i], x + (_dialogueButtonW >> 1) - (screen()->getTextWidth(_dialogueButtonString[i])) / 2, + (_dialogueButtonYoffs + _dialogueButtonPosY[i]) + 2, _dialogueHighlightedButton == i ? _dialogueButtonLabelCol1 : _dialogueButtonLabelCol2, 0); + } + } + screen()->setFont(of); + screen()->setCurPage(cp); +} + +uint16 LolEobBaseEngine::processDialogue() { + int df = _dialogueHighlightedButton; + int res = 0; + + for (int i = 0; i < _dialogueNumButtons; i++) { + int x = _dialogueButtonPosX[i]; + int y = (gameFlags().use16ColorMode ? ((_dialogueButtonYoffs + _dialogueButtonPosY[i]) & ~7) - 1 : (_dialogueButtonYoffs + _dialogueButtonPosY[i])); + Common::Point p = getMousePos(); + if (posWithinRect(p.x, p.y, x, y, x + _dialogueButtonW, y + _dialogueButtonH)) { + _dialogueHighlightedButton = i; + break; + } + } + + if (_dialogueNumButtons == 0) { + int e = checkInput(0, false) & 0xFF; + removeInputTop(); + + if (e) { + gui_notifyButtonListChanged(); + + if (e == _keyMap[Common::KEYCODE_SPACE] || e == _keyMap[Common::KEYCODE_RETURN]) { + snd_stopSpeech(true); + } + } + + if (snd_updateCharacterSpeech() != 2) { + res = 1; + if (!shouldQuit()) { + removeInputTop(); + gui_notifyButtonListChanged(); + } + } + } else { + int e = checkInput(0, false) & 0xFF; + removeInputTop(); + if (e) + gui_notifyButtonListChanged(); + + if (_flags.gameID == GI_LOL && (e == 200 || e == 202) || _flags.gameID != GI_LOL && (e == 199 || e == 201)) { + for (int i = 0; i < _dialogueNumButtons; i++) { + int x = _dialogueButtonPosX[i]; + int y = (gameFlags().use16ColorMode ? ((_dialogueButtonYoffs + _dialogueButtonPosY[i]) & ~7) - 1 : (_dialogueButtonYoffs + _dialogueButtonPosY[i])); + Common::Point p = getMousePos(); + if (posWithinRect(p.x, p.y, x, y, x + _dialogueButtonW, y + _dialogueButtonH)) { + _dialogueHighlightedButton = i; + res = _dialogueHighlightedButton + 1; + break; + } + } + } else if (e == _keyMap[Common::KEYCODE_SPACE] || e == _keyMap[Common::KEYCODE_RETURN]) { + snd_stopSpeech(true); + res = _dialogueHighlightedButton + 1; + } else if (e == _keyMap[Common::KEYCODE_LEFT] || e == _keyMap[Common::KEYCODE_DOWN]) { + if (_dialogueNumButtons > 1 && _dialogueHighlightedButton > 0) + _dialogueHighlightedButton--; + } else if (e == _keyMap[Common::KEYCODE_RIGHT] || e == _keyMap[Common::KEYCODE_UP]) { + if (_dialogueNumButtons > 1 && _dialogueHighlightedButton < (_dialogueNumButtons - 1)) + _dialogueHighlightedButton++; + } + } + + if (df != _dialogueHighlightedButton) + drawDialogueButtons(); + + screen()->updateScreen(); + + if (res == 0) + return 0; + + stopPortraitSpeechAnim(); + + if (game() == GI_LOL) { + if (!textEnabled() && _currentControlMode) { + screen()->setScreenDim(5); + const ScreenDim *d = screen()->getScreenDim(5); + screen()->fillRect(d->sx, d->sy + d->h - 9, d->sx + d->w - 1, d->sy + d->h - 1, d->unkA); + } else { + const ScreenDim *d = screen()->_curDim; + if (gameFlags().use16ColorMode) + screen()->fillRect(d->sx, d->sy, d->sx + d->w - 3, d->sy + d->h - 2, d->unkA); + else + screen()->fillRect(d->sx, d->sy, d->sx + d->w - 2, d->sy + d->h - 1, d->unkA); + txt()->clearDim(4); + txt()->resetDimTextPositions(4); + } + } + + return res; +} + +void LolEobBaseEngine::delayUntil(uint32 time, bool doUpdate, bool isMainLoop) { + uint32 curTime = _system->getMillis(); + if (time > curTime) + delay(time - curTime, doUpdate, isMainLoop); +} + +int LolEobBaseEngine::rollDice(int times, int pips, int inc) { + if (times <= 0 || pips <= 0) + return inc; + + int res = 0; + while (times--) + res += _rnd.getRandomNumberRng(1, pips); + + return res + inc; +} + +bool LolEobBaseEngine::snd_processEnvironmentalSoundEffect(int soundId, int block) { + if (!_sound->sfxEnabled() || shouldQuit()) + return false; + + if (_environmentSfx) + snd_playSoundEffect(_environmentSfx, _environmentSfxVol); + + int dist = 0; + if (block) { + dist = getBlockDistance(_currentBlock, block); + if (dist > _envSfxDistThreshold) { + _environmentSfx = 0; + return false; + } + } + + _environmentSfx = soundId; + _environmentSfxVol = (15 - ((block || (_flags.gameID == GI_LOL && dist < 2)) ? dist : 0)) << 4; + + return true; +} + +void LolEobBaseEngine::updateEnvironmentalSfx(int soundId) { + snd_processEnvironmentalSoundEffect(soundId, _currentBlock); +} + +} // End of namespace Kyra + +#endif // ENABLE_EOB || ENABLE_LOL diff --git a/engines/kyra/loleobbase.h b/engines/kyra/loleobbase.h new file mode 100644 index 0000000000..042f9c0e78 --- /dev/null +++ b/engines/kyra/loleobbase.h @@ -0,0 +1,324 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef KYRA_LOLEOBBASE_H +#define KYRA_LOLEOBBASE_H + +#if defined(ENABLE_EOB) || defined(ENABLE_LOL) + +#include "kyra/kyra_v1.h" +#include "kyra/screen_eob.h" +#include "kyra/gui_eob.h" +#include "kyra/text_lol.h" + +namespace Kyra { + +struct LevelDecorationProperty { + uint16 shapeIndex[10]; + uint8 scaleFlag[10]; + int16 shapeX[10]; + int16 shapeY[10]; + int8 next; + uint8 flags; +}; + +struct LevelBlockProperty { + uint8 walls[4]; + uint16 assignedObjects; + uint16 drawObjects; + uint8 direction; + uint16 flags; +}; + +struct OpenDoorState { + uint16 block; + int8 wall; + int8 state; +}; + +struct LevelTempData { + uint8 *wallsXorData; + uint16 *flags; + void *monsters; + void *flyingObjects; + uint8 monsterDifficulty; +}; + +class LolEobBaseEngine : public KyraEngine_v1 { +friend class TextDisplayer_Eob; +public: + LolEobBaseEngine(OSystem *system, const GameFlags &flags); + virtual ~LolEobBaseEngine(); + + virtual Screen *screen() = 0; + virtual GUI *gui() const = 0; + +protected: + // Startup + virtual Common::Error init(); + virtual Common::Error go() = 0; + + // Init + void initStaticResource(); + + const uint8 **_itemIconShapes; + + // Main loop + virtual void update() = 0; + void updateEnvironmentalSfx(int soundId); + + // timers + virtual void setupTimers() = 0; + void enableSysTimer(int sysTimer); + void disableSysTimer(int sysTimer); + void enableTimer(int id); + virtual uint8 getClock2Timer(int index) = 0; + virtual uint8 getNumClock2Timers() = 0; + + void timerProcessDoors(int timerNum); + + // mouse + bool posWithinRect(int mouseX, int mouseY, int x1, int y1, int x2, int y2); + virtual void setHandItem(Item itemIndex) = 0; + + // Characters + int _updateCharNum; + int _updatePortraitSpeechAnimDuration; + bool _fadeText; + int _resetPortraitAfterSpeechAnim; + int _needSceneRestore; + + // Items + int _itemInHand; + + // Monsters + int getBlockDistance(uint16 block1, uint16 block2); + + uint8 **_monsterPalettes; + uint8 **_monsterShapes; + + int16 _shpDmX1; + int16 _shpDmX2; + + int _monsterStepCounter; + int _monsterStepMode; + + // Level + virtual void addLevelItems() = 0; + virtual void loadBlockProperties(const char *file) = 0; + + virtual void drawScene(int pageNum) = 0; + virtual void drawSceneShapes() = 0; + virtual void drawDecorations(int index) = 0; + + void setLevelShapesDim(int index, int16 &x1, int16 &x2, int dim); + void scaleLevelShapesDim(int index, int16 &y1, int16 &y2, int dim); + void drawLevelModifyScreenDim(int dim, int16 x1, int16 y1, int16 x2, int16 y2); + void generateBlockDrawingBuffer(); + void generateVmpTileData(int16 startBlockX, uint8 startBlockY, uint8 wllVmpIndex, int16 vmpOffset, uint8 numBlocksX, uint8 numBlocksY); + void generateVmpTileDataFlipped(int16 startBlockX, uint8 startBlockY, uint8 wllVmpIndex, int16 vmpOffset, uint8 numBlocksX, uint8 numBlocksY); + bool hasWall(int index); + void assignVisibleBlocks(int block, int direction); + bool checkSceneUpdateNeed(int block); + void drawVcnBlocks(); + uint16 calcNewBlockPosition(uint16 curBlock, uint16 direction); + + virtual int clickedDoorSwitch(uint16 block, uint16 direction) = 0; + int clickedWallShape(uint16 block, uint16 direction); + int clickedLeverOn(uint16 block, uint16 direction); + int clickedLeverOff(uint16 block, uint16 direction); + int clickedWallOnlyScript(uint16 block); + virtual int clickedNiche(uint16 block, uint16 direction) = 0; + + void processDoorSwitch(uint16 block, int openClose); + void openCloseDoor(int block, int openClose); + void completeDoorOperations(); + + uint8 *_wllVmpMap; + int8 *_wllShapeMap; + uint8 *_specialWallTypes; + uint8 *_wllWallFlags; + + int _sceneXoffset; + int _sceneShpDim; + + LevelBlockProperty *_levelBlockProperties; + LevelBlockProperty *_visibleBlocks[18]; + LevelDecorationProperty *_levelDecorationData; + uint16 _levelDecorationDataSize; + LevelDecorationProperty *_levelDecorationProperties; + uint8 **_levelDecorationShapes; + uint16 _decorationCount; + int16 _mappedDecorationsCount; + uint16 *_vmpPtr; + uint8 *_vcnBlocks; + uint8 *_vcfBlocks; + uint8 *_vcnShift; + uint8 *_vcnExpTable; + uint16 *_blockDrawingBuffer; + uint8 *_sceneWindowBuffer; + uint8 _blockBrightness; + uint8 _wllVcnOffset; + + uint8 **_doorShapes; + + uint8 _currentLevel; + uint16 _currentBlock; + uint16 _currentDirection; + int _sceneDefaultUpdate; + bool _sceneUpdateRequired; + + int16 _visibleBlockIndex[18]; + int16 *_lvlShapeLeftRight; + int16 *_lvlShapeTop; + int16 *_lvlShapeBottom; + + char _lastBlockDataFile[13]; + uint32 _hasTempDataFlags; + + int16 _sceneDrawVarDown; + int16 _sceneDrawVarRight; + int16 _sceneDrawVarLeft; + int _wllProcessFlag; + + OpenDoorState _openDoorState[3]; + + int _sceneDrawPage1; + int _sceneDrawPage2; + + const int8 *_dscShapeIndex; + const uint8 *_dscDimMap; + const int8 *_dscDim1; + const int8 *_dscDim2; + const int16 *_dscShapeX; + const uint8 *_dscUnk2; + const uint8 *_dscBlockMap; + const int8 *_dscBlockIndex; + const uint8 *_dscTileIndex; + + const uint8 *_dscDoorShpIndex; + const uint8 *_dscDoorY2; + + // Script + virtual void runLevelScript(int block, int flags) = 0; + + // Gui + void removeInputTop(); + void gui_drawBox(int x, int y, int w, int h, int frameColor1, int frameColor2, int fillColor); + virtual void gui_drawHorizontalBarGraph(int x, int y, int w, int h, int32 curVal, int32 maxVal, int col1, int col2); + void gui_initButtonsFromList(const int16 *list); + virtual void gui_initButton(int index, int x = -1, int y = -1, int val = -1) = 0; + void gui_resetButtonList(); + void gui_notifyButtonListChanged(); + + bool clickedShape(int shapeIndex); + + int _clickedShapeXOffs; + int _clickedShapeYOffs; + + Button *_activeButtons; + Button _activeButtonData[70]; + + uint8 _mouseClick; + bool _preserveEvents; + bool _buttonListChanged; + + int _updateFlags; + int _clickedSpecialFlag; + + int _compassDirection; + + static const uint8 _dropItemDirIndex[]; + + // text + void drawDialogueButtons(); + uint16 processDialogue(); + + TextDisplayer_Eob *_txt; + virtual TextDisplayer_Eob *txt() { return _txt; } + + bool _dialogueField; + + const char *_dialogueButtonString[9]; + const uint16 *_dialogueButtonPosX; + const uint8 *_dialogueButtonPosY; + int16 _dialogueButtonYoffs; + uint16 _dialogueButtonW; + uint16 _dialogueButtonH; + int _dialogueNumButtons; + int _dialogueHighlightedButton; + int _currentControlMode; + int _specialSceneFlag; + uint8 _dialogueButtonLabelCol1; + uint8 _dialogueButtonLabelCol2; + + int _bkgColor_1; + uint8 _color1_1; + uint8 _color2_1; + + const char *const *_moreStrings; + + // misc + virtual void delay(uint32 millis, bool doUpdate = false, bool isMainLoop = false) = 0; + void delayUntil(uint32 time, bool doUpdate = false, bool isMainLoop = false); + int rollDice(int times, int pips, int inc = 0); + + virtual Common::Error loadGameState(int slot) = 0; + virtual Common::Error saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail) = 0; + + void generateTempData(); + void restoreBlockTempData(int levelIndex); + void releaseTempData(); + virtual void *generateMonsterTempData(LevelTempData *tmp) = 0; + virtual void *generateFlyingObjectTempData(LevelTempData *tmp) = 0; + virtual void restoreMonsterTempData(LevelTempData *tmp) = 0; + virtual void restoreFlyingObjectTempData(LevelTempData *tmp) = 0; + virtual void releaseMonsterTempData(LevelTempData *tmp) = 0; + virtual void releaseFlyingObjectTempData(LevelTempData *tmp) = 0; + + LevelTempData *_lvlTempData[29]; + + // sound + virtual bool snd_processEnvironmentalSoundEffect(int soundId, int block); + virtual void snd_stopSpeech(bool) {} + virtual int snd_updateCharacterSpeech() { return 0; } + virtual void stopPortraitSpeechAnim() {} + virtual void setupOpcodeTable() {} + virtual void snd_playVoiceFile(int) {} + + int _environmentSfx; + int _environmentSfxVol; + int _envSfxDistThreshold; + + uint32 _activeVoiceFileTotalTime; + + // unused + void setWalkspeed(uint8) {} + void removeHandItem() {} + bool lineIsPassable(int, int) { return false; } +}; + +} // End of namespace Kyra + +#endif // ENABLE_EOB || ENABLE_LOL + +#endif
\ No newline at end of file diff --git a/engines/kyra/magic_eob.cpp b/engines/kyra/magic_eob.cpp new file mode 100644 index 0000000000..aefe20c06a --- /dev/null +++ b/engines/kyra/magic_eob.cpp @@ -0,0 +1,780 @@ +/* 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. + * + */ + +#ifdef ENABLE_EOB + +#include "kyra/eobcommon.h" +#include "kyra/resource.h" + +namespace Kyra { + +void EobCoreEngine::useMagicBookOrSymbol(int charIndex, int type) { + EobCharacter *c = &_characters[charIndex]; + _openBookSpellLevel = c->slotStatus[3]; + _openBookSpellSelectedItem = c->slotStatus[2]; + _openBookSpellListOffset = c->slotStatus[4]; + _openBookChar = charIndex; + _openBookType = type; + _openBookSpellList = (type == 1) ? _clericSpellList : _mageSpellList; + _openBookAvailableSpells = (type == 1) ? c->clericSpells : c->mageSpells; + int8 *tmp = _openBookAvailableSpells + _openBookSpellLevel * 10 + _openBookSpellListOffset + _openBookSpellSelectedItem; + + if (*tmp <= 0) { + for (bool loop = true; loop && _openBookSpellSelectedItem < 10; ) { + tmp = _openBookAvailableSpells + _openBookSpellLevel * 10 + _openBookSpellListOffset + _openBookSpellSelectedItem; + if (*tmp > 0) { + if (_openBookSpellSelectedItem > 5) { + _openBookSpellListOffset = 6; + _openBookSpellSelectedItem -= 6; + } + loop = false; + } else { + _openBookSpellSelectedItem++; + } + } + + if (_openBookSpellSelectedItem == 10) { + _openBookSpellListOffset = 0; + _openBookSpellSelectedItem = 6; + } + } + + if (!_updateFlags) + _screen->copyRegion(64, 121, 0, 0, 112, 56, 0, 10, Screen::CR_NO_P_CHECK); + _updateFlags = 1; + gui_setPlayFieldButtons(); + gui_drawSpellbook(); +} + +void EobCoreEngine::useMagicScroll(int charIndex, int type, int weaponSlot) { + _openBookCharBackup = _openBookChar; + _openBookTypeBackup = _openBookType; + _castScrollSlot = weaponSlot + 1; + _openBookChar = charIndex; + _openBookType = type <= _clericSpellOffset ? 0 : 1; + castSpell(type, weaponSlot); +} + +void EobCoreEngine::usePotion(int charIndex, int weaponSlot) { + EobCharacter *c = &_characters[_openBookChar]; + int offset = (_flags.gameID == GI_EOB1) ? 13 : (_flags.lang == Common::DE_DEU ? 19 : 15); + + int val = deleteInventoryItem(charIndex, weaponSlot); + snd_playSoundEffect(10); + + switch (val) { + case 0: + sparkEffectDefensive(charIndex); + c->strengthCur = 22; + c->strengthExtCur = 0; + setCharEventTimer(charIndex, 546 * rollDice(1, 4, 4), 7, 1); + break; + + case 1: + sparkEffectDefensive(charIndex); + modifyCharacterHitpoints(charIndex, rollDice(2, 4, 2)); + break; + + case 2: + sparkEffectDefensive(charIndex); + modifyCharacterHitpoints(charIndex, rollDice(3, 8, 3)); + break; + + case 3: + statusAttack(charIndex, 2, _itemExtraStrings[offset], 0, 1, 8, 1); + c->effectFlags &= ~0x2000; + if (c->flags & 2) + return; + break; + + case 4: + sparkEffectDefensive(charIndex); + c->food = 100; + if (_currentControlMode) + gui_drawCharPortraitWithStats(charIndex); + break; + + case 5: + sparkEffectDefensive(charIndex); + c->effectFlags |= 0x10000; + setCharEventTimer(charIndex, 546 * rollDice(1, 4, 4), 12, 1); + snd_playSoundEffect(100); + gui_drawCharPortraitWithStats(charIndex); + break; + + case 6: + sparkEffectDefensive(charIndex); + c->effectFlags |= 0x40; + gui_drawCharPortraitWithStats(charIndex); + break; + + case 7: + sparkEffectDefensive(charIndex); + neutralizePoison(charIndex); + break; + + default: + break; + } + + _txt->printMessage(_itemExtraStrings[offset + 1], -1, c->name, _potionEffectStrings[val]); +} + +void EobCoreEngine::castSpell(int spell, int weaponSlot) { + EobSpell *s = &_spells[spell]; + EobCharacter *c = &_characters[_openBookChar]; + _activeSpell = spell; + + if ((s->flags & 0x100) && (c->effectFlags & 0x40)) + // remove invisibility effect + removeCharacterEffect(10, _openBookChar, 1); + + int ci = _openBookChar; + if (ci > 3) + ci -= 2; + + _activeSpellCasterPos = _dropItemDirIndex[(_currentDirection << 2) + ci]; + + if (s->flags & 0x400) { + if (c->inventory[0] && c->inventory[1]) { + _txt->printMessage(_magicStrings1[2]); + snd_playSoundEffect(79); + return; + } + + if (isMagicWeapon(c->inventory[0]) || isMagicWeapon(c->inventory[1])) { + _txt->printMessage(_magicStrings1[3]); + snd_playSoundEffect(79); + return; + } + } + + if (!(_flags.gameID == GI_EOB2 && _activeSpell == 62)) { + if (!_castScrollSlot) { + int8 tmp = _openBookAvailableSpells[_openBookSpellLevel * 10 + _openBookSpellListOffset + _openBookSpellSelectedItem]; + if (_openBookSpellListOffset + _openBookSpellSelectedItem < 8) + memmove(&_openBookAvailableSpells[_openBookSpellLevel * 10 + _openBookSpellListOffset + _openBookSpellSelectedItem], &_openBookAvailableSpells[_openBookSpellLevel * 10 + _openBookSpellListOffset + _openBookSpellSelectedItem + 1], 8 - (_openBookSpellListOffset + _openBookSpellSelectedItem)); + _openBookAvailableSpells[_openBookSpellLevel * 10 + 8] = -tmp; + if (_openBookAvailableSpells[_openBookSpellLevel * 10 + _openBookSpellListOffset + _openBookSpellSelectedItem] < 0) { + if (--_openBookSpellSelectedItem == -1) + if (_openBookSpellListOffset) { + _openBookSpellListOffset = 0; + _openBookSpellSelectedItem = 5; + } else { + _openBookSpellSelectedItem = 6; + } + } + } else if (weaponSlot != -1) { + updateUsedCharacterHandItem(_openBookChar, weaponSlot); + } + } + + _txt->printMessage(_magicStrings1[4], -1, c->name, s->name); + + if (s->flags & 0x20) { + castOnWhomDialogue(); + return; + } + + _activeSpellCaster = _openBookChar; + startSpell(spell); +} + +void EobCoreEngine::removeCharacterEffect(int spell, int charIndex, int showWarning) { + EobCharacter *c = &_characters[charIndex]; + EobSpell *s = &_spells[spell]; + + if (showWarning) { + _txt->printMessage(_magicStrings3[2], -1, c->name, s->name); + snd_playSoundEffect(79); + } + + if (s->endCallback) + (this->*s->endCallback)(0); + + if (s->flags & 1) + c->effectFlags &= ~s->effectFlags; + + if (s->flags & 4) + _partyEffectFlags &= ~s->effectFlags; + + if (s->flags & 0x200) { + for (int i = 0; i < 6; i++) { + if (!testCharacter(i, 1)) + continue; + if (!testCharacter(i, 2) && !(s->flags & 0x800)) + continue; + _characters[i].effectFlags &= ~s->effectFlags; + } + } + + if (s->flags & 0x2) + recalcArmorClass(_activeSpellCaster); + + if (showWarning) { + if (s->flags & 0x20A0) + gui_drawCharPortraitWithStats(charIndex); + else if (s->flags & 0x20A0) + gui_drawAllCharPortraitsWithStats(); + } +} + +void EobCoreEngine::removeAllCharacterEffects(int charIndex) { + EobCharacter *c = &_characters[charIndex]; + memset(c->effectsRemainder, 0, 4); + + for (int i = 0; i < 10; i++) { + if (c->events[i] < 0) + removeCharacterEffect(-c->events[i], charIndex, 0); + c->timers[i] = 0; + c->events[i] = 0; + } + + setupCharacterTimers(); + recalcArmorClass(charIndex); + c->disabledSlots = 0; + c->slotStatus[0] = c->slotStatus[1] = 0; + c->damageTaken = 0; + c->strengthCur = c->strengthMax; + c->strengthExtCur = c->strengthExtMax; + gui_drawAllCharPortraitsWithStats(); +} + +void EobCoreEngine::castOnWhomDialogue() { + _txt->printMessage(_magicStrings3[0]); + snd_playSoundEffect(79); + gui_setCastOnWhomButtons(); +} + +void EobCoreEngine::startSpell(int spell) { + EobSpell *s = &_spells[spell]; + EobCharacter *c = &_characters[_activeSpellCaster]; + snd_playSoundEffect(s->sound); + + if (s->flags & 0xa0) + sparkEffectDefensive(_activeSpellCaster); + else if (s->flags & 0x40) + sparkEffectDefensive(-1); + else if (s->flags & 0x1000) + sparkEffectOffensive(); + + if (s->flags & 0x20) { + _txt->printMessage(c->name); + _txt->printMessage(_magicStrings1[5]); + } + + if ((s->flags & 0x30) && (s->effectFlags & c->effectFlags)) { + _txt->printMessage(_magicStrings7[0], -1, c->name, s->name); + snd_playSoundEffect(79); + } else if ((s->flags & 0x50) && (s->effectFlags & _partyEffectFlags)) { + _txt->printMessage(_magicStrings7[1], -1, s->name); + snd_playSoundEffect(79); + } else { + if (s->flags & 8) + setSpellEventTimer(spell, s->timingPara[0], s->timingPara[1], s->timingPara[2], s->timingPara[3]); + + _returnAfterSpellCallback = false; + if (s->startCallback) + (this->*s->startCallback)(); + if (_returnAfterSpellCallback) + return; + + if (s->flags & 1) + c->effectFlags |= s->effectFlags; + if (s->flags & 4) + _partyEffectFlags |= s->effectFlags; + + if (s->flags & 0x200) { + for (int i = 0; i < 6; i++) { + if (!testCharacter(i, 1)) + continue; + if (!testCharacter(i, 2) && !(s->flags & 0x800)) + continue; + _characters[i].effectFlags |= s->effectFlags; + } + } + + if (s->flags & 2) + recalcArmorClass(_activeSpellCaster); + + if (s->flags & 0x20A0) + gui_drawCharPortraitWithStats(_activeSpellCaster); + if (s->flags & 0x40) + gui_drawAllCharPortraitsWithStats(); + } + + if (_castScrollSlot) { + gui_updateSlotAfterScrollUse(); + } else { + c->disabledSlots |= 4; + setCharEventTimer(_openBookChar, 72, 11, 1); + gui_toggleButtons(); + gui_drawSpellbook(); + } + + if (_flags.gameID == GI_EOB2) { + //_castSpellWd1 = spell; + runLevelScript(_currentBlock, 0x800); + //_castSpellWd1 = 0; + } +} + +void EobCoreEngine::sparkEffectDefensive(int charIndex) { + int first = charIndex; + int last = charIndex; + if (charIndex == -1) { + first = 0; + last = 5; + } + + for (int i = 0; i < 8; i++) { + for (int ii = first; ii <= last; ii++) { + if (!testCharacter(ii, 1) || (_currentControlMode && ii != _updateCharNum)) + continue; + + gui_drawCharPortraitWithStats(ii); + + for (int iii = 0; iii < 4; iii++) { + int shpIndex = ((_sparkEffectDefSteps[i] & _sparkEffectDefSubSteps[iii]) >> _sparkEffectDefShift[iii]); + if (!shpIndex) + continue; + int x = _sparkEffectDefAdd[iii * 2] - 8; + int y = _sparkEffectDefAdd[iii * 2 + 1]; + if (_currentControlMode) { + x += 181; + y += 3; + } else { + x += (_sparkEffectDefX[ii] << 3); + y += _sparkEffectDefY[ii]; + } + _screen->drawShape(0, _sparkShapes[shpIndex - 1], x, y, 0); + _screen->updateScreen(); + } + } + resetSkipFlag(); + delay(2 * _tickLength); + } + + for (int i = first; i < last; i++) + gui_drawCharPortraitWithStats(i); +} + +void EobCoreEngine::sparkEffectOffensive() { + disableSysTimer(2); + _screen->copyRegion(0, 0, 0, 0, 176, 120, 0, 2, Screen::CR_NO_P_CHECK); + + for (int i = 0; i < 16; i++) + _screen->copyRegionToBuffer(0, _sparkEffectOfX[i], _sparkEffectOfY[i], 16, 16, &_spellAnimBuffer[i << 8]); + _screen->updateScreen(); + + for (int i = 0; i < 11; i++) { + for (int ii = 0; ii < 16; ii++) + _screen->copyBlockToPage(2, _sparkEffectOfX[ii], _sparkEffectOfY[ii], 16, 16, &_spellAnimBuffer[ii << 8]); + + for (int ii = 0; ii < 16; ii++) { + int shpIndex = (_sparkEffectOfFlags1[i] & _sparkEffectOfFlags2[ii]) >> _sparkEffectOfShift[ii]; + if (shpIndex) + _screen->drawShape(2, _sparkShapes[shpIndex - 1], _sparkEffectOfX[ii], _sparkEffectOfY[ii], 0); + } + resetSkipFlag(); + delay(2 * _tickLength); + _screen->copyRegion(0, 0, 0, 0, 176, 120, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + } + + for (int i = 0; i < 16; i++) + _screen->copyBlockToPage(0, _sparkEffectOfX[i], _sparkEffectOfY[i], 16, 16, &_spellAnimBuffer[i << 8]); + + _screen->updateScreen(); + enableSysTimer(2); +} + +void EobCoreEngine::setSpellEventTimer(int spell, int timerBaseFactor, int timerLength, int timerLevelFactor, int updateExistingTimer) { + int l = _openBookType == 1 ? getCharacterClericPaladinLevel(_openBookChar) : getCharacterMageLevel(_openBookChar); + uint32 countdown = timerLength * timerBaseFactor + timerLength * l * timerLevelFactor; + setCharEventTimer(_activeSpellCaster, countdown, -spell, updateExistingTimer); +} + +bool EobCoreEngine::magicObjectHit(EobFlyingObject *fo, int dcTimes, int dcPips, int dcOffs, int level) { + int ignoreAttackerId = fo->flags & 0x10; + int singleTargetCheckAdjacent = fo->flags & 1; + int blockDamage = fo->flags & 2; + int hitTest = fo->flags & 4; + + int s = 5; + int dmgType = 3; + if (fo->flags & 8) { + s = 4; + dmgType = 0; + } + + int dmgFlag = _spells[fo->callBackIndex].damageFlags; + if (fo->attackerId >= 0) + dmgFlag |= 0x800; + + bool res = false; + if (!level) + level = 1; + + if ((_levelBlockProperties[fo->curBlock].flags & 7) && (fo->attackerId >= 0 || ignoreAttackerId)) { + _inflictMonsterDamageUnk = 1; + + for (const int16 *m = findBlockMonsters(fo->curBlock, fo->curPos, fo->direction, blockDamage, singleTargetCheckAdjacent); *m != -1; m++) { + int dmg = rollDice(dcTimes, dcPips, dcOffs) * level; + + if (hitTest) { + if (!characterAttackHitTest(fo->attackerId, *m, 0, 0)) + continue; + } + + calcAndInflictMonsterDamage(&_monsters[*m], 0, 0, dmg, dmgFlag, s, dmgType); + res = true; + } + updateAllMonsterShapes(); + + } else if (fo->curBlock == _currentBlock && (fo->attackerId < 0 || ignoreAttackerId)) { + if (blockDamage) { + for (int i = 0; i < 6; i++) { + if (!testCharacter(i, 1)) + continue; + if (hitTest && !monsterAttackHitTest(&_monsters[0], i)) + continue; + + int dmg = rollDice(dcTimes, dcPips, dcOffs) * level; + res = true; + + calcAndInflictCharacterDamage(i, 0, 0, dmg, dmgFlag, s, dmgType); + } + } else { + int c = _dscItemPosIndex[(_currentDirection << 2) + (fo->curPos & 3)]; + if ((c > 2) && (testCharacter(5, 1) || testCharacter(6, 1)) && rollDice(1, 2, -1)) + c += 2; + + if (!fo->item && (_characters[c].effectFlags & 8)) { + res = true; + } else { + if ((_characters[c].flags & 1) && hitTest && !monsterAttackHitTest(&_monsters[0], c)) { + int dmg = rollDice(dcTimes, dcPips, dcOffs) * level; + res = true; + calcAndInflictCharacterDamage(c, 0, 0, dmg, dmgFlag, s, dmgType); + } + } + } + } + + if (res && (fo->flags & 0x40)) + updateFlyingObject_s3(fo); + else if ((_flags.gameID == GI_EOB1 && fo->item == 5) || (_flags.gameID == GI_EOB2 && fo->item == 4)) + res = false; + + return res; +} + +void EobCoreEngine::spellCallback_start_armor() { + +} + +void EobCoreEngine::spellCallback_start_burningHands() { + +} + +void EobCoreEngine::spellCallback_start_detectMagic() { + +} + +bool EobCoreEngine::spellCallback_end_detectMagic(EobFlyingObject*) { + return true; +} + +void EobCoreEngine::spellCallback_start_magicMissile() { + launchMagicObject(_openBookChar, 0, _currentBlock, _activeSpellCasterPos, _currentDirection); +} + +bool EobCoreEngine::spellCallback_end_magicMissile(EobFlyingObject *fo) { + return magicObjectHit(fo, 1, 4, 1, (getCharacterMageLevel(fo->attackerId) - 1) >> 1); +} + +void EobCoreEngine::spellCallback_start_shockingGrasp() { + +} + +bool EobCoreEngine::spellCallback_end_shockingGraspFlameBlade(EobFlyingObject*) { + return true; +} + +void EobCoreEngine::spellCallback_start_improvedIdentify() { + +} + +void EobCoreEngine::spellCallback_start_melfsAcidArrow() { + launchMagicObject(_openBookChar, 1, _currentBlock, _activeSpellCasterPos, _currentDirection); +} + +bool EobCoreEngine::spellCallback_end_melfsAcidArrow(EobFlyingObject *fo) { + return magicObjectHit(fo, 2, 4, 0, getCharacterMageLevel(fo->attackerId) / 3); +} + +void EobCoreEngine::spellCallback_start_dispelMagic() { + +} + +void EobCoreEngine::spellCallback_start_fireball() { + launchMagicObject(_openBookChar, 2, _currentBlock, _activeSpellCasterPos, _currentDirection); +} + +bool EobCoreEngine::spellCallback_end_fireball(EobFlyingObject *fo) { + return magicObjectHit(fo, 1, 6, 0, getCharacterMageLevel(fo->attackerId)); +} + +void EobCoreEngine::spellCallback_start_flameArrow() { + launchMagicObject(_openBookChar, 3, _currentBlock, _activeSpellCasterPos, _currentDirection); +} + +bool EobCoreEngine::spellCallback_end_flameArrow(EobFlyingObject *fo) { + return magicObjectHit(fo, 5, 6, 0, getCharacterMageLevel(fo->attackerId)); +} + +void EobCoreEngine::spellCallback_start_holdPerson() { + launchMagicObject(_openBookChar, _flags.gameID == GI_EOB1 ? 4 : 3, _currentBlock, _activeSpellCasterPos, _currentDirection); +} + +bool EobCoreEngine::spellCallback_end_holdPerson(EobFlyingObject *fo) { + return true; +} + +void EobCoreEngine::spellCallback_start_lightningBolt() { + launchMagicObject(_openBookChar, _flags.gameID == GI_EOB1 ? 5 : 4, _currentBlock, _activeSpellCasterPos, _currentDirection); +} + +bool EobCoreEngine::spellCallback_end_lightningBolt(EobFlyingObject *fo) { + return magicObjectHit(fo, 1, 6, 0, getCharacterMageLevel(fo->attackerId)); +} + +void EobCoreEngine::spellCallback_start_vampiricTouch() { + +} + +bool EobCoreEngine::spellCallback_end_vampiricTouch(EobFlyingObject*) { + return true; +} + +void EobCoreEngine::spellCallback_start_fear() { + +} + +void EobCoreEngine::spellCallback_start_iceStorm() { + launchMagicObject(_openBookChar, _flags.gameID == GI_EOB1 ? 6 : 5, _currentBlock, _activeSpellCasterPos, _currentDirection); +} + +bool EobCoreEngine::spellCallback_end_iceStorm(EobFlyingObject *fo) { + static int8 blockAdv[] = { -32, 32, 1, -1 }; + bool res = magicObjectHit(fo, 1, 6, 0, getCharacterMageLevel(fo->attackerId)); + if (res) { + for (int i = 0; i < 4; i++) { + uint16 bl = fo->curBlock; + fo->curBlock = (fo->curBlock + blockAdv[i]) & 0x3ff; + magicObjectHit(fo, 1, 6, 0, getCharacterMageLevel(fo->attackerId)); + fo->curBlock = bl; + } + } + return res; +} + +void EobCoreEngine::spellCallback_start_removeCurse() { + +} + +void EobCoreEngine::spellCallback_start_coneOfCold() { + +} + +void EobCoreEngine::spellCallback_start_holdMonster() { + +} + +bool EobCoreEngine::spellCallback_end_holdMonster(EobFlyingObject *fo) { + return true; +} + +void EobCoreEngine::spellCallback_start_wallOfForce() { + +} + +void EobCoreEngine::spellCallback_start_disintegrate() { + +} + +void EobCoreEngine::spellCallback_start_fleshToStone() { + +} + +void EobCoreEngine::spellCallback_start_stoneToFlesh() { + +} + +void EobCoreEngine::spellCallback_start_trueSeeing() { + +} + +bool EobCoreEngine::spellCallback_end_trueSeeing(EobFlyingObject*) { + return true; +} + +void EobCoreEngine::spellCallback_start_slayLiving() { + +} + +void EobCoreEngine::spellCallback_start_powerWordStun() { + +} + +void EobCoreEngine::spellCallback_start_causeLightWounds() { + +} + +void EobCoreEngine::spellCallback_start_cureLightWounds() { + modifyCharacterHitpoints(_activeSpellCaster, rollDice(1, 8)); +} + +void EobCoreEngine::spellCallback_start_aid() { + +} + +bool EobCoreEngine::spellCallback_end_aid(EobFlyingObject*) { + return true; +} + +void EobCoreEngine::spellCallback_start_flameBlade() { + +} + +void EobCoreEngine::spellCallback_start_slowPoison() { + +} + +bool EobCoreEngine::spellCallback_end_slowPoison(EobFlyingObject*) { + return true; +} + +void EobCoreEngine::spellCallback_start_createFood() { + for (int i = 0; i < 6; i++) { + if (!testCharacter(i, 3)) + continue; + _characters[_activeSpellCaster].food = 100; + } +} + +void EobCoreEngine::spellCallback_start_removeParalysis() { + +} + +void EobCoreEngine::spellCallback_start_causeSeriousWounds() { + modifyCharacterHitpoints(_activeSpellCaster, rollDice(2, 8, 1)); +} + +void EobCoreEngine::spellCallback_start_cureSeriousWounds() { + +} + +void EobCoreEngine::spellCallback_start_neutralizePoison() { + +} + +void EobCoreEngine::spellCallback_start_causeCriticalWounds() { + +} + +void EobCoreEngine::spellCallback_start_cureCriticalWounds() { + modifyCharacterHitpoints(_activeSpellCaster, rollDice(3, 8, 3)); +} + +void EobCoreEngine::spellCallback_start_flameStrike() { + launchMagicObject(_openBookChar, _flags.gameID == GI_EOB1 ? 8 : 7, _currentBlock, _activeSpellCasterPos, _currentDirection); +} + +bool EobCoreEngine::spellCallback_end_flameStrike(EobFlyingObject *fo) { + return magicObjectHit(fo, 6, 8, 0, 0); +} + +void EobCoreEngine::spellCallback_start_raiseDead() { + +} + +void EobCoreEngine::spellCallback_start_harm() { + +} + +void EobCoreEngine::spellCallback_start_heal() { + EobCharacter *c = &_characters[_activeSpellCaster]; + if (c->hitPointsMax <= c->hitPointsCur) { + _txt->printMessage(_magicStrings4[0]); + snd_playSoundEffect(79); + } else { + modifyCharacterHitpoints(_activeSpellCaster, c->hitPointsMax - c->hitPointsCur); + } +} + +void EobCoreEngine::spellCallback_start_layOnHands() { + +} + +void EobCoreEngine::spellCallback_start_turnUndead() { + +} + +bool EobCoreEngine::spellCallback_end_unk1Passive(EobFlyingObject *fo) { + bool res = false; + if (_partyEffectFlags & 0x20000) { + res = magicObjectHit(fo, 4, 10, 6, 0); + if (res) { + gui_drawAllCharPortraitsWithStats(); + _partyEffectFlags &= ~0x20000; + } + } else { + res = magicObjectHit(fo, 12, 10, 6, 0); + } + return res; +} + +bool EobCoreEngine::spellCallback_end_unk2Passive(EobFlyingObject *fo) { + return magicObjectHit(fo, 0, 0, 18, 0); +} + +bool EobCoreEngine::spellCallback_end_deathSpellPassive(EobFlyingObject *fo) { + return true; +} + +bool EobCoreEngine::spellCallback_end_disintegratePassive(EobFlyingObject *fo) { + return true; +} + +bool EobCoreEngine::spellCallback_end_causeCriticalWoundsPassive(EobFlyingObject *fo) { + return true; +} + +bool EobCoreEngine::spellCallback_end_fleshToStonePassive(EobFlyingObject *fo) { + return true; +} + +} // End of namespace Kyra + +#endif // ENABLE_EOB diff --git a/engines/kyra/module.mk b/engines/kyra/module.mk index abd535ee29..50bcbdd6e9 100644 --- a/engines/kyra/module.mk +++ b/engines/kyra/module.mk @@ -72,8 +72,19 @@ MODULE_OBJS := \ vqa.o \ wsamovie.o +KYRARPG_COMMON_OBJ = \ + gui_eob.o \ + loleobbase.o \ + saveload_eob.o \ + scene_eob.o \ + sprites_eob.o \ + staticres_eob.o \ + text_eob.o \ + timer_eob.o + ifdef ENABLE_LOL MODULE_OBJS += \ + $(KYRARPG_COMMON_OBJ) \ gui_lol.o \ items_lol.o \ lol.o \ @@ -89,6 +100,24 @@ MODULE_OBJS += \ timer_lol.o endif +ifdef ENABLE_EOB +ifndef ENABLE_LOL +MODULE_OBJS += \ + $(KYRARPG_COMMON_OBJ) +endif +MODULE_OBJS += \ + chargen.o \ + eobcommon.o \ + eob1.o \ + eob2.o \ + items_eob.o \ + magic_eob.o \ + screen_eob.o \ + script_eob.o \ + sequences_eob1.o \ + sequences_eob2.o +endif + # This module can be built as a plugin ifeq ($(ENABLE_KYRA), DYNAMIC_PLUGIN) PLUGIN := 1 diff --git a/engines/kyra/resource.cpp b/engines/kyra/resource.cpp index a35ec3d81b..3aaab07c0d 100644 --- a/engines/kyra/resource.cpp +++ b/engines/kyra/resource.cpp @@ -59,7 +59,7 @@ bool Resource::reset() { if (!dir.exists() || !dir.isDirectory()) error("invalid game path '%s'", dir.getPath().c_str()); - if (_vm->game() == GI_KYRA1) { + if (_vm->game() == GI_KYRA1 || _vm->game() == GI_EOB1) { // We only need kyra.dat for the demo. if (_vm->gameFlags().isDemo && !_vm->gameFlags().isTalkie) return true; @@ -128,7 +128,7 @@ bool Resource::reset() { loadProtectedFiles(list); } - } else { + } else if (_vm->game() != GI_EOB2) { error("Unknown game id: %d", _vm->game()); return false; // for compilers that don't support NORETURN } diff --git a/engines/kyra/resource.h b/engines/kyra/resource.h index c2a697f18d..2c484b4d01 100644 --- a/engines/kyra/resource.h +++ b/engines/kyra/resource.h @@ -35,6 +35,7 @@ #include "common/archive.h" #include "kyra/kyra_v1.h" +#include "kyra/eob2.h" #include "kyra/lol.h" #include "kyra/kyra_hof.h" @@ -250,7 +251,264 @@ enum KyraResources { k3ItemMagicTable, k3ItemStringMap, -#ifdef ENABLE_LOL +#if defined(ENABLE_EOB) || defined(ENABLE_LOL) + kLolEobCommonMoreStrings, + kLolEobCommonDscShapeIndex, + kLolEobCommonDscX, + kLolEobCommonDscTileIndex, + kLolEobCommonDscUnk2, + kLolEobCommonDscDoorShapeIndex, + kLolEobCommonDscDimData1, + kLolEobCommonDscDimData2, + kLolEobCommonDscBlockMap, + kLolEobCommonDscDimMap, + kLolEobCommonDscDoorY2, + kLolEobCommonDscBlockIndex, + + kEobBaseChargenStrings1, + kEobBaseChargenStrings2, + kEobBaseChargenStartLevels, + kEobBaseChargenStatStrings, + kEobBaseChargenRaceSexStrings, + kEobBaseChargenClassStrings, + kEobBaseChargenAlignmentStrings, + kEobBaseChargenEnterGameStrings, + kEobBaseChargenClassMinStats, + kEobBaseChargenRaceMinStats, + kEobBaseChargenRaceMaxStats, + + kEobBaseConstModTable1, + kEobBaseConstModTable2, + kEobBaseConstModTable3, + kEobBaseConstModTable4, + kEobBaseConstModLvlIndex, + kEobBaseConstModDiv, + kEobBaseConstModExt, + + kEobBasePryDoorStrings, + kEobBaseWarningStrings, + kEobBaseItemSuffixStrings, + kEobBaseItemExtraStrings, + kEobBaseTakenStrings, + kEobBasePotionEffectStrings, + + kEobBaseYesNoStrings, + kEobBaseNpcMaxStrings, + kEobBaseOkStrings, + kEobBaseNpcJoinStrings, + kEobBaseCancelStrings, + kEobBaseAbortStrings, + + kEobBaseCharGuiStringsHp, + kEobBaseCharGuiStringsWp1, + kEobBaseCharGuiStringsWp2, + kEobBaseCharGuiStringsWr, + kEobBaseCharGuiStringsSt1, + kEobBaseCharGuiStringsSt2, + kEobBaseCharGuiStringsIn, + + kEobBaseCharStatusStrings7, + kEobBaseCharStatusStrings81, + kEobBaseCharStatusStrings82, + kEobBaseCharStatusStrings9, + kEobBaseCharStatusStrings12, + kEobBaseCharStatusStrings131, + kEobBaseCharStatusStrings132, + + kEobBaseLevelGainStrings, + kEobBaseExperienceTable0, + kEobBaseExperienceTable1, + kEobBaseExperienceTable2, + kEobBaseExperienceTable3, + kEobBaseExperienceTable4, + + kEobBaseClassModifierFlags, + + kEobBaseMonsterStepTable01, + kEobBaseMonsterStepTable02, + kEobBaseMonsterStepTable1, + kEobBaseMonsterStepTable2, + kEobBaseMonsterStepTable3, + kEobBaseMonsterCloseAttPosTable1, + kEobBaseMonsterCloseAttPosTable21, + kEobBaseMonsterCloseAttPosTable22, + kEobBaseMonsterCloseAttUnkTable, + kEobBaseMonsterCloseAttChkTable1, + kEobBaseMonsterCloseAttChkTable2, + kEobBaseMonsterCloseAttDstTable1, + kEobBaseMonsterCloseAttDstTable2, + + kEobBaseMonsterProximityTable, + kEobBaseFindBlockMonstersTable, + kEobBaseMonsterDirChangeTable, + kEobBaseMonsterDistAttStrings, + + kEobBaseEncodeMonsterDefs, + kEobBaseNpcPresets, + + kEobBaseWllFlagPreset, + kEobBaseDscShapeCoords, + + kEobBaseDscDoorScaleOffs, + kEobBaseDscDoorScaleMult1, + kEobBaseDscDoorScaleMult2, + kEobBaseDscDoorScaleMult3, + kEobBaseDscDoorScaleMult4, + kEobBaseDscDoorScaleMult5, + kEobBaseDscDoorScaleMult6, + kEobBaseDscDoorType5Offs, + kEobBaseDscDoorY1, + kEobBaseDscDoorY3, + kEobBaseDscDoorY4, + kEobBaseDscDoorY5, + kEobBaseDscDoorY6, + kEobBaseDscDoorCoordsExt, + + kEobBaseDscItemPosIndex, + kEobBaseDscItemShpX, + kEobBaseDscItemScaleIndex, + kEobBaseDscItemTileIndex, + kEobBaseDscItemShapeMap, + + kEobBaseDscMonsterFrmOffsTbl1, + kEobBaseDscMonsterFrmOffsTbl2, + + kEobBaseInvSlotX, + kEobBaseInvSlotY, + kEobBaseSlotValidationFlags, + kEobBaseDrawObjPosIndex, + kEobBaseFlightObjFlipIndex, + kEobBaseFlightObjShpMap, + kEobBaseFlightObjSclIndex, + + kEobBaseDscTelptrShpCoords, + + kEobBaseBookNumbers, + kEobBaseMageSpellsList, + kEobBaseClericSpellsList, + kEobBaseSpellNames, + kEobBaseMagicStrings1, + kEobBaseMagicStrings2, + kEobBaseMagicStrings3, + kEobBaseMagicStrings4, + kEobBaseMagicStrings5, + kEobBaseMagicStrings6, + kEobBaseMagicStrings7, + kEobBaseMagicStrings8, + + kEobBaseSparkDefSteps, + kEobBaseSparkDefSubSteps, + kEobBaseSparkDefShift, + kEobBaseSparkDefAdd, + kEobBaseSparkDefX, + kEobBaseSparkDefY, + kEobBaseSparkOfFlags1, + kEobBaseSparkOfFlags2, + kEobBaseSparkOfShift, + kEobBaseSparkOfX, + kEobBaseSparkOfY, + + kEobBaseSpellProperties, + kEobBaseMagicFlightProps, + + kEob1MainMenuStrings, + kEob1DoorShapeDefs, + kEob1DoorSwitchShapeDefs, + kEob1DoorSwitchCoords, + kEob1MonsterProperties, + + kEob1MonsterDistAttType10, + kEob1MonsterDistAttSfx10, + kEob1MonsterDistAttType17, + kEob1MonsterDistAttSfx17, + + kEob2MainMenuStrings, + kEob2IntroStrings, + kEob2IntroCPSFiles, + kEob2IntroSeqData00, + kEob2IntroSeqData01, + kEob2IntroSeqData02, + kEob2IntroSeqData03, + kEob2IntroSeqData04, + kEob2IntroSeqData05, + kEob2IntroSeqData06, + kEob2IntroSeqData07, + kEob2IntroSeqData08, + kEob2IntroSeqData09, + kEob2IntroSeqData10, + kEob2IntroSeqData11, + kEob2IntroSeqData12, + kEob2IntroSeqData13, + kEob2IntroSeqData14, + kEob2IntroSeqData15, + kEob2IntroSeqData16, + kEob2IntroSeqData17, + kEob2IntroSeqData18, + kEob2IntroSeqData19, + kEob2IntroSeqData20, + kEob2IntroSeqData21, + kEob2IntroSeqData22, + kEob2IntroSeqData23, + kEob2IntroSeqData24, + kEob2IntroSeqData25, + kEob2IntroSeqData26, + kEob2IntroSeqData27, + kEob2IntroSeqData28, + kEob2IntroSeqData29, + kEob2IntroSeqData30, + kEob2IntroSeqData31, + kEob2IntroSeqData32, + kEob2IntroSeqData33, + kEob2IntroSeqData34, + kEob2IntroSeqData35, + kEob2IntroSeqData36, + kEob2IntroSeqData37, + kEob2IntroSeqData38, + kEob2IntroSeqData39, + kEob2IntroSeqData40, + kEob2IntroSeqData41, + kEob2IntroSeqData42, + kEob2IntroSeqData43, + kEob2IntroShapes00, + kEob2IntroShapes01, + kEob2IntroShapes04, + kEob2IntroShapes07, + + kEob2FinaleStrings, + kEob2CreditsData, + kEob2FinaleCPSFiles, + kEob2FinaleSeqData00, + kEob2FinaleSeqData01, + kEob2FinaleSeqData02, + kEob2FinaleSeqData03, + kEob2FinaleSeqData04, + kEob2FinaleSeqData05, + kEob2FinaleSeqData06, + kEob2FinaleSeqData07, + kEob2FinaleSeqData08, + kEob2FinaleSeqData09, + kEob2FinaleSeqData10, + kEob2FinaleSeqData11, + kEob2FinaleSeqData12, + kEob2FinaleSeqData13, + kEob2FinaleSeqData14, + kEob2FinaleSeqData15, + kEob2FinaleSeqData16, + kEob2FinaleSeqData17, + kEob2FinaleSeqData18, + kEob2FinaleSeqData19, + kEob2FinaleSeqData20, + kEob2FinaleShapes00, + kEob2FinaleShapes03, + kEob2FinaleShapes07, + kEob2FinaleShapes09, + kEob2FinaleShapes10, + + kEob2NpcShapeData, + kEob2Npc1Strings, + kEob2Npc2Strings, + kEob2MonsterDustStrings, + kLolIngamePakFiles, kLolCharacterDefs, kLolIngameSfxFiles, @@ -284,27 +542,17 @@ enum KyraResources { kLolItemPrices, kLolStashSetup, - kLolDscUnk1, - kLolDscShapeIndex, + kLolDscWalls, kLolDscOvlMap, kLolDscScaleWidthData, kLolDscScaleHeightData, - kLolDscX, - kLolDscY, - kLolDscTileIndex, - kLolDscUnk2, - kLolDscDoorShapeIndex, - kLolDscDimData1, - kLolDscDimData2, - kLolDscBlockMap, - kLolDscDimMap, - kLolDscDoor1, + kLolBaseDscY, + kLolDscDoorScale, kLolDscDoor4, kLolDscDoorX, kLolDscDoorY, kLolDscOvlIndex, - kLolDscBlockIndex, kLolScrollXTop, kLolScrollYTop, @@ -334,7 +582,7 @@ enum KyraResources { kLolCredits, kLolHistory, -#endif +#endif // ENABLE_EOB || ENABLE_LOL kMaxResIDs }; @@ -363,15 +611,22 @@ public: const HofSeqData *loadHofSequenceData(int id, int &entries); const ItemAnimData_v1 *loadShapeAnimData_v1(int id, int &entries); const ItemAnimData_v2 *loadShapeAnimData_v2(int id, int &entries); +#if defined(ENABLE_EOB) || defined(ENABLE_LOL) + const uint16 *loadRawDataBe16(int id, int &entries); + const uint32 *loadRawDataBe32(int id, int &entries); +#endif // (ENABLE_EOB || ENABLE_LOL) #ifdef ENABLE_LOL const LoLCharacter *loadCharData(int id, int &entries); const SpellProperty *loadSpellData(int id, int &entries); const CompassDef *loadCompassData(int id, int &entries); const FlyingObjectShape *loadFlyingObjectData(int id, int &entries); - const uint16 *loadRawDataBe16(int id, int &entries); - const uint32 *loadRawDataBe32(int id, int &entries); - const ButtonDef *loadButtonDefs(int id, int &entries); + const LoLButtonDef *loadButtonDefs(int id, int &entries); #endif // ENABLE_LOL +#ifdef ENABLE_EOB + const EobSequenceStep *loadEob2SeqData(int id, int &entries); + const EobShapeDef *loadEob2ShapeData(int id, int &entries); + const EobCharacter *loadEobNpcData(int id, int &entries); +#endif // ENABLE_EOB // use '-1' to prefetch/unload all ids // prefetchId retruns false if only on of the resources @@ -398,15 +653,22 @@ private: bool loadHofSequenceData(Common::SeekableReadStream &stream, void *&ptr, int &size); bool loadShapeAnimData_v1(Common::SeekableReadStream &stream, void *&ptr, int &size); bool loadShapeAnimData_v2(Common::SeekableReadStream &stream, void *&ptr, int &size); +#if defined(ENABLE_EOB) || defined(ENABLE_LOL) + bool loadRawDataBe16(Common::SeekableReadStream &stream, void *&ptr, int &size); + bool loadRawDataBe32(Common::SeekableReadStream &stream, void *&ptr, int &size); +#endif // (ENABLE_LOL || ENABLE_EOB) #ifdef ENABLE_LOL bool loadCharData(Common::SeekableReadStream &stream, void *&ptr, int &size); bool loadSpellData(Common::SeekableReadStream &stream, void *&ptr, int &size); bool loadCompassData(Common::SeekableReadStream &stream, void *&ptr, int &size); bool loadFlyingObjectData(Common::SeekableReadStream &stream, void *&ptr, int &size); - bool loadRawDataBe16(Common::SeekableReadStream &stream, void *&ptr, int &size); - bool loadRawDataBe32(Common::SeekableReadStream &stream, void *&ptr, int &size); bool loadButtonDefs(Common::SeekableReadStream &stream, void *&ptr, int &size); #endif // ENABLE_LOL +#ifdef ENABLE_EOB + bool loadEob2SeqData(Common::SeekableReadStream &stream, void *&ptr, int &size); + bool loadEob2ShapeData(Common::SeekableReadStream &stream, void *&ptr, int &size); + bool loadEobNpcData(Common::SeekableReadStream &stream, void *&ptr, int &size); +#endif // ENABLE_EOB void freeRawData(void *&ptr, int &size); void freeStringTable(void *&ptr, int &size); @@ -416,15 +678,22 @@ private: void freeHofSequenceData(void *&ptr, int &size); void freeHofShapeAnimDataV1(void *&ptr, int &size); void freeHofShapeAnimDataV2(void *&ptr, int &size); +#if defined(ENABLE_EOB) || defined(ENABLE_LOL) + void freeRawDataBe16(void *&ptr, int &size); + void freeRawDataBe32(void *&ptr, int &size); +#endif // (ENABLE_EOB || ENABLE_LOL) #ifdef ENABLE_LOL void freeCharData(void *&ptr, int &size); void freeSpellData(void *&ptr, int &size); void freeCompassData(void *&ptr, int &size); void freeFlyingObjectData(void *&ptr, int &size); - void freeRawDataBe16(void *&ptr, int &size); - void freeRawDataBe32(void *&ptr, int &size); void freeButtonDefs(void *&ptr, int &size); #endif // ENABLE_LOL +#ifdef ENABLE_EOB + void freeEob2SeqData(void *&ptr, int &size); + void freeEob2ShapeData(void *&ptr, int &size); + void freeEobNpcData(void *&ptr, int &size); +#endif // ENABLE_EOB enum ResTypes { kStringList = 0, @@ -443,7 +712,11 @@ private: kLolFlightShpData = 11, kLolButtonData = 12, kLolRawDataBe16 = 13, - kLolRawDataBe32 = 14 + kLolRawDataBe32 = 14, + + kEob2SequenceData = 15, + kEob2ShapeData = 16, + kEobNpcData = 17 }; struct FileType { diff --git a/engines/kyra/resource_intern.cpp b/engines/kyra/resource_intern.cpp index f9aae2c550..af050b12e7 100644 --- a/engines/kyra/resource_intern.cpp +++ b/engines/kyra/resource_intern.cpp @@ -254,6 +254,8 @@ bool ResLoaderPak::isLoadable(const Common::String &filename, Common::SeekableRe offset = SWAP_BYTES_32(offset); } + int32 firstOffset = offset; + Common::String file; while (!stream.eos()) { // The start offset of a file should never be in the filelist @@ -276,7 +278,7 @@ bool ResLoaderPak::isLoadable(const Common::String &filename, Common::SeekableRe firstFile = false; offset = switchEndian ? stream.readUint32BE() : stream.readUint32LE(); - if (!offset || offset == filesize) + if (!offset || offset == filesize || firstOffset == stream.pos()) break; } @@ -297,6 +299,7 @@ Common::Archive *ResLoaderPak::load(Common::ArchiveMemberPtr memberFile, Common: bool firstFile = true; startoffset = stream.readUint32LE(); + int32 firstOffset = startoffset; if (startoffset > filesize || startoffset < 0) { switchEndian = true; startoffset = SWAP_BYTES_32(startoffset); @@ -330,12 +333,12 @@ Common::Archive *ResLoaderPak::load(Common::ArchiveMemberPtr memberFile, Common: firstFile = false; endoffset = switchEndian ? stream.readUint32BE() : stream.readUint32LE(); - if (endoffset < 0) { + if (endoffset < 0 && stream.pos() != firstOffset) { warning("PAK file '%s' is corrupted", memberFile->getDisplayName().c_str()); return 0; } - if (!endoffset) + if (!endoffset || stream.pos() == firstOffset) endoffset = filesize; if (startoffset != endoffset) @@ -493,7 +496,7 @@ public: void advSrcBitsBy1(); void advSrcBitsByIndex(uint8 newIndex); - uint8 getKeyLower() { return _key & 0xff; } + uint8 getKeyLower() const { return _key & 0xff; } void setIndex(uint8 index) { _index = index; } uint16 getKeyMasked(uint8 newIndex); uint16 keyMaskedAlign(uint16 val); diff --git a/engines/kyra/saveload_eob.cpp b/engines/kyra/saveload_eob.cpp new file mode 100644 index 0000000000..3b3964bdc5 --- /dev/null +++ b/engines/kyra/saveload_eob.cpp @@ -0,0 +1,549 @@ +/* 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. + * + */ + +#if defined(ENABLE_EOB) || defined(ENABLE_LOL) + +#include "kyra/eobcommon.h" +#include "kyra/resource.h" +#include "kyra/script_eob.h" + +#include "common/system.h" +#include "common/savefile.h" +#include "common/substream.h" + +namespace Kyra { + +void LolEobBaseEngine::generateTempData() { + int l = _currentLevel - 1; + if (_lvlTempData[l]) { + delete[] _lvlTempData[l]->wallsXorData; + delete[] _lvlTempData[l]->flags; + releaseMonsterTempData(_lvlTempData[l]); + releaseFlyingObjectTempData(_lvlTempData[l]); + delete _lvlTempData[l]; + } + + _lvlTempData[l] = new LevelTempData; + + _lvlTempData[l]->wallsXorData = new uint8[4096]; + _lvlTempData[l]->flags = new uint16[1024]; + + char filename[13]; + const uint8 *p = 0; + const uint8 *p2 = 0; + if (_flags.gameID == GI_LOL) { + snprintf(filename, sizeof(filename), "LEVEL%d.CMZ", _currentLevel); + screen()->loadBitmap(filename, 15, 15, 0); + p = screen()->getCPagePtr(14); + } else { + snprintf(filename, sizeof(filename), "LEVEL%d.MAZ", _currentLevel); + p2 = p = _res->fileData(filename, 0); + } + + uint16 len = READ_LE_UINT16(p + 4); + p += 6; + + memset(_lvlTempData[l]->wallsXorData, 0, 4096); + memset(_lvlTempData[l]->flags, 0, 1024 * sizeof(uint16)); + uint8 *d = _lvlTempData[l]->wallsXorData; + uint16 *df = _lvlTempData[l]->flags; + + for (int i = 0; i < 1024; i++) { + for (int ii = 0; ii < 4; ii++) + *d++ = p[i * len + ii] ^ _levelBlockProperties[i].walls[ii]; + *df++ = _levelBlockProperties[i].flags; + } + + _lvlTempData[l]->monsters = generateMonsterTempData(_lvlTempData[l]); + _lvlTempData[l]->flyingObjects = generateFlyingObjectTempData(_lvlTempData[l]); + + _hasTempDataFlags |= (1 << l); + delete[] p2; +} + +void LolEobBaseEngine::restoreBlockTempData(int levelIndex) { + int l = levelIndex - 1; + char filename[13]; + const uint8 *p = 0; + const uint8 *p2 = 0; + if (_flags.gameID == GI_LOL) { + snprintf(filename, sizeof(filename), "LEVEL%d.CMZ", levelIndex); + screen()->loadBitmap(filename, 3, 3, 0); + p = screen()->getCPagePtr(2); + } else { + snprintf(filename, sizeof(filename), "LEVEL%d.MAZ", levelIndex); + p2 = p = _res->fileData(filename, 0); + } + + uint16 len = READ_LE_UINT16(p + 4); + p += 6; + + memset(_levelBlockProperties, 0, 1024 * sizeof(LevelBlockProperty)); + + uint8 *t = _lvlTempData[l]->wallsXorData; + uint16 *t2 = _lvlTempData[l]->flags; + + for (int i = 0; i < 1024; i++) { + for (int ii = 0; ii < 4; ii++) + _levelBlockProperties[i].walls[ii] = p[i * len + ii] ^ *t++; + _levelBlockProperties[i].flags = *t2++; + } + + restoreMonsterTempData(_lvlTempData[l]); + restoreFlyingObjectTempData(_lvlTempData[l]); + + delete[] p2; +} + +void LolEobBaseEngine::releaseTempData() { + for (int i = 0; i < 29; i++) { + if (_lvlTempData[i]) { + delete[] _lvlTempData[i]->wallsXorData; + delete[] _lvlTempData[i]->flags; + releaseMonsterTempData(_lvlTempData[i]); + releaseFlyingObjectTempData(_lvlTempData[i]); + delete _lvlTempData[i]; + _lvlTempData[i] = 0; + } + } +} + +#ifdef ENABLE_EOB + +Common::Error EobCoreEngine::loadGameState(int slot) { + const char *fileName = getSavegameFilename(slot); + + SaveHeader header; + Common::InSaveFile *saveFile = openSaveForReading(fileName, header); + if (!saveFile) { + //_txt->printMessage(2, "%s", getLangString(0x425d)); + return Common::kNoError; + } + + Common::SeekableSubReadStreamEndian in(saveFile, saveFile->pos(), saveFile->size(), !header.originalSave, DisposeAfterUse::YES); + + for (int i = 0; i < 6; i++) { + EobCharacter *c = &_characters[i]; + c->id = in.readByte(); + c->flags = in.readByte(); + in.read(c->name, 11); + c->strengthCur = in.readSByte(); + c->strengthMax = in.readSByte(); + c->strengthExtCur = in.readSByte(); + c->strengthExtMax = in.readSByte(); + c->intelligenceCur = in.readSByte(); + c->intelligenceMax = in.readSByte(); + c->wisdomCur = in.readSByte(); + c->wisdomMax = in.readSByte(); + c->dexterityCur = in.readSByte(); + c->dexterityMax = in.readSByte(); + c->constitutionCur = in.readSByte(); + c->constitutionMax = in.readSByte(); + c->charismaCur = in.readSByte(); + c->charismaMax = in.readSByte(); + c->hitPointsCur = in.readSint16BE(); + c->hitPointsMax = in.readSint16BE(); + c->armorClass = in.readSByte(); + c->disabledSlots = in.readByte(); + c->raceSex = in.readByte(); + c->cClass = in.readByte(); + c->alignment = in.readByte(); + c->portrait = in.readSByte(); + c->food = in.readByte(); + in.read(c->level, 3); + for (int ii = 0; ii < 3; ii++) + c->experience[ii] = in.readUint32BE(); + delete[] c->faceShape; + c->faceShape = 0; + in.read(c->mageSpells, 80); + in.read(c->clericSpells, 80); + c->mageSpellsAvailabilityFlags = in.readUint32BE(); + for (int ii = 0; ii < 27; ii++) + c->inventory[ii] = in.readSint16BE(); + uint32 ct = _system->getMillis(); + for (int ii = 0; ii < 10; ii++) { + c->timers[ii] = in.readUint32BE(); + if (c->timers[ii]) + c->timers[ii] += ct; + } + in.read(c->events, 10); + in.read(c->effectsRemainder, 4); + c->effectFlags = in.readUint32BE(); + c->damageTaken = in.readByte(); + in.read(c->slotStatus, 5); + } + + setupCharacterTimers(); + + _screen->loadEobBitmap("CHARGENA", 3, 3); + for (int i = 0; i < 6; i++) { + EobCharacter *c = &_characters[i]; + if (!c->flags || c->portrait < 0) + continue; + c->faceShape = _screen->encodeShape((c->portrait % 10) << 2, (c->portrait / 10) << 5, 4, 32, true); + } + + _screen->loadEobBitmap(_flags.gameID == GI_EOB2 ? "OUTPORTS" : "OUTTAKE", 3, 3); + for (int i = 0; i < 6; i++) { + EobCharacter *c = &_characters[i]; + if (!c->flags || c->portrait >= 0) + continue; + c->faceShape = _screen->encodeShape(-(c->portrait + 1), _flags.gameID == GI_EOB2 ? 0 : 160, 4, 32, true); + } + _screen->_curPage = 0; + + _currentLevel = in.readByte(); + _currentSub = in.readSByte(); + _currentBlock = in.readUint16BE(); + _currentDirection = in.readUint16BE(); + _itemInHand = in.readSint16BE(); + _hasTempDataFlags = in.readUint32BE(); + _partyEffectFlags = in.readUint32BE(); + _inf->loadState(in); + + for (int i = 0; i < 600; i++) { + EobItem *t = &_items[i]; + t->nameUnid = in.readByte(); + t->nameId = in.readByte(); + t->flags = in.readByte(); + t->icon = in.readSByte(); + t->type = in.readSByte(); + t->pos = in.readSByte(); + t->block = in.readSint16BE(); + t->next = in.readSint16BE(); + t->prev = in.readSint16BE(); + t->level = in.readByte(); + t->value = in.readSByte(); + } + + for (int i = 51; i < 65; i++) { + EobItemType *t = &_itemTypes[i]; + t->invFlags = in.readUint16BE(); + t->handFlags = in.readUint16BE(); + t->armorClass = in.readSByte(); + t->allowedClasses = in.readSByte(); + t->requiredHands = in.readSByte(); + t->dmgNumDiceS = in.readSByte(); + t->dmgNumPipsS = in.readSByte(); + t->dmgIncS = in.readSByte(); + t->dmgNumDiceL = in.readSByte(); + t->dmgNumPipsL = in.readSByte(); + t->dmgIncL = in.readSByte(); + t->unk1 = in.readByte(); + t->extraProperties = in.readUint16BE(); + } + + for (int i = 0; i < 18; i++) { + if (!(_hasTempDataFlags & (1 << i))) + continue; + + if (_lvlTempData[i]) { + delete[] _lvlTempData[i]->wallsXorData; + delete[] _lvlTempData[i]->flags; + releaseMonsterTempData(_lvlTempData[i]); + releaseFlyingObjectTempData(_lvlTempData[i]); + delete _lvlTempData[i]; + } + + _lvlTempData[i] = new LevelTempData; + _lvlTempData[i]->wallsXorData = new uint8[4096]; + _lvlTempData[i]->flags = new uint16[1024]; + EobMonsterInPlay *lm = new EobMonsterInPlay[30]; + _lvlTempData[i]->monsters = lm; + EobFlyingObject *lf = new EobFlyingObject[10]; + _lvlTempData[i]->flyingObjects = lf; + LevelTempData *l = _lvlTempData[i]; + + in.read(l->wallsXorData, 4096); + for (int ii = 0; ii < 1024; ii++) + l->flags[ii] = in.readByte(); + + for (int ii = 0; ii < 30; ii++) { + EobMonsterInPlay *m = &lm[ii]; + m->type = in.readByte(); + m->unit = in.readByte(); + m->block = in.readUint16BE(); + m->pos = in.readByte(); + m->dir = in.readSByte(); + m->animStep = in.readByte(); + m->shpIndex = in.readByte(); + m->mode = in.readSByte(); + m->f_9 = in.readSByte(); + m->curAttackFrame = in.readSByte(); + m->f_b = in.readByte(); + m->hitPointsMax = in.readSint16BE(); + m->hitPointsCur = in.readSint16BE(); + m->dest = in.readUint16BE(); + m->randItem = in.readUint16BE(); + m->fixedItem = in.readUint16BE(); + m->flags = in.readByte(); + m->idleAnimState = in.readByte(); + m->curRemoteWeapon = in.readByte(); + m->numRemoteAttacks = in.readByte(); + m->palette = in.readSByte(); + m->directionChanged = in.readByte(); + m->stepsTillRemoteAttack = in.readByte(); + m->sub = in.readByte(); + } + + for (int ii = 0; ii < 10; ii++) { + EobFlyingObject *m = &lf[ii]; + m->enable = in.readByte(); + m->objectType = in.readByte(); + m->attackerId = in.readSint16BE(); + m->item = in.readSint16BE(); + m->curBlock = in.readUint16BE(); + m->u2 = in.readUint16BE(); + m->u1 = in.readByte(); + m->direction = in.readByte(); + m->distance = in.readByte(); + m->callBackIndex = in.readSByte(); + m->curPos = in.readByte(); + m->flags = in.readByte(); + m->unused = in.readByte(); + } + } + + if (_saveLoadMode != -1) { + loadLevel(_currentLevel, _currentSub); + gui_drawPlayField(0); + _sceneUpdateRequired = true; + _screen->setCurPage(0); + _screen->setFont(Screen::FID_6_FNT); + gui_drawAllCharPortraitsWithStats(); + updateHandItemCursor(); + _saveLoadMode = 1; + } + + while (!_screen->isMouseVisible()) + _screen->showMouse(); + + return Common::kNoError; +} + +Common::Error EobCoreEngine::saveGameStateIntern(int slot, const char *saveName, const Graphics::Surface *thumbnail) { + const Common::String finSuffix(".FIN"); + const char *fileName = (slot != -1) ? getSavegameFilename(slot) : (_targetName + finSuffix).c_str(); + + Common::OutSaveFile *out = openSaveForWriting(fileName, saveName, thumbnail); + if (!out) + return _saveFileMan->getError(); + + completeDoorOperations(); + generateTempData(); + + for (int i = 0; i < 6; i++) { + timerSpecialCharacterUpdate(0x30 + i); + uint32 ct = _system->getMillis(); + EobCharacter *c = &_characters[i]; + + out->writeByte(c->id); + out->writeByte(c->flags); + out->write(c->name, 11); + out->writeSByte(c->strengthCur); + out->writeSByte(c->strengthMax); + out->writeSByte(c->strengthExtCur); + out->writeSByte(c->strengthExtMax); + out->writeSByte(c->intelligenceCur); + out->writeSByte(c->intelligenceMax); + out->writeSByte(c->wisdomCur); + out->writeSByte(c->wisdomMax); + out->writeSByte(c->dexterityCur); + out->writeSByte(c->dexterityMax); + out->writeSByte(c->constitutionCur); + out->writeSByte(c->constitutionMax); + out->writeSByte(c->charismaCur); + out->writeSByte(c->charismaMax); + out->writeSint16BE(c->hitPointsCur); + out->writeSint16BE(c->hitPointsMax); + out->writeSByte(c->armorClass); + out->writeByte(c->disabledSlots); + out->writeByte(c->raceSex); + out->writeByte(c->cClass); + out->writeByte(c->alignment); + out->writeByte(c->portrait); + out->writeByte(c->food); + out->write(c->level, 3); + for (int ii = 0; ii < 3; ii++) + out->writeUint32BE(c->experience[ii]); + out->write(c->mageSpells, 80); + out->write(c->clericSpells, 80); + out->writeUint32BE(c->mageSpellsAvailabilityFlags); + for (int ii = 0; ii < 27; ii++) + out->writeSint16BE(c->inventory[ii]); + for (int ii = 0; ii < 10; ii++) + out->writeUint32BE(c->timers[ii] ? c->timers[ii] - ct : 0); + + out->write(c->events, 10); + out->write(c->effectsRemainder, 4); + out->writeUint32BE(c->effectFlags); + out->writeByte(c->damageTaken); + out->write(c->slotStatus, 5); + } + + out->writeByte(_currentLevel); + out->writeSByte(_currentSub); + out->writeUint16BE(_currentBlock); + out->writeUint16BE(_currentDirection); + out->writeSint16BE(_itemInHand); + out->writeUint32BE(_hasTempDataFlags); + out->writeUint32BE(_partyEffectFlags); + _inf->saveState(out); + + for (int i = 0; i < 600; i++) { + EobItem *t = &_items[i]; + out->writeByte(t->nameUnid); + out->writeByte(t->nameId); + out->writeByte(t->flags); + out->writeSByte(t->icon); + out->writeSByte(t->type); + out->writeSByte(t->pos); + out->writeSint16BE(t->block); + out->writeSint16BE(t->next); + out->writeSint16BE(t->prev); + out->writeByte(t->level); + out->writeSByte(t->value); + } + + for (int i = 51; i < 65; i++) { + EobItemType *t = &_itemTypes[i]; + out->writeUint16BE(t->invFlags); + out->writeUint16BE(t->handFlags); + out->writeSByte(t->armorClass); + out->writeSByte(t->allowedClasses); + out->writeSByte(t->requiredHands); + out->writeSByte(t->dmgNumDiceS); + out->writeSByte(t->dmgNumPipsS); + out->writeSByte(t->dmgIncS); + out->writeSByte(t->dmgNumDiceL); + out->writeSByte(t->dmgNumPipsL); + out->writeSByte(t->dmgIncL); + out->writeByte(t->unk1); + out->writeUint16BE(t->extraProperties); + } + + for (int i = 0; i < 18; i++) { + LevelTempData *l = _lvlTempData[i]; + if (!l || !(_hasTempDataFlags & (1 << i))) + continue; + + out->write(l->wallsXorData, 4096); + for (int ii = 0; ii < 1024; ii++) + out->writeByte(l->flags[ii] & 0xff); + + EobMonsterInPlay *lm = (EobMonsterInPlay*)_lvlTempData[i]->monsters; + EobFlyingObject *lf = (EobFlyingObject*)_lvlTempData[i]->flyingObjects; + + for (int ii = 0; ii < 30; ii++) { + EobMonsterInPlay *m = &lm[ii]; + out->writeByte(m->type); + out->writeByte(m->unit); + out->writeUint16BE(m->block); + out->writeByte(m->pos); + out->writeSByte(m->dir); + out->writeByte(m->animStep); + out->writeByte(m->shpIndex); + out->writeSByte(m->mode); + out->writeSByte(m->f_9); + out->writeSByte(m->curAttackFrame); + out->writeByte(m->f_b); + out->writeSint16BE(m->hitPointsMax); + out->writeSint16BE(m->hitPointsCur); + out->writeUint16BE(m->dest); + out->writeUint16BE(m->randItem); + out->writeUint16BE(m->fixedItem); + out->writeByte(m->flags); + out->writeByte(m->idleAnimState); + out->writeByte(m->curRemoteWeapon); + out->writeByte(m->numRemoteAttacks); + out->writeSByte(m->palette); + out->writeByte(m->directionChanged); + out->writeByte(m->stepsTillRemoteAttack); + out->writeByte(m->sub); + } + + for (int ii = 0; ii < 10; ii++) { + EobFlyingObject *m = &lf[ii]; + out->writeByte(m->enable); + out->writeByte(m->objectType); + out->writeSint16BE(m->attackerId); + out->writeSint16BE(m->item); + out->writeUint16BE(m->curBlock); + out->writeUint16BE(m->u2); + out->writeByte(m->u1); + out->writeByte(m->direction); + out->writeByte(m->distance); + out->writeSByte(m->callBackIndex); + out->writeByte(m->curPos); + out->writeByte(m->flags); + out->writeByte(m->unused); + } + } + + out->finalize(); + + // check for errors + if (out->err()) { + warning("Can't write file '%s'. (Disk full?)", fileName); + return Common::kUnknownError; + } else { + debugC(1, kDebugLevelMain, "Saved game '%s.'", saveName); + } + + delete out; + return Common::kNoError; +} + +void *EobCoreEngine::generateMonsterTempData(LevelTempData *tmp) { + EobMonsterInPlay *m = new EobMonsterInPlay[30]; + memcpy(m, _monsters, sizeof(EobMonsterInPlay) * 30); + return m; +} + +void *EobCoreEngine::generateFlyingObjectTempData(LevelTempData *tmp) { + EobFlyingObject *f = new EobFlyingObject[10]; + memcpy(f, _flyingObjects, sizeof(EobFlyingObject) * 10); + return f; +} + +void EobCoreEngine::restoreMonsterTempData(LevelTempData *tmp) { + memcpy(_monsters, tmp->monsters, sizeof(EobMonsterInPlay) * 30); +} + +void EobCoreEngine::restoreFlyingObjectTempData(LevelTempData *tmp) { + memcpy(_flyingObjects, tmp->flyingObjects, sizeof(EobFlyingObject) * 10); +} + +void EobCoreEngine::releaseMonsterTempData(LevelTempData *tmp) { + EobMonsterInPlay *p = (EobMonsterInPlay*)tmp->monsters; + delete[] p; +} + +void EobCoreEngine::releaseFlyingObjectTempData(LevelTempData *tmp) { + +} + +#endif // ENABLE_EOB + +} // End of namespace Kyra + +#endif // ENABLE_EOB || ENABLE_LOL diff --git a/engines/kyra/saveload_lol.cpp b/engines/kyra/saveload_lol.cpp index 1bf26477e6..7d8e6c32d9 100644 --- a/engines/kyra/saveload_lol.cpp +++ b/engines/kyra/saveload_lol.cpp @@ -96,7 +96,7 @@ Common::Error LoLEngine::loadGameState(int slot) { } } - in.read(_wllBuffer4, 80); + in.read(_wllAutomapData, 80); _currentBlock = in.readUint16BE(); _partyPosX = in.readUint16BE(); @@ -185,23 +185,26 @@ Common::Error LoLEngine::loadGameState(int slot) { if (_lvlTempData[i]) { delete[] _lvlTempData[i]->wallsXorData; delete[] _lvlTempData[i]->flags; - delete[] _lvlTempData[i]->monsters; - delete[] _lvlTempData[i]->flyingObjects; + releaseMonsterTempData(_lvlTempData[i]); + releaseFlyingObjectTempData(_lvlTempData[i]); delete _lvlTempData[i]; } _lvlTempData[i] = new LevelTempData; _lvlTempData[i]->wallsXorData = new uint8[4096]; - _lvlTempData[i]->flags = new uint8[1024]; - _lvlTempData[i]->monsters = new MonsterInPlay[30]; - _lvlTempData[i]->flyingObjects = new FlyingObject[8]; + _lvlTempData[i]->flags = new uint16[1024]; + LolMonsterInPlay *lm = new LolMonsterInPlay[30]; + _lvlTempData[i]->monsters = lm; + FlyingObject *lf = new FlyingObject[8]; + _lvlTempData[i]->flyingObjects = lf; LevelTempData *l = _lvlTempData[i]; in.read(l->wallsXorData, 4096); - in.read(l->flags, 1024); + for (int ii = 0; ii < 1024; ii++) + l->flags[ii] = in.readByte(); for (int ii = 0; ii < 30; ii++) { - MonsterInPlay *m = &l->monsters[ii]; + LolMonsterInPlay *m = &lm[ii]; m->nextAssignedObject = in.readUint16BE(); m->nextDrawObject = in.readUint16BE(); m->flyingHeight = in.readByte(); @@ -234,7 +237,7 @@ Common::Error LoLEngine::loadGameState(int slot) { } for (int ii = 0; ii < 8; ii++) { - FlyingObject *m = &l->flyingObjects[ii]; + FlyingObject *m = &lf[ii]; m->enable = in.readByte(); m->objectType = in.readByte(); m->attackerId = in.readUint16BE(); @@ -319,7 +322,7 @@ Common::Error LoLEngine::saveGameStateIntern(int slot, const char *saveName, con out->writeByte(c->characterUpdateDelay[ii]); } - out->write(_wllBuffer4, 80); + out->write(_wllAutomapData, 80); out->writeUint16BE(_currentBlock); out->writeUint16BE(_partyPosX); @@ -379,10 +382,14 @@ Common::Error LoLEngine::saveGameStateIntern(int slot, const char *saveName, con continue; out->write(l->wallsXorData, 4096); - out->write(l->flags, 1024); + for (int ii = 0; ii < 1024; ii++) + out->writeByte(l->flags[ii] & 0xff); + + LolMonsterInPlay *lm = (LolMonsterInPlay*)_lvlTempData[i]->monsters; + FlyingObject *lf = (FlyingObject*)_lvlTempData[i]->flyingObjects; for (int ii = 0; ii < 30; ii++) { - MonsterInPlay *m = &l->monsters[ii]; + LolMonsterInPlay *m = &lm[ii]; out->writeUint16BE(m->nextAssignedObject); out->writeUint16BE(m->nextDrawObject); out->writeByte(m->flyingHeight); @@ -414,7 +421,7 @@ Common::Error LoLEngine::saveGameStateIntern(int slot, const char *saveName, con } for (int ii = 0; ii < 8; ii++) { - FlyingObject *m = &l->flyingObjects[ii]; + FlyingObject *m = &lf[ii]; out->writeByte(m->enable); out->writeByte(m->objectType); out->writeUint16BE(m->attackerId); @@ -469,6 +476,69 @@ Graphics::Surface *LoLEngine::generateSaveThumbnail() const { return dst; } +void LoLEngine::restoreBlockTempData(int levelIndex) { + memset(_tempBuffer5120, 0, 5120); + LolEobBaseEngine::restoreBlockTempData(levelIndex); + restoreTempDataAdjustMonsterStrength(levelIndex - 1); +} + +void *LoLEngine::generateMonsterTempData(LevelTempData *tmp) { + LolMonsterInPlay *m = new LolMonsterInPlay[30]; + memcpy(m, _monsters, sizeof(LolMonsterInPlay) * 30); + tmp->monsterDifficulty = _monsterDifficulty; + return m; +} + +void *LoLEngine::generateFlyingObjectTempData(LevelTempData *tmp) { + FlyingObject *f = new FlyingObject[8]; + memcpy(f, _flyingObjects, sizeof(FlyingObject) * 8); + return f; +} + +void LoLEngine::restoreTempDataAdjustMonsterStrength(int index) { + if (_lvlTempData[index]->monsterDifficulty == _monsterDifficulty) + return; + + uint16 d = (_monsterModifiers[_lvlTempData[index]->monsterDifficulty] << 8) / _monsterModifiers[_monsterDifficulty]; + + for (int i = 0; i < 30; i++) { + if (_monsters[i].mode >= 14 || _monsters[i].block == 0 || _monsters[i].hitPoints <= 0) + continue; + + _monsters[i].hitPoints = (d * _monsters[i].hitPoints) >> 8; + if (_monsterDifficulty < _lvlTempData[index]->monsterDifficulty) + _monsters[i].hitPoints++; + if (_monsters[i].hitPoints == 0) + _monsters[i].hitPoints = 1; + } +} + +void LoLEngine::restoreMonsterTempData(LevelTempData *tmp) { + memcpy(_monsters, tmp->monsters, sizeof(LolMonsterInPlay) * 30); + + for (int i = 0; i < 30; i++) { + if (_monsters[i].block) { + _monsters[i].block = 0; + _monsters[i].properties = &_monsterProperties[_monsters[i].type]; + placeMonster(&_monsters[i], _monsters[i].x, _monsters[i].y); + } + } +} + +void LoLEngine::restoreFlyingObjectTempData(LevelTempData *tmp) { + memcpy(_flyingObjects, tmp->flyingObjects, sizeof(FlyingObject) * 8); +} + +void LoLEngine::releaseMonsterTempData(LevelTempData *tmp) { + LolMonsterInPlay *p = (LolMonsterInPlay*)tmp->monsters; + delete[] p; +} + +void LoLEngine::releaseFlyingObjectTempData(LevelTempData *tmp) { + FlyingObject *p = (FlyingObject*)tmp->flyingObjects; + delete[] p; +} + } // End of namespace Kyra #endif // ENABLE_LOL diff --git a/engines/kyra/scene_eob.cpp b/engines/kyra/scene_eob.cpp new file mode 100644 index 0000000000..90aa9ff533 --- /dev/null +++ b/engines/kyra/scene_eob.cpp @@ -0,0 +1,1350 @@ +/* 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. + * + */ + +#if defined(ENABLE_EOB) || defined(ENABLE_LOL) + +#include "kyra/eobcommon.h" +#include "kyra/resource.h" +#include "kyra/script_eob.h" +#include "kyra/timer.h" +#include "kyra/sound.h" + +#include "common/system.h" + +namespace Kyra { + +void LolEobBaseEngine::setLevelShapesDim(int index, int16 &x1, int16 &x2, int dim) { + if (_lvlShapeLeftRight[index << 1] == -1) { + x1 = 0; + x2 = 22; + + int16 y1 = 0; + int16 y2 = 120; + + int m = index * 18; + + for (int i = 0; i < 18; i++) { + uint8 d = _visibleBlocks[i]->walls[_sceneDrawVarDown]; + uint8 a = _wllWallFlags[d]; + + if (a & 8) { + int t = _dscDim2[(m + i) << 1]; + + if (t > x1) { + x1 = t; + if (!(a & 0x10)) + scaleLevelShapesDim(index, y1, y2, -1); + } + + t = _dscDim2[((m + i) << 1) + 1]; + + if (t < x2) { + x2 = t; + if (!(a & 0x10)) + scaleLevelShapesDim(index, y1, y2, -1); + } + } else { + int t = _dscDim1[m + i]; + + if (!_wllVmpMap[d] || t == -40) + continue; + + if (t == -41) { + x1 = 22; + x2 = 0; + break; + } + + if (t > 0 && x2 > t) + x2 = t; + + if (t < 0 && x1 < -t) + x1 = -t; + } + + if (x2 < x1) + break; + } + + x1 += (_sceneXoffset >> 3); + x2 += (_sceneXoffset >> 3); + + + _lvlShapeTop[index] = y1; + _lvlShapeBottom[index] = y2; + _lvlShapeLeftRight[index << 1] = x1; + _lvlShapeLeftRight[(index << 1) + 1] = x2; + } else { + x1 = _lvlShapeLeftRight[index << 1]; + x2 = _lvlShapeLeftRight[(index << 1) + 1]; + } + + drawLevelModifyScreenDim(dim, x1, 0, x2, 15); +} + +void LolEobBaseEngine::scaleLevelShapesDim(int index, int16 &y1, int16 &y2, int dim) { + static const int8 dscY1[] = { 0x1E, 0x18, 0x10, 0x00 }; + static const int8 dscY2[] = { 0x3B, 0x47, 0x56, 0x78 }; + + uint8 a = _dscDimMap[index]; + + if (dim == -1 && a != 3) + a++; + + y1 = dscY1[a]; + y2 = dscY2[a]; + + if (dim == -1) + return; + + const ScreenDim *cDim = screen()->getScreenDim(dim); + + screen()->modifyScreenDim(dim, cDim->sx, y1, cDim->w, y2 - y1); +} + +void LolEobBaseEngine::drawLevelModifyScreenDim(int dim, int16 x1, int16 y1, int16 x2, int16 y2) { + screen()->modifyScreenDim(dim, x1, y1 << 3, x2 - x1, (y2 - y1) << 3); +} + +void LolEobBaseEngine::generateBlockDrawingBuffer() { + _sceneDrawVarDown = _dscBlockMap[_currentDirection]; + _sceneDrawVarRight = _dscBlockMap[_currentDirection + 4]; + _sceneDrawVarLeft = _dscBlockMap[_currentDirection + 8]; + + /******************************************* + * _visibleBlocks map * + * * + * | | | | | | * + * 00 | 01 | 02 | 03 | 04 | 05 | 06 * + * ____|_____|_____|_____|_____|_____|_____ * + * | | | | | | * + * | 07 | 08 | 09 | 10 | 11 | * + * |_____|_____|_____|_____|_____| * + * | | | | * + * | 12 | 13 | 14 | * + * |_____|_____|_____| * + * | | * + * 15 | 16 | 17 * + * | (P) | * + ********************************************/ + + memset(_blockDrawingBuffer, 0, 660 * sizeof(uint16)); + + _wllProcessFlag = ((_currentBlock >> 5) + (_currentBlock & 0x1f) + _currentDirection) & 1; + + if (_wllProcessFlag) // floor and ceiling + generateVmpTileDataFlipped(0, 15, 1, -330, 22, 15); + else + generateVmpTileData(0, 15, 1, -330, 22, 15); + + assignVisibleBlocks(_currentBlock, _currentDirection); + + uint8 t = _visibleBlocks[0]->walls[_sceneDrawVarRight]; + if (t) + generateVmpTileData(-2, 3, t, 102, 3, 5); + + t = _visibleBlocks[6]->walls[_sceneDrawVarLeft]; + if (t) + generateVmpTileDataFlipped(21, 3, t, 102, 3, 5); + + t = _visibleBlocks[1]->walls[_sceneDrawVarRight]; + uint8 t2 = _visibleBlocks[2]->walls[_sceneDrawVarDown]; + + if (hasWall(t) && !(_wllWallFlags[t2] & 8)) + generateVmpTileData(2, 3, t, 102, 3, 5); + else if (t && (_wllWallFlags[t2] & 8)) + generateVmpTileData(2, 3, t2, 102, 3, 5); + + t = _visibleBlocks[5]->walls[_sceneDrawVarLeft]; + t2 = _visibleBlocks[4]->walls[_sceneDrawVarDown]; + + if (hasWall(t) && !(_wllWallFlags[t2] & 8)) + generateVmpTileDataFlipped(17, 3, t, 102, 3, 5); + else if (t && (_wllWallFlags[t2] & 8)) + generateVmpTileDataFlipped(17, 3, t2, 102, 3, 5); + + t = _visibleBlocks[2]->walls[_sceneDrawVarRight]; + if (t) + generateVmpTileData(8, 3, t, 97, 1, 5); + + t = _visibleBlocks[4]->walls[_sceneDrawVarLeft]; + if (t) + generateVmpTileDataFlipped(13, 3, t, 97, 1, 5); + + t = _visibleBlocks[1]->walls[_sceneDrawVarDown]; + if (hasWall(t)) + generateVmpTileData(-4, 3, t, 129, 6, 5); + + t = _visibleBlocks[5]->walls[_sceneDrawVarDown]; + if (hasWall(t)) + generateVmpTileData(20, 3, t, 129, 6, 5); + + t = _visibleBlocks[2]->walls[_sceneDrawVarDown]; + if (hasWall(t)) + generateVmpTileData(2, 3, t, 129, 6, 5); + + t = _visibleBlocks[4]->walls[_sceneDrawVarDown]; + if (hasWall(t)) + generateVmpTileData(14, 3, t, 129, 6, 5); + + t = _visibleBlocks[3]->walls[_sceneDrawVarDown]; + if (t) + generateVmpTileData(8, 3, t, 129, 6, 5); + + t = _visibleBlocks[7]->walls[_sceneDrawVarRight]; + if (t) + generateVmpTileData(0, 3, t, 117, 2, 6); + + t = _visibleBlocks[11]->walls[_sceneDrawVarLeft]; + if (t) + generateVmpTileDataFlipped(20, 3, t, 117, 2, 6); + + t = _visibleBlocks[8]->walls[_sceneDrawVarRight]; + if (t) + generateVmpTileData(6, 2, t, 81, 2, 8); + + t = _visibleBlocks[10]->walls[_sceneDrawVarLeft]; + if (t) + generateVmpTileDataFlipped(14, 2, t, 81, 2, 8); + + t = _visibleBlocks[8]->walls[_sceneDrawVarDown]; + if (hasWall(t)) + generateVmpTileData(-4, 2, t, 159, 10, 8); + + t = _visibleBlocks[10]->walls[_sceneDrawVarDown]; + if (hasWall(t)) + generateVmpTileData(16, 2, t, 159, 10, 8); + + t = _visibleBlocks[9]->walls[_sceneDrawVarDown]; + if (t) + generateVmpTileData(6, 2, t, 159, 10, 8); + + t = _visibleBlocks[12]->walls[_sceneDrawVarRight]; + if (t) + generateVmpTileData(3, 1, t, 45, 3, 12); + + t = _visibleBlocks[14]->walls[_sceneDrawVarLeft]; + if (t) + generateVmpTileDataFlipped(16, 1, t, 45, 3, 12); + + t = _visibleBlocks[12]->walls[_sceneDrawVarDown]; + if (!(_wllWallFlags[t] & 8)) + generateVmpTileData(-13, 1, t, 239, 16, 12); + + t = _visibleBlocks[14]->walls[_sceneDrawVarDown]; + if (!(_wllWallFlags[t] & 8)) + generateVmpTileData(19, 1, t, 239, 16, 12); + + t = _visibleBlocks[13]->walls[_sceneDrawVarDown]; + if (t) + generateVmpTileData(3, 1, t, 239, 16, 12); + + t = _visibleBlocks[15]->walls[_sceneDrawVarRight]; + t2 = _visibleBlocks[17]->walls[_sceneDrawVarLeft]; + if (t) + generateVmpTileData(0, 0, t, 0, 3, 15); + if (t2) + generateVmpTileDataFlipped(19, 0, t2, 0, 3, 15); +} + +void LolEobBaseEngine::generateVmpTileData(int16 startBlockX, uint8 startBlockY, uint8 vmpMapIndex, int16 vmpOffset, uint8 numBlocksX, uint8 numBlocksY) { + if (!_wllVmpMap[vmpMapIndex]) + return; + + uint16 *vmp = &_vmpPtr[(_wllVmpMap[vmpMapIndex] - 1) * 431 + vmpOffset + 330]; + + for (int i = 0; i < numBlocksY; i++) { + uint16 *bl = &_blockDrawingBuffer[(startBlockY + i) * 22 + startBlockX]; + for (int ii = 0; ii < numBlocksX; ii++) { + if ((startBlockX + ii >= 0) && (startBlockX + ii < 22) && *vmp) + *bl = *vmp; + bl++; + vmp++; + } + } +} + +void LolEobBaseEngine::generateVmpTileDataFlipped(int16 startBlockX, uint8 startBlockY, uint8 vmpMapIndex, int16 vmpOffset, uint8 numBlocksX, uint8 numBlocksY) { + if (!_wllVmpMap[vmpMapIndex]) + return; + + uint16 *vmp = &_vmpPtr[(_wllVmpMap[vmpMapIndex] - 1) * 431 + vmpOffset + 330]; + + for (int i = 0; i < numBlocksY; i++) { + for (int ii = 0; ii < numBlocksX; ii++) { + if ((startBlockX + ii) < 0 || (startBlockX + ii) > 21) + continue; + + uint16 v = vmp[i * numBlocksX + (numBlocksX - 1 - ii)]; + if (!v) + continue; + + if (v & 0x4000) + v -= 0x4000; + else + v |= 0x4000; + + _blockDrawingBuffer[(startBlockY + i) * 22 + startBlockX + ii] = v; + } + } +} + +bool LolEobBaseEngine::hasWall(int index) { + if (!index || (_wllWallFlags[index] & 8)) + return false; + return true; +} + +void LolEobBaseEngine::assignVisibleBlocks(int block, int direction) { + for (int i = 0; i < 18; i++) { + uint16 t = (block + _dscBlockIndex[direction * 18 + i]) & 0x3ff; + _visibleBlockIndex[i] = t; + + _visibleBlocks[i] = &_levelBlockProperties[t]; + _lvlShapeLeftRight[i] = _lvlShapeLeftRight[18 + i] = -1; + } +} + +bool LolEobBaseEngine::checkSceneUpdateNeed(int block) { + if (_sceneUpdateRequired) + return true; + + for (int i = 0; i < 15; i++) { + if (_visibleBlockIndex[i] == block) { + _sceneUpdateRequired = true; + return true; + } + } + + if (_currentBlock == block){ + _sceneUpdateRequired = true; + return true; + } + + return false; +} + +void LolEobBaseEngine::drawVcnBlocks() { + uint8 *d = _sceneWindowBuffer; + uint16 *bdb = _blockDrawingBuffer; + + for (int y = 0; y < 15; y++) { + for (int x = 0; x < 22; x++) { + bool horizontalFlip = false; + int remainder = 0; + + uint16 vcnOffset = *bdb++; + int wllVcnOffset = 0; + int wllVcnRmdOffset = 0; + + if (vcnOffset & 0x8000) { + // this renders a wall block over the transparent pixels of a floor/ceiling block + remainder = vcnOffset - 0x8000; + vcnOffset = 0; + wllVcnRmdOffset = _wllVcnOffset; + } + + if (vcnOffset & 0x4000) { + horizontalFlip = true; + vcnOffset &= 0x3fff; + } + + uint8 *src = 0; + if (vcnOffset) { + src = &_vcnBlocks[vcnOffset << 5]; + wllVcnOffset = _wllVcnOffset; + } else { + // floor/ceiling blocks + vcnOffset = bdb[329]; + if (vcnOffset & 0x4000) { + horizontalFlip = true; + vcnOffset &= 0x3fff; + } + + src = (_vcfBlocks ? _vcfBlocks : _vcnBlocks) + (vcnOffset << 5); + } + + uint8 shift = _vcnShift ? _vcnShift[vcnOffset] : _blockBrightness; + + if (horizontalFlip) { + for (int blockY = 0; blockY < 8; blockY++) { + src += 3; + for (int blockX = 0; blockX < 4; blockX++) { + uint8 t = *src--; + *d++ = _vcnExpTable[((t & 0x0f) + wllVcnOffset) | shift]; + *d++ = _vcnExpTable[((t >> 4) + wllVcnOffset) | shift]; + } + src += 5; + d += 168; + } + } else { + for (int blockY = 0; blockY < 8; blockY++) { + for (int blockX = 0; blockX < 4; blockX++) { + uint8 t = *src++; + *d++ = _vcnExpTable[((t >> 4) + wllVcnOffset) | shift]; + *d++ = _vcnExpTable[((t & 0x0f) + wllVcnOffset) | shift]; + } + d += 168; + } + } + d -= 1400; + + if (remainder) { + d -= 8; + horizontalFlip = false; + + if (remainder & 0x4000) { + remainder &= 0x3fff; + horizontalFlip = true; + } + + shift = _vcnShift ? _vcnShift[remainder] : _blockBrightness; + src = &_vcnBlocks[remainder << 5]; + + if (horizontalFlip) { + for (int blockY = 0; blockY < 8; blockY++) { + src += 3; + for (int blockX = 0; blockX < 4; blockX++) { + uint8 t = *src--; + uint8 h = _vcnExpTable[((t & 0x0f) + wllVcnRmdOffset) | shift]; + uint8 l = _vcnExpTable[((t >> 4) + wllVcnRmdOffset) | shift]; + if (h) + *d = h; + d++; + if (l) + *d = l; + d++; + } + src += 5; + d += 168; + } + } else { + for (int blockY = 0; blockY < 8; blockY++) { + for (int blockX = 0; blockX < 4; blockX++) { + uint8 t = *src++; + uint8 h = _vcnExpTable[((t >> 4) + wllVcnRmdOffset) | shift]; + uint8 l = _vcnExpTable[((t & 0x0f) + wllVcnRmdOffset) | shift]; + if (h) + *d = h; + d++; + if (l) + *d = l; + d++; + } + d += 168; + } + } + d -= 1400; + } + } + d += 1232; + } + + screen()->copyBlockToPage(_sceneDrawPage1, _sceneXoffset, 0, 176, 120, _sceneWindowBuffer); +} + +uint16 LolEobBaseEngine::calcNewBlockPosition(uint16 curBlock, uint16 direction) { + static const int16 blockPosTable[] = { -32, 1, 32, -1 }; + return (curBlock + blockPosTable[direction]) & 0x3ff; +} + +int LolEobBaseEngine::clickedWallShape(uint16 block, uint16 direction) { + uint8 v = _wllShapeMap[_levelBlockProperties[block].walls[direction]]; + if (!clickedShape(v)) + return 0; + + snd_stopSpeech(true); + runLevelScript(block, 0x40); + + return 1; +} + +int LolEobBaseEngine::clickedLeverOn(uint16 block, uint16 direction) { + uint8 v = _wllShapeMap[_levelBlockProperties[block].walls[direction]]; + if (!clickedShape(v)) + return 0; + + _levelBlockProperties[block].walls[direction]++; + _sceneUpdateRequired = true; + + if (_flags.gameID == GI_LOL) + snd_playSoundEffect(30, -1); + + runLevelScript(block, _clickedSpecialFlag); + + return 1; +} + +int LolEobBaseEngine::clickedLeverOff(uint16 block, uint16 direction) { + uint8 v = _wllShapeMap[_levelBlockProperties[block].walls[direction]]; + if (!clickedShape(v)) + return 0; + + _levelBlockProperties[block].walls[direction]--; + _sceneUpdateRequired = true; + + if (_flags.gameID == GI_LOL) + snd_playSoundEffect(29, -1); + + runLevelScript(block, _clickedSpecialFlag); + return 1; +} + +int LolEobBaseEngine::clickedWallOnlyScript(uint16 block) { + runLevelScript(block, _clickedSpecialFlag); + return 1; +} + +void LolEobBaseEngine::processDoorSwitch(uint16 block, int openClose) { + if (block == _currentBlock) + return; + + if ((_flags.gameID == GI_LOL && (_levelBlockProperties[block].assignedObjects & 0x8000)) || (_flags.gameID != GI_LOL && (_levelBlockProperties[block].flags & 7))) + return; + + if (openClose == 0) { + for (int i = 0; i < 3; i++) { + if (_openDoorState[i].block != block) + continue; + openClose = -_openDoorState[i].state; + break; + } + } + + if (openClose == 0) { + openClose = (_wllWallFlags[_levelBlockProperties[block].walls[_wllWallFlags[_levelBlockProperties[block].walls[0]] & 8 ? 0 : 1]] & 1) ? 1 : -1; + if (_flags.gameID != GI_LOL) + openClose *= -1; + } + + openCloseDoor(block, openClose); +} + +void LolEobBaseEngine::openCloseDoor(int block, int openClose) { + int s1 = -1; + int s2 = -1; + + int c = (_wllWallFlags[_levelBlockProperties[block].walls[0]] & 8) ? 0 : 1; + int v = _levelBlockProperties[block].walls[c]; + int flg = (openClose == 1) ? 0x10 : (openClose == -1 ? 0x20 : 0); + + if (_wllWallFlags[v] & flg) + return; + + for (int i = 0; i < 3; i++) { + if (_openDoorState[i].block == block) { + s1 = i; + break; + } else if (_openDoorState[i].block == 0 && s2 == -1) { + s2 = i; + } + } + + if (s1 != -1 || s2 != -1) { + if (s1 == -1) + s1 = s2; + + _openDoorState[s1].block = block; + _openDoorState[s1].state = openClose; + _openDoorState[s1].wall = c; + + flg = (-openClose == 1) ? 0x10 : (-openClose == -1 ? 0x20 : 0); + + if (_wllWallFlags[v] & flg) { + _levelBlockProperties[block].walls[c] += openClose; + _levelBlockProperties[block].walls[c ^ 2] += openClose; + + int snd = (openClose == -1) ? 4 : 3; + if (_flags.gameID == GI_LOL) { + snd_processEnvironmentalSoundEffect(snd + 28, _currentBlock); + if (!checkSceneUpdateNeed(block)) + updateEnvironmentalSfx(0); + } else { + updateEnvironmentalSfx(snd); + } + } + + enableTimer(_flags.gameID == GI_LOL ? 0 : 12); + + } else { + while (!(flg & _wllWallFlags[v])) + v += openClose; + + _levelBlockProperties[block].walls[c] = _levelBlockProperties[block].walls[c ^ 2] = v; + checkSceneUpdateNeed(block); + } +} + +void LolEobBaseEngine::completeDoorOperations() { + for (int i = 0; i < 3; i++) { + if (!_openDoorState[i].block) + continue; + + uint16 b = _openDoorState[i].block; + + do { + _levelBlockProperties[b].walls[_openDoorState[i].wall] += _openDoorState[i].state; + _levelBlockProperties[b].walls[_openDoorState[i].wall ^ 2] += _openDoorState[i].state; + } while (!(_wllWallFlags[_levelBlockProperties[b].walls[_openDoorState[i].wall]] & 0x30)); + + _openDoorState[i].block = 0; + } +} + +} // End of namespace Kyra + +#endif // ENABLE_EOB || ENABLE_LOL +#ifdef ENABLE_EOB + +namespace Kyra { + +void EobCoreEngine::loadLevel(int level, int sub) { + _currentLevel = level; + _currentSub = sub; + + char file[13]; + snprintf(file, 13, "LEVEL%d.INF", level); + + Common::SeekableReadStream *s = _res->createReadStream(file); + if (!s) { + snprintf(file, 13, "LEVEL%d.DRO", level); + s = _res->createReadStream(file); + } + + if (!s) { + snprintf(file, 13, "LEVEL%d.ELO", level); + s = _res->createReadStream(file); + } + + if (!s) + error("Failed loading level file LEVEL%d.INF/DRO/ELO", level); + + if (s->readUint16LE() + 2 == s->size()) { + if (s->readUint16LE() == 4) { + delete s; + s = 0; + _screen->loadBitmap(file, 5, 5, 0); + } + } + + if (s) { + s->seek(0); + _screen->loadFileDataToPage(s, 5, 15000); + delete s; + } + + const char *gfxFile = initLevelData(sub); + + const uint8 *data = _screen->getCPagePtr(5); + const uint8 *pos = data + READ_LE_UINT16(data); + uint16 len = READ_LE_UINT16(pos); + uint16 len2 = len; + pos += 2; + + if (_flags.gameID == GI_EOB2) { + if (*pos++ == 0xEC) + pos = loadActiveMonsterData(pos, level); + else if (!(_hasTempDataFlags & (1 << (level - 1)))) + memset(_monsters, 0, 30 * sizeof(EobMonsterInPlay)); + + len2 = len - (pos - data); + _inf->loadData(pos, len2); + } else { + _inf->loadData(data, READ_LE_UINT16(data)); + } + + _screen->setCurPage(2); + addLevelItems(); + + if (_flags.gameID == GI_EOB2) { + pos = data + len; + len2 = READ_LE_UINT16(pos); + pos += 2; + } + + for (uint16 i = 0; i < len2; i++) { + LevelBlockProperty *p = &_levelBlockProperties[READ_LE_UINT16(pos)]; + pos += 2; + if (_flags.gameID == GI_EOB2) { + p->flags |= READ_LE_UINT16(pos); + pos += 2; + } else { + p->flags |= *pos++; + } + p->assignedObjects = READ_LE_UINT16(pos); + pos += 2; + } + + loadVcnData(gfxFile, 0); + _screen->loadEobCpsFileToPage("INVENT", 0, 5, 3, 2); + + enableSysTimer(2); + _sceneDrawPage1 = 2; + _sceneDrawPage2 = 1; + _screen->setCurPage(0); +} + +const char *EobCoreEngine::initLevelData(int sub){ + const uint8 *data = _screen->getCPagePtr(5) + 2; + const uint8 *pos = data; + + int slen = (_flags.gameID == GI_EOB1) ? 12 : 13; + + char tmpStr[13]; + _sound->playTrack(0); + + for (int i = 0; i < sub; i++) + pos = data + READ_LE_UINT16(pos); + + pos += 2; + if (*pos++ == 0xEC || _flags.gameID == GI_EOB1) { + if (_flags.gameID == GI_EOB1) + pos -= 3; + + loadBlockProperties((const char*)pos); + pos += slen; + + snprintf(tmpStr, slen, "%s.VMP", (const char*) pos); + Common::SeekableReadStream *s = _res->createReadStream(tmpStr); + uint16 size = s->readUint16LE(); + delete[] _vmpPtr; + _vmpPtr = new uint16[size]; + for (int i = 0; i < size; i++) + _vmpPtr[i] = s->readUint16LE(); + delete s; + + snprintf(tmpStr, 13, "%s.PAL", (const char*) pos); + strcpy(_curGfxFile, (const char*) pos); + pos += slen; + + if (*pos++ != 0xff && _flags.gameID == GI_EOB2) { + snprintf(tmpStr, 13, "%s.PAL", (const char*) pos); + pos += 13; + } + + //////// _screen->loadPalette(tmpStr, _screen->getPalette(0)); + + if (_flags.gameID == GI_EOB1) { + pos += 11; + _screen->setShapeFadeMode(0, false); + _screen->setShapeFadeMode(1, false); + } //////////////////// + else + _screen->loadPalette(tmpStr, _screen->getPalette(0)); + ////////////////////7 + + Palette backupPal(256); + backupPal.copy(_screen->getPalette(0), 224, 32, 224); + _screen->getPalette(0).fill(224, 32, 0x3f); + uint8 *src = _screen->getPalette(0).getData(); + + _screen->createFadeTable(src, _screen->getFadeTable(0), 4, 75); // green + _screen->createFadeTable(src, _screen->getFadeTable(1), 12, 200); // black + _screen->createFadeTable(src, _screen->getFadeTable(2), 10, 85); // blue + _screen->createFadeTable(src, _screen->getFadeTable(3), 11, 125); // light blue + + _screen->getPalette(0).copy(backupPal, 224, 32, 224); + _screen->createFadeTable(src, _screen->getFadeTable(4), 12, 85); // grey (shadow) + _screen->setFadeTableIndex(4); + } + + if (_flags.gameID == GI_EOB2) { + delay(_tickLength); + _sound->loadSoundFile((const char*) pos); + pos += 13; + } + + releaseDoorShapes(); + releaseMonsterShapes(0, 36); + releaseDecorations(); + + if (_flags.gameID == GI_EOB1) { + loadDoorShapes(pos[0], pos[1], pos[2], pos[3]); + pos += 4; + _scriptTimersMode = *pos++; + _scriptTimers[0].ticks = READ_LE_UINT16(pos); + _scriptTimers[0].func = 0; + _scriptTimers[0].next = _system->getMillis() + _scriptTimers[0].ticks * _tickLength; + pos+= 2; + } else { + for (int i = 0; i < 2; i++) { + int a = (*pos == 0xEC) ? 0 : ((*pos == 0xEA) ? 1 : -1); + pos++; + if (a == -1) + continue; + toggleWallState(pos[13], a); + _doorType[pos[13]] = pos[14]; + _noDoorSwitch[pos[13]] = pos[15]; + pos = loadDoorShapes((const char*)pos, pos[13], pos + 16); + } + } + + _stepsUntilScriptCall = READ_LE_UINT16(pos); + pos+= 2; + _stepCounter = 0; + + for (int i = 0; i < 2; i++) { + if (_flags.gameID == GI_EOB1) { + if (*pos == 0xFF) + continue; + loadMonsterShapes((const char *)(pos + 1), i * 18, false, *pos * 18); + pos += 13; + } else { + if (*pos++ != 0xEC) + continue; + loadMonsterShapes((const char *)(pos + 2), pos[1] * 18, pos[15] ? true : false, *pos * 18); + pos += 16; + } + } + + if (_flags.gameID == GI_EOB1) + pos = loadActiveMonsterData(pos, _currentLevel) - 1; + else + pos = loadMonsterProperties(pos); + + if (*pos++ == 0xEC || _flags.gameID == GI_EOB1) { + int num = READ_LE_UINT16(pos); + pos += 2; + + for (int i = 0; i < num; i++) { + if (*pos++ == 0xEC) { + loadDecorations((const char*)pos, (const char*)(pos + slen)); + pos += (slen << 1); + } else { + assignWallsAndDecorations(pos[0], pos[1], (int8)pos[2], pos[3], pos[4]); + pos += 5; + } + } + } + + if (_flags.gameID == GI_EOB2) + pos = initScriptTimers(pos); + + return _curGfxFile; +} + +void EobCoreEngine::addLevelItems() { + for (int i = 0; i < 1024; i++) + _levelBlockProperties[i].drawObjects = 0; + + for (int i = 0; i < 600; i++) { + if (_items[i].level != _currentLevel || _items[i].block <= 0) + continue; + setItemPosition((Item*)&_levelBlockProperties[_items[i].block & 0x3ff].drawObjects, _items[i].block, i, _items[i].pos); + } +} + +void EobCoreEngine::loadVcnData(const char *file, const char*/*nextFile*/) { + if (file) + strcpy(_lastBlockDataFile, file); + + char fname[13]; + snprintf(fname, sizeof(fname), "%s.VCN", _lastBlockDataFile); + _screen->loadBitmap(fname, 3, 3, 0); + const uint8 *v = _screen->getCPagePtr(2); + uint32 tlen = READ_LE_UINT16(v) << 5; + v += 2; + memcpy(_vcnExpTable, v, 32); + v += 32; + delete[] _vcnBlocks; + _vcnBlocks = new uint8[tlen]; + memcpy(_vcnBlocks, v, tlen); +} + +void EobCoreEngine::loadBlockProperties(const char *mazFile) { + memset(_levelBlockProperties, 0, 1024 * sizeof(LevelBlockProperty)); + Common::SeekableReadStream *s = _res->createReadStream(mazFile); + _screen->loadFileDataToPage(s, 2, 4096); + delete s; + + if (_hasTempDataFlags & (1 << (_currentLevel - 1))) { + restoreBlockTempData(_currentLevel); + return; + } + + const uint8 *p = _screen->getCPagePtr(2) + 6; + + for (int i = 0; i < 1024; i++) { + for (int ii = 0; ii < 4; ii++) + _levelBlockProperties[i].walls[ii] = *p++; + } +} + +void EobCoreEngine::loadDecorations(const char *cpsFile, const char *decFile) { + _screen->loadEobBitmap(cpsFile, 3, 3); + Common::SeekableReadStream *s = _res->createReadStream(decFile); + + _levelDecorationDataSize = s->readUint16LE(); + delete[] _levelDecorationData; + _levelDecorationData = new LevelDecorationProperty[_levelDecorationDataSize]; + + for (int i = 0; i < _levelDecorationDataSize; i++) { + LevelDecorationProperty *l = &_levelDecorationData[i]; + for (int ii = 0; ii < 10; ii++) { + l->shapeIndex[ii] = s->readByte(); + if (l->shapeIndex[ii] == 0xff) + l->shapeIndex[ii] = 0xffff; + } + l->next = s->readByte(); + l->flags = s->readByte(); + for (int ii = 0; ii < 10; ii++) + l->shapeX[ii] = s->readSint16LE(); + for (int ii = 0; ii < 10; ii++) + l->shapeY[ii] = s->readSint16LE(); + } + + int len = s->readUint16LE(); + delete[] _levelDecorationRects; + _levelDecorationRects = new EobRect8[len]; + for (int i = 0; i < len; i++) { + EobRect8 *l = &_levelDecorationRects[i]; + l->x = s->readUint16LE(); + l->y = s->readUint16LE(); + l->w = s->readUint16LE(); + l->h = s->readUint16LE(); + } + + delete s; +} + +void EobCoreEngine::assignWallsAndDecorations(int wallIndex, int vmpIndex, int decIndex, int specialType, int flags) { + _wllVmpMap[wallIndex] = vmpIndex; + _wllShapeMap[wallIndex] = _mappedDecorationsCount + 1; + _specialWallTypes[wallIndex] = specialType; + _wllWallFlags[wallIndex] = flags ^ 4; + + if (decIndex == -1) { + _wllShapeMap[wallIndex] = 0; + return; + } + + do { + memcpy(&_levelDecorationProperties[_mappedDecorationsCount], &_levelDecorationData[decIndex], sizeof(LevelDecorationProperty)); + + for (int i = 0; i < 10; i++) { + uint16 t = _levelDecorationProperties[_mappedDecorationsCount].shapeIndex[i]; + if (t == 0xffff) + continue; + + if (_levelDecorationShapes[t]) + continue; + + EobRect8 *r = &_levelDecorationRects[t]; + if (r->w == 0 || r->h == 0) + error("Error trying to make decoration %d x: %d y:%d w:%d h:%d", decIndex, r->x, r->y, r->w, r->h); + + _levelDecorationShapes[t] = _screen->encodeShape(r->x, r->y, r->w, r->h); + } + + decIndex = _levelDecorationProperties[_mappedDecorationsCount++].next; + + if (decIndex) + _levelDecorationProperties[_mappedDecorationsCount - 1].next = _mappedDecorationsCount + 1; + else + decIndex = -1; + + } while (decIndex != -1); +} + +void EobCoreEngine::releaseDecorations() { + if (_levelDecorationShapes) { + for (int i = 0; i < 400; i++) { + delete[] _levelDecorationShapes[i]; + _levelDecorationShapes[i] = 0; + } + } + _mappedDecorationsCount = 0; +} + +void EobCoreEngine::releaseDoorShapes() { + for (int i = 0; i < 6; i++) { + delete[] _doorShapes[i]; + _doorShapes[i] = 0; + delete[] _doorSwitches[i].shp; + _doorSwitches[i].shp = 0; + } +} + +void EobCoreEngine::toggleWallState(int wall, int toggle) { + wall = wall * 10 + 3; + + for (int i = 0; i < 9 ; i++) { + if (i == 4) + continue; + + if (toggle) + _wllWallFlags[wall + i] |= 2; + else + _wllWallFlags[wall + i] &= 0xfd; + } +} + +void EobCoreEngine::drawScene(int update) { + generateBlockDrawingBuffer(); + drawVcnBlocks(); + drawSceneShapes(); + + if (_sceneDrawPage2) { + if (update) + _screen->fillRect(0, 0, 176, 120, 12); + + _screen->setScreenPalette(_screen->getPalette(0)); + _sceneDrawPage2 = 0; + } + + uint32 ct = _system->getMillis(); + if (_flashShapeTimer > ct) { + int diff = _flashShapeTimer - ct; + while ((diff > 0) && !shouldQuit()) { + updateInput(); + uint32 step = MIN<uint32>(diff, _tickLength / 5); + _system->delayMillis(step); + diff -= step; + } + } + + if (_sceneDefaultUpdate) { + resetSkipFlag(); + delayUntil(_drawSceneTimer); + } + + if (update && !_partyResting) + _screen->copyRegion(0, 0, 0, 0, 176, 120, 2, 0, Screen::CR_NO_P_CHECK); + + updateEnvironmentalSfx(0); + + if (!_dialogueField && update && !_updateFlags) + gui_drawCompass(false); + + if (update && !_partyResting) + _screen->updateScreen(); + + if (_sceneDefaultUpdate) { + _sceneDefaultUpdate = false; + _drawSceneTimer = _system->getMillis() /*+ 4 * _tickLength*/; + } + + _sceneUpdateRequired = false; +} + +void EobCoreEngine::drawSceneShapes() { + for (int i = 0; i < 18; i++) { + uint8 t = _dscTileIndex[i]; + uint8 s = _visibleBlocks[t]->walls[_sceneDrawVarDown]; + + _shpDmX1 = 0; + _shpDmX2 = 0; + + setLevelShapesDim(t, _shpDmX1, _shpDmX2, _sceneShpDim); + + if (_shpDmX2 <= _shpDmX1) + continue; + + drawDecorations(t); + + if (_visibleBlocks[t]->drawObjects) + drawBlockItems(t); + + if (t < 15) { + uint16 w = _wllWallFlags[s]; + + if (w & 8) + drawDoor(t); + + if (_visibleBlocks[t]->flags & 7) { + const ScreenDim *dm = _screen->getScreenDim(5); + _screen->modifyScreenDim(5, dm->sx, _lvlShapeTop[t], dm->w, _lvlShapeBottom[t] - _lvlShapeTop[t]); + drawMonsters(t); + drawLevelModifyScreenDim(5, _lvlShapeLeftRight[(t << 1)], 0, _lvlShapeLeftRight[(t << 1) + 1], 15); + } + + if (_flags.gameID == GI_EOB2 && s == 74) + drawWallOfForce(t); + } + + drawFlyingObjects(t); + + if (s == _teleporterWallId) + drawTeleporter(t); + } +} + +void EobCoreEngine::drawDecorations(int index) { + static const int16 *dscWalls[] = { 0, 0, &_sceneDrawVarDown, &_sceneDrawVarRight, &_sceneDrawVarDown, + &_sceneDrawVarRight, &_sceneDrawVarDown, 0, &_sceneDrawVarDown, &_sceneDrawVarLeft, &_sceneDrawVarDown, + &_sceneDrawVarLeft, 0, 0, &_sceneDrawVarDown, &_sceneDrawVarRight, &_sceneDrawVarDown, &_sceneDrawVarRight, + &_sceneDrawVarDown, 0, &_sceneDrawVarDown, &_sceneDrawVarLeft, &_sceneDrawVarDown, &_sceneDrawVarLeft, + &_sceneDrawVarDown, &_sceneDrawVarRight, &_sceneDrawVarDown, 0, &_sceneDrawVarDown, &_sceneDrawVarLeft, + 0, &_sceneDrawVarRight, &_sceneDrawVarDown, 0, 0, &_sceneDrawVarLeft + }; + + for (int i = 1; i >= 0; i--) { + int s = index * 2 + i; + if (dscWalls[s]) { + int d = *dscWalls[s]; + int8 l = _wllShapeMap[_visibleBlocks[index]->walls[d]]; + + uint8 *shapeData = 0; + + int x = 0; + + while (l > 0) { + l--; + int8 ix = _dscShapeIndex[s]; + uint8 shpIx = ABS(ix) - 1; + uint8 flg = _levelDecorationProperties[l].flags; + + if ((i == 0) && (flg & 1 || ((flg & 2) && _wllProcessFlag))) + ix = -ix; + + if (_levelDecorationProperties[l].shapeIndex[shpIx] == 0xffff) { + l = _levelDecorationProperties[l].next; + continue; + } + + shapeData = _levelDecorationShapes[_levelDecorationProperties[l].shapeIndex[shpIx]]; + if (shapeData) { + x = 0; + if (i == 0) { + if (flg & 4) + x += _dscShapeCoords[(index * 5 + 4) << 1]; + else + x += _dscShapeX[index]; + } + + if (ix < 0) { + x += (176 - _levelDecorationProperties[l].shapeX[shpIx] - (shapeData[2] << 3)); + drawBlockObject(1, 2, shapeData, x, _levelDecorationProperties[l].shapeY[shpIx], _sceneShpDim); + } else { + x += _levelDecorationProperties[l].shapeX[shpIx]; + drawBlockObject(0, 2, shapeData, x, _levelDecorationProperties[l].shapeY[shpIx], _sceneShpDim); + + } + } + l = _levelDecorationProperties[l].next; + continue; + } + } + } +} + +int EobCoreEngine::calcNewBlockPositionAndTestPassability(uint16 curBlock, uint16 direction) { + uint16 b = calcNewBlockPosition(curBlock, direction); + int w = _levelBlockProperties[b].walls[direction ^ 2]; + int f = _wllWallFlags[w]; + + if (w == 74 && _currentBlock == curBlock) { + for (int i = 0; i < 5; i++) { + + } + } + + if (!(f & 1) || _levelBlockProperties[b].flags & 7) + return -1; + + return b; +} + +void EobCoreEngine::notifyBlockNotPassable() { + _txt->printMessage(_warningStrings[0]); + snd_playSoundEffect(29); + removeInputTop(); +} + +void EobCoreEngine::moveParty(uint16 block) { + //processMonstersUnk1(); + uint16 old = _currentBlock; + _currentBlock = block; + + runLevelScript(old, 2); + + if (++_moveCounter > 3) { + _txt->printMessage("\r"); + _moveCounter = 0; + } + + runLevelScript(block, 1); + + if (_levelBlockProperties[block].walls[0] == 26) + memset(_levelBlockProperties[block].walls, 0, 4); + + //processMonstersUnk1(); + _stepCounter++; + //_keybControlUnk = -1; + _sceneUpdateRequired = true; + + checkFlyingObjects(); +} + +int EobCoreEngine::clickedDoorSwitch(uint16 block, uint16 direction) { + uint8 v = _visibleBlocks[13]->walls[_sceneDrawVarDown]; + SpriteDecoration *d = &_doorSwitches[((v > 12 && v < 23) || v == 31) ? 3 : 0]; + int x1 = d->x + _dscShapeCoords[138] - 4; + int y1 = d->y - 4; + if (!posWithinRect(_mouseX, _mouseY, x1, y1, x1 + (d->shp[2] << 3) + 8, y1 + d->shp[1] + 8) && (_clickedSpecialFlag == 0x40)) + return clickedDoorNoPry(block, direction); + + processDoorSwitch(block, 0); + snd_playSoundEffect(6); + + return 1; +} + +int EobCoreEngine::clickedNiche(uint16 block, uint16 direction) { + uint8 v = _wllShapeMap[_levelBlockProperties[block].walls[direction]]; + if (!clickedShape(v)) + return 0; + + if (_itemInHand) { + if (_dscItemShapeMap[_items[_itemInHand].icon] <= 14) { + _txt->printMessage(_pryDoorStrings[5]); + } else { + setItemPosition((Item*)&_levelBlockProperties[block & 0x3ff].drawObjects, block, _itemInHand, 8); + runLevelScript(block, 4); + setHandItem(0); + _sceneUpdateRequired = true; + } + } else { + int d = getQueuedItem((Item*)&_levelBlockProperties[block].drawObjects, 8, -1); + if (!d) + return 1; + runLevelScript(block, 8); + setHandItem(d); + _sceneUpdateRequired = true; + } + + return 1; +} + +int EobCoreEngine::clickedDoorPry(uint16 block, uint16 direction) { + if (!posWithinRect(_mouseX, _mouseY, 40, 16, 136, 88) && (_clickedSpecialFlag == 0x40)) + return 0; + + int d = -1; + for (int i = 0; i < 6; i++) { + if (!testCharacter(i, 0x0d)) + continue; + if (d >= 0) { + int s1 = _characters[i].strengthCur + _characters[i].strengthExtCur; + int s2 = _characters[d].strengthCur + _characters[d].strengthExtCur; + if (s1 >= s2) + d = i; + } else { + d = i; + } + } + + if (d == -1) { + _txt->printMessage(_pryDoorStrings[_flags.gameID == GI_EOB2 ? 1 : 0]); + return 1; + } + + static const uint8 forceDoorChanceTable[] = { 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 10, 11, 12, 13 }; + int s = _characters[d].strengthCur > 18 ? 18 : _characters[d].strengthCur; + + if (rollDice(1, 20) < forceDoorChanceTable[s]) { + _txt->printMessage(_pryDoorStrings[_flags.gameID == GI_EOB2 ? 2 : 1]); + _levelBlockProperties[block].walls[direction] = _levelBlockProperties[block].walls[direction ^ 2] = + (_levelBlockProperties[block].walls[direction] == (_flags.gameID == GI_EOB2 ? 51 : 30)) ? 8 : 18; + openDoor(block); + } else { + _txt->printMessage(_pryDoorStrings[3]); + } + + return 1; +} + +int EobCoreEngine::clickedDoorNoPry(uint16 block, uint16 direction) { + if (!posWithinRect(_mouseX, _mouseY, 40, 16, 136, 88) && (_clickedSpecialFlag == 0x40)) + return 0; + + if (!(_wllWallFlags[_levelBlockProperties[block].walls[direction]] & 0x20)) + return 0; + _txt->printMessage(_pryDoorStrings[6]); + return 1; +} + +int EobCoreEngine::specialWallAction(int block, int direction) { + direction ^= 2; + uint8 type = _specialWallTypes[_levelBlockProperties[block].walls[direction]]; + if (!type || !(_clickedSpecialFlag & (((_levelBlockProperties[block].flags & 0xf8) >> 3) | 0xe0))) + return 0; + + int res = 0; + switch (type) { + case 1: + res = clickedDoorSwitch(block, direction); + break; + + case 2: + case 8: + res = clickedWallShape(block, direction); + break; + + case 3: + res = clickedLeverOn(block, direction); + break; + + case 4: + res = clickedLeverOff(block, direction); + break; + + case 5: + res = clickedDoorPry(block, direction); + break; + + case 6: + res = clickedDoorNoPry(block, direction); + break; + + case 7: + case 9: + res = clickedWallOnlyScript(block); + break; + + case 10: + res = clickedNiche(block, direction); + break; + + default: + break; + } + + _clickedSpecialFlag = 0; + _sceneUpdateRequired = true; + + return res; +} + +void EobCoreEngine::openDoor(int block) { + openCloseDoor(block, 1); +} + +void EobCoreEngine::closeDoor(int block) { + if (block == _currentBlock || _levelBlockProperties[block].flags & 7) + return; + openCloseDoor(block, -1); +} + +} // End of namespace Kyra + +#endif // ENABLE_EOB diff --git a/engines/kyra/scene_lol.cpp b/engines/kyra/scene_lol.cpp index a5a2562448..c2b16cab88 100644 --- a/engines/kyra/scene_lol.cpp +++ b/engines/kyra/scene_lol.cpp @@ -42,8 +42,8 @@ void LoLEngine::loadLevel(int index) { stopPortraitSpeechAnim(); for (int i = 0; i < 400; i++) { - delete[] _levelShapes[i]; - _levelShapes[i] = 0; + delete[] _levelDecorationShapes[i]; + _levelDecorationShapes[i] = 0; } _emc->unload(&_scriptData); @@ -160,7 +160,7 @@ void LoLEngine::loadLevelWallData(int index, bool mapShapes) { if (mapShapes) { int16 sh = (int16) READ_LE_UINT16(d); if (sh > 0) - _wllShapeMap[c] = assignLevelShapes(sh); + _wllShapeMap[c] = assignLevelDecorationShapes(sh); else _wllShapeMap[c] = *d; } @@ -169,7 +169,7 @@ void LoLEngine::loadLevelWallData(int index, bool mapShapes) { d += 2; _wllWallFlags[c] = *d; d += 2; - _wllBuffer4[c] = *d; + _wllAutomapData[c] = *d; d += 2; } @@ -179,7 +179,7 @@ void LoLEngine::loadLevelWallData(int index, bool mapShapes) { _lvlShpFileHandle = 0; } -int LoLEngine::assignLevelShapes(int index) { +int LoLEngine::assignLevelDecorationShapes(int index) { uint16 *p1 = (uint16 *)_tempBuffer5120; uint16 *p2 = (uint16 *)(_tempBuffer5120 + 4000); @@ -187,34 +187,34 @@ int LoLEngine::assignLevelShapes(int index) { if (r) return r; - uint16 o = _lvlBlockIndex++; + uint16 o = _mappedDecorationsCount++; - memcpy(&_levelShapeProperties[o], &_levelFileData[index], sizeof(LevelShapeProperty)); + memcpy(&_levelDecorationProperties[o], &_levelDecorationData[index], sizeof(LevelDecorationProperty)); for (int i = 0; i < 10; i++) { - uint16 t = _levelShapeProperties[o].shapeIndex[i]; + uint16 t = _levelDecorationProperties[o].shapeIndex[i]; if (t == 0xffff) continue; uint16 pv = p1[t]; if (pv) { - _levelShapeProperties[o].shapeIndex[i] = pv; + _levelDecorationProperties[o].shapeIndex[i] = pv; } else { - _levelShapes[_lvlShapeIndex] = getLevelShapes(t); + _levelDecorationShapes[_lvlShapeIndex] = getLevelDecorationShapes(t); p1[t] = _lvlShapeIndex; - _levelShapeProperties[o].shapeIndex[i] = _lvlShapeIndex++; + _levelDecorationProperties[o].shapeIndex[i] = _lvlShapeIndex++; } } p2[index] = o; - if (_levelShapeProperties[o].next) - _levelShapeProperties[o].next = assignLevelShapes(_levelShapeProperties[o].next); + if (_levelDecorationProperties[o].next) + _levelDecorationProperties[o].next = assignLevelDecorationShapes(_levelDecorationProperties[o].next); return o; } -uint8 *LoLEngine::getLevelShapes(int shapeIndex) { - if (_lvlShpNum <= shapeIndex) +uint8 *LoLEngine::getLevelDecorationShapes(int shapeIndex) { + if (_decorationCount <= shapeIndex) return 0; _lvlShpFileHandle->seek(shapeIndex * 4 + 2, SEEK_SET); @@ -232,60 +232,6 @@ uint8 *LoLEngine::getLevelShapes(int shapeIndex) { return res; } -void LoLEngine::restoreBlockTempData(int index) { - memset(_tempBuffer5120, 0, 5120); - int l = index - 1; - - memcpy(_monsters, _lvlTempData[l]->monsters, sizeof(MonsterInPlay) * 30); - memcpy(_flyingObjects, _lvlTempData[l]->flyingObjects, sizeof(FlyingObject) * 8); - - Common::String filename = Common::String::format("LEVEL%d.CMZ", index); - - _screen->loadBitmap(filename.c_str(), 3, 3, 0); - const uint8 *p = _screen->getCPagePtr(2); - uint16 len = READ_LE_UINT16(p + 4); - p += 6; - - memset(_levelBlockProperties, 0, 1024 * sizeof(LevelBlockProperty)); - - uint8 *t = _lvlTempData[l]->wallsXorData; - uint8 *t2 = _lvlTempData[l]->flags; - - for (int i = 0; i < 1024; i++) { - for (int ii = 0; ii < 4; ii++) - _levelBlockProperties[i].walls[ii] = p[i * len + ii] ^ *t++; - _levelBlockProperties[i].flags = *t2++; - } - - for (int i = 0; i < 30; i++) { - if (_monsters[i].block) { - _monsters[i].block = 0; - _monsters[i].properties = &_monsterProperties[_monsters[i].type]; - placeMonster(&_monsters[i], _monsters[i].x, _monsters[i].y); - } - } - - restoreTempDataAdjustMonsterStrength(l); -} - -void LoLEngine::restoreTempDataAdjustMonsterStrength(int index) { - if (_lvlTempData[index]->monsterDifficulty == _monsterDifficulty) - return; - - uint16 d = (_monsterModifiers[_lvlTempData[index]->monsterDifficulty] << 8) / _monsterModifiers[_monsterDifficulty]; - - for (int i = 0; i < 30; i++) { - if (_monsters[i].mode >= 14 || _monsters[i].block == 0 || _monsters[i].hitPoints <= 0) - continue; - - _monsters[i].hitPoints = (d * _monsters[i].hitPoints) >> 8; - if (_monsterDifficulty < _lvlTempData[index]->monsterDifficulty) - _monsters[i].hitPoints++; - if (_monsters[i].hitPoints == 0) - _monsters[i].hitPoints = 1; - } -} - void LoLEngine::loadBlockProperties(const char *cmzFile) { memset(_levelBlockProperties, 0, 1024 * sizeof(LevelBlockProperty)); _screen->loadBitmap(cmzFile, 2, 2, 0); @@ -299,7 +245,7 @@ void LoLEngine::loadBlockProperties(const char *cmzFile) { _levelBlockProperties[i].direction = 5; - if (_wllBuffer4[_levelBlockProperties[i].walls[0]] == 17) { + if (_wllAutomapData[_levelBlockProperties[i].walls[0]] == 17) { _levelBlockProperties[i].flags &= 0xef; _levelBlockProperties[i].flags |= 0x20; } @@ -310,15 +256,15 @@ void LoLEngine::loadLevelShpDat(const char *shpFile, const char *datFile, bool f memset(_tempBuffer5120, 0, 5120); _lvlShpFileHandle = _res->createReadStream(shpFile); - _lvlShpNum = _lvlShpFileHandle->readUint16LE(); + _decorationCount = _lvlShpFileHandle->readUint16LE(); Common::SeekableReadStream *s = _res->createReadStream(datFile); - _levelFileDataSize = s->readUint16LE(); - delete[] _levelFileData; - _levelFileData = new LevelShapeProperty[_levelFileDataSize]; - for (int i = 0; i < _levelFileDataSize; i++) { - LevelShapeProperty * l = &_levelFileData[i]; + _levelDecorationDataSize = s->readUint16LE(); + delete[] _levelDecorationData; + _levelDecorationData = new LevelDecorationProperty[_levelDecorationDataSize]; + for (int i = 0; i < _levelDecorationDataSize; i++) { + LevelDecorationProperty * l = &_levelDecorationData[i]; for (int ii = 0; ii < 10; ii++) l->shapeIndex[ii] = s->readUint16LE(); for (int ii = 0; ii < 10; ii++) @@ -334,7 +280,7 @@ void LoLEngine::loadLevelShpDat(const char *shpFile, const char *datFile, bool f delete s; if (!flag) { - _lvlBlockIndex = 1; + _mappedDecorationsCount = 1; _lvlShapeIndex = 1; } } @@ -515,10 +461,10 @@ void LoLEngine::resetItems(int flag) { for (int i = 0; i < 1024; i++) { _levelBlockProperties[i].direction = 5; uint16 id = _levelBlockProperties[i].assignedObjects; - MonsterInPlay *r = 0; + LolMonsterInPlay *r = 0; while (id & 0x8000) { - r = (MonsterInPlay *)findObject(id); + r = (LolMonsterInPlay *)findObject(id); id = r->nextAssignedObject; } @@ -537,7 +483,7 @@ void LoLEngine::resetItems(int flag) { } void LoLEngine::disableMonsters() { - memset(_monsters, 0, 30 * sizeof(MonsterInPlay)); + memset(_monsters, 0, 30 * sizeof(LolMonsterInPlay)); for (int i = 0; i < 30; i++) _monsters[i].mode = 0x10; } @@ -751,11 +697,6 @@ void LoLEngine::moveParty(uint16 direction, int unk1, int unk2, int buttonShape) updateAutoMap(_currentBlock); } -uint16 LoLEngine::calcNewBlockPosition(uint16 curBlock, uint16 direction) { - static const int16 blockPosTable[] = { -32, 1, 32, -1 }; - return (curBlock + blockPosTable[direction]) & 0x3ff; -} - uint16 LoLEngine::calcBlockIndex(uint16 x, uint16 y) { return (((y & 0xff00) >> 3) | (x >> 8)) & 0x3ff; } @@ -827,51 +768,6 @@ void LoLEngine::notifyBlockNotPassable(int scrollFlag) { snd_playSoundEffect(19, -1); } -int LoLEngine::clickedWallShape(uint16 block, uint16 direction) { - uint8 v = _wllShapeMap[_levelBlockProperties[block].walls[direction]]; - if (!clickedShape(v)) - return 0; - - snd_stopSpeech(true); - runLevelScript(block, 0x40); - - return 1; -} - -int LoLEngine::clickedLeverOn(uint16 block, uint16 direction) { - uint8 v = _wllShapeMap[_levelBlockProperties[block].walls[direction]]; - if (!clickedShape(v)) - return 0; - - _levelBlockProperties[block].walls[direction]++; - _sceneUpdateRequired = true; - - snd_playSoundEffect(30, -1); - - runLevelScript(block, 0x40); - - return 1; -} - -int LoLEngine::clickedLeverOff(uint16 block, uint16 direction) { - uint8 v = _wllShapeMap[_levelBlockProperties[block].walls[direction]]; - if (!clickedShape(v)) - return 0; - - _levelBlockProperties[block].walls[direction]--; - _sceneUpdateRequired = true; - - snd_playSoundEffect(29, -1); - - runLevelScript(block, 0x40); - return 1; -} - -int LoLEngine::clickedWallOnlyScript(uint16 block) { - runLevelScript(block, 0x40); - return 1; -} - int LoLEngine::clickedDoorSwitch(uint16 block, uint16 direction) { uint8 v = _wllShapeMap[_levelBlockProperties[block].walls[direction]]; if (!clickedShape(v)) @@ -903,119 +799,6 @@ int LoLEngine::clickedNiche(uint16 block, uint16 direction) { return 1; } -bool LoLEngine::clickedShape(int shapeIndex) { - while (shapeIndex) { - uint16 s = _levelShapeProperties[shapeIndex].shapeIndex[1]; - - if (s == 0xffff) { - shapeIndex = _levelShapeProperties[shapeIndex].next; - continue; - } - - int w = _levelShapes[s][3]; - int h = _levelShapes[s][2]; - int x = _levelShapeProperties[shapeIndex].shapeX[1] + 136; - int y = _levelShapeProperties[shapeIndex].shapeY[1] + 8; - - if (_levelShapeProperties[shapeIndex].flags & 1) - w <<= 1; - - if (posWithinRect(_mouseX, _mouseY, x - 4, y - 4, x + w + 8, y + h + 8)) - return true; - - shapeIndex = _levelShapeProperties[shapeIndex].next; - } - - return false; -} - -void LoLEngine::processDoorSwitch(uint16 block, int openClose) { - if ((block == _currentBlock) || (_levelBlockProperties[block].assignedObjects & 0x8000)) - return; - - if (openClose == 0) { - for (int i = 0; i < 3; i++) { - if (_openDoorState[i].block != block) - continue; - openClose = -_openDoorState[i].state; - break; - } - } - - if (openClose == 0) - openClose = (_wllWallFlags[_levelBlockProperties[block].walls[_wllWallFlags[_levelBlockProperties[block].walls[0]] & 8 ? 0 : 1]] & 1) ? 1 : -1; - - openCloseDoor(block, openClose); -} - -void LoLEngine::openCloseDoor(uint16 block, int openClose) { - int s1 = -1; - int s2 = -1; - - int c = (_wllWallFlags[_levelBlockProperties[block].walls[0]] & 8) ? 0 : 1; - int v = _levelBlockProperties[block].walls[c]; - int flg = (openClose == 1) ? 0x10 : (openClose == -1 ? 0x20 : 0); - - if (_wllWallFlags[v] & flg) - return; - - for (int i = 0; i < 3; i++) { - if (_openDoorState[i].block == block) { - s1 = i; - break; - } else if (_openDoorState[i].block == 0 && s2 == -1) { - s2 = i; - } - } - - if (s1 != -1 || s2 != -1) { - if (s1 == -1) - s1 = s2; - - _openDoorState[s1].block = block; - _openDoorState[s1].state = openClose; - _openDoorState[s1].wall = c; - - flg = (-openClose == 1) ? 0x10 : (-openClose == -1 ? 0x20 : 0); - - if (_wllWallFlags[v] & flg) { - _levelBlockProperties[block].walls[c] += openClose; - _levelBlockProperties[block].walls[c ^ 2] += openClose; - - int snd = (openClose == -1) ? 32 : 31; - - snd_processEnvironmentalSoundEffect(snd, block); - if (!checkSceneUpdateNeed(block)) - updateEnvironmentalSfx(0); - } - - enableTimer(0); - - } else { - while (!(flg & _wllWallFlags[v])) - v += openClose; - - _levelBlockProperties[block].walls[c] = _levelBlockProperties[block].walls[c ^ 2] = v; - checkSceneUpdateNeed(block); - } -} - -void LoLEngine::completeDoorOperations() { - for (int i = 0; i < 3; i++) { - if (!_openDoorState[i].block) - continue; - - uint16 b = _openDoorState[i].block; - - do { - _levelBlockProperties[b].walls[_openDoorState[i].wall] += _openDoorState[i].state; - _levelBlockProperties[b].walls[_openDoorState[i].wall ^ 2] += _openDoorState[i].state; - } while (!(_wllWallFlags[_levelBlockProperties[b].walls[_openDoorState[i].wall]] & 0x30)); - - _openDoorState[i].block = 0; - } -} - void LoLEngine::movePartySmoothScrollBlocked(int speed) { if (!_smoothScrollingEnabled || (_smoothScrollingEnabled && _needSceneRestore)) return; @@ -1447,7 +1230,7 @@ void LoLEngine::setWallType(int block, int wall, int val) { if (wall == -1) { for (int i = 0; i < 4; i++) _levelBlockProperties[block].walls[i] = val; - if (_wllBuffer4[val] == 17) { + if (_wllAutomapData[val] == 17) { _levelBlockProperties[block].flags &= 0xef; _levelBlockProperties[block].flags |= 0x20; } else { @@ -1603,334 +1386,20 @@ void LoLEngine::setDefaultButtonState() { _lampStatusSuspended = false; } -void LoLEngine::generateBlockDrawingBuffer() { - _sceneDrawVarDown = _dscBlockMap[_currentDirection]; - _sceneDrawVarRight = _dscBlockMap[_currentDirection + 4]; - _sceneDrawVarLeft = _dscBlockMap[_currentDirection + 8]; - - /******************************************* - * _visibleBlocks map * - * * - * | | | | | | * - * 00 | 01 | 02 | 03 | 04 | 05 | 06 * - * ____|_____|_____|_____|_____|_____|_____ * - * | | | | | | * - * | 07 | 08 | 09 | 10 | 11 | * - * |_____|_____|_____|_____|_____| * - * | | | | * - * | 12 | 13 | 14 | * - * |_____|_____|_____| * - * | | * - * 15 | 16 | 17 * - * | (P) | * - ********************************************/ - - memset(_blockDrawingBuffer, 0, 660 * sizeof(uint16)); - - _wllProcessFlag = ((_currentBlock >> 5) + (_currentBlock & 0x1f) + _currentDirection) & 1; - - if (_wllProcessFlag) // floor and ceiling - generateVmpTileDataFlipped(0, 15, 1, -330, 22, 15); - else - generateVmpTileData(0, 15, 1, -330, 22, 15); - - assignVisibleBlocks(_currentBlock, _currentDirection); - - uint8 t = _visibleBlocks[0]->walls[_sceneDrawVarRight]; - if (t) - generateVmpTileData(-2, 3, t, 102, 3, 5); - - t = _visibleBlocks[6]->walls[_sceneDrawVarLeft]; - if (t) - generateVmpTileDataFlipped(21, 3, t, 102, 3, 5); - - t = _visibleBlocks[1]->walls[_sceneDrawVarRight]; - uint8 t2 = _visibleBlocks[2]->walls[_sceneDrawVarDown]; - - if (hasWall(t) && !(_wllWallFlags[t2] & 8)) - generateVmpTileData(2, 3, t, 102, 3, 5); - else if (t && (_wllWallFlags[t2] & 8)) - generateVmpTileData(2, 3, t2, 102, 3, 5); - - t = _visibleBlocks[5]->walls[_sceneDrawVarLeft]; - t2 = _visibleBlocks[4]->walls[_sceneDrawVarDown]; - - if (hasWall(t) && !(_wllWallFlags[t2] & 8)) - generateVmpTileDataFlipped(17, 3, t, 102, 3, 5); - else if (t && (_wllWallFlags[t2] & 8)) - generateVmpTileDataFlipped(17, 3, t2, 102, 3, 5); - - t = _visibleBlocks[2]->walls[_sceneDrawVarRight]; - if (t) - generateVmpTileData(8, 3, t, 97, 1, 5); - - t = _visibleBlocks[4]->walls[_sceneDrawVarLeft]; - if (t) - generateVmpTileDataFlipped(13, 3, t, 97, 1, 5); - - t = _visibleBlocks[1]->walls[_sceneDrawVarDown]; - if (hasWall(t)) - generateVmpTileData(-4, 3, t, 129, 6, 5); - - t = _visibleBlocks[5]->walls[_sceneDrawVarDown]; - if (hasWall(t)) - generateVmpTileData(20, 3, t, 129, 6, 5); - - t = _visibleBlocks[2]->walls[_sceneDrawVarDown]; - if (hasWall(t)) - generateVmpTileData(2, 3, t, 129, 6, 5); - - t = _visibleBlocks[4]->walls[_sceneDrawVarDown]; - if (hasWall(t)) - generateVmpTileData(14, 3, t, 129, 6, 5); - - t = _visibleBlocks[3]->walls[_sceneDrawVarDown]; - if (t) - generateVmpTileData(8, 3, t, 129, 6, 5); - - t = _visibleBlocks[7]->walls[_sceneDrawVarRight]; - if (t) - generateVmpTileData(0, 3, t, 117, 2, 6); - - t = _visibleBlocks[11]->walls[_sceneDrawVarLeft]; - if (t) - generateVmpTileDataFlipped(20, 3, t, 117, 2, 6); - - t = _visibleBlocks[8]->walls[_sceneDrawVarRight]; - if (t) - generateVmpTileData(6, 2, t, 81, 2, 8); - - t = _visibleBlocks[10]->walls[_sceneDrawVarLeft]; - if (t) - generateVmpTileDataFlipped(14, 2, t, 81, 2, 8); - - t = _visibleBlocks[8]->walls[_sceneDrawVarDown]; - if (hasWall(t)) - generateVmpTileData(-4, 2, t, 159, 10, 8); - - t = _visibleBlocks[10]->walls[_sceneDrawVarDown]; - if (hasWall(t)) - generateVmpTileData(16, 2, t, 159, 10, 8); - - t = _visibleBlocks[9]->walls[_sceneDrawVarDown]; - if (t) - generateVmpTileData(6, 2, t, 159, 10, 8); - - t = _visibleBlocks[12]->walls[_sceneDrawVarRight]; - if (t) - generateVmpTileData(3, 1, t, 45, 3, 12); - - t = _visibleBlocks[14]->walls[_sceneDrawVarLeft]; - if (t) - generateVmpTileDataFlipped(16, 1, t, 45, 3, 12); - - t = _visibleBlocks[12]->walls[_sceneDrawVarDown]; - if (!(_wllWallFlags[t] & 8)) - generateVmpTileData(-13, 1, t, 239, 16, 12); - - t = _visibleBlocks[14]->walls[_sceneDrawVarDown]; - if (!(_wllWallFlags[t] & 8)) - generateVmpTileData(19, 1, t, 239, 16, 12); - - t = _visibleBlocks[13]->walls[_sceneDrawVarDown]; - if (t) - generateVmpTileData(3, 1, t, 239, 16, 12); - - t = _visibleBlocks[15]->walls[_sceneDrawVarRight]; - t2 = _visibleBlocks[17]->walls[_sceneDrawVarLeft]; - if (t) - generateVmpTileData(0, 0, t, 0, 3, 15); - if (t2) - generateVmpTileDataFlipped(19, 0, t2, 0, 3, 15); -} - -void LoLEngine::generateVmpTileData(int16 startBlockX, uint8 startBlockY, uint8 vmpMapIndex, int16 vmpOffset, uint8 numBlocksX, uint8 numBlocksY) { - if (!_wllVmpMap[vmpMapIndex]) - return; - - uint16 *vmp = &_vmpPtr[(_wllVmpMap[vmpMapIndex] - 1) * 431 + vmpOffset + 330]; - - for (int i = 0; i < numBlocksY; i++) { - uint16 *bl = &_blockDrawingBuffer[(startBlockY + i) * 22 + startBlockX]; - for (int ii = 0; ii < numBlocksX; ii++) { - if ((startBlockX + ii >= 0) && (startBlockX + ii < 22) && *vmp) - *bl = *vmp; - bl++; - vmp++; - } - } -} - -void LoLEngine::generateVmpTileDataFlipped(int16 startBlockX, uint8 startBlockY, uint8 vmpMapIndex, int16 vmpOffset, uint8 numBlocksX, uint8 numBlocksY) { - if (!_wllVmpMap[vmpMapIndex]) - return; - - uint16 *vmp = &_vmpPtr[(_wllVmpMap[vmpMapIndex] - 1) * 431 + vmpOffset + 330]; - - for (int i = 0; i < numBlocksY; i++) { - for (int ii = 0; ii < numBlocksX; ii++) { - if ((startBlockX + ii) < 0 || (startBlockX + ii) > 21) - continue; - - uint16 v = vmp[i * numBlocksX + (numBlocksX - 1 - ii)]; - if (!v) - continue; - - if (v & 0x4000) - v -= 0x4000; - else - v |= 0x4000; - - _blockDrawingBuffer[(startBlockY + i) * 22 + startBlockX + ii] = v; - } - } -} - -bool LoLEngine::hasWall(int index) { - if (!index || (_wllWallFlags[index] & 8)) - return false; - return true; -} - -void LoLEngine::assignVisibleBlocks(int block, int direction) { - for (int i = 0; i < 18; i++) { - uint16 t = (block + _dscBlockIndex[direction * 18 + i]) & 0x3ff; - _visibleBlockIndex[i] = t; - - _visibleBlocks[i] = &_levelBlockProperties[t]; - _lvlShapeLeftRight[i] = _lvlShapeLeftRight[18 + i] = -1; - } -} - -void LoLEngine::drawVcnBlocks() { - uint8 *d = _sceneWindowBuffer; - uint16 *bdb = _blockDrawingBuffer; - - for (int y = 0; y < 15; y++) { - for (int x = 0; x < 22; x++) { - bool horizontalFlip = false; - int remainder = 0; - - uint16 vcnOffset = *bdb++; - - if (vcnOffset & 0x8000) { - // this renders a wall block over the transparent pixels of a floor/ceiling block - remainder = vcnOffset - 0x8000; - vcnOffset = 0; - } - - if (vcnOffset & 0x4000) { - horizontalFlip = true; - vcnOffset &= 0x3fff; - } - - uint8 *src = 0; - if (vcnOffset) { - src = &_vcnBlocks[vcnOffset << 5]; - } else { - // floor/ceiling blocks - vcnOffset = bdb[329]; - if (vcnOffset & 0x4000) { - horizontalFlip = true; - vcnOffset &= 0x3fff; - } - - src = (_vcfBlocks ? _vcfBlocks : _vcnBlocks) + (vcnOffset << 5); - } - - uint8 shift = _vcnShift ? _vcnShift[vcnOffset] : _blockBrightness; - - if (horizontalFlip) { - for (int blockY = 0; blockY < 8; blockY++) { - src += 3; - for (int blockX = 0; blockX < 4; blockX++) { - uint8 t = *src--; - *d++ = _vcnExpTable[(t & 0x0f) | shift]; - *d++ = _vcnExpTable[(t >> 4) | shift]; - } - src += 5; - d += 168; - } - } else { - for (int blockY = 0; blockY < 8; blockY++) { - for (int blockX = 0; blockX < 4; blockX++) { - uint8 t = *src++; - *d++ = _vcnExpTable[(t >> 4) | shift]; - *d++ = _vcnExpTable[(t & 0x0f) | shift]; - } - d += 168; - } - } - d -= 1400; - - if (remainder) { - d -= 8; - horizontalFlip = false; - - if (remainder & 0x4000) { - remainder &= 0x3fff; - horizontalFlip = true; - } - - shift = _vcnShift ? _vcnShift[remainder] : _blockBrightness; - src = &_vcnBlocks[remainder << 5]; - - if (horizontalFlip) { - for (int blockY = 0; blockY < 8; blockY++) { - src += 3; - for (int blockX = 0; blockX < 4; blockX++) { - uint8 t = *src--; - uint8 h = _vcnExpTable[(t & 0x0f) | shift]; - uint8 l = _vcnExpTable[(t >> 4) | shift]; - if (h) - *d = h; - d++; - if (l) - *d = l; - d++; - } - src += 5; - d += 168; - } - } else { - for (int blockY = 0; blockY < 8; blockY++) { - for (int blockX = 0; blockX < 4; blockX++) { - uint8 t = *src++; - uint8 h = _vcnExpTable[(t >> 4) | shift]; - uint8 l = _vcnExpTable[(t & 0x0f) | shift]; - if (h) - *d = h; - d++; - if (l) - *d = l; - d++; - } - d += 168; - } - } - d -= 1400; - } - } - d += 1232; - } - - _screen->copyBlockToPage(_sceneDrawPage1, 112, 0, 176, 120, _sceneWindowBuffer); -} - void LoLEngine::drawSceneShapes() { for (int i = 0; i < 18; i++) { uint8 t = _dscTileIndex[i]; uint8 s = _visibleBlocks[t]->walls[_sceneDrawVarDown]; - int16 x1 = 0; - int16 x2 = 0; + _shpDmX1 = 0; + _shpDmX2 = 0; int16 dimY1 = 0; int16 dimY2 = 0; - setLevelShapesDim(t, x1, x2, 13); + setLevelShapesDim(t, _shpDmX1, _shpDmX2, _sceneShpDim); - if (x2 <= x1) + if (_shpDmX2 <= _shpDmX1) continue; drawDecorations(t); @@ -1954,104 +1423,12 @@ void LoLEngine::drawSceneShapes() { if (v > 80) v = 80; - scaleLevelShapesDim(t, dimY1, dimY2, 13); + scaleLevelShapesDim(t, dimY1, dimY2, _sceneShpDim); drawDoor(_doorShapes[(s < 23 ? _dscDoorShpIndex[s] : 0)], 0, t, 10, 0, -v, 2); - setLevelShapesDim(t, dimY1, dimY2, 13); + setLevelShapesDim(t, dimY1, dimY2, _sceneShpDim); } } -void LoLEngine::setLevelShapesDim(int index, int16 &x1, int16 &x2, int dim) { - if (_lvlShapeLeftRight[index << 1] == -1) { - x1 = 0; - x2 = 22; - - int16 y1 = 0; - int16 y2 = 120; - - int m = index * 18; - - for (int i = 0; i < 18; i++) { - uint8 d = _visibleBlocks[i]->walls[_sceneDrawVarDown]; - uint8 a = _wllWallFlags[d]; - - if (a & 8) { - int t = _dscDim2[(m + i) << 1]; - - if (t > x1) { - x1 = t; - if (!(a & 0x10)) - scaleLevelShapesDim(index, y1, y2, -1); - } - - t = _dscDim2[((m + i) << 1) + 1]; - - if (t < x2) { - x2 = t; - if (!(a & 0x10)) - scaleLevelShapesDim(index, y1, y2, -1); - } - } else { - int t = _dscDim1[m + i]; - - if (!_wllVmpMap[d] || t == -40) - continue; - - if (t == -41) { - x1 = 22; - x2 = 0; - break; - } - - if (t > 0 && x2 > t) - x2 = t; - - if (t < 0 && x1 < -t) - x1 = -t; - } - - if (x2 < x1) - break; - } - - x1 += 14; - x2 += 14; - - _lvlShapeTop[index] = y1; - _lvlShapeBottom[index] = y2; - _lvlShapeLeftRight[index << 1] = x1; - _lvlShapeLeftRight[(index << 1) + 1] = x2; - } else { - x1 = _lvlShapeLeftRight[index << 1]; - x2 = _lvlShapeLeftRight[(index << 1) + 1]; - } - - drawLevelModifyScreenDim(dim, x1, 0, x2, 15); -} - -void LoLEngine::scaleLevelShapesDim(int index, int16 &y1, int16 &y2, int dim) { - static const int8 dscY1[] = { 0x1E, 0x18, 0x10, 0x00 }; - static const int8 dscY2[] = { 0x3B, 0x47, 0x56, 0x78 }; - - uint8 a = _dscDimMap[index]; - - if (dim == -1 && a != 3) - a++; - - y1 = dscY1[a]; - y2 = dscY2[a]; - - if (dim == -1) - return; - - const ScreenDim *cDim = _screen->getScreenDim(dim); - - _screen->modifyScreenDim(dim, cDim->sx, y1, cDim->w, y2 - y1); -} - -void LoLEngine::drawLevelModifyScreenDim(int dim, int16 x1, int16 y1, int16 x2, int16 y2) { - _screen->modifyScreenDim(dim, x1, y1 << 3, x2 - x1, (y2 - y1) << 3); -} - void LoLEngine::drawDecorations(int index) { for (int i = 1; i >= 0; i--) { int s = index * 2 + i; @@ -2066,7 +1443,7 @@ void LoLEngine::drawDecorations(int index) { if (!scaleW || !scaleH) continue; - uint8 d = (_currentDirection + _dscUnk1[s]) & 3; + uint8 d = (_currentDirection + _dscWalls[s]) & 3; int8 l = _wllShapeMap[_visibleBlocks[index]->walls[d]]; uint8 *shapeData = 0; @@ -2076,21 +1453,21 @@ void LoLEngine::drawDecorations(int index) { int flags = 0; while (l > 0) { - if ((_levelShapeProperties[l].flags & 8) && index != 3 && index != 9 && index != 13) { - l = _levelShapeProperties[l].next; + if ((_levelDecorationProperties[l].flags & 8) && index != 3 && index != 9 && index != 13) { + l = _levelDecorationProperties[l].next; continue; } - if (_dscOvlMap[shpIx] == 1 && ((_levelShapeProperties[l].flags & 2) || ((_levelShapeProperties[l].flags & 4) && _wllProcessFlag))) + if (_dscOvlMap[shpIx] == 1 && ((_levelDecorationProperties[l].flags & 2) || ((_levelDecorationProperties[l].flags & 4) && _wllProcessFlag))) ix = -ix; int xOffs = 0; int yOffs = 0; uint8 *ovl = 0; - if (_levelShapeProperties[l].scaleFlag[shpIx] & 1) { - xOffs = _levelShapeProperties[l].shapeX[shpIx]; - yOffs = _levelShapeProperties[l].shapeY[shpIx]; + if (_levelDecorationProperties[l].scaleFlag[shpIx] & 1) { + xOffs = _levelDecorationProperties[l].shapeX[shpIx]; + yOffs = _levelDecorationProperties[l].shapeY[shpIx]; shpIx = _dscOvlMap[shpIx]; int ov = ovlIndex; if (_flags.use16ColorMode) { @@ -2100,8 +1477,8 @@ void LoLEngine::drawDecorations(int index) { else ov = 0; } - ovl = _screen->getLevelOverlay(ov); - } else if (_levelShapeProperties[l].shapeIndex[shpIx] != 0xffff) { + ovl = screen()->getLevelOverlay(ov); + } else if (_levelDecorationProperties[l].shapeIndex[shpIx] != 0xffff) { scaleW = scaleH = 0x100; int ov = 7; if (_flags.use16ColorMode) { @@ -2111,37 +1488,37 @@ void LoLEngine::drawDecorations(int index) { else ov = 0; } - ovl = _screen->getLevelOverlay(ov); + ovl = screen()->getLevelOverlay(ov); } - if (_levelShapeProperties[l].shapeIndex[shpIx] != 0xffff) { - shapeData = _levelShapes[_levelShapeProperties[l].shapeIndex[shpIx]]; + if (_levelDecorationProperties[l].shapeIndex[shpIx] != 0xffff) { + shapeData = _levelDecorationShapes[_levelDecorationProperties[l].shapeIndex[shpIx]]; if (shapeData) { if (ix < 0) { - x = _dscShapeX[s] + xOffs + ((_levelShapeProperties[l].shapeX[shpIx] * scaleW) >> 8); + x = _dscShapeX[s] + xOffs + ((_levelDecorationProperties[l].shapeX[shpIx] * scaleW) >> 8); if (ix == _dscShapeIndex[s]) { - x = _dscShapeX[s] - ((_levelShapeProperties[l].shapeX[shpIx] * scaleW) >> 8) - - _screen->getShapeScaledWidth(shapeData, scaleW) - xOffs; + x = _dscShapeX[s] - ((_levelDecorationProperties[l].shapeX[shpIx] * scaleW) >> 8) - + screen()->getShapeScaledWidth(shapeData, scaleW) - xOffs; } flags = 0x105; } else { - x = _dscShapeX[s] + xOffs + ((_levelShapeProperties[l].shapeX[shpIx] * scaleW) >> 8); + x = _dscShapeX[s] + xOffs + ((_levelDecorationProperties[l].shapeX[shpIx] * scaleW) >> 8); flags = 0x104; } - y = _dscShapeY[s] + yOffs + ((_levelShapeProperties[l].shapeY[shpIx] * scaleH) >> 8); - _screen->drawShape(_sceneDrawPage1, shapeData, x + 112, y, 13, flags, ovl, 1, scaleW, scaleH); + y = _dscShapeY[s] + yOffs + ((_levelDecorationProperties[l].shapeY[shpIx] * scaleH) >> 8); + screen()->drawShape(_sceneDrawPage1, shapeData, x + 112, y, _sceneShpDim, flags, ovl, 1, scaleW, scaleH); - if ((_levelShapeProperties[l].flags & 1) && shpIx < 4) { + if ((_levelDecorationProperties[l].flags & 1) && shpIx < 4) { //draw shadow - x += (_screen->getShapeScaledWidth(shapeData, scaleW)); + x += (screen()->getShapeScaledWidth(shapeData, scaleW)); flags ^= 1; - _screen->drawShape(_sceneDrawPage1, shapeData, x + 112, y, 13, flags, ovl, 1, scaleW, scaleH); + screen()->drawShape(_sceneDrawPage1, shapeData, x + 112, y, _sceneShpDim, flags, ovl, 1, scaleW, scaleH); } } } - l = _levelShapeProperties[l].next; + l = _levelDecorationProperties[l].next; shpIx = (_dscShapeIndex[s] < 0) ? -_dscShapeIndex[s] : _dscShapeIndex[s]; } } diff --git a/engines/kyra/screen.cpp b/engines/kyra/screen.cpp index 4eae89e0d4..5e424b4817 100644 --- a/engines/kyra/screen.cpp +++ b/engines/kyra/screen.cpp @@ -976,6 +976,58 @@ void Screen::fillRect(int x1, int y1, int x2, int y2, uint8 color, int pageNum, } } +void Screen::crossFadeRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPage, int dstPage) { + if (srcPage > 13 || dstPage > 13) + error("Screen::crossFadeRegion: attempting to use temp page as source or dest page."); + + hideMouse(); + + uint16 *wB = (uint16*)_pagePtrs[14]; + uint8 *hB = _pagePtrs[14] + 640; + + for (int i = 0; i < w; i++) + wB[i] = i; + + for (int i = 0; i < h; i++) + hB[i] = i; + + for (int i = 0; i < w; i++) + SWAP(wB[_vm->_rnd.getRandomNumberRng(0, w - 1)], wB[i]); + + for (int i = 0; i < h; i++) + SWAP(hB[_vm->_rnd.getRandomNumberRng(0, h - 1)], hB[i]); + + uint8 *s = _pagePtrs[srcPage]; + uint8 *d = _pagePtrs[dstPage]; + + for (int i = 0; i < h; i++) { + int iH = i; + uint32 end = _system->getMillis() + 1; + for (int ii = 0; ii < w; ii++) { + int sX = x1 + wB[ii]; + int sY = y1 + hB[iH]; + int dX = x2 + wB[ii]; + int dY = y2 + hB[iH]; + + if (++iH >= h) + iH = 0; + + d[dY * 320 + dX] = s[sY * 320 + sX]; + addDirtyRect(dX, dY, 1, 1); + } + + // This tries to speed things up, to get similiar speeds as in DOSBox etc. + if ((i & 5) == 5) + updateScreen(); + + uint32 cur = _system->getMillis(); + if (end > cur) + _system->delayMillis(end - cur); + } + + showMouse(); +} + void Screen::drawBox(int x1, int y1, int x2, int y2, int color) { drawClippedLine(x1, y1, x2, y1, color); drawClippedLine(x1, y1, x1, y2, color); @@ -1088,6 +1140,10 @@ bool Screen::loadFont(FontId fontId, const char *filename) { if (!fnt) { if (_isAmiga) fnt = new AMIGAFont(); +#ifdef ENABLE_EOB + else if (_vm->game() == GI_EOB1 || _vm->game() == GI_EOB2) + fnt = new OldDOSFont(); +#endif // ENABLE_EOB else fnt = new DOSFont(); @@ -2859,7 +2915,7 @@ void Screen::loadBitmap(const char *filename, int tempPage, int dstPage, Palette const char *ext = filename + strlen(filename) - 3; uint8 compType = srcData[2]; - uint32 imgSize = scumm_stricmp(ext, "CMP") ? READ_LE_UINT32(srcData + 4) : READ_LE_UINT16(srcData); + uint32 imgSize = (_vm->game() == GI_KYRA2 && !scumm_stricmp(ext, "CMP")) ? READ_LE_UINT16(srcData) : READ_LE_UINT32(srcData + 4); uint16 palSize = READ_LE_UINT16(srcData + 8); if (pal && palSize) diff --git a/engines/kyra/screen.h b/engines/kyra/screen.h index 51a9a7f744..cb7d73d1c1 100644 --- a/engines/kyra/screen.h +++ b/engines/kyra/screen.h @@ -137,6 +137,37 @@ private: uint16 *_bitmapOffsets; }; +#ifdef ENABLE_EOB +/** + * Implementation of the Font interface for old DOS fonts used + * in EOB and EOB II. + * + */ +class OldDOSFont : public Font { +public: + OldDOSFont(); + ~OldDOSFont() { unload(); } + + bool load(Common::SeekableReadStream &file); + int getHeight() const { return _height; } + int getWidth() const { return _width; } + int getCharWidth(uint16 c) const; + void setColorMap(const uint8 *src) { _colorMap = src; } + void drawChar(uint16 c, byte *dst, int pitch) const; + +private: + void unload(); + + uint8 *_data; + uint16 *_bitmapOffsets; + + int _width, _height; + const uint8 *_colorMap; + + int _numGlyphs; +}; +#endif // ENABLE_EOB + /** * Implementation of the Font interface for AMIGA fonts. */ @@ -369,6 +400,8 @@ public: void shuffleScreen(int sx, int sy, int w, int h, int srcPage, int dstPage, int ticks, bool transparent); void fillRect(int x1, int y1, int x2, int y2, uint8 color, int pageNum = -1, bool xored = false); + void crossFadeRegion(int x1, int y1, int x2, int y2, int w, int h, int srcPage, int dstPage); + void clearPage(int pageNum); uint8 getPagePixel(int pageNum, int x, int y); @@ -420,6 +453,8 @@ public: virtual void setScreenDim(int dim) = 0; virtual const ScreenDim *getScreenDim(int dim) = 0; + virtual int curDimIndex() const { return 0; } + virtual void modifyScreenDim(int dim, int x, int y, int w, int h) {} virtual int screenDimTableCount() const = 0; const ScreenDim *_curDim; @@ -430,13 +465,15 @@ public: int setNewShapeHeight(uint8 *shape, int height); int resetShapeHeight(uint8 *shape); - void drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int sd, int flags, ...); + virtual int getShapeScaledWidth( const uint8*, int) { return 0; } + + virtual void drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int sd, int flags, ...); // mouse handling void hideMouse(); void showMouse(); bool isMouseVisible() const; - void setMouseCursor(int x, int y, const byte *shape); + virtual void setMouseCursor(int x, int y, const byte *shape); // rect handling virtual int getRectSize(int w, int h) = 0; @@ -463,6 +500,8 @@ public: void blockInRegion(int x, int y, int width, int height); void blockOutRegion(int x, int y, int width, int height); + virtual uint8 *getLevelOverlay(int) { return 0; } + int _charWidth; int _charOffset; int _curPage; @@ -514,7 +553,7 @@ protected: uint8 _sjisInvisibleColor; Palette *_screenPalette; - Common::Array<Palette *> _palettes; + Common::Array<Palette*> _palettes; Palette *_internFadePalette; Font *_fonts[FID_NUM]; diff --git a/engines/kyra/screen_eob.cpp b/engines/kyra/screen_eob.cpp new file mode 100644 index 0000000000..eb5a52477b --- /dev/null +++ b/engines/kyra/screen_eob.cpp @@ -0,0 +1,965 @@ +/* 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. + * + */ + + +#if !defined(ENABLE_EOB) +#include "kyra/screen.h" +#endif + +#ifdef ENABLE_EOB + +#include "kyra/eobcommon.h" +#include "kyra/resource.h" + +#include "common/system.h" + +#include "graphics/cursorman.h" + +namespace Kyra { + +Screen_Eob::Screen_Eob(EobCoreEngine *vm, OSystem *system) : Screen(vm, system) { + _shapeFadeMode[0] = _shapeFadeMode[1] = 0; + _shapeFadeInternal = 0; + _fadeData = 0; + _fadeDataIndex = 0; + _dsX1 = _dsX2 = _dsY1 = _dsY2 = 0; + _customDimTable = 0; + _dsTempPage = 0; +} + +Screen_Eob::~Screen_Eob() { + delete[] _fadeData; + delete[] _customDimTable; + delete[] _dsTempPage; +} + +bool Screen_Eob::init() { + if (Screen::init()) { + _customDimTable = new ScreenDim*[_screenDimTableCount]; + memset(_customDimTable, 0, sizeof(ScreenDim *)* _screenDimTableCount); + + _fadeData = _vm->resource()->fileData("FADING.DAT", 0); + + if (!_fadeData) { + _fadeData = new uint8[0x700]; + memset(_fadeData, 0, 0x700); + uint8 *pal = _vm->resource()->fileData("palette1.pal", 0); // EGA: palette0.pal + for (int i = 0; i < 7; i++) + createFadeTable(pal, &_fadeData[i << 8], 18, (i + 1) * 36); + delete[] pal; + } + + _dsTempPage = new uint8[6000]; + + return true; + } + return false; +} + +void Screen_Eob::setScreenDim(int dim) { + assert(dim < _screenDimTableCount); + _curDim = _customDimTable[dim] ? (const ScreenDim *)_customDimTable[dim] : &_screenDimTable[dim]; + _curDimIndex = dim; +} + +const ScreenDim *Screen_Eob::getScreenDim(int dim) { + assert(dim < _screenDimTableCount); + return _customDimTable[dim] ? (const ScreenDim *)_customDimTable[dim] : &_screenDimTable[dim]; +} + +void Screen_Eob::modifyScreenDim(int dim, int x, int y, int w, int h) { + delete _customDimTable[dim]; + _customDimTable[dim] = new ScreenDim; + memcpy(_customDimTable[dim], &_screenDimTable[dim], sizeof(ScreenDim)); + _customDimTable[dim]->sx = x; + _customDimTable[dim]->sy = y; + _customDimTable[dim]->w = w; + _customDimTable[dim]->h = h; + if (dim == _curDimIndex) + setScreenDim(dim); +} + +void Screen_Eob::setClearScreenDim(int dim) { + setScreenDim(dim); + clearCurDim(); +} + +void Screen_Eob::clearCurDim() { + fillRect(_curDim->sx << 3, _curDim->sy, ((_curDim->sx + _curDim->w) << 3) - 1, (_curDim->sy + _curDim->h) - 1, _curDim->unkA); +} + +void Screen_Eob::setMouseCursor(int x, int y, const byte *shape) { + if (!shape) + return; + int mouseW = shape[2] << 3; + int mouseH = shape[3]; + uint8 *cursor = new uint8[mouseW * mouseH]; + fillRect(0, 0, mouseW, mouseH, _cursorColorKey, 8); + drawShape(8, shape, 0, 0, 0); + CursorMan.showMouse(false); + copyRegionToBuffer(8, 0, 0, mouseW, mouseH, cursor); + CursorMan.replaceCursor(cursor, mouseW, mouseH, x, y, _cursorColorKey); + if (isMouseVisible()) + CursorMan.showMouse(true); + delete[] cursor; + + // makes sure that the cursor is drawn + // we do not use Screen::updateScreen here + // so we can be sure that changes to page 0 + // are NOT updated on the real screen here + _system->updateScreen(); +} + +void Screen_Eob::loadFileDataToPage(Common::SeekableReadStream *s, int pageNum, uint32 size) { + s->read(_pagePtrs[pageNum], size); +} + +void Screen_Eob::printShadedText(const char *string, int x, int y, int col1, int col2) { + printText(string, x - 1, y, 12, col2); + printText(string, x, y + 1, 12, 0); + printText(string, x - 1, y + 1, 12, 0); + printText(string, x, y, col1, 0); +} + +void Screen_Eob::loadEobBitmap(const char *file, int tempPage, int destPage) { + loadEobCpsFileToPage(file, 0, tempPage, destPage, -1); + _curPage = 2; +} + +void Screen_Eob::loadEobCpsFileToPage(const char *file, const uint8 *ditheringData, int tempPage, int destPage, int copyToPage) { + char tmp[13]; + sprintf(tmp, "%s.CPS", file); + + Common::SeekableReadStream *s = _vm->resource()->createReadStream(tmp); + bool loadAlternative = false; + if (s) { + // This additional check is necessary since some localized versions of EOB II seem to contain invalid (size zero) cps files + if (s->size()) + loadBitmap(tmp, tempPage, destPage, 0); + else + loadAlternative = true; + + delete s; + } else { + loadAlternative = true; + } + + if (loadAlternative) { + tmp[0] = 'X'; + s = _vm->resource()->createReadStream(tmp); + if (!s) + error("Screen_Eob::loadEobCpsFileToPage(): CPS file loading failed."); + s->seek(768); + loadFileDataToPage(s, destPage, 64000); + delete s; + } + + if (copyToPage == -1) { + return; + } else if (copyToPage == 0) { + copyPage(destPage, 2); + copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK); + } else { + copyPage(destPage, copyToPage); + } +} + +uint8 *Screen_Eob::encodeShape(uint16 x, uint16 y, uint16 w, uint16 h, bool flag) { + uint8 *shp = 0; + uint16 shapesize = 0; + + uint8 *srcPage = getPagePtr(_curPage) + y * 320 + (x << 3); + uint8 *src = srcPage; + + if (flag) { + uint16 h1 = h; + while (h1--) { + uint8 *lineEnd = src + (w << 3); + do { + if (!*src++) { + shapesize++; + uint8 *startZeroPos = src; + while (src != lineEnd && *src == 0) + src++; + + uint16 numZero = src - startZeroPos + 1; + if (numZero >> 8) + shapesize += 2; + } + shapesize++; + } while (src != lineEnd); + + srcPage += 320; + src = srcPage; + } + + shapesize += 4; + + shp = new uint8[shapesize]; + memset (shp, 0, shapesize); + uint8 *dst = shp; + + *dst++ = 0; + *dst++ = (h & 0xff); + *dst++ = (w & 0xff); + *dst++ = (h & 0xff); + + srcPage = getPagePtr(_curPage) + y * 320 + (x << 3); + src = srcPage; + + h1 = h; + while (h1--) { + uint8 *lineEnd = src + (w << 3); + do { + uint8 val = *src++; + if (!val) { + *dst++ = val; + uint8 *startZeroPos = src; + while (src != lineEnd && *src == 0) + src++; + + uint16 numZero = src - startZeroPos + 1; + if (numZero >> 8) { + numZero -= 0xff; + *dst++ = 0xff; + *dst++ = 0; + } + val = (numZero & 0xff); + } + *dst++ = val; + } while (src != lineEnd); + + srcPage += 320; + src = srcPage; + } + + } else { + uint8 nib = 0, col = 0; + uint8 *colorMap = new uint8[0x100]; + memset (colorMap, 0xff, 0x100); + + shapesize = h * (w << 2) + 0x14; + shp = new uint8[shapesize]; + memset (shp, 0, shapesize); + uint8 *dst = shp; + + *dst++ = 1; + *dst++ = (h & 0xff); + *dst++ = (w & 0xff); + *dst++ = (h & 0xff); + memset (dst, 0xff, 0x10); + + uint8 *pal = dst; + dst += 0x10; + + srcPage = getPagePtr(_curPage) + y * 320 + (x << 3); + src = srcPage; + nib = col = 0; + + uint16 h1 = h; + while (h1--) { + uint16 w1 = w << 3; + while (w1--) { + uint8 s = *src++; + uint8 c = colorMap[s]; + if (c == 0xff) { + if (col < 0x10) { + *pal++ = s; + c = colorMap[s] = col++; + if (!col) + c = 0; + } else { + c = 0; + } + } + + if(++nib & 1) { + *dst = c << 4; + } else { + *dst++ |= c; + } + } + srcPage += 320; + src = srcPage; + } + delete [] colorMap; + } + + return shp; +} + +void Screen_Eob::drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int sd, int flags, ...) { + uint8 *dst = getPagePtr(pageNum); + const uint8 *src = shapeData; + + if (!src) + return; + + va_list args; + va_start(args, flags); + uint8 *ovl = (flags & 2) ? va_arg(args, uint8*) : 0; + va_end(args); + + if (sd != -1) { + const ScreenDim *dm = getScreenDim(sd); + setShapeFrame(dm->sx, dm->sy, dm->sx + dm->w, dm->sy + dm->h); + x += (_dsX1 << 3); + y += _dsY1; + } + + dst += (_dsX1 << 3); + int16 dX = x - (_dsX1 << 3); + int16 dY = y; + int16 dW = _dsX2 - _dsX1; + uint8 flag = *src++; + + uint16 dH = *src++; + uint16 width = (*src++) << 3; + src++; + + int rX = x; + int rY = y; + int rW = width + 8; + int rH = dH; + + uint16 w2 = width; + int d = dY - _dsY1; + + int pixelStep = (flags & 1) ? -1 : 1; + + if (flag) { + const uint8 *pal = ovl ? ovl : src; + src += 16; + + if (d < 0) { + d = -d; + if (d >= dH) + return; + src += (d * (width >> 1)); + d = dY + dH - _dsY1; + if (d >=0) { + dH = d; + dY = _dsY1; + d = _dsY2 - dY; + } + } else { + d = _dsY2 - dY; + } + + if (d < 1) + return; + + if (d < dH) + dH = d; + + int16 cnt1 = 0; + int16 cnt2 = 0; + int16 dXbit1 = dX & 1; + + if (dX < 0) { + width += dX; + d = -dX; + if ((flags & 1)) + src -= (d >> 1); + else + src += (d >> 1); + + if (d >= w2) + return; + + dX = 0; + cnt1++; + } + + d = (dW << 3) - dX; + + if (d < 1) + return; + + if (d < width) { + width = d; + cnt2++; + } + + dst += (dY * 320 + dX); + + if (pageNum == 0 || pageNum == 1) + addDirtyRect(rX, rY, rW, rH); + + int w3 = w2; + dY = 320 - width; + width >>= 1; + w2 >>= 1; + if ((flags & 1)) + src += (w2 - 1); + + int16 w1shr = width; + + if (cnt1 && (dXbit1 & 1)) { + w1shr++; + w2++; + if (!cnt2) + dY += 2; + } + + if (cnt2 && (dXbit1 & 1)) + w1shr++; + + int lineSrcStep = (w2 - w1shr); + if ((flags & 1)) + lineSrcStep = w3 - lineSrcStep; + + while (dH--) { + int16 hpos = width; + uint8 col = 0; + uint8 b = 0; + uint8 nextloop = 0; + + if (cnt1 && dXbit1) { + if (!hpos) + return; + b = *src; + src += pixelStep; + nextloop = 2; + } else { + nextloop = hpos ? 1 : 3; + } + + while (nextloop) { + switch (nextloop) { + case 1: + b = *src; + src += pixelStep; + col = pal[(flags & 1) ? (b & 0x0f) : (b >> 4)]; + if (col) + drawShapeSetPixel(dst, col); + dst++; + + case 2: + col = pal[(flags & 1) ? (b >> 4) : (b & 0x0f)]; + + if (!col) { + nextloop = 4; + break; + } + + drawShapeSetPixel(dst++, col); + nextloop = --hpos ? 1 : 3; + break; + + case 3: + if (cnt2 && dXbit1) { + col = pal[(flags & 1) ? (*src & 0x0f) : (*src >> 4)]; + src += pixelStep; + if (col) + drawShapeSetPixel(dst, col); + dst++; + } + + src += lineSrcStep; + dst += dY; + nextloop = 0; + break; + + case 4: + dst++; + nextloop = --hpos ? 1 : 3; + break; + } + } + } + } else { + uint16 marginLeft = 0; + uint16 marginRight = 0; + + if (d < 0) { + dH += d; + if (dH <= 0) + return; + d = -d; + + for (int ii = 0; ii < d; ii++) { + marginLeft = width; + int i = 0; + do { + for (i = 0; i < marginLeft; i++) + if (!*src++) + break; + + if (!*(src-1) || i < marginLeft) + marginLeft = ++marginLeft - *src++; + else + marginLeft = 0; + + } while (marginLeft); + } + dY = _dsY1; + } + + d = _dsY2 - dY; + + if (d < 1) + return; + + if (d < dH) + dH = d; + + marginLeft = 0; + + if (dX < 0) { + width += dX; + marginLeft = -dX; + + if (marginLeft >= w2) + return; + + dX = 0; + } + + marginRight = 0; + d = (dW << 3) - dX; + + if (d < 1) + return; + + if (d < width) { + width = d; + marginRight = w2 - marginLeft - width; + } + + dst += (y * 320 + dX); + uint8 * dstL = dst; + + if (pageNum == 0 || pageNum == 1) + addDirtyRect(rX, rY, rW, rH); + + while (dH--) { + int16 xpos = (int16) marginLeft; + + if (xpos) { + do { + while (*src && xpos) { + src++; + xpos--; + } + + if (!*src) { + uint8 bt = *++src; + src++; + xpos = xpos - bt; + } + } while (xpos > 0); + } + + dst -= xpos; + xpos += width; + + while (xpos > 0) { + uint8 c = *src++; + + if (c) { + drawShapeSetPixel(dst++, c); + xpos--; + } else { + dst += *src; + xpos -= *src++; + } + } + xpos += marginRight; + + if (xpos) { + do { + while (*src && xpos) { + src++; + xpos--; + } + + if (!*src) { + uint8 bt = *++src; + src++; + xpos = xpos - bt; + } + } while (xpos > 0); + } + + dstL += 320; + dst = dstL; + } + } +} + +void Screen_Eob::drawShapeSetPixel(uint8 * dst, uint8 c) { + if (_shapeFadeMode[0]) { + if (_shapeFadeMode[1]) { + c = *dst; + } else { + _shapeFadeInternal &= 7; + c = *(dst + _shapeFadeInternal++); + } + } + + if (_shapeFadeMode[1]) { + uint8 cnt = _shapeFadeMode[1]; + while (cnt--) + c = _fadeData[_fadeDataIndex + c]; + } + + *dst = c; +} + +const uint8 *Screen_Eob::scaleShape(const uint8 *shapeData, int steps) { + setShapeFadeMode(1, steps ? true : false); + + while (shapeData && steps--) + shapeData = scaleShapeStep(shapeData); + + return shapeData; +} + +const uint8 *Screen_Eob::scaleShapeStep(const uint8 *shp) { + uint8 *d = _dsTempPage; + *d++ = *shp++; + + uint16 h = (*shp++) + 1; + d[0] = d[2] = (h << 1) / 3; + d++; + + uint16 w = *shp++; + uint16 w2 = w << 2; + uint16 t = ((w << 1) % 3) ? 1 : 0; + *d++ = ((w << 1) / 3) + t; + + shp++; + d++; + + int i = 0; + while (i < 16) { + if (!shp[i]) { + i = -i; + break; + } + i++; + } + + if (i >= 0) + i = 0; + else + i = -i; + + _dsScaleTmp = (i << 4) | (i & 0x0f); + memcpy(d, shp, 16); + d += 16; + shp += 16; + + _dsDiv = w2 / 3; + _dsRem = w2 % 3; + + do { + scaleShapeProcessLine(d, shp); + if (!--h) + break; + scaleShapeProcessLine(d, shp); + if (!--h) + break; + shp += w2; + } while (--h); + + return (const uint8 *) _dsTempPage; +} + +void Screen_Eob::replaceShapePalette(uint8 *shp, const uint8 *pal) { + if (*shp != 1) + return; + shp += 4; + memcpy(shp, pal, 16); +} + +void Screen_Eob::applyShapeOverlay(uint8 *shp, int ovlIndex) { + if (*shp != 1) + return; + shp += 4; + uint8 *ovl = getFadeTable(ovlIndex); + for (int i = 0; i < 16; i++) + shp[i] = ovl[shp[i]]; +} + +void Screen_Eob::scaleShapeProcessLine(uint8 *&dst, const uint8 *&src) { + for (int i = 0; i < _dsDiv; i++) { + *dst++ = *src++; + *dst++ = READ_BE_UINT16(src) >> 4; + src += 2; + } + + if (_dsRem == 1) { + *dst++ = *src++; + *dst++ = _dsScaleTmp; + + } if (_dsRem == 2) { + *dst++ = (src[0] & 0xf0) | (src[1] >> 4); + src += 2; + *dst++ = _dsScaleTmp; + *dst++ = _dsScaleTmp; + *dst++ = _dsScaleTmp; + } +} + +void Screen_Eob::fadeTextColor(Palette *pal, int color1, int rate) { + uint8 *col = pal->getData(); + + for (bool loop = true; loop; ) { + loop = true; + uint32 end = _system->getMillis() + 16; + + loop = false; + for (int ii = 0; ii < 3; ii++) { + uint8 c = col[color1 * 3 + ii]; + if (c > rate) { + col[color1 * 3 + ii] -= rate; + loop = true; + } else if (c) { + col[color1 * 3 + ii] = 0; + loop = true; + } + } + + if (loop) { + setScreenPalette(*pal); + updateScreen(); + uint32 cur = _system->getMillis(); + if (end > cur) + _system->delayMillis(end - cur); + } + } +} + +bool Screen_Eob::delayedFadePalStep(Palette *fadePal, Palette *destPal, int rate) { + bool res = false; + + uint8 *s = fadePal->getData(); + uint8 *d = destPal->getData(); + + for (int i = 0; i < 765; i++) { + int fadeVal = *s++; + int dstCur = *d; + int diff = ABS(fadeVal - dstCur); + + if (diff == 0) { + d++; + continue; + } + + res = true; + diff = MIN(diff, rate); + + if (dstCur < fadeVal) + *d += diff; + else + *d -= diff; + d++; + } + + return res; +} + +void Screen_Eob::createFadeTable(uint8 *palData, uint8 *dst, uint8 rootColor, uint8 weight) { + if (!palData) + return; + + uint8 *src = palData + 3 * rootColor; + uint8 r = *src++; + uint8 g = *src++; + uint8 b = *src; + uint8 tr, tg, tb; + src = palData + 3; + + *dst++ = 0; + weight >>= 1; + + for (uint8 i = 1; i; i++) { + uint16 tmp = (uint16)((*src - r) * weight) << 1; + tr = *src++ - ((tmp >> 8) & 0xff); + tmp = (uint16)((*src - g) * weight) << 1; + tg = *src++ - ((tmp >> 8) & 0xff); + tmp = (uint16)((*src - b) * weight) << 1; + tb = *src++ - ((tmp >> 8) & 0xff); + + uint8 * d = palData + 3; + uint16 v = 0xffff; + uint8 col = rootColor; + + for (uint8 ii = 1; ii; ii++) { + int a = *d++ - tr; + int t = a * a; + a = *d++ - tg; + t += (a * a); + a = *d++ - tb; + t += (a * a); + + if (t <= v && (ii == rootColor || ii != i)) { + v = t; + col = ii ; + } + } + *dst++ = col; + } +} + +OldDOSFont::OldDOSFont() { + _data = 0; + _width = _height = _numGlyphs = 0; + _bitmapOffsets = 0; +} + +bool OldDOSFont::load(Common::SeekableReadStream &file) { + unload(); + + _data = new uint8[file.size()]; + assert(_data); + + file.read(_data, file.size()); + if (file.err()) + return false; + + if (file.size() - 2 != READ_LE_UINT16(_data)) + return false; + + _width = _data[0x103]; + _height = _data[0x102]; + _numGlyphs = 255; + + _bitmapOffsets = (uint16 *)(_data + 2); + + for (int i = 0; i < _numGlyphs; ++i) + _bitmapOffsets[i] = READ_LE_UINT16(&_bitmapOffsets[i]); + + return true; +} + +int OldDOSFont::getCharWidth(uint16 c) const { + if (c >= _numGlyphs) + return 0; + return _width; +} + +void OldDOSFont::drawChar(uint16 c, byte *dst, int pitch) const { + static const uint8 renderMaskTable6[] = { 0xFC, 0x00, 0x7E, 0x00, 0x3F, 0x00, 0x1F, 0x80, 0x0F, 0xC0, 0x07, 0xE0, 0x03, 0xF0, 0x01, 0xF8 }; + static const uint8 renderMaskTable8[] = { 0xFF, 0x00, 0x7F, 0x80, 0x3F, 0xC0, 0x1F, 0xE0, 0x0F, 0xF0, 0x07, 0xF8, 0x03, 0xFC, 0x01, 0xFE }; + + if (_width != 8 && _width != 6) + error("EOB font rendering not implemented for other font widths than 6 and 8."); + + if (_width == 6) { + switch (c) { + case 0x81: + case 0x9a: + c = 0x5d; + break; + case 0x84: + case 0x8e: + c = 0x5b; + break; + case 0x94: + case 0x99: + c = 0x40; + case 0xe1: + // TODO: recheck this: no conversion for 'ß' ? + break; + } + } else if (_width == 8){ + switch (c) { + case 0x81: + case 0x9a: + case 0x5d: + c = 0x1d; + break; + case 0x84: + case 0x5b: + c = 0x1e; + break; + case 0x94: + case 0x40: + c = 0x1f; + break; + case 0x8e: + c = 0x1b; + break; + case 0x99: + c = 0x1c; + break; + case 0xe1: + c = 0x19; + break; + } + } + + const uint8 *src = &_data[_bitmapOffsets[c]]; + + int w = (_width - 1) >> 3; + pitch -= _width; + + uint8 color1 = _colorMap[1]; + uint8 color2 = _colorMap[0]; + + int cH = _height; + while (cH--) { + int cW = w; + const uint8 *mtbl = _width == 8 ? renderMaskTable8 : renderMaskTable6; + + for (bool runWidthLoop = true; runWidthLoop; ) { + uint8 s = *src++; + uint8 m = *mtbl++; + + for (uint8 i = 0x80; i; i >>= 1) { + if (!(m & i)) { + runWidthLoop = false; + break; + } + + if (s & i) { + if (color1) + *dst = color1; + } else if (color2) { + *dst = color2; + } + dst++; + } + + if (cW) + cW--; + else + runWidthLoop = false; + } + + dst += pitch; + } +} + +void OldDOSFont::unload() { + delete[] _data; + _data = 0; + _width = _height = _numGlyphs = 0; + _bitmapOffsets = 0; +} + +} // End of namespace Kyra + +#endif // ENABLE_EOB diff --git a/engines/kyra/screen_eob.h b/engines/kyra/screen_eob.h new file mode 100644 index 0000000000..038ba85791 --- /dev/null +++ b/engines/kyra/screen_eob.h @@ -0,0 +1,105 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef KYRA_SCREEN_EOB_H +#define KYRA_SCREEN_EOB_H + +#ifdef ENABLE_EOB + +#include "kyra/screen.h" + +namespace Kyra { + +class EobCoreEngine; +class Screen_Eob : public Screen{ +public: + Screen_Eob(EobCoreEngine *vm, OSystem *system); + virtual ~Screen_Eob(); + + bool init(); + + void setScreenDim(int dim); + const ScreenDim *getScreenDim(int dim); + int curDimIndex() const { return _curDimIndex; } + void modifyScreenDim(int dim, int x, int y, int w, int h); + int screenDimTableCount() const { return _screenDimTableCount; } + + void setClearScreenDim(int dim); + void clearCurDim(); + + void setMouseCursor(int x, int y, const byte *shape); + + void loadFileDataToPage(Common::SeekableReadStream *s, int pageNum, uint32 size); + + void printShadedText(const char *string, int x, int y, int col1, int col2); + void loadEobCpsFileToPage(const char *file, const uint8 *ditheringData, int tempPage, int destPage, int copyToPage); + void loadEobBitmap(const char *file, int tempPage, int destPage); + + uint8 *encodeShape(uint16 x, uint16 y, uint16 w, uint16 h, bool flag = false); + void drawShape(uint8 pageNum, const uint8 *shapeData, int x, int y, int sd = -1, int flags = 0, ...); + const uint8 *scaleShape(const uint8 *shapeData, int blockDistance); + const uint8 *scaleShapeStep(const uint8 *shp); + void replaceShapePalette(uint8 *shp, const uint8 *pal); + void applyShapeOverlay(uint8 *shp, int ovlIndex); + + void setShapeFrame(int x1, int y1, int x2, int y2) { _dsX1 = x1; _dsY1 = y1; _dsX2 = x2; _dsY2 = y2; } + void setShapeFadeMode (uint8 i, bool b) { if (!i || i == 1) _shapeFadeMode[i] = b; } + + void fadeTextColor(Palette *pal, int color1, int fadeTextColor); + bool delayedFadePalStep(Palette *fadePal, Palette *destPal, int rate); + + void setTextColorMap(const uint8 *cmap) {} + int getRectSize(int w, int h) { return w * h; } + + void setFadeTableIndex(int index) { _fadeDataIndex = (CLIP(index, 0, 7) << 8); } + void createFadeTable(uint8 *palData, uint8 *dst, uint8 rootColor, uint8 weight); + uint8 *getFadeTable(int index) { return (index >= 0 && index < 5) ? &_fadeData[index << 8] : 0; } + +protected: + int16 _dsX1, _dsX2, _dsY1, _dsY2; + bool _shapeFadeMode[2]; + uint16 _shapeFadeInternal; + uint8 *_fadeData; + int _fadeDataIndex; + + uint8 *_dsTempPage; + + static const ScreenDim _screenDimTable[]; + static const int _screenDimTableCount; + + ScreenDim **_customDimTable; + int _curDimIndex; + +private: + void drawShapeSetPixel(uint8 *dst, uint8 c); + void scaleShapeProcessLine(uint8 *&dst, const uint8 *&src); + + int _dsDiv, _dsRem, _dsScaleTmp; +}; + +} // End of namespace Kyra + +#endif // ENABLE_EOB + +#endif + + diff --git a/engines/kyra/script.cpp b/engines/kyra/script.cpp index 303cbb45aa..1981341063 100644 --- a/engines/kyra/script.cpp +++ b/engines/kyra/script.cpp @@ -95,7 +95,7 @@ bool EMCInterpreter::callback(Common::IFFChunk &chunk) { return false; } -bool EMCInterpreter::load(const char *filename, EMCData *scriptData, const Common::Array<const Opcode *> *opcodes) { +bool EMCInterpreter::load(const char *filename, EMCData *scriptData, const Common::Array<const Opcode*> *opcodes) { Common::SeekableReadStream *stream = _vm->resource()->createReadStream(filename); if (!stream) { error("Couldn't open script file '%s'", filename); diff --git a/engines/kyra/script.h b/engines/kyra/script.h index 5bd75f7b80..858b2b11b8 100644 --- a/engines/kyra/script.h +++ b/engines/kyra/script.h @@ -41,7 +41,7 @@ struct EMCData { uint16 *ordr; uint16 dataSize; - const Common::Array<const Opcode *> *sysFuncs; + const Common::Array<const Opcode*> *sysFuncs; }; struct EMCState { @@ -92,7 +92,7 @@ class EMCInterpreter { public: EMCInterpreter(KyraEngine_v1 *vm); - bool load(const char *filename, EMCData *data, const Common::Array<const Opcode *> *opcodes); + bool load(const char *filename, EMCData *data, const Common::Array<const Opcode*> *opcodes); void unload(EMCData *data); void init(EMCState *scriptState, const EMCData *data); diff --git a/engines/kyra/script_eob.cpp b/engines/kyra/script_eob.cpp new file mode 100644 index 0000000000..40656c575a --- /dev/null +++ b/engines/kyra/script_eob.cpp @@ -0,0 +1,1528 @@ +/* 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. + * + */ + +#ifdef ENABLE_EOB + +#include "kyra/eobcommon.h" +#include "kyra/screen_eob.h" +#include "kyra/script_eob.h" +#include "kyra/resource.h" +#include "kyra/sound.h" + +#include "common/system.h" + +namespace Kyra { + +void EobCoreEngine::runLevelScript(int block, int flags) { + _inf->run(block, flags); +} + +void EobCoreEngine::setScriptFlag(int flag) { + _inf->setFlag(flag); +} + +bool EobCoreEngine::checkScriptFlag(int flag) { + return _inf->checkFlag(0x8000); +} + +const uint8 *EobCoreEngine::initScriptTimers(const uint8 *pos) { + _scriptTimersCount = 0; + + while (((int16)READ_LE_UINT16(pos)) != -1) { + _scriptTimers[_scriptTimersCount].func = READ_LE_UINT16(pos); + pos += 2; + uint16 ticks = (int16)READ_LE_UINT16(pos) * 18; + _scriptTimers[_scriptTimersCount].ticks = ticks; + pos += 2; + _scriptTimers[_scriptTimersCount++].next = _system->getMillis() + ticks * _tickLength; + } + + return pos; +} + +void EobCoreEngine::updateScriptTimers() { + if ((_scriptTimersMode & 1) && _stepsUntilScriptCall && _stepCounter > _stepsUntilScriptCall) { + _inf->run(0, 0x20); + _stepCounter = 0; + } + + if (_scriptTimersMode & 2) { + for (int i = 0; i < _scriptTimersCount; i++) { + if (_scriptTimers[i].next < _system->getMillis()) { + _inf->run(_scriptTimers[i].func, _flags.gameID == GI_EOB1 ? 0x20 : 0x80); + _scriptTimers[i].next = _system->getMillis() + _scriptTimers[i].ticks * _tickLength; + _sceneUpdateRequired = true; + } + } + } +} + +EobInfProcessor::EobInfProcessor(EobCoreEngine *engine, Screen_Eob *screen) : _vm(engine), _screen(screen), + _commandMin(engine->game() == GI_EOB1 ? -27 : -31) { + +#define Opcode(x) _opcodes.push_back(new InfProc(this, &EobInfProcessor::x)) +#define OpcodeAlt(x, y) if (_vm->game() == GI_EOB1) Opcode(x); else Opcode(y); + Opcode(oeob_setWallType); + Opcode(oeob_toggleWallState); + Opcode(oeob_openDoor); + Opcode(oeob_closeDoor); + Opcode(oeob_replaceMonster); + Opcode(oeob_movePartyOrObject); + Opcode(oeob_moveInventoryItemToBlock); + OpcodeAlt(oeob_printMessage_v1, oeob_printMessage_v2); + Opcode(oeob_setFlags); + Opcode(oeob_playSoundEffect); + Opcode(oeob_removeFlags); + Opcode(oeob_modifyCharacterHitPoints); + Opcode(oeob_calcAndInflictCharacterDamage); + Opcode(oeob_jump); + Opcode(oeob_end); + Opcode(oeob_popPosAndReturn); + Opcode(oeob_pushPosAndJump); + OpcodeAlt(oeob_eval_v1, oeob_eval_v2); + Opcode(oeob_deleteItem); + Opcode(oeob_loadNewLevelOrMonsters); + Opcode(oeob_increasePartyExperience); + OpcodeAlt(oeob_createItem_v1, oeob_createItem_v2); + Opcode(oeob_launchObject); + Opcode(oeob_changeDirection); + Opcode(oeob_identifyItems); + Opcode(oeob_sequence); + Opcode(oeob_delay); + Opcode(oeob_drawScene); + Opcode(oeob_dialogue); + Opcode(oeob_specialEvent); +#undef Opcode +#undef OpcodeAlt + + _scriptData = 0; + + _abortScript = 0; + _abortAfterSubroutine = 0; + _dlgResult = 0; + _script2 = 0; + + _lastScriptFunc = 0; + _lastScriptSub = 0; + + _scriptPosStack = new int8*[10]; + memset(_scriptPosStack, 0, 10 * sizeof(int8*)); + _scriptPosStackIndex = 0; + + _flagTable = new uint32[18]; + memset(_flagTable, 0, 18 * sizeof(uint32)); + + _stack = new int16[30]; + memset(_stack, 0, 30 * sizeof(int16)); + _stackIndex = 0; + + memset(_scriptPosStack, 0, sizeof(_scriptPosStack)); + _scriptPosStackIndex = 0; + + _activeCharacter = -1; +} + +EobInfProcessor::~EobInfProcessor() { + delete[] _scriptPosStack; + delete[] _flagTable; + delete[] _stack; + delete[] _scriptData; + for (Common::Array<const InfProc*>::const_iterator a = _opcodes.begin(); a != _opcodes.end(); ++a) + delete *a; + _opcodes.clear(); +} + +void EobInfProcessor::loadData(const uint8 *data, uint32 dataSize) { + delete[] _scriptData; + _scriptData = new int8[dataSize]; + memcpy(_scriptData, data, dataSize); +} + +void EobInfProcessor::run(int func, int sub) { + int o = _vm->_levelBlockProperties[func].assignedObjects; + if (!o) + return; + + uint16 f = _vm->_levelBlockProperties[func].flags; + + uint16 subFlags = ((f & 0xfff8) >> 3) | 0xe0; + if (!(sub & subFlags)) + return; + + _abortScript = 0; + _abortAfterSubroutine = 0; + _dlgResult = 0; + _activeCharacter = -1; + + _lastScriptFunc = func; + _lastScriptSub = sub; + + _vm->resetSkipFlag(true); + + int8 *pos = (int8*)(_scriptData + o); + + do { + int8 cmd = *pos++; + if (cmd <= _commandMin || cmd >= 0) + continue; + pos += (*_opcodes[-(cmd + 1)])(pos); + } while (!_abortScript && !_abortAfterSubroutine); +} + +void EobInfProcessor::loadState(Common::SeekableSubReadStreamEndian &in) { + _script2 = in.readByte(); + for (int i = 0; i < 18; i++) + _flagTable[i] = in.readUint16BE(); +} + +void EobInfProcessor::saveState(Common::OutSaveFile *out) { + out->writeByte(_script2); + for (int i = 0; i < 18; i++) + out->writeUint16BE(_flagTable[i]); +} + +const char *EobInfProcessor::getString(uint16 index) { + if (index == 0xffff) + return 0; + + int8 *res = _scriptData + READ_LE_UINT16(_scriptData); + + while (index) { + if(*res++) + continue; + index--; + } + + return (const char*)res; +} + +int EobInfProcessor::oeob_setWallType(int8 *data) { + int8 *pos = data; + + uint16 block = 0; + int8 dir = 0; + + switch (*pos++) { + case -23: + block = READ_LE_UINT16(pos); + pos += 2; + dir = *pos++; + _vm->_levelBlockProperties[block].walls[dir] = *pos++; + _vm->checkSceneUpdateNeed(block); + break; + + case -19: + _vm->_currentDirection = *pos++; + break; + + case -9: + block = READ_LE_UINT16(pos); + pos += 2; + dir = *pos++; + memset(_vm->_levelBlockProperties[block].walls, dir, 4 * sizeof(uint8)); + _vm->checkSceneUpdateNeed(block); + break; + + default: + break; + } + + return pos - data; +} + +int EobInfProcessor::oeob_toggleWallState(int8 *data) { + int8 *pos = data; + + uint16 block = 0; + int8 dir = 0; + uint8 a = 0; + uint8 b = 0; + + switch (*pos++) { + case -23: + block = READ_LE_UINT16(pos); + pos += 2; + dir = *pos++; + a = (uint8)*pos++; + b = (uint8)*pos++; + a = (_vm->_levelBlockProperties[block].walls[dir] == a) ? b : a; + _vm->_levelBlockProperties[block].walls[dir] = a; + _vm->checkSceneUpdateNeed(block); + break; + + case -22: + _vm->processDoorSwitch(READ_LE_UINT16(pos), 0); + pos += 2; + break; + + case -9: + block = READ_LE_UINT16(pos); + pos += 2; + a = (uint8)*pos++; + b = (uint8)*pos++; + a = (_vm->_levelBlockProperties[block].walls[dir] == a) ? b : a; + memset(_vm->_levelBlockProperties[block].walls, a, 4 * sizeof(uint8)); + _vm->checkSceneUpdateNeed(block); + break; + + default: + break; + } + + return pos - data; +} + +int EobInfProcessor::oeob_openDoor(int8 *data) { + int8 *pos = data; + _vm->openDoor(READ_LE_UINT16(pos)); + pos += 2; + return pos - data; +} + +int EobInfProcessor::oeob_closeDoor(int8 *data) { + int8 *pos = data; + _vm->closeDoor(READ_LE_UINT16(pos)); + pos += 2; + return pos - data; +} + +int EobInfProcessor::oeob_replaceMonster(int8 *data) { + int8 *pos = data; + _vm->replaceMonster(pos[1], READ_LE_UINT16(pos + 2), pos[4], pos[5], pos[6], pos[7], pos[8], pos[9], READ_LE_UINT16(pos + 10), READ_LE_UINT16(pos + 12)); + pos += 14; + return pos - data; +} + +int EobInfProcessor::oeob_movePartyOrObject(int8 *data) { + int8 *pos = data; + + int8 a = *pos++; + uint16 b = 0xffff; + uint16 c = 0; + uint16 d = 0; + + if (_vm->game() == GI_EOB2 && a == -31) { + b = READ_LE_UINT16(pos); + pos += 2; + } + + if (_vm->game() == GI_EOB1) { + if (a != -15) { + c = READ_LE_UINT16(pos); + pos += 2; + } + d = READ_LE_UINT16(pos); + pos += 2; + } + + if (_vm->game() == GI_EOB2 && a != -31 && a != -11) { + c = READ_LE_UINT16(pos); + pos += 2; + d = READ_LE_UINT16(pos); + pos += 2; + } + + if (a == -13) { + // move monster from block c to block d + for (int i = 0; i < 30; i++) { + if (_vm->_monsters[i].block != c) + continue; + _vm->placeMonster(&_vm->_monsters[i], d, _vm->_monsters[i].pos); + } + + } else if (a == -24) { + // move party to block d + int ba = _dlgResult; + int bb = _lastScriptFunc; + int bc = _lastScriptSub; + int bd = _abortScript; + int be = _activeCharacter; + int bf = _scriptPosStackIndex; + + _vm->moveParty(d); + + _dlgResult = ba; + _lastScriptFunc = bb; + _lastScriptSub = bc; + _abortScript = bd; + _activeCharacter = be; + if (!_abortAfterSubroutine) + _scriptPosStackIndex = bf; + _vm->_sceneDefaultUpdate = 0; + + } else if ((a == -31 && _vm->game() == GI_EOB2) || a == -11) { + // move item + int8 e = _vm->_currentLevel; + int8 f = _vm->_currentLevel; + + if (_vm->game() == GI_EOB2) { + e = (*pos++ == -21) ? _vm->_currentLevel : *pos++; + c = READ_LE_UINT16(pos); + pos += 2; + f = (*pos++ == -21) ? _vm->_currentLevel : *pos++; + d = READ_LE_UINT16(pos); + pos += 2; + } + + if (e == _vm->_currentLevel) { + int i = _vm->countQueuedItems(_vm->_levelBlockProperties[c].drawObjects, -1, (int16)b, 0, 1); + while (i) { + int p = _vm->_items[i].pos; + _vm->getQueuedItem((Item*)&_vm->_levelBlockProperties[c].drawObjects, 0, i); + if (_vm->_currentLevel == f) { + _vm->setItemPosition((Item*)&_vm->_levelBlockProperties[d].drawObjects, d, i, p); + } else { + _vm->_items[i].level = f; + _vm->_items[i].block = d; + if (p < 8) + _vm->_items[i].pos = p & 3; + } + i = _vm->countQueuedItems(_vm->_levelBlockProperties[c].drawObjects, -1, (int16)b, 0, 1); + } + + for (i = 0; i < 10; i++) { + if (_vm->_flyingObjects[i].enable != 1 || _vm->_flyingObjects[i].curBlock != c) + continue; + if (f == _vm->_currentLevel || _vm->game() == GI_EOB1) + _vm->_flyingObjects[i].curBlock = d; + else + _vm->_flyingObjects[i].enable = 0; + } + + } else { + for (int i = 0; i < 600; i++) { + if (_vm->_items[i].level != e || _vm->_items[i].block != c) + continue; + _vm->_items[i].level = f; + _vm->_items[i].block = d; + } + } + } + + _vm->_sceneUpdateRequired = true; + return pos - data; +} + +int EobInfProcessor::oeob_moveInventoryItemToBlock(int8 *data) { + int8 *pos = data; + int8 c = *pos++; + uint16 block = READ_LE_UINT16(pos); + pos += 2; + int8 p = *pos++; + + if (c == -1) + c = _vm->rollDice(1, 6, -1); + + while (!(_vm->_characters[c].flags & 1)) { + if (++c == 5) + c = 0; + } + + if (_vm->_currentControlMode && (_vm->_updateCharNum == c)) + return pos - data; + + int slot = _vm->rollDice(1, 27, 0); + int itm = 0; + int i = 0; + + for (; i < 27; i++) { + if ((!_vm->_currentControlMode && slot > 1) || slot == 16) + continue; + + itm = _vm->_characters[c].inventory[slot]; + + if (!itm) + continue; + + if (_vm->_dscItemShapeMap[_vm->_items[itm].icon] >= 15) + break; + + if (++slot == 27) + slot = 0; + } + + if (i < 27 && itm) { + _vm->_characters[c].inventory[slot] = 0; + _vm->setItemPosition((Item*)&_vm->_levelBlockProperties[block].drawObjects, block, itm, p); + } + + return pos - data; +} + +int EobInfProcessor::oeob_printMessage_v1(int8 *data) { + static const char ColorConfig[] = { 6, 33, 2, 33, 0 }; + char col[5]; + int8 *pos = data; + + strcpy(col, ColorConfig); + const char *str = (const char*) pos; + pos += (strlen(str) + 1); + + col[1] = *pos++; + col[3] = *pos++; + _vm->txt()->printMessage(col); + _vm->txt()->printMessage(str); + + col[1] = _screen->_curDim->unk8; + col[3] = _screen->_curDim->unkA; + _vm->txt()->printMessage(col); + _vm->txt()->printMessage("\r"); + + return pos - data; +} + +int EobInfProcessor::oeob_printMessage_v2(int8 *data) { + int8 *pos = data; + uint16 str = READ_LE_UINT16(pos); + pos += 2; + uint8 col = (uint8)*pos; + pos += 2; + + int c = 0; + if (_activeCharacter == -1) { + c = _vm->rollDice(1, 6, -1); + while (!_vm->testCharacter(c, 3)) + c = (c + 1) % 6; + } else { + c = _activeCharacter; + } + + _vm->txt()->printMessage(getString(str), col, _vm->_characters[c].name); + _vm->txt()->printMessage("\r"); + + return pos - data; +} + +int EobInfProcessor::oeob_setFlags(int8 *data) { + int8 *pos = data; + int8 b = 0; + + switch (*pos++) { + case -47: + _script2 = 0; + break; + + case -28: + _dlgResult = 1; + break; + + case -17: + _flagTable[_vm->_currentLevel] |= (1 << (*pos++)); + break; + + case -16: + _flagTable[17] |= (1 << (*pos++)); + break; + + case -13: + b = *pos++; + _vm->_monsters[b].flags |= (1 << (*pos++)); + _vm->_monsters[b].mode = 0; + break; + + default: + break; + } + + return pos - data; +} + +int EobInfProcessor::oeob_playSoundEffect(int8 *data) { + int8 *pos = data; + uint16 block = READ_LE_UINT16(pos + 1); + + if (block) { + _vm->snd_processEnvironmentalSoundEffect(pos[0], block); + } else { + _vm->snd_playSoundEffect(pos[0]); + } + + pos += 3; + return pos - data; +} + +int EobInfProcessor::oeob_removeFlags(int8 *data) { + int8 *pos = data; + int8 a = *pos++; + int8 b = *pos++; + + switch (a) { + case -47: + _script2 = 1; + break; + + case -28: + _dlgResult = 0; + break; + + case -17: + _flagTable[_vm->_currentLevel] &= ~(1 << b); + break; + + case -16: + _flagTable[17] &= ~(1 << b); + break; + + default: + break; + } + + return pos - data; +} + +int EobInfProcessor::oeob_modifyCharacterHitPoints(int8 *data) { + int8 *pos = data; + int8 c = *pos++; + int8 p = *pos++; + + if (c == -1) { + for (c = 0; c < 6; c++) + _vm->modifyCharacterHitpoints(c, p); + } else { + _vm->modifyCharacterHitpoints(c, p); + } + + return pos - data; +} + +int EobInfProcessor::oeob_calcAndInflictCharacterDamage(int8 *data) { + int8 *pos = data; + int charIndex = *pos++; + int times = *pos++; + int itemOrPips = *pos++; + int useStrModifierOrBase = *pos++; + + int flg = (charIndex == -1) ? 4 : 0; + int a = 5; + int damageType = 1; + + if (_vm->game() == GI_EOB2) { + flg = *pos++; + a = *pos++; + damageType = *pos++; + } else if (!itemOrPips) { + useStrModifierOrBase = times; + times = 0; + } + + if (charIndex == -1) { + for (int i = 0; i < 6; i++) + _vm->calcAndInflictCharacterDamage(i, times, itemOrPips, useStrModifierOrBase, flg, a, damageType); + } else { + _vm->calcAndInflictCharacterDamage(charIndex, times, itemOrPips, useStrModifierOrBase, flg, a, damageType); + } + return pos - data; +} + +int EobInfProcessor::oeob_jump(int8 *data) { + int8 *pos = data; + pos = _scriptData + READ_LE_UINT16(pos); + return pos - data; +} + +int EobInfProcessor::oeob_end(int8 *data) { + _abortScript = 1; + _scriptPosStackIndex = 0; + return 0; +} + +int EobInfProcessor::oeob_popPosAndReturn(int8 *data) { + int8 *pos = data; + + if (_scriptPosStackIndex) + pos = _scriptPosStack[--_scriptPosStackIndex]; + else + _abortScript = 1; + + return pos - data; +} + +int EobInfProcessor::oeob_pushPosAndJump(int8 *data) { + int8 *pos = data; + uint16 offs = READ_LE_UINT16(pos); + pos += 2; + + if (_scriptPosStackIndex < 10) { + _scriptPosStack[_scriptPosStackIndex++] = pos; + pos = _scriptData + offs; + } + + return pos - data; +} + +int EobInfProcessor::oeob_eval_v1(int8 *data) { + int8 *pos = data; + int8 cmd = *pos++; + + int a = 0; + int b = 0; + int i = 0; + EobItem *itm = &_vm->_items[_vm->_itemInHand]; + Common::String tempString1; + Common::String tempString2; + + while (cmd != -18) { + switch (cmd + 38) { + case 0: + a = 0; + for (i = 0; i < 6; i++) { + if (!(_vm->_characters[i].flags & 1)) + continue; + if (_vm->_characters[i].effectFlags & 4) + continue; + a = 1; + break; + } + _stack[_stackIndex++] = a; + break; + + case 1: + _stack[_stackIndex++] = _vm->rollDice(pos[0], pos[1], pos[2]); + pos += 3; + break; + + case 2: + cmd = *pos++; + b = 0; + for (i = 0; i < 6; i++) { + if (!(_vm->_characters[i].flags & 1)) + continue; + if (_vm->_classModifierFlags[_vm->_characters[i].cClass] & cmd) { + b = 1; + break; + } + } + _stack[_stackIndex++] = b; + break; + + case 3: + cmd = *pos++; + b = 0; + for (i = 0; i < 6; i++) { + if (!(_vm->_characters[i].flags & 1)) + continue; + if ((_vm->_characters[a].raceSex >> 1) == cmd) { + b = 1; + break; + } + } + _stack[_stackIndex++] = b; + break; + + case 6: + _stack[_stackIndex++] = _lastScriptSub; + break; + + case 13: + itm = &_vm->_items[_vm->_itemInHand]; + switch (*pos++) { + case -31: + _stack[_stackIndex++] = itm->type; + break; + + case -11: + _stack[_stackIndex++] = _vm->_itemInHand; + break; + + default: + _stack[_stackIndex++] = itm->value; + break; + } + break; + + case 15: + _stack[_stackIndex++] = _vm->_levelBlockProperties[READ_LE_UINT16(pos + 1)].walls[pos[0]]; + pos += 3; + break; + + case 19: + _stack[_stackIndex++] = _vm->_currentDirection; + break; + + case 21: + _stack[_stackIndex++] = (_flagTable[_vm->_currentLevel] & (1 << (*pos++))) ? 1 : 0; + break; + + case 22: + _stack[_stackIndex++] = (_flagTable[17] & (1 << (*pos++))) ? 1 : 0; + break; + + case 23: + _stack[_stackIndex++] = (_vm->_currentBlock == READ_LE_UINT16(pos)) ? 1 : 0; + pos += 2; + break; + + case 24: + a = (int16)READ_LE_UINT16(pos); + pos += 2; + b = READ_LE_UINT16(pos); + pos += 2; + _stack[_stackIndex++] = _vm->countQueuedItems(_vm->_levelBlockProperties[b].drawObjects, a, -1, 0, 1); + break; + + case 25: + _stack[_stackIndex++] = _vm->_levelBlockProperties[READ_LE_UINT16(pos)].flags ? 1 : 0; + pos += 2; + break; + + case 27: + b = *pos++; + i = READ_LE_UINT16(pos); + pos += 2; + _stack[_stackIndex++] = _vm->countQueuedItems(_vm->_levelBlockProperties[i].drawObjects, -1, b, 1, 1); + break; + + case 29: + _stack[_stackIndex++] = _vm->_levelBlockProperties[READ_LE_UINT16(pos)].walls[0]; + pos += 2; + break; + + case 30: + a = _stack[--_stackIndex]; + b = _stack[--_stackIndex]; + _stack[_stackIndex++] = (a || b) ? 1 : 0; + break; + + case 31: + a = _stack[--_stackIndex]; + b = _stack[--_stackIndex]; + _stack[_stackIndex++] = (a && b) ? 1 : 0; + break; + + case 32: + a = _stack[--_stackIndex]; + b = _stack[--_stackIndex]; + _stack[_stackIndex++] = (a <= b) ? 1 : 0; + break; + + case 33: + a = _stack[--_stackIndex]; + b = _stack[--_stackIndex]; + _stack[_stackIndex++] = (a < b) ? 1 : 0; + break; + + case 34: + a = _stack[--_stackIndex]; + b = _stack[--_stackIndex]; + _stack[_stackIndex++] = (a >= b) ? 1 : 0; + break; + + case 35: + a = _stack[--_stackIndex]; + b = _stack[--_stackIndex]; + _stack[_stackIndex++] = (a > b) ? 1 : 0; + break; + + case 36: + a = _stack[--_stackIndex]; + b = _stack[--_stackIndex]; + _stack[_stackIndex++] = (a != b) ? 1 : 0; + break; + + case 37: + a = _stack[--_stackIndex]; + b = _stack[--_stackIndex]; + _stack[_stackIndex++] = (a == b) ? 1 : 0; + break; + + default: + a = cmd; + if (a >= 0 && a < 128) + _stack[_stackIndex++] = a; + break; + } + cmd = *pos++; + } + + cmd = _stack[--_stackIndex]; + if (cmd) + pos += 2; + else + pos = _scriptData + READ_LE_UINT16(pos); + + return pos - data; +} + +int EobInfProcessor::oeob_eval_v2(int8 *data) { + int8 *pos = data; + int8 cmd = *pos++; + + int a = 0; + int b = 0; + int i = 0; + EobItem *itm = (_vm->_itemInHand != -1) ? &_vm->_items[_vm->_itemInHand] : 0; + Common::String tempString1; + Common::String tempString2; + + while (cmd != -18) { + switch (cmd + 50) { + case 0: + a = 0; + b = *pos++; + + for (i = 0; i < 6; i++) { + if (!_vm->testCharacter(i, 5)) + continue; + + if (_vm->_characters[i].portrait != b) { + a = 1; + _activeCharacter = i; + break; + } + } + + _stack[_stackIndex++] = a; + break; + + case 4: + _stack[_stackIndex++] = (int16)READ_LE_UINT16(pos); + pos += 2; + break; + + case 9: + switch (*pos++) { + case -36: + _stack[_stackIndex++] = _vm->_itemTypes[_vm->_items[_vm->_lastUsedItem].type].extraProperties & 0x7f; + break; + case -31: + _stack[_stackIndex++] = _vm->_items[_vm->_lastUsedItem].type; + break; + case -11: + _stack[_stackIndex++] = _vm->_lastUsedItem; + break; + case -10: + _stack[_stackIndex++] = _vm->_items[_vm->_lastUsedItem].value; + break; + default: + break; + } + break; + + case 12: + a = 0; + for (i = 0; i < 6; i++) { + if (!(_vm->_characters[i].flags & 1)) + continue; + if (_vm->_characters[i].effectFlags & 0x40) + continue; + a = 1; + break; + } + _stack[_stackIndex++] = a; + break; + + case 13: + _stack[_stackIndex++] = _vm->rollDice(pos[0], pos[1], pos[2]); + pos += 3; + break; + + case 14: + cmd = *pos++; + a = _vm->rollDice(1, 6); + b = 0; + for (i = 0; i < 6 && b == 0; i++) { + if (++a > 5) + a = 0; + if (_vm->testCharacter(a, 5)) { + if (_vm->_classModifierFlags[_vm->_characters[a].cClass] & cmd) { + _activeCharacter = a; + b = 1; + } + } + } + _stack[_stackIndex++] = b; + break; + + case 15: + cmd = *pos++; + a = _vm->rollDice(1, 6); + b = 0; + for (i = 0; i < 6; i++) { + if (++a > 5) + a = 0; + if (_vm->testCharacter(a, 5)) { + if ((_vm->_characters[a].raceSex >> 1) == cmd) { + _activeCharacter = a; + b = 1; + } + } + } + _stack[_stackIndex++] = b; + break; + + case 17: + _stack[_stackIndex++] = _vm->_activeSpell; + break; + + case 18: + _stack[_stackIndex++] = _lastScriptSub; + break; + + case 22: + _stack[_stackIndex++] = _dlgResult; + break; + + case 25: + itm = &_vm->_items[_vm->_itemInHand]; + + switch (*pos++) { + case -49: + a = *pos++; + tempString1 = _vm->_itemNames[itm->nameId]; + tempString1.toUppercase(); + tempString2 = (const char*)pos; + tempString2.toUppercase(); + pos += a; + _stack[_stackIndex++] = tempString1.contains(tempString2) ? 1 : 0; + break; + + case -48: + a = *pos++; + tempString1 = _vm->_itemNames[itm->nameUnid]; + tempString1.toUppercase(); + tempString2 = (const char*)pos; + tempString2.toUppercase(); + pos += a; + _stack[_stackIndex++] = tempString1.contains(tempString2) ? 1 : 0; + break; + + case -31: + _stack[_stackIndex++] = itm->type; + break; + + case -11: + _stack[_stackIndex++] = _vm->_itemInHand; + break; + + case -10: + _stack[_stackIndex++] = itm->value; + break; + + default: + break; + } + + break; + + case 26: + a = 0; + for (i = 0; i < 6; i++) { + if (_vm->testCharacter(i, 0x0f)) + a++; + } + _stack[_stackIndex++] = a; + break; + + case 27: + _stack[_stackIndex++] = _vm->_levelBlockProperties[READ_LE_UINT16(pos + 1)].walls[pos[0]]; + pos += 3; + break; + + case 31: + _stack[_stackIndex++] = _vm->_currentDirection; + break; + + case 33: + _stack[_stackIndex++] = (_flagTable[_vm->_currentLevel] & (1 << (*pos++))) ? 1 : 0; + break; + + case 34: + _stack[_stackIndex++] = (_flagTable[17] & (1 << (*pos++))) ? 1 : 0; + break; + + case 35: + if (*pos++ == -11) { + a = (int16)READ_LE_UINT16(pos); + pos += 2; + b = (int16)READ_LE_UINT16(pos); + pos += 2; + _stack[_stackIndex++] = _vm->countCharactersWithSpecificItems(a, b); + } else { + _stack[_stackIndex++] = (_vm->_currentBlock == READ_LE_UINT16(pos)) ? 1 : 0; + pos += 2; + } + break; + + case 36: + a = (int16)READ_LE_UINT16(pos); + pos += 2; + b = READ_LE_UINT16(pos); + pos += 2; + _stack[_stackIndex++] = _vm->countQueuedItems(_vm->_levelBlockProperties[b].drawObjects, a, -1, 0, 0); + break; + + case 37: + if (*pos++ == -1) { + _stack[_stackIndex++] = _vm->_levelBlockProperties[READ_LE_UINT16(pos)].flags & 7; + pos += 2; + } else { + do { + a += _vm->countSpecificMonsters(*pos++); + } while (*pos != -1); + pos++; + _stack[_stackIndex++] = a; + } + break; + + case 39: + a = *pos++; + b = *pos++; + i = READ_LE_UINT16(pos); + pos += 2; + _stack[_stackIndex++] = _vm->countQueuedItems(_vm->_levelBlockProperties[i].drawObjects, -1, b, 1, a); + break; + + case 41: + _stack[_stackIndex++] = _vm->_levelBlockProperties[READ_LE_UINT16(pos)].walls[0]; + pos += 2; + break; + + case 42: + a = _stack[--_stackIndex]; + b = _stack[--_stackIndex]; + _stack[_stackIndex++] = (a || b) ? 1 : 0; + break; + + case 43: + a = _stack[--_stackIndex]; + b = _stack[--_stackIndex]; + _stack[_stackIndex++] = (a && b) ? 1 : 0; + break; + + case 44: + a = _stack[--_stackIndex]; + b = _stack[--_stackIndex]; + _stack[_stackIndex++] = (a <= b) ? 1 : 0; + break; + + case 45: + a = _stack[--_stackIndex]; + b = _stack[--_stackIndex]; + _stack[_stackIndex++] = (a < b) ? 1 : 0; + break; + + case 46: + a = _stack[--_stackIndex]; + b = _stack[--_stackIndex]; + _stack[_stackIndex++] = (a >= b) ? 1 : 0; + break; + + case 47: + a = _stack[--_stackIndex]; + b = _stack[--_stackIndex]; + _stack[_stackIndex++] = (a > b) ? 1 : 0; + break; + + case 48: + a = _stack[--_stackIndex]; + b = _stack[--_stackIndex]; + _stack[_stackIndex++] = (a != b) ? 1 : 0; + break; + + case 49: + a = _stack[--_stackIndex]; + b = _stack[--_stackIndex]; + _stack[_stackIndex++] = (a == b) ? 1 : 0; + break; + + default: + break; + } + cmd = *pos++; + } + + cmd = _stack[--_stackIndex]; + if (cmd) + pos += 2; + else + pos = _scriptData + READ_LE_UINT16(pos); + + return pos - data; +} + +int EobInfProcessor::oeob_deleteItem(int8 *data) { + int8 *pos = data; + int8 c = *pos++; + if (c == -1) { + _vm->deleteInventoryItem(0, -1); + } else { + _vm->deleteBlockItem(READ_LE_UINT16(pos), (c == -2) ? -1 : c); + pos += 2; + } + + return pos - data; +} + +int EobInfProcessor::oeob_loadNewLevelOrMonsters(int8 *data) { + int8 *pos = data; + _vm->gui_updateControls(); + + int8 cmd = *pos++; + int8 index = *pos++; + int res = 0; + + if (cmd == -27 || _vm->game() == GI_EOB1) { + cmd = _vm->game() == GI_EOB2 ? *pos++ : 0; + _vm->_currentBlock = READ_LE_UINT16(pos); + pos += 2; + uint8 dir = (uint8)*pos++; + + if (dir != 0xff) + _vm->_currentDirection = dir; + + for (int i = 0; i < 30; i++) + _vm->_monsters[i].curAttackFrame = 0; + + for (int i = 0; i < 10; i++) { + EobFlyingObject *fo = &_vm->_flyingObjects[i]; + if (fo->enable == 1) { + _vm->_items[fo->item].pos &= 3; + run(_vm->_items[fo->item].block, 4); + } + fo->enable = 0; + } + + _vm->completeDoorOperations(); + + _vm->generateTempData(); + _vm->txt()->removePageBreakFlag(); + _screen->setScreenDim(7); + + _vm->loadLevel(index, cmd); + + if (_vm->_dialogueField) + _vm->restoreAfterDialogueSequence(); + + _vm->moveParty(_vm->_currentBlock); + + _abortScript = 1; + _abortAfterSubroutine = 1; + _vm->_sceneUpdateRequired = true; + + _vm->gui_drawAllCharPortraitsWithStats(); + _scriptPosStackIndex = 0; + + } else { + cmd = *pos++; + _vm->releaseMonsterShapes(cmd * 18, 18); + _vm->loadMonsterShapes((const char*)pos, cmd * 18, true, index * 18); + pos += 13; + _vm->gui_restorePlayField(); + res = pos - data; + } + + return res; +} + +int EobInfProcessor::oeob_increasePartyExperience(int8 *data) { + int8 *pos = data; + if (*pos++ == -30) { + _vm->increasePartyExperience((int16)READ_LE_UINT16(pos)); + pos += 2; + } + return pos - data; +} + +int EobInfProcessor::oeob_createItem_v1(int8 *data) { + int8 *pos = data; + uint16 itm = _vm->duplicateItem(READ_LE_UINT16(pos)); + pos += 2; + uint16 block = READ_LE_UINT16(pos); + pos += 2; + uint8 itmPos = *pos++; + + if (itm) { + if (block == 0xffff && !_vm->_itemInHand) + _vm->setHandItem(itm); + else if (block != 0xffff ) + _vm->setItemPosition((Item*)&_vm->_levelBlockProperties[block & 0x3ff].drawObjects, block, itm, itmPos); + } + + return pos - data; +} + +int EobInfProcessor::oeob_createItem_v2(int8 *data) { + static const uint8 _itemPos[] = { 0, 1, 2, 3, 1, 3, 0, 2, 3, 2, 1, 0, 2, 0, 3, 1 }; + int8 *pos = data; + + uint16 itm = _vm->duplicateItem(READ_LE_UINT16(pos)); + pos += 2; + uint16 block = READ_LE_UINT16(pos); + pos += 2; + uint8 itmPos = *pos++; + uint8 flg = *pos++; + + if (flg & 1) + _vm->_items[itm].value = *pos++; + + if (flg & 2) + _vm->_items[itm].flags = *pos++; + + if (flg & 4) + _vm->_items[itm].icon = *pos++; + + if (!itm) + return pos - data; + + if (block == 0xffff) { + if (!_vm->_itemInHand) + _vm->setHandItem(itm); + else + _vm->setItemPosition((Item*)&_vm->_levelBlockProperties[_vm->_currentBlock & 0x3ff].drawObjects, _vm->_currentBlock, itm, _itemPos[_vm->rollDice(1, 2, -1)]); + } else if (block == 0xfffe) { + _vm->setItemPosition((Item*)&_vm->_levelBlockProperties[_vm->_currentBlock & 0x3ff].drawObjects, _vm->_currentBlock, itm, _itemPos[(_vm->_currentDirection << 2) + _vm->rollDice(1, 2, -1)]); + } else { + _vm->setItemPosition((Item*)&_vm->_levelBlockProperties[block & 0x3ff].drawObjects, block, itm, itmPos); + } + + return pos - data; +} + +int EobInfProcessor::oeob_launchObject(int8 *data) { + static const uint8 startPos[] = { 2, 3, 0, 2, 1, 0, 3, 1 }; + + int8 *pos = data; + bool m = (*pos++ == -33); + int i = READ_LE_UINT16(pos); + pos += 2; + uint16 block = READ_LE_UINT16(pos); + pos += 2; + int dir = *pos++; + int dirOffs = *pos++; + + if (m) { + uint8 openBookType = _vm->_openBookType; + _vm->_openBookType = 0; + _vm->launchMagicObject(-1, i, block, startPos[dir * 2 + dirOffs], dir); + _vm->_openBookType = openBookType; + } else { + Item itm = _vm->duplicateItem(i); + if (itm) { + if (!_vm->launchObject(-1, itm, block, startPos[dir * 2 + dirOffs], dir, _vm->_items[itm].type)) + _vm->_items[itm].block = -1; + } + } + + return pos - data; +} + +int EobInfProcessor::oeob_changeDirection(int8 *data) { + int8 *pos = data; + + int8 cmd = *pos++; + int8 dir = *pos++; + + if (cmd == -15) { + _vm->_currentDirection = (_vm->_currentDirection + dir) & 3; + //_vm->_keybControlUnk = -1; + _vm->_sceneUpdateRequired = true; + + } else if (cmd == -11) { + for (int i = 0; i < 10; i++) { + if (_vm->_flyingObjects[i].enable) + _vm->_flyingObjects[i].direction = (_vm->_flyingObjects[i].direction + dir) & 3; + } + } + + return pos - data; +} + +int EobInfProcessor::oeob_identifyItems(int8 *data) { + int8 *pos = data; + uint16 block = READ_LE_UINT16(pos); + + if (block == _vm->_currentBlock) { + for (int i = 0; i < 6; i++) { + if (!(_vm->_characters[i].flags & 1)) + continue; + + for (int ii = 0; ii < 27; ii++) { + int inv = _vm->_characters[i].inventory[ii]; + if (inv) + _vm->_items[inv].flags |= 0x40; + } + + _vm->identifyQueuedItems(_vm->_characters[i].inventory[16]); + } + } + + _vm->identifyQueuedItems(_vm->_levelBlockProperties[block].drawObjects); + return pos - data; +} + +int EobInfProcessor::oeob_sequence(int8 *data) { + int8 *pos = data; + _vm->_dlgUnk1 = -1; + _vm->txt()->setWaitButtonMode(0); + _vm->gui_updateControls(); + _vm->drawScene(1); + + int cmd = *pos++; + + if (_vm->game() == GI_EOB1) { + if (cmd == 10) + cmd = -1; + else if (cmd == 9) + cmd = -3; + else if (cmd == 8) + cmd = -2; + } + + switch (cmd) { + case -3: + // eob1 outro + _vm->_runFlag = false; + _vm->_playFinale = true; + _abortScript = 1; + return 0; + + case -2: + // portal sequence + pos=pos; + break; + + case -1: + // copy protection + pos=pos; + break; + + default: + _vm->npcSequence(cmd); + break; + } + _vm->screen()->setScreenDim(7); + return pos - data; +} + +int EobInfProcessor::oeob_delay(int8 *data) { + int8 *pos = data; + _vm->delay(READ_LE_UINT16(pos) * _vm->tickLength()); + pos += 2; + return pos - data; +} + +int EobInfProcessor::oeob_drawScene(int8 *data) { + _vm->drawScene(1); + return 0; +} + +int EobInfProcessor::oeob_dialogue(int8 *data) { + int8 *pos = data; + + switch (*pos++) { + case -45: + _vm->drawSequenceBitmap((const char*)pos, pos[13], READ_LE_UINT16(pos + 14), READ_LE_UINT16(pos + 16), READ_LE_UINT16(pos + 18)); + pos += 20; + break; + + case -44: + _vm->restoreAfterDialogueSequence(); + break; + + case -43: + _vm->initDialogueSequence(); + break; + + case -42: + _vm->gui_drawDialogueBox(); + break; + + case -40: + _dlgResult = _vm->runDialogue(READ_LE_UINT16(pos), READ_LE_UINT16(pos + 6) == 0xffff ? 0 : 1, getString(READ_LE_UINT16(pos + 2)), getString(READ_LE_UINT16(pos + 4)), getString(READ_LE_UINT16(pos + 6)), 0); + pos += 8; + break; + + case -8: + _vm->txt()->printDialogueText(READ_LE_UINT16(pos), getString(READ_LE_UINT16(pos + 2))); + pos += 4; + break; + + default: + break; + } + + return pos - data; +} + +int EobInfProcessor::oeob_specialEvent(int8 *data) { + int8 *pos = data; + uint16 cmd = READ_LE_UINT16(pos); + pos += 2; + + uint32 endTime = 0; + int i = 0; + + switch (cmd) { + case 0: + _vm->drawScene(1); + _screen->_curPage = 2; + _screen->copyRegion(72, 0, 0, 0, 32, 120, 2, 12, Screen::CR_NO_P_CHECK); + + for (; i < 4; i++) { + endTime = _vm->_system->getMillis() + _vm->_tickLength; + _vm->drawLightningColumn(); + _screen->copyRegion(72, 0, 72, 0, 32, 120, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + _screen->copyRegion(0, 0, 72, 0, 32, 120, 12, 2, Screen::CR_NO_P_CHECK); + _vm->delayUntil(endTime); + } + + _screen->_curPage = 0; + _vm->_sceneUpdateRequired = true; + break; + + case 1: + _dlgResult = _vm->charSelectDialogue(); + break; + + case 2: + _vm->characterLevelGain(_dlgResult); + break; + + case 3: + _dlgResult = _vm->resurrectionSelectDialogue(); + break; + + case 4: + if (_vm->prepareForNewPartyMember(33, 5)) + _vm->initNpc(4); + break; + + case 5: + _vm->deletePartyItem(46, 5); + _vm->deletePartyItem(46, 6); + break; + + case 6: + _vm->loadVcnData(0, 0); + break; + + default: + break; + } + + return pos - data; +} + +} // End of namespace Kyra + +#endif // ENABLE_EOB diff --git a/engines/kyra/script_eob.h b/engines/kyra/script_eob.h new file mode 100644 index 0000000000..2b02807531 --- /dev/null +++ b/engines/kyra/script_eob.h @@ -0,0 +1,121 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#ifdef ENABLE_EOB + +#ifndef KYRA_SCRIPT_EOB_H +#define KYRA_SCRIPT_EOB_H + +#include "common/func.h" +#include "common/substream.h" +#include "common/savefile.h" + +namespace Kyra { + +class LolEobBaseEngine; + +class EobInfProcessor { +public: + EobInfProcessor(EobCoreEngine *engine, Screen_Eob *_screen); + ~EobInfProcessor(); + + void loadData(const uint8 *data, uint32 dataSize); + void run(int func, int sub); + + void setFlag(int flag) { _flagTable[17] |= flag; } + bool checkFlag(int flag) { return (_flagTable[17] & flag) ? true : false; } + + void loadState(Common::SeekableSubReadStreamEndian &in); + void saveState(Common::OutSaveFile *out); + +private: + const char *getString(uint16 index); + + int oeob_setWallType(int8 *data); + int oeob_toggleWallState(int8 *data); + int oeob_openDoor(int8 *data); + int oeob_closeDoor(int8 *data); + int oeob_replaceMonster(int8 *data); + int oeob_movePartyOrObject(int8 *data); + int oeob_moveInventoryItemToBlock(int8 *data); + int oeob_printMessage_v1(int8 *data); + int oeob_printMessage_v2(int8 *data); + int oeob_setFlags(int8 *data); + int oeob_playSoundEffect(int8 *data); + int oeob_removeFlags(int8 *data); + int oeob_modifyCharacterHitPoints(int8 *data); + int oeob_calcAndInflictCharacterDamage(int8 *data); + int oeob_jump(int8 *data); + int oeob_end(int8 *data); + int oeob_popPosAndReturn(int8 *data); + int oeob_pushPosAndJump(int8 *data); + int oeob_eval_v1(int8 *data); + int oeob_eval_v2(int8 *data); + int oeob_deleteItem(int8 *data); + int oeob_loadNewLevelOrMonsters(int8 *data); + int oeob_increasePartyExperience(int8 *data); + int oeob_createItem_v1(int8 *data); + int oeob_createItem_v2(int8 *data); + int oeob_launchObject(int8 *data); + int oeob_changeDirection(int8 *data); + int oeob_identifyItems(int8 *data); + int oeob_sequence(int8 *data); + int oeob_delay(int8 *data); + int oeob_drawScene(int8 *data); + int oeob_dialogue(int8 *data); + int oeob_specialEvent(int8 *data); + + EobCoreEngine *_vm; + Screen_Eob *_screen; + + typedef Common::Functor1Mem<int8*, int, EobInfProcessor> InfProc; + Common::Array<const InfProc*> _opcodes; + + int8 *_scriptData; + + uint8 _abortScript; + uint16 _abortAfterSubroutine; + int _dlgResult; + uint8 _script2; + + uint16 _lastScriptFunc; + uint16 _lastScriptSub; + + int8 **_scriptPosStack; + int _scriptPosStackIndex; + + uint32 *_flagTable; + + int16 *_stack; + int _stackIndex; + + int8 _activeCharacter; + + const int _commandMin; +}; + +} // End of namespace Kyra + +#endif + +#endif // ENABLE_EOB diff --git a/engines/kyra/script_hof.cpp b/engines/kyra/script_hof.cpp index b80b8105a1..73b6915c89 100644 --- a/engines/kyra/script_hof.cpp +++ b/engines/kyra/script_hof.cpp @@ -1480,7 +1480,7 @@ typedef Common::Functor2Mem<const TIM *, const uint16 *, int, KyraEngine_HoF> TI #define OpcodeTimUnImpl() _timOpcodes.push_back(new TIMOpcodeV2(this, 0)) void KyraEngine_HoF::setupOpcodeTable() { - Common::Array<const Opcode *> *table = 0; + Common::Array<const Opcode*> *table = 0; _opcodes.reserve(176); SetOpcodeTable(_opcodes); diff --git a/engines/kyra/script_lok.cpp b/engines/kyra/script_lok.cpp index 4d40971124..ae6fb4b5e5 100644 --- a/engines/kyra/script_lok.cpp +++ b/engines/kyra/script_lok.cpp @@ -1754,7 +1754,7 @@ typedef Common::Functor1Mem<EMCState *, int, KyraEngine_LoK> OpcodeV1; #define SetOpcodeTable(x) table = &x; #define Opcode(x) table->push_back(new OpcodeV1(this, &KyraEngine_LoK::x)) void KyraEngine_LoK::setupOpcodeTable() { - Common::Array<const Opcode *> *table = 0; + Common::Array<const Opcode*> *table = 0; _opcodes.reserve(157); SetOpcodeTable(_opcodes); diff --git a/engines/kyra/script_lol.cpp b/engines/kyra/script_lol.cpp index d8ba32ce09..ead7e17f4c 100644 --- a/engines/kyra/script_lol.cpp +++ b/engines/kyra/script_lol.cpp @@ -93,25 +93,6 @@ void LoLEngine::runLevelScriptCustom(int block, int flags, int charNum, int item checkSceneUpdateNeed(block); } -bool LoLEngine::checkSceneUpdateNeed(int func) { - if (_sceneUpdateRequired) - return true; - - for (int i = 0; i < 15; i++) { - if (_visibleBlockIndex[i] == func) { - _sceneUpdateRequired = true; - return true; - } - } - - if (_currentBlock == func){ - _sceneUpdateRequired = true; - return true; - } - - return false; -} - int LoLEngine::olol_setWallType(EMCState *script) { debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_setWallType(%p) (%d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2)); if (stackPos(2) != -1) { @@ -796,9 +777,9 @@ int LoLEngine::olol_updateBlockAnimations(EMCState *script) { return 0; } -int LoLEngine::olol_mapShapeToBlock(EMCState *script) { - debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_mapShapeToBlock(%p) (%d)", (const void *)script, stackPos(0)); - return assignLevelShapes(stackPos(0)); +int LoLEngine::olol_assignLevelDecorationShape(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_assignLevelDecorationShape(%p) (%d)", (const void *)script, stackPos(0)); + return assignLevelDecorationShapes(stackPos(0)); } int LoLEngine::olol_resetBlockShapeAssignment(EMCState *script) { @@ -830,11 +811,11 @@ int LoLEngine::olol_initMonster(EMCState *script) { return -1; for (uint8 i = 0; i < 30; i++) { - MonsterInPlay *l = &_monsters[i]; + LolMonsterInPlay *l = &_monsters[i]; if (l->hitPoints || l->mode == 13) continue; - memset(l, 0, sizeof(MonsterInPlay)); + memset(l, 0, sizeof(LolMonsterInPlay)); l->id = i; l->x = x; l->y = y; @@ -945,7 +926,7 @@ int LoLEngine::olol_loadMonsterProperties(EMCState *script) { stackPos(28), stackPos(29), stackPos(30), stackPos(31), stackPos(32), stackPos(33), stackPos(34), stackPos(35), stackPos(36), stackPos(37), stackPos(38), stackPos(39), stackPos(40), stackPos(41)); - MonsterProperty *l = &_monsterProperties[stackPos(0)]; + LolMonsterProperty *l = &_monsterProperties[stackPos(0)]; l->shapeIndex = stackPos(1) & 0xff; int shpWidthMax = 0; @@ -1017,7 +998,7 @@ int LoLEngine::olol_inflictDamage(EMCState *script) { int LoLEngine::olol_moveMonster(EMCState *script) { debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_moveMonster(%p) (%d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); - MonsterInPlay *m = &_monsters[stackPos(0)]; + LolMonsterInPlay *m = &_monsters[stackPos(0)]; if (m->mode == 1 || m->mode == 2) { calcCoordinates(m->destX, m->destY, stackPos(1), stackPos(2), stackPos(3)); @@ -1029,10 +1010,9 @@ int LoLEngine::olol_moveMonster(EMCState *script) { return 1; } -int LoLEngine::olol_dialogueBox(EMCState *script) { - debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_dialogueBox(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); - - _tim->drawDialogueBox(stackPos(0), getLangString(stackPos(1)), getLangString(stackPos(2)), getLangString(stackPos(3))); +int LoLEngine::olol_setupDialogueButtons(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_setupDialogueButtons(%p) (%d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3)); + setupDialogueButtons(stackPos(0), getLangString(stackPos(1)), getLangString(stackPos(2)), getLangString(stackPos(3))); return 1; } @@ -1194,7 +1174,7 @@ int LoLEngine::olol_playSoundEffect(EMCState *script) { int LoLEngine::olol_processDialogue(EMCState *script) { debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_processDialogue(%p)", (const void *)script); - return _tim->processDialogue(); + return processDialogue(); } int LoLEngine::olol_stopTimScript(EMCState *script) { @@ -1213,7 +1193,7 @@ int LoLEngine::olol_changeMonsterStat(EMCState *script) { if (stackPos(0) == -1) return 1; - MonsterInPlay *m = &_monsters[stackPos(0) & 0x7fff]; + LolMonsterInPlay *m = &_monsters[stackPos(0) & 0x7fff]; int16 d = stackPos(2); uint16 x = 0; @@ -1254,7 +1234,7 @@ int LoLEngine::olol_getMonsterStat(EMCState *script) { if (stackPos(0) == -1) return 0; - MonsterInPlay *m = &_monsters[stackPos(0) & 0x7fff]; + LolMonsterInPlay *m = &_monsters[stackPos(0) & 0x7fff]; int d = stackPos(1); switch (d) { @@ -1567,7 +1547,7 @@ int LoLEngine::olol_moveBlockObjects(EMCState *script) { l &= 0x7fff; - MonsterInPlay *m = &_monsters[l]; + LolMonsterInPlay *m = &_monsters[l]; setMonsterMode(m, 14); checkSceneUpdateNeed(m->block); @@ -1582,8 +1562,8 @@ int LoLEngine::olol_moveBlockObjects(EMCState *script) { placeMoveLevelItem(l, level, destBlock, _itemsInPlay[l].x & 0xff, _itemsInPlay[l].y & 0xff, _itemsInPlay[l].flyingHeight); res = 1; - if (!runScript || level != _currentLevel) - continue; + if (!runScript || level != _currentLevel) + continue; runLevelScriptCustom(destBlock, 0x80, -1, l, 0, 0); } @@ -1638,7 +1618,7 @@ int LoLEngine::olol_dummy1(EMCState *script) { int LoLEngine::olol_suspendMonster(EMCState *script) { debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_suspendMonster(%p) (%d)", (const void *)script, stackPos(0)); - MonsterInPlay *m = &_monsters[stackPos(0) & 0x7fff]; + LolMonsterInPlay *m = &_monsters[stackPos(0) & 0x7fff]; setMonsterMode(m, 14); checkSceneUpdateNeed(m->block); placeMonster(m, 0, 0); @@ -1889,9 +1869,9 @@ int LoLEngine::olol_checkBlockForMonster(EMCState *script) { return -1; } -int LoLEngine::olol_transformRegion(EMCState *script) { - debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_transformRegion(%p) (%d, %d, %d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7)); - transformRegion(stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7)); +int LoLEngine::olol_crossFadeRegion(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_crossFadeRegion(%p) (%d, %d, %d, %d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7)); + _screen->crossFadeRegion(stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4), stackPos(5), stackPos(6), stackPos(7)); return 1; } @@ -1984,7 +1964,7 @@ int LoLEngine::olol_getAnimationLastPart(EMCState *script) { int LoLEngine::olol_assignSpecialGuiShape(EMCState *script) { debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_assignSpecialGuiShape(%p) (%d, %d, %d, %d, %d)", (const void *)script, stackPos(0), stackPos(1), stackPos(2), stackPos(3), stackPos(4)); if (stackPos(0)) { - _specialGuiShape = _levelShapes[_levelShapeProperties[_wllShapeMap[stackPos(0)]].shapeIndex[stackPos(1)]]; + _specialGuiShape = _levelDecorationShapes[_levelDecorationProperties[_wllShapeMap[stackPos(0)]].shapeIndex[stackPos(1)]]; _specialGuiShapeX = stackPos(2); _specialGuiShapeY = stackPos(3); _specialGuiShapeMirrorFlag = stackPos(4); @@ -2291,10 +2271,10 @@ int LoLEngine::olol_calcNewBlockPosition(EMCState *script) { return calcNewBlockPosition(stackPos(0), stackPos(1)); } -int LoLEngine::olol_fadeScene(EMCState *script) { - debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_fadeScene(%p)", (const void *)script); +int LoLEngine::olol_crossFadeScene(EMCState *script) { + debugC(3, kDebugLevelScriptFuncs, "LoLEngine::olol_crossFadeScene(%p)", (const void *)script); gui_drawScene(2); - transformRegion(112, 0, 112, 0, 176, 120, 2, 0); + _screen->crossFadeRegion(112, 0, 112, 0, 176, 120, 2, 0); updateDrawPage2(); return 1; } @@ -2700,7 +2680,7 @@ typedef Common::Functor2Mem<const TIM *, const uint16 *, int, LoLEngine> TIMOpco #define OpcodeTimUnImpl() timTable->push_back(new TIMOpcodeLoL(this, 0)) void LoLEngine::setupOpcodeTable() { - Common::Array<const Opcode *> *table = 0; + Common::Array<const Opcode*> *table = 0; _opcodes.reserve(192); SetOpcodeTable(_opcodes); @@ -2784,7 +2764,7 @@ void LoLEngine::setupOpcodeTable() { // 0x34 Opcode(olol_updateBlockAnimations); - Opcode(olol_mapShapeToBlock); + Opcode(olol_assignLevelDecorationShape); Opcode(olol_resetBlockShapeAssignment); Opcode(olol_copyRegion); @@ -2808,7 +2788,7 @@ void LoLEngine::setupOpcodeTable() { // 0x44 Opcode(olol_moveMonster); - Opcode(olol_dialogueBox); + Opcode(olol_setupDialogueButtons); Opcode(olol_giveTakeMoney); Opcode(olol_checkMoney); @@ -2933,7 +2913,7 @@ void LoLEngine::setupOpcodeTable() { Opcode(olol_checkBlockForMonster); // 0x98 - Opcode(olol_transformRegion); + Opcode(olol_crossFadeRegion); Opcode(olol_calcCoordinatesAddDirectionOffset); Opcode(olol_resetPortraitsAndDisableSysTimer); Opcode(olol_enableSysTimer); @@ -2981,7 +2961,7 @@ void LoLEngine::setupOpcodeTable() { Opcode(olol_calcNewBlockPosition); // 0xB8 - Opcode(olol_fadeScene); + Opcode(olol_crossFadeScene); Opcode(olol_updateDrawPage2); Opcode(olol_setMouseCursor); Opcode(olol_characterSays); @@ -2992,7 +2972,7 @@ void LoLEngine::setupOpcodeTable() { Opcode(olol_getLanguage); Opcode(olol_dummy0); - Common::Array<const TIMOpcode *> *timTable = 0; + Common::Array<const TIMOpcode*> *timTable = 0; _timIntroOpcodes.reserve(8); SetTimOpcodeTable(_timIntroOpcodes); diff --git a/engines/kyra/script_mr.cpp b/engines/kyra/script_mr.cpp index afe11aba02..56b2a48b89 100644 --- a/engines/kyra/script_mr.cpp +++ b/engines/kyra/script_mr.cpp @@ -1129,7 +1129,7 @@ typedef Common::Functor1Mem<EMCState *, int, KyraEngine_MR> OpcodeV3; #define Opcode(x) table->push_back(new OpcodeV3(this, &KyraEngine_MR::x)) #define OpcodeUnImpl() table->push_back(new OpcodeV3(this, 0)) void KyraEngine_MR::setupOpcodeTable() { - Common::Array<const Opcode *> *table = 0; + Common::Array<const Opcode*> *table = 0; _opcodes.reserve(176); SetOpcodeTable(_opcodes); diff --git a/engines/kyra/script_tim.cpp b/engines/kyra/script_tim.cpp index 83d03d1f63..75977fc87c 100644 --- a/engines/kyra/script_tim.cpp +++ b/engines/kyra/script_tim.cpp @@ -135,7 +135,7 @@ bool TIMInterpreter::callback(Common::IFFChunk &chunk) { return false; } -TIM *TIMInterpreter::load(const char *filename, const Common::Array<const TIMOpcode *> *opcodes) { +TIM *TIMInterpreter::load(const char *filename, const Common::Array<const TIMOpcode*> *opcodes) { if (!_vm->resource()->exists(filename)) return 0; @@ -833,18 +833,6 @@ int TIMInterpreter::cmd_stopFuncNow(const uint16 *param) { return 1; } -int TIMInterpreter::cmd_stopAllFuncs(const uint16 *param) { - while (_currentTim->dlgFunc == -1 && _currentTim->clickedButton == 0 && !_vm->shouldQuit()) { - update(); - _currentTim->clickedButton = processDialogue(); - } - - for (int i = 0; i < TIM::kCountFuncs; ++i) - _currentTim->func[i].ip = 0; - - return -1; -} - // TODO: Consider moving to another file #ifdef ENABLE_LOL @@ -908,9 +896,6 @@ TIMInterpreter_LoL::TIMInterpreter_LoL(LoLEngine *engine, Screen_v2 *screen_v2, _animator = new TimAnimator(engine, screen_v2, system, true); _drawPage2 = 0; - - memset(_dialogueButtonString, 0, 3 * sizeof(const char *)); - _dialogueButtonPosX = _dialogueButtonPosY = _dialogueNumButtons = _dialogueButtonXoffs = _dialogueHighlightedButton = 0; } int TIMInterpreter_LoL::initAnimStruct(int index, const char *filename, int x, int y, int frameDelay, int, uint16 wsaFlags) { @@ -978,157 +963,12 @@ void TIMInterpreter_LoL::advanceToOpcode(int opcode) { f->nextTime = _system->getMillis(); } -void TIMInterpreter_LoL::drawDialogueBox(int numStr, const char *s1, const char *s2, const char *s3) { - _screen->setScreenDim(5); - - if (numStr == 1 && _vm->speechEnabled()) { - _dialogueNumButtons = 0; - _dialogueButtonString[0] = _dialogueButtonString[1] = _dialogueButtonString[2] = 0; - } else { - _dialogueNumButtons = numStr; - _dialogueButtonString[0] = s1; - _dialogueButtonString[1] = s2; - _dialogueButtonString[2] = s3; - _dialogueHighlightedButton = 0; - - const ScreenDim *d = _screen->getScreenDim(5); - _dialogueButtonPosY = d->sy + d->h - 9; - - if (numStr == 1) { - _dialogueButtonXoffs = 0; - _dialogueButtonPosX = d->sx + d->w - 77; - } else { - _dialogueButtonXoffs = d->w / numStr; - _dialogueButtonPosX = d->sx + (_dialogueButtonXoffs >> 1) - 37; - } - - drawDialogueButtons(); - } - - if (!_vm->shouldQuit()) - _vm->removeInputTop(); -} - -void TIMInterpreter_LoL::drawDialogueButtons() { - int cp = _screen->setCurPage(0); - Screen::FontId of = _screen->setFont(_vm->gameFlags().use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT); - - int x = _dialogueButtonPosX; - - for (int i = 0; i < _dialogueNumButtons; i++) { - if (_vm->gameFlags().use16ColorMode) { - _vm->gui_drawBox(x, (_dialogueButtonPosY & ~7) - 1, 74, 10, 0xee, 0xcc, -1); - _screen->printText(_dialogueButtonString[i], (x + 37 - (_screen->getTextWidth(_dialogueButtonString[i])) / 2) & ~3, - (_dialogueButtonPosY + 2) & ~7, _dialogueHighlightedButton == i ? 0xc1 : 0xe1, 0); - } else { - _vm->gui_drawBox(x, _dialogueButtonPosY, 74, 9, 136, 251, -1); - _screen->printText(_dialogueButtonString[i], x + 37 - (_screen->getTextWidth(_dialogueButtonString[i])) / 2, - _dialogueButtonPosY + 2, _dialogueHighlightedButton == i ? 144 : 254, 0); - } - - x += _dialogueButtonXoffs; - } - _screen->setFont(of); - _screen->setCurPage(cp); -} - -uint16 TIMInterpreter_LoL::processDialogue() { - int df = _dialogueHighlightedButton; - int res = 0; - int x = _dialogueButtonPosX; - int y = (_vm->gameFlags().use16ColorMode ? (_dialogueButtonPosY & ~7) - 1 : _dialogueButtonPosY); - - for (int i = 0; i < _dialogueNumButtons; i++) { - Common::Point p = _vm->getMousePos(); - if (_vm->posWithinRect(p.x, p.y, x, y, x + 74, y + 9)) { - _dialogueHighlightedButton = i; - break; - } - x += _dialogueButtonXoffs; - } - - if (_dialogueNumButtons == 0) { - int e = _vm->checkInput(0, false) & 0xFF; - _vm->removeInputTop(); - - if (e) { - _vm->gui_notifyButtonListChanged(); - - if (e == 43 || e == 61) { - _vm->snd_stopSpeech(true); - } - } - - if (_vm->snd_updateCharacterSpeech() != 2) { - res = 1; - if (!_vm->shouldQuit()) { - _vm->removeInputTop(); - _vm->gui_notifyButtonListChanged(); - } - } - } else { - int e = _vm->checkInput(0, false) & 0xFF; - _vm->removeInputTop(); - if (e) - _vm->gui_notifyButtonListChanged(); - - if (e == 200 || e == 202) { - x = _dialogueButtonPosX; - - for (int i = 0; i < _dialogueNumButtons; i++) { - Common::Point p = _vm->getMousePos(); - if (_vm->posWithinRect(p.x, p.y, x, y, x + 74, y + 9)) { - _dialogueHighlightedButton = i; - res = _dialogueHighlightedButton + 1; - break; - } - x += _dialogueButtonXoffs; - } - } else if (e == _vm->_keyMap[Common::KEYCODE_SPACE] || e == _vm->_keyMap[Common::KEYCODE_RETURN]) { - _vm->snd_stopSpeech(true); - res = _dialogueHighlightedButton + 1; - } else if (e == _vm->_keyMap[Common::KEYCODE_LEFT] || e == _vm->_keyMap[Common::KEYCODE_DOWN]) { - if (_dialogueNumButtons > 1 && _dialogueHighlightedButton > 0) - _dialogueHighlightedButton--; - } else if (e == _vm->_keyMap[Common::KEYCODE_RIGHT] || e == _vm->_keyMap[Common::KEYCODE_UP]) { - if (_dialogueNumButtons > 1 && _dialogueHighlightedButton < (_dialogueNumButtons - 1)) - _dialogueHighlightedButton++; - } - } - - if (df != _dialogueHighlightedButton) - drawDialogueButtons(); - - _screen->updateScreen(); - - if (res == 0) - return 0; - - _vm->stopPortraitSpeechAnim(); - - if (!_vm->textEnabled() && _vm->_currentControlMode) { - _screen->setScreenDim(5); - const ScreenDim *d = _screen->getScreenDim(5); - _screen->fillRect(d->sx, d->sy + d->h - 9, d->sx + d->w - 1, d->sy + d->h - 1, d->unkA); - } else { - const ScreenDim *d = _screen->_curDim; - if (_vm->gameFlags().use16ColorMode) - _screen->fillRect(d->sx, d->sy, d->sx + d->w - 3, d->sy + d->h - 2, d->unkA); - else - _screen->fillRect(d->sx, d->sy, d->sx + d->w - 2, d->sy + d->h - 1, d->unkA); - _vm->_txt->clearDim(4); - _vm->_txt->resetDimTextPositions(4); - } - - return res; -} - void TIMInterpreter_LoL::resetDialogueState(TIM *tim) { if (!tim) return; tim->procFunc = 0; - tim->procParam = _dialogueNumButtons ? _dialogueNumButtons : 1; + tim->procParam = _vm->_dialogueNumButtons ? _vm->_dialogueNumButtons : 1; tim->clickedButton = 0; tim->dlgFunc = -1; } @@ -1168,6 +1008,18 @@ int TIMInterpreter_LoL::execCommand(int cmd, const uint16 *param) { return (this->*_commands[cmd].proc)(param); } +int TIMInterpreter_LoL::cmd_stopAllFuncs(const uint16 *param) { + while (_currentTim->dlgFunc == -1 && _currentTim->clickedButton == 0 && !_vm->shouldQuit()) { + update(); + _currentTim->clickedButton = _vm->processDialogue(); + } + + for (int i = 0; i < TIM::kCountFuncs; ++i) + _currentTim->func[i].ip = 0; + + return -1; +} + int TIMInterpreter_LoL::cmd_setLoopIp(const uint16 *param) { if (_vm->speechEnabled()) { if (_vm->snd_updateCharacterSpeech() == 2) @@ -1201,7 +1053,7 @@ int TIMInterpreter_LoL::cmd_continueLoop(const uint16 *param) { } int TIMInterpreter_LoL::cmd_processDialogue(const uint16 *param) { - int res = processDialogue(); + int res = _vm->processDialogue(); if (!res || !_currentTim->procParam) return res; @@ -1238,7 +1090,7 @@ int TIMInterpreter_LoL::cmd_dialogueBox(const uint16 *param) { } } - drawDialogueBox(cnt, tmpStr[0], tmpStr[1], tmpStr[2]); + _vm->setupDialogueButtons(cnt, tmpStr[0], tmpStr[1], tmpStr[2]); _vm->gui_notifyButtonListChanged(); return -3; diff --git a/engines/kyra/script_tim.h b/engines/kyra/script_tim.h index 11b716c3a9..e132ed78c5 100644 --- a/engines/kyra/script_tim.h +++ b/engines/kyra/script_tim.h @@ -146,7 +146,7 @@ struct TIM { uint16 *avtl; uint8 *text; - const Common::Array<const TIMOpcode *> *opcodes; + const Common::Array<const TIMOpcode*> *opcodes; // TODO: Get rid of this ugly HACK to allow the // Lands of Lore outro to be working properly. @@ -159,7 +159,7 @@ public: TIMInterpreter(KyraEngine_v1 *engine, Screen_v2 *screen_v2, OSystem *system); virtual ~TIMInterpreter(); - TIM *load(const char *filename, const Common::Array<const TIMOpcode *> *opcodes); + TIM *load(const char *filename, const Common::Array<const TIMOpcode*> *opcodes); void unload(TIM *&tim) const; bool callback(Common::IFFChunk &chunk); @@ -186,8 +186,6 @@ public: void displayText(uint16 textId, int16 flags, uint8 color); void setupTextPalette(uint index, int fadePalette); - virtual void drawDialogueBox(int numStr, const char *s1, const char *s2, const char *s3) {} - virtual uint16 processDialogue() { return 1; } virtual void resetDialogueState(TIM *tim) {} int _drawPage2; @@ -255,7 +253,6 @@ protected: int cmd_execOpcode(const uint16 *param); int cmd_initFuncNow(const uint16 *param); int cmd_stopFuncNow(const uint16 *param); - int cmd_stopAllFuncs(const uint16 *param); #define cmd_return(n, v) \ int cmd_return_##n(const uint16 *){ return v; } cmd_return( 1, 1) @@ -273,8 +270,6 @@ public: int initAnimStruct(int index, const char *filename, int x, int y, int frameDelay, int, uint16 wsaCopyParams); int freeAnimStruct(int index); - void drawDialogueBox(int numStr, const char *s1, const char *s2, const char *s3); - uint16 processDialogue(); void resetDialogueState(TIM *tim); private: @@ -284,18 +279,9 @@ private: char *getTableString(int id); void advanceToOpcode(int opcode); - void drawDialogueButtons(); - LoLEngine *_vm; Screen_LoL *_screen; - const char *_dialogueButtonString[3]; - uint16 _dialogueButtonPosX; - uint16 _dialogueButtonPosY; - int _dialogueNumButtons; - uint16 _dialogueButtonXoffs; - int _dialogueHighlightedButton; - virtual int execCommand(int cmd, const uint16 *param); typedef int (TIMInterpreter_LoL::*CommandProc)(const uint16 *); @@ -307,6 +293,7 @@ private: const CommandEntry *_commands; int _commandsSize; + int cmd_stopAllFuncs(const uint16 *param); int cmd_setLoopIp(const uint16 *param); int cmd_continueLoop(const uint16 *param); int cmd_processDialogue(const uint16 *param); diff --git a/engines/kyra/sequences_eob1.cpp b/engines/kyra/sequences_eob1.cpp new file mode 100644 index 0000000000..0606742421 --- /dev/null +++ b/engines/kyra/sequences_eob1.cpp @@ -0,0 +1,140 @@ +/* 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. + * + */ + +#ifdef ENABLE_EOB + +#include "kyra/eob1.h" +#include "kyra/screen_eob.h" +#include "kyra/resource.h" +#include "kyra/sound.h" + +#include "common/system.h" + +#include "base/version.h" + +namespace Kyra { + +int EobEngine::mainMenu() { + int menuChoice = 4; + + Screen::FontId of = _screen->_currentFont; + Common::SeekableReadStream *s = 0; + + while (menuChoice >= 0 && !shouldQuit()) { + switch (menuChoice) { + case 0: + _screen->loadPalette("EOBPAL.COL", _screen->getPalette(0)); + _screen->loadEobCpsFileToPage("INTRO", 0, 5, 3, 2); + _screen->setScreenPalette(_screen->getPalette(0)); + _screen->_curPage = 2; + of = _screen->setFont(Screen::FID_6_FNT); + _screen->printText(gScummVMVersion, 280 - strlen(gScummVMVersion) * 6, 153, _screen->getPagePixel(2, 0, 0), 0); + _screen->setFont(of); + _screen->fillRect(0, 159, 319, 199, _screen->getPagePixel(2, 0, 0)); + gui_drawBox(77, 165, 173, 29, 13, 14, 12); + gui_drawBox(76, 164, 175, 31, 13, 14, -1); + _screen->_curPage = 0; + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + menuChoice = mainMenuLoop(); + break; + + case 1: + // load game in progress + // + menuChoice = -1; + break; + + case 2: + // create new party + menuChoice = -2; + break; + + case 3: + // quit + menuChoice = -5; + break; + + case 4: + // intro + _sound->loadSoundFile("SOUND"); + _screen->hideMouse(); + seq_playOpeningCredits(); + seq_playIntro(); + _screen->showMouse(); + _sound->loadSoundFile("ADLIB"); + menuChoice = 0; + break; + } + } + + return shouldQuit() ? -5 : menuChoice; +} + +int EobEngine::mainMenuLoop() { + int sel = -1; + do { + _screen->setScreenDim(28); + _gui->setupMenu(8, 0, _mainMenuStrings, -1, 0, 0); + + while (sel == -1 && !shouldQuit()) + sel = _gui->handleMenu(8, _mainMenuStrings, 0, -1, 0); + } while ((sel < 0 || sel > 5) && !shouldQuit()); + + return sel + 1; +} + +void EobEngine::seq_playOpeningCredits() { + static const char *cmpList[] = { "WESTWOOD.CMP", "AND.CMP", "SSI.CMP", "PRESENT.CMP", "DAND.CMP" }; + static const uint8 frameDelay[] = { 140, 50, 100, 50, 140 }; + + _screen->loadPalette("WESTWOOD.COL", _screen->getPalette(0)); + _screen->setScreenPalette(_screen->getPalette(0)); + + _screen->loadBitmap(cmpList[0], 5, 3, 0); + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + + _sound->playTrack(1); + delay(frameDelay[0] * _tickLength); + + for (int i = 1; i < 5 && !shouldQuit() && !skipFlag(); i++) { + _screen->loadBitmap(cmpList[i], 5, 3, 0); + uint32 nextFrameTimer = _system->getMillis() + frameDelay[i] * _tickLength; + _screen->crossFadeRegion(0, 50, 0, 50, 320, 102, 2, 0); + delayUntil(nextFrameTimer); + } + + delay(50 * _tickLength); +} + +void EobEngine::seq_playIntro() { + //_sound->playTrack(2); +} + +void EobEngine::seq_playFinale() { + +} + +} // End of namespace Kyra + +#endif // ENABLE_EOB diff --git a/engines/kyra/sequences_eob2.cpp b/engines/kyra/sequences_eob2.cpp new file mode 100644 index 0000000000..708e9cd23a --- /dev/null +++ b/engines/kyra/sequences_eob2.cpp @@ -0,0 +1,1277 @@ +/* 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. + * + */ + +#ifdef ENABLE_EOB + +#include "kyra/eob2.h" +#include "kyra/screen_eob.h" +#include "kyra/resource.h" +#include "kyra/sound.h" + +#include "common/system.h" + +#include "base/version.h" + +namespace Kyra { + +class DarkmoonSequenceHelper { +friend class DarkMoonEngine; +public: + enum Mode { + kIntro, + kFinale + }; + + DarkmoonSequenceHelper(OSystem *system, DarkMoonEngine *vm, Screen_Eob *screen, Mode mode, const char *const *strings, const char *const *cpsFiles, const char *const *palFiles, const EobShapeDef **shapeDefList, const EobSequenceStep **seqList); + ~DarkmoonSequenceHelper(); + + void loadScene(int index, int pageNum); + void runSequence(int index, int del = -1); + + void printText(int index, int color); + void fadeText(); + + void update(int srcPage); + + void setPalette(int index); + void fadePalette(int index, int del); + void copyPalette(int srcIndex, int destIndex); + + void initDelayedPaletteFade(int palIndex, int rate); + bool processDelayedPaletteFade(); + + void delay(uint32 ticks); + void waitForSongNotifier(int index, bool introUpdateAnim = false); + +private: + void setPaletteWithoutTextColor(int index); + + OSystem *_system; + DarkMoonEngine *_vm; + Screen_Eob *_screen; + Mode _mode; + + uint8 _notifier; + + const char *const *_strings; + const char *const *_cpsFiles; + const char *const *_palFiles; + const EobShapeDef **_shapeDefs; + const EobSequenceStep **_seqData; + + Palette *_palettes[12]; + + const uint8 **_shapes; + + uint32 _fadePalTimer; + int _fadePalRate; + int _fadePalIndex; +}; + +int DarkMoonEngine::mainMenu() { + int menuChoice = 4; + + _sound->loadSoundFile("INTRO"); + Screen::FontId of = _screen->_currentFont; + int op = 0; + Common::SeekableReadStream *s = 0; + + while (menuChoice >= 0 && !shouldQuit()) { + switch (menuChoice) { + case 0: + s = _res->createReadStream("XENU.CPS"); + if (s) { + s->read(_screen->getPalette(0).getData(), 768); + _screen->loadFileDataToPage(s, 3, 64000); + delete s; + } else { + _screen->loadBitmap("MENU.CPS", 3, 2, &_screen->getPalette(0)); + } + + _screen->setScreenPalette(_screen->getPalette(0)); + + of = _screen->setFont(Screen::FID_6_FNT); + op = _screen->setCurPage(2); + _screen->printText(gScummVMVersion, 267 - strlen(gScummVMVersion) * 6, 160, 13, 0); + _screen->setFont(of); + _screen->_curPage = op; + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + menuChoice = mainMenuLoop(); + break; + + case 1: + // load game in progress + // + menuChoice = -1; + break; + + case 2: + // create new party + menuChoice = -2; + break; + + case 3: + // transfer party + seq_playFinale(); + menuChoice = -3; + break; + + case 4: + // play intro + seq_playIntro(); + menuChoice = 0; + break; + + case 5: + // quit + menuChoice = -5; + break; + } + } + + return shouldQuit() ? -5 : menuChoice; +} + +int DarkMoonEngine::mainMenuLoop() { + int sel = -1; + do { + _screen->setScreenDim(6); + _gui->setupMenu(6, 0, _mainMenuStrings, -1, 0, 0); + + while (sel == -1 && !shouldQuit()) + sel = _gui->handleMenu(6, _mainMenuStrings, 0, -1, 0); + } while ((sel < 0 || sel > 5) && !shouldQuit()); + + return sel + 1; +} + +void DarkMoonEngine::seq_playIntro() { + DarkmoonSequenceHelper sq(_system, this, _screen, DarkmoonSequenceHelper::kIntro, _introStrings, _cpsFilesIntro, _palFilesIntro, _shapesIntro, _seqIntro); + + _screen->setCurPage(0); + _screen->clearCurPage(); + + _sound->playTrack(0); + + sq.loadScene(4, 2); + sq.loadScene(0, 2); + + sq.delay(1); + + if (!skipFlag() && !shouldQuit()) + _sound->playTrack(12); + + _screen->copyRegion(0, 0, 8, 8, 304, 128, 2, 0, Screen::CR_NO_P_CHECK); + sq.setPalette(9); + sq.fadePalette(0, 3); + + _screen->setCurPage(2); + _screen->setClearScreenDim(17); + _screen->setCurPage(0); + + removeInputTop(); + sq.delay(18); + + sq.runSequence(3, 18); + sq.runSequence(6, 18); + sq.runSequence(0); + + sq.waitForSongNotifier(1); + + sq.runSequence(11); + sq.runSequence(7, 6); + sq.runSequence(2, 6); + + sq.waitForSongNotifier(2); + + sq.runSequence(38); + sq.runSequence(3); + sq.runSequence(8); + sq.runSequence(1, 10); + sq.runSequence(0, 6); + sq.runSequence(2); + + sq.waitForSongNotifier(3); + + _screen->setClearScreenDim(17); + _screen->setCurPage(2); + _screen->setClearScreenDim(17); + _screen->setCurPage(0); + + sq.runSequence(40); + sq.runSequence(7, 18); + + sq.printText(0, 16); // You were settling... + sq.runSequence(7, 90); + sq.fadeText(); + + sq.printText(1, 16); // Then a note was slipped to you + sq.runSequence(8); + sq.runSequence(2, 72); + sq.fadeText(); + + sq.printText(2, 16); // It was from your friend Khelben Blackstaff... + sq.runSequence(2); + sq.runSequence(6, 36); + sq.runSequence(3); + sq.fadeText(); + + sq.printText(3, 16); // The message was urgent. + + sq.loadScene(1, 2); + sq.waitForSongNotifier(4); + + //intro scroll + if (!skipFlag() && !shouldQuit()) { + for (int i = 0; i < 280; i += 3) { + uint32 endtime = _system->getMillis() + _tickLength; + _screen->copyRegion(11, 8, 8, 8, 301, 128, 0, 0, Screen::CR_NO_P_CHECK); + _screen->copyRegion(i, 0, 309, 8, 3, 128, 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + if (i == 96) + sq.runSequence(42); + delayUntil(endtime); + } + } + + _screen->copyRegion(8, 8, 0, 0, 304, 128, 0, 2, Screen::CR_NO_P_CHECK); + sq.runSequence(4); + sq.fadeText(); + sq.delay(10); + + sq.loadScene(2, 2); + sq.update(2); + sq.delay(10); + + sq.printText(4, 16); // What could Khelben want? + sq.delay(25); + + sq.loadScene(3, 2); + sq.delay(54); + sq.runSequence(13); + _screen->copyRegion(104, 16, 96, 8, 120, 100, 0, 2, Screen::CR_NO_P_CHECK); + sq.fadeText(); + + sq.printText(5, 15); // Welcome, please come in + sq.runSequence(10); + sq.runSequence(10); + sq.runSequence(9); + sq.runSequence(9); + sq.fadeText(); + + sq.printText(6, 15); // Khelben awaits you in his study + for (int i = 0; i < 3; i++) + sq.runSequence(10); + sq.runSequence(9); + sq.runSequence(14); + sq.loadScene(5, 2); + + sq.waitForSongNotifier(5); + + sq.fadeText(); + _screen->clearCurPage(); + _screen->updateScreen(); + + for (int i = 0; i < 6; i++) + sq.runSequence(15); + + sq.loadScene(6, 2); + sq.loadScene(7, 2); + _screen->clearCurPage(); + sq.update(2); + + sq.runSequence(16); + sq.printText(7, 15); // Thank you for coming so quickly + sq.runSequence(16); + sq.runSequence(17); + for (int i = 0; i < 3; i++) + sq.runSequence(16); + sq.fadeText(); + sq.runSequence(16); + + sq.loadScene(8, 2); + sq.update(2); + sq.runSequence(32); + sq.printText(8, 15); // I am troubled my friend + sq.runSequence(33); + sq.runSequence(33); + for (int i = 0; i < 4; i++) + sq.runSequence(32); + sq.fadeText(); + + sq.printText(9, 15); // Ancient evil stirs in the Temple Darkmoon + sq.runSequence(33); + sq.runSequence(43); + sq.runSequence(33); + for (int i = 0; i < 3; i++) + sq.runSequence(32); + sq.fadeText(); + + sq.printText(10, 15); // I fear for the safety of our city + for (int i = 0; i < 4; i++) + sq.runSequence(33); + sq.runSequence(32); + sq.runSequence(32); + + sq.loadScene(9, 2); + sq.fadeText(); + + sq.waitForSongNotifier(6); + + sq.update(2); + sq.runSequence(34); + + sq.printText(11, 15); // I need your help + for (int i = 0; i < 3; i++) + sq.runSequence(34); + sq.runSequence(35); + for (int i = 0; i < 4; i++) + sq.runSequence(34); + sq.fadeText(); + + sq.loadScene(12, 2); + sq.update(2); + sq.loadScene(6, 2); + sq.runSequence(18); + + sq.printText(12, 15); // Three nights ago I sent forth a scout + sq.runSequence(19); + sq.runSequence(20); + sq.runSequence(22); + sq.runSequence(19); + sq.runSequence(20); + sq.runSequence(18); + sq.fadeText(); + + sq.printText(13, 15); // She has not yet returned + sq.runSequence(20); + sq.runSequence(19); + sq.runSequence(23); + sq.runSequence(24); + sq.runSequence(20); + sq.runSequence(19); + sq.runSequence(17); + sq.runSequence(18); + sq.fadeText(); + + sq.printText(14, 15); // I fear for her safety + sq.runSequence(19); + sq.runSequence(20); + sq.runSequence(20); + sq.runSequence(18); + sq.runSequence(25); + sq.runSequence(18); + sq.runSequence(18); + sq.fadeText(); + sq.runSequence(18); + sq.runSequence(18); + + sq.printText(15, 15); // Take this coin + sq.runSequence(28); + sq.runSequence(19); + sq.runSequence(20); + sq.runSequence(18); + sq.runSequence(18); + sq.fadeText(); + + sq.loadScene(10, 2); + _screen->clearCurPage(); + _screen->updateScreen(); + + sq.runSequence(37, 18); + sq.runSequence(36, 36); + + sq.loadScene(12, 2); + _screen->clearCurPage(); + sq.update(2); + + sq.loadScene(11, 2); + sq.printText(16, 15); // I will use it to contact you + sq.runSequence(19); + sq.runSequence(20); + sq.runSequence(20); + sq.runSequence(18); + sq.runSequence(18); + sq.fadeText(); + + sq.printText(17, 15); // You must act quickly + sq.runSequence(19); + sq.runSequence(20); + sq.runSequence(19); + sq.runSequence(18); + sq.runSequence(18); + sq.fadeText(); + sq.runSequence(18); + + sq.printText(18, 15); // I will teleport you near Darkmoon + sq.runSequence(20); + sq.runSequence(27); + sq.runSequence(20); + sq.runSequence(19); + sq.runSequence(18); + sq.runSequence(18); + sq.fadeText(); + sq.runSequence(18); + + sq.printText(19, 15); // May luck be with you my friend + sq.runSequence(19); + sq.runSequence(19); + sq.runSequence(20); + sq.runSequence(18); + sq.fadeText(); + sq.runSequence(29); + + sq.waitForSongNotifier(7); + + sq.runSequence(30); + sq.runSequence(31); + + sq.waitForSongNotifier(8, true); + + if (skipFlag() || shouldQuit()) { + _sound->playTrack(15); + } else { + _screen->setScreenDim(17); + _screen->clearCurDim(); + _sound->playTrack(14); + sq.fadePalette(10, 1); + _screen->setClearScreenDim(18); + sq.delay(6); + sq.fadePalette(9, 1); + _screen->clearCurPage(); + } + + sq.fadePalette(9, 10); +} + +void DarkMoonEngine::seq_playFinale() { + DarkmoonSequenceHelper sq(_system, this, _screen, DarkmoonSequenceHelper::kFinale, _finaleStrings, _cpsFilesFinale, _palFilesFinale, _shapesFinale, _seqFinale); + + _screen->setCurPage(0); + + _sound->loadSoundFile("FINALE1"); + _sound->playTrack(0); + + sq.delay(3); + + _screen->clearCurPage(); + _screen->updateScreen(); + + sq.loadScene(0, 2); + sq.delay(18); + + if (!skipFlag() && !shouldQuit()) + _sound->playTrack(1); + sq.update(2); + + sq.loadScene(1, 2); + + sq.runSequence(0); + sq.runSequence(0); + for (int i = 0; i < 3; i++) + sq.runSequence(2); + sq.runSequence(1); + sq.runSequence(2); + sq.runSequence(2); + + sq.printText(0, 10); // Finally, Dran has been defeated + for (int i = 0; i < 7; i++) + sq.runSequence(2); + sq.fadeText(); + sq.runSequence(2); + + sq.waitForSongNotifier(1); + + sq.printText(1, 10); // Suddenly, your friend Khelben appears + sq.runSequence(4); + for (int i = 0; i < 3; i++) + sq.runSequence(2); + sq.fadeText(); + + sq.printText(2, 15); // Greetings, my victorious friends + for (int i = 0; i < 4; i++) + sq.runSequence(5); + sq.runSequence(2); + sq.runSequence(2); + sq.fadeText(); + sq.runSequence(6); + + sq.printText(3, 15); // You have defeated Dran + for (int i = 0; i < 5; i++) + sq.runSequence(5); + sq.runSequence(2); + sq.runSequence(2); + sq.fadeText(); + + sq.printText(4, 15); // I did not know Dran was a dragon + for (int i = 0; i < 4; i++) + sq.runSequence(5); + sq.runSequence(2); + sq.runSequence(2); + sq.fadeText(); + + sq.printText(5, 15); // He must have been over 300 years old + for (int i = 0; i < 4; i++) + sq.runSequence(5); + sq.runSequence(2); + sq.runSequence(2); + sq.fadeText(); + + sq.printText(6, 15); // His power is gone + for (int i = 0; i < 3; i++) + sq.runSequence(5); + sq.runSequence(2); + sq.runSequence(2); + sq.fadeText(); + + sq.printText(7, 15); // But Darkmoon is still a source of great evil + for (int i = 0; i < 4; i++) + sq.runSequence(5); + sq.runSequence(2); + sq.runSequence(2); + sq.fadeText(); + + sq.printText(8, 15); // And many of his minions remain + for (int i = 0; i < 4; i++) + sq.runSequence(5); + sq.runSequence(2); + sq.runSequence(2); + sq.fadeText(); + + sq.loadScene(2, 2); + sq.update(2); + sq.loadScene(3, 2); + _screen->copyRegion(8, 8, 0, 0, 304, 128, 0, 2, Screen::CR_NO_P_CHECK); + + sq.printText(9, 15); // Now we must leave this place + sq.runSequence(7); + sq.runSequence(8); + sq.runSequence(7); + sq.runSequence(7, 36); + sq.fadeText(); + + sq.printText(10, 15); // So my forces can destroy it.. + for (int i = 0; i < 3; i++) + sq.runSequence(7); + sq.runSequence(8); + sq.runSequence(7); + sq.runSequence(7, 36); + sq.runSequence(8, 18); + sq.fadeText(); + + sq.printText(11, 15); // Follow me + sq.runSequence(7, 18); + sq.runSequence(9, 18); + sq.runSequence(8, 18); + sq.fadeText(); + + sq.loadScene(7, 2); + sq.copyPalette(3, 0); + sq.loadScene(4, 2); + + sq.waitForSongNotifier(2); + + _screen->clearCurPage(); + sq.update(2); + + sq.loadScene(8, 2); + sq.loadScene(6, 6); + sq.delay(10); + + sq.printText(12, 10); // Powerful mages stand ready for the final assault... + sq.delay(90); + sq.fadeText(); + + sq.waitForSongNotifier(3); + + if (!skipFlag() && !shouldQuit()) + _sound->playTrack(7); + sq.delay(8); + + sq.runSequence(10); + sq.runSequence(13); + sq.initDelayedPaletteFade(4, 1); + + sq.runSequence(14); + sq.runSequence(13); + sq.runSequence(14); + sq.runSequence(14); + sq.runSequence(13); + sq.initDelayedPaletteFade(2, 1); + + sq.runSequence(15); + sq.runSequence(14); + sq.runSequence(13); + sq.runSequence(15); + sq.runSequence(15); + sq.runSequence(11); + + sq.printText(13, 10); // The temple's evil is very strong + sq.delay(72); + sq.fadeText(); + + sq.printText(14, 10); // It must not be allowed... + sq.delay(72); + sq.fadeText(); + + sq.waitForSongNotifier(4); + + if (!skipFlag() && !shouldQuit()) + _sound->playTrack(7); + sq.delay(8); + + sq.runSequence(10); + sq.initDelayedPaletteFade(5, 1); + sq.runSequence(13); + sq.runSequence(14); + sq.runSequence(13); + sq.runSequence(14); + sq.runSequence(13); + sq.runSequence(13); + sq.runSequence(14); + sq.runSequence(14); + sq.runSequence(13); + sq.runSequence(12); + for (int i = 0; i < 4; i++) + sq.runSequence(16); + sq.runSequence(17); + sq.runSequence(18); + + sq.printText(15, 10); // The temple ceases to exist + sq.initDelayedPaletteFade(6, 1); + sq.delay(36); + + if (!skipFlag() && !shouldQuit()) + _sound->playTrack(11); + + sq.delay(54); + sq.fadeText(); + sq.loadScene(12, 2); + + sq.waitForSongNotifier(5); + + if (!skipFlag() && !shouldQuit()) + _sound->playTrack(6); + + if (!skipFlag() && !shouldQuit()) + _screen->crossFadeRegion(0, 0, 8, 8, 304, 128, 2, 0); + sq.delay(18); + + sq.printText(16, 15); // My friends, our work is done + sq.runSequence(20); + sq.runSequence(19); + sq.runSequence(19, 36); + if (!skipFlag() && !shouldQuit()) + _sound->playTrack(12); + sq.fadeText(); + + sq.printText(17, 15); // Thank you + sq.runSequence(19); + sq.runSequence(20, 36); + sq.fadeText(); + + sq.printText(18, 15); // You have earned my deepest respect + if (!skipFlag() && !shouldQuit()) + _sound->playTrack(11); + sq.runSequence(20); + sq.runSequence(19); + sq.runSequence(19); + if (!skipFlag() && !shouldQuit()) + _sound->playTrack(11); + sq.delay(36); + sq.fadeText(); + + sq.printText(19, 15); // We will remember you always + sq.runSequence(19); + sq.runSequence(19, 18); + if (!skipFlag() && !shouldQuit()) + _sound->playTrack(11); + sq.runSequence(20, 18); + sq.fadeText(); + + sq.delay(28); + if (!skipFlag() && !shouldQuit()) + _sound->playTrack(12); + sq.delay(3); + + sq.loadScene(5, 2); + if (skipFlag() || shouldQuit()) { + _screen->copyRegion(0, 0, 8, 8, 304, 128, 2, 0, Screen::CR_NO_P_CHECK); + } else { + _sound->playTrack(6); + _screen->crossFadeRegion(0, 0, 8, 8, 304, 128, 2, 0); + } + + if (!skipFlag() && !shouldQuit()) + _sound->playTrack(12); + sq.delay(5); + if (!skipFlag() && !shouldQuit()) + _sound->playTrack(11); + sq.delay(11); + if (!skipFlag() && !shouldQuit()) + _sound->playTrack(12); + sq.delay(7); + if (!skipFlag() && !shouldQuit()) + _sound->playTrack(11); + sq.delay(12); + if (!skipFlag() && !shouldQuit()) + _sound->playTrack(12); + + removeInputTop(); + resetSkipFlag(true); + + sq.loadScene(10, 2); + sq.loadScene(9, 2); + _sound->playTrack(0); + sq.delay(3); + + _sound->loadSoundFile("FINALE2"); + + sq.delay(18); + if (!skipFlag() && !shouldQuit()) + _sound->playTrack(1); + + seq_playCredits(&sq, _creditsData, 18, 2, 6, 2); + + sq.delay(90); + + resetSkipFlag(true); + + sq.setPalette(11); + sq.fadePalette(9, 10); + + _screen->clearCurPage(); + sq.loadScene(11, 2); + + static const uint8 finPortraitPos[] = { 0x50, 0x50, 0xD0, 0x50, 0x50, 0x90, 0xD0, 0x90, 0x90, 0x50, 0x90, 0x90 }; + + for (int i = 0; i < 6; i++) { + if (!testCharacter(i, 1)) + continue; + if (i > 3) + _screen->drawShape(2, sq._shapes[6 + i], finPortraitPos[i << 1] - 16, finPortraitPos[(i << 1) + 1] - 16, 0); + _screen->drawShape(2, _characters[i].faceShape, finPortraitPos[i << 1], finPortraitPos[(i << 1) + 1], 0); + } + + _screen->copyRegion(0, 0, 0, 0, 320, 200, 2, 0, Screen::CR_NO_P_CHECK); + + sq.setPalette(9); + sq.fadePalette(0, 18); + + while (!skipFlag() && !shouldQuit()) + delay(_tickLength); + + _sound->playTrack(0); + sq.fadePalette(9, 10); +} + +void DarkMoonEngine::seq_playCredits(DarkmoonSequenceHelper *sq, const uint8 *data, int sd, int backupPage, int tempPage, int speed) { + if (!data) + return; + + _screen->setScreenDim(sd); + const ScreenDim *dm = _screen->_curDim; + + _screen->copyRegion(dm->sx << 3, dm->sy, dm->sx << 3, dm->sy, dm->w << 3, dm->h, 0, backupPage, Screen::CR_NO_P_CHECK); + + struct CreditsDataItem { + int16 x; + int16 y; + const void *data; + char *str; + uint8 crlf; + uint8 size; + uint8 dataType; + } items[36]; + memset(items, 0, sizeof(items)); + + const char *pos = (const char *)data; + uint32 end = _system->getMillis(); + uint32 cur = 0; + int i = 0; + + do { + for (bool loop = true; loop; ) { + sq->processDelayedPaletteFade(); + cur = _system->getMillis(); + if (end <= cur) + break; + delay(MIN<uint32>(_tickLength, end - cur)); + } + + end = _system->getMillis() + speed * _tickLength; + + for (; i < 35 && *pos; i++) { + int16 nextY = i ? items[i].y + items[i].size + (items[i].size >> 2) : dm->h; + + const char *posOld = pos; + pos = strchr(pos, 0x0d); + if (!pos) + pos = strchr(posOld, 0x00); + + items[i + 1].crlf = *pos++; + + if (*posOld == 2) { + const uint8 *shp = sq->_shapes[(*++posOld) - 1]; + items[i + 1].data = shp; + items[i + 1].size = shp[1]; + items[i + 1].x = (dm->w - shp[2]) << 2 ; + items[i + 1].dataType = 1; + delete[] items[i + 1].str; + items[i + 1].str = 0; + + } else { + if (*posOld == 1) { + posOld++; + items[i + 1].size = 6; + } else { + items[i + 1].size = _screen->getFontWidth(); + } + + items[i + 1].dataType = 0; + + int l = pos - posOld; + if (items[i + 1].crlf != 0x0d) + l++; + + delete[] items[i + 1].str; + items[i + 1].str = new char[l]; + memcpy(items[i + 1].str, posOld, l); + items[i + 1].str[l - 1] = 0; + items[i + 1].data = 0; + items[i + 1].x = (((dm->w << 3) - (strlen(items[i + 1].str) * items[i + 1].size)) >> 1) + 1; + } + + items[i + 1].y = nextY; + } + + _screen->copyRegion(dm->sx << 3, dm->sy, dm->sx << 3, dm->sy, dm->w << 3, dm->h, backupPage, tempPage, Screen::CR_NO_P_CHECK); + + for (int h = 0; h < i; h++) { + if (items[h + 1].y < dm->h) { + if (items[h + 1].dataType == 1) { + _screen->drawShape(tempPage, (const uint8*)items[h + 1].data, items[h + 1].x, items[h + 1].y, sd); + } else { + _screen->setCurPage(tempPage); + + if (items[h + 1].size == 6) + _screen->setFont(Screen::FID_6_FNT); + + _screen->printText(items[h + 1].str, (dm->sx << 3) + items[h + 1].x - 1, dm->sy + items[h + 1].y + 1, 12, 0); + _screen->printText(items[h + 1].str, (dm->sx << 3) + items[h + 1].x, dm->sy + items[h + 1].y, 240, 0); + + if (items[h + 1].size == 6) + _screen->setFont(Screen::FID_8_FNT); + + _screen->setCurPage(0); + } + } + + items[h + 1].y -= 2; + } + + _screen->copyRegion(dm->sx << 3, dm->sy, dm->sx << 3, dm->sy, dm->w << 3, dm->h, tempPage, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + + if (-items[1].size > items[1].y) { + delete[] items[1].str; + --i; + for (int t = 1; t <= i; t++) + memcpy(&items[t], &items[t + 1], sizeof(CreditsDataItem)); + items[i + 1].str = 0; + } + + if (i < 35 && ((items[i].y + items[i].size) < (dm->sy + dm->h))) { + resetSkipFlag(true); + break; + } + + sq->processDelayedPaletteFade(); + } while (!skipFlag() && i && !shouldQuit()); + + for (i = 0; i < 35; i++) + delete[] items[i].str; +} + +DarkmoonSequenceHelper::DarkmoonSequenceHelper(OSystem *system, DarkMoonEngine *vm, Screen_Eob *screen, Mode mode, const char *const *strings, const char *const *cpsFiles, const char *const *palFiles, const EobShapeDef **shapeDefList, const EobSequenceStep **seqList) : + _system(system), _vm(vm), _screen(screen), _mode(mode), _strings(strings), _cpsFiles(cpsFiles), _palFiles(palFiles), _shapeDefs(shapeDefList), _seqData(seqList) { + + for (int i = 0; _palFiles[i]; i++) { + if (i < 4) + _palettes[i] = &_screen->getPalette(i); + else + _palettes[i] = new Palette(256); + _screen->loadPalette(_palFiles[i], *_palettes[i]); + } + + _palettes[9] = new Palette(256); + _palettes[9]->fill(0, 256, 0); + _palettes[10] = new Palette(256); + _palettes[10]->fill(0, 256, 63); + _palettes[11] = new Palette(256); + _palettes[11]->fill(0, 256, 0); + + _shapes = new const uint8*[30]; + memset(_shapes, 0, 30 * sizeof(uint8*)); + + _fadePalTimer = 0; + _fadePalRate = 0; + + _screen->setScreenPalette(*_palettes[0]); + _screen->hideMouse(); + + _system->delayMillis(150); + _vm->resetSkipFlag(true); +} + +DarkmoonSequenceHelper::~DarkmoonSequenceHelper() { + for (int i = 4; _palFiles[i]; i++) + delete _palettes[i]; + delete _palettes[9]; + delete _palettes[10]; + delete _palettes[11]; + + for (int i = 0; i < 30; i++) + delete _shapes[i]; + delete[] _shapes; + + _screen->clearCurPage(); + _screen->showMouse(); + _screen->updateScreen(); + + _system->delayMillis(150); + _vm->resetSkipFlag(true); +} + +void DarkmoonSequenceHelper::loadScene(int index, int pageNum) { + char file[13]; + strcpy(file, _cpsFiles[index]); + + Common::SeekableReadStream *s = _vm->resource()->createReadStream(file); + if (s && file[0] != 'X') { + delete s; + _screen->loadBitmap(_cpsFiles[index], pageNum | 1, pageNum | 1, _palettes[0]); + } else { + if (!s) { + file[0] = 'X'; + s = _vm->resource()->createReadStream(file); + } + + if (!s) + error("DarkmoonSequenceHelper::loadScene(): Sequence CPS file loading failed."); + + if (_mode == kFinale) + s->read(_palettes[0]->getData(), 768); + else + s->seek(768); + _screen->loadFileDataToPage(s, 3, 64000); + delete s; + } + + int cp = _screen->setCurPage(pageNum); + + if (_shapeDefs[index]) { + for (const EobShapeDef *df = _shapeDefs[index]; df->w; df++ ) { + uint16 shapeIndex = (df->index < 0) ? df->index * -1 : df->index; + if (_shapes[shapeIndex]) + delete[] _shapes[shapeIndex]; + _shapes[shapeIndex] = _screen->encodeShape(df->x, df->y, df->w, df->h, (df->index >> 8) != 0); + } + } + + _screen->setCurPage(cp); + _screen->copyPage(pageNum | 1, pageNum); + + if ((pageNum == 0 || pageNum == 1) && !_vm->skipFlag() && !_vm->shouldQuit()) + _screen->updateScreen(); +} + +void DarkmoonSequenceHelper::runSequence(int index, int del) { + if (_vm->skipFlag() || _vm->shouldQuit()) + return; + + uint32 end = 0; + + for (const EobSequenceStep *s = _seqData[index]; s->command != 0xff && !_vm->skipFlag() && !_vm->shouldQuit(); s++) { + int palIndex = _mode == kFinale ? (s->pal + 1) : s->pal; + int x = s->x1; + int y = s->y1; + int x2 = 0; + uint16 shapeW = 0; + uint16 shapeH = 0; + + switch(s->command) { + case 0: + // flash palette + if (s->pal) + setPaletteWithoutTextColor(palIndex); + delay(s->delay); + if (_mode == DarkmoonSequenceHelper::kIntro && s->pal) + setPaletteWithoutTextColor(0); + break; + + case 1: + // draw shape, then restore beackground + shapeW = _shapes[s->obj][2]; + shapeH = _shapes[s->obj][3]; + + if (_mode == DarkmoonSequenceHelper::kFinale) { + _screen->setScreenDim(18); + x -= (_screen->_curDim->sx << 3); + y -= _screen->_curDim->sy; + if (x < 0) + shapeW -= ((-x >> 3) + 1); + else + x2 = x; + } + + _screen->drawShape(0, _shapes[s->obj], x, y, _mode == DarkmoonSequenceHelper::kIntro ? 0 : 18); + + if (s->pal) + setPaletteWithoutTextColor(palIndex); + else + _screen->updateScreen(); + + delay(s->delay); + + if (_mode == DarkmoonSequenceHelper::kIntro) { + if (s->pal) + setPaletteWithoutTextColor(0); + _screen->copyRegion(x - 8, y - 8, x, y, (shapeW + 1) << 3, shapeH, 2, 0, Screen::CR_NO_P_CHECK); + } else { + _screen->copyRegion(x2, y, x2 + (_screen->_curDim->sx << 3), y + _screen->_curDim->sy, (shapeW + 1) << 3, shapeH, 2, 0, Screen::CR_NO_P_CHECK); + } + + _screen->updateScreen(); + break; + + case 2: + // draw shape + _screen->drawShape(_screen->_curPage, _shapes[s->obj], x, y, 0); + + if (s->pal) + setPaletteWithoutTextColor(palIndex); + else if(!_screen->_curPage) + _screen->updateScreen(); + + delay(s->delay); + + if (_mode == DarkmoonSequenceHelper::kIntro && s->pal) + setPaletteWithoutTextColor(0); + break; + + case 3: + case 4: + // fade shape in or out or restore background + if (_mode == DarkmoonSequenceHelper::kFinale) + break; + + _screen->setShapeFadeMode(0, true); + _screen->setShapeFadeMode(1, true); + + end = _system->getMillis() + s->delay * _vm->tickLength(); + + if (palIndex) { + _screen->setFadeTableIndex(palIndex - 1); + + _screen->copyRegion(s->x1 - 8, s->y1 - 8, 0, 0, (_shapes[s->obj][2] + 1) << 3, _shapes[s->obj][3], 2, 4, Screen::CR_NO_P_CHECK); + _screen->drawShape(4, _shapes[s->obj], s->x1 & 7, 0, 0); + _screen->copyRegion(0, 0, s->x1, s->y1, (_shapes[s->obj][2] + 1) << 3, _shapes[s->obj][3], 4, 0, Screen::CR_NO_P_CHECK); + } else { + _screen->copyRegion(s->x1 - 8, s->y1 - 8, s->x1, s->y1, (_shapes[s->obj][2] + 1) << 3, _shapes[s->obj][3], 2, 0, Screen::CR_NO_P_CHECK); + } + _screen->updateScreen(); + + _vm->delayUntil(end); + _screen->setShapeFadeMode(0, false); + _screen->setShapeFadeMode(1, false); + break; + + case 5: + // copyregion + if (_mode == DarkmoonSequenceHelper::kFinale && s->pal) + setPaletteWithoutTextColor(palIndex); + + _screen->copyRegion(s->x2 << 3, s->y2, s->x1, s->y1, s->w << 3, s->h, (s->obj && _mode == DarkmoonSequenceHelper::kFinale) ? 6 : 2, 0, Screen::CR_NO_P_CHECK); + _screen->updateScreen(); + delay(s->delay); + break; + + case 6: + // play sound effect + if (s->obj != 0xff) + _vm->sound()->playSoundEffect(s->obj); + break; + + default: + error("DarkmoonSequenceHelper::runSequence(): Unknown animation opcode encountered."); + break; + } + } + + if (del > 0) + delay(del); +} + +void DarkmoonSequenceHelper::printText(int index, int color) { + if (_vm->skipFlag() || _vm->shouldQuit()) + return; + + _screen->setClearScreenDim(17); + _palettes[0]->copy(*_palettes[0], color, 1, 255); + setPalette(0); + + char *temp = new char[strlen(_strings[index]) + 1]; + char *str = temp; + strcpy(str, _strings[index]); + + const ScreenDim *dm = _screen->_curDim; + + for (int yOffs = 0; *str; yOffs += 9) { + char *cr = strchr(str, 13); + + if (cr) + *cr = 0; + + uint32 len = strlen(str); + _screen->printText(str, (dm->sx + ((dm->w - len) >> 1)) << 3, dm->sy + yOffs, 255, dm->unkA); + + if (cr) { + *cr = 13; + str = cr + 1; + } else { + str += len; + } + } + + delete[] temp; + _screen->updateScreen(); +} + +void DarkmoonSequenceHelper::fadeText() { + if (_vm->skipFlag() || _vm->shouldQuit()) + return; + + _screen->fadeTextColor(_palettes[0], 255, 2); + _screen->clearCurDim(); +} + +void DarkmoonSequenceHelper::update(int srcPage) { + if (_vm->skipFlag() || _vm->shouldQuit()) + return; + + _screen->copyRegion(0, 0, 8, 8, 304, 128, srcPage, 0, Screen::CR_NO_P_CHECK); + setPaletteWithoutTextColor(0); +} + +void DarkmoonSequenceHelper::setPaletteWithoutTextColor(int index) { + if (_vm->skipFlag() || _vm->shouldQuit()) + return; + + if (!memcmp(_palettes[11]->getData(), _palettes[index]->getData(), 765)) + return; + + _palettes[11]->copy(*_palettes[index], 0, 255); + _palettes[11]->copy(*_palettes[0], 255, 1, 255); + setPalette(11); + + _screen->updateScreen(); +} + +void DarkmoonSequenceHelper::setPalette(int index) { + _screen->setScreenPalette(*_palettes[index]); +} + +void DarkmoonSequenceHelper::fadePalette(int index, int del) { + if (_vm->skipFlag() || _vm->shouldQuit()) + return; + + _screen->fadePalette(*_palettes[index], del * _vm->tickLength()); +} + +void DarkmoonSequenceHelper::copyPalette(int srcIndex, int destIndex) { + _palettes[destIndex]->copy(*_palettes[srcIndex]); +} + +void DarkmoonSequenceHelper::initDelayedPaletteFade(int palIndex, int rate) { + _palettes[11]->copy(*_palettes[0]); + + _fadePalIndex = palIndex; + _fadePalRate = rate; + _fadePalTimer = _system->getMillis() + 2 * _vm->_tickLength; +} + +bool DarkmoonSequenceHelper::processDelayedPaletteFade() { + if (_vm->skipFlag() || _vm->shouldQuit()) + return true; + + if (!_fadePalRate || (_system->getMillis() <= _fadePalTimer)) + return false; + + if (_screen->delayedFadePalStep(_palettes[_fadePalIndex], _palettes[0], _fadePalRate)) { + setPaletteWithoutTextColor(0); + _fadePalTimer = _system->getMillis() + 3 * _vm->_tickLength; + } else { + _fadePalRate = 0; + } + + return false; +} + +void DarkmoonSequenceHelper::delay(uint32 ticks) { + if (_vm->skipFlag() || _vm->shouldQuit()) + return; + + uint32 end = _system->getMillis() + ticks * _vm->_tickLength; + + if (_mode == kFinale) { + do { + if (processDelayedPaletteFade()) + break; + _vm->updateInput(); + } while (end > _system->getMillis()); + processDelayedPaletteFade(); + + } else { + _vm->delayUntil(end); + } +} + +void DarkmoonSequenceHelper::waitForSongNotifier(int index, bool introUpdateAnim) { + int seq = 0; + while (_notifier < index && !(_vm->skipFlag() || _vm->shouldQuit())) { + if (introUpdateAnim) { + runSequence(30 | seq); + seq ^= 1; + } + + if (_mode == kFinale) + processDelayedPaletteFade(); + + _vm->updateInput(); + } +} + +} // End of namespace Kyra + +#endif // ENABLE_EOB diff --git a/engines/kyra/sound.cpp b/engines/kyra/sound.cpp index 9325513066..73c20ee6df 100644 --- a/engines/kyra/sound.cpp +++ b/engines/kyra/sound.cpp @@ -266,7 +266,7 @@ void KyraEngine_v1::snd_playTheme(int file, int track) { } void KyraEngine_v1::snd_playSoundEffect(int track, int volume) { - _sound->playSoundEffect(track); + _sound->playSoundEffect(track, volume); } void KyraEngine_v1::snd_playWanderScoreViaMap(int command, int restart) { diff --git a/engines/kyra/sound_lol.cpp b/engines/kyra/sound_lol.cpp index 968488eef3..6bf047fe35 100644 --- a/engines/kyra/sound_lol.cpp +++ b/engines/kyra/sound_lol.cpp @@ -197,24 +197,9 @@ void LoLEngine::snd_playSoundEffect(int track, int volume) { } } -void LoLEngine::snd_processEnvironmentalSoundEffect(int soundId, int block) { - if (!_sound->sfxEnabled() || shouldQuit()) - return; - - if (_environmentSfx) - snd_playSoundEffect(_environmentSfx, _environmentSfxVol); - - int dist = 0; - if (block) { - dist = getMonsterDistance(_currentBlock, block); - if (dist > _envSfxDistThreshold) { - _environmentSfx = 0; - return; - } - } - - _environmentSfx = soundId; - _environmentSfxVol = (15 - ((block || dist < 2) ? dist : 0)) << 4; +bool LoLEngine::snd_processEnvironmentalSoundEffect(int soundId, int block) { + if (!LolEobBaseEngine::snd_processEnvironmentalSoundEffect(soundId, block)) + return false; if (block != _currentBlock) { static const int8 blockShiftTable[] = { -32, -31, 1, 33, 32, 31, -1, -33 }; @@ -231,9 +216,9 @@ void LoLEngine::snd_processEnvironmentalSoundEffect(int soundId, int block) { } if (!soundId || _sceneUpdateRequired) - return; + return false; - snd_processEnvironmentalSoundEffect(0, 0); + return snd_processEnvironmentalSoundEffect(0, 0); } void LoLEngine::snd_queueEnvironmentalSoundEffect(int soundId, int block) { diff --git a/engines/kyra/sprites_eob.cpp b/engines/kyra/sprites_eob.cpp new file mode 100644 index 0000000000..aac3c274db --- /dev/null +++ b/engines/kyra/sprites_eob.cpp @@ -0,0 +1,1249 @@ +/* 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. + * + */ + +#if defined(ENABLE_EOB) || defined(ENABLE_LOL) + +#include "kyra/eobcommon.h" +#include "kyra/script_eob.h" +#include "kyra/resource.h" +#include "kyra/timer.h" + +#include "common/system.h" + +namespace Kyra { + +int LolEobBaseEngine::getBlockDistance(uint16 block1, uint16 block2) { + int b1x = block1 & 0x1f; + int b1y = block1 >> 5; + int b2x = block2 & 0x1f; + int b2y = block2 >> 5; + + uint8 dy = ABS(b2y - b1y); + uint8 dx = ABS(b2x - b1x); + + if (dx > dy) + SWAP(dx, dy); + + return (dx >> 1) + dy; +} + +} // namespace Kyra + +#endif +#ifdef ENABLE_EOB + +namespace Kyra { + +void EobCoreEngine::loadMonsterShapes(const char *filename, int monsterIndex, bool hasDecorations, int encodeTableIndex) { + _screen->loadEobBitmap(filename, 3, 3); + const uint16 *enc = &_encodeMonsterShpTable[encodeTableIndex << 2]; + + for (int i = 0; i < 6; i++, enc += 4) + _monsterShapes[monsterIndex + i] = _screen->encodeShape(enc[0], enc[1], enc[2], enc[3]); + + generateMonsterPalettes(filename, monsterIndex); + + if (hasDecorations) + loadMonsterDecoration(filename, monsterIndex); + + _screen->_curPage = 0; +} + +void EobCoreEngine::releaseMonsterShapes(int first, int num) { + for (int i = first; i < first + num; i++) { + delete[] _monsterShapes[i]; + _monsterShapes[i] = 0; + delete[] _monsterDecorations[i].shp; + _monsterDecorations[i].shp = 0; + } +} + +const uint8 *EobCoreEngine::loadMonsterProperties(const uint8 *data) { + uint8 cmd = *data++; + while (cmd != 0xff) { + EobMonsterProperty *d = &_monsterProps[cmd]; + d->armorClass = (int8)*data++; + d->hitChance = (int8)*data++; + d->level = *data++; + d->hpDcTimes = *data++; + d->hpDcPips = *data++; + d->hpDcBase = *data++; + d->attacksPerRound = *data++; + d->dmgDc[0].times = *data++; + d->dmgDc[0].pips = *data++; + d->dmgDc[0].base = (int8)*data++; + d->dmgDc[1].times = *data++; + d->dmgDc[1].pips = *data++; + d->dmgDc[1].base = (int8)*data++; + d->dmgDc[2].times = *data++; + d->dmgDc[2].pips = *data++; + d->dmgDc[3].base = (int8)*data++; + d->statusFlags = READ_LE_UINT16(data); + data += 2; + d->flags = READ_LE_UINT16(data); + data += 2; + d->u22 = (int16)READ_LE_UINT16(data); + data += 2; + d->experience = READ_LE_UINT16(data); + data += 2; + + d->u30 = *data++; + d->sound1 = *data++; + d->sound2 = *data++; + d->numRemoteAttacks = *data++; + + if (*data++ != 0xff) { + d->remoteWeaponChangeMode = *data++; + d->numRemoteWeapons = *data++; + + for (int i = 0; i < d->numRemoteWeapons; i++) { + d->remoteWeapons[i] = (int8)*data; + data += 2; + } + } + + d->u41 = *data++; + d->dmgModifierEvade = *data++; + + for (int i = 0; i < 3; i++) + d->decorations[i] = *data++; + + cmd = *data++; + } + + return data; +} + +const uint8 *EobCoreEngine::loadActiveMonsterData(const uint8 *data, int level) { + for (uint8 p = *data++; p != 0xff; p = *data++) { + uint8 v = *data++; + _timer->setCountdown(0x20 + (p << 1), v); + _timer->setCountdown(0x21 + (p << 1), v); + } + + if (_hasTempDataFlags & (1 << (level - 1))) + return data + 420; + + memset(_monsters, 0, 30 * sizeof(EobMonsterInPlay)); + + for (int i = 0; i < 30; i++, data += 14) { + if (*data == 0xff) + continue; + + initMonster(data[0], data[1], READ_LE_UINT16(&data[2]), data[4], (int8)data[5], data[6], data[7], data[8], data[9], READ_LE_UINT16(&data[10]), READ_LE_UINT16(&data[12])); + _monsters[data[0]].flags |= 0x40; + } + + return data; +} + +void EobCoreEngine::initMonster(int index, int unit, uint16 block, int pos, int dir, int type, int shpIndex, int mode, int i, int randItem, int fixedItem) { + EobMonsterInPlay *m = &_monsters[index]; + EobMonsterProperty *p = &_monsterProps[type]; + memset(m, 0, sizeof(EobMonsterInPlay)); + + if (!block) + return; + + unit <<= 1; + if (index & 1) + unit++; + + m->stepsTillRemoteAttack = _flags.gameID == GI_EOB2 ? rollDice(1, 3, 0) : 0; + m->type = type; + m->numRemoteAttacks = p->numRemoteAttacks; + m->curRemoteWeapon = 0; + m->unit = unit; + m->pos = pos; + m->shpIndex = shpIndex; + m->mode = mode; + m->f_b = i; + m->dir = dir; + m->palette = _flags.gameID == GI_EOB2 ? (index % 3) : 0; + m->hitPointsCur = m->hitPointsMax = _flags.gameID == GI_EOB2 ? rollDice(p->hpDcTimes, p->hpDcPips, p->hpDcBase) : (p->hpDcTimes == 255 ? rollDice(1, 4, 0) : rollDice(p->hpDcTimes, 8, 0)); + m->randItem = randItem; + m->fixedItem = fixedItem; + m->sub = _currentSub; + + placeMonster(m, block, dir); +} + +void EobCoreEngine::placeMonster(EobMonsterInPlay *m, uint16 block, int dir) { + if (block != 0xffff){ + checkSceneUpdateNeed(m->block); + if (_levelBlockProperties[m->block].flags & 7) { + _levelBlockProperties[m->block].flags--; + if (_flags.gameID == GI_EOB2) + runLevelScript(m->block, 0x400); + } + m->block = block; + _levelBlockProperties[block].flags++; + if (_flags.gameID == GI_EOB2) + runLevelScript(m->block, 0x200); + } + + if (dir != -1) { + m->dir = dir; + block = m->block; + } + + checkSceneUpdateNeed(block); +} + +void EobCoreEngine::killMonster(EobMonsterInPlay *m, bool giveExperience) { + m->hitPointsCur = -1; + int pos = (m->pos == 4) ? rollDice(1, 4, -1) : m->pos; + + if (m->randItem) { + if (rollDice(1, 10, 0) == 1) + setItemPosition((Item*)&_levelBlockProperties[m->block & 0x3ff].drawObjects, m->block, duplicateItem(m->randItem), pos); + } + + if (m->fixedItem) + setItemPosition((Item*)&_levelBlockProperties[m->block & 0x3ff].drawObjects, m->block, duplicateItem(m->fixedItem), pos); + + if (giveExperience) + increasePartyExperience(_monsterProps[m->type].experience); + + if ((_flags.gameID == GI_EOB2) && (_currentLevel == 16) && (_currentSub == 1) && (_monsterProps[m->type].flags & 4)) { + if (m->type) { + _playFinale = true; + _runFlag = false; + } else { + m->hitPointsCur = 150; + m->curRemoteWeapon = 0; + m->numRemoteAttacks = 255; + m->shpIndex++; + m->type++; + //// TODO + // dranDragonTransformation(); + } + } else { + placeMonster(m, 0, -1); + + if ((_flags.gameID == GI_EOB1) && (m->type == 21)) { + _playFinale = true; + _runFlag = false; + } + + if (m->mode == 8) + updateAttackingMonsterFlags(); + } +} + +int EobCoreEngine::countSpecificMonsters(int type) { + int res = 0; + for (int i = 0; i < 30; i++) { + if (_monsters[i].type != type || _monsters[i].sub != _currentSub || _monsters[i].hitPointsCur < 0) + continue; + res++; + } + return res; +} + +void EobCoreEngine::updateAttackingMonsterFlags() { + EobMonsterInPlay *m2 = 0; + for (EobMonsterInPlay *m = _monsters; m < &_monsters[30]; m++) { + if (m->mode != 8) + continue; + m->mode = 0; + m->dest = _currentBlock; + m2 = m; + } + + if (m2->type == 7) + _inf->setFlag(4); + + if (m2->type == 12) + _inf->setFlag(0x800); +} + +const int8 *EobCoreEngine::getMonsterBlockPositions(uint16 block) { + static int8 pos[6]; + memset(pos, -1, sizeof(pos)); + for (int8 i = 0; i < 30; i++) { + if (_monsters[i].block != block) + continue; + pos[_monsters[i].pos] = i; + } + return pos; +} + +int EobCoreEngine::getClosestMonsterPos(int charIndex, int block) { + const int8 *pos = getMonsterBlockPositions(block); + if (pos[4] != -1) + return pos[4]; + + const uint8 *p = &_monsterProximityTable[(_currentDirection << 3) + ((charIndex & 1) << 2)]; + for (int i = 0; i < 4; i++) { + if (pos[p[i]] != -1) + return pos[p[i]]; + } + return -1; +} + +bool EobCoreEngine::blockHasMonsters(uint16 block) { + return _levelBlockProperties[block].flags & 7 ? true : false; +} + +bool EobCoreEngine::isMonsterOnPos(EobMonsterInPlay *m, uint16 block, int pos, int checkPos4) { + return (m->block == block && (m->pos == pos || (m->pos == 4 && checkPos4))) ? true : false; +} + +const int16 *EobCoreEngine::findBlockMonsters(uint16 block, int pos, int dir, int blockDamage, int singleTargetCheckAdjacent) { + static const uint8 cpos4[] = { 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1 }; + int checkPos4 = (pos <= 4) ? cpos4[(dir << 2) + pos] : 1; + int16 *dst = _foundMonstersArray; + + if (blockDamage) { + for (int i = 0; i < 30; i++) { + if (_monsters[i].block == block && (_monsters[i].pos != 4 || checkPos4)) + *dst++ = i; + } + + } else if (singleTargetCheckAdjacent) { + int16 r = -1; + int f = 5; + + for (int i = 0; i < 30; i++) { + const uint8 *tbl = &_findBlockMonstersTable[(dir << 4) + (pos << 2)]; + + if (_monsters[i].block != block) + continue; + + if (_monsters[i].pos == pos) { + r = i; + break; + } + + for (int ii = 0; ii < 4; ii++) { + if (_monsters[i].pos == tbl[ii] && ii < f) { + f = ii; + r = i; + } + } + } + + *dst++ = r; + + } else { + for (int i = 0; i < 30; i++) { + if (isMonsterOnPos(&_monsters[i], block, pos, checkPos4)) + *dst++ = i; + } + } + + *dst = -1; + return _foundMonstersArray; +} + +void EobCoreEngine::drawBlockObject(int flipped, int page, const uint8 *shape, int x, int y, int sd, uint8 *ovl) { + const ScreenDim *d = _screen->getScreenDim(sd); + _screen->drawShape(page, shape, x - (d->sx << 3), y - d->sy, sd, flipped | (ovl ? 2 : 0), ovl); +} + +void EobCoreEngine::drawMonsterShape(const uint8 *shape, int x, int y, int flipped, int flags, int palIndex) { + uint8 *ovl = 0; + + if (flags & 2) + ovl = _monsterOvl1; + else if (_flags.gameID == GI_EOB2 && flags & 0x20) + ovl = _monsterOvl2; + else if (palIndex != -1) + ovl = _monsterPalettes[palIndex]; + + drawBlockObject(flipped, 2, shape, x, y, 5, ovl); +} + +void EobCoreEngine::flashMonsterShape(EobMonsterInPlay *m) { + disableSysTimer(2); + _flashShapeTimer = 0; + drawScene(1); + m->flags &= 0xfd; + _flashShapeTimer = _system->getMillis() + _tickLength; + enableSysTimer(2); + + _sceneUpdateRequired = true; +} + +void EobCoreEngine::updateAllMonsterShapes() { + drawScene(1); + bool update = false; + + for (EobMonsterInPlay *m = _monsters; m < &_monsters[30]; m++) { + if (m->flags & 2) { + m->flags &= ~2; + update = true; + if (m->hitPointsCur <= 0) + killMonster(m, true); + } + } + + if (update) { + _sceneUpdateRequired = true; + _flashShapeTimer = _system->getMillis() + _tickLength; + } else { + _sceneUpdateRequired = false; + } + _inflictMonsterDamageUnk = 0; +} + +void EobCoreEngine::drawBlockItems(int index) { + uint16 o = _visibleBlocks[index]->drawObjects; + uint8 w = _visibleBlocks[index]->walls[_sceneDrawVarDown]; + uint8 flg = (index == 16) ? 0x80 : _wllWallFlags[w]; + + if (_wllVmpMap[w] && !(flg & 0x80)) + return; + + uint16 o2 = o = _items[o].next; + bool forceLoop = true; + static const int8 itemPosYNiche[] = { 0x25, 0x31, 0x38, 0x00 }; + static const int8 itemPosFin[] = { 0, -2, 1, -1, 2, 0, 1, -1 }; + int tile2 = 0; + + while (o != o2 || forceLoop) { + EobItem *itm = &_items[o]; + if (itm->pos == 8 || itm->pos < 4) { + tile2 = -1; + + uint8 ps = (itm->pos == 8) ? 4 : _dscItemPosIndex[(_currentDirection << 2) + (itm->pos & 3)]; + uint16 wo = (index * 5 + ps) << 1; + int x = _dscShapeCoords[wo] + 88; + int y = 0; + + if (itm->pos == 8) { + x = _dscItemShpX[index]; + y = itemPosYNiche[_dscDimMap[index]]; + ps = 0; + } else { + y = _dscShapeCoords[wo + 1] + 124; + } + + int8 scaleSteps = (int8)_dscItemScaleIndex[(_dscDimMap[index] << 2) + ps]; + if (flg & 8 && ps < 2 && scaleSteps) { + tile2 = _dscItemTileIndex[index]; + if (tile2 != -1) + setLevelShapesDim(tile2, _shpDmX1, _shpDmX2, 5); + y -= 4; + } + + if (scaleSteps >= 0) { + const uint8 *shp = _screen->scaleShape(_dscItemShapeMap[itm->icon] < _numLargeItemShapes ? _largeItemShapes[_dscItemShapeMap[itm->icon]] : (_dscItemShapeMap[itm->icon] < 15 ? 0 : _smallItemShapes[_dscItemShapeMap[itm->icon] - 15]), scaleSteps); + x = x + itemPosFin[o & 7] - (shp[2] << 2); + y -= shp[1]; + + if (itm->pos != 8) + y += itemPosFin[(o >> 1) & 7]; + + drawBlockObject(0, 2, shp, x, y, 5); + _screen->setShapeFadeMode(1, false); + } + } + + o = itm->next; + forceLoop = false; + if(tile2 != -1) + setLevelShapesDim(index, _shpDmX1, _shpDmX2, 5); + } +} + +void EobCoreEngine::drawDoor(int index) { + int s = _visibleBlocks[index]->walls[_sceneDrawVarDown]; + int type = _dscDoorShpIndex[s]; + int d = _dscDimMap[index]; + int w = _dscShapeCoords[(index * 5 + 4) << 1]; + + int x = 88 + w; + int y = 0; + + int16 y1 = 0; + int16 y2 = 0; + scaleLevelShapesDim(index, y1, y2, 5); + drawDoorIntern(type, index, x, y, w, s, d, y1, y2); + drawLevelModifyScreenDim(5, _shpDmX1, 0, _shpDmX2, 15); +} + +void EobCoreEngine::drawMonsters(int index) { + static const uint8 distMap[] = { 2, 1, 0, 4 }; + static const uint8 yAdd[] = { 20, 12, 4, 4, 2, 0, 0 }; + + int blockDistance = distMap[_dscDimMap[index]]; + + uint16 bl = _visibleBlockIndex[index]; + if (!bl) + return; + + int drawObjDirIndex = _currentDirection * 5; + int cDirOffs = _currentDirection << 2; + + EobMonsterInPlay *drawObj[5]; + memset(drawObj, 0, 5 * sizeof(EobMonsterInPlay*)); + + for (int i = 0; i < 30; i++) { + if (_monsters[i].block != bl) + continue; + drawObj[_drawObjPosIndex[drawObjDirIndex + _monsters[i].pos]] = &_monsters[i]; + } + + for (int i = 0; i < 5; i++) { + EobMonsterInPlay *d = drawObj[i]; + if (!d) + continue; + + EobMonsterProperty *p = &_monsterProps[d->type]; + + if (_flags.gameID == GI_EOB2 && (p->flags & 0x100) && !(_partyEffectFlags & 0x220) && !(d->flags & 2)) + continue; + + int f = (d->animStep << 4) + cDirOffs + d->dir; + f = (p->flags & 2) ? _monsterFrmOffsTable1[f] : _monsterFrmOffsTable2[f]; + + if (!blockDistance && d->curAttackFrame < 0) + f = d->curAttackFrame + 7; + + int subFrame = ABS(f); + int shpIndex = d->shpIndex ? 18 : 0; + int palIndex = d->palette ? ((((shpIndex == 18) ? subFrame + 5 : subFrame - 1) << 1) + (d->palette - 1)) : -1; + + const uint8 *shp = _screen->scaleShape(_monsterShapes[subFrame + shpIndex - 1], blockDistance); + + int v30 = (subFrame == 1 || subFrame > 3) ? 1 : 0; + int v1e = (d->pos == 4) ? 4 : _dscItemPosIndex[cDirOffs + d->pos]; + int posIndex = (index * 5 + v1e) << 1; + + int x = _dscShapeCoords[posIndex] + 88; + int y = _dscShapeCoords[posIndex + 1] + 127; + + if (p->u30 == 1) { + if (v30) { + if (_flags.gameID == GI_EOB2) + posIndex = ((posIndex >> 1) - v1e) << 1; + y = _dscShapeCoords[posIndex + 1] + 127 + yAdd[blockDistance + ((v1e == 4 || _flags.gameID == GI_EOB1) ? 0 : 3)]; + } else { + if (_flags.gameID == GI_EOB2) + posIndex = ((posIndex >> 1) - v1e + 4) << 1; + x = _dscShapeCoords[posIndex] + 88; + } + } + + int w = shp[2] << 3; + int h = shp[1]; + + x = x - (w >> 1) + (d->idleAnimState >> 4); + y = y - h + (d->idleAnimState & 0x0f); + + drawMonsterShape(shp, x, y, f >= 0 ? 0 : 1, d->flags, palIndex); + + if (_flags.gameID == GI_EOB1) { + _screen->setShapeFadeMode(1, false); + continue; + } + + for (int ii = 0; ii < 3; ii++) { + if (!p->decorations[ii]) + continue; + + SpriteDecoration *dcr = &_monsterDecorations[(p->decorations[ii] - 1) * 6 + subFrame + shpIndex - 1]; + + if (!dcr) + continue; + if (!dcr->shp) + continue; + + shp = _screen->scaleShape(dcr->shp, blockDistance); + int dx = dcr->x; + int dy = dcr->y; + + for (int iii = 0; iii < blockDistance; iii++) { + dx = (dx << 1) / 3; + dy = (dy << 1) / 3; + } + + drawMonsterShape(shp, x + ((f < 0) ? (w - dx - (shp[2] << 3)) : dx), y + dy, f >= 0 ? 0 : 1, d->flags, -1); + } + _screen->setShapeFadeMode(1, false); + } +} + +void EobCoreEngine::drawWallOfForce(int index) { + +} + +void EobCoreEngine::drawFlyingObjects(int index) { + LevelBlockProperty *bl = _visibleBlocks[index]; + int blockIndex = _visibleBlockIndex[index]; + int w = bl->walls[_sceneDrawVarDown]; + + if (_wllVmpMap[w] && !(_wllWallFlags[w] & 0x80)) + return; + + EobFlyingObject *drawObj[5]; + memset(drawObj, 0, 5 * sizeof(EobFlyingObject*)); + + for (int i = 0; i < 10; i++) { + if (!_flyingObjects[i].enable || blockIndex != _flyingObjects[i].curBlock) + continue; + drawObj[_drawObjPosIndex[_currentDirection * 5 + (_flyingObjects[i].curPos & 3)]] = &_flyingObjects[i]; + } + + for (int i = 0; i < 5; i++) { + EobFlyingObject *fo = drawObj[i]; + if (!fo) + continue; + + int p = _dscItemPosIndex[(_currentDirection << 2) + (fo->curPos & 3)]; + int x = _dscShapeCoords[(index * 5 + p) << 1] + 88; + int y = 39; + + int sclValue = _flightObjSclIndex[(index << 2) + p]; + int flipped = 0; + + if (sclValue < 0) { + _screen->setShapeFadeMode(1, false); + continue; + } + + const uint8 *shp = 0; + bool rstFade = false; + + if (fo->enable == 1) { + int shpIx = _dscItemShapeMap[_items[fo->item].icon]; + int dirOffs = (fo->direction == _currentDirection) ? 0 : ((fo->direction == (_currentDirection ^ 2)) ? 1 : -1); + + if (dirOffs == -1 || _flightObjShpMap[shpIx] == -1) { + shp = shpIx < _numLargeItemShapes ? _largeItemShapes[shpIx] : (shpIx < 15 ? 0 : _smallItemShapes[shpIx - 15]); + flipped = fo->direction == ((_currentDirection + 1) & 3) ? 1 : 0; + } else { + shp = (_flightObjShpMap[shpIx] + dirOffs) < _numThrownItemShapes ? _thrownItemShapes[_flightObjShpMap[shpIx] + dirOffs] : _spellShapes[_flightObjShpMap[shpIx - _numThrownItemShapes] + dirOffs]; + flipped = _flightObjFlipIndex[(fo->direction << 2) + fo->curPos]; + } + + } else { + rstFade = true; + shp = (fo->objectType < _numThrownItemShapes) ? _thrownItemShapes[fo->objectType] : _spellShapes[fo->objectType - _numThrownItemShapes]; + flipped = _flightObjFlipIndex[(fo->direction << 2) + fo->curPos]; + + if (fo->flags & 0x40) { + x = _dscShapeCoords[(index * 5 + 4) << 1] + 88; + y = 44; + } + } + + shp = _screen->scaleShape(shp, sclValue); + + if (rstFade) { + _screen->setShapeFadeMode(1, false); + rstFade = false; + } + + x -= (shp[2] << 2); + y -= (y == 44 ? (shp[1] >> 1) : shp[1]); + + drawBlockObject(flipped, 2, shp, x, y, 5); + _screen->setShapeFadeMode(1, false); + } +} + +void EobCoreEngine::drawTeleporter(int index) { + static const uint8 telprtX[] = { 0x28, 0x1C, 0x12 }; + static const uint8 telprtY[] = { 0x0D, 0x15, 0x1A }; + + int t = 2 - _dscDimMap[index]; + if (t < 0) + return; + + int16 x1 = _dscItemShpX[index] - telprtX[t]; + int16 y1 = telprtY[t]; + + for (int i = 0; i < 2; i++) { + + int16 x2 = 0; + int16 y2 = 0; + int d = (t << 1) + i; + if (!d) + x2 = y2 = -4; + + const uint8 *shp = _teleporterShapes[d ^ _teleporterPulse]; + + for (int ii = 0; ii < 13; ii++) + drawBlockObject(0, 2, shp, x1 + x2 + _teleporterShapeCoords[d * 26 + ii * 2], y1 + y2 + _teleporterShapeCoords[d * 26 + ii * 2 + 1], 5); + } +} + +void EobCoreEngine::updateMonsters(int unit) { + for (int i = 0; i < 30; i++) { + EobMonsterInPlay *m = &_monsters[i]; + + if (m->unit == unit) { + if (m->hitPointsCur <= 0 || m->flags & 0x20) + continue; + if (m->directionChanged) { + m->directionChanged = 0; + continue; + } + + updateMonsterDest(m); + + if (m->mode > 0) + updateMonsterDest2(m); + + switch (m->mode) { + case 0: + updateMoveMonster(m); + break; + case 1: + updateMonsterFollowPath(m, 2); + break; + case 2: + updateMonsterFollowPath(m, -1); + break; + case 3: + updateMonsterFollowPath(m, 1); + break; + case 5: + updateMonstersStraying(m, -1); + break; + case 6: + updateMonstersStraying(m, 1); + break; + case 7: + case 10: + updateMonsters_mode710(m); + break; + default: + break; + } + + if (m->mode != 4 && m->mode != 7 && m->mode != 8) + m->animStep ^= 1; + + if (_monsterProps[m->type].u30 == 1) + setBlockMonsterDirection(m->block, m->dir); + } + } + checkFlyingObjects(); +} + +void EobCoreEngine::updateMonsterDest(EobMonsterInPlay *m) { + if (m->mode >= 7 && m->mode <= 10) + return; + int dist = getBlockDistance(m->block, _currentBlock); + if (dist >= 4) + return; + + int s = getNextMonsterDirection(m->block, _currentBlock) - (m->dir << 1) - 3; + + if (s < 0) + s += 8; + + if (s <= 2 && dist >= 2) + return; + + m->mode = 0; + m->dest = _currentBlock; +} + +void EobCoreEngine::updateMonsterDest2(EobMonsterInPlay *m) { + if (!(m->flags & 1) || m->mode == 10) + return; + if (m->mode == 8) { + turnFriendlyMonstersHostile(); + return; + } + m->mode = 0; + m->dest = _currentBlock; +} + +void EobCoreEngine::turnFriendlyMonstersHostile() { + EobMonsterInPlay *m = 0; + for (int i = 0; i < 30; i++) { + if (_monsters[i].mode == 8) { + _monsters[i].mode = 0; + _monsters[i].dest = _currentBlock; + m = &_monsters[i]; + } + } + + if (m) { + if (m->type == 7) + _inf->setFlag(0x40000); + else if (m->type == 12) + _inf->setFlag(0x8000000); + } +} + +int EobCoreEngine::getNextMonsterDirection(int curBlock, int destBlock) { + uint8 c = destBlock % 32; + uint8 d = destBlock / 32; + uint8 e = curBlock % 32; + uint8 f = curBlock / 32; + + int r = 0; + + int s1 = f - d; + int d1 = ABS(s1); + s1 <<= 1; + int s2 = c - e; + int d2 = ABS(s2); + s2 <<= 1; + + if (s1 >= d2) + r |= 8; + if (-s1 >= d2) + r |= 4; + if (s2 >= d1) + r |= 2; + if (-s2 >= d1) + r |= 1; + + return _monsterDirChangeTable[r]; +} + +int EobCoreEngine::getNextMonsterPos(EobMonsterInPlay *m, int block) { + if ((_flags.gameID == GI_EOB1 && _monsterProps[m->type].u30 != 0) || (_flags.gameID == GI_EOB2 && _monsterProps[m->type].u30 == 2)) + return -1; + int d = findFreeMonsterPos(block, _monsterProps[m->type].u30); + if (d < 0) + return -1; + + int dir = m->dir; + if (_flags.gameID == GI_EOB2) { + if (_monsterProps[m->type].u30 == 1) { + if (d == 9) + return -1; + + int v = _monsterCloseAttUnkTable[d]; + if (v != -1) + ////// + m->dir = 0; + return v; + } + } else { + dir &= 1; + } + + for (int i = 0; i < 4; i++) { + int v = m->dir ^ _monsterCloseAttPosTable2[(dir << 2) + i]; + if (!(d & (1 << v))) + return v; + } + + return -1; +} + +int EobCoreEngine::findFreeMonsterPos(int block, int size) { + int nm = _levelBlockProperties[block].flags & 7; + if (nm == 4) + return -2; + + int res = 0; + + for (int i = 0; i < 30; i++) { + EobMonsterInPlay *m = &_monsters[i]; + if (m->block != block) + continue; + if (_monsterProps[m->type].u30 != size) + return -1; + + if (m->pos == 4 && !(_flags.gameID == GI_EOB2 && m->flags & 0x20)) + m->pos = (_flags.gameID == GI_EOB2 && _monsterProps[m->type].u30 == 1) ? 0 : _monsterCloseAttPosTable1[m->dir]; + + res |= (1 << m->pos); + if (--nm == 0) + break; + } + + return res; +} + +void EobCoreEngine::updateMoveMonster(EobMonsterInPlay *m) { + EobMonsterProperty *p = &_monsterProps[m->type]; + int d = getNextMonsterDirection(m->block, _currentBlock); + + if ((p->flags & 0x800) && !(d & 1)) + d >>= 1; + else + d = m->dir; + + d = calcNewBlockPosition(m->block, d); + + if (m->dest == d && _currentBlock != d) { + m->mode = rollDice(1, 2, -1) + 5; + return; + } + + if (updateMonsterTryDistanceAttack(m)) + return; + + if (updateMonsterTryCloseAttack(m, d)) + return; + + m->curAttackFrame = 0; + walkMonster(m, m->dest); + + if (p->flags & 8) + updateMonsterTryCloseAttack(m, -1); +} + +bool EobCoreEngine::updateMonsterTryDistanceAttack(EobMonsterInPlay *m) { + EobMonsterProperty *p = &_monsterProps[m->type]; + if (!m->numRemoteAttacks || ((_flags.gameID == GI_EOB1) && !(p->flags & 0x40))) + return false; + + if ((_flags.gameID == GI_EOB1 && m->stepsTillRemoteAttack == 5) || (_flags.gameID == GI_EOB2 && rollDice(1, 3) > m->stepsTillRemoteAttack)) { + m->stepsTillRemoteAttack++; + return false; + } + + if (getBlockDistance(m->block, _currentBlock) > 3 || getNextMonsterDirection(m->block, _currentBlock) != (m->dir << 1)) + return false; + + int d = m->dir; + int bl = calcNewBlockPosition(m->block, d); + + while (bl != _currentBlock) { + if (!(_wllWallFlags[_levelBlockProperties[bl].walls[d ^ 2]] & 3) || (_levelBlockProperties[bl].flags & 7)) + return false; + bl = calcNewBlockPosition(bl, d); + } + + Item itm = 0; + if (_flags.gameID == GI_EOB1) { + switch (m->type - 4) { + case 0: + launchMagicObject(-1, 9, m->block, m->pos, m->dir); + snd_processEnvironmentalSoundEffect(31, m->block); + break; + case 10: + launchMagicObject(-1, _monsterDistAttType10[m->numRemoteAttacks], m->block, m->pos, m->dir); + snd_processEnvironmentalSoundEffect(_monsterDistAttSfx10[m->numRemoteAttacks], m->block); + break; + case 11: + itm = duplicateItem(60); + if (itm) { + if (launchObject(-1, itm, m->block, m->pos, m->dir, _items[itm].type)) + _items[itm].block = -1; + } + break; + case 12: + launchMagicObject(-1, 0, m->block, m->pos, m->dir); + snd_processEnvironmentalSoundEffect(85, m->block); + break; + case 13: + snd_processEnvironmentalSoundEffect(83, m->block); + _txt->printMessage(_monsterSpecAttStrings[1]); + for (int i = 0; i < 6; i++) + statusAttack(i, 4, _monsterSpecAttStrings[2], 1, 5, 9, 1); + break; + case 17: + d = rollDice(1, 4, -1); + if (d >= 3) { + for (int i = 0; i < 6; i++) { + if (!testCharacter(i, 3)) + continue; + _txt->printMessage(_monsterSpecAttStrings[0], -1, _characters[i].name); + inflictCharacterDamage(i, rollDice(2, 8, 1)); + } + snd_processEnvironmentalSoundEffect(108, m->block); + } else { + launchMagicObject(-1, _monsterDistAttType17[m->numRemoteAttacks], m->block, m->pos, m->dir); + snd_processEnvironmentalSoundEffect(_monsterDistAttSfx17[m->numRemoteAttacks], m->block); + } + break; + default: + break; + } + + } else { + int cw = 0; + if (p->remoteWeaponChangeMode == 1) { + cw = m->curRemoteWeapon++; + if (m->curRemoteWeapon == p->numRemoteWeapons) + m->curRemoteWeapon = 0; + } else if (p->remoteWeaponChangeMode == 2) { + cw = rollDice(1, p->numRemoteWeapons, -1); + } + + int s = p->remoteWeapons[cw]; + if (s >= 0) { + if (s < 20) { + monsterSpellCast(m, s); + } else if (s == 20) { + snd_processEnvironmentalSoundEffect(103, m->block); + _txt->printMessage(_monsterSpecAttStrings[0]); + for (int i = 0; i < 6; i++) + statusAttack(i, 4, _monsterSpecAttStrings[1], 1, 5, 9, 1); + } + } else { + Item itm = duplicateItem(-s); + if (itm) { + if (launchObject(-1, itm, m->block, m->pos, m->dir, _items[itm].type)) + _items[itm].block = -1; + } + } + } + + if (m->numRemoteAttacks != 255) + m->numRemoteAttacks--; + m->stepsTillRemoteAttack = 0; + return true; +} + +bool EobCoreEngine::updateMonsterTryCloseAttack(EobMonsterInPlay *m, int block) { + if (block == -1) + block = calcNewBlockPosition(m->block, m->dir); + + if (block != _currentBlock) + return false; + + int r = (m->pos == 4 || (_flags.gameID == GI_EOB2 && _monsterProps[m->type].u30 == 1)) ? 1 : _monsterCloseAttChkTable1[(m->dir << 2) + m->pos]; + + if (r) { + m->flags ^= 4; + if (!(m->flags & 4)) + return true; + + bool facing = (m->block == _visibleBlockIndex[13]); + + if (facing) { + disableSysTimer(2); + if (m->type == 4) + updateEnvironmentalSfx(_monsterProps[m->type].sound1); + m->curAttackFrame = -2; + _flashShapeTimer = 0; + drawScene(1); + m->curAttackFrame = -1; + if (m->type != 4) + updateEnvironmentalSfx(_monsterProps[m->type].sound1); + _flashShapeTimer = _system->getMillis() + 8 * _tickLength; + drawScene(1); + } else { + updateEnvironmentalSfx(_monsterProps[m->type].sound1); + } + + monsterCloseAttack(m); + + if (facing) { + m->curAttackFrame = 0; + m->animStep ^= 1; + _sceneUpdateRequired = 1; + enableSysTimer(2); + _flashShapeTimer = _system->getMillis() + 8 * _tickLength; + } + } else { + int b = m->block; + if ((_levelBlockProperties[b].flags & 7) == 1) { + m->pos = 4; + } else { + b = getNextMonsterPos(m, b); + if (b >= 0) + m->pos = b; + } + checkSceneUpdateNeed(m->block); + } + + return true; +} + +void EobCoreEngine::walkMonster(EobMonsterInPlay *m, int destBlock) { + if (++_monsterStepCounter > 10) { + _monsterStepCounter = 0; + _monsterStepMode ^= 1; + } + + const int8 *tbl = _monsterStepMode ? _monsterStepTable3 : _monsterStepTable2; + + int s = m->dir << 1; + int b = m->block; + int d = getNextMonsterDirection(b, destBlock); + if (d == -1) + return; + + if (m->flags & 8) { + if (_flags.gameID == GI_EOB1 ) { + d ^= 4; + } else if (--m->f_b <= 0) { + m->f_b = 0; + m->flags &= ~8; + } else { + d ^= 4; + } + } + + int d2 = (d - s) & 7; + + if (b + _monsterStepTable0[_flags.gameID == GI_EOB1 ? (d >> 1) : d] == destBlock) { + if (_flags.gameID == GI_EOB1 && !(d & 1)) { + if (d2 >= 5) { + s = m->dir - 1; + } else if (d2 != 0) { + s = m->dir + 1; + } + walkMonsterNextStep(m, -1, s & 3); + return; + } else if (_flags.gameID == GI_EOB2) { + if (d & 1) { + int e = _monsterStepTable1[((d - 1) << 1) + m->dir]; + if (e && !((_monsterProps[m->type].flags & 0x200) && (rollDice(1, 4) == 4))) { + if (walkMonsterNextStep(m, b + e, -1)) + return; + } + } else { + walkMonsterNextStep(m, -1, d >> 1); + return; + } + } + } + + if (d2) { + if (d2 >= 5) + s -= (1 + ((d & 1) ^ 1)); + else + s += (1 + ((d & 1) ^ 1)); + s &= 7; + } + + for (int i = 7; i > -1; i--) { + s = (s + tbl[i]) & 7; + uint16 b2 = (s & 1) ? 0 : calcNewBlockPosition(b, s >> 1); + if (!b2) + continue; + if (walkMonsterNextStep(m, b2, s >> 1)) + return; + } +} + +bool EobCoreEngine::walkMonsterNextStep(EobMonsterInPlay *m, int destBlock, int direction) { + EobMonsterProperty *p = &_monsterProps[m->type]; + int obl = m->block; + + if (destBlock != m->block && destBlock != -1) { + if (m->flags & 8) { + if (getBlockDistance(destBlock, _currentBlock) < getBlockDistance(m->block, _currentBlock)) + return false; + } + + if (destBlock == _currentBlock) + return false; + + if (direction == -1) + direction = m->dir; + + LevelBlockProperty *l = &_levelBlockProperties[destBlock]; + uint8 w = l->walls[direction ^ 2]; + + if (!(_wllWallFlags[w] & 4)) { + if (_flags.gameID == GI_EOB1 ||!(p->flags & 0x1000) || _wllShapeMap[w] != -1) + return false; + + if (_wllWallFlags[w] & 0x20) { + if (p->flags & 4 && m->type == 1) + l->walls[direction] = l->walls[direction ^ 2] = 72; + else + openDoor(destBlock); + } + + if (direction != -1) { + m->dir = direction; + checkSceneUpdateNeed(m->block); + } + return true; + } + + if ((l->flags & 7) && destBlock) { + int pos = getNextMonsterPos(m, destBlock); + if (pos == -1) + return false; + m->pos = pos; + } + + placeMonster(m, destBlock, direction); + direction = -1; + } + + if (direction != -1) + m->dir = direction; + + checkSceneUpdateNeed(obl); + if (!_partyResting && p->sound2) + snd_processEnvironmentalSoundEffect(p->sound2, m->block); + + return true; +} + +void EobCoreEngine::updateMonsterFollowPath(EobMonsterInPlay *m, int turnSteps) { + if (!walkMonsterNextStep(m, calcNewBlockPosition(m->block, m->dir), -1)) { + m->dir = (m->dir + turnSteps) & 3; + walkMonsterNextStep(m, -1, m->dir); + } +} + +void EobCoreEngine::updateMonstersStraying(EobMonsterInPlay *m, int a) { + if (m->f_9 >= 0) { + if (m->f_9 == 0) + updateMonsterFollowPath(m, -a); + + int8 d = (m->dir + a) & 3; + uint16 bl = calcNewBlockPosition(m->block, d); + uint8 flg = _wllWallFlags[_levelBlockProperties[bl].walls[_dscBlockMap[d]]] & 4; + + if (m->f_9 == 0) { + if (!flg) + m->f_9 = -1; + return; + } + + if (flg) { + walkMonsterNextStep(m, -1, d); + m->f_9 = -1; + return; + } + } + + if (walkMonsterNextStep(m, calcNewBlockPosition(m->block, m->dir), -1)) { + m->f_9 = 1; + } else { + walkMonsterNextStep(m, -1, (m->dir - a) & 3); + m->f_9 = 0; + } +} + +void EobCoreEngine::updateMonsters_mode710(EobMonsterInPlay *m) { + if (m->f_b) { + if (!--m->f_b) + m->mode = 0; + } +} + +void EobCoreEngine::setBlockMonsterDirection(int block, int dir) { + for (int i = 0; i < 30; i++) { + if (_monsters[i].block != block || _monsters[i].dir == dir) + continue; + _monsters[i].dir = dir; + _monsters[i].directionChanged == 1; + } +} + +} // End of namespace Kyra + +#endif // ENABLE_EOB diff --git a/engines/kyra/sprites_lol.cpp b/engines/kyra/sprites_lol.cpp index fbf4c7c5c2..9f7e05627b 100644 --- a/engines/kyra/sprites_lol.cpp +++ b/engines/kyra/sprites_lol.cpp @@ -59,7 +59,7 @@ void LoLEngine::loadMonsterShapes(const char *file, int monsterIndex, int animTy for (int i = 0; i < 4; i++) { for (int ii = 0; ii < 16; ii++) { - uint8 **of = &_monsterShapesEx[monsterIndex * 192 + i * 48 + ii * 3]; + uint8 **of = &_monsterDecorationShapes[monsterIndex * 192 + i * 48 + ii * 3]; int s = (i << 4) + ii + 17; of[0] = _screen->makeShapeCopy(p, s); of[1] = _screen->makeShapeCopy(p, s + 1); @@ -140,9 +140,9 @@ void LoLEngine::releaseMonsterShapes(int monsterIndex) { for (int i = 0; i < 192; i++) { int pos = (monsterIndex * 192) + i; - if (_monsterShapesEx[pos]) { - delete[] _monsterShapesEx[pos]; - _monsterShapesEx[pos] = 0; + if (_monsterDecorationShapes[pos]) { + delete[] _monsterDecorationShapes[pos]; + _monsterDecorationShapes[pos] = 0; } } } @@ -159,7 +159,7 @@ int LoLEngine::deleteMonstersFromBlock(int block) { continue; } - MonsterInPlay *m = &_monsters[i & 0x7fff]; + LolMonsterInPlay *m = &_monsters[i & 0x7fff]; cnt++; setMonsterMode(m, 14); @@ -173,7 +173,7 @@ int LoLEngine::deleteMonstersFromBlock(int block) { return cnt; } -void LoLEngine::setMonsterMode(MonsterInPlay *monster, int mode) { +void LoLEngine::setMonsterMode(LolMonsterInPlay *monster, int mode) { if (monster->mode == 13 && mode != 14) return; @@ -210,7 +210,7 @@ void LoLEngine::setMonsterMode(MonsterInPlay *monster, int mode) { } } -bool LoLEngine::updateMonsterAdjustBlocks(MonsterInPlay *monster) { +bool LoLEngine::updateMonsterAdjustBlocks(LolMonsterInPlay *monster) { static const uint8 dims[] = { 0, 13, 9, 3 }; if (monster->properties->flags & 8) return true; @@ -251,7 +251,7 @@ bool LoLEngine::updateMonsterAdjustBlocks(MonsterInPlay *monster) { return (fx1 >= fx2) ? false : true; } -void LoLEngine::placeMonster(MonsterInPlay *monster, uint16 x, uint16 y) { +void LoLEngine::placeMonster(LolMonsterInPlay *monster, uint16 x, uint16 y) { bool cont = true; int t = monster->block; if (monster->block) { @@ -329,7 +329,7 @@ int LoLEngine::calcMonsterDirection(uint16 x1, uint16 y1, uint16 x2, uint16 y2) return retVal[r]; } -void LoLEngine::setMonsterDirection(MonsterInPlay *monster, int dir) { +void LoLEngine::setMonsterDirection(LolMonsterInPlay *monster, int dir) { monster->direction = dir; if (!(dir & 1) || ((monster->direction - (monster->facing << 1)) >= 2)) @@ -338,7 +338,7 @@ void LoLEngine::setMonsterDirection(MonsterInPlay *monster, int dir) { checkSceneUpdateNeed(monster->block); } -void LoLEngine::monsterDropItems(MonsterInPlay *monster) { +void LoLEngine::monsterDropItems(LolMonsterInPlay *monster) { uint16 a = monster->assignedItems; while (a) { uint16 b = a; @@ -503,7 +503,7 @@ int LoLEngine::checkBlockForWallsAndSufficientSpace(int block, int x, int y, int uint16 b = _levelBlockProperties[block].assignedObjects; while (b & 0x8000) { - MonsterInPlay *monster = &_monsters[b & 0x7fff]; + LolMonsterInPlay *monster = &_monsters[b & 0x7fff]; if (monster->mode < 13) { int r = checkDrawObjectSpace(x, y, monster->x, monster->y); @@ -645,7 +645,7 @@ void LoLEngine::drawBlockObjects(int blockArrayIndex) { } void LoLEngine::drawMonster(uint16 id) { - MonsterInPlay *m = &_monsters[id]; + LolMonsterInPlay *m = &_monsters[id]; int16 flg = _monsterDirFlags[(_currentDirection << 2) + m->facing]; int curFrm = getMonsterCurFrame(m, flg & 0xffef); uint8 *shp = 0; @@ -670,7 +670,7 @@ void LoLEngine::drawMonster(uint16 id) { if (v == -1) break; - uint8 *shp2 = _monsterShapesEx[m->properties->shapeIndex * 192 + v * 48 + curFrm * 3]; + uint8 *shp2 = _monsterDecorationShapes[m->properties->shapeIndex * 192 + v * 48 + curFrm * 3]; if (!shp2) continue; @@ -721,7 +721,7 @@ void LoLEngine::drawMonster(uint16 id) { delete[] tbl; } -int LoLEngine::getMonsterCurFrame(MonsterInPlay *m, uint16 dirFlags) { +int LoLEngine::getMonsterCurFrame(LolMonsterInPlay *m, uint16 dirFlags) { int tmp = 0; switch (_monsterAnimType[m->properties->shapeIndex]) { case 0: @@ -901,7 +901,7 @@ void LoLEngine::drawDoor(uint8 *shape, uint8 *doorPalette, int index, int unk2, if (!shape) return; - uint8 c = _dscDoor1[(_currentDirection << 5) + unk2]; + uint8 c = _dscDoorY2[(_currentDirection << 5) + unk2]; int r = (c / 5) + 5 * _dscDimMap[index]; uint16 d = _dscShapeOvlIndex[r]; uint16 t = (index << 5) + c; @@ -1080,7 +1080,7 @@ int LoLEngine::calcDrawingLayerParameters(int x1, int y1, int &x2, int &y2, uint return l; } -void LoLEngine::updateMonster(MonsterInPlay *monster) { +void LoLEngine::updateMonster(LolMonsterInPlay *monster) { static const uint8 flags[] = { 1, 0, 1, 3, 3, 0, 0, 3, 4, 1, 0, 0, 4, 0, 0 }; if (monster->mode > 14) return; @@ -1228,7 +1228,7 @@ void LoLEngine::updateMonster(MonsterInPlay *monster) { monster->flags &= 0xffef; } -void LoLEngine::moveMonster(MonsterInPlay *monster) { +void LoLEngine::moveMonster(LolMonsterInPlay *monster) { static const int8 turnPos[] = { 0, 2, 6, 6, 0, 2, 4, 4, 2, 2, 4, 6, 0, 0, 4, 6, 0 }; if (monster->x != monster->destX || monster->y != monster->destY) { walkMonster(monster); @@ -1238,7 +1238,7 @@ void LoLEngine::moveMonster(MonsterInPlay *monster) { } } -void LoLEngine::walkMonster(MonsterInPlay *monster) { +void LoLEngine::walkMonster(LolMonsterInPlay *monster) { if (monster->properties->flags & 0x400) return; @@ -1253,7 +1253,7 @@ void LoLEngine::walkMonster(MonsterInPlay *monster) { } else { setMonsterDirection(monster, s); if (monster->numDistAttacks) { - if (getMonsterDistance(monster->block, _currentBlock) >= 2) { + if (getBlockDistance(monster->block, _currentBlock) >= 2) { if (checkForPossibleDistanceAttack(monster->block, monster->direction, 3, _currentBlock) != 5) { if (monster->distAttackTick) return; @@ -1269,7 +1269,7 @@ void LoLEngine::walkMonster(MonsterInPlay *monster) { placeMonster(monster, fx, fy); } -bool LoLEngine::chasePartyWithDistanceAttacks(MonsterInPlay *monster) { +bool LoLEngine::chasePartyWithDistanceAttacks(LolMonsterInPlay *monster) { if (!monster->numDistAttacks) return false; @@ -1295,7 +1295,7 @@ bool LoLEngine::chasePartyWithDistanceAttacks(MonsterInPlay *monster) { int flyingObject = monster->properties->distWeapons[s]; if (flyingObject & 0xc000) { - if (getMonsterDistance(monster->block, _currentBlock) > 1) { + if (getBlockDistance(monster->block, _currentBlock) > 1) { int type = flyingObject & 0x4000 ? 0 : 1; flyingObject = makeItem(flyingObject & 0x3fff, 0, 0); @@ -1305,7 +1305,7 @@ bool LoLEngine::chasePartyWithDistanceAttacks(MonsterInPlay *monster) { } } } else if (!(flyingObject & 0x2000)) { - if (getMonsterDistance(monster->block, _currentBlock) > 1) + if (getBlockDistance(monster->block, _currentBlock) > 1) return false; if (flyingObject == 1) { @@ -1326,7 +1326,7 @@ bool LoLEngine::chasePartyWithDistanceAttacks(MonsterInPlay *monster) { } else if (flyingObject == 3) { // shriek for (int i = 0; i < 30; i++) { - if (getMonsterDistance(monster->block, _monsters[i].block) < 7) + if (getBlockDistance(monster->block, _monsters[i].block) < 7) setMonsterMode(monster, 7); } _txt->printMessage(2, "%s", getLangString(0x401a)); @@ -1347,7 +1347,7 @@ bool LoLEngine::chasePartyWithDistanceAttacks(MonsterInPlay *monster) { return true; } -void LoLEngine::chasePartyWithCloseAttacks(MonsterInPlay *monster) { +void LoLEngine::chasePartyWithCloseAttacks(LolMonsterInPlay *monster) { if (!(monster->flags & 8)) { int dir = calcMonsterDirection(monster->x & 0xff00, monster->y & 0xff00, _partyPosX & 0xff00, _partyPosY & 0xff00); int x1 = _partyPosX; @@ -1389,7 +1389,7 @@ void LoLEngine::chasePartyWithCloseAttacks(MonsterInPlay *monster) { } } -int LoLEngine::walkMonsterCalcNextStep(MonsterInPlay *monster) { +int LoLEngine::walkMonsterCalcNextStep(LolMonsterInPlay *monster) { static const int8 walkMonsterTable1[] = { 7, -6, 5, -4, 3, -2, 1, 0 }; static const int8 walkMonsterTable2[] = { -7, 6, -5, 4, -3, 2, -1, 0 }; @@ -1445,23 +1445,8 @@ int LoLEngine::walkMonsterCalcNextStep(MonsterInPlay *monster) { return -1; } -int LoLEngine::getMonsterDistance(uint16 block1, uint16 block2) { - int b1x = block1 & 0x1f; - int b1y = block1 >> 5; - int b2x = block2 & 0x1f; - int b2y = block2 >> 5; - - uint8 dy = ABS(b2y - b1y); - uint8 dx = ABS(b2x - b1x); - - if (dx > dy) - SWAP(dx, dy); - - return (dx >> 1) + dy; -} - int LoLEngine::checkForPossibleDistanceAttack(uint16 monsterBlock, int direction, int distance, uint16 curBlock) { - int mdist = getMonsterDistance(curBlock, monsterBlock); + int mdist = getBlockDistance(curBlock, monsterBlock); if (mdist > distance) return 5; @@ -1494,7 +1479,7 @@ int LoLEngine::checkForPossibleDistanceAttack(uint16 monsterBlock, int direction return 5; } -int LoLEngine::walkMonsterCheckDest(int x, int y, MonsterInPlay *monster, int unk) { +int LoLEngine::walkMonsterCheckDest(int x, int y, LolMonsterInPlay *monster, int unk) { uint8 m = monster->mode; monster->mode = 15; @@ -1512,7 +1497,7 @@ void LoLEngine::getNextStepCoords(int16 srcX, int16 srcY, int &newX, int &newY, newY = (srcY + shiftTableY[direction]) & 0x1fff; } -void LoLEngine::rearrangeAttackingMonster(MonsterInPlay *monster) { +void LoLEngine::rearrangeAttackingMonster(LolMonsterInPlay *monster) { int t = (monster->direction >> 1); uint16 mx = monster->x; uint16 my = monster->y; @@ -1579,7 +1564,7 @@ void LoLEngine::rearrangeAttackingMonster(MonsterInPlay *monster) { placeMonster(monster, mx, my); } -void LoLEngine::moveStrayingMonster(MonsterInPlay *monster) { +void LoLEngine::moveStrayingMonster(LolMonsterInPlay *monster) { int x = 0; int y = 0; @@ -1616,13 +1601,13 @@ void LoLEngine::moveStrayingMonster(MonsterInPlay *monster) { } } -void LoLEngine::killMonster(MonsterInPlay *monster) { +void LoLEngine::killMonster(LolMonsterInPlay *monster) { setMonsterMode(monster, 14); monsterDropItems(monster); checkSceneUpdateNeed(monster->block); uint8 w = _levelBlockProperties[monster->block].walls[0]; - uint8 f = _levelBlockProperties[monster->block].flags; + uint16 f = _levelBlockProperties[monster->block].flags; if (_wllVmpMap[w] == 0 && _wllShapeMap[w] == 0 && !(f & 0x40) && !(monster->properties->flags & 0x1000)) _levelBlockProperties[monster->block].flags |= 0x80; diff --git a/engines/kyra/staticres.cpp b/engines/kyra/staticres.cpp index e03369f700..acc61353f4 100644 --- a/engines/kyra/staticres.cpp +++ b/engines/kyra/staticres.cpp @@ -75,7 +75,9 @@ const IndexTable iGameTable[] = { { GI_KYRA1, 0 }, { GI_KYRA2, 1 }, { GI_KYRA3, 2 }, - { GI_LOL, 3 }, + { GI_EOB1, 3 }, + { GI_EOB2, 4 }, + { GI_LOL, 5 }, { -1, -1 } }; @@ -257,6 +259,12 @@ bool StaticResource::init() { { kLolButtonData, proc(loadButtonDefs), proc(freeButtonDefs) }, #endif // ENABLE_LOL +#ifdef ENABLE_EOB + { kEob2SequenceData, proc(loadEob2SeqData), proc(freeEob2SeqData) }, + { kEob2ShapeData, proc(loadEob2ShapeData), proc(freeEob2ShapeData) }, + { kEobNpcData, proc(loadEobNpcData), proc(freeEobNpcData) }, +#endif // ENABLE_EOB + { 0, 0, 0 } }; #undef proc @@ -503,9 +511,9 @@ bool StaticResource::loadHofSequenceData(Common::SeekableReadStream &stream, voi tmp_s[i].flags = stream.readUint16BE(); tmp_s[i].wsaFile = new char[14]; - stream.read(const_cast<char *>(tmp_s[i].wsaFile), 14); + stream.read(const_cast<char*>(tmp_s[i].wsaFile), 14); tmp_s[i].cpsFile = new char[14]; - stream.read(const_cast<char *>(tmp_s[i].cpsFile), 14); + stream.read(const_cast<char*>(tmp_s[i].cpsFile), 14); tmp_s[i].startupCommand = stream.readByte(); tmp_s[i].finalCommand = stream.readByte(); tmp_s[i].stringIndex1 = stream.readUint16BE(); @@ -529,7 +537,7 @@ bool StaticResource::loadHofSequenceData(Common::SeekableReadStream &stream, voi tmp_n[i].flags = stream.readUint16BE(); tmp_n[i].wsaFile = new char[14]; - stream.read(const_cast<char *>(tmp_n[i].wsaFile), 14); + stream.read(const_cast<char*>(tmp_n[i].wsaFile), 14); tmp_n[i].startframe = stream.readUint16BE(); tmp_n[i].endFrame = stream.readUint16BE(); tmp_n[i].frameDelay = stream.readUint16BE(); diff --git a/engines/kyra/staticres_eob.cpp b/engines/kyra/staticres_eob.cpp new file mode 100644 index 0000000000..88bfbd8f08 --- /dev/null +++ b/engines/kyra/staticres_eob.cpp @@ -0,0 +1,1062 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * + */ + +#include "kyra/eob1.h" +#include "kyra/eob2.h" +#include "kyra/resource.h" + + +namespace Kyra { + + +#if defined(ENABLE_EOB) || defined(ENABLE_LOL) +const uint16 *StaticResource::loadRawDataBe16(int id, int &entries) { + return (const uint16 *)getData(id, kLolRawDataBe16, entries); +} + +const uint32 *StaticResource::loadRawDataBe32(int id, int &entries) { + return (const uint32 *)getData(id, kLolRawDataBe32, entries); +} +#endif // defined(ENABLE_EOB) || defined(ENABLE_LOL) + +#ifdef ENABLE_EOB +const EobSequenceStep *StaticResource::loadEob2SeqData(int id, int &entries) { + return (const EobSequenceStep *)getData(id, kEob2SequenceData, entries); +} + +const EobShapeDef *StaticResource::loadEob2ShapeData(int id, int &entries) { + return (const EobShapeDef *)getData(id, kEob2ShapeData, entries); +} + +const EobCharacter *StaticResource::loadEobNpcData(int id, int &entries) { + return (const EobCharacter *)getData(id, kEobNpcData, entries); +} +#endif // ENABLE_EOB + +#if defined(ENABLE_EOB) || defined(ENABLE_LOL) +bool StaticResource::loadRawDataBe16(Common::SeekableReadStream &stream, void *&ptr, int &size) { + size = stream.size() >> 1; + + uint16 *r = new uint16[size]; + + for (int i = 0; i < size; i++) + r[i] = stream.readUint16BE(); + + ptr = r; + return true; +} + +bool StaticResource::loadRawDataBe32(Common::SeekableReadStream &stream, void *&ptr, int &size) { + size = stream.size() >> 2; + + uint32 *r = new uint32[size]; + + for (int i = 0; i < size; i++) + r[i] = stream.readUint32BE(); + + ptr = r; + return true; +} +#endif // defined(ENABLE_EOB) || defined(ENABLE_LOL) + +#ifdef ENABLE_EOB +bool StaticResource::loadEob2SeqData(Common::SeekableReadStream &stream, void *&ptr, int &size) { + size = stream.size() / 11; + + EobSequenceStep *s = new EobSequenceStep[size]; + + for (int i = 0; i < size; i++) { + s[i].command = stream.readByte(); + s[i].obj = stream.readByte(); + s[i].x1 = stream.readSint16BE(); + s[i].y1 = stream.readByte(); + s[i].delay = stream.readByte(); + s[i].pal = stream.readByte(); + s[i].x2 = stream.readByte(); + s[i].y2 = stream.readByte(); + s[i].w = stream.readByte(); + s[i].h = stream.readByte(); + } + + ptr = s; + return true; +} + +bool StaticResource::loadEob2ShapeData(Common::SeekableReadStream &stream, void *&ptr, int &size) { + size = stream.size() / 6; + + EobShapeDef *s = new EobShapeDef[size]; + + for (int i = 0; i < size; i++) { + s[i].index = stream.readSint16BE(); + s[i].x = stream.readByte(); + s[i].y = stream.readByte(); + s[i].w = stream.readByte(); + s[i].h = stream.readByte(); + } + + ptr = s; + return true; +} + +bool StaticResource::loadEobNpcData(Common::SeekableReadStream &stream, void *&ptr, int &size) { + size = stream.readUint16BE(); + + EobCharacter *e = new EobCharacter[size]; + memset(e, 0, size * sizeof(EobCharacter)); + EobCharacter *s = e; + + for (int i = 0; i < size; i++, s++) { + s->id = stream.readByte(); + s->flags = stream.readByte(); + stream.read(s->name, 11); + s->strengthCur = stream.readSByte(); + s->strengthMax = stream.readSByte(); + s->strengthExtCur = stream.readSByte(); + s->strengthExtMax = stream.readSByte(); + s->intelligenceCur = stream.readSByte(); + s->intelligenceMax = stream.readSByte(); + s->wisdomCur = stream.readSByte(); + s->wisdomMax = stream.readSByte(); + s->dexterityCur = stream.readSByte(); + s->dexterityMax = stream.readSByte(); + s->constitutionCur = stream.readSByte(); + s->constitutionMax = stream.readSByte(); + s->charismaCur = stream.readSByte(); + s->charismaMax = stream.readSByte(); + s->hitPointsCur = stream.readSint16BE(); + s->hitPointsMax = stream.readSint16BE(); + s->armorClass = stream.readSByte(); + s->disabledSlots = stream.readByte(); + s->raceSex = stream.readByte(); + s->cClass = stream.readByte(); + s->alignment = stream.readByte(); + s->portrait = stream.readByte(); + s->food = stream.readByte(); + stream.read(s->level, 3); + s->experience[0] = stream.readUint32BE(); + s->experience[1] = stream.readUint32BE(); + s->experience[2] = stream.readUint32BE(); + s->mageSpellsAvailabilityFlags = stream.readUint32BE(); + for (int ii = 0; ii < 27; ii++) + s->inventory[i] = stream.readUint16BE(); + } + + ptr = e; + return true; +} +#endif // ENABLE_EOB + +#if defined(ENABLE_EOB) || defined(ENABLE_LOL) +void StaticResource::freeRawDataBe16(void *&ptr, int &size) { + uint16 *data = (uint16 *)ptr; + delete[] data; + ptr = 0; + size = 0; +} + +void StaticResource::freeRawDataBe32(void *&ptr, int &size) { + uint32 *data = (uint32 *)ptr; + delete[] data; + ptr = 0; + size = 0; +} +#endif // defined(ENABLE_EOB) || defined(ENABLE_LOL) + +#ifdef ENABLE_EOB +void StaticResource::freeEob2SeqData(void *&ptr, int &size) { + EobSequenceStep *d = (EobSequenceStep *)ptr; + delete[] d; + ptr = 0; + size = 0; +} + +void StaticResource::freeEob2ShapeData(void *&ptr, int &size) { + EobShapeDef *d = (EobShapeDef *)ptr; + delete[] d; + ptr = 0; + size = 0; +} + +void StaticResource::freeEobNpcData(void *&ptr, int &size) { + EobCharacter *d = (EobCharacter *)ptr; + delete[] d; + ptr = 0; + size = 0; +} + +const ScreenDim Screen_Eob::_screenDimTable[] = { + { 0x00, 0x00, 0x28, 0xC8, 0x0F, 0x0C, 0x00, 0x00 }, + { 0x08, 0x48, 0x18, 0x38, 0x0E, 0x0C, 0x00, 0x00 }, + { 0x13, 0x40, 0x14, 0x80, 0x06, 0x0C, 0x00, 0x00 }, + { 0x1D, 0x78, 0x08, 0x40, 0x0F, 0x0D, 0x00, 0x00 }, + { 0x02, 0x18, 0x14, 0x78, 0x0F, 0x02, 0x03, 0x00 }, + { 0x00, 0x00, 0x16, 0x78, 0x0F, 0x0D, 0x00, 0x00 }, + { 0x0A, 0x6C, 0x15, 0x28, 0x0F, 0x00, 0x00, 0x00 }, + { 0x01, 0xB4, 0x22, 0x12, 0x0F, 0x0C, 0x00, 0x00 }, + { 0x02, 0x18, 0x14, 0x00, 0x0F, 0x02, 0x03, 0x00 }, + { 0x01, 0x7D, 0x26, 0x40, 0x0F, 0x00, 0x03, 0x00 }, + { 0x00, 0x00, 0x16, 0x90, 0x0F, 0x02, 0x00, 0x00 }, + { 0x01, 0x14, 0x14, 0x38, 0x0F, 0x02, 0x00, 0x00 }, + { 0x01, 0x04, 0x14, 0x9C, 0x0F, 0x02, 0x00, 0x00 }, + { 0x01, 0x19, 0x26, 0x64, 0x0F, 0x02, 0x00, 0x00 }, + { 0x01, 0x14, 0x14, 0x58, 0x0F, 0x02, 0x00, 0x00 }, + { 0x02, 0x06, 0x23, 0x78, 0x0F, 0x02, 0x00, 0x00 }, + { 0x09, 0x14, 0x16, 0x38, 0x0F, 0x02, 0x00, 0x00 }, + { 0x01, 0x96, 0x26, 0x31, 0x0F, 0x00, 0x00, 0x00 }, + { 0x01, 0x08, 0x26, 0x80, 0x0C, 0x0F, 0x00, 0x00 }, + { 0x01, 0x10, 0x26, 0x14, 0x00, 0x0F, 0x06, 0x00 }, + { 0x00, 0x10, 0x10, 0x0C, 0x00, 0x0F, 0x06, 0x00 }, + { 0x00, 0x10, 0x17, 0x00, 0x00, 0x0F, 0x06, 0x00 }, + { 0x00, 0x10, 0x10, 0x00, 0x00, 0x0F, 0x06, 0x00 }, + { 0x00, 0x10, 0x07, 0x04, 0x00, 0x0F, 0x06, 0x00 }, + { 0x00, 0x00, 0x11, 0x05, 0x00, 0x0F, 0x06, 0x00 }, + { 0x00, 0x00, 0x15, 0x05, 0x00, 0x0F, 0x06, 0x00 }, + { 0x00, 0x00, 0x11, 0x08, 0x00, 0x0F, 0x06, 0x00 }, + { 0x00, 0x00, 0x15, 0x03, 0x00, 0x0F, 0x06, 0x00 }, + { 0x0A, 0xA8, 0x15, 0x18, 0x0F, 0x0C, 0x00, 0x00 } +}; + +const int Screen_Eob::_screenDimTableCount = ARRAYSIZE(Screen_Eob::_screenDimTable); +#endif + +#if defined(ENABLE_EOB) || defined(ENABLE_LOL) +const uint8 LolEobBaseEngine::_dropItemDirIndex[] = { 0, 1, 2, 3, 1, 3, 0, 2, 3, 2, 1, 0, 2, 0, 3, 1 }; + +void LolEobBaseEngine::initStaticResource() { + int temp; + _dscShapeX = (const int16 *)_staticres->loadRawDataBe16(kLolEobCommonDscX, temp); + _dscShapeIndex = (const int8 *)_staticres->loadRawData(kLolEobCommonDscShapeIndex, temp); + _dscTileIndex = _staticres->loadRawData(kLolEobCommonDscTileIndex, temp); + _dscDim1 = (const int8 *)_staticres->loadRawData(kLolEobCommonDscDimData1, temp); + _dscDim2 = (const int8 *)_staticres->loadRawData(kLolEobCommonDscDimData2, temp); + _dscUnk2 = _staticres->loadRawData(kLolEobCommonDscUnk2, temp); + _dscBlockMap = _staticres->loadRawData(kLolEobCommonDscBlockMap, temp); + _dscBlockIndex = (const int8 *)_staticres->loadRawData(kLolEobCommonDscBlockIndex, temp); + _dscDimMap = _staticres->loadRawData(kLolEobCommonDscDimMap, temp); + _dscDoorShpIndex = _staticres->loadRawData(kLolEobCommonDscDoorShapeIndex, temp); + _dscDoorY2 = _staticres->loadRawData(kLolEobCommonDscDoorY2, temp); + _moreStrings = _staticres->loadStrings(kLolEobCommonMoreStrings, temp); +} + +#endif // (ENABLE_EOB || ENABLE_LOL) +#ifdef ENABLE_EOB + +const uint8 EobCoreEngine::_hpIncrPerLevel[] = { 10, 4, 8, 6, 10, 10, 9, 10, 9, 10, 9, 9, 3, 1, 2, 2, 3, 3 }; + +const uint8 EobCoreEngine::_numLevelsPerClass[] = { 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 2, 2, 3, 2, 2 }; + +const int8 EobCoreEngine::_classHpIncreaseType[] = { + 0, -1, -1, 5, -1, -1, 4, -1, -1, 1, -1, -1, 2, -1, -1, 3, -1, -1, 0, + 2, -1, 0, 3, -1, 0, 1, -1, 0, 1, 3, 3, 1, -1, 2, 3, -1, 0, 2, 1, 5, + 2, -1, 2, 1, -1 +}; + +const int16 EobCoreEngine::_hpConstModifiers[] = { -1, -3, -2, -2, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 6, 6, 7, 7 }; + +const uint8 EobCoreEngine::_charClassModUnk[] = { + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x02, + 0x00, 0x00, 0x02 +}; + +const uint8 EobCoreEngine::_teleporterShapeDefs[] = { + 0x0C, 0x58, 0x02, 0x0E, + 0x0C, 0x67, 0x01, 0x07, + 0x0C, 0x6F, 0x01, 0x07, + 0x0C, 0x77, 0x01, 0x05, + 0x0C, 0x7D, 0x01, 0x05, + 0x0C, 0x83, 0x01, 0x03 +}; + +const uint8 EobCoreEngine::_wallOfForceShapeDefs[] = { + 0x00, 0x00, 0x04, 0x08, + 0x00, 0x08, 0x04, 0x08, + 0x04, 0x00, 0x04, 0x08, + 0x04, 0x08, 0x04, 0x08, + 0x08, 0x00, 0x05, 0x10, + 0x0C, 0x00, 0x05, 0x10 +}; + +const int16 EobCoreEngine::_buttonList1[] = { + 58, 0, 1, 2, 3, 90, 91, 4, 5, 6, 7, 8, 9, 10, 11, 12, 78, 79, 13, 14, 15, 16, + 80, 81, 17, 18, 19, 20, 82, 83, 49, 50, 51, 52, 53, 54, 56, 57, -1 +}; + +const int16 EobCoreEngine::_buttonList2[] = { + 58, 61, 62, 63, 64, 65, 93, 94, 66, 67, 68, 69, 70, 71, 76, 77, 88, 0, 1, 2, 3, + 90, 91, 4, 5, 6, 7, 8, 9, 10, 11, 12, 78, 79, 13, 14, 15, 16, 80, 81, 17, 18, + 19, 20, 82, 83, 49, 50, 51, 52, 53, 54, 56, 57, -1 +}; + +const int16 EobCoreEngine::_buttonList3[] = { + 58, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 84, 85, 46, 47, 48, 60, 59, 92, 4, 5, 6, 7, 8, 49, 50, + 51, 52, 53, 54, 56, 57, -1 +}; + +const int16 EobCoreEngine::_buttonList4[] = { + 58, 47, 48, 60, 59, 92, 4, 5, 6, 7, 8, 49, 50, 51, 52, 53, 54, 56, 57, -1 +}; + +const int16 EobCoreEngine::_buttonList5[] = { + 58, 61, 62, 63, 64, 65, 93, 66, 67, 68, 69, 70, 71, 88, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 84, + 85, 46, 47, 48, 60, 59, 92, 4, 5, 6, 7, 8, 49, 50, 51, 52, 53, 54, 56, 57, -1 +}; + +const int16 EobCoreEngine::_buttonList6[] = { + 58, 61, 62, 63, 64, 65, 93, 66, 67, 68, 69, 70, 71, 88, 46, 47, 48, 60, 59, 92, + 4, 5, 6, 7, 8, 49, 50, 51, 52, 53, 54, 56, 57, -1 +}; + +const int16 EobCoreEngine::_buttonList7[] = { + 17, 18, 19, 20, 82, 83, 55, -1 +}; + +const int16 EobCoreEngine::_buttonList8[] = { + 72, 73, 74, 75, 86, 87, 89, -1 +}; + +const uint8 EobCoreEngine::_clock2Timers[] = { + 0x00, 0x01, 0x20, 0x21, 0x22, 0x22, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, + 0x04, 0x05, 0x06, 0x07 +}; + +const uint8 EobCoreEngine::_numClock2Timers = ARRAYSIZE(EobCoreEngine::_clock2Timers); + +void EobCoreEngine::initStaticResource() { + int temp; + _chargenStatStrings = _staticres->loadStrings(kEobBaseChargenStatStrings, temp); + _chargenRaceSexStrings = _staticres->loadStrings(kEobBaseChargenRaceSexStrings, temp); + _chargenClassStrings = _staticres->loadStrings(kEobBaseChargenClassStrings, temp); + _chargenAlignmentStrings = _staticres->loadStrings(kEobBaseChargenAlignmentStrings, temp); + + _pryDoorStrings = _staticres->loadStrings(kEobBasePryDoorStrings, temp); + _warningStrings = _staticres->loadStrings(kEobBaseWarningStrings, temp); + _itemSuffixStrings = _staticres->loadStrings(kEobBaseItemSuffixStrings, temp); + _itemExtraStrings = _staticres->loadStrings(kEobBaseItemExtraStrings, temp); + _takenStrings = _staticres->loadStrings(kEobBaseTakenStrings, temp); + _potionEffectStrings = _staticres->loadStrings(kEobBasePotionEffectStrings, temp); + + _yesNoStrings = _staticres->loadStrings(kEobBaseYesNoStrings, temp); + _npcMaxStrings = _staticres->loadStrings(kEobBaseNpcMaxStrings, temp); + _okStrings = _staticres->loadStrings(_flags.gameID == GI_EOB2 ? kEobBaseOkStrings : kLolEobCommonMoreStrings, temp); + _npcJoinStrings = _staticres->loadStrings(kEobBaseNpcJoinStrings, temp); + _cancelStrings = _staticres->loadStrings(kEobBaseCancelStrings, temp); + _abortStrings = _staticres->loadStrings(_flags.gameID == GI_EOB2 ? kEobBaseAbortStrings : kEobBaseCancelStrings, temp); + + _characterGuiStringsHp = _staticres->loadStrings(kEobBaseCharGuiStringsHp, temp); + _characterGuiStringsWp = _staticres->loadStrings(_flags.gameID == GI_EOB2 ? kEobBaseCharGuiStringsWp2 : kEobBaseCharGuiStringsWp1, temp); + _characterGuiStringsWr = _staticres->loadStrings(kEobBaseCharGuiStringsWr, temp); + _characterGuiStringsSt = _staticres->loadStrings(_flags.gameID == GI_EOB2 ? kEobBaseCharGuiStringsSt2 : kEobBaseCharGuiStringsSt1, temp); + _characterGuiStringsIn = _staticres->loadStrings(kEobBaseCharGuiStringsIn, temp); + + _characterStatusStrings7 = _staticres->loadStrings(kEobBaseCharStatusStrings7, temp); + _characterStatusStrings8 = _staticres->loadStrings(_flags.gameID == GI_EOB2 ? kEobBaseCharStatusStrings82 : kEobBaseCharStatusStrings81, temp); + _characterStatusStrings9 = _staticres->loadStrings(kEobBaseCharStatusStrings9, temp); + _characterStatusStrings12 = _staticres->loadStrings(kEobBaseCharStatusStrings12, temp); + _characterStatusStrings13 = _staticres->loadStrings(_flags.gameID == GI_EOB2 ? kEobBaseCharStatusStrings132 : kEobBaseCharStatusStrings131, temp); + + _levelGainStrings = _staticres->loadStrings(kEobBaseLevelGainStrings, temp); + _expRequirementTables[0] = _staticres->loadRawDataBe32(kEobBaseExperienceTable0, temp); + _expRequirementTables[1] = _staticres->loadRawDataBe32(kEobBaseExperienceTable1, temp); + _expRequirementTables[2] = _staticres->loadRawDataBe32(kEobBaseExperienceTable2, temp); + _expRequirementTables[3] = _staticres->loadRawDataBe32(kEobBaseExperienceTable3, temp); + _expRequirementTables[4] = _staticres->loadRawDataBe32(kEobBaseExperienceTable4, temp); + _expRequirementTables[5] = _staticres->loadRawDataBe32(kEobBaseExperienceTable4, temp); + + _classModifierFlags = _staticres->loadRawData(kEobBaseClassModifierFlags, temp); + + _constModTables[0] = _constModTables[4] = _constModTables[5] = _staticres->loadRawData(kEobBaseConstModTable1, temp); + _constModTables[1] = _staticres->loadRawData(kEobBaseConstModTable2, temp); + _constModTables[2] = _staticres->loadRawData(kEobBaseConstModTable3, temp); + _constModTables[3] = _staticres->loadRawData(kEobBaseConstModTable4, temp); + _constModLevelIndex = _staticres->loadRawData(kEobBaseConstModLvlIndex, temp); + _constModDiv = _staticres->loadRawData(kEobBaseConstModDiv, temp); + _constModExt = _staticres->loadRawData(kEobBaseConstModExt, temp); + + _encodeMonsterShpTable = _staticres->loadRawDataBe16(kEobBaseEncodeMonsterDefs, temp); + _npcPreset = _staticres->loadEobNpcData(kEobBaseNpcPresets, temp); + + _teleporterShapeCoords = _staticres->loadRawData(kEobBaseDscTelptrShpCoords, temp); + + _monsterStepTable0 = (int8*) _staticres->loadRawData(_flags.gameID == GI_EOB2 ? kEobBaseMonsterStepTable02 : kEobBaseMonsterStepTable01, temp); + _monsterStepTable1 = (int8*)_staticres->loadRawData(kEobBaseMonsterStepTable1, temp); + _monsterStepTable2 = (int8*)_staticres->loadRawData(kEobBaseMonsterStepTable2, temp); + _monsterStepTable3 = (int8*)_staticres->loadRawData(kEobBaseMonsterStepTable3, temp); + _monsterCloseAttPosTable1 = _staticres->loadRawData(kEobBaseMonsterCloseAttPosTable1, temp); + _monsterCloseAttPosTable2 = _staticres->loadRawData(_flags.gameID == GI_EOB2 ? kEobBaseMonsterCloseAttPosTable22 : kEobBaseMonsterCloseAttPosTable21, temp); + _monsterCloseAttUnkTable = (int8*)_staticres->loadRawData(kEobBaseMonsterCloseAttUnkTable, temp); + _monsterCloseAttChkTable1 = _staticres->loadRawData(kEobBaseMonsterCloseAttChkTable1, temp); + _monsterCloseAttChkTable2 = _staticres->loadRawData(kEobBaseMonsterCloseAttChkTable2, temp); + _monsterCloseAttDstTable1 = _staticres->loadRawData(kEobBaseMonsterCloseAttDstTable1, temp); + _monsterCloseAttDstTable2 = _staticres->loadRawData(kEobBaseMonsterCloseAttDstTable2, temp); + + _monsterProximityTable = _staticres->loadRawData(kEobBaseMonsterProximityTable, temp); + _findBlockMonstersTable = _staticres->loadRawData(kEobBaseFindBlockMonstersTable, temp); + _monsterDirChangeTable = (const int8*)_staticres->loadRawData(kEobBaseMonsterDirChangeTable, temp); + _monsterSpecAttStrings = _staticres->loadStrings(kEobBaseMonsterDistAttStrings, temp); + + _monsterFrmOffsTable1 = (const int8*)_staticres->loadRawData(kEobBaseDscMonsterFrmOffsTbl1, temp); + _monsterFrmOffsTable2 = (const int8*)_staticres->loadRawData(kEobBaseDscMonsterFrmOffsTbl2, temp); + + _inventorySlotsX = _staticres->loadRawDataBe16(kEobBaseInvSlotX, temp); + _inventorySlotsY = _staticres->loadRawData(kEobBaseInvSlotY, temp); + _slotValidationFlags = _staticres->loadRawDataBe16(kEobBaseSlotValidationFlags, temp); + + _drawObjPosIndex = _staticres->loadRawData(kEobBaseDrawObjPosIndex, temp); + _flightObjFlipIndex = _staticres->loadRawData(kEobBaseFlightObjFlipIndex, temp); + _flightObjShpMap = (const int8*)_staticres->loadRawData(kEobBaseFlightObjShpMap, temp); + _flightObjSclIndex = (const int8*)_staticres->loadRawData(kEobBaseFlightObjSclIndex, temp); + + _wllFlagPreset = _staticres->loadRawData(kEobBaseWllFlagPreset, _wllFlagPresetSize); + _dscShapeCoords = (const int16*)_staticres->loadRawDataBe16(kEobBaseDscShapeCoords, temp); + + _dscDoorScaleOffs = _staticres->loadRawData(kEobBaseDscDoorScaleOffs, temp); + _dscDoorScaleMult1 = _staticres->loadRawData(kEobBaseDscDoorScaleMult1, temp); + _dscDoorScaleMult2 = _staticres->loadRawData(kEobBaseDscDoorScaleMult2, temp); + _dscDoorScaleMult3 = _staticres->loadRawData(kEobBaseDscDoorScaleMult3, temp); + _dscDoorY1 = _staticres->loadRawData(kEobBaseDscDoorY1, temp); + + _dscItemPosIndex= _staticres->loadRawData(kEobBaseDscItemPosIndex, temp); + _dscItemShpX = (const int16*)_staticres->loadRawDataBe16(kEobBaseDscItemShpX, temp); + _dscItemScaleIndex = _staticres->loadRawData(kEobBaseDscItemScaleIndex, temp); + _dscItemTileIndex = _staticres->loadRawData(kEobBaseDscItemTileIndex, temp); + _dscItemShapeMap = _staticres->loadRawData(kEobBaseDscItemShapeMap, temp); + + _bookNumbers = _staticres->loadStrings(kEobBaseBookNumbers, temp); + _mageSpellList = _staticres->loadStrings(kEobBaseMageSpellsList, _mageSpellListSize); + _clericSpellList = _staticres->loadStrings(kEobBaseClericSpellsList, temp); + _spellNames = _staticres->loadStrings(kEobBaseSpellNames, temp); + + _magicStrings1 = _staticres->loadStrings(kEobBaseMagicStrings1, temp); + _magicStrings2 = _staticres->loadStrings(kEobBaseMagicStrings2, temp); + _magicStrings3 = _staticres->loadStrings(kEobBaseMagicStrings3, temp); + _magicStrings4 = _staticres->loadStrings(kEobBaseMagicStrings4, temp); + _magicStrings5 = _staticres->loadStrings(kEobBaseMagicStrings5, temp); + _magicStrings6 = _staticres->loadStrings(kEobBaseMagicStrings6, temp); + _magicStrings7 = _staticres->loadStrings(kEobBaseMagicStrings7, temp); + _magicStrings8 = _staticres->loadStrings(kEobBaseMagicStrings8, temp); + + _sparkEffectDefSteps = _staticres->loadRawData(kEobBaseSparkDefSteps, temp); + _sparkEffectDefSubSteps = _staticres->loadRawData(kEobBaseSparkDefSubSteps, temp); + _sparkEffectDefShift = _staticres->loadRawData(kEobBaseSparkDefShift, temp); + _sparkEffectDefAdd = _staticres->loadRawData(kEobBaseSparkDefAdd, temp); + _sparkEffectDefX = _staticres->loadRawData(kEobBaseSparkDefX, temp); + _sparkEffectDefY = _staticres->loadRawData(kEobBaseSparkDefY, temp); + _sparkEffectOfFlags1 = _staticres->loadRawDataBe32(kEobBaseSparkOfFlags1, temp); + _sparkEffectOfFlags2 = _staticres->loadRawDataBe32(kEobBaseSparkOfFlags2, temp); + _sparkEffectOfShift = _staticres->loadRawData(kEobBaseSparkOfShift, temp); + _sparkEffectOfX = _staticres->loadRawData(kEobBaseSparkOfX, temp); + _sparkEffectOfY = _staticres->loadRawData(kEobBaseSparkOfY, temp); + _magicFlightObjectProperties = _staticres->loadRawData(kEobBaseMagicFlightProps, temp); +} + +void EobCoreEngine::initButtonData() { + #define EOB_CB(x) BUTTON_FUNCTOR(EobCoreEngine, this, &EobCoreEngine::x) + static const EobGuiButtonDef buttonDefs[] = { + { 112, 0, 0x1100, 184, 2, 63, 50, EOB_CB(clickedCharPortraitDefault), 0 }, + { 113, 0, 0x1100, 256, 2, 63, 50, EOB_CB(clickedCharPortraitDefault), 1 }, + { 114, 0, 0x1100, 184, 54, 63, 50, EOB_CB(clickedCharPortraitDefault), 2 }, + { 115, 0, 0x1100, 256, 54, 63, 50, EOB_CB(clickedCharPortraitDefault), 3 }, + { 48, 110, 0x1100, 289, 177, 31, 21, EOB_CB(clickedCamp), 0 }, + { 0, 0, 0x1100, 0, 102, 88, 18, EOB_CB(clickedSceneDropPickupItem), 0 }, + { 0, 0, 0x1100, 89, 102, 88, 18, EOB_CB(clickedSceneDropPickupItem), 1 }, + { 0, 0, 0x1100, 0, 72, 88, 29, EOB_CB(clickedSceneDropPickupItem), 2 }, + { 0, 0, 0x1100, 89, 72, 88, 29, EOB_CB(clickedSceneDropPickupItem), 3 }, + { 24, 0, 0x1100, 184, 10, 33, 33, EOB_CB(clickedCharPortrait2), 0 }, + { 0, 0, 0x1100, 256, 10, 33, 33, EOB_CB(clickedCharPortrait2), 1 }, + { 0, 0, 0x1100, 184, 62, 33, 33, EOB_CB(clickedCharPortrait2), 2 }, + { 0, 0, 0x1100, 256, 62, 33, 33, EOB_CB(clickedCharPortrait2), 3 }, + { 0, 0, 0x1100, 216, 10, 31, 33, EOB_CB(clickedWeaponSlot), 0 }, + { 0, 0, 0x1100, 288, 10, 31, 33, EOB_CB(clickedWeaponSlot), 1 }, + { 0, 0, 0x1100, 216, 62, 31, 33, EOB_CB(clickedWeaponSlot), 2 }, + { 0, 0, 0x1100, 288, 62, 31, 33, EOB_CB(clickedWeaponSlot), 3 }, + { 368, 0, 0x1000, 184, 2, 63, 8, EOB_CB(clickedCharNameLabelRight), 0 }, + { 369, 0, 0x1000, 256, 2, 63, 8, EOB_CB(clickedCharNameLabelRight), 1 }, + { 370, 0, 0x1000, 184, 54, 63, 8, EOB_CB(clickedCharNameLabelRight), 2 }, + { 371, 0, 0x1000, 256, 54, 63, 8, EOB_CB(clickedCharNameLabelRight), 3 }, + { 0, 0, 0x1100, 230, 116, 16, 16, EOB_CB(clickedInventorySlot), 0 }, + { 0, 0, 0x1100, 278, 116, 16, 16, EOB_CB(clickedInventorySlot), 1 }, + { 0, 0, 0x1100, 181, 40, 16, 16, EOB_CB(clickedInventorySlot), 2 }, + { 0, 0, 0x1100, 199, 40, 16, 16, EOB_CB(clickedInventorySlot), 3 }, + { 0, 0, 0x1100, 181, 58, 16, 16, EOB_CB(clickedInventorySlot), 4 }, + { 0, 0, 0x1100, 199, 58, 16, 16, EOB_CB(clickedInventorySlot), 5 }, + { 0, 0, 0x1100, 181, 76, 16, 16, EOB_CB(clickedInventorySlot), 6 }, + { 0, 0, 0x1100, 199, 76, 16, 16, EOB_CB(clickedInventorySlot), 7 }, + { 0, 0, 0x1100, 181, 94, 16, 16, EOB_CB(clickedInventorySlot), 8 }, + { 0, 0, 0x1100, 199, 94, 16, 16, EOB_CB(clickedInventorySlot), 9 }, + { 0, 0, 0x1100, 181, 112, 16, 16, EOB_CB(clickedInventorySlot), 10 }, + { 0, 0, 0x1100, 199, 112, 16, 16, EOB_CB(clickedInventorySlot), 11 }, + { 0, 0, 0x1100, 181, 130, 16, 16, EOB_CB(clickedInventorySlot), 12 }, + { 0, 0, 0x1100, 199, 130, 16, 16, EOB_CB(clickedInventorySlot), 13 }, + { 0, 0, 0x1100, 181, 148, 16, 16, EOB_CB(clickedInventorySlot), 14 }, + { 0, 0, 0x1100, 199, 148, 16, 16, EOB_CB(clickedInventorySlot), 15 }, + { 0, 0, 0x1100, 225, 55, 16, 16, EOB_CB(clickedInventorySlot), 16 }, + { 0, 0, 0x1100, 224, 76, 16, 16, EOB_CB(clickedInventorySlot), 17 }, + { 0, 0, 0x1100, 225, 96, 16, 16, EOB_CB(clickedInventorySlot), 18 }, + { 0, 0, 0x1100, 298, 55, 16, 16, EOB_CB(clickedInventorySlot), 19 }, + { 0, 0, 0x1100, 287, 75, 16, 16, EOB_CB(clickedInventorySlot), 20 }, + { 0, 0, 0x1100, 277, 137, 16, 16, EOB_CB(clickedInventorySlot), 21 }, + { 0, 0, 0x1100, 300, 94, 16, 16, EOB_CB(clickedInventorySlot), 22 }, + { 0, 0, 0x1100, 300, 112, 16, 16, EOB_CB(clickedInventorySlot), 23 }, + { 0, 0, 0x1100, 300, 130, 16, 16, EOB_CB(clickedInventorySlot), 24 }, + { 0, 0, 0x1100, 236, 37, 31, 16, EOB_CB(clickedEatItem), 25 }, + { 26, 0, 0x1100, 291, 149, 25, 17, EOB_CB(clickedInventoryNextPage), 25 }, + { 110, 24, 0x1100, 181, 3, 32, 32, EOB_CB(clickedPortraitRestore), 25 }, + { 96, 352, 0x1100, 24, 128, 21, 16, EOB_CB(clickedUpArrow), 25 }, + { 98, 97, 0x1100, 24, 144, 21, 16, EOB_CB(clickedDownArrow), 25 }, + { 92, 348, 0x1100, 3, 144, 21, 16, EOB_CB(clickedLeftArrow), 25 }, + { 102, 358, 0x1100, 45, 144, 21, 16, EOB_CB(clickedRightArrow), 25 }, + { 91, 0, 0x1100, 3, 128, 21, 16, EOB_CB(clickedTurnLeftArrow), 25 }, + { 101, 0, 0x1100, 45, 128, 21, 16, EOB_CB(clickedTurnRightArrow), 25 }, + { 110, 0, 0x1100, 184, 0, 136, 120, EOB_CB(clickedAbortCharSwitch), 0 }, + { 0, 0, 0x1100, 0, 8, 88, 48, EOB_CB(clickedSceneThrowItem), 0 }, + { 0, 0, 0x1100, 88, 8, 88, 48, EOB_CB(clickedSceneThrowItem), 1 }, + { 0, 0, 0x1100, 24, 8, 128, 96, EOB_CB(clickedSceneSpecial), 1 }, + { 112, 113, 0x1100, 274, 35, 20, 15, EOB_CB(clickedInventoryPrevChar), 1 }, + { 114, 115, 0x1100, 297, 35, 20, 15, EOB_CB(clickedInventoryNextChar), 1 }, + { 2, 0, 0x1100, 68, 121, 18, 10, EOB_CB(clickedSpellbookTab), 0 }, + { 3, 0, 0x1100, 86, 121, 18, 10, EOB_CB(clickedSpellbookTab), 1 }, + { 4, 0, 0x1100, 104, 121, 15, 10, EOB_CB(clickedSpellbookTab), 2 }, + { 5, 0, 0x1100, 122, 121, 15, 10, EOB_CB(clickedSpellbookTab), 3 }, + { 6, 0, 0x1100, 140, 121, 15, 10, EOB_CB(clickedSpellbookTab), 4 }, + { 0, 0, 0x1100, 75, 131, 97, 6, EOB_CB(clickedSpellbookList), 0 }, + { 0, 0, 0x1100, 75, 137, 97, 6, EOB_CB(clickedSpellbookList), 1 }, + { 0, 0, 0x1100, 75, 143, 97, 6, EOB_CB(clickedSpellbookList), 2 }, + { 0, 0, 0x1100, 75, 149, 97, 6, EOB_CB(clickedSpellbookList), 3 }, + { 0, 0, 0x1100, 75, 155, 97, 6, EOB_CB(clickedSpellbookList), 4 }, + { 0, 0, 0x1100, 75, 161, 97, 6, EOB_CB(clickedSpellbookList), 5 }, + { 112, 0, 0x1100, 184, 2, 63, 50, EOB_CB(clickedCastSpellOnCharacter), 0 }, + { 113, 0, 0x1100, 256, 2, 63, 50, EOB_CB(clickedCastSpellOnCharacter), 1 }, + { 114, 0, 0x1100, 184, 54, 63, 50, EOB_CB(clickedCastSpellOnCharacter), 2 }, + { 115, 0, 0x1100, 256, 54, 63, 50, EOB_CB(clickedCastSpellOnCharacter), 3 }, + { 53, 54, 0x1100, 320, 200, 0, 0, EOB_CB(clickedSpellbookList), 6 }, + { 61, 0, 0x1100, 320, 200, 0, 0, EOB_CB(clickedSpellbookList), 7 }, + { 0, 0, 0x1100, 184, 114, 33, 33, EOB_CB(clickedCharPortrait2), 4 }, + { 0, 0, 0x1100, 256, 114, 33, 33, EOB_CB(clickedCharPortrait2), 5 }, + { 0, 0, 0x1100, 216, 114, 31, 33, EOB_CB(clickedWeaponSlot), 4 }, + { 0, 0, 0x1100, 288, 114, 31, 33, EOB_CB(clickedWeaponSlot), 5 }, + { 372, 0, 0x1000, 184, 106, 63, 8, EOB_CB(clickedCharNameLabelRight), 4 }, + { 373, 0, 0x1000, 256, 106, 63, 8, EOB_CB(clickedCharNameLabelRight), 5 }, + { 0, 0, 0x1100, 227, 135, 10, 10, EOB_CB(clickedInventorySlot), 25 }, + { 0, 0, 0x1100, 239, 135, 10, 10, EOB_CB(clickedInventorySlot), 26 }, + { 116, 0, 0x1100, 184, 106, 63, 50, EOB_CB(clickedCastSpellOnCharacter), 4 }, + { 117, 0, 0x1100, 256, 106, 63, 50, EOB_CB(clickedCastSpellOnCharacter), 5 }, + { 110, 0, 0x1100, 68, 168, 78, 10, EOB_CB(clickedSpellbookAbort), 0 }, + { 110, 0, 0x1100, 68, 168, 78, 10, EOB_CB(clickedCastSpellOnCharacter), 65535 }, + { 116, 0, 0x1100, 184, 106, 63, 50, EOB_CB(clickedCharPortraitDefault), 4 }, + { 117, 0, 0x1100, 256, 106, 63, 50, EOB_CB(clickedCharPortraitDefault), 5 }, + { 116, 117, 0x1100, 320, 200, 1, 1, EOB_CB(clickedInventoryNextChar), 2 }, + { 7, 0, 0x1100, 158, 121, 15, 10, EOB_CB(clickedSpellbookTab), 5 }, + { 0, 0, 0x1100, 146, 168, 32, 10, EOB_CB(clickedSpellbookScroll), 0 }, + + // EOB1 spellbook modifications + { 2, 0, 0x1100, 71, 122, 20, 8, EOB_CB(clickedSpellbookTab), 0 }, + { 3, 0, 0x1100, 92, 122, 20, 8, EOB_CB(clickedSpellbookTab), 1 }, + { 4, 0, 0x1100, 113, 122, 20, 8, EOB_CB(clickedSpellbookTab), 2 }, + { 5, 0, 0x1100, 134, 122, 20, 8, EOB_CB(clickedSpellbookTab), 3 }, + { 6, 0, 0x1100, 155, 122, 20, 8, EOB_CB(clickedSpellbookTab), 4 }, + { 110, 0, 0x1100, 75, 168, 97, 6, EOB_CB(clickedSpellbookAbort), 0 }, + }; + + _buttonDefs = buttonDefs; +} + +void EobCoreEngine::initSpells() { +#define mpn magicTimingParaAssign.push_back(0); +#define mp1n if (_flags.gameID == GI_EOB1) magicTimingParaAssign.push_back(0); +#define mp2n if (_flags.gameID == GI_EOB2) magicTimingParaAssign.push_back(0); +#define mp(x) magicTimingParaAssign.push_back(&magicTimingPara[x << 2]); +#define mp1(x) if (_flags.gameID == GI_EOB1) magicTimingParaAssign.push_back(&magicTimingPara[x << 2]); +#define mp2(x) if (_flags.gameID == GI_EOB2) magicTimingParaAssign.push_back(&magicTimingPara[x << 2]); + +#define sc(x) startCallback.push_back(&EobCoreEngine::spellCallback_start_##x); +#define sc1(x) if (_flags.gameID == GI_EOB1) startCallback.push_back(&EobCoreEngine::spellCallback_start_##x); +#define sc2(x) if (_flags.gameID == GI_EOB2) startCallback.push_back(&EobCoreEngine::spellCallback_start_##x); +#define ec(x) endCallback.push_back(&EobCoreEngine::spellCallback_end_##x); +#define ec1(x) if (_flags.gameID == GI_EOB1) endCallback.push_back(&EobCoreEngine::spellCallback_end_##x); +#define ec2(x) if (_flags.gameID == GI_EOB2) endCallback.push_back(&EobCoreEngine::spellCallback_end_##x); + + static const uint16 magicTimingPara[] = { + 0, 546, 2, 1, // 0 detect magic + 0, 546, 5, 1, // 1 shield, detect invis, magical vestment + 0, 546, 1, 1, // 2 shocking grasp, vamp touch, true seeing, prayer + 3, 546, 1, 1, // 3 blur, haste + 5, 546, 1, 1, // 4 imp invisibility + 6, 546, 0, 1, // 5 bless + 0, 546, 3, 1, // 6 prot from evil + 1, 546, 1, 1, // 7 aid + 4, 546, 1, 1, // 8 flame blade + 0, 32760, 1, 1, // 9 slow poison + 1, 546, 0, 1, // 10 mystic defense + }; + + Common::Array<const uint16*> magicTimingParaAssign; + mpn; + mpn; + mpn; + mp(0); // Detect Magic + mpn; // Magic Missile + mp1n; /// + mp(1); // Shield + mp(2); // Shocking Grasp + mp2(3); // Blur /// + mp2(1); // Detect Invis /// + mp2n; // Imp Identify /// + mpn; // Invis + mp1n; /// + mpn; // Melf + mp1n; // Stinking Cloud /// + mpn; // Dispel Magic + mpn; // Fireball + mp1n; // Flame Arrow /// + mp(3); // Haste + mpn; // Hold Person + mpn; // Invisibility + mpn; // Lightning Bolt + mp(2); // Vampiric Touch + mpn; // Fear + mpn; // Ice Storm + mp1n; // Stone Skin /// --- para required? + mp1n; // Cloud Kill /// + mp2(4); // Improved Invisibility /// + mp2n; // remove Curse /// + mpn; // Cone of Cold + mpn; // Hold Monster + mp2n; // Wall of Force /// + mp2n; // Disintegrate /// + mp2n; // Flesh To Stone /// + mp2n; // Stone To Flesh /// + mp2(2); // True Seeing /// + mp2n; // Finger of Death /// + mp2n; // Power Word Stun /// + mp2n; // Bigby's Fist /// + mp2n; // empty /// + mp(5); // Bless + mpn; /// EOB1: cure, EOB2: cause + mpn; /// EOB1: cause, EOB2: cure + mp(0); // Detect Magic + mp(6); // Prot from Evil + mp(7); // Aid + mp(8); // Flame Blad + mpn; // Hold Person + mp(9); // Slow Poison + mpn; // Create Food + mpn; // Dispel Magic + mp(1); // Magical Vestment + mp(2); // Prayer + mpn; // Remove Paralysis + mpn; /// EOB1: cure, EOB2: cause + mpn; /// EOB1: cause, EOB2: cure + mpn; // Neutral Poison + mp(6); // Prot From Evil 10' + mp1n; // Prot From Lightning /// --- para required? + mpn; /// EOB1: cure, EOB2: cause + mpn; /// EOB1: cause, EOB2: cure + mpn; // Flame Strike + mpn; // Raise Dead + mp2n; // Slay Living /// + mp2(2); // True Seeing /// + mp2n; // Harm /// + mp2n; // Heal /// + mp2n; // Resurrect /// + mpn; // Lay on Hands + mp2n; // Turn Undead /// + mpn; // UNK 1 passive + mp2(10);// Mystic Defense /// + mp2n; // UNK 2 passive /// + mpn; // death spell passive + mpn; // disintegrate passive + mp2n; // cause critical passive + mp2n; // flesh to stone passive + + Common::Array<SpellStartCallback> startCallback; + sc(empty); + sc(armor); + sc(burningHands); + sc(detectMagic); + sc(magicMissile); + sc1(empty); + sc(empty); + sc(shockingGrasp); + sc(empty); + sc2(empty); + sc2(improvedIdentify); + sc(empty); + sc(melfsAcidArrow); + sc1(empty); // Stinking Cloud + sc(dispelMagic); + sc(fireball); + sc1(flameArrow); + sc(empty); + sc(holdPerson); + sc(empty); + sc(lightningBolt); + sc(vampiricTouch); + sc(fear); + sc(iceStorm); + sc(empty); // EOB1: stone skin, EOB2: imp invisibility + sc1(empty); // Cloudkill + sc2(removeCurse); + sc(coneOfCold); + sc(holdMonster); + sc2(wallOfForce); + sc2(disintegrate); + sc2(fleshToStone); + sc2(stoneToFlesh); + sc2(trueSeeing); + sc2(slayLiving); + sc2(powerWordStun); + sc2(empty); + sc2(empty); + sc(empty); // Bless + sc2(causeLightWounds); + sc(cureLightWounds); + sc1(causeLightWounds); + sc(detectMagic); + sc(empty); + sc(aid); + sc(flameBlade); + sc(holdPerson); + sc(slowPoison); + sc(createFood); + sc(dispelMagic); + sc(empty); + sc(empty); + sc(removeParalysis); + sc2(causeSeriousWounds); + sc(cureSeriousWounds); + sc1(causeSeriousWounds); + sc(neutralizePoison); + sc(empty); + sc1(empty); // Prot from Lightning + sc2(causeCriticalWounds); + sc(cureCriticalWounds); + sc1(causeCriticalWounds); + sc(flameStrike); + sc(raiseDead); + sc2(slayLiving); + sc2(trueSeeing); + sc2(harm); + sc2(heal); + sc2(empty); + sc(layOnHands); + sc2(turnUndead); + sc(empty); + sc2(empty); + sc2(empty); + sc(empty); + sc(empty); + sc2(empty); + sc2(empty); + + Common::Array<SpellEndCallback> endCallback; + ec(empty); + ec(empty); + ec(empty); + ec(detectMagic); + ec(magicMissile); + ec1(empty); + ec(empty); + ec(shockingGraspFlameBlade); + ec(empty); + ec(empty); + ec2(empty); + ec2(empty); + ec(melfsAcidArrow); + ec1(empty); // Stinking Cloud + ec(empty); + ec(fireball); + ec1(flameArrow); + ec(empty); + ec(holdPerson); + ec(empty); + ec(lightningBolt); + ec(vampiricTouch); + ec(empty); + ec(iceStorm); + ec(empty); // EOB1: stone skin, EOB2: imp invisibility + ec(empty); // EOB1: cloud kill, EOB2: remove curse + ec(empty); + ec(holdMonster); + ec2(empty); + ec2(empty); + ec2(empty); + ec2(empty); + ec2(trueSeeing); + ec2(empty); + ec2(empty); + ec2(empty); + ec2(empty); + ec(empty); // Bless + ec(empty); + ec(empty); + ec(detectMagic); + ec(empty); + ec(aid); + ec(shockingGraspFlameBlade); + ec(holdPerson); + ec(slowPoison); + ec(empty); + ec(empty); + ec(empty); + ec(empty); + ec(empty); + ec(empty); + ec(empty); + ec(empty); + ec(empty); + ec1(empty); // Prot from Lightning + ec(empty); + ec(empty); + ec(flameStrike); + ec(empty); + ec2(empty); + ec2(trueSeeing); + ec2(empty); + ec2(empty); + ec2(empty); + ec(empty); + ec2(empty); + ec(unk1Passive); + ec2(empty); + ec2(unk2Passive); + ec(deathSpellPassive); + ec(disintegratePassive); + ec2(causeCriticalWoundsPassive); + ec2(fleshToStonePassive); + + _spells = new EobSpell[_numSpells]; + memset(_spells, 0, _numSpells * sizeof(EobSpell)); + + for (int i = 0; i < _numSpells; i++) { + EobSpell *s = &_spells[i]; + s->name = _flags.gameID == GI_EOB2 ? ((i == 0 || i == _mageSpellListSize) ? _mageSpellList[0] : ((i < (_mageSpellListSize + 1)) ? _spellNames[i - 1] : _spellNames[i - 2])) : _spellNames[i]; + s->startCallback = startCallback[i]; + s->timingPara = magicTimingParaAssign[i]; + s->endCallback = endCallback[i]; + } + + magicTimingParaAssign.clear(); + startCallback.clear(); + endCallback.clear(); + + _clericSpellOffset = _mageSpellListSize; + +#undef mpn +#undef mp1n +#undef mp2n +#undef mp +#undef mp1 +#undef mp2 +#undef sc +#undef sc1 +#undef sc2 +#undef ec +#undef ec1 +#undef ec2 +} + +void EobEngine::initStaticResource() { + int temp; + _mainMenuStrings = _staticres->loadStrings(kEob1MainMenuStrings, temp); + + _doorShapeEncodeDefs = _staticres->loadRawData(kEob1DoorShapeDefs, temp); + _doorSwitchShapeEncodeDefs = _staticres->loadRawData(kEob1DoorSwitchShapeDefs, temp); + _doorSwitchCoords = _staticres->loadRawData(kEob1DoorSwitchCoords, temp); + + _dscDoorScaleMult4 = _staticres->loadRawData(kEobBaseDscDoorScaleMult4, temp); + _dscDoorScaleMult5 = _staticres->loadRawData(kEobBaseDscDoorScaleMult5, temp); + _dscDoorScaleMult6 = _staticres->loadRawData(kEobBaseDscDoorScaleMult6, temp); + _dscDoorY3 = _staticres->loadRawData(kEobBaseDscDoorY3, temp); + _dscDoorY4 = _staticres->loadRawData(kEobBaseDscDoorY4, temp); + _dscDoorY5 = _staticres->loadRawData(kEobBaseDscDoorY5, temp); + _dscDoorY6 = _staticres->loadRawData(kEobBaseDscDoorY6, temp); + _dscDoorCoordsExt = (const int16*)_staticres->loadRawDataBe16(kEobBaseDscDoorCoordsExt, temp); + + _monsterDistAttType10 = _staticres->loadRawData(kEob1MonsterDistAttType10, temp); + _monsterDistAttSfx10 = _staticres->loadRawData(kEob1MonsterDistAttSfx10, temp); + _monsterDistAttType17 = _staticres->loadRawData(kEob1MonsterDistAttType17, temp); + _monsterDistAttSfx17 = _staticres->loadRawData(kEob1MonsterDistAttSfx17, temp); + + const uint8 *ps = _staticres->loadRawData(kEob1MonsterProperties, temp); + temp /= 27; + _monsterProps = new EobMonsterProperty[temp]; + memset(_monsterProps, 0, temp * sizeof(EobMonsterProperty)); + // Try to convert EOB1 (hard coded) monster properties to EOB2 type monster properties. + // This is still WIP, since most properties are unknown for now. + for (int i = 0; i < temp; i++) { + EobMonsterProperty *p = &_monsterProps[i]; + p->armorClass = (int8)*ps++; + p->hitChance = (int8)*ps++; + p->hpDcTimes = *ps++; + p->attacksPerRound = *ps++; + p->dmgDc[0].times = *ps++; + p->dmgDc[0].pips = *ps++; + p->dmgDc[0].base = (int8)*ps++; + p->dmgDc[1].times = *ps++; + p->dmgDc[1].pips = *ps++; + p->dmgDc[1].base =(int8) *ps++; + p->dmgDc[2].times = *ps++; + p->dmgDc[2].pips = *ps++; + p->dmgDc[2].base = (int8)*ps++; + ps++; + p->flags = *ps++; + ps++; + ps++; + ps++; + ps++; + p->experience = READ_LE_UINT16(ps); + ps += 2; + p->u30 = *ps++; + p->sound1 = *ps++; + p->sound2 = *ps++; + p->numRemoteAttacks = *ps++; + ps++; + p->dmgModifierEvade = *ps++; + } +} + +void EobEngine::initSpells() { + EobCoreEngine::initSpells(); + + static const uint32 eflags[] = { + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0008, 0x0000, 0x0040, 0x0000, + 0x0000, 0x0000 /*stinking cloud*/, 0x0000, 0x0000, 0x0000 /*flame arrow*/, 0x10000,0x0000, 0x0040, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000 /*stone skin*/, 0x0000 /*cloud kill*/, 0x0000, 0x0000, 0x0400, 0x0000, 0x0000, 0x0002, + 0x0800, 0x0000, 0x0000, 0x0000, 0x2000, 0x0000, 0x0000, 0x4000, 0x8000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0800, 0x0000 /*Prot From Lightning*/, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000 + }; + + static const uint8 dflags[] = { + 0x00, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x11, 0x00 /*stinking cloud*/, 0x00, 0x21, 0x11 /*flame arrow*/, 0x00, 0x00, 0x00, 0x03, 0x01, + 0x00, 0x41, 0x00 /*stone skin*/, 0x00 /*cloud kill*/, 0x41, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00 /*Prot From Lightning*/, 0x00, 0x01, 0x21, 0x00, 0x00, + 0x00, 0x00, 0x00 + }; + + int temp; + const uint8 *src = _staticres->loadRawData(kEobBaseSpellProperties, temp); + _clericSpellOffset -= 3; + + for (int i = 0; i < _numSpells; i++) { + EobSpell *s = &_spells[i]; + src += 4; + s->flags = convertSpellFlagToEob2Format(src[0], src[14]); + s->damageFlags = dflags[i]; + s->effectFlags = eflags[i]; + s->sound = src[13]; + src += 15; + } +} + +void DarkMoonEngine::initStaticResource() { + int temp; + _mainMenuStrings = _staticres->loadStrings(kEob2MainMenuStrings, temp); + _introStrings = _staticres->loadStrings(kEob2IntroStrings, temp); + _cpsFilesIntro = _staticres->loadStrings(kEob2IntroCPSFiles, temp); + + _seqIntro = new const EobSequenceStep*[44]; + for (int i = 0; i < 44; i++) + _seqIntro[i] = _staticres->loadEob2SeqData(kEob2IntroSeqData00 + i, temp); + + _shapesIntro = new const EobShapeDef*[13]; + memset(_shapesIntro, 0, sizeof(EobShapeDef*) * 13); + _shapesIntro[0] = _staticres->loadEob2ShapeData(kEob2IntroShapes00, temp); + _shapesIntro[1] = _staticres->loadEob2ShapeData(kEob2IntroShapes01, temp); + _shapesIntro[4] = _staticres->loadEob2ShapeData(kEob2IntroShapes04, temp); + _shapesIntro[7] = _staticres->loadEob2ShapeData(kEob2IntroShapes07, temp); + + _finaleStrings = _staticres->loadStrings(kEob2FinaleStrings, temp); + _creditsData = _staticres->loadRawData(kEob2CreditsData, temp); + _cpsFilesFinale = _staticres->loadStrings(kEob2FinaleCPSFiles, temp); + + _seqFinale = new const EobSequenceStep*[21]; + for (int i = 0; i < 21; i++) + _seqFinale[i] = _staticres->loadEob2SeqData(kEob2FinaleSeqData00 + i, temp); + + _shapesFinale = new const EobShapeDef*[13]; + memset(_shapesFinale, 0, sizeof(EobShapeDef*) * 13); + _shapesFinale[0] = _staticres->loadEob2ShapeData(kEob2FinaleShapes00, temp); + _shapesFinale[3] = _staticres->loadEob2ShapeData(kEob2FinaleShapes03, temp); + _shapesFinale[7] = _staticres->loadEob2ShapeData(kEob2FinaleShapes07, temp); + _shapesFinale[9] = _staticres->loadEob2ShapeData(kEob2FinaleShapes09, temp); + _shapesFinale[10] = _staticres->loadEob2ShapeData(kEob2FinaleShapes10, temp); + + _dscDoorType5Offs = _staticres->loadRawData(kEobBaseDscDoorType5Offs, temp); + + _npcShpData = _staticres->loadRawData(kEob2NpcShapeData, temp); + _npc1Strings = _staticres->loadStrings(kEob2Npc1Strings, temp); + _npc2Strings = _staticres->loadStrings(kEob2Npc2Strings, temp); + _monsterDustStrings = _staticres->loadStrings(kEob2MonsterDustStrings, temp); +} + +void DarkMoonEngine::initSpells() { + EobCoreEngine::initSpells(); + + int temp; + const uint8 *src = _staticres->loadRawData(kEobBaseSpellProperties, temp); + + for (int i = 0; i < _numSpells; i++) { + EobSpell *s = &_spells[i]; + src += 8; + s->flags = READ_LE_UINT16(src); + src += 10; + s->sound = *src++; + s->effectFlags = READ_LE_UINT32(src); + src += 4; + s->damageFlags = READ_LE_UINT16(src); + src += 2; + } +} + +const char *DarkMoonEngine::_palFilesIntro[] = { + "PALETTE1.PAL", // EGA: palette0.pal + "PALETTE3.PAL", + "PALETTE2.PAL", + "PALETTE4.PAL", + 0 +}; + +const char *DarkMoonEngine::_palFilesFinale[] = { + "FINALE_0.PAL", + "FINALE_0.PAL", + "FINALE_1.PAL", + "FINALE_2.PAL", + "FINALE_3.PAL", + "FINALE_4.PAL", + "FINALE_5.PAL", + "FINALE_6.PAL", + "FINALE_7.PAL", + 0 +}; + +#endif // ENABLE_EOB + +} // End of namespace Kyra + diff --git a/engines/kyra/staticres_lol.cpp b/engines/kyra/staticres_lol.cpp index bf838cd572..44b82ceb00 100644 --- a/engines/kyra/staticres_lol.cpp +++ b/engines/kyra/staticres_lol.cpp @@ -45,16 +45,8 @@ const FlyingObjectShape *StaticResource::loadFlyingObjectData(int id, int &entri return (const FlyingObjectShape *)getData(id, kLolFlightShpData, entries); } -const uint16 *StaticResource::loadRawDataBe16(int id, int &entries) { - return (const uint16 *)getData(id, kLolRawDataBe16, entries); -} - -const uint32 *StaticResource::loadRawDataBe32(int id, int &entries) { - return (const uint32 *)getData(id, kLolRawDataBe32, entries); -} - -const ButtonDef *StaticResource::loadButtonDefs(int id, int &entries) { - return (const ButtonDef *)getData(id, kLolButtonData, entries); +const LoLButtonDef *StaticResource::loadButtonDefs(int id, int &entries) { + return (const LoLButtonDef *)getData(id, kLolButtonData, entries); } bool StaticResource::loadCharData(Common::SeekableReadStream &stream, void *&ptr, int &size) { @@ -164,34 +156,10 @@ bool StaticResource::loadFlyingObjectData(Common::SeekableReadStream &stream, vo return true; } -bool StaticResource::loadRawDataBe16(Common::SeekableReadStream &stream, void *&ptr, int &size) { - size = stream.size() >> 1; - - uint16 *r = new uint16[size]; - - for (int i = 0; i < size; i++) - r[i] = stream.readUint16BE(); - - ptr = r; - return true; -} - -bool StaticResource::loadRawDataBe32(Common::SeekableReadStream &stream, void *&ptr, int &size) { - size = stream.size() >> 2; - - uint32 *r = new uint32[size]; - - for (int i = 0; i < size; i++) - r[i] = stream.readUint32BE(); - - ptr = r; - return true; -} - bool StaticResource::loadButtonDefs(Common::SeekableReadStream &stream, void *&ptr, int &size) { size = stream.size() / 18; - ButtonDef *r = new ButtonDef[size]; + LoLButtonDef *r = new LoLButtonDef[size]; for (int i = 0; i < size; i++) { r[i].buttonflags = stream.readUint16BE(); @@ -237,23 +205,8 @@ void StaticResource::freeFlyingObjectData(void *&ptr, int &size) { size = 0; } - -void StaticResource::freeRawDataBe16(void *&ptr, int &size) { - uint16 *data = (uint16 *)ptr; - delete[] data; - ptr = 0; - size = 0; -} - -void StaticResource::freeRawDataBe32(void *&ptr, int &size) { - uint32 *data = (uint32 *)ptr; - delete[] data; - ptr = 0; - size = 0; -} - void StaticResource::freeButtonDefs(void *&ptr, int &size) { - ButtonDef *d = (ButtonDef *)ptr; + LoLButtonDef *d = (LoLButtonDef *)ptr; delete[] d; ptr = 0; size = 0; @@ -289,67 +242,59 @@ void LoLEngine::initStaticResource() { if (_flags.isDemo) return; + int tempSize; _pakFileList = _staticres->loadStrings(kLolIngamePakFiles, _pakFileListSize); _charDefaults = _staticres->loadCharData(kLolCharacterDefs, _charDefaultsSize); - _ingameSoundIndex = (const uint16 *)_staticres->loadRawData(kLolIngameSfxIndex, _ingameSoundIndexSize); - _musicTrackMap = _staticres->loadRawData(kLolMusicTrackMap, _musicTrackMapSize); + _ingameSoundIndex = (const uint16 *)_staticres->loadRawData(kLolIngameSfxIndex, tempSize); + _musicTrackMap = _staticres->loadRawData(kLolMusicTrackMap, tempSize); _ingameGMSoundIndex = _staticres->loadRawData(kLolIngameGMSfxIndex, _ingameGMSoundIndexSize); _ingameMT32SoundIndex = _staticres->loadRawData(kLolIngameMT32SfxIndex, _ingameMT32SoundIndexSize); _ingamePCSpeakerSoundIndex = _staticres->loadRawData(kLolIngamePcSpkSfxIndex, _ingamePCSpeakerSoundIndexSize); - _spellProperties = _staticres->loadSpellData(kLolSpellProperties, _spellPropertiesSize); - _gameShapeMap = (const int8 *)_staticres->loadRawData(kLolGameShapeMap, _gameShapeMapSize); - _sceneItemOffs = (const int8 *)_staticres->loadRawData(kLolSceneItemOffs, _sceneItemOffsSize); - _charInvIndex = _staticres->loadRawData(kLolCharInvIndex, _charInvIndexSize); - _charInvDefs = _staticres->loadRawData(kLolCharInvDefs, _charInvDefsSize); - _charDefsMan = _staticres->loadRawDataBe16(kLolCharDefsMan, _charDefsManSize); - _charDefsWoman = _staticres->loadRawDataBe16(kLolCharDefsWoman, _charDefsWomanSize); - _charDefsKieran = _staticres->loadRawDataBe16(kLolCharDefsKieran, _charDefsKieranSize); - _charDefsAkshel = _staticres->loadRawDataBe16(kLolCharDefsAkshel, _charDefsAkshelSize); - _expRequirements = (const int32 *)_staticres->loadRawDataBe32(kLolExpRequirements, _expRequirementsSize); - _monsterModifiers = _staticres->loadRawDataBe16(kLolMonsterModifiers, _monsterModifiersSize); - _monsterShiftOffs = (const int8 *)_staticres->loadRawData(kLolMonsterShiftOffsets, _monsterShiftOffsSize); - _monsterDirFlags = _staticres->loadRawData(kLolMonsterDirFlags, _monsterDirFlagsSize); - _monsterScaleX = _staticres->loadRawData(kLolMonsterScaleX, _monsterScaleXSize); - _monsterScaleY = _staticres->loadRawData(kLolMonsterScaleY, _monsterScaleYSize); - _monsterScaleWH = _staticres->loadRawDataBe16(kLolMonsterScaleWH, _monsterScaleWHSize); - _inventorySlotDesc = _staticres->loadRawDataBe16(kLolInventoryDesc, _inventorySlotDescSize); - _levelShpList = _staticres->loadStrings(kLolLevelShpList, _levelShpListSize); - _levelDatList = _staticres->loadStrings(kLolLevelDatList, _levelDatListSize); - _compassDefs = _staticres->loadCompassData(kLolCompassDefs, _compassDefsSize); - _flyingItemShapes = _staticres->loadFlyingObjectData(kLolFlyingObjectShp, _flyingItemShapesSize); - _itemCost = _staticres->loadRawDataBe16(kLolItemPrices, _itemCostSize); - _stashSetupData = _staticres->loadRawData(kLolStashSetup, _stashSetupDataSize); - - _dscUnk1 = (const int8 *)_staticres->loadRawData(kLolDscUnk1, _dscUnk1Size); - _dscShapeIndex = (const int8 *)_staticres->loadRawData(kLolDscShapeIndex, _dscShapeIndexSize); - _dscOvlMap = _staticres->loadRawData(kLolDscOvlMap, _dscOvlMapSize); - _dscShapeScaleW = _staticres->loadRawDataBe16(kLolDscScaleWidthData, _dscShapeScaleWSize); - _dscShapeScaleH = _staticres->loadRawDataBe16(kLolDscScaleHeightData, _dscShapeScaleHSize); - _dscShapeX = (const int16 *)_staticres->loadRawDataBe16(kLolDscX, _dscShapeXSize); - _dscShapeY = (const int8 *)_staticres->loadRawData(kLolDscY, _dscShapeYSize); - _dscTileIndex = _staticres->loadRawData(kLolDscTileIndex, _dscTileIndexSize); - _dscUnk2 = _staticres->loadRawData(kLolDscUnk2, _dscUnk2Size); - _dscDoorShpIndex = _staticres->loadRawData(kLolDscDoorShapeIndex, _dscDoorShpIndexSize); - _dscDim1 = (const int8 *)_staticres->loadRawData(kLolDscDimData1, _dscDim1Size); - _dscDim2 = (const int8 *)_staticres->loadRawData(kLolDscDimData2, _dscDim2Size); - _dscBlockMap = _staticres->loadRawData(kLolDscBlockMap, _dscBlockMapSize); - _dscDimMap = _staticres->loadRawData(kLolDscDimMap, _dscDimMapSize); - _dscDoorMonsterScaleTable = _staticres->loadRawDataBe16(kLolDscDoorScale, _dscDoorMonsterScaleTableSize); - _dscShapeOvlIndex = _staticres->loadRawData(kLolDscOvlIndex, _dscShapeOvlIndexSize); - _dscDoor4 = _staticres->loadRawDataBe16(kLolDscDoor4, _dscDoor4Size); - _dscBlockIndex = (const int8 *)_staticres->loadRawData(kLolDscBlockIndex, _dscBlockIndexSize); - _dscDoor1 = _staticres->loadRawData(kLolDscDoor1, _dscDoor1Size); - _dscDoorMonsterX = (const int16 *)_staticres->loadRawDataBe16(kLolDscDoorX, _dscDoorMonsterXSize); - _dscDoorMonsterY = (const int16 *)_staticres->loadRawDataBe16(kLolDscDoorY, _dscDoorMonsterYSize); - - _scrollXTop = _staticres->loadRawData(kLolScrollXTop, _scrollXTopSize); - _scrollYTop = _staticres->loadRawData(kLolScrollYTop, _scrollYTopSize); - _scrollXBottom = _staticres->loadRawData(kLolScrollXBottom, _scrollXBottomSize); - _scrollYBottom = _staticres->loadRawData(kLolScrollYBottom, _scrollYBottomSize); + _spellProperties = _staticres->loadSpellData(kLolSpellProperties, tempSize); + _gameShapeMap = (const int8 *)_staticres->loadRawData(kLolGameShapeMap, tempSize); + _sceneItemOffs = (const int8 *)_staticres->loadRawData(kLolSceneItemOffs, tempSize); + _charInvIndex = _staticres->loadRawData(kLolCharInvIndex, tempSize); + _charInvDefs = _staticres->loadRawData(kLolCharInvDefs, tempSize); + _charDefsMan = _staticres->loadRawDataBe16(kLolCharDefsMan, tempSize); + _charDefsWoman = _staticres->loadRawDataBe16(kLolCharDefsWoman, tempSize); + _charDefsKieran = _staticres->loadRawDataBe16(kLolCharDefsKieran, tempSize); + _charDefsAkshel = _staticres->loadRawDataBe16(kLolCharDefsAkshel, tempSize); + _expRequirements = (const int32 *)_staticres->loadRawDataBe32(kLolExpRequirements, tempSize); + _monsterModifiers = _staticres->loadRawDataBe16(kLolMonsterModifiers, tempSize); + _monsterShiftOffs = (const int8 *)_staticres->loadRawData(kLolMonsterShiftOffsets, tempSize); + _monsterDirFlags = _staticres->loadRawData(kLolMonsterDirFlags, tempSize); + _monsterScaleX = _staticres->loadRawData(kLolMonsterScaleX, tempSize); + _monsterScaleY = _staticres->loadRawData(kLolMonsterScaleY, tempSize); + _monsterScaleWH = _staticres->loadRawDataBe16(kLolMonsterScaleWH, tempSize); + _inventorySlotDesc = _staticres->loadRawDataBe16(kLolInventoryDesc, tempSize); + _levelShpList = _staticres->loadStrings(kLolLevelShpList, tempSize); + _levelDatList = _staticres->loadStrings(kLolLevelDatList, tempSize); + _compassDefs = _staticres->loadCompassData(kLolCompassDefs, tempSize); + _flyingItemShapes = _staticres->loadFlyingObjectData(kLolFlyingObjectShp, tempSize); + _itemCost = _staticres->loadRawDataBe16(kLolItemPrices, tempSize); + _stashSetupData = _staticres->loadRawData(kLolStashSetup, tempSize); + + _dscWalls = (const int8 *)_staticres->loadRawData(kLolDscWalls, tempSize); + + _dscOvlMap = _staticres->loadRawData(kLolDscOvlMap, tempSize); + _dscShapeOvlIndex = _staticres->loadRawData(kLolDscOvlIndex, tempSize); + _dscShapeScaleW = _staticres->loadRawDataBe16(kLolDscScaleWidthData, tempSize); + _dscShapeScaleH = _staticres->loadRawDataBe16(kLolDscScaleHeightData, tempSize); + _dscShapeY = (const int8 *)_staticres->loadRawData(kLolBaseDscY, tempSize); + + _dscDoorMonsterScaleTable = _staticres->loadRawDataBe16(kLolDscDoorScale, tempSize); + _dscDoor4 = _staticres->loadRawDataBe16(kLolDscDoor4, tempSize); + _dscDoorMonsterX = (const int16 *)_staticres->loadRawDataBe16(kLolDscDoorX, tempSize); + _dscDoorMonsterY = (const int16 *)_staticres->loadRawDataBe16(kLolDscDoorY, tempSize); + + _scrollXTop = _staticres->loadRawData(kLolScrollXTop, tempSize); + _scrollYTop = _staticres->loadRawData(kLolScrollYTop, tempSize); + _scrollXBottom = _staticres->loadRawData(kLolScrollXBottom, tempSize); + _scrollYBottom = _staticres->loadRawData(kLolScrollYBottom, tempSize); const char *const *tmpSndList = _staticres->loadStrings(kLolIngameSfxFiles, _ingameSoundListSize); if (tmpSndList) { - _ingameSoundList = new char *[_ingameSoundListSize]; + _ingameSoundList = new char*[_ingameSoundListSize]; for (int i = 0; i < _ingameSoundListSize; i++) { _ingameSoundList[i] = new char[strlen(tmpSndList[i]) + 1]; strcpy(_ingameSoundList[i], tmpSndList[i]); @@ -357,19 +302,18 @@ void LoLEngine::initStaticResource() { _staticres->unloadId(kLolIngameSfxFiles); } - _buttonData = _staticres->loadButtonDefs(kLolButtonDefs, _buttonDataSize); - _buttonList1 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList1, _buttonList1Size); - _buttonList2 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList2, _buttonList2Size); - _buttonList3 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList3, _buttonList3Size); - _buttonList4 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList4, _buttonList4Size); - _buttonList5 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList5, _buttonList5Size); - _buttonList6 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList6, _buttonList6Size); - _buttonList7 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList7, _buttonList7Size); - _buttonList8 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList8, _buttonList8Size); + _buttonData = _staticres->loadButtonDefs(kLolButtonDefs, tempSize); + _buttonList1 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList1, tempSize); + _buttonList2 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList2, tempSize); + _buttonList3 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList3, tempSize); + _buttonList4 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList4, tempSize); + _buttonList5 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList5, tempSize); + _buttonList6 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList6, tempSize); + _buttonList7 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList7, tempSize); + _buttonList8 = (const int16 *)_staticres->loadRawDataBe16(kLolButtonList8, tempSize); - _autoMapStrings = _staticres->loadRawDataBe16(kLolMapStringId, _autoMapStringsSize); + _autoMapStrings = _staticres->loadRawDataBe16(kLolMapStringId, tempSize); - int tempSize; const uint8 *tmp = _staticres->loadRawData(kLolLegendData, tempSize); uint8 entrySize = tempSize / 12; tempSize /= entrySize; @@ -386,13 +330,15 @@ void LoLEngine::initStaticResource() { } tmp = _staticres->loadRawData(kLolMapCursorOvl, tempSize); - _mapCursorOverlay = new uint8[tempSize]; - memcpy(_mapCursorOverlay, tmp, tempSize); - _staticres->unloadId(kLolMapCursorOvl); + if (tmp) { + _mapCursorOverlay = new uint8[tempSize]; + memcpy(_mapCursorOverlay, tmp, tempSize); + _staticres->unloadId(kLolMapCursorOvl); + } - _updateSpellBookCoords = _staticres->loadRawData(kLolSpellbookCoords, _updateSpellBookCoordsSize); - _updateSpellBookAnimData = _staticres->loadRawData(kLolSpellbookAnim, _updateSpellBookAnimDataSize); - _healShapeFrames = _staticres->loadRawData(kLolHealShapeFrames, _healShapeFramesSize); + _updateSpellBookCoords = _staticres->loadRawData(kLolSpellbookCoords, tempSize); + _updateSpellBookAnimData = _staticres->loadRawData(kLolSpellbookAnim, tempSize); + _healShapeFrames = _staticres->loadRawData(kLolHealShapeFrames, tempSize); tmp = _staticres->loadRawData(kLolLightningDefs, tempSize); if (tmp) { @@ -405,7 +351,7 @@ void LoLEngine::initStaticResource() { _staticres->unloadId(kLolLightningDefs); } - _fireBallCoords = (const int16 *)_staticres->loadRawDataBe16(kLolFireballCoords, _fireBallCoordsSize); + _fireBallCoords = (const int16*)_staticres->loadRawDataBe16(kLolFireballCoords, tempSize); _buttonCallbacks.clear(); _buttonCallbacks.reserve(95); diff --git a/engines/kyra/text_eob.cpp b/engines/kyra/text_eob.cpp new file mode 100644 index 0000000000..dc53ae295a --- /dev/null +++ b/engines/kyra/text_eob.cpp @@ -0,0 +1,649 @@ +/* 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. + * + * + */ + +#if defined(ENABLE_EOB) || defined(ENABLE_LOL) + +#include "kyra/loleobbase.h" +#include "kyra/screen.h" +#include "kyra/timer.h" + +#include "common/system.h" + +namespace Kyra { + +TextDisplayer_Eob::TextDisplayer_Eob(LolEobBaseEngine *engine, Screen *sScreen) : _vm(engine), _screen(sScreen), + _lineCount(0), _printFlag(false), _lineWidth(0), _numCharsTotal(0), _allowPageBreak(true), + _numCharsLeft(0), _numCharsPrinted(0), _sjisLineBreakFlag(false), _waitButtonMode(1) { + + _dialogueBuffer = new char[1024]; + memset(_dialogueBuffer, 0, 1024); + + _currentLine = new char[85]; + memset(_currentLine, 0, 85); + + _textDimData = new TextDimData[_screen->screenDimTableCount()]; + + for (int i = 0; i < _screen->screenDimTableCount(); i++){ + const ScreenDim *d = _screen->getScreenDim(i); + _textDimData[i].color1 = d->unk8; + _textDimData[i].color2 = d->unkA; + _textDimData[i].line = d->unkC; + _textDimData[i].column = d->unkE; + } + + _waitButtonSpace = 0; +} + +TextDisplayer_Eob::~TextDisplayer_Eob() { + delete[] _dialogueBuffer; + delete[] _currentLine; + delete[] _textDimData; +} + +void TextDisplayer_Eob::setupField(int dim, bool mode) { + setPageBreakFlag(); + + _textDimData[dim].color2 = _vm->_bkgColor_1; + _screen->setScreenDim(dim); + + if (mode) + clearCurDim(); + else + resetDimTextPositions(dim); + + //_textPageBreakFunc = textPageBreakMore; + 0x25 +} + +void TextDisplayer_Eob::resetDimTextPositions(int dim) { + _textDimData[dim].column = 0; + _textDimData[dim].line = 0; +} + +void TextDisplayer_Eob::resetPageBreakString() { + if (vm()->_moreStrings) + strcpy(_pageBreakString, vm()->_moreStrings[0]); +} + +void TextDisplayer_Eob::setPageBreakFlag() { + _allowPageBreak = true; + _lineCount = 0; +} + +void TextDisplayer_Eob::removePageBreakFlag() { + _allowPageBreak = false; +} + +void TextDisplayer_Eob::displayText(char *str, ...) { + const bool isPc98 = (_vm->gameFlags().platform == Common::kPlatformPC98); + + _printFlag = false; + + _lineWidth = 0; + _numCharsLeft = 0; + _numCharsPrinted = 0; + + _tempString1 = str; + _tempString2 = 0; + + _currentLine[0] = 0; + + memset(_ctrl, 0, 3); + + char c = parseCommand(); + + va_list args; + va_start(args, str); + + const ScreenDim *sd = _screen->_curDim; + int sdx = _screen->curDimIndex(); + + bool pc98PrintFlag = (isPc98 && (sdx == 3 || sdx == 4 || sdx == 5 || sdx == 15)) ? true : false; + uint16 charsPerLine = (sd->w << 3) / (_screen->getFontWidth() + _screen->_charWidth); + + while (c) { + char a = tolower(_ctrl[1]); + + if (!_tempString2 && c == '%') { + if (a == 'd') { + snprintf(_scriptParaString, 11, "%d", va_arg(args, int)); + _tempString2 = _scriptParaString; + } else if (a == 's') { + _tempString2 = va_arg(args, char *); + } else { + break; + } + + _ctrl[0] = _ctrl[2]; + _ctrl[2] = _ctrl[1] = 0; + c = parseCommand(); + } + + if (isPc98) { + uint8 cu = (uint8) c; + if (cu >= 0xE0 || (cu > 0x80 && cu < 0xA0)) { + _currentLine[_numCharsLeft++] = c; + _currentLine[_numCharsLeft++] = parseCommand(); + _currentLine[_numCharsLeft] = '\0'; + _lineWidth += 8; + if ((_textDimData[sdx].column + _lineWidth) > (sd->w << 3)) + printLine(_currentLine); + c = parseCommand(); + continue; + } + } + + uint16 dv = _textDimData[sdx].column / (_screen->getFontWidth() + _screen->_charWidth); + + switch (c - 1) { + case 0: + printLine(_currentLine); + textPageBreak(); + _numCharsPrinted = 0; + break; + + case 1: + printLine(_currentLine); + _textDimData[sdx].color2 = parseCommand(); + break; + + case 5: + printLine(_currentLine); + _textDimData[sdx].color1 = parseCommand(); + break; + + case 8: + printLine(_currentLine); + dv = _textDimData[sdx].column / (_screen->getFontWidth() + _screen->_charWidth); + dv = ((dv + 8) & 0xfff8) - 1; + if (dv >= charsPerLine) + dv = 0; + _textDimData[sdx].column = (_screen->getFontWidth() + _screen->_charWidth) * dv; + break; + + case 11: + _sjisLineBreakFlag=_sjisLineBreakFlag; + // TODO (UNUSED) + break; + + case 12: + if (isPc98) + _sjisLineBreakFlag = true; + printLine(_currentLine); + _sjisLineBreakFlag = false; + _lineCount++; + _textDimData[sdx].column = 0; + _textDimData[sdx].line++; + break; + + case 18: + _sjisLineBreakFlag=_sjisLineBreakFlag; + // TODO (UNUSED) + break; + + case 23: + _sjisLineBreakFlag=_sjisLineBreakFlag; + // TODO (UNUSED) + break; + + case 24: + _sjisLineBreakFlag=_sjisLineBreakFlag; + // TODO (UNUSED) + break; + + case 26: + _sjisLineBreakFlag=_sjisLineBreakFlag; + // TODO (UNUSED) + break; + + case 28: + _sjisLineBreakFlag=_sjisLineBreakFlag; + // TODO (UNUSED) + break; + + default: + _lineWidth += (pc98PrintFlag ? 4 : _screen->getCharWidth((uint8)c)); + _currentLine[_numCharsLeft++] = c; + _currentLine[_numCharsLeft] = 0; + + if ((_textDimData[sdx].column + _lineWidth) > (sd->w << 3)) + printLine(_currentLine); + } + + c = parseCommand(); + } + + va_end(args); + + if (_numCharsLeft) + printLine(_currentLine); +} + +char TextDisplayer_Eob::parseCommand() { + if (!_ctrl[1]) + readNextPara(); + + char res = _ctrl[1]; + _ctrl[1] = _ctrl[2]; + _ctrl[2] = 0; + + if (!_ctrl[1]) + readNextPara(); + + return res; +} + +void TextDisplayer_Eob::readNextPara() { + char d = 0; + + if (_tempString2) { + if (*_tempString2) { + d = *_tempString2++; + } else { + _tempString2 = 0; + d = _ctrl[0]; + } + } + + if (!d && _tempString1) { + if (*_tempString1) + d = *_tempString1++; + else + _tempString1 = 0; + } + + _ctrl[1] = d; + _ctrl[2] = 0; +} + +void TextDisplayer_Eob::printLine(char *str) { + const bool isPc98 = (_vm->gameFlags().platform == Common::kPlatformPC98); + const ScreenDim *sd = _screen->_curDim; + int sdx = _screen->curDimIndex(); + bool pc98PrintFlag = (isPc98 && (sdx == 3 || sdx == 4 || sdx == 5 || sdx == 15)) ? true : false; + + int fh = (_screen->_currentFont == Screen::FID_SJIS_FNT) ? 9 : (_screen->getFontHeight() + _screen->_charOffset); + int lines = (sd->h - _screen->_charOffset) / fh; + + while (_textDimData[sdx].line >= lines) { + if ((lines - _waitButtonSpace) <= _lineCount && _allowPageBreak) { + _lineCount = 0; + textPageBreak(); + _numCharsPrinted = 0; + } + + int h1 = ((sd->h / fh) - 1) * fh; + int h2 = sd->h - fh; + + if (h2) + _screen->copyRegion(sd->sx << 3, sd->sy + fh, sd->sx << 3, sd->sy, sd->w << 3, h2, _screen->_curPage, _screen->_curPage, Screen::CR_NO_P_CHECK); + + _screen->fillRect(sd->sx << 3, sd->sy + h1, ((sd->sx + sd->w) << 3) - 1, sd->sy + sd->h - 1, _textDimData[sdx].color2); + if (_textDimData[sdx].line) + _textDimData[sdx].line--; + } + + int x1 = (sd->sx << 3) + _textDimData[sdx].column; + int y = sd->sy + (pc98PrintFlag ? (_textDimData[sdx].line << 3) : (fh * _textDimData[sdx].line)); + int w = sd->w << 3; + int lw = _lineWidth; + int s = _numCharsLeft; + char c = 0; + + if (pc98PrintFlag) { + bool ct = true; + + if ((lw + _textDimData[sdx].column) > w) { + if ((lines - 1 - (_waitButtonSpace << 1)) <= _lineCount) + // cut off line to leave space for "MORE" button + w -= 80; + } else { + if (!_sjisLineBreakFlag || (_lineCount + 1 < lines - 1)) + ct = false; + else + // cut off line to leave space for "MORE" button + w -= 80; + } + + if (ct) { + w -= _textDimData[sdx].column; + + int n2 = 0; + int n1 = (w / 4) - 1; + + while (n2 < n1 && n2 < s) { + c = str[n2]; + uint8 cu = (uint8) c; + if (cu >= 0xE0 || (cu > 0x80 && cu < 0xA0)) + n2++; + n2++; + } + s = n2; + } + } else { + if ((lw + _textDimData[sdx].column) > w) { + if ((lines - 1) <= _lineCount && _allowPageBreak) + // cut off line to leave space for "MORE" button + w -= (10 * (_screen->getFontWidth() + _screen->_charWidth)); + + w -= _textDimData[sdx].column; + + int n2 = 0; + int n1 = s - 1; + + while (n1 > 0) { + //cut off line after last space + c = str[n1]; + + lw -= _screen->getCharWidth((uint8)c); + + if (!n2 && lw <= w) + n2 = n1; + + if (n2 && c == ' ') { + s = n1; + _printFlag = false; + break; + } + n1--; + } + + if (!n1) { + if (_textDimData[sdx].column && !_printFlag) { + s = lw = 0; + _printFlag = true; + } else { + s = n2; + } + } + } + } + + c = str[s]; + str[s] = 0; + + uint8 col = _textDimData[sdx].color1; + if (isPc98 && (sdx == 2 || sdx == 3 || sdx == 4 || sdx == 5 || sdx == 15)) { + switch (_textDimData[sdx].color1) { + case 0x88: + col = 0x41; + break; + case 0x55: + col = 0x81; + break; + case 0xaa: + col = 0x21; + break; + case 0x99: + col = 0xa1; + break; + case 0x33: + col = 0xe1; + break; + case 0x18: + col = 0x61; + break; + default: + col = 1; + break; + } + _screen->printText(str, x1 & ~3, (y + 8) & ~7, col, 0); + } else { + _screen->printText(str, x1, y, col, _textDimData[sdx].color2); + } + + _textDimData[sdx].column += lw; + _numCharsPrinted += strlen(str); + + str[s] = c; + + if (c == ' ') + s++; + + if (str[s] == ' ') + s++; + + uint32 len = strlen(&str[s]); + for (uint32 i = 0; i < len; i++) + str[i] = str[s + i]; + str[len] = 0; + + _numCharsLeft = strlen(str); + _lineWidth = pc98PrintFlag ? (_numCharsLeft << 2) : _screen->getTextWidth(str); + + if (!_numCharsLeft && _textDimData[sdx].column < (sd->w << 3)) + return; + + _textDimData[sdx].column = 0; + _textDimData[sdx].line++; + _lineCount++; + + printLine(str); +} + +void TextDisplayer_Eob::printDialogueText(int stringId, const char *pageBreakString) { + strcpy(_dialogueBuffer, (const char *)(screen()->getCPagePtr(5) + READ_LE_UINT16(&screen()->getCPagePtr(5)[(stringId - 1) << 1]))); + displayText(_dialogueBuffer); + + if (pageBreakString) { + strcpy(_pageBreakString, pageBreakString); + displayWaitButton(); + resetPageBreakString(); + } +} + +void TextDisplayer_Eob::printDialogueText(const char *str, bool wait) { + strcpy(_dialogueBuffer, str); + displayText(_dialogueBuffer); + if (wait) + displayWaitButton(); +} + +void TextDisplayer_Eob::printMessage(const char *str, int textColor, ...) { + int tc = _textDimData[screen()->curDimIndex()].color1; + + if (textColor != -1) + _textDimData[screen()->curDimIndex()].color1 = textColor; + + va_list args; + va_start(args, textColor); + vsnprintf(_dialogueBuffer, 240, str, args); + va_end(args); + + displayText(_dialogueBuffer); + + //if (textColor != -1) + _textDimData[screen()->curDimIndex()].color1 = tc; + + if (!screen()->_curPage) + screen()->updateScreen(); +} + +int TextDisplayer_Eob::clearDim(int dim) { + int res = screen()->curDimIndex(); + screen()->setScreenDim(dim); + _textDimData[dim].color1 = screen()->_curDim->unk8; + _textDimData[dim].color2 = vm()->game() == GI_LOL ? screen()->_curDim->unkA : vm()->_bkgColor_1; + clearCurDim(); + return res; +} + +void TextDisplayer_Eob::clearCurDim() { + int d = screen()->curDimIndex(); + const ScreenDim *tmp = screen()->getScreenDim(d); + if (vm()->gameFlags().use16ColorMode) { + screen()->fillRect(tmp->sx << 3, tmp->sy, ((tmp->sx + tmp->w) << 3) - 2, (tmp->sy + tmp->h) - 2, _textDimData[d].color2); + } else + screen()->fillRect(tmp->sx << 3, tmp->sy, ((tmp->sx + tmp->w) << 3) - 1, (tmp->sy + tmp->h) - 1, _textDimData[d].color2); + + _lineCount = 0; + _textDimData[d].column = _textDimData[d].line = 0; +} + +void TextDisplayer_Eob::textPageBreak() { + if (vm()->game() != GI_LOL) + SWAP(vm()->_dialogueButtonLabelCol1, vm()->_dialogueButtonLabelCol2); + + int cp = _screen->setCurPage(0); + Screen::FontId cf = screen()->setFont(vm()->gameFlags().use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT); + + vm()->_timer->pauseSingleTimer(11, true); + + vm()->_fadeText = false; + int resetPortraitAfterSpeechAnim = 0; + int updatePortraitSpeechAnimDuration = 0; + + if (vm()->_updateCharNum != -1) { + resetPortraitAfterSpeechAnim = vm()->_resetPortraitAfterSpeechAnim; + vm()->_resetPortraitAfterSpeechAnim = 0; + updatePortraitSpeechAnimDuration = vm()->_updatePortraitSpeechAnimDuration; + if (vm()->_updatePortraitSpeechAnimDuration > 36) + vm()->_updatePortraitSpeechAnimDuration = 36; + } + + uint32 speechPartTime = 0; + if (vm()->speechEnabled() && vm()->_activeVoiceFileTotalTime && _numCharsTotal) + speechPartTime = vm()->_system->getMillis() + ((_numCharsPrinted * vm()->_activeVoiceFileTotalTime) / _numCharsTotal); + + const ScreenDim *dim = screen()->getScreenDim(screen()->curDimIndex()); + + int x = ((dim->sx + dim->w) << 3) - (_vm->_dialogueButtonW + 3); + int y = 0; + + if (vm()->_needSceneRestore && (vm()->_updateFlags & 2)) { + if (vm()->_currentControlMode || !(vm()->_updateFlags & 2)) { + y = dim->sy + dim->h - 5; + } else { + x += 6; + y = dim->sy + dim->h - 2; + } + } else if (vm()->game() == GI_LOL) { + y = dim->sy + dim->h - 10; + } else { + y = _waitButtonMode ? 162 : 189; + x = _waitButtonMode ? 76 : 221; + } + + if (vm()->gameFlags().use16ColorMode) { + vm()->gui_drawBox(x + 8, (y & ~7) - 1, 66, 10, 0xee, 0xcc, -1); + screen()->printText(_pageBreakString, (x + 37 - (strlen(_pageBreakString) << 1) + 4) & ~3, (y + 2) & ~7, 0xc1, 0); + } else { + vm()->gui_drawBox(x, y, vm()->_dialogueButtonW, vm()->_dialogueButtonH, vm()->_color1_1, vm()->_color2_1, vm()->_bkgColor_1); + screen()->printText(_pageBreakString, x + (vm()->_dialogueButtonW >> 1) - (vm()->screen()->getTextWidth(_pageBreakString) >> 1), y + 2, vm()->_dialogueButtonLabelCol1, 0); + } + + vm()->removeInputTop(); + + bool loop = true; + bool target = false; + + do { + int inputFlag = vm()->checkInput(0, false) & 0xFF; + vm()->removeInputTop(); + + while (!inputFlag) { + vm()->update(); + + if (vm()->speechEnabled()) { + if (((vm()->_system->getMillis() > speechPartTime) || (vm()->snd_updateCharacterSpeech() != 2)) && speechPartTime) { + loop = false; + inputFlag = vm()->_keyMap[Common::KEYCODE_RETURN]; + break; + } + } + + inputFlag = vm()->checkInput(0, false) & 0xFF; + vm()->removeInputTop(); + } + + vm()->gui_notifyButtonListChanged(); + + if (inputFlag == vm()->_keyMap[Common::KEYCODE_SPACE] || inputFlag == vm()->_keyMap[Common::KEYCODE_RETURN]) { + loop = false; + } else if (inputFlag == 199 || inputFlag == 201) { + if (vm()->posWithinRect(vm()->_mouseX, vm()->_mouseY, x, y, x + _vm->_dialogueButtonW, y + 9)) { + if (_vm->game() == GI_LOL) + target = true; + else + loop = false; + } + } else if (inputFlag == 200 || inputFlag == 202) { + if (target) + loop = false; + } + } while (loop); + + if (vm()->gameFlags().use16ColorMode) + screen()->fillRect(x + 8, y, x + 57, y + 9, _textDimData[screen()->curDimIndex()].color2); + else + screen()->fillRect(x, y, x + 73, y + 8, _textDimData[screen()->curDimIndex()].color2); + + clearCurDim(); + + vm()->_timer->pauseSingleTimer(11, false); + + if (vm()->_updateCharNum != -1) { + vm()->_resetPortraitAfterSpeechAnim = resetPortraitAfterSpeechAnim; + if (updatePortraitSpeechAnimDuration > 36) + updatePortraitSpeechAnimDuration -= 36; + else + updatePortraitSpeechAnimDuration >>= 1; + + vm()->_updatePortraitSpeechAnimDuration = updatePortraitSpeechAnimDuration; + } + + screen()->setFont(cf); + screen()->setCurPage(cp); + + if (vm()->game() != GI_LOL) + SWAP(vm()->_dialogueButtonLabelCol1, vm()->_dialogueButtonLabelCol2); + + vm()->removeInputTop(); +} + +void TextDisplayer_Eob::displayWaitButton() { + vm()->_dialogueNumButtons = 1; + vm()->_dialogueButtonString[0] = _pageBreakString; + vm()->_dialogueButtonString[1] = 0; + vm()->_dialogueButtonString[2] = 0; + vm()->_dialogueHighlightedButton = 0; + + static const uint16 posX[] = { 221, 76 }; + static const uint8 posY[] = { 189, 162 }; + + vm()->_dialogueButtonPosX = &posX[_waitButtonMode]; + vm()->_dialogueButtonPosY = &posY[_waitButtonMode]; + vm()->_dialogueButtonYoffs = 0; + + SWAP(_vm->_dialogueButtonLabelCol1, _vm->_dialogueButtonLabelCol2); + vm()->drawDialogueButtons(); + + if (!vm()->shouldQuit()) + vm()->removeInputTop(); + + while (!vm()->processDialogue() && !vm()->shouldQuit()) {} + SWAP(_vm->_dialogueButtonLabelCol1, _vm->_dialogueButtonLabelCol2); +} + +} // End of namespace Kyra + +#endif // (ENABLE_EOB || ENABLE_LOL) diff --git a/engines/kyra/text_eob.h b/engines/kyra/text_eob.h new file mode 100644 index 0000000000..eb4957448b --- /dev/null +++ b/engines/kyra/text_eob.h @@ -0,0 +1,112 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are to 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. + * + */ + +#if defined(ENABLE_EOB) || defined(ENABLE_LOL) + +#ifndef KYRA_TEXT_EOB_H +#define KYRA_TEXT_EOB_H + +#include "common/scummsys.h" + +namespace Kyra { + +class Screen; +class LolEobBaseEngine; + +class TextDisplayer_Eob { +public: + TextDisplayer_Eob(LolEobBaseEngine *engine, Screen *sScreen); + virtual ~TextDisplayer_Eob(); + + virtual void setupField(int dim, bool mode); + + void printDialogueText(int stringId, const char *pageBreakString); + void printDialogueText(const char *str, bool wait = false); + void printMessage(const char *str, int textColor = -1, ...); + + int clearDim(int dim); + void clearCurDim(); + + void resetDimTextPositions(int dim); + void resetPageBreakString(); + void setPageBreakFlag(); + void removePageBreakFlag(); + + void allowPageBreak(bool mode) { _allowPageBreak = mode; } + void setWaitButtonMode(int mode) { _waitButtonMode = mode; } + int lineCount() { return _lineCount; } + +protected: + virtual LolEobBaseEngine *vm() { return _vm; } + virtual Screen *screen() { return _screen; } + + void displayText(char *str, ...); + char parseCommand(); + void readNextPara(); + void printLine(char *str); + virtual void textPageBreak(); + void displayWaitButton(); + + char *_dialogueBuffer; + + char *_tempString1; + char *_tempString2; + char *_currentLine; + char _ctrl[3]; + + uint16 _lineWidth; + uint32 _numCharsTotal; + uint32 _numCharsLeft; + uint32 _numCharsPrinted; + + bool _printFlag; + bool _sjisLineBreakFlag; + + char _pageBreakString[20]; + char _scriptParaString[11]; + int _lineCount; + + bool _allowPageBreak; + int _waitButtonSpace; + int _waitButtonMode; + + static const char _pageBreakDefault[3][5]; + + struct TextDimData { + uint8 color1; + uint8 color2; + uint16 column; + uint8 line; + }; + + TextDimData *_textDimData; + +private: + LolEobBaseEngine *_vm; + Screen *_screen; +}; + +} // End of namespace Kyra + +#endif + +#endif // ENABLE_EOB || ENABLE_LOL diff --git a/engines/kyra/text_lol.cpp b/engines/kyra/text_lol.cpp index 1c2167b892..3127e6d54b 100644 --- a/engines/kyra/text_lol.cpp +++ b/engines/kyra/text_lol.cpp @@ -32,36 +32,18 @@ namespace Kyra { -TextDisplayer_LoL::TextDisplayer_LoL(LoLEngine *vm, Screen_LoL *screen) : _vm(vm), _screen(screen), - _scriptTextParameter(0), _lineCount(0), _printFlag(false), _lineWidth(0), _numCharsTotal(0), - _numCharsLeft(0), _numCharsPrinted(0), _sjisLineBreakFlag(false) { +TextDisplayer_LoL::TextDisplayer_LoL(LoLEngine *engine, Screen_LoL *screenLoL) : TextDisplayer_Eob(engine, engine->screen()), + _vm(engine), _screen(screenLoL), _scriptTextParameter(0) { memset(_stringParameters, 0, 15 * sizeof(char *)); _buffer = new char[600]; memset(_buffer, 0, 600); - _dialogueBuffer = new char[1024]; - memset(_dialogueBuffer, 0, 1024); - - _currentLine = new char[85]; - memset(_currentLine, 0, 85); - - _textDimData = new TextDimData[_screen->screenDimTableCount()]; - - for (int i = 0; i < _screen->screenDimTableCount(); i++){ - const ScreenDim *d = _screen->getScreenDim(i); - _textDimData[i].color1 = d->unk8; - _textDimData[i].color2 = d->unkA; - _textDimData[i].line = d->unkC; - _textDimData[i].column = d->unkE; - } + _waitButtonSpace = 0; } TextDisplayer_LoL::~TextDisplayer_LoL() { delete[] _buffer; - delete[] _dialogueBuffer; - delete[] _currentLine; - delete[] _textDimData; } void TextDisplayer_LoL::setupField(bool mode) { @@ -151,20 +133,6 @@ void TextDisplayer_LoL::expandField() { } } -int TextDisplayer_LoL::clearDim(int dim) { - int res = _screen->curDimIndex(); - _screen->setScreenDim(dim); - _textDimData[dim].color1 = _screen->_curDim->unk8; - _textDimData[dim].color2 = _screen->_curDim->unkA; - clearCurDim(); - return res; -} - -void TextDisplayer_LoL::resetDimTextPositions(int dim) { - _textDimData[dim].column = 0; - _textDimData[dim].line = 0; -} - void TextDisplayer_LoL::printDialogueText(int dim, char *str, EMCState *script, const uint16 *paramList, int16 paramIndex) { int oldDim = 0; @@ -201,10 +169,9 @@ void TextDisplayer_LoL::printDialogueText(int dim, char *str, EMCState *script, displayText(_dialogueBuffer); _screen->setScreenDim(oldDim); - _lineCount = 0; _screen->setCurPage(cp); _screen->setFont(of); - + _lineCount = 0; _vm->_fadeText = false; } @@ -366,465 +333,17 @@ void TextDisplayer_LoL::preprocessString(char *str, EMCState *script, const uint *dst = 0; } -void TextDisplayer_LoL::displayText(char *str, ...) { - const bool isPc98 = (_vm->gameFlags().platform == Common::kPlatformPC98); - - _printFlag = false; - - _lineWidth = 0; - _numCharsLeft = 0; - _numCharsPrinted = 0; - - _tempString1 = str; - _tempString2 = 0; - - _currentLine[0] = 0; - memset(_ctrl, 0, 3); - - char c = parseCommand(); - - va_list args; - va_start(args, str); - - const ScreenDim *sd = _screen->_curDim; - int sdx = _screen->curDimIndex(); - - bool pc98PrintFlag = (isPc98 && (sdx == 3 || sdx == 4 || sdx == 5 || sdx == 15)) ? true : false; - uint16 charsPerLine = (sd->w << 3) / (_screen->getFontWidth() + _screen->_charWidth); - - while (c) { - char a = tolower(_ctrl[1]); - - if (!_tempString2 && c == '%') { - if (a == 'd') { - snprintf(_scriptParaString, 11, "%d", va_arg(args, int)); - _tempString2 = _scriptParaString; - } else if (a == 's') { - _tempString2 = va_arg(args, char *); - } else { - break; - } - - _ctrl[0] = _ctrl[2]; - _ctrl[2] = _ctrl[1] = 0; - c = parseCommand(); - } - - if (isPc98) { - uint8 cu = (uint8) c; - if (cu >= 0xE0 || (cu > 0x80 && cu < 0xA0)) { - _currentLine[_numCharsLeft++] = c; - _currentLine[_numCharsLeft++] = parseCommand(); - _currentLine[_numCharsLeft] = '\0'; - _lineWidth += 8; - if ((_textDimData[sdx].column + _lineWidth) > (sd->w << 3)) - printLine(_currentLine); - c = parseCommand(); - continue; - } - } - - uint16 dv = _textDimData[sdx].column / (_screen->getFontWidth() + _screen->_charWidth); - - switch (c - 1) { - case 0: - printLine(_currentLine); - textPageBreak(); - _numCharsPrinted = 0; - break; - - case 1: - printLine(_currentLine); - _textDimData[sdx].color2 = parseCommand(); - break; - - case 5: - printLine(_currentLine); - _textDimData[sdx].color1 = parseCommand(); - break; - - case 8: - printLine(_currentLine); - dv = _textDimData[sdx].column / (_screen->getFontWidth() + _screen->_charWidth); - dv = ((dv + 8) & 0xfff8) - 1; - if (dv >= charsPerLine) - dv = 0; - _textDimData[sdx].column = (_screen->getFontWidth() + _screen->_charWidth) * dv; - break; - - case 11: - // TODO (UNUSED) - break; - - case 12: - if (isPc98) - _sjisLineBreakFlag = true; - printLine(_currentLine); - _sjisLineBreakFlag = false; - _lineCount++; - _textDimData[sdx].column = 0; - _textDimData[sdx].line++; - break; - - case 18: - // TODO (UNUSED) - break; - - case 23: - // TODO (UNUSED) - break; - - case 24: - // TODO (UNUSED) - break; - - case 26: - // TODO (UNUSED) - break; - - case 28: - // TODO (UNUSED) - break; - - default: - _lineWidth += (pc98PrintFlag ? 4 : _screen->getCharWidth((uint8)c)); - _currentLine[_numCharsLeft++] = c; - _currentLine[_numCharsLeft] = 0; - - if ((_textDimData[sdx].column + _lineWidth) > (sd->w << 3)) - printLine(_currentLine); - } - - c = parseCommand(); - } - - va_end(args); - - if (_numCharsLeft) - printLine(_currentLine); -} - -char TextDisplayer_LoL::parseCommand() { - if (!_ctrl[1]) - readNextPara(); - - char res = _ctrl[1]; - _ctrl[1] = _ctrl[2]; - _ctrl[2] = 0; - - if (!_ctrl[1]) - readNextPara(); - - return res; +LolEobBaseEngine *TextDisplayer_LoL::vm() { + return _vm; } -void TextDisplayer_LoL::readNextPara() { - char d = 0; - - if (_tempString2) { - if (*_tempString2) { - d = *_tempString2++; - } else { - _tempString2 = 0; - d = _ctrl[0]; - } - } - - if (!d && _tempString1) { - if (*_tempString1) - d = *_tempString1++; - else - _tempString1 = 0; - } - - _ctrl[1] = d; - _ctrl[2] = 0; -} - -void TextDisplayer_LoL::printLine(char *str) { - const bool isPc98 = (_vm->gameFlags().platform == Common::kPlatformPC98); - const ScreenDim *sd = _screen->_curDim; - int sdx = _screen->curDimIndex(); - bool pc98PrintFlag = (isPc98 && (sdx == 3 || sdx == 4 || sdx == 5 || sdx == 15)) ? true : false; - - int fh = (_screen->_currentFont == Screen::FID_SJIS_FNT) ? 9 : (_screen->getFontHeight() + _screen->_charOffset); - int lines = (sd->h - _screen->_charOffset) / fh; - - while (_textDimData[sdx].line >= lines) { - if (lines <= _lineCount) { - _lineCount = 0; - textPageBreak(); - _numCharsPrinted = 0; - } - - int h1 = ((sd->h / fh) - 1) * fh; - int h2 = sd->h - fh; - - if (h2) - _screen->copyRegion(sd->sx << 3, sd->sy + fh, sd->sx << 3, sd->sy, sd->w << 3, h2, _screen->_curPage, _screen->_curPage, Screen::CR_NO_P_CHECK); - - _screen->fillRect(sd->sx << 3, sd->sy + h1, (sd->sx + sd->w - 1) << 3, sd->sy + sd->h - 1, _textDimData[sdx].color2); - if (_textDimData[sdx].line) - _textDimData[sdx].line--; - } - - int x1 = (sd->sx << 3) + _textDimData[sdx].column; - int y = sd->sy + (pc98PrintFlag ? (_textDimData[sdx].line << 3) : (fh * _textDimData[sdx].line)); - int w = sd->w << 3; - int lw = _lineWidth; - int s = _numCharsLeft; - char c = 0; - - if (pc98PrintFlag) { - bool ct = true; - - if ((lw + _textDimData[sdx].column) > w) { - if ((lines - 1) <= _lineCount) - // cut off line to leave space for "MORE" button - w -= 80; - } else { - if (!_sjisLineBreakFlag || (_lineCount + 1 < lines - 1)) - ct = false; - else - // cut off line to leave space for "MORE" button - w -= 80; - } - - if (ct) { - w -= _textDimData[sdx].column; - - int n2 = 0; - int n1 = (w / 4) - 1; - - while (n2 < n1 && n2 < s) { - c = str[n2]; - uint8 cu = (uint8) c; - if (cu >= 0xE0 || (cu > 0x80 && cu < 0xA0)) - n2++; - n2++; - } - s = n2; - } - } else { - if ((lw + _textDimData[sdx].column) > w) { - if ((lines - 1) <= _lineCount) - // cut off line to leave space for "MORE" button - w -= (10 * (_screen->getFontWidth() + _screen->_charWidth)); - - w -= _textDimData[sdx].column; - - int n2 = 0; - int n1 = s - 1; - - while (n1 > 0) { - //cut off line after last space - c = str[n1]; - - lw -= _screen->getCharWidth((uint8)c); - - if (!n2 && lw <= w) - n2 = n1; - - if (n2 && c == ' ') { - s = n1; - _printFlag = false; - break; - } - n1--; - } - - if (!n1) { - if (_textDimData[sdx].column && !_printFlag) { - s = lw = 0; - _printFlag = true; - } else { - s = n2; - } - } - } - } - - c = str[s]; - str[s] = 0; - - uint8 col = _textDimData[sdx].color1; - if (isPc98 && (sdx == 2 || sdx == 3 || sdx == 4 || sdx == 5 || sdx == 15)) { - switch (_textDimData[sdx].color1) { - case 0x88: - col = 0x41; - break; - case 0x55: - col = 0x81; - break; - case 0xaa: - col = 0x21; - break; - case 0x99: - col = 0xa1; - break; - case 0x33: - col = 0xe1; - break; - case 0x18: - col = 0x61; - break; - default: - col = 1; - break; - } - _screen->printText(str, x1 & ~3, (y + 8) & ~7, col, 0); - } else { - _screen->printText(str, x1, y, col, _textDimData[sdx].color2); - } - - _textDimData[sdx].column += lw; - _numCharsPrinted += strlen(str); - - str[s] = c; - - if (c == ' ') - s++; - - if (str[s] == ' ') - s++; - - uint32 len = strlen(&str[s]); - for (uint32 i = 0; i < len; i++) - str[i] = str[s + i]; - str[len] = 0; - - _numCharsLeft = strlen(str); - _lineWidth = pc98PrintFlag ? (_numCharsLeft << 2) : _screen->getTextWidth(str); - - if (!_numCharsLeft && _textDimData[sdx].column < (sd->w << 3)) - return; - - _textDimData[sdx].column = 0; - _textDimData[sdx].line++; - _lineCount++; - - printLine(str); +Screen *TextDisplayer_LoL::screen() { + return _screen; } void TextDisplayer_LoL::textPageBreak() { - int cp = _screen->setCurPage(0); - Screen::FontId cf = _screen->setFont(_vm->gameFlags().use16ColorMode ? Screen::FID_SJIS_FNT : Screen::FID_6_FNT); - - _vm->_timer->pauseSingleTimer(11, true); - - _vm->_fadeText = false; - int resetPortraitAfterSpeechAnim = 0; - int updatePortraitSpeechAnimDuration = 0; - - if (_vm->_updateCharNum != -1) { - resetPortraitAfterSpeechAnim = _vm->_resetPortraitAfterSpeechAnim; - _vm->_resetPortraitAfterSpeechAnim = 0; - updatePortraitSpeechAnimDuration = _vm->_updatePortraitSpeechAnimDuration; - if (_vm->_updatePortraitSpeechAnimDuration > 36) - _vm->_updatePortraitSpeechAnimDuration = 36; - } - - uint32 speechPartTime = 0; - if (_vm->speechEnabled() && _vm->_activeVoiceFileTotalTime && _numCharsTotal) - speechPartTime = _vm->_system->getMillis() + ((_numCharsPrinted * _vm->_activeVoiceFileTotalTime) / _numCharsTotal); - - const ScreenDim *dim = _screen->getScreenDim(_screen->curDimIndex()); - - int x = ((dim->sx + dim->w) << 3) - 77; - int y = 0; - - if (_vm->_needSceneRestore && (_vm->_updateFlags & 2)) { - if (_vm->_currentControlMode || !(_vm->_updateFlags & 2)) { - y = dim->sy + dim->h - 5; - } else { - x += 6; - y = dim->sy + dim->h - 2; - } - } else { - y = dim->sy + dim->h - 10; - } - - char *txt = _vm->getLangString(0x4073); - if (_vm->gameFlags().use16ColorMode) { - _vm->gui_drawBox(x + 8, (y & ~7) - 1, 66, 10, 0xee, 0xcc, -1); - _vm->_screen->printText(txt, (x + 37 - (strlen(txt) << 1) + 4) & ~3, (y + 2) & ~7, 0xc1, 0); - } else { - _vm->gui_drawBox(x, y, 74, 9, 136, 251, -1); - _vm->_screen->printText(txt, x + 37 - (_vm->_screen->getTextWidth(txt) >> 1), y + 2, 144, 0); - } - - _vm->removeInputTop(); - - bool loop = true; - bool target = false; - - do { - int inputFlag = _vm->checkInput(0, false) & 0xFF; - _vm->removeInputTop(); - - while (!inputFlag) { - _vm->update(); - - if (_vm->speechEnabled()) { - if (((_vm->_system->getMillis() > speechPartTime) || (_vm->snd_updateCharacterSpeech() != 2)) && speechPartTime) { - loop = false; - inputFlag = _vm->_keyMap[Common::KEYCODE_RETURN]; - break; - } - } - - inputFlag = _vm->checkInput(0, false) & 0xFF; - _vm->removeInputTop(); - } - - _vm->gui_notifyButtonListChanged(); - - if (inputFlag == _vm->_keyMap[Common::KEYCODE_SPACE] || inputFlag == _vm->_keyMap[Common::KEYCODE_RETURN]) { - loop = false; - } else if (inputFlag == 199 || inputFlag == 201) { - if (_vm->posWithinRect(_vm->_mouseX, _vm->_mouseY, x, y, x + 74, y + 9)) - target = true; - } else if (inputFlag == 200 || inputFlag == 202) { - if (target) - loop = false; - } - } while (loop); - - - if (_vm->gameFlags().use16ColorMode) - _screen->fillRect(x + 8, y, x + 57, y + 9, _textDimData[_screen->curDimIndex()].color2); - else - _screen->fillRect(x, y, x + 73, y + 8, _textDimData[_screen->curDimIndex()].color2); - - clearCurDim(); - - _vm->_timer->pauseSingleTimer(11, false); - - if (_vm->_updateCharNum != -1) { - _vm->_resetPortraitAfterSpeechAnim = resetPortraitAfterSpeechAnim; - if (updatePortraitSpeechAnimDuration > 36) - updatePortraitSpeechAnimDuration -= 36; - else - updatePortraitSpeechAnimDuration >>= 1; - - _vm->_updatePortraitSpeechAnimDuration = updatePortraitSpeechAnimDuration; - } - - _screen->setFont(cf); - _screen->setCurPage(cp); - _vm->removeInputTop(); -} - -void TextDisplayer_LoL::clearCurDim() { - int d = _screen->curDimIndex(); - const ScreenDim *tmp = _screen->getScreenDim(d); - if (_vm->gameFlags().use16ColorMode) { - _screen->fillRect(tmp->sx << 3, tmp->sy, ((tmp->sx + tmp->w) << 3) - 2, (tmp->sy + tmp->h) - 2, _textDimData[d].color2); - } else - _screen->fillRect(tmp->sx << 3, tmp->sy, ((tmp->sx + tmp->w) << 3) - 1, (tmp->sy + tmp->h) - 1, _textDimData[d].color2); - - _lineCount = 0; - _textDimData[d].column = _textDimData[d].line = 0; + strcpy(_pageBreakString, _vm->getLangString(0x4073)); + TextDisplayer_Eob::textPageBreak(); } } // End of namespace Kyra diff --git a/engines/kyra/text_lol.h b/engines/kyra/text_lol.h index 3e59bc90fe..0dcc4b73b3 100644 --- a/engines/kyra/text_lol.h +++ b/engines/kyra/text_lol.h @@ -20,80 +20,52 @@ * */ -#ifdef ENABLE_LOL - #ifndef KYRA_TEXT_LOL_H #define KYRA_TEXT_LOL_H +#if defined(ENABLE_EOB) || defined(ENABLE_LOL) +#include "kyra/text_eob.h" +#endif #include "common/scummsys.h" +#ifdef ENABLE_LOL + namespace Kyra { class Screen_LoL; class LoLEngine; struct EMCState; -class TextDisplayer_LoL { -friend class LoLEngine; +class TextDisplayer_LoL : public TextDisplayer_Eob { public: - TextDisplayer_LoL(LoLEngine *vm, Screen_LoL *screen); + TextDisplayer_LoL(LoLEngine *engine, Screen_LoL *screenLoL); ~TextDisplayer_LoL(); void setupField(bool mode); void expandField(); - int clearDim(int dim); - void resetDimTextPositions(int dim); - void printDialogueText(int dim, char *str, EMCState *script, const uint16 *paramList, int16 paramIndex); void printMessage(uint16 type, const char *str, ...) GCC_PRINTF(3, 4); int16 _scriptTextParameter; private: - void displayText(char *str, ...); - char parseCommand(); - void readNextPara(); - void printLine(char *str); + LolEobBaseEngine *vm(); + Screen *screen(); + void preprocessString(char *str, EMCState *script, const uint16 *paramList, int16 paramIndex); void textPageBreak(); - void clearCurDim(); - char *_stringParameters[15]; char *_buffer; - char *_dialogueBuffer; - char *_tempString1; - char *_tempString2; - char *_currentLine; - char _ctrl[3]; - - char _scriptParaString[11]; - - uint16 _lineWidth; - int _lineCount; - uint32 _numCharsTotal; - uint32 _numCharsLeft; - uint32 _numCharsPrinted; - - bool _printFlag; - bool _sjisLineBreakFlag; LoLEngine *_vm; Screen_LoL *_screen; - - struct TextDimData { - uint8 color1; - uint8 color2; - uint16 column; - uint8 line; - }; - - TextDimData *_textDimData; }; } // End of namespace Kyra +#endif // ENABLE_LOL + #endif -#endif // ENABLE_LOL diff --git a/engines/kyra/timer.cpp b/engines/kyra/timer.cpp index 9834646a45..2ab2b621f3 100644 --- a/engines/kyra/timer.cpp +++ b/engines/kyra/timer.cpp @@ -217,7 +217,7 @@ void TimerManager::pauseSingleTimer(uint8 id, bool p) { bool TimerManager::isEnabled(uint8 id) const { CIterator timer = Common::find_if(_timers.begin(), _timers.end(), TimerEqual(id)); if (timer != _timers.end()) - return (timer->enabled == 1); + return (timer->enabled & 1); warning("TimerManager::isEnabled: No timer %d", id); return false; diff --git a/engines/kyra/timer_eob.cpp b/engines/kyra/timer_eob.cpp new file mode 100644 index 0000000000..92c7f34ee6 --- /dev/null +++ b/engines/kyra/timer_eob.cpp @@ -0,0 +1,378 @@ +/* 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. + * + */ + +#if defined(ENABLE_EOB) || defined(ENABLE_LOL) + +#include "kyra/eobcommon.h" +#include "kyra/timer.h" + +#include "common/system.h" + +namespace Kyra { + +void LolEobBaseEngine::enableSysTimer(int sysTimer) { + if (sysTimer != 2) + return; + + for (int i = 0; i < getNumClock2Timers(); i++) + _timer->pauseSingleTimer(getClock2Timer(i), false); +} + +void LolEobBaseEngine::disableSysTimer(int sysTimer) { + if (sysTimer != 2) + return; + + for (int i = 0; i < getNumClock2Timers(); i++) + _timer->pauseSingleTimer(getClock2Timer(i), true); +} + +void LolEobBaseEngine::enableTimer(int id) { + _timer->enable(id); + _timer->setCountdown(id, _timer->getDelay(id)); +} + +void LolEobBaseEngine::timerProcessDoors(int timerNum) { + for (int i = 0; i < 3; i++) { + uint16 b = _openDoorState[i].block; + if (!b) + continue; + + int v = _openDoorState[i].state; + int c = _openDoorState[i].wall; + + _levelBlockProperties[b].walls[c] += v; + _levelBlockProperties[b].walls[c ^ 2] += v; + + int snd = 3; + int flg = _wllWallFlags[_levelBlockProperties[b].walls[c]]; + if (flg & 0x20) + snd = 5; + else if (v == -1) + snd = 4; + + if (_flags.gameID == GI_LOL) { + if (!(_updateFlags & 1)) { + snd_processEnvironmentalSoundEffect(snd + 28, b); + if (!checkSceneUpdateNeed(b)) + updateEnvironmentalSfx(0); + } + } else { + checkSceneUpdateNeed(b); + updateEnvironmentalSfx(snd); + } + + if (flg & 0x30) + _openDoorState[i].block = 0; + } +} + +} // namespace Kyra + +#endif +#ifdef ENABLE_EOB + +namespace Kyra { + +#define TimerV2(x) new Common::Functor1Mem<int, void, EobCoreEngine>(this, &EobCoreEngine::x) + +void EobCoreEngine::setupTimers() { + _timer->addTimer(0, TimerV2(timerProcessCharacterExchange), 9, false); + _timer->addTimer(1, TimerV2(timerProcessFlyingObjects), 3, true); + _timer->addTimer(0x20, TimerV2(timerProcessMonsters), 20, true); + _timer->addTimer(0x21, TimerV2(timerProcessMonsters), 20, true); + _timer->addTimer(0x22, TimerV2(timerProcessMonsters), 20, true); + _timer->addTimer(0x23, TimerV2(timerProcessMonsters), 20, true); + _timer->setNextRun(0x21, _system->getMillis() + 7 * _tickLength); + _timer->setNextRun(0x22, _system->getMillis() + 14 * _tickLength); + _timer->setNextRun(0x23, _system->getMillis() + 14 * _tickLength); + _timer->addTimer(0x30, TimerV2(timerSpecialCharacterUpdate), 50, false); + _timer->addTimer(0x31, TimerV2(timerSpecialCharacterUpdate), 50, false); + _timer->addTimer(0x32, TimerV2(timerSpecialCharacterUpdate), 50, false); + _timer->addTimer(0x33, TimerV2(timerSpecialCharacterUpdate), 50, false); + _timer->addTimer(0x34, TimerV2(timerSpecialCharacterUpdate), 50, false); + _timer->addTimer(0x35, TimerV2(timerSpecialCharacterUpdate), 50, false); + _timer->addTimer(4, TimerV2(timerProcessDoors), 5, true); + _timer->addTimer(5, TimerV2(timerUpdateTeleporters), 10, true); + _timer->addTimer(6, TimerV2(timerUpdateFoodStatus), 1080, true); + _timer->addTimer(7, TimerV2(timerUpdateMonsterIdleAnim), 25, true); +} + +void EobCoreEngine::setCharEventTimer(int charIndex, uint32 countdown, int evnt, int updateExistingTimer) { + uint32 ntime = _system->getMillis() + countdown * _tickLength; + uint8 timerId = 0x30 | (charIndex & 0x0f); + EobCharacter *c = &_characters[charIndex]; + + if (!_timer->isEnabled(timerId)) { + c->timers[0] = ntime; + c->events[0] = evnt; + _timer->setCountdown(timerId, countdown); + enableTimer(timerId); + return; + } + + if (ntime < _timer->getNextRun(timerId)) + _timer->setNextRun(timerId, ntime); + + if (updateExistingTimer) { + bool br = false; + int d = -1; + + for (int i = 0; i < 10 && br == false; i++) { + if (d == -1 && !c->timers[i]) + d = i; + + if (!br && c->events[i] == evnt) { + d = i; + br = true; + } + } + + c->timers[d] = ntime; + c->events[d] = evnt; + } else { + for (int i = 0; i < 10; i++) { + if (c->timers[i]) + continue; + c->timers[i] = ntime; + c->events[i] = evnt; + return; + } + } +} + +void EobCoreEngine::deleteCharEventTimer(int charIndex, int evnt) { + EobCharacter *c = &_characters[charIndex]; + for (int i = 0; i < 10; i++) { + if (c->events[i] == evnt) { + c->events[i] = 0; + c->timers[i] = 0; + } + } + setupCharacterTimers(); +} + +void EobCoreEngine::setupCharacterTimers() { + for (int i = 0; i < 6; i++) { + EobCharacter *c = &_characters[i]; + if (!testCharacter(i, 1)) + continue; + + uint32 nextTimer = 0xffffffff; + + for (int ii = 0; ii < 10; ii++) { + if (c->timers[ii] < nextTimer) + nextTimer = c->timers[ii]; + } + uint32 ctime = _system->getMillis(); + + if (nextTimer == 0xffffffff) + _timer->disable(0x30 | i); + else { + enableTimer(0x30 | i); + _timer->setCountdown(0x30 | i, (nextTimer - ctime) / _tickLength); + } + } +} + +void EobCoreEngine::timerProcessCharacterExchange(int timerNum) { + _charExchangeSwap ^= 1; + if (_charExchangeSwap) { + int index = _exchangeCharacterId; + _exchangeCharacterId = -1; + gui_drawCharPortraitWithStats(index); + _exchangeCharacterId = index; + } else { + gui_drawCharPortraitWithStats(_exchangeCharacterId); + } +} + +void EobCoreEngine::timerProcessFlyingObjects(int timerNum) { + static const uint8 dirPosIndex[] = { 0x82, 0x83, 0x00, 0x01, 0x01, 0x80, 0x03, 0x82, 0x02, 0x03, 0x80, 0x81, 0x81, 0x00, 0x83, 0x02 }; + for (int i = 0; i < 10; i++) { + EobFlyingObject *fo = &_flyingObjects[i]; + if (!fo->enable) + continue; + + bool endFlight = fo->distance ? false : true; + + uint8 pos = dirPosIndex[(fo->direction << 2) + (fo->curPos & 3)]; + uint16 bl = fo->curBlock; + bool newBl = (pos & 0x80) ? true : false; + + if (newBl) { + bl = calcNewBlockPosition(fo->curBlock, fo->direction); + pos &= 3; + fo->u2 = 0; + } + + if (updateObjectFlight(fo, bl, pos)) { + if (newBl) + runLevelScript(bl, 0x10); + if (updateFlyingObjectHitTest(fo, bl, pos)) + endFlight = true; + } else { + if (fo->flags & 0x20) { + if (!updateFlyingObjectHitTest(fo, fo->curBlock, fo->curPos)) + updateFlyingObject_s3(fo); + } + endFlight = true; + } + + if (endFlight) + endObjectFlight(fo); + + _sceneUpdateRequired = true; + } +} + +void EobCoreEngine::timerProcessMonsters(int timerNum) { + updateMonsters(timerNum & 0x0f); +} + + +void EobCoreEngine::timerSpecialCharacterUpdate(int timerNum) { + int charIndex = timerNum & 0x0f; + EobCharacter *c = &_characters[charIndex]; + uint32 ctime = _system->getMillis(); + + for (int i = 0; i < 10; i++) { + if (!c->timers[i]) + continue; + if (c->timers[i] > ctime) + continue; + + c->timers[i] = 0; + int evt = c->events[i]; + + if (evt < 0) { + removeCharacterEffect(evt, charIndex, 1); + continue; + } + + switch (evt) { + case 2: + case 3: + setCharEventTimer(charIndex, (c->effectFlags & 0x10000) ? 9 : 36, evt + 2, 1); + case 0: + case 1: + case 4: + case 5: + setWeaponSlotStatus(charIndex, evt / 2, evt & 1); + break; + + case 6: + c->damageTaken = 0; + gui_drawCharPortraitWithStats(charIndex); + break; + + case 7: + _txt->printMessage(_characterStatusStrings7[0], -1, c->name); + c->strengthCur = c->strengthMax; + c->strengthExtCur = c->strengthExtMax; + if (_currentControlMode == 2) + gui_drawCharPortraitWithStats(charIndex); + break; + + case 8: + if (c->flags & 2) { + calcAndInflictCharacterDamage(charIndex, 0, 0, 5, 0x400, 5, 3); + setCharEventTimer(charIndex, 546, 8, 1); + } else { + c->flags &= 0xfd; + gui_drawCharPortraitWithStats(charIndex); + } + break; + + case 9: + if (c->flags & 4) { + _txt->printMessage(_characterStatusStrings9[0], -1, c->name); + c->flags &= 0xfb; + gui_drawCharPortraitWithStats(charIndex); + } + break; + + case 11: + if (c->disabledSlots & 4) { + c->disabledSlots &= 0xfb; + if (_openBookChar == charIndex && _updateFlags) + gui_drawSpellbook(); + } + break; + + case 12: + c->effectFlags &= ~0x1000; + _txt->printMessage(_characterStatusStrings12[0], -1, c->name); + break; + + default: + break; + } + } + + uint32 nextTimer = (uint32)(-1); + for (int i = 0; i < 10; i++) { + if (c->timers[i] && c->timers[i] < nextTimer) + nextTimer = c->timers[i]; + } + + if (nextTimer == (uint32)(-1)) + _timer->disable(timerNum); + else + _timer->setCountdown(timerNum, (nextTimer - ctime) / _tickLength); +} + +void EobCoreEngine::timerUpdateTeleporters(int timerNum) { + _teleporterPulse ^= 1; + for (int i = 0; i < 18; i++) { + uint8 w = _visibleBlocks[i]->walls[_sceneDrawVarDown]; + if (w == 44 || w == 74) { + _sceneUpdateRequired = true; + return; + } + } +} + +void EobCoreEngine::timerUpdateFoodStatus(int timerNum) { + for (int i = 0; i < 6; i++) { + if (checkInventoryForRings(i, 2)) + continue; + EobCharacter *c = &_characters[i]; + if (c->food != 0 && c->flags & 1 && c->hitPointsCur > -10) { + c->food--; + gui_drawFoodStatusGraph(i); + } + } +} + +void EobCoreEngine::timerUpdateMonsterIdleAnim(int timerNum) { + for (int i = 0; i < 18; i++) { + EobMonsterInPlay *m = &_monsters[i]; + if (m->mode == 7 || m->mode == 10 || (m->flags & 0x20) || (rollDice(1, 2, 0) != 1)) + continue; + m->idleAnimState = (rollDice(1, 2, 0) << 4) | rollDice(1, 2, 0); + checkSceneUpdateNeed(m->block); + } +} + +} // End of namespace Kyra + +#endif // ENABLE_EOB diff --git a/engines/kyra/timer_lol.cpp b/engines/kyra/timer_lol.cpp index 3221556e6d..5a6677a4f6 100644 --- a/engines/kyra/timer_lol.cpp +++ b/engines/kyra/timer_lol.cpp @@ -47,58 +47,6 @@ void LoLEngine::setupTimers() { _timer->addTimer(11, TimerV2(timerFadeMessageText), 360, false); } -void LoLEngine::enableTimer(int id) { - _timer->enable(id); - _timer->setCountdown(id, _timer->getDelay(id)); -} - -void LoLEngine::enableSysTimer(int sysTimer) { - if (sysTimer != 2) - return; - - for (int i = 0; i < _numClock2Timers; i++) - _timer->pauseSingleTimer(_clock2Timers[i], false); -} - -void LoLEngine::disableSysTimer(int sysTimer) { - if (sysTimer != 2) - return; - - for (int i = 0; i < _numClock2Timers; i++) - _timer->pauseSingleTimer(_clock2Timers[i], true); -} - -void LoLEngine::timerProcessDoors(int timerNum) { - for (int i = 0; i < 3; i++) { - uint16 b = _openDoorState[i].block; - if (!b) - continue; - - int v = _openDoorState[i].state; - int c = _openDoorState[i].wall; - - _levelBlockProperties[b].walls[c] += v; - _levelBlockProperties[b].walls[c ^ 2] += v; - - int snd = 31; - - int flg = _wllWallFlags[_levelBlockProperties[b].walls[c]]; - if (flg & 0x20) - snd = 33; - else if (v == -1) - snd = 32; - - if (!(_updateFlags & 1)) { - snd_processEnvironmentalSoundEffect(snd, b); - if (!checkSceneUpdateNeed(b)) - updateEnvironmentalSfx(0); - } - - if (flg & 0x30) - _openDoorState[i].block = 0; - } -} - void LoLEngine::timerProcessMonsters(int timerNum) { for (int i = timerNum & 0x0f; i < 30; i += 2) updateMonster(&_monsters[i]); |