aboutsummaryrefslogtreecommitdiff
path: root/engines/dm/dm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/dm/dm.cpp')
-rw-r--r--engines/dm/dm.cpp1038
1 files changed, 1038 insertions, 0 deletions
diff --git a/engines/dm/dm.cpp b/engines/dm/dm.cpp
new file mode 100644
index 0000000000..25d741f376
--- /dev/null
+++ b/engines/dm/dm.cpp
@@ -0,0 +1,1038 @@
+/* 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.
+*
+*/
+
+/*
+* Based on the Reverse Engineering work of Christophe Fontanel,
+* maintainer of the Dungeon Master Encyclopaedia (http://dmweb.free.fr/)
+*/
+
+#include "advancedDetector.h"
+
+#include "common/config-manager.h"
+#include "common/scummsys.h"
+#include "common/system.h"
+
+#include "common/debug.h"
+#include "common/debug-channels.h"
+#include "common/error.h"
+
+#include "common/file.h"
+#include "common/events.h"
+#include "common/array.h"
+#include "common/algorithm.h"
+#include "common/translation.h"
+
+#include "engines/util.h"
+#include "engines/engine.h"
+
+#include "graphics/cursorman.h"
+#include "graphics/palette.h"
+#include "graphics/surface.h"
+
+#include "gui/saveload.h"
+
+#include "dm/dm.h"
+#include "dm/gfx.h"
+#include "dm/dungeonman.h"
+#include "dm/eventman.h"
+#include "dm/menus.h"
+#include "dm/champion.h"
+#include "dm/loadsave.h"
+#include "dm/objectman.h"
+#include "dm/inventory.h"
+#include "dm/text.h"
+#include "dm/movesens.h"
+#include "dm/group.h"
+#include "dm/timeline.h"
+#include "dm/projexpl.h"
+#include "dm/dialog.h"
+#include "dm/sounds.h"
+
+namespace DM {
+
+bool DMEngine::isDemo() const {
+ return (bool)(_gameVersion->_desc.flags & ADGF_DEMO);
+}
+
+Direction DMEngine::turnDirRight(int16 dir) {
+ Direction result = (Direction)((dir + 1) & 3);
+ return result;
+}
+
+Direction DMEngine::returnOppositeDir(int16 dir) {
+ Direction result = (Direction)((dir + 2) & 3);
+ return result;
+}
+
+Direction DMEngine::turnDirLeft(int16 dir) {
+ Direction result = (Direction)((dir + 3) & 3);
+ return result;
+}
+
+bool DMEngine::isOrientedWestEast(int16 dir) {
+ return dir & 1;
+}
+
+uint16 DMEngine::normalizeModulo4(int16 dir) {
+ return dir & 3;
+}
+
+int32 DMEngine::filterTime(int32 mapTime) {
+ return mapTime & 0x00FFFFFF;
+}
+
+int32 DMEngine::setMapAndTime(uint32 map, uint32 time) {
+ return (time | (map << 24));
+}
+
+uint16 DMEngine::getMap(int32 mapTime) {
+ return ((uint16)(mapTime >> 24));
+}
+
+int32 DMEngine::setMap(int32 mapTime, uint32 map) {
+ return ((mapTime & 0x00FFFFFF) | (map << 24));
+}
+
+Thing DMEngine::thingWithNewCell(Thing thing, int16 cell) {
+ return Thing(((thing.toUint16()) & 0x3FFF) | ((cell) << 14));
+}
+
+int16 DMEngine::getDistance(int16 mapx1, int16 mapy1, int16 mapx2, int16 mapy2) {
+ return ABS(mapx1 - mapx2) + ABS(mapy1 - mapy2);
+}
+
+DMEngine::DMEngine(OSystem *syst, const DMADGameDescription *desc) : Engine(syst), _console(nullptr), _gameVersion(desc) {
+ // register random source
+ _rnd = new Common::RandomSource("dm");
+
+ _dungeonMan = nullptr;
+ _displayMan = nullptr;
+ _eventMan = nullptr;
+ _menuMan = nullptr;
+ _championMan = nullptr;
+ _objectMan = nullptr;
+ _inventoryMan = nullptr;
+ _textMan = nullptr;
+ _moveSens = nullptr;
+ _groupMan = nullptr;
+ _timeline = nullptr;
+ _projexpl = nullptr;
+ _displayMan = nullptr;
+ _sound = nullptr;
+
+ _engineShouldQuit = false;
+ _dungeonId = 0;
+
+ _gameMode = kDMModeLoadSavedGame;
+ _restartGameRequest = false;
+ _stopWaitingForPlayerInput = true;
+ _gameTimeTicking = false;
+ _restartGameAllowed = false;
+ _pressingEye = false;
+ _stopPressingEye = false;
+ _pressingMouth = false;
+ _stopPressingMouth = false;
+ _highlightBoxInversionRequested = false;
+ _projectileDisableMovementTicks = 0;
+ _lastProjectileDisabledMovementDirection = 0;
+ _gameWon = false;
+ _newPartyMapIndex = kDMMapIndexNone;
+ _setMousePointerToObjectInMainLoop = false;
+ _disabledMovementTicks = 0;
+ _gameTime = 0;
+ _stringBuildBuffer[0] = '\0';
+ _waitForInputMaxVerticalBlankCount = 0;
+ _savedScreenForOpenEntranceDoors = nullptr;
+ for (uint16 i = 0; i < 10; ++i)
+ _entranceDoorAnimSteps[i] = nullptr;
+ _interfaceCredits = nullptr;
+ debug("DMEngine::DMEngine");
+
+ _saveThumbnail = nullptr;
+ _canLoadFromGMM = false;
+ _loadSaveSlotAtRuntime = -1;
+ _dialog = nullptr;
+}
+
+DMEngine::~DMEngine() {
+ debug("DMEngine::~DMEngine");
+
+ // dispose of resources
+ delete _rnd;
+ delete _console;
+ delete _displayMan;
+ delete _dungeonMan;
+ delete _eventMan;
+ delete _menuMan;
+ delete _championMan;
+ delete _objectMan;
+ delete _inventoryMan;
+ delete _textMan;
+ delete _moveSens;
+ delete _groupMan;
+ delete _timeline;
+ delete _projexpl;
+ delete _dialog;
+ delete _sound;
+
+ delete _saveThumbnail;
+
+ delete[] _savedScreenForOpenEntranceDoors;
+ // clear debug channels
+ DebugMan.clearAllDebugChannels();
+}
+
+bool DMEngine::hasFeature(EngineFeature f) const {
+ return
+ (f == kSupportsSavingDuringRuntime) ||
+ (f == kSupportsLoadingDuringRuntime);
+}
+
+Common::Error DMEngine::loadGameState(int slot) {
+ if (loadgame(slot) != kDMLoadgameFailure) {
+ _displayMan->fillScreen(kDMColorBlack);
+ _displayMan->startEndFadeToPalette(_displayMan->_palDungeonView[0]);
+ _gameMode = kDMModeLoadSavedGame;
+
+ startGame();
+ _restartGameRequest = false;
+ _eventMan->hideMouse();
+ _eventMan->discardAllInput();
+ return Common::kNoError;
+ }
+
+ return Common::kNoGameDataFoundError;
+}
+
+bool DMEngine::canLoadGameStateCurrently() {
+ return _canLoadFromGMM;
+}
+
+void DMEngine::delay(uint16 verticalBlank) {
+ for (uint16 i = 0; i < verticalBlank * 2; ++i) {
+ _eventMan->processInput();
+ _displayMan->updateScreen();
+ _system->delayMillis(10); // Google says most Amiga games had a refreshrate of 50 hz
+ }
+}
+
+uint16 DMEngine::getScaledProduct(uint16 val, uint16 scale, uint16 vale2) {
+ return ((uint32)val * vale2) >> scale;
+}
+
+void DMEngine::initializeGame() {
+ initMemoryManager();
+ _displayMan->loadGraphics();
+ _displayMan->initializeGraphicData();
+ _displayMan->loadFloorSet(kDMFloorSetStone);
+ _displayMan->loadWallSet(kDMWallSetStone);
+
+ _sound->loadSounds(); // @ F0506_AMIGA_AllocateData
+
+ if (!ConfMan.hasKey("save_slot")) // skip drawing title if loading from launcher
+ drawTittle();
+
+ _textMan->initialize();
+ _objectMan->loadObjectNames();
+ _eventMan->initMouse();
+
+ int16 saveSlot = -1;
+ do {
+ // if loading from the launcher
+ if (ConfMan.hasKey("save_slot")) {
+ saveSlot = ConfMan.getInt("save_slot");
+ } else { // else show the entrance
+ processEntrance();
+ if (_engineShouldQuit)
+ return;
+
+ if (_gameMode == kDMModeLoadSavedGame) { // if resume was clicked, bring up ScummVM load screen
+ GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
+ saveSlot = dialog->runModalWithCurrentTarget();
+ delete dialog;
+ }
+ }
+ } while (loadgame(saveSlot) != kDMLoadgameSuccess);
+
+ _displayMan->loadIntoBitmap(kDMGraphicIdxMenuSpellAreLines, _menuMan->_bitmapSpellAreaLines); // @ F0396_MENUS_LoadSpellAreaLinesBitmap
+
+ // There was some memory wizardy for the Amiga platform, I skipped that part
+ _displayMan->allocateFlippedWallBitmaps();
+
+ startGame();
+ if (_gameMode != kDMModeLoadSavedGame)
+ _moveSens->getMoveResult(Thing::_party, kDMMapXNotOnASquare, 0, _dungeonMan->_partyMapX, _dungeonMan->_partyMapY);
+ _eventMan->showMouse();
+ _eventMan->discardAllInput();
+}
+
+void DMEngine::initMemoryManager() {
+ static uint16 palSwoosh[16] = {0x000, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0x000, 0xFFF, 0xAAA, 0xFFF, 0xAAA, 0x444, 0xFF0, 0xFF0}; // @ K0057_aui_Palette_Swoosh
+
+ _displayMan->buildPaletteChangeCopperList(palSwoosh, palSwoosh);
+ for (uint16 i = 0; i < 16; ++i) {
+ _displayMan->_paletteTopAndBottomScreen[i] = _displayMan->_palDungeonView[0][i];
+ _displayMan->_paletteMiddleScreen[i] = _displayMan->_palDungeonView[0][i];
+ }
+}
+
+void DMEngine::startGame() {
+ static Box boxScreenTop(0, 319, 0, 32); // @ G0061_s_Graphic562_Box_ScreenTop
+ static Box boxScreenRight(224, 319, 33, 169); // @ G0062_s_Graphic562_Box_ScreenRight
+ static Box boxScreenBottom(0, 319, 169, 199); // @ G0063_s_Graphic562_Box_ScreenBottom
+
+ _pressingEye = false;
+ _stopPressingEye = false;
+ _pressingMouth = false;
+ _stopPressingMouth = false;
+ _highlightBoxInversionRequested = false;
+ _eventMan->_highlightBoxEnabled = false;
+ _championMan->_partyIsSleeping = false;
+ _championMan->_actingChampionOrdinal = indexToOrdinal(kDMChampionNone);
+ _menuMan->_actionAreaContainsIcons = true;
+ _eventMan->_useChampionIconOrdinalAsMousePointerBitmap = indexToOrdinal(kDMChampionNone);
+
+ _eventMan->_primaryMouseInput = _eventMan->_primaryMouseInputInterface;
+ _eventMan->_secondaryMouseInput = _eventMan->_secondaryMouseInputMovement;
+ _eventMan->_primaryKeyboardInput = _eventMan->_primaryKeyboardInputInterface;
+ _eventMan->_secondaryKeyboardInput = _eventMan->_secondaryKeyboardInputMovement;
+
+ processNewPartyMap(_dungeonMan->_partyMapIndex);
+
+ if (_gameMode == kDMModeLoadSavedGame) {
+ _displayMan->startEndFadeToPalette(_displayMan->_paletteTopAndBottomScreen);
+ _displayMan->_useByteBoxCoordinates = false;
+ delay(1);
+ _displayMan->fillScreenBox(boxScreenTop, kDMColorBlack);
+ _displayMan->fillScreenBox(boxScreenRight, kDMColorBlack);
+ _displayMan->fillScreenBox(boxScreenBottom, kDMColorBlack);
+ } else {
+ _displayMan->_useByteBoxCoordinates = false;
+ _displayMan->fillScreenBox(boxScreenTop, kDMColorBlack);
+ _displayMan->fillScreenBox(boxScreenRight, kDMColorBlack);
+ _displayMan->fillScreenBox(boxScreenBottom, kDMColorBlack);
+ }
+
+ _displayMan->buildPaletteChangeCopperList(_displayMan->_palDungeonView[0], _displayMan->_paletteTopAndBottomScreen);
+ _menuMan->drawMovementArrows();
+ _championMan->resetDataToStartGame();
+ _gameTimeTicking = true;
+}
+
+void DMEngine::processNewPartyMap(uint16 mapIndex) {
+ _groupMan->removeAllActiveGroups();
+ _dungeonMan->setCurrentMapAndPartyMap(mapIndex);
+ _displayMan->loadCurrentMapGraphics();
+ _groupMan->addAllActiveGroups();
+ _inventoryMan->setDungeonViewPalette();
+}
+
+Common::Error DMEngine::run() {
+ initConstants();
+
+ // scummvm/engine specific
+ initGraphics(320, 200, false);
+ _console = new Console(this);
+ _displayMan = new DisplayMan(this);
+ _dungeonMan = new DungeonMan(this);
+ _eventMan = new EventManager(this);
+ _menuMan = new MenuMan(this);
+ _championMan = new ChampionMan(this);
+ _objectMan = new ObjectMan(this);
+ _inventoryMan = new InventoryMan(this);
+ _textMan = new TextMan(this);
+ _moveSens = new MovesensMan(this);
+ _groupMan = new GroupMan(this);
+ _timeline = new Timeline(this);
+ _projexpl = new ProjExpl(this);
+ _dialog = new DialogMan(this);
+ _sound = SoundMan::getSoundMan(this, _gameVersion);
+ _displayMan->setUpScreens(320, 200);
+
+ initializeGame();
+ while (true) {
+ gameloop();
+
+ if (_engineShouldQuit)
+ return Common::kNoError;
+
+ if (_loadSaveSlotAtRuntime == -1)
+ endGame(_championMan->_partyDead);
+ else {
+ loadGameState(_loadSaveSlotAtRuntime);
+ _menuMan->drawEnabledMenus();
+ _displayMan->updateScreen();
+ _loadSaveSlotAtRuntime = -1;
+ }
+ }
+
+ return Common::kNoError;
+}
+
+void DMEngine::gameloop() {
+ _canLoadFromGMM = true;
+ _waitForInputMaxVerticalBlankCount = 15;
+ while (true) {
+ if (_engineShouldQuit) {
+ _canLoadFromGMM = false;
+ return;
+ }
+
+ // DEBUG CODE
+ for (int16 i = 0; i < _championMan->_partyChampionCount; ++i) {
+ Champion &champ = _championMan->_champions[i];
+ if (_console->_debugGodmodeHP)
+ champ._currHealth = champ._maxHealth;
+ if (_console->_debugGodmodeMana)
+ champ._currMana = champ._maxMana;
+ if (_console->_debugGodmodeStamina)
+ champ._currStamina = champ._maxStamina;
+ }
+
+ for (;;) {
+
+
+ if (_newPartyMapIndex != kDMMapIndexNone) {
+ processNewPartyMap(_newPartyMapIndex);
+ _moveSens->getMoveResult(Thing::_party, kDMMapXNotOnASquare, 0, _dungeonMan->_partyMapX, _dungeonMan->_partyMapY);
+ _newPartyMapIndex = kDMMapIndexNone;
+ _eventMan->discardAllInput();
+ }
+ _timeline->processTimeline();
+
+ if (_newPartyMapIndex == kDMMapIndexNone)
+ break;
+ }
+
+ if (!_inventoryMan->_inventoryChampionOrdinal && !_championMan->_partyIsSleeping) {
+ Box box(0, 223, 0, 135);
+ _displayMan->fillBoxBitmap(_displayMan->_bitmapViewport, box, kDMColorBlack, k112_byteWidthViewport, k136_heightViewport); // (possibly dummy code)
+ _displayMan->drawDungeon(_dungeonMan->_partyDir, _dungeonMan->_partyMapX, _dungeonMan->_partyMapY);
+ if (_setMousePointerToObjectInMainLoop) {
+ _setMousePointerToObjectInMainLoop = false;
+ _eventMan->showMouse();
+ _eventMan->setPointerToObject(_objectMan->_objectIconForMousePointer);
+ _eventMan->hideMouse();
+
+ }
+ if (_eventMan->_refreshMousePointerInMainLoop) {
+ _eventMan->_refreshMousePointerInMainLoop = false;
+ _eventMan->_mousePointerBitmapUpdated = true;
+ _eventMan->showMouse();
+ _eventMan->hideMouse();
+ }
+ }
+ _eventMan->highlightBoxDisable();
+ _sound->playPendingSound();
+ _championMan->applyAndDrawPendingDamageAndWounds();
+ if (_championMan->_partyDead)
+ break;
+
+ _gameTime++;
+
+ if (!(_gameTime & 511))
+ _inventoryMan->decreaseTorchesLightPower();
+
+ if (_championMan->_party._freezeLifeTicks)
+ _championMan->_party._freezeLifeTicks -= 1;
+
+ _menuMan->refreshActionAreaAndSetChampDirMaxDamageReceived();
+
+ if (!(_gameTime & (_championMan->_partyIsSleeping ? 15 : 63)))
+ _championMan->applyTimeEffects();
+
+ if (_disabledMovementTicks)
+ _disabledMovementTicks--;
+
+ if (_projectileDisableMovementTicks)
+ _projectileDisableMovementTicks--;
+
+ _textMan->clearExpiredRows();
+ _stopWaitingForPlayerInput = false;
+ uint16 vblankCounter = 0;
+ do {
+ _eventMan->processInput();
+
+ if (_stopPressingEye) {
+ _pressingEye = false;
+ _stopPressingEye = false;
+ _inventoryMan->drawStopPressingEye();
+ } else if (_stopPressingMouth) {
+ _pressingMouth = false;
+ _stopPressingMouth = false;
+ _inventoryMan->drawStopPressingMouth();
+ }
+
+ _eventMan->processCommandQueue();
+ if (_engineShouldQuit || _loadSaveSlotAtRuntime != -1) {
+ _canLoadFromGMM = false;
+ return;
+ }
+ _displayMan->updateScreen();
+ if (!_stopWaitingForPlayerInput) {
+ _eventMan->highlightBoxDisable();
+ }
+
+ if (++vblankCounter > _waitForInputMaxVerticalBlankCount)
+ _stopWaitingForPlayerInput = true;
+ else if (!_stopWaitingForPlayerInput)
+ _system->delayMillis(10);
+
+ } while (!_stopWaitingForPlayerInput || !_gameTimeTicking);
+ }
+ _canLoadFromGMM = false;
+}
+
+int16 DMEngine::ordinalToIndex(int16 val) {
+ return val - 1;
+}
+
+int16 DMEngine::indexToOrdinal(int16 val) {
+ return val + 1;
+}
+
+void DMEngine::processEntrance() {
+ _eventMan->_primaryMouseInput = _eventMan->_primaryMouseInputEntrance;
+ _eventMan->_secondaryMouseInput = nullptr;
+ _eventMan->_primaryKeyboardInput = nullptr;
+ _eventMan->_secondaryKeyboardInput = nullptr;
+ _entranceDoorAnimSteps[0] = new byte[128 * 161 * 12];
+ for (uint16 idx = 1; idx < 8; idx++)
+ _entranceDoorAnimSteps[idx] = _entranceDoorAnimSteps[idx - 1] + 128 * 161;
+
+ _entranceDoorAnimSteps[8] = _entranceDoorAnimSteps[7] + 128 * 161;
+ _entranceDoorAnimSteps[9] = _entranceDoorAnimSteps[8] + 128 * 161 * 2;
+
+ _displayMan->loadIntoBitmap(kDMGraphicIdxEntranceRightDoor, _entranceDoorAnimSteps[4]);
+ _displayMan->loadIntoBitmap(kDMGraphicIdxEntranceLeftDoor, _entranceDoorAnimSteps[0]);
+ _interfaceCredits = _displayMan->getNativeBitmapOrGraphic(kDMGraphicIdxCredits);
+ _displayMan->_useByteBoxCoordinates = false;
+ Box displayBox(0, 100, 0, 160);
+ for (uint16 idx = 1; idx < 4; idx++) {
+ _displayMan->blitToBitmap(_entranceDoorAnimSteps[0], _entranceDoorAnimSteps[idx], displayBox, idx << 2, 0, k64_byteWidth, k64_byteWidth, kDMColorNoTransparency, 161, 161);
+ displayBox._rect.right -= 4;
+ }
+ displayBox._rect.right = 127;
+ for (uint16 idx = 5; idx < 8; idx++) {
+ displayBox._rect.left += 4;
+ _displayMan->blitToBitmap(_entranceDoorAnimSteps[4], _entranceDoorAnimSteps[idx], displayBox, 0, 0, k64_byteWidth, k64_byteWidth, kDMColorNoTransparency, 161, 161);
+ }
+
+ do {
+ drawEntrance();
+ _eventMan->showMouse();
+ _eventMan->discardAllInput();
+ _gameMode = kDMModeWaitingOnEntrance;
+ do {
+ _eventMan->processInput();
+ if (_engineShouldQuit)
+ return;
+ _eventMan->processCommandQueue();
+ _displayMan->updateScreen();
+ } while (_gameMode == kDMModeWaitingOnEntrance);
+ } while (_gameMode == kDMModeEntranceDrawCredits);
+
+ //Strangerke: CHECKME: Earlier versions were using G0566_puc_Graphic534_Sound01Switch
+ _sound->play(kDMSoundIndexSwitch, 112, 0x40, 0x40);
+ delay(20);
+ _eventMan->showMouse();
+ if (_gameMode != kDMModeLoadSavedGame)
+ openEntranceDoors();
+
+ delete[] _entranceDoorAnimSteps[0];
+ for (uint16 i = 0; i < 10; ++i)
+ _entranceDoorAnimSteps[i] = nullptr;
+}
+
+void DMEngine::endGame(bool doNotDrawCreditsOnly) {
+ static Box boxEndgameRestartOuterEN(103, 217, 145, 159);
+ static Box boxEndgameRestartInnerEN(105, 215, 147, 157);
+
+ static Box boxEndgameRestartOuterDE(82, 238, 145, 159);
+ static Box boxEndgameRestartInnerDE(84, 236, 147, 157);
+
+ static Box boxEndgameRestartOuterFR(100, 220, 145, 159);
+ static Box boxEndgameRestartInnerFR(102, 218, 147, 157);
+
+ Box restartOuterBox;
+ Box restartInnerBox;
+
+ switch (getGameLanguage()) { // localized
+ default:
+ case Common::EN_ANY:
+ restartOuterBox = boxEndgameRestartOuterEN;
+ restartInnerBox = boxEndgameRestartInnerEN;
+ break;
+ case Common::DE_DEU:
+ restartOuterBox = boxEndgameRestartOuterDE;
+ restartInnerBox = boxEndgameRestartInnerDE;
+ break;
+ case Common::FR_FRA:
+ restartOuterBox = boxEndgameRestartOuterFR;
+ restartInnerBox = boxEndgameRestartInnerFR;
+ break;
+ }
+
+ static Box theEndBox(120, 199, 95, 108);
+ static Box championMirrorBox(11, 74, 7, 49);
+ static Box championPortraitBox(27, 58, 13, 41);
+
+ bool waitBeforeDrawingRestart = true;
+
+ _eventMan->setMousePointerToNormal(k0_pointerArrow);
+ _eventMan->showMouse();
+ _eventMan->_primaryMouseInput = nullptr;
+ _eventMan->_secondaryMouseInput = nullptr;
+ _eventMan->_primaryKeyboardInput = nullptr;
+ _eventMan->_secondaryKeyboardInput = nullptr;
+ if (doNotDrawCreditsOnly && !_gameWon) {
+ _sound->requestPlay(kDMSoundIndexScream, _dungeonMan->_partyMapX, _dungeonMan->_partyMapY, kDMSoundModePlayImmediately);
+ delay(240);
+ }
+
+ if (_displayMan->_paletteSwitchingEnabled) {
+ uint16 oldPalTopAndBottomScreen[16];
+ for (uint16 i = 0; i < 16; ++i)
+ oldPalTopAndBottomScreen[i] = _displayMan->_paletteTopAndBottomScreen[i];
+ for (int i = 0; i <= 7; i++) {
+ delay(1);
+ for (int colIdx = 0; colIdx < 16; colIdx++) {
+ _displayMan->_paletteMiddleScreen[colIdx] = _displayMan->getDarkenedColor(_displayMan->_paletteMiddleScreen[colIdx]);
+ _displayMan->_paletteTopAndBottomScreen[colIdx] = _displayMan->getDarkenedColor(_displayMan->_paletteTopAndBottomScreen[colIdx]);
+ }
+ }
+ _displayMan->_paletteSwitchingEnabled = false;
+ delay(1);
+ for (uint16 i = 0; i < 16; ++i)
+ _displayMan->_paletteTopAndBottomScreen[i] = oldPalTopAndBottomScreen[i];
+ } else
+ _displayMan->startEndFadeToPalette(_displayMan->_blankBuffer);
+
+ uint16 darkBluePalette[16];
+ if (doNotDrawCreditsOnly) {
+ if (_gameWon) {
+ // Strangerke: Related to portraits. Game data could be missing for earlier versions of the game.
+ _displayMan->fillScreen(kDMColorDarkestGray);
+ for (int16 championIndex = kDMChampionFirst; championIndex < _championMan->_partyChampionCount; championIndex++) {
+ int16 textPosY = championIndex * 48;
+ Champion *curChampion = &_championMan->_champions[championIndex];
+ _displayMan->blitToScreen(_displayMan->getNativeBitmapOrGraphic(kDMGraphicIdxWallOrnChampMirror), &championMirrorBox, k32_byteWidth, kDMColorFlesh, 43);
+ _displayMan->blitToScreen(curChampion->_portrait, &championPortraitBox, k16_byteWidth, kDMColorDarkGary, 29);
+ _textMan->printEndGameString(87, textPosY += 14, kDMColorGold, curChampion->_name);
+ int textPosX = (6 * strlen(curChampion->_name)) + 87;
+ char championTitleFirstCharacter = curChampion->_title[0];
+ if ((championTitleFirstCharacter != ',') && (championTitleFirstCharacter != ';') && (championTitleFirstCharacter != '-'))
+ textPosX += 6;
+
+ _textMan->printEndGameString(textPosX, textPosY++, kDMColorGold, curChampion->_title);
+ for (int16 idx = kDMSkillFighter; idx <= kDMSkillWizard; idx++) {
+ uint16 skillLevel = MIN<uint16>(16, _championMan->getSkillLevel(championIndex, idx | (kDMIgnoreObjectModifiers | kDMIgnoreTemporaryExperience)));
+ if (skillLevel == 1)
+ continue;
+
+ Common::String displStr = Common::String::format("%s %s", _inventoryMan->_skillLevelNames[skillLevel - 2], _championMan->_baseSkillName[idx]);
+ _textMan->printEndGameString(105, textPosY = textPosY + 8, kDMColorLightestGray, displStr.c_str());
+ }
+ championMirrorBox._rect.top += 48;
+ championMirrorBox._rect.bottom += 48;
+ championPortraitBox._rect.top += 48;
+ championPortraitBox._rect.top += 48;
+ }
+ _displayMan->startEndFadeToPalette(_displayMan->_paletteTopAndBottomScreen);
+ _engineShouldQuit = true;
+ return;
+ }
+T0444017:
+ _displayMan->fillScreen(kDMColorBlack);
+ _displayMan->blitToScreen(_displayMan->getNativeBitmapOrGraphic(kDMGraphicIdxTheEnd), &theEndBox, k40_byteWidth, kDMColorNoTransparency, 14);
+ for (uint16 i = 0; i < 16; ++i)
+ darkBluePalette[i] = D01_RGB_DARK_BLUE;
+ uint16 curPalette[16];
+ for (uint16 i = 0; i < 15; ++i)
+ curPalette[i] = darkBluePalette[i];
+ curPalette[15] = D09_RGB_WHITE;
+ _displayMan->startEndFadeToPalette(curPalette);
+ _displayMan->updateScreen();
+ if (waitBeforeDrawingRestart)
+ delay(300);
+
+ if (_restartGameAllowed) {
+ _displayMan->_useByteBoxCoordinates = false;
+ _displayMan->fillScreenBox(restartOuterBox, kDMColorDarkestGray);
+ _displayMan->fillScreenBox(restartInnerBox, kDMColorBlack);
+
+ switch (getGameLanguage()) { // localized
+ default:
+ case Common::EN_ANY:
+ _textMan->printToLogicalScreen(110, 154, kDMColorCyan, kDMColorBlack, "RESTART THIS GAME");
+ break;
+ case Common::DE_DEU:
+ _textMan->printToLogicalScreen(110, 154, kDMColorCyan, kDMColorBlack, "DIESES SPIEL NEU STARTEN");
+ break;
+ case Common::FR_FRA:
+ _textMan->printToLogicalScreen(110, 154, kDMColorCyan, kDMColorBlack, "RECOMMENCER CE JEU");
+ break;
+ }
+
+ curPalette[1] = D03_RGB_PINK;
+ curPalette[4] = D09_RGB_WHITE;
+ _eventMan->_primaryMouseInput = _eventMan->_primaryMouseInputRestartGame;
+ _eventMan->discardAllInput();
+ _eventMan->hideMouse();
+ _displayMan->startEndFadeToPalette(curPalette);
+ for (int16 verticalBlankCount = 900; --verticalBlankCount && !_restartGameRequest; delay(1))
+ _eventMan->processCommandQueue();
+
+ _eventMan->showMouse();
+ if (_restartGameRequest) {
+ _displayMan->startEndFadeToPalette(darkBluePalette);
+ _displayMan->fillScreen(kDMColorBlack);
+ _displayMan->startEndFadeToPalette(_displayMan->_palDungeonView[0]);
+ _gameMode = kDMModeLoadSavedGame;
+ if (loadgame(1) != kDMLoadgameFailure) {
+ startGame();
+ _restartGameRequest = false;
+ _eventMan->hideMouse();
+ _eventMan->discardAllInput();
+ return;
+ }
+ }
+ }
+
+ _displayMan->startEndFadeToPalette(darkBluePalette);
+ }
+ Box box(0, 319, 0, 199);
+ _displayMan->blitToScreen(_displayMan->getNativeBitmapOrGraphic(kDMGraphicIdxCredits), &box, k160_byteWidthScreen, kDMColorNoTransparency, k200_heightScreen);
+
+ _displayMan->startEndFadeToPalette(_displayMan->_palCredits);
+ _eventMan->waitForMouseOrKeyActivity();
+ if (_engineShouldQuit)
+ return;
+
+ if (_restartGameAllowed && doNotDrawCreditsOnly) {
+ waitBeforeDrawingRestart = false;
+ _displayMan->startEndFadeToPalette(darkBluePalette);
+ goto T0444017;
+ }
+
+ _engineShouldQuit = true;
+ return;
+}
+
+
+void DMEngine::drawEntrance() {
+ static Box doorsUpperHalfBox = Box(0, 231, 0, 80);
+ static Box doorsLowerHalfBox = Box(0, 231, 81, 160);
+ static Box closedDoorLeftBox = Box(0, 104, 30, 190);
+ static Box closedDoorRightBox = Box(105, 231, 30, 190);
+ /* Atari ST: { 0x000, 0x333, 0x444, 0x420, 0x654, 0x210, 0x040, 0x050, 0x432, 0x700, 0x543, 0x321, 0x222, 0x555, 0x310, 0x777 }, RGB colors are different */
+ static uint16 palEntrance[16] = {0x000, 0x666, 0x888, 0x840, 0xCA8, 0x0C0, 0x080, 0x0A0, 0x864, 0xF00, 0xA86, 0x642, 0x444, 0xAAA, 0x620, 0xFFF}; // @ G0020_aui_Graphic562_Palette_Entrance
+
+ byte *microDungeonCurrentMapData[32];
+
+ _dungeonMan->_partyMapIndex = kDMMapIndexEntrance;
+ _displayMan->_drawFloorAndCeilingRequested = true;
+ _dungeonMan->_currMapWidth = 5;
+ _dungeonMan->_currMapHeight = 5;
+ _dungeonMan->_currMapData = microDungeonCurrentMapData;
+
+ Map map; // uninitialized, won't be used
+ _dungeonMan->_currMap = &map;
+ Square microDungeonSquares[25];
+ for (uint16 i = 0; i < 25; ++i)
+ microDungeonSquares[i] = Square(kDMElementTypeWall, 0);
+
+ for (int16 idx = 0; idx < 5; idx++) {
+ microDungeonCurrentMapData[idx] = (byte*)&microDungeonSquares[idx * 5];
+ microDungeonSquares[idx + 10] = Square(kDMElementTypeCorridor, 0);
+ }
+ microDungeonSquares[7] = Square(kDMElementTypeCorridor, 0);
+ _displayMan->startEndFadeToPalette(_displayMan->_blankBuffer);
+
+ // note, a global variable is used here in the original
+ _displayMan->loadIntoBitmap(kDMGraphicIdxEntrance, _displayMan->_bitmapScreen);
+ _displayMan->drawDungeon(kDMDirSouth, 2, 0);
+
+ if (!_savedScreenForOpenEntranceDoors)
+ _savedScreenForOpenEntranceDoors = new byte[k200_heightScreen * k160_byteWidthScreen * 2];
+ memcpy(_savedScreenForOpenEntranceDoors, _displayMan->_bitmapScreen, 320 * 200);
+
+ _displayMan->_useByteBoxCoordinates = false;
+ _displayMan->blitToBitmap(_displayMan->_bitmapScreen, _entranceDoorAnimSteps[8], doorsUpperHalfBox, 0, 30, k160_byteWidthScreen, k128_byteWidth, kDMColorNoTransparency, 200, 161);
+ _displayMan->_useByteBoxCoordinates = false;
+ _displayMan->blitToBitmap(_displayMan->_bitmapScreen, _entranceDoorAnimSteps[8], doorsLowerHalfBox, 0, 111, k160_byteWidthScreen, k128_byteWidth, kDMColorNoTransparency, 200, 161);
+
+ _displayMan->blitToScreen(_entranceDoorAnimSteps[0], &closedDoorLeftBox, k64_byteWidth, kDMColorNoTransparency, 161);
+ _displayMan->blitToScreen(_entranceDoorAnimSteps[4], &closedDoorRightBox, k64_byteWidth, kDMColorNoTransparency, 161);
+ _displayMan->startEndFadeToPalette(palEntrance);
+}
+
+void DMEngine::openEntranceDoors() {
+ Box rightDoorBox(109, 231, 30, 193);
+ byte *rightDoorBitmap = _displayMan->getNativeBitmapOrGraphic(kDMGraphicIdxEntranceRightDoor);
+ Box leftDoorBox(0, 100, 30, 193);
+ uint16 leftDoorBlitFrom = 0;
+ byte *leftDoorBitmap = _displayMan->getNativeBitmapOrGraphic(kDMGraphicIdxEntranceLeftDoor);
+
+ Box screenBox(0, 319, 0, 199);
+
+ for (uint16 animStep = 1; animStep < 32; ++animStep) {
+ if ((animStep % 3) == 1) {
+ // Strangerke: CHECKME: Earlier versions of the game were using G0565_puc_Graphic535_Sound02DoorRattle instead of k02_soundDOOR_RATTLE 2
+ _sound->play(kDMSoundIndexDoorRattle, 145, 0x40, 0x40);
+ }
+
+ _displayMan->blitToScreen(_savedScreenForOpenEntranceDoors, &screenBox, 160, kDMColorNoTransparency, 200);
+ _displayMan->blitToBitmap(leftDoorBitmap, _displayMan->_bitmapScreen, leftDoorBox, leftDoorBlitFrom, 0, 64, k160_byteWidthScreen,
+ kDMColorNoTransparency, 161, k200_heightScreen);
+ _displayMan->blitToBitmap(rightDoorBitmap, _displayMan->_bitmapScreen, rightDoorBox, 0, 0, 64, k160_byteWidthScreen,
+ kDMColorNoTransparency, 161, k200_heightScreen);
+ _eventMan->discardAllInput();
+ _displayMan->updateScreen();
+
+ leftDoorBox._rect.right -= 4;
+ leftDoorBlitFrom += 4;
+ rightDoorBox._rect.left += 4;
+
+ delay(3);
+ }
+ delete[] _savedScreenForOpenEntranceDoors;
+ _savedScreenForOpenEntranceDoors = nullptr;
+}
+
+void DMEngine::drawTittle() {
+ static Box boxTitleStrikesBackDestination(0, 319, 118, 174);
+ static Box boxTitleStrikesBackSource(0, 319, 0, 56);
+ static Box boxTitlePresents(0, 319, 90, 105);
+ static Box boxTitleDungeonChaos(0, 319, 0, 79);
+
+ _displayMan->_useByteBoxCoordinates = false;
+
+ byte *allocatedMem = new byte[145600 * 2];
+ byte *titleSteps = allocatedMem;
+ byte *bitmapTitle = titleSteps;
+ _displayMan->loadIntoBitmap(kDMGraphicIdxTitle, titleSteps);
+
+ titleSteps += 320 * 200;
+ uint16 blitPalette[16];
+ for (uint16 i = 0; i < 16; ++i)
+ blitPalette[i] = D01_RGB_DARK_BLUE;
+
+ _displayMan->startEndFadeToPalette(blitPalette);
+ _displayMan->fillScreen(kDMColorBlack);
+ // uncomment this to draw 'Presents'
+ //_displayMan->f132_blitToBitmap(L1384_puc_Bitmap_Title, _displayMan->_g348_bitmapScreen, G0005_s_Graphic562_Box_Title_Presents, 0, 137, k160_byteWidthScreen, k160_byteWidthScreen, kM1_ColorNoTransparency, k200_heightScreen, k200_heightScreen);
+ blitPalette[15] = D09_RGB_WHITE;
+ _displayMan->startEndFadeToPalette(blitPalette);
+ byte *masterStrikesBack = titleSteps;
+ _displayMan->blitToBitmap(bitmapTitle, masterStrikesBack, boxTitleStrikesBackSource, 0, 80, k160_byteWidthScreen, k160_byteWidthScreen, kDMColorNoTransparency, 200, 57);
+ titleSteps += 320 * 57;
+ byte *bitmapDungeonChaos = titleSteps; /* Unreferenced on Atari ST */
+ _displayMan->blitToBitmap(bitmapTitle, bitmapDungeonChaos, boxTitleDungeonChaos, 0, 0, k160_byteWidthScreen, k160_byteWidthScreen, kDMColorNoTransparency, 200, 80);
+ titleSteps += 320 * 80;
+ bitmapTitle = bitmapDungeonChaos;
+ uint16 destinationHeight = 12;
+ int16 destinationPixelWidth = 48;
+ byte *shrinkedTitle[20]; /* Only the first 18 entries are actually used */
+ int16 blitCoordinates[20][5]; /* Only the first 18 entries are actually used */
+ for (int16 i = 0; i < 18; i++) {
+ shrinkedTitle[i] = titleSteps;
+ _displayMan->blitToBitmapShrinkWithPalChange(bitmapTitle, titleSteps, 320, 80, destinationPixelWidth, destinationHeight, _displayMan->_palChangesNoChanges);
+ blitCoordinates[i][0] = (320 - destinationPixelWidth) / 2;
+ blitCoordinates[i][1] = blitCoordinates[i][0] + destinationPixelWidth - 1;
+ blitCoordinates[i][2] = (160 - destinationHeight) / 2;
+ blitCoordinates[i][3] = blitCoordinates[i][2] + destinationHeight - 1;
+ titleSteps += (blitCoordinates[i][4] = ((destinationPixelWidth + 15) / 16) * 8) * destinationHeight * 2;
+ destinationHeight += 4;
+ destinationPixelWidth += 16;
+ }
+ blitPalette[15] = D01_RGB_DARK_BLUE;
+ _displayMan->startEndFadeToPalette(blitPalette);
+ _displayMan->fillScreen(kDMColorBlack);
+ blitPalette[3] = D05_RGB_DARK_GOLD;
+ blitPalette[4] = D02_RGB_LIGHT_BROWN;
+ blitPalette[5] = D06_RGB_GOLD;
+ blitPalette[6] = D04_RGB_LIGHTER_BROWN;
+ blitPalette[8] = D08_RGB_YELLOW;
+ blitPalette[15] = D07_RGB_RED;
+ blitPalette[10] = D01_RGB_DARK_BLUE;
+ blitPalette[12] = D01_RGB_DARK_BLUE;
+ _displayMan->startEndFadeToPalette(blitPalette);
+ delay(1);
+ for (int16 i = 0; i < 18; i++) {
+ delay(2);
+ Box box(blitCoordinates[i][0], blitCoordinates[i][1], blitCoordinates[i][2], blitCoordinates[i][3]);
+ _displayMan->blitToBitmap(shrinkedTitle[i], _displayMan->_bitmapScreen, box, 0, 0, blitCoordinates[i][4], k160_byteWidthScreen, kDMColorNoTransparency, blitCoordinates[i][3] - blitCoordinates[i][2] + 1, k200_heightScreen);
+ }
+ delay(25);
+ _displayMan->blitToBitmap(masterStrikesBack, _displayMan->_bitmapScreen, boxTitleStrikesBackDestination, 0, 0, k160_byteWidthScreen, k160_byteWidthScreen, kDMColorBlack, 57, k200_heightScreen);
+ blitPalette[10] = D00_RGB_BLACK;
+ blitPalette[12] = D07_RGB_RED;
+ _displayMan->startEndFadeToPalette(blitPalette);
+ delete[] allocatedMem;
+ delay(75);
+}
+
+void DMEngine::entranceDrawCredits() {
+ _eventMan->showMouse();
+ _displayMan->startEndFadeToPalette(_displayMan->_blankBuffer);
+ _displayMan->loadIntoBitmap(kDMGraphicIdxCredits, _displayMan->_bitmapScreen);
+ _displayMan->startEndFadeToPalette(_displayMan->_palCredits);
+ delay(50);
+ _eventMan->waitForMouseOrKeyActivity();
+ _gameMode = kDMModeEntranceDrawCredits;
+}
+
+void DMEngine::fuseSequence() {
+ _gameWon = true;
+ if (_inventoryMan->_inventoryChampionOrdinal)
+ _inventoryMan->toggleInventory(kDMChampionCloseInventory);
+
+ _eventMan->highlightBoxDisable();
+ _championMan->_party._magicalLightAmount = 200;
+ _inventoryMan->setDungeonViewPalette();
+ _championMan->_party._fireShieldDefense = _championMan->_party._spellShieldDefense = _championMan->_party._shieldDefense = 100;
+ _timeline->refreshAllChampionStatusBoxes();
+ fuseSequenceUpdate();
+ int16 lordChaosMapX = _dungeonMan->_partyMapX;
+ int16 lordChaosMapY = _dungeonMan->_partyMapY;
+ lordChaosMapX += _dirIntoStepCountEast[_dungeonMan->_partyDir], lordChaosMapY += _dirIntoStepCountNorth[_dungeonMan->_partyDir];
+ Thing lordChaosThing = _groupMan->groupGetThing(lordChaosMapX, lordChaosMapY);
+ Group *lordGroup = (Group*)_dungeonMan->getThingData(lordChaosThing);
+ lordGroup->_health[0] = 10000;
+ _dungeonMan->setGroupCells(lordGroup, kDMCreatureTypeSingleCenteredCreature, _dungeonMan->_partyMapIndex);
+ _dungeonMan->setGroupDirections(lordGroup, returnOppositeDir(_dungeonMan->_partyDir), _dungeonMan->_partyMapIndex);
+
+ bool removeFluxcagesFromLordChaosSquare = true;
+ int16 fluxCageMapX = _dungeonMan->_partyMapX;
+ int16 fluxcageMapY = _dungeonMan->_partyMapY;
+
+ for (;;) {
+ Thing curThing = _dungeonMan->getSquareFirstObject(fluxCageMapX, fluxcageMapY);
+ while (curThing != Thing::_endOfList) {
+ if (curThing.getType() == kDMThingTypeExplosion) {
+ Explosion *curExplosion = (Explosion*)_dungeonMan->getThingData(curThing);
+ if (curExplosion->getType() == kDMExplosionTypeFluxcage) {
+ _dungeonMan->unlinkThingFromList(curThing, Thing(0), fluxCageMapX, fluxcageMapY);
+ curExplosion->setNextThing(Thing::_none);
+ continue;
+ }
+ }
+ curThing = _dungeonMan->getNextThing(curThing);
+ }
+ if (removeFluxcagesFromLordChaosSquare) {
+ removeFluxcagesFromLordChaosSquare = false;
+ fluxCageMapX = lordChaosMapX;
+ fluxcageMapY = lordChaosMapY;
+ } else
+ break;
+ }
+ fuseSequenceUpdate();
+ for (int16 attackId = 55; attackId <= 255; attackId += 40) {
+ _projexpl->createExplosion(Thing::_explFireBall, attackId, lordChaosMapX, lordChaosMapY, kDMCreatureTypeSingleCenteredCreature);
+ fuseSequenceUpdate();
+ }
+ _sound->requestPlay(kDMSoundIndexBuzz, lordChaosMapX, lordChaosMapY, kDMSoundModePlayIfPrioritized);
+ lordGroup->_type = kDMCreatureTypeLordOrder;
+ fuseSequenceUpdate();
+ for (int16 attackId = 55; attackId <= 255; attackId += 40) {
+ _projexpl->createExplosion(Thing::_explHarmNonMaterial, attackId, lordChaosMapX, lordChaosMapY, kDMCreatureTypeSingleCenteredCreature);
+ fuseSequenceUpdate();
+ }
+ for (int16 cycleCount = 3; cycleCount > 0; cycleCount--) {
+ for (int16 switchCount = 4; switchCount > 0; switchCount--) {
+ _sound->requestPlay(kDMSoundIndexBuzz, lordChaosMapX, lordChaosMapY, kDMSoundModePlayIfPrioritized);
+ lordGroup->_type = (switchCount & 0x0001) ? kDMCreatureTypeLordOrder : kDMCreatureTypeLordChaos;
+ for (int16 fuseSequenceUpdateCount = cycleCount - 1; fuseSequenceUpdateCount >= 0; fuseSequenceUpdateCount--)
+ fuseSequenceUpdate();
+ }
+ }
+ _projexpl->createExplosion(Thing::_explFireBall, 255, lordChaosMapX, lordChaosMapY, kDMCreatureTypeSingleCenteredCreature);
+ _projexpl->createExplosion(Thing::_explHarmNonMaterial, 255, lordChaosMapX, lordChaosMapY, kDMCreatureTypeSingleCenteredCreature);
+ fuseSequenceUpdate();
+ lordGroup->_type = kDMCreatureTypeGreyLord;
+ fuseSequenceUpdate();
+ _displayMan->_doNotDrawFluxcagesDuringEndgame = true;
+ fuseSequenceUpdate();
+ for (int16 curMapX = 0; curMapX < _dungeonMan->_currMapWidth; curMapX++) {
+ for (int curMapY = 0; curMapY < _dungeonMan->_currMapHeight; curMapY++) {
+ Thing curThing = _groupMan->groupGetThing(curMapX, curMapY);
+ if ((curThing != Thing::_endOfList) && ((curMapX != lordChaosMapX) || (curMapY != lordChaosMapY))) {
+ _groupMan->groupDelete(curMapX, curMapY);
+ }
+ }
+ }
+ fuseSequenceUpdate();
+ /* Count and get list of text things located at 0, 0 in the current map. Their text is then printed as messages in the order specified by their first letter (which is not printed) */
+ Thing curThing = _dungeonMan->getSquareFirstThing(0, 0);
+ int16 textStringThingCount = 0;
+ Thing textStringThings[8];
+ while (curThing != Thing::_endOfList) {
+ if (curThing.getType() == kDMstringTypeText)
+ textStringThings[textStringThingCount++] = curThing;
+
+ curThing = _dungeonMan->getNextThing(curThing);
+ }
+ char textFirstChar = 'A';
+ int16 maxCount = textStringThingCount;
+ while (textStringThingCount--) {
+ for (int16 idx = 0; idx < maxCount; idx++) {
+ char decodedString[200];
+ _dungeonMan->decodeText(decodedString, textStringThings[idx], (TextType)(kDMTextTypeMessage | kDMMaskDecodeEvenIfInvisible));
+ if (decodedString[1] == textFirstChar) {
+ _textMan->clearAllRows();
+ decodedString[1] = '\n'; /* New line */
+ _textMan->printMessage(kDMColorWhite, &decodedString[1]);
+ fuseSequenceUpdate();
+ delay(780);
+ textFirstChar++;
+ break;
+ }
+ }
+ }
+
+ for (int16 attackId = 55; attackId <= 255; attackId += 40) {
+ _projexpl->createExplosion(Thing::_explHarmNonMaterial, attackId, lordChaosMapX, lordChaosMapY, kDMCreatureTypeSingleCenteredCreature);
+ fuseSequenceUpdate();
+ }
+
+ delay(600);
+ _restartGameAllowed = false;
+ endGame(true);
+}
+
+void DMEngine::fuseSequenceUpdate() {
+ _timeline->processTimeline();
+ _displayMan->drawDungeon(_dungeonMan->_partyDir, _dungeonMan->_partyMapX, _dungeonMan->_partyMapY);
+ _sound->playPendingSound();
+ _eventMan->discardAllInput();
+ _displayMan->updateScreen();
+ delay(2);
+ _gameTime++; /* BUG0_71 Some timings are too short on fast computers.
+ The ending animation when Lord Chaos is fused plays too quickly because the execution speed is not limited */
+}
+
+Common::Language DMEngine::getGameLanguage() {
+ return _gameVersion->_desc.language;
+}
+
+} // End of namespace DM