diff options
Diffstat (limited to 'engines/xeen/dialogs')
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 */ |