diff options
Diffstat (limited to 'engines/kyra/scene_eob.cpp')
-rw-r--r-- | engines/kyra/scene_eob.cpp | 890 |
1 files changed, 890 insertions, 0 deletions
diff --git a/engines/kyra/scene_eob.cpp b/engines/kyra/scene_eob.cpp new file mode 100644 index 0000000000..3db055db90 --- /dev/null +++ b/engines/kyra/scene_eob.cpp @@ -0,0 +1,890 @@ +/* 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/script_eob.h" +#include "kyra/timer.h" +#include "kyra/sound.h" + +#include "common/system.h" + + +namespace Kyra { + +void EoBCoreEngine::loadLevel(int level, int sub) { + _currentLevel = level; + _currentSub = sub; + uint32 end = _system->getMillis() + 500; + + readLevelFileData(level); + + Common::String gfxFile; + // Work around for issue with corrupt (incomplete) monster property data + // when loading a savegame saved in a sub level + for (int i = 0; i <= sub; i++) + gfxFile = initLevelData(i); + + 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.c_str(), (_flags.gameID == GI_EOB1) ? _cgaMappingLevel[_cgaLevelMappingIndex[level - 1]] : 0); + _screen->loadEoBBitmap("INVENT", _cgaMappingInv, 5, 3, 2); + delayUntil(end); + snd_stopSound(); + + enableSysTimer(2); + _sceneDrawPage1 = 2; + _sceneDrawPage2 = 1; + _screen->setCurPage(0); +} + +void EoBCoreEngine::readLevelFileData(int level) { + Common::String file; + Common::SeekableReadStream *s = 0; + static const char *suffix[] = { "INF", "DRO", "ELO", 0 }; + + for (const char *const *sf = suffix; *sf && !s; sf++) { + file = Common::String::format("LEVEL%d.%s", level, *sf); + s = _res->createReadStream(file); + } + + if (!s) + error("Failed to load 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.c_str(), 5, 5, 0); + } + } + + if (s) { + s->seek(0); + _screen->loadFileDataToPage(s, 5, 15000); + delete s; + } +} + +Common::String EoBCoreEngine::initLevelData(int sub) { + const uint8 *data = _screen->getCPagePtr(5) + 2; + const uint8 *pos = data; + + int slen = (_flags.gameID == GI_EOB1) ? 12 : 13; + + 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; + + const char *vmpPattern = (_flags.gameID == GI_EOB1 && (_configRenderMode == Common::kRenderEGA || _configRenderMode == Common::kRenderCGA)) ? "%s.EMP" : "%s.VMP"; + Common::SeekableReadStream *s = _res->createReadStream(Common::String::format(vmpPattern, (const char *)pos)); + _vmpSize = s->readUint16LE(); + delete[] _vmpPtr; + _vmpPtr = new uint16[_vmpSize]; + for (int i = 0; i < _vmpSize; i++) + _vmpPtr[i] = s->readUint16LE(); + delete s; + + const char *paletteFilePattern = (_flags.gameID == GI_EOB2 && _configRenderMode == Common::kRenderEGA) ? "%s.EGA" : "%s.PAL"; + + Common::String tmpStr = Common::String::format(paletteFilePattern, (const char *)pos); + _curGfxFile = (const char *)pos; + pos += slen; + + if (*pos++ != 0xff && _flags.gameID == GI_EOB2) { + tmpStr = Common::String::format(paletteFilePattern, (const char *)pos); + pos += 13; + } + + if (_flags.gameID == GI_EOB1) { + pos += 11; + _screen->setShapeFadeMode(0, false); + _screen->setShapeFadeMode(1, false); + } + + if (_flags.gameID == GI_EOB2 || _configRenderMode != Common::kRenderEGA) + _screen->loadPalette(tmpStr.c_str(), _screen->getPalette(0)); + + if (_configRenderMode != Common::kRenderCGA) { + 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 && _configRenderMode == Common::kRenderEGA) + _screen->setScreenPalette(_screen->getPalette(0)); + } + } + + if (_flags.gameID == GI_EOB2) { + delay(3 * _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) + 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 uint8 *cgaMapping) { + if (file) + strcpy(_lastBlockDataFile, file); + + const char *filePattern = (_flags.gameID == GI_EOB1 && (_configRenderMode == Common::kRenderEGA || _configRenderMode == Common::kRenderCGA)) ? "%s.ECN" : "%s.VCN"; + _screen->loadBitmap(Common::String::format(filePattern, _lastBlockDataFile).c_str(), 3, 3, 0); + const uint8 *pos = _screen->getCPagePtr(3); + + uint32 vcnSize = READ_LE_UINT16(pos) * _vcnBlockWidth * _vcnBlockHeight; + pos += 2; + + const uint8 *colMap = pos; + pos += 32; + + delete[] _vcnBlocks; + _vcnBlocks = new uint8[vcnSize]; + + if (_flags.gameID == GI_EOB2 && _configRenderMode == Common::kRenderEGA) { + const uint8 *egaTable = _screen->getEGADitheringTable(); + assert(_vmpPtr); + assert(egaTable); + + delete[] _vcnTransitionMask; + _vcnTransitionMask = new uint8[vcnSize]; + + for (int i = 0; i < _vmpSize; i++) { + uint16 vcnOffs = _vmpPtr[i] & 0x3FFF; + const uint8 *src = &pos[vcnOffs << 5]; + uint8 *dst1 = &_vcnBlocks[vcnOffs << 7]; + uint8 *dst3 = &_vcnTransitionMask[vcnOffs << 7]; + int palOffset = (i < 330) ? 0 : _wllVcnOffset; + + for (int y = 0; y < 8; y++) { + uint8 *dst2 = dst1 + 8; + uint8 *dst4 = dst3 + 8; + + for (int x = 0; x < 4; x++) { + uint8 in = *src++; + + dst1[0] = dst2[0] = egaTable[colMap[(in >> 4) + palOffset]]; + dst1[1] = dst2[1] = egaTable[colMap[(in & 0x0f) + palOffset]]; + dst3[0] = dst4[0] = (in & 0xf0) ? 0 : 0xff; + dst3[1] = dst4[1] = (in & 0x0f) ? 0 : 0xff; + + dst1 += 2; + dst2 += 2; + dst3 += 2; + dst4 += 2; + } + + dst1 += 8; + dst3 += 8; + } + } + } else if (_configRenderMode == Common::kRenderCGA) { + uint8 *tmp = _screen->encodeShape(0, 0, 1, 8, false, cgaMapping); + delete[] tmp; + + delete[] _vcnTransitionMask; + _vcnTransitionMask = new uint8[vcnSize]; + uint8 tblSwitch = 0; + uint8 *dst = _vcnBlocks; + uint8 *dst2 = _vcnTransitionMask; + + while (dst < _vcnBlocks + vcnSize) { + const uint16 *table = _screen->getCGADitheringTable((tblSwitch++) & 1); + for (int ii = 0; ii < 2; ii++) { + *dst++ = ((table[pos[0]] & 0x000f) << 4) | ((table[pos[0]] & 0x0f00) >> 8); + *dst++= ((table[pos[1]] & 0x000f) << 4) | ((table[pos[1]] & 0x0f00) >> 8); + + uint8 msk = 0; + if (pos[0] & 0xf0) + msk |= 0x30; + if (pos[0] & 0x0f) + msk |= 0x03; + *dst2++ = msk ^ 0x33; + + msk = 0; + if (pos[1] & 0xf0) + msk |= 0x30; + if (pos[1] & 0x0f) + msk |= 0x03; + *dst2++ = msk ^ 0x33; + + pos += 2; + } + } + } else { + if (_configRenderMode != Common::kRenderEGA) + memcpy(_vcnColTable, colMap, 32); + memcpy(_vcnBlocks, pos, vcnSize); + } +} + +void EoBCoreEngine::loadBlockProperties(const char *mazFile) { + memset(_levelBlockProperties, 0, 1024 * sizeof(LevelBlockProperty)); + const uint8 *p = getBlockFileData(mazFile) + 6; + + if (_hasTempDataFlags & (1 << (_currentLevel - 1))) { + restoreBlockTempData(_currentLevel); + return; + } + + for (int i = 0; i < 1024; i++) { + for (int ii = 0; ii < 4; ii++) + _levelBlockProperties[i].walls[ii] = *p++; + } +} + +const uint8 *EoBCoreEngine::getBlockFileData(int) { + Common::SeekableReadStream *s = _res->createReadStream(_curBlockFile); + _screen->loadFileDataToPage(s, 15, s->size()); + delete s; + return _screen->getCPagePtr(15); +} + +Common::String EoBCoreEngine::getBlockFileName(int levelIndex, int sub) { + readLevelFileData(levelIndex); + const uint8 *data = _screen->getCPagePtr(5) + 2; + const uint8 *pos = data; + + 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; + + return Common::String((const char *)pos); + } + + return Common::String(); +} + +const uint8 *EoBCoreEngine::getBlockFileData(const char *mazFile) { + _curBlockFile = mazFile; + return getBlockFileData(); +} + +void EoBCoreEngine::loadDecorations(const char *cpsFile, const char *decFile) { + _screen->loadShapeSetBitmap(cpsFile, 5, 3); + Common::SeekableReadStream *s = _res->createReadStream(decFile); + + _levelDecorationDataSize = s->readUint16LE(); + delete[] _levelDecorationData; + _levelDecorationData = new LevelDecorationProperty[_levelDecorationDataSize]; + memset(_levelDecorationData, 0, _levelDecorationDataSize * sizeof(LevelDecorationProperty)); + + 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; + for (int i = 0; i < 6; i++) { + for (int ii = 0; ii < 10; ii++) { + if (_characters[i].events[ii] == -57) + spellCallback_start_trueSeeing(); + } + } + _wllShapeMap[wallIndex] = _mappedDecorationsCount + 1; + _specialWallTypes[wallIndex] = specialType; + _wllWallFlags[wallIndex] = flags ^ 4; + + if (decIndex == -1) { + _wllShapeMap[wallIndex] = 0; + return; + } + + do { + assert(decIndex < _levelDecorationDataSize); + 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, false, (_flags.gameID == GI_EOB1) ? _cgaMappingLevel[_cgaLevelMappingIndex[_currentLevel - 1]] : 0); + } + + 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 refresh) { + generateBlockDrawingBuffer(); + drawVcnBlocks(); + drawSceneShapes(); + + if (_sceneDrawPage2) { + if (refresh) + _screen->fillRect(0, 0, 176, 120, 12); + + if (!_loading) + _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) + delayUntil(_drawSceneTimer); + + if (refresh && !_partyResting) + _screen->copyRegion(0, 0, 0, 0, 176, 120, 2, 0, Screen::CR_NO_P_CHECK); + + updateEnvironmentalSfx(0); + + if (!_dialogueField && refresh && !_updateFlags) + gui_drawCompass(false); + + if (refresh && !_partyResting && !_loading) + _screen->updateScreen(); + + if (_sceneDefaultUpdate) { + _sceneDefaultUpdate = false; + _drawSceneTimer = _system->getMillis() + 4 * _tickLength; + } + + _sceneUpdateRequired = false; +} + +void EoBCoreEngine::drawSceneShapes(int start) { + for (int i = start; 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) { + for (int i = 1; i >= 0; i--) { + int s = index * 2 + i; + if (_dscWallMapping[s]) { + int16 d = *_dscWallMapping[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]; + + assert((_flags.gameID == GI_EOB1 && w < 70) || (_flags.gameID == GI_EOB2 && w < 80)); + + if (_flags.gameID == GI_EOB2 && w == 74 && _currentBlock == curBlock) { + for (int i = 0; i < 5; i++) { + if (_wallsOfForce[i].block == b) { + destroyWallOfForce(i); + f = _wllWallFlags[0]; + } + } + } + + 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) { + updateAllMonsterDests(); + uint16 old = _currentBlock; + _currentBlock = block; + + runLevelScript(old, 2); + + if (++_moveCounter > 3) { + _txt->printMessage("\r"); + _moveCounter = 0; + } + + runLevelScript(block, 1); + + if (_flags.gameID == GI_EOB2 && _levelBlockProperties[block].walls[0] == 26) + memset(_levelBlockProperties[block].walls, 0, 4); + + updateAllMonsterDests(); + _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 (_flags.gameID == GI_EOB1 && _currentLevel >= 4 && _currentLevel <= 6) { + if (v >= 30) + x1 += 4; + else + x1 += ((v - _dscDoorXE[v]) * 9); + } + + 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 |