From ce96094c9bc54266c9742364e656c97446a6ecaf Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Mon, 5 Jan 2015 08:11:16 -0500 Subject: XEEN: In progress implementing map loading --- engines/xeen/dialogs.cpp | 25 +- engines/xeen/dialogs.h | 7 +- engines/xeen/dialogs_error.cpp | 77 ++++ engines/xeen/dialogs_error.h | 50 +++ engines/xeen/events.cpp | 16 +- engines/xeen/events.h | 19 +- engines/xeen/files.cpp | 5 +- engines/xeen/files.h | 6 +- engines/xeen/interface.cpp | 221 +++++++++-- engines/xeen/interface.h | 18 +- engines/xeen/map.cpp | 824 +++++++++++++++++++++++++++++++++++++++++ engines/xeen/map.h | 264 +++++++++++++ engines/xeen/module.mk | 2 + engines/xeen/party.cpp | 12 +- engines/xeen/party.h | 11 +- engines/xeen/resources.cpp | 24 ++ engines/xeen/resources.h | 10 + engines/xeen/saves.cpp | 8 + engines/xeen/saves.h | 4 + engines/xeen/screen.cpp | 4 +- engines/xeen/screen.h | 2 +- engines/xeen/sprites.cpp | 8 +- engines/xeen/sprites.h | 2 + engines/xeen/xeen.cpp | 35 +- engines/xeen/xeen.h | 13 +- 25 files changed, 1591 insertions(+), 76 deletions(-) create mode 100644 engines/xeen/dialogs_error.cpp create mode 100644 engines/xeen/dialogs_error.h create mode 100644 engines/xeen/map.cpp create mode 100644 engines/xeen/map.h (limited to 'engines') diff --git a/engines/xeen/dialogs.cpp b/engines/xeen/dialogs.cpp index 70a22d16ee..8e8e7521cb 100644 --- a/engines/xeen/dialogs.cpp +++ b/engines/xeen/dialogs.cpp @@ -50,19 +50,19 @@ void ButtonContainer::addButton(const Common::Rect &bounds, int val, SpriteResou _buttons.push_back(UIButton(bounds, val, sprites, draw)); } -void ButtonContainer::checkEvents(XeenEngine *vm) { +bool ButtonContainer::checkEvents(XeenEngine *vm) { EventsManager &events = *vm->_events; - events.pollEventsAndWait(); if (events._leftButton) { // Check whether any button is selected - events.debounceMouse(); Common::Point pt = events._mousePos; for (uint i = 0; i < _buttons.size(); ++i) { if (_buttons[i]._bounds.contains(pt)) { + events.debounceMouse(); + _buttonValue = _buttons[i]._value; - return; + return true; } } } else if (events.isKeyPending()) { @@ -70,9 +70,11 @@ void ButtonContainer::checkEvents(XeenEngine *vm) { events.getKey(keyState); if (keyState.ascii >= 32 && keyState.ascii <= 127) { _buttonValue = keyState.ascii; - return; + return true; } } + + return false; } @@ -193,10 +195,10 @@ void ButtonContainer::drawButtons(XSurface *surface) { } } - /*------------------------------------------------------------------------*/ void SettingsBaseDialog::showContents(SpriteResource &title1, bool waitFlag) { + _vm->_events->pollEventsAndWait(); checkEvents(_vm); } @@ -231,4 +233,15 @@ void CreditsScreen::execute() { doScroll(_vm, true, false); } +/*------------------------------------------------------------------------*/ + +void PleaseWait::show(XeenEngine *vm) { + if (vm->_mode != MODE_0) { + Window &w = vm->_screen->_windows[9]; + w.open(); + w.writeString(PLEASE_WAIT); + w.update(); + } +} + } // End of namespace Xeen diff --git a/engines/xeen/dialogs.h b/engines/xeen/dialogs.h index 61ae73473d..987df6b2f1 100644 --- a/engines/xeen/dialogs.h +++ b/engines/xeen/dialogs.h @@ -54,7 +54,7 @@ protected: void doScroll(XeenEngine *vm, bool drawFlag, bool doFade); - void checkEvents(XeenEngine *vm); + bool checkEvents(XeenEngine *vm); void drawButtons(XSurface *surface); public: @@ -89,6 +89,11 @@ public: static void show(XeenEngine *vm); }; +class PleaseWait { +public: + static void show(XeenEngine *vm); +}; + } // End of namespace Xeen #endif /* XEEN_DIALOGS_H */ diff --git a/engines/xeen/dialogs_error.cpp b/engines/xeen/dialogs_error.cpp new file mode 100644 index 0000000000..c0c072386d --- /dev/null +++ b/engines/xeen/dialogs_error.cpp @@ -0,0 +1,77 @@ +/* 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 "common/scummsys.h" +#include "xeen/dialogs_error.h" +#include "xeen/events.h" +#include "xeen/xeen.h" + +namespace Xeen { + +void ErrorScroll::show(XeenEngine *vm, const Common::String &msg, ErrorWaitType waitType) { + ErrorScroll *dlg = new ErrorScroll(vm); + dlg->execute(msg, waitType); + delete dlg; +} + +void ErrorScroll::execute(const Common::String &msg, ErrorWaitType waitType) { + Screen &screen = *_vm->_screen; + EventsManager &events = *_vm->_events; + Window &w = screen._windows[6]; + + Common::String s = Common::String::format("\x03c\v010\t000%s", msg); + w.open(); + w.writeString(s); + w.update(); + + switch (waitType) { + case WT_FREEZE_WAIT: + while (!_vm->shouldQuit() && !events.isKeyPending()) + events.pollEventsAndWait(); + + events.clearEvents(); + break; + case WT_3: + if (w._enabled || _vm->_mode == MODE_17) { + warning("TODO: sub_26D8F"); + break; + } + // Deliberate fall-through + case WT_NONFREEZED_WAIT: + do { + events.updateGameCounter(); + _vm->_interface->draw3d(true); + + events.wait(1, true); + if (checkEvents(_vm)) + break; + } while (!_vm->shouldQuit() && !_buttonValue); + break; + case WT_2: + warning("TODO: sub_26D8F"); + break; + default: + break; + } +} + +} // End of namespace Xeen diff --git a/engines/xeen/dialogs_error.h b/engines/xeen/dialogs_error.h new file mode 100644 index 0000000000..03a6f16cf2 --- /dev/null +++ b/engines/xeen/dialogs_error.h @@ -0,0 +1,50 @@ +/* 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 XEEN_DIALOGS_ERROR_H +#define XEEN_DIALOGS_ERROR_H + +#include "common/array.h" +#include "common/stack.h" +#include "common/rect.h" +#include "xeen/dialogs.h" + +namespace Xeen { + +enum ErrorWaitType { WT_FREEZE_WAIT = 0, WT_NONFREEZED_WAIT = 1, + WT_2 = 2, WT_3 = 3}; + +class ErrorScroll: public ButtonContainer { +private: + XeenEngine *_vm; + + ErrorScroll(XeenEngine *vm) : ButtonContainer(), _vm(vm) {} + + void execute(const Common::String &msg, ErrorWaitType waitType); +public: + static void show(XeenEngine *vm, const Common::String &msg, + ErrorWaitType waitType = WT_FREEZE_WAIT); +}; + +} // End of namespace Xeen + +#endif /* XEEN_DIALOGS_ERROR_H */ diff --git a/engines/xeen/events.cpp b/engines/xeen/events.cpp index 285005af98..e0c3b26dc6 100644 --- a/engines/xeen/events.cpp +++ b/engines/xeen/events.cpp @@ -39,6 +39,7 @@ EventsManager::EventsManager(XeenEngine *vm) : _vm(vm), _priorGameCounterTime(0), _keyCode(Common::KEYCODE_INVALID), _leftButton(false), _rightButton(false), _sprites("mouse.icn") { + Common::fill(&_gameCounters[0], &_gameCounters[6], 0); } /** @@ -157,21 +158,6 @@ bool EventsManager::isKeyMousePressed() { return result; } -/** - * Updates the game counter to match the current frame counter - */ -void EventsManager::updateGameCounter() { - _gameCounter = _frameCounter; -} - -/** - * Returns the number of frames elapsed since the last call to - * updateGameCounter() - */ -uint32 EventsManager::timeElapsed() { - return _frameCounter - _gameCounter; -} - bool EventsManager::wait(uint numFrames, bool interruptable) { while (!_vm->shouldQuit() && timeElapsed() < numFrames) { pollEventsAndWait(); diff --git a/engines/xeen/events.h b/engines/xeen/events.h index 7a6a65f4fb..1705bb43d3 100644 --- a/engines/xeen/events.h +++ b/engines/xeen/events.h @@ -39,6 +39,7 @@ private: uint32 _frameCounter; uint32 _priorFrameCounterTime; uint32 _gameCounter; + uint32 _gameCounters[6]; uint32 _priorGameCounterTime; Common::KeyCode _keyCode; SpriteResource _sprites; @@ -52,8 +53,6 @@ public: ~EventsManager(); - uint32 getFrameCounter() { return _frameCounter; } - void setCursor(int cursorId); void showCursor(); @@ -76,9 +75,19 @@ public: bool isKeyMousePressed(); - void updateGameCounter(); - - uint32 timeElapsed(); + void updateGameCounter() { _gameCounter = _frameCounter; } + void timeMark1() { _gameCounters[1] = _frameCounter; } + void timeMark2() { _gameCounters[2] = _frameCounter; } + void timeMark3() { _gameCounters[3] = _frameCounter; } + void timeMark4() { _gameCounters[4] = _frameCounter; } + void timeMark5() { _gameCounters[5] = _frameCounter; } + + uint32 timeElapsed() const { return _frameCounter - _gameCounter; } + uint32 timeElapsed1() const { return _frameCounter - _gameCounters[1]; } + uint32 timeElapsed2() const { return _frameCounter - _gameCounters[2]; } + uint32 timeElapsed3() const { return _frameCounter - _gameCounters[3]; } + uint32 timeElapsed4() const { return _frameCounter - _gameCounters[4]; } + uint32 timeElapsed5() const { return _frameCounter - _gameCounters[5]; } bool wait(uint numFrames, bool interruptable = false); }; diff --git a/engines/xeen/files.cpp b/engines/xeen/files.cpp index 107cde45a1..a0ca2435a1 100644 --- a/engines/xeen/files.cpp +++ b/engines/xeen/files.cpp @@ -160,10 +160,11 @@ bool CCArchive::getHeaderEntry(const Common::String &resourceName, CCEntry &ccEn /** * Instantiates the resource manager */ -void FileManager::init(XeenEngine *vm) { +FileManager::FileManager(XeenEngine *vm) { Common::File f; - if (vm->getGameID() != GType_Clouds) + _isDarkCc = vm->getGameID() != GType_Clouds; + if (_isDarkCc) SearchMan.add("dark", new CCArchive("dark.cc")); SearchMan.add("xeen", new CCArchive("xeen.cc")); SearchMan.add("intro", new CCArchive("intro.cc")); diff --git a/engines/xeen/files.h b/engines/xeen/files.h index 8a028a95c7..3e38ad842c 100644 --- a/engines/xeen/files.h +++ b/engines/xeen/files.h @@ -38,7 +38,11 @@ class XeenEngine; */ class FileManager { public: - static void init(XeenEngine *vm); + bool _isDarkCc; +public: + FileManager(XeenEngine *vm); + + void setGameCc(bool isDarkCc) { _isDarkCc = isDarkCc; } }; /** diff --git a/engines/xeen/interface.cpp b/engines/xeen/interface.cpp index 3a07cecec6..08128e54f0 100644 --- a/engines/xeen/interface.cpp +++ b/engines/xeen/interface.cpp @@ -21,6 +21,7 @@ */ #include "xeen/interface.h" +#include "xeen/dialogs_error.h" #include "xeen/resources.h" #include "xeen/xeen.h" @@ -41,16 +42,18 @@ Interface::Interface(XeenEngine *vm) : ButtonContainer(), _vm(vm) { _buttonsLoaded = false; _hiliteChar = -1; Common::fill(&_combatCharIds[0], &_combatCharIds[8], 0); + _intrIndex1 = 0; _faceDrawStructs[0] = DrawStruct(nullptr, 0, 0, 0); _faceDrawStructs[1] = DrawStruct(nullptr, 0, 101, 0); _faceDrawStructs[2] = DrawStruct(nullptr, 0, 0, 43); _faceDrawStructs[3] = DrawStruct(nullptr, 0, 101, 43); - - loadSprites(); } -void Interface::loadSprites() { +void Interface::manageCharacters(bool soundPlayed) { + Screen &screen = *_vm->_screen; + EventsManager &events = *_vm->_events; + bool flag = false; _globalSprites.load("global.icn"); _borderSprites.load("border.icn"); _spellFxSprites.load("spellfx.icn"); @@ -58,21 +61,21 @@ void Interface::loadSprites() { _blessSprites.load("bless.icn"); _restoreSprites.load("restorex.icn"); _hpSprites.load("hpbars.icn"); -} - -void Interface::setup(bool soundPlayed) { - Screen &screen = *_vm->_screen; - SpriteResource uiSprites("inn.icn"); + _uiSprites.load("inn.icn"); +start: // Get mappings to the active characters in the party _vm->_party._activeParty.resize(_vm->_party._partyCount); for (int i = 0; i < _vm->_party._partyCount; ++i) { - _vm->_party._activeParty[i] = &_vm->_roster[_vm->_party._partyMembers[i]]; + _vm->_party._activeParty[i] = _vm->_roster[_vm->_party._partyMembers[i]]; } _isEarlyGame = _vm->_party._minutes >= 300; - if (_vm->_party._mazeId == 0) { + if (_vm->_party._mazeId != 0) { + _vm->_mode = MODE_0; + _buttonsLoaded = true; + } else { if (!soundPlayed) { warning("TODO: loadSound?"); } @@ -100,25 +103,156 @@ void Interface::setup(bool soundPlayed) { // Add in buttons for the UI _interfaceText = ""; _buttonsLoaded = true; - addButton(Common::Rect(16, 100, 40, 120), 242, &uiSprites, true); - addButton(Common::Rect(52, 100, 76, 120), 243, &uiSprites, true); - addButton(Common::Rect(87, 100, 111, 120), 68, &uiSprites, true); - addButton(Common::Rect(122, 100, 146, 120), 82, &uiSprites, true); - addButton(Common::Rect(157, 100, 181, 120), 67, &uiSprites, true); - addButton(Common::Rect(192, 100, 216, 120), 88, &uiSprites, true); - addButton(Common::Rect(), 27, &uiSprites, false); - addButton(Common::Rect(16, 16, 48, 48), 49, &uiSprites, false); - addButton(Common::Rect(117, 16, 139, 48), 50, &uiSprites, false); - addButton(Common::Rect(16, 59, 48, 81), 51, &uiSprites, false); - addButton(Common::Rect(117, 59, 149, 81), 52, &uiSprites, false); + addButton(Common::Rect(16, 100, 40, 120), 242, &_uiSprites, true); + addButton(Common::Rect(52, 100, 76, 120), 243, &_uiSprites, true); + addButton(Common::Rect(87, 100, 111, 120), 68, &_uiSprites, true); + addButton(Common::Rect(122, 100, 146, 120), 82, &_uiSprites, true); + addButton(Common::Rect(157, 100, 181, 120), 67, &_uiSprites, true); + addButton(Common::Rect(192, 100, 216, 120), 88, &_uiSprites, true); + addButton(Common::Rect(), 27, &_uiSprites, false); + addButton(Common::Rect(16, 16, 48, 48), 49, &_uiSprites, false); + addButton(Common::Rect(117, 16, 139, 48), 50, &_uiSprites, false); + addButton(Common::Rect(16, 59, 48, 81), 51, &_uiSprites, false); + addButton(Common::Rect(117, 59, 149, 81), 52, &_uiSprites, false); setupBackground(); - screen._windows[11].open(); - setupFaces(0, xeenSideChars, 0); - screen._windows[11].writeString(_interfaceText); + Window &w = screen._windows[11]; + w.open(); + setupFaces(0, xeenSideChars, false); + w.writeString(_interfaceText); + w.drawList(&_faceDrawStructs[0], 4); + + _uiSprites.draw(w, 0, Common::Point(16, 100)); + _uiSprites.draw(w, 2, Common::Point(52, 100)); + _uiSprites.draw(w, 4, Common::Point(87, 100)); + _uiSprites.draw(w, 6, Common::Point(122, 100)); + _uiSprites.draw(w, 8, Common::Point(157, 100)); + _uiSprites.draw(w, 10, Common::Point(192, 100)); + + screen.loadPalette("mm4.pal"); + + if (flag) { + screen._windows[0].update(); + events.setCursor(0); + screen.fadeIn(4); + } else { + if (_vm->getGameID() == GType_DarkSide) { + screen.fadeOut(4); + screen._windows[0].update(); + } + + doScroll(_vm, false, false); + events.setCursor(0); + + if (_vm->getGameID() == GType_DarkSide) { + screen.fadeIn(4); + } + } // TODO + bool breakFlag = false; + while (!_vm->shouldQuit() && !breakFlag) { + events.pollEventsAndWait(); + checkEvents(_vm); + + switch (_buttonValue) { + case Common::KEYCODE_ESCAPE: + case Common::KEYCODE_SPACE: + case Common::KEYCODE_e: + case Common::KEYCODE_x: + if (_vm->_party._partyCount == 0) { + ErrorScroll::show(_vm, NO_ONE_TO_ADVENTURE_WITH); + } else { + if (_vm->_mode != MODE_0) { + for (_intrIndex1 = 4; _intrIndex1 >= 0; --_intrIndex1) { + events.updateGameCounter(); + drawViewBackground(_intrIndex1); + w.update(); + + while (events.timeElapsed() < 1) + events.pollEventsAndWait(); + } + } + + w.close(); + _vm->_party._realPartyCount = _vm->_party._partyCount; + _vm->_party._mazeId = _vm->_party._priorMazeId; + + _vm->_party.copyPartyToRoster(_vm->_roster); + _vm->_saves->writeCharFile(); + breakFlag = true; + break; + } + break; + case Common::KEYCODE_1: + break; + case Common::KEYCODE_2: + break; + case Common::KEYCODE_3: + break; + case Common::KEYCODE_4: + break; + case Common::KEYCODE_c: + if (xeenSideChars.size() == 24) { + ErrorScroll::show(_vm, YOUR_ROSTER_IS_FULL); + } else { + screen.fadeOut(4); + w.close(); + addCharacterToRoster(); + _vm->_saves->writeCharFile(); + screen.fadeOut(4); + flag = true; + _buttonsLoaded = true; + goto start; + } + break; + case Common::KEYCODE_d: + break; + case Common::KEYCODE_r: + if (_vm->_party._partyCount > 0) { + // TODO + } + break; + case 201: + // TODO + break; + case 202: + // TODO + break; + case 203: + // TODO + break; + case 204: + // TODO + break; + case 205: + // TODO + break; + case 206: + // TODO + break; + case 242: + // TODO + break; + case 243: + // TODO + break; + default: + break; + } + } } + + for (int i = 0; i < TOTAL_CHARACTERS; ++i) + _charFaces[i].clear(); + _globalSprites.clear(); + _borderSprites.clear(); + _spellFxSprites.clear(); + _fecpSprites.clear(); + _blessSprites.clear(); + _restoreSprites.clear(); + _hpSprites.clear(); + _uiSprites.clear(); } void Interface::loadCharIcons(int numChars) { @@ -244,7 +378,7 @@ void Interface::assembleBorder() { screen._windows[12].frame(); } -void Interface::setupFaces(int charIndex, Common::Array xeenSideChars, int v3) { +void Interface::setupFaces(int charIndex, Common::Array xeenSideChars, bool updateFlag) { Common::String playerNames[4]; Common::String playerRaces[4]; Common::String playerSex[4]; @@ -271,7 +405,7 @@ void Interface::setupFaces(int charIndex, Common::Array xeenSideChars, int playerClass[posIndex] = CLASS_NAMES[ps._class]; } - charIconsPrint(v3); + charIconsPrint(updateFlag); // Set up the sprite set to use for each face charId = xeenSideChars[charIndex]; @@ -300,7 +434,7 @@ void Interface::charIconsPrint(bool updateFlag) { for (int idx = 0; idx < (stateFlag ? _vm->_party._combatPartyCount : _vm->_party._partyCount); ++idx) { int charIndex = stateFlag ? _combatCharIds[idx] : idx; - PlayerStruct &ps = *_vm->_party._activeParty[charIndex]; + PlayerStruct &ps = _vm->_party._activeParty[charIndex]; Condition charCondition = ps.findCondition(); int charFrame = FACE_CONDITION_FRAMES[charCondition]; @@ -316,7 +450,7 @@ void Interface::charIconsPrint(bool updateFlag) { for (int idx = 0; idx < (stateFlag ? _vm->_party._combatPartyCount : _vm->_party._partyCount); ++idx) { int charIndex = stateFlag ? _combatCharIds[idx] : idx; - PlayerStruct &ps = *_vm->_party._activeParty[charIndex]; + PlayerStruct &ps = _vm->_party._activeParty[charIndex]; // Draw the Hp bar int maxHp = ps.getMaxHp(); @@ -343,4 +477,35 @@ void Interface::charIconsPrint(bool updateFlag) { screen._windows[33].update(); } +void Interface::drawViewBackground(int bgType) { + if (bgType >= 4) + return; + + if (bgType == 0) { + // Totally black background + _vm->_screen->fillRect(Common::Rect(8, 8, 224, 140), 0); + } else { + const byte *lookup = BACKGROUND_XLAT + bgType; + for (int yp = 8; yp < 140; ++yp) { + byte *destP = (byte *)_vm->_screen->getBasePtr(8, yp); + for (int xp = 8; xp < 224; ++xp, ++destP) + *destP = lookup[*destP]; + } + } +} + +void Interface::addCharacterToRoster() { + error("TODO"); +} + +void Interface::draw3d(bool flag) { + Screen &screen = *_vm->_screen; + EventsManager &events = *_vm->_events; + + if (!screen._windows[11]._enabled) + return; + + warning("TODO"); +} + } // End of namespace Xeen diff --git a/engines/xeen/interface.h b/engines/xeen/interface.h index 3ceb08239e..1ed7afe628 100644 --- a/engines/xeen/interface.h +++ b/engines/xeen/interface.h @@ -43,6 +43,7 @@ private: SpriteResource _blessSprites; SpriteResource _restoreSprites; SpriteResource _hpSprites; + SpriteResource _uiSprites; SpriteResource _charFaces[TOTAL_CHARACTERS]; SpriteResource *_partyFaces[MAX_ACTIVE_PARTY]; DrawStruct _faceDrawStructs[4]; @@ -61,22 +62,27 @@ private: bool _buttonsLoaded; Common::String _interfaceText; int _hiliteChar; - - void loadSprites(); - - void loadCharIcons(int numChars); + int _intrIndex1; void assembleBorder(); void setupBackground(); - void setupFaces(int charIndex, Common::Array xeenSideChars, int v3); + void setupFaces(int charIndex, Common::Array xeenSideChars, bool updateFlag); void charIconsPrint(bool updateFlag); + + void drawViewBackground(int bgType); + + void addCharacterToRoster(); public: Interface(XeenEngine *vm); - void setup(bool soundPlayed); + void manageCharacters(bool soundPlayed); + + void loadCharIcons(int numChars); + + void draw3d(bool flag); }; } // End of namespace Xeen diff --git a/engines/xeen/map.cpp b/engines/xeen/map.cpp new file mode 100644 index 0000000000..d797179e15 --- /dev/null +++ b/engines/xeen/map.cpp @@ -0,0 +1,824 @@ +/* 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 "common/serializer.h" +#include "xeen/map.h" +#include "xeen/resources.h" +#include "xeen/saves.h" +#include "xeen/screen.h" +#include "xeen/xeen.h" + +namespace Xeen { + +MonsterStruct::MonsterStruct() { + _experience = 0; + _hp = 0; + _accuracy = 0; + _speed = 0; + _numberOfAttacks = 0; + _hatesClass = CLASS_KNIGHT; + _strikes = 0; + _dmgPerStrike = 0; + _attackType = DT_PHYSICAL; + _specialAttack = SA_NONE; + _hitChance = 0; + _rangeAttack = 0; + _monsterType = MONSTER_0; + _fireResistence = 0; + _electricityResistence = 0; + _coldResistence = 0; + _poisonResistence = 0; + _energyResistence = 0; + _magicResistence = 0; + _phsyicalResistence = 0; + _field29 = 0; + _gold = 0; + _gems = 0; + _itemDrop = 0; + _flying = 0; + _imageNumber = 0; + _loopAnimation = 0; + _animationEffect = 0; + _field32 = 0; +} + +MonsterStruct::MonsterStruct(Common::String name, int experience, int hp, int accuracy, + int speed, int numberOfAttacks, CharacterClass hatesClass, int strikes, + int dmgPerStrike, DamageType attackType, SpecialAttack specialAttack, + int hitChance, int rangeAttack, MonsterType monsterType, + int fireResistence, int electricityResistence, int coldResistence, + int poisonResistence, int energyResistence, int magicResistence, + int phsyicalResistence, int field29, int gold, int gems, int itemDrop, + int flying, int imageNumber, int loopAnimation, int animationEffect, + int field32, Common::String attackVoc): + _name(name), _experience(experience), _hp(hp), _accuracy(accuracy), + _speed(speed), _numberOfAttacks(numberOfAttacks), _hatesClass(hatesClass), + _strikes(strikes), _dmgPerStrike(dmgPerStrike), _attackType(attackType), + _specialAttack(specialAttack), _hitChance(hitChance), _rangeAttack(rangeAttack), + _monsterType(monsterType), _fireResistence(fireResistence), + _electricityResistence(electricityResistence), _coldResistence(coldResistence), + _poisonResistence(poisonResistence), _energyResistence(energyResistence), + _magicResistence(magicResistence), _phsyicalResistence(phsyicalResistence), + _field29(field29), _gold(gold), _gems(gems), _itemDrop(itemDrop), + _flying(flying), _imageNumber(imageNumber), _loopAnimation(loopAnimation), + _animationEffect(animationEffect), _field32(field32), _attackVoc(attackVoc) { +} + + +void MonsterStruct::synchronize(Common::SeekableReadStream &s) { + _experience = s.readByte(); + _hp = s.readByte(); + _accuracy = s.readByte(); + _speed = s.readByte(); + _numberOfAttacks = s.readByte(); + _hatesClass = CLASS_KNIGHT; + _strikes = s.readByte(); + _dmgPerStrike = s.readByte(); + _attackType = (DamageType)s.readByte(); + _specialAttack = (SpecialAttack)s.readByte(); + _hitChance = s.readByte(); + _rangeAttack = s.readByte(); + _monsterType = (MonsterType)s.readByte(); + _fireResistence = s.readByte(); + _electricityResistence = s.readByte(); + _coldResistence = s.readByte(); + _poisonResistence = s.readByte(); + _energyResistence = s.readByte(); + _magicResistence = s.readByte(); + _phsyicalResistence = s.readByte(); + _field29 = s.readByte(); + _gold = s.readByte(); + _gems = s.readByte(); + _itemDrop = s.readByte(); + _flying = s.readByte(); + _imageNumber = s.readByte(); + _loopAnimation = s.readByte(); + _animationEffect = s.readByte(); + _field32 = s.readByte(); + + char attackVoc[9]; + s.read(attackVoc, 9); + attackVoc[8] = '\0'; + _attackVoc = Common::String(attackVoc); +} + +MonsterData::MonsterData() { + push_back(MonsterStruct("", 0, 0, 0, 0, 0, CLASS_KNIGHT, 1, 1, DT_PHYSICAL, + SA_NONE, 1, 0, MONSTER_0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 100, "Slime")); + push_back(MonsterStruct("Whirlwind", 250000, 1000, 10, 250, 1, CLASS_15, 5, + 100, DT_PHYSICAL, SA_CONFUSE, 250, 0, MONSTER_0, 100, + 100, 100, 100, 0, 0, 100, 0, 0, 0, 0, 0, 1, 0, 0, 176, + "airmon")); + push_back(MonsterStruct("Annihilator", 1000000, 1500, 40, 200, 12, CLASS_16, 5, + 50, DT_ENERGY, SA_NONE, 1, 1, MONSTER_0, 80, 80, 100, + 100, 0, 0, 80, 0, 0, 0, 0, 0, 2, 0, 0, 102, "alien1")); + push_back(MonsterStruct("Autobot", 1000000, 2500, 100, 200, 2, CLASS_16, 5, + 100, DT_ENERGY, SA_NONE, 1, 0, MONSTER_0, 50, 50, 100, + 100, 0, 0, 50, 0, 0, 0, 0, 1, 3, 0, 0, 101, "alien2")); + push_back(MonsterStruct("Sewer Stalker", 50000, 250, 30, 25, 1, CLASS_16, 3, + 100, DT_PHYSICAL, SA_NONE, 50, 0, MONSTER_ANIMAL, 0, + 0, 50, 50, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 113, + "iguana")); + push_back(MonsterStruct("Armadillo", 60000, 800, 50, 15, 1, CLASS_16, 100, 6, + DT_PHYSICAL, SA_BREAKWEAPON, 60, 0, MONSTER_ANIMAL, + 50, 0, 80, 80, 50, 0, 50, 0, 0, 0, 0, 0, 5, 1, 0, 113, + "unnh")); + push_back(MonsterStruct("Barbarian", 5000, 50, 5, 40, 3, CLASS_SORCERER, 1, 20, + DT_PHYSICAL, SA_NONE, 20, 1, MONSTER_HUMANOID, 0, 0, + 0, 0, 0, 0, 0, 0, 100, 0, 3, 0, 6, 0, 0, 100, + "barbarch")); + push_back(MonsterStruct("Electrapede", 10000, 200, 10, 50, 1, CLASS_PALADIN, + 50, 1, DT_ELECTRICAL, SA_PARALYZE, 1, 0, + MONSTER_INSECT, 50, 100, 50, 50, 50, 0, 0, 0, 0, 0, 0, + 0, 7, 1, 0, 107, "centi")); + push_back(MonsterStruct("Cleric of Mok", 30000, 125, 10, 40, 1, CLASS_CLERIC, + 250, 1, DT_ELECTRICAL, SA_NONE, 1, 1, MONSTER_HUMANOID, + 10, 100, 10, 10, 10, 10, 0, 0, 0, 10, 0, 0, 8, 0, 0, + 117, "cleric")); + push_back(MonsterStruct("Mok Heretic", 50000, 150, 12, 50, 1, CLASS_CLERIC, + 500, 1, DT_1, SA_NONE, 1, 1, MONSTER_HUMANOID, 20, 50, + 20, 20, 20, 30, 0, 0, 0, 25, 4, 0, 8, 0, 0, 117, + "cleric")); + push_back(MonsterStruct("Mantis Ant", 40000, 300, 30, 40, 2, CLASS_16, 2, 100, + DT_PHYSICAL, SA_POISON, 30, 0, MONSTER_INSECT, 0, 0, + 0, 100, 0, 0, 30, 0, 0, 0, 0, 0, 10, 0, 0, 104, + "spell001")); + push_back(MonsterStruct("Cloud Dragon", 500000, 2000, 40, 150, 1, CLASS_15, + 600, 1, DT_COLD, SA_NONE, 1, 1, MONSTER_DRAGON, 0, 50, + 100, 100, 50, 25, 50, 0, 0, 10, 0, 0, 11, 0, 0, 140, + "tiger1")); + push_back(MonsterStruct("Phase Dragon", 2000000, 4000, 80, 200, 1, CLASS_15, + 750, 1, DT_COLD, SA_NONE, 1, 1, MONSTER_DRAGON, 0, 50, + 100, 100, 80, 50, 50, 0, 0, 20, 0, 0, 11, 0, 10, 140, + "Begger")); + push_back(MonsterStruct("Green Dragon", 500000, 2500, 50, 150, 1, CLASS_15, + 500, 1, DT_FIRE, SA_NONE, 1, 1, MONSTER_DRAGON, 100, + 50, 0, 100, 50, 25, 50, 0, 0, 10, 0, 0, 13, 0, 0, 140, + "tiger1")); + push_back(MonsterStruct("Energy Dragon", 2000000, 5000, 100, 250, 1, CLASS_15, + 1000, 1, DT_ENERGY, SA_NONE, 1, 1, MONSTER_DRAGON, 80, + 80, 60, 100, 100, 30, 50, 0, 0, 20, 0, 0, 13, 0, 7, + 140, "begger")); + push_back(MonsterStruct("Dragon Mummy", 2000000, 3000, 30, 100, 1, + CLASS_CLERIC, 2000, 2, DT_PHYSICAL, SA_DISEASE, 200, + 0, MONSTER_DRAGON, 0, 80, 100, 100, 0, 10, 90, 0, 0, 0, + 0, 0, 15, 0, 0, 140, "dragmum")); + push_back(MonsterStruct("Scraps", 2000000, 3000, 30, 100, 1, CLASS_16, 2000, 2, + DT_PHYSICAL, SA_NONE, 200, 0, MONSTER_DRAGON, 0, 80, + 100, 100, 0, 10, 90, 0, 0, 0, 0, 0, 15, 0, 0, 140, + "dragmum")); + push_back(MonsterStruct("Earth Blaster", 250000, 1000, 10, 100, 1, CLASS_15, 5, + 100, DT_PHYSICAL, SA_NONE, 200, 0, MONSTER_0, 100, 90, + 90, 100, 0, 0, 90, 0, 0, 0, 0, 0, 17, 0, 0, 100, + "earthmon")); + push_back(MonsterStruct("Beholder Bat", 10000, 75, 15, 80, 1, CLASS_15, 5, 5, + DT_FIRE, SA_NONE, 1, 0, MONSTER_0, 100, 50, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 18, 0, 0, 120, "eyeball")); + push_back(MonsterStruct("Fire Blower", 250000, 1000, 20, 60, 1, CLASS_15, 5, + 100, DT_FIRE, SA_NONE, 1, 0, MONSTER_0, 100, 50, 0, + 100, 50, 0, 50, 0, 0, 0, 0, 0, 19, 0, 0, 110, "fire")); + push_back(MonsterStruct("Hell Hornet", 50000, 250, 30, 50, 2, CLASS_DRUID, 2, + 250, DT_POISON, SA_WEAKEN, 1, 0, MONSTER_INSECT, 50, + 50, 50, 100, 50, 0, 50, 0, 0, 0, 0, 1, 20, 0, 0, 123, + "insect")); + push_back(MonsterStruct("Gargoyle", 30000, 150, 35, 30, 2, CLASS_16, 5, 50, + DT_PHYSICAL, SA_NONE, 60, 0, MONSTER_0, 0, 0, 0, 0, 0, + 20, 0, 0, 0, 0, 0, 0, 21, 0, 10, 100, "gargrwl")); + push_back(MonsterStruct("Giant", 100000, 500, 25, 45, 2, CLASS_16, 100, 5, + DT_PHYSICAL, SA_UNCONSCIOUS, 100, 0, MONSTER_0, 0, 0, + 0, 0, 0, 0, 0, 0, 1000, 0, 5, 0, 22, 0, 0, 100, + "giant")); + push_back(MonsterStruct("Goblin", 1000, 10, 5, 30, 2, CLASS_16, 2, 6, + DT_PHYSICAL, SA_NONE, 1, 0, MONSTER_0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 25, 0, 0, 131, "gremlin")); + push_back(MonsterStruct("Onyx Golem", 1000000, 10000, 50, 100, 1, CLASS_15, 2, + 250, DT_1, SA_DRAINSP, 1, 0, MONSTER_GOLEM, 100, 100, + 100, 100, 100, 100, 50, 0, 0, 100, 0, 1, 24, 0, 10, + 100, "golem")); + push_back(MonsterStruct("Gremlin", 2000, 20, 7, 35, 2, CLASS_16, 2, 10, + DT_PHYSICAL, SA_NONE, 10, 0, MONSTER_0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 101, "gremlink")); + push_back(MonsterStruct("Gremlin Guard", 3000, 50, 10, 35, 2, CLASS_16, 6, 5, + DT_PHYSICAL, SA_NONE, 20, 0, MONSTER_0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 101, "gremlink")); + push_back(MonsterStruct("Griffin", 60000, 800, 35, 150, 2, CLASS_KNIGHT, 50, 6, + DT_PHYSICAL, SA_NONE, 150, 0, MONSTER_ANIMAL, 0, 0, 0, + 0, 0, 80, 0, 0, 0, 0, 0, 0, 27, 0, 0, 120, "screech")); + push_back(MonsterStruct("Gamma Gazer", 1000000, 5000, 60, 200, 7, CLASS_16, 10, + 20, DT_ENERGY, SA_NONE, 1, 0, MONSTER_0, 100, 100, 0, + 100, 100, 0, 60, 0, 0, 0, 0, 0, 28, 0, 0, 140, "hydra")); + push_back(MonsterStruct("Iguanasaurus", 100000, 2500, 20, 30, 1, CLASS_16, 10, + 50, DT_PHYSICAL, SA_INSANE, 150, 0, MONSTER_ANIMAL, 50, + 50, 50, 50, 50, 0, 20, 0, 0, 0, 0, 0, 29, 0, 0, 113, + "iguana")); + push_back(MonsterStruct("Slayer Knight", 50000, 500, 30, 50, 1, CLASS_PALADIN, + 2, 250, DT_PHYSICAL, SA_NONE, 100, 0, MONSTER_HUMANOID, + 50, 50, 50, 50, 50, 0, 0, 0, 50, 0, 5, 0, 30, 0, 0, + 141, "knight")); + push_back(MonsterStruct("Death Knight", 100000, 750, 50, 80, 2, CLASS_PALADIN, + 2, 250, DT_PHYSICAL, SA_NONE, 150, 0, MONSTER_HUMANOID, + 50, 50, 50, 50, 50, 10, 0, 0, 100, 0, 6, 0, 30, 0, 0, + 141, "knight")); + push_back(MonsterStruct("Lava Dweller", 500000, 1500, 30, 40, 1, CLASS_15, 5, + 100, DT_FIRE, SA_NONE, 1, 0, MONSTER_0, 100, 100, 0, + 100, 50, 0, 50, 0, 0, 0, 0, 0, 19, 0, 0, 110, "fire")); + push_back(MonsterStruct("Lava Roach", 50000, 500, 20, 70, 1, CLASS_16, 5, 50, + DT_FIRE, SA_NONE, 1, 0, MONSTER_INSECT, 100, 100, 0, + 100, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 131, "Phantom")); + push_back(MonsterStruct("Power Lich", 200000, 500, 20, 60, 1, CLASS_15, 10, 10, + DT_1, SA_UNCONSCIOUS, 1, 1, MONSTER_UNDEAD, 0, 0, 0, 0, + 0, 80, 70, 0, 0, 0, 0, 1, 34, 0, 0, 141, "lich")); + push_back(MonsterStruct("Mystic Mage", 100000, 200, 20, 70, 1, CLASS_15, 10, + 20, DT_ELECTRICAL, SA_NONE, 1, 1, MONSTER_0, 50, 100, + 50, 50, 50, 30, 0, 0, 0, 50, 0, 1, 35, 0, 0, 163, + "monsterb")); + push_back(MonsterStruct("Magic Mage", 200000, 300, 25, 80, 1, CLASS_15, 10, 30, + DT_ELECTRICAL, SA_NONE, 1, 1, MONSTER_0, 50, 100, 50, + 50, 50, 50, 0, 0, 0, 75, 0, 1, 35, 0, 0, 163, + "monsterb")); + push_back(MonsterStruct("Minotaur", 250000, 3000, 80, 120, 1, CLASS_16, 100, 4, + DT_PHYSICAL, SA_AGING, 150, 0, MONSTER_0, 0, 0, 10, 0, + 0, 50, 60, 0, 0, 0, 0, 0, 37, 0, 0, 141, "stonegol")); + push_back(MonsterStruct("Gorgon", 250000, 4000, 90, 100, 1, CLASS_16, 100, 3, + DT_PHYSICAL, SA_STONE, 100, 0, MONSTER_0, 0, 0, 0, 0, + 0, 60, 70, 0, 0, 0, 0, 0, 37, 0, 0, 141, "stonegol")); + push_back(MonsterStruct("Higher Mummy", 100000, 400, 20, 60, 1, CLASS_CLERIC, + 10, 40, DT_PHYSICAL, SA_CURSEITEM, 100, 0, + MONSTER_UNDEAD, 0, 50, 50, 100, 50, 20, 75, 0, 0, 0, 0, + 0, 39, 0, 0, 141, "mummy")); + push_back(MonsterStruct("Orc Guard", 5000, 60, 10, 20, 1, CLASS_12, 3, 10, + DT_PHYSICAL, SA_NONE, 20, 0, MONSTER_HUMANOID, 0, 0, + 0, 0, 0, 0, 0, 0, 50, 0, 2, 0, 40, 0, 0, 125, "orc")); + push_back(MonsterStruct("Octopod", 250000, 2500, 40, 80, 1, CLASS_15, 2, 100, + DT_POISON, SA_POISON, 1, 0, MONSTER_ANIMAL, 0, 0, 50, + 100, 0, 0, 0, 0, 0, 0, 0, 1, 41, 0, 0, 101, "photon")); + push_back(MonsterStruct("Ogre", 10000, 100, 15, 30, 1, CLASS_16, 4, 10, + DT_PHYSICAL, SA_NONE, 30, 0, MONSTER_0, 0, 0, 0, 0, 0, + 0, 0, 0, 100, 0, 0, 0, 42, 0, 0, 136, "ogre")); + push_back(MonsterStruct("Orc Shaman", 10000, 50, 15, 30, 1, CLASS_15, 5, 5, + DT_COLD, SA_SLEEP, 1, 1, MONSTER_HUMANOID, 0, 0, 0, 0, + 0, 10, 0, 0, 75, 10, 2, 0, 43, 0, 0, 125, "fx7")); + push_back(MonsterStruct("Sabertooth", 10000, 100, 20, 60, 3, CLASS_16, 5, 10, + DT_PHYSICAL, SA_NONE, 30, 0, MONSTER_ANIMAL, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, 1, 0, 101, "saber")); + push_back(MonsterStruct("Sand Flower", 10000, 100, 10, 50, 5, CLASS_16, 5, 5, + DT_PHYSICAL, SA_INLOVE, 50, 0, MONSTER_0, 0, 0, 0, 0, + 0, 50, 50, 0, 0, 0, 0, 0, 45, 0, 0, 106, "sand")); + push_back(MonsterStruct("Killer Cobra", 25000, 1000, 25, 100, 1, CLASS_16, 2, + 100, DT_PHYSICAL, SA_AGING, 30, 0, MONSTER_ANIMAL, 0, + 0, 0, 100, 0, 50, 0, 0, 0, 0, 0, 0, 46, 0, 0, 100, + "hiss")); + push_back(MonsterStruct("Sewer Rat", 2000, 40, 5, 35, 1, CLASS_16, 3, 10, + DT_PHYSICAL, SA_NONE, 10, 0, MONSTER_ANIMAL, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 47, 0, 0, 136, "rat")); + push_back(MonsterStruct("Sewer Slug", 1000, 25, 2, 25, 1, CLASS_16, 2, 10, + DT_PHYSICAL, SA_NONE, 5, 0, MONSTER_INSECT, 0, 0, 0, + 100, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 111, "zombie")); + push_back(MonsterStruct("Skeletal Lich", 500000, 2000, 30, 200, 1, + CLASS_SORCERER, 1000, 1, DT_ENERGY, SA_ERADICATE, 1, 1, + MONSTER_UNDEAD, 80, 70, 80, 100, 100, 50, 50, 0, 0, 0, + 0, 0, 49, 0, 0, 140, "elecbolt")); + push_back(MonsterStruct("Enchantress", 40000, 100, 25, 60, 1, CLASS_CLERIC, 3, + 150, DT_ELECTRICAL, SA_NONE, 1, 1, MONSTER_HUMANOID, + 10, 100, 10, 10, 10, 20, 0, 0, 0, 20, 0, 0, 50, 0, 0, + 163, "disint")); + push_back(MonsterStruct("Sorceress", 80000, 200, 30, 80, 1, CLASS_15, 2, 50, + DT_1, SA_NONE, 1, 1, MONSTER_HUMANOID, 10, 20, 10, 10, + 10, 80, 0, 0, 0, 50, 5, 0, 50, 0, 0, 163, "disint")); + push_back(MonsterStruct("Arachnoid", 4000, 50, 10, 40, 1, CLASS_16, 3, 5, + DT_POISON, SA_POISON, 1, 0, MONSTER_INSECT, 0, 0, 0, + 100, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 104, "web")); + push_back(MonsterStruct("Medusa Sprite", 5000, 30, 5, 30, 1, CLASS_RANGER, 3, + 3, DT_PHYSICAL, SA_STONE, 10, 0, MONSTER_0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 53, 0, 0, 42, "hiss")); + push_back(MonsterStruct("Rogue", 5000, 50, 10, 30, 1, CLASS_ROBBER, 1, 60, + DT_PHYSICAL, SA_NONE, 10, 0, MONSTER_HUMANOID, 0, 0, + 0, 0, 0, 0, 0, 0, 70, 0, 0, 0, 54, 0, 0, 100, "thief")); + push_back(MonsterStruct("Thief", 10000, 100, 15, 40, 1, CLASS_ROBBER, 1, 100, + DT_PHYSICAL, SA_NONE, 20, 0, MONSTER_HUMANOID, 0, 0, + 0, 0, 0, 0, 0, 0, 200, 0, 0, 0, 54, 0, 0, 100, + "thief")); + push_back(MonsterStruct("Troll Grunt", 10000, 100, 5, 50, 1, CLASS_16, 2, 25, + DT_PHYSICAL, SA_NONE, 30, 0, MONSTER_0, 50, 50, 50, + 50, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 136, "troll")); + push_back(MonsterStruct("Vampire", 200000, 400, 30, 80, 1, CLASS_CLERIC, 10, + 10, DT_PHYSICAL, SA_WEAKEN, 100, 0, MONSTER_UNDEAD, 50, + 50, 50, 50, 50, 50, 50, 0, 0, 0, 0, 0, 57, 0, 0, 42, + "vamp")); + push_back(MonsterStruct("Vampire Lord", 300000, 500, 35, 100, 1, CLASS_CLERIC, + 10, 30, DT_PHYSICAL, SA_SLEEP, 120, 0, MONSTER_UNDEAD, + 50, 50, 50, 50, 50, 50, 70, 0, 0, 0, 0, 0, 58, 0, 0, + 42, "vamp")); + push_back(MonsterStruct("Vulture Roc", 200000, 2500, 50, 150, 1, CLASS_16, 5, + 60, DT_PHYSICAL, SA_NONE, 100, 0, MONSTER_ANIMAL, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 59, 0, 0, 120, "vulture")); + push_back(MonsterStruct("Sewer Hag", 50000, 75, 10, 40, 1, CLASS_PALADIN, 10, + 25, DT_ELECTRICAL, SA_INSANE, 1, 1, MONSTER_HUMANOID, + 0, 100, 0, 100, 0, 20, 0, 0, 0, 10, 0, 0, 62, 0, 0, + 108, "elecspel")); + push_back(MonsterStruct("Tidal Terror", 500000, 1000, 10, 200, 1, CLASS_15, 5, + 100, DT_COLD, SA_NONE, 1, 0, MONSTER_0, 100, 50, 50, + 100, 50, 0, 100, 0, 0, 0, 0, 1, 61, 0, 0, 101, + "splash3")); + push_back(MonsterStruct("Witch", 80000, 150, 15, 70, 1, CLASS_15, 10, 10, + DT_ELECTRICAL, SA_NONE, 1, 1, MONSTER_HUMANOID, 0, 100, + 0, 20, 0, 20, 0, 0, 0, 10, 0, 0, 63, 0, 0, 114, + "elecspel")); + push_back(MonsterStruct("Coven Leader", 120000, 250, 20, 100, 1, CLASS_15, 10, + 15, DT_ENERGY, SA_DRAINSP, 1, 1, MONSTER_HUMANOID, 10, + 100, 0, 50, 100, 50, 0, 0, 0, 20, 6, 0, 63, 0, 10, 114, + "elecspel")); + push_back(MonsterStruct("Master Wizard", 120000, 500, 25, 150, 2, CLASS_KNIGHT, + 10, 40, DT_FIRE, SA_NONE, 1, 1, MONSTER_HUMANOID, 100, + 50, 50, 50, 50, 50, 0, 0, 0, 50, 0, 0, 64, 0, 0, 163, + "boltelec")); + push_back(MonsterStruct("Wizard", 60000, 250, 20, 125, 1, CLASS_PALADIN, 10, + 25, DT_1, SA_NONE, 1, 1, MONSTER_HUMANOID, 50, 30, 30, + 30, 30, 30, 0, 0, 0, 20, 0, 0, 65, 0, 0, 163, "wizard")); + push_back(MonsterStruct("Dark Wolf", 10000, 70, 10, 70, 3, CLASS_16, 3, 8, + DT_PHYSICAL, SA_NONE, 10, 0, MONSTER_ANIMAL, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 1, 0, 100, "wolf")); + push_back(MonsterStruct("Screamer", 500000, 3000, 50, 200, 1, CLASS_15, 10, 20, + DT_POISON, SA_POISON, 1, 0, MONSTER_0, 0, 0, 0, 100, 0, + 0, 60, 0, 0, 0, 0, 0, 67, 0, 0, 110, "dragon")); + push_back(MonsterStruct("Cult Leader", 100000, 100, 20, 60, 1, CLASS_15, 10, + 10, DT_ENERGY, SA_NONE, 1, 1, MONSTER_HUMANOID, 50, 50, + 50, 50, 100, 50, 0, 0, 0, 100, 6, 0, 8, 0, 0, 100, + "cleric")); + push_back(MonsterStruct("Mega Dragon", 100000000, 64000, 100, 200, 1, CLASS_15, + 10, 200, DT_ENERGY, SA_ERADICATE, 1, 1, MONSTER_DRAGON, + 100, 100, 100, 100, 100, 100, 90, 0, 0, 232, 0, 0, 11, + 0, 7, 100, "tiger1")); + push_back(MonsterStruct("Gettlewaithe", 5000, 100, 15, 35, 2, CLASS_16, 5, 5, + DT_PHYSICAL, SA_NONE, 10, 0, MONSTER_0, 0, 0, 0, 0, 0, + 0, 0, 0, 2000, 0, 5, 0, 25, 0, 0, 100, "gremlin")); + push_back(MonsterStruct("Doom Knight", 500000, 1000, 50, 100, 4, CLASS_PALADIN, + 2, 250, DT_PHYSICAL, SA_DEATH, 150, 0, + MONSTER_HUMANOID, 80, 80, 80, 80, 80, 20, 0, 0, 200, + 0, 7, 0, 30, 0, 10, 100, "knight")); + push_back(MonsterStruct("Sandro", 200000, 1000, 20, 75, 1, CLASS_15, 10, 10, + DT_1, SA_DEATH, 1, 1, MONSTER_UNDEAD, 0, 0, 0, 0, 0, + 90, 80, 0, 0, 100, 7, 1, 34, 0, 10, 100, "lich")); + push_back(MonsterStruct("Mega Mage", 500000, 500, 35, 100, 1, CLASS_15, 10, 40, + DT_ELECTRICAL, SA_NONE, 1, 1, MONSTER_0, 80, 100, 80, + 80, 80, 80, 0, 0, 0, 100, 6, 1, 35, 0, 11, 100, + "monsterb")); + push_back(MonsterStruct("Orc Elite", 15000, 200, 15, 40, 2, CLASS_12, 5, 10, + DT_PHYSICAL, SA_NONE, 20, 0, MONSTER_HUMANOID, 0, 0, + 0, 0, 0, 0, 0, 0, 100, 0, 3, 0, 40, 0, 0, 100, "orc")); + push_back(MonsterStruct("Shaalth", 20000, 300, 15, 50, 1, CLASS_15, 5, 10, + DT_COLD, SA_SLEEP, 1, 0, MONSTER_HUMANOID, 0, 0, 0, 0, + 0, 20, 0, 0, 1000, 50, 5, 0, 43, 0, 10, 100, "fx7")); + push_back(MonsterStruct("Rooka", 5000, 60, 5, 40, 1, CLASS_16, 3, 10, + DT_PHYSICAL, SA_DISEASE, 15, 0, MONSTER_ANIMAL, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 10, 4, 0, 47, 0, 0, 100, "rat")); + push_back(MonsterStruct("Morgana", 200000, 300, 35, 100, 1, CLASS_15, 2, 60, + DT_ENERGY, SA_PARALYZE, 1, 1, MONSTER_HUMANOID, 50, 50, + 50, 50, 100, 80, 0, 0, 0, 100, 6, 0, 50, 0, 10, 100, + "disint")); + push_back(MonsterStruct("Master Thief", 20000, 100, 20, 50, 1, CLASS_ROBBER, 1, + 250, DT_PHYSICAL, SA_NONE, 40, 0, MONSTER_HUMANOID, 0, + 0, 0, 0, 0, 0, 0, 0, 250, 20, 4, 0, 54, 0, 14, 100, + "thief")); + push_back(MonsterStruct("Royal Vampire", 400000, 750, 40, 125, 1, CLASS_CLERIC, + 10, 50, DT_PHYSICAL, SA_CURSEITEM, 120, 0, + MONSTER_UNDEAD, 50, 50, 50, 50, 50, 50, 65, 0, 0, 0, 0, + 0, 57, 0, 0, 100, "vamp")); + push_back(MonsterStruct("Ct. Blackfang", 2000000, 1500, 50, 150, 1, + CLASS_CLERIC, 10, 100, DT_PHYSICAL, SA_DEATH, 120, 0, + MONSTER_UNDEAD, 75, 75, 75, 75, 75, 75, 75, 0, 0, 0, 0, + 0, 58, 0, 10, 100, "vamp")); + push_back(MonsterStruct("Troll Guard", 15000, 200, 10, 60, 1, CLASS_16, 2, 35, + DT_PHYSICAL, SA_NONE, 30, 0, MONSTER_0, 50, 50, 50, + 50, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 100, "troll")); + push_back(MonsterStruct("Troll Chief", 20000, 300, 15, 65, 1, CLASS_16, 2, 50, + DT_PHYSICAL, SA_NONE, 30, 0, MONSTER_0, 50, 50, 50, + 50, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 100, "troll")); + push_back(MonsterStruct("Hobstadt", 25000, 400, 20, 70, 1, CLASS_16, 2, 50, + DT_PHYSICAL, SA_NONE, 30, 0, MONSTER_0, 50, 50, 50, + 50, 0, 0, 0, 0, 1000, 0, 4, 0, 56, 0, 0, 100, "troll")); + push_back(MonsterStruct("Graalg", 20000, 200, 15, 50, 1, CLASS_16, 5, 10, + DT_PHYSICAL, SA_NONE, 30, 0, MONSTER_0, 0, 0, 0, 0, 0, + 0, 0, 0, 1000, 0, 5, 0, 42, 0, 0, 100, "ogre")); + push_back(MonsterStruct("Vampire King", 3000000, 10000, 60, 200, 1, + CLASS_CLERIC, 10, 250, DT_PHYSICAL, SA_ERADICATE, 150, + 0, MONSTER_UNDEAD, 80, 80, 80, 80, 80, 80, 90, 0, 0, 0, + 0, 0, 58, 0, 0, 100, "vamp")); + push_back(MonsterStruct("Valio", 60000, 150, 15, 60, 1, CLASS_PALADIN, 10, 25, + DT_1, SA_NONE, 1, 0, MONSTER_HUMANOID, 50, 30, 30, 30, + 40, 30, 0, 0, 0, 0, 0, 0, 65, 0, 0, 100, "wizard")); + push_back(MonsterStruct("Sky Golem", 200000, 1000, 50, 100, 1, CLASS_15, 2, + 100, DT_COLD, SA_NONE, 1, 1, MONSTER_GOLEM, 50, 50, + 100, 50, 50, 50, 50, 0, 0, 0, 0, 1, 24, 0, 0, 100, + "golem")); + push_back(MonsterStruct("Gurodel", 100000, 750, 30, 60, 2, CLASS_16, 100, 6, + DT_PHYSICAL, SA_UNCONSCIOUS, 110, 0, MONSTER_0, 0, 0, + 0, 0, 0, 0, 0, 0, 5000, 0, 6, 0, 22, 0, 0, 100, + "giant")); + push_back(MonsterStruct("Yog", 25000, 100, 5, 60, 1, CLASS_SORCERER, 1, 30, + DT_PHYSICAL, SA_NONE, 25, 0, MONSTER_HUMANOID, 0, 0, + 0, 0, 0, 0, 0, 0, 200, 0, 4, 0, 6, 0, 10, 100, + "barbarch")); + push_back(MonsterStruct("Sharla", 10000, 50, 5, 50, 1, CLASS_RANGER, 3, 4, + DT_PHYSICAL, SA_NONE, 20, 0, MONSTER_0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 53, 0, 0, 100, "hiss")); + push_back(MonsterStruct("Ghost Mummy", 500000, 500, 35, 175, 1, CLASS_CLERIC, + 200, 5, DT_PHYSICAL, SA_AGING, 150, 0, MONSTER_UNDEAD, + 0, 60, 80, 80, 80, 50, 80, 0, 0, 0, 0, 0, 40, 0, 6, + 100, "orc")); + push_back(MonsterStruct("Phase Mummy", 500000, 500, 35, 175, 1, CLASS_CLERIC, + 200, 6, DT_PHYSICAL, SA_DRAINSP, 150, 0, + MONSTER_UNDEAD, 0, 70, 80, 80, 80, 60, 85, 0, 0, 0, 0, + 0, 39, 0, 7, 100, "mummy")); + push_back(MonsterStruct("Xenoc", 250000, 700, 35, 175, 1, CLASS_15, 10, 50, + DT_ENERGY, SA_NONE, 1, 0, MONSTER_HUMANOID, 50, 50, 50, + 50, 100, 50, 0, 0, 0, 100, 6, 0, 64, 0, 0, 100, + "boltelec")); + push_back(MonsterStruct("Barkman", 4000000, 40000, 25, 100, 3, CLASS_16, 250, + 1, DT_FIRE, SA_NONE, 1, 0, MONSTER_0, 100, 50, 0, 100, + 0, 0, 0, 0, 0, 0, 6, 0, 19, 0, 11, 100, "fire")); +} + +void MonsterData::load(const Common::String &name) { + File f(name); + synchronize(f); +} + +void MonsterData::synchronize(Common::SeekableReadStream &s) { + clear(); + + MonsterData spr; + while (!s.eos()) { + spr.synchronize(s); + push_back(spr); + } +} + +/*------------------------------------------------------------------------*/ + +SurroundingMazes::SurroundingMazes() { + _north = 0; + _east = 0; + _south = 0; + _west = 0; +} + +void SurroundingMazes::synchronize(Common::SeekableReadStream &s) { + _north = s.readUint16LE(); + _east = s.readUint16LE(); + _south = s.readUint16LE(); + _west = s.readUint16LE(); +} + +/*------------------------------------------------------------------------*/ + +MazeDifficulties::MazeDifficulties() { + _unlockDoor = 0; + _unlockBox = 0; + _bashDoor = 0; + _bashGrate = 0; + _bashWall = 0; +} + +void MazeDifficulties::synchronize(Common::SeekableReadStream &s) { + _wallNoPass = s.readByte(); + _surfaceNoPass = s.readByte(); + _unlockDoor = s.readByte(); + _unlockBox = s.readByte(); + _bashDoor = s.readByte(); + _bashGrate = s.readByte(); + _bashWall = s.readByte(); + _chance2Run = s.readByte(); +} + +/*------------------------------------------------------------------------*/ + +MazeData::MazeData() { + for (int y = 0; y < MAP_HEIGHT; ++y) { + Common::fill(&_wallData[y][0], &_wallData[y][MAP_WIDTH], 0); + Common::fill(&_cellFlag[y][0], &_cellFlag[y][MAP_WIDTH], 0); + Common::fill(&_seenTiles[y][0], &_seenTiles[y][MAP_WIDTH], 0); + Common::fill(&_steppedOnTiles[y][0], &_steppedOnTiles[y][MAP_WIDTH], 0); + _wallTypes[y] = 0; + _surfaceTypes[y] = 0; + } + _mazeNumber = 0; + _mazeFlags = _mazeFlags2 = 0; + _floorType = 0; + _trapDamage = 0; + _wallKind = 0; + _tavernTips = 0; +} + +void MazeData::synchronize(Common::SeekableReadStream &s) { + for (int y = 0; y < MAP_HEIGHT; ++y) { + for (int x = 0; x < MAP_WIDTH; ++x) + _wallData[y][x] = s.readUint16LE(); + } + for (int y = 0; y < MAP_HEIGHT; ++y) { + for (int x = 0; x < MAP_WIDTH; ++x) + _cellFlag[y][x] = s.readByte(); + } + + _mazeNumber = s.readUint16LE(); + _surroundingMazes.synchronize(s); + _mazeFlags = s.readUint16LE(); + _mazeFlags2 = s.readUint16LE(); + + for (int i = 0; i < 16; ++i) + _wallTypes[i] = s.readByte(); + for (int i = 0; i < 16; ++i) + _surfaceTypes[i] = s.readByte(); + + _floorType = s.readByte(); + _runPosition.x = s.readByte(); + _difficulties.synchronize(s); + _runPosition.y = s.readByte(); + _trapDamage = s.readByte(); + _wallKind = s.readByte(); + _tavernTips = s.readByte(); + + Common::Serializer ser(&s, nullptr); + for (int y = 0; y < MAP_HEIGHT; ++y) + SavesManager::syncBitFlags(ser, &_seenTiles[y][0], &_seenTiles[y][MAP_WIDTH]); + for (int y = 0; y < MAP_HEIGHT; ++y) + SavesManager::syncBitFlags(ser, &_steppedOnTiles[y][0], &_steppedOnTiles[y][MAP_WIDTH]); +} + +/** + * Flags all tiles for the map as having been stepped on + */ +void MazeData::setAllTilesStepped() { + for (int y = 0; y < MAP_HEIGHT; ++y) + Common::fill(&_steppedOnTiles[y][0], &_steppedOnTiles[y][MAP_WIDTH], true); +} + +void MazeData::clearCellBits() { + for (int y = 0; y < MAP_HEIGHT; ++y) { + for (int x = 0; x < MAP_WIDTH; ++x) + _cellFlag[y][x] &= 0xF8; + } +} + +/*------------------------------------------------------------------------*/ + +MobStruct::MobStruct() { + _id = 0; + _direction = DIR_NORTH; +} + +bool MobStruct::synchronize(Common::SeekableReadStream &s) { + _pos.x = (int8)s.readByte(); + _pos.y = (int8)s.readByte(); + _id = s.readByte(); + _direction = (Direction)s.readByte(); + + return _id != 0xff || _pos.x != -1 || _pos.y != -1; +} + +/*------------------------------------------------------------------------*/ + +MonsterObjectData::MonsterObjectData(XeenEngine *vm): _vm(vm) { +} + +void MonsterObjectData::synchronize(Common::SeekableReadStream &s, + bool isOutdoors, MonsterData monsterData) { + _objects.clear(); + _monsters.clear(); + _wallPicIds.clear(); + _wallImages.clear(); + Common::Array objectSprites; + Common::Array monsterIds; + Common::Array objData; + Common::Array monData; + byte b; + + for (int i = 0; i < 16; ++i) { + if ((b = s.readByte()) != 0xff) + objectSprites.push_back(b); + } + for (int i = 0; i < 16; ++i) { + if ((b = s.readByte()) != 0xff) + monsterIds.push_back(b); + } + for (int i = 0; i < 16; ++i) { + if ((b = s.readByte()) != 0xff) + _wallPicIds.push_back(b); + } + + MobStruct mobStruct; + while (mobStruct.synchronize(s)) + objData.push_back(mobStruct); + while (mobStruct.synchronize(s)) + monData.push_back(mobStruct); + if (!isOutdoors) { + while (mobStruct.synchronize(s)) + _wallImages.push_back(mobStruct); + } + + // Merge up objects + _objects.resize(objData.size()); + for (uint i = 0; i < objData.size(); ++i) { + MazeObject &dest = _objects[i]; + dest._position = objData[i]._pos; + dest._number = objData[i]._id; + dest._id = objectSprites[dest._number]; + dest._direction = objData[i]._direction; + dest._frame = 100; + } + + // merge up monsters + for (uint i = 0; i < monData.size(); ++i) { + MazeMonster &dest = _monsters[i]; + dest._position = monData[i]._pos; + dest._id = monData[i]._id; + dest._refId = monsterIds[dest._id]; + + MonsterStruct &mon = monsterData[dest._refId]; + dest._hp = mon._hp; + dest._frame = _vm->getRandomNumber(7); + dest._effect1 = dest._effect2 = mon._animationEffect; + if (mon._animationEffect) + dest._effect3 = _vm->getRandomNumber(7); + } +} + +/*------------------------------------------------------------------------*/ + +HeadData::HeadData() { + for (int y = 0; y < MAP_HEIGHT; ++y) { + for (int x = 0; x < MAP_WIDTH; ++x) { + _data[y][x]._left = _data[y][x]._right = 0; + } + } +} + +void HeadData::synchronize(Common::SeekableReadStream &s) { + for (int y = 0; y < MAP_HEIGHT; ++y) { + for (int x = 0; x < MAP_WIDTH; ++x) { + _data[y][x]._left = s.readByte(); + _data[y][x]._right = s.readByte(); + } + } +} + +/*------------------------------------------------------------------------*/ + +Map::Map(XeenEngine *vm) : _vm(vm), _mobData(vm) { + _townPortalSide = 0; + _sideObj = 0; + _sideMon = 0; + _isOutdoors = false; +} + +void Map::load(int mapId) { + Screen &screen = *_vm->_screen; + if (_vm->_falling) { + Window &w = screen._windows[9]; + w.open(); + w.writeString(OOPS); + } else { + PleaseWait::show(_vm); + } + + if (mapId >= 113 && mapId <= 127) { + _townPortalSide = 0; + } else { + _townPortalSide = _vm->_loadDarkSide; + } + + if (_vm->getGameID() == GType_WorldOfXeen) { + if (_vm->_loadDarkSide) { + _objPicSprites.load("clouds.dat"); + _monsterData.load("xeen.mon"); + _wallPicSprites.load("xeenpic.dat"); + } else { + switch (mapId) { + case 113: + case 114: + case 115: + case 116: + case 128: + _objPicSprites.load("clouds.dat"); + _monsterData.load("dark.mon"); + _wallPicSprites.load("darkpic.dat"); + _sideObj = 0; + break; + case 117: + case 118: + case 119: + case 120: + case 124: + _objPicSprites.load("clouds.dat"); + _monsterData.load("xeen.mon"); + _wallPicSprites.load("darkpic.dat"); + _sideObj = 0; + _sideMon = 0; + break; + case 125: + case 126: + case 127: + _objPicSprites.load("clouds.dat"); + _monsterData.load("dark.mon"); + _wallPicSprites.load("xeenpic.dat"); + break; + default: + _objPicSprites.load("dark.dat"); + _monsterData.load("ddark.mon"); + _wallPicSprites.load("darkpic.dat"); + break; + } + } + } + + bool isDarkCc = _vm->getGameID() == GType_DarkSide; + uint16 mapGrid[9]; + uint16 *gridPtr = &mapGrid[0]; + bool textLoaded = false; + + // Iterate through loading the given maze as well as the two successive + // mazes in each of the four cardinal directions + for (int idx = 0; idx < 9; ++idx, ++gridPtr) { + if (mapId != 0) { + // Load in the maze's data file + Common::String datName = Common::String::format("maze%c%03u.dat", + (_vm->_party._mazeId >= 100) ? 'x' : '0', _vm->_party._mazeId); + File datFile(datName); + _mazeData.synchronize(datFile); + datFile.close(); + + if (isDarkCc && mapId == 50) + _mazeData.setAllTilesStepped(); + if (!isDarkCc && _vm->_party._gameFlags[25] && + (mapId == 42 || mapId == 43 || mapId == 4)) { + _mazeData.clearCellBits(); + } + + _isOutdoors = (_mazeData._mazeFlags2 & FLAG_IS_OUTDOORS) != 0; + + // Handle loading text data + if (!textLoaded) { + textLoaded = true; + Common::String txtName = Common::String::format("%s%c%03u.txt", + isDarkCc ? "dark" : "xeen", mapId >= 100 ? 'x' : '0'); + File fText(txtName); + char mazeName[33]; + fText.read(mazeName, 33); + mazeName[32] = '\0'; + + _mazeName = Common::String(mazeName); + fText.close(); + + // Load the monster data + Common::String mobName = Common::String::format("maze%c%03u.mob", + (_vm->_party._mazeId >= 100) ? 'x' : '0', _vm->_party._mazeId); + File mobFile(mobName); + _mobData.synchronize(mobFile, _isOutdoors, _monsterData); + mobFile.close(); + + Common::String headName = Common::String::format("aaze%c%03u.hed", + (_vm->_party._mazeId >= 100) ? 'x' : '0', _vm->_party._mazeId); + File headFile(headName); + _headData.synchronize(headFile); + headFile.close(); + + if (!isDarkCc && _vm->_party._mazeId) + _mobData._monsters.clear(); + + if (!isDarkCc && mapId == 15) { + if ((_mobData._monsters[0]._position.x > 31 || _mobData._monsters[0]._position.y > 31) && + (_mobData._monsters[1]._position.x > 31 || _mobData._monsters[1]._position.y > 31) && + (_mobData._monsters[2]._position.x > 31 || _mobData._monsters[2]._position.y > 31)) { + _vm->_party._gameFlags[56] = true; + } + } + } + } + + // TODO: Move to next surrounding maze + } + + // TODO +} + +} // End of namespace Xeen diff --git a/engines/xeen/map.h b/engines/xeen/map.h new file mode 100644 index 0000000000..4482173642 --- /dev/null +++ b/engines/xeen/map.h @@ -0,0 +1,264 @@ +/* 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 XEEN_MAP_H +#define XEEN_MAP_H + +#include "common/stream.h" +#include "common/rect.h" +#include "xeen/party.h" +#include "xeen/sprites.h" + +namespace Xeen { + +#define MAP_WIDTH 16 +#define MAP_HEIGHT 16 + +class XeenEngine; + +enum DamageType { + DT_PHYSICAL = 0, DT_1 = 1, DT_FIRE = 2, DT_ELECTRICAL = 3, + DT_COLD = 4, DT_POISON = 5, DT_ENERGY = 6, DT_SLEEP = 7, + DT_FINGEROFDEATH = 8, DT_HOLYWORD = 9, DT_MASS_DISTORTION = 10, + DT_UNDED = 11, DT_BEASTMASTER = 12, DT_DRAGONSLEEP = 13, + DT_GOLEMSTOPPER = 14, DT_HYPNOTIZE = 15, DT_INSECT_SPRAY = 16, + DT_POISON_VALLEY = 17, DT_MAGIC_ARROW = 18 +}; + +enum SpecialAttack { + SA_NONE = 0, SA_MAGIC = 1, SA_FIRE = 2, SA_ELEC = 3, SA_COLD = 4, + SA_POISON = 5, SA_ENERGY = 6, SA_DISEASE = 7, SA_INSANE = 8, + SA_SLEEP = 9, SA_CURSEITEM = 10, SA_INLOVE = 11, SA_DRAINSP = 12, + SA_CURSE = 13, SA_PARALYZE = 14, SA_UNCONSCIOUS = 15, + SA_CONFUSE = 16, SA_BREAKWEAPON = 17, SA_WEAKEN = 18, + SA_ERADICATE = 19, SA_AGING = 20, SA_DEATH = 21, SA_STONE = 22 +}; + +enum MonsterType { + MONSTER_0 = 0, MONSTER_ANIMAL = 1, MONSTER_INSECT = 2, + MONSTER_HUMANOID = 3, MONSTER_UNDEAD = 4, MONSTER_GOLEM = 5, + MONSTER_DRAGON = 6 +}; + +class MonsterStruct { +public: + Common::String _name; + int _experience; + int _hp; + int _accuracy; + int _speed; + int _numberOfAttacks; + CharacterClass _hatesClass; + int _strikes; + int _dmgPerStrike; + DamageType _attackType; + SpecialAttack _specialAttack; + int _hitChance; + int _rangeAttack; + MonsterType _monsterType; + int _fireResistence; + int _electricityResistence; + int _coldResistence; + int _poisonResistence; + int _energyResistence; + int _magicResistence; + int _phsyicalResistence; + int _field29; + int _gold; + int _gems; + int _itemDrop; + int _flying; + int _imageNumber; + int _loopAnimation; + int _animationEffect; + int _field32; + Common::String _attackVoc; +public: + MonsterStruct(); + MonsterStruct(Common::String name, int experience, int hp, int accuracy, + int speed, int numberOfAttacks, CharacterClass hatesClass, int strikes, + int dmgPerStrike, DamageType attackType, SpecialAttack specialAttack, + int hitChance, int rangeAttack, MonsterType monsterType, + int fireResistence, int electricityResistence, int coldResistence, + int poisonResistence, int energyResistence, int magicResistence, + int phsyicalResistence, int field29, int gold, int gems, int itemDrop, + int flying, int imageNumber, int loopAnimation, int animationEffect, + int field32, Common::String attackVoc); + + void synchronize(Common::SeekableReadStream &s); +}; + +class MonsterData : public Common::Array { +private: + void synchronize(Common::SeekableReadStream &s); +public: + MonsterData(); + + void load(const Common::String &name); +}; + +class SurroundingMazes { +public: + int _north; + int _east; + int _south; + int _west; +public: + SurroundingMazes(); + + void synchronize(Common::SeekableReadStream &s); +}; + +class MazeDifficulties { +public: + int _wallNoPass; + int _surfaceNoPass; + int _unlockDoor; + int _unlockBox; + int _bashDoor; + int _bashGrate; + int _bashWall; + int _chance2Run; +public: + MazeDifficulties(); + + void synchronize(Common::SeekableReadStream &s); +}; + +enum MazeFlags { + OUTFLAG_GRATE = 0x80, OUTFLAG_DRAIN = 0x20, OUTFLAG_OBJECT_EXISTS = 0x08, + INFLAG_INSIDE = 0x08, + FLAG_WATER = 0x40, FLAG_AUTOEXECUTE_EVENT = 0x10, + FLAG_GROUND_BITS = 7 +}; + +enum MazeFlags2 { FLAG_IS_OUTDOORS = 0x8000, FLAG_IS_DARK = 0x4000 }; + +class MazeData { +public: + int _wallData[MAP_HEIGHT][MAP_WIDTH]; + int _cellFlag[MAP_HEIGHT][MAP_WIDTH]; + int _mazeNumber; + SurroundingMazes _surroundingMazes; + int _mazeFlags; + int _mazeFlags2; + int _wallTypes[16]; + int _surfaceTypes[16]; + int _floorType; + Common::Point _runPosition; + MazeDifficulties _difficulties; + int _trapDamage; + int _wallKind; + int _tavernTips; + bool _seenTiles[MAP_HEIGHT][MAP_WIDTH]; + bool _steppedOnTiles[MAP_HEIGHT][MAP_WIDTH]; +public: + MazeData(); + + void synchronize(Common::SeekableReadStream &s); + + void setAllTilesStepped(); + + void clearCellBits(); +}; + +class MobStruct { +public: + Common::Point _pos; + int _id; + Direction _direction; +public: + MobStruct(); + + bool synchronize(Common::SeekableReadStream &s); +}; + +struct MazeObject { + Common::Point _position; + int _number; + int _frame; + int _id; + Direction _direction; + bool _flipped; +}; + +struct MazeMonster { + Common::Point _position; + int _frame; + int _id; + int _refId; + int _hp; + int _effect1, _effect2; + int _effect3; +}; + +class MonsterObjectData { +private: + XeenEngine *_vm; +public: + Common::Array _objects; + Common::Array _monsters; + Common::Array _wallPicIds; + Common::Array _wallImages; +public: + MonsterObjectData(XeenEngine *vm); + + void synchronize(Common::SeekableReadStream &s, bool isOutdoors, + MonsterData monsterData); +}; + +class HeadData { +public: + struct HeadEntry { + int _left; + int _right; + }; + HeadEntry _data[MAP_HEIGHT][MAP_WIDTH]; +public: + HeadData(); + + void synchronize(Common::SeekableReadStream &s); +}; + +class Map { +private: + XeenEngine *_vm; + MazeData _mazeData; + Common::String _mazeName; + MonsterObjectData _mobData; + HeadData _headData; + SpriteResource _objPicSprites; + MonsterData _monsterData; + SpriteResource _wallPicSprites; + int _townPortalSide; + int _sideObj; + int _sideMon; + bool _isOutdoors; +public: + Map(XeenEngine *vm); + + void load(int mapId); +}; + +} // End of namespace Xeen + +#endif /* XEEN_MAP_H */ diff --git a/engines/xeen/module.mk b/engines/xeen/module.mk index 7f0caf7b10..5cbb1bb064 100644 --- a/engines/xeen/module.mk +++ b/engines/xeen/module.mk @@ -7,12 +7,14 @@ MODULE_OBJS := \ debugger.o \ detection.o \ dialogs.o \ + dialogs_error.o \ dialogs_options.o \ events.o \ files.o \ font.o \ interface.o \ items.o \ + map.o \ party.o \ resdata.o \ resources.o \ diff --git a/engines/xeen/party.cpp b/engines/xeen/party.cpp index 25366d52ed..ec25f4a71c 100644 --- a/engines/xeen/party.cpp +++ b/engines/xeen/party.cpp @@ -44,7 +44,7 @@ PlayerStruct::PlayerStruct() { _sex = MALE; _race = HUMAN; _xeenSide = 0; - _class = KNIGHT; + _class = CLASS_KNIGHT; _ACTemp = 0; _dbDay = 0; _tempAge = 0; @@ -302,7 +302,7 @@ void Party::synchronize(Common::Serializer &s) { bool Party::checkSkill(Skill skillId) { uint total = 0; for (uint i = 0; i < _activeParty.size(); ++i) { - if (_activeParty[i]->_skills[skillId]) { + if (_activeParty[i]._skills[skillId]) { ++total; switch (skillId) { @@ -324,6 +324,8 @@ bool Party::checkSkill(Skill skillId) { } } } + + return false; } bool Party::isInParty(int charId) { @@ -335,4 +337,10 @@ bool Party::isInParty(int charId) { return false; } +void Party::copyPartyToRoster(Roster &r) { + for (int i = 0; i < _partyCount; ++i) { + r[_partyMembers[i]] = _activeParty[i]; + } +} + } // End of namespace Xeen diff --git a/engines/xeen/party.h b/engines/xeen/party.h index 7e5010427f..b415707344 100644 --- a/engines/xeen/party.h +++ b/engines/xeen/party.h @@ -39,8 +39,11 @@ enum Sex { MALE = 0, FEMALE = 1, YES_PLEASE = 2 }; enum Race { HUMAN = 0, ELF = 1, DWARF = 2, GNOME = 3, HALF_ORC = 4 }; -enum CharacterClass { KNIGHT = 0, PALADIN = 1, ARCHER = 2, CLERIC = 3, - SORCERER = 4, ROBBER = 5, NINJA = 6, BARBARIAN = 7, DRUID = 8, RANGER = 9 +enum CharacterClass { + CLASS_KNIGHT = 0, CLASS_PALADIN = 1, CLASS_ARCHER = 2, CLASS_CLERIC = 3, + CLASS_SORCERER = 4, CLASS_ROBBER = 5, CLASS_NINJA = 6, CLASS_BARBARIAN = 7, + CLASS_DRUID = 8, CLASS_RANGER = 9, + CLASS_12 = 12, CLASS_15 = 15, CLASS_16 = 16 }; enum Skill { THIEVERY = 0, ARMS_MASTER = 1, ASTROLOGER = 2, BODYBUILDER = 3, @@ -195,7 +198,7 @@ public: bool _characterFlags[30][24]; public: // Other party related runtime data - Common::Array _activeParty; + Common::Array _activeParty; int _combatPartyCount; public: Party(); @@ -205,6 +208,8 @@ public: bool checkSkill(Skill skillId); bool isInParty(int charId); + + void copyPartyToRoster(Roster &r); }; } // End of namespace Xeen diff --git a/engines/xeen/resources.cpp b/engines/xeen/resources.cpp index 79e8ba183c..3f59f3e1c9 100644 --- a/engines/xeen/resources.cpp +++ b/engines/xeen/resources.cpp @@ -279,4 +279,28 @@ const int CHAR_FACES_X[6] = { 10, 45, 81, 117, 153, 189 }; const int HP_BARS_X[6] = { 13, 50, 86, 122, 158, 194 }; +const char *const NO_ONE_TO_ADVENTURE_WITH = "You have no one to adventure with"; + +const char *const YOUR_ROSTER_IS_FULL = "Your Roster is full!"; + +const byte BACKGROUND_XLAT[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF7, 0xFF, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF9, 0xFF, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF7, 0xFF, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF5, 0xFF, 0x0B, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF3, 0xFF, 0x0D, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 +}; + +const char *const PLEASE_WAIT = "\014""d\003""c\011""000" + "\013""002Please Wait..."; + +const char *const OOPS = "\003""c\011""000\013""002Oops..."; + } // End of namespace Xeen diff --git a/engines/xeen/resources.h b/engines/xeen/resources.h index 80febec9b4..78104b233c 100644 --- a/engines/xeen/resources.h +++ b/engines/xeen/resources.h @@ -58,6 +58,16 @@ extern const int CHAR_FACES_X[6]; extern const int HP_BARS_X[6]; +extern const char *const NO_ONE_TO_ADVENTURE_WITH; + +extern const byte BACKGROUND_XLAT[]; + +extern const char *const YOUR_ROSTER_IS_FULL; + +extern const char *const PLEASE_WAIT; + +extern const char *const OOPS; + } // End of namespace Xeen #endif /* XEEN_RESOURCES_H */ diff --git a/engines/xeen/saves.cpp b/engines/xeen/saves.cpp index 7482a12ff5..95957c4daa 100644 --- a/engines/xeen/saves.cpp +++ b/engines/xeen/saves.cpp @@ -75,4 +75,12 @@ void SavesManager::reset() { delete pty; } +void SavesManager::readCharFile() { + warning("TODO: readCharFile"); +} + +void SavesManager::writeCharFile() { + warning("TODO: writeCharFile"); +} + } // End of namespace Xeen diff --git a/engines/xeen/saves.h b/engines/xeen/saves.h index f3ace80a03..1e67638fe4 100644 --- a/engines/xeen/saves.h +++ b/engines/xeen/saves.h @@ -49,6 +49,10 @@ public: public: SavesManager(XeenEngine *vm, Party &party, Roster &roster); void reset(); + + void readCharFile(); + + void writeCharFile(); }; } // End of namespace Xeen diff --git a/engines/xeen/screen.cpp b/engines/xeen/screen.cpp index ba2c97b267..cfde7804ae 100644 --- a/engines/xeen/screen.cpp +++ b/engines/xeen/screen.cpp @@ -166,10 +166,10 @@ void Window::writeString(const Common::String &s) { _vm->_screen->writeString(s, _innerBounds); } -void Window::drawList(DrawStruct *items) { +void Window::drawList(DrawStruct *items, int count) { Screen &screen = *_vm->_screen; - for (; items->_sprites != nullptr; ++items) { + for (int i = 0; i < count; ++i, ++items) { if (items->_frame == -1 || items->_scale == -1) continue; diff --git a/engines/xeen/screen.h b/engines/xeen/screen.h index ae38ec3a03..1e164a3166 100644 --- a/engines/xeen/screen.h +++ b/engines/xeen/screen.h @@ -90,7 +90,7 @@ public: void writeString(const Common::String &s); - void drawList(DrawStruct *items); + void drawList(DrawStruct *items, int count); }; class Screen: public FontSurface { diff --git a/engines/xeen/sprites.cpp b/engines/xeen/sprites.cpp index 5b8fe5266a..598870dc57 100644 --- a/engines/xeen/sprites.cpp +++ b/engines/xeen/sprites.cpp @@ -39,6 +39,10 @@ SpriteResource::SpriteResource(const Common::String &filename) { load(filename); } +SpriteResource::~SpriteResource() { + clear(); +} + void SpriteResource::load(const Common::String &filename) { // Open the resource File f(filename); @@ -60,8 +64,10 @@ void SpriteResource::load(const Common::String &filename) { } } -SpriteResource::~SpriteResource() { +void SpriteResource::clear() { delete[] _data; + _data = nullptr; + _filesize = 0; } void SpriteResource::drawOffset(XSurface &dest, uint16 offset, const Common::Point &destPos) const { diff --git a/engines/xeen/sprites.h b/engines/xeen/sprites.h index e9cdf721eb..998d5d3e79 100644 --- a/engines/xeen/sprites.h +++ b/engines/xeen/sprites.h @@ -51,6 +51,8 @@ public: void load(const Common::String &filename); + void clear(); + void draw(XSurface &dest, int frame, const Common::Point &destPos) const; void draw(XSurface &dest, int frame) const; diff --git a/engines/xeen/xeen.cpp b/engines/xeen/xeen.cpp index 1574871c25..b3ac7433a3 100644 --- a/engines/xeen/xeen.cpp +++ b/engines/xeen/xeen.cpp @@ -38,7 +38,9 @@ XeenEngine::XeenEngine(OSystem *syst, const XeenGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc), _randomSource("Xeen") { _debugger = nullptr; _events = nullptr; + _files = nullptr; _interface = nullptr; + _map = nullptr; _saves = nullptr; _screen = nullptr; _sound = nullptr; @@ -49,16 +51,19 @@ XeenEngine::XeenEngine(OSystem *syst, const XeenGameDescription *gameDesc) _face1State = 0; _face2State = 0; _noDirectionSense = false; + _falling = false; } XeenEngine::~XeenEngine() { delete _debugger; delete _events; delete _interface; + delete _map; delete _saves; delete _screen; delete _sound; delete _eventData; + delete _files; } void XeenEngine::initialize() { @@ -69,10 +74,11 @@ void XeenEngine::initialize() { DebugMan.addDebugChannel(kDebugSound, "sound", "Sound and Music handling"); // Create sub-objects of the engine - FileManager::init(this); + _files = new FileManager(this); _debugger = new Debugger(this); _events = new EventsManager(this); _interface = new Interface(this); + _map = new Map(this); _saves = new SavesManager(this, _party, _roster); _screen = new Screen(this); _screen->setupWindows(); @@ -249,9 +255,34 @@ void XeenEngine::showMainMenu() { //OptionsMenu::show(this); } +/** + * Main method for playing the game + */ void XeenEngine::playGame() { _saves->reset(); - _interface->setup(true); + play(); +} + +/* + * Secondary method for handling the actual gameplay + */ +void XeenEngine::play() { + // TODO: Init variables + + _screen->loadBackground("back.raw"); + _screen->loadPalette("mm4.pal"); + _interface->loadCharIcons(_party._partyCount); + _iconsSprites.load("main.icn"); + + if (getGameID() != GType_WorldOfXeen && !_loadDarkSide) { + _loadDarkSide = true; + _party._mazeId = 29; + _party._mazeDirection = DIR_NORTH; + _party._mazePosition.x = 25; + _party._mazePosition.y = 21; + } + + _map->load(_party._mazeId); } } // End of namespace Xeen diff --git a/engines/xeen/xeen.h b/engines/xeen/xeen.h index a7210ca871..9938519b9b 100644 --- a/engines/xeen/xeen.h +++ b/engines/xeen/xeen.h @@ -34,7 +34,9 @@ #include "xeen/debugger.h" #include "xeen/dialogs.h" #include "xeen/events.h" +#include "xeen/files.h" #include "xeen/interface.h" +#include "xeen/map.h" #include "xeen/party.h" #include "xeen/saves.h" #include "xeen/screen.h" @@ -77,7 +79,8 @@ enum Mode { MODE_6 = 6, MODE_7 = 7, MODE_8 = 8, - MODE_9 = 9 + MODE_9 = 9, + MODE_17 = 17 }; struct XeenGameDescription; @@ -90,10 +93,15 @@ private: const XeenGameDescription *_gameDescription; Common::RandomSource _randomSource; int _loadSaveSlot; + SpriteResource _iconsSprites; void showIntro(); void showMainMenu(); + + void play(); + + void pleaseWait(); protected: /** * Play the game @@ -119,7 +127,9 @@ private: public: Debugger *_debugger; EventsManager *_events; + FileManager *_files; Interface *_interface; + Map *_map; SavesManager *_saves; Screen *_screen; SoundManager *_sound; @@ -134,6 +144,7 @@ public: int _face1State; int _face2State; bool _noDirectionSense; + bool _falling; public: XeenEngine(OSystem *syst, const XeenGameDescription *gameDesc); virtual ~XeenEngine(); -- cgit v1.2.3