aboutsummaryrefslogtreecommitdiff
path: root/engines/xeen/dialogs
diff options
context:
space:
mode:
Diffstat (limited to 'engines/xeen/dialogs')
-rw-r--r--engines/xeen/dialogs/dialogs.cpp257
-rw-r--r--engines/xeen/dialogs/dialogs.h137
-rw-r--r--engines/xeen/dialogs/dialogs_awards.cpp131
-rw-r--r--engines/xeen/dialogs/dialogs_awards.h52
-rw-r--r--engines/xeen/dialogs/dialogs_char_info.cpp571
-rw-r--r--engines/xeen/dialogs/dialogs_char_info.h69
-rw-r--r--engines/xeen/dialogs/dialogs_control_panel.cpp220
-rw-r--r--engines/xeen/dialogs/dialogs_control_panel.h66
-rw-r--r--engines/xeen/dialogs/dialogs_create_char.cpp648
-rw-r--r--engines/xeen/dialogs/dialogs_create_char.h124
-rw-r--r--engines/xeen/dialogs/dialogs_difficulty.cpp75
-rw-r--r--engines/xeen/dialogs/dialogs_difficulty.h60
-rw-r--r--engines/xeen/dialogs/dialogs_dismiss.cpp95
-rw-r--r--engines/xeen/dialogs/dialogs_dismiss.h46
-rw-r--r--engines/xeen/dialogs/dialogs_exchange.cpp80
-rw-r--r--engines/xeen/dialogs/dialogs_exchange.h46
-rw-r--r--engines/xeen/dialogs/dialogs_info.cpp129
-rw-r--r--engines/xeen/dialogs/dialogs_info.h46
-rw-r--r--engines/xeen/dialogs/dialogs_input.cpp323
-rw-r--r--engines/xeen/dialogs/dialogs_input.h105
-rw-r--r--engines/xeen/dialogs/dialogs_items.cpp1066
-rw-r--r--engines/xeen/dialogs/dialogs_items.h89
-rw-r--r--engines/xeen/dialogs/dialogs_map.cpp461
-rw-r--r--engines/xeen/dialogs/dialogs_map.h62
-rw-r--r--engines/xeen/dialogs/dialogs_message.cpp123
-rw-r--r--engines/xeen/dialogs/dialogs_message.h61
-rw-r--r--engines/xeen/dialogs/dialogs_party.cpp451
-rw-r--r--engines/xeen/dialogs/dialogs_party.h87
-rw-r--r--engines/xeen/dialogs/dialogs_query.cpp158
-rw-r--r--engines/xeen/dialogs/dialogs_query.h50
-rw-r--r--engines/xeen/dialogs/dialogs_quests.cpp254
-rw-r--r--engines/xeen/dialogs/dialogs_quests.h49
-rw-r--r--engines/xeen/dialogs/dialogs_quick_fight.cpp105
-rw-r--r--engines/xeen/dialogs/dialogs_quick_fight.h60
-rw-r--r--engines/xeen/dialogs/dialogs_quick_ref.cpp88
-rw-r--r--engines/xeen/dialogs/dialogs_quick_ref.h41
-rw-r--r--engines/xeen/dialogs/dialogs_spells.cpp1045
-rw-r--r--engines/xeen/dialogs/dialogs_spells.h151
-rw-r--r--engines/xeen/dialogs/dialogs_whowill.cpp105
-rw-r--r--engines/xeen/dialogs/dialogs_whowill.h41
40 files changed, 7827 insertions, 0 deletions
diff --git a/engines/xeen/dialogs/dialogs.cpp b/engines/xeen/dialogs/dialogs.cpp
new file mode 100644
index 0000000000..c9b5658a0e
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs.cpp
@@ -0,0 +1,257 @@
+/* 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/dialogs.h"
+#include "xeen/events.h"
+#include "xeen/resources.h"
+#include "xeen/screen.h"
+#include "xeen/xeen.h"
+
+namespace Xeen {
+
+void ButtonContainer::saveButtons() {
+ _savedButtons.push(_buttons);
+ clearButtons();
+}
+
+/*
+ * Clears the current list of defined buttons
+ */
+void ButtonContainer::clearButtons() {
+ _buttons.clear();
+}
+
+void ButtonContainer::restoreButtons() {
+ _buttons = _savedButtons.pop();
+}
+
+void ButtonContainer::addButton(const Common::Rect &bounds, int val,
+ SpriteResource *sprites) {
+ _buttons.push_back(UIButton(bounds, val, _buttons.size() * 2, sprites, sprites != nullptr));
+}
+
+void ButtonContainer::addButton(const Common::Rect &bounds, int val,
+ int frameNum, SpriteResource *sprites) {
+ _buttons.push_back(UIButton(bounds, val, frameNum, sprites, sprites != nullptr));
+}
+
+void ButtonContainer::addPartyButtons(XeenEngine *vm) {
+ for (uint idx = 0; idx < MAX_ACTIVE_PARTY; ++idx) {
+ addButton(Common::Rect(Res.CHAR_FACES_X[idx], 150, Res.CHAR_FACES_X[idx] + 32, 182),
+ Common::KEYCODE_F1 + idx);
+ }
+}
+
+bool ButtonContainer::checkEvents(XeenEngine *vm) {
+ EventsManager &events = *vm->_events;
+ Party &party = *vm->_party;
+ Windows &windows = *_vm->_windows;
+ _buttonValue = 0;
+
+ if (events._leftButton) {
+ Common::Point pt = events._mousePos;
+
+ // Check for party member glyphs being clicked
+ Common::Rect r(0, 0, 32, 32);
+ for (uint idx = 0; idx < party._activeParty.size(); ++idx) {
+ r.moveTo(Res.CHAR_FACES_X[idx], 150);
+ if (r.contains(pt)) {
+ _buttonValue = Common::KEYCODE_F1 + idx;
+ break;
+ }
+ }
+
+ // Check whether any button is selected
+ for (uint i = 0; i < _buttons.size(); ++i) {
+ if (_buttons[i]._bounds.contains(pt)) {
+ events.debounceMouse();
+
+ _buttonValue = _buttons[i]._value;
+ break;
+ }
+ }
+
+ if (!_buttonValue && Common::Rect(8, 8, 224, 135).contains(pt)) {
+ _buttonValue = 1;
+ return true;
+ }
+ } else if (events.isKeyPending()) {
+ Common::KeyState keyState;
+ events.getKey(keyState);
+
+ _buttonValue = keyState.keycode;
+ if (_buttonValue == Common::KEYCODE_KP8)
+ _buttonValue = Common::KEYCODE_UP;
+ else if (_buttonValue == Common::KEYCODE_KP2)
+ _buttonValue = Common::KEYCODE_DOWN;
+ else if (_buttonValue == Common::KEYCODE_KP_ENTER)
+ _buttonValue = Common::KEYCODE_RETURN;
+
+ _buttonValue |= (keyState.flags & ~Common::KBD_STICKY) << 16;
+ }
+
+ if (_buttonValue) {
+ // Check for a button matching the selected _buttonValue
+ Window &win = windows[39];
+ for (uint btnIndex = 0; btnIndex < _buttons.size(); ++btnIndex) {
+ UIButton &btn = _buttons[btnIndex];
+ if (btn._draw && btn._value == _buttonValue) {
+ // Found the correct button
+ // Draw button depressed
+ btn._sprites->draw(0, btn._frameNum + 1,
+ Common::Point(btn._bounds.left, btn._bounds.top));
+ win.setBounds(btn._bounds);
+ win.update();
+
+ // Slight delay
+ events.updateGameCounter();
+ events.wait(2);
+
+ // Redraw button in it's original non-depressed form
+ btn._sprites->draw(0, btn._frameNum,
+ Common::Point(btn._bounds.left, btn._bounds.top));
+ win.setBounds(btn._bounds);
+ win.update();
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+void ButtonContainer::drawButtons(XSurface *surface) {
+ for (uint btnIndex = 0; btnIndex < _buttons.size(); ++btnIndex) {
+ UIButton &btn = _buttons[btnIndex];
+ if (btn._draw) {
+ btn._sprites->draw(*surface, btn._frameNum,
+ Common::Point(btn._bounds.left, btn._bounds.top));
+ }
+ }
+}
+
+bool ButtonContainer::doScroll(bool rollUp, bool fadeIn) {
+ if (_vm->_files->_isDarkCc) {
+ return Cutscenes::doScroll(rollUp, fadeIn);
+ } else {
+ saveButtons();
+ clearButtons();
+ bool result = Cutscenes::doScroll(rollUp, fadeIn);
+ restoreButtons();
+ return result;
+ }
+}
+
+void ButtonContainer::loadStrings(const Common::String &name) {
+ File f(name);
+ _textStrings.clear();
+ while (f.pos() < f.size())
+ _textStrings.push_back(f.readString());
+ f.close();
+}
+
+void ButtonContainer::loadStrings(const Common::String &name, int ccMode) {
+ File f(name, ccMode);
+ _textStrings.clear();
+ while (f.pos() < f.size())
+ _textStrings.push_back(f.readString());
+ f.close();
+}
+
+/*------------------------------------------------------------------------*/
+
+void SettingsBaseDialog::showContents(SpriteResource &title1, bool waitFlag) {
+ _vm->_events->pollEventsAndWait();
+ checkEvents(_vm);
+}
+
+/*------------------------------------------------------------------------*/
+
+void CreditsScreen::show(XeenEngine *vm) {
+ CreditsScreen *dlg = new CreditsScreen(vm);
+
+ switch (vm->getGameID()) {
+ case GType_Clouds:
+ dlg->execute(Res.CLOUDS_CREDITS);
+ break;
+ case GType_Swords:
+ dlg->execute(Res.SWORDS_CREDITS1);
+ dlg->execute(Res.SWORDS_CREDITS2);
+ break;
+ default:
+ dlg->execute(Res.DARK_SIDE_CREDITS);
+ break;
+ }
+
+ delete dlg;
+}
+
+void CreditsScreen::execute(const char *content) {
+ Screen &screen = *_vm->_screen;
+ Windows &windows = *_vm->_windows;
+ EventsManager &events = *_vm->_events;
+
+ // Handle drawing the credits screen
+ doScroll(true, false);
+ windows[GAME_WINDOW].close();
+
+ screen.loadBackground("marb.raw");
+ windows[0].writeString(content);
+ doScroll(false, false);
+
+ events.setCursor(0);
+ windows[0].update();
+ clearButtons();
+
+ // Wait for keypress
+ while (!events.isKeyMousePressed())
+ events.pollEventsAndWait();
+
+ doScroll(true, false);
+}
+
+/*------------------------------------------------------------------------*/
+
+PleaseWait::PleaseWait(bool isOops) {
+ _msg = isOops ? Res.OOPS : Res.PLEASE_WAIT;
+}
+
+PleaseWait::~PleaseWait() {
+ Windows &windows = *g_vm->_windows;
+ windows[9].close();
+}
+
+void PleaseWait::show() {
+ Windows &windows = *g_vm->_windows;
+ Window &w = windows[9];
+
+ if (g_vm->_mode != MODE_0) {
+ w.open();
+ w.writeString(_msg);
+ w.update();
+ }
+}
+
+} // End of namespace Xeen
diff --git a/engines/xeen/dialogs/dialogs.h b/engines/xeen/dialogs/dialogs.h
new file mode 100644
index 0000000000..cabc921536
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs.h
@@ -0,0 +1,137 @@
+/* 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_H
+#define XEEN_DIALOGS_H
+
+#include "common/array.h"
+#include "common/stack.h"
+#include "common/rect.h"
+#include "xeen/cutscenes.h"
+#include "xeen/sprites.h"
+#include "xeen/xsurface.h"
+
+namespace Xeen {
+
+class XeenEngine;
+
+class UIButton {
+public:
+ Common::Rect _bounds;
+ SpriteResource *_sprites;
+ int _value;
+ uint _frameNum;
+ bool _draw;
+
+ UIButton(const Common::Rect &bounds, int value, uint frameNum, SpriteResource *sprites, bool draw) :
+ _bounds(bounds), _value(value), _frameNum(frameNum),
+ _sprites(sprites), _draw(draw) {}
+
+ UIButton() : _value(0), _frameNum(0), _sprites(nullptr), _draw(false) {}
+};
+
+class ButtonContainer : public Cutscenes {
+private:
+ Common::Stack< Common::Array<UIButton> > _savedButtons;
+protected:
+ Common::Array<UIButton> _buttons;
+ Common::StringArray _textStrings;
+ int _buttonValue;
+
+ bool checkEvents(XeenEngine *vm);
+
+ /**
+ * Draws the scroll in the background
+ * @param rollUp If true, rolls up the scroll. If false, unrolls.
+ * @param fadeIn If true, does an initial fade in
+ * @returns True if key or mouse pressed
+ */
+ virtual bool doScroll(bool rollUp, bool fadeIn);
+
+ /**
+ * Load a set of text strings from the given resource
+ * @param name Name of resource containing strings
+ */
+ void loadStrings(const Common::String &name);
+
+ /**
+ * Load a set of text strings from the given resource
+ * @param name Name of resource containing strings
+ * @param ccMode Optional cc file number to explicitly use
+ */
+ void loadStrings(const Common::String &name, int ccMode);
+public:
+ ButtonContainer(XeenEngine *vm) : Cutscenes(vm), _buttonValue(0) {}
+
+ /**
+ * Saves the current list of buttons
+ */
+ void saveButtons();
+
+ void clearButtons();
+
+ void restoreButtons();
+
+ void addButton(const Common::Rect &bounds, int val,
+ SpriteResource *sprites = nullptr);
+ void addButton(const Common::Rect &bounds, int val,
+ int frameNum, SpriteResource *sprites = nullptr);
+
+ void addPartyButtons(XeenEngine *vm);
+
+ /**
+ * Draws the buttons onto the passed surface
+ */
+ void drawButtons(XSurface *surface);
+};
+
+class SettingsBaseDialog : public ButtonContainer {
+protected:
+ virtual void showContents(SpriteResource &title1, bool mode);
+public:
+ SettingsBaseDialog(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ virtual ~SettingsBaseDialog() {}
+};
+
+class CreditsScreen: public ButtonContainer {
+private:
+ CreditsScreen(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ void execute(const char *content);
+public:
+ static void show(XeenEngine *vm);
+};
+
+class PleaseWait {
+private:
+ Common::String _msg;
+public:
+ PleaseWait(bool isOops = false);
+ ~PleaseWait();
+
+ void show();
+};
+
+} // End of namespace Xeen
+
+#endif /* XEEN_DIALOGS_H */
diff --git a/engines/xeen/dialogs/dialogs_awards.cpp b/engines/xeen/dialogs/dialogs_awards.cpp
new file mode 100644
index 0000000000..8e8bfcf67f
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_awards.cpp
@@ -0,0 +1,131 @@
+/* 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 "xeen/dialogs/dialogs_awards.h"
+#include "xeen/party.h"
+#include "xeen/resources.h"
+#include "xeen/xeen.h"
+
+namespace Xeen {
+
+void Awards::show(XeenEngine *vm, const Character *ch) {
+ Awards *dlg = new Awards(vm);
+ dlg->execute(ch);
+ delete dlg;
+}
+
+void Awards::execute(const Character *ch) {
+ EventsManager &events = *g_vm->_events;
+ Windows &windows = *g_vm->_windows;
+ Common::StringArray awards;
+ int numAwards;
+ Mode oldMode = g_vm->_mode;
+ int topIndex = 0;
+
+ loadStrings("award.bin", 1);
+ addButtons();
+
+ // Open the window and draw contents
+ bool win29Open = windows[29]._enabled;
+ if (!win29Open) {
+ windows[29].open();
+ windows[30].open();
+ }
+
+ windows[29].writeString(Res.AWARDS_TEXT);
+ drawButtons(&windows[0]);
+
+ while (!_vm->shouldExit()) {
+ // Build up a list of awards the character has
+ awards.clear();
+ awards.resize(AWARDS_TOTAL);
+ numAwards = 0;
+
+ for (int awardNum = 0; awardNum < AWARDS_TOTAL; ++awardNum) {
+ if (ch->hasAward(awardNum)) {
+ if (awardNum == 9) {
+ // # Warzone Wins
+ awards[numAwards] = Common::String::format(_textStrings[9].c_str(), 28);
+ } else if (awardNum == 17) {
+ // Legendary Race
+ awards[numAwards] = Common::String::format(_textStrings[17].c_str(),
+ Res.RACE_NAMES[ch->_race]);
+ } else {
+ awards[numAwards] = _textStrings[awardNum];
+ }
+ ++numAwards;
+ }
+ }
+
+ // If no awards, add in a message indicating so
+ if (numAwards == 0) {
+ awards[1] = Res.NO_AWARDS;
+ }
+
+ Common::String msg = Common::String::format(Res.AWARDS_FOR,
+ ch->_name.c_str(), Res.CLASS_NAMES[ch->_class],
+ awards[topIndex].c_str(),
+ awards[topIndex + 1].c_str(),
+ awards[topIndex + 2].c_str(),
+ awards[topIndex + 3].c_str(),
+ awards[topIndex + 4].c_str(),
+ awards[topIndex + 5].c_str(),
+ awards[topIndex + 6].c_str(),
+ awards[topIndex + 7].c_str(),
+ awards[topIndex + 8].c_str()
+ );
+ windows[30].writeString(msg);
+ windows[24].update();
+
+ // Wait for keypress
+ do {
+ events.pollEventsAndWait();
+ checkEvents(_vm);
+ } while (!g_vm->shouldExit() && !_buttonValue);
+
+ if (_buttonValue == Common::KEYCODE_ESCAPE) {
+ break;
+ } else if (_buttonValue == Common::KEYCODE_u) {
+ topIndex = MAX(topIndex - 1, 0);
+ } else if (_buttonValue == Common::KEYCODE_d) {
+ if ((++topIndex + 9) > numAwards)
+ --topIndex;
+ }
+ }
+
+ // Close the window
+ if (win29Open) {
+ windows[30].close();
+ windows[29].close();
+ }
+
+ g_vm->_mode = oldMode;
+}
+
+void Awards::addButtons() {
+ _iconSprites.load("award.icn");
+ addButton(Common::Rect(216, 109, 240, 129), Common::KEYCODE_u, &_iconSprites);
+ addButton(Common::Rect(250, 109, 274, 129), Common::KEYCODE_d, &_iconSprites);
+ addButton(Common::Rect(284, 109, 308, 129), Common::KEYCODE_ESCAPE, &_iconSprites);
+}
+
+} // End of namespace Xeen
diff --git a/engines/xeen/dialogs/dialogs_awards.h b/engines/xeen/dialogs/dialogs_awards.h
new file mode 100644
index 0000000000..2ca68171a4
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_awards.h
@@ -0,0 +1,52 @@
+/* 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_AWARDS_H
+#define XEEN_DIALOGS_AWARDS_H
+
+#include "xeen/dialogs/dialogs.h"
+#include "xeen/character.h"
+
+namespace Xeen {
+
+class Awards : public ButtonContainer {
+private:
+ SpriteResource _iconSprites;
+private:
+ Awards(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ /**
+ * Executes the dialog
+ */
+ void execute(const Character *ch);
+
+ /**
+ * Add buttons for the dialog
+ */
+ void addButtons();
+public:
+ static void show(XeenEngine *vm, const Character *ch);
+};
+
+} // End of namespace Xeen
+
+#endif /* XEEN_DIALOGS_AWARDS_H */
diff --git a/engines/xeen/dialogs/dialogs_char_info.cpp b/engines/xeen/dialogs/dialogs_char_info.cpp
new file mode 100644
index 0000000000..0ae64ed608
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_char_info.cpp
@@ -0,0 +1,571 @@
+/* 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 "xeen/dialogs/dialogs_awards.h"
+#include "xeen/dialogs/dialogs_char_info.h"
+#include "xeen/dialogs/dialogs_exchange.h"
+#include "xeen/dialogs/dialogs_items.h"
+#include "xeen/dialogs/dialogs_quick_ref.h"
+#include "xeen/resources.h"
+#include "xeen/xeen.h"
+
+namespace Xeen {
+
+void CharacterInfo::show(XeenEngine *vm, int charIndex) {
+ CharacterInfo *dlg = new CharacterInfo(vm);
+ dlg->execute(charIndex);
+ delete dlg;
+}
+
+void CharacterInfo::execute(int charIndex) {
+ Combat &combat = *_vm->_combat;
+ EventsManager &events = *_vm->_events;
+ Interface &intf = *_vm->_interface;
+ Party &party = *_vm->_party;
+ Windows &windows = *_vm->_windows;
+
+ bool redrawFlag = true;
+ Mode oldMode = _vm->_mode;
+ _vm->_mode = MODE_CHARACTER_INFO;
+ loadDrawStructs();
+ addButtons();
+
+ Character *c = (oldMode != MODE_COMBAT) ? &party._activeParty[charIndex] : combat._combatParty[charIndex];
+ intf.highlightChar(charIndex);
+ Window &w = windows[24];
+ w.open();
+
+ do {
+ if (redrawFlag) {
+ Common::String charDetails = loadCharacterDetails(*c);
+ w.writeString(Common::String::format(Res.CHARACTER_TEMPLATE, charDetails.c_str()));
+ w.drawList(_drawList, 24);
+ w.update();
+ redrawFlag = false;
+ }
+
+ // Wait for keypress, showing a blinking cursor
+ events.updateGameCounter();
+ bool cursorFlag = false;
+ _buttonValue = 0;
+ while (!_vm->shouldExit() && !_buttonValue) {
+ events.pollEventsAndWait();
+ if (events.timeElapsed() > 4) {
+ cursorFlag = !cursorFlag;
+ events.updateGameCounter();
+ }
+
+ showCursor(cursorFlag);
+ w.update();
+ checkEvents(_vm);
+ }
+ events.clearEvents();
+
+ switch (_buttonValue) {
+ case Common::KEYCODE_F1:
+ case Common::KEYCODE_F2:
+ case Common::KEYCODE_F3:
+ case Common::KEYCODE_F4:
+ case Common::KEYCODE_F5:
+ case Common::KEYCODE_F6:
+ _buttonValue -= Common::KEYCODE_F1;
+ if (_buttonValue < (int)(oldMode == MODE_COMBAT ? combat._combatParty.size() : party._activeParty.size())) {
+ charIndex = _buttonValue;
+ c = (oldMode != MODE_COMBAT) ? &party._activeParty[charIndex] : combat._combatParty[charIndex];
+ } else {
+ _vm->_mode = MODE_CHARACTER_INFO;
+ }
+
+ intf.highlightChar(_buttonValue);
+ redrawFlag = true;
+ break;
+
+ case Common::KEYCODE_UP:
+ case Common::KEYCODE_KP8:
+ if (_cursorCell > 0) {
+ showCursor(false);
+ --_cursorCell;
+ showCursor(true);
+ }
+ w.update();
+ break;
+
+ case Common::KEYCODE_DOWN:
+ case Common::KEYCODE_KP2:
+ if (_cursorCell < 20) {
+ showCursor(false);
+ ++_cursorCell;
+ showCursor(true);
+ }
+ w.update();
+ break;
+
+ case Common::KEYCODE_LEFT:
+ case Common::KEYCODE_KP4:
+ if (_cursorCell >= 5) {
+ showCursor(false);
+ _cursorCell -= 5;
+ showCursor(true);
+ }
+ w.update();
+ break;
+
+ case Common::KEYCODE_RIGHT:
+ case Common::KEYCODE_KP6:
+ if (_cursorCell <= 15) {
+ showCursor(false);
+ _cursorCell += 5;
+ showCursor(true);
+ }
+ w.update();
+ break;
+
+ case Common::KEYCODE_RETURN:
+ case Common::KEYCODE_KP_ENTER:
+ _buttonValue = _cursorCell + Common::KEYCODE_a;
+ // Deliberate fall-through
+
+ case 1001:
+ case 1002:
+ case 1003:
+ case 1004:
+ case 1005:
+ case 1006:
+ case 1007:
+ case 1008:
+ case 1009:
+ case 1010:
+ case 1011:
+ case 1012:
+ case 1013:
+ case 1014:
+ case 1015:
+ case 1016:
+ case 1017:
+ case 1018:
+ case 1019:
+ case 1020: {
+ showCursor(false);
+ _cursorCell = _buttonValue - 1001;
+ showCursor(true);
+ w.update();
+
+ bool result = expandStat(_cursorCell, *c);
+ _vm->_mode = MODE_COMBAT;
+ if (result)
+ redrawFlag = true;
+ break;
+ }
+
+ case Common::KEYCODE_e:
+ if (oldMode == MODE_COMBAT) {
+ ErrorScroll::show(_vm, Res.EXCHANGING_IN_COMBAT, WT_FREEZE_WAIT);
+ } else {
+ _vm->_mode = oldMode;
+ ExchangeDialog::show(_vm, c, charIndex);
+ _vm->_mode = MODE_CHARACTER_INFO;
+ redrawFlag = true;
+ }
+ break;
+
+ case Common::KEYCODE_i:
+ _vm->_mode = oldMode;
+ _vm->_combat->_itemFlag = _vm->_mode == MODE_COMBAT;
+ c = ItemsDialog::show(_vm, c, ITEMMODE_CHAR_INFO);
+
+ if (!c) {
+ party._stepped = true;
+ goto exit;
+ }
+
+ _vm->_mode = MODE_CHARACTER_INFO;
+ break;
+
+ case Common::KEYCODE_q:
+ QuickReferenceDialog::show(_vm);
+ redrawFlag = true;
+ break;
+
+ case Common::KEYCODE_ESCAPE:
+ goto exit;
+ }
+ } while (!_vm->shouldExit());
+exit:
+ w.close();
+ intf.unhighlightChar();
+ _vm->_mode = oldMode;
+ _vm->_combat->_itemFlag = false;
+}
+
+void CharacterInfo::loadDrawStructs() {
+ _drawList[0] = DrawStruct(0, 2, 16);
+ _drawList[1] = DrawStruct(2, 2, 39);
+ _drawList[2] = DrawStruct(4, 2, 62);
+ _drawList[3] = DrawStruct(6, 2, 85);
+ _drawList[4] = DrawStruct(8, 2, 108);
+ _drawList[5] = DrawStruct(10, 53, 16);
+ _drawList[6] = DrawStruct(12, 53, 39);
+ _drawList[7] = DrawStruct(14, 53, 62);
+ _drawList[8] = DrawStruct(16, 53, 85);
+ _drawList[9] = DrawStruct(18, 53, 108);
+ _drawList[10] = DrawStruct(20, 104, 16);
+ _drawList[11] = DrawStruct(22, 104, 39);
+ _drawList[12] = DrawStruct(24, 104, 62);
+ _drawList[13] = DrawStruct(26, 104, 85);
+ _drawList[14] = DrawStruct(28, 104, 108);
+ _drawList[15] = DrawStruct(30, 169, 16);
+ _drawList[16] = DrawStruct(32, 169, 39);
+ _drawList[17] = DrawStruct(34, 169, 62);
+ _drawList[18] = DrawStruct(36, 169, 85);
+ _drawList[19] = DrawStruct(38, 169, 108);
+ _drawList[20] = DrawStruct(40, 277, 3);
+ _drawList[21] = DrawStruct(42, 277, 35);
+ _drawList[22] = DrawStruct(44, 277, 67);
+ _drawList[23] = DrawStruct(46, 277, 99);
+
+ _iconSprites.load("view.icn");
+ for (int idx = 0; idx < 24; ++idx)
+ _drawList[idx]._sprites = &_iconSprites;
+}
+
+void CharacterInfo::addButtons() {
+ addButton(Common::Rect(10, 24, 34, 44), 1001, &_iconSprites);
+ addButton(Common::Rect(10, 47, 34, 67), 1002, &_iconSprites);
+ addButton(Common::Rect(10, 70, 34, 90), 1003, &_iconSprites);
+ addButton(Common::Rect(10, 93, 34, 113), 1004, &_iconSprites);
+ addButton(Common::Rect(10, 116, 34, 136), 1005, &_iconSprites);
+ addButton(Common::Rect(61, 24, 85, 44), 1006, &_iconSprites);
+ addButton(Common::Rect(61, 47, 85, 67), 1007, &_iconSprites);
+ addButton(Common::Rect(61, 70, 85, 90), 1008, &_iconSprites);
+ addButton(Common::Rect(61, 93, 85, 113), 1009, &_iconSprites);
+ addButton(Common::Rect(61, 116, 85, 136), 1010, &_iconSprites);
+ addButton(Common::Rect(112, 24, 136, 44), 1011, &_iconSprites);
+ addButton(Common::Rect(112, 47, 136, 67), 1012, &_iconSprites);
+ addButton(Common::Rect(112, 70, 136, 90), 1013, &_iconSprites);
+ addButton(Common::Rect(112, 93, 136, 113), 1014, &_iconSprites);
+ addButton(Common::Rect(112, 116, 136, 136), 1015, &_iconSprites);
+ addButton(Common::Rect(177, 24, 201, 44), 1016, &_iconSprites);
+ addButton(Common::Rect(177, 47, 201, 67), 1017, &_iconSprites);
+ addButton(Common::Rect(177, 70, 201, 90), 1018, &_iconSprites);
+ addButton(Common::Rect(177, 93, 201, 113), 1019, &_iconSprites);
+ addButton(Common::Rect(177, 116, 201, 136), 1020, &_iconSprites);
+ addButton(Common::Rect(285, 11, 309, 31), Common::KEYCODE_i, &_iconSprites);
+ addButton(Common::Rect(285, 43, 309, 63), Common::KEYCODE_q, &_iconSprites);
+ addButton(Common::Rect(285, 75, 309, 95), Common::KEYCODE_e, &_iconSprites);
+ addButton(Common::Rect(285, 107, 309, 127), Common::KEYCODE_ESCAPE, &_iconSprites);
+ addPartyButtons(_vm);
+}
+
+Common::String CharacterInfo::loadCharacterDetails(const Character &c) {
+ Condition condition = c.worstCondition();
+ Party &party = *_vm->_party;
+ int foodVal = party._food / party._activeParty.size() / 3;
+
+ int totalResist =
+ c._fireResistence._permanent + c.itemScan(11) + c._fireResistence._temporary +
+ c._coldResistence._permanent + c.itemScan(13) + c._coldResistence._temporary +
+ c._electricityResistence._permanent + c.itemScan(12) + c._electricityResistence._temporary +
+ c._poisonResistence._permanent + c.itemScan(14) + c._poisonResistence._temporary +
+ c._energyResistence._permanent + c.itemScan(15) + c._energyResistence._temporary +
+ c._magicResistence._permanent + c.itemScan(16) + c._magicResistence._temporary;
+
+ return Common::String::format(Res.CHARACTER_DETAILS,
+ Res.PARTY_GOLD, c._name.c_str(), Res.SEX_NAMES[c._sex],
+ Res.RACE_NAMES[c._race], Res.CLASS_NAMES[c._class],
+ c.statColor(c.getStat(MIGHT), c.getStat(MIGHT, true)), c.getStat(MIGHT),
+ c.statColor(c.getStat(ACCURACY), c.getStat(ACCURACY, true)), c.getStat(ACCURACY),
+ c.statColor(c._currentHp, c.getMaxHP()), c._currentHp,
+ c.getCurrentExperience(),
+ c.statColor(c.getStat(INTELLECT), c.getStat(INTELLECT, true)), c.getStat(INTELLECT),
+ c.statColor(c.getStat(LUCK), c.getStat(LUCK, true)), c.getStat(LUCK),
+ c.statColor(c._currentSp, c.getMaxSP()), c._currentSp,
+ party._gold,
+ c.statColor(c.getStat(PERSONALITY), c.getStat(PERSONALITY, true)), c.getStat(PERSONALITY),
+ c.statColor(c.getAge(), c.getAge(true)), c.getAge(),
+ totalResist,
+ party._gems,
+ c.statColor(c.getStat(ENDURANCE), c.getStat(ENDURANCE, true)), c.getStat(ENDURANCE),
+ c.statColor(c.getCurrentLevel(), c._level._permanent), c.getCurrentLevel(),
+ c.getNumSkills(),
+ foodVal, (foodVal == 1) ? ' ' : 's',
+ c.statColor(c.getStat(SPEED), c.getStat(SPEED, true)), c.getStat(SPEED),
+ c.statColor(c.getArmorClass(), c.getArmorClass(true)), c.getArmorClass(),
+ c.getNumAwards(),
+ Res.CONDITION_COLORS[condition], Res.CONDITION_NAMES[condition],
+ condition == NO_CONDITION && party._blessed ? Res.PLUS_14 : "",
+ condition == NO_CONDITION && party._powerShield ? Res.PLUS_14 : "",
+ condition == NO_CONDITION && party._holyBonus ? Res.PLUS_14 : "",
+ condition == NO_CONDITION && party._heroism ? Res.PLUS_14 : ""
+ );
+}
+
+void CharacterInfo::showCursor(bool flag) {
+ const int CURSOR_X[5] = { 9, 60, 111, 176, 0 };
+ const int CURSOR_Y[5] = { 23, 46, 69, 92, 115 };
+
+ if (_cursorCell < 20) {
+ _iconSprites.draw(0, flag ? 49 : 48,
+ Common::Point(CURSOR_X[_cursorCell / 5], CURSOR_Y[_cursorCell % 5]));
+ }
+}
+
+bool CharacterInfo::expandStat(int attrib, const Character &c) {
+ const int STAT_POS[2][20] = {
+ {
+ 61, 61, 61, 61, 61, 112, 112, 112, 112, 112,
+ 177, 177, 177, 177, 177, 34, 34, 34, 34, 34
+ }, {
+ 24, 47, 70, 93, 116, 24, 47, 70, 93, 116,
+ 24, 47, 70, 93, 116, 24, 47, 70, 93, 116
+ }
+ };
+ assert(attrib < 20);
+ Common::Rect bounds(STAT_POS[0][attrib], STAT_POS[1][attrib],
+ STAT_POS[0][attrib] + 143, STAT_POS[1][attrib] + 52);
+ Party &party = *_vm->_party;
+ Windows &windows = *_vm->_windows;
+ uint stat1, stat2;
+ uint idx;
+ Common::String msg;
+
+ switch (attrib) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ // Basic attributes
+ stat1 = c.getStat((Attribute)attrib, false);
+ stat2 = c.getStat((Attribute)attrib, true);
+ idx = 0;
+ while (Res.STAT_VALUES[idx] <= (int)stat1)
+ ++idx;
+
+ msg = Common::String::format(Res.CURRENT_MAXIMUM_RATING_TEXT, Res.STAT_NAMES[attrib],
+ stat1, stat2, Res.RATING_TEXT[idx]);
+ break;
+
+ case 7:
+ // Age
+ stat1 = c.getAge(false);
+ stat2 = c.getAge(true);
+ msg = Common::String::format(Res.AGE_TEXT, Res.STAT_NAMES[attrib],
+ stat1, stat2, c._birthDay, c._birthYear);
+ break;
+
+ case 8: {
+ // Level
+ const int CLASS_ATTACK_GAINS[10] = { 5, 6, 6, 7, 8, 6, 5, 4, 7, 6 };
+ idx = c.getCurrentLevel() / CLASS_ATTACK_GAINS[c._class] + 1;
+
+ msg = Common::String::format(Res.LEVEL_TEXT, Res.STAT_NAMES[attrib],
+ c.getCurrentLevel(), c._level._permanent,
+ idx, idx > 1 ? "s" : "",
+ c._level._permanent);
+ break;
+ }
+
+ case 9:
+ // Armor Class
+ stat1 = c.getArmorClass(false);
+ stat2 = c.getArmorClass(true);
+ msg = Common::String::format(Res.CURRENT_MAXIMUM_TEXT, Res.STAT_NAMES[attrib],
+ stat1, stat2);
+ bounds.setHeight(42);
+ break;
+
+ case 10:
+ // Hit Points
+ stat1 = c._currentHp;
+ stat2 = c.getMaxHP();
+ msg = Common::String::format(Res.CURRENT_MAXIMUM_TEXT, Res.STAT_NAMES[attrib],
+ stat1, stat2);
+ bounds.setHeight(42);
+ break;
+
+ case 11:
+ // Spell Points
+ stat1 = c._currentSp;
+ stat2 = c.getMaxSP();
+ msg = Common::String::format(Res.CURRENT_MAXIMUM_TEXT, Res.STAT_NAMES[attrib],
+ stat1, stat2);
+ bounds.setHeight(42);
+ break;
+
+ case 12:
+ // Resistences
+ msg = Common::String::format(Res.RESISTENCES_TEXT, Res.STAT_NAMES[attrib],
+ c._fireResistence._permanent + c.itemScan(11) + c._fireResistence._temporary,
+ c._coldResistence._permanent + c.itemScan(13) + c._coldResistence._temporary,
+ c._electricityResistence._permanent + c.itemScan(12) + c._electricityResistence._temporary,
+ c._poisonResistence._permanent + c.itemScan(14) + c._poisonResistence._temporary,
+ c._energyResistence._permanent + c.itemScan(15) + c._energyResistence._temporary,
+ c._magicResistence._permanent + c.itemScan(16) + c._magicResistence._temporary);
+ bounds.setHeight(80);
+ break;
+
+ case 13: {
+ // Skills
+ Common::String lines[20];
+ int numLines = c.getNumSkills();
+ if (numLines > 0) {
+ for (int skill = THIEVERY; skill <= DANGER_SENSE; ++skill) {
+ if (c._skills[skill]) {
+ if (skill == THIEVERY) {
+ lines[0] = Common::String::format("\n\t020%s%u",
+ Res.SKILL_NAMES[THIEVERY], c.getThievery());
+ } else {
+ lines[skill] = Common::String::format("\n\t020%s", Res.SKILL_NAMES[skill]);
+ }
+ }
+ }
+ } else {
+ lines[0] = Res.NONE;
+ numLines = 1;
+ }
+
+ msg = Common::String::format("\x2\x3""c%s\x3l%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ Res.STAT_NAMES[attrib], lines[0].c_str(), lines[1].c_str(),
+ lines[2].c_str(), lines[3].c_str(), lines[4].c_str(), lines[5].c_str(),
+ lines[17].c_str(), lines[6].c_str(), lines[7].c_str(), lines[8].c_str(),
+ lines[9].c_str(), lines[10].c_str(), lines[11].c_str(), lines[12].c_str(),
+ lines[13].c_str(), lines[16].c_str(), lines[14].c_str(), lines[15].c_str());
+
+ bounds.top -= (numLines / 2) * 8;
+ bounds.setHeight(numLines * 9 + 26);
+ if (bounds.bottom >= SCREEN_HEIGHT)
+ bounds.moveTo(bounds.left, SCREEN_HEIGHT - bounds.height() - 1);
+ break;
+ }
+
+ case 14:
+ // Awards
+ Awards::show(_vm, &c);
+ return false;
+
+ case 15:
+ // Experience
+ stat1 = c.getCurrentExperience();
+ stat2 = c.experienceToNextLevel();
+ msg = Common::String::format(Res.EXPERIENCE_TEXT,
+ Res.STAT_NAMES[attrib], stat1,
+ stat2 == 0 ? Res.ELIGIBLE : Common::String::format("%d", stat2).c_str()
+ );
+ bounds.setHeight(43);
+ break;
+
+ case 16:
+ // Gold
+ msg = Common::String::format(Res.IN_PARTY_IN_BANK, Res.CONSUMABLE_NAMES[0],
+ party._gold, party._bankGold);
+ bounds.setHeight(43);
+ break;
+
+ case 17:
+ // Gems
+ msg = Common::String::format(Res.IN_PARTY_IN_BANK, Res.CONSUMABLE_NAMES[1],
+ party._gems, party._bankGems);
+ bounds.setHeight(43);
+ break;
+
+ case 18: {
+ // Food
+ int food = (party._food / party._activeParty.size()) / 3;
+ msg = Common::String::format(Res.FOOD_TEXT, Res.CONSUMABLE_NAMES[2],
+ party._food, food, food != 1 ? "s" : "");
+ break;
+ }
+
+ case 19: {
+ // Conditions
+ Common::String lines[20];
+ int total = 0;
+ for (int condition = CURSED; condition <= ERADICATED; ++condition) {
+ if (c._conditions[condition]) {
+ if (condition >= UNCONSCIOUS) {
+ lines[condition] = Common::String::format("\n\t020%s",
+ Res.CONDITION_NAMES[condition]);
+ } else {
+ lines[condition] = Common::String::format("\n\t020%s\t095-%d",
+ Res.CONDITION_NAMES[condition], c._conditions[condition]);
+ }
+
+ ++total;
+ }
+ }
+
+ Condition condition = c.worstCondition();
+ if (condition == NO_CONDITION) {
+ lines[0] = Common::String::format("\n\t020%s", Res.GOOD);
+ ++total;
+ }
+
+ if (party._blessed)
+ lines[16] = Common::String::format(Res.BLESSED, party._blessed);
+ if (party._powerShield)
+ lines[17] = Common::String::format(Res.POWER_SHIELD, party._powerShield);
+ if (party._holyBonus)
+ lines[18] = Common::String::format(Res.HOLY_BONUS, party._holyBonus);
+ if (party._heroism)
+ lines[19] = Common::String::format(Res.HEROISM, party._heroism);
+
+ msg = Common::String::format("\x2\x3""c%s\x3l%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\x1",
+ Res.CONSUMABLE_NAMES[3], lines[0].c_str(), lines[1].c_str(),
+ lines[2].c_str(), lines[3].c_str(), lines[4].c_str(),
+ lines[5].c_str(), lines[6].c_str(), lines[7].c_str(),
+ lines[8].c_str(), lines[9].c_str(), lines[10].c_str(),
+ lines[11].c_str(), lines[12].c_str(), lines[13].c_str(),
+ lines[14].c_str(), lines[15].c_str(), lines[16].c_str(),
+ lines[17].c_str(), lines[18].c_str(), lines[19].c_str()
+ );
+
+ bounds.top -= ((total - 1) / 2) * 8;
+ bounds.setHeight(total * 9 + 26);
+ if (bounds.bottom >= SCREEN_HEIGHT)
+ bounds.moveTo(bounds.left, SCREEN_HEIGHT - bounds.height() - 1);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ // Write the data for the stat display
+ Window &w = windows[28];
+ w.setBounds(bounds);
+ w.open();
+ w.writeString(msg);
+ w.update();
+
+ // Wait for a user key/click
+ EventsManager &events = *_vm->_events;
+ while (!_vm->shouldExit() && !events.isKeyMousePressed())
+ events.pollEventsAndWait();
+ events.clearEvents();
+
+ w.close();
+ return false;
+}
+
+} // End of namespace Xeen
diff --git a/engines/xeen/dialogs/dialogs_char_info.h b/engines/xeen/dialogs/dialogs_char_info.h
new file mode 100644
index 0000000000..6dc7eaa51a
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_char_info.h
@@ -0,0 +1,69 @@
+/* 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_CHAR_INFO_H
+#define XEEN_DIALOGS_CHAR_INFO_H
+
+#include "xeen/dialogs/dialogs.h"
+#include "xeen/party.h"
+#include "xeen/window.h"
+
+namespace Xeen {
+
+class CharacterInfo : public ButtonContainer {
+private:
+ SpriteResource _iconSprites;
+ DrawStruct _drawList[24];
+ int _cursorCell;
+
+ CharacterInfo(XeenEngine *vm) : ButtonContainer(vm), _cursorCell(0) {}
+
+ void execute(int charIndex);
+
+ /**
+ * Load the draw structure list with frame numbers and positions
+ */
+ void loadDrawStructs();
+
+ /**
+ * Set up the button list for the dialog
+ */
+ void addButtons();
+
+ /**
+ * Return a string containing the details of the character
+ */
+ Common::String loadCharacterDetails(const Character &c);
+
+ /**
+ * Cursor display handling
+ */
+ void showCursor(bool flag);
+
+ bool expandStat(int attrib, const Character &c);
+public:
+ static void show(XeenEngine *vm, int charIndex);
+};
+
+} // End of namespace Xeen
+
+#endif /* XEEN_DIALOGS_CHAR_INFO_H */
diff --git a/engines/xeen/dialogs/dialogs_control_panel.cpp b/engines/xeen/dialogs/dialogs_control_panel.cpp
new file mode 100644
index 0000000000..9c933b30a3
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_control_panel.cpp
@@ -0,0 +1,220 @@
+/* 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 "xeen/dialogs/dialogs_control_panel.h"
+#include "xeen/dialogs/dialogs_query.h"
+#include "xeen/party.h"
+#include "xeen/resources.h"
+#include "xeen/xeen.h"
+
+namespace Xeen {
+
+int ControlPanel::show(XeenEngine *vm) {
+ ControlPanel *dlg = new ControlPanel(vm);
+ int result = dlg->execute();
+ delete dlg;
+
+ return result;
+}
+
+int ControlPanel::execute() {
+ EventsManager &events = *_vm->_events;
+ Interface &intf = *_vm->_interface;
+ Map &map = *_vm->_map;
+ Party &party = *_vm->_party;
+ SavesManager &saves = *_vm->_saves;
+ Sound &sound = *_vm->_sound;
+ Windows &windows = *_vm->_windows;
+ Window &w = windows[23];
+ Window &w3 = windows[3];
+
+ loadButtons();
+
+ int result = 0, debugCtr = 0;
+ w.open();
+
+ do {
+ Common::String btnText = getButtonText();
+ Common::String text = Common::String::format(Res.CONTROL_PANEL_TEXT, btnText.c_str());
+
+ drawButtons(&w);
+ w.writeString(text);
+ w.writeString("\xB""000\t000\x1");
+ w.update();
+
+ do {
+ events.updateGameCounter();
+ intf.draw3d(false, false);
+ w.writeString("\r");
+ drawButtons(&w);
+ w.writeString(text);
+ w.writeString("\v000\t000");
+ w.frame();
+
+ if (_debugFlag)
+ w.writeString(getTimeText());
+
+ w3.update();
+ w.update();
+
+ events.pollEventsAndWait();
+ checkEvents(_vm);
+ if (_vm->shouldExit())
+ return 0;
+ } while (!_buttonValue && !events.timeElapsed());
+
+ switch (_buttonValue) {
+ case Common::KEYCODE_q:
+ if (Confirm::show(g_vm, Res.CONFIRM_QUIT)) {
+ g_vm->_gameMode = GMODE_QUIT;
+ result = 1;
+ }
+ break;
+
+ case Common::KEYCODE_w:
+ if (Confirm::show(g_vm, Res.MR_WIZARD)) {
+ w.close();
+ if (!windows[2]._enabled) {
+ sound.playFX(51);
+
+ if (g_vm->getGameID() == GType_WorldOfXeen) {
+ map._loadDarkSide = false;
+ map.load(28);
+ party._mazeDirection = DIR_EAST;
+ } else {
+ map._loadDarkSide = true;
+ map.load(29);
+ party._mazeDirection = DIR_SOUTH;
+ }
+ party.moveToRunLocation();
+ }
+
+ party._gems = 0;
+ result = 2;
+ }
+ break;
+
+ case Common::KEYCODE_l:
+ if (_vm->_mode == MODE_COMBAT) {
+ ErrorScroll::show(_vm, Res.NO_LOADING_IN_COMBAT);
+ } else {
+ // Close dialog and show loading dialog
+ result = 3;
+ }
+ break;
+
+ case Common::KEYCODE_s:
+ if (_vm->_mode == MODE_COMBAT) {
+ ErrorScroll::show(_vm, Res.NO_SAVING_IN_COMBAT);
+ } else {
+ // Close dialog and show saving dialog
+ result = 4;
+ }
+ break;
+
+ case Common::KEYCODE_e:
+ sound.setEffectsOn(!sound._soundOn);
+ break;
+
+ case Common::KEYCODE_m:
+ sound.setMusicOn(!sound._musicOn);
+ break;
+
+ case Common::KEYCODE_ESCAPE:
+ result = 1;
+ break;
+
+ // Goober cheat sequence
+ case Common::KEYCODE_g:
+ debugCtr = 1;
+ break;
+ case Common::KEYCODE_o:
+ debugCtr = (debugCtr == 1 || debugCtr == 2) ? 2 : 0;
+ break;
+ case Common::KEYCODE_b:
+ debugCtr = (debugCtr == 2) ? 3 : 0;
+ break;
+ case Common::KEYCODE_r:
+ if (debugCtr == 3)
+ _debugFlag = true;
+ else
+ debugCtr = 0;
+ break;
+
+ default:
+ break;
+ }
+ } while (!result);
+
+ w.close();
+ intf.drawParty(true);
+
+ if (result == 3) {
+ saves.loadGame();
+ } else if (result == 4) {
+ saves.saveGame();
+ }
+
+ return result;
+}
+
+void ControlPanel::loadButtons() {
+ _iconSprites.load("cpanel.icn");
+
+ addButton(Common::Rect(214, 56, 244, 69), Common::KEYCODE_e, 0, &_iconSprites);
+ addButton(Common::Rect(214, 75, 244, 88), Common::KEYCODE_m, 0, &_iconSprites);
+ addButton(Common::Rect(135, 56, 165, 69), Common::KEYCODE_l, 0, &_iconSprites);
+ addButton(Common::Rect(135, 75, 165, 88), Common::KEYCODE_s, 0, &_iconSprites);
+
+ // For ScummVM we've merged both Save and Save As into a single
+ // save item, so we don't need this one
+ addButton(Common::Rect(), 0);
+
+ addButton(Common::Rect(135, 94, 165, 107), Common::KEYCODE_q, 0, &_iconSprites);
+ addButton(Common::Rect(175, 113, 205, 126), Common::KEYCODE_w, 0, &_iconSprites);
+}
+
+Common::String ControlPanel::getButtonText() {
+ Sound &sound = *g_vm->_sound;
+ _btnSoundText = sound._soundOn ? Res.ON : Res.OFF;
+ _btnMusicText = sound._musicOn ? Res.ON : Res.OFF;
+
+ return Common::String::format(Res.CONTROL_PANEL_BUTTONS,
+ _btnSoundText.c_str(), _btnMusicText.c_str());
+}
+
+Common::String ControlPanel::getTimeText() const {
+ TimeDate td;
+ g_system->getTimeAndDate(td);
+ Common::String timeStr = Common::String::format("%d:%.2d:%.2d%c",
+ td.tm_hour == 0 || td.tm_hour == 12 ? 12 : (td.tm_hour % 12),
+ td.tm_min, td.tm_sec, (td.tm_hour >= 12) ? 'p' : 'c');
+
+ uint32 playtime = g_vm->_events->playTime() / GAME_FRAME_RATE;
+ Common::String playtimeStr = Common::String::format("%d:%.2d:%.2d",
+ playtime / 3600, (playtime / 60) % 60, playtime % 60);
+ return Common::String::format(
+ "\x2\x3l\xB""000\t000\x4""160%s\x3r\xB""000\t000%s\x1",
+ timeStr.c_str(), playtimeStr.c_str());
+}
+
+} // End of namespace Xeen
diff --git a/engines/xeen/dialogs/dialogs_control_panel.h b/engines/xeen/dialogs/dialogs_control_panel.h
new file mode 100644
index 0000000000..5181825630
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_control_panel.h
@@ -0,0 +1,66 @@
+/* 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_CONTROL_PANEL_H
+#define XEEN_DIALOGS_CONTROL_PANEL_H
+
+#include "xeen/dialogs/dialogs.h"
+
+namespace Xeen {
+
+class ControlPanel : public ButtonContainer {
+private:
+ SpriteResource _iconSprites;
+ Common::String _btnSoundText, _btnMusicText;
+ bool _debugFlag;
+private:
+ ControlPanel(XeenEngine *vm) : ButtonContainer(vm), _debugFlag(false) {}
+
+ /**
+ * Inner handler for showing the dialog
+ */
+ int execute();
+
+ /**
+ * Loads the buttons for the dialog
+ */
+ void loadButtons();
+
+ /**
+ * Gets the text for the dialog buttons
+ */
+ Common::String getButtonText();
+
+ /**
+ * Gets the current time
+ */
+ Common::String getTimeText() const;
+public:
+ /**
+ * Show the control panel
+ */
+ static int show(XeenEngine *vm);
+};
+
+} // End of namespace Xeen
+
+#endif /* XEEN_DIALOGS_CONTROL_PANEL_H */
diff --git a/engines/xeen/dialogs/dialogs_create_char.cpp b/engines/xeen/dialogs/dialogs_create_char.cpp
new file mode 100644
index 0000000000..577ae5314b
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_create_char.cpp
@@ -0,0 +1,648 @@
+/* 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 "xeen/dialogs/dialogs_create_char.h"
+#include "xeen/dialogs/dialogs_input.h"
+#include "xeen/xeen.h"
+
+namespace Xeen {
+
+void CreateCharacterDialog::show(XeenEngine *vm) {
+ CreateCharacterDialog *dlg = new CreateCharacterDialog(vm);
+ dlg->execute();
+ delete dlg;
+}
+
+CreateCharacterDialog::CreateCharacterDialog(XeenEngine *vm) : ButtonContainer(vm) {
+ Common::fill(&_attribs[0], &_attribs[TOTAL_ATTRIBUTES], 0);
+ Common::fill(&_allowedClasses[0], &_allowedClasses[TOTAL_CLASSES], false);
+ _dicePos[0] = Common::Point(20, 17);
+ _dicePos[1] = Common::Point(112, 35);
+ _dicePos[2] = Common::Point(61, 50);
+ _diceFrame[0] = 0;
+ _diceFrame[1] = 2;
+ _diceFrame[2] = 4;
+ _diceInc[0] = Common::Point(10, -10);
+ _diceInc[1] = Common::Point(-10, -10);
+ _diceInc[2] = Common::Point(-10, 10);
+
+ _dice.load("dice.vga");
+ _diceSize = _dice.getFrameSize(0);
+
+ loadButtons();
+}
+
+void CreateCharacterDialog::execute() {
+ EventsManager &events = *_vm->_events;
+ Party &party = *_vm->_party;
+ Screen &screen = *_vm->_screen;
+ Windows &windows = *_vm->_windows;
+ Window &w = windows[0];
+ Common::Array<int> freeCharList;
+ int classId = -1;
+ int selectedClass = 0;
+ bool hasFadedIn = false;
+ bool restartFlag = true;
+ Race race = HUMAN;
+ Sex sex = MALE;
+ Common::String msg, details;
+ int charIndex = 0;
+
+ Mode oldMode = _vm->_mode;
+ _vm->_mode = MODE_4;
+
+ // Load the background
+ screen.loadBackground("create.raw");
+ events.setCursor(0);
+
+ while (!_vm->shouldExit()) {
+ if (restartFlag) {
+ // Build up list of roster slot indexes that are free
+ freeCharList.clear();
+ for (uint idx = 0; idx < XEEN_TOTAL_CHARACTERS; ++idx) {
+ if (party._roster[idx]._name.empty())
+ freeCharList.push_back(idx);
+ }
+ charIndex = 0;
+
+ if (freeCharList.size() == XEEN_TOTAL_CHARACTERS)
+ break;
+
+ // Get and race and sex for the given character
+ race = (Race)((freeCharList[charIndex] / 4) % 5);
+ sex = (Sex)(freeCharList[charIndex] & 1);
+
+ // Randomly determine attributes, and which classes they allow
+ rollAttributes();
+
+ // Get the display of the rolled character details
+ selectedClass = newCharDetails(race, sex, classId, selectedClass, details);
+ msg = Common::String::format(Res.CREATE_CHAR_DETAILS,
+ details.c_str());
+
+ // Draw the icons and the currently selected headshot
+ drawIcons();
+ party._roster[freeCharList[charIndex]]._faceSprites->draw(
+ w, 0, Common::Point(27, 102));
+
+ // Render all on-screen text
+ w.writeString(msg);
+ w.update();
+
+ // Draw the arrow for the selected class, if applicable
+ if (selectedClass != -1)
+ printSelectionArrow(selectedClass);
+
+ // Draw the dice
+ drawDice();
+ if (!hasFadedIn) {
+ screen.fadeIn();
+ hasFadedIn = true;
+ }
+
+ restartFlag = false;
+ }
+
+ // Animate the dice until a user action occurs
+ _buttonValue = 0;
+ while (!_vm->shouldExit() && !_buttonValue)
+ drawDice();
+
+ // Handling for different actions
+ if (_buttonValue == Common::KEYCODE_ESCAPE)
+ break;
+
+ switch (_buttonValue) {
+ case Common::KEYCODE_UP:
+ if (charIndex == 0)
+ continue;
+
+ --charIndex;
+ race = (Race)((freeCharList[charIndex] / 4) % 5);
+ sex = (Sex)(freeCharList[charIndex] & 1);
+ break;
+
+ case Common::KEYCODE_DOWN:
+ if (++charIndex == (int)freeCharList.size()) {
+ --charIndex;
+ continue;
+ } else {
+ race = (Race)((freeCharList[charIndex] / 4) % 5);
+ sex = (Sex)(freeCharList[charIndex] & 1);
+ }
+ break;
+
+ case Common::KEYCODE_PAGEUP:
+ for (int tempClass = selectedClass - 1; tempClass >= 0; --tempClass) {
+ if (_allowedClasses[tempClass]) {
+ selectedClass = tempClass;
+ break;
+ }
+ }
+
+ printSelectionArrow(selectedClass);
+ continue;
+
+ case Common::KEYCODE_PAGEDOWN:
+ break;
+
+ case Common::KEYCODE_m:
+ case Common::KEYCODE_i:
+ case Common::KEYCODE_p:
+ case Common::KEYCODE_e:
+ case Common::KEYCODE_s:
+ case Common::KEYCODE_a:
+ case Common::KEYCODE_l:
+ if (swapAttributes(_buttonValue)) {
+ checkClass();
+ classId = -1;
+ selectedClass = newCharDetails(race, sex, classId, selectedClass, msg);
+ }
+ break;
+
+ case 1000:
+ case 1001:
+ case 1002:
+ case 1003:
+ case 1004:
+ case 1005:
+ case 1006:
+ case 1007:
+ case 1008:
+ case 1009:
+ if (_allowedClasses[_buttonValue - 1000]) {
+ selectedClass = classId = _buttonValue - 1000;
+ }
+ break;
+
+ case Common::KEYCODE_c: {
+ _vm->_mode = MODE_FF;
+ bool result = saveCharacter(party._roster[freeCharList[charIndex]],
+ classId, race, sex);
+ _vm->_mode = MODE_4;
+
+ if (result)
+ restartFlag = true;
+ continue;
+ }
+
+ case Common::KEYCODE_RETURN:
+ classId = selectedClass;
+ break;
+
+ case Common::KEYCODE_SPACE:
+ case Common::KEYCODE_r:
+ // Re-roll the attributes
+ rollAttributes();
+ classId = -1;
+ break;
+
+ default:
+ // For all other keypresses, skip the code below the switch
+ // statement, and go to wait for the next key
+ continue;
+ }
+
+ if (_buttonValue != Common::KEYCODE_PAGEDOWN) {
+ selectedClass = newCharDetails(race, sex, classId, selectedClass, msg);
+
+ drawIcons2();
+ party._roster[freeCharList[charIndex]]._faceSprites->draw(w, 0,
+ Common::Point(27, 102));
+
+ w.writeString(msg);
+ w.update();
+
+ if (selectedClass != -1) {
+ printSelectionArrow(selectedClass);
+ continue;
+ }
+ }
+
+ // Move to next available class, or if the code block above resulted in
+ // selectedClass being -1, move to select the first available class
+ for (int tempClass = selectedClass + 1; tempClass <= CLASS_RANGER; ++tempClass) {
+ if (_allowedClasses[tempClass]) {
+ selectedClass = tempClass;
+ break;
+ }
+ }
+
+ printSelectionArrow(selectedClass);
+ } while (!_vm->shouldExit() && _buttonValue != Common::KEYCODE_ESCAPE);
+
+ _vm->_mode = oldMode;
+}
+
+void CreateCharacterDialog::loadButtons() {
+ _icons.load("create.icn");
+
+ // Add buttons
+ addButton(Common::Rect(132, 98, 156, 118), Common::KEYCODE_r, &_icons);
+ addButton(Common::Rect(132, 128, 156, 148), Common::KEYCODE_c, &_icons);
+ addButton(Common::Rect(132, 158, 156, 178), Common::KEYCODE_ESCAPE, &_icons);
+ addButton(Common::Rect(86, 98, 110, 118), Common::KEYCODE_UP, &_icons);
+ addButton(Common::Rect(86, 120, 110, 140), Common::KEYCODE_DOWN, &_icons);
+ addButton(Common::Rect(168, 19, 192, 39), Common::KEYCODE_n, nullptr);
+ addButton(Common::Rect(168, 43, 192, 63), Common::KEYCODE_i, nullptr);
+ addButton(Common::Rect(168, 67, 192, 87), Common::KEYCODE_p, nullptr);
+ addButton(Common::Rect(168, 91, 192, 111), Common::KEYCODE_e, nullptr);
+ addButton(Common::Rect(168, 115, 192, 135), Common::KEYCODE_s, nullptr);
+ addButton(Common::Rect(168, 139, 192, 159), Common::KEYCODE_a, nullptr);
+ addButton(Common::Rect(168, 163, 192, 183), Common::KEYCODE_l, nullptr);
+ addButton(Common::Rect(227, 19, 239, 29), 1000, nullptr);
+ addButton(Common::Rect(227, 30, 239, 40), 1001, nullptr);
+ addButton(Common::Rect(227, 41, 239, 51), 1002, nullptr);
+ addButton(Common::Rect(227, 52, 239, 62), 1003, nullptr);
+ addButton(Common::Rect(227, 63, 239, 73), 1004, nullptr);
+ addButton(Common::Rect(227, 74, 239, 84), 1005, nullptr);
+ addButton(Common::Rect(227, 85, 239, 95), 1006, nullptr);
+ addButton(Common::Rect(227, 96, 239, 106), 1007, nullptr);
+ addButton(Common::Rect(227, 107, 239, 117), 1008, nullptr);
+ addButton(Common::Rect(227, 118, 239, 128), 1009, nullptr);
+}
+
+void CreateCharacterDialog::drawIcons() {
+ // Draw the screen
+ _icons.draw(0, 10, Common::Point(168, 19));
+ _icons.draw(0, 12, Common::Point(168, 43));
+ _icons.draw(0, 14, Common::Point(168, 67));
+ _icons.draw(0, 16, Common::Point(168, 91));
+ _icons.draw(0, 18, Common::Point(168, 115));
+ _icons.draw(0, 20, Common::Point(168, 139));
+ _icons.draw(0, 22, Common::Point(168, 163));
+ for (int idx = 0; idx < 9; ++idx)
+ _icons.draw(0, 24 + idx * 2, Common::Point(227, 19 + 11 * idx));
+
+ for (int idx = 0; idx < 7; ++idx)
+ _icons.draw(0, 50 + idx, Common::Point(195, 31 + 24 * idx));
+
+ _icons.draw(0, 57, Common::Point(62, 148));
+ _icons.draw(0, 58, Common::Point(62, 158));
+ _icons.draw(0, 59, Common::Point(62, 168));
+ _icons.draw(0, 61, Common::Point(220, 19));
+ _icons.draw(0, 64, Common::Point(220, 155));
+ _icons.draw(0, 65, Common::Point(220, 170));
+
+ _icons.draw(0, 0, Common::Point(132, 98));
+ _icons.draw(0, 2, Common::Point(132, 128));
+ _icons.draw(0, 4, Common::Point(132, 158));
+ _icons.draw(0, 6, Common::Point(86, 98));
+ _icons.draw(0, 8, Common::Point(86, 120));
+}
+
+void CreateCharacterDialog::drawIcons2() {
+ for (int idx = 0; idx < 7; ++idx)
+ _icons.draw(0, 10 + idx * 2, Common::Point(168, 19 + idx * 24));
+ for (int idx = 0; idx < 10; ++idx)
+ _icons.draw(0, 24 + idx * 2, Common::Point(227, 19 + idx * 11));
+ for (int idx = 0; idx < 8; ++idx)
+ _icons.draw(0, 50 + idx, Common::Point(195, 31 + idx * 24));
+
+ _icons.draw(0, 57, Common::Point(62, 148));
+ _icons.draw(0, 58, Common::Point(62, 158));
+ _icons.draw(0, 59, Common::Point(62, 168));
+ _icons.draw(0, 61, Common::Point(220, 19));
+ _icons.draw(0, 64, Common::Point(220, 155));
+ _icons.draw(0, 65, Common::Point(220, 170));
+
+ _icons.draw(0, 0, Common::Point(132, 98));
+ _icons.draw(0, 2, Common::Point(132, 128));
+ _icons.draw(0, 4, Common::Point(132, 158));
+ _icons.draw(0, 6, Common::Point(86, 98));
+ _icons.draw(0, 8, Common::Point(86, 120));
+}
+
+void CreateCharacterDialog::rollAttributes() {
+ bool repeat = true;
+ do {
+ // Default all the attributes to zero
+ Common::fill(&_attribs[0], &_attribs[TOTAL_ATTRIBUTES], 0);
+
+ // Assign random amounts to each attribute
+ for (int idx1 = 0; idx1 < 3; ++idx1) {
+ for (int idx2 = 0; idx2 < TOTAL_ATTRIBUTES; ++idx2) {
+ _attribs[idx2] += _vm->getRandomNumber(10, 79) / 10;
+ }
+ }
+
+ // Check which classes are allowed based on the rolled attributes
+ checkClass();
+
+ // Only exit if the attributes allow for at least one class
+ for (int idx = 0; idx < TOTAL_CLASSES; ++idx) {
+ if (_allowedClasses[idx])
+ repeat = false;
+ }
+ } while (repeat);
+}
+
+void CreateCharacterDialog::checkClass() {
+ _allowedClasses[CLASS_KNIGHT] = _attribs[MIGHT] >= 15;
+ _allowedClasses[CLASS_PALADIN] = _attribs[MIGHT] >= 13
+ && _attribs[PERSONALITY] >= 13 && _attribs[ENDURANCE] >= 13;
+ _allowedClasses[CLASS_ARCHER] = _attribs[INTELLECT] >= 13 && _attribs[ACCURACY] >= 13;
+ _allowedClasses[CLASS_CLERIC] = _attribs[PERSONALITY] >= 13;
+ _allowedClasses[CLASS_SORCERER] = _attribs[INTELLECT] >= 13;
+ _allowedClasses[CLASS_ROBBER] = _attribs[LUCK] >= 13;
+ _allowedClasses[CLASS_NINJA] = _attribs[SPEED] >= 13 && _attribs[ACCURACY] >= 13;
+ _allowedClasses[CLASS_BARBARIAN] = _attribs[ENDURANCE] >= 15;
+ _allowedClasses[CLASS_DRUID] = _attribs[INTELLECT] >= 15 && _attribs[PERSONALITY] >= 15;
+ _allowedClasses[CLASS_RANGER] = _attribs[INTELLECT] >= 12 && _attribs[PERSONALITY] >= 12
+ && _attribs[ENDURANCE] >= 12 && _attribs[SPEED] >= 12;
+}
+
+int CreateCharacterDialog::newCharDetails(Race race, Sex sex, int classId,
+ int selectedClass, Common::String &msg) {
+ int foundClass = -1;
+ Common::String skillStr, classStr, raceSkillStr;
+
+ // If a selected class is provided, set the default skill for that class
+ if (classId != -1 && Res.NEW_CHAR_SKILLS[classId] != -1) {
+ const char *skillP = Res.SKILL_NAMES[Res.NEW_CHAR_SKILLS[classId]];
+ skillStr = Common::String(skillP, skillP + Res.NEW_CHAR_SKILLS_LEN[classId]);
+ }
+
+ // If a class is provided, set the class name
+ if (classId != -1) {
+ classStr = Common::String::format("\t062\v168%s", Res.CLASS_NAMES[classId]);
+ }
+
+ // Set up default skill for the race, if any
+ if (Res.NEW_CHAR_RACE_SKILLS[race] != -1) {
+ raceSkillStr = Res.SKILL_NAMES[Res.NEW_CHAR_RACE_SKILLS[race]];
+ }
+
+ // Set up color to use for each skill string to be displayed, based
+ // on whether each class is allowed or not for the given attributes
+ int classColors[TOTAL_CLASSES];
+ Common::fill(&classColors[0], &classColors[TOTAL_CLASSES], 0);
+ for (int classNum = CLASS_KNIGHT; classNum <= CLASS_RANGER; ++classNum) {
+ if (_allowedClasses[classNum]) {
+ if (classId == -1 && (foundClass == -1 || foundClass < classNum))
+ foundClass = classNum;
+ classColors[classNum] = 4;
+ }
+ }
+ if (classId != -1)
+ classColors[selectedClass] = 12;
+
+ // Return stats details and character class
+ msg = Common::String::format(Res.NEW_CHAR_STATS, Res.RACE_NAMES[race], Res.SEX_NAMES[sex],
+ _attribs[MIGHT], _attribs[INTELLECT], _attribs[PERSONALITY],
+ _attribs[ENDURANCE], _attribs[SPEED], _attribs[ACCURACY], _attribs[LUCK],
+ classColors[CLASS_KNIGHT], classColors[CLASS_PALADIN],
+ classColors[CLASS_ARCHER], classColors[CLASS_CLERIC],
+ classColors[CLASS_SORCERER], classColors[CLASS_ROBBER],
+ classColors[CLASS_NINJA], classColors[CLASS_BARBARIAN],
+ classColors[CLASS_DRUID], classColors[CLASS_RANGER],
+ skillStr.c_str(), raceSkillStr.c_str(), classStr.c_str()
+ );
+ return classId == -1 ? foundClass : selectedClass;
+}
+
+void CreateCharacterDialog::printSelectionArrow(int selectedClass) {
+ Windows &windows = *_vm->_windows;
+ Window &w = windows[0];
+
+ _icons.draw(0, 61, Common::Point(220, 19));
+ _icons.draw(0, 63, Common::Point(220, selectedClass * 11 + 21));
+ w.update();
+}
+
+void CreateCharacterDialog::drawDice() {
+ EventsManager &events = *_vm->_events;
+ Windows &windows = *_vm->_windows;
+ Window &w = windows[32];
+
+ // Draw the dice area background
+ events.updateGameCounter();
+ _dice.draw(w, 7, Common::Point(12, 11));
+
+ // Iterate through each of the three dice
+ for (int diceNum = 0; diceNum < 3; ++diceNum) {
+ _diceFrame[diceNum] = (_diceFrame[diceNum] + 1) % 7;
+ _dicePos[diceNum] += _diceInc[diceNum];
+
+ if (_dicePos[diceNum].x < 13) {
+ _dicePos[diceNum].x = 13;
+ _diceInc[diceNum].x *= -1;
+ } else if (_dicePos[diceNum].x >= (163 - _diceSize.x)) {
+ _dicePos[diceNum].x = 163 - _diceSize.x;
+ _diceInc[diceNum].x *= -1;
+ }
+
+ if (_dicePos[diceNum].y < 12) {
+ _dicePos[diceNum].y = 12;
+ _diceInc[diceNum].y *= -1;
+ } else if (_dicePos[diceNum].y >= (93 - _diceSize.y)) {
+ _dicePos[diceNum].y = 93 - _diceSize.y;
+ _diceInc[diceNum].y *= -1;
+ }
+
+ _dice.draw(w, _diceFrame[diceNum], _dicePos[diceNum]);
+ }
+
+ // Wait for a single frame, checking for any events
+ w.update();
+ events.wait(1);
+ checkEvents(_vm);
+}
+
+int CreateCharacterDialog::getAttribFromKeycode(int keycode) const {
+ switch (keycode) {
+ case Common::KEYCODE_m:
+ return MIGHT;
+ case Common::KEYCODE_i:
+ return INTELLECT;
+ case Common::KEYCODE_p:
+ return PERSONALITY;
+ case Common::KEYCODE_e:
+ return ENDURANCE;
+ case Common::KEYCODE_s:
+ return SPEED;
+ case Common::KEYCODE_a:
+ return ACCURACY;
+ case Common::KEYCODE_l:
+ return LUCK;
+ default:
+ return -1;
+ }
+}
+
+bool CreateCharacterDialog::swapAttributes(int keycode) {
+ Windows &windows = *_vm->_windows;
+ Window &w = windows[0];
+
+ int srcAttrib = getAttribFromKeycode(keycode);
+ assert(srcAttrib >= 0);
+
+ _vm->_mode = MODE_86;
+ _icons.draw(w, srcAttrib * 2 + 11, Common::Point(
+ _buttons[srcAttrib + 5]._bounds.left, _buttons[srcAttrib + 5]._bounds.top));
+ w.update();
+
+ int destAttrib = exchangeAttribute(srcAttrib);
+ if (destAttrib != -1) {
+ _icons.draw(w, destAttrib * 2 + 11, Common::Point(
+ _buttons[destAttrib + 5]._bounds.left,
+ _buttons[destAttrib + 5]._bounds.top));
+
+ SWAP(_attribs[srcAttrib], _attribs[destAttrib]);
+ return true;
+
+ } else {
+ _icons.draw(w, srcAttrib * 2 + 10, Common::Point(
+ _buttons[srcAttrib + 5]._bounds.left,
+ _buttons[srcAttrib + 5]._bounds.top));
+ w.update();
+ _vm->_mode = MODE_SLEEPING;
+ return false;
+ }
+}
+
+int CreateCharacterDialog::exchangeAttribute(int srcAttr) {
+ EventsManager &events = *_vm->_events;
+ Windows &windows = *_vm->_windows;
+ SpriteResource icons;
+ icons.load("create2.icn");
+
+ saveButtons();
+ addButton(Common::Rect(118, 58, 142, 78), Common::KEYCODE_ESCAPE, &_icons);
+ addButton(Common::Rect(168, 19, 192, 39), Common::KEYCODE_m);
+ addButton(Common::Rect(168, 43, 192, 63), Common::KEYCODE_i);
+ addButton(Common::Rect(168, 67, 192, 87), Common::KEYCODE_p);
+ addButton(Common::Rect(168, 91, 192, 111), Common::KEYCODE_e);
+ addButton(Common::Rect(168, 115, 192, 135), Common::KEYCODE_s);
+ addButton(Common::Rect(168, 139, 192, 159), Common::KEYCODE_a);
+ addButton(Common::Rect(168, 163, 192, 183), Common::KEYCODE_l);
+
+ Window &w = windows[26];
+ w.open();
+ w.writeString(Common::String::format(Res.EXCHANGE_ATTR_WITH, Res.STAT_NAMES[srcAttr]));
+ icons.draw(w, 0, Common::Point(118, 58));
+ w.update();
+
+ int result = -1;
+ bool breakFlag = false;
+ while (!_vm->shouldExit() && !breakFlag) {
+ // Wait for an action
+ do {
+ events.pollEventsAndWait();
+ checkEvents(_vm);
+ } while (!_vm->shouldExit() && !_buttonValue);
+ if (_buttonValue == Common::KEYCODE_ESCAPE)
+ break;
+
+ int destAttr = getAttribFromKeycode(_buttonValue);
+
+ if (destAttr != -1 && srcAttr != destAttr) {
+ result = destAttr;
+ break;
+ }
+ }
+
+ w.close();
+ restoreButtons();
+ _buttonValue = 0;
+ return result;
+}
+
+bool CreateCharacterDialog::saveCharacter(Character &c, int classId, Race race, Sex sex) {
+ if (classId == -1) {
+ ErrorScroll::show(_vm, Res.SELECT_CLASS_BEFORE_SAVING);
+ return false;
+ }
+
+ Map &map = *_vm->_map;
+ Party &party = *_vm->_party;
+ Windows &windows = *_vm->_windows;
+ Window &w = windows[6];
+ Common::String name;
+ int result;
+ bool isDarkCc = _vm->_files->_isDarkCc;
+
+ // Prompt for a character name
+ w.open();
+ w.writeString(Res.NAME_FOR_NEW_CHARACTER);
+ saveButtons();
+ result = Input::show(_vm, &w, name, 10, 200);
+ restoreButtons();
+ w.close();
+
+ if (!result)
+ // Name aborted, so exit
+ return false;
+
+ // Save new character details
+ c.clear();
+ c._name = name;
+ c._savedMazeId = party._priorMazeId;
+ c._xeenSide = map._loadDarkSide;
+ c._sex = sex;
+ c._race = race;
+ c._class = (CharacterClass)classId;
+ c._level._permanent = isDarkCc ? 5 : 1;
+
+ c._might._permanent = _attribs[MIGHT];
+ c._intellect._permanent = _attribs[INTELLECT];
+ c._personality._permanent = _attribs[PERSONALITY];
+ c._endurance._permanent = _attribs[ENDURANCE];
+ c._speed._permanent = _attribs[SPEED];
+ c._accuracy._permanent = _attribs[ACCURACY];
+ c._luck._permanent = _attribs[LUCK];
+
+ c._magicResistence._permanent = Res.RACE_MAGIC_RESISTENCES[race];
+ c._fireResistence._permanent = Res.RACE_FIRE_RESISTENCES[race];
+ c._electricityResistence._permanent = Res.RACE_ELECTRIC_RESISTENCES[race];
+ c._coldResistence._permanent = Res.RACE_COLD_RESISTENCES[race];
+ c._energyResistence._permanent = Res.RACE_ENERGY_RESISTENCES[race];
+ c._poisonResistence._permanent = Res.RACE_POISON_RESISTENCES[race];
+
+ c._birthYear = party._year - 18;
+ c._birthDay = party._day;
+ c._hasSpells = false;
+ c._currentSpell = -1;
+
+ // Set up any default spells for the character's class
+ for (int idx = 0; idx < 4; ++idx) {
+ if (Res.NEW_CHARACTER_SPELLS[c._class][idx] != -1) {
+ c._hasSpells = true;
+ c._currentSpell = Res.NEW_CHARACTER_SPELLS[c._class][idx];
+ c._spells[c._currentSpell] = true;
+ }
+ }
+
+ int classSkill = Res.NEW_CHAR_SKILLS[c._class];
+ if (classSkill != -1)
+ c._skills[classSkill] = 1;
+
+ int raceSkill = Res.NEW_CHAR_RACE_SKILLS[c._race];
+ if (raceSkill != -1)
+ c._skills[raceSkill] = 1;
+
+ c._currentHp = c.getMaxHP();
+ c._currentSp = c.getMaxSP();
+ return true;
+}
+
+} // End of namespace Xeen
diff --git a/engines/xeen/dialogs/dialogs_create_char.h b/engines/xeen/dialogs/dialogs_create_char.h
new file mode 100644
index 0000000000..dc1422eb23
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_create_char.h
@@ -0,0 +1,124 @@
+/* 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_CREATE_CHAR_H
+#define XEEN_DIALOGS_CREATE_CHAR_H
+
+#include "xeen/dialogs/dialogs.h"
+#include "xeen/character.h"
+
+namespace Xeen {
+
+class CreateCharacterDialog : public ButtonContainer {
+private:
+ SpriteResource _icons;
+ SpriteResource _dice;
+ Common::Point _diceSize;
+ int _diceFrame[3];
+ Common::Point _dicePos[3];
+ Common::Point _diceInc[3];
+ uint _attribs[TOTAL_ATTRIBUTES];
+ bool _allowedClasses[TOTAL_CLASSES];
+private:
+ /**
+ * Constructor
+ */
+ CreateCharacterDialog(XeenEngine *vm);
+
+ /**
+ * Loads the buttons for the dialog
+ */
+ void loadButtons();
+
+ /**
+ * Draws on-screen icons
+ */
+ void drawIcons();
+
+ /**
+ * Draws on-screen icons
+ */
+ void drawIcons2();
+
+ /**
+ * Animate the dice rolling around
+ */
+ void drawDice();
+
+ /**
+ * Executes the dialog
+ */
+ void execute();
+
+ /**
+ * Returns the attribute that a given keycode represents
+ */
+ int getAttribFromKeycode(int keycode) const;
+
+ /**
+ * Handles the logic for swapping attributes
+ * @param keycode Key pressed representing one of the attributes
+ * @returns True if swap occurred
+ */
+ bool swapAttributes(int keycode);
+
+ /**
+ * Exchanging two attributes for the character being rolled
+ */
+ int exchangeAttribute(int srcAttr);
+
+ /**
+ * Set a list of flags for which classes the passed attribute set meet the
+ * minimum requirements of
+ */
+ void checkClass();
+
+ /**
+ * Return details of the generated character
+ */
+ int newCharDetails(Race race, Sex sex, int classId, int selectedClass, Common::String &msg);
+
+ /**
+ * Print the selection arrow to indicate the selected class
+ */
+ void printSelectionArrow(int selectedClass);
+
+ /**
+ * Saves the rolled character into the roster
+ */
+ bool saveCharacter(Character &c, int classId, Race race, Sex sex);
+
+ /**
+ * Roll up some random values for the attributes, and return both them as
+ * well as a list of classes that the attributes meet the requirements for
+ */
+ void rollAttributes();
+public:
+ /**
+ * Shows the Create Character dialog
+ */
+ static void show(XeenEngine *vm);
+};
+
+} // End of namespace Xeen
+
+#endif /* XEEN_DIALOGS_CREATE_CHAR_H */
diff --git a/engines/xeen/dialogs/dialogs_difficulty.cpp b/engines/xeen/dialogs/dialogs_difficulty.cpp
new file mode 100644
index 0000000000..0b024e1857
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_difficulty.cpp
@@ -0,0 +1,75 @@
+/* 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 "xeen/dialogs/dialogs_difficulty.h"
+#include "xeen/resources.h"
+#include "xeen/xeen.h"
+
+namespace Xeen {
+
+int DifficultyDialog::show(XeenEngine *vm) {
+ DifficultyDialog *dlg = new DifficultyDialog(vm);
+ int result = dlg->execute();
+ delete dlg;
+
+ return result;
+}
+
+DifficultyDialog::DifficultyDialog(XeenEngine *vm) : ButtonContainer(vm) {
+ loadButtons();
+}
+
+int DifficultyDialog::execute() {
+ EventsManager &events = *_vm->_events;
+ Windows &windows = *_vm->_windows;
+
+ Window &w = windows[6];
+ w.open();
+ w.writeString(Res.DIFFICULTY_TEXT);
+ drawButtons(&w);
+
+ int result = -1;
+ while (!_vm->shouldExit()) {
+ events.pollEventsAndWait();
+ checkEvents(_vm);
+
+ if (_buttonValue == Common::KEYCODE_a)
+ result = ADVENTURER;
+ else if (_buttonValue == Common::KEYCODE_w)
+ result = WARRIOR;
+ else if (_buttonValue != Common::KEYCODE_ESCAPE)
+ continue;
+
+ break;
+ }
+
+ w.close();
+ return result;
+}
+
+void DifficultyDialog::loadButtons() {
+ _sprites.load("choice.icn");
+ addButton(Common::Rect(68, 167, 158, 187), Common::KEYCODE_a, &_sprites);
+ addButton(Common::Rect(166, 167, 256, 187), Common::KEYCODE_w, &_sprites);
+}
+
+} // End of namespace Xeen
diff --git a/engines/xeen/dialogs/dialogs_difficulty.h b/engines/xeen/dialogs/dialogs_difficulty.h
new file mode 100644
index 0000000000..5ff2c93a2b
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_difficulty.h
@@ -0,0 +1,60 @@
+/* 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_DIFFICULTY_H
+#define XEEN_DIALOGS_DIFFICULTY_H
+
+#include "xeen/dialogs/dialogs.h"
+#include "xeen/party.h"
+
+namespace Xeen {
+
+class DifficultyDialog : public ButtonContainer {
+private:
+ SpriteResource _sprites;
+
+ /**
+ * Constructor
+ */
+ DifficultyDialog(XeenEngine *vm);
+
+ /**
+ * Shows the dialog
+ */
+ int execute();
+
+ /**
+ * Loads buttons for the dialog
+ */
+ void loadButtons();
+public:
+ /**
+ * Shows the difficulty selection dialog
+ * @param vm Engine reference
+ * @returns 0=Adventurer, 1=Warrior, -1 exit
+ */
+ static int show(XeenEngine *vm);
+};
+
+} // End of namespace Xeen
+
+#endif /* XEEN_DIALOGS_DIFFICULTY_H */
diff --git a/engines/xeen/dialogs/dialogs_dismiss.cpp b/engines/xeen/dialogs/dialogs_dismiss.cpp
new file mode 100644
index 0000000000..716f8f0035
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_dismiss.cpp
@@ -0,0 +1,95 @@
+/* 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 "xeen/dialogs/dialogs_dismiss.h"
+#include "xeen/party.h"
+#include "xeen/resources.h"
+#include "xeen/xeen.h"
+
+namespace Xeen {
+
+void Dismiss::show(XeenEngine *vm) {
+ Dismiss *dlg = new Dismiss(vm);
+ dlg->execute();
+ delete dlg;
+}
+
+void Dismiss::execute() {
+ EventsManager &events = *_vm->_events;
+ Interface &intf = *_vm->_interface;
+ Party &party = *_vm->_party;
+ Windows &windows = *_vm->_windows;
+ loadButtons();
+
+ Window &w = windows[31];
+ w.open();
+ _iconSprites.draw(w, 0, Common::Point(225, 120));
+ w.update();
+
+ bool breakFlag = false;
+ while (!_vm->shouldExit() && !breakFlag) {
+ do {
+ events.updateGameCounter();
+ intf.draw3d(false);
+ w.frame();
+ w.writeString("\r");
+ _iconSprites.draw(w, 0, Common::Point(225, 120));
+ windows[3].update();
+ w.update();
+
+ do {
+ events.pollEventsAndWait();
+ checkEvents(_vm);
+ } while (!_vm->shouldExit() && !_buttonValue && events.timeElapsed() == 0);
+ } while (!_vm->shouldExit() && !_buttonValue);
+
+ if (_buttonValue >= Common::KEYCODE_F1 && _buttonValue <= Common::KEYCODE_F6) {
+ _buttonValue -= Common::KEYCODE_F1;
+
+ if (_buttonValue < (int)party._activeParty.size()) {
+ if (party._activeParty.size() == 1) {
+ w.close();
+ ErrorScroll::show(_vm, Res.CANT_DISMISS_LAST_CHAR, WT_NONFREEZED_WAIT);
+ w.open();
+ } else {
+ // Remove the character from the party
+ party._activeParty.remove_at(_buttonValue);
+ breakFlag = true;
+ }
+ break;
+ }
+ } else if (_buttonValue == Common::KEYCODE_ESCAPE) {
+ breakFlag = true;
+ }
+ }
+}
+
+void Dismiss::loadButtons() {
+ _iconSprites.load("esc.icn");
+ addButton(Common::Rect(225, 120, 249, 140), Common::KEYCODE_ESCAPE, &_iconSprites);
+ addButton(Common::Rect(16, 16, 48, 48), Common::KEYCODE_1);
+ addButton(Common::Rect(117, 16, 149, 48), Common::KEYCODE_2);
+ addButton(Common::Rect(16, 59, 48, 91), Common::KEYCODE_3);
+ addButton(Common::Rect(117, 59, 149, 91), Common::KEYCODE_4);
+}
+
+} // End of namespace Xeen
diff --git a/engines/xeen/dialogs/dialogs_dismiss.h b/engines/xeen/dialogs/dialogs_dismiss.h
new file mode 100644
index 0000000000..832f779a09
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_dismiss.h
@@ -0,0 +1,46 @@
+/* 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_DISMISS_H
+#define XEEN_DIALOGS_DISMISS_H
+
+#include "xeen/dialogs/dialogs.h"
+#include "xeen/party.h"
+
+namespace Xeen {
+
+class Dismiss : public ButtonContainer {
+private:
+ SpriteResource _iconSprites;
+
+ Dismiss(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ void execute();
+
+ void loadButtons();
+public:
+ static void show(XeenEngine *vm);
+};
+
+} // End of namespace Xeen
+
+#endif /* XEEN_DIALOGS_DISMISS_H */
diff --git a/engines/xeen/dialogs/dialogs_exchange.cpp b/engines/xeen/dialogs/dialogs_exchange.cpp
new file mode 100644
index 0000000000..87dde1b0c3
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_exchange.cpp
@@ -0,0 +1,80 @@
+/* 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 "xeen/dialogs/dialogs_exchange.h"
+#include "xeen/resources.h"
+#include "xeen/xeen.h"
+
+namespace Xeen {
+
+void ExchangeDialog::show(XeenEngine *vm, Character *&c, int &charIndex) {
+ ExchangeDialog *dlg = new ExchangeDialog(vm);
+ dlg->execute(c, charIndex);
+ delete dlg;
+}
+
+void ExchangeDialog::execute(Character *&c, int &charIndex) {
+ EventsManager &events = *_vm->_events;
+ Interface &intf = *_vm->_interface;
+ Party &party = *_vm->_party;
+ Windows &windows = *_vm->_windows;
+ loadButtons();
+
+ Window &w = windows[31];
+ w.open();
+ w.writeString(Res.EXCHANGE_WITH_WHOM);
+ _iconSprites.draw(w, 0, Common::Point(225, 120));
+ w.update();
+
+ while (!_vm->shouldExit()) {
+ events.pollEventsAndWait();
+ checkEvents(_vm);
+
+ if (_buttonValue >= Common::KEYCODE_F1 && _buttonValue <= Common::KEYCODE_F6) {
+ _buttonValue -= Common::KEYCODE_F1;
+ if (_buttonValue < (int)party._activeParty.size()) {
+ SWAP(party._activeParty[charIndex], party._activeParty[_buttonValue]);
+
+ charIndex = _buttonValue;
+ c = &party._activeParty[charIndex];
+ break;
+ }
+ } else if (_buttonValue == Common::KEYCODE_ESCAPE) {
+ break;
+ }
+ }
+
+ w.close();
+ intf.drawParty(true);
+ intf.highlightChar(charIndex);
+}
+
+void ExchangeDialog::loadButtons() {
+ _iconSprites.load("esc.icn");
+ addButton(Common::Rect(225, 120, 249, 245), Common::KEYCODE_ESCAPE, &_iconSprites);
+ addButton(Common::Rect(16, 16, 48, 48), Common::KEYCODE_1);
+ addButton(Common::Rect(117, 16, 149, 48), Common::KEYCODE_2);
+ addButton(Common::Rect(16, 59, 48, 91), Common::KEYCODE_3);
+ addButton(Common::Rect(117, 59, 149, 91), Common::KEYCODE_4);
+}
+
+} // End of namespace Xeen
diff --git a/engines/xeen/dialogs/dialogs_exchange.h b/engines/xeen/dialogs/dialogs_exchange.h
new file mode 100644
index 0000000000..ff7e214787
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_exchange.h
@@ -0,0 +1,46 @@
+/* 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_EXCHANGE_H
+#define XEEN_DIALOGS_EXCHANGE_H
+
+#include "xeen/dialogs/dialogs.h"
+#include "xeen/party.h"
+
+namespace Xeen {
+
+class ExchangeDialog : public ButtonContainer {
+private:
+ SpriteResource _iconSprites;
+
+ ExchangeDialog(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ void execute(Character *&c, int &charIndex);
+
+ void loadButtons();
+public:
+ static void show(XeenEngine *vm, Character *&c, int &charIndex);
+};
+
+} // End of namespace Xeen
+
+#endif /* XEEN_DIALOGS_EXCHANGE_H */
diff --git a/engines/xeen/dialogs/dialogs_info.cpp b/engines/xeen/dialogs/dialogs_info.cpp
new file mode 100644
index 0000000000..50558534d1
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_info.cpp
@@ -0,0 +1,129 @@
+/* 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 "xeen/dialogs/dialogs_info.h"
+#include "xeen/resources.h"
+#include "xeen/xeen.h"
+
+namespace Xeen {
+
+void InfoDialog::show(XeenEngine *vm) {
+ InfoDialog *dlg = new InfoDialog(vm);
+ dlg->execute();
+ delete dlg;
+}
+
+void InfoDialog::execute() {
+ EventsManager &events = *_vm->_events;
+ Interface &intf = *_vm->_interface;
+ Party &party = *_vm->_party;
+ Windows &windows = *_vm->_windows;
+
+ protectionText();
+ Common::String statusText = "";
+ for (uint idx = 0; idx < _lines.size(); ++idx)
+ statusText += _lines[idx];
+
+ Common::String gameName;
+ if (_vm->getGameID() == GType_Swords)
+ gameName = Res.SWORDS_GAME_TEXT;
+ else if (_vm->getGameID() == GType_Clouds)
+ gameName = Res.CLOUDS_GAME_TEXT;
+ else if (_vm->getGameID() == GType_DarkSide)
+ gameName = Res.DARKSIDE_GAME_TEXT;
+ else
+ gameName = Res.WORLD_GAME_TEXT;
+
+ // Form the display message
+ int hour = party._minutes / 60;
+ Common::String details = Common::String::format(Res.GAME_INFORMATION,
+ gameName.c_str(), Res.WEEK_DAY_STRINGS[party._day % 10],
+ (hour > 12) ? hour - 12 : (!hour ? 12 : hour),
+ party._minutes % 60, (hour > 11) ? 'p' : 'a',
+ party._day, party._year, statusText.c_str());
+
+ Window &w = windows[28];
+ w.setBounds(Common::Rect(88, 20, 248, 112 + (_lines.empty() ? 0 : _lines.size() * 9 + 13)));
+ w.open();
+ w.writeString(details);
+
+ do {
+ events.updateGameCounter();
+ intf.draw3d(false, false);
+ w.frame();
+ w.writeString(details);
+ w.update();
+
+ events.wait(1);
+ } while (!_vm->shouldExit() && !events.isKeyMousePressed());
+
+ events.clearEvents();
+ w.close();
+}
+
+void InfoDialog::protectionText() {
+ Party &party = *_vm->_party;
+// Common::StringArray _lines;
+ const char *const AA_L024 = "\x3l\n\x9""024";
+ const char *const AA_R124 = "\x3r\x9""124";
+
+ if (party._lightCount) {
+ _lines.push_back(Common::String::format(Res.LIGHT_COUNT_TEXT, party._lightCount));
+ }
+
+ if (party._fireResistence) {
+ _lines.push_back(Common::String::format(Res.FIRE_RESISTENCE_TEXT,
+ _lines.size() == 0 ? 10 : 1, AA_L024, AA_R124, party._fireResistence));
+ }
+
+ if (party._electricityResistence) {
+ _lines.push_back(Common::String::format(Res.ELECRICITY_RESISTENCE_TEXT,
+ _lines.size() == 0 ? 10 : 1, AA_L024, AA_R124, party._electricityResistence));
+ }
+
+ if (party._coldResistence) {
+ _lines.push_back(Common::String::format(Res.COLD_RESISTENCE_TEXT,
+ _lines.size() == 0 ? 10 : 1, AA_L024, AA_R124, party._coldResistence));
+ }
+
+ if (party._poisonResistence) {
+ _lines.push_back(Common::String::format(Res.POISON_RESISTENCE_TEXT,
+ _lines.size() == 0 ? 10 : 1, AA_L024, AA_R124, party._poisonResistence));
+ }
+
+ if (party._clairvoyanceActive) {
+ _lines.push_back(Common::String::format(Res.CLAIRVOYANCE_TEXT,
+ _lines.size() == 0 ? 10 : 1, AA_L024, AA_R124));
+ }
+
+ if (party._levitateCount) {
+ _lines.push_back(Common::String::format(Res.LEVITATE_TEXT,
+ _lines.size() == 0 ? 10 : 1, AA_L024, AA_R124));
+ }
+
+ if (party._walkOnWaterActive) {
+ _lines.push_back(Common::String::format(Res.WALK_ON_WATER_TEXT,
+ _lines.size() == 0 ? 10 : 1, AA_L024, AA_R124));
+ }
+}
+
+} // End of namespace Xeen
diff --git a/engines/xeen/dialogs/dialogs_info.h b/engines/xeen/dialogs/dialogs_info.h
new file mode 100644
index 0000000000..fdbbc22163
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_info.h
@@ -0,0 +1,46 @@
+/* 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_INFO_H
+#define XEEN_DIALOGS_INFO_H
+
+#include "common/str-array.h"
+#include "xeen/dialogs/dialogs.h"
+
+namespace Xeen {
+
+class InfoDialog : public ButtonContainer {
+private:
+ Common::StringArray _lines;
+
+ InfoDialog(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ void execute();
+
+ void protectionText();
+public:
+ static void show(XeenEngine *vm);
+};
+
+} // End of namespace Xeen
+
+#endif /* XEEN_DIALOGS_INFO_H */
diff --git a/engines/xeen/dialogs/dialogs_input.cpp b/engines/xeen/dialogs/dialogs_input.cpp
new file mode 100644
index 0000000000..1d05c81f32
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_input.cpp
@@ -0,0 +1,323 @@
+/* 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 "xeen/dialogs/dialogs_input.h"
+#include "xeen/scripts.h"
+#include "xeen/xeen.h"
+
+namespace Xeen {
+
+int Input::show(XeenEngine *vm, Window *window, Common::String &line,
+ uint maxLen, int maxWidth, bool isNumeric) {
+ Input *dlg = new Input(vm, window);
+ int result = dlg->getString(line, maxLen, maxWidth, isNumeric);
+ delete dlg;
+
+ return result;
+}
+
+int Input::getString(Common::String &line, uint maxLen, int maxWidth, bool isNumeric) {
+ _vm->_noDirectionSense = true;
+ Common::String msg = Common::String::format("\x3""l\t000\x4%03d\x3""c", maxWidth);
+ _window->writeString(msg);
+ _window->update();
+
+ while (!_vm->shouldExit()) {
+ Common::KeyState keyState = waitForKey(msg);
+ const Common::KeyCode keyCode = keyState.keycode;
+
+ bool refresh = false;
+ if ((keyCode == Common::KEYCODE_BACKSPACE || keyCode == Common::KEYCODE_DELETE)
+ && line.size() > 0) {
+ line.deleteLastChar();
+ refresh = true;
+ } else if (line.size() < maxLen && (line.size() > 0 || keyCode != Common::KEYCODE_SPACE)
+ && ((isNumeric && keyState.ascii >= '0' && keyState.ascii <= '9') ||
+ (!isNumeric && keyState.ascii >= ' ' && keyState.ascii <= (char)127))) {
+ line += keyState.ascii;
+ refresh = true;
+ } else if (keyCode == Common::KEYCODE_RETURN || keyCode == Common::KEYCODE_KP_ENTER) {
+ break;
+ } else if (keyCode == Common::KEYCODE_ESCAPE) {
+ line = "";
+ break;
+ }
+
+ if (refresh) {
+ msg = Common::String::format("\x3""l\t000\x4%03d\x3""c%s", maxWidth, line.c_str());
+ _window->writeString(msg);
+ _window->update();
+ }
+ }
+
+ _vm->_noDirectionSense = false;
+ return line.size();
+}
+
+Common::KeyState Input::waitForKey(const Common::String &msg) {
+ EventsManager &events = *_vm->_events;
+ Interface &intf = *_vm->_interface;
+ Windows &windows = *_vm->_windows;
+
+ bool oldUpDoorText = intf._upDoorText;
+ byte oldTillMove = intf._tillMove;
+ intf._upDoorText = false;
+ intf._tillMove = 0;
+
+ bool flag = !_vm->_startupWindowActive && !windows[25]._enabled
+ && _vm->_mode != MODE_FF && _vm->_mode != MODE_17;
+
+ Common::KeyState ks;
+ while (!_vm->shouldExit()) {
+ events.updateGameCounter();
+
+ if (flag)
+ intf.draw3d(false);
+ _window->writeString(msg);
+ animateCursor();
+ _window->update();
+
+ if (flag)
+ windows[3].update();
+
+ events.wait(1);
+
+ if (events.isKeyPending()) {
+ events.getKey(ks);
+ break;
+ }
+ }
+
+ _window->writeString("");
+ _window->update();
+
+ intf._tillMove = oldTillMove;
+ intf._upDoorText = oldUpDoorText;
+
+ return ks;
+}
+
+void Input::animateCursor() {
+ // Iterate through each frame
+ _cursorAnimIndex = _cursorAnimIndex ? _cursorAnimIndex - 1 : 5;
+ static const char CURSOR_ANIMATION_IDS[] = { 32, 124, 126, 127, 126, 124 };
+
+ // Form a string for the cursor and write it out
+ Common::Point writePos = _window->_writePos;
+ _window->writeCharacter(CURSOR_ANIMATION_IDS[_cursorAnimIndex]);
+ _window->_writePos = writePos;
+}
+
+/*------------------------------------------------------------------------*/
+
+StringInput::StringInput(XeenEngine *vm): Input(vm, &(*vm->_windows)[6]) {
+}
+
+int StringInput::show(XeenEngine *vm, bool type, const Common::String &msg1,
+ const Common::String &msg2, int opcode) {
+ StringInput *dlg = new StringInput(vm);
+ int result = dlg->execute(type, msg1, msg2, opcode);
+ delete dlg;
+
+ return result;
+}
+
+int StringInput::execute(bool type, const Common::String &expected,
+ const Common::String &title, int opcode) {
+ FileManager &files = *_vm->_files;
+ Interface &intf = *_vm->_interface;
+ Scripts &scripts = *_vm->_scripts;
+ Windows &windows = *_vm->_windows;
+ Window &w = windows[6];
+ Sound &sound = *_vm->_sound;
+ int result = 0;
+
+ w.open();
+ w.writeString(Common::String::format("\r\x03""c%s\v024\t000", title.c_str()));
+ w.update();
+
+ Common::String line;
+ if (getString(line, 30, 200, false)) {
+ if (type) {
+ if (line == intf._interfaceText) {
+ result = true;
+ } else if (line == expected) {
+ result = (opcode == 55) ? -1 : 1;
+ }
+ } else {
+ // Load in the mirror list
+ MirrorEntry me;
+ scripts._mirror.clear();
+
+ File f(Common::String::format("%smirr.txt", files._isDarkCc ? "dark" : "xeen"), 1);
+ while (me.synchronize(f))
+ scripts._mirror.push_back(me);
+ f.close();
+
+ // Load in any extended mirror entries
+ Common::File f2;
+ if (f2.open(Common::String::format("%smirr.ext", files._isDarkCc ? "dark" : "xeen"))) {
+ while (me.synchronize(f2))
+ scripts._mirror.push_back(me);
+ f2.close();
+ }
+
+ for (uint idx = 0; idx < scripts._mirror.size(); ++idx) {
+ if (!line.compareToIgnoreCase(scripts._mirror[idx]._name)) {
+ result = idx + 1;
+ sound.playFX(_vm->_files->_isDarkCc ? 35 : 61);
+ break;
+ }
+ }
+ }
+ }
+
+ w.close();
+ return result;
+}
+
+/*------------------------------------------------------------------------*/
+
+NumericInput::NumericInput(XeenEngine *vm, int window) : Input(vm, &(*vm->_windows)[window]) {
+}
+
+int NumericInput::show(XeenEngine *vm, int window, int maxLength, int maxWidth) {
+ NumericInput *dlg = new NumericInput(vm, window);
+ int result = dlg->execute(maxLength, maxWidth);
+ delete dlg;
+
+ return result;
+}
+
+int NumericInput::execute(int maxLength, int maxWidth) {
+ Common::String line;
+
+ if (getString(line, maxLength, maxWidth, true))
+ return atoi(line.c_str());
+ else
+ return 0;
+}
+
+/*------------------------------------------------------------------------*/
+
+int Choose123::show(XeenEngine *vm, int numOptions) {
+ assert(numOptions <= 3);
+ Choose123 *dlg = new Choose123(vm);
+ int result = dlg->execute(numOptions);
+ delete dlg;
+
+ return result;
+}
+
+int Choose123::execute(int numOptions) {
+ EventsManager &events = *_vm->_events;
+ Interface &intf = *_vm->_interface;
+ LocationManager &loc = *_vm->_locations;
+ Windows &windows = *_vm->_windows;
+
+ Mode oldMode = _vm->_mode;
+ _vm->_mode = MODE_DIALOG_123;
+
+ loadButtons(numOptions);
+ _iconSprites.draw(0, 7, Common::Point(232, 74));
+ drawButtons(&windows[0]);
+ windows[34].update();
+
+ int result = -1;
+ while (result == -1) {
+ do {
+ events.updateGameCounter();
+ int delay;
+ if (loc.isActive()) {
+ loc.drawAnim(true);
+ delay = 3;
+ } else {
+ intf.draw3d(true);
+ delay = 1;
+ }
+
+ events.wait(delay);
+ if (_vm->shouldExit())
+ return 0;
+ } while (!_buttonValue);
+
+ switch (_buttonValue) {
+ case Common::KEYCODE_ESCAPE:
+ result = 0;
+ break;
+ case Common::KEYCODE_1:
+ case Common::KEYCODE_2:
+ case Common::KEYCODE_3: {
+ int v = _buttonValue - Common::KEYCODE_1 + 1;
+ if (v <= numOptions)
+ result = v;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ _vm->_mode = oldMode;
+ intf.mainIconsPrint();
+
+ return result;
+}
+
+void Choose123::loadButtons(int numOptions) {
+ _iconSprites.load("choose.icn");
+
+ if (numOptions >= 1)
+ addButton(Common::Rect(235, 75, 259, 95), Common::KEYCODE_1, &_iconSprites);
+ if (numOptions >= 2)
+ addButton(Common::Rect(260, 75, 284, 95), Common::KEYCODE_2, &_iconSprites);
+ if (numOptions >= 3)
+ addButton(Common::Rect(286, 75, 311, 95), Common::KEYCODE_3, &_iconSprites);
+}
+
+/*------------------------------------------------------------------------*/
+
+int HowMuch::show(XeenEngine *vm) {
+ HowMuch *dlg = new HowMuch(vm);
+ int result = dlg->execute();
+ delete dlg;
+
+ return result;
+}
+
+int HowMuch::execute() {
+ Windows &windows = *_vm->_windows;
+ Window &w = windows[6];
+ Common::String num;
+
+ w.open();
+ w.writeString(Res.HOW_MUCH);
+ w.update();
+ int lineSize = Input::show(_vm, &w, num, 8, 70, true);
+ w.close();
+
+ if (!lineSize)
+ return -1;
+ return atoi(num.c_str());
+}
+
+} // End of namespace Xeen
diff --git a/engines/xeen/dialogs/dialogs_input.h b/engines/xeen/dialogs/dialogs_input.h
new file mode 100644
index 0000000000..270495ffd5
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_input.h
@@ -0,0 +1,105 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef XEEN_DIALOGS_STRING_INPUT_H
+#define XEEN_DIALOGS_STRING_INPUT_H
+
+#include "common/keyboard.h"
+#include "xeen/dialogs/dialogs.h"
+#include "xeen/screen.h"
+
+namespace Xeen {
+
+class Input : public ButtonContainer {
+private:
+ /**
+ * Draws the text input and cursor and waits until the user presses a key
+ */
+ Common::KeyState waitForKey(const Common::String &msg);
+
+ /**
+ * Animates the box text cursor
+ */
+ void animateCursor();
+protected:
+ Window *_window;
+ int _cursorAnimIndex;
+
+ /**
+ * Allows the user to enter a string
+ */
+ int getString(Common::String &line, uint maxLen, int maxWidth, bool isNumeric);
+
+ Input(XeenEngine *vm, Window *window) : ButtonContainer(vm),
+ _window(window), _cursorAnimIndex(0) {}
+public:
+ static int show(XeenEngine *vm, Window *window, Common::String &line,
+ uint maxLen, int maxWidth, bool isNumeric = false);
+};
+
+class StringInput : public Input {
+protected:
+ StringInput(XeenEngine *vm);
+
+ int execute(bool type, const Common::String &expected,
+ const Common::String &title, int opcode);
+public:
+ static int show(XeenEngine *vm, bool type, const Common::String &msg1,
+ const Common::String &msg2, int opcode);
+};
+
+class NumericInput : public Input {
+private:
+ NumericInput(XeenEngine *vm, int window);
+
+ int execute(int maxLength, int maxWidth);
+public:
+ static int show(XeenEngine *vm, int window, int maxLength, int maxWidth);
+};
+
+class Choose123 : public ButtonContainer {
+private:
+ SpriteResource _iconSprites;
+
+ Choose123(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ int execute(int numOptions);
+
+ void loadButtons(int numOptions);
+public:
+ static int show(XeenEngine *vm, int numOptions);
+};
+
+class HowMuch : public ButtonContainer {
+private:
+ SpriteResource _iconSprites;
+
+ HowMuch(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ int execute();
+public:
+ static int show(XeenEngine *vm);
+};
+
+} // End of namespace Xeen
+
+#endif /* XEEN_DIALOGS_STRING_INPUT_H */
diff --git a/engines/xeen/dialogs/dialogs_items.cpp b/engines/xeen/dialogs/dialogs_items.cpp
new file mode 100644
index 0000000000..0ca0fd2267
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_items.cpp
@@ -0,0 +1,1066 @@
+/* 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 "xeen/dialogs/dialogs_items.h"
+#include "xeen/dialogs/dialogs_query.h"
+#include "xeen/dialogs/dialogs_quests.h"
+#include "xeen/resources.h"
+#include "xeen/xeen.h"
+
+namespace Xeen {
+
+Character *ItemsDialog::show(XeenEngine *vm, Character *c, ItemsMode mode) {
+ ItemsDialog *dlg = new ItemsDialog(vm);
+ Character *result = dlg->execute(c, mode);
+ delete dlg;
+
+ return result;
+}
+
+Character *ItemsDialog::execute(Character *c, ItemsMode mode) {
+ Combat &combat = *_vm->_combat;
+ EventsManager &events = *_vm->_events;
+ Interface &intf = *_vm->_interface;
+ Party &party = *_vm->_party;
+ Windows &windows = *_vm->_windows;
+
+ Character *startingChar = c;
+ ItemCategory category = mode == ITEMMODE_RECHARGE || mode == ITEMMODE_COMBAT ?
+ CATEGORY_MISC : CATEGORY_WEAPON;
+ int varA = mode == ITEMMODE_COMBAT ? 1 : 0;
+ if (varA != 0)
+ mode = ITEMMODE_CHAR_INFO;
+ bool updateStock = mode == ITEMMODE_BLACKSMITH;
+ int itemIndex = -1;
+ Common::StringArray lines;
+ uint arr[40];
+ int actionIndex = -1;
+
+ events.setCursor(0);
+ loadButtons(mode, c);
+
+ windows[29].open();
+ windows[30].open();
+
+ enum { REDRAW_NONE, REDRAW_TEXT, REDRAW_FULL } redrawFlag = REDRAW_FULL;
+ for (;;) {
+ if (redrawFlag == REDRAW_FULL) {
+ if ((mode != ITEMMODE_CHAR_INFO || category != CATEGORY_MISC) && mode != ITEMMODE_ENCHANT
+ && mode != ITEMMODE_RECHARGE && mode != ITEMMODE_TO_GOLD) {
+ _buttons[4]._bounds.moveTo(148, _buttons[4]._bounds.top);
+ _buttons[9]._draw = false;
+ } else if (mode == ITEMMODE_RECHARGE) {
+ _buttons[4]._value = Common::KEYCODE_r;
+ } else if (mode == ITEMMODE_ENCHANT) {
+ _buttons[4]._value = Common::KEYCODE_e;
+ } else if (mode == ITEMMODE_TO_GOLD) {
+ _buttons[4]._value = Common::KEYCODE_g;
+ } else {
+ _buttons[4]._bounds.moveTo(0, _buttons[4]._bounds.top);
+ _buttons[9]._draw = true;
+ _buttons[9]._value = Common::KEYCODE_u;
+ }
+
+ // Write text for the dialog
+ Common::String msg;
+ if (mode != ITEMMODE_CHAR_INFO && mode != ITEMMODE_8 && mode != ITEMMODE_ENCHANT
+ && mode != ITEMMODE_RECHARGE && mode != ITEMMODE_TO_GOLD) {
+ msg = Common::String::format(Res.ITEMS_DIALOG_TEXT1,
+ Res.BTN_BUY, Res.BTN_SELL, Res.BTN_IDENTIFY, Res.BTN_FIX);
+ } else if (mode != ITEMMODE_ENCHANT && mode != ITEMMODE_RECHARGE && mode != ITEMMODE_TO_GOLD) {
+ msg = Common::String::format(Res.ITEMS_DIALOG_TEXT1,
+ category == 3 ? Res.BTN_USE : Res.BTN_EQUIP,
+ Res.BTN_REMOVE, Res.BTN_DISCARD, Res.BTN_QUEST);
+ } else if (mode == ITEMMODE_ENCHANT) {
+ msg = Common::String::format(Res.ITEMS_DIALOG_TEXT2, Res.BTN_ENCHANT);
+ } else if (mode == ITEMMODE_RECHARGE) {
+ msg = Common::String::format(Res.ITEMS_DIALOG_TEXT2, Res.BTN_RECHARGE);
+ } else {
+ msg = Common::String::format(Res.ITEMS_DIALOG_TEXT2, Res.BTN_GOLD);
+ }
+
+ windows[29].writeString(msg);
+ drawButtons(&windows[0]);
+
+ Common::fill(&arr[0], &arr[40], 0);
+ itemIndex = -1;
+ }
+
+ if (redrawFlag == REDRAW_TEXT || redrawFlag == REDRAW_FULL) {
+ lines.clear();
+
+ if (mode == ITEMMODE_CHAR_INFO || category != 3) {
+ _iconSprites.draw(0, 8, Common::Point(148, 109));
+ }
+ if (mode != ITEMMODE_ENCHANT && mode != ITEMMODE_RECHARGE && mode != ITEMMODE_TO_GOLD) {
+ _iconSprites.draw(0, 10, Common::Point(182, 109));
+ _iconSprites.draw(0, 12, Common::Point(216, 109));
+ _iconSprites.draw(0, 14, Common::Point(250, 109));
+ }
+
+ switch (mode) {
+ case ITEMMODE_CHAR_INFO:
+ _iconSprites.draw(0, 9, Common::Point(148, 109));
+ break;
+ case ITEMMODE_BLACKSMITH:
+ _iconSprites.draw(0, 11, Common::Point(182, 109));
+ break;
+ case ITEMMODE_REPAIR:
+ _iconSprites.draw(0, 15, Common::Point(250, 109));
+ break;
+ case ITEMMODE_IDENTIFY:
+ _iconSprites.draw(0, 13, Common::Point(216, 109));
+ break;
+ default:
+ break;
+ }
+
+ for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) {
+ DrawStruct &ds = _itemsDrawList[idx];
+ XeenItem &i = c->_items[category][idx];
+
+ ds._sprites = nullptr;
+ ds._x = 8;
+ ds._y = 18 + idx * 9;
+
+ switch (category) {
+ case CATEGORY_WEAPON:
+ case CATEGORY_ARMOR:
+ case CATEGORY_ACCESSORY:
+ if (i._id) {
+ if (mode == ITEMMODE_CHAR_INFO || mode == ITEMMODE_8
+ || mode == ITEMMODE_ENCHANT || mode == ITEMMODE_RECHARGE) {
+ lines.push_back(Common::String::format(Res.ITEMS_DIALOG_LINE1,
+ arr[idx], idx + 1,
+ c->_items[category].getFullDescription(idx, arr[idx]).c_str()));
+ } else {
+ lines.push_back(Common::String::format(Res.ITEMS_DIALOG_LINE2,
+ arr[idx], idx + 1,
+ c->_items[category].getFullDescription(idx, arr[idx]).c_str(),
+ calcItemCost(c, idx, mode,
+ mode == ITEMMODE_TO_GOLD ? 1 : startingChar->_skills[MERCHANT],
+ category)
+ ));
+ }
+
+ ds._sprites = &_equipSprites;
+ if (c->_weapons.passRestrictions(i._id, true))
+ ds._frame = i._frame;
+ else
+ ds._frame = 14;
+ } else if (ds._sprites == nullptr && idx == 0) {
+ lines.push_back(Res.NO_ITEMS_AVAILABLE);
+ }
+ break;
+
+ case CATEGORY_MISC:
+ if (i._material == 0) {
+ // No item
+ if (idx == 0) {
+ lines.push_back(Res.NO_ITEMS_AVAILABLE);
+ }
+ } else {
+ ItemsMode tempMode = mode;
+ int skill = startingChar->_skills[MERCHANT];
+
+ if (mode == ITEMMODE_CHAR_INFO || mode == ITEMMODE_8
+ || mode == ITEMMODE_ENCHANT || mode == ITEMMODE_RECHARGE) {
+ tempMode = ITEMMODE_ENCHANT;
+ } else if (mode == ITEMMODE_TO_GOLD) {
+ skill = 1;
+ }
+
+ lines.push_back(Common::String::format(Res.ITEMS_DIALOG_LINE2,
+ arr[idx], idx + 1,
+ c->_items[category].getFullDescription(idx, arr[idx]).c_str(),
+ calcItemCost(c, idx, tempMode, skill, category)
+ ));
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ while (lines.size() < INV_ITEMS_TOTAL)
+ lines.push_back("");
+
+ // Draw out overall text and the list of items
+ switch (mode) {
+ case ITEMMODE_CHAR_INFO:
+ case ITEMMODE_8:
+ windows[30].writeString(Common::String::format(Res.X_FOR_THE_Y,
+ category == CATEGORY_MISC ? "\x3l" : "\x3c",
+ Res.CATEGORY_NAMES[category], c->_name.c_str(), Res.CLASS_NAMES[c->_class],
+ category == CATEGORY_MISC ? Res.FMT_CHARGES : " ",
+ lines[0].c_str(), lines[1].c_str(), lines[2].c_str(), lines[3].c_str(),
+ lines[4].c_str(), lines[5].c_str(), lines[6].c_str(), lines[7].c_str(),
+ lines[8].c_str()
+ ));
+ break;
+
+ case ITEMMODE_BLACKSMITH:
+ windows[30].writeString(Common::String::format(Res.AVAILABLE_GOLD_COST,
+ Res.CATEGORY_NAMES[category], party._gold,
+ lines[0].c_str(), lines[1].c_str(), lines[2].c_str(), lines[3].c_str(),
+ lines[4].c_str(), lines[5].c_str(), lines[6].c_str(), lines[7].c_str(),
+ lines[8].c_str()
+ ));
+ break;
+
+ case ITEMMODE_2:
+ case ITEMMODE_RECHARGE:
+ case ITEMMODE_ENCHANT:
+ case ITEMMODE_REPAIR:
+ case ITEMMODE_IDENTIFY:
+ case ITEMMODE_TO_GOLD:
+ windows[30].writeString(Common::String::format(Res.X_FOR_Y,
+ Res.CATEGORY_NAMES[category], startingChar->_name.c_str(),
+ (mode == ITEMMODE_RECHARGE || mode == ITEMMODE_ENCHANT) ? Res.CHARGES : Res.COST,
+ lines[0].c_str(), lines[1].c_str(), lines[2].c_str(), lines[3].c_str(),
+ lines[4].c_str(), lines[5].c_str(), lines[6].c_str(), lines[7].c_str(),
+ lines[8].c_str()
+ ));
+ break;
+
+ case ITEMMODE_3:
+ case ITEMMODE_5:
+ windows[30].writeString(Common::String::format(Res.X_FOR_Y_GOLD,
+ Res.CATEGORY_NAMES[category], c->_name.c_str(), party._gold, Res.CHARGES,
+ lines[0].c_str(), lines[1].c_str(), lines[2].c_str(), lines[3].c_str(),
+ lines[4].c_str(), lines[5].c_str(), lines[6].c_str(), lines[7].c_str(),
+ lines[8].c_str()
+ ));
+ break;
+
+ default:
+ break;
+ }
+
+ // Draw the glyphs for the items
+ windows[0].drawList(_itemsDrawList, INV_ITEMS_TOTAL);
+ windows[0].update();
+ }
+
+ redrawFlag = REDRAW_NONE;
+
+ if (itemIndex != -1) {
+ switch (mode) {
+ case ITEMMODE_BLACKSMITH:
+ actionIndex = 0;
+ break;
+ case ITEMMODE_2:
+ actionIndex = 1;
+ break;
+ case ITEMMODE_REPAIR:
+ actionIndex = 3;
+ break;
+ case ITEMMODE_IDENTIFY:
+ actionIndex = 2;
+ break;
+ default:
+ break;
+ }
+ }
+
+ // If it's time to do an item action, take care of it
+ if (actionIndex >= 0) {
+ int result = doItemOptions(*c, actionIndex, itemIndex, category, mode);
+ if (result == 1) {
+ // Finish dialog with no selected character
+ c = nullptr;
+ break;
+ } else if (result == 2) {
+ // Close dialogs and finish dialog with original starting character
+ windows[30].close();
+ windows[29].close();
+ c = startingChar;
+ break;
+ }
+
+ // Otherwise, result and continue showing dialog
+ actionIndex = -1;
+ }
+
+ // Wait for a selection
+ _buttonValue = 0;
+ while (!_vm->shouldExit() && !_buttonValue) {
+ events.pollEventsAndWait();
+ checkEvents(_vm);
+ }
+ if (_vm->shouldExit())
+ return nullptr;
+
+ // Handle escaping out of dialog
+ if (_buttonValue == Common::KEYCODE_ESCAPE) {
+ if (mode == ITEMMODE_8)
+ continue;
+ c = startingChar;
+ break;
+ }
+
+ // Handle other selections
+ switch (_buttonValue) {
+ case Common::KEYCODE_F1:
+ case Common::KEYCODE_F2:
+ case Common::KEYCODE_F3:
+ case Common::KEYCODE_F4:
+ case Common::KEYCODE_F5:
+ case Common::KEYCODE_F6:
+ if (!varA && mode != ITEMMODE_3 && mode != ITEMMODE_ENCHANT
+ && mode != ITEMMODE_RECHARGE && mode != ITEMMODE_TO_GOLD
+ && party._mazeId != 0) {
+ _buttonValue -= Common::KEYCODE_F1;
+
+ if (_buttonValue < (int)(_vm->_mode == MODE_COMBAT ?
+ combat._combatParty.size() : party._activeParty.size())) {
+ // Character number is valid
+ redrawFlag = REDRAW_TEXT;
+ Character *newChar = _vm->_mode == MODE_COMBAT ?
+ combat._combatParty[_buttonValue] : &party._activeParty[_buttonValue];
+
+ if (mode == ITEMMODE_BLACKSMITH) {
+ _oldCharacter = newChar;
+ startingChar = newChar;
+ c = &_itemsCharacter;
+ } else if (mode != ITEMMODE_2 && mode != ITEMMODE_REPAIR
+ && mode != ITEMMODE_IDENTIFY && itemIndex != -1) {
+ InventoryItems &destItems = newChar->_items[category];
+ XeenItem &destItem = destItems[INV_ITEMS_TOTAL - 1];
+ InventoryItems &srcItems = c->_items[category];
+ XeenItem &srcItem = srcItems[itemIndex];
+
+ if (srcItem._bonusFlags & ITEMFLAG_CURSED)
+ ErrorScroll::show(_vm, Res.CANNOT_REMOVE_CURSED_ITEM);
+ else if (destItems[INV_ITEMS_TOTAL - 1]._id)
+ ErrorScroll::show(_vm, Common::String::format(
+ Res.CATEGORY_BACKPACK_IS_FULL[category], c->_name.c_str()));
+ else {
+ destItem = srcItem;
+ srcItem.clear();
+ destItem._frame = 0;
+
+ srcItems.sort();
+ destItems.sort();
+ }
+
+ continue;
+ }
+
+ c = newChar;
+ startingChar = newChar;
+ intf.highlightChar(_buttonValue);
+ }
+ }
+ break;
+
+ case Common::KEYCODE_1:
+ case Common::KEYCODE_2:
+ case Common::KEYCODE_3:
+ case Common::KEYCODE_4:
+ case Common::KEYCODE_5:
+ case Common::KEYCODE_6:
+ case Common::KEYCODE_7:
+ case Common::KEYCODE_8:
+ case Common::KEYCODE_9:
+ // Select an item
+ if (mode == ITEMMODE_3)
+ break;
+
+ _buttonValue -= Common::KEYCODE_1;
+ if (_buttonValue != itemIndex) {
+ // Check whether the new selection has an associated item
+ if (!c->_items[category][_buttonValue].empty()) {
+ itemIndex = _buttonValue;
+ Common::fill(&arr[0], &arr[40], 0);
+ arr[itemIndex] = 15;
+ }
+
+ redrawFlag = REDRAW_TEXT;
+ }
+ break;
+
+ case Common::KEYCODE_a:
+ // Armor category
+ category = CATEGORY_ARMOR;
+ redrawFlag = REDRAW_FULL;
+ break;
+
+ case Common::KEYCODE_b:
+ // Buy
+ if (mode != ITEMMODE_CHAR_INFO && mode != ITEMMODE_RECHARGE &&
+ mode != ITEMMODE_ENCHANT && mode != ITEMMODE_TO_GOLD) {
+ mode = ITEMMODE_BLACKSMITH;
+ c = &_itemsCharacter;
+ redrawFlag = REDRAW_FULL;
+ }
+ break;
+
+ case Common::KEYCODE_c:
+ // Accessories category
+ category = CATEGORY_ACCESSORY;
+ redrawFlag = REDRAW_FULL;
+ break;
+
+ case Common::KEYCODE_d:
+ if (mode == ITEMMODE_CHAR_INFO)
+ actionIndex = 3;
+ break;
+
+ case Common::KEYCODE_e:
+ if (mode == ITEMMODE_CHAR_INFO || mode == ITEMMODE_ENCHANT ||
+ mode == ITEMMODE_TO_GOLD) {
+ if (category != CATEGORY_MISC) {
+ actionIndex = mode == ITEMMODE_ENCHANT ? 4 : 0;
+ }
+ }
+ break;
+
+ case Common::KEYCODE_f:
+ if (mode != ITEMMODE_CHAR_INFO && mode != ITEMMODE_RECHARGE &&
+ mode != ITEMMODE_ENCHANT && mode != ITEMMODE_TO_GOLD) {
+ mode = ITEMMODE_REPAIR;
+ c = startingChar;
+ redrawFlag = REDRAW_TEXT;
+ }
+ break;
+
+ case Common::KEYCODE_g:
+ if (mode == ITEMMODE_TO_GOLD)
+ actionIndex = 6;
+ break;
+
+ case Common::KEYCODE_i:
+ if (mode != ITEMMODE_CHAR_INFO && mode != ITEMMODE_RECHARGE &&
+ mode != ITEMMODE_ENCHANT && mode != ITEMMODE_TO_GOLD) {
+ mode = ITEMMODE_IDENTIFY;
+ c = startingChar;
+ redrawFlag = REDRAW_TEXT;
+ }
+ break;
+
+ case Common::KEYCODE_m:
+ // Misc
+ category = CATEGORY_MISC;
+ redrawFlag = REDRAW_TEXT;
+ break;
+
+ case Common::KEYCODE_q:
+ // Quests
+ if (mode == ITEMMODE_CHAR_INFO) {
+ Quests::show(_vm);
+ redrawFlag = REDRAW_FULL;
+ }
+ break;
+
+ case Common::KEYCODE_r:
+ if (mode == ITEMMODE_CHAR_INFO)
+ actionIndex = 1;
+ else if (mode == ITEMMODE_RECHARGE)
+ actionIndex = 5;
+ break;
+
+ case Common::KEYCODE_s:
+ if (mode != ITEMMODE_CHAR_INFO && mode != ITEMMODE_RECHARGE &&
+ mode != ITEMMODE_ENCHANT && mode != ITEMMODE_TO_GOLD) {
+ mode = ITEMMODE_2;
+ c = startingChar;
+ redrawFlag = REDRAW_TEXT;
+ }
+ break;
+
+ case Common::KEYCODE_u:
+ if (mode == ITEMMODE_CHAR_INFO && category == CATEGORY_MISC)
+ actionIndex = 2;
+ break;
+
+ case Common::KEYCODE_w:
+ // Weapons category
+ category = CATEGORY_WEAPON;
+ redrawFlag = REDRAW_TEXT;
+ break;
+ }
+ }
+
+ windows[30].close();
+ windows[29].close();
+
+ intf.drawParty(true);
+ if (updateStock)
+ charData2BlackData();
+
+ return c;
+}
+
+void ItemsDialog::loadButtons(ItemsMode mode, Character *&c) {
+ _iconSprites.load(Common::String::format("%s.icn",
+ (mode == ITEMMODE_CHAR_INFO) ? "items" : "buy"));
+ _equipSprites.load("equip.icn");
+
+ if (mode == ITEMMODE_ENCHANT || mode == ITEMMODE_RECHARGE || mode == ITEMMODE_TO_GOLD) {
+ // Enchant button list
+ addButton(Common::Rect(12, 109, 36, 129), Common::KEYCODE_w, &_iconSprites);
+ addButton(Common::Rect(46, 109, 70, 129), Common::KEYCODE_a, &_iconSprites);
+ addButton(Common::Rect(80, 109, 104, 129), Common::KEYCODE_c, &_iconSprites);
+ addButton(Common::Rect(114, 109, 138, 129), Common::KEYCODE_m, &_iconSprites);
+ addButton(Common::Rect(148, 109, 172, 129), Common::KEYCODE_e, &_iconSprites);
+ addButton(Common::Rect(284, 109, 308, 129), Common::KEYCODE_ESCAPE, &_iconSprites);
+ addButton(Common::Rect(148, 109, 172, 129), Common::KEYCODE_u, &_iconSprites);
+ addButton(Common::Rect(8, 20, 263, 28), Common::KEYCODE_1);
+ addButton(Common::Rect(8, 29, 263, 37), Common::KEYCODE_2);
+ addButton(Common::Rect(8, 38, 263, 46), Common::KEYCODE_3);
+ addButton(Common::Rect(8, 47, 263, 55), Common::KEYCODE_4);
+ addButton(Common::Rect(8, 56, 263, 64), Common::KEYCODE_5);
+ addButton(Common::Rect(8, 65, 263, 73), Common::KEYCODE_6);
+ addButton(Common::Rect(8, 74, 263, 82), Common::KEYCODE_7);
+ addButton(Common::Rect(8, 83, 263, 91), Common::KEYCODE_8);
+ addButton(Common::Rect(8, 92, 263, 100), Common::KEYCODE_9);
+ } else {
+ addButton(Common::Rect(12, 109, 36, 129), Common::KEYCODE_w, &_iconSprites);
+ addButton(Common::Rect(46, 109, 70, 129), Common::KEYCODE_a, &_iconSprites);
+ addButton(Common::Rect(80, 109, 104, 129), Common::KEYCODE_c, &_iconSprites);
+ addButton(Common::Rect(114, 109, 138, 129), Common::KEYCODE_m, &_iconSprites);
+ addButton(Common::Rect(148, 109, 172, 129), Common::KEYCODE_e, &_iconSprites);
+ addButton(Common::Rect(182, 109, 206, 129), Common::KEYCODE_r, &_iconSprites);
+ addButton(Common::Rect(216, 109, 240, 129), Common::KEYCODE_d, &_iconSprites);
+ addButton(Common::Rect(250, 109, 274, 129), Common::KEYCODE_q, &_iconSprites);
+ addButton(Common::Rect(284, 109, 308, 129), Common::KEYCODE_ESCAPE, &_iconSprites);
+ addButton(Common::Rect(8, 20, 263, 28), Common::KEYCODE_1);
+ addButton(Common::Rect(8, 29, 263, 37), Common::KEYCODE_2);
+ addButton(Common::Rect(8, 38, 263, 46), Common::KEYCODE_3);
+ addButton(Common::Rect(8, 47, 263, 55), Common::KEYCODE_4);
+ addButton(Common::Rect(8, 56, 263, 64), Common::KEYCODE_5);
+ addButton(Common::Rect(8, 65, 263, 73), Common::KEYCODE_6);
+ addButton(Common::Rect(8, 74, 263, 82), Common::KEYCODE_7);
+ addButton(Common::Rect(8, 83, 263, 91), Common::KEYCODE_8);
+ addButton(Common::Rect(8, 92, 263, 100), Common::KEYCODE_9);
+ addPartyButtons(_vm);
+ }
+
+ if (mode == ITEMMODE_BLACKSMITH) {
+ _oldCharacter = c;
+ c = &_itemsCharacter;
+ blackData2CharData();
+
+ _buttons[4]._value = Common::KEYCODE_b;
+ _buttons[5]._value = Common::KEYCODE_s;
+ _buttons[6]._value = Common::KEYCODE_i;
+ _buttons[7]._value = Common::KEYCODE_f;
+
+ setEquipmentIcons();
+ } else {
+ _buttons[4]._value = Common::KEYCODE_e;
+ _buttons[5]._value = Common::KEYCODE_r;
+ _buttons[6]._value = Common::KEYCODE_d;
+ _buttons[7]._value = Common::KEYCODE_q;
+ }
+}
+
+void ItemsDialog::blackData2CharData() {
+ Party &party = *_vm->_party;
+ bool isDarkCc = _vm->_files->_isDarkCc;
+ int slotIndex = 0;
+ while (slotIndex < 4 && party._mazeId != (int)Res.BLACKSMITH_MAP_IDS[isDarkCc][slotIndex])
+ ++slotIndex;
+ if (slotIndex == 4)
+ slotIndex = 0;
+
+ for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) {
+ _itemsCharacter._weapons[idx] = party._blacksmithWeapons[isDarkCc][idx];
+ _itemsCharacter._armor[idx] = party._blacksmithArmor[isDarkCc][idx];
+ _itemsCharacter._accessories[idx] = party._blacksmithAccessories[isDarkCc][idx];
+ _itemsCharacter._misc[idx] = party._blacksmithMisc[isDarkCc][idx];
+ }
+}
+
+void ItemsDialog::charData2BlackData() {
+ Party &party = *_vm->_party;
+ bool isDarkCc = _vm->_files->_isDarkCc;
+ int slotIndex = 0;
+ while (slotIndex < 4 && party._mazeId != (int)Res.BLACKSMITH_MAP_IDS[isDarkCc][slotIndex])
+ ++slotIndex;
+ if (slotIndex == 4)
+ slotIndex = 0;
+
+ for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) {
+ party._blacksmithWeapons[isDarkCc][idx] = _itemsCharacter._weapons[idx];
+ party._blacksmithArmor[isDarkCc][idx] = _itemsCharacter._armor[idx];
+ party._blacksmithAccessories[isDarkCc][idx] = _itemsCharacter._accessories[idx];
+ party._blacksmithMisc[isDarkCc][idx] = _itemsCharacter._misc[idx];
+ }
+}
+
+void ItemsDialog::setEquipmentIcons() {
+ for (int typeIndex = 0; typeIndex < 4; ++typeIndex) {
+ for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) {
+ switch (typeIndex) {
+ case CATEGORY_WEAPON: {
+ XeenItem &i = _itemsCharacter._weapons[idx];
+ if (i._id <= 17)
+ i._frame = 1;
+ else if (i._id <= 29 || i._id > 33)
+ i._frame = 13;
+ else
+ i._frame = 4;
+ break;
+ }
+
+ case CATEGORY_ARMOR: {
+ XeenItem &i = _itemsCharacter._armor[idx];
+ if (i._id <= 7)
+ i._frame = 3;
+ else if (i._id == 9)
+ i._frame = 5;
+ else if (i._id == 10)
+ i._frame = 9;
+ else if (i._id <= 12)
+ i._frame = 10;
+ else
+ i._frame = 6;
+ break;
+ }
+
+ case CATEGORY_ACCESSORY: {
+ XeenItem &i = _itemsCharacter._accessories[idx];
+ if (i._id == 1)
+ i._id = 8;
+ else if (i._id == 2)
+ i._frame = 12;
+ else if (i._id <= 7)
+ i._frame = 7;
+ else
+ i._frame = 11;
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+ }
+}
+
+int ItemsDialog::calcItemCost(Character *c, int itemIndex, ItemsMode mode,
+ int skillLevel, ItemCategory category) {
+ int amount1 = 0, amount2 = 0, amount3 = 0, amount4 = 0;
+ int result = 0;
+ int level = skillLevel & 0x7f;
+
+ InventoryItems *invGroups[4] = {
+ &c->_weapons, &c->_armor, &c->_accessories, &c->_misc
+ };
+ const int *BASE_COSTS[4] = {
+ Res.WEAPON_BASE_COSTS, Res.ARMOR_BASE_COSTS, Res.ACCESSORY_BASE_COSTS, Res.MISC_BASE_COSTS
+ };
+
+ switch (mode) {
+ case ITEMMODE_BLACKSMITH:
+ level = 0;
+ break;
+ case ITEMMODE_2:
+ case ITEMMODE_TO_GOLD:
+ level = level == 0 ? 1 : 0;
+ break;
+ case ITEMMODE_IDENTIFY:
+ level = 2;
+ break;
+ case ITEMMODE_REPAIR:
+ level = 3;
+ break;
+ default:
+ break;
+ }
+
+ switch (category) {
+ case CATEGORY_WEAPON:
+ case CATEGORY_ARMOR:
+ case CATEGORY_ACCESSORY: {
+ XeenItem &i = (*invGroups[category])[itemIndex];
+ amount1 = (BASE_COSTS[category])[i._id];
+
+ if (i._material > 36 && i._material < 59) {
+ switch (i._material) {
+ case 37:
+ amount1 /= 10;
+ break;
+ case 38:
+ amount1 /= 4;
+ break;
+ case 39:
+ amount1 /= 2;
+ break;
+ case 40:
+ amount1 /= 4;
+ break;
+ default:
+ amount1 *= Res.METAL_BASE_MULTIPLIERS[i._material - 37];
+ break;
+ }
+ }
+
+ if (i._material < 37)
+ amount2 = Res.ELEMENTAL_DAMAGE[i._material] * 100;
+ else if (i._material > 58)
+ amount3 = Res.ELEMENTAL_DAMAGE[i._material - 59 + 7] * 100;
+
+ switch (mode) {
+ case ITEMMODE_BLACKSMITH:
+ case ITEMMODE_2:
+ case ITEMMODE_REPAIR:
+ case ITEMMODE_IDENTIFY:
+ case ITEMMODE_TO_GOLD:
+ result = (amount1 + amount2 + amount3 + amount4) / Res.ITEM_SKILL_DIVISORS[level];
+ if (!result)
+ result = 1;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ case CATEGORY_MISC: {
+ // Misc
+ XeenItem &i = c->_misc[itemIndex];
+ amount1 = Res.MISC_MATERIAL_COSTS[i._material];
+ amount4 = Res.MISC_BASE_COSTS[i._id];
+
+ switch (mode) {
+ case ITEMMODE_BLACKSMITH:
+ case ITEMMODE_2:
+ case ITEMMODE_REPAIR:
+ case ITEMMODE_IDENTIFY:
+ case ITEMMODE_TO_GOLD:
+ result = (amount1 + amount2 + amount3 + amount4) / Res.ITEM_SKILL_DIVISORS[level];
+ if (!result)
+ result = 1;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return (mode == ITEMMODE_CHAR_INFO) ? 0 : result;
+}
+
+int ItemsDialog::doItemOptions(Character &c, int actionIndex, int itemIndex, ItemCategory category,
+ ItemsMode mode) {
+ Combat &combat = *_vm->_combat;
+ EventsManager &events = *_vm->_events;
+ Interface &intf = *_vm->_interface;
+ Party &party = *_vm->_party;
+ Sound &sound = *_vm->_sound;
+ Spells &spells = *_vm->_spells;
+ Windows &windows = *_vm->_windows;
+ bool isDarkCc = _vm->_files->_isDarkCc;
+
+ XeenItem *itemCategories[4] = { &c._weapons[0], &c._armor[0], &c._accessories[0], &c._misc[0] };
+ XeenItem *items = itemCategories[category];
+ if (!items[0]._id)
+ // Inventory is empty
+ return category == CATEGORY_MISC ? 0 : 2;
+
+ Window &w = windows[11];
+ SpriteResource escSprites;
+ if (itemIndex < 0 || itemIndex > 8) {
+ saveButtons();
+
+ escSprites.load("esc.icn");
+ addButton(Common::Rect(235, 111, 259, 131), Common::KEYCODE_ESCAPE, &escSprites);
+ addButton(Common::Rect(8, 20, 263, 28), Common::KEYCODE_1);
+ addButton(Common::Rect(8, 29, 263, 37), Common::KEYCODE_2);
+ addButton(Common::Rect(8, 38, 263, 46), Common::KEYCODE_3);
+ addButton(Common::Rect(8, 47, 263, 55), Common::KEYCODE_4);
+ addButton(Common::Rect(8, 56, 263, 64), Common::KEYCODE_5);
+ addButton(Common::Rect(8, 65, 263, 73), Common::KEYCODE_6);
+ addButton(Common::Rect(8, 74, 263, 82), Common::KEYCODE_7);
+ addButton(Common::Rect(8, 83, 263, 91), Common::KEYCODE_8);
+ addButton(Common::Rect(8, 92, 263, 100), Common::KEYCODE_9);
+
+ w.open();
+ w.writeString(Common::String::format(Res.WHICH_ITEM, Res.ITEM_ACTIONS[actionIndex]));
+ _iconSprites.draw(0, 0, Common::Point(235, 111));
+ w.update();
+
+ while (!_vm->shouldExit()) {
+ while (!_buttonValue) {
+ events.pollEventsAndWait();
+ checkEvents(_vm);
+ if (_vm->shouldExit())
+ return false;
+ }
+
+ if (_buttonValue == Common::KEYCODE_ESCAPE) {
+ itemIndex = -1;
+ break;
+ } else if (_buttonValue >= Common::KEYCODE_1 && _buttonValue <= Common::KEYCODE_9) {
+ // Check whether there's an item at the selected index
+ int selectedIndex = _buttonValue - Common::KEYCODE_1;
+ if (!items[selectedIndex]._id)
+ continue;
+
+ itemIndex = selectedIndex;
+ break;
+ }
+ }
+
+ w.close();
+ restoreButtons();
+ }
+
+ if (itemIndex != -1) {
+ XeenItem &item = c._items[category][itemIndex];
+
+ switch (mode) {
+ case ITEMMODE_CHAR_INFO:
+ case ITEMMODE_8:
+ switch (actionIndex) {
+ case 0:
+ c._items[category].equipItem(itemIndex);
+ break;
+ case 1:
+ c._items[category].removeItem(itemIndex);
+ break;
+ case 2:
+ if (!party._mazeId) {
+ ErrorScroll::show(_vm, Res.WHATS_YOUR_HURRY);
+ } else {
+ XeenItem &i = c._misc[itemIndex];
+
+ Condition condition = c.worstCondition();
+ switch (condition) {
+ case ASLEEP:
+ case PARALYZED:
+ case UNCONSCIOUS:
+ case DEAD:
+ case STONED:
+ case ERADICATED:
+ ErrorScroll::show(_vm, Common::String::format(Res.IN_NO_CONDITION, c._name.c_str()));
+ break;
+ default:
+ if (combat._itemFlag) {
+ ErrorScroll::show(_vm, Res.USE_ITEM_IN_COMBAT);
+ } else if (i._id && (i._bonusFlags & ITEMFLAG_BONUS_MASK)
+ && !(i._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED))) {
+ int charges = (i._bonusFlags & ITEMFLAG_BONUS_MASK) - 1;
+ i._bonusFlags = charges;
+ _oldCharacter = &c;
+
+ windows[30].close();
+ windows[29].close();
+ windows[24].close();
+ spells.castItemSpell(i._id);
+
+ if (!charges) {
+ // Ran out of charges, so make item disappear
+ c._items[category][itemIndex].clear();
+ c._items[category].sort();
+ }
+ } else {
+ ErrorScroll::show(_vm, Common::String::format(Res.NO_SPECIAL_ABILITIES,
+ c._items[category].getFullDescription(itemIndex).c_str()
+ ));
+ }
+ }
+ }
+ break;
+ case 3:
+ c._items[category].discardItem(itemIndex);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case ITEMMODE_BLACKSMITH: {
+ InventoryItems &invItems = _oldCharacter->_items[category];
+ if (invItems[INV_ITEMS_TOTAL - 1]._id) {
+ // If the last slot is in use, it means the list is full
+ ErrorScroll::show(_vm, Common::String::format(Res.BACKPACK_IS_FULL,
+ _oldCharacter->_name.c_str()));
+ } else {
+ int cost = calcItemCost(_oldCharacter, itemIndex, mode, 0, category);
+ Common::String desc = c._items[category].getFullDescription(itemIndex);
+ if (Confirm::show(_vm, Common::String::format(Res.BUY_X_FOR_Y_GOLD,
+ desc.c_str(), cost))) {
+ if (party.subtract(CONS_GOLD, cost, WHERE_PARTY, WT_FREEZE_WAIT)) {
+ if (isDarkCc) {
+ sound.stopSound();
+ sound.playSound("choice2.voc");
+ }
+
+ // Add entry to the end of the list
+ _oldCharacter->_items[category][8] = c._items[category][itemIndex];
+ _oldCharacter->_items[category][8]._frame = 0;
+ c._items[category].clear();
+ c._items[category].sort();
+ _oldCharacter->_items[category].sort();
+ }
+ }
+ }
+ return 0;
+ }
+
+ case ITEMMODE_2: {
+ bool noNeed;
+ switch (category) {
+ case CATEGORY_WEAPON:
+ noNeed = (item._bonusFlags & ITEMFLAG_CURSED) || item._id == 34;
+ break;
+ default:
+ noNeed = item._bonusFlags & ITEMFLAG_CURSED;
+ break;
+ }
+
+ if (noNeed) {
+ ErrorScroll::show(_vm, Common::String::format(Res.NO_NEED_OF_THIS,
+ c._items[category].getFullDescription(itemIndex).c_str()));
+ } else {
+ int cost = calcItemCost(&c, itemIndex, mode, c._skills[MERCHANT], category);
+ Common::String desc = c._items[category].getFullDescription(itemIndex);
+ Common::String msg = Common::String::format(Res.SELL_X_FOR_Y_GOLD,
+ desc.c_str(), cost);
+
+ if (Confirm::show(_vm, msg)) {
+ // Remove the sold item and add gold to the party's total
+ item.clear();
+ c._items[category].sort();
+
+ party._gold += cost;
+ }
+ }
+ return 0;
+ }
+
+ case ITEMMODE_RECHARGE:
+ if (category != CATEGORY_MISC || c._misc[itemIndex]._material > 9
+ || c._misc[itemIndex]._id == 53 || c._misc[itemIndex]._id == 0) {
+ sound.playFX(21);
+ ErrorScroll::show(_vm, Common::String::format(Res.NOT_RECHARGABLE, Res.SPELL_FAILED));
+ } else {
+ int charges = MIN(63, _vm->getRandomNumber(1, 6) +
+ (c._misc[itemIndex]._bonusFlags & ITEMFLAG_BONUS_MASK));
+ sound.playFX(20);
+
+ c._misc[itemIndex]._bonusFlags = (c._misc[itemIndex]._bonusFlags
+ & ~ITEMFLAG_BONUS_MASK) | charges;
+ }
+ return 2;
+
+ case ITEMMODE_ENCHANT: {
+ int amount = _vm->getRandomNumber(1, _oldCharacter->getCurrentLevel() / 5 + 1);
+ amount = MIN(amount, 5);
+ _oldCharacter->_items[category].enchantItem(itemIndex, amount);
+ break;
+ }
+
+ case ITEMMODE_REPAIR:
+ if (!(item._bonusFlags & ITEMFLAG_BROKEN)) {
+ ErrorScroll::show(_vm, Res.ITEM_NOT_BROKEN);
+ } else {
+ int cost = calcItemCost(&c, itemIndex, mode, actionIndex, category);
+ Common::String msg = Common::String::format(Res.FIX_IDENTIFY_GOLD,
+ Res.FIX_IDENTIFY[0],
+ c._items[category].getFullDescription(itemIndex).c_str(),
+ cost);
+
+ if (Confirm::show(_vm, msg) && party.subtract(CONS_GOLD, cost, WHERE_PARTY)) {
+ item._bonusFlags &= ~ITEMFLAG_BROKEN;
+ }
+ }
+ break;
+
+ case ITEMMODE_IDENTIFY: {
+ int cost = calcItemCost(&c, itemIndex, mode, actionIndex, category);
+ Common::String msg = Common::String::format(Res.FIX_IDENTIFY_GOLD,
+ Res.FIX_IDENTIFY[1],
+ c._items[category].getFullDescription(itemIndex).c_str(),
+ cost);
+
+ if (Confirm::show(_vm, msg) && party.subtract(CONS_GOLD, cost, WHERE_PARTY)) {
+ Common::String details = c._items[category].getIdentifiedDetails(itemIndex);
+ Common::String desc = c._items[category].getFullDescription(itemIndex);
+ Common::String str = Common::String::format(Res.IDENTIFY_ITEM_MSG,
+ desc.c_str(), details.c_str());
+
+ Window &win = windows[14];
+ win.open();
+ win.writeString(str);
+ win.update();
+
+ saveButtons();
+ clearButtons();
+
+ while (!_vm->shouldExit() && !events.isKeyMousePressed())
+ events.pollEventsAndWait();
+ events.clearEvents();
+
+ restoreButtons();
+ win.close();
+ }
+ break;
+ }
+
+ case ITEMMODE_TO_GOLD:
+ // Convert item in inventory to gold
+ itemToGold(c, itemIndex, category, mode);
+ return 2;
+
+ default:
+ break;
+ }
+ }
+
+ intf._charsShooting = false;
+ combat.moveMonsters();
+ combat._whosTurn = -1;
+ return true;
+}
+
+void ItemsDialog::itemToGold(Character &c, int itemIndex, ItemCategory category,
+ ItemsMode mode) {
+ XeenItem &item = c._items[category][itemIndex];
+ Party &party = *_vm->_party;
+ Sound &sound = *_vm->_sound;
+
+ if (category == CATEGORY_WEAPON && item._id == 34) {
+ sound.playFX(21);
+ ErrorScroll::show(_vm, Common::String::format("\v012\t000\x03""c%s",
+ Res.SPELL_FAILED));
+ } else if (item._id != 0) {
+ // There is a valid item present
+ // Calculate cost of item and add it to the party's total
+ int cost = calcItemCost(&c, itemIndex, mode, 1, category);
+ party._gold += cost;
+
+ // Remove the item from the inventory
+ item.clear();
+ c._items[category].sort();
+ }
+}
+
+} // End of namespace Xeen
diff --git a/engines/xeen/dialogs/dialogs_items.h b/engines/xeen/dialogs/dialogs_items.h
new file mode 100644
index 0000000000..b9af06eb74
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_items.h
@@ -0,0 +1,89 @@
+/* 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_ITEMS_H
+#define XEEN_DIALOGS_ITEMS_H
+
+#include "xeen/dialogs/dialogs.h"
+#include "xeen/party.h"
+#include "xeen/window.h"
+
+namespace Xeen {
+
+enum ItemsMode {
+ ITEMMODE_CHAR_INFO = 0, ITEMMODE_BLACKSMITH = 1, ITEMMODE_2 = 2, ITEMMODE_3 = 3,
+ ITEMMODE_RECHARGE = 4, ITEMMODE_5 = 5, ITEMMODE_ENCHANT = 6, ITEMMODE_COMBAT = 7, ITEMMODE_8 = 8,
+ ITEMMODE_REPAIR = 9, ITEMMODE_IDENTIFY = 10, ITEMMODE_TO_GOLD = 11
+};
+
+class ItemsDialog : public ButtonContainer {
+private:
+ SpriteResource _iconSprites;
+ SpriteResource _equipSprites;
+ Character _itemsCharacter;
+ Character *_oldCharacter;
+ DrawStruct _itemsDrawList[INV_ITEMS_TOTAL];
+
+ ItemsDialog(XeenEngine *vm) : ButtonContainer(vm), _oldCharacter(nullptr) {}
+
+ Character *execute(Character *c, ItemsMode mode);
+
+ /**
+ * Load the buttons for the dialog
+ */
+ void loadButtons(ItemsMode mode, Character *&c);
+
+ /**
+ * Loads the temporary _itemsCharacter character with the item set
+ * the given blacksmith has available, so the user can "view" the
+ * set as if it were a standard character's inventory
+ */
+ void blackData2CharData();
+
+ /**
+ * Saves the inventory from the temporary _itemsCharacter character back into the
+ * blacksmith storage, so changes in blacksmith inventory remain persistent
+ */
+ void charData2BlackData();
+
+ /**
+ * Sets the equipment icon to use for each item for display
+ */
+ void setEquipmentIcons();
+
+ /**
+ * Calculate the cost of an item
+ */
+ int calcItemCost(Character *c, int itemIndex, ItemsMode mode, int skillLevel,
+ ItemCategory category);
+
+ int doItemOptions(Character &c, int actionIndex, int itemIndex,
+ ItemCategory category, ItemsMode mode);
+
+ void itemToGold(Character &c, int itemIndex, ItemCategory category, ItemsMode mode);
+public:
+ static Character *show(XeenEngine *vm, Character *c, ItemsMode mode);
+};
+
+} // End of namespace Xeen
+
+#endif /* XEEN_DIALOGS_ITEMS_H */
diff --git a/engines/xeen/dialogs/dialogs_map.cpp b/engines/xeen/dialogs/dialogs_map.cpp
new file mode 100644
index 0000000000..b822c71472
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_map.cpp
@@ -0,0 +1,461 @@
+/* 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 "xeen/dialogs/dialogs_map.h"
+#include "xeen/resources.h"
+#include "xeen/xeen.h"
+
+namespace Xeen {
+
+#define MAP_SIZE 16
+#define MAP_DIFF (MAP_SIZE / 2)
+#define MAP_XSTART 80
+#define MAP_YSTART 38
+#define TILE_WIDTH 10
+#define TILE_HEIGHT 8
+
+void MapDialog::show(XeenEngine *vm) {
+ MapDialog *dlg = new MapDialog(vm);
+ dlg->execute();
+ delete dlg;
+}
+
+void MapDialog::execute() {
+ EventsManager &events = *_vm->_events;
+ Interface &intf = *_vm->_interface;
+ Map &map = *_vm->_map;
+ Party &party = *_vm->_party;
+ Windows &windows = *_vm->_windows;
+
+ _pt = party._mazePosition;
+ _globalSprites.load("global.icn");
+
+ if (_pt.x < 8 && map.mazeData()._surroundingMazes._west == 0) {
+ _arrowPt.x = _pt.x * 10 + 4;
+ _pt.x = 7;
+ } else if (_pt.x > 23) {
+ _arrowPt.x = (byte)(_pt.x * 10 + 100);
+ _pt.x = 23;
+ } else if (_pt.x > 8 && map.mazeData()._surroundingMazes._east == 0) {
+ _arrowPt.x = (byte)(_pt.x * 10 + 4);
+ _pt.x = 7;
+ } else {
+ _arrowPt.x = 74;
+ }
+
+ if (_pt.y < 8 && map.mazeData()._surroundingMazes._south == 0) {
+ _arrowPt.y = ((15 - _pt.y) << 3) + 13;
+ _pt.y = 8;
+ } else if (_pt.y > 24) {
+ _arrowPt.y = ((15 - (_pt.y - 16)) << 3) + 13;
+ _pt.y = 24;
+ } else if (_pt.y >= 8 && map.mazeData()._surroundingMazes._north == 0) {
+ _arrowPt.y = ((15 - _pt.y) << 3) + 13;
+ _pt.y = 8;
+ } else {
+ _arrowPt.y = 69;
+ }
+
+ windows[5].open();
+ bool drawFlag = true;
+
+ events.updateGameCounter();
+ do {
+ if (drawFlag)
+ intf.draw3d(false, false);
+ windows[5].writeString("\r");
+
+ if (map._isOutdoors)
+ drawOutdoors();
+ else
+ drawIndoors();
+
+ windows[5].frame();
+ if (!map._isOutdoors) {
+ map._tileSprites.draw(0, 52, Common::Point(76, 30));
+ } else if (_frameEndFlag) {
+ _globalSprites.draw(0, party._mazeDirection + 1,
+ Common::Point(_arrowPt.x + 76, _arrowPt.y + 25));
+ }
+
+ if (events.timeElapsed() > 5) {
+ // Set the flag to make the basic arrow blinking effect
+ _frameEndFlag = !_frameEndFlag;
+ events.updateGameCounter();
+ }
+
+ windows[5].writeString(Common::String::format(Res.MAP_TEXT,
+ map._mazeName.c_str(), party._mazePosition.x,
+ party._mazePosition.y, Res.DIRECTION_TEXT[party._mazeDirection]));
+ windows[5].update();
+ windows[3].update();
+
+ events.ipause5(2);
+ drawFlag = false;
+ } while (!_vm->shouldExit() && !events.isKeyMousePressed());
+
+ events.clearEvents();
+ windows[5].close();
+}
+
+void MapDialog::drawOutdoors() {
+ Map &map = *g_vm->_map;
+ int v, frame;
+
+ // Draw outdoors map
+ for (int yp = MAP_YSTART, mazeY = _pt.y + MAP_DIFF - 1; mazeY >= (_pt.y - MAP_DIFF);
+ --mazeY, yp += TILE_HEIGHT) {
+ for (int xp = MAP_XSTART, mazeX = _pt.x - (MAP_DIFF - 1); mazeX <= (_pt.x + MAP_DIFF);
+ xp += TILE_WIDTH, ++mazeX) {
+ v = map.mazeLookup(Common::Point(mazeX, mazeY), 0);
+ assert(v != INVALID_CELL);
+ frame = map.mazeDataCurrent()._surfaceTypes[v];
+
+ if (map._currentSteppedOn) {
+ map._tileSprites.draw(0, frame, Common::Point(xp, yp));
+ }
+ }
+ }
+
+ for (int yp = MAP_YSTART, mazeY = _pt.y + MAP_DIFF - 1; mazeY >= (_pt.y - MAP_DIFF);
+ --mazeY, yp += TILE_HEIGHT) {
+ for (int xp = MAP_XSTART, mazeX = _pt.x - (MAP_DIFF - 1); mazeX <= (_pt.x + MAP_DIFF);
+ xp += TILE_WIDTH, ++mazeX) {
+ v = map.mazeLookup(Common::Point(mazeX, mazeY), 4);
+ assert(v != INVALID_CELL);
+ frame = map.mazeDataCurrent()._wallTypes[v];
+
+ if (frame && map._currentSteppedOn)
+ map._tileSprites.draw(0, frame + 16, Common::Point(xp, yp));
+ }
+ }
+
+ for (int yp = MAP_YSTART, mazeY = _pt.y + MAP_DIFF - 1; mazeY >= (_pt.y - MAP_DIFF);
+ --mazeY, yp += TILE_HEIGHT) {
+ for (int xp = MAP_XSTART, mazeX = _pt.x - (MAP_DIFF - 1); mazeX <= (_pt.x + MAP_DIFF);
+ xp += TILE_WIDTH, ++mazeX) {
+ frame = map.mazeLookup(Common::Point(mazeX, mazeY), 8, 0xff);
+
+ if (frame && map._currentSteppedOn)
+ map._tileSprites.draw(0, frame + 32, Common::Point(xp, yp));
+ }
+ }
+}
+
+void MapDialog::drawIndoors() {
+ Map &map = *g_vm->_map;
+ Party &party = *g_vm->_party;
+ int v, frame;
+ int frame2 = _animFrame;
+ _animFrame = (_animFrame + 2) % 8;
+
+ // Draw indoors map
+ frame2 = (frame2 + 2) % 8;
+
+ // Draw default ground for all the valid explored areas
+ for (int yp = MAP_YSTART, mazeY = _pt.y + MAP_DIFF - 1; mazeY >= (_pt.y - MAP_DIFF);
+ yp += TILE_HEIGHT, --mazeY) {
+ for (int xp = MAP_XSTART, mazeX = _pt.x - (MAP_DIFF - 1); mazeX <= (_pt.x + MAP_DIFF);
+ xp += TILE_WIDTH, ++mazeX) {
+ v = map.mazeLookup(Common::Point(mazeX, mazeY), 0, 0xffff);
+
+ if (v != INVALID_CELL && map._currentSteppedOn)
+ map._tileSprites.draw(0, 0, Common::Point(xp, yp));
+ }
+ }
+
+ // Draw thinner ground tiles on the left edge of the map
+ for (int yp = MAP_YSTART + 5, mazeY = _pt.y + MAP_DIFF - 1; mazeY >= (_pt.y - MAP_DIFF);
+ yp += TILE_HEIGHT, --mazeY) {
+ v = map.mazeLookup(Common::Point(_pt.x - 8, mazeY), 0, 0xffff);
+
+ if (v != INVALID_CELL && map._currentSurfaceId != 0 && map._currentSteppedOn)
+ map._tileSprites.draw(0, 36 + map.mazeData()._surfaceTypes[
+ map._currentSurfaceId], Common::Point(75, yp));
+ }
+
+ // Draw thin tile portion on top-left corner of map
+ v = map.mazeLookup(Common::Point(_pt.x - 8, _pt.y + 8), 0, 0xffff);
+ if (v != INVALID_CELL && map._currentSurfaceId != 0 && map._currentSteppedOn)
+ map._tileSprites.draw(0, 36 + map.mazeData()._surfaceTypes[
+ map._currentSurfaceId], Common::Point(75, 35));
+
+ // Draw any thin tiles at the very top of the map
+ for (int xp = MAP_XSTART + 5, mazeX = _pt.x - (MAP_DIFF - 1); mazeX <= (_pt.x + MAP_DIFF);
+ xp += TILE_WIDTH, ++mazeX) {
+ v = map.mazeLookup(Common::Point(mazeX, _pt.y + 8), 0, 0xffff);
+
+ if (v != INVALID_CELL && map._currentSurfaceId != 0 && map._currentSteppedOn)
+ map._tileSprites.draw(0, 36 + map.mazeData()._surfaceTypes[
+ map._currentSurfaceId], Common::Point(xp, 35));
+ }
+
+ // Draw the default ground tiles
+ for (int yp = MAP_YSTART + 5, mazeY = _pt.y + MAP_DIFF - 1; mazeY >= (_pt.y - MAP_DIFF);
+ yp += TILE_HEIGHT, --mazeY) {
+ for (int xp = MAP_XSTART + 5, mazeX = _pt.x - (MAP_DIFF - 1); mazeX <= (_pt.x + MAP_DIFF);
+ xp += TILE_WIDTH, ++mazeX) {
+ v = map.mazeLookup(Common::Point(mazeX, mazeY), 0, 0xffff);
+
+ if (v != INVALID_CELL && map._currentSurfaceId && map._currentSteppedOn)
+ map._tileSprites.draw(0, 36 + map.mazeData()._surfaceTypes[
+ map._currentSurfaceId], Common::Point(xp, yp));
+ }
+ }
+
+ // Draw walls on left and top edges of map
+ for (int xp = MAP_XSTART, yp = MAP_YSTART + (MAP_SIZE - 1) * TILE_HEIGHT,
+ mazeX = _pt.x - (MAP_DIFF - 1), mazeY = _pt.y - MAP_DIFF;
+ mazeX < (_pt.x + MAP_DIFF); xp += TILE_WIDTH, yp -= TILE_HEIGHT, ++mazeX, ++mazeY) {
+ // Draw walls on left edge of map
+ v = map.mazeLookup(Common::Point(_pt.x - 8, mazeY), 12);
+
+ switch (v) {
+ case SURFTYPE_DIRT:
+ frame = 18;
+ break;
+ case SURFTYPE_SNOW:
+ frame = 22;
+ break;
+ case SURFTYPE_SWAMP:
+ case SURFTYPE_CLOUD:
+ frame = 16;
+ break;
+ case SURFTYPE_LAVA:
+ case SURFTYPE_DWATER:
+ frame = 2;
+ break;
+ case SURFTYPE_DESERT:
+ frame = 30;
+ break;
+ case SURFTYPE_ROAD:
+ frame = 32;
+ break;
+ case SURFTYPE_TFLR:
+ frame = 20;
+ break;
+ case SURFTYPE_SKY:
+ frame = 28;
+ break;
+ case SURFTYPE_CROAD:
+ frame = 14;
+ break;
+ case SURFTYPE_SEWER:
+ frame = frame2 + 4;
+ break;
+ case SURFTYPE_SCORCH:
+ frame = 24;
+ break;
+ case SURFTYPE_SPACE:
+ frame = 26;
+ break;
+ default:
+ frame = -1;
+ break;
+ }
+
+ if (frame != -1 && map._currentSteppedOn)
+ map._tileSprites.draw(0, frame, Common::Point(70, yp));
+
+ // Draw walls on top edge of map
+ v = map.mazeLookup(Common::Point(mazeX, _pt.y + 8), 0);
+
+ switch (v) {
+ case SURFTYPE_DIRT:
+ frame = 19;
+ break;
+ case SURFTYPE_GRASS:
+ frame = 35;
+ break;
+ case SURFTYPE_SNOW:
+ frame = 23;
+ break;
+ case SURFTYPE_SWAMP:
+ case SURFTYPE_CLOUD:
+ frame = 17;
+ break;
+ case SURFTYPE_LAVA:
+ case SURFTYPE_DWATER:
+ frame = 3;
+ break;
+ case SURFTYPE_DESERT:
+ frame = 31;
+ break;
+ case SURFTYPE_ROAD:
+ frame = 33;
+ break;
+ case SURFTYPE_TFLR:
+ frame = 21;
+ break;
+ case SURFTYPE_SKY:
+ frame = 29;
+ break;
+ case SURFTYPE_CROAD:
+ frame = 15;
+ break;
+ case SURFTYPE_SEWER:
+ frame = frame2 + 5;
+ break;
+ case SURFTYPE_SCORCH:
+ frame = 25;
+ break;
+ case SURFTYPE_SPACE:
+ frame = 27;
+ break;
+ default:
+ frame = -1;
+ break;
+ }
+
+ if (frame != -1 && map._currentSteppedOn)
+ map._tileSprites.draw(0, frame, Common::Point(xp, 30));
+ }
+
+ // Draw the walls for the remaining cells of the minimap
+ for (int yp = MAP_YSTART, mazeY = _pt.y + MAP_DIFF - 1, yCtr = 0; yCtr < MAP_SIZE;
+ yp += TILE_HEIGHT, --mazeY, ++yCtr) {
+ for (int xp = MAP_XSTART, mazeX = _pt.x - (MAP_DIFF - 1), xCtr = 0; xCtr < MAP_SIZE;
+ xp += TILE_WIDTH, ++mazeX, ++xCtr) {
+ // Draw the arrow if at the correct position
+ if ((_arrowPt.x / 10) == xCtr && (14 - (_arrowPt.y / 10)) == yCtr && _frameEndFlag) {
+ _globalSprites.draw(0, party._mazeDirection + 1,
+ Common::Point(_arrowPt.x + 81, _arrowPt.y + 29));
+ }
+
+ v = map.mazeLookup(Common::Point(mazeX, mazeY), 12);
+ switch (v) {
+ case 1:
+ frame = 18;
+ break;
+ case 2:
+ frame = 34;
+ break;
+ case 3:
+ frame = 22;
+ break;
+ case 4:
+ case 13:
+ frame = 16;
+ break;
+ case 5:
+ case 8:
+ frame = 2;
+ break;
+ case 6:
+ frame = 30;
+ break;
+ case 7:
+ frame = 32;
+ break;
+ case 9:
+ frame = 20;
+ break;
+ case 10:
+ frame = 28;
+ break;
+ case 11:
+ frame = 14;
+ break;
+ case 12:
+ frame = frame2 + 4;
+ break;
+ case 14:
+ frame = 24;
+ break;
+ case 15:
+ frame = 26;
+ break;
+ default:
+ frame = -1;
+ break;
+ }
+
+ if (frame != -1 && map._currentSteppedOn)
+ map._tileSprites.draw(0, frame, Common::Point(xp, yp));
+
+ v = map.mazeLookup(Common::Point(mazeX, mazeY), 0);
+ switch (v) {
+ case 1:
+ frame = 19;
+ break;
+ case 2:
+ frame = 35;
+ break;
+ case 3:
+ frame = 23;
+ break;
+ case 4:
+ case 13:
+ frame = 17;
+ break;
+ case 5:
+ case 8:
+ frame = 3;
+ break;
+ case 6:
+ frame = 31;
+ break;
+ case 7:
+ frame = 33;
+ break;
+ case 9:
+ frame = 21;
+ break;
+ case 10:
+ frame = 29;
+ break;
+ case 11:
+ frame = 15;
+ break;
+ case 12:
+ frame = frame2 + 5;
+ break;
+ case 14:
+ frame = 25;
+ break;
+ case 15:
+ frame = 27;
+ break;
+ default:
+ frame = -1;
+ break;
+ }
+
+ if (frame != -1 && map._currentSteppedOn)
+ map._tileSprites.draw(0, frame, Common::Point(xp, yp));
+ }
+ }
+
+ // Draw overlay on cells that haven't been stepped on yet
+ for (int yp = MAP_YSTART, mazeY = _pt.y + MAP_DIFF - 1; mazeY >= (_pt.y - MAP_DIFF);
+ yp += TILE_HEIGHT, --mazeY) {
+ for (int xp = MAP_XSTART, mazeX = _pt.x - (MAP_DIFF - 1); mazeX <= (_pt.x + MAP_DIFF);
+ xp += TILE_WIDTH, ++mazeX) {
+ v = map.mazeLookup(Common::Point(mazeX, mazeY), 0, 0xffff);
+
+ if (v == INVALID_CELL || !map._currentSteppedOn)
+ map._tileSprites.draw(0, 1, Common::Point(xp, yp));
+ }
+ }
+}
+
+} // End of namespace Xeen
diff --git a/engines/xeen/dialogs/dialogs_map.h b/engines/xeen/dialogs/dialogs_map.h
new file mode 100644
index 0000000000..5e22e5268d
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_map.h
@@ -0,0 +1,62 @@
+/* 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_MAP_H
+#define XEEN_DIALOGS_MAP_H
+
+#include "xeen/dialogs/dialogs.h"
+
+namespace Xeen {
+
+class XeenEngine;
+
+class MapDialog: public ButtonContainer {
+private:
+ int _animFrame;
+ SpriteResource _globalSprites;
+ Common::Point _pt, _arrowPt;
+ bool _frameEndFlag;
+private:
+ MapDialog(XeenEngine *vm) : ButtonContainer(vm),
+ _animFrame(0), _frameEndFlag(false) {}
+
+ /**
+ * Draws the map contents when outdoors
+ */
+ void drawOutdoors();
+
+ /**
+ * Draws the map contents when indoors
+ */
+ void drawIndoors();
+
+ /**
+ * Handles the display of the dialog
+ */
+ void execute();
+public:
+ static void show(XeenEngine *vm);
+};
+
+} // End of namespace Xeen
+
+#endif /* XEEN_DIALOGS_AUTOMAP_H */
diff --git a/engines/xeen/dialogs/dialogs_message.cpp b/engines/xeen/dialogs/dialogs_message.cpp
new file mode 100644
index 0000000000..df8afea34c
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_message.cpp
@@ -0,0 +1,123 @@
+/* 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/dialogs_message.h"
+#include "xeen/events.h"
+#include "xeen/xeen.h"
+
+namespace Xeen {
+
+void MessageDialog::show(XeenEngine *vm, const Common::String &msg, MessageWaitType waitType) {
+ MessageDialog *dlg = new MessageDialog(vm);
+ dlg->execute(msg, waitType);
+ delete dlg;
+}
+
+void MessageDialog::execute(const Common::String &msg, MessageWaitType waitType) {
+ EventsManager &events = *_vm->_events;
+ Windows &windows = *_vm->_windows;
+ Window &w = windows[6];
+
+ w.open();
+ w.writeString(msg);
+ w.update();
+
+ switch (waitType) {
+ case WT_FREEZE_WAIT:
+ while (!_vm->shouldExit() && !events.isKeyMousePressed())
+ events.pollEventsAndWait();
+
+ events.clearEvents();
+ break;
+
+ case WT_ANIMATED_WAIT:
+ if (windows[11]._enabled || _vm->_mode == MODE_17) {
+ g_vm->_locations->wait();
+ break;
+ }
+ // fall through
+
+ case WT_NONFREEZED_WAIT:
+ do {
+ events.updateGameCounter();
+ _vm->_interface->draw3d(true);
+
+ events.wait(1);
+ if (checkEvents(_vm))
+ break;
+ } while (!_vm->shouldExit() && !_buttonValue);
+ break;
+
+ case WT_LOC_WAIT:
+ g_vm->_locations->wait();
+ break;
+
+ default:
+ break;
+ }
+
+ w.close();
+}
+
+/*------------------------------------------------------------------------*/
+
+void ErrorScroll::show(XeenEngine *vm, const Common::String &msg, MessageWaitType waitType) {
+ Common::String s = Common::String::format("\x3""c\v010\t000%s", msg.c_str());
+ MessageDialog::show(vm, s, waitType);
+}
+
+/*------------------------------------------------------------------------*/
+
+void CantCast::show(XeenEngine *vm, int spellId, int componentNum) {
+ CantCast *dlg = new CantCast(vm);
+ dlg->execute(spellId, componentNum);
+ delete dlg;
+}
+
+void CantCast::execute(int spellId, int componentNum) {
+ EventsManager &events = *_vm->_events;
+ Sound &sound = *_vm->_sound;
+ Spells &spells = *_vm->_spells;
+ Windows &windows = *_vm->_windows;
+ Window &w = windows[6];
+ Mode oldMode = _vm->_mode;
+ _vm->_mode = MODE_FF;
+
+ sound.playFX(21);
+ w.open();
+ w.writeString(Common::String::format(Res.NOT_ENOUGH_TO_CAST,
+ Res.SPELL_CAST_COMPONENTS[componentNum - 1],
+ spells._spellNames[spellId].c_str()
+ ));
+ w.update();
+
+ do {
+ events.pollEventsAndWait();
+ } while (!_vm->shouldExit() && !events.isKeyMousePressed());
+ events.clearEvents();
+
+ w.close();
+ _vm->_mode = oldMode;
+}
+
+} // End of namespace Xeen
diff --git a/engines/xeen/dialogs/dialogs_message.h b/engines/xeen/dialogs/dialogs_message.h
new file mode 100644
index 0000000000..95d942858c
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_message.h
@@ -0,0 +1,61 @@
+/* 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_message_H
+#define XEEN_dialogs_message_H
+
+#include "xeen/dialogs/dialogs.h"
+#include "xeen/character.h"
+
+namespace Xeen {
+
+enum MessageWaitType { WT_FREEZE_WAIT = 0, WT_NONFREEZED_WAIT = 1,
+ WT_LOC_WAIT = 2, WT_ANIMATED_WAIT = 3 };
+
+class MessageDialog : public ButtonContainer {
+private:
+ MessageDialog(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ void execute(const Common::String &msg, MessageWaitType waitType);
+public:
+ static void show(XeenEngine *vm, const Common::String &msg,
+ MessageWaitType waitType = WT_FREEZE_WAIT);
+};
+
+class ErrorScroll {
+public:
+ static void show(XeenEngine *vm, const Common::String &msg,
+ MessageWaitType waitType = WT_FREEZE_WAIT);
+};
+
+class CantCast: public ButtonContainer {
+private:
+ CantCast(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ void execute(int spellId, int componentNum);
+public:
+ static void show(XeenEngine *vm, int spellId, int componentNum);
+};
+
+} // End of namespace Xeen
+
+#endif /* XEEN_dialogs_message_H */
diff --git a/engines/xeen/dialogs/dialogs_party.cpp b/engines/xeen/dialogs/dialogs_party.cpp
new file mode 100644
index 0000000000..33e138b6ac
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_party.cpp
@@ -0,0 +1,451 @@
+/* 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 "xeen/dialogs/dialogs_char_info.h"
+#include "xeen/dialogs/dialogs_create_char.h"
+#include "xeen/dialogs/dialogs_party.h"
+#include "xeen/dialogs/dialogs_input.h"
+#include "xeen/dialogs/dialogs_query.h"
+#include "xeen/character.h"
+#include "xeen/events.h"
+#include "xeen/party.h"
+#include "xeen/xeen.h"
+
+namespace Xeen {
+
+PartyDialog::PartyDialog(XeenEngine *vm) : ButtonContainer(vm),
+ PartyDrawer(vm), _vm(vm) {
+ initDrawStructs();
+}
+
+void PartyDialog::show(XeenEngine *vm) {
+ PartyDialog *dlg = new PartyDialog(vm);
+ dlg->execute();
+ delete dlg;
+}
+
+void PartyDialog::execute() {
+ EventsManager &events = *_vm->_events;
+ Interface &intf = *_vm->_interface;
+ Map &map = *_vm->_map;
+ Party &party = *_vm->_party;
+ Screen &screen = *_vm->_screen;
+ Sound &sound = *_vm->_sound;
+ Windows &windows = *_vm->_windows;
+ bool modeFlag = false;
+ int startingChar = 0;
+
+ loadButtons();
+ setupBackground();
+
+ while (!_vm->shouldExit()) {
+ _vm->_mode = MODE_1;
+
+ // Build up a list of available characters in the Roster that are on the
+ // same side of Xeen as the player is currently on
+ _charList.clear();
+ for (int i = 0; i < XEEN_TOTAL_CHARACTERS; ++i) {
+ Character &player = party._roster[i];
+ if (player._name.empty() || player._xeenSide != (map._loadDarkSide ? 1 : 0))
+ continue;
+
+ _charList.push_back(i);
+ }
+
+ Window &w = windows[11];
+ w.open();
+ setupFaces(startingChar, false);
+ w.writeString(Common::String::format(Res.PARTY_DIALOG_TEXT, _partyDetails.c_str()));
+ 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));
+ if (g_vm->getGameID() == GType_Swords)
+ Res._logoSprites.draw(1, 0, Common::Point(232, 9));
+
+ screen.loadPalette("mm4.pal");
+
+ if (modeFlag) {
+ windows[0].update();
+ events.setCursor(0);
+ screen.fadeIn();
+ } else {
+ if (_vm->getGameID() == GType_DarkSide) {
+ screen.fadeOut();
+ windows[0].update();
+ }
+
+ doScroll(false, false);
+ events.setCursor(0);
+
+ if (_vm->getGameID() == GType_DarkSide) {
+ screen.fadeIn();
+ }
+ }
+
+ bool breakFlag = false;
+ while (!_vm->shouldExit() && !breakFlag) {
+ do {
+ events.pollEventsAndWait();
+ checkEvents(_vm);
+ } while (!_vm->shouldExit() && !_buttonValue);
+
+ switch (_buttonValue) {
+ case Common::KEYCODE_ESCAPE:
+ case Common::KEYCODE_SPACE:
+ case Common::KEYCODE_e:
+ case Common::KEYCODE_x:
+ if (party._activeParty.size() == 0) {
+ ErrorScroll::show(_vm, Res.NO_ONE_TO_ADVENTURE_WITH);
+ } else {
+ if (_vm->_mode != MODE_0) {
+ for (int idx = OBSCURITY_NONE; idx >= OBSCURITY_BLACK; --idx) {
+ events.updateGameCounter();
+ intf.obscureScene((Obscurity)idx);
+ w.update();
+
+ while (events.timeElapsed() < 1)
+ events.pollEventsAndWait();
+ }
+ }
+
+ w.close();
+ party._mazeId = party._priorMazeId;
+
+ party.copyPartyToRoster();
+ //_vm->_saves->writeCharFile();
+ return;
+ }
+ break;
+
+ case Common::KEYCODE_F1:
+ case Common::KEYCODE_F2:
+ case Common::KEYCODE_F3:
+ case Common::KEYCODE_F4:
+ case Common::KEYCODE_F5:
+ case Common::KEYCODE_F6:
+ // Show character info
+ _buttonValue -= Common::KEYCODE_F1;
+ if (_buttonValue < (int)party._activeParty.size())
+ CharacterInfo::show(_vm, _buttonValue);
+ break;
+
+ case Common::KEYCODE_1:
+ case Common::KEYCODE_2:
+ case Common::KEYCODE_3:
+ case Common::KEYCODE_4:
+ _buttonValue -= Common::KEYCODE_1 - 7;
+ if ((_buttonValue - 7 + startingChar) < (int)_charList.size()) {
+ // Check if the selected character is already in the party
+ uint idx = 0;
+ for (; idx < party._activeParty.size(); ++idx) {
+ if (_charList[_buttonValue - 7 + startingChar] ==
+ party._activeParty[idx]._rosterId)
+ break;
+ }
+
+ // Only add the character if they're not already in the party
+ if (idx == party._activeParty.size()) {
+ if (party._activeParty.size() == MAX_ACTIVE_PARTY) {
+ sound.playFX(21);
+ ErrorScroll::show(_vm, Res.YOUR_PARTY_IS_FULL);
+ } else {
+ // Add the character to the active party
+ party._activeParty.push_back(party._roster[
+ _charList[_buttonValue - 7 + startingChar]]);
+ startingCharChanged(startingChar);
+ }
+ }
+ }
+ break;
+
+ case Common::KEYCODE_UP:
+ case Common::KEYCODE_KP8:
+ // Up arrow
+ if (startingChar > 0) {
+ startingChar -= 4;
+ startingCharChanged(startingChar);
+ }
+ break;
+
+ case Common::KEYCODE_DOWN:
+ case Common::KEYCODE_KP2:
+ // Down arrow
+ if (startingChar < ((int)_charList.size() - 4)) {
+ startingChar += 4;
+ startingCharChanged(startingChar);
+ }
+ break;
+
+ case Common::KEYCODE_c:
+ // Create
+ if (_charList.size() == XEEN_TOTAL_CHARACTERS) {
+ ErrorScroll::show(_vm, Res.YOUR_ROSTER_IS_FULL);
+ } else {
+ screen.fadeOut();
+ w.close();
+
+ // Show the create character dialog
+ CreateCharacterDialog::show(_vm);
+
+ party.copyPartyToRoster();
+ //_vm->_saves->writeCharFile();
+ screen.fadeOut();
+ modeFlag = true;
+ breakFlag = true;
+ }
+ break;
+
+ case Common::KEYCODE_d:
+ // Delete character
+ if (_charList.size() > 0) {
+ int charButtonValue = selectCharacter(true, startingChar);
+ if (charButtonValue != 0) {
+ int charIndex = charButtonValue - Common::KEYCODE_1 + startingChar;
+ Character &c = party._roster[_charList[charIndex]];
+ if (c.hasSlayerSword()) {
+ ErrorScroll::show(_vm, Res.HAS_SLAYER_SWORD);
+ } else {
+ Common::String msg = Common::String::format(Res.SURE_TO_DELETE_CHAR,
+ c._name.c_str(), Res.CLASS_NAMES[c._class]);
+ if (Confirm::show(_vm, msg)) {
+ // If the character is in the party, remove it
+ for (uint idx = 0; idx < party._activeParty.size(); ++idx) {
+ if (party._activeParty[idx]._rosterId == c._rosterId) {
+ party._activeParty.remove_at(idx);
+ break;
+ }
+ }
+
+ // Empty the character in the roster
+ c.clear();
+
+ // Rebuild the character list
+ _charList.clear();
+ for (int idx = 0; idx < XEEN_TOTAL_CHARACTERS; ++idx) {
+ Character &ch = party._roster[idx];
+ if (!ch._name.empty() && ch._savedMazeId == party._priorMazeId) {
+ _charList.push_back(idx);
+ }
+ }
+
+ startingCharChanged(startingChar);
+ }
+ }
+ }
+ }
+ break;
+
+ case Common::KEYCODE_r:
+ // Remove character
+ if (party._activeParty.size() > 0) {
+ int charButtonValue = selectCharacter(false, startingChar);
+ if (charButtonValue != 0) {
+ party.copyPartyToRoster();
+ party._activeParty.remove_at(charButtonValue - Common::KEYCODE_F1);
+ }
+ startingCharChanged(startingChar);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+}
+
+void PartyDialog::loadButtons() {
+ _uiSprites.load("inn.icn");
+ addButton(Common::Rect(16, 100, 40, 120), Common::KEYCODE_UP, &_uiSprites);
+ addButton(Common::Rect(52, 100, 76, 120), Common::KEYCODE_DOWN, &_uiSprites);
+ addButton(Common::Rect(87, 100, 111, 120), Common::KEYCODE_d, &_uiSprites);
+ addButton(Common::Rect(122, 100, 146, 120), Common::KEYCODE_r, &_uiSprites);
+ addButton(Common::Rect(157, 100, 181, 120), Common::KEYCODE_c, &_uiSprites);
+ addButton(Common::Rect(192, 100, 216, 120), Common::KEYCODE_x, &_uiSprites);
+ addButton(Common::Rect(0, 0, 0, 0), Common::KEYCODE_ESCAPE);
+}
+
+void PartyDialog::initDrawStructs() {
+ _faceDrawStructs[0] = DrawStruct(0, 0, 0);
+ _faceDrawStructs[1] = DrawStruct(0, 101, 0);
+ _faceDrawStructs[2] = DrawStruct(0, 0, 43);
+ _faceDrawStructs[3] = DrawStruct(0, 101, 43);
+}
+
+void PartyDialog::setupBackground() {
+ _vm->_screen->loadBackground("back.raw");
+ _vm->_interface->assembleBorder();
+}
+
+void PartyDialog::setupFaces(int firstDisplayChar, bool updateFlag) {
+ Party &party = *_vm->_party;
+ Common::String charNames[4];
+ Common::String charRaces[4];
+ Common::String charSex[4];
+ Common::String charClasses[4];
+ int posIndex;
+ int charId;
+
+ // Reset the button areas for the display character images
+ while (_buttons.size() > 7)
+ _buttons.remove_at(7);
+ addButton(Common::Rect(16, 16, 48, 48), Common::KEYCODE_1);
+ addButton(Common::Rect(117, 16, 149, 48), Common::KEYCODE_2);
+ addButton(Common::Rect(59, 59, 91, 91), Common::KEYCODE_3);
+ addButton(Common::Rect(117, 59, 151, 91), Common::KEYCODE_4);
+
+
+ for (posIndex = 0; posIndex < 4; ++posIndex) {
+ charId = (firstDisplayChar + posIndex) >= (int)_charList.size() ? -1 :
+ _charList[firstDisplayChar + posIndex];
+ bool isInParty = party.isInParty(charId);
+
+ if (charId == -1) {
+ while ((int)_buttons.size() >(7 + posIndex))
+ _buttons.remove_at(_buttons.size() - 1);
+ break;
+ }
+
+ Common::Rect &b = _buttons[7 + posIndex]._bounds;
+ b.moveTo((posIndex & 1) ? 117 : 16, b.top);
+ Character &ps = party._roster[_charList[firstDisplayChar + posIndex]];
+ charNames[posIndex] = isInParty ? Res.IN_PARTY : ps._name;
+ charRaces[posIndex] = Res.RACE_NAMES[ps._race];
+ charSex[posIndex] = Res.SEX_NAMES[ps._sex];
+ charClasses[posIndex] = Res.CLASS_NAMES[ps._class];
+ }
+
+ drawParty(updateFlag);
+
+ // Set up the sprite set to use for each face
+ for (posIndex = 0; posIndex < 4; ++posIndex) {
+ if ((firstDisplayChar + posIndex) >= (int)_charList.size())
+ _faceDrawStructs[posIndex]._sprites = nullptr;
+ else
+ _faceDrawStructs[posIndex]._sprites = party._roster[
+ _charList[firstDisplayChar + posIndex]]._faceSprites;
+ }
+
+ _partyDetails = Common::String::format(Res.PARTY_DETAILS,
+ charNames[0].c_str(), charRaces[0].c_str(), charSex[0].c_str(), charClasses[0].c_str(),
+ charNames[1].c_str(), charRaces[1].c_str(), charSex[1].c_str(), charClasses[1].c_str(),
+ charNames[2].c_str(), charRaces[2].c_str(), charSex[2].c_str(), charClasses[2].c_str(),
+ charNames[3].c_str(), charRaces[3].c_str(), charSex[3].c_str(), charClasses[3].c_str()
+ );
+}
+
+void PartyDialog::startingCharChanged(int firstDisplayChar) {
+ Windows &windows = *_vm->_windows;
+ Window &w = windows[11];
+
+ setupFaces(firstDisplayChar, true);
+ w.writeString(Common::String::format(Res.PARTY_DIALOG_TEXT, _partyDetails.c_str()));
+ w.drawList(_faceDrawStructs, 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));
+
+ w.update();
+}
+
+int PartyDialog::selectCharacter(bool isDelete, int firstDisplayChar) {
+ EventsManager &events = *_vm->_events;
+ Party &party = *_vm->_party;
+ Windows &windows = *_vm->_windows;
+ Window &w = windows[28];
+
+ SpriteResource iconSprites;
+ iconSprites.load("esc.icn");
+
+ w.setBounds(Common::Rect(50, isDelete ? 112 : 76, 266, isDelete ? 148 : 112));
+ w.open();
+ w.writeString(Common::String::format(Res.REMOVE_OR_DELETE_WHICH,
+ Res.REMOVE_DELETE[isDelete ? 1 : 0]));
+ iconSprites.draw(w, 0, Common::Point(225, isDelete ? 120 : 84));
+ w.update();
+
+ saveButtons();
+ addButton(Common::Rect(225, isDelete ? 120 : 84, 249, isDelete ? 140 : 104),
+ Common::KEYCODE_ESCAPE, &iconSprites);
+ addButton(Common::Rect(16, 16, 48, 48), Common::KEYCODE_1);
+ addButton(Common::Rect(117, 16, 149, 48), Common::KEYCODE_2);
+ addButton(Common::Rect(16, 59, 48, 91), Common::KEYCODE_3);
+ addButton(Common::Rect(117, 59, 149, 91), Common::KEYCODE_4);
+ addPartyButtons(_vm);
+
+ int result = -1, v;
+ while (!_vm->shouldExit() && result == -1) {
+ _buttonValue = 0;
+ while (!_vm->shouldExit() && !_buttonValue) {
+ events.pollEventsAndWait();
+ checkEvents(_vm);
+ }
+
+ switch (_buttonValue) {
+ case Common::KEYCODE_ESCAPE:
+ result = 0;
+ break;
+
+ case Common::KEYCODE_F1:
+ case Common::KEYCODE_F2:
+ case Common::KEYCODE_F3:
+ case Common::KEYCODE_F4:
+ case Common::KEYCODE_F5:
+ case Common::KEYCODE_F6:
+ if (!isDelete) {
+ v = _buttonValue - Common::KEYCODE_F1;
+ if (v < (int)party._activeParty.size())
+ result = _buttonValue;
+ }
+ break;
+
+ case Common::KEYCODE_1:
+ case Common::KEYCODE_2:
+ case Common::KEYCODE_3:
+ case Common::KEYCODE_4:
+ if (isDelete) {
+ v = _buttonValue - Common::KEYCODE_1;
+ if ((firstDisplayChar + v) < (int)_charList.size())
+ result = _buttonValue;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ w.close();
+ restoreButtons();
+ return result == -1 ? 0 : result;
+}
+
+} // End of namespace Xeen
diff --git a/engines/xeen/dialogs/dialogs_party.h b/engines/xeen/dialogs/dialogs_party.h
new file mode 100644
index 0000000000..8f87ca3aa6
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_party.h
@@ -0,0 +1,87 @@
+/* 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_PARTY_H
+#define XEEN_DIALOGS_PARTY_H
+
+#include "common/array.h"
+#include "xeen/dialogs/dialogs.h"
+#include "xeen/interface.h"
+#include "xeen/screen.h"
+#include "xeen/sprites.h"
+
+namespace Xeen {
+
+/**
+ * Shows the Party dialog that's shown when signing into an inn
+ */
+class PartyDialog : public ButtonContainer, public PartyDrawer {
+private:
+ XeenEngine *_vm;
+ SpriteResource _uiSprites;
+ DrawStruct _faceDrawStructs[4];
+ Common::String _partyDetails;
+ Common::Array<int> _charList;
+
+ /**
+ * Constructor
+ */
+ PartyDialog(XeenEngine *vm);
+
+ /**
+ * Executes the dialog
+ */
+ void execute();
+
+ /**
+ * Loads buttons for the dialog
+ */
+ void loadButtons();
+
+ /**
+ * Initialises a list of elements to draw
+ */
+ void initDrawStructs();
+
+ /**
+ * Sets up the background
+ */
+ void setupBackground();
+
+ /**
+ * Sets up the faces from the avaialble roster for display in the party dialog
+ */
+ void setupFaces(int firstDisplayChar, bool updateFlag);
+
+ void startingCharChanged(int firstDisplayChar);
+
+ int selectCharacter(bool isDelete, int firstDisplayChar);
+public:
+ /**
+ * Show the Party dialog
+ */
+ static void show(XeenEngine *vm);
+};
+
+} // End of namespace Xeen
+
+#endif /* XEEN_DIALOGS_PARTY_H */
diff --git a/engines/xeen/dialogs/dialogs_query.cpp b/engines/xeen/dialogs/dialogs_query.cpp
new file mode 100644
index 0000000000..79f46826cd
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_query.cpp
@@ -0,0 +1,158 @@
+/* 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 "xeen/dialogs/dialogs_query.h"
+#include "xeen/xeen.h"
+
+namespace Xeen {
+
+bool Confirm::show(XeenEngine *vm, const Common::String &msg, int mode) {
+ Confirm *dlg = new Confirm(vm);
+ bool result = dlg->execute(msg, mode);
+ delete dlg;
+
+ return result;
+}
+
+bool Confirm::execute(const Common::String &msg, int mode) {
+ EventsManager &events = *_vm->_events;
+ Windows &windows = *_vm->_windows;
+ SpriteResource confirmSprites;
+
+ confirmSprites.load("confirm.icn");
+ addButton(Common::Rect(129, 112, 153, 122), Common::KEYCODE_y, &confirmSprites);
+ addButton(Common::Rect(185, 112, 209, 122), Common::KEYCODE_n, &confirmSprites);
+
+ Window &w = windows[mode ? 22 : 21];
+ w.open();
+
+ if (!mode) {
+ confirmSprites.draw(w, 0, Common::Point(129, 112));
+ confirmSprites.draw(w, 2, Common::Point(185, 112));
+ _buttons[0]._bounds.moveTo(129, 112);
+ _buttons[1]._bounds.moveTo(185, 112);
+ } else {
+ if (mode & 0x80) {
+ clearButtons();
+ } else {
+ confirmSprites.draw(w, 0, Common::Point(120, 133));
+ confirmSprites.draw(w, 2, Common::Point(176, 133));
+ _buttons[0]._bounds.moveTo(120, 133);
+ _buttons[1]._bounds.moveTo(176, 133);
+ }
+ }
+
+ w.writeString(msg);
+ w.update();
+
+ events.clearEvents();
+ bool result = false;
+
+ while (!_vm->shouldExit()) {
+ events.pollEvents();
+ checkEvents(_vm);
+
+ if ((mode & 0x80) || _buttonValue == Common::KEYCODE_ESCAPE
+ || _buttonValue == Common::KEYCODE_n)
+ break;
+
+ if (_buttonValue == Common::KEYCODE_y) {
+ result = true;
+ break;
+ }
+ }
+
+ w.close();
+ return result;
+}
+
+/*------------------------------------------------------------------------*/
+
+bool YesNo::show(XeenEngine *vm, bool type, bool townFlag) {
+ YesNo *dlg = new YesNo(vm);
+ bool result = dlg->execute(type, townFlag);
+ delete dlg;
+
+ return result;
+}
+
+bool YesNo::execute(bool type, bool townFlag) {
+ EventsManager &events = *_vm->_events;
+ Interface &intf = *_vm->_interface;
+ Map &map = *_vm->_map;
+ Party &party = *_vm->_party;
+ Resources &res = *_vm->_resources;
+ LocationManager &loc = *_vm->_locations;
+ Windows &windows = *_vm->_windows;
+ SpriteResource confirmSprites;
+ bool result = false;
+
+ Mode oldMode = _vm->_mode;
+ _vm->_mode = oldMode == MODE_7 ? MODE_8 : MODE_7;
+
+ if (!type) {
+ confirmSprites.load("confirm.icn");
+ res._globalSprites.draw(0, 7, Common::Point(232, 74));
+ confirmSprites.draw(0, 0, Common::Point(235, 75));
+ confirmSprites.draw(0, 2, Common::Point(260, 75));
+ windows[34].update();
+
+ addButton(Common::Rect(235, 75, 259, 95), Common::KEYCODE_y, &confirmSprites);
+ addButton(Common::Rect(260, 75, 284, 95), Common::KEYCODE_n, &confirmSprites);
+
+ intf._face1State = map._headData[party._mazePosition.y][party._mazePosition.x]._left;
+ intf._face2State = map._headData[party._mazePosition.y][party._mazePosition.x]._right;
+ }
+
+ while (!_vm->shouldExit()) {
+ events.updateGameCounter();
+
+ if (loc.isActive()) {
+ loc.drawAnim(townFlag);
+ //numFrames = 3;
+ } else {
+ intf.draw3d(true);
+ //numFrames = 1;
+ }
+
+ events.wait(3);
+ checkEvents(_vm);
+ if (!_buttonValue)
+ continue;
+
+ if (type || _buttonValue == Common::KEYCODE_y) {
+ result = true;
+ break;
+ } else if (_buttonValue == Common::KEYCODE_n || _buttonValue == Common::KEYCODE_ESCAPE)
+ break;
+ }
+
+ intf._face1State = intf._face2State = 2;
+ _vm->_mode = oldMode;
+
+ if (!type)
+ intf.mainIconsPrint();
+
+ return result;
+}
+
+} // End of namespace Xeen
diff --git a/engines/xeen/dialogs/dialogs_query.h b/engines/xeen/dialogs/dialogs_query.h
new file mode 100644
index 0000000000..911de3d79d
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_query.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_QUERY_H
+#define XEEN_DIALOGS_QUERY_H
+
+#include "xeen/dialogs/dialogs.h"
+
+namespace Xeen {
+
+class Confirm : public ButtonContainer {
+private:
+ Confirm(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ bool execute(const Common::String &msg, int mode);
+public:
+ static bool show(XeenEngine *vm, const Common::String &msg, int mode = 0);
+};
+
+class YesNo : public ButtonContainer {
+private:
+ YesNo(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ bool execute(bool type, bool townFlag);
+public:
+ static bool show(XeenEngine *vm, bool type, bool townFlag = false);
+};
+
+} // End of namespace Xeen
+
+#endif /* XEEN_DIALOGS_QUERY_H */
diff --git a/engines/xeen/dialogs/dialogs_quests.cpp b/engines/xeen/dialogs/dialogs_quests.cpp
new file mode 100644
index 0000000000..e4f62270ef
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_quests.cpp
@@ -0,0 +1,254 @@
+/* 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/dialogs_quests.h"
+#include "xeen/events.h"
+#include "xeen/party.h"
+#include "xeen/xeen.h"
+
+namespace Xeen {
+
+#define MAX_DIALOG_LINES 128
+
+void Quests::show(XeenEngine *vm) {
+ Quests *dlg = new Quests(vm);
+ dlg->execute();
+ delete dlg;
+}
+
+void Quests::execute() {
+ EventsManager &events = *_vm->_events;
+ Party &party = *_vm->_party;
+ Windows &windows = *_vm->_windows;
+ Mode oldMode = _vm->_mode;
+ int count = 0;
+ bool headerShown = false;
+ int topRow = 0;
+
+ addButtons();
+ loadQuestNotes();
+
+ enum { QUEST_ITEMS, CURRENT_QUESTS, AUTO_NOTES } mode = QUEST_ITEMS;
+ bool windowFlag;
+ if (windows[29]._enabled) {
+ windowFlag = false;
+ } else {
+ windows[29].open();
+ windows[30].open();
+ windowFlag = true;
+ }
+
+ windows[29].writeString(Res.QUESTS_DIALOG_TEXT);
+ drawButtons(&windows[0]);
+
+ while (!_vm->shouldExit()) {
+ Common::String lines[MAX_DIALOG_LINES];
+
+ switch (mode) {
+ case QUEST_ITEMS:
+ for (int idx = 0; idx < TOTAL_QUEST_ITEMS; ++idx)
+ lines[idx] = "\b \b*";
+
+ count = 0;
+ headerShown = false;
+ for (int idx = 0; idx < TOTAL_QUEST_ITEMS; ++idx) {
+ if (party._questItems[idx]) {
+ if (!count && !headerShown && idx < 35) {
+ lines[count++] = Res.CLOUDS_OF_XEEN_LINE;
+ }
+ if (idx >= 35 && !headerShown) {
+ lines[count++] = Res.DARKSIDE_OF_XEEN_LINE;
+ headerShown = true;
+ }
+
+ switch (idx) {
+ case 17:
+ case 26:
+ case 79:
+ case 80:
+ case 81:
+ case 82:
+ case 83:
+ case 84:
+ lines[count++] = Common::String::format("%d %s%c",
+ party._questItems[idx], Res.QUEST_ITEM_NAMES[idx],
+ party._questItems[idx] == 1 ? ' ' : 's');
+ break;
+ default:
+ lines[count++] = Res.QUEST_ITEM_NAMES[idx];
+ break;
+ }
+ }
+ }
+
+ if (count == 0) {
+ windows[30].writeString(Res.NO_QUEST_ITEMS);
+ } else {
+ windows[30].writeString(Common::String::format(Res.QUEST_ITEMS_DATA,
+ lines[topRow].c_str(), lines[topRow + 1].c_str(),
+ lines[topRow + 2].c_str(), lines[topRow + 3].c_str(),
+ lines[topRow + 4].c_str(), lines[topRow + 5].c_str(),
+ lines[topRow + 6].c_str(), lines[topRow + 7].c_str(),
+ lines[topRow + 8].c_str()
+ ));
+ }
+ break;
+
+ case CURRENT_QUESTS:
+ for (int idx = 0; idx < TOTAL_QUEST_ITEMS; ++idx)
+ lines[idx] = "";
+
+ count = 0;
+ headerShown = false;
+ for (int idx = 0; idx < TOTAL_QUEST_FLAGS; ++idx) {
+ if (party._questFlags[(idx + 1) / 30][(idx + 1) % 30]) {
+ if (!count && !headerShown && idx < 29) {
+ lines[count++] = Res.CLOUDS_OF_XEEN_LINE;
+ }
+ if (idx > 28 && !headerShown) {
+ lines[count++] = Res.DARKSIDE_OF_XEEN_LINE;
+ headerShown = true;
+ }
+
+ lines[count++] = _questNotes[idx];
+ }
+ }
+
+ if (count == 0)
+ lines[1] = Res.NO_CURRENT_QUESTS;
+
+ windows[30].writeString(Common::String::format(Res.CURRENT_QUESTS_DATA,
+ lines[topRow].c_str(), lines[topRow + 1].c_str(), lines[topRow + 2].c_str()));
+ break;
+
+ case AUTO_NOTES:
+ for (int idx = 0; idx < MAX_DIALOG_LINES; ++idx)
+ lines[idx] = "";
+
+ count = 0;
+ headerShown = false;
+ for (int idx = 0; idx < MAX_DIALOG_LINES; ++idx) {
+ if (party._worldFlags[idx]) {
+ if (!count && !headerShown && idx < 72) {
+ lines[count++] = Res.CLOUDS_OF_XEEN_LINE;
+ }
+ if (idx >= 72 && !headerShown) {
+ lines[count++] = Res.DARKSIDE_OF_XEEN_LINE;
+ headerShown = true;
+ }
+
+ lines[count++] = _questNotes[idx + 56];
+ }
+ }
+
+ if (count == 0)
+ lines[1] = Res.NO_AUTO_NOTES;
+
+ windows[30].writeString(Common::String::format(Res.AUTO_NOTES_DATA,
+ lines[topRow].c_str(), lines[topRow + 1].c_str(),
+ lines[topRow + 2].c_str(), lines[topRow + 3].c_str(),
+ lines[topRow + 4].c_str(), lines[topRow + 5].c_str(),
+ lines[topRow + 6].c_str(), lines[topRow + 7].c_str(),
+ lines[topRow + 8].c_str()
+ ));
+ break;
+ }
+
+ windows[30].writeString("\v000\t000");
+ windows[24].update();
+
+ // Key handling
+ _buttonValue = 0;
+ while (!_vm->shouldExit() && !_buttonValue) {
+ events.pollEventsAndWait();
+ checkEvents(_vm);
+ }
+
+ if (_buttonValue == Common::KEYCODE_ESCAPE)
+ break;
+
+ switch (_buttonValue) {
+ case Common::KEYCODE_a:
+ mode = AUTO_NOTES;
+ topRow = 0;
+ break;
+ case Common::KEYCODE_i:
+ mode = QUEST_ITEMS;
+ topRow = 0;
+ break;
+ case Common::KEYCODE_q:
+ mode = CURRENT_QUESTS;
+ topRow = 0;
+ break;
+ case Common::KEYCODE_HOME:
+ topRow = 0;
+ break;
+ case Common::KEYCODE_END:
+ topRow = MAX(count - 1, 0);
+ break;
+ case Common::KEYCODE_PAGEUP:
+ topRow = MAX(topRow - 3, 0);
+ break;
+ case Common::KEYCODE_PAGEDOWN:
+ topRow = CLIP(topRow + 3, 0, MAX(count - 1, 0));
+ break;
+ case Common::KEYCODE_UP:
+ case Common::KEYCODE_KP8:
+ topRow = MAX(topRow - 1, 0);
+ break;
+ case Common::KEYCODE_DOWN:
+ case Common::KEYCODE_KP2:
+ topRow = CLIP(topRow + 1, 0, MAX(count - 1, 0));
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (windowFlag) {
+ windows[30].close();
+ windows[29].close();
+ }
+ _vm->_mode = oldMode;
+}
+
+void Quests::addButtons() {
+ _iconSprites.load("quest.icn");
+
+
+ addButton(Common::Rect(12, 109, 36, 129), Common::KEYCODE_i, &_iconSprites);
+ addButton(Common::Rect(80, 109, 104, 129), Common::KEYCODE_q, &_iconSprites);
+ addButton(Common::Rect(148, 109, 172, 129), Common::KEYCODE_a, &_iconSprites);
+ addButton(Common::Rect(216, 109, 240, 129), Common::KEYCODE_UP, &_iconSprites);
+ addButton(Common::Rect(250, 109, 274, 129), Common::KEYCODE_DOWN, &_iconSprites);
+ addButton(Common::Rect(284, 109, 308, 129), Common::KEYCODE_ESCAPE, &_iconSprites);
+}
+
+void Quests::loadQuestNotes() {
+ File f("qnotes.bin");
+ while (f.pos() < f.size())
+ _questNotes.push_back(f.readString());
+ f.close();
+}
+
+} // End of namespace Xeen
diff --git a/engines/xeen/dialogs/dialogs_quests.h b/engines/xeen/dialogs/dialogs_quests.h
new file mode 100644
index 0000000000..a3f1980c67
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_quests.h
@@ -0,0 +1,49 @@
+/* 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_QUESTS_H
+#define XEEN_DIALOGS_QUESTS_H
+
+#include "common/str-array.h"
+#include "xeen/dialogs/dialogs.h"
+
+namespace Xeen {
+
+class Quests : public ButtonContainer {
+private:
+ SpriteResource _iconSprites;
+ Common::StringArray _questNotes;
+
+ Quests(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ void execute();
+
+ void addButtons();
+
+ void loadQuestNotes();
+public:
+ static void show(XeenEngine *vm);
+};
+
+} // End of namespace Xeen
+
+#endif /* XEEN_DIALOGS_QUESTS_H */
diff --git a/engines/xeen/dialogs/dialogs_quick_fight.cpp b/engines/xeen/dialogs/dialogs_quick_fight.cpp
new file mode 100644
index 0000000000..63acf55655
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_quick_fight.cpp
@@ -0,0 +1,105 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "xeen/dialogs/dialogs_quick_fight.h"
+#include "xeen/resources.h"
+#include "xeen/xeen.h"
+
+namespace Xeen {
+
+void QuickFight::show(XeenEngine *vm, Character *currentChar) {
+ QuickFight *dlg = new QuickFight(vm, currentChar);
+ dlg->execute();
+ delete dlg;
+}
+
+QuickFight::QuickFight(XeenEngine *vm, Character *currentChar) : ButtonContainer(vm),
+ _currentChar(currentChar) {
+ loadButtons();
+}
+
+void QuickFight::execute() {
+ Combat &combat = *_vm->_combat;
+ EventsManager &events = *_vm->_events;
+ Interface &intf = *_vm->_interface;
+ Party &party = *_vm->_party;
+ Windows &windows = *_vm->_windows;
+ Window &w = windows[10];
+ w.open();
+
+ do {
+ // Draw the dialog text and buttons
+ Common::String msg = Common::String::format(Res.QUICK_FIGHT_TEXT,
+ _currentChar->_name.c_str(),
+ Res.QUICK_FIGHT_OPTIONS[_currentChar->_quickOption]);
+ w.writeString(msg);
+ drawButtons(&w);
+
+ // Wait for selection
+ _buttonValue = 0;
+ events.updateGameCounter();
+ do {
+ intf.draw3d(false, false);
+
+ events.pollEventsAndWait();
+ checkEvents(_vm);
+ if (_vm->shouldExit())
+ return;
+ } while (!_buttonValue && !events.timeElapsed());
+
+ switch (_buttonValue) {
+ case Common::KEYCODE_n:
+ case Common::KEYCODE_t:
+ _currentChar->_quickOption = (QuickAction)(((int)_currentChar->_quickOption + 1) % 4);
+ break;
+
+ case Common::KEYCODE_F1:
+ case Common::KEYCODE_F2:
+ case Common::KEYCODE_F3:
+ case Common::KEYCODE_F4:
+ case Common::KEYCODE_F5:
+ case Common::KEYCODE_F6: {
+ int charIdx = _buttonValue - Common::KEYCODE_F1;
+ if (charIdx < (int)combat._combatParty.size()) {
+ // Highlight new character
+ _currentChar = &party._activeParty[charIdx];
+ intf.highlightChar(charIdx);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ } while (_buttonValue != Common::KEYCODE_RETURN && _buttonValue != Common::KEYCODE_ESCAPE);
+
+ w.close();
+ events.clearEvents();
+}
+
+void QuickFight::loadButtons() {
+ _icons.load("train.icn");
+ addButton(Common::Rect(281, 108, 305, 128), Common::KEYCODE_ESCAPE, &_icons);
+ addButton(Common::Rect(242, 108, 266, 128), Common::KEYCODE_t, &_icons);
+}
+
+} // End of namespace Xeen
diff --git a/engines/xeen/dialogs/dialogs_quick_fight.h b/engines/xeen/dialogs/dialogs_quick_fight.h
new file mode 100644
index 0000000000..e3662c930a
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_quick_fight.h
@@ -0,0 +1,60 @@
+/* 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_QUICK_FIGHT_H
+#define XEEN_DIALOGS_QUICK_FIGHT_H
+
+#include "xeen/character.h"
+#include "xeen/dialogs/dialogs.h"
+#include "xeen/sprites.h"
+
+namespace Xeen {
+
+class QuickFight : public ButtonContainer {
+private:
+ SpriteResource _icons;
+ Character *_currentChar;
+private:
+ /**
+ * Constructor
+ */
+ QuickFight(XeenEngine *vm, Character *currentChar);
+
+ /**
+ * Executes the display of the dialog
+ */
+ void execute();
+
+ /**
+ * Load butons for the dialog
+ */
+ void loadButtons();
+public:
+ /**
+ * Show the dialog
+ */
+ static void show(XeenEngine *vm, Character *currentChar);
+};
+
+} // End of namespace Xeen
+
+#endif /* XEEN_DIALOGS_QUICK_FIGHT_H */
diff --git a/engines/xeen/dialogs/dialogs_quick_ref.cpp b/engines/xeen/dialogs/dialogs_quick_ref.cpp
new file mode 100644
index 0000000000..0c8a63b43a
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_quick_ref.cpp
@@ -0,0 +1,88 @@
+/* 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 "xeen/dialogs/dialogs_quick_ref.h"
+#include "xeen/resources.h"
+#include "xeen/xeen.h"
+
+namespace Xeen {
+
+void QuickReferenceDialog::show(XeenEngine *vm) {
+ QuickReferenceDialog *dlg = new QuickReferenceDialog(vm);
+ dlg->execute();
+ delete dlg;
+}
+
+void QuickReferenceDialog::execute() {
+ Combat &combat = *_vm->_combat;
+ EventsManager &events = *_vm->_events;
+ Party &party = *_vm->_party;
+ Windows &windows = *_vm->_windows;
+ Common::String lines[8];
+
+ events.setCursor(0);
+
+ for (uint idx = 0; idx < (combat._globalCombat == 2 ? combat._combatParty.size() :
+ party._activeParty.size()); ++idx) {
+ Character &c = combat._globalCombat == 2 ? *combat._combatParty[idx] :
+ party._activeParty[idx];
+ Condition condition = c.worstCondition();
+ lines[idx] = Common::String::format(Res.QUICK_REF_LINE,
+ idx * 10 + 24, idx + 1, c._name.c_str(),
+ Res.CLASS_NAMES[c._class][0], Res.CLASS_NAMES[c._class][1], Res.CLASS_NAMES[c._class][2],
+ c.statColor(c.getCurrentLevel(), c._level._permanent), c._level._permanent,
+ c.statColor(c._currentHp, c.getMaxHP()), c._currentHp,
+ c.statColor(c._currentSp, c.getMaxSP()), c._currentSp,
+ c.statColor(c.getArmorClass(), c.getArmorClass(true)), c.getArmorClass(),
+ Res.CONDITION_COLORS[condition],
+ Res.CONDITION_NAMES[condition][0], Res.CONDITION_NAMES[condition][1],
+ Res.CONDITION_NAMES[condition][2], Res.CONDITION_NAMES[condition][3]
+ );
+ }
+
+ int food = (party._food / party._activeParty.size()) / 3;
+ Common::String msg = Common::String::format(Res.QUICK_REFERENCE,
+ lines[0].c_str(), lines[1].c_str(), lines[2].c_str(),
+ lines[3].c_str(), lines[4].c_str(), lines[5].c_str(),
+ lines[6].c_str(), lines[7].c_str(),
+ party._gold, party._gems,
+ food, food == 1 ? "" : "s"
+ );
+
+ Window &w = windows[24];
+ bool windowOpen = w._enabled;
+ if (!windowOpen)
+ w.open();
+ w.writeString(msg);
+ w.update();
+
+ // Wait for a key/mouse press
+ events.clearEvents();
+ while (!_vm->shouldExit() && !events.isKeyMousePressed())
+ events.pollEventsAndWait();
+ events.clearEvents();
+
+ if (!windowOpen)
+ w.close();
+}
+
+} // End of namespace Xeen
diff --git a/engines/xeen/dialogs/dialogs_quick_ref.h b/engines/xeen/dialogs/dialogs_quick_ref.h
new file mode 100644
index 0000000000..4630043c3f
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_quick_ref.h
@@ -0,0 +1,41 @@
+/* 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_QUICK_REF_H
+#define XEEN_DIALOGS_QUICK_REF_H
+
+#include "xeen/dialogs/dialogs.h"
+
+namespace Xeen {
+
+class QuickReferenceDialog : public ButtonContainer {
+private:
+ QuickReferenceDialog(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ void execute();
+public:
+ static void show(XeenEngine *vm);
+};
+
+} // End of namespace Xeen
+
+#endif /* XEEN_DIALOGS_QUICK_REF_H */
diff --git a/engines/xeen/dialogs/dialogs_spells.cpp b/engines/xeen/dialogs/dialogs_spells.cpp
new file mode 100644
index 0000000000..3da5a5149e
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_spells.cpp
@@ -0,0 +1,1045 @@
+/* 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 "xeen/dialogs/dialogs_spells.h"
+#include "xeen/dialogs/dialogs_input.h"
+#include "xeen/dialogs/dialogs_query.h"
+#include "xeen/resources.h"
+#include "xeen/spells.h"
+#include "xeen/sprites.h"
+#include "xeen/xeen.h"
+
+namespace Xeen {
+
+Character *SpellsDialog::show(XeenEngine *vm, ButtonContainer *priorDialog,
+ Character *c, int isCasting) {
+ SpellsDialog *dlg = new SpellsDialog(vm);
+ Character *result = dlg->execute(priorDialog, c, isCasting);
+ delete dlg;
+
+ return result;
+}
+
+Character *SpellsDialog::execute(ButtonContainer *priorDialog, Character *c, int isCasting) {
+ EventsManager &events = *_vm->_events;
+ Interface &intf = *_vm->_interface;
+ Party &party = *_vm->_party;
+ Sound &sound = *_vm->_sound;
+ Spells &spells = *_vm->_spells;
+ Windows &windows = *_vm->_windows;
+ bool isDarkCc = _vm->_files->_isDarkCc;
+ loadButtons();
+
+ int castingCopy = isCasting;
+ isCasting &= 0x7f;
+ int selection = -1;
+ int topIndex = 0;
+ int newSelection;
+ windows[25].open();
+
+ do {
+ if (!isCasting) {
+ if (!c->guildMember()) {
+ sound.stopSound();
+ intf._overallFrame = 5;
+ sound.playSound(isDarkCc ? "skull1.voc" : "guild11.voc", 1);
+ break;
+ }
+
+ Common::String title = Common::String::format(Res.BUY_SPELLS, c->_name.c_str());
+ Common::String msg = Common::String::format(Res.GUILD_OPTIONS,
+ title.c_str(), XeenEngine::printMil(party._gold).c_str());
+ windows[10].writeString(msg);
+
+ warning("TODO: Sprite draw using previously used button sprites");
+ }
+
+ _spells.clear();
+ const char *errorMsg = setSpellText(c, castingCopy);
+ windows[25].writeString(Common::String::format(Res.SPELLS_FOR,
+ errorMsg == nullptr ? Res.SPELL_LINES_0_TO_9 : "",
+ c->_name.c_str()));
+
+ // Setup and write out spell list
+ const char *names[10];
+ int colors[10];
+ Common::String emptyStr = "";
+ Common::fill(&names[0], &names[10], emptyStr.c_str());
+ Common::fill(&colors[0], &colors[10], 9);
+
+ for (int idx = 0; idx < 10; ++idx) {
+ if ((topIndex + idx) < (int)_spells.size()) {
+ names[idx] = _spells[topIndex + idx]._name.c_str();
+ colors[idx] = _spells[topIndex + idx]._color;
+ }
+ }
+
+ if (selection >= topIndex && selection < (topIndex + 10))
+ colors[selection - topIndex] = 15;
+ if (_spells.size() == 0)
+ names[0] = errorMsg;
+
+ windows[37].writeString(Common::String::format(Res.SPELLS_DIALOG_SPELLS,
+ colors[0], names[0], colors[1], names[1], colors[2], names[2],
+ colors[3], names[3], colors[4], names[4], colors[5], names[5],
+ colors[6], names[6], colors[7], names[7], colors[8], names[8],
+ colors[9], names[9],
+ isCasting ? Res.SPELL_PTS : Res.GOLD,
+ isCasting ? c->_currentSp : party._gold
+ ));
+
+ _scrollSprites.draw(0, 4, Common::Point(39, 26));
+ _scrollSprites.draw(0, 0, Common::Point(187, 26));
+ _scrollSprites.draw(0, 2, Common::Point(187, 111));
+ if (isCasting)
+ _scrollSprites.draw(windows[25], 5, Common::Point(132, 123));
+
+ windows[25].update();
+
+ do {
+ events.pollEventsAndWait();
+ checkEvents(_vm);
+ } while (!_vm->shouldExit() && !_buttonValue);
+
+ switch (_buttonValue) {
+ case Common::KEYCODE_F1:
+ case Common::KEYCODE_F2:
+ case Common::KEYCODE_F3:
+ case Common::KEYCODE_F4:
+ case Common::KEYCODE_F5:
+ case Common::KEYCODE_F6:
+ if (_vm->_mode != MODE_COMBAT) {
+ _buttonValue -= Common::KEYCODE_F1;
+ if (_buttonValue < (int)party._activeParty.size()) {
+ c = &party._activeParty[_buttonValue];
+ spells._lastCaster = _buttonValue;
+ intf.highlightChar(_buttonValue);
+
+ if (_vm->_mode == MODE_17) {
+ windows[10].writeString(Common::String::format(Res.GUILD_OPTIONS,
+ XeenEngine::printMil(party._gold).c_str(), Res.GUILD_TEXT, c->_name.c_str()));
+ } else {
+ int category;
+ switch (c->_class) {
+ case CLASS_ARCHER:
+ case CLASS_SORCERER:
+ category = 1;
+ break;
+ case CLASS_DRUID:
+ case CLASS_RANGER:
+ category = 2;
+ break;
+ default:
+ category = 0;
+ break;
+ }
+
+ int spellIndex = (c->_currentSpell == -1) ? 39 : c->_currentSpell;
+ int spellId = Res.SPELLS_ALLOWED[category][spellIndex];
+ windows[10].writeString(Common::String::format(Res.CAST_SPELL_DETAILS,
+ c->_name.c_str(), spells._spellNames[spellId].c_str(),
+ spells.calcSpellPoints(spellId, c->getCurrentLevel()),
+ Res.SPELL_GEM_COST[spellId], c->_currentSp));
+ }
+
+ if (priorDialog != nullptr)
+ priorDialog->drawButtons(&windows[0]);
+ windows[10].update();
+ }
+ }
+ break;
+
+ case Common::KEYCODE_RETURN:
+ case Common::KEYCODE_KP_ENTER:
+ case Common::KEYCODE_s:
+ if (selection != -1)
+ _buttonValue = Common::KEYCODE_ESCAPE;
+ break;
+
+ case Common::KEYCODE_ESCAPE:
+ selection = -1;
+ _buttonValue = Common::KEYCODE_ESCAPE;
+ break;
+
+ case Common::KEYCODE_0:
+ case Common::KEYCODE_1:
+ case Common::KEYCODE_2:
+ case Common::KEYCODE_3:
+ case Common::KEYCODE_4:
+ case Common::KEYCODE_5:
+ case Common::KEYCODE_6:
+ case Common::KEYCODE_7:
+ case Common::KEYCODE_8:
+ case Common::KEYCODE_9:
+ newSelection = topIndex + ((_buttonValue == Common::KEYCODE_0) ? 9 :
+ (_buttonValue - Common::KEYCODE_1));
+
+ if (newSelection < (int)_spells.size()) {
+ int expenseFactor = 0;
+ int category = 0;
+
+ switch (c->_class) {
+ case CLASS_PALADIN:
+ expenseFactor = 1;
+ category = 0;
+ break;
+ case CLASS_ARCHER:
+ expenseFactor = 1;
+ category = 1;
+ break;
+ case CLASS_CLERIC:
+ category = 0;
+ break;
+ case CLASS_SORCERER:
+ category = 1;
+ break;
+ case CLASS_DRUID:
+ category = 2;
+ break;
+ case CLASS_RANGER:
+ expenseFactor = 1;
+ category = 2;
+ break;
+ default:
+ break;
+ }
+
+ int spellIndex = _spells[newSelection]._spellIndex;
+ int spellId = Res.SPELLS_ALLOWED[category][spellIndex];
+ int spellCost = spells.calcSpellCost(spellId, expenseFactor);
+
+ if (isCasting) {
+ selection = newSelection;
+ } else {
+ Common::String spellName = _spells[newSelection]._name;
+ Common::String msg = (castingCopy & 0x80) ?
+ Common::String::format(Res.SPELLS_PRESS_A_KEY, spellName.c_str()) :
+ Common::String::format(Res.SPELLS_PURCHASE, spellName.c_str(), spellCost);
+
+ if (Confirm::show(_vm, msg, castingCopy + 1)) {
+ if (party.subtract(CONS_GOLD, spellCost, WHERE_PARTY, WT_FREEZE_WAIT)) {
+ c->_spells[spellIndex] = true;
+ sound.stopSound();
+ intf._overallFrame = 0;
+ sound.playSound(isDarkCc ? "guild12.voc" : "parrot2.voc", 1);
+ } else {
+ sound.playFX(21);
+ }
+ }
+ }
+ }
+ break;
+
+ case Common::KEYCODE_PAGEUP:
+ case Common::KEYCODE_KP9:
+ topIndex = MAX((int)topIndex - 10, 0);
+ break;
+
+ case Common::KEYCODE_PAGEDOWN:
+ case Common::KEYCODE_KP3:
+ topIndex = MIN(topIndex + 10, (((int)_spells.size() - 1) / 10) * 10);
+ break;
+
+ case Common::KEYCODE_UP:
+ case Common::KEYCODE_KP8:
+ if (topIndex > 0)
+ --topIndex;
+ break;
+
+ case Common::KEYCODE_DOWN:
+ case Common::KEYCODE_KP2:
+ if (topIndex < ((int)_spells.size() - 10))
+ ++topIndex;
+ break;
+ }
+ } while (!_vm->shouldExit() && _buttonValue != Common::KEYCODE_ESCAPE);
+
+ windows[25].close();
+
+ if (_vm->shouldExit())
+ selection = -1;
+ if (isCasting && selection != -1)
+ c->_currentSpell = _spells[selection]._spellIndex;
+
+ return c;
+}
+
+void SpellsDialog::loadButtons() {
+ _iconSprites.load("main.icn");
+ _scrollSprites.load("scroll.icn");
+ addButton(Common::Rect(187, 26, 198, 36), Common::KEYCODE_UP, &_scrollSprites);
+ addButton(Common::Rect(187, 111, 198, 121), Common::KEYCODE_DOWN, &_scrollSprites);
+ addButton(Common::Rect(40, 28, 187, 36), Common::KEYCODE_1);
+ addButton(Common::Rect(40, 37, 187, 45), Common::KEYCODE_2);
+ addButton(Common::Rect(40, 46, 187, 54), Common::KEYCODE_3);
+ addButton(Common::Rect(40, 55, 187, 63), Common::KEYCODE_4);
+ addButton(Common::Rect(40, 64, 187, 72), Common::KEYCODE_5);
+ addButton(Common::Rect(40, 73, 187, 81), Common::KEYCODE_6);
+ addButton(Common::Rect(40, 82, 187, 90), Common::KEYCODE_7);
+ addButton(Common::Rect(40, 91, 187, 99), Common::KEYCODE_8);
+ addButton(Common::Rect(40, 100, 187, 108), Common::KEYCODE_9);
+ addButton(Common::Rect(40, 109, 187, 117), Common::KEYCODE_0);
+ addButton(Common::Rect(174, 123, 198, 133), Common::KEYCODE_ESCAPE);
+ addButton(Common::Rect(187, 35, 198, 73), Common::KEYCODE_PAGEUP);
+ addButton(Common::Rect(187, 74, 198, 112), Common::KEYCODE_PAGEDOWN);
+ addButton(Common::Rect(132, 123, 168, 133), Common::KEYCODE_s);
+ addPartyButtons(_vm);
+}
+
+const char *SpellsDialog::setSpellText(Character *c, int isCasting) {
+ Party &party = *_vm->_party;
+ Spells &spells = *_vm->_spells;
+ bool isDarkCc = _vm->_files->_isDarkCc;
+ int expenseFactor = 0;
+ int currLevel = c->getCurrentLevel();
+ int category;
+
+ if ((isCasting & 0x7f) == 0) {
+ switch (c->_class) {
+ case CLASS_PALADIN:
+ expenseFactor = 1;
+ category = 0;
+ break;
+ case CLASS_ARCHER:
+ expenseFactor = 1;
+ category = 1;
+ break;
+ case CLASS_CLERIC:
+ category = 0;
+ break;
+ case CLASS_SORCERER:
+ category = 1;
+ break;
+ case CLASS_DRUID:
+ category = 2;
+ break;
+ case CLASS_RANGER:
+ expenseFactor = 1;
+ category = 2;
+ break;
+ default:
+ category = -1;
+ break;
+ }
+
+ if (category != -1) {
+ if (party._mazeId == 49 || party._mazeId == 37) {
+ for (uint spellId = 0; spellId < 76; ++spellId) {
+ int idx = 0;
+ while (idx < MAX_SPELLS_PER_CLASS && Res.SPELLS_ALLOWED[category][idx] != (int)spellId)
+ ++idx;
+
+ // Handling if the spell is appropriate for the character's class
+ if (idx < MAX_SPELLS_PER_CLASS) {
+ if (!c->_spells[idx] || (isCasting & 0x80)) {
+ int cost = spells.calcSpellCost(Res.SPELLS_ALLOWED[category][idx], expenseFactor);
+ _spells.push_back(SpellEntry(Common::String::format("\x3l%s\x3r\x9""000%u",
+ spells._spellNames[Res.SPELLS_ALLOWED[category][idx]].c_str(), cost),
+ idx, spellId));
+ }
+ }
+ }
+ } else if (isDarkCc) {
+ int groupIndex = (party._mazeId - 29) / 2;
+ for (int spellId = Res.DARK_SPELL_RANGES[groupIndex][0];
+ spellId < Res.DARK_SPELL_RANGES[groupIndex][1]; ++spellId) {
+ int idx = 0;
+ while (idx < 40 && Res.SPELLS_ALLOWED[category][idx] ==
+ Res.DARK_SPELL_OFFSETS[category][spellId]);
+
+ if (idx < 40) {
+ if (!c->_spells[idx] || (isCasting & 0x80)) {
+ int cost = spells.calcSpellCost(Res.SPELLS_ALLOWED[category][idx], expenseFactor);
+ _spells.push_back(SpellEntry(Common::String::format("\x3l%s\x3r\x9""000%u",
+ spells._spellNames[Res.SPELLS_ALLOWED[category][idx]].c_str(), cost),
+ idx, spellId));
+ }
+ }
+ }
+ } else {
+ for (int spellId = 0; spellId < 20; ++spellId) {
+ int idx = 0;
+ while (Res.CLOUDS_SPELL_OFFSETS[party._mazeId - 29][spellId] !=
+ (int)Res.SPELLS_ALLOWED[category][idx] && idx < 40) ;
+
+ if (idx < 40) {
+ if (!c->_spells[idx] || (isCasting & 0x80)) {
+ int cost = spells.calcSpellCost(Res.SPELLS_ALLOWED[category][idx], expenseFactor);
+ _spells.push_back(SpellEntry(Common::String::format("\x3l%s\x3r\x9""000%u",
+ spells._spellNames[Res.SPELLS_ALLOWED[category][idx]].c_str(), cost),
+ idx, spellId));
+ }
+ }
+ }
+ }
+ }
+
+ if (c->getMaxSP() == 0)
+ return Res.NOT_A_SPELL_CASTER;
+
+ } else if ((isCasting & 0x7f) == 1) {
+ switch (c->_class) {
+ case CLASS_ARCHER:
+ case CLASS_SORCERER:
+ category = 1;
+ break;
+ case CLASS_DRUID:
+ case CLASS_RANGER:
+ category = 2;
+ break;
+ case CLASS_PALADIN:
+ case CLASS_CLERIC:
+ default:
+ category = 0;
+ break;
+ }
+
+ if (c->getMaxSP() == 0) {
+ return Res.NOT_A_SPELL_CASTER;
+ } else {
+ for (int spellIndex = 0; spellIndex < MAX_SPELLS_PER_CLASS; ++spellIndex) {
+ if (c->_spells[spellIndex]) {
+ int spellId = Res.SPELLS_ALLOWED[category][spellIndex];
+ int gemCost = Res.SPELL_GEM_COST[spellId];
+ int spCost = spells.calcSpellPoints(spellId, currLevel);
+
+ Common::String msg = Common::String::format("\x3l%s\x3r\x9""000%u/%u",
+ spells._spellNames[spellId].c_str(), spCost, gemCost);
+ _spells.push_back(SpellEntry(msg, spellIndex, spellId));
+ }
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+/*------------------------------------------------------------------------*/
+
+CastSpell::CastSpell(XeenEngine *vm) : ButtonContainer(vm) {
+ Windows &windows = *_vm->_windows;
+ _oldMode = _vm->_mode;
+ _vm->_mode = MODE_3;
+
+ windows[10].open();
+ loadButtons();
+}
+
+CastSpell::~CastSpell() {
+ Interface &intf = *_vm->_interface;
+ Windows &windows = *_vm->_windows;
+
+ windows[10].close();
+ intf.unhighlightChar();
+
+ _vm->_mode = (Mode)_oldMode;
+}
+
+
+int CastSpell::show(XeenEngine *vm) {
+ Combat &combat = *vm->_combat;
+ Interface &intf = *vm->_interface;
+ Party &party = *vm->_party;
+ Spells &spells = *vm->_spells;
+ int charNum;
+
+ // Get which character is doing the casting
+ if (vm->_mode == MODE_COMBAT) {
+ charNum = combat._whosTurn;
+ } else if (spells._lastCaster >= 0 && spells._lastCaster < (int)party._activeParty.size()) {
+ charNum = spells._lastCaster;
+ } else {
+ for (charNum = (int)party._activeParty.size() - 1; charNum >= 0; --charNum) {
+ if (party._activeParty[charNum]._hasSpells) {
+ spells._lastCaster = charNum;
+ break;
+ }
+ }
+ }
+
+ Character *c = &party._activeParty[charNum];
+ intf.highlightChar(charNum);
+
+ return show(vm, c);
+}
+
+int CastSpell::show(XeenEngine *vm, Character *&c) {
+ Spells &spells = *vm->_spells;
+ CastSpell *dlg = new CastSpell(vm);
+ int spellId;
+ int result = -1;
+
+ do {
+ spellId = dlg->execute(c);
+
+ if (g_vm->shouldExit() || spellId == -1) {
+ result = 0;
+ } else {
+ result = spells.castSpell(c, (MagicSpell)spellId);
+ }
+ } while (result == -1);
+
+ delete dlg;
+ return result;
+}
+
+int CastSpell::execute(Character *&c) {
+ EventsManager &events = *_vm->_events;
+ Interface &intf = *_vm->_interface;
+ Party &party = *_vm->_party;
+ Spells &spells = *_vm->_spells;
+ Windows &windows = *_vm->_windows;
+ Window &w = windows[10];
+
+ int spellId = -1;
+ bool redrawFlag = true;
+ do {
+ if (redrawFlag) {
+ int category = c->getClassCategory();
+ int spellIndex = c->_currentSpell != -1 ? c->_currentSpell : 39;
+ spellId = Res.SPELLS_ALLOWED[category][spellIndex];
+ int gemCost = Res.SPELL_GEM_COST[spellId];
+ int spCost = spells.calcSpellPoints(spellId, c->getCurrentLevel());
+
+ w.writeString(Common::String::format(Res.CAST_SPELL_DETAILS,
+ c->_name.c_str(), spells._spellNames[spellId].c_str(),
+ spCost, gemCost, c->_currentSp));
+ drawButtons(&windows[0]);
+ w.update();
+
+ redrawFlag = false;
+ }
+
+ events.updateGameCounter();
+ intf.draw3d(true);
+
+ // Wait for event or time expiry
+ do {
+ events.pollEventsAndWait();
+ checkEvents(_vm);
+ } while (!_vm->shouldExit() && events.timeElapsed() < 1 && !_buttonValue);
+
+ switch (_buttonValue) {
+ case Common::KEYCODE_F1:
+ case Common::KEYCODE_F2:
+ case Common::KEYCODE_F3:
+ case Common::KEYCODE_F4:
+ case Common::KEYCODE_F5:
+ case Common::KEYCODE_F6:
+ // Only allow changing character if the party is not in combat
+ if (_oldMode != MODE_COMBAT) {
+ _vm->_mode = (Mode)_oldMode;
+ _buttonValue -= Common::KEYCODE_F1;
+
+ if (_buttonValue < (int)party._activeParty.size()) {
+ c = &party._activeParty[_buttonValue];
+ intf.highlightChar(_buttonValue);
+ redrawFlag = true;
+ break;
+ }
+ }
+ break;
+
+ case Common::KEYCODE_ESCAPE:
+ spellId = -1;
+ break;
+
+ case Common::KEYCODE_c:
+ // Cast spell - return the selected spell Id to be cast
+ if (c->_currentSpell != -1 && !c->noActions())
+ _buttonValue = Common::KEYCODE_ESCAPE;
+ break;
+
+ case Common::KEYCODE_n:
+ // Select new spell
+ _vm->_mode = (Mode)_oldMode;
+ c = SpellsDialog::show(_vm, this, c, 1);
+ redrawFlag = true;
+ break;
+
+ default:
+ break;
+ }
+ } while (!_vm->shouldExit() && _buttonValue != Common::KEYCODE_ESCAPE);
+
+ if (_vm->shouldExit())
+ spellId = -1;
+ return spellId;
+}
+
+void CastSpell::loadButtons() {
+ _iconSprites.load("cast.icn");
+ addButton(Common::Rect(234, 108, 259, 128), Common::KEYCODE_c, &_iconSprites);
+ addButton(Common::Rect(261, 108, 285, 128), Common::KEYCODE_n, &_iconSprites);
+ addButton(Common::Rect(288, 108, 312, 128), Common::KEYCODE_ESCAPE, &_iconSprites);
+ addPartyButtons(_vm);
+}
+
+/*------------------------------------------------------------------------*/
+
+Character *SpellOnWho::show(XeenEngine *vm, int spellId) {
+ SpellOnWho *dlg = new SpellOnWho(vm);
+ int result = dlg->execute(spellId);
+ delete dlg;
+
+ if (result == -1)
+ return nullptr;
+
+ Combat &combat = *vm->_combat;
+ Party &party = *vm->_party;
+ return combat._combatMode == 2 ? combat._combatParty[result] :
+ &party._activeParty[result];
+}
+
+int SpellOnWho::execute(int spellId) {
+ Combat &combat = *_vm->_combat;
+ EventsManager &events = *_vm->_events;
+ Interface &intf = *_vm->_interface;
+ Party &party = *_vm->_party;
+ Spells &spells = *_vm->_spells;
+ Windows &windows = *_vm->_windows;
+ Window &w = windows[16];
+ Mode oldMode = _vm->_mode;
+ _vm->_mode = MODE_3;
+ int result = 999;
+
+ w.open();
+ w.writeString(Res.ON_WHO);
+ w.update();
+ addPartyButtons(_vm);
+
+ while (result == 999) {
+ do {
+ events.updateGameCounter();
+ intf.draw3d(true);
+
+ do {
+ events.pollEventsAndWait();
+ if (_vm->shouldExit())
+ return -1;
+
+ checkEvents(_vm);
+ } while (!_buttonValue && events.timeElapsed() < 1);
+ } while (!_buttonValue);
+
+ switch (_buttonValue) {
+ case Common::KEYCODE_ESCAPE:
+ result = -1;
+ spells.addSpellCost(*combat._oldCharacter, spellId);
+ break;
+
+ case Common::KEYCODE_F1:
+ case Common::KEYCODE_F2:
+ case Common::KEYCODE_F3:
+ case Common::KEYCODE_F4:
+ case Common::KEYCODE_F5:
+ case Common::KEYCODE_F6:
+ _buttonValue -= Common::KEYCODE_F1;
+ if (_buttonValue < (int)(combat._combatMode == 2 ? combat._combatParty.size() :
+ party._activeParty.size())) {
+ result = _buttonValue;
+ }
+ break;
+ }
+ }
+
+ w.close();
+ _vm->_mode = oldMode;
+ return result;
+}
+
+/*------------------------------------------------------------------------*/
+
+int SelectElement::show(XeenEngine *vm, int spellId) {
+ SelectElement *dlg = new SelectElement(vm);
+ int result = dlg->execute(spellId);
+ delete dlg;
+
+ return result;
+}
+
+int SelectElement::execute(int spellId) {
+ Combat &combat = *_vm->_combat;
+ EventsManager &events = *_vm->_events;
+ Interface &intf = *_vm->_interface;
+ Spells &spells = *_vm->_spells;
+ Windows &windows = *_vm->_windows;
+ Window &w = windows[15];
+ Mode oldMode = _vm->_mode;
+ _vm->_mode = MODE_3;
+ int result = 999;
+
+ loadButtons();
+
+ w.open();
+ w.writeString(Res.WHICH_ELEMENT1);
+ drawButtons(&windows[0]);
+ w.update();
+
+ while (result == 999) {
+ do {
+ events.updateGameCounter();
+ intf.draw3d(true);
+ w.frame();
+ w.writeString(Res.WHICH_ELEMENT2);
+ drawButtons(&windows[0]);
+ w.update();
+
+ do {
+ events.pollEventsAndWait();
+ if (_vm->shouldExit())
+ return -1;
+
+ checkEvents(_vm);
+ } while (!_buttonValue && events.timeElapsed() < 1);
+ } while (!_buttonValue);
+
+ switch (_buttonValue) {
+ case Common::KEYCODE_ESCAPE:
+ result = -1;
+ spells.addSpellCost(*combat._oldCharacter, spellId);
+ break;
+
+ case Common::KEYCODE_a:
+ result = DT_POISON;
+ break;
+ case Common::KEYCODE_c:
+ result = DT_COLD;
+ break;
+ case Common::KEYCODE_e:
+ result = DT_ELECTRICAL;
+ break;
+ case Common::KEYCODE_f:
+ result = DT_FIRE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ w.close();
+ _vm->_mode = oldMode;
+ return result;
+}
+
+void SelectElement::loadButtons() {
+ _iconSprites.load("element.icn");
+ addButton(Common::Rect(60, 92, 84, 112), Common::KEYCODE_f, &_iconSprites);
+ addButton(Common::Rect(90, 92, 114, 112), Common::KEYCODE_e, &_iconSprites);
+ addButton(Common::Rect(120, 92, 144, 112), Common::KEYCODE_c, &_iconSprites);
+ addButton(Common::Rect(150, 92, 174, 112), Common::KEYCODE_a, &_iconSprites);
+}
+
+/*------------------------------------------------------------------------*/
+
+void NotWhileEngaged::show(XeenEngine *vm, int spellId) {
+ NotWhileEngaged *dlg = new NotWhileEngaged(vm);
+ dlg->execute(spellId);
+ delete dlg;
+}
+
+void NotWhileEngaged::execute(int spellId) {
+ EventsManager &events = *_vm->_events;
+ Spells &spells = *_vm->_spells;
+ Windows &windows = *_vm->_windows;
+ Window &w = windows[6];
+ Mode oldMode = _vm->_mode;
+ _vm->_mode = MODE_3;
+
+ w.open();
+ w.writeString(Common::String::format(Res.CANT_CAST_WHILE_ENGAGED,
+ spells._spellNames[spellId].c_str()));
+ w.update();
+
+ while (!_vm->shouldExit() && !events.isKeyMousePressed())
+ events.pollEventsAndWait();
+ events.clearEvents();
+
+ w.close();
+ _vm->_mode = oldMode;
+}
+
+/*------------------------------------------------------------------------*/
+
+bool LloydsBeacon::show(XeenEngine *vm) {
+ LloydsBeacon *dlg = new LloydsBeacon(vm);
+ bool result = dlg->execute();
+ delete dlg;
+
+ return result;
+}
+
+bool LloydsBeacon::execute() {
+ Combat &combat = *_vm->_combat;
+ EventsManager &events = *_vm->_events;
+ Interface &intf = *_vm->_interface;
+ Map &map = *_vm->_map;
+ Party &party = *_vm->_party;
+ Sound &sound = *_vm->_sound;
+ Windows &windows = *_vm->_windows;
+ Window &w = windows[10];
+ bool isDarkCc = _vm->_files->_isDarkCc;
+ Character &c = *combat._oldCharacter;
+
+ loadButtons();
+
+ if (!c._lloydMap) {
+ // No destination previously set, so have a default ready
+ if (isDarkCc) {
+ c._lloydSide = 1;
+ c._lloydPosition = Common::Point(25, 21);
+ c._lloydMap = 29;
+ } else {
+ c._lloydSide = 0;
+ c._lloydPosition = Common::Point(18, 4);
+ c._lloydMap = 28;
+ }
+ }
+
+ // Open up the text file for the destination map and read in it's name
+ File textFile(Common::String::format("%s%c%03d.txt",
+ c._lloydSide == 0 ? "xeen" : "dark",
+ c._lloydMap >= 100 ? 'x' : '0',
+ c._lloydMap));
+ Common::String mapName = textFile.readString();
+ textFile.close();
+
+ // Display the dialog
+ w.open();
+ w.writeString(Common::String::format(Res.LLOYDS_BEACON,
+ mapName.c_str(), c._lloydPosition.x, c._lloydPosition.y));
+ drawButtons(&windows[0]);
+ w.update();
+
+ bool result = true;
+ do {
+ do {
+ events.updateGameCounter();
+ intf.draw3d(true);
+
+ do {
+ events.pollEventsAndWait();
+ if (_vm->shouldExit())
+ return true;
+
+ checkEvents(_vm);
+ } while (!_buttonValue && events.timeElapsed() < 1);
+ } while (!_buttonValue);
+
+ switch (_buttonValue) {
+ case Common::KEYCODE_r:
+ if (!isDarkCc && c._lloydMap >= 75 && c._lloydMap <= 78 && !party._cloudsEnd) {
+ result = false;
+ } else {
+ sound.playFX(51);
+ map._loadDarkSide = isDarkCc;
+ if (c._lloydMap != party._mazeId || c._lloydSide != (isDarkCc ? 1 : 0)) {
+ map.load(c._lloydMap);
+ }
+
+ party._mazePosition = c._lloydPosition;
+ }
+
+ _buttonValue = Common::KEYCODE_ESCAPE;
+ break;
+
+ case Common::KEYCODE_s:
+ case Common::KEYCODE_t:
+ sound.playFX(20);
+ c._lloydMap = party._mazeId;
+ c._lloydPosition = party._mazePosition;
+ c._lloydSide = isDarkCc ? 1 : 0;
+
+ _buttonValue = Common::KEYCODE_ESCAPE;
+ break;
+ }
+ } while (_buttonValue != Common::KEYCODE_ESCAPE);
+
+ w.close();
+ return result;
+}
+
+void LloydsBeacon::loadButtons() {
+ _iconSprites.load("lloyds.icn");
+
+ addButton(Common::Rect(281, 108, 305, 128), Common::KEYCODE_r, &_iconSprites);
+ addButton(Common::Rect(242, 108, 266, 128), Common::KEYCODE_t, &_iconSprites);
+}
+
+/*------------------------------------------------------------------------*/
+
+int Teleport::show(XeenEngine *vm) {
+ Teleport *dlg = new Teleport(vm);
+ int result = dlg->execute();
+ delete dlg;
+
+ return result;
+}
+
+int Teleport::execute() {
+ Map &map = *_vm->_map;
+ Party &party = *_vm->_party;
+ Windows &windows = *_vm->_windows;
+ Window &w = windows[6];
+ Common::String num;
+
+ w.open();
+ w.writeString(Common::String::format(Res.HOW_MANY_SQUARES,
+ Res.DIRECTION_TEXT[party._mazeDirection]));
+ w.update();
+ int lineSize = Input::show(_vm, &w, num, 1, 200, true);
+ w.close();
+
+ if (!lineSize)
+ return -1;
+ int numSquares = atoi(num.c_str());
+ Common::Point pt = party._mazePosition;
+ int v;
+
+ switch (party._mazeDirection) {
+ case DIR_NORTH:
+ pt.y += numSquares;
+ break;
+ case DIR_EAST:
+ pt.x += numSquares;
+ break;
+ case DIR_SOUTH:
+ pt.y -= numSquares;
+ break;
+ case DIR_WEST:
+ pt.x -= numSquares;
+ break;
+ default:
+ break;
+ }
+
+ v = map.mazeLookup(pt, map._isOutdoors ? 0xF : 0xFFFF, 0);
+
+ if ((v != (map._isOutdoors ? 0 : INVALID_CELL)) &&
+ (!map._isOutdoors || v == SURFTYPE_DWATER)) {
+ party._mazePosition = pt;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/*------------------------------------------------------------------------*/
+
+int TownPortal::show(XeenEngine *vm) {
+ TownPortal *dlg = new TownPortal(vm);
+ int townNumber = dlg->execute();
+ delete dlg;
+
+ return townNumber;
+}
+
+int TownPortal::execute() {
+ Map &map = *_vm->_map;
+ Windows &windows = *_vm->_windows;
+ Window &w = windows[20];
+ Common::String townNames[5];
+ Mode oldMode = _vm->_mode;
+ _vm->_mode = MODE_FF;
+
+ // Build up a lsit of the names of the towns on the current side of Xeen
+ for (int idx = 0; idx < 5; ++idx) {
+ File f(Common::String::format("%s%04d.txt",
+ map._sideTownPortal ? "dark" : "xeen",
+ Res.TOWN_MAP_NUMBERS[map._sideTownPortal][idx]));
+ townNames[idx] = f.readString();
+ f.close();
+ }
+
+ w.open();
+ w.writeString(Common::String::format(Res.TOWN_PORTAL,
+ townNames[0].c_str(), townNames[1].c_str(), townNames[2].c_str(),
+ townNames[3].c_str(), townNames[4].c_str()
+ ));
+ w.update();
+
+ // Get the town number
+ int townNumber;
+ Common::String num;
+ do {
+ int result = Input::show(_vm, &w, num, 1, 160, true);
+ townNumber = !result ? 0 : atoi(num.c_str());
+ } while (townNumber > 5);
+
+ w.close();
+ _vm->_mode = oldMode;
+
+ return townNumber;
+}
+
+/*------------------------------------------------------------------------*/
+
+void IdentifyMonster::show(XeenEngine *vm) {
+ IdentifyMonster *dlg = new IdentifyMonster(vm);
+ dlg->execute();
+ delete dlg;
+}
+
+void IdentifyMonster::execute() {
+ Combat &combat = *_vm->_combat;
+ EventsManager &events = *_vm->_events;
+ Interface &intf = *_vm->_interface;
+ Map &map = *_vm->_map;
+ Sound &sound = *_vm->_sound;
+ Windows &windows = *_vm->_windows;
+ Window &w = windows[17];
+ Common::String monsterDesc[3];
+
+ for (int monIndex = 0; monIndex < 3; ++monIndex) {
+ if (combat._attackMonsters[monIndex] == -1)
+ continue;
+
+ MazeMonster &monster = map._mobData._monsters[combat._attackMonsters[monIndex]];
+ MonsterStruct &monsterData = *monster._monsterData;
+
+ monsterDesc[monIndex] = Common::String::format(Res.MONSTER_DETAILS,
+ monsterData._name.c_str(),
+ _vm->printK2(monster._hp).c_str(),
+ monsterData._armorClass, monsterData._numberOfAttacks,
+ Res.MONSTER_SPECIAL_ATTACKS[monsterData._specialAttack]
+ );
+ }
+
+ sound.playFX(20);
+ w.open();
+ w.writeString(Common::String::format(Res.IDENTIFY_MONSTERS,
+ monsterDesc[0].c_str(), monsterDesc[1].c_str(), monsterDesc[2].c_str()));
+ w.update();
+
+ do {
+ events.updateGameCounter();
+ intf.draw3d(false);
+ w.frame();
+ windows[3].update();
+
+ events.wait(1, false);
+ } while (!events.isKeyMousePressed());
+
+ w.close();
+}
+
+} // End of namespace Xeen
diff --git a/engines/xeen/dialogs/dialogs_spells.h b/engines/xeen/dialogs/dialogs_spells.h
new file mode 100644
index 0000000000..2bcaef43e5
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_spells.h
@@ -0,0 +1,151 @@
+/* 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_SPELLS_H
+#define XEEN_DIALOGS_SPELLS_H
+
+#include "common/array.h"
+#include "xeen/dialogs/dialogs.h"
+#include "xeen/party.h"
+
+namespace Xeen {
+
+struct SpellEntry {
+ Common::String _name;
+ int _spellIndex;
+ int _spellId;
+ int _color;
+
+ SpellEntry(const Common::String &name, int spellIndex, int spellId) :
+ _name(name), _spellIndex(spellIndex), _spellId(spellId), _color(9) {}
+};
+
+class SpellsDialog : public ButtonContainer {
+private:
+ SpriteResource _iconSprites;
+ SpriteResource _scrollSprites;
+ Common::Array<SpellEntry> _spells;
+
+ SpellsDialog(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ Character *execute(ButtonContainer *priorDialog, Character *c, int isCasting);
+
+ void loadButtons();
+
+ const char *setSpellText(Character *c, int isCasting);
+public:
+ static Character *show(XeenEngine *vm, ButtonContainer *priorDialog,
+ Character *c, int isCasting);
+};
+
+class CastSpell : public ButtonContainer {
+private:
+ SpriteResource _iconSprites;
+ int _oldMode;
+private:
+ CastSpell(XeenEngine *vm);
+ ~CastSpell();
+
+ int execute(Character *&c);
+
+ void loadButtons();
+public:
+ static int show(XeenEngine *vm);
+ static int show(XeenEngine *vm, Character *&c);
+};
+
+class SpellOnWho : public ButtonContainer {
+private:
+ SpellOnWho(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ int execute(int spellId);
+public:
+ static Character *show(XeenEngine *vm, int spellId);
+};
+
+class SelectElement : public ButtonContainer {
+private:
+ SpriteResource _iconSprites;
+
+ SelectElement(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ int execute(int spellId);
+
+ void loadButtons();
+public:
+ static int show(XeenEngine *vm, int spellId);
+};
+
+class NotWhileEngaged : public ButtonContainer {
+private:
+ NotWhileEngaged(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ void execute(int spellId);
+public:
+ static void show(XeenEngine *vm, int spellId);
+};
+
+class LloydsBeacon : public ButtonContainer {
+private:
+ SpriteResource _iconSprites;
+
+ LloydsBeacon(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ bool execute();
+
+ void loadButtons();
+public:
+ static bool show(XeenEngine *vm);
+};
+
+class Teleport : public ButtonContainer {
+private:
+ SpriteResource _iconSprites;
+
+ Teleport(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ int execute();
+public:
+ static int show(XeenEngine *vm);
+};
+
+class TownPortal : public ButtonContainer {
+private:
+ TownPortal(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ int execute();
+public:
+ static int show(XeenEngine *vm);
+};
+
+class IdentifyMonster : public ButtonContainer {
+private:
+ IdentifyMonster(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ void execute();
+public:
+ static void show(XeenEngine *vm);
+};
+
+} // End of namespace Xeen
+
+#endif /* XEEN_DIALOGS_SPELLS_H */
diff --git a/engines/xeen/dialogs/dialogs_whowill.cpp b/engines/xeen/dialogs/dialogs_whowill.cpp
new file mode 100644
index 0000000000..842804219b
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_whowill.cpp
@@ -0,0 +1,105 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "xeen/dialogs/dialogs_whowill.h"
+#include "xeen/resources.h"
+#include "xeen/xeen.h"
+
+namespace Xeen {
+
+int WhoWill::show(XeenEngine *vm, int message, int action, bool type) {
+ WhoWill *dlg = new WhoWill(vm);
+ int result = dlg->execute(message, action, type);
+ delete dlg;
+
+ return result;
+}
+
+int WhoWill::execute(int message, int action, bool type) {
+ EventsManager &events = *_vm->_events;
+ Interface &intf = *_vm->_interface;
+ Map &map = *_vm->_map;
+ Party &party = *_vm->_party;
+ Scripts &scripts = *_vm->_scripts;
+ LocationManager &loc = *_vm->_locations;
+ Windows &windows = *_vm->_windows;
+ int numFrames;
+
+ if (party._activeParty.size() <= 1)
+ // Unless there's at least two characters, just return the first one
+ return 1;
+
+ windows[38].close();
+ windows[12].close();
+
+ Common::String actionStr = type ? map._events._text[action] : Res.WHO_WILL_ACTIONS[action];
+ Common::String msg = Common::String::format(Res.WHO_WILL, actionStr.c_str(),
+ Res.WHO_ACTIONS[message], party._activeParty.size());
+
+ windows[36].open();
+ windows[36].writeString(msg);
+ windows[36].update();
+
+ intf._face1State = map._headData[party._mazePosition.y][party._mazePosition.x]._left;
+ intf._face2State = map._headData[party._mazePosition.y][party._mazePosition.x]._right;
+
+ while (!_vm->shouldExit()) {
+ events.updateGameCounter();
+
+ if (windows[11]._enabled) {
+ loc.drawAnim(false);
+ windows[36].frame();
+ numFrames = 3;
+ } else {
+ intf.draw3d(false);
+ windows[36].frame();
+ windows[3].update();
+ numFrames = 1;
+ }
+
+ events.wait(numFrames);
+ checkEvents(_vm);
+ if (!_buttonValue)
+ continue;
+
+ if (_buttonValue == 27) {
+ _buttonValue = 0;
+ break;
+ } else if (_buttonValue >= Common::KEYCODE_F1 && _buttonValue <= Common::KEYCODE_F6) {
+ _buttonValue -= Common::KEYCODE_F1 - 1;
+ if (_buttonValue > (int)party._activeParty.size())
+ continue;
+
+ if (party._activeParty[_buttonValue - 1].noActions())
+ continue;
+
+ scripts._whoWill = _buttonValue;
+ break;
+ }
+ }
+
+ intf._face1State = intf._face2State = 2;
+ windows[36].close();
+ return _buttonValue;
+}
+
+} // End of namespace Xeen
diff --git a/engines/xeen/dialogs/dialogs_whowill.h b/engines/xeen/dialogs/dialogs_whowill.h
new file mode 100644
index 0000000000..5303bdbea3
--- /dev/null
+++ b/engines/xeen/dialogs/dialogs_whowill.h
@@ -0,0 +1,41 @@
+/* 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_WHOWHILL_H
+#define XEEN_DIALOGS_WHOWHILL_H
+
+#include "xeen/dialogs/dialogs.h"
+
+namespace Xeen {
+
+class WhoWill : public ButtonContainer {
+private:
+ WhoWill(XeenEngine *vm) : ButtonContainer(vm) {}
+
+ int execute(int message, int action, bool type);
+public:
+ static int show(XeenEngine *vm, int message, int action, bool type);
+};
+
+} // End of namespace Xeen
+
+#endif /* XEEN_DIALOGS_WHOWHILL_H */