aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Gilbert2015-02-04 20:22:59 -0500
committerPaul Gilbert2015-02-04 20:22:59 -0500
commit8eb9ad50f8ddf23147b999fa40c6231a1a623d41 (patch)
tree1d0d34dcf4b41500272ce60a571725b5fb5243c5
parent4953b7954aca078be9655971f925f82c51e472db (diff)
downloadscummvm-rg350-8eb9ad50f8ddf23147b999fa40c6231a1a623d41.tar.gz
scummvm-rg350-8eb9ad50f8ddf23147b999fa40c6231a1a623d41.tar.bz2
scummvm-rg350-8eb9ad50f8ddf23147b999fa40c6231a1a623d41.zip
XEEN: Split Character class into it's own file
-rw-r--r--engines/xeen/character.cpp1008
-rw-r--r--engines/xeen/character.h231
-rw-r--r--engines/xeen/items.cpp86
-rw-r--r--engines/xeen/items.h54
-rw-r--r--engines/xeen/module.mk1
-rw-r--r--engines/xeen/party.cpp891
-rw-r--r--engines/xeen/party.h141
7 files changed, 1241 insertions, 1171 deletions
diff --git a/engines/xeen/character.cpp b/engines/xeen/character.cpp
new file mode 100644
index 0000000000..f712e78e8c
--- /dev/null
+++ b/engines/xeen/character.cpp
@@ -0,0 +1,1008 @@
+/* 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/character.h"
+#include "xeen/resources.h"
+#include "xeen/xeen.h"
+
+namespace Xeen {
+
+XeenItem::XeenItem() {
+ clear();
+}
+
+void XeenItem::clear() {
+ _material = _id = _bonusFlags = 0;
+ _frame = 0;
+}
+
+void XeenItem::synchronize(Common::Serializer &s) {
+ s.syncAsByte(_material);
+ s.syncAsByte(_id);
+ s.syncAsByte(_bonusFlags);
+ s.syncAsByte(_frame);
+}
+
+int XeenItem::getElementalCategory() const {
+ int idx;
+ for (idx = 0; ELEMENTAL_CATEGORIES[idx] < _material; ++idx)
+ ;
+
+ return idx;
+}
+
+int XeenItem::getAttributeCategory() const {
+ int m = _material - 59;
+ int idx;
+ for (idx = 0; ATTRIBUTE_CATEGORIES[idx] < m; ++idx)
+ ;
+
+ return idx;
+}
+
+/*------------------------------------------------------------------------*/
+
+InventoryItems::InventoryItems() {
+ resize(INV_ITEMS_TOTAL);
+}
+
+void InventoryItems::discardItem(int itemIndex) {
+ operator[](itemIndex).clear();
+ sort();
+}
+
+void InventoryItems::sort() {
+ for (uint idx = 0; idx < size(); ++idx) {
+ if (operator[](idx)._id == 0) {
+ // Found empty slot
+ operator[](idx).clear();
+
+ // Scan through the rest of the list to find any item
+ for (uint idx2 = idx + 1; idx2 < size(); ++idx2) {
+ if (operator[](idx2)._id) {
+ // Found an item, so move it into the blank slot
+ operator[](idx) = operator[](idx2);
+ operator[](idx2).clear();
+ break;
+ }
+ }
+ }
+ }
+}
+
+void InventoryItems::equipItem(int itemIndex) {
+ error("TODO");
+}
+
+void InventoryItems::removeItem(int itemIndex) {
+ error("TODO");
+}
+
+/*------------------------------------------------------------------------*/
+
+InventoryItemsGroup::InventoryItemsGroup(InventoryItems &weapons, InventoryItems &armor,
+ InventoryItems &accessories, InventoryItems &misc) {
+ _itemSets[0] = &weapons;
+ _itemSets[1] = &armor;
+ _itemSets[2] = &accessories;
+ _itemSets[3] = &misc;
+}
+
+InventoryItems &InventoryItemsGroup::operator[](ItemCategory category) {
+ return *_itemSets[category];
+}
+
+/*------------------------------------------------------------------------*/
+
+
+void AttributePair::synchronize(Common::Serializer &s) {
+ s.syncAsByte(_permanent);
+ s.syncAsByte(_temporary);
+}
+
+/*------------------------------------------------------------------------*/
+
+AttributePair::AttributePair() {
+ _temporary = _permanent = 0;
+}
+
+/*------------------------------------------------------------------------*/
+
+Character::Character(): _items(_weapons, _armor, _accessories, _misc) {
+ _sex = MALE;
+ _race = HUMAN;
+ _xeenSide = 0;
+ _class = CLASS_KNIGHT;
+ _ACTemp = 0;
+ _birthDay = 0;
+ _tempAge = 0;
+ Common::fill(&_skills[0], &_skills[18], 0);
+ Common::fill(&_awards[0], &_awards[128], false);
+ Common::fill(&_spells[0], &_spells[39], 0);
+ _lloydMap = 0;
+ _hasSpells = false;
+ _currentSpell = 0;
+ _quickOption = 0;
+ _lloydSide = 0;
+ Common::fill(&_conditions[0], &_conditions[16], 0);
+ _townUnknown = 0;
+ _savedMazeId = 0;
+ _currentHp = 0;
+ _currentSp = 0;
+ _birthYear = 0;
+ _experience = 0;
+ _currentAdventuringSpell = 0;
+ _currentCombatSpell = 0;
+}
+
+void Character::synchronize(Common::Serializer &s) {
+ char name[16];
+ Common::fill(&name[0], &name[16], '\0');
+ strncpy(name, _name.c_str(), 16);
+ s.syncBytes((byte *)name, 16);
+
+ if (s.isLoading())
+ _name = Common::String(name);
+
+ s.syncAsByte(_sex);
+ s.syncAsByte(_race);
+ s.syncAsByte(_xeenSide);
+ s.syncAsByte(_class);
+
+ _might.synchronize(s);
+ _intellect.synchronize(s);
+ _personality.synchronize(s);
+ _endurance.synchronize(s);
+ _speed.synchronize(s);
+ _accuracy.synchronize(s);
+ _luck.synchronize(s);
+ s.syncAsByte(_ACTemp);
+ _level.synchronize(s);
+ s.syncAsByte(_birthDay);
+ s.syncAsByte(_tempAge);
+
+ // Synchronize the skill list
+ for (int idx = 0; idx < 18; ++idx)
+ s.syncAsByte(_skills[idx]);
+
+ // Synchronize character awards
+ for (int idx = 0; idx < 64; ++idx) {
+ byte b = (_awards[idx] ? 1 : 0) | (_awards[idx + 64] ? 0x10 : 0);
+ s.syncAsByte(b);
+ if (s.isLoading()) {
+ _awards[idx] = (b & 0xF) != 0;
+ _awards[idx + 64] = (b & 0xF0) != 0;
+ }
+ }
+
+ // Synchronize spell list
+ for (int i = 0; i < MAX_SPELLS_PER_CLASS - 1; ++i)
+ s.syncAsByte(_spells[i]);
+ s.syncAsByte(_lloydMap);
+ s.syncAsByte(_lloydPosition.x);
+ s.syncAsByte(_lloydPosition.y);
+ s.syncAsByte(_hasSpells);
+ s.syncAsByte(_currentSpell);
+ s.syncAsByte(_quickOption);
+
+ for (int i = 0; i < 9; ++i)
+ _weapons[i].synchronize(s);
+ for (int i = 0; i < 9; ++i)
+ _armor[i].synchronize(s);
+ for (int i = 0; i < 9; ++i)
+ _accessories[i].synchronize(s);
+ for (int i = 0; i < 9; ++i)
+ _misc[i].synchronize(s);
+
+ s.syncAsByte(_lloydSide);
+ _fireResistence.synchronize(s);
+ _coldResistence.synchronize(s);
+ _electricityResistence.synchronize(s);
+ _poisonResistence.synchronize(s);
+ _energyResistence.synchronize(s);
+ _magicResistence.synchronize(s);
+
+ for (int i = 0; i < 16; ++i)
+ s.syncAsByte(_conditions[i]);
+
+ s.syncAsUint16LE(_townUnknown);
+ s.syncAsByte(_savedMazeId);
+ s.syncAsUint16LE(_currentHp);
+ s.syncAsUint16LE(_currentSp);
+ s.syncAsUint16LE(_birthYear);
+ s.syncAsUint32LE(_experience);
+ s.syncAsByte(_currentAdventuringSpell);
+ s.syncAsByte(_currentCombatSpell);
+}
+
+Condition Character::worstCondition() const {
+ for (int cond = ERADICATED; cond >= CURSED; --cond) {
+ if (_conditions[cond])
+ return (Condition)cond;
+ }
+
+ return NO_CONDITION;
+}
+
+int Character::getAge(bool ignoreTemp) const {
+ int year = MIN(Party::_vm->_party->_year - _birthYear, (uint)254);
+
+ return ignoreTemp ? year : year + _tempAge;
+}
+
+int Character::getMaxHP() const {
+ int hp = BASE_HP_BY_CLASS[_class];
+ hp += statBonus(getStat(ENDURANCE));
+ hp += RACE_HP_BONUSES[_race];
+ if (_skills[BODYBUILDER])
+ ++hp;
+ if (hp < 1)
+ hp = 1;
+
+ hp *= getCurrentLevel();
+ hp += itemScan(7);
+
+ return MAX(hp, 0);
+}
+
+int Character::getMaxSP() const {
+ int result = 0;
+ bool flag = false;
+ int amount = 0;
+ Attribute attrib;
+ Skill skill;
+
+ if (!_hasSpells)
+ return 0;
+
+ if (_class == CLASS_SORCERER || _class == CLASS_ARCHER) {
+ attrib = INTELLECT;
+ skill = PRESTIDIGITATION;
+ } else {
+ attrib = PERSONALITY;
+ skill = PRAYER_MASTER;
+ }
+ if (_class == CLASS_DRUID || _class == CLASS_RANGER)
+ skill = ASTROLOGER;
+
+ for (;;) {
+ // Get the base number of spell points
+ result = statBonus(getStat(attrib)) + 3;
+ result += RACE_SP_BONUSES[_race][attrib - 1];
+
+ if (_skills[skill])
+ result += 2;
+ if (result < 1)
+ result = 1;
+
+ // Multiply it by the character's level
+ result *= getCurrentLevel();
+
+ // Classes other than sorcerer, clerics, and druids only get half the SP
+ if (_class != CLASS_SORCERER && _class != CLASS_CLERIC && _class != CLASS_DRUID)
+ result /= 2;
+
+ if (flag || (_class != CLASS_DRUID && _class != CLASS_RANGER))
+ break;
+
+ // Druids and rangers get bonuses averaged on both personality and intellect
+ attrib = INTELLECT;
+ flag = true;
+ amount = result;
+ }
+ if (flag)
+ result = (amount + result) / 2;
+
+ result += itemScan(8);
+ if (result < 0)
+ result = 0;
+
+ return result;
+}
+
+/**
+ * Get the effective value of a given stat for the character
+ */
+uint Character::getStat(Attribute attrib, bool baseOnly) const {
+ AttributePair attr;
+ int mode = 0;
+
+ switch (attrib) {
+ case MIGHT:
+ attr = _might;
+ break;
+ case INTELLECT:
+ attr = _intellect;
+ mode = 1;
+ break;
+ case PERSONALITY:
+ attr = _personality;
+ mode = 1;
+ break;
+ case ENDURANCE:
+ attr = _endurance;
+ break;
+ case SPEED:
+ attr = _speed;
+ break;
+ case ACCURACY:
+ attr = _accuracy;
+ break;
+ case LUCK:
+ attr = _luck;
+ mode = 2;
+ break;
+ default:
+ return 0;
+ }
+
+ // All the attributes except luck are affected by the character's age
+ if (mode < 2) {
+ int age = getAge(false);
+ int ageIndex = 0;
+ while (AGE_RANGES[ageIndex] <= age)
+ ++ageIndex;
+
+ attr._permanent += AGE_RANGES_ADJUST[mode][ageIndex];
+ }
+
+
+ attr._permanent += itemScan((int)attrib);
+
+ if (!baseOnly) {
+ attr._permanent += conditionMod(attrib);
+ attr._permanent += attr._temporary;
+ }
+
+ 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(uint statValue) const {
+ int idx;
+ for (idx = 0; STAT_VALUES[idx] <= statValue; ++idx)
+ ;
+
+ return STAT_BONUSES[idx];
+}
+
+bool Character::charSavingThrow(DamageType attackType) const {
+ int v, vMax;
+
+ if (attackType == DT_PHYSICAL) {
+ v = statBonus(getStat(LUCK)) + getCurrentLevel();
+ vMax = v + 20;
+ } else {
+ switch (attackType) {
+ case DT_MAGICAL:
+ v = _magicResistence._permanent + _magicResistence._temporary + itemScan(16);
+ break;
+ case DT_FIRE:
+ v = _fireResistence._permanent + _fireResistence._temporary + itemScan(11);
+ break;
+ case DT_ELECTRICAL:
+ v = _electricityResistence._permanent + _electricityResistence._temporary + itemScan(12);
+ break;
+ case DT_COLD:
+ v = _coldResistence._permanent + _coldResistence._temporary + itemScan(13);
+ break;
+ case DT_POISON:
+ v = _poisonResistence._permanent + _poisonResistence._temporary + itemScan(14);
+ break;
+ case DT_ENERGY:
+ v = _energyResistence._permanent + _energyResistence._temporary + itemScan(15);
+ break;
+ default:
+ v = 0;
+ break;
+ }
+
+ vMax = v + 40;
+ }
+
+ return Party::_vm->getRandomNumber(1, vMax) <= v;
+}
+
+bool Character::noActions() {
+ Condition condition = worstCondition();
+
+ switch (condition) {
+ case CURSED:
+ case POISONED:
+ case DISEASED:
+ case INSANE:
+ case IN_LOVE:
+ case DRUNK: {
+ Common::String msg = Common::String::format(IN_NO_CONDITION, _name.c_str());
+ ErrorScroll::show(Party::_vm, msg,
+ Party::_vm->_mode == 17 ? WT_2 : WT_NONFREEZED_WAIT);
+ return true;
+ }
+ default:
+ return false;
+ }
+}
+
+void Character::setAward(int awardId, bool value) {
+ int v = awardId;
+ if (awardId == 73)
+ v = 126;
+ else if (awardId == 81)
+ v = 127;
+
+ _awards[v] = value;
+}
+
+bool Character::hasAward(int awardId) const {
+ int v = awardId;
+ if (awardId == 73)
+ v = 126;
+ else if (awardId == 81)
+ v = 127;
+
+ return _awards[v];
+}
+
+int Character::getArmorClass(bool baseOnly) const {
+ Party &party = *Party::_vm->_party;
+
+ int result = statBonus(getStat(SPEED)) + itemScan(9);
+ if (!baseOnly)
+ result += party._blessed + _ACTemp;
+
+ return MAX(result, 0);
+}
+
+/**
+ * Returns the thievery skill level, adjusted by class and race
+ */
+int Character::getThievery() const {
+ int result = getCurrentLevel() * 2;
+
+ if (_class == CLASS_NINJA)
+ result += 15;
+ else if (_class == CLASS_ROBBER)
+ result += 30;
+
+ switch (_race) {
+ case ELF:
+ case GNOME:
+ result += 10;
+ break;
+ case DWARF:
+ result += 5;
+ break;
+ case HALF_ORC:
+ result -= 10;
+ break;
+ default:
+ break;
+ }
+
+ result += itemScan(10);
+
+ // If the character doesn't have a thievery skill, then do'nt allow any result
+ if (!_skills[THIEVERY])
+ result = 0;
+
+ return MAX(result, 0);
+}
+
+uint Character::getCurrentLevel() const {
+ return MAX(_level._permanent + _level._temporary, (uint)0);
+}
+
+int Character::itemScan(int itemId) const {
+ int result = 0;
+
+ for (int accessIdx = 0; accessIdx < 3; ++accessIdx) {
+ switch (accessIdx) {
+ case 0:
+ for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) {
+ const XeenItem &item = _weapons[idx];
+
+ if (item._frame && !(item._bonusFlags & 0xC0) && itemId < 11
+ && itemId != 3 && item._material >= 59 && item._material <= 130) {
+ int mIndex = item.getAttributeCategory();
+ if (mIndex > 2)
+ ++mIndex;
+
+ if (mIndex == itemId)
+ result += ATTRIBUTE_BONUSES[item._material - 59];
+ }
+ }
+ break;
+
+ case 1:
+ for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) {
+ const XeenItem &item = _armor[idx];
+
+ if (item._frame && !(item._bonusFlags & 0xC0)) {
+ if (itemId < 11 && itemId != 3 && item._material >= 59 && item._material <= 130) {
+ int mIndex = item.getAttributeCategory();
+ if (mIndex > 2)
+ ++mIndex;
+
+ if (mIndex == itemId)
+ result += ATTRIBUTE_BONUSES[item._material - 59];
+ }
+
+ if (itemId > 10 && item._material < 37) {
+ int mIndex = item.getElementalCategory() + 11;
+
+ if (mIndex == itemId) {
+ result += ELEMENTAL_RESISTENCES[item._material];
+ }
+ }
+
+ if (itemId == 9) {
+ result += ARMOR_STRENGTHS[item._id];
+
+ if (item._material >= 37 && item._material <= 58)
+ result += METAL_LAC[item._material - 37];
+ }
+ }
+ }
+ break;
+
+ case 2:
+ for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) {
+ const XeenItem &item = _accessories[idx];
+
+ if (item._frame && !(item._bonusFlags & 0xC0) && itemId < 11 && itemId != 3) {
+ if (item._material >= 59 && item._material <= 130) {
+ int mIndex = item.getAttributeCategory();
+ if (mIndex > 2)
+ ++mIndex;
+
+ if (mIndex == itemId) {
+ result += ATTRIBUTE_BONUSES[item._material - 59];
+ }
+ }
+
+ if (itemId > 10 && item._material < 37) {
+ int mIndex = item.getElementalCategory() + 11;
+
+ if (mIndex == itemId)
+ result += ELEMENTAL_RESISTENCES[item._material];
+ }
+ }
+ }
+ break;
+ }
+ };
+
+ return result;
+}
+
+/**
+ * Modifies a passed attribute value based on player's condition
+ */
+int Character::conditionMod(Attribute attrib) const {
+ if (_conditions[DEAD] || _conditions[STONED] || _conditions[ERADICATED])
+ return 0;
+
+ int v[7];
+ Common::fill(&v[0], &v[7], 0);
+ if (_conditions[CURSED])
+ v[6] -= _conditions[CURSED];
+
+ if (_conditions[INSANE]) {
+ v[2] -= _conditions[INSANE];
+ v[1] -= _conditions[INSANE];
+ v[5] -= _conditions[INSANE];
+ v[0] -= _conditions[INSANE];
+ v[4] -= _conditions[INSANE];
+ }
+
+ if (_conditions[POISONED]) {
+ v[0] -= _conditions[POISONED];
+ v[4] -= _conditions[POISONED];
+ v[5] -= _conditions[POISONED];
+ }
+
+ if (_conditions[DISEASED]) {
+ v[3] -= _conditions[DISEASED];
+ v[2] -= _conditions[DISEASED];
+ v[1] -= _conditions[DISEASED];
+ }
+
+ for (int idx = 0; idx < 7; ++idx) {
+ v[idx] -= _conditions[HEART_BROKEN];
+ v[idx] -= _conditions[IN_LOVE];
+ v[idx] -= _conditions[WEAK];
+ v[idx] -= _conditions[DRUNK];
+ }
+
+ return v[attrib];
+}
+
+void Character::setValue(int id, uint value) {
+ Party &party = *Party::_vm->_party;
+ Scripts &scripts = *Party::_vm->_scripts;
+
+ switch (id) {
+ case 3:
+ // Set character sex
+ _sex = (Sex)value;
+ break;
+ case 4:
+ // Set race
+ _race = (Race)value;
+ break;
+ case 5:
+ // Set class
+ _class = (CharacterClass)value;
+ break;
+ case 8:
+ // Set the current Hp
+ _currentHp = value;
+ break;
+ case 9:
+ // Set the current Sp
+ _currentSp = value;
+ break;
+ case 10:
+ case 77:
+ // Set temporary armor class
+ _ACTemp = value;
+ break;
+ case 11:
+ // Set temporary level
+ _level._temporary = value;
+ break;
+ case 12:
+ // Set the character's temporary age
+ _tempAge = value;
+ break;
+ case 16:
+ // Set character experience
+ _experience = value;
+ break;
+ case 17:
+ // Set party poison resistence
+ party._poisonResistence = value;
+ break;
+ case 18:
+ // Set condition
+ if (value == 16) {
+ // Clear all the conditions
+ Common::fill(&_conditions[CURSED], &_conditions[NO_CONDITION], false);
+ } else if (value == 6) {
+ _conditions[value] = 1;
+ } else {
+ ++_conditions[value];
+ }
+
+ if (value >= DEAD && value <= ERADICATED && _currentHp > 0)
+ _currentHp = 0;
+ break;
+ case 25:
+ // Set time of day in minutes (0-1440)
+ party._minutes = value;
+ break;
+ case 34:
+ // Set party gold
+ party._gold = value;
+ break;
+ case 35:
+ // Set party gems
+ party._gems = value;
+ break;
+ case 37:
+ _might._temporary = value;
+ break;
+ case 38:
+ _intellect._temporary = value;
+ break;
+ case 39:
+ _personality._temporary = value;
+ break;
+ case 40:
+ _endurance._temporary = value;
+ break;
+ case 41:
+ _speed._temporary = value;
+ break;
+ case 42:
+ _accuracy._temporary = value;
+ break;
+ case 43:
+ _luck._temporary = value;
+ break;
+ case 45:
+ _might._permanent = value;
+ break;
+ case 46:
+ _intellect._permanent = value;
+ break;
+ case 47:
+ _personality._permanent = value;
+ break;
+ case 48:
+ _endurance._permanent = value;
+ break;
+ case 49:
+ _speed._permanent = value;
+ break;
+ case 50:
+ _accuracy._permanent = value;
+ break;
+ case 51:
+ _luck._permanent = value;
+ break;
+ case 52:
+ _fireResistence._permanent = value;
+ break;
+ case 53:
+ _electricityResistence._permanent = value;
+ break;
+ case 54:
+ _coldResistence._permanent = value;
+ break;
+ case 55:
+ _poisonResistence._permanent = value;
+ break;
+ case 56:
+ _energyResistence._permanent = value;
+ break;
+ case 57:
+ _magicResistence._permanent = value;
+ break;
+ case 58:
+ _fireResistence._temporary = value;
+ break;
+ case 59:
+ _electricityResistence._temporary = value;
+ break;
+ case 60:
+ _coldResistence._temporary = value;
+ break;
+ case 61:
+ _poisonResistence._temporary = value;
+ break;
+ case 62:
+ _energyResistence._temporary = value;
+ break;
+ case 63:
+ _magicResistence._temporary = value;
+ break;
+ case 64:
+ _level._permanent = value;
+ break;
+ case 65:
+ // Set party food
+ party._food = value;
+ break;
+ case 69:
+ // Set levitate active
+ party._levitateActive = value != 0;
+ break;
+ case 70:
+ party._lightCount = value;
+ break;
+ case 71:
+ party._fireResistence = value;
+ break;
+ case 72:
+ party._electricityResistence = value;
+ break;
+ case 73:
+ party._coldResistence = value;
+ break;
+ case 74:
+ party._walkOnWaterActive = value != 0;
+ party._poisonResistence = value;
+ party._wizardEyeActive = value != 0;
+ party._coldResistence = value;
+ party._electricityResistence = value;
+ party._fireResistence = value;
+ party._lightCount = value;
+ party._levitateActive = value != 0;
+ break;
+ case 76:
+ // Set day of the year (0-99)
+ party._day = value;
+ break;
+ case 79:
+ party._wizardEyeActive = true;
+ break;
+ case 83:
+ scripts._nEdamageType = value;
+ break;
+ case 84:
+ party._mazeDirection = (Direction)value;
+ break;
+ case 85:
+ party._year = value;
+ break;
+ case 94:
+ party._walkOnWaterActive = value != 0;
+ break;
+ default:
+ break;
+ }
+}
+
+bool Character::guildMember() const {
+ Party &party = *Party::_vm->_party;
+
+ if (party._mazeId == 49 && !Party::_vm->_files->_isDarkCc) {
+ return hasAward(5);
+ }
+
+ switch (party._mazeId) {
+ case 29:
+ return hasAward(83);
+ case 31:
+ return hasAward(84);
+ case 33:
+ return hasAward(85);
+ case 35:
+ return hasAward(86);
+ default:
+ return hasAward(87);
+ }
+}
+
+uint Character::experienceToNextLevel() const {
+ uint next = nextExperienceLevel();
+ uint curr = getCurrentExperience();
+ return (curr >= next) ? 0 : next - curr;
+}
+
+uint Character::nextExperienceLevel() const {
+ int shift, base;
+ if (_level._permanent >= 12) {
+ base = _level._permanent - 12;
+ shift = 10;
+ } else {
+ base = 0;
+ shift = _level._permanent - 1;
+ }
+
+ return (base * 1024000) + (CLASS_EXP_LEVELS[_class] << shift);
+}
+
+uint Character::getCurrentExperience() const {
+ int lev = _level._permanent - 1;
+ int shift, base;
+
+ if (lev > 0 && lev < 12)
+ return _experience;
+
+ if (lev >= 12) {
+ base = lev - 12;
+ shift = 10;
+ } else {
+ base = 0;
+ shift = lev - 1;
+ }
+
+ return (base * 1024000) + (CLASS_EXP_LEVELS[_class] << shift) +
+ _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;
+}
+
+/**
+ * Assembles a full lines description for a specified item for use in
+ * the Items dialog
+ */
+Common::String Character::assembleItemName(int itemIndex, int displayNum,
+ ItemCategory category) {
+ Spells &spells = *Party::_vm->_spells;
+
+ switch (category) {
+ case CATEGORY_WEAPON: {
+ // Weapons
+ XeenItem &i = _weapons[itemIndex];
+ return Common::String::format("\f%02u%s%s%s\f%02u%s%s%s", displayNum,
+ !i._bonusFlags ? spells._maeNames[i._material] : "",
+ (i._bonusFlags & ITEMFLAG_BROKEN) ? ITEM_BROKEN : "",
+ (i._bonusFlags & ITEMFLAG_CURSED) ? ITEM_CURSED : "",
+ WEAPON_NAMES[i._id],
+ !i._bonusFlags ? "" : BONUS_NAMES[i._bonusFlags & ITEMFLAG_BONUS_MASK],
+ (i._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED)) ||
+ !i._bonusFlags ? "\b " : ""
+ );
+ }
+
+ case CATEGORY_ARMOR: {
+ // Armor
+ XeenItem &i = _armor[itemIndex];
+ return Common::String::format("\f%02u%s%s%s\f%02u%s%s", displayNum,
+ !i._bonusFlags ? "" : spells._maeNames[i._material],
+ (i._bonusFlags & ITEMFLAG_BROKEN) ? ITEM_BROKEN : "",
+ (i._bonusFlags & ITEMFLAG_CURSED) ? ITEM_CURSED : "",
+ ARMOR_NAMES[i._id],
+ (i._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED)) ||
+ !i._bonusFlags ? "\b " : ""
+ );
+ }
+
+ case CATEGORY_ACCESSORY: {
+ // Accessories
+ XeenItem &i = _accessories[itemIndex];
+ return Common::String::format("\f%02u%s%s%s\f%02u%s%s", displayNum,
+ !i._bonusFlags ? "" : spells._maeNames[i._material],
+ (i._bonusFlags & ITEMFLAG_BROKEN) ? ITEM_BROKEN : "",
+ (i._bonusFlags & ITEMFLAG_CURSED) ? ITEM_CURSED : "",
+ ARMOR_NAMES[i._id],
+ (i._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED)) ||
+ !i._bonusFlags ? "\b " : ""
+ );
+ }
+
+ case CATEGORY_MISC: {
+ // Misc
+ XeenItem &i = _misc[itemIndex];
+ return Common::String::format("\f%02u%s%s%s\f%02u%s%s", displayNum,
+ !i._bonusFlags ? "" : spells._maeNames[i._material],
+ (i._bonusFlags & ITEMFLAG_BROKEN) ? ITEM_BROKEN : "",
+ (i._bonusFlags & ITEMFLAG_CURSED) ? ITEM_CURSED : "",
+ ARMOR_NAMES[i._id],
+ (i._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED)) ||
+ !i._id ? "\b " : ""
+ );
+ }
+ default:
+ return "";
+ }
+}
+
+} // End of namespace Xeen
diff --git a/engines/xeen/character.h b/engines/xeen/character.h
new file mode 100644
index 0000000000..54da44a011
--- /dev/null
+++ b/engines/xeen/character.h
@@ -0,0 +1,231 @@
+/* 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_CHARACTER_H
+#define XEEN_CHARACTER_H
+
+#include "common/scummsys.h"
+#include "common/array.h"
+#include "common/rect.h"
+#include "common/serializer.h"
+#include "xeen/combat.h"
+
+namespace Xeen {
+
+#define INV_ITEMS_TOTAL 9
+
+enum BonusFlags {
+ ITEMFLAG_BONUS_MASK = 0xBF, ITEMFLAG_CURSED = 0x40, ITEMFLAG_BROKEN = 0x80
+};
+
+enum ItemCategory {
+ CATEGORY_WEAPON = 0, CATEGORY_ARMOR = 1, CATEGORY_ACCESSORY = 2, CATEGORY_MISC = 3
+};
+
+enum Sex { MALE = 0, FEMALE = 1, YES_PLEASE = 2 };
+
+enum Race { HUMAN = 0, ELF = 1, DWARF = 2, GNOME = 3, HALF_ORC = 4 };
+
+enum CharacterClass {
+ CLASS_KNIGHT = 0, CLASS_PALADIN = 1, CLASS_ARCHER = 2, CLASS_CLERIC = 3,
+ CLASS_SORCERER = 4, CLASS_ROBBER = 5, CLASS_NINJA = 6, CLASS_BARBARIAN = 7,
+ CLASS_DRUID = 8, CLASS_RANGER = 9,
+ CLASS_12 = 12, CLASS_15 = 15, CLASS_16 = 16
+};
+
+enum Attribute {
+ MIGHT = 0, INTELLECT = 1, PERSONALITY = 2, ENDURANCE = 3, SPEED = 4,
+ ACCURACY = 5, LUCK = 6
+};
+
+enum Skill {
+ THIEVERY = 0, ARMS_MASTER = 1, ASTROLOGER = 2, BODYBUILDER = 3,
+ CARTOGRAPHER = 4, CRUSADER = 5, DIRECTION_SENSE = 6, LINGUIST = 7,
+ MERCHANT = 8, MOUNTAINEER = 9, NAVIGATOR = 10, PATHFINDER = 11,
+ PRAYER_MASTER = 12, PRESTIDIGITATION = 13, SWIMMING = 14, TRACKING = 15,
+ SPOT_DOORS = 16, DANGER_SENSE = 17
+};
+
+enum Condition {
+ CURSED = 0, HEART_BROKEN = 1, WEAK = 2, POISONED = 3,
+ DISEASED = 4, INSANE = 5, IN_LOVE = 6, DRUNK = 7, SLEEP = 8,
+ DEPRESSED = 9, CONFUSED = 10, PARALYZED = 11, UNCONSCIOUS = 12,
+ DEAD = 13, STONED = 14, ERADICATED = 15,
+ NO_CONDITION = 16
+};
+
+class XeenEngine;
+
+class XeenItem {
+public:
+ int _material;
+ uint _id;
+ int _bonusFlags;
+ int _frame;
+public:
+ XeenItem();
+
+ void clear();
+
+ void synchronize(Common::Serializer &s);
+
+ int getElementalCategory() const;
+
+ int getAttributeCategory() const;
+};
+
+class InventoryItems : public Common::Array<XeenItem> {
+public:
+ InventoryItems();
+
+ void discardItem(int itemIndex);
+
+ void equipItem(int itemIndex);
+
+ void removeItem(int itemIndex);
+
+ void sort();
+};
+
+class InventoryItemsGroup {
+private:
+ InventoryItems *_itemSets[4];
+public:
+ InventoryItemsGroup(InventoryItems &weapons, InventoryItems &armor,
+ InventoryItems &accessories, InventoryItems &misc);
+
+ InventoryItems &operator[](ItemCategory category);
+};
+
+
+class AttributePair {
+public:
+ uint _permanent;
+ uint _temporary;
+public:
+ AttributePair();
+ void synchronize(Common::Serializer &s);
+};
+
+class Character {
+private:
+ int conditionMod(Attribute attrib) const;
+public:
+ Common::String _name;
+ Sex _sex;
+ Race _race;
+ int _xeenSide;
+ CharacterClass _class;
+ AttributePair _might;
+ AttributePair _intellect;
+ AttributePair _personality;
+ AttributePair _endurance;
+ AttributePair _speed;
+ AttributePair _accuracy;
+ AttributePair _luck;
+ int _ACTemp;
+ AttributePair _level;
+ uint _birthDay;
+ int _tempAge;
+ int _skills[18];
+ bool _awards[128];
+ int _spells[39];
+ int _lloydMap;
+ Common::Point _lloydPosition;
+ bool _hasSpells;
+ int _currentSpell;
+ int _quickOption;
+ InventoryItemsGroup _items;
+ InventoryItems _weapons;
+ InventoryItems _armor;
+ InventoryItems _accessories;
+ InventoryItems _misc;
+ int _lloydSide;
+ AttributePair _fireResistence;
+ AttributePair _coldResistence;
+ AttributePair _electricityResistence;
+ AttributePair _poisonResistence;
+ AttributePair _energyResistence;
+ AttributePair _magicResistence;
+ int _conditions[16];
+ int _townUnknown;
+ int _savedMazeId;
+ int _currentHp;
+ int _currentSp;
+ uint _birthYear;
+ uint32 _experience;
+ int _currentAdventuringSpell;
+ int _currentCombatSpell;
+public:
+ Character();
+ void synchronize(Common::Serializer &s);
+
+ Condition worstCondition() const;
+
+ int getAge(bool ignoreTemp = false) const;
+
+ int getMaxHP() const;
+
+ int getMaxSP() const;
+
+ uint getStat(Attribute attrib, bool baseOnly = false) const;
+
+ static int statColor(int amount, int threshold);
+
+ int statBonus(uint statValue) const;
+
+ bool charSavingThrow(DamageType attackType) const;
+
+ bool noActions();
+
+ void setAward(int awardId, bool value);
+
+ bool hasAward(int awardId) const;
+
+ int getArmorClass(bool baseOnly = false) const;
+
+ int getThievery() const;
+
+ uint getCurrentLevel() const;
+
+ int itemScan(int itemId) const;
+
+ void setValue(int id, uint value);
+
+ bool guildMember() const;
+
+ uint experienceToNextLevel() const;
+
+ uint nextExperienceLevel() const;
+
+ uint getCurrentExperience() const;
+
+ int getNumSkills() const;
+
+ int getNumAwards() const;
+
+ Common::String assembleItemName(int itemIndex, int displayNum, ItemCategory category);
+};
+
+} // End of namespace Xeen
+
+#endif /* XEEN_CHARACTER_H */
diff --git a/engines/xeen/items.cpp b/engines/xeen/items.cpp
index 13418262bd..b9b531db7b 100644
--- a/engines/xeen/items.cpp
+++ b/engines/xeen/items.cpp
@@ -25,92 +25,6 @@
namespace Xeen {
-XeenItem::XeenItem() {
- clear();
-}
-
-void XeenItem::clear() {
- _material = _id = _bonusFlags = 0;
- _frame = 0;
-}
-
-void XeenItem::synchronize(Common::Serializer &s) {
- s.syncAsByte(_material);
- s.syncAsByte(_id);
- s.syncAsByte(_bonusFlags);
- s.syncAsByte(_frame);
-}
-
-int XeenItem::getElementalCategory() const {
- int idx;
- for (idx = 0; ELEMENTAL_CATEGORIES[idx] < _material; ++idx)
- ;
-
- return idx;
-}
-
-int XeenItem::getAttributeCategory() const {
- int m = _material - 59;
- int idx;
- for (idx = 0; ATTRIBUTE_CATEGORIES[idx] < m; ++idx)
- ;
-
- return idx;
-}
-
-/*------------------------------------------------------------------------*/
-
-InventoryItems::InventoryItems() : Common::Array<XeenItem>((XeenItem *)nullptr, INV_ITEMS_TOTAL) {
-}
-
-void InventoryItems::discardItem(int itemIndex) {
- operator[](itemIndex).clear();
- sort();
-}
-
-void InventoryItems::sort() {
- for (uint idx = 0; idx < size(); ++idx) {
- if (operator[](idx)._id == 0) {
- // Found empty slot
- operator[](idx).clear();
-
- // Scan through the rest of the list to find any item
- for (uint idx2 = idx + 1; idx2 < size(); ++idx2) {
- if (operator[](idx2)._id) {
- // Found an item, so move it into the blank slot
- operator[](idx) = operator[](idx2);
- operator[](idx2).clear();
- break;
- }
- }
- }
- }
-}
-
-void InventoryItems::equipItem(int itemIndex) {
- error("TODO");
-}
-
-void InventoryItems::removeItem(int itemIndex) {
- error("TODO");
-}
-
-/*------------------------------------------------------------------------*/
-
-InventoryItemsGroup::InventoryItemsGroup(InventoryItems &weapons, InventoryItems &armor,
- InventoryItems &accessories, InventoryItems &misc) {
- _itemSets[0] = &weapons;
- _itemSets[1] = &armor;
- _itemSets[2] = &accessories;
- _itemSets[3] = &misc;
-}
-
-InventoryItems &InventoryItemsGroup::operator[](ItemCategory category) {
- return *_itemSets[category];
-}
-
-/*------------------------------------------------------------------------*/
-
Treasure::Treasure() {
_hasItems = false;
}
diff --git a/engines/xeen/items.h b/engines/xeen/items.h
index c0b82b9fce..1e208615f8 100644
--- a/engines/xeen/items.h
+++ b/engines/xeen/items.h
@@ -23,63 +23,11 @@
#ifndef XEEN_ITEMS_H
#define XEEN_ITEMS_H
-#include "common/scummsys.h"
-#include "common/array.h"
-#include "common/serializer.h"
+#include "xeen/character.h"
namespace Xeen {
#define TOTAL_ITEMS 10
-#define INV_ITEMS_TOTAL 9
-
-enum BonusFlags {
- ITEMFLAG_BONUS_MASK = 0xBF, ITEMFLAG_CURSED = 0x40, ITEMFLAG_BROKEN = 0x80
-};
-
-enum ItemCategory {
- CATEGORY_WEAPON = 0, CATEGORY_ARMOR = 1, CATEGORY_ACCESSORY = 2, CATEGORY_MISC = 3
-};
-
-class XeenItem {
-public:
- int _material;
- uint _id;
- int _bonusFlags;
- int _frame;
-public:
- XeenItem();
-
- void clear();
-
- void synchronize(Common::Serializer &s);
-
- int getElementalCategory() const;
-
- int getAttributeCategory() const;
-};
-
-class InventoryItems : public Common::Array<XeenItem> {
-public:
- InventoryItems();
-
- void discardItem(int itemIndex);
-
- void equipItem(int itemIndex);
-
- void removeItem(int itemIndex);
-
- void sort();
-};
-
-class InventoryItemsGroup {
-private:
- InventoryItems *_itemSets[4];
-public:
- InventoryItemsGroup(InventoryItems &weapons, InventoryItems &armor,
- InventoryItems &accessories, InventoryItems &misc);
-
- InventoryItems &operator[](ItemCategory category);
-};
class Treasure {
public:
diff --git a/engines/xeen/module.mk b/engines/xeen/module.mk
index c1cdd055b8..25568523cd 100644
--- a/engines/xeen/module.mk
+++ b/engines/xeen/module.mk
@@ -4,6 +4,7 @@ MODULE_OBJS := \
clouds\clouds_game.o \
darkside\darkside_game.o \
worldofxeen\worldofxeen_game.o \
+ character.o \
combat.o \
debugger.o \
detection.o \
diff --git a/engines/xeen/party.cpp b/engines/xeen/party.cpp
index 7d47eafaf4..be8f4e6e18 100644
--- a/engines/xeen/party.cpp
+++ b/engines/xeen/party.cpp
@@ -32,897 +32,6 @@
namespace Xeen {
-AttributePair::AttributePair() {
- _temporary = _permanent = 0;
-}
-
-void AttributePair::synchronize(Common::Serializer &s) {
- s.syncAsByte(_permanent);
- s.syncAsByte(_temporary);
-}
-
-/*------------------------------------------------------------------------*/
-
-Character::Character(): _items(_weapons, _armor, _accessories, _misc) {
- _sex = MALE;
- _race = HUMAN;
- _xeenSide = 0;
- _class = CLASS_KNIGHT;
- _ACTemp = 0;
- _birthDay = 0;
- _tempAge = 0;
- Common::fill(&_skills[0], &_skills[18], 0);
- Common::fill(&_awards[0], &_awards[128], false);
- Common::fill(&_spells[0], &_spells[39], 0);
- _lloydMap = 0;
- _hasSpells = false;
- _currentSpell = 0;
- _quickOption = 0;
- _lloydSide = 0;
- Common::fill(&_conditions[0], &_conditions[16], 0);
- _townUnknown = 0;
- _savedMazeId = 0;
- _currentHp = 0;
- _currentSp = 0;
- _birthYear = 0;
- _experience = 0;
- _currentAdventuringSpell = 0;
- _currentCombatSpell = 0;
-}
-
-void Character::synchronize(Common::Serializer &s) {
- char name[16];
- Common::fill(&name[0], &name[16], '\0');
- strncpy(name, _name.c_str(), 16);
- s.syncBytes((byte *)name, 16);
-
- if (s.isLoading())
- _name = Common::String(name);
-
- s.syncAsByte(_sex);
- s.syncAsByte(_race);
- s.syncAsByte(_xeenSide);
- s.syncAsByte(_class);
-
- _might.synchronize(s);
- _intellect.synchronize(s);
- _personality.synchronize(s);
- _endurance.synchronize(s);
- _speed.synchronize(s);
- _accuracy.synchronize(s);
- _luck.synchronize(s);
- s.syncAsByte(_ACTemp);
- _level.synchronize(s);
- s.syncAsByte(_birthDay);
- s.syncAsByte(_tempAge);
-
- // Synchronize the skill list
- for (int idx = 0; idx < 18; ++idx)
- s.syncAsByte(_skills[idx]);
-
- // Synchronize character awards
- for (int idx = 0; idx < 64; ++idx) {
- byte b = (_awards[idx] ? 1 : 0) | (_awards[idx + 64] ? 0x10 : 0);
- s.syncAsByte(b);
- if (s.isLoading()) {
- _awards[idx] = (b & 0xF) != 0;
- _awards[idx + 64] = (b & 0xF0) != 0;
- }
- }
-
- // Synchronize spell list
- for (int i = 0; i < MAX_SPELLS_PER_CLASS - 1; ++i)
- s.syncAsByte(_spells[i]);
- s.syncAsByte(_lloydMap);
- s.syncAsByte(_lloydPosition.x);
- s.syncAsByte(_lloydPosition.y);
- s.syncAsByte(_hasSpells);
- s.syncAsByte(_currentSpell);
- s.syncAsByte(_quickOption);
-
- for (int i = 0; i < 9; ++i)
- _weapons[i].synchronize(s);
- for (int i = 0; i < 9; ++i)
- _armor[i].synchronize(s);
- for (int i = 0; i < 9; ++i)
- _accessories[i].synchronize(s);
- for (int i = 0; i < 9; ++i)
- _misc[i].synchronize(s);
-
- s.syncAsByte(_lloydSide);
- _fireResistence.synchronize(s);
- _coldResistence.synchronize(s);
- _electricityResistence.synchronize(s);
- _poisonResistence.synchronize(s);
- _energyResistence.synchronize(s);
- _magicResistence.synchronize(s);
-
- for (int i = 0; i < 16; ++i)
- s.syncAsByte(_conditions[i]);
-
- s.syncAsUint16LE(_townUnknown);
- s.syncAsByte(_savedMazeId);
- s.syncAsUint16LE(_currentHp);
- s.syncAsUint16LE(_currentSp);
- s.syncAsUint16LE(_birthYear);
- s.syncAsUint32LE(_experience);
- s.syncAsByte(_currentAdventuringSpell);
- s.syncAsByte(_currentCombatSpell);
-}
-
-Condition Character::worstCondition() const {
- for (int cond = ERADICATED; cond >= CURSED; --cond) {
- if (_conditions[cond])
- return (Condition)cond;
- }
-
- return NO_CONDITION;
-}
-
-int Character::getAge(bool ignoreTemp) const {
- int year = MIN(Party::_vm->_party->_year - _birthYear, (uint)254);
-
- return ignoreTemp ? year : year + _tempAge;
-}
-
-int Character::getMaxHP() const {
- int hp = BASE_HP_BY_CLASS[_class];
- hp += statBonus(getStat(ENDURANCE));
- hp += RACE_HP_BONUSES[_race];
- if (_skills[BODYBUILDER])
- ++hp;
- if (hp < 1)
- hp = 1;
-
- hp *= getCurrentLevel();
- hp += itemScan(7);
-
- return MAX(hp, 0);
-}
-
-int Character::getMaxSP() const {
- int result = 0;
- bool flag = false;
- int amount = 0;
- Attribute attrib;
- Skill skill;
-
- if (!_hasSpells)
- return 0;
-
- if (_class == CLASS_SORCERER || _class == CLASS_ARCHER) {
- attrib = INTELLECT;
- skill = PRESTIDIGITATION;
- } else {
- attrib = PERSONALITY;
- skill = PRAYER_MASTER;
- }
- if (_class == CLASS_DRUID || _class == CLASS_RANGER)
- skill = ASTROLOGER;
-
- for (;;) {
- // Get the base number of spell points
- result = statBonus(getStat(attrib)) + 3;
- result += RACE_SP_BONUSES[_race][attrib - 1];
-
- if (_skills[skill])
- result += 2;
- if (result < 1)
- result = 1;
-
- // Multiply it by the character's level
- result *= getCurrentLevel();
-
- // Classes other than sorcerer, clerics, and druids only get half the SP
- if (_class != CLASS_SORCERER && _class != CLASS_CLERIC && _class != CLASS_DRUID)
- result /= 2;
-
- if (flag || (_class != CLASS_DRUID && _class != CLASS_RANGER))
- break;
-
- // Druids and rangers get bonuses averaged on both personality and intellect
- attrib = INTELLECT;
- flag = true;
- amount = result;
- }
- if (flag)
- result = (amount + result) / 2;
-
- result += itemScan(8);
- if (result < 0)
- result = 0;
-
- return result;
-}
-
-/**
- * Get the effective value of a given stat for the character
- */
-uint Character::getStat(Attribute attrib, bool baseOnly) const {
- AttributePair attr;
- int mode = 0;
-
- switch (attrib) {
- case MIGHT:
- attr = _might;
- break;
- case INTELLECT:
- attr = _intellect;
- mode = 1;
- break;
- case PERSONALITY:
- attr = _personality;
- mode = 1;
- break;
- case ENDURANCE:
- attr = _endurance;
- break;
- case SPEED:
- attr = _speed;
- break;
- case ACCURACY:
- attr = _accuracy;
- break;
- case LUCK:
- attr = _luck;
- mode = 2;
- break;
- default:
- return 0;
- }
-
- // All the attributes except luck are affected by the character's age
- if (mode < 2) {
- int age = getAge(false);
- int ageIndex = 0;
- while (AGE_RANGES[ageIndex] <= age)
- ++ageIndex;
-
- attr._permanent += AGE_RANGES_ADJUST[mode][ageIndex];
- }
-
-
- attr._permanent += itemScan((int)attrib);
-
- if (!baseOnly) {
- attr._permanent += conditionMod(attrib);
- attr._permanent += attr._temporary;
- }
-
- 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(uint statValue) const {
- int idx;
- for (idx = 0; STAT_VALUES[idx] <= statValue; ++idx)
- ;
-
- return STAT_BONUSES[idx];
-}
-
-bool Character::charSavingThrow(DamageType attackType) const {
- int v, vMax;
-
- if (attackType == DT_PHYSICAL) {
- v = statBonus(getStat(LUCK)) + getCurrentLevel();
- vMax = v + 20;
- } else {
- switch (attackType) {
- case DT_MAGICAL:
- v = _magicResistence._permanent + _magicResistence._temporary + itemScan(16);
- break;
- case DT_FIRE:
- v = _fireResistence._permanent + _fireResistence._temporary + itemScan(11);
- break;
- case DT_ELECTRICAL:
- v = _electricityResistence._permanent + _electricityResistence._temporary + itemScan(12);
- break;
- case DT_COLD:
- v = _coldResistence._permanent + _coldResistence._temporary + itemScan(13);
- break;
- case DT_POISON:
- v = _poisonResistence._permanent + _poisonResistence._temporary + itemScan(14);
- break;
- case DT_ENERGY:
- v = _energyResistence._permanent + _energyResistence._temporary + itemScan(15);
- break;
- default:
- v = 0;
- break;
- }
-
- vMax = v + 40;
- }
-
- return Party::_vm->getRandomNumber(1, vMax) <= v;
-}
-
-bool Character::noActions() {
- Condition condition = worstCondition();
-
- switch (condition) {
- case CURSED:
- case POISONED:
- case DISEASED:
- case INSANE:
- case IN_LOVE:
- case DRUNK: {
- Common::String msg = Common::String::format(IN_NO_CONDITION, _name.c_str());
- ErrorScroll::show(Party::_vm, msg,
- Party::_vm->_mode == 17 ? WT_2 : WT_NONFREEZED_WAIT);
- return true;
- }
- default:
- return false;
- }
-}
-
-void Character::setAward(int awardId, bool value) {
- int v = awardId;
- if (awardId == 73)
- v = 126;
- else if (awardId == 81)
- v = 127;
-
- _awards[v] = value;
-}
-
-bool Character::hasAward(int awardId) const {
- int v = awardId;
- if (awardId == 73)
- v = 126;
- else if (awardId == 81)
- v = 127;
-
- return _awards[v];
-}
-
-int Character::getArmorClass(bool baseOnly) const {
- Party &party = *Party::_vm->_party;
-
- int result = statBonus(getStat(SPEED)) + itemScan(9);
- if (!baseOnly)
- result += party._blessed + _ACTemp;
-
- return MAX(result, 0);
-}
-
-/**
- * Returns the thievery skill level, adjusted by class and race
- */
-int Character::getThievery() const {
- int result = getCurrentLevel() * 2;
-
- if (_class == CLASS_NINJA)
- result += 15;
- else if (_class == CLASS_ROBBER)
- result += 30;
-
- switch (_race) {
- case ELF:
- case GNOME:
- result += 10;
- break;
- case DWARF:
- result += 5;
- break;
- case HALF_ORC:
- result -= 10;
- break;
- default:
- break;
- }
-
- result += itemScan(10);
-
- // If the character doesn't have a thievery skill, then do'nt allow any result
- if (!_skills[THIEVERY])
- result = 0;
-
- return MAX(result, 0);
-}
-
-uint Character::getCurrentLevel() const {
- return MAX(_level._permanent + _level._temporary, (uint)0);
-}
-
-int Character::itemScan(int itemId) const {
- int result = 0;
-
- for (int accessIdx = 0; accessIdx < 3; ++accessIdx) {
- switch (accessIdx) {
- case 0:
- for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) {
- const XeenItem &item = _weapons[idx];
-
- if (item._frame && !(item._bonusFlags & 0xC0) && itemId < 11
- && itemId != 3 && item._material >= 59 && item._material <= 130) {
- int mIndex = item.getAttributeCategory();
- if (mIndex > 2)
- ++mIndex;
-
- if (mIndex == itemId)
- result += ATTRIBUTE_BONUSES[item._material - 59];
- }
- }
- break;
-
- case 1:
- for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) {
- const XeenItem &item = _armor[idx];
-
- if (item._frame && !(item._bonusFlags & 0xC0)) {
- if (itemId < 11 && itemId != 3 && item._material >= 59 && item._material <= 130) {
- int mIndex = item.getAttributeCategory();
- if (mIndex > 2)
- ++mIndex;
-
- if (mIndex == itemId)
- result += ATTRIBUTE_BONUSES[item._material - 59];
- }
-
- if (itemId > 10 && item._material < 37) {
- int mIndex = item.getElementalCategory() + 11;
-
- if (mIndex == itemId) {
- result += ELEMENTAL_RESISTENCES[item._material];
- }
- }
-
- if (itemId == 9) {
- result += ARMOR_STRENGTHS[item._id];
-
- if (item._material >= 37 && item._material <= 58)
- result += METAL_LAC[item._material - 37];
- }
- }
- }
- break;
-
- case 2:
- for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) {
- const XeenItem &item = _accessories[idx];
-
- if (item._frame && !(item._bonusFlags & 0xC0) && itemId < 11 && itemId != 3) {
- if (item._material >= 59 && item._material <= 130) {
- int mIndex = item.getAttributeCategory();
- if (mIndex > 2)
- ++mIndex;
-
- if (mIndex == itemId) {
- result += ATTRIBUTE_BONUSES[item._material - 59];
- }
- }
-
- if (itemId > 10 && item._material < 37) {
- int mIndex = item.getElementalCategory() + 11;
-
- if (mIndex == itemId)
- result += ELEMENTAL_RESISTENCES[item._material];
- }
- }
- }
- break;
- }
- };
-
- return result;
-}
-
-/**
- * Modifies a passed attribute value based on player's condition
- */
-int Character::conditionMod(Attribute attrib) const {
- if (_conditions[DEAD] || _conditions[STONED] || _conditions[ERADICATED])
- return 0;
-
- int v[7];
- Common::fill(&v[0], &v[7], 0);
- if (_conditions[CURSED])
- v[6] -= _conditions[CURSED];
-
- if (_conditions[INSANE]) {
- v[2] -= _conditions[INSANE];
- v[1] -= _conditions[INSANE];
- v[5] -= _conditions[INSANE];
- v[0] -= _conditions[INSANE];
- v[4] -= _conditions[INSANE];
- }
-
- if (_conditions[POISONED]) {
- v[0] -= _conditions[POISONED];
- v[4] -= _conditions[POISONED];
- v[5] -= _conditions[POISONED];
- }
-
- if (_conditions[DISEASED]) {
- v[3] -= _conditions[DISEASED];
- v[2] -= _conditions[DISEASED];
- v[1] -= _conditions[DISEASED];
- }
-
- for (int idx = 0; idx < 7; ++idx) {
- v[idx] -= _conditions[HEART_BROKEN];
- v[idx] -= _conditions[IN_LOVE];
- v[idx] -= _conditions[WEAK];
- v[idx] -= _conditions[DRUNK];
- }
-
- return v[attrib];
-}
-
-void Character::setValue(int id, uint value) {
- Party &party = *Party::_vm->_party;
- Scripts &scripts = *Party::_vm->_scripts;
-
- switch (id) {
- case 3:
- // Set character sex
- _sex = (Sex)value;
- break;
- case 4:
- // Set race
- _race = (Race)value;
- break;
- case 5:
- // Set class
- _class = (CharacterClass)value;
- break;
- case 8:
- // Set the current Hp
- _currentHp = value;
- break;
- case 9:
- // Set the current Sp
- _currentSp = value;
- break;
- case 10:
- case 77:
- // Set temporary armor class
- _ACTemp = value;
- break;
- case 11:
- // Set temporary level
- _level._temporary = value;
- break;
- case 12:
- // Set the character's temporary age
- _tempAge = value;
- break;
- case 16:
- // Set character experience
- _experience = value;
- break;
- case 17:
- // Set party poison resistence
- party._poisonResistence = value;
- break;
- case 18:
- // Set condition
- if (value == 16) {
- // Clear all the conditions
- Common::fill(&_conditions[CURSED], &_conditions[NO_CONDITION], false);
- } else if (value == 6) {
- _conditions[value] = 1;
- } else {
- ++_conditions[value];
- }
-
- if (value >= DEAD && value <= ERADICATED && _currentHp > 0)
- _currentHp = 0;
- break;
- case 25:
- // Set time of day in minutes (0-1440)
- party._minutes = value;
- break;
- case 34:
- // Set party gold
- party._gold = value;
- break;
- case 35:
- // Set party gems
- party._gems = value;
- break;
- case 37:
- _might._temporary = value;
- break;
- case 38:
- _intellect._temporary = value;
- break;
- case 39:
- _personality._temporary = value;
- break;
- case 40:
- _endurance._temporary = value;
- break;
- case 41:
- _speed._temporary = value;
- break;
- case 42:
- _accuracy._temporary = value;
- break;
- case 43:
- _luck._temporary = value;
- break;
- case 45:
- _might._permanent = value;
- break;
- case 46:
- _intellect._permanent = value;
- break;
- case 47:
- _personality._permanent = value;
- break;
- case 48:
- _endurance._permanent = value;
- break;
- case 49:
- _speed._permanent = value;
- break;
- case 50:
- _accuracy._permanent = value;
- break;
- case 51:
- _luck._permanent = value;
- break;
- case 52:
- _fireResistence._permanent = value;
- break;
- case 53:
- _electricityResistence._permanent = value;
- break;
- case 54:
- _coldResistence._permanent = value;
- break;
- case 55:
- _poisonResistence._permanent = value;
- break;
- case 56:
- _energyResistence._permanent = value;
- break;
- case 57:
- _magicResistence._permanent = value;
- break;
- case 58:
- _fireResistence._temporary = value;
- break;
- case 59:
- _electricityResistence._temporary = value;
- break;
- case 60:
- _coldResistence._temporary = value;
- break;
- case 61:
- _poisonResistence._temporary = value;
- break;
- case 62:
- _energyResistence._temporary = value;
- break;
- case 63:
- _magicResistence._temporary = value;
- break;
- case 64:
- _level._permanent = value;
- break;
- case 65:
- // Set party food
- party._food = value;
- break;
- case 69:
- // Set levitate active
- party._levitateActive = value != 0;
- break;
- case 70:
- party._lightCount = value;
- break;
- case 71:
- party._fireResistence = value;
- break;
- case 72:
- party._electricityResistence = value;
- break;
- case 73:
- party._coldResistence = value;
- break;
- case 74:
- party._walkOnWaterActive = value != 0;
- party._poisonResistence = value;
- party._wizardEyeActive = value != 0;
- party._coldResistence = value;
- party._electricityResistence = value;
- party._fireResistence = value;
- party._lightCount = value;
- party._levitateActive = value != 0;
- break;
- case 76:
- // Set day of the year (0-99)
- party._day = value;
- break;
- case 79:
- party._wizardEyeActive = true;
- break;
- case 83:
- scripts._nEdamageType = value;
- break;
- case 84:
- party._mazeDirection = (Direction)value;
- break;
- case 85:
- party._year = value;
- break;
- case 94:
- party._walkOnWaterActive = value != 0;
- break;
- default:
- break;
- }
-}
-
-bool Character::guildMember() const {
- Party &party = *Party::_vm->_party;
-
- if (party._mazeId == 49 && !Party::_vm->_files->_isDarkCc) {
- return hasAward(5);
- }
-
- switch (party._mazeId) {
- case 29:
- return hasAward(83);
- case 31:
- return hasAward(84);
- case 33:
- return hasAward(85);
- case 35:
- return hasAward(86);
- default:
- return hasAward(87);
- }
-}
-
-uint Character::experienceToNextLevel() const {
- uint next = nextExperienceLevel();
- uint curr = getCurrentExperience();
- return (curr >= next) ? 0 : next - curr;
-}
-
-uint Character::nextExperienceLevel() const {
- int shift, base;
- if (_level._permanent >= 12) {
- base = _level._permanent - 12;
- shift = 10;
- } else {
- base = 0;
- shift = _level._permanent - 1;
- }
-
- return (base * 1024000) + (CLASS_EXP_LEVELS[_class] << shift);
-}
-
-uint Character::getCurrentExperience() const {
- int lev = _level._permanent - 1;
- int shift, base;
-
- if (lev > 0 && lev < 12)
- return _experience;
-
- if (lev >= 12) {
- base = lev - 12;
- shift = 10;
- } else {
- base = 0;
- shift = lev - 1;
- }
-
- return (base * 1024000) + (CLASS_EXP_LEVELS[_class] << shift) +
- _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;
-}
-
-/**
- * Assembles a full lines description for a specified item for use in
- * the Items dialog
- */
-Common::String Character::assembleItemName(int itemIndex, int displayNum,
- ItemCategory category) {
- Spells &spells = *Party::_vm->_spells;
-
- switch (category) {
- case CATEGORY_WEAPON: {
- // Weapons
- XeenItem &i = _weapons[itemIndex];
- return Common::String::format("\f%02u%s%s%s\f%02u%s%s%s", displayNum,
- !i._bonusFlags ? spells._maeNames[i._material] : "",
- (i._bonusFlags & ITEMFLAG_BROKEN) ? ITEM_BROKEN : "",
- (i._bonusFlags & ITEMFLAG_CURSED) ? ITEM_CURSED : "",
- WEAPON_NAMES[i._id],
- !i._bonusFlags ? "" : BONUS_NAMES[i._bonusFlags & ITEMFLAG_BONUS_MASK],
- (i._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED)) ||
- !i._bonusFlags ? "\b " : ""
- );
- }
-
- case CATEGORY_ARMOR: {
- // Armor
- XeenItem &i = _armor[itemIndex];
- return Common::String::format("\f%02u%s%s%s\f%02u%s%s", displayNum,
- !i._bonusFlags ? "" : spells._maeNames[i._material],
- (i._bonusFlags & ITEMFLAG_BROKEN) ? ITEM_BROKEN : "",
- (i._bonusFlags & ITEMFLAG_CURSED) ? ITEM_CURSED : "",
- ARMOR_NAMES[i._id],
- (i._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED)) ||
- !i._bonusFlags ? "\b " : ""
- );
- }
-
- case CATEGORY_ACCESSORY: {
- // Accessories
- XeenItem &i = _accessories[itemIndex];
- return Common::String::format("\f%02u%s%s%s\f%02u%s%s", displayNum,
- !i._bonusFlags ? "" : spells._maeNames[i._material],
- (i._bonusFlags & ITEMFLAG_BROKEN) ? ITEM_BROKEN : "",
- (i._bonusFlags & ITEMFLAG_CURSED) ? ITEM_CURSED : "",
- ARMOR_NAMES[i._id],
- (i._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED)) ||
- !i._bonusFlags ? "\b " : ""
- );
- }
-
- case CATEGORY_MISC: {
- // Misc
- XeenItem &i = _misc[itemIndex];
- return Common::String::format("\f%02u%s%s%s\f%02u%s%s", displayNum,
- !i._bonusFlags ? "" : spells._maeNames[i._material],
- (i._bonusFlags & ITEMFLAG_BROKEN) ? ITEM_BROKEN : "",
- (i._bonusFlags & ITEMFLAG_CURSED) ? ITEM_CURSED : "",
- ARMOR_NAMES[i._id],
- (i._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED)) ||
- !i._id ? "\b " : ""
- );
- }
- default:
- return "";
- }
-}
-
-/*------------------------------------------------------------------------*/
-
void Roster::synchronize(Common::Serializer &s) {
if (s.isLoading())
resize(30);
diff --git a/engines/xeen/party.h b/engines/xeen/party.h
index 73de7065aa..138ecba2d4 100644
--- a/engines/xeen/party.h
+++ b/engines/xeen/party.h
@@ -39,153 +39,12 @@ enum Direction {
enum Difficulty { ADVENTURER = 0, WARRIOR = 1 };
-enum Sex { MALE = 0, FEMALE = 1, YES_PLEASE = 2 };
-
-enum Race { HUMAN = 0, ELF = 1, DWARF = 2, GNOME = 3, HALF_ORC = 4 };
-
-enum CharacterClass {
- CLASS_KNIGHT = 0, CLASS_PALADIN = 1, CLASS_ARCHER = 2, CLASS_CLERIC = 3,
- CLASS_SORCERER = 4, CLASS_ROBBER = 5, CLASS_NINJA = 6, CLASS_BARBARIAN = 7,
- CLASS_DRUID = 8, CLASS_RANGER = 9,
- CLASS_12 = 12, CLASS_15 = 15, CLASS_16 = 16
-};
-
-enum Attribute{
- MIGHT = 0, INTELLECT = 1, PERSONALITY = 2, ENDURANCE = 3, SPEED = 4,
- ACCURACY = 5, LUCK = 6
-};
-
-enum Skill { THIEVERY = 0, ARMS_MASTER = 1, ASTROLOGER = 2, BODYBUILDER = 3,
- CARTOGRAPHER = 4, CRUSADER = 5, DIRECTION_SENSE = 6, LINGUIST = 7,
- MERCHANT = 8, MOUNTAINEER = 9, NAVIGATOR = 10, PATHFINDER = 11,
- PRAYER_MASTER = 12, PRESTIDIGITATION = 13, SWIMMING = 14, TRACKING = 15,
- SPOT_DOORS = 16, DANGER_SENSE = 17
-};
-
-enum Condition { CURSED = 0, HEART_BROKEN = 1, WEAK = 2, POISONED = 3,
- DISEASED = 4, INSANE = 5, IN_LOVE = 6, DRUNK = 7, SLEEP = 8,
- DEPRESSED = 9, CONFUSED = 10, PARALYZED = 11, UNCONSCIOUS = 12,
- DEAD = 13, STONED = 14, ERADICATED = 15,
- NO_CONDITION = 16
-};
-
#define ITEMS_COUNT 36
#define TOTAL_CHARACTERS 30
#define XEEN_TOTAL_CHARACTERS 24
#define MAX_ACTIVE_PARTY 6
#define TOTAL_STATS 7
-class XeenEngine;
-
-class AttributePair {
-public:
- uint _permanent;
- uint _temporary;
-public:
- AttributePair();
- void synchronize(Common::Serializer &s);
-};
-
-class Character {
-private:
- int conditionMod(Attribute attrib) const;
-public:
- Common::String _name;
- Sex _sex;
- Race _race;
- int _xeenSide;
- CharacterClass _class;
- AttributePair _might;
- AttributePair _intellect;
- AttributePair _personality;
- AttributePair _endurance;
- AttributePair _speed;
- AttributePair _accuracy;
- AttributePair _luck;
- int _ACTemp;
- AttributePair _level;
- uint _birthDay;
- int _tempAge;
- int _skills[18];
- bool _awards[128];
- int _spells[39];
- int _lloydMap;
- Common::Point _lloydPosition;
- bool _hasSpells;
- int _currentSpell;
- int _quickOption;
- InventoryItemsGroup _items;
- InventoryItems _weapons;
- InventoryItems _armor;
- InventoryItems _accessories;
- InventoryItems _misc;
- int _lloydSide;
- AttributePair _fireResistence;
- AttributePair _coldResistence;
- AttributePair _electricityResistence;
- AttributePair _poisonResistence;
- AttributePair _energyResistence;
- AttributePair _magicResistence;
- int _conditions[16];
- int _townUnknown;
- int _savedMazeId;
- int _currentHp;
- int _currentSp;
- uint _birthYear;
- uint32 _experience;
- int _currentAdventuringSpell;
- int _currentCombatSpell;
-public:
- Character();
- void synchronize(Common::Serializer &s);
-
- Condition worstCondition() const;
-
- int getAge(bool ignoreTemp = false) const;
-
- int getMaxHP() const;
-
- int getMaxSP() const;
-
- uint getStat(Attribute attrib, bool baseOnly = false) const;
-
- static int statColor(int amount, int threshold);
-
- int statBonus(uint statValue) const;
-
- bool charSavingThrow(DamageType attackType) const;
-
- bool noActions();
-
- void setAward(int awardId, bool value);
-
- bool hasAward(int awardId) const;
-
- int getArmorClass(bool baseOnly = false) const;
-
- int getThievery() const;
-
- uint getCurrentLevel() const;
-
- int itemScan(int itemId) const;
-
- void setValue(int id, uint value);
-
- bool guildMember() const;
-
- uint experienceToNextLevel() const;
-
- uint nextExperienceLevel() const;
-
- uint getCurrentExperience() const;
-
- int getNumSkills() const;
-
- int getNumAwards() const;
-
- Common::String assembleItemName(int itemIndex, int displayNum, ItemCategory category);
-};
-
class Roster: public Common::Array<Character> {
public:
Roster() {}