From d855aa0dab588f2993a4c4f103a4632a9f0ba278 Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Sun, 1 Feb 2015 11:56:08 -0500 Subject: XEEN: Implemented Character Info dialog --- engines/xeen/dialogs_automap.cpp | 3 +- engines/xeen/dialogs_char_info.cpp | 567 +++++++++++++++++++++++++++++++++++++ engines/xeen/dialogs_char_info.h | 58 ++++ engines/xeen/dialogs_spells.cpp | 18 +- engines/xeen/interface.cpp | 17 +- engines/xeen/interface_map.cpp | 2 +- engines/xeen/items.cpp | 1 + engines/xeen/items.h | 1 + engines/xeen/module.mk | 1 + engines/xeen/party.cpp | 47 ++- engines/xeen/party.h | 19 +- engines/xeen/resources.cpp | 111 +++++++- engines/xeen/resources.h | 51 +++- engines/xeen/town.h | 2 +- engines/xeen/xeen.h | 3 +- 15 files changed, 869 insertions(+), 32 deletions(-) create mode 100644 engines/xeen/dialogs_char_info.cpp create mode 100644 engines/xeen/dialogs_char_info.h (limited to 'engines/xeen') diff --git a/engines/xeen/dialogs_automap.cpp b/engines/xeen/dialogs_automap.cpp index 61821425dd..d9b7e8d6c7 100644 --- a/engines/xeen/dialogs_automap.cpp +++ b/engines/xeen/dialogs_automap.cpp @@ -40,7 +40,6 @@ void AutoMapDialog::execute() { Map &map = *_vm->_map; Party &party = *_vm->_party; int frame2 = intf._overallFrame * 2; - int varSI = 1; bool frameEndFlag = false; Common::Point pt = party._mazePosition; @@ -74,7 +73,7 @@ void AutoMapDialog::execute() { } screen._windows[5].open(); - MazeData &mazeData = map.mazeDataCurrent(); +// MazeData &mazeData = map.mazeDataCurrent(); bool drawFlag = true; int v; diff --git a/engines/xeen/dialogs_char_info.cpp b/engines/xeen/dialogs_char_info.cpp new file mode 100644 index 0000000000..3ff40034a3 --- /dev/null +++ b/engines/xeen/dialogs_char_info.cpp @@ -0,0 +1,567 @@ +/* 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_char_info.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) { + Screen &screen = *_vm->_screen; + EventsManager &events = *_vm->_events; + Interface &intf = *_vm->_interface; + Party &party = *_vm->_party; + + bool redrawFlag = false; + Mode oldMode = _vm->_mode; + _vm->_mode = MODE_CHARACTER_INFO; + loadDrawStructs(); + addButtons(); + + Character *c = (oldMode != MODE_InCombat) ? &party._activeParty[charIndex] : party._combatParty[charIndex]; + intf.highlightChar(charIndex); + Window &w = screen._windows[24]; + w.open(); + + do { + if (redrawFlag) { + Common::String charDetails = loadCharacterDetails(*c); + w.writeString(Common::String::format(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; + while (!_vm->shouldQuit() && !events.isKeyMousePressed()) { + 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_InCombat ? party._combatParty.size() : party._activeParty.size())) { + charIndex = _buttonValue; + c = (oldMode != MODE_InCombat) ? &party._activeParty[charIndex] : party._combatParty[charIndex]; + } + else { + _iconSprites.load("view.icn"); + _vm->_mode = MODE_CHARACTER_INFO; + } + 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_InCombat; + if (result) + redrawFlag = true; + break; + } + + case Common::KEYCODE_e: + if (oldMode == MODE_InCombat) { + ErrorScroll::show(_vm, EXCHANGING_IN_COMBAT, WT_FREEZE_WAIT); + } else { + _vm->_mode = oldMode; + error("c = exchangeChar(&charIndex)"); + _vm->_mode = MODE_CHARACTER_INFO; + redrawFlag = true; + } + break; + + case Common::KEYCODE_i: + _vm->_mode = oldMode; + _vm->_treasure._v1 = _vm->_mode == MODE_InCombat; + error("TODO: c = ItemDialog::show"); + + if (!c) { + party._stepped = true; + goto exit; + } + + _vm->_mode = MODE_CHARACTER_INFO; + break; + + case Common::KEYCODE_ESCAPE: + goto exit; + } + } while (!_vm->shouldQuit()); +exit: + w.close(); + intf.unhighlightChar(); + _vm->_mode = oldMode; + _vm->_treasure._v1 = false; +} + +/** + * Load the draw structure list with frame numbers and positions + */ +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; +} + +/** + * Set up the button list for the dialog + */ +void CharacterInfo::addButtons() { + addButton(Common::Rect(10, 24, 34, 64), 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); +} + +/** + * Return a string containing the details of the character + */ +Common::String CharacterInfo::loadCharacterDetails(const Character &c) { + Condition condition = c.worstCondition(); + Party &party = *_vm->_party; + int foodVal = party._food / party._partyCount / 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(CHARACTER_DETAILS, + PARTY_GOLD, c._name.c_str(), SEX_NAMES[c._sex], + RACE_NAMES[c._race], 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.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(), + CONDITION_COLORS[condition], CONDITION_NAMES[condition], + condition == NO_CONDITION && party._blessed ? PLUS_14 : "", + condition == NO_CONDITION && party._powerShield ? PLUS_14 : "", + condition == NO_CONDITION && party._holyBonus ? PLUS_14 : "", + condition == NO_CONDITION && party._heroism ? PLUS_14 : "" + ); +} + +/** + * Cursor display handling + */ +void CharacterInfo::showCursor(bool flag) { + Screen &screen = *_vm->_screen; + 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(screen, 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; + int 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 (STAT_VALUES[idx] <= stat1) + ++idx; + + msg = Common::String::format(CURRENT_MAXIMUM_RATING_TEXT, STAT_NAMES[attrib], + stat1, stat2, RATING_TEXT[idx]); + break; + + case 7: + // Age + stat1 = c.getAge(false); + stat2 = c.getAge(true); + msg = Common::String::format(AGE_TEXT, STAT_NAMES[attrib], + stat2, c._dbDay, c._ybDay); + 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(LEVEL_TEXT, 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(CURRENT_MAXIMUM_TEXT, STAT_NAMES[attrib], + stat1, stat2); + bounds.setHeight(42); + break; + + case 10: + // Hit Points + stat1 = c._currentHp; + stat2 = c.getMaxHP(); + msg = Common::String::format(CURRENT_MAXIMUM_TEXT, STAT_NAMES[attrib], + stat1, stat2); + bounds.setHeight(42); + break; + + case 11: + // Spell Points + stat1 = c._currentSp; + stat2 = c.getMaxSP(); + msg = Common::String::format(CURRENT_MAXIMUM_TEXT, STAT_NAMES[attrib], + stat1, stat2); + bounds.setHeight(42); + break; + + case 12: + // Resistences + msg = Common::String::format(RESISTENCES_TEXT, 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); + 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", + SKILL_NAMES[THIEVERY], c.getThievery()); + } else { + lines[skill] = Common::String::format("\n\t020%s", SKILL_NAMES[skill]); + } + } + } + } else { + lines[0] = 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", + 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 + error("AwardsDialog::show"); + return false; + + case 15: + // Experience + stat1 = c.getCurrentExperience(); + stat2 = c.nextExperienceLevel(); + msg = Common::String::format(EXPERIENCE_TEXT, + STAT_NAMES[attrib], stat1, + stat2 == 0 ? ELIGIBLE : Common::String::format("%d", stat2) + ); + bounds.setHeight(43); + break; + + case 16: + // Gold + msg = Common::String::format(IN_PARTY_IN_BANK, STAT_NAMES[attrib], + party._gold, party._bankGold); + break; + + case 17: + // Gems + msg = Common::String::format(IN_PARTY_IN_BANK, STAT_NAMES[attrib], + party._gems, party._bankGems); + bounds.setHeight(43); + break; + + case 18: { + // Food + int food = (party._food / party._partyCount) / 3; + msg = Common::String::format(FOOD_TEXT, STAT_NAMES[attrib], + 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", + CONDITION_NAMES[condition]); + } else { + lines[condition] = Common::String::format("\n\t020%s\t095-%d", + CONDITION_NAMES[condition], c._conditions[condition]); + } + + ++total; + } + } + + Condition condition = c.worstCondition(); + if (condition == NO_CONDITION) { + lines[0] = Common::String::format("\n\t020%s", GOOD); + } + + if (party._blessed) + lines[16] = Common::String::format(BLESSED, party._blessed); + if (party._powerShield) + lines[17] = Common::String::format(POWER_SHIELD, party._powerShield); + if (party._holyBonus) + lines[18] = Common::String::format(HOLY_BONUS, party._holyBonus); + if (party._heroism) + lines[19] = Common::String::format(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", + 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[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 = _vm->_screen->_windows[28]; + w.setBounds(bounds); + w.open(); + w.writeString(msg); + w.update(); + + // Wait for a user key/click + EventsManager &events = *_vm->_events; + while (!_vm->shouldQuit() && !events.isKeyMousePressed()) + events.pollEventsAndWait(); + events.clearEvents(); + + w.close(); + return false; +} + + +} // End of namespace Xeen diff --git a/engines/xeen/dialogs_char_info.h b/engines/xeen/dialogs_char_info.h new file mode 100644 index 0000000000..5a20ff2248 --- /dev/null +++ b/engines/xeen/dialogs_char_info.h @@ -0,0 +1,58 @@ +/* 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.h" +#include "xeen/party.h" +#include "xeen/screen.h" + +namespace Xeen { + +class CharacterInfo : public ButtonContainer { +private: + XeenEngine *_vm; + SpriteResource _iconSprites; + DrawStruct _drawList[24]; + int _cursorCell; + + CharacterInfo(XeenEngine *vm) : ButtonContainer(), _vm(vm), _cursorCell(0) {} + + void execute(int charIndex); + + void loadDrawStructs(); + + void addButtons(); + + Common::String loadCharacterDetails(const Character &c); + + 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_spells.cpp b/engines/xeen/dialogs_spells.cpp index 7ae5e7ad90..714e881128 100644 --- a/engines/xeen/dialogs_spells.cpp +++ b/engines/xeen/dialogs_spells.cpp @@ -50,7 +50,7 @@ Character *SpellsScroll::execute(Character *c, int v2) { int v2Copy = v2; v2 &= 0x7f; int selection = -1; - int topIndex = 0; + uint topIndex = 0; int newSelection; screen._windows[25].open(); @@ -109,7 +109,7 @@ Character *SpellsScroll::execute(Character *c, int v2) { switch (_buttonValue) { case Common::KEYCODE_F1: case Common::KEYCODE_F6: - if (_vm->_mode != MODE_2) { + if (_vm->_mode != MODE_InCombat) { _buttonValue -= Common::KEYCODE_F1; if (_buttonValue < party._partyCount) { c = &party._activeParty[_buttonValue]; @@ -233,12 +233,12 @@ Character *SpellsScroll::execute(Character *c, int v2) { case Common::KEYCODE_PAGEUP: case Common::KEYCODE_KP9: - topIndex = MAX(topIndex - 10, 0); + 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); + topIndex = MIN(topIndex + 10, ((_spells.size() - 1) / 10) * 10); break; case Common::KEYCODE_UP: @@ -249,7 +249,7 @@ Character *SpellsScroll::execute(Character *c, int v2) { case Common::KEYCODE_DOWN: case Common::KEYCODE_KP2: - if (topIndex < ((int)_spells.size() - 10)) + if (topIndex < (_spells.size() - 10)) ++topIndex; break; } @@ -321,7 +321,7 @@ const char *SpellsScroll::setSpellText(Character *c, int v2) { if (category != -1) { if (party._mazeId == 49 || party._mazeId == 37) { - for (int spellId = 0; spellId < 76; ++spellId) { + for (uint spellId = 0; spellId < 76; ++spellId) { int idx = 0; while (idx < MAX_SPELLS_PER_CLASS && SPELLS_ALLOWED[category][idx] == spellId) ++idx; @@ -338,8 +338,8 @@ const char *SpellsScroll::setSpellText(Character *c, int v2) { } } else if (isDarkCc) { int groupIndex = (party._mazeId - 29) / 2; - for (int spellId = DARK_SPELL_RANGES[category][0]; - spellId < DARK_SPELL_RANGES[category][1]; ++spellId) { + for (int spellId = DARK_SPELL_RANGES[groupIndex][0]; + spellId < DARK_SPELL_RANGES[groupIndex][1]; ++spellId) { int idx = 0; while (idx < 40 && SPELLS_ALLOWED[category][idx] == DARK_SPELL_OFFSETS[category][spellId]); @@ -357,7 +357,7 @@ const char *SpellsScroll::setSpellText(Character *c, int v2) { for (int spellId = 0; spellId < 20; ++spellId) { int idx = 0; while (CLOUDS_SPELL_OFFSETS[party._mazeId - 29][spellId] != - SPELLS_ALLOWED[category][idx] && idx < 40) ; + (int)SPELLS_ALLOWED[category][idx] && idx < 40) ; if (idx < 40) { if (!c->_spells[idx] || (v2 & 0x80)) { diff --git a/engines/xeen/interface.cpp b/engines/xeen/interface.cpp index 0ea5a8df83..a33c726fd3 100644 --- a/engines/xeen/interface.cpp +++ b/engines/xeen/interface.cpp @@ -21,6 +21,7 @@ */ #include "xeen/interface.h" +#include "xeen/dialogs_char_info.h" #include "xeen/dialogs_error.h" #include "xeen/dialogs_automap.h" #include "xeen/dialogs_info.h" @@ -331,7 +332,7 @@ void Interface::setupFaces(int charIndex, Common::Array xeenSideChars, bool void Interface::charIconsPrint(bool updateFlag) { Screen &screen = *_vm->_screen; - bool stateFlag = _vm->_mode == MODE_2; + bool stateFlag = _vm->_mode == MODE_InCombat; _restoreSprites.draw(screen, 0, Common::Point(8, 149)); // Handle drawing the party faces @@ -669,6 +670,20 @@ void Interface::perform() { } 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 < party._partyCount) { + CharacterInfo::show(_vm, _buttonValue); + if (party._stepped) + moveMonsters(); + } + break; + case Common::KEYCODE_EQUALS: case Common::KEYCODE_KP_EQUALS: // Toggle minimap diff --git a/engines/xeen/interface_map.cpp b/engines/xeen/interface_map.cpp index 84f8fa6aa1..c8683a7e50 100644 --- a/engines/xeen/interface_map.cpp +++ b/engines/xeen/interface_map.cpp @@ -426,7 +426,7 @@ void InterfaceMap::draw3d(bool updateFlag) { _flipUIFrame = (_flipUIFrame + 1) % 4; if (_flipUIFrame == 0) _flipWater = !_flipWater; - if (_tillMove && (_vm->_mode == MODE_1 || _vm->_mode == MODE_2) && + if (_tillMove && (_vm->_mode == MODE_1 || _vm->_mode == MODE_InCombat) && !_flag1 && _vm->_moveMonsters) { if (--_tillMove == 0) moveMonsters(); diff --git a/engines/xeen/items.cpp b/engines/xeen/items.cpp index f7dfeccbb1..c55a227502 100644 --- a/engines/xeen/items.cpp +++ b/engines/xeen/items.cpp @@ -56,6 +56,7 @@ int XeenItem::getAttributeCategory() const { Treasure::Treasure() { _hasItems = false; + _v1 = false; } } // End of namespace Xeen diff --git a/engines/xeen/items.h b/engines/xeen/items.h index 958a0b8afc..8e8f172790 100644 --- a/engines/xeen/items.h +++ b/engines/xeen/items.h @@ -55,6 +55,7 @@ public: XeenItem _armor[TOTAL_ITEMS]; XeenItem _weapons[TOTAL_ITEMS]; bool _hasItems; + bool _v1; public: Treasure(); }; diff --git a/engines/xeen/module.mk b/engines/xeen/module.mk index 4bb8bd99c0..6a9ff3012c 100644 --- a/engines/xeen/module.mk +++ b/engines/xeen/module.mk @@ -10,6 +10,7 @@ MODULE_OBJS := \ dialogs.o \ automap.o \ dialogs_automap.o \ + dialogs_char_info.o \ dialogs_confirm.o \ dialogs_error.o \ dialogs_options.o \ diff --git a/engines/xeen/party.cpp b/engines/xeen/party.cpp index 66078b67e8..3cfc56e1ec 100644 --- a/engines/xeen/party.cpp +++ b/engines/xeen/party.cpp @@ -238,7 +238,7 @@ int Character::getMaxSP() const { /** * Get the effective value of a given stat for the character */ -int Character::getStat(Attribute attrib, bool baseOnly) const { +uint Character::getStat(Attribute attrib, bool baseOnly) const { AttributePair attr; int mode = 0; @@ -289,7 +289,24 @@ int Character::getStat(Attribute attrib, bool baseOnly) const { attr._permanent += attr._temporary; } - return MAX(attr._permanent, 0); + return MAX(attr._permanent, (uint)0); +} + +/** + * Return the color number to use for a given stat value in the character + * info or quick reference dialogs + */ +int Character::statColor(int amount, int threshold) { + if (amount < 1) + return 6; + else if (amount > threshold) + return 2; + else if (amount == threshold) + return 15; + else if (amount <= (threshold / 4)) + return 9; + else + return 32; } int Character::statBonus(int statValue) const { @@ -422,8 +439,8 @@ int Character::getThievery() const { return MAX(result, 0); } -int Character::getCurrentLevel() const { - return MAX(_level._permanent + _level._temporary, 0); +uint Character::getCurrentLevel() const { + return MAX(_level._permanent + _level._temporary, (uint)0); } int Character::itemScan(int itemId) const { @@ -817,6 +834,28 @@ uint Character::getCurrentExperience() const { _experience; } + +int Character::getNumSkills() const { + int total = 0; + for (int idx = THIEVERY; idx <= DANGER_SENSE; ++idx) { + if (_skills[idx]) + ++total; + } + + return total; +} + +int Character::getNumAwards() const { + int total = 0; + for (int idx = 0; idx < 88; ++idx) { + if (hasAward(idx)) + ++total; + } + + return total; +} + + /*------------------------------------------------------------------------*/ void Roster::synchronize(Common::Serializer &s) { diff --git a/engines/xeen/party.h b/engines/xeen/party.h index e00b42476a..b793b5c696 100644 --- a/engines/xeen/party.h +++ b/engines/xeen/party.h @@ -80,8 +80,8 @@ class XeenEngine; class AttributePair { public: - int _permanent; - int _temporary; + uint _permanent; + uint _temporary; public: AttributePair(); void synchronize(Common::Serializer &s); @@ -141,13 +141,15 @@ public: Condition worstCondition() const; - int getAge(bool ignoreTemp) const; + int getAge(bool ignoreTemp = false) const; int getMaxHP() const; int getMaxSP() const; - int getStat(Attribute attrib, bool baseOnly = false) const; + uint getStat(Attribute attrib, bool baseOnly = false) const; + + static int statColor(int amount, int threshold); int statBonus(int statValue) const; @@ -159,11 +161,11 @@ public: bool hasAward(int awardId) const; - int getArmorClass(bool baseOnly) const; + int getArmorClass(bool baseOnly = false) const; int getThievery() const; - int getCurrentLevel() const; + uint getCurrentLevel() const; int itemScan(int itemId) const; @@ -176,6 +178,10 @@ public: uint currentExperienceLevel() const; uint getCurrentExperience() const; + + int getNumSkills() const; + + int getNumAwards() const; }; class Roster: public Common::Array { @@ -247,6 +253,7 @@ public: public: // Other party related runtime data Common::Array _activeParty; + Common::Array _combatParty; int _combatPartyCount; bool _partyDead; bool _newDay; diff --git a/engines/xeen/resources.cpp b/engines/xeen/resources.cpp index dafe321ab5..4e958a155d 100644 --- a/engines/xeen/resources.cpp +++ b/engines/xeen/resources.cpp @@ -287,6 +287,13 @@ const char *const ALIGNMENT_NAMES[3] = { "Good", "Neutral", "Evil" }; const char *const SEX_NAMES[2] = { "Male", "Female" }; +const char *const SKILL_NAMES[18] = { + "Thievery", "Arms Master", "Astrologer", "Body Builder", "Cartographer", + "Crusader", "Direction Sense", "Linguist", "Merchant", "Mountaineer", + "Navigator", "Path Finder", "Prayer Master", "Prestidigitator", + "Swimmer", "Tracker", "Spot Secret Door", "Danger Sense" +}; + const char *const CLASS_NAMES[11] = { "Knight", "Paladin", "Archer", "Cleric", "Sorcerer", "Robber", "Ninja", "Barbarian", "Druid", "Ranger", nullptr @@ -296,12 +303,26 @@ const uint CLASS_EXP_LEVELS[10] = { 1500, 2000, 2000, 1500, 2000, 1000, 1500, 1500, 1500, 2000 }; -const char *const CONDITION_NAMES[18] = { +const char *const CONDITION_NAMES[17] = { nullptr, "Cursed", "Heart Broken", "Weak", "Poisoned", "Diseased", "Insane", "In Love", "Drunk", "Asleep", "Depressed", "Confused", - "Paralyzed", "Unconscious", "Dead", "Stone", "Eradicated", "Good" + "Paralyzed", "Unconscious", "Dead", "Stone", "Eradicated" }; +const int CONDITION_COLORS[17] = { + 9, 9, 9, 9, 9, 9, 9, 9, 32, 32, 32, 32, 6, 6, 6, 6, 15 +}; + +const char *const GOOD = "Good"; + +const char *const BLESSED = "\n\t020Blessed\t095%+d"; + +const char *const POWER_SHIELD = "\n\t020Power Shield\t095%+d"; + +const char *const HOLY_BONUS = "\n\t020Holy Bonus\t095%+d"; + +const char *const HEROISM = "\n\t020Heroism\t095%+d"; + const char *const IN_PARTY = "\014""15In Party\014""d"; const char *const PARTY_DETAILS = "\015\003l\002\014""00" @@ -494,7 +515,7 @@ const int MONSTER_EFFECT_FLAGS[15][8] = { { 0x108, 0x108, 0x108, 0x108, 0x108, 0x108, 0x108, 0x108 } }; -const int SPELLS_ALLOWED[3][40] = { +const uint SPELLS_ALLOWED[3][40] = { { 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 12, 14, 16, 23, 26, 27, 28, 30, 31, 32, @@ -760,7 +781,7 @@ const int CLOUDS_SPELL_OFFSETS[5][20] = { } }; -const int DARK_SPELL_OFFSETS[3][39] = { +const uint DARK_SPELL_OFFSETS[3][39] = { { 42, 1, 26, 59, 27, 10, 50, 68, 55, 62, 67, 73, 2, 5, 3, 31, 30, 52, 49, 28, 74, 0, 9, 7, 14, 8, @@ -866,4 +887,86 @@ const char *const WEEK_DAY_STRINGS[10] = { "Ten", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine" }; +const char *const CHARACTER_DETAILS = + "\x3l\xB""041\x9""196%s\x9""000\xB""002%s : %s %s %s" + "\x3r\x9""053\xB""028\xC%02u%u\xC""d\x9""103\xC""%02u%u\xC""d" + "\x3l\x9""131\xC""%02u%d\xC""d\x9""196\xC""15%lu\xC""d\x3r" + "\x9""053\xB""051\xC""%02u%u\xC""d\x9""103\xC""%02u%u\xC""d" + "\x3l\x9""131\xC""%02u%u\xC""d\x9""196\xC""15%lu\xC""d" + "\x3r\x9""053\xB""074\xC""%02u%u\xC""d\x9""103\xC""%02u%u\xC""d" + "\x3l\x9""131\xC""15%u\xC""d\x9""196\xC""15%lu\xC""d" + "\x3r\x9""053\xB""097\xC""%02u%u\xC""d\x9""103\xC""%02u%u\xC""d" + "\x3l\x9""131\xC""15%u\xC""d\x9""196\xC""15%u day%c\xC""d" + "\x3r\x9""053\xB""120\xC""%02u%u\xC""d\x9""103\xC""%02u%u\xC""d" + "\x3l\x9""131\xC""15%u\xC""d\x9""196\xC""%02u%s\xC""d" + "\x9""230%s%s%s%s\xC""d"; + +const char *const PARTY_GOLD = "Party Gold"; + +const char *const PLUS_14 = "14+"; + +const char *const CHARACTER_TEMPLATE = + "\x1\xC""00\xD\x3l\x9""029\xB""018Mgt\x9""080Acy\x9""131H.P.\x9""196Experience" + "\x9""029\xB""041Int\x9""080Lck\x9""131S.P.\x9""029\xB""064Per\x9""080Age" + "\x9""131Resis\x9""196Party Gems\x9""029\xB""087End\x9""080Lvl\x9""131Skills" + "\x9""196Party Food\x9""029\xB""110Spd\x9""080AC\x9""131Awrds\x9""196Condition\x3""c" + "\x9""290\xB""025\xC""37I\xC""dtem\x9""290\xB""057\xC""37Q" + "\xC""duick\x9""290\xB""089\xC""37E\xC""dxch\x9""290\xB""121Exit\x3l%s"; + +const char *const EXCHANGING_IN_COMBAT = "\x3""c\xB""007\x9""000Exchanging in combat is not allowed!"; + +const char *const CURRENT_MAXIMUM_RATING_TEXT = "\x2\x3""c%s\n" + "Current / Maximum\n" + "\x3r\x9""054%lu\x3l\x9""058/ %lu\n" + "\x3""cRating: %s"; + +const char *const CURRENT_MAXIMUM_TEXT = "\x2\x3""c%s\n" + "Current / Maximum\n" + "\x3r\x9""054%u\x3l\x9""058/ %u"; + +const char *const RATING_TEXT[24] = { + "Nonexistant", "Very Poor", "Poor", "Very Low", "Low", "Averarage", "Good", + "Very Good", "High", "Very High", "Great", "Super", "Amazing", "Incredible", + "Gigantic", "Fantastic", "Astoundig", "Astonishing", "Monumental", "Tremendous", + "Collosal", "Awesome", "AweInspiring", "aUltimate" +}; + +const char *const AGE_TEXT = "\x2\x3""c%s\n" + "Current / Natural\n" + "\x3r\x9""057%u\x3l\x9""061/ %u\n" + "\x3""cBorn: %u / %u\x1"; + +const char *const LEVEL_TEXT = + "\x2\x3""c%s\n" + "Current / Maximum\n" + "\x3r\x9""054%u\x3l\x9""058/ %u\n" + "\x3""c%u Attack%s/Round\x1"; + +const char *const RESISTENCES_TEXT = + "\x2\x3""c%s\x3l\n" + "\x9""020Fire\x9""100%u\n" + "\x9""020Cold\x9""100%u\n" + "\x9""020Electricity\x9""100%u\n" + "\x9""020Poison\x9""100%u\n" + "\x9""020Energy\x9""100%u\n" + "\x9""020Magic\x9""100%u"; + +const char *const NONE = "\n\x9""020"; + +const char *const EXPERIENCE_TEXT = "\x2\x3""c%s\x3l\n" + "\x9""010Current:\x9""070%lu\n" + "\x9""010Next Level:\x9""070%s\x1"; + +const char *const ELIGIBLE = "\xC""12Eligible\xC""d"; + +const char *const IN_PARTY_IN_BANK = + "\x2\x3""cParty %s\n" + "%lu on hand\n" + "%lu in bank\x1\x3l"; + +const char *const FOOD_TEXT = + "\x2\x3""cParty %s\n" + "%u on hand\n" + "Enough for %u day%s\x3l"; + } // End of namespace Xeen diff --git a/engines/xeen/resources.h b/engines/xeen/resources.h index 8beeb56442..a696e540e1 100644 --- a/engines/xeen/resources.h +++ b/engines/xeen/resources.h @@ -74,8 +74,21 @@ extern const char *const ALIGNMENT_NAMES[3]; extern const char *const SEX_NAMES[2]; -extern const char *const CONDITION_NAMES[18]; +extern const char *const SKILL_NAMES[18]; +extern const char *const CONDITION_NAMES[17]; + +extern const int CONDITION_COLORS[17]; + +extern const char *const GOOD; + +extern const char *const BLESSED; + +extern const char *const POWER_SHIELD; + +extern const char *const HOLY_BONUS; + +extern const char *const HEROISM; extern const char *const IN_PARTY; extern const char *const PARTY_DETAILS; @@ -126,7 +139,7 @@ extern const int COMBAT_FLOAT_Y[8]; extern const int MONSTER_EFFECT_FLAGS[15][8]; -extern const int SPELLS_ALLOWED[3][40]; +extern const uint SPELLS_ALLOWED[3][40]; extern const int BASE_HP_BY_CLASS[10]; @@ -220,7 +233,7 @@ extern const int SPELL_COSTS[77]; extern const int CLOUDS_SPELL_OFFSETS[5][20]; -extern const int DARK_SPELL_OFFSETS[3][39]; +extern const uint DARK_SPELL_OFFSETS[3][39]; extern const int DARK_SPELL_RANGES[12][2]; @@ -276,6 +289,38 @@ extern const char *const SWORDS_GAME_TEXT; extern const char *const WEEK_DAY_STRINGS[10]; +extern const char *const CHARACTER_DETAILS; + +extern const char *const PARTY_GOLD; + +extern const char *const PLUS_14; + +extern const char *const CHARACTER_TEMPLATE; + +extern const char *const EXCHANGING_IN_COMBAT; + +extern const char *const CURRENT_MAXIMUM_RATING_TEXT; + +extern const char *const CURRENT_MAXIMUM_TEXT; + +extern const char *const RATING_TEXT[24]; + +extern const char *const AGE_TEXT; + +extern const char *const LEVEL_TEXT; + +extern const char *const RESISTENCES_TEXT; + +extern const char *const NONE; + +extern const char *const EXPERIENCE_TEXT; + +extern const char *const ELIGIBLE; + +extern const char *const IN_PARTY_IN_BANK; + +extern const char *const FOOD_TEXT; + } // End of namespace Xeen #endif /* XEEN_RESOURCES_H */ diff --git a/engines/xeen/town.h b/engines/xeen/town.h index 8237f8102e..807f6b5138 100644 --- a/engines/xeen/town.h +++ b/engines/xeen/town.h @@ -47,7 +47,7 @@ private: int _v5, _v6; int _v10, _v11, _v12; int _v13, _v14; - int _v20; + uint _v20; int _v21; uint _v22; int _v23; diff --git a/engines/xeen/xeen.h b/engines/xeen/xeen.h index cb09c280cd..7f1a2cd0f0 100644 --- a/engines/xeen/xeen.h +++ b/engines/xeen/xeen.h @@ -77,7 +77,7 @@ enum Mode { MODE_FF = -1, MODE_0 = 0, MODE_1 = 1, - MODE_2 = 2, + MODE_InCombat = 2, MODE_3 = 3, MODE_4 = 4, MODE_5 = 5, @@ -85,6 +85,7 @@ enum Mode { MODE_7 = 7, MODE_8 = 8, MODE_9 = 9, + MODE_CHARACTER_INFO = 10, MODE_17 = 17 }; -- cgit v1.2.3