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 \  | 
