From eeca9536153aa6d6a00b89d8b4ce4e665d3ab28b Mon Sep 17 00:00:00 2001 From: obligaron Date: Wed, 5 Apr 2023 21:36:26 +0200 Subject: [PATCH] Introduce UpdateHellfireFlag to set missing CF_HELLFIRE flag --- Source/items.cpp | 57 ++++++++++++++++++++++++++++++++++----------- Source/items.h | 4 ++++ Source/loadsave.cpp | 1 + test/pack_test.cpp | 11 +++++++++ 4 files changed, 60 insertions(+), 13 deletions(-) diff --git a/Source/items.cpp b/Source/items.cpp index 163a1aa62..8375073bd 100644 --- a/Source/items.cpp +++ b/Source/items.cpp @@ -1117,7 +1117,7 @@ std::string GenerateStaffName(const ItemData &baseItemData, SpellID spellId, boo return name; } -std::string GenerateStaffNameMagical(const ItemData &baseItemData, SpellID spellId, int preidx, bool translate) +std::string GenerateStaffNameMagical(const ItemData &baseItemData, SpellID spellId, int preidx, bool translate, std::optional forceNameLengthCheck) { string_view baseName = translate ? _(baseItemData.iName) : baseItemData.iName; string_view magicFmt = translate ? pgettext("spell", /* TRANSLATORS: Constructs item names. Format: {Prefix} {Item} of {Spell}. Example: King's War Staff of Firewall */ "{0} {1} of {2}") : "{0} {1} of {2}"; @@ -1125,7 +1125,7 @@ std::string GenerateStaffNameMagical(const ItemData &baseItemData, SpellID spell string_view prefixName = translate ? _(ItemPrefixes[preidx].PLName) : ItemPrefixes[preidx].PLName; std::string identifiedName = fmt::format(fmt::runtime(magicFmt), prefixName, baseName, spellName); - if (!StringInPanel(identifiedName.c_str())) { + if (forceNameLengthCheck ? *forceNameLengthCheck : !StringInPanel(identifiedName.c_str())) { string_view shortName = translate ? _(baseItemData.iSName) : baseItemData.iSName; identifiedName = fmt::format(fmt::runtime(magicFmt), prefixName, shortName, spellName); } @@ -1146,7 +1146,7 @@ void GetStaffPower(const Player &player, Item &item, int lvl, SpellID bs, bool o CopyUtf8(item._iName, staffName, sizeof(item._iName)); if (preidx != -1) { - std::string staffNameMagical = GenerateStaffNameMagical(baseItemData, item._iSpell, preidx, false); + std::string staffNameMagical = GenerateStaffNameMagical(baseItemData, item._iSpell, preidx, false, std::nullopt); CopyUtf8(item._iIName, staffNameMagical, sizeof(item._iIName)); } else { CopyUtf8(item._iIName, item._iName, sizeof(item._iIName)); @@ -2287,7 +2287,7 @@ StringOrView GetTranslatedItemName(const Item &item) } } -std::string GetTranslatedItemNameMagical(const Item &item) +std::string GetTranslatedItemNameMagical(const Item &item, bool hellfireItem, bool translate, std::optional forceNameLengthCheck) { std::string identifiedName; const auto &baseItemData = AllItemsList[static_cast(item.IDidx)]; @@ -2331,7 +2331,6 @@ std::string GetTranslatedItemNameMagical(const Item &item) if (minlvl > 25) minlvl = 25; - bool hellfireItem = (item.dwBuff & CF_HELLFIRE) == CF_HELLFIRE; AffixItemType affixItemType = AffixItemType::None; switch (item._itype) { @@ -2365,11 +2364,16 @@ std::string GetTranslatedItemNameMagical(const Item &item) 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; + if (forceNameLengthCheck) { + // We generate names to check if it's a diablo or hellfire item. This checks fails => invalid item => don't generate a item name + identifiedName.clear(); + } else { + // 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); + identifiedName = GenerateStaffNameMagical(baseItemData, item._iSpell, preidx, translate, forceNameLengthCheck); } } break; @@ -2407,9 +2411,9 @@ std::string GetTranslatedItemNameMagical(const Item &item) pSufix = &suffix; }); - identifiedName = GenerateMagicItemName(_(baseItemData.iName), pPrefix, pSufix, true); - if (!StringInPanel(identifiedName.c_str())) { - identifiedName = GenerateMagicItemName(_(baseItemData.iSName), pPrefix, pSufix, true); + identifiedName = GenerateMagicItemName(_(baseItemData.iName), pPrefix, pSufix, translate); + if (forceNameLengthCheck ? *forceNameLengthCheck : !StringInPanel(identifiedName.c_str())) { + identifiedName = GenerateMagicItemName(_(baseItemData.iSName), pPrefix, pSufix, translate); } } @@ -4737,7 +4741,7 @@ StringOrView Item::getName() const const auto &baseItemData = AllItemsList[static_cast(IDidx)]; return _(UniqueItems[_iUid].UIName); } else { - return GetTranslatedItemNameMagical(*this); + return GetTranslatedItemNameMagical(*this, dwBuff & CF_HELLFIRE, true, std::nullopt); } } @@ -4924,4 +4928,31 @@ bool ApplyOilToItem(Item &item, Player &player) return true; } +void UpdateHellfireFlag(Item &item, const char *identifiedItemName) +{ + // DevilutionX support vanilla and hellfire items in one save file and for that introduced CF_HELLFIRE + // But vanilla hellfire items don't have CF_HELLFIRE set in Item::dwBuff + // This functions tries to set this flag for vanilla hellfire items based on the item name + // This ensures that Item::getName() returns the correct translated item name + if (item.dwBuff & CF_HELLFIRE) + return; // Item is already a hellfire item + if (item._iMagical != ITEM_QUALITY_MAGIC) + return; // Only magic item's name can differ between diablo and hellfire + if (gbIsMultiplayer) + return; // Vanilla hellfire multiplayer is not supported in devilutionX, so there can't be items with missing dwBuff from there + // We need to test both short and long name, cause StringInPanel can return a different result (other font and some bugfixes) + std::string diabloItemNameShort = GetTranslatedItemNameMagical(item, false, false, false); + if (diabloItemNameShort == identifiedItemName) + return; // Diablo item name is identical => not a hellfire specific item + std::string diabloItemNameLong = GetTranslatedItemNameMagical(item, false, false, true); + if (diabloItemNameLong == identifiedItemName) + return; // Diablo item name is identical => not a hellfire specific item + std::string hellfireItemNameShort = GetTranslatedItemNameMagical(item, true, false, false); + std::string hellfireItemNameLong = GetTranslatedItemNameMagical(item, true, false, true); + if (hellfireItemNameShort == identifiedItemName || hellfireItemNameLong == identifiedItemName) { + // This item should be a vanilla hellfire item that has CF_HELLFIRE missing, cause only then the item name matches + item.dwBuff |= CF_HELLFIRE; + } +} + } // namespace devilution diff --git a/Source/items.h b/Source/items.h index de64e65b8..36a88005f 100644 --- a/Source/items.h +++ b/Source/items.h @@ -556,6 +556,10 @@ void initItemGetRecords(); void RepairItem(Item &item, int lvl); void RechargeItem(Item &item, Player &player); bool ApplyOilToItem(Item &item, Player &player); +/** + * @brief Checks if the item is generated in vanilla hellfire. If yes it updates dwBuff to include CF_HELLFIRE. + */ +void UpdateHellfireFlag(Item &item, const char *identifiedItemName); #ifdef _DEBUG std::string DebugSpawnItem(std::string itemName); diff --git a/Source/loadsave.cpp b/Source/loadsave.cpp index 9d26b74fa..036f6a6d3 100644 --- a/Source/loadsave.cpp +++ b/Source/loadsave.cpp @@ -315,6 +315,7 @@ void LoadItemData(LoadHelper &file, Item &item) item._iDamAcFlags = static_cast(file.NextLE()); else item._iDamAcFlags = ItemSpecialEffectHf::None; + UpdateHellfireFlag(item, item._iIName); RemoveInvalidItem(item); } diff --git a/test/pack_test.cpp b/test/pack_test.cpp index f0c96b2d7..2132f4700 100644 --- a/test/pack_test.cpp +++ b/test/pack_test.cpp @@ -122,6 +122,17 @@ static void TestItemNameGeneration(const Item &item) if (allowIdentified) { testItem._iIdentified = true; ASSERT_STREQ(testItem.getName().str().data(), testItem._iIName) << "identified name"; + + // Check that UpdateHellfireFlag ensures that dwBuff is updated to get the correct name + if (item._iMagical == ITEM_QUALITY_MAGIC) { + bool isHellfireItem = (testItem.dwBuff & CF_HELLFIRE); + testItem.dwBuff = 0; + UpdateHellfireFlag(testItem, testItem._iIName); + + testItem._iIdentified = true; + ASSERT_STREQ(testItem.getName().str().data(), testItem._iIName) << "identified name with UpdateHellfireFlag"; + ASSERT_TRUE(isHellfireItem || ((item.dwBuff & CF_HELLFIRE) != CF_HELLFIRE)) << "item was wrongly converted to hellfire"; + } } }