From 0426908988cd74fa8db4111d6e2f543f6a5296ed Mon Sep 17 00:00:00 2001 From: obligaron Date: Thu, 30 Mar 2023 20:12:26 +0200 Subject: [PATCH] Introduce GetTranslatedItemName/GetTranslatedItemNameMagical --- Source/items.cpp | 174 +++++++++++++++++++++++++++++++++++++++++++-- test/pack_test.cpp | 26 ++++++- 2 files changed, 194 insertions(+), 6 deletions(-) diff --git a/Source/items.cpp b/Source/items.cpp index d5a6359fb..77f9da0ff 100644 --- a/Source/items.cpp +++ b/Source/items.cpp @@ -42,6 +42,7 @@ #include "stores.h" #include "utils/format_int.hpp" #include "utils/language.h" +#include "utils/log.hpp" #include "utils/math.h" #include "utils/stdcompat/algorithm.hpp" #include "utils/str_cat.hpp" @@ -2258,6 +2259,164 @@ _item_indexes RndItemForMonsterLevel(int8_t monsterLevel) }); } +StringOrView GetTranslatedItemName(const Item &item) +{ + const auto &baseItemData = AllItemsList[static_cast(item.IDidx)]; + + if (item._iCreateInfo == 0) { + return _(baseItemData.iName); + } else if (item._iMiscId == IMISC_BOOK) { + std::string name; + const string_view spellName = pgettext("spell", GetSpellData(item._iSpell).sNameText); + StrAppend(name, _(baseItemData.iName)); + StrAppend(name, spellName); + return name; + } else if (item._iMiscId == IMISC_EAR) { + return fmt::format(fmt::runtime(_(/* TRANSLATORS: {:s} will be a Character Name */ "Ear of {:s}")), item._iIName); + } else if (item._iMiscId > IMISC_OILFIRST && item._iMiscId < IMISC_OILLAST) { + for (size_t i = 0; i < 10; i++) { + if (OilMagic[i] != item._iMiscId) + continue; + return _(OilNames[i]); + } + app_fatal("unkown oil"); + } else if (item._itype == ItemType::Staff && item._iSpell != SpellID::Null && item._iMagical != ITEM_QUALITY_UNIQUE) { + return GenerateStaffName(baseItemData, item._iSpell, true); + } else { + return _(baseItemData.iName); + } +} + +std::string GetTranslatedItemNameMagical(const Item &item) +{ + std::string identifiedName; + const auto &baseItemData = AllItemsList[static_cast(item.IDidx)]; + + int lvl = item._iCreateInfo & CF_LEVEL; + bool onlygood = (item._iCreateInfo & (CF_ONLYGOOD | CF_SMITHPREMIUM | CF_BOY | CF_WITCH)) != 0; + + uint32_t currentSeed = GetLCGEngineState(); + SetRndSeed(item._iSeed); + + int minlvl; + int maxlvl; + if ((item._iCreateInfo & CF_SMITHPREMIUM) != 0) { + AdvanceRndSeed(); // RndVendorItem + AdvanceRndSeed(); // GetItemAttrs + minlvl = lvl / 2; + maxlvl = lvl; + } else if ((item._iCreateInfo & CF_BOY) != 0) { + AdvanceRndSeed(); // RndVendorItem + AdvanceRndSeed(); // GetItemAttrs + minlvl = lvl; + maxlvl = lvl * 2; + } else if ((item._iCreateInfo & CF_WITCH) != 0) { + AdvanceRndSeed(); // RndVendorItem + AdvanceRndSeed(); // GetItemAttrs + int iblvl = -1; + if (GenerateRnd(100) <= 5) + iblvl = 2 * lvl; + if (iblvl == -1 && item._iMiscId == IMISC_STAFF) + iblvl = 2 * lvl; + minlvl = iblvl / 2; + maxlvl = iblvl; + } else { + AdvanceRndSeed(); // GetItemAttrs + int iblvl = GetItemBLevel(lvl, item._iMiscId, onlygood, item._iCreateInfo & CF_UPER15); + minlvl = iblvl / 2; + maxlvl = iblvl; + AdvanceRndSeed(); // CheckUnique + } + + if (minlvl > 25) + minlvl = 25; + + bool hellfireItem = (item.dwBuff & CF_HELLFIRE) == CF_HELLFIRE; + AffixItemType affixItemType = AffixItemType::None; + + switch (item._itype) { + case ItemType::Sword: + case ItemType::Axe: + case ItemType::Mace: + affixItemType = AffixItemType::Weapon; + break; + case ItemType::Bow: + affixItemType = AffixItemType::Bow; + break; + case ItemType::Shield: + affixItemType = AffixItemType::Shield; + break; + case ItemType::LightArmor: + case ItemType::Helm: + case ItemType::MediumArmor: + case ItemType::HeavyArmor: + affixItemType = AffixItemType::Armor; + break; + case ItemType::Staff: { + bool allowspells = !hellfireItem || ((item._iCreateInfo & CF_SMITHPREMIUM) == 0); + + if (!allowspells) + affixItemType = AffixItemType::Staff; + else if (!hellfireItem && FlipCoin(4)) { + affixItemType = AffixItemType::Staff; + } else { + AdvanceRndSeed(); // Spell + AdvanceRndSeed(); // Charges + + int preidx = GetStaffPrefixId(maxlvl, onlygood, hellfireItem); + if (preidx == -1 || item._iSpell == SpellID::Null) { + // This can happen, if the item is hacked or a bug in the logic exists + LogWarn("GetTranslatedItemNameMagical failed for item '{}' with preidx '{}' and spellid '{}'", item._iIName, preidx, static_cast>(item._iSpell)); + identifiedName = item._iIName; + } else { + identifiedName = GenerateStaffNameMagical(baseItemData, item._iSpell, preidx, true); + } + } + break; + } + case ItemType::Ring: + case ItemType::Amulet: + affixItemType = AffixItemType::Misc; + break; + case ItemType::None: + case ItemType::Misc: + case ItemType::Gold: + break; + } + + if (affixItemType != AffixItemType::None) { + const PLStruct *pPrefix = nullptr; + const PLStruct *pSufix = nullptr; + GetItemPowerPrefixAndSuffix( + minlvl, maxlvl, affixItemType, onlygood, hellfireItem, + [&pPrefix](const PLStruct &prefix) { + pPrefix = &prefix; + // GenerateRnd(prefix.power.param2 - prefix.power.param2 + 1) + AdvanceRndSeed(); + switch (pPrefix->power.type) { + case IPL_TOHIT_DAMP: + AdvanceRndSeed(); + AdvanceRndSeed(); + break; + case IPL_TOHIT_DAMP_CURSE: + AdvanceRndSeed(); + break; + } + }, + [&pSufix](const PLStruct &suffix) { + pSufix = &suffix; + }); + + identifiedName = GenerateMagicItemName(_(baseItemData.iName), pPrefix, pSufix, true); + if (!StringInPanel(identifiedName.c_str())) { + identifiedName = GenerateMagicItemName(_(baseItemData.iSName), pPrefix, pSufix, true); + } + } + + SetRndSeed(currentSeed); + return identifiedName; +} + } // namespace bool IsItemAvailable(int i) @@ -4570,11 +4729,16 @@ void Item::updateRequiredStatsCacheForPlayer(const Player &player) StringOrView Item::getName() const { - string_view view; - if (_iIdentified) - view = _iIName; - view = _iName; - return view; + if (isEmpty()) { + return string_view(""); + } else if (!_iIdentified || _iCreateInfo == 0 || _iMagical == ITEM_QUALITY_NORMAL) { + return GetTranslatedItemName(*this); + } else if (_iMagical == ITEM_QUALITY_UNIQUE) { + const auto &baseItemData = AllItemsList[static_cast(IDidx)]; + return _(UniqueItems[_iUid].UIName); + } else { + return GetTranslatedItemNameMagical(*this); + } } bool CornerStoneStruct::isAvailable() diff --git a/test/pack_test.cpp b/test/pack_test.cpp index b7f5ef832..f0c96b2d7 100644 --- a/test/pack_test.cpp +++ b/test/pack_test.cpp @@ -110,6 +110,21 @@ typedef struct TestItemStruct { int IDidx; } TestItemStruct; +static void TestItemNameGeneration(const Item &item) +{ + bool allowIdentified = (item._iMiscId != IMISC_EAR); // Ears can't be identified. Item::getName() doesn't handle it, so don't test it. + ASSERT_EQ(allowIdentified & item._iIdentified, item._iIdentified); + + Item testItem = item; + + testItem._iIdentified = false; + ASSERT_STREQ(testItem.getName().str().data(), testItem._iName) << "unidentified name"; + if (allowIdentified) { + testItem._iIdentified = true; + ASSERT_STREQ(testItem.getName().str().data(), testItem._iIName) << "identified name"; + } +} + static void CompareItems(const Item &item1, const TestItemStruct &item2) { ASSERT_STREQ(item1._iIName, item2._iIName); @@ -378,6 +393,7 @@ TEST_F(PackTest, UnPackItem_diablo) const ItemPack packed = SwappedLE(PackedDiabloItems[i]); UnPackItem(packed, *MyPlayer, id, false); CompareItems(id, DiabloItems[i]); + TestItemNameGeneration(id); PackItem(is, id, gbIsHellfire); ComparePackedItems(is, packed); @@ -450,6 +466,7 @@ TEST_F(PackTest, UnPackItem_spawn) const ItemPack packed = SwappedLE(PackedSpawnItems[i]); UnPackItem(packed, *MyPlayer, id, false); CompareItems(id, SpawnItems[i]); + TestItemNameGeneration(id); PackItem(is, id, gbIsHellfire); ComparePackedItems(is, packed); @@ -495,6 +512,7 @@ TEST_F(PackTest, UnPackItem_diablo_multiplayer) const ItemPack packed = SwappedLE(PackedDiabloMPItems[i]); UnPackItem(packed, *MyPlayer, id, false); CompareItems(id, DiabloMPItems[i]); + TestItemNameGeneration(id); PackItem(is, id, gbIsHellfire); ComparePackedItems(is, packed); @@ -576,7 +594,7 @@ const ItemPack PackedHellfireItems[] = { { 1403842263, 858, 70, 5, 90, 90, 0, 0, 0, 0 }, // Demon Plate Armor { 1543909415, 284, 86, 0, 0, 0, 0, 0, 0, 0 }, // Oil of Fortitude { 1572202402, 769, 157, 5, 0, 0, 0, 0, 0, 0 }, // Ring of Regha - { 1572202657, 257, 156, 5, 0, 0, 0, 0, 0, 0 }, // Bronze Ring of dexterity + { 1572202657, 257, 156, 3, 0, 0, 0, 0, 0, 0 }, // Bronze Ring of dexterity { 1642077210, 264, 84, 0, 0, 0, 0, 0, 0, 0 }, // Oil of Accuracy { 2049461998, 388, 35, 0, 0, 0, 0, 0, 0, 0 }, // Blacksmith Oil { 2054447852, 2050, 151, 3, 25, 25, 0, 0, 0, 0 }, // Spider's Staff of devastation @@ -709,6 +727,7 @@ TEST_F(PackTest, UnPackItem_hellfire) const ItemPack packed = SwappedLE(PackedHellfireItems[i]); UnPackItem(packed, *MyPlayer, id, true); CompareItems(id, HellfireItems[i]); + TestItemNameGeneration(id); PackItem(is, id, gbIsHellfire); is.dwBuff &= ~CF_HELLFIRE; @@ -726,6 +745,7 @@ TEST_F(PackTest, UnPackItem_diablo_strip_hellfire_items) gbIsSpawn = false; UnPackItem(is, *MyPlayer, id, true); + TestItemNameGeneration(id); ASSERT_EQ(id._itype, ItemType::None); } @@ -736,6 +756,7 @@ TEST_F(PackTest, UnPackItem_empty) Item id; UnPackItem(is, *MyPlayer, id, false); + TestItemNameGeneration(id); ASSERT_EQ(id._itype, ItemType::None); } @@ -752,6 +773,7 @@ TEST_F(PackTest, PackItem_empty) // Copy the value out before comparing to avoid loading a misaligned address. const auto idx = is.idx; ASSERT_EQ(SDL_SwapLE16(idx), 0xFFFF); + TestItemNameGeneration(id); } static void compareGold(const ItemPack &is, int iCurs) @@ -765,6 +787,7 @@ static void compareGold(const ItemPack &is, int iCurs) ASSERT_EQ(id._ivalue, wvalue); ASSERT_EQ(id._itype, ItemType::Gold); ASSERT_EQ(id._iClass, ICLASS_GOLD); + TestItemNameGeneration(id); ItemPack is2; PackItem(is2, id, gbIsHellfire); @@ -797,6 +820,7 @@ TEST_F(PackTest, UnPackItem_ear) UnPackItem(is, *MyPlayer, id, false); ASSERT_STREQ(id._iName, "Ear of Dead-RogueDM"); ASSERT_EQ(id._ivalue, 3); + TestItemNameGeneration(id); ItemPack is2; PackItem(is2, id, gbIsHellfire);