Repository navigation
feat(items): the tooltip's durability lines come from the data - #825
ezequielzacca wants to merge 74 commits into
Conversation
The tooltip's damage, defense, magic defense, defense rate, attack and
walk speed, resistance and requirement lines ("(lacking ...)" included),
and the staves', books' and magic swords' wizardry or curse damage
increase and the sceptres' pet attack increase, are data now:
Data/Items/Tooltips/Tooltip.json holds their texts with translations and
the layout "default", which every item uses. RenderItemInfo writes the
layout's lines between the parts that stay code: the name, the item's own
texts and durability, the classes, and the options.
A line shows when every entry of its "when" holds: a built-in value that
is not 0, a state (excellent, twoHanded, skillBook, ...), or an item tag
("tag:sceptre"); "!" turns an entry around. Its color is the first of its
color rules that holds, else its own; "values" binds a placeholder of the
text for the line, so one "(lacking {missing})" serves every requirement.
Unknown texts, conditions, tags, colors and placeholders, translations
with other placeholders, and misplaced sections are load errors, with the
name meant suggested for a typo.
Two item checks of the code are tags: noDamageLine (the Scrolls of
Teleport, Teleport Ally and Soul Barrier) and noLevelRequirement (Loch's
Feather).
The recorded tooltips change for 11 items, both from the old damage
block's TextNum-- when it skipped the line: the eight skill scrolls whose
skill has no damage keep the blank line under their name now, and the
excellent teleport scrolls no longer color that blank line.
The .resx texts only these lines used are gone; the ones other windows
use stay.
Every item's recorded tooltip was at full durability and not timed, so the durability lines of a stack's count (potions and jewels have no durability in their data) and the lines a timed item leaves out were never recorded. Two more variants record them: "count", at a durability of 3, and "timed", an expired timed item (one that is not expired shows the time it expires, which depends on the clock). The hashes are recorded again for the six variants; the tooltip code is unchanged.
The standard durability line, "Durability: [{durability}/{maxDurability}]",
is a line of the default tooltip layout: shown for an item with a
durability (the old rule, now the state hasDurability) that is not timed
and has no tag noDurabilityLine. The other durability lines are lines of
the items' descriptions: a stack's "Number of items", a pet's "Life", a
ticket's "usable ... times", the Splinter of Armor's "{durability} / 20",
the Transformation Ring's, the Talisman of Luck's, the Varka tickets' and
the Talisman of Mobility's texts, the wing-mix charms' texts and the
lucky item tickets' text. The 57 items with a durability but not the
standard line have the tag noDurabilityLine; the condition with about 20
exceptions is gone.
Description lines take a shared text of the tooltip file ("@life") and
conditions ("when", as a layout line's). A condition can compare a value
with a whole number: "durability=2", "level!=1". A description's shared
text that the tooltip file does not have, or that has a placeholder a
description cannot fill, is a load error; a shared text that neither a
layout nor a description uses is a warning.
Only the Talisman of Mobility's saved location stays code, under its
description's line.
The recorded tooltips of every item, now also with a count and timed,
are unchanged except for the 10 lucky item tickets: the old code wrote an
empty full-height line after their blank line, and they end with the
blank line now.
The .resx texts only these lines used are gone, as is IsCharacterCard,
which nothing calls now.
The tickets' "usable %dtimes" ran the number into the word ("usable
10times") in English, Spanish and Polish, left "times" in English in
Russian and Tagalog, and read "time 10 that can be used" in Indonesian.
The shared text of the tooltip file and the .resx text, which the Chaos
Castle ticket and the Santa Village invitation still use, are fixed in
those languages.
…oltip-layouts # Conflicts: # src/Localization/Game.pl.resx
…ooltip-durability
…ooltip-durability # Conflicts: # tests/data/item_tooltip_snapshot.txt # tests/data/test_item_tooltips.cpp
Main dropped the 38 "-J" items and renamed the elf (x,50) and Dark Lord lucky (x,66) sets, so the snapshot records 911 items and their new tooltips.
…ooltip-durability # Conflicts: # docs/control-socket.md
… line never writes an empty row As the review of sven-n#813 asked for description texts: - Tooltip.json's texts (shared texts and layout lines' own texts) get the checks item names get: the English text is required (a blank line has none), a language code has only letters, digits and "-", and a translation in a language the game has no texts in is a warning (ValidateTooltipTextLocales, when the item data loads). - A layout line whose text comes out empty in the UI's language writes the tooltip's blank row ("\n"): an empty row ends the tooltip. Its row width comes from TextList instead of a literal 99.
…ooltip-durability # Conflicts: # docs/item-data.md # src/source/Engine/Object/ZzzInventory.cpp
…ooltip-durability
…names stay English As for the description: sven-n#843 makes the item database's display locale the item names' language, so the stat and requirement lines turned English with the names when "Translate item names" is off. They are read in the UI's language now.
…ooltip-durability # Conflicts: # docs/item-data.md # src/source/Engine/Object/ZzzInventory.cpp
…ooltip-durability
…ooltip-durability
|
Thanks, fixed in 8a2fea0. #822 now records eleven snapshot variants, after review there. They cover this PR's |
…ooltip-durability
…translation's placeholders and another layout
…ooltip-durability
…ooltip-durability
…ooltip-durability
…ooltip-durability # Conflicts: # docs/item-data.md
…ooltip-durability
…ooltip-durability
sven-n#824 merged as a rebuilt history, so the conflicts were resolved by applying this PR's own changes to main: the layout states it moved to ItemTooltipConditions now include sven-n#824's strengthRequired/dexterityRequired; the Talisman of Mobility line it moved out of the durability block keeps main's IsPortalPositionSaved() guard; the layout test keeps sven-n#824's sceptre tag case (noDamageLine is gone) next to this PR's comparison checks; the item JSON layout keeps main's LayOutItemJsonText with this PR's "when" lists.
|
#824 is merged, so I merged main into this branch (a normal merge commit, no history rewrite, since this PR has a review). #824 landed as a rebuilt history, so I resolved the conflicts by applying this PR's own changes onto main:
Checked: Windows x64 Release with the editor; all 1482 tests pass, and the tooltip snapshot is unchanged. |
Mosch0512
left a comment
There was a problem hiding this comment.
Verdict: changes requested for one item; the rest is non-blocking. Moving the durability block into data is right:
-
Each old branch has its replacement. I mapped every branch of the old block to the new data:
- the standard line and its
hasDurabilitystate, which is the oldbDurExistrule with the same precedence; - the 57
noDurabilityLineitems; - the description lines of the potions, arrows, pets, tickets, charms and talismans.
The exclusions without a tag (Loch's Feather, the fruits, the character cards, the elixirs) show no line either, since they have no durability in their data or are outside groups 0 to 11 and 13.
- the standard line and its
-
The Talisman of Mobility's saved location keeps its place. Nothing is written between the description and the new code for that item, and main's
IsPortalPositionSaved()guard is kept. -
The merge with main keeps #824's fixes. The Polish texts, the removed
noDamageLineand locale helpers,strengthRequired/dexterityRequiredand main's formatting of the #824 blocks are all still there. -
The removed
.resxids are safe. Nothing else looks up 95, 2296, 2605, 2606, 3105 or 3106, by name or by a computed id.
Required
- Three items lose their Japanese counter (inline, Group13_Helper.json:1434).
Non-blocking
- Long translated lines now wrap (inline, Group13_Helper.json:2979).
- Two comments say a condition can read a stat file table again (inline, ItemTooltipConditions.h:24).
- Untouched blocks of
ZzzInventory.cppare reformatted, and its BOM is dropped (inline, ZzzInventory.cpp:3201). - The description reader copies the layout reader's condition loop (inline, ItemJsonFormat.cpp:464).
"@"alone is a silent blank line in a description (inline, ItemJsonFormat.cpp:430).- A table used only by a shared text isn't seen by the table checks (inline, ItemDataValidation.cpp:639).
- The old-code comparison tests now skip every line with a condition (inline, test_item_database_files.cpp:547).
Before merging (not code)
- The PR text says Polish ran the number into "times". Main's Polish
.resxalready reads "do użycia %d razy", and the Polish tooltips don't change here.
What I checked:
- every branch of the old durability block against the new data and conditions;
- the merge resolution against main's #824 (the files #824 touched and its review fixes);
- every
Lookupby number or computed id for the removed ids; - the editor code that reads or copies item descriptions (nothing drops
textNameorwhen).
I also compared every item's tooltips in all 11 languages: a local test (not pushed) wrote each item's tooltips in each language for the new Dark Wizard, 176 per item, built once on main and once on this branch. The full text of each differing item showed:
- the "usable … times" fix in English, Spanish, Indonesian, Russian and Tagalog, and the lucky tickets, as described;
- the two findings below on Japanese and on wrapping;
- no other change in any language.
Built on Windows x64 Debug with the editor and the control socket: test_item_database_files passes whole, 222 of 222, in file order and three random orders, and so do test_item_database, test_control_tooltip_commands and test_control_protocol. I didn't run the game.
| "description": [ | ||
| { | ||
| "text": { | ||
| "en": "{durability} / 20" |
There was a problem hiding this comment.
Required. The Splinter of Armor, the Bless of Guardian (line 1464) and the Claw of Beast (line 1494) now have only an English text, "{durability} / 20" (/ 10). The old code printed I18N::Game::DD, which is "%d / %d" in every language but Japanese, where it's "%d個/ %d個". With the UI in Japanese, the tooltip changes:
main: 1個/ 20個
here: 1 / 20
Adding "ja": "{durability}個/ 20個" (and / 10個) to the three texts keeps it. The recorded snapshot runs in English, so no test sees it. These three are the only new in-place texts without translations; the other 23 have all 11 languages.
There was a problem hiding this comment.
Fixed in c4b2e84: the three texts have "ja": "{durability}個/ 20個" (/ 10個 for the Claw of Beast), the old I18N::Game::DD's Japanese form. Other languages read the English text, which is the same %d / %d as before.
| }, | ||
| { | ||
| "text": { | ||
| "en": "Saves the location with the right-mouse-click.", |
There was a problem hiding this comment.
Non-blocking. Description lines wrap at the description width, while the old code wrote each of these texts as one row. So long translations now take two rows:
- the Talisman of Mobility's two texts in Polish, Russian, Tagalog and Ukrainian;
- the Master Seal of Ascension's (13/93) text in Polish;
- the lucky tickets' text in German.
From the language comparison, Polish:
main: Zapisuje lokalizację po kliknięciu prawym przyciskiem myszy.
here: Zapisuje lokalizację po kliknięciu prawym
przyciskiem myszy.
English is short enough not to wrap, so the snapshot doesn't show it. Wrapping is how every other description behaves, so it's probably fine. But the PR says every other tooltip is unchanged, so it's worth listing.
There was a problem hiding this comment.
Listed in the PR text now, under the tooltips that changed: the Talisman of Mobility in Polish, Russian, Tagalog and Ukrainian, the Master Seal of Ascension in Polish, and the lucky tickets in German. I kept the wrapping, since it's how every other description behaves.
| { | ||
| enum class Kind : unsigned char | ||
| { | ||
| Value, // a built-in value (or {level}, or a table of the item stat file), compared with `number` |
There was a problem hiding this comment.
Non-blocking. This says a value condition can be "a table of the item stat file", and so does AreTooltipConditionsMet's comment (ItemTooltipValues.h:83). #824's review removed that claim, because a condition can't name a table. Here, ReadTooltipCondition stops the name at the : of "table:x", so it's refused with "compares no built-in value". The merge brought the old wording back.
| else | ||
| if (Level == 1) | ||
| TextListColor[TextNum] = TEXT_COLOR_RED; | ||
| else if (Level == 1) |
There was a problem hiding this comment.
Non-blocking. Three blocks this PR doesn't otherwise change are reformatted:
- the chocolate boxes (3198–3211);
- the Box of Luck (3216–3229);
- the Armor of Guardsman's Chaos Castle table (3278–3315).
Line 1 also loses the file's UTF-8 BOM. With /utf-8 on MuClient the Korean literals still compile the same, so it's harmless. But these blocks add unrelated lines to the diff, as the ones #824 reverted did.
There was a problem hiding this comment.
Fixed in c4b2e84: the chocolate boxes, the Box of Luck and the Armor of Guardsman's Chaos Castle table are back to main's formatting, and the BOM is back, so the file's diff is only this PR's change.
| } | ||
|
|
||
| // The conditions a description line is shown on, all of them: a list of texts such as "durability" or "!timed". | ||
| void ItemReader::ReadDescriptionConditions(const OrderedJson& json, const std::string& field, ItemDescriptionLine& line) |
There was a problem hiding this comment.
Non-blocking. ReadDescriptionConditions is the same loop as ItemTooltipReader::ReadConditions: a list check, a string check per entry, ReadTooltipCondition, push. Only the error messages differ slightly. A shared ReadTooltipConditions(json, field, conditions, addError) next to ReadTooltipCondition would keep the two readers from drifting.
There was a problem hiding this comment.
Done in c4b2e84: ReadTooltipConditions sits next to ReadTooltipCondition (in ItemTooltipConditions.cpp, declared in a small ItemTooltipConditionsJson.h so the headers that include the conditions don't take in the JSON library). Both readers call it; the error texts are unchanged.
| [this](const std::string& errorField, const std::string& message) | ||
| { AddIssue(ItemDataIssueSeverity::Error, errorField, message); }); | ||
| bool read = true; | ||
| if (text->is_string() && text->get<std::string>().starts_with("@")) |
There was a problem hiding this comment.
Non-blocking. A description text of just "@" gives an empty textName:
ValidateDescriptionTextsskips empty names;- the tooltip writes the line as a blank line.
So a typo like {"text": "@"} loads without a message. In a layout, the same text is an error ("@" is not a text of "texts"). An empty name after the @ could be the same error here.
There was a problem hiding this comment.
Fixed in c4b2e84: "@" alone is an error now ("names no text"), as in a layout. A test covers it and a named one.
| const PlaceholderScan scan = ScanPlaceholders(text->GetNeutral()); | ||
| for (const std::string& placeholder : scan.names) | ||
| { | ||
| if (!IsBuiltInPlaceholder(placeholder) && !GetPlaceholderValue(placeholder, 0, profiles)) |
There was a problem hiding this comment.
Non-blocking. A shared text used by a description may have a {table:…} placeholder; this check allows it. But the table checks only look at the descriptions' own texts:
ValidateTableUseswarns about the table as unused;- the editor's table counts (
DescriptionNamesTable,FindTableDescriptionUsers) don't count it, so the Formulas window offers to delete it; RenameTableInDescriptionsleaves the shared text with the old name, which makes the next load fail.
No shipped shared text uses a table today. Refusing tables in shared texts would keep that true until the table checks know about them.
There was a problem hiding this comment.
Done in c4b2e84, as you suggest: a description's shared text takes built-in values only, with an error saying a table goes in the item's own text, where the table checks see it. ValidateDescriptionTexts doesn't need the stat profiles anymore, so it lost that parameter. The shared-text test checks a table.
| { | ||
| if (line.IsShownAt(level)) | ||
| // Not the durability lines, which came from another part of the old code. | ||
| if (line.IsShownAt(level) && line.textName.empty() && line.when.empty()) |
There was a problem hiding this comment.
Non-blocking. Both old-code comparison tests now skip every description line with a when or a shared text (also line 409). Today those are exactly the durability lines. But a later non-durability line with a condition would drop out of the comparison silently. Skipping by what the line is (the shared texts and the durability conditions this PR adds) would keep the tests' reach.
There was a problem hiding this comment.
Fixed in c4b2e84: both tests skip by what the line is: IsDurabilityLine is a shared text or a line shown by the item's durability (durability, durability=2, ...). A later line with another condition stays in the comparison.
- The Splinter of Armor, the Bless of Guardian and the Claw of Beast keep their
Japanese counter ("{durability}個/ 20個"), as the old I18N::Game::DD had it.
- The condition comments no longer say a condition can name a stat file table.
- ZzzInventory.cpp's untouched blocks are back to main's formatting, with its
BOM.
- The item and tooltip readers share ReadTooltipConditions
(ItemTooltipConditionsJson.h).
- A description's "@" alone is an error, as in a layout, not a blank line.
- A description's shared text takes built-in values only: the table checks
don't see a shared text, so a table there would go unseen by the unused-table
warning and the editor's counts and renames. ValidateDescriptionTexts drops
its stat profiles.
- The old-code comparison tests skip just the durability lines (a shared text,
or a line shown by the item's durability), not every line with a condition.
|
Thanks for the thorough review. Pushed c4b2e84 as a new commit on top (no history rewrite):
|
This step's own changes: compare with #824. 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 5b-3b of #815 (data-driven item tooltips; design). Stacked on #824 (→ #823 → … → #813): only the last three commits are this step.
The tooltip's durability block is data now. It was one condition with about 20 item exceptions, followed by a chain of 25 branches by item type, and it's down to one line of code: the Talisman of Mobility's saved location.
The commits
test(items): the tooltip snapshot also records a count and a timed item, before anything moves. "Every item's tooltip is as recorded" only recorded full durability and items that aren't timed, which missed the branches this step moves. Potions have no durability in their data, so their count was never shown, and a pet's "Life" line is hidden when it's timed. This commit added two variants,count(durability 3) andtimed(expired). After review, test(items): every item's tooltip in a test, against recorded hashes #822 records eleven variants that cover both (durability 1 and 0, andexpired), so they're merged into those: the snapshot here is recorded with the eleven.feat(items): the tooltip's durability lines come from the data: the move. Against commit 1, every tooltip is unchanged except the 10 lucky item tickets (below). That was 91,104 tooltips per hero with the six variants of then, both in the test and in the game'stooltip-snapshot; with test(items): every item's tooltip in a test, against recorded hashes #822's eleven variants (160,336 per hero), the full tooltip text against feat(items): the tooltip's stat lines come from a tooltip layout #824 changes only for the 10 lucky item tickets and, from commit 3, the nine tickets with "usable 10 times".fix(items): "usable 10 times", with its space and translated: a text fix, re-recorded on its own.How
The standard line is in the default layout:
{"text": "@durability", "when": ["hasDurability", "!timed", "!tag:noDurabilityLine"]}hasDurabilityis the old rule as a state: groups 0–11 and 13 with a durability, plus the wings and capes 12/0–6, 12/36–43 and 12/49–50.The other lines are in the items' own descriptions, which come right before the durability block. So each line keeps its place:
{"text": "@numberOfItems", "when": ["durability"]}{"text": "@numberOfItems"}{"text": "@life", "when": ["!timed"]}{"text": "@usableTimes"}; the Varka tickets add their weekday line{"text": {"en": "{durability} / 20"}}(/ 10){"text": "@durability", "when": ["!timed", "level!=1", "level!=2"]}@durabilitywhen!timed,@usableTimeswhentimed@numberOfItemsatlevels: [3, 3]when: ["durability=2"]/["durability=1"]noDurabilityLine: this tag replaces the old exception list. 57 items have a durability but not the standard line, and each one is now visible in its own data.New in the data
Tooltip.json("@life") and conditions ("when", as a layout line's)."durability=2","level!=1", with=,!=,<,<=,>,>=against a whole number. Comparing a state, or a typo, is a load error.hasDurability,timed.noDurabilityLine.Tooltips that changed, on purpose
The 10 lucky item tickets: after their blue text and blank line, the old code wrote one more line, left empty. The tooltip stops drawing at an empty line (
RenderTipTextList(), and the modern theme too), and only blank lines came after it, so the tooltip looks the same; the recorded lines lose the empty one. (Corrected: this said "full height, half a line shorter" before; #830 found that the drawing stops there.) Recorded lines,<color> <text>:"usable %dtimes" (commit 3):
They're fixed in
Tooltip.jsonand in the .resx, which the Chaos Castle ticket and the Santa Village invitation still use.Wrapping (commit 2): a description line wraps at the description's width, while the old code wrote each of these texts as one row. So a few long translations now take two rows, as every other description does:
English is short enough not to wrap, so the recorded snapshot (in English) doesn't show it.
Removed
The .resx texts only the durability block used:
Durability: [%d]%d%% Combination success rate increaseThe rest stay: other code uses them, including the charms' numbered texts, which the second item chain also reads (feat(items): the second item chain's cases with values come from the data #829 moves that one).
IsCharacterCard(), which nothing calls now.Not done
"when": ["durability=1"], but each language needs its own form. That's for a separate change if wanted.matchrules, with a per-item"tooltip"override, aren't needed. A layout per item kind would have repeated the 36 stat lines, while per-item description lines with conditions cover every case. The design comment on [Architecture] Data-driven item tooltips: texts, placeholders and values from the item data (phase 8) #815 is updated.Screenshots
Before (#824 plus the snapshot commit) and after this PR, for a level 400 Dark Knight. They look the same, except the Doppelganger ticket's text fix. The lucky item tickets, the one other visible change, aren't in OpenMU's Season 6 item data, so they're shown as their recorded lines below instead.
Large Healing Potion ×3
A stack's count, from its description (
@numberOfItems,when: [durability]).The item's data (number, name, tags, description)
{ "number": 3, "name": { "en": "Large Healing Potion" }, "tags": [ "healingPotion" ], "description": [ { "text": "@numberOfItems", "when": [ "durability" ] } ] }Its shared texts in
Tooltip.json(English):Guardian Angel
A pet's life after its step-4 lines (
@life,when: [!timed]), with the tagnoDurabilityLine.The item's data (number, name, tags, description)
{ "number": 0, "name": { "en": "Guardian Angel" }, "tags": [ "noDurabilityLine" ], "description": [ { "text": { "en": "Absorb 20% of Damage", "de": "Absorbiert 20% des Schadens", "es": "Absorber daño 20% of", "id": "Menyerap Kerusakan 20% of", "ja": "ダメージ 20%吸収", "pl": "Pochłania 20% obrażeń", "pt": "Absorver Dano 20% of", "ru": "Поглощение 20% of урона", "tl": "Isipsip ang 20% of Pinsala", "uk": "Поглинання шкоди 20% of", "zh-TW": "吸收20% of傷害" } }, { "text": { "en": "Max HP +50 increased", "de": "Max. HP +50 erhöht", "es": "HP máximo +50 aumentado", "id": "HP Maks +50 meningkat", "ja": "最大生命力 +50増加", "pl": "Zwiększono maksymalne HP +50", "pt": "HP máximo +50 aumentado", "ru": "Макс. HP +50 увеличен.", "tl": "Tumaas ang Max HP +50", "uk": "Макс HP +50 збільшено", "zh-TW": "最大生命值+50增加" } }, { "text": "@life", "when": [ "!timed" ] } ] }Its shared texts in
Tooltip.json(English):Splinter of Armor at 7
Pieces to collect, a text written in place:
{durability} / 20.The item's data (number, name, tags, description)
{ "number": 32, "name": { "en": "Splinter of Armor" }, "tags": [ "noDurabilityLine" ], "description": [ { "text": { "en": "{durability} / 20" } } ] }Open Access Ticket to Doppelganger
@usableTimes, which reads "usable 1 times" now instead of "usable 1times" (the fix commit).The item's data (number, name, tags, description)
{ "number": 125, "name": { "en": "Open Access Ticket to Doppelganger" }, "tags": [ "cashShop", "noDurabilityLine" ], "description": [ { "text": "@usableTimes" } ] }Its shared texts in
Tooltip.json(English):Related issues
Step 5b-3b of #815. Builds on #824, #823, #822, #821, #820, #819, #818, #817, #816 and #813.
Checklist
docs/CODING_RULES.md.docs/build/README.md).tooltip-snapshotof two heroes before and after, and the screenshots above in both themes.