forked from sven-n/MuMain
-
Notifications
You must be signed in to change notification settings - Fork 0
feat(items): item effects from the model files (phase 4c3) #93
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+1,882
−725
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
173808e
docs(items): looks in data and the look editor (D25, phase 13)
Mosch0512 6a447df
docs(items): what belongs together beyond items, shared definitions (…
Mosch0512 ba8dba0
docs: roadmap for data-driven content beyond items
Mosch0512 e83ec02
feat(items): item effects from the model files (phase 4c3)
Mosch0512 121e0c4
fix(items): review of the item effects (phase 4c3)
Mosch0512 2e118d8
refactor(items): name the item effects itemEffect (phase 4c3)
Mosch0512 9fe1c06
fix(items): second review of the item effects (phase 4c3)
Mosch0512 aa6eb9f
fix(items): event items glow by their level (phase 4c3)
Mosch0512 283d632
fix(items): the Devil's Square items glow in pairs of levels (phase 4c3)
Mosch0512 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
90 changes: 77 additions & 13 deletions
90
docs/superpowers/specs/2026-09-25-data-driven-items-design.md
Large diffs are not rendered by default.
Oops, something went wrong.
112 changes: 112 additions & 0 deletions
112
docs/superpowers/specs/2026-09-29-data-driven-content-roadmap-design.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| # Data-Driven Content Roadmap (WIP) | ||
|
|
||
| > **Working document.** It keeps the order of the data-driven refactorings | ||
| > that go beyond one area, the decisions they share, and the editor parts | ||
| > they share. Each area has its own design document with its own phases; | ||
| > this document links them and stays while areas are open. | ||
| > | ||
| > Started from the items design | ||
| > ([2026-09-25-data-driven-items-design.md](2026-09-25-data-driven-items-design.md)), | ||
| > based on MuMain `upstream/main` @ `e4831483`. | ||
|
|
||
| ## Goal | ||
|
|
||
| Content that is hardcoded in the client today (items and their looks, | ||
| effects and particles, skills, monsters and NPCs, ...) becomes data that | ||
| can be changed and edited in MuEditor without code. What belongs together | ||
| is planned together: items, skills and monsters all use the effect code, | ||
| so they share one way to describe looks and one set of editor parts. | ||
| Nothing gets slower in game: names and values are resolved when loading. | ||
|
|
||
| ## Why these areas belong together | ||
|
|
||
| Items are not the only users of the effect code. About 3,950 calls create | ||
| effects, particles, lightning and sprites: about 1,600 for the maps and | ||
| their monsters (`World/GameMaps`), 1,100 for characters, objects and items | ||
| (`Engine`), 760 inside the effects themselves, 290 for skills, combat, | ||
| pets and events (`GameLogic`) and 140 for skill results from the server | ||
| (`Network`). The effect code knows about 440 effect types, 100 particle | ||
| types and 30 lightning types; `Render/Effects/EffectRegistry` has started | ||
| to describe them as a table instead of three large switches. Skills are | ||
| still read from `Skill.bmd` (with a table editor, as items had before | ||
| their phase 2), and monsters and NPCs are set up in a switch of 403 cases | ||
| in `ZzzCharacter.cpp`. | ||
|
|
||
| ## Shared decisions | ||
|
|
||
| These decisions were made in the items design; they keep their numbers, | ||
| so the references there stay valid. | ||
|
|
||
| | # | Topic | Decision | | ||
| |---|---|---| | ||
| | D25 | Looks in data | Items, skills, monsters and NPCs reference their looks by name (for items: the render style and the item effect; the glow colors already are names in data). What a named look is made of moves from code into data files in `Data/Effects/`: a list of building blocks, namely draw passes (mesh or body, flags, texture, color, alpha, texture scrolling), things placed on bones (sprites, particles, lightning between two bones, effects), animated object values (glow mesh brightness, hidden mesh, texture scrolling), timing (pulses, random chances per frame) and conditions (item level, doppelganger, ...). The drawing code runs these lists; data that names looks stays valid when their definitions move. One look format and one look editor for all areas: a look made for an item can be used by a monster. Effect and particle types are referenced by the names of the effect catalogue (FX1) and become data themselves later (FX2). | | ||
| | D26 | Shared definitions | Anything that several things use is defined once, with a name, in a data file, and its users reference that name: glow colors, looks, effect types, skills. Editors show where a definition is used (items, skills, monsters, NPCs, other effects). Saving a change to a definition that others use first shows the list of those users; a copy makes a variant for one user; renaming updates all references; a definition that is still used cannot be deleted (the list shows why). Unknown names are reported when loading. Names are resolved when loading, so drawing and game logic never look names up. Every move from code into data is compared with the old code (recorder) before the old code goes. | | ||
|
|
||
| ## Bones | ||
|
|
||
| Effects place sprites, particles and lightning on bones of a model, so | ||
| they follow its animation: the lights of a wing move as it flaps, the | ||
| casting effect of a skill sits in the caster's hand. Looks store bones as | ||
| numbers, as the code does, and the model loader checks that they exist. | ||
|
|
||
| - **Item models:** the names in the `.bmd` files are export names that do | ||
| not help to find a bone. Of the 778 item model files, 340 have the | ||
| biped skeleton with names like "Bip01 L Hand", 249 only names like | ||
| `Bone01`, `Box02` or `zx12`, in 396 the bone called "BoneNN" is not bone | ||
| NN, and one file has a name twice. Some models have helper bones placed | ||
| as effect points (the 22 blue lights of the Wing of Eternal sit on the | ||
| bones `zx01` to `zx22`). | ||
| - **Characters:** skills place effects mostly on the caster's bones | ||
| (hands, weapon), and effects follow their owner's bones. Characters have | ||
| the biped skeleton, whose names are readable ("Bip01 R Hand"). | ||
|
|
||
| The look editor shows numbers and names together and picks bones by | ||
| clicking them. | ||
|
|
||
| ## Areas and order | ||
|
|
||
| | Area | Name | Design document | Depends on | What moves into data | Editor | | ||
| |---|---|---|---|---|---| | ||
| | Items | Items | [items design](2026-09-25-data-driven-items-design.md), phases 0–14 | – | Item data, rules and categories, models, glow, render styles and effects (as names). | The item tools of section 9 there. | | ||
| | FX1 | Effect catalogue | not yet | items 4c | Every effect, particle, lightning and sprite type gets a name and its creation values (the `CreateParams` of the registry) in `Data/Effects/`: the effect types (`effectTypes` in the data). Behavior stays code. Items, skills and monsters then name effect types instead of using type numbers; an item effect (`itemEffect`, what an item does every frame before it is drawn) creates instances of effect types. | Effect browser: each effect with a preview, its values, and where it is used. | | ||
| | Items 13 | Looks in data (items) | items design, phase 13 | FX1, items 6 | What the item looks are made of (D25). | Look editor. | | ||
| | SK1 | Skills as data | not yet | items 2 (the same format rules) | `Skill.bmd` into JSON, like the items in their phase 2: names, requirements, rules; later synced with OpenMU like the items. | Focused skill editors, like the item tools; "used by" (classes, items that give a skill, monsters). | | ||
| | SK2 | Skill looks | not yet | SK1, FX1, items 13 | How a skill looks when cast, flying and hitting (effects, particles, sounds, character animations), as looks (D25), mostly on the caster's bones. | Look editor, with a preview of the skill cast by a test character. | | ||
| | MN | Monsters and NPCs | not yet | FX1, items 13, SK1 | The monster and NPC setup (model, size, the equipment of player-shaped NPCs), their looks (glow, render styles and effects as in D25; the plate of the helper NPCs, `helperNpcPlate`, moves from the items to them; the player transformations) and their skills. | Monster and NPC editors with preview; the look editor. | | ||
| | FX2 | Effect behavior as data | not yet | FX1 | How effects and particles move, fade, spawn and draw, as building blocks, one effect family at a time (continuing the handlers of the registry). New effects without code. | Effect editor with a live preview. | | ||
| | later | Map objects, buffs, pets, sounds | not yet | FX1 | To decide: the objects of the maps with their effects (most of `World/GameMaps`), buff visuals, pets, sound names. | – | | ||
|
|
||
| - **The effect catalogue comes first.** Its names are what the looks of | ||
| items (items 13), skills (SK2) and monsters (MN) reference. It moves no | ||
| behavior, so it is small and can be checked like the render styles. | ||
| - **Skills before monsters,** because monsters use skills. | ||
| - **Effect behavior comes last.** Once everything references effects by | ||
| name, their behavior can move into data without touching their users. | ||
| - **Map objects later,** although the maps are the largest users of | ||
| effects: they only need the effect catalogue, not the looks of items, | ||
| skills or monsters. | ||
|
|
||
| ## Shared editor parts | ||
|
|
||
| The editors of all areas follow D26: focused tools that share one | ||
| selection, "used by" lists, a warning with the list of users before a | ||
| shared definition changes, copies for variants. These parts are shared | ||
| and opened from the item, skill, monster and NPC editors: | ||
|
|
||
| - **Look editor** (from items 13 on): edits a named look (D25) as its | ||
| list of building blocks, with a live preview of the model and its | ||
| animations (for skills: cast by a test character). Shows the skeleton | ||
| with every bone's number and name, highlights the bones the look uses, | ||
| and picks bones by clicking them. Lists all users of the look; saving a | ||
| change to a look that others use shows a warning with that list. A new | ||
| look starts as a copy of another. | ||
| - **Effect browser** (FX1) and **effect editor** (FX2): an effect alone in | ||
| a preview, its values, and where it is used. | ||
|
|
||
| ## When an area starts | ||
|
|
||
| Its design document is written from the code first, like the items design | ||
| (current state, decisions, phases), linked in the table above, and this | ||
| document is updated. The default verification is the recorder method of | ||
| the items design: the old and the new code are run for every model, setup | ||
| and level, and what they draw and spawn is compared. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,150 @@ | ||
| #include "stdafx.h" | ||
|
|
||
| #ifdef _EDITOR | ||
|
|
||
| #include "ItemEditorLooks.h" | ||
|
|
||
| #include "Data/GameData/ItemData/ItemDatabase.h" | ||
| #include "Data/GameData/ItemData/ItemModelDatabase.h" | ||
| #include "Data/GameData/ItemData/ItemModelGlowJson.h" | ||
| #include "I18N/All.h" | ||
| #include "imgui.h" | ||
|
|
||
| #include <algorithm> | ||
| #include <span> | ||
| #include <string> | ||
| #include <utility> | ||
| #include <vector> | ||
|
|
||
| namespace | ||
| { | ||
| using Data::Items::ItemModelDefinition; | ||
| using LookName = std::string ItemModelDefinition::*; | ||
|
|
||
| // At most this many items are listed at once; the list scrolls. | ||
| constexpr int MaxListedRows = 8; | ||
|
|
||
| struct Look | ||
| { | ||
| std::string name; | ||
| // The items that use the look: item type and log name. | ||
| std::vector<std::pair<int, std::string>> users; | ||
| }; | ||
|
|
||
| // What the section shows for the selected item, built when the selection, the | ||
| // item model data or the items (their names in the lists) change, not every | ||
| // frame. | ||
| struct LooksCache | ||
| { | ||
| int itemType = -1; | ||
| int modelDatabaseVersion = -1; | ||
| int itemDatabaseVersion = -1; | ||
| std::string glow; | ||
| Look renderStyle; | ||
| Look itemEffect; | ||
| }; | ||
|
|
||
| LooksCache g_cache; | ||
|
|
||
| // The glow values as the model file has them, or "-" without any. | ||
| std::string DescribeGlow(const ItemModelDefinition& model) | ||
| { | ||
| Data::Items::Json::OrderedJson json = Data::Items::Json::OrderedJson::object(); | ||
| Data::Items::GlowJson::Write(model, json); | ||
| const auto glow = json.find(Data::Items::GlowJson::GlowKey); | ||
| return glow != json.end() ? glow->dump() : "-"; | ||
| } | ||
|
|
||
| Look TakeLook(const ItemModelDefinition& model, LookName look) | ||
| { | ||
| Look result{model.*look, {}}; | ||
| if (result.name.empty()) | ||
| { | ||
| return result; | ||
| } | ||
| const std::span<const ItemModelDefinition> models = g_ItemModelDatabase.GetAllSlots(); | ||
| for (int itemType = 0; itemType < static_cast<int>(models.size()); ++itemType) | ||
| { | ||
| if (models[itemType].Exists() && models[itemType].*look == result.name) | ||
| { | ||
| result.users.emplace_back(itemType, g_ItemDatabase.GetLogName(itemType)); | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| void Refresh(int itemType, const ItemModelDefinition& model) | ||
| { | ||
| const int modelDatabaseVersion = g_ItemModelDatabase.GetVersion(); | ||
| const int itemDatabaseVersion = g_ItemDatabase.GetVersion(); | ||
| if (g_cache.itemType == itemType && g_cache.modelDatabaseVersion == modelDatabaseVersion && | ||
| g_cache.itemDatabaseVersion == itemDatabaseVersion) | ||
| { | ||
| return; | ||
| } | ||
| g_cache.itemType = itemType; | ||
| g_cache.modelDatabaseVersion = modelDatabaseVersion; | ||
| g_cache.itemDatabaseVersion = itemDatabaseVersion; | ||
| g_cache.glow = DescribeGlow(model); | ||
| g_cache.renderStyle = TakeLook(model, &ItemModelDefinition::renderStyle); | ||
| g_cache.itemEffect = TakeLook(model, &ItemModelDefinition::itemEffect); | ||
| } | ||
|
|
||
| // A look with the number of items that use it; opened, the list of those | ||
| // items in a box of fixed height. Returns the item clicked, -1 for none. | ||
| int RenderLook(const char* label, const Look& look) | ||
| { | ||
| if (look.name.empty()) | ||
| { | ||
| ImGui::Text("%s: -", label); | ||
| return -1; | ||
| } | ||
|
|
||
| int clicked = -1; | ||
| ImGui::PushID(label); | ||
| if (ImGui::TreeNode("look", "%s: %s (%s %d)", label, look.name.c_str(), I18N::Editor::UsedBy, | ||
| static_cast<int>(look.users.size()))) | ||
| { | ||
| const int rows = std::min(static_cast<int>(look.users.size()), MaxListedRows); | ||
| const ImVec2 size(-FLT_MIN, | ||
| ImGui::GetTextLineHeightWithSpacing() * rows + ImGui::GetStyle().FramePadding.y * 2.0f); | ||
| if (ImGui::BeginListBox("##users", size)) | ||
| { | ||
| for (const auto& [itemType, name] : look.users) | ||
| { | ||
| if (ImGui::Selectable(name.c_str(), itemType == g_cache.itemType)) | ||
| { | ||
| clicked = itemType; | ||
| } | ||
| } | ||
| ImGui::EndListBox(); | ||
| } | ||
| ImGui::TreePop(); | ||
| } | ||
| ImGui::PopID(); | ||
| return clicked; | ||
| } | ||
| } // namespace | ||
|
|
||
| int CItemEditorLooks::Render(int itemType) | ||
| { | ||
| if (!ImGui::CollapsingHeader(I18N::Editor::Looks)) | ||
| { | ||
| return -1; | ||
| } | ||
|
|
||
| const ItemModelDefinition* model = g_ItemModelDatabase.Find(itemType); | ||
| if (model == nullptr) | ||
| { | ||
| ImGui::TextDisabled("%s", itemType < 0 ? I18N::Editor::SelectAnItem : I18N::Editor::NoModel); | ||
| return -1; | ||
| } | ||
| Refresh(itemType, *model); | ||
| ImGui::Text("%s: %s", I18N::Editor::ModelFile, model->file.c_str()); | ||
| ImGui::Text("%s: %s", I18N::Editor::Glow, g_cache.glow.c_str()); | ||
| const int clickedStyleUser = RenderLook(I18N::Editor::RenderStyle, g_cache.renderStyle); | ||
| const int clickedEffectUser = RenderLook(I18N::Editor::ItemEffect, g_cache.itemEffect); | ||
| return clickedStyleUser >= 0 ? clickedStyleUser : clickedEffectUser; | ||
| } | ||
|
|
||
| #endif // _EDITOR | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| #pragma once | ||
|
|
||
| #ifdef _EDITOR | ||
|
|
||
| // A read-only view of the looks of the selected item (Data/Items/Models): its | ||
| // model file, glow, render style and item effect, and the items that share a | ||
| // look. | ||
| class CItemEditorLooks | ||
| { | ||
| public: | ||
| // Shown above the item table; `itemType` is the selected item, -1 for none. | ||
| // Returns the item clicked in a list of the items that share a look, -1 | ||
| // for none. | ||
| static int Render(int itemType); | ||
| }; | ||
|
|
||
| #endif // _EDITOR |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Non-blocking. While the section is open, every frame builds and dumps the glow JSON, goes through all
MAX_ITEMslots for each look to count its users (again for an open list), and builds a log name string per listed item. Small in absolute terms, but CODING_RULES rule 12 counts per-frame render functions as hot paths; the lists and the glow text could be cached per selected item andg_ItemModelDatabase.GetVersion().