diff options
-rw-r--r-- | engines/xeen/character.cpp | 651 | ||||
-rw-r--r-- | engines/xeen/character.h | 217 | ||||
-rw-r--r-- | engines/xeen/item.cpp | 658 | ||||
-rw-r--r-- | engines/xeen/item.h | 270 | ||||
-rw-r--r-- | engines/xeen/module.mk | 1 |
5 files changed, 932 insertions, 865 deletions
diff --git a/engines/xeen/character.cpp b/engines/xeen/character.cpp index c6ebde8a39..248d432a82 100644 --- a/engines/xeen/character.cpp +++ b/engines/xeen/character.cpp @@ -21,656 +21,15 @@ */ #include "xeen/character.h" -#include "xeen/dialogs/dialogs_query.h" -#include "xeen/dialogs/dialogs_message.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); -} - -ElementalCategory XeenItem::getElementalCategory() const { - int idx; - for (idx = 0; Res.ELEMENTAL_CATEGORIES[idx] < _material; ++idx) - ; - - return (ElementalCategory)idx; -} - -AttributeCategory XeenItem::getAttributeCategory() const { - int m = _material - 59; - int idx; - for (idx = 0; Res.ATTRIBUTE_CATEGORIES[idx] < m; ++idx) - ; - - return (AttributeCategory)idx; -} - -const char *XeenItem::getItemName(ItemCategory category, uint id) { - if (id < 82) - return Res.ITEM_NAMES[category][id]; - - switch (category) { - case CATEGORY_WEAPON: - return Res.QUEST_ITEM_NAMES[id - 82]; - - case CATEGORY_ARMOR: - return Res.QUEST_ITEM_NAMES[id - 82 + 35]; - - case CATEGORY_ACCESSORY: - return Res.QUEST_ITEM_NAMES[id - 82 + 35 + 14]; - - default: - return Res.QUEST_ITEM_NAMES[id - 82 + 35 + 14 + 11]; - } -} - -/*------------------------------------------------------------------------*/ - -InventoryItems::InventoryItems(Character *character, ItemCategory category): - _character(character), _category(category) { - resize(INV_ITEMS_TOTAL); - - _names = Res.ITEM_NAMES[category]; -} - -void InventoryItems::clear() { - for (uint idx = 0; idx < size(); ++idx) - operator[](idx).clear(); -} - -bool InventoryItems::passRestrictions(int itemId, bool showError) const { - CharacterClass charClass = _character->_class; - - switch (charClass) { - case CLASS_KNIGHT: - case CLASS_PALADIN: - return true; - - case CLASS_ARCHER: - case CLASS_CLERIC: - case CLASS_SORCERER: - case CLASS_ROBBER: - case CLASS_NINJA: - case CLASS_BARBARIAN: - case CLASS_DRUID: - case CLASS_RANGER: { - if (!(Res.ITEM_RESTRICTIONS[itemId + Res.RESTRICTION_OFFSETS[_category]] & - (1 << (charClass - CLASS_ARCHER)))) - return true; - break; - } - - default: - break; - } - - Common::String name = _names[itemId]; - if (showError) { - Common::String msg = Common::String::format(Res.NOT_PROFICIENT, - Res.CLASS_NAMES[charClass], name.c_str()); - ErrorScroll::show(Party::_vm, msg, WT_FREEZE_WAIT); - } - - return false; -} - -Common::String InventoryItems::getName(int itemIndex) { - int id = operator[](itemIndex)._id; - return _names[id]; -} - -Common::String InventoryItems::getIdentifiedDetails(int itemIndex) { - XeenItem &item = operator[](itemIndex); - - Common::String classes; - for (int charClass = CLASS_KNIGHT; charClass <= CLASS_RANGER; ++charClass) { - if (passRestrictions(charClass, true)) { - const char *const name = Res.CLASS_NAMES[charClass]; - classes += name[0]; - classes += name[1]; - classes += " "; - } - } - if (classes.size() == 30) - classes = Res.ALL; - - return getAttributes(item, classes); -} - -bool InventoryItems::discardItem(int itemIndex) { - XeenItem &item = operator[](itemIndex); - XeenEngine *vm = Party::_vm; - - if (item._bonusFlags & ITEMFLAG_CURSED) { - ErrorScroll::show(vm, Res.CANNOT_DISCARD_CURSED_ITEM); - } else { - Common::String itemDesc = getFullDescription(itemIndex, 4); - Common::String msg = Common::String::format(Res.PERMANENTLY_DISCARD, itemDesc.c_str()); - - if (Confirm::show(vm, msg)) { - operator[](itemIndex).clear(); - sort(); - - return true; - } - } - - return true; -} - -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::removeItem(int itemIndex) { - XeenItem &item = operator[](itemIndex); - XeenEngine *vm = Party::_vm; - - if (item._bonusFlags & ITEMFLAG_CURSED) - ErrorScroll::show(vm, Res.CANNOT_REMOVE_CURSED_ITEM); - else - item._frame = 0; -} - -XeenEngine *InventoryItems::getVm() { - return Party::_vm; -} - -void InventoryItems::equipError(int itemIndex1, ItemCategory category1, int itemIndex2, - ItemCategory category2) { - XeenEngine *vm = Party::_vm; - - if (itemIndex1 >= 0) { - Common::String itemName1 = _character->_items[category1].getName(itemIndex1); - Common::String itemName2 = _character->_items[category2].getName(itemIndex2); - - MessageDialog::show(vm, Common::String::format(Res.REMOVE_X_TO_EQUIP_Y, - itemName1.c_str(), itemName2.c_str())); - } else { - MessageDialog::show(vm, Common::String::format(Res.EQUIPPED_ALL_YOU_CAN, - (itemIndex1 == -1) ? Res.RING : Res.MEDAL)); - } -} - -void InventoryItems::enchantItem(int itemIndex, int amount) { - XeenEngine *vm = Party::_vm; - vm->_sound->playFX(21); - ErrorScroll::show(vm, Common::String::format(Res.NOT_ENCHANTABLE, Res.SPELL_FAILED)); -} - -bool InventoryItems::isFull() const { - return operator[](size() - 1)._id != 0; -} - -/*------------------------------------------------------------------------*/ - -void WeaponItems::equipItem(int itemIndex) { - XeenItem &item = operator[](itemIndex); - - if (item._id <= 17) { - if (passRestrictions(item._id, false)) { - for (uint idx = 0; idx < size(); ++idx) { - XeenItem &i = operator[](idx); - if (i._frame == 13 || i._frame == 1) { - equipError(itemIndex, CATEGORY_WEAPON, idx, CATEGORY_WEAPON); - return; - } - } - - item._frame = 1; - } - } else if (item._id >= 30 && item._id <= 33) { - if (passRestrictions(item._id, false)) { - for (uint idx = 0; idx < size(); ++idx) { - XeenItem &i = operator[](idx); - if (i._frame == 4) { - equipError(itemIndex, CATEGORY_WEAPON, idx, CATEGORY_WEAPON); - return; - } - } - - item._frame = 4; - } - } else { - if (passRestrictions(item._id, false)) { - for (uint idx = 0; idx < size(); ++idx) { - XeenItem &i = operator[](idx); - if (i._frame == 13 || i._frame == 1) { - equipError(itemIndex, CATEGORY_WEAPON, idx, CATEGORY_WEAPON); - return; - } - } - - for (uint idx = 0; idx < _character->_armor.size(); ++idx) { - XeenItem &i = _character->_armor[idx]; - if (i._frame == 2) { - equipError(itemIndex, CATEGORY_WEAPON, idx, CATEGORY_ARMOR); - return; - } - } - - item._frame = 13; - } - } -} - -Common::String WeaponItems::getFullDescription(int itemIndex, int displayNum) { - XeenItem &i = operator[](itemIndex); - Resources &res = *getVm()->_resources; - - return Common::String::format("\f%02u%s%s%s\f%02u%s%s%s", displayNum, - !i._bonusFlags ? res._maeNames[i._material].c_str() : "", - (i._bonusFlags & ITEMFLAG_BROKEN) ? Res.ITEM_BROKEN : "", - (i._bonusFlags & ITEMFLAG_CURSED) ? Res.ITEM_CURSED : "", - displayNum, - Res.WEAPON_NAMES[i._id], - !i._bonusFlags ? "" : Res.BONUS_NAMES[i._bonusFlags & ITEMFLAG_BONUS_MASK], - (i._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED)) || - !i._bonusFlags ? "\b " : "" - ); -} - -void WeaponItems::enchantItem(int itemIndex, int amount) { - Sound &sound = *getVm()->_sound; - XeenItem &item = operator[](itemIndex); - Character tempCharacter; - - if (item._material == 0 && item._bonusFlags == 0 && item._id != 34) { - tempCharacter.makeItem(amount, 0, 1); - XeenItem &tempItem = tempCharacter._weapons[0]; - - item._material = tempItem._material; - item._bonusFlags = tempItem._bonusFlags; - sound.playFX(19); - } else { - InventoryItems::enchantItem(itemIndex, amount); - } -} - -/* - * Returns a text string listing all the stats/attributes of a given item - */ -Common::String WeaponItems::getAttributes(XeenItem &item, const Common::String &classes) { - Common::String attrBonus, elemDamage, physDamage, toHit, specialPower; - attrBonus = elemDamage = physDamage = toHit = specialPower = Res.FIELD_NONE; - - // First calculate physical damage - int minVal = Res.WEAPON_DAMAGE_BASE[item._id]; - int maxVal = minVal * Res.WEAPON_DAMAGE_MULTIPLIER[item._id]; - - if (item._material >= 37 && item._material <= 58) { - minVal += Res.METAL_DAMAGE[item._material - 37]; - maxVal += Res.METAL_DAMAGE[item._material - 37]; - toHit = Common::String::format("%+d", Res.METAL_DAMAGE_PERCENT[item._material - 37]); - } - - physDamage = Common::String::format(Res.DAMAGE_X_TO_Y, minVal, maxVal); - - // Next handle elemental/attribute damage - if (item._material < 37) { - int damage = Res.ELEMENTAL_DAMAGE[item._material]; - if (damage > 0) { - ElementalCategory elemCategory = item.getElementalCategory(); - elemDamage = Common::String::format(Res.ELEMENTAL_XY_DAMAGE, - damage, Res.ELEMENTAL_NAMES[elemCategory]); - } - } else if (item._material >= 59) { - int bonus = Res.ATTRIBUTE_BONUSES[item._material - 59]; - AttributeCategory attrCategory = item.getAttributeCategory(); - attrBonus = Common::String::format(Res.ATTR_XY_BONUS, bonus, - Res.ATTRIBUTE_NAMES[attrCategory]); - } - - // Handle weapon effective against - int effective = item._bonusFlags & ITEMFLAG_BONUS_MASK; - if (effective) { - specialPower = Common::String::format(Res.EFFECTIVE_AGAINST, - Res.EFFECTIVENESS_NAMES[effective]); - } - - return Common::String::format(Res.ITEM_DETAILS, classes.c_str(), - toHit.c_str(), physDamage.c_str(), elemDamage.c_str(), - Res.FIELD_NONE, Res.FIELD_NONE, attrBonus.c_str(), specialPower.c_str() - ); -} - -/*------------------------------------------------------------------------*/ - -void ArmorItems::equipItem(int itemIndex) { - XeenItem &item = operator[](itemIndex); - - if (item._id <= 7) { - if (passRestrictions(item._id, false)) { - for (uint idx = 0; idx < size(); ++idx) { - XeenItem &i = operator[](idx); - if (i._frame == 9) { - equipError(itemIndex, CATEGORY_ARMOR, idx, CATEGORY_ARMOR); - return; - } - } - - item._frame = 3; - } - } else if (item._id == 8) { - if (passRestrictions(item._id, false)) { - for (uint idx = 0; idx < size(); ++idx) { - XeenItem &i = operator[](idx); - if (i._frame == 2) { - equipError(itemIndex, CATEGORY_ARMOR, idx, CATEGORY_ARMOR); - return; - } - } - - for (uint idx = 0; idx < _character->_weapons.size(); ++idx) { - XeenItem &i = _character->_weapons[idx]; - if (i._frame == 13) { - equipError(itemIndex, CATEGORY_ARMOR, idx, CATEGORY_WEAPON); - return; - } - } - - item._frame = 2; - } - } else if (item._id == 9) { - for (uint idx = 0; idx < size(); ++idx) { - XeenItem &i = operator[](idx); - if (i._frame == 5) { - equipError(itemIndex, CATEGORY_ARMOR, idx, CATEGORY_ARMOR); - return; - } - } - - item._frame = 5; - } else if (item._id == 10) { - for (uint idx = 0; idx < size(); ++idx) { - XeenItem &i = operator[](idx); - if (i._frame == 9) { - equipError(itemIndex, CATEGORY_ARMOR, idx, CATEGORY_ARMOR); - return; - } - } - - item._frame = 9; - } else if (item._id <= 12) { - for (uint idx = 0; idx < size(); ++idx) { - XeenItem &i = operator[](idx); - if (i._frame == 10) { - equipError(itemIndex, CATEGORY_ARMOR, idx, CATEGORY_ARMOR); - return; - } - } - - item._frame = 10; - } else { - for (uint idx = 0; idx < size(); ++idx) { - XeenItem &i = operator[](idx); - if (i._frame == 6) { - equipError(itemIndex, CATEGORY_ARMOR, idx, CATEGORY_ARMOR); - return; - } - } - - item._frame = 6; - } -} - -Common::String ArmorItems::getFullDescription(int itemIndex, int displayNum) { - XeenItem &i = operator[](itemIndex); - Resources &res = *getVm()->_resources; - - return Common::String::format("\f%02u%s%s%s\f%02u%s%s", displayNum, - !i._bonusFlags ? "" : res._maeNames[i._material].c_str(), - (i._bonusFlags & ITEMFLAG_BROKEN) ? Res.ITEM_BROKEN : "", - (i._bonusFlags & ITEMFLAG_CURSED) ? Res.ITEM_CURSED : "", - displayNum, - Res.ARMOR_NAMES[i._id], - (i._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED)) || - !i._bonusFlags ? "\b " : "" - ); -} - -void ArmorItems::enchantItem(int itemIndex, int amount) { - Sound &sound = *getVm()->_sound; - XeenItem &item = operator[](itemIndex); - Character tempCharacter; - - if (item._material == 0 && item._bonusFlags == 0) { - tempCharacter.makeItem(amount, 0, 2); - XeenItem &tempItem = tempCharacter._armor[0]; - - item._material = tempItem._material; - item._bonusFlags = tempItem._bonusFlags; - sound.playFX(19); - } else { - InventoryItems::enchantItem(itemIndex, amount); - } -} - -/* -* Returns a text string listing all the stats/attributes of a given item -*/ -Common::String ArmorItems::getAttributes(XeenItem &item, const Common::String &classes) { - Common::String elemResist, attrBonus, acBonus; - elemResist = attrBonus = acBonus = Res.FIELD_NONE; - - if (item._material < 36) { - int resistence = Res.ELEMENTAL_RESISTENCES[item._material]; - if (resistence > 0) { - int eCategory = ELEM_FIRE; - while (eCategory < ELEM_MAGIC && Res.ELEMENTAL_CATEGORIES[eCategory] < item._material) - ++eCategory; - - elemResist = Common::String::format(Res.ATTR_XY_BONUS, resistence, - Res.ELEMENTAL_NAMES[eCategory]); - } - } else if (item._material >= 59) { - int bonus = Res.ATTRIBUTE_BONUSES[item._material - 59]; - AttributeCategory aCategory = item.getAttributeCategory(); - attrBonus = Common::String::format(Res.ATTR_XY_BONUS, bonus, - Res.ATTRIBUTE_NAMES[aCategory]); - } - - int strength = Res.ARMOR_STRENGTHS[item._id]; - if (item._material >= 37 && item._material <= 58) { - strength += Res.METAL_LAC[item._material - 37]; - } - acBonus = Common::String::format("%+d", strength); - - return Common::String::format(Res.ITEM_DETAILS, classes.c_str(), - Res.FIELD_NONE, Res.FIELD_NONE, Res.FIELD_NONE, - elemResist.c_str(), acBonus.c_str(), attrBonus.c_str(), Res.FIELD_NONE); -} - -/*------------------------------------------------------------------------*/ - -void AccessoryItems::equipItem(int itemIndex) { - XeenItem &item = operator[](itemIndex); - - if (item._id == 1) { - int count = 0; - for (uint idx = 0; idx < size(); ++idx) { - XeenItem &i = operator[](idx); - if (i._frame == 8) - ++count; - } - - if (count <= 1) - item._frame = 8; - else - equipError(-1, CATEGORY_ACCESSORY, itemIndex, CATEGORY_ACCESSORY); - } else if (item._id == 2) { - for (uint idx = 0; idx < size(); ++idx) { - XeenItem &i = operator[](idx); - if (i._frame == 12) { - equipError(itemIndex, CATEGORY_ACCESSORY, idx, CATEGORY_ACCESSORY); - return; - } - } - } else if (item._id <= 7) { - int count = 0; - for (uint idx = 0; idx < size(); ++idx) { - XeenItem &i = operator[](idx); - if (i._frame == 7) - ++count; - } - - if (count <= 1) - item._frame = 7; - else - equipError(-2, CATEGORY_ACCESSORY, itemIndex, CATEGORY_ACCESSORY); - } else { - for (uint idx = 0; idx < size(); ++idx) { - XeenItem &i = operator[](idx); - if (i._frame == 11) { - equipError(itemIndex, CATEGORY_ACCESSORY, idx, CATEGORY_ACCESSORY); - return; - } - } - - item._frame = 11; - } -} - -Common::String AccessoryItems::getFullDescription(int itemIndex, int displayNum) { - XeenItem &i = operator[](itemIndex); - Resources &res = *getVm()->_resources; - - return Common::String::format("\f%02u%s%s%s\f%02u%s%s", displayNum, - !i._bonusFlags ? "" : res._maeNames[i._material].c_str(), - (i._bonusFlags & ITEMFLAG_BROKEN) ? Res.ITEM_BROKEN : "", - (i._bonusFlags & ITEMFLAG_CURSED) ? Res.ITEM_CURSED : "", - displayNum, - Res.ARMOR_NAMES[i._id], - (i._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED)) || - !i._bonusFlags ? "\b " : "" - ); -} - -/* -* Returns a text string listing all the stats/attributes of a given item -*/ -Common::String AccessoryItems::getAttributes(XeenItem &item, const Common::String &classes) { - Common::String elemResist, attrBonus; - elemResist = attrBonus = Res.FIELD_NONE; - - if (item._material < 36) { - int resistence = Res.ELEMENTAL_RESISTENCES[item._material]; - if (resistence > 0) { - int eCategory = ELEM_FIRE; - while (eCategory < ELEM_MAGIC && Res.ELEMENTAL_CATEGORIES[eCategory] < item._material) - ++eCategory; - - elemResist = Common::String::format(Res.ATTR_XY_BONUS, resistence, - Res.ELEMENTAL_NAMES[eCategory]); - } - } else if (item._material >= 59) { - int bonus = Res.ATTRIBUTE_BONUSES[item._material - 59]; - AttributeCategory aCategory = item.getAttributeCategory(); - attrBonus = Common::String::format(Res.ATTR_XY_BONUS, bonus, - Res.ATTRIBUTE_NAMES[aCategory]); - } - - return Common::String::format(Res.ITEM_DETAILS, classes.c_str(), - Res.FIELD_NONE, Res.FIELD_NONE, Res.FIELD_NONE, - elemResist.c_str(), Res.FIELD_NONE, attrBonus.c_str(), Res.FIELD_NONE); -} - -/*------------------------------------------------------------------------*/ - -Common::String MiscItems::getFullDescription(int itemIndex, int displayNum) { - XeenItem &i = operator[](itemIndex); - Resources &res = *getVm()->_resources; - - return Common::String::format("\f%02u%s%s%s\f%02u%s%s", displayNum, - !i._bonusFlags ? "" : res._maeNames[i._material].c_str(), - (i._bonusFlags & ITEMFLAG_BROKEN) ? Res.ITEM_BROKEN : "", - (i._bonusFlags & ITEMFLAG_CURSED) ? Res.ITEM_CURSED : "", - displayNum, - Res.ARMOR_NAMES[i._id], - (i._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED)) || - !i._id ? "\b " : "" - ); -} - - -/* -* Returns a text string listing all the stats/attributes of a given item -*/ -Common::String MiscItems::getAttributes(XeenItem &item, const Common::String &classes) { - Common::String specialPower = Res.FIELD_NONE; - Spells &spells = *getVm()->_spells; - - if (item._id) { - specialPower = spells._spellNames[Res.MISC_SPELL_INDEX[item._id]]; - } - - return Common::String::format(Res.ITEM_DETAILS, classes.c_str(), - Res.FIELD_NONE, Res.FIELD_NONE, Res.FIELD_NONE, Res.FIELD_NONE, Res.FIELD_NONE, - Res.FIELD_NONE, specialPower.c_str()); -} -/*------------------------------------------------------------------------*/ - -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 InventoryItemsGroup::breakAllItems() { - for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) { - if ((*_itemSets[0])[idx]._id != 34) { - (*_itemSets[0])[idx]._bonusFlags |= ITEMFLAG_BROKEN; - (*_itemSets[0])[idx]._frame = 0; - } - - (*_itemSets[1])[idx]._bonusFlags |= ITEMFLAG_BROKEN; - (*_itemSets[2])[idx]._bonusFlags |= ITEMFLAG_BROKEN; - (*_itemSets[3])[idx]._bonusFlags |= ITEMFLAG_BROKEN; - (*_itemSets[1])[idx]._frame = 0; - (*_itemSets[2])[idx]._frame = 0; - } +AttributePair::AttributePair() { + _temporary = _permanent = 0; } -/*------------------------------------------------------------------------*/ - - void AttributePair::synchronize(Common::Serializer &s) { s.syncAsByte(_permanent); s.syncAsByte(_temporary); @@ -678,12 +37,6 @@ void AttributePair::synchronize(Common::Serializer &s) { /*------------------------------------------------------------------------*/ -AttributePair::AttributePair() { - _temporary = _permanent = 0; -} - -/*------------------------------------------------------------------------*/ - Character::Character(): _weapons(this), _armor(this), _accessories(this), _misc(this), _items(_weapons, _armor, _accessories, _misc) { diff --git a/engines/xeen/character.h b/engines/xeen/character.h index 649da7ec12..05a61c3f2e 100644 --- a/engines/xeen/character.h +++ b/engines/xeen/character.h @@ -28,6 +28,7 @@ #include "common/rect.h" #include "common/serializer.h" #include "xeen/combat.h" +#include "xeen/item.h" #include "xeen/sprites.h" namespace Xeen { @@ -47,11 +48,6 @@ 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, - NUM_ITEM_CATEGORIES = 4 -}; - enum Sex { MALE = 0, FEMALE = 1, YES_PLEASE = 2 }; enum Race { HUMAN = 0, ELF = 1, DWARF = 2, GNOME = 3, HALF_ORC = 4 }; @@ -83,222 +79,11 @@ enum Condition { NO_CONDITION = 16 }; -enum AttributeCategory { - ATTR_MIGHT = 0, ATTR_INTELLECT = 1, ATTR_PERSONALITY = 2, ATTR_SPEED = 3, - ATTR_ACCURACY = 4, ATTR_LUCK = 5, ATTR_HIT_POINTS = 6, ATTR_SPELL_POINTS = 7, - ATTR_ARMOR_CLASS = 8, ATTR_THIEVERY = 9 -}; - enum QuickAction { QUICK_ATTACK = 0, QUICK_SPELL = 1, QUICK_BLOCK = 2, QUICK_RUN = 3 }; class XeenEngine; -class Character; - -class XeenItem { -public: - int _material; - uint _id; - int _bonusFlags; - int _frame; -public: - /** - * Return the name of the item - */ - static const char *getItemName(ItemCategory category, uint id); -public: - XeenItem(); - - /** - * Clear the data for the item - */ - void clear(); - - /** - * Returns true if no item is set - */ - bool empty() const { return _id != 0; } - - /** - * Synchronizes the data for the item - */ - void synchronize(Common::Serializer &s); - - /** - * Gets the elemental category for the item - */ - ElementalCategory getElementalCategory() const; - - /** - * Gets the attribute category for the item - */ - AttributeCategory getAttributeCategory() const; -}; - -class InventoryItems : public Common::Array<XeenItem> { -protected: - Character *_character; - ItemCategory _category; - const char **_names; - - XeenEngine *getVm(); - void equipError(int itemIndex1, ItemCategory category1, int itemIndex2, - ItemCategory category2); - - virtual Common::String getAttributes(XeenItem &item, const Common::String &classes) = 0; -public: - InventoryItems(Character *character, ItemCategory category); - virtual ~InventoryItems() {} - - /** - * Clears the set of items - */ - void clear(); - - /** - * Return whether a given item passes class-based usage restrictions - */ - bool passRestrictions(int itemId, bool showError) const; - - /** - * Return the bare name of a given inventory item - */ - Common::String getName(int itemIndex); - - virtual Common::String getFullDescription(int itemIndex, int displayNum = 15) = 0; - - /** - * Returns the identified details for an item - */ - Common::String getIdentifiedDetails(int itemIndex); - - /** - * Discard an item from the inventory - */ - bool discardItem(int itemIndex); - - /** - * Equips an item - */ - virtual void equipItem(int itemIndex) {} - - /** - * Un-equips the given item - */ - void removeItem(int itemIndex); - - /** - * Sorts the items list, removing any empty item slots to the end of the array - */ - void sort(); - - /** - * Enchants an item - */ - virtual void enchantItem(int itemIndex, int amount); - - /** - * Return if the given inventory items list is full - */ - bool isFull() const; -}; - -class WeaponItems: public InventoryItems { -protected: - virtual Common::String getAttributes(XeenItem &item, const Common::String &classes); -public: - WeaponItems(Character *character) : InventoryItems(character, CATEGORY_WEAPON) {} - virtual ~WeaponItems() {} - - /** - * Equip a given weapon - */ - virtual void equipItem(int itemIndex); - - /** - * Assembles a full lines description for a specified item for use in - * the Items dialog - */ - virtual Common::String getFullDescription(int itemIndex, int displayNum); - - /** - * Enchants a weapon - */ - virtual void enchantItem(int itemIndex, int amount); -}; - -class ArmorItems : public InventoryItems { -protected: - virtual Common::String getAttributes(XeenItem &item, const Common::String &classes); -public: - ArmorItems(Character *character) : InventoryItems(character, CATEGORY_ARMOR) {} - virtual ~ArmorItems() {} - - /** - * Equip a given piece of armor - */ - virtual void equipItem(int itemIndex); - - /** - * Assembles a full lines description for a specified item for use in - * the Items dialog - */ - virtual Common::String getFullDescription(int itemIndex, int displayNum); - - /** - * Enchants an armor - */ - virtual void enchantItem(int itemIndex, int amount); -}; - -class AccessoryItems : public InventoryItems { -protected: - virtual Common::String getAttributes(XeenItem &item, const Common::String &classes); -public: - AccessoryItems(Character *character) : InventoryItems(character, CATEGORY_ACCESSORY) {} - - /** - * Equip a given accessory - */ - virtual void equipItem(int itemIndex); - - /** - * Assembles a full lines description for a specified item for use in - * the Items dialog - */ - virtual Common::String getFullDescription(int itemIndex, int displayNum); -}; - -class MiscItems : public InventoryItems { -protected: - virtual Common::String getAttributes(XeenItem &item, const Common::String &classes); -public: - MiscItems(Character *character) : InventoryItems(character, CATEGORY_MISC) {} - virtual ~MiscItems() {} - - /** - * Assembles a full lines description for a specified item for use in - * the Items dialog - */ - virtual Common::String getFullDescription(int itemIndex, int displayNum); -}; - -class InventoryItemsGroup { -private: - InventoryItems *_itemSets[4]; -public: - InventoryItemsGroup(InventoryItems &weapons, InventoryItems &armor, - InventoryItems &accessories, InventoryItems &misc); - - InventoryItems &operator[](ItemCategory category); - - /** - * Breaks all the items in a given character's inventory - */ - void breakAllItems(); -}; - class AttributePair { public: diff --git a/engines/xeen/item.cpp b/engines/xeen/item.cpp new file mode 100644 index 0000000000..722c2497eb --- /dev/null +++ b/engines/xeen/item.cpp @@ -0,0 +1,658 @@ +/* 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/item.h" +#include "xeen/resources.h" +#include "xeen/xeen.h" +#include "xeen/dialogs/dialogs_query.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); +} + +ElementalCategory XeenItem::getElementalCategory() const { + int idx; + for (idx = 0; Res.ELEMENTAL_CATEGORIES[idx] < _material; ++idx) + ; + + return (ElementalCategory)idx; +} + +AttributeCategory XeenItem::getAttributeCategory() const { + int m = _material - 59; + int idx; + for (idx = 0; Res.ATTRIBUTE_CATEGORIES[idx] < m; ++idx) + ; + + return (AttributeCategory)idx; +} + +const char *XeenItem::getItemName(ItemCategory category, uint id) { + if (id < 82) + return Res.ITEM_NAMES[category][id]; + + switch (category) { + case CATEGORY_WEAPON: + return Res.QUEST_ITEM_NAMES[id - 82]; + + case CATEGORY_ARMOR: + return Res.QUEST_ITEM_NAMES[id - 82 + 35]; + + case CATEGORY_ACCESSORY: + return Res.QUEST_ITEM_NAMES[id - 82 + 35 + 14]; + + default: + return Res.QUEST_ITEM_NAMES[id - 82 + 35 + 14 + 11]; + } +} + +/*------------------------------------------------------------------------*/ + +InventoryItems::InventoryItems(Character *character, ItemCategory category): + _character(character), _category(category) { + resize(INV_ITEMS_TOTAL); + + _names = Res.ITEM_NAMES[category]; +} + +void InventoryItems::clear() { + for (uint idx = 0; idx < size(); ++idx) + operator[](idx).clear(); +} + +bool InventoryItems::passRestrictions(int itemId, bool showError) const { + CharacterClass charClass = _character->_class; + + switch (charClass) { + case CLASS_KNIGHT: + case CLASS_PALADIN: + return true; + + case CLASS_ARCHER: + case CLASS_CLERIC: + case CLASS_SORCERER: + case CLASS_ROBBER: + case CLASS_NINJA: + case CLASS_BARBARIAN: + case CLASS_DRUID: + case CLASS_RANGER: { + if (!(Res.ITEM_RESTRICTIONS[itemId + Res.RESTRICTION_OFFSETS[_category]] & + (1 << (charClass - CLASS_ARCHER)))) + return true; + break; + } + + default: + break; + } + + Common::String name = _names[itemId]; + if (showError) { + Common::String msg = Common::String::format(Res.NOT_PROFICIENT, + Res.CLASS_NAMES[charClass], name.c_str()); + ErrorScroll::show(Party::_vm, msg, WT_FREEZE_WAIT); + } + + return false; +} + +Common::String InventoryItems::getName(int itemIndex) { + int id = operator[](itemIndex)._id; + return _names[id]; +} + +Common::String InventoryItems::getIdentifiedDetails(int itemIndex) { + XeenItem &item = operator[](itemIndex); + + Common::String classes; + for (int charClass = CLASS_KNIGHT; charClass <= CLASS_RANGER; ++charClass) { + if (passRestrictions(charClass, true)) { + const char *const name = Res.CLASS_NAMES[charClass]; + classes += name[0]; + classes += name[1]; + classes += " "; + } + } + if (classes.size() == 30) + classes = Res.ALL; + + return getAttributes(item, classes); +} + +bool InventoryItems::discardItem(int itemIndex) { + XeenItem &item = operator[](itemIndex); + XeenEngine *vm = Party::_vm; + + if (item._bonusFlags & ITEMFLAG_CURSED) { + ErrorScroll::show(vm, Res.CANNOT_DISCARD_CURSED_ITEM); + } else { + Common::String itemDesc = getFullDescription(itemIndex, 4); + Common::String msg = Common::String::format(Res.PERMANENTLY_DISCARD, itemDesc.c_str()); + + if (Confirm::show(vm, msg)) { + operator[](itemIndex).clear(); + sort(); + + return true; + } + } + + return true; +} + +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::removeItem(int itemIndex) { + XeenItem &item = operator[](itemIndex); + XeenEngine *vm = Party::_vm; + + if (item._bonusFlags & ITEMFLAG_CURSED) + ErrorScroll::show(vm, Res.CANNOT_REMOVE_CURSED_ITEM); + else + item._frame = 0; +} + +XeenEngine *InventoryItems::getVm() { + return Party::_vm; +} + +void InventoryItems::equipError(int itemIndex1, ItemCategory category1, int itemIndex2, + ItemCategory category2) { + XeenEngine *vm = Party::_vm; + + if (itemIndex1 >= 0) { + Common::String itemName1 = _character->_items[category1].getName(itemIndex1); + Common::String itemName2 = _character->_items[category2].getName(itemIndex2); + + MessageDialog::show(vm, Common::String::format(Res.REMOVE_X_TO_EQUIP_Y, + itemName1.c_str(), itemName2.c_str())); + } else { + MessageDialog::show(vm, Common::String::format(Res.EQUIPPED_ALL_YOU_CAN, + (itemIndex1 == -1) ? Res.RING : Res.MEDAL)); + } +} + +void InventoryItems::enchantItem(int itemIndex, int amount) { + XeenEngine *vm = Party::_vm; + vm->_sound->playFX(21); + ErrorScroll::show(vm, Common::String::format(Res.NOT_ENCHANTABLE, Res.SPELL_FAILED)); +} + +bool InventoryItems::isFull() const { + return operator[](size() - 1)._id != 0; +} + +/*------------------------------------------------------------------------*/ + +void WeaponItems::equipItem(int itemIndex) { + XeenItem &item = operator[](itemIndex); + + if (item._id <= 17) { + if (passRestrictions(item._id, false)) { + for (uint idx = 0; idx < size(); ++idx) { + XeenItem &i = operator[](idx); + if (i._frame == 13 || i._frame == 1) { + equipError(itemIndex, CATEGORY_WEAPON, idx, CATEGORY_WEAPON); + return; + } + } + + item._frame = 1; + } + } else if (item._id >= 30 && item._id <= 33) { + if (passRestrictions(item._id, false)) { + for (uint idx = 0; idx < size(); ++idx) { + XeenItem &i = operator[](idx); + if (i._frame == 4) { + equipError(itemIndex, CATEGORY_WEAPON, idx, CATEGORY_WEAPON); + return; + } + } + + item._frame = 4; + } + } else { + if (passRestrictions(item._id, false)) { + for (uint idx = 0; idx < size(); ++idx) { + XeenItem &i = operator[](idx); + if (i._frame == 13 || i._frame == 1) { + equipError(itemIndex, CATEGORY_WEAPON, idx, CATEGORY_WEAPON); + return; + } + } + + for (uint idx = 0; idx < _character->_armor.size(); ++idx) { + XeenItem &i = _character->_armor[idx]; + if (i._frame == 2) { + equipError(itemIndex, CATEGORY_WEAPON, idx, CATEGORY_ARMOR); + return; + } + } + + item._frame = 13; + } + } +} + +Common::String WeaponItems::getFullDescription(int itemIndex, int displayNum) { + XeenItem &i = operator[](itemIndex); + Resources &res = *getVm()->_resources; + + return Common::String::format("\f%02u%s%s%s\f%02u%s%s%s", displayNum, + !i._bonusFlags ? res._maeNames[i._material].c_str() : "", + (i._bonusFlags & ITEMFLAG_BROKEN) ? Res.ITEM_BROKEN : "", + (i._bonusFlags & ITEMFLAG_CURSED) ? Res.ITEM_CURSED : "", + displayNum, + Res.WEAPON_NAMES[i._id], + !i._bonusFlags ? "" : Res.BONUS_NAMES[i._bonusFlags & ITEMFLAG_BONUS_MASK], + (i._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED)) || + !i._bonusFlags ? "\b " : "" + ); +} + +void WeaponItems::enchantItem(int itemIndex, int amount) { + Sound &sound = *getVm()->_sound; + XeenItem &item = operator[](itemIndex); + Character tempCharacter; + + if (item._material == 0 && item._bonusFlags == 0 && item._id != 34) { + tempCharacter.makeItem(amount, 0, 1); + XeenItem &tempItem = tempCharacter._weapons[0]; + + item._material = tempItem._material; + item._bonusFlags = tempItem._bonusFlags; + sound.playFX(19); + } else { + InventoryItems::enchantItem(itemIndex, amount); + } +} + +Common::String WeaponItems::getAttributes(XeenItem &item, const Common::String &classes) { + Common::String attrBonus, elemDamage, physDamage, toHit, specialPower; + attrBonus = elemDamage = physDamage = toHit = specialPower = Res.FIELD_NONE; + + // First calculate physical damage + int minVal = Res.WEAPON_DAMAGE_BASE[item._id]; + int maxVal = minVal * Res.WEAPON_DAMAGE_MULTIPLIER[item._id]; + + if (item._material >= 37 && item._material <= 58) { + minVal += Res.METAL_DAMAGE[item._material - 37]; + maxVal += Res.METAL_DAMAGE[item._material - 37]; + toHit = Common::String::format("%+d", Res.METAL_DAMAGE_PERCENT[item._material - 37]); + } + + physDamage = Common::String::format(Res.DAMAGE_X_TO_Y, minVal, maxVal); + + // Next handle elemental/attribute damage + if (item._material < 37) { + int damage = Res.ELEMENTAL_DAMAGE[item._material]; + if (damage > 0) { + ElementalCategory elemCategory = item.getElementalCategory(); + elemDamage = Common::String::format(Res.ELEMENTAL_XY_DAMAGE, + damage, Res.ELEMENTAL_NAMES[elemCategory]); + } + } else if (item._material >= 59) { + int bonus = Res.ATTRIBUTE_BONUSES[item._material - 59]; + AttributeCategory attrCategory = item.getAttributeCategory(); + attrBonus = Common::String::format(Res.ATTR_XY_BONUS, bonus, + Res.ATTRIBUTE_NAMES[attrCategory]); + } + + // Handle weapon effective against + int effective = item._bonusFlags & ITEMFLAG_BONUS_MASK; + if (effective) { + specialPower = Common::String::format(Res.EFFECTIVE_AGAINST, + Res.EFFECTIVENESS_NAMES[effective]); + } + + return Common::String::format(Res.ITEM_DETAILS, classes.c_str(), + toHit.c_str(), physDamage.c_str(), elemDamage.c_str(), + Res.FIELD_NONE, Res.FIELD_NONE, attrBonus.c_str(), specialPower.c_str() + ); +} + +/*------------------------------------------------------------------------*/ + +void ArmorItems::equipItem(int itemIndex) { + XeenItem &item = operator[](itemIndex); + + if (item._id <= 7) { + if (passRestrictions(item._id, false)) { + for (uint idx = 0; idx < size(); ++idx) { + XeenItem &i = operator[](idx); + if (i._frame == 9) { + equipError(itemIndex, CATEGORY_ARMOR, idx, CATEGORY_ARMOR); + return; + } + } + + item._frame = 3; + } + } else if (item._id == 8) { + if (passRestrictions(item._id, false)) { + for (uint idx = 0; idx < size(); ++idx) { + XeenItem &i = operator[](idx); + if (i._frame == 2) { + equipError(itemIndex, CATEGORY_ARMOR, idx, CATEGORY_ARMOR); + return; + } + } + + for (uint idx = 0; idx < _character->_weapons.size(); ++idx) { + XeenItem &i = _character->_weapons[idx]; + if (i._frame == 13) { + equipError(itemIndex, CATEGORY_ARMOR, idx, CATEGORY_WEAPON); + return; + } + } + + item._frame = 2; + } + } else if (item._id == 9) { + for (uint idx = 0; idx < size(); ++idx) { + XeenItem &i = operator[](idx); + if (i._frame == 5) { + equipError(itemIndex, CATEGORY_ARMOR, idx, CATEGORY_ARMOR); + return; + } + } + + item._frame = 5; + } else if (item._id == 10) { + for (uint idx = 0; idx < size(); ++idx) { + XeenItem &i = operator[](idx); + if (i._frame == 9) { + equipError(itemIndex, CATEGORY_ARMOR, idx, CATEGORY_ARMOR); + return; + } + } + + item._frame = 9; + } else if (item._id <= 12) { + for (uint idx = 0; idx < size(); ++idx) { + XeenItem &i = operator[](idx); + if (i._frame == 10) { + equipError(itemIndex, CATEGORY_ARMOR, idx, CATEGORY_ARMOR); + return; + } + } + + item._frame = 10; + } else { + for (uint idx = 0; idx < size(); ++idx) { + XeenItem &i = operator[](idx); + if (i._frame == 6) { + equipError(itemIndex, CATEGORY_ARMOR, idx, CATEGORY_ARMOR); + return; + } + } + + item._frame = 6; + } +} + +Common::String ArmorItems::getFullDescription(int itemIndex, int displayNum) { + XeenItem &i = operator[](itemIndex); + Resources &res = *getVm()->_resources; + + return Common::String::format("\f%02u%s%s%s\f%02u%s%s", displayNum, + !i._bonusFlags ? "" : res._maeNames[i._material].c_str(), + (i._bonusFlags & ITEMFLAG_BROKEN) ? Res.ITEM_BROKEN : "", + (i._bonusFlags & ITEMFLAG_CURSED) ? Res.ITEM_CURSED : "", + displayNum, + Res.ARMOR_NAMES[i._id], + (i._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED)) || + !i._bonusFlags ? "\b " : "" + ); +} + +void ArmorItems::enchantItem(int itemIndex, int amount) { + Sound &sound = *getVm()->_sound; + XeenItem &item = operator[](itemIndex); + Character tempCharacter; + + if (item._material == 0 && item._bonusFlags == 0) { + tempCharacter.makeItem(amount, 0, 2); + XeenItem &tempItem = tempCharacter._armor[0]; + + item._material = tempItem._material; + item._bonusFlags = tempItem._bonusFlags; + sound.playFX(19); + } else { + InventoryItems::enchantItem(itemIndex, amount); + } +} + +Common::String ArmorItems::getAttributes(XeenItem &item, const Common::String &classes) { + Common::String elemResist, attrBonus, acBonus; + elemResist = attrBonus = acBonus = Res.FIELD_NONE; + + if (item._material < 36) { + int resistence = Res.ELEMENTAL_RESISTENCES[item._material]; + if (resistence > 0) { + int eCategory = ELEM_FIRE; + while (eCategory < ELEM_MAGIC && Res.ELEMENTAL_CATEGORIES[eCategory] < item._material) + ++eCategory; + + elemResist = Common::String::format(Res.ATTR_XY_BONUS, resistence, + Res.ELEMENTAL_NAMES[eCategory]); + } + } else if (item._material >= 59) { + int bonus = Res.ATTRIBUTE_BONUSES[item._material - 59]; + AttributeCategory aCategory = item.getAttributeCategory(); + attrBonus = Common::String::format(Res.ATTR_XY_BONUS, bonus, + Res.ATTRIBUTE_NAMES[aCategory]); + } + + int strength = Res.ARMOR_STRENGTHS[item._id]; + if (item._material >= 37 && item._material <= 58) { + strength += Res.METAL_LAC[item._material - 37]; + } + acBonus = Common::String::format("%+d", strength); + + return Common::String::format(Res.ITEM_DETAILS, classes.c_str(), + Res.FIELD_NONE, Res.FIELD_NONE, Res.FIELD_NONE, + elemResist.c_str(), acBonus.c_str(), attrBonus.c_str(), Res.FIELD_NONE); +} + +void AccessoryItems::equipItem(int itemIndex) { + XeenItem &item = operator[](itemIndex); + + if (item._id == 1) { + int count = 0; + for (uint idx = 0; idx < size(); ++idx) { + XeenItem &i = operator[](idx); + if (i._frame == 8) + ++count; + } + + if (count <= 1) + item._frame = 8; + else + equipError(-1, CATEGORY_ACCESSORY, itemIndex, CATEGORY_ACCESSORY); + } else if (item._id == 2) { + for (uint idx = 0; idx < size(); ++idx) { + XeenItem &i = operator[](idx); + if (i._frame == 12) { + equipError(itemIndex, CATEGORY_ACCESSORY, idx, CATEGORY_ACCESSORY); + return; + } + } + } else if (item._id <= 7) { + int count = 0; + for (uint idx = 0; idx < size(); ++idx) { + XeenItem &i = operator[](idx); + if (i._frame == 7) + ++count; + } + + if (count <= 1) + item._frame = 7; + else + equipError(-2, CATEGORY_ACCESSORY, itemIndex, CATEGORY_ACCESSORY); + } else { + for (uint idx = 0; idx < size(); ++idx) { + XeenItem &i = operator[](idx); + if (i._frame == 11) { + equipError(itemIndex, CATEGORY_ACCESSORY, idx, CATEGORY_ACCESSORY); + return; + } + } + + item._frame = 11; + } +} + +Common::String AccessoryItems::getFullDescription(int itemIndex, int displayNum) { + XeenItem &i = operator[](itemIndex); + Resources &res = *getVm()->_resources; + + return Common::String::format("\f%02u%s%s%s\f%02u%s%s", displayNum, + !i._bonusFlags ? "" : res._maeNames[i._material].c_str(), + (i._bonusFlags & ITEMFLAG_BROKEN) ? Res.ITEM_BROKEN : "", + (i._bonusFlags & ITEMFLAG_CURSED) ? Res.ITEM_CURSED : "", + displayNum, + Res.ARMOR_NAMES[i._id], + (i._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED)) || + !i._bonusFlags ? "\b " : "" + ); +} + +/* +* Returns a text string listing all the stats/attributes of a given item +*/ +Common::String AccessoryItems::getAttributes(XeenItem &item, const Common::String &classes) { + Common::String elemResist, attrBonus; + elemResist = attrBonus = Res.FIELD_NONE; + + if (item._material < 36) { + int resistence = Res.ELEMENTAL_RESISTENCES[item._material]; + if (resistence > 0) { + int eCategory = ELEM_FIRE; + while (eCategory < ELEM_MAGIC && Res.ELEMENTAL_CATEGORIES[eCategory] < item._material) + ++eCategory; + + elemResist = Common::String::format(Res.ATTR_XY_BONUS, resistence, + Res.ELEMENTAL_NAMES[eCategory]); + } + } else if (item._material >= 59) { + int bonus = Res.ATTRIBUTE_BONUSES[item._material - 59]; + AttributeCategory aCategory = item.getAttributeCategory(); + attrBonus = Common::String::format(Res.ATTR_XY_BONUS, bonus, + Res.ATTRIBUTE_NAMES[aCategory]); + } + + return Common::String::format(Res.ITEM_DETAILS, classes.c_str(), + Res.FIELD_NONE, Res.FIELD_NONE, Res.FIELD_NONE, + elemResist.c_str(), Res.FIELD_NONE, attrBonus.c_str(), Res.FIELD_NONE); +} + +/*------------------------------------------------------------------------*/ + +Common::String MiscItems::getFullDescription(int itemIndex, int displayNum) { + XeenItem &i = operator[](itemIndex); + Resources &res = *getVm()->_resources; + + return Common::String::format("\f%02u%s%s%s\f%02u%s%s", displayNum, + !i._bonusFlags ? "" : res._maeNames[i._material].c_str(), + (i._bonusFlags & ITEMFLAG_BROKEN) ? Res.ITEM_BROKEN : "", + (i._bonusFlags & ITEMFLAG_CURSED) ? Res.ITEM_CURSED : "", + displayNum, + Res.ARMOR_NAMES[i._id], + (i._bonusFlags & (ITEMFLAG_BROKEN | ITEMFLAG_CURSED)) || + !i._id ? "\b " : "" + ); +} + +Common::String MiscItems::getAttributes(XeenItem &item, const Common::String &classes) { + Common::String specialPower = Res.FIELD_NONE; + Spells &spells = *getVm()->_spells; + + if (item._id) { + specialPower = spells._spellNames[Res.MISC_SPELL_INDEX[item._id]]; + } + + return Common::String::format(Res.ITEM_DETAILS, classes.c_str(), + Res.FIELD_NONE, Res.FIELD_NONE, Res.FIELD_NONE, Res.FIELD_NONE, Res.FIELD_NONE, + Res.FIELD_NONE, specialPower.c_str()); +} +/*------------------------------------------------------------------------*/ + +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 InventoryItemsGroup::breakAllItems() { + for (int idx = 0; idx < INV_ITEMS_TOTAL; ++idx) { + if ((*_itemSets[0])[idx]._id != 34) { + (*_itemSets[0])[idx]._bonusFlags |= ITEMFLAG_BROKEN; + (*_itemSets[0])[idx]._frame = 0; + } + + (*_itemSets[1])[idx]._bonusFlags |= ITEMFLAG_BROKEN; + (*_itemSets[2])[idx]._bonusFlags |= ITEMFLAG_BROKEN; + (*_itemSets[3])[idx]._bonusFlags |= ITEMFLAG_BROKEN; + (*_itemSets[1])[idx]._frame = 0; + (*_itemSets[2])[idx]._frame = 0; + } +} + +} // End of namespace Xeen diff --git a/engines/xeen/item.h b/engines/xeen/item.h new file mode 100644 index 0000000000..2b6058c2fd --- /dev/null +++ b/engines/xeen/item.h @@ -0,0 +1,270 @@ +/* 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_ITEM_H +#define XEEN_ITEM_H + +#include "common/scummsys.h" +#include "common/array.h" +#include "common/rect.h" +#include "common/serializer.h" +#include "xeen/combat.h" +#include "xeen/sprites.h" + +namespace Xeen { + +#define INV_ITEMS_TOTAL 9 + +class XeenEngine; + +enum ItemCategory { + CATEGORY_WEAPON = 0, CATEGORY_ARMOR = 1, CATEGORY_ACCESSORY = 2, CATEGORY_MISC = 3, + NUM_ITEM_CATEGORIES = 4 +}; + +enum AttributeCategory { + ATTR_MIGHT = 0, ATTR_INTELLECT = 1, ATTR_PERSONALITY = 2, ATTR_SPEED = 3, + ATTR_ACCURACY = 4, ATTR_LUCK = 5, ATTR_HIT_POINTS = 6, ATTR_SPELL_POINTS = 7, + ATTR_ARMOR_CLASS = 8, ATTR_THIEVERY = 9 +}; + +class XeenItem { +public: + int _material; + uint _id; + int _bonusFlags; + int _frame; +public: + /** + * Return the name of the item + */ + static const char *getItemName(ItemCategory category, uint id); +public: + XeenItem(); + + /** + * Clear the data for the item + */ + void clear(); + + /** + * Returns true if no item is set + */ + bool empty() const { return _id != 0; } + + /** + * Synchronizes the data for the item + */ + void synchronize(Common::Serializer &s); + + /** + * Gets the elemental category for the item + */ + ElementalCategory getElementalCategory() const; + + /** + * Gets the attribute category for the item + */ + AttributeCategory getAttributeCategory() const; +}; + +class InventoryItems : public Common::Array<XeenItem> { +protected: + Character *_character; + ItemCategory _category; + const char **_names; + + XeenEngine *getVm(); + void equipError(int itemIndex1, ItemCategory category1, int itemIndex2, + ItemCategory category2); + + /** + ** Returns a text string listing all the stats/attributes of a given item + */ + virtual Common::String getAttributes(XeenItem &item, const Common::String &classes) = 0; +public: + InventoryItems(Character *character, ItemCategory category); + virtual ~InventoryItems() {} + + /** + * Clears the set of items + */ + void clear(); + + /** + * Return whether a given item passes class-based usage restrictions + */ + bool passRestrictions(int itemId, bool showError) const; + + /** + * Return the bare name of a given inventory item + */ + Common::String getName(int itemIndex); + + virtual Common::String getFullDescription(int itemIndex, int displayNum = 15) = 0; + + /** + * Returns the identified details for an item + */ + Common::String getIdentifiedDetails(int itemIndex); + + /** + * Discard an item from the inventory + */ + bool discardItem(int itemIndex); + + /** + * Equips an item + */ + virtual void equipItem(int itemIndex) {} + + /** + * Un-equips the given item + */ + void removeItem(int itemIndex); + + /** + * Sorts the items list, removing any empty item slots to the end of the array + */ + void sort(); + + /** + * Enchants an item + */ + virtual void enchantItem(int itemIndex, int amount); + + /** + * Return if the given inventory items list is full + */ + bool isFull() const; +}; + +class WeaponItems: public InventoryItems { +protected: + /** + ** Returns a text string listing all the stats/attributes of a given item + */ + virtual Common::String getAttributes(XeenItem &item, const Common::String &classes); +public: + WeaponItems(Character *character) : InventoryItems(character, CATEGORY_WEAPON) {} + virtual ~WeaponItems() {} + + /** + * Equip a given weapon + */ + virtual void equipItem(int itemIndex); + + /** + * Assembles a full lines description for a specified item for use in + * the Items dialog + */ + virtual Common::String getFullDescription(int itemIndex, int displayNum); + + /** + * Enchants a weapon + */ + virtual void enchantItem(int itemIndex, int amount); +}; + +class ArmorItems : public InventoryItems { +protected: + /** + ** Returns a text string listing all the stats/attributes of a given item + */ + virtual Common::String getAttributes(XeenItem &item, const Common::String &classes); +public: + ArmorItems(Character *character) : InventoryItems(character, CATEGORY_ARMOR) {} + virtual ~ArmorItems() {} + + /** + * Equip a given piece of armor + */ + virtual void equipItem(int itemIndex); + + /** + * Assembles a full lines description for a specified item for use in + * the Items dialog + */ + virtual Common::String getFullDescription(int itemIndex, int displayNum); + + /** + * Enchants an armor + */ + virtual void enchantItem(int itemIndex, int amount); +}; + +class AccessoryItems : public InventoryItems { +protected: + /** + ** Returns a text string listing all the stats/attributes of a given item + */ + virtual Common::String getAttributes(XeenItem &item, const Common::String &classes); +public: + AccessoryItems(Character *character) : InventoryItems(character, CATEGORY_ACCESSORY) {} + + /** + * Equip a given accessory + */ + virtual void equipItem(int itemIndex); + + /** + * Assembles a full lines description for a specified item for use in + * the Items dialog + */ + virtual Common::String getFullDescription(int itemIndex, int displayNum); +}; + +class MiscItems : public InventoryItems { +protected: + /** + ** Returns a text string listing all the stats/attributes of a given item + */ + virtual Common::String getAttributes(XeenItem &item, const Common::String &classes); +public: + MiscItems(Character *character) : InventoryItems(character, CATEGORY_MISC) {} + virtual ~MiscItems() {} + + /** + * Assembles a full lines description for a specified item for use in + * the Items dialog + */ + virtual Common::String getFullDescription(int itemIndex, int displayNum); +}; + +class InventoryItemsGroup { +private: + InventoryItems *_itemSets[4]; +public: + InventoryItemsGroup(InventoryItems &weapons, InventoryItems &armor, + InventoryItems &accessories, InventoryItems &misc); + + InventoryItems &operator[](ItemCategory category); + + /** + * Breaks all the items in a given character's inventory + */ + void breakAllItems(); +}; + +} // End of namespace Xeen + +#endif /* XEEN_CHARACTER_H */ diff --git a/engines/xeen/module.mk b/engines/xeen/module.mk index b3e0d9ae86..a988137701 100644 --- a/engines/xeen/module.mk +++ b/engines/xeen/module.mk @@ -42,6 +42,7 @@ MODULE_OBJS := \ interface.o \ interface_minimap.o \ interface_scene.o \ + item.o \ locations.o \ map.o \ party.o \ |