Skip to content

refactor(items): the second item chain's tooltip texts come from the data - #820

Merged
Mosch0512 merged 46 commits into
sven-n:mainfrom
ezequielzacca:feat/item-tooltip-chain-texts
Oct 24, 2026
Merged

Mosch0512 merged 46 commits into
sven-n:mainfrom
ezequielzacca:feat/item-tooltip-chain-texts

Conversation

@ezequielzacca

@ezequielzacca ezequielzacca commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

This step's own changes: compare with #819. The diff of this PR also holds every earlier step of #815, since a PR from a fork can only target this repository's branches.

Summary

Step 5a of #815 (data-driven item tooltips). Stacked on #819 (→ #818 → #817 → #816 → #813): only the last commit, refactor(items): the second item chain's tooltip texts come from the data, is this step.

RenderItemInfo() has a second item chain, right under the item's name: about 90 if (ip->Type == …) cases for seals, talismans, tickets, cards, rings, necklaces, keys, boxes, pets, cherry blossoms and more. The 55 cases that only write fixed lines become the descriptions of their 72 items:

"description": [
  {"text": {"en": "Increases Attack Power and Wizardry by 40%", …}, "color": "blue"},
  {"text": {"en": "Increases Attack Speed by 10", …}, "color": "purple"}
]
  • What moves: a case moves when its condition names item types (==, ranges, ||) and its body only writes lines: texts, colors, blank lines, constant numbers, and a text as %s. I sorted the cases with a script over the chain; the commit lists the ones that stay.
  • Texts: all 11 UI languages, converted as the code printed them.
    • A printf text has %% as %, with its numbers and text arguments written in.
    • A %ls text stays as it is.
    • A translation that has no %d keeps its own number, as printf leaves the argument out. The Japanese party-EXP text says 190% where the others say 170%; that's the translation's own wording.
  • Moonstone Pendant: its "Cannot Repair" (red) goes before the description it got in refactor(items): the original items' tooltip texts come from their data #816, as it was drawn above it.
  • Same order on screen: none of the 72 items has stat lines (damage, defense, speeds) or a box list. Those are the only things drawn between this chain and the description.
  • Removed:
    • the cases (527 lines);
    • the 51 .resx texts no other code prints, in every language. None is in a computed Lookup() range (2500–2501, 2503–2508, 2512–2516, 2732–2741).
  • Comparison test: "The second item chain's tooltip texts come from the descriptions as from the old code" checks that each of the 72 descriptions starts with the old code's English lines and colors. The table was generated from the old code's cases and the committed English texts.

What stays code (34 cases)

  • Values of the item: {durability} ("Number of items: %d") and the item add options (g_pItemAddOptioninfo). They need the built-ins of step 5b and the add-option data of phase 11.
  • Logic: ticket maps, timers, the resurrection point, level switches (Wizard's Ring, Secromicon), a bold line, and helper conditions (quest items, gamble items).
  • Four cards whose texts the game doesn't have: the Summoner Character Card and the Chaos Cards Gold, Rare and Mini name legacy ids 2551–2556, which no .resx has. Lookup() gives an empty string, so they show empty lines today; that's left for whoever knows their texts.

Screenshots

Before (#819) and after, the same. I used items the local OpenMU server has; most of the 72 are cash shop or later-season items.

Moonstone Pendant (the chain's line, then its description)

Legacy theme Modern theme
Before
After
The item data of the "after" images (13,38)

src/bin/Data/Items/Group13_Helper.json:

{
  "number": 38,
  "name": {
    "en": "Moonstone Pendant",
    "de": "Mondsteinanhänger",
    "es": "Pendiente de Piedra Lunar",
    "pt": "Pingente da pedra da lua"
  },
  "description": [
    {
      "text": {
        "en": "Cannot Repair",
        "de": "Kann nicht repariert werden",
        "es": "No se puede reparar",
        "id": "Tidak Dapat Memperbaiki",
        "ja": "修理不可能",
        "pl": "Nie można naprawić",
        "pt": "Não é possível reparar",
        "ru": "Невозможно восстановить",
        "tl": "Hindi Maaayos",
        "uk": "Неможливо відремонтувати",
        "zh-TW": "無法修復"
      },
      "color": "red"
    },
    {
      "text": {
        "en": "I.D. of Kantur Chief Scientist. You can enter the Refinery Tower.",
        "de": "Ausweis des Chefwissenschaftlers von Kantur. Sie können den Raffinerieturm betreten.",
        "es": "IDENTIFICACIÓN. del científico jefe de Kantur. Puedes ingresar a la Torre de la Refinería.",
        "id": "PENGENAL. dari Kepala Ilmuwan Kantur. Anda bisa memasuki Menara Kilang.",
        "ja": "カントルの首席研究員たちの入出許可証。精製の塔へ入ることが出来ます。",
        "pl": "ID. głównego naukowca Kantur. Możesz wejść do Refinery Tower.",
        "pt": "EU IA. do Cientista Chefe Kantur. Você pode entrar na Torre da Refinaria.",
        "ru": "ИДЕНТИФИКАТОР. главного научного сотрудника Кантура. Вы можете войти в Башню нефтеперерабатывающего завода.",
        "tl": "I.D. ng Kantur Chief Scientist. Maaari kang pumasok sa Refinery Tower.",
        "uk": "І.Д. головного наукового співробітника Кантур. Ви можете увійти до вежі нафтопереробного заводу.",
        "zh-TW": "ID。坎圖爾首席科學家。您可以進入煉油塔。"
      }
    }
  ]
  …
}

Demon

Legacy theme Modern theme
Before
After
The item data of the "after" images (13,64)

src/bin/Data/Items/Group13_Helper.json:

{
  "number": 64,
  "name": {
    "en": "Demon",
    "de": "Dämon",
    "es": "Demonio",
    "pt": "Demônio"
  },
  "tags": ["guardianPet", "cashShop", "valuable"],
  "description": [
    {
      "text": {
        "en": "Increases Attack Power and Wizardry by 40%",
        "de": "Erhöht Angriffskraft und Zauberei um 40%",
        "es": "Aumenta el poder de ataque y la hechicería en un 40%",
        "id": "Meningkatkan Kekuatan Serangan dan Sihir sebesar 40%",
        "ja": "攻撃力、魔力40%向上",
        "pl": "Zwiększa siłę ataku i czary o 40%",
        "pt": "Aumenta o poder de ataque e a magia em 40%",
        "ru": "Увеличивает силу атаки и волшебство на 40%",
        "tl": "Pinapataas ng 40% ang Attack Power at Wizardry",
        "uk": "Збільшує силу атаки та магію на 40%",
        "zh-TW": "攻擊力和魔法增加40%"
      },
      "color": "blue"
    },
    {
      "text": {
        "en": "Increases Attack Speed by 10",
        "de": "Erhöht die Angriffsgeschwindigkeit um 10",
        "es": "Aumenta la velocidad de ataque en 10",
        "id": "Meningkatkan Kecepatan Serangan sebanyak 10",
        "ja": "攻撃速度+10増加",
        "pl": "Zwiększa prędkość ataku o 10",
        "pt": "Aumenta a velocidade de ataque em 10",
        "ru": "Увеличивает скорость атаки на 10.",
        "tl": "Pinapataas ng 10 ang Bilis ng Pag-atake",
        "uk": "Збільшує швидкість атаки на 10",
        "zh-TW": "攻擊速度提高 10"
      },
      "color": "purple"
    }
  ]
  …
}

Pet Panda

Legacy theme Modern theme
Before
After
The item data of the "after" images (13,80)

src/bin/Data/Items/Group13_Helper.json:

{
  "number": 80,
  "name": {
    "en": "Pet Panda",
    "de": "Haustier Panda",
    "es": "Mascota Panda",
    "pt": "Animal de estimação Panda"
  },
  "tags": ["pandaOrSkeleton", "valuable"],
  "description": [
    {
      "text": {
        "en": "Auto-collects zen around you.",
        "de": "Sammelt automatisch Zen in Ihrer Umgebung.",
        "es": "Recoge automáticamente el zen a tu alrededor.",
        "id": "Mengumpulkan zen secara otomatis di sekitar Anda.",
        "ja": "周りのゼンを自動的に拾得してくれます。",
        "pl": "Automatycznie zbiera zen wokół ciebie.",
        "pt": "Coleta automaticamente o zen ao seu redor.",
        "ru": "Автоматически собирает дзен вокруг себя.",
        "tl": "Auto-collect ng zen sa paligid mo.",
        "uk": "Автоматично збирає дзен навколо вас.",
        "zh-TW": "自動收集你周圍的禪宗。"
      },
      "color": "blue"
    },
    {
      "text": {
        "en": "EXP rate 50% increase",
        "de": "Erfahrungsrate +50%",
        "es": "Tasa de EXP 50% iaumentar",
        "id": "Tingkat EXP 50% imeningkat",
        "ja": "経験値50%向上",
        "pl": "Stawka EXP 50% iwzrost",
        "pt": "Taxa de EXP 50% inaumento",
        "ru": "Скорость опыта 50% increase",
        "tl": "EXP rate 50% inadagdagan",
        "uk": "Швидкість досвіду збільшується на 50% in",
        "zh-TW": "EXP率50% i增加"
      },
      "color": "blue"
    },
    {
      "text": {
        "en": "Increase Defensive Skill +50",
        "de": "Verteidigung +50",
        "es": "Aumentar la habilidad defensiva +50",
        "id": "Tingkatkan Keterampilan Pertahanan +50",
        "ja": "防御力+50",
        "pl": "Zwiększa umiejętność obrony +50",
        "pt": "Aumente a habilidade defensiva +50",
        "ru": "Увеличение навыка защиты +50",
        "tl": "Dagdagan ang Defensive Skill +50",
        "uk": "Підвищення навичок захисту +50",
        "zh-TW": "防禦技能增加+50"
      },
      "color": "blue"
    }
  ]
  …
}

Devil's Invitation

Legacy theme Modern theme
Before
After
The item data of the "after" images (14,19)

src/bin/Data/Items/Group14_Potion.json:

{
  "number": 19,
  "name": {
    "en": "Devil's Invitation",
    "de": "Einladung des Teufels",
    "es": "Invitación del Diablo",
    "pt": "Convite do diabo"
  },
  "description": [
    {
      "text": {
        "en": "The remaining time is shown",
        "de": "Die verbleibende Zeit wird angezeigt",
        "es": "Se muestra el tiempo restante",
        "id": "Waktu yang tersisa ditampilkan",
        "ja": "マウスボタンを右クリックすれば",
        "pl": "Wyświetlany jest pozostały czas",
        "pt": "O tempo restante é mostrado",
        "ru": "Оставшееся время показано",
        "tl": "Ang natitirang oras ay ipinapakita",
        "uk": "Показано час, що залишився",
        "zh-TW": "顯示剩餘時間"
      }
    },
    {
      "text": {
        "en": "when you right click on your mouse.",
        "de": "wenn Sie mit der rechten Maustaste klicken.",
        "es": "cuando haces clic derecho en tu mouse.",
        "id": "ketika Anda mengklik kanan pada mouse Anda.",
        "ja": "残った時間を見ることができます。",
        "pl": "po kliknięciu prawym przyciskiem myszy.",
        "pt": "quando você clica com o botão direito do mouse.",
        "ru": "когда вы щелкаете правой кнопкой мыши.",
        "tl": "kapag nag-right click ka sa iyong mouse.",
        "uk": "коли ви клацаєте правою кнопкою миші.",
        "zh-TW": "當您右鍵單擊滑鼠時。"
      }
    }
  ]
  …
}

Condor Flame

Legacy theme Modern theme
Before
After
The item data of the "after" images (13,52)

src/bin/Data/Items/Group13_Helper.json:

{
  "number": 52,
  "name": {
    "en": "Condor Flame",
    "de": "Kondorflamme",
    "es": "Llama de Cóndor",
    "pt": "Chama do Condor"
  },
  "tags": ["valuable"],
  "description": [
    {
      "text": {
        "en": "Ingredients for the 3rd wing assembly.",
        "de": "Zutaten für die Herstellung der Flügel der 3. Stufe.",
        "es": "Ingredientes para el montaje de la 3ª ala.",
        "id": "Bahan untuk perakitan sayap ke-3.",
        "ja": "3次羽合成のための材料",
        "pl": "Składniki na montaż trzeciego skrzydła.",
        "pt": "Ingredientes para a montagem da 3ª asa.",
        "ru": "Ингредиенты для сборки третьего крыла.",
        "tl": "Mga sangkap para sa 3rd wing assembly.",
        "uk": "Інгредієнти для складання 3-го крила.",
        "zh-TW": "第三機翼組件的材料。"
      }
    }
  ]
  …
}

Related issues

Step 5a of #815. Builds on #819, #818, #817, #816 and #813.

Checklist

  • I have read and followed docs/CODING_RULES.md.
  • I have built the project locally (see docs/build/README.md).
    • Windows x64 Release: all 989 tests pass, including the comparison test.
    • Changed lines pass clang-format 21.1.8 as CI checks them.
    • In game (Release x64 against a local OpenMU), the five items above were compared before and after in both themes.
  • Changes are focused on one concern; the diff is as small as it reasonably can be.

…m their data

The damage increase and absorption the tooltip code computed for the
wings, capes and pets are their descriptions now, with placeholders over
11 new level bonus tables of Stats/ItemStats.json (wingDamageIncrease1st
is 12 + 2 per level, ...): 24 wings and capes, plus Horn of Dinorant,
Guardian Angel and Imp, whose fixed numbers are written in the text.
Their cases leave RenderItemInfo; the two .resx texts no code uses any
more go too (the shared "Increase %d%% of Damage" and "Increase speed"
stay, other code prints them).

The printf texts become data as the game showed them: %d is the
placeholder or the number, %% is %. Two translations of the Imp's text
had broken escapes ("% %", shown as %; Portuguese "% o", read as an
octal number); they read as plain text now.

A table no stat uses is no longer a warning of the stat file alone: it
can be one for descriptions, so ValidateDescriptionPlaceholders warns
about a table that neither a stat nor a description uses.

A test compares every one of the 27 items at every level from +0 to +15
with the old formulas.
A description line can be limited to item levels: "levels": [1, 8]
shows it from +1 to +8 (both included; left out, at every level). The
tooltip draws only the lines of the item's level.

With it, the last fixed texts of RenderItemInfo's item chain are
descriptions: Town Portal scroll (+1 to +8), Spirit, Loch's Feather,
Fruits, Siege Potion, Contract Summon, Life Stone and Rena by level,
and Scroll of Archangel, Blood Bone and Invisibility Cloak, which have
one text at every level. Their cases go, and so do the 18 .resx texts
no other code prints (no Lookup() range reaches their legacy ids).

The old code drew a stale line of the text list at the Contract
Summon's, Life Stone's and Rena's levels without a text (+2 and up,
Rena +0); those levels show none now. Otherwise nothing changes: a test
compares every level of the 11 items with the old code's lines and
colors.

The Armor of Guardsman's Chaos Castle table and the Lost Map / Symbol
of Kundun stay code: they read the hero and other tables.
…data

The fixed lines of the item chain RenderItemInfo writes under an item's
name (seals, talismans, tickets, cards, rings, necklaces, keys, boxes,
pets, cherry blossoms, ...) are the descriptions of 72 items now: 55
cases with only fixed texts, colors, blank lines and constant numbers.
Their texts keep all 11 UI languages, converted as the code printed
them (printf %% as %, its numbers and text arguments written in; a %ls
text as it is; a translation without the %d keeps its own number, as
printf left the argument out). The Moonstone Pendant's "Cannot Repair"
comes before its description.

None of the 72 items has stat lines or a box list, which are drawn
between the chain and the description, so the tooltips look the same;
a test compares each item's lines and colors with the old code's.

The cases go (527 lines), and so do the 51 .resx texts no other code
prints (none is in a computed Lookup() range: 2500-2501, 2503-2508,
2512-2516, 2732-2741).

The other 34 cases stay code: values of the item (durability, the
item add options), timers and maps, level switches, bold lines, helper
conditions, and the four cards whose legacy texts the game does not
have (Lookup() shows them as empty lines).
…at/item-tooltip-values-to-data

# Conflicts:
#	docs/item-data.md
#	src/Localization/Game.pl.resx
#	src/source/Data/GameData/ItemData/ItemStatProfiles.cpp
#	src/source/Data/GameData/ItemData/ItemStatsJson.cpp
…/item-description-levels

# Conflicts:
#	src/Localization/Game.pl.resx
…m-tooltip-chain-texts

# Conflicts:
#	src/Localization/Game.pl.resx
…at/item-tooltip-values-to-data

# Conflicts:
#	docs/item-data.md
…/item-description-levels

# Conflicts:
#	docs/item-data.md
#	src/source/Engine/Object/ZzzInventory.cpp
…at/item-tooltip-values-to-data

# Conflicts:
#	docs/item-data.md
…/item-description-levels

# Conflicts:
#	docs/item-data.md
#	src/source/Engine/Object/ZzzInventory.cpp
…at/item-tooltip-values-to-data

# Conflicts:
#	docs/item-data.md
…ip-values-to-data

# Conflicts:
#	tests/data/test_item_description_placeholders.cpp
… names its tier's tables

The unused-table warning moves out of the placeholder check into
ValidateTableUses, run where that check is (Load, Save Items, bmd import).
It also warns about a wing whose description names other tables than most of
the other wings of its tier, cape tag and group: the tooltip code chose the
wings' damage and absorption lines by the tier, so a new wing without them, or
a re-tiered wing that keeps its old tier's tables, is pointed out.
…t are the game's

"Zwiększa prędkość" as the game's "Increase speed" text has it, and the
Imp's Portuguese text without its broken "de % of".

@sven-n sven-n left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the fixed-text second-chain migration in src/source/Engine/Object/ZzzInventory.cpp and the item description representation. No independently actionable defect identified in the inspected code. Please retain a before/after comparison for the affected fixed-text item cases, including localized output and line ordering; the later snapshot test in #822 can help cover these changes. Build not run.

Reviewed with AGENTS.md and docs/CODING_RULES.md as the quality baseline.

Mosch0512 and others added 14 commits October 23, 2026 19:15
…s own item table

Run in one process after a test that loaded the item data (the MU
Helper pickup filter tests do), the comparison test crashed: it
switches the UI language for every language, the item data follows
each switch and refills ItemAttribute, which is null outside a
ClientDataScope. CTest runs each test case in its own process, so CI
did not show it. The test now runs inside a ClientDataScope, as
ClientDataScope's comment asks of a test that switches the language.
… a tie, and checks a lone wing against the other groups

Following the review: a wing is compared by its lines (their texts, tables
included), so one that loses or gains a line without a table is pointed out
too; wings that split evenly get one warning that they don't agree, so neither
side is blamed; wings without lines count as a set of lines; and a wing alone
in its tier, cape tag and group is compared with its tier's wings in the other
groups that have more than one (Cape of Fighter and Cape of Lord, each alone,
aren't compared with each other). The warning is in the docs, and the stat
file test's comment names ValidateTableUses.
The wing check took the lines most wings of a tier have as the ones the
others are measured by, and wings without lines counted as one set of
lines. New wings imported without their lines (a bmd import gives new
items none), more of them than the wings with lines, then made "no
lines" the majority: the wings with the right lines were pointed out and
the new ones were not. Wings without lines no longer set the lines the
others are compared with, so the new ones are the ones pointed out; a
tie among the wings with lines is still reported as such.
They were read as an int before the range check, so a number like 2^32 + 1 wrapped into level 1.
… old code showed

item_chain_texts_before.json records them from the .resx of the commit before the move, a format filled in with the
item's values; the test compares each moved line in every language with them.
…wed them

Moving the texts into the item data changed them: "Maks. HP +100", "Zwiększa maks. HP +4%", "Szansa na obrażenia
doskonałe +10%" and the Master Level EXP line are the .resx translations the tooltip showed. The new translation test
found them.
@ezequielzacca

Copy link
Copy Markdown
Contributor Author

Thanks, and it was worth it: the new test found four Polish texts that this PR had changed while moving them. They're fixed in 31d7303 and back to what the old code showed: "Maks. HP +100", "Zwiększa maks. HP +4%", "Szansa na obrażenia doskonałe +10%" and the Master Level EXP line.

  • English, colors, order: "The second item chain's tooltip texts come from the descriptions as from the old code" already pinned the 72 items' English text, color and line order.
  • Translations: the new test, "The second item chain's tooltip texts keep their translations", covers them.
    • tests/data/item_chain_texts_before.json records what the old code showed in every language, taken from the .resx of the commit before this one.
    • Where the old code filled a format in, the fixture holds the translation filled in the same way: the item's number for "Automatic HP recovery %d%%", the translated map name for "Enables entrance into %s.".
    • The test compares each moved line in every recorded language: 1050 translations.
    • From feat(items): the descriptions' repeated texts are written once #827 on, it follows a line's shared text into the tooltip file, so those lines stay covered.
  • Not covered: three texts have no recorded translations, because the old code showed them in English only.

From #822 on, the snapshot test also records every item's tooltip, line order included, in eleven variants.

…or every language and the levels

Three Polish Fruits texts were from before main corrected them (17fc723): Dowodzenie, "Można użyć po zdjęciu
ekwipunku." and "Może być używany przez: Dark Lord". A test now compares the level lines whose texts other code still
prints with the game's texts in every language, another that a line is only written at its levels. The levels note
says the level variants' lines move into their items later.
…m-tooltip-chain-texts

# Conflicts:
#	tests/data/test_item_database_files.cpp
@Mosch0512
Mosch0512 merged commit 839715b into sven-n:main Oct 24, 2026
8 checks passed
ezequielzacca added a commit to ezequielzacca/MuMain that referenced this pull request Oct 25, 2026
… Archangel and the Command Fruit come from the data

The 30 cases of the second item chain that write texts with values move
into the items' descriptions, as their first lines: the quest items, the
event, Doppelganger and Varka tickets (with their map's name written in),
the elite potions, buff scrolls, elixirs and seals, the cherry blossom
items, the cash shop cards, the gamble items, the Wizard's Ring by level,
the wing-mix charms, the Secromicon items, the Chaos Castle and paid
channel tickets and the Heart. The reset fruits, the Talisman of
Resurrection, the socket seeds and spheres and the Sign of Dimensions
stay code: their lines need the game (reset points, the saved revive
place, the socket system, the signs still needed). Texts written more
than once are shared texts of the tooltip file; the .resx texts only
these cases used are gone.

The Weapon of Archangel's lines, a preview of the reward weapon its +
level stands for (+0 the staff, +1 the sword, +2 the crossbow: damage,
speed, requirements and options), are its description now, with the
sword's and crossbow's skill mana from the skill data.

"Can be equipped by Dark Lord" of the Command Fruits (+4) was white for
every hero; like every other line that names an item's classes, it is
white for a Dark Lord and dark red for the others now ("class:darkLord").

Tooltips that change: lines longer than the tooltip's width wrap, as
description lines do since sven-n#813 (the tickets', seals' and Secromicon
papers' long lines, the Weapon of Archangel's longest); the cash shop
cards and cherry blossom branches lose the empty full-height line they
wrote for a text no .resx has (legacy ids 2535-2537 and 2551-2556); the
Master Seal of Ascension shows its own lines again without the wing-mix
charm's, which it got when sven-n#820 moved its case out of the chain ahead of
the charms' (its number is in the charms' range). The Weapon of
Archangel's speed and requirement lines above +2, which the item never
has, showed uninitialized values; they're gone.

The tooltip snapshot records eleven variants per item (plain, full,
ancient, harmony, each option alone, worn, broken and expired) and blank
lines without color or style: they show none, and what their slot kept of
an earlier tooltip made the recorded hashes change with unrelated
tooltips.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants