diff --git a/src/Bin/CodeGen/CodeGen.cpp b/src/Bin/CodeGen/CodeGen.cpp index 8d739e6a8e4..485f2d2953b 100644 --- a/src/Bin/CodeGen/CodeGen.cpp +++ b/src/Bin/CodeGen/CodeGen.cpp @@ -48,11 +48,11 @@ int runItemIdCodeGen(const CodeGenOptions &options, GameResourceManager *resourc CodeGenMap map; map.insert(ITEM_NULL, "NULL", ""); - for(ItemId i : itemTable.pItems.indices()) { - const ItemDesc &desc = itemTable.pItems[i]; + for(ItemId i : itemTable.items.indices()) { + const ItemDesc &desc = itemTable.items[i]; std::string icon = desc.iconName; std::string name = desc.name; - std::string description = desc.pDescription; + std::string description = desc.description; if (icon.empty() || icon == "null") { map.insert(i, "", "Unused."); @@ -97,24 +97,24 @@ int runItemIdCodeGen(const CodeGenOptions &options, GameResourceManager *resourc if (enumName.starts_with("LETTER_FROM") && enumName.contains("_TO_")) enumName = enumName.substr(0, enumName.find("_TO_")); - if (desc.uEquipType == ITEM_TYPE_REAGENT) { + if (desc.type == ITEM_TYPE_REAGENT) { enumName = "REAGENT_" + enumName; - } else if (desc.uEquipType == ITEM_TYPE_POTION) { + } else if (desc.type == ITEM_TYPE_POTION) { if (!enumName.starts_with("POTION_")) enumName = "POTION_" + enumName; if (enumName.ends_with("_POTION")) enumName = enumName.substr(0, enumName.size() - 7); - } else if (desc.uEquipType == ITEM_TYPE_SPELL_SCROLL) { + } else if (desc.type == ITEM_TYPE_SPELL_SCROLL) { enumName = "SCROLL_" + enumName; - } else if (desc.uEquipType == ITEM_TYPE_BOOK) { + } else if (desc.type == ITEM_TYPE_BOOK) { enumName = "SPELLBOOK_" + enumName; - } else if (desc.uEquipType == ITEM_TYPE_MESSAGE_SCROLL) { + } else if (desc.type == ITEM_TYPE_MESSAGE_SCROLL) { if (enumName.ends_with("_RECIPE")) { enumName = "RECIPE_" + enumName.substr(0, enumName.size() - 7); } else if (!enumName.starts_with("MESSAGE_")) { enumName = "MESSAGE_" + enumName; } - } else if (desc.uEquipType == ITEM_TYPE_GOLD) { + } else if (desc.type == ITEM_TYPE_GOLD) { if (description == "A small pile of gold coins.") { enumName = "GOLD_SMALL"; } else if (description == "A pile of gold coins.") { @@ -124,15 +124,15 @@ int runItemIdCodeGen(const CodeGenOptions &options, GameResourceManager *resourc } else { throw Exception("Unrecognized gold pile description '{}'", description); } - } else if (desc.uEquipType == ITEM_TYPE_GEM) { + } else if (desc.type == ITEM_TYPE_GEM) { enumName = "GEM_" + enumName; } - if (desc.uMaterial == RARITY_ARTIFACT) { + if (desc.rarity == RARITY_ARTIFACT) { enumName = "ARTIFACT_" + enumName; - } else if (desc.uMaterial == RARITY_RELIC) { + } else if (desc.rarity == RARITY_RELIC) { enumName = "RELIC_" + enumName; - } else if (desc.uMaterial == RARITY_SPECIAL) { + } else if (desc.rarity == RARITY_SPECIAL) { enumName = "SPECIAL_" + enumName; } else if (description.starts_with("Quest")) { enumName = "QUEST_" + enumName; diff --git a/src/Engine/Data/CMakeLists.txt b/src/Engine/Data/CMakeLists.txt index 8c2ba77f6dd..1a1475d370b 100644 --- a/src/Engine/Data/CMakeLists.txt +++ b/src/Engine/Data/CMakeLists.txt @@ -21,7 +21,9 @@ set(ENGINE_DATA_HEADERS PortraitFrameData.h TileData.h TileEnums.h - TileEnumFunctions.h) + TileEnumFunctions.h + StandardEnchantmentData.h + SpecialEnchantmentData.h) add_library(engine_data STATIC ${ENGINE_DATA_SOURCES} ${ENGINE_DATA_HEADERS}) target_link_libraries(engine_data PUBLIC library_serialization utility) diff --git a/src/Engine/Data/PortraitFrameData.h b/src/Engine/Data/PortraitFrameData.h index 200baf1e99f..652cafa7058 100644 --- a/src/Engine/Data/PortraitFrameData.h +++ b/src/Engine/Data/PortraitFrameData.h @@ -1,6 +1,6 @@ #pragma once -#include "Engine/Objects/CharacterEnums.h" // TODO(captainurist): doesn't belong here, move out. +#include "Engine/Objects/CharacterEnums.h" // TODO(captainurist): Data -> Objects dependency, we don't want that. #include "Engine/Time/Duration.h" diff --git a/src/Engine/Data/SpecialEnchantmentData.h b/src/Engine/Data/SpecialEnchantmentData.h new file mode 100644 index 00000000000..5ff68b87f61 --- /dev/null +++ b/src/Engine/Data/SpecialEnchantmentData.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include "Engine/Objects/ItemEnums.h" // TODO(captainurist): Data -> Objects dependency, we don't want that. + +#include "Utility/IndexedArray.h" + +struct SpecialEnchantmentData { + std::string description; // Enchantment description, e.g. "Explosive impact!". + std::string itemSuffixOrPrefix; // Suffix or prefix for the enchanted item, e.g. "of Carnage". Whether it's a prefix + // or a suffix is hardcoded in item name generation function. + IndexedArray chanceByItemType; + int additionalValue; // Value in gold added to enchanted item's base value. + int iTreasureLevel; +}; diff --git a/src/Engine/Data/StandardEnchantmentData.h b/src/Engine/Data/StandardEnchantmentData.h new file mode 100644 index 00000000000..2c4ee8661d9 --- /dev/null +++ b/src/Engine/Data/StandardEnchantmentData.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#include "Utility/IndexedArray.h" + +#include "Engine/Objects/ItemEnums.h" // TODO(captainurist): Data -> Objects dependency, we don't want that. + +struct StandardEnchantmentData { + std::string attributeName; // Name of the attribute this applies to, e.g. "Might" or "Armsmaster skill". + std::string itemSuffix; // Suffix for the enchanted item, e.g. "of Might" or "of Arms". + IndexedArray chanceByItemType; +}; diff --git a/src/Engine/Graphics/Indoor.cpp b/src/Engine/Graphics/Indoor.cpp index f0fe59c1686..92b941ceea1 100644 --- a/src/Engine/Graphics/Indoor.cpp +++ b/src/Engine/Graphics/Indoor.cpp @@ -1041,7 +1041,7 @@ void loadAndPrepareBLV(MapId mapid, bool bLoading) { if (pSpriteObjects[i].uObjectDescID) { if (pSpriteObjects[i].containing_item.itemId != ITEM_NULL) { if (pSpriteObjects[i].containing_item.itemId != ITEM_POTION_BOTTLE && - pItemTable->pItems[pSpriteObjects[i].containing_item.itemId].uEquipType == ITEM_TYPE_POTION && + pItemTable->items[pSpriteObjects[i].containing_item.itemId].type == ITEM_TYPE_POTION && !pSpriteObjects[i].containing_item.potionPower) pSpriteObjects[i].containing_item.potionPower = grng->random(15) + 5; pItemTable->SetSpecialBonus(&pSpriteObjects[i].containing_item); @@ -1986,7 +1986,7 @@ int SpawnEncounterMonsters(MapInfo *map_info, int enc_index) { int DropTreasureAt(ItemTreasureLevel trs_level, RandomItemType trs_type, Vec3f pos, uint16_t facing) { SpriteObject a1; pItemTable->generateItem(trs_level, trs_type, &a1.containing_item); - a1.uType = pItemTable->pItems[a1.containing_item.itemId].uSpriteID; + a1.uType = pItemTable->items[a1.containing_item.itemId].spriteId; a1.uObjectDescID = pObjectList->ObjectIDByItemID(a1.uType); a1.vPosition = pos; a1.uFacing = facing; @@ -2019,12 +2019,12 @@ void SpawnRandomTreasure(MapInfo *mapInfo, SpawnPoint *a2) { } a1a.containing_item.generateGold(a2->uItemIndex); - a1a.uType = pItemTable->pItems[a1a.containing_item.itemId].uSpriteID; + a1a.uType = pItemTable->items[a1a.containing_item.itemId].spriteId; a1a.uObjectDescID = pObjectList->ObjectIDByItemID(a1a.uType); } else { if (!a1a.containing_item.GenerateArtifact()) return; - a1a.uType = pItemTable->pItems[a1a.containing_item.itemId].uSpriteID; + a1a.uType = pItemTable->items[a1a.containing_item.itemId].spriteId; a1a.uObjectDescID = pObjectList->ObjectIDByItemID(a1a.uType); a1a.containing_item.Reset(); // TODO(captainurist): this needs checking } diff --git a/src/Engine/Graphics/Outdoor.cpp b/src/Engine/Graphics/Outdoor.cpp index a2e313cb25b..d8ae6932bd1 100644 --- a/src/Engine/Graphics/Outdoor.cpp +++ b/src/Engine/Graphics/Outdoor.cpp @@ -630,7 +630,7 @@ void OutdoorLocation::ArrangeSpriteObjects() { } if (pSpriteObjects[i].containing_item.itemId != ITEM_NULL) { if (pSpriteObjects[i].containing_item.itemId != ITEM_POTION_BOTTLE && - pItemTable->pItems[pSpriteObjects[i].containing_item.itemId].uEquipType == ITEM_TYPE_POTION && + pItemTable->items[pSpriteObjects[i].containing_item.itemId].type == ITEM_TYPE_POTION && !pSpriteObjects[i].containing_item.potionPower) pSpriteObjects[i].containing_item.potionPower = grng->random(15) + 5; pItemTable->SetSpecialBonus(&pSpriteObjects[i].containing_item); diff --git a/src/Engine/Graphics/Viewport.cpp b/src/Engine/Graphics/Viewport.cpp index 447f6a81f40..9e8d4ee2cc6 100644 --- a/src/Engine/Graphics/Viewport.cpp +++ b/src/Engine/Graphics/Viewport.cpp @@ -217,14 +217,14 @@ void ViewingParams::_443365() { } void ItemInteraction(unsigned int item_id) { - if (pItemTable->pItems[pSpriteObjects[item_id].containing_item.itemId].uEquipType == ITEM_TYPE_GOLD) { + if (pItemTable->items[pSpriteObjects[item_id].containing_item.itemId].type == ITEM_TYPE_GOLD) { pParty->partyFindsGold(pSpriteObjects[item_id].containing_item.goldAmount, GOLD_RECEIVE_SHARE); } else { if (pParty->pPickedItem.itemId != ITEM_NULL) { return; } - engine->_statusBar->setEvent(LSTR_FMT_YOU_FOUND_ITEM, pItemTable->pItems[pSpriteObjects[item_id].containing_item.itemId].pUnidentifiedName); + engine->_statusBar->setEvent(LSTR_FMT_YOU_FOUND_ITEM, pItemTable->items[pSpriteObjects[item_id].containing_item.itemId].unidentifiedName); // TODO: WTF? 184 / 185 qbits are associated with Tatalia's Mercenery Guild Harmondale raids. Are these about castle's tapestries ? if (pSpriteObjects[item_id].containing_item.itemId == ITEM_ARTIFACT_SPLITTER) { diff --git a/src/Engine/Objects/Actor.cpp b/src/Engine/Objects/Actor.cpp index dd87f19d48d..bf4f3ffd615 100644 --- a/src/Engine/Objects/Actor.cpp +++ b/src/Engine/Objects/Actor.cpp @@ -1839,7 +1839,7 @@ void Actor::Die(unsigned int uActorID) { drop.itemId = itemDropForMonsterType(monsterTypeForMonsterId(actor->monsterInfo.id)); if (grng->random(100) < 20 && drop.itemId != ITEM_NULL) { - SpriteObject::dropItemAt(pItemTable->pItems[drop.itemId].uSpriteID, + SpriteObject::dropItemAt(pItemTable->items[drop.itemId].spriteId, actor->pos + Vec3f(0, 0, 16), grng->random(200) + 200, 1, true, 0, &drop); } @@ -3576,7 +3576,7 @@ void Actor::LootActor() { Dst.Reset(); Dst.itemId = this->carriedItemId; - StatusBarItemFound(foundGold, pItemTable->pItems[Dst.itemId].pUnidentifiedName); + StatusBarItemFound(foundGold, pItemTable->items[Dst.itemId].unidentifiedName); if (Dst.isWand()) { Dst.numCharges = grng->random(6) + Dst.GetDamageMod() + 1; @@ -3612,7 +3612,7 @@ void Actor::LootActor() { Dst = this->items[3]; this->items[3].Reset(); - StatusBarItemFound(foundGold, pItemTable->pItems[Dst.itemId].pUnidentifiedName); + StatusBarItemFound(foundGold, pItemTable->items[Dst.itemId].unidentifiedName); if (!pParty->addItemToParty(&Dst)) { pParty->setHoldingItem(&Dst); @@ -3623,7 +3623,7 @@ void Actor::LootActor() { if (grng->random(100) < this->monsterInfo.treasureDropChance && this->monsterInfo.treasureLevel != ITEM_TREASURE_LEVEL_INVALID) { pItemTable->generateItem(this->monsterInfo.treasureLevel, this->monsterInfo.treasureType, &Dst); - StatusBarItemFound(foundGold, pItemTable->pItems[Dst.itemId].pUnidentifiedName); + StatusBarItemFound(foundGold, pItemTable->items[Dst.itemId].unidentifiedName); if (!pParty->addItemToParty(&Dst)) { pParty->setHoldingItem(&Dst); diff --git a/src/Engine/Objects/Character.cpp b/src/Engine/Objects/Character.cpp index 5469a2f8559..7e60200deda 100644 --- a/src/Engine/Objects/Character.cpp +++ b/src/Engine/Objects/Character.cpp @@ -611,7 +611,7 @@ void Character::SetCondition(Condition condition, int blockable) { } bool Character::canFitItem(unsigned int uSlot, ItemId uItemID) const { - auto img = assets->getImage_ColorKey(pItemTable->pItems[uItemID].iconName); + auto img = assets->getImage_ColorKey(pItemTable->items[uItemID].iconName); int slotWidth = GetSizeInInventorySlots(img->width()); int slotHeight = GetSizeInInventorySlots(img->height()); @@ -675,7 +675,7 @@ void Character::WearItem(ItemId uItemID) { if (item_indx != -1) { pInventoryItemList[item_indx].itemId = uItemID; - ItemSlot item_body_anch = pEquipTypeToBodyAnchor[pItemTable->pItems[uItemID].uEquipType]; + ItemSlot item_body_anch = pEquipTypeToBodyAnchor[pItemTable->items[uItemID].type]; pEquipment[item_body_anch] = item_indx + 1; pInventoryItemList[item_indx].equippedSlot = item_body_anch; } @@ -752,7 +752,7 @@ void Character::PutItemAtInventoryIndex( ItemId uItemID, int itemListPos, int index) { // originally accepted ItemGen *but needed only its uItemID - auto img = assets->getImage_ColorKey(pItemTable->pItems[uItemID].iconName); + auto img = assets->getImage_ColorKey(pItemTable->items[uItemID].iconName); int slot_width = GetSizeInInventorySlots(img->width()); int slot_height = GetSizeInInventorySlots(img->height()); @@ -817,7 +817,7 @@ bool Character::CanIdentify(ItemGen *pItem) const { // check item level against skill bool result = (multiplier * val.level()) >= - pItemTable->pItems[pItem->itemId].identifyDifficulty; + pItemTable->items[pItem->itemId].identifyDifficulty; return result; } @@ -838,7 +838,7 @@ bool Character::CanRepair(ItemGen *pItem) const { // check item level against skill bool result = (multiplier * val.level()) >= - pItemTable->pItems[pItem->itemId].identifyDifficulty; + pItemTable->items[pItem->itemId].identifyDifficulty; return result; } @@ -1066,7 +1066,7 @@ int Character::CalculateMeleeDamageTo(bool ignoreSkillBonus, bool ignoreOffhand, ItemGen *mainHandItemGen = this->GetMainHandItem(); ItemId itemId = mainHandItemGen->itemId; bool addOneDice = false; - if (pItemTable->pItems[itemId].uSkillType == CHARACTER_SKILL_SPEAR && + if (pItemTable->items[itemId].skill == CHARACTER_SKILL_SPEAR && !this->pEquipment[ITEM_SLOT_OFF_HAND]) // using spear in two hands adds a dice roll addOneDice = true; @@ -1109,11 +1109,11 @@ int Character::CalculateMeleeDmgToEnemyWithWeapon(ItemGen *weapon, MonsterId uTargetActorID, bool addOneDice) { ItemId itemId = weapon->itemId; - int diceCount = pItemTable->pItems[itemId].uDamageDice; + int diceCount = pItemTable->items[itemId].damageDice; if (addOneDice) diceCount++; - int diceSides = pItemTable->pItems[itemId].uDamageRoll; + int diceSides = pItemTable->items[itemId].damageRoll; int diceResult = 0; for (int i = 0; i < diceCount; i++) { // roll dice @@ -1121,7 +1121,7 @@ int Character::CalculateMeleeDmgToEnemyWithWeapon(ItemGen *weapon, } int totalDmg = - pItemTable->pItems[itemId].uDamageMod + diceResult; // add modifer + pItemTable->items[itemId].damageMod + diceResult; // add modifer if (uTargetActorID > MONSTER_INVALID) { // if an actor has been provided ItemEnchantment enchType = @@ -1148,7 +1148,7 @@ int Character::CalculateMeleeDmgToEnemyWithWeapon(ItemGen *weapon, // master dagger triple damage backstab if (getActualSkillValue(CHARACTER_SKILL_DAGGER).mastery() >= CHARACTER_SKILL_MASTERY_MASTER && - pItemTable->pItems[itemId].uSkillType == CHARACTER_SKILL_DAGGER && grng->random(100) < 10) + pItemTable->items[itemId].skill == CHARACTER_SKILL_DAGGER && grng->random(100) < 10) totalDmg *= 3; return totalDmg; @@ -1205,13 +1205,13 @@ int Character::CalculateRangedDamageTo(MonsterId uMonsterInfoID) { (ItemGen*)&this->pInventoryItemList[this->pEquipment[ITEM_SLOT_BOW] - 1]; ItemEnchantment itemenchant = bow->specialEnchantment; - signed int dmgperroll = pItemTable->pItems[bow->itemId].uDamageRoll; + signed int dmgperroll = pItemTable->items[bow->itemId].damageRoll; int damagefromroll = 0; int damage = 0; - damagefromroll = grng->randomDice(pItemTable->pItems[bow->itemId].uDamageDice, dmgperroll); + damagefromroll = grng->randomDice(pItemTable->items[bow->itemId].damageDice, dmgperroll); - damage = pItemTable->pItems[bow->itemId].uDamageMod + + damage = pItemTable->items[bow->itemId].damageMod + damagefromroll; // total damage if (uMonsterInfoID != MONSTER_INVALID) { // check against bow enchantments @@ -1533,10 +1533,10 @@ StealResult Character::StealFromActor(unsigned int uActorID, int _steal_perm, in if (carriedItemId != ITEM_NULL) { // load item into tempitem actroPtr->carriedItemId = ITEM_NULL; tempItem.itemId = carriedItemId; - if (pItemTable->pItems[carriedItemId].uEquipType == ITEM_TYPE_WAND) { - tempItem.numCharges = grng->random(6) + pItemTable->pItems[carriedItemId].uDamageMod + 1; + if (pItemTable->items[carriedItemId].type == ITEM_TYPE_WAND) { + tempItem.numCharges = grng->random(6) + pItemTable->items[carriedItemId].damageMod + 1; tempItem.maxCharges = tempItem.numCharges; - } else if (pItemTable->pItems[carriedItemId].uEquipType == ITEM_TYPE_POTION && carriedItemId != ITEM_POTION_BOTTLE) { + } else if (pItemTable->items[carriedItemId].type == ITEM_TYPE_POTION && carriedItemId != ITEM_POTION_BOTTLE) { tempItem.potionPower = 2 * grng->random(4) + 2; } } else { @@ -1547,7 +1547,7 @@ StealResult Character::StealFromActor(unsigned int uActorID, int _steal_perm, in } if (carriedItemId != ITEM_NULL) { - engine->_statusBar->setEvent(LSTR_FMT_S_STOLE_D_ITEM, this->name, pItemTable->pItems[carriedItemId].pUnidentifiedName); + engine->_statusBar->setEvent(LSTR_FMT_S_STOLE_D_ITEM, this->name, pItemTable->items[carriedItemId].unidentifiedName); pParty->setHoldingItem(&tempItem); return STEAL_SUCCESS; } @@ -1687,7 +1687,7 @@ int Character::ReceiveSpecialAttackEffect(SpecialAttackType attType, Actor *pAct itemtobreak = &this->pInventoryItemList [itemstobreaklist[grng->random(itemstobreakcounter)]]; statcheckbonus = - 3 * (std::to_underlying(pItemTable->pItems[itemtobreak->itemId].uMaterial) + + 3 * (std::to_underlying(pItemTable->items[itemtobreak->itemId].rarity) + itemtobreak->GetDamageMod()); break; @@ -1710,7 +1710,7 @@ int Character::ReceiveSpecialAttackEffect(SpecialAttackType attType, Actor *pAct itemtobreak = &this->pInventoryItemList [itemstobreaklist[grng->random(itemstobreakcounter)]]; statcheckbonus = - 3 * (std::to_underlying(pItemTable->pItems[itemtobreak->itemId].uMaterial) + + 3 * (std::to_underlying(pItemTable->items[itemtobreak->itemId].rarity) + itemtobreak->GetDamageMod()); break; @@ -1736,7 +1736,7 @@ int Character::ReceiveSpecialAttackEffect(SpecialAttackType attType, Actor *pAct itemtobreak = &this->pInventoryItemList [itemstobreaklist[grng->random(itemstobreakcounter)]]; statcheckbonus = - 3 * (std::to_underlying(pItemTable->pItems[itemtobreak->itemId].uMaterial) + + 3 * (std::to_underlying(pItemTable->items[itemtobreak->itemId].rarity) + itemtobreak->GetDamageMod()); break; @@ -2635,10 +2635,10 @@ int Character::GetItemsBonus(CharacterAttribute attr, bool getOnlyMainHandDmg /* if (pItemTable->IsMaterialNonCommon(currEquippedItem) && !pItemTable->IsMaterialSpecial(currEquippedItem)) { currEquippedItem->GetItemBonusArtifact(this, attr, &v62); - } else if (currEquippedItem->attributeEnchantment) { - if (*currEquippedItem->attributeEnchantment == attr) { + } else if (currEquippedItem->standardEnchantment) { + if (*currEquippedItem->standardEnchantment == attr) { // if (currEquippedItem->IsRegularEnchanmentForAttribute(attr)) - v5 += currEquippedItem->attributeEnchantmentStrength; + v5 += currEquippedItem->standardEnchantmentStrength; } } else { currEquippedItem->GetItemBonusSpecialEnchantment(this, attr, &v5, &v61); @@ -7144,7 +7144,7 @@ MerchantPhrase Character::SelectPhrasesTransaction(ItemGen *pItem, HouseType bui case HOUSE_TYPE_MAGIC_SHOP: if (idemId >= ITEM_ARTIFACT_HERMES_SANDALS) return MERCHANT_PHRASE_INVALID_ACTION; - if (pItemTable->pItems[idemId].uSkillType != CHARACTER_SKILL_MISC) + if (pItemTable->items[idemId].skill != CHARACTER_SKILL_MISC) return MERCHANT_PHRASE_INCOMPATIBLE_ITEM; break; case HOUSE_TYPE_ALCHEMY_SHOP: diff --git a/src/Engine/Objects/Chest.cpp b/src/Engine/Objects/Chest.cpp index bffb24b6e7e..61abedfc2f8 100644 --- a/src/Engine/Objects/Chest.cpp +++ b/src/Engine/Objects/Chest.cpp @@ -247,7 +247,7 @@ bool Chest::CanPlaceItemAt(int test_cell_position, ItemId item_id, int uChestID) int chest_cell_heght = pChestHeightsByType[vChests[uChestID].uChestBitmapID]; int chest_cell_width = pChestWidthsByType[vChests[uChestID].uChestBitmapID]; - auto img = assets->getImage_ColorKey(pItemTable->pItems[item_id].iconName); + auto img = assets->getImage_ColorKey(pItemTable->items[item_id].iconName); int slot_width = GetSizeInInventorySlots(img->width()); int slot_height = GetSizeInInventorySlots(img->height()); @@ -347,7 +347,7 @@ void Chest::PlaceItemAt(unsigned int put_cell_pos, unsigned int item_at_cell, in vChests[uChestID].igChestItems[item_at_cell].maxCharges = v6; } - auto img = assets->getImage_Alpha(pItemTable->pItems[uItemID].iconName); + auto img = assets->getImage_Alpha(pItemTable->items[uItemID].iconName); int v9 = img->width(); if (v9 < 14) v9 = 14; @@ -529,7 +529,7 @@ void Chest::GrabItem(bool all) { // new fucntion to grab items from chest using if (pParty->hasActiveCharacter() && (InventSlot = pParty->activeCharacter().AddItem(-1, chestitem.itemId)) != 0) { // can place pParty->activeCharacter().pInventoryItemList[InventSlot - 1] = chestitem; grabcount++; - engine->_statusBar->setEvent(LSTR_FMT_YOU_FOUND_ITEM, pItemTable->pItems[chestitem.itemId].pUnidentifiedName); + engine->_statusBar->setEvent(LSTR_FMT_YOU_FOUND_ITEM, pItemTable->items[chestitem.itemId].unidentifiedName); } else { // no room so set as holding item pParty->setHoldingItem(&chestitem); RemoveItemAtChestIndex(loop); diff --git a/src/Engine/Objects/ItemEnchantment.h b/src/Engine/Objects/ItemEnchantment.h index 20c60886c4b..46b0d5a5384 100644 --- a/src/Engine/Objects/ItemEnchantment.h +++ b/src/Engine/Objects/ItemEnchantment.h @@ -1,14 +1,9 @@ #pragma once -#include - #include "Engine/Objects/CharacterEnums.h" -#include "Engine/Objects/ItemEnums.h" - -#include "Utility/IndexedArray.h" - -class Character; +// TODO(captainurist): This was an attempt at refactoring the various enchantments we had, and it wasn't finished. +// Enchantment unification makes sense, so finish this! struct CEnchantment { CEnchantment() {} explicit CEnchantment(int bonus, CharacterSkillType skill = CHARACTER_SKILL_INVALID) @@ -17,22 +12,3 @@ struct CEnchantment { CharacterSkillType skillType = CHARACTER_SKILL_INVALID; int statBonus = 0; }; - -struct ItemEnchantmentTable { // Bonus|Sta|Of - // Name|Arm|Shld|Helm|Belt|Cape|Gaunt|Boot|Ring|Amul - std::string pBonusStat; - std::string pOfName; - IndexedArray chancesByItemType; -}; - -struct ItemSpecialEnchantmentTable { // 1Ch - // Bonus Stat|Name - // Add|W1|W2|Miss|Arm|Shld|Helm|Belt|Cape|Gaunt|Boot|Ring|Amul|Value|Lvl|Description - // fo special Bonuses and values - - std::string pBonusStatement; // 0 - std::string pNameAdd; // 4 - IndexedArray to_item_apply; // 8 - int iValue; // 14 - int iTreasureLevel; // 18 -}; diff --git a/src/Engine/Objects/Items.cpp b/src/Engine/Objects/Items.cpp index 6153fbc1826..27ce28bd2d7 100644 --- a/src/Engine/Objects/Items.cpp +++ b/src/Engine/Objects/Items.cpp @@ -146,7 +146,7 @@ void ItemGen::Reset() { void ItemGen::UpdateTempBonus(Time time) { if (this->flags & ITEM_TEMP_BONUS) { if (time > this->enchantmentExpirationTime) { - this->attributeEnchantment = {}; + this->standardEnchantment = {}; this->specialEnchantment = ITEM_ENCHANTMENT_NULL; this->flags &= ~ITEM_TEMP_BONUS; } @@ -155,15 +155,15 @@ void ItemGen::UpdateTempBonus(Time time) { //----- (00456442) -------------------------------------------------------- int ItemGen::GetValue() const { - int uBaseValue = pItemTable->pItems[this->itemId].uValue; + int uBaseValue = pItemTable->items[this->itemId].baseValue; if (flags & ITEM_TEMP_BONUS || pItemTable->IsMaterialNonCommon(this)) return uBaseValue; - if (potionPower || attributeEnchantment) // TODO(captainurist): can drop potionPower? - return uBaseValue + 100 * attributeEnchantmentStrength; + if (potionPower || standardEnchantment) // TODO(captainurist): can drop potionPower? + return uBaseValue + 100 * standardEnchantmentStrength; if (specialEnchantment != ITEM_ENCHANTMENT_NULL) { - int mod = (pItemTable->pSpecialEnchantments[specialEnchantment].iTreasureLevel & 4); - int bonus = pItemTable->pSpecialEnchantments[specialEnchantment].iValue; + int mod = (pItemTable->specialEnchantments[specialEnchantment].iTreasureLevel & 4); + int bonus = pItemTable->specialEnchantments[specialEnchantment].additionalValue; if (!mod) return uBaseValue + bonus; else @@ -177,7 +177,7 @@ std::string ItemGen::GetDisplayName() { if (IsIdentified()) { return GetIdentifiedName(); } else { - return pItemTable->pItems[itemId].pUnidentifiedName; + return pItemTable->items[itemId].unidentifiedName; } } @@ -186,7 +186,7 @@ std::string ItemGen::GetIdentifiedName() { ItemType equip_type = GetItemEquipType(); if ((equip_type == ITEM_TYPE_REAGENT) || (equip_type == ITEM_TYPE_POTION) || (equip_type == ITEM_TYPE_GOLD)) { - return pItemTable->pItems[itemId].name; + return pItemTable->items[itemId].name; } if (itemId == ITEM_QUEST_LICH_JAR_FULL) { // Lich Jar @@ -200,11 +200,11 @@ std::string ItemGen::GetIdentifiedName() { } if (!pItemTable->IsMaterialNonCommon(this)) { - if (attributeEnchantment) { - return std::string(pItemTable->pItems[itemId].name) + " " + - pItemTable->standardEnchantments[*attributeEnchantment].pOfName; + if (standardEnchantment) { + return std::string(pItemTable->items[itemId].name) + " " + + pItemTable->standardEnchantments[*standardEnchantment].itemSuffix; } else if (specialEnchantment == ITEM_ENCHANTMENT_NULL) { - return pItemTable->pItems[itemId].name; + return pItemTable->items[itemId].name; } else { if (specialEnchantment == ITEM_ENCHANTMENT_VAMPIRIC || specialEnchantment == ITEM_ENCHANTMENT_DEMON_SLAYING @@ -223,17 +223,17 @@ std::string ItemGen::GetIdentifiedName() { ) { // enchantment and name positions inverted! return fmt::format( "{} {}", - pItemTable->pSpecialEnchantments[specialEnchantment].pNameAdd, - pItemTable->pItems[itemId].name + pItemTable->specialEnchantments[specialEnchantment].itemSuffixOrPrefix, + pItemTable->items[itemId].name ); } else { - return std::string(pItemTable->pItems[itemId].name) + " " + - pItemTable->pSpecialEnchantments[specialEnchantment].pNameAdd; + return std::string(pItemTable->items[itemId].name) + " " + + pItemTable->specialEnchantments[specialEnchantment].itemSuffixOrPrefix; } } } - return pItemTable->pItems[itemId].name; + return pItemTable->items[itemId].name; } //----- (004505CC) -------------------------------------------------------- @@ -710,7 +710,7 @@ void ItemGen::GetItemBonusArtifact(const Character *owner, } bool ItemGen::IsRegularEnchanmentForAttribute(CharacterAttribute attrToGet) { - //auto pos = specialBonusMap.find(this->attributeEnchantment); + //auto pos = specialBonusMap.find(this->standardEnchantment); //if (pos == specialBonusMap.end()) // return false; @@ -724,11 +724,11 @@ ItemType ItemGen::GetItemEquipType() const { if (this->itemId == ITEM_NULL) return ITEM_TYPE_NONE; else - return pItemTable->pItems[this->itemId].uEquipType; + return pItemTable->items[this->itemId].type; } CharacterSkillType ItemGen::GetPlayerSkillType() const { - CharacterSkillType skl = pItemTable->pItems[this->itemId].uSkillType; + CharacterSkillType skl = pItemTable->items[this->itemId].skill; if (skl == CHARACTER_SKILL_CLUB && engine->config->gameplay.TreatClubAsMace.value()) { // club skill not used but some items load it skl = CHARACTER_SKILL_MACE; @@ -737,19 +737,19 @@ CharacterSkillType ItemGen::GetPlayerSkillType() const { } const std::string& ItemGen::GetIconName() const { - return pItemTable->pItems[this->itemId].iconName; + return pItemTable->items[this->itemId].iconName; } uint8_t ItemGen::GetDamageDice() const { - return pItemTable->pItems[this->itemId].uDamageDice; + return pItemTable->items[this->itemId].damageDice; } uint8_t ItemGen::GetDamageRoll() const { - return pItemTable->pItems[this->itemId].uDamageRoll; + return pItemTable->items[this->itemId].damageRoll; } uint8_t ItemGen::GetDamageMod() const { - return pItemTable->pItems[this->itemId].uDamageMod; + return pItemTable->items[this->itemId].damageMod; } //----- (0043C91D) -------------------------------------------------------- @@ -758,7 +758,7 @@ std::string GetItemTextureFilename(ItemId item_id, int index, int shoulder) { // and textures under original ids simply don't exist. int texture_id = std::to_underlying(valueOr(itemTextureIdByItemId, item_id, item_id)); - switch (pItemTable->pItems[item_id].uEquipType) { + switch (pItemTable->items[item_id].type) { case ITEM_TYPE_ARMOUR: if (shoulder == 0) return fmt::format("item{:03}v{}", texture_id, index); diff --git a/src/Engine/Objects/Items.h b/src/Engine/Objects/Items.h index 85f3979f24b..5eb83cebb88 100644 --- a/src/Engine/Objects/Items.h +++ b/src/Engine/Objects/Items.h @@ -11,6 +11,8 @@ #include "Engine/Time/Time.h" #include "Engine/MapEnums.h" +#include "Library/Geometry/Point.h" + #include "Utility/IndexedArray.h" class Character; @@ -110,8 +112,8 @@ struct ItemGen { // 0x24 int goldAmount = 0; // Only for gold. // TODO(captainurist): introduce ATTRIBUTE_NULL? - std::optional attributeEnchantment; // Attribute enchantment, if any. - int attributeEnchantmentStrength = 0; // Attribute enchantment strength - bonus value for the attribute. + std::optional standardEnchantment; // Standard (attribute) enchantment, if any. + int standardEnchantmentStrength = 0; // Attribute enchantment strength - bonus value for the attribute. ItemEnchantment specialEnchantment = ITEM_ENCHANTMENT_NULL; // Special named enchantment, if any. int numCharges = 0; // Number of wand charges, wand disappears when this gets down to 0. int maxCharges = 0; // Max charges in a wand. This is used when recharging. @@ -130,22 +132,21 @@ struct ItemGen { // 0x24 struct ItemDesc { std::string iconName = ""; // Item's icon as shown in character inventory, stored in icons.lod. std::string name = ""; // Item's base name, w/o any enchantments. - std::string pUnidentifiedName = ""; // Unidentified name. - std::string pDescription = ""; // Item description that's shown on right click. - uint32_t uValue = 0; // Item's base value in gold coins. - SpriteId uSpriteID = SPRITE_NULL; // Sprite id that's used when item is dropped. + std::string unidentifiedName = ""; // Unidentified name. + std::string description = ""; // Item description that's shown on right click. + uint32_t baseValue = 0; // Item's base value in gold coins. + SpriteId spriteId = SPRITE_NULL; // Sprite id that's used when item is dropped. int16_t field_1A = 0; - int16_t uEquipX = 0; // Paperdoll offset for the item sprite when equipped, relative to the item type-specific anchor point. - int16_t uEquipY = 0; - ItemType uEquipType = ITEM_TYPE_NONE; // Item type. - CharacterSkillType uSkillType = CHARACTER_SKILL_MISC; // Skill associated with the item. E.g. `CHARACTER_SKILL_SWORD`. - uint8_t uDamageDice = 0; // Damage dice. - uint8_t uDamageRoll = 0; - uint8_t uDamageMod = 0; - ItemRarity uMaterial = RARITY_COMMON; // Item rarity. + Pointi paperdollAnchorOffset; // Paperdoll offset for the item sprite when equipped, relative to the item type-specific anchor point. + ItemType type = ITEM_TYPE_NONE; // Item type. + CharacterSkillType skill = CHARACTER_SKILL_MISC; // Skill associated with the item. E.g. `CHARACTER_SKILL_SWORD`. + uint8_t damageDice = 0; // Damage dice. + uint8_t damageRoll = 0; + uint8_t damageMod = 0; + ItemRarity rarity = RARITY_COMMON; // Item rarity. ItemEnchantment specialEnchantment = ITEM_ENCHANTMENT_NULL; // Special enchantment, applied only to `RARITY_SPECIAL` items. - std::optional attributeEnchantment; // Attribute enchantment, applied only to `RARITY_SPECIAL` items. - int attributeEnchantmentStrength = 0; // Strength of the attribute enchantment above. + std::optional standardEnchantment; // Standard (attribute) enchantment, applied only to `RARITY_SPECIAL` items. + int standardEnchantmentStrength = 0; // Strength of the standard enchantment above. IndexedArray uChanceByTreasureLvl = {{}}; // Weights for seeing this item in random loot by treasure level. int identifyDifficulty = 0; // Value that the id item skill is checked against, 0 means always identified. }; diff --git a/src/Engine/Objects/SpriteObject.cpp b/src/Engine/Objects/SpriteObject.cpp index 64edc84f1d4..4f36496cfe4 100644 --- a/src/Engine/Objects/SpriteObject.cpp +++ b/src/Engine/Objects/SpriteObject.cpp @@ -658,8 +658,8 @@ bool SpriteObject::dropItemAt(SpriteId sprite, Vec3f pos, int speed, int count, } if (!(pSpellObject.uAttributes & SPRITE_IGNORE_RANGE)) { - for (ItemId i : pItemTable->pItems.indices()) { - if (pItemTable->pItems[i].uSpriteID == sprite) { + for (ItemId i : pItemTable->items.indices()) { + if (pItemTable->items[i].spriteId == sprite) { pSpellObject.containing_item.itemId = i; } } diff --git a/src/Engine/Party.cpp b/src/Engine/Party.cpp index b6e31205cd3..a5834b0c42e 100644 --- a/src/Engine/Party.cpp +++ b/src/Engine/Party.cpp @@ -1004,7 +1004,7 @@ void Party::dropHeldItem() { } SpriteObject sprite; - sprite.uType = pItemTable->pItems[pPickedItem.itemId].uSpriteID; + sprite.uType = pItemTable->items[pPickedItem.itemId].spriteId; sprite.uObjectDescID = pObjectList->ObjectIDByItemID(sprite.uType); sprite.spell_caster_pid = Pid(OBJECT_Character, 0); sprite.vPosition = pos + Vec3f(0, 0, eyeLevel); @@ -1036,11 +1036,11 @@ void Party::placeHeldItemInInventoryOrDrop() { } bool Party::addItemToParty(ItemGen *pItem, bool isSilent) { - if (!pItemTable->pItems[pItem->itemId].identifyDifficulty) { + if (!pItemTable->items[pItem->itemId].identifyDifficulty) { pItem->SetIdentified(); } - if (!pItemTable->pItems[pItem->itemId].iconName.empty()) { + if (!pItemTable->items[pItem->itemId].iconName.empty()) { int playerId = hasActiveCharacter() ? (pParty->_activeCharacter - 1) : 0; for (int i = 0; i < pCharacters.size(); i++, playerId++) { if (playerId >= pCharacters.size()) { diff --git a/src/Engine/Snapshots/CompositeSnapshots.cpp b/src/Engine/Snapshots/CompositeSnapshots.cpp index 6b3cff8eed2..c7161539908 100644 --- a/src/Engine/Snapshots/CompositeSnapshots.cpp +++ b/src/Engine/Snapshots/CompositeSnapshots.cpp @@ -220,7 +220,7 @@ void reconstruct(const IndoorDelta_MM7 &src, IndoorLocation *dst) { for (size_t i = 0; i < pSpriteObjects.size(); ++i) { if (pSpriteObjects[i].containing_item.itemId != ITEM_NULL && !(pSpriteObjects[i].uAttributes & SPRITE_MISSILE)) { - pSpriteObjects[i].uType = static_cast(pItemTable->pItems[pSpriteObjects[i].containing_item.itemId].uSpriteID); + pSpriteObjects[i].uType = static_cast(pItemTable->items[pSpriteObjects[i].containing_item.itemId].spriteId); pSpriteObjects[i].uObjectDescID = pObjectList->ObjectIDByItemID(pSpriteObjects[i].uType); } } diff --git a/src/Engine/Snapshots/EntitySnapshots.cpp b/src/Engine/Snapshots/EntitySnapshots.cpp index ca4bdc6b934..03aee4620af 100644 --- a/src/Engine/Snapshots/EntitySnapshots.cpp +++ b/src/Engine/Snapshots/EntitySnapshots.cpp @@ -448,13 +448,13 @@ void snapshot(const ItemGen &src, ItemGen_MM7 *dst) { dst->itemId = std::to_underlying(src.itemId); if (isPotion(src.itemId)) { - dst->attributeEnchantmentOrPotionPower = src.potionPower; - } else if (src.attributeEnchantment) { - dst->attributeEnchantmentOrPotionPower = std::to_underlying(*src.attributeEnchantment) + 1; + dst->standardEnchantmentOrPotionPower = src.potionPower; + } else if (src.standardEnchantment) { + dst->standardEnchantmentOrPotionPower = std::to_underlying(*src.standardEnchantment) + 1; } else { - dst->attributeEnchantmentOrPotionPower = 0; + dst->standardEnchantmentOrPotionPower = 0; } - dst->attributeEnchantmentStrength = src.attributeEnchantmentStrength; + dst->standardEnchantmentStrength = src.standardEnchantmentStrength; if (isGold(src.itemId)) { dst->specialEnchantmentOrGoldAmount = src.goldAmount; } else { @@ -472,16 +472,16 @@ void snapshot(const ItemGen &src, ItemGen_MM7 *dst) { void reconstruct(const ItemGen_MM7 &src, ItemGen *dst) { dst->itemId = static_cast(src.itemId); if (isPotion(dst->itemId)) { - dst->potionPower = src.attributeEnchantmentOrPotionPower; - dst->attributeEnchantment = {}; - } else if (src.attributeEnchantmentOrPotionPower) { + dst->potionPower = src.standardEnchantmentOrPotionPower; + dst->standardEnchantment = {}; + } else if (src.standardEnchantmentOrPotionPower) { dst->potionPower = 0; - dst->attributeEnchantment = static_cast(src.attributeEnchantmentOrPotionPower - 1); + dst->standardEnchantment = static_cast(src.standardEnchantmentOrPotionPower - 1); } else { dst->potionPower = 0; - dst->attributeEnchantment = {}; + dst->standardEnchantment = {}; } - dst->attributeEnchantmentStrength = src.attributeEnchantmentStrength; + dst->standardEnchantmentStrength = src.standardEnchantmentStrength; if (isGold(dst->itemId)) { dst->goldAmount = src.specialEnchantmentOrGoldAmount; dst->specialEnchantment = ITEM_ENCHANTMENT_NULL; diff --git a/src/Engine/Snapshots/EntitySnapshots.h b/src/Engine/Snapshots/EntitySnapshots.h index d58eba6cf42..3aae63191b6 100644 --- a/src/Engine/Snapshots/EntitySnapshots.h +++ b/src/Engine/Snapshots/EntitySnapshots.h @@ -229,17 +229,17 @@ void reconstruct(const NPCData_MM7 &src, NPCData *dst); struct ItemGen_MM7 { - /* 00 */ int32_t itemId; - /* 04 */ int32_t attributeEnchantmentOrPotionPower; // Potion power for potions, attribute index + 1 for attribute enchantments. - /* 08 */ int32_t attributeEnchantmentStrength; - /* 0C */ int32_t specialEnchantmentOrGoldAmount; // Gold amount for gold, otherwise special enchantment. - /* 10 */ int32_t numCharges; - /* 14 */ uint32_t flags; - /* 18 */ uint8_t equippedSlot; - /* 19 */ uint8_t maxCharges; - /* 1A */ uint8_t lichJarCharacterIndex; // Only for full lich jars. 1-based index of the character whose essence it stored in it. - /* 1B */ uint8_t placedInChest; // Unknown unused 8-bit field, was repurposed. - /* 1C */ int64_t enchantmentExpirationTime; + int32_t itemId; + int32_t standardEnchantmentOrPotionPower; // Potion power for potions, attribute index + 1 for standard enchantments. + int32_t standardEnchantmentStrength; + int32_t specialEnchantmentOrGoldAmount; // Gold amount for gold, otherwise special enchantment. + int32_t numCharges; + uint32_t flags; + uint8_t equippedSlot; + uint8_t maxCharges; + uint8_t lichJarCharacterIndex; // Only for full lich jars. 1-based index of the character whose essence it stored in it. + uint8_t placedInChest; // Unknown unused 8-bit field, was repurposed. + int64_t enchantmentExpirationTime; }; static_assert(sizeof(ItemGen_MM7) == 0x24); MM_DECLARE_MEMCOPY_SERIALIZABLE(ItemGen_MM7) diff --git a/src/Engine/Spells/CastSpellInfo.cpp b/src/Engine/Spells/CastSpellInfo.cpp index 95bf00dc521..f44b618fb7d 100644 --- a/src/Engine/Spells/CastSpellInfo.cpp +++ b/src/Engine/Spells/CastSpellInfo.cpp @@ -692,7 +692,7 @@ void CastSpellInfoHelpers::castSpell() { item->IsBroken() || pItemTable->IsMaterialNonCommon(item) || item->specialEnchantment != ITEM_ENCHANTMENT_NULL || - item->attributeEnchantment || + item->standardEnchantment || !item->isWeapon()) { AfterEnchClickEventId = UIMSG_Escape; AfterEnchClickEventSecondParam = 0; @@ -1401,7 +1401,7 @@ void CastSpellInfoHelpers::castSpell() { int rnd = grng->random(100); pPlayer = &pParty->pCharacters[pCastSpell->targetCharacterIndex]; ItemGen *spell_item_to_enchant = &pPlayer->pInventoryItemList[pCastSpell->targetInventoryIndex]; - ItemType this_equip_type = pItemTable->pItems[spell_item_to_enchant->itemId].uEquipType; + ItemType this_equip_type = pItemTable->items[spell_item_to_enchant->itemId].type; // refs // https://www.gog.com/forum/might_and_magic_series/a_little_enchant_item_testing_in_mm7 @@ -1415,8 +1415,8 @@ void CastSpellInfoHelpers::castSpell() { if ((spell_mastery == CHARACTER_SKILL_MASTERY_MASTER || spell_mastery == CHARACTER_SKILL_MASTERY_GRANDMASTER) && isRegular(spell_item_to_enchant->itemId) && spell_item_to_enchant->specialEnchantment == ITEM_ENCHANTMENT_NULL && - !spell_item_to_enchant->attributeEnchantment && - spell_item_to_enchant->attributeEnchantmentStrength == 0 && + !spell_item_to_enchant->standardEnchantment && + spell_item_to_enchant->standardEnchantmentStrength == 0 && !spell_item_to_enchant->IsBroken()) { // break items with low value if ((spell_item_to_enchant->GetValue() < 450 && !isWeapon(this_equip_type)) || // not weapons @@ -1441,9 +1441,9 @@ void CastSpellInfoHelpers::castSpell() { // finds how many possible enchaments and adds up to item apply values // if (pItemTable->pEnchantments_count > 0) { for (CharacterAttribute attr : allEnchantableAttributes()) { - const std::string &bonusStat = pItemTable->standardEnchantments[attr].pBonusStat; + const std::string &bonusStat = pItemTable->standardEnchantments[attr].attributeName; if (!bonusStat.empty()) { - int this_to_apply = pItemTable->standardEnchantments[attr].chancesByItemType[this_equip_type]; + int this_to_apply = pItemTable->standardEnchantments[attr].chanceByItemType[this_equip_type]; to_item_apply_sum += this_to_apply; if (this_to_apply) { ench_array[ench_found] = attr; @@ -1461,14 +1461,14 @@ void CastSpellInfoHelpers::castSpell() { // step through until we hit that ench for (step = 0; step < ench_found; step++) { - current_item_apply_sum += pItemTable->standardEnchantments[ench_array[step]].chancesByItemType[this_equip_type]; + current_item_apply_sum += pItemTable->standardEnchantments[ench_array[step]].chanceByItemType[this_equip_type]; if (current_item_apply_sum >= target_item_apply_rand) { break; } } // assign ench and power - spell_item_to_enchant->attributeEnchantment = ench_array[step]; + spell_item_to_enchant->standardEnchantment = ench_array[step]; int ench_power = 0; // master 3-8 - guess work needs checking @@ -1476,7 +1476,7 @@ void CastSpellInfoHelpers::castSpell() { // gm 6-12 - guess work needs checking if (spell_mastery== CHARACTER_SKILL_MASTERY_GRANDMASTER) ench_power = grng->random(7) + 6; - spell_item_to_enchant->attributeEnchantmentStrength = ench_power; + spell_item_to_enchant->standardEnchantmentStrength = ench_power; spell_item_to_enchant->flags |= ITEM_AURA_EFFECT_BLUE; ItemEnchantmentTimer = Duration::fromRealtimeSeconds(2); spell_failed = false; @@ -1486,22 +1486,20 @@ void CastSpellInfoHelpers::castSpell() { ItemEnchantment ench_array[100] = {}; // finds how many possible enchaments and adds up to item apply values - if (pItemTable->pSpecialEnchantments_count > 0) { - for (ItemEnchantment spec_ench_loop : pItemTable->pSpecialEnchantments.indices()) { - const std::string &bonusStatement = pItemTable->pSpecialEnchantments[spec_ench_loop].pBonusStatement; - if (!bonusStatement.empty()) { - if (pItemTable->pSpecialEnchantments[spec_ench_loop].iTreasureLevel == 3) { - continue; - } - if (spell_mastery == CHARACTER_SKILL_MASTERY_MASTER && (pItemTable->pSpecialEnchantments[spec_ench_loop].iTreasureLevel != 0)) { - continue; - } - int this_to_apply = pItemTable->pSpecialEnchantments[spec_ench_loop].to_item_apply[this_equip_type]; - to_item_apply_sum += this_to_apply; - if (this_to_apply) { - ench_array[ench_found] = spec_ench_loop; - ench_found++; - } + for (ItemEnchantment spec_ench_loop : pItemTable->specialEnchantments.indices()) { + const std::string &bonusStatement = pItemTable->specialEnchantments[spec_ench_loop].description; + if (!bonusStatement.empty()) { + if (pItemTable->specialEnchantments[spec_ench_loop].iTreasureLevel == 3) { + continue; + } + if (spell_mastery == CHARACTER_SKILL_MASTERY_MASTER && (pItemTable->specialEnchantments[spec_ench_loop].iTreasureLevel != 0)) { + continue; + } + int this_to_apply = pItemTable->specialEnchantments[spec_ench_loop].chanceByItemType[this_equip_type]; + to_item_apply_sum += this_to_apply; + if (this_to_apply) { + ench_array[ench_found] = spec_ench_loop; + ench_found++; } } } @@ -1514,7 +1512,7 @@ void CastSpellInfoHelpers::castSpell() { // step through until we hit that ench for (step = 0; step < ench_found; step++) { - current_item_apply_sum += pItemTable->pSpecialEnchantments[ench_array[step]].to_item_apply[this_equip_type]; + current_item_apply_sum += pItemTable->specialEnchantments[ench_array[step]].chanceByItemType[this_equip_type]; if (current_item_apply_sum >= target_item_apply_rand) { break; } @@ -1980,7 +1978,7 @@ void CastSpellInfoHelpers::castSpell() { } else { for (const ItemGen &actorItem : pActors[monster_id].items) { if (actorItem.itemId != ITEM_NULL && - pItemTable->pItems[actorItem.itemId].uEquipType != ITEM_TYPE_GOLD) { + pItemTable->items[actorItem.itemId].type != ITEM_TYPE_GOLD) { item = actorItem; } } @@ -2181,7 +2179,7 @@ void CastSpellInfoHelpers::castSpell() { if (pSpriteObjects[obj_id].containing_item.isGold()) { pParty->partyFindsGold(pSpriteObjects[obj_id].containing_item.goldAmount, GOLD_RECEIVE_SHARE); } else { - engine->_statusBar->setEvent(LSTR_FMT_YOU_FOUND_ITEM, pItemTable->pItems[pSpriteObjects[obj_id].containing_item.itemId].pUnidentifiedName); + engine->_statusBar->setEvent(LSTR_FMT_YOU_FOUND_ITEM, pItemTable->items[pSpriteObjects[obj_id].containing_item.itemId].unidentifiedName); if (!pParty->addItemToParty(&pSpriteObjects[obj_id].containing_item)) { pParty->setHoldingItem(&pSpriteObjects[obj_id].containing_item); } diff --git a/src/Engine/Tables/ItemTable.cpp b/src/Engine/Tables/ItemTable.cpp index 6baa6ee6050..2e1b83f443e 100644 --- a/src/Engine/Tables/ItemTable.cpp +++ b/src/Engine/Tables/ItemTable.cpp @@ -86,43 +86,42 @@ void ItemTable::Initialize(GameResourceManager *resourceManager) { strtok(txtRaw.data(), "\r"); strtokSkipLines(3); // Standard Bonuses by Group - chanceByItemTypeSums.fill(0); + standardEnchantmentChanceSumByItemType.fill(0); for (CharacterAttribute i : allEnchantableAttributes()) { lineContent = strtok(NULL, "\r") + 1; auto tokens = tokenize(lineContent, '\t'); - standardEnchantments[i].pBonusStat = removeQuotes(tokens[0]); - standardEnchantments[i].pOfName = removeQuotes(tokens[1]); + standardEnchantments[i].attributeName = removeQuotes(tokens[0]); + standardEnchantments[i].itemSuffix = removeQuotes(tokens[1]); int k = 2; - for (ItemType equipType : standardEnchantments[i].chancesByItemType.indices()) { - standardEnchantments[i].chancesByItemType[equipType] = atoi(tokens[k++]); - chanceByItemTypeSums[equipType] += standardEnchantments[i].chancesByItemType[equipType]; + for (ItemType equipType : standardEnchantments[i].chanceByItemType.indices()) { + standardEnchantments[i].chanceByItemType[equipType] = atoi(tokens[k++]); + standardEnchantmentChanceSumByItemType[equipType] += standardEnchantments[i].chanceByItemType[equipType]; } } // Bonus range for Standard by Level strtokSkipLines(5); - for (ItemTreasureLevel i : bonusRanges.indices()) { // counted from 1 + for (ItemTreasureLevel i : standardEnchantmentRangeByTreasureLevel.indices()) { // counted from 1 lineContent = strtok(NULL, "\r") + 1; auto tokens = tokenize(lineContent, '\t'); assert(tokens.size() == 4 && "Invalid number of tokens"); - bonusRanges[i].minR = atoi(tokens[2]); - bonusRanges[i].maxR = atoi(tokens[3]); + standardEnchantmentRangeByTreasureLevel[i] = Segment(atoi(tokens[2]), atoi(tokens[3])); } txtRaw = resourceManager->getEventsFile("spcitems.txt").string_view(); strtok(txtRaw.data(), "\r"); strtokSkipLines(3); - for (ItemEnchantment i : pSpecialEnchantments.indices()) { + for (ItemEnchantment i : specialEnchantments.indices()) { lineContent = strtok(NULL, "\r") + 1; auto tokens = tokenize(lineContent, '\t'); assert(tokens.size() >= 17 && "Invalid number of tokens"); - pSpecialEnchantments[i].pBonusStatement = removeQuotes(tokens[0]); - pSpecialEnchantments[i].pNameAdd = removeQuotes(tokens[1]); + specialEnchantments[i].description = removeQuotes(tokens[0]); + specialEnchantments[i].itemSuffixOrPrefix = removeQuotes(tokens[1]); int k = 2; - for (ItemType j : pSpecialEnchantments[i].to_item_apply.indices()) - pSpecialEnchantments[i].to_item_apply[j] = atoi(tokens[k++]); + for (ItemType j : specialEnchantments[i].chanceByItemType.indices()) + specialEnchantments[i].chanceByItemType[j] = atoi(tokens[k++]); int res = atoi(tokens[14]); int mask = 0; @@ -133,12 +132,10 @@ void ItemTable::Initialize(GameResourceManager *resourceManager) { res = atoi(tokens[14]); mask = 4; // bit encode for when we need to multiply value } - pSpecialEnchantments[i].iValue = res; - pSpecialEnchantments[i].iTreasureLevel = (tolower(tokens[15][0]) - 'a') | mask; + specialEnchantments[i].additionalValue = res; + specialEnchantments[i].iTreasureLevel = (tolower(tokens[15][0]) - 'a') | mask; } - pSpecialEnchantments_count = 72; - txtRaw = resourceManager->getEventsFile("items.txt").string_view(); strtok(txtRaw.data(), "\r"); strtokSkipLines(1); @@ -147,59 +144,59 @@ void ItemTable::Initialize(GameResourceManager *resourceManager) { auto tokens = tokenize(lineContent, '\t'); ItemId item_counter = ItemId(atoi(tokens[0])); - pItems[item_counter].iconName = removeQuotes(tokens[1]); - pItems[item_counter].name = removeQuotes(tokens[2]); - pItems[item_counter].uValue = atoi(tokens[3]); - pItems[item_counter].uEquipType = valueOr(equipStatMap, tokens[4], ITEM_TYPE_NONE); - pItems[item_counter].uSkillType = valueOr(equipSkillMap, tokens[5], CHARACTER_SKILL_MISC); + items[item_counter].iconName = removeQuotes(tokens[1]); + items[item_counter].name = removeQuotes(tokens[2]); + items[item_counter].baseValue = atoi(tokens[3]); + items[item_counter].type = valueOr(equipStatMap, tokens[4], ITEM_TYPE_NONE); + items[item_counter].skill = valueOr(equipSkillMap, tokens[5], CHARACTER_SKILL_MISC); auto diceRollTokens = tokenize(tokens[6], 'd'); if (diceRollTokens.size() == 2) { - pItems[item_counter].uDamageDice = atoi(diceRollTokens[0]); - pItems[item_counter].uDamageRoll = atoi(diceRollTokens[1]); + items[item_counter].damageDice = atoi(diceRollTokens[0]); + items[item_counter].damageRoll = atoi(diceRollTokens[1]); } else if (tolower(diceRollTokens[0][0]) != 's') { - pItems[item_counter].uDamageDice = atoi(diceRollTokens[0]); - pItems[item_counter].uDamageRoll = 1; + items[item_counter].damageDice = atoi(diceRollTokens[0]); + items[item_counter].damageRoll = 1; } else { - pItems[item_counter].uDamageDice = 0; - pItems[item_counter].uDamageRoll = 0; + items[item_counter].damageDice = 0; + items[item_counter].damageRoll = 0; } - pItems[item_counter].uDamageMod = atoi(tokens[7]); - pItems[item_counter].uMaterial = valueOr(materialMap, tokens[8], RARITY_COMMON); - pItems[item_counter].identifyDifficulty = atoi(tokens[9]); - pItems[item_counter].pUnidentifiedName = removeQuotes(tokens[10]); - pItems[item_counter].uSpriteID = static_cast(atoi(tokens[11])); - - pItems[item_counter].specialEnchantment = ITEM_ENCHANTMENT_NULL; - pItems[item_counter].attributeEnchantment = {}; - if (pItems[item_counter].uMaterial == RARITY_SPECIAL) { + items[item_counter].damageMod = atoi(tokens[7]); + items[item_counter].rarity = valueOr(materialMap, tokens[8], RARITY_COMMON); + items[item_counter].identifyDifficulty = atoi(tokens[9]); + items[item_counter].unidentifiedName = removeQuotes(tokens[10]); + items[item_counter].spriteId = static_cast(atoi(tokens[11])); + + items[item_counter].specialEnchantment = ITEM_ENCHANTMENT_NULL; + items[item_counter].standardEnchantment = {}; + if (items[item_counter].rarity == RARITY_SPECIAL) { for (CharacterAttribute ii : allEnchantableAttributes()) { - if (ascii::noCaseEquals(tokens[12], standardEnchantments[ii].pOfName)) { // TODO(captainurist): #unicode this is not ascii - pItems[item_counter].attributeEnchantment = ii; + if (ascii::noCaseEquals(tokens[12], standardEnchantments[ii].itemSuffix)) { // TODO(captainurist): #unicode this is not ascii + items[item_counter].standardEnchantment = ii; break; } } - if (!pItems[item_counter].attributeEnchantment) { - for (ItemEnchantment ii : pSpecialEnchantments.indices()) { - if (ascii::noCaseEquals(tokens[12], pSpecialEnchantments[ii].pNameAdd)) { // TODO(captainurist): #unicode this is not ascii - pItems[item_counter].specialEnchantment = ii; + if (!items[item_counter].standardEnchantment) { + for (ItemEnchantment ii : specialEnchantments.indices()) { + if (ascii::noCaseEquals(tokens[12], specialEnchantments[ii].itemSuffixOrPrefix)) { // TODO(captainurist): #unicode this is not ascii + items[item_counter].specialEnchantment = ii; } } } } - if ((pItems[item_counter].uMaterial == RARITY_SPECIAL) && - (pItems[item_counter].attributeEnchantment)) { + if ((items[item_counter].rarity == RARITY_SPECIAL) && + (items[item_counter].standardEnchantment)) { char b_s = atoi(tokens[13]); if (b_s) - pItems[item_counter].attributeEnchantmentStrength = b_s; + items[item_counter].standardEnchantmentStrength = b_s; else - pItems[item_counter].attributeEnchantmentStrength = 1; + items[item_counter].standardEnchantmentStrength = 1; } else { - pItems[item_counter].attributeEnchantmentStrength = 0; + items[item_counter].standardEnchantmentStrength = 0; } - pItems[item_counter].uEquipX = atoi(tokens[14]); - pItems[item_counter].uEquipY = atoi(tokens[15]); - pItems[item_counter].pDescription = removeQuotes(tokens[16]); + items[item_counter].paperdollAnchorOffset.x = atoi(tokens[14]); + items[item_counter].paperdollAnchorOffset.y = atoi(tokens[15]); + items[item_counter].description = removeQuotes(tokens[16]); } txtRaw = resourceManager->getEventsFile("rnditems.txt").string_view(); @@ -211,19 +208,19 @@ void ItemTable::Initialize(GameResourceManager *resourceManager) { assert(tokens.size() > 7 && "Invalid number of tokens"); ItemId item_counter = ItemId(atoi(tokens[0])); - pItems[item_counter].uChanceByTreasureLvl[ITEM_TREASURE_LEVEL_1] = atoi(tokens[2]); - pItems[item_counter].uChanceByTreasureLvl[ITEM_TREASURE_LEVEL_2] = atoi(tokens[3]); - pItems[item_counter].uChanceByTreasureLvl[ITEM_TREASURE_LEVEL_3] = atoi(tokens[4]); - pItems[item_counter].uChanceByTreasureLvl[ITEM_TREASURE_LEVEL_4] = atoi(tokens[5]); - pItems[item_counter].uChanceByTreasureLvl[ITEM_TREASURE_LEVEL_5] = atoi(tokens[6]); - pItems[item_counter].uChanceByTreasureLvl[ITEM_TREASURE_LEVEL_6] = atoi(tokens[7]); + items[item_counter].uChanceByTreasureLvl[ITEM_TREASURE_LEVEL_1] = atoi(tokens[2]); + items[item_counter].uChanceByTreasureLvl[ITEM_TREASURE_LEVEL_2] = atoi(tokens[3]); + items[item_counter].uChanceByTreasureLvl[ITEM_TREASURE_LEVEL_3] = atoi(tokens[4]); + items[item_counter].uChanceByTreasureLvl[ITEM_TREASURE_LEVEL_4] = atoi(tokens[5]); + items[item_counter].uChanceByTreasureLvl[ITEM_TREASURE_LEVEL_5] = atoi(tokens[6]); + items[item_counter].uChanceByTreasureLvl[ITEM_TREASURE_LEVEL_6] = atoi(tokens[7]); } // ChanceByTreasureLvl Summ - to calculate chance - memset(&chanceByTreasureLevelSums, 0, 24); - for (ItemTreasureLevel i : chanceByTreasureLevelSums.indices()) - for (ItemId j : pItems.indices()) - chanceByTreasureLevelSums[i] += pItems[j].uChanceByTreasureLvl[i]; + itemChanceSumByTreasureLevel.fill(0); + for (ItemTreasureLevel i : itemChanceSumByTreasureLevel.indices()) + for (ItemId j : items.indices()) + itemChanceSumByTreasureLevel[i] += items[j].uChanceByTreasureLvl[i]; strtokSkipLines(5); for (int i = 0; i < 3; ++i) { @@ -232,28 +229,28 @@ void ItemTable::Initialize(GameResourceManager *resourceManager) { assert(tokens.size() > 7 && "Invalid number of tokens"); switch (i) { case 0: - uBonusChanceStandart[ITEM_TREASURE_LEVEL_1] = atoi(tokens[2]); - uBonusChanceStandart[ITEM_TREASURE_LEVEL_2] = atoi(tokens[3]); - uBonusChanceStandart[ITEM_TREASURE_LEVEL_3] = atoi(tokens[4]); - uBonusChanceStandart[ITEM_TREASURE_LEVEL_4] = atoi(tokens[5]); - uBonusChanceStandart[ITEM_TREASURE_LEVEL_5] = atoi(tokens[6]); - uBonusChanceStandart[ITEM_TREASURE_LEVEL_6] = atoi(tokens[7]); + standardEnchantmentChanceForEquipment[ITEM_TREASURE_LEVEL_1] = atoi(tokens[2]); + standardEnchantmentChanceForEquipment[ITEM_TREASURE_LEVEL_2] = atoi(tokens[3]); + standardEnchantmentChanceForEquipment[ITEM_TREASURE_LEVEL_3] = atoi(tokens[4]); + standardEnchantmentChanceForEquipment[ITEM_TREASURE_LEVEL_4] = atoi(tokens[5]); + standardEnchantmentChanceForEquipment[ITEM_TREASURE_LEVEL_5] = atoi(tokens[6]); + standardEnchantmentChanceForEquipment[ITEM_TREASURE_LEVEL_6] = atoi(tokens[7]); break; case 1: - uBonusChanceSpecial[ITEM_TREASURE_LEVEL_1] = atoi(tokens[2]); - uBonusChanceSpecial[ITEM_TREASURE_LEVEL_2] = atoi(tokens[3]); - uBonusChanceSpecial[ITEM_TREASURE_LEVEL_3] = atoi(tokens[4]); - uBonusChanceSpecial[ITEM_TREASURE_LEVEL_4] = atoi(tokens[5]); - uBonusChanceSpecial[ITEM_TREASURE_LEVEL_5] = atoi(tokens[6]); - uBonusChanceSpecial[ITEM_TREASURE_LEVEL_6] = atoi(tokens[7]); + specialEnchantmentChanceForEquipment[ITEM_TREASURE_LEVEL_1] = atoi(tokens[2]); + specialEnchantmentChanceForEquipment[ITEM_TREASURE_LEVEL_2] = atoi(tokens[3]); + specialEnchantmentChanceForEquipment[ITEM_TREASURE_LEVEL_3] = atoi(tokens[4]); + specialEnchantmentChanceForEquipment[ITEM_TREASURE_LEVEL_4] = atoi(tokens[5]); + specialEnchantmentChanceForEquipment[ITEM_TREASURE_LEVEL_5] = atoi(tokens[6]); + specialEnchantmentChanceForEquipment[ITEM_TREASURE_LEVEL_6] = atoi(tokens[7]); break; case 2: - uBonusChanceWpSpecial[ITEM_TREASURE_LEVEL_1] = atoi(tokens[2]); - uBonusChanceWpSpecial[ITEM_TREASURE_LEVEL_2] = atoi(tokens[3]); - uBonusChanceWpSpecial[ITEM_TREASURE_LEVEL_3] = atoi(tokens[4]); - uBonusChanceWpSpecial[ITEM_TREASURE_LEVEL_4] = atoi(tokens[5]); - uBonusChanceWpSpecial[ITEM_TREASURE_LEVEL_5] = atoi(tokens[6]); - uBonusChanceWpSpecial[ITEM_TREASURE_LEVEL_6] = atoi(tokens[7]); + specialEnchantmentChanceForWeapons[ITEM_TREASURE_LEVEL_1] = atoi(tokens[2]); + specialEnchantmentChanceForWeapons[ITEM_TREASURE_LEVEL_2] = atoi(tokens[3]); + specialEnchantmentChanceForWeapons[ITEM_TREASURE_LEVEL_3] = atoi(tokens[4]); + specialEnchantmentChanceForWeapons[ITEM_TREASURE_LEVEL_4] = atoi(tokens[5]); + specialEnchantmentChanceForWeapons[ITEM_TREASURE_LEVEL_5] = atoi(tokens[6]); + specialEnchantmentChanceForWeapons[ITEM_TREASURE_LEVEL_6] = atoi(tokens[7]); break; } } @@ -265,23 +262,23 @@ void ItemTable::Initialize(GameResourceManager *resourceManager) { //----- (00456D17) -------------------------------------------------------- void ItemTable::SetSpecialBonus(ItemGen *pItem) { - if (pItems[pItem->itemId].uMaterial == RARITY_SPECIAL) { - pItem->attributeEnchantment = pItems[pItem->itemId].attributeEnchantment; - pItem->specialEnchantment = pItems[pItem->itemId].specialEnchantment; - pItem->attributeEnchantmentStrength = pItems[pItem->itemId].attributeEnchantmentStrength; + if (items[pItem->itemId].rarity == RARITY_SPECIAL) { + pItem->standardEnchantment = items[pItem->itemId].standardEnchantment; + pItem->specialEnchantment = items[pItem->itemId].specialEnchantment; + pItem->standardEnchantmentStrength = items[pItem->itemId].standardEnchantmentStrength; } } //----- (00456D43) -------------------------------------------------------- bool ItemTable::IsMaterialSpecial(const ItemGen *pItem) { - return this->pItems[pItem->itemId].uMaterial == RARITY_SPECIAL; + return this->items[pItem->itemId].rarity == RARITY_SPECIAL; } //----- (00456D5E) -------------------------------------------------------- bool ItemTable::IsMaterialNonCommon(const ItemGen *pItem) { - return pItems[pItem->itemId].uMaterial == RARITY_SPECIAL || - pItems[pItem->itemId].uMaterial == RARITY_RELIC || - pItems[pItem->itemId].uMaterial == RARITY_ARTIFACT; + return items[pItem->itemId].rarity == RARITY_SPECIAL || + items[pItem->itemId].rarity == RARITY_RELIC || + items[pItem->itemId].rarity == RARITY_ARTIFACT; } //----- (00453B3C) -------------------------------------------------------- @@ -303,12 +300,12 @@ void ItemTable::LoadPotions(const Blob &potions) { return; } - for (ItemId row : Segment(ITEM_FIRST_REAL_POTION, ITEM_LAST_REAL_POTION)) { + for (ItemId row : Segment(ITEM_FIRST_REAL_POTION, ITEM_LAST_REAL_POTION)) { if (tokens.size() < 50) { logger->error("Error Parsing Potion Table at Row: {} Column: {}", std::to_underlying(row) - std::to_underlying(ITEM_FIRST_REAL_POTION), tokens.size()); return; } - for (ItemId column : Segment(ITEM_FIRST_REAL_POTION, ITEM_LAST_REAL_POTION)) { + for (ItemId column : Segment(ITEM_FIRST_REAL_POTION, ITEM_LAST_REAL_POTION)) { int flatPotionId = std::to_underlying(column) - std::to_underlying(ITEM_FIRST_REAL_POTION); char *currValue = tokens[flatPotionId + 7]; potion_value = atoi(currValue); @@ -347,12 +344,12 @@ void ItemTable::LoadPotionNotes(const Blob &potionNotes) { return; } - for (ItemId row : Segment(ITEM_FIRST_REAL_POTION, ITEM_LAST_REAL_POTION)) { + for (ItemId row : Segment(ITEM_FIRST_REAL_POTION, ITEM_LAST_REAL_POTION)) { if (tokens.size() < 50) { logger->error("Error Parsing Potion Table at Row: {} Column: {}", std::to_underlying(row), tokens.size()); return; } - for (ItemId column : Segment(ITEM_FIRST_REAL_POTION, ITEM_LAST_REAL_POTION)) { + for (ItemId column : Segment(ITEM_FIRST_REAL_POTION, ITEM_LAST_REAL_POTION)) { int flatPotionId = std::to_underlying(column) - std::to_underlying(ITEM_FIRST_REAL_POTION); char *currValue = tokens[flatPotionId + 7]; this->potionNotes[row][column] = atoi(currValue); @@ -472,9 +469,9 @@ void ItemTable::generateItem(ItemTreasureLevel treasureLevel, RandomItemType uTr if (requestedSkill == CHARACTER_SKILL_INVALID) { // no skill for this item needed for (ItemId itemId : allSpawnableItems()) { - if (pItems[itemId].uEquipType == requestedEquip) { - if (pItems[itemId].uChanceByTreasureLvl[treasureLevel]) { - weightSum += pItems[itemId].uChanceByTreasureLvl[treasureLevel]; + if (items[itemId].type == requestedEquip) { + if (items[itemId].uChanceByTreasureLvl[treasureLevel]) { + weightSum += items[itemId].uChanceByTreasureLvl[treasureLevel]; possibleItems.push_back(itemId); cumulativeWeights.push_back(weightSum); } @@ -482,9 +479,9 @@ void ItemTable::generateItem(ItemTreasureLevel treasureLevel, RandomItemType uTr } } else { // have needed skill for (ItemId itemId : allSpawnableItems()) { - if (pItems[itemId].uSkillType == requestedSkill) { - if (pItems[itemId].uChanceByTreasureLvl[treasureLevel]) { - weightSum += pItems[itemId].uChanceByTreasureLvl[treasureLevel]; + if (items[itemId].skill == requestedSkill) { + if (items[itemId].uChanceByTreasureLvl[treasureLevel]) { + weightSum += items[itemId].uChanceByTreasureLvl[treasureLevel]; possibleItems.push_back(itemId); cumulativeWeights.push_back(weightSum); } @@ -520,9 +517,9 @@ void ItemTable::generateItem(ItemTreasureLevel treasureLevel, RandomItemType uTr } // Otherwise try to spawn any random item - int randomWeight = grng->random(this->chanceByTreasureLevelSums[treasureLevel]) + 1; + int randomWeight = grng->random(this->itemChanceSumByTreasureLevel[treasureLevel]) + 1; for (ItemId itemId : allSpawnableItems()) { - weightSum += pItems[itemId].uChanceByTreasureLvl[treasureLevel]; + weightSum += items[itemId].uChanceByTreasureLvl[treasureLevel]; if (weightSum >= randomWeight) { outItem->itemId = itemId; break; @@ -535,21 +532,21 @@ void ItemTable::generateItem(ItemTreasureLevel treasureLevel, RandomItemType uTr if (outItem->itemId == ITEM_SPELLBOOK_DIVINE_INTERVENTION && !pParty->_questBits[QBIT_DIVINE_INTERVENTION_RETRIEVED]) outItem->itemId = ITEM_SPELLBOOK_SUNRAY; - if (pItemTable->pItems[outItem->itemId].identifyDifficulty) + if (pItemTable->items[outItem->itemId].identifyDifficulty) outItem->flags = 0; else outItem->flags = ITEM_IDENTIFIED; if (!outItem->isPotion()) { outItem->specialEnchantment = ITEM_ENCHANTMENT_NULL; - outItem->attributeEnchantment = {}; + outItem->standardEnchantment = {}; } // try get special enchantment switch (outItem->GetItemEquipType()) { case ITEM_TYPE_SINGLE_HANDED: case ITEM_TYPE_TWO_HANDED: case ITEM_TYPE_BOW: - if (!uBonusChanceWpSpecial[treasureLevel] || grng->random(100) >= uBonusChanceWpSpecial[treasureLevel]) + if (!specialEnchantmentChanceForWeapons[treasureLevel] || grng->random(100) >= specialEnchantmentChanceForWeapons[treasureLevel]) return; break; case ITEM_TYPE_ARMOUR: @@ -561,34 +558,34 @@ void ItemTable::generateItem(ItemTreasureLevel treasureLevel, RandomItemType uTr case ITEM_TYPE_BOOTS: case ITEM_TYPE_RING: case ITEM_TYPE_AMULET: { - if (!uBonusChanceStandart[treasureLevel]) + if (!standardEnchantmentChanceForEquipment[treasureLevel]) return; int bonusChanceRoll = grng->random(100); - if (bonusChanceRoll < uBonusChanceStandart[treasureLevel]) { - int enchantmentChanceSumRoll = grng->random(chanceByItemTypeSums[outItem->GetItemEquipType()]) + 1; + if (bonusChanceRoll < standardEnchantmentChanceForEquipment[treasureLevel]) { + int enchantmentChanceSumRoll = grng->random(standardEnchantmentChanceSumByItemType[outItem->GetItemEquipType()]) + 1; int currentEnchantmentChancesSum = 0; for (CharacterAttribute attr : allEnchantableAttributes()) { if (currentEnchantmentChancesSum >= enchantmentChanceSumRoll) break; - currentEnchantmentChancesSum += standardEnchantments[attr].chancesByItemType[outItem->GetItemEquipType()]; - outItem->attributeEnchantment = attr; + currentEnchantmentChancesSum += standardEnchantments[attr].chanceByItemType[outItem->GetItemEquipType()]; + outItem->standardEnchantment = attr; } - assert(outItem->attributeEnchantment); + assert(outItem->standardEnchantment); - outItem->attributeEnchantmentStrength = bonusRanges[treasureLevel].minR + grng->random(bonusRanges[treasureLevel].maxR - bonusRanges[treasureLevel].minR + 1); - CharacterAttribute standardEnchantmentAttributeSkill = *outItem->attributeEnchantment; + outItem->standardEnchantmentStrength = grng->randomSample(standardEnchantmentRangeByTreasureLevel[treasureLevel]); + CharacterAttribute standardEnchantmentAttributeSkill = *outItem->standardEnchantment; if (standardEnchantmentAttributeSkill == ATTRIBUTE_SKILL_ARMSMASTER || standardEnchantmentAttributeSkill == ATTRIBUTE_SKILL_DODGE || standardEnchantmentAttributeSkill == ATTRIBUTE_SKILL_UNARMED) { - outItem->attributeEnchantmentStrength /= 2; + outItem->standardEnchantmentStrength /= 2; } // if enchantment generated, it needs to actually have an effect - if (outItem->attributeEnchantmentStrength <= 0) { - outItem->attributeEnchantmentStrength = 1; + if (outItem->standardEnchantmentStrength <= 0) { + outItem->standardEnchantmentStrength = 1; } return; - } else if (bonusChanceRoll >= uBonusChanceStandart[treasureLevel] + uBonusChanceSpecial[treasureLevel]) { + } else if (bonusChanceRoll >= standardEnchantmentChanceForEquipment[treasureLevel] + specialEnchantmentChanceForEquipment[treasureLevel]) { return; } } @@ -603,8 +600,8 @@ void ItemTable::generateItem(ItemTreasureLevel treasureLevel, RandomItemType uTr cumulativeWeights.clear(); weightSum = 0; - for (ItemEnchantment ench : pSpecialEnchantments.indices()) { - int tr_lv = (pSpecialEnchantments[ench].iTreasureLevel) & 3; + for (ItemEnchantment ench : specialEnchantments.indices()) { + int tr_lv = (specialEnchantments[ench].iTreasureLevel) & 3; // tr_lv 0 = treasure level 3/4 // tr_lv 1 = treasure level 3/4/5 @@ -615,7 +612,7 @@ void ItemTable::generateItem(ItemTreasureLevel treasureLevel, RandomItemType uTr (treasureLevel == ITEM_TREASURE_LEVEL_4) && (tr_lv == 2 || tr_lv == 1 || tr_lv == 0) || (treasureLevel == ITEM_TREASURE_LEVEL_5) && (tr_lv == 3 || tr_lv == 2 || tr_lv == 1) || (treasureLevel == ITEM_TREASURE_LEVEL_6) && (tr_lv == 3)) { - int spc = pSpecialEnchantments[ench].to_item_apply[outItem->GetItemEquipType()]; + int spc = specialEnchantments[ench].chanceByItemType[outItem->GetItemEquipType()]; if (spc) { weightSum += spc; possibleEnchantments.push_back(ench); diff --git a/src/Engine/Tables/ItemTable.h b/src/Engine/Tables/ItemTable.h index 5ea526485f2..6c117d9e272 100644 --- a/src/Engine/Tables/ItemTable.h +++ b/src/Engine/Tables/ItemTable.h @@ -2,19 +2,16 @@ #include -#include "Engine/Objects/ItemEnchantment.h" +#include "Engine/Data/SpecialEnchantmentData.h" +#include "Engine/Data/StandardEnchantmentData.h" #include "Engine/Objects/Items.h" #include "Utility/IndexedArray.h" +#include "Utility/Segment.h" class GameResourceManager; class Blob; -struct BonusRange { - unsigned int minR; - unsigned int maxR; -}; // TODO(captainurist): Segment? - struct ItemTable { void Initialize(GameResourceManager *resourceManager); void LoadPotions(const Blob &potions); @@ -28,29 +25,51 @@ struct ItemTable { bool IsMaterialSpecial(const ItemGen *pItem); bool IsMaterialNonCommon(const ItemGen *pItem); - IndexedArray pItems; // 4-9604h - IndexedArray standardEnchantments; // 9604h - IndexedArray pSpecialEnchantments; // 97E4h -9FC4h + /** Item data for all items in the game. */ + IndexedArray items; + + /** Data for standard item enchantments. */ + IndexedArray standardEnchantments; + + /** Data for special item enchantments. */ + IndexedArray specialEnchantments; + char field_9FC4[5000]; char field_B348[5000]; char field_C6D0[5000]; char field_DA58[5000]; char field_EDE0[384]; - // 77B2h*2=EF64h -102ECh + + /** Mapping `potion1 x potion2 => potion3`. Alchemy recipes, basically. + * + * `ITEM_NULL` means "can't mix even if you try", and is set for cases when `potion1 == potion2`. Item ids in + * `[1, 4]` denote damage level from mixing. */ IndexedArray, ITEM_FIRST_REAL_POTION, ITEM_LAST_REAL_POTION> potionCombination; - // 8176h*2=102ECh -11674 + + /** Index of autonote bit (`Party::_autonoteBits`) for the potion recipe. */ IndexedArray, ITEM_FIRST_REAL_POTION, ITEM_LAST_REAL_POTION> potionNotes; - IndexedArray chanceByTreasureLevelSums; // 11684 - IndexedArray uBonusChanceStandart; // 1169c - IndexedArray uBonusChanceSpecial; // 116B4 - IndexedArray uBonusChanceWpSpecial; // 116cc -116e4 - IndexedArray chanceByItemTypeSums; // 116E4h -11708h - IndexedArray bonusRanges; // 45C2h*4 =11708h - unsigned int pSpecialEnchantments_count; // 11798h - char field_1179C; - char field_1179D; - char field_1179E; - char field_1179F; + + /** Items have a per-treasure level chances to be randomly generated. This array is a per-treasure level sum of + * these chances for all items. Effectively used for weighted random sampling in item generation. */ + IndexedArray itemChanceSumByTreasureLevel; + + /** Chance to get a standard enchantment on a non-weapon item, by treasure level. Number in `[0, 100]`. */ + IndexedArray standardEnchantmentChanceForEquipment; + + /** Chance to get an attribute enchantment OR a special enchantment on a non-weapon item, by treasure level. + * Number in `[0, 100]`. This basically a cumulative chance to get some enchantment. */ + IndexedArray specialEnchantmentChanceForEquipment; + + /** Chance to get a special enchantment on a weapon, by treasure level. Number in `[0, 100]`. */ + IndexedArray specialEnchantmentChanceForWeapons; + + /** Standard enchantments have a per-item type chance to be randomly generated. This array is a per-item type + * sum of these chances for all standard enchantments. Effectively used for weighted random sampling in standard + * enchantment generation. */ + IndexedArray standardEnchantmentChanceSumByItemType; + + /** Ranges of standard enchantment strength by item treasure level. */ + IndexedArray, ITEM_TREASURE_LEVEL_FIRST_RANDOM, ITEM_TREASURE_LEVEL_LAST_RANDOM> standardEnchantmentRangeByTreasureLevel; }; extern ItemTable *pItemTable; diff --git a/src/GUI/UI/Houses/MagicGuild.cpp b/src/GUI/UI/Houses/MagicGuild.cpp index 9332b848722..6de4b5f6a7a 100644 --- a/src/GUI/UI/Houses/MagicGuild.cpp +++ b/src/GUI/UI/Houses/MagicGuild.cpp @@ -374,6 +374,6 @@ void GUIWindow_MagicGuild::generateSpellBooksForGuild() { itemSpellbook->itemId = pItemNum; itemSpellbook->SetIdentified(); - shop_ui_items_in_store[i] = assets->getImage_ColorKey(pItemTable->pItems[pItemNum].iconName); + shop_ui_items_in_store[i] = assets->getImage_ColorKey(pItemTable->items[pItemNum].iconName); } } diff --git a/src/GUI/UI/NPCTopics.cpp b/src/GUI/UI/NPCTopics.cpp index ddc04b9fa7f..6d8d69fc80f 100644 --- a/src/GUI/UI/NPCTopics.cpp +++ b/src/GUI/UI/NPCTopics.cpp @@ -403,7 +403,7 @@ void oracleDialogue() { // TODO(captainurist): what if fmt throws? current_npc_text = fmt::sprintf(pNPCTopics[666].pText, // "Here's %s that you lost. Be careful" // NOLINT: this is not ::sprintf. fmt::format("{::}{}\f00000", colorTable.Sunflower.tag(), - pItemTable->pItems[item_id].pUnidentifiedName)); + pItemTable->items[item_id].unidentifiedName)); } // missing item is lich jar and we need to bind soul vessel to lich class character diff --git a/src/GUI/UI/UICharacter.cpp b/src/GUI/UI/UICharacter.cpp index f074b5df679..3dfb35cba3d 100644 --- a/src/GUI/UI/UICharacter.cpp +++ b/src/GUI/UI/UICharacter.cpp @@ -1038,8 +1038,8 @@ void CharacterUI_DrawPaperdoll(Character *player) { // main hand's item item = itemMainHand; if (item) { - item_X = pPaperdoll_BodyX + paperdoll_Weapon[pBodyComplection][1][0] - pItemTable->pItems[item->itemId].uEquipX; - item_Y = pPaperdoll_BodyY + paperdoll_Weapon[pBodyComplection][1][1] - pItemTable->pItems[item->itemId].uEquipY; + item_X = pPaperdoll_BodyX + paperdoll_Weapon[pBodyComplection][1][0] - pItemTable->items[item->itemId].paperdollAnchorOffset.x; + item_Y = pPaperdoll_BodyY + paperdoll_Weapon[pBodyComplection][1][1] - pItemTable->items[item->itemId].paperdollAnchorOffset.y; GraphicsImage *texture = nullptr; if (item->itemId == ITEM_BLASTER) @@ -1051,8 +1051,8 @@ void CharacterUI_DrawPaperdoll(Character *player) { // bow item = player->GetBowItem(); if (item) { - item_X = pPaperdoll_BodyX + paperdoll_Weapon[pBodyComplection][2][0] - pItemTable->pItems[item->itemId].uEquipX; - item_Y = pPaperdoll_BodyY + paperdoll_Weapon[pBodyComplection][2][1] - pItemTable->pItems[item->itemId].uEquipY; + item_X = pPaperdoll_BodyX + paperdoll_Weapon[pBodyComplection][2][0] - pItemTable->items[item->itemId].paperdollAnchorOffset.x; + item_Y = pPaperdoll_BodyY + paperdoll_Weapon[pBodyComplection][2][1] - pItemTable->items[item->itemId].paperdollAnchorOffset.y; CharacterUI_DrawItem(item_X, item_Y, item, player->pEquipment[ITEM_SLOT_BOW], nullptr, !bRingsShownInCharScreen); } @@ -1202,8 +1202,8 @@ void CharacterUI_DrawPaperdoll(Character *player) { // main hand's item item = itemMainHand; if (item) { - item_X = pPaperdoll_BodyX + paperdoll_Weapon[pBodyComplection][1][0] - pItemTable->pItems[item->itemId].uEquipX; - item_Y = pPaperdoll_BodyY + paperdoll_Weapon[pBodyComplection][1][1] - pItemTable->pItems[item->itemId].uEquipY; + item_X = pPaperdoll_BodyX + paperdoll_Weapon[pBodyComplection][1][0] - pItemTable->items[item->itemId].paperdollAnchorOffset.x; + item_Y = pPaperdoll_BodyY + paperdoll_Weapon[pBodyComplection][1][1] - pItemTable->items[item->itemId].paperdollAnchorOffset.y; GraphicsImage *texture = nullptr; if (item->itemId == ITEM_BLASTER) @@ -1215,8 +1215,8 @@ void CharacterUI_DrawPaperdoll(Character *player) { // offhand's item item = itemOffHand; if (item) { - item_X = pPaperdoll_BodyX + paperdoll_Weapon[pBodyComplection][0][0] - pItemTable->pItems[item->itemId].uEquipX; - item_Y = pPaperdoll_BodyY + paperdoll_Weapon[pBodyComplection][0][1] - pItemTable->pItems[item->itemId].uEquipY; + item_X = pPaperdoll_BodyX + paperdoll_Weapon[pBodyComplection][0][0] - pItemTable->items[item->itemId].paperdollAnchorOffset.x; + item_Y = pPaperdoll_BodyY + paperdoll_Weapon[pBodyComplection][0][1] - pItemTable->items[item->itemId].paperdollAnchorOffset.y; /* * MM6 artifacts. diff --git a/src/GUI/UI/UIMessageScroll.cpp b/src/GUI/UI/UIMessageScroll.cpp index a522d056e71..a0e1c4913ff 100644 --- a/src/GUI/UI/UIMessageScroll.cpp +++ b/src/GUI/UI/UIMessageScroll.cpp @@ -39,7 +39,7 @@ void GUIWindow_MessageScroll::Update() { a1.uFrameZ = a1.uFrameWidth + a1.uFrameX - 1; a1.uFrameW = a1.uFrameHeight + a1.uFrameY - 1; - const std::string &name = pItemTable->pItems[pGUIWindow_ScrollWindow->scroll_type].name; + const std::string &name = pItemTable->items[pGUIWindow_ScrollWindow->scroll_type].name; a1.DrawTitleText(assets->pFontCreate.get(), 0, 0, colorTable.White, fmt::format("{::}{}\f00000\n", colorTable.PaleCanary.tag(), name), 3); a1.DrawText(assets->pFontSmallnum.get(), {1, assets->pFontCreate->GetHeight() - 3}, colorTable.White, pMessageScrolls[pGUIWindow_ScrollWindow->scroll_type]); diff --git a/src/GUI/UI/UIPopup.cpp b/src/GUI/UI/UIPopup.cpp index aa7c4e72487..08f9cd6a310 100644 --- a/src/GUI/UI/UIPopup.cpp +++ b/src/GUI/UI/UIPopup.cpp @@ -330,7 +330,7 @@ void GameUI_DrawItemInfo(ItemGen *inspect_item) { // added so window is correct size with broken items iteminfo_window.uFrameHeight = inspect_item_image->height() + itemYspacing + 54; - if (!pItemTable->pItems[inspect_item->itemId].identifyDifficulty) + if (!pItemTable->items[inspect_item->itemId].identifyDifficulty) inspect_item->SetIdentified(); int GoldAmount = 0; @@ -419,7 +419,7 @@ void GameUI_DrawItemInfo(ItemGen *inspect_item) { (itemYspacing + (float)iteminfo_window.uFrameY + 30) / 480.0f, inspect_item_image); iteminfo_window.DrawTitleText( assets->pFontArrus.get(), 0, 0xCu, colorTable.PaleCanary, - pItemTable->pItems[inspect_item->itemId].pUnidentifiedName, 3); + pItemTable->items[inspect_item->itemId].unidentifiedName, 3); iteminfo_window.DrawTitleText( assets->pFontArrus.get(), 0x64u, ((int)iteminfo_window.uFrameHeight >> 1) - @@ -438,7 +438,7 @@ void GameUI_DrawItemInfo(ItemGen *inspect_item) { text[0] = localization->FormatString( LSTR_FMT_TYPE_S, - pItemTable->pItems[inspect_item->itemId].pUnidentifiedName); + pItemTable->items[inspect_item->itemId].unidentifiedName); switch (inspect_item->GetItemEquipType()) { case ITEM_TYPE_SINGLE_HANDED: @@ -491,15 +491,15 @@ void GameUI_DrawItemInfo(ItemGen *inspect_item) { text[2] = fmt::format("{}: {}", localization->GetString(LSTR_POWER), inspect_item->potionPower); } else if (inspect_item->isReagent()) { text[2] = fmt::format("{}: {}", localization->GetString(LSTR_POWER), inspect_item->GetDamageDice()); - } else if (inspect_item->attributeEnchantment) { + } else if (inspect_item->standardEnchantment) { text[2] = fmt::format("{}: {} +{}", localization->GetString(LSTR_SPECIAL_2), - pItemTable->standardEnchantments[*inspect_item->attributeEnchantment].pBonusStat, - inspect_item->attributeEnchantmentStrength); + pItemTable->standardEnchantments[*inspect_item->standardEnchantment].attributeName, + inspect_item->standardEnchantmentStrength); } else if (inspect_item->specialEnchantment != ITEM_ENCHANTMENT_NULL) { text[2] = fmt::format("{}: {}", localization->GetString(LSTR_SPECIAL_2), - pItemTable->pSpecialEnchantments[inspect_item->specialEnchantment].pBonusStatement); + pItemTable->specialEnchantments[inspect_item->specialEnchantment].description); } else if (inspect_item->isWand()) { text[2] = fmt::sprintf(localization->GetString(LSTR_FMT_S_U_OUT_OF_U), localization->GetString(LSTR_CHARGES), @@ -514,15 +514,15 @@ void GameUI_DrawItemInfo(ItemGen *inspect_item) { for (const std::string &s : text) if (!s.empty()) Str_int += assets->pFontComic->CalcTextHeight(s, iteminfo_window.uFrameWidth, 100) + 3; - if (!pItemTable->pItems[inspect_item->itemId].pDescription.empty()) + if (!pItemTable->items[inspect_item->itemId].description.empty()) Str_int += assets->pFontSmallnum->CalcTextHeight( - pItemTable->pItems[inspect_item->itemId].pDescription, + pItemTable->items[inspect_item->itemId].description, iteminfo_window.uFrameWidth, 100); iteminfo_window.uFrameHeight = inspect_item_image->height() + itemYspacing + 54; if ((signed int)Str_int > (signed int)iteminfo_window.uFrameHeight) iteminfo_window.uFrameHeight = (unsigned int)Str_int; if (inspect_item->flags & ITEM_TEMP_BONUS && - (inspect_item->specialEnchantment != ITEM_ENCHANTMENT_NULL || inspect_item->attributeEnchantment)) + (inspect_item->specialEnchantment != ITEM_ENCHANTMENT_NULL || inspect_item->standardEnchantment)) iteminfo_window.uFrameHeight += assets->pFontComic->GetHeight(); v85 = 0; if (assets->pFontArrus->GetHeight()) { @@ -565,8 +565,8 @@ void GameUI_DrawItemInfo(ItemGen *inspect_item) { v34 += assets->pFontComic->CalcTextHeight(s, iteminfo_window.uFrameWidth, 100, 0) + 3; } } - if (!pItemTable->pItems[inspect_item->itemId].pDescription.empty()) - iteminfo_window.DrawText(assets->pFontSmallnum.get(), {100, v34}, colorTable.White, pItemTable->pItems[inspect_item->itemId].pDescription); + if (!pItemTable->items[inspect_item->itemId].description.empty()) + iteminfo_window.DrawText(assets->pFontSmallnum.get(), {100, v34}, colorTable.White, pItemTable->items[inspect_item->itemId].description); iteminfo_window.uFrameX += 12; iteminfo_window.uFrameWidth -= 24; iteminfo_window.DrawTitleText(assets->pFontArrus.get(), 0, 0xCu, colorTable.PaleCanary, @@ -580,7 +580,7 @@ void GameUI_DrawItemInfo(ItemGen *inspect_item) { render->ResetUIClipRect(); } else { if ((inspect_item->flags & ITEM_TEMP_BONUS) && - (inspect_item->specialEnchantment != ITEM_ENCHANTMENT_NULL || inspect_item->attributeEnchantment)) { + (inspect_item->specialEnchantment != ITEM_ENCHANTMENT_NULL || inspect_item->standardEnchantment)) { LongCivilDuration d = (inspect_item->enchantmentExpirationTime - pParty->GetPlayingTime()).toLongCivilDuration(); std::string txt4 = "Duration:"; @@ -2270,7 +2270,7 @@ void Inventory_ItemPopupAndAlchemy() { pParty->activeCharacter().SetVariable(VAR_AutoNotes, pItemTable->potionNotes[potionSrc1][potionSrc2]); } } - if (!(pItemTable->pItems[item->itemId].identifyDifficulty)) { + if (!(pItemTable->items[item->itemId].identifyDifficulty)) { item->flags |= ITEM_IDENTIFIED; } pParty->activeCharacter().playReaction(SPEECH_POTION_SUCCESS); @@ -2350,7 +2350,7 @@ void Inventory_ItemPopupAndAlchemy() { return; } if (item->isWeapon()) { - if (item->specialEnchantment != ITEM_ENCHANTMENT_NULL || item->attributeEnchantment) { + if (item->specialEnchantment != ITEM_ENCHANTMENT_NULL || item->standardEnchantment) { // Sound error and stop right click item actions until button is released pAudioPlayer->playUISound(SOUND_error); rightClickItemActionPerformed = true; diff --git a/src/Io/Mouse.cpp b/src/Io/Mouse.cpp index 564258c4ffb..8b631d0af24 100644 --- a/src/Io/Mouse.cpp +++ b/src/Io/Mouse.cpp @@ -45,7 +45,7 @@ void Io::Mouse::RemoveHoldingItem() { } void Io::Mouse::SetCursorBitmapFromItemID(ItemId uItemID) { - SetCursorImage(pItemTable->pItems[uItemID].iconName); + SetCursorImage(pItemTable->items[uItemID].iconName); } void Io::Mouse::SetCurrentCursorBitmap() { SetCursorImage(this->cursor_name); } diff --git a/src/Scripting/GameBindings.cpp b/src/Scripting/GameBindings.cpp index 5734bce7c65..03793ea1118 100644 --- a/src/Scripting/GameBindings.cpp +++ b/src/Scripting/GameBindings.cpp @@ -205,7 +205,7 @@ void GameBindings::_registerItemBindings(sol::state_view &solState, sol::table & table["items"] = solState.create_table_with( "getItemInfo", sol::as_function([&solState, createItemTable](ItemId itemId) { if (itemId >= ITEM_FIRST_VALID && itemId <= ITEM_LAST_VALID) { - const ItemDesc &itemDesc = pItemTable->pItems[itemId]; + const ItemDesc &itemDesc = pItemTable->items[itemId]; return sol::object(solState, createItemTable(itemDesc)); } return sol::make_object(solState, sol::lua_nil); diff --git a/test/Bin/GameTest/GameTests_0000.cpp b/test/Bin/GameTest/GameTests_0000.cpp index 38a28465d77..9260237c6d6 100644 --- a/test/Bin/GameTest/GameTests_0000.cpp +++ b/test/Bin/GameTest/GameTests_0000.cpp @@ -85,13 +85,13 @@ GAME_TEST(Issues, Issue198) { game.runGameRoutine([&] { forEachInventoryItem([](const ItemGen &item, int /*x*/, int /*y*/) { // Calling width() forces the texture to be created. - assets->getImage_ColorKey(pItemTable->pItems[item.itemId].iconName)->width(); + assets->getImage_ColorKey(pItemTable->items[item.itemId].iconName)->width(); }); }); // Then can safely check everything. forEachInventoryItem([](const ItemGen &item, int x, int y) { - GraphicsImage *image = assets->getImage_ColorKey(pItemTable->pItems[item.itemId].iconName); + GraphicsImage *image = assets->getImage_ColorKey(pItemTable->items[item.itemId].iconName); int width = GetSizeInInventorySlots(image->width()); int height = GetSizeInInventorySlots(image->height()); diff --git a/test/Bin/GameTest/GameTests_0500.cpp b/test/Bin/GameTest/GameTests_0500.cpp index ddef9530643..c283512ab42 100644 --- a/test/Bin/GameTest/GameTests_0500.cpp +++ b/test/Bin/GameTest/GameTests_0500.cpp @@ -413,13 +413,13 @@ GAME_TEST(Issues, Issue675) { pItemTable->generateItem(level, RANDOM_ITEM_ANY, &item); if (isPotion(item.itemId)) { EXPECT_GE(item.potionPower, 1); - EXPECT_FALSE(item.attributeEnchantment); + EXPECT_FALSE(item.standardEnchantment); } else { EXPECT_EQ(item.potionPower, 0); - if (item.attributeEnchantment) { - EXPECT_GE(*item.attributeEnchantment, ATTRIBUTE_FIRST_ENCHANTABLE); - EXPECT_LE(*item.attributeEnchantment, ATTRIBUTE_LAST_ENCHANTABLE); - generatedEnchantments.insert(*item.attributeEnchantment); + if (item.standardEnchantment) { + EXPECT_GE(*item.standardEnchantment, ATTRIBUTE_FIRST_ENCHANTABLE); + EXPECT_LE(*item.standardEnchantment, ATTRIBUTE_LAST_ENCHANTABLE); + generatedEnchantments.insert(*item.standardEnchantment); } } } diff --git a/test/Bin/GameTest/GameTests_1500.cpp b/test/Bin/GameTest/GameTests_1500.cpp index 7b4a02599bc..370d20c27fb 100644 --- a/test/Bin/GameTest/GameTests_1500.cpp +++ b/test/Bin/GameTest/GameTests_1500.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -168,7 +169,7 @@ GAME_TEST(Issues, Issue1597) { int specialEnchantmentsNum = 0; for (int i = 0; i < 100; i++) { pItemTable->generateItem(ITEM_TREASURE_LEVEL_5, RANDOM_ITEM_AMULET, &item); - if (item.attributeEnchantment) + if (item.standardEnchantment) attrEnchantmentsNum++; if (item.specialEnchantment != ITEM_ENCHANTMENT_NULL) specialEnchantmentsNum++; @@ -604,3 +605,16 @@ GAME_TEST(Issues, Issue1925) { EXPECT_CONTAINS(spritesTape.flattened(), SPRITE_SPELL_FIRE_FIRE_BOLT); } } + +GAME_TEST(Prs, Pr1934) { + // Should be able to generate standard enchantments with +25 bonus. + ItemGen item; + int maxStrength = 0; + for (int i = 0; i < 100; i++) { + pItemTable->generateItem(ITEM_TREASURE_LEVEL_6, RANDOM_ITEM_RING, &item); + if (item.standardEnchantment) + maxStrength = std::max(maxStrength, item.standardEnchantmentStrength); + } + + EXPECT_EQ(maxStrength, 25); +}