Melodies
Codec: cooked_schemas.definitions._dsl_spec("MelodyDefinition", "melodydef.json", …)
— a generic DSL spec with byte-stable round-trip over all 12 retail files
(tests/test_melody_kind.py::test_all_retail_melodydefs_round_trip_byte_for_byte).
oe::dt::MelodyDefinition::vftable0x140f29b70(31 slots, deserialize-shaped)oCTLibrary<oe::dt::MelodyDefinition>::vftable0x140f29af8- runtime:
MelodyEntityCpnt0x140ed74a0,MelodyProfileData0x140f00af0,MelodyUiViewerEntityCpnt0x140f2e910
No function in the attached DB is named *Melody* — the deserializer is still an
unnamed FUN_*, so the field names below are mined from the data population,
not decompiled.
Corpus
12 defs under data/uncooked/Definitions/Melodies (Deal_Damage_Around,
Fully_Heal, Grant_Damage_Overtime, Increase_Fountain_Effect,
Increase_Move_Speed_At_Day, Instant_Level_Up, Power_Up_Level_Max,
Reduce_MO_Per_Collection, Refill_Sandman_Dreams, Remove_Key_Requirement,
Reveal_Map, Slow_Hourglass_Flow). Each has a paired effect entity, cooked and
present: EntitySettings\Objects\Melodies\<stem>.entity.ot.EntitySettingsResource.gen.
Decoded layout
The DSL spec exposes field_a + entity_ref; everything after is preserved
verbatim in _tail_hex. Parsing the tail across the corpus gives a completely
uniform shape:
field_a u32 melody enum indexentity_ref tresptr ["EntitySettings", "Objects\\Melodies\\<stem>.entity.ot"]--- _tail_hex ---a u32 4 | 5 | 6 (unknown; tier/weight?)b u32 always 3 (version tag?)flags u8 x3 varies per melody (unknown)guid 16B unique per melody u32=1, u8=0 (constant across all 12)BEGIN(4) BEGIN(5) u32 count=0 END (empty list, always) BEGIN(5) u32 count, lstr*count END <- game-modifier exclusion listENDBEGIN/END = 1111bbaa / 2222bbaa, the standard ot_decoder block framing;
the u32 after BEGIN is a class tag, not a length.
field_a is a dense enum index
Across the 12 defs field_a takes the values {0..11}, each exactly once
(test_field_a_is_a_dense_unique_enum_index). It is an index, not free data.
The trailing string list is game-modifier exclusions
Every string in every def is an exact stem of a file in
data/uncooked/Definitions/GameModifiers
(test_every_retail_exclusion_is_a_real_modifier_stem):
| Melody | Exclusions |
|---|---|
Fully_Heal | NightOnly, DayOnly, NoFeathers |
Slow_Hourglass_Flow | NoBossTimer, OneChapter |
Reveal_Map | NoMinimap |
Increase_Fountain_Effect | NoFountains |
Grant_Damage_Overtime | NoBossTimer |
Power_Up_Level_Max | OneChapter |
| (6 others) | (empty) |
The semantics read straight off the pairings: a melody is withheld when a run
modifier would make it meaningless (Reveal_Map under NoMinimap). Direction
is inferred, not proven — exclude-when-active fits all 12, but a whitelist
reading with an inverted flag elsewhere isn’t ruled out.
Why this kind is override-only
Same wall as skins and heroes:
field_ais dense{0..11}— no free index for a 13th melody.Ui\Melodiesships exactly 12 per-melody icons plus_bg/_Hover/_Lockedchrome — a 1:1 match with the 12 defs.- The melodydef carries no icon ref and no name/text ref. The def→icon→name mapping is resolved outside the asset, by index or by name, somewhere the def cannot reach.
A net-new melodydef would deserialize but have no index, no icon and no label. The
kind doesn’t attempt it — it edits the 12 that exist, as a plain retail override
(emitted at the vanilla decoded path; rsmm apply backs up + replaces).
Levers the melody kind exposes
effect="<stem>"— repointentity_refat another retail melody’s effect entity. The melody keeps its own index, GUID, icon and name; only the granted effect changes.exclude=[...]— replace the modifier exclusion list ([]clears it). Unknown stems raise, as a typo guard against theGameModifierscorpus.
field_a, a, flags and guid are deliberately not exposed — the first
and last are identity, the middle two are unmined.
Open
- No in-game playtest of either lever — that’s why the kind is
guess. a(∈ {4,5,6}) and the 3 flag bytes are unidentified. Candidates: unlock tier, chapter gating, day/night applicability.- Exclusion-list direction (blacklist vs whitelist) unconfirmed.
- Corroborating runtime offsets exist from a read-only third-party melody
overlay: melody GUID slots at scene-context
+0x760(3 × 16B, slot order = unlock order), hero melody array at hero+0x13a0with u32 count at+0x13a8, melody state at cpnt+0x68(1 = collecting, 2 = completed), melody asset GUID at+0x1c8. The per-melody GUID in the def is the natural join key if a runtimeR.melodyAPI is ever wanted.