Skip to content

Sounds: sound events at moments, in sound sets that the monsters name (MNS) - #142

Open
Mosch0512 wants to merge 26 commits into
mainfrom
feat/monster-sounds
Open

Mosch0512 wants to merge 26 commits into
mainfrom
feat/monster-sounds

Conversation

@Mosch0512

@Mosch0512 Mosch0512 commented Oct 29, 2026 •

Copy link
Copy Markdown
Owner

The third of the six PRs that finish the data-driven monsters work (design, "Pull requests and phases", MNS): the sound slots of 74 models become sound events that play at moments, and then all monster sounds move out of the models into sound sets (Data/Monsters/Sounds.json) that every monster names, with cues on the models for sounds at frames and a random variant for the Chaos Castle wizard look. The game sounds as before.

The Attacks PR (sven-n#886) is merged, so this PR now shows only its own commits.

Commits (the sound slots)

  1. docs(monsters): the answers to Q-MN76 and Q-MN77 — the slots play at moments, not with animations: the converted events name moments (on: attack, hit, death, stand, walk) and play where the slots played, whatever the animation (Q-MN76); the death moment plays in Chaos Castle too, next to its explosion (Q-MN77). The chain is six PRs since the sounds PR joined it.
  2. sound events can play at moments, by SubType, heard anywhere and at the monster — what the slots do and the events could not: on moments (an event names animations or moments), subType / notSubType (the second set of SubType 9), heardAnywhere (no 500-unit cut-off, no map list), atMonster (the character as PlayBuffer's object, which does not start the sound again while it plays), and a pick with one sound (the sound one time in two, else none: a slot pair whose second slot was empty). SoundEvents::PlayMoment plays a character's events at a moment. No data used them yet.
  3. fix(monsters): a model no entry opens has no sound slots, so the skeletons play none — the Skeleton monsters (the player model) and the traps (object models) went through the slot code with slots nothing had set: in a Debug build they played sound 210 when set to stand, walking, attacking, hit and dying. A BMD started with no sound in any slot.
  4. the sound slots become sound events at moments — 209 events for the 74 models, in the order the slots played: idle on stand and walk (one in 16 by rand_fps_check, then a pick; the Balrog's only on stand, its bone sound comes first while it walks), attack on attack and hit (the Assassin's only on attack), die on death, the Death Centurion and Shriker models' second set with subType 9 and the first with notSubType 9; all heard anywhere and at the monster. The five places that played the slots play the events of their moment (SetPlayerStop, SetPlayerWalk, SetPlayerAttack, SetPlayerShock, SetPlayerDie). Models.json is format 2 and refuses soundSlots; PlaySlotSound, HasSlotSound, MonsterSlotSound, BMD::Sounds, the slot reader, writer and checks and Monster Model's slot rows go. Monster Preview plays the events of an animation's moment where it played a slot.
  5. test(monsters): a walk, a death in Chaos Castle and the preview's hit and death play their moments.
  6. feat(editor): Monster Model's sound events play at moments or in animations, by SubType, anywhere and at the monster — the table's When offers the moments and the animations (one of the other kind makes the event one of it; the last one stays), SubType (any, only n, not n), Anywhere and At monster; a pick with one sound. Texts in English and German.
  7. docs(monsters): the sound events at moments, Models.json format 2 — monster-data.md.
  8. fix(editor): Monster Model's Play plays one of a pick's sounds at random — with "One of two" set, Play always played the first sound; it now draws which one, like the game (a pick of one sound plays it each time). The chance is still not drawn, so Play always makes a sound.

Comparison (commit 4)

A one-time recorder ran every monster on 22 maps (those the sound events name, Lorencia, Devil Square 1–4 and 5–7, Chaos Castle 1 and 7), with its SubType and with SubType 9 (or 0), near and far from the hero, two seeds and two frame rates, through SetPlayerStop and SetPlayerWalk (24 calls each), SetPlayerAttack, SetPlayerShock, SetPlayerDie (death effect armed and not) and the events of every animation at three frames, recording each sound, whether the monster was its object, and the next rand() and engine draw. Before the change (on the Attacks head) and after it, 191,840 cases: the same sounds in the same order with the same rand(), except

  • the death in Chaos Castle (16,768 steps): the death sound plays too, before the explosion (Q-MN77). No monster with a death sound dies there today: the Chaos Castle knight, elf and wizard have none.
  • SubType 9 on a model without a second set (214,952 steps): its sounds play now (the slots of 5 to 9 were empty). Only the Death Centurion and Illusion of Kundun have SubType 9, and their models have a second set, which plays as before.
  • the stray slot sound of the skeletons and traps (27,568 steps): gone, commit 3.
  • fewer rand_fps_check draws for a monster without idle sounds (89,040 stand and 89,040 walk steps): its events draw, the code no longer draws for it; the same sounds and the same rand().

The Molt (a model without a body, whose sounds are not loaded) was left out of the second run: numbering its sound files there shifts the numbers of later files; its events play its own unloaded sounds, so it is silent either way.

Decided by me (commits 2–8; the sound sets replace the SubType conditions, see below)

  • The field names on, subType / notSubType, heardAnywhere and atMonster; heard anywhere and at the monster are two fields, both set for the converted events.
  • Only the two models with a second set get a SubType condition; on the other 72 a SubType 9 monster (none exists) would play their sounds.
  • The Balrog's walk and the Assassin's hit are left out of their events instead of being checked in code.
  • A pair whose second slot was empty is a pick with one sound.
  • Monster Preview plays the moment of the animation it starts: stand for stop1 and stop2, attack for the attacks, hit for the shock, death, and walk at each frame of a walk, with the moment's draws (an idle sound one time in 16) and SubType 0.
  • A format 1 file without soundSlots still reads; one with them is refused, with the new form in the message.

Sound sets (commits 9–12)

After the owner tried the editor, the sound data moved out of the models (design Q-MN78 to Q-MN83, commit 9):

  • Sound sets (Data/Monsters/Sounds.json): a set holds sound files, sound events and the maps they play on, and every monster names its set (soundSet). A model holds no sounds. The Rogue and the Death Centurion share a model and now simply name different sets, so no event needs SubType any more; monsters of different models can share a set.
  • Cues on the models: the 24 frame rows (the steps of 12 Imperial Guardian and Karutan monsters, walk frames 1–2 and 7–8) become two named cues per model (step 1, step 2), and their events play "cues": ["step 1"]. A set then fits any model that has its cues.
  • A random variant for the ranged Chaos Castle soldiers (163, 165 … 427): the client draws them as an elf or a wizard (rand() % 2; OpenMU sends only the number of one Energy Ball monster), and the two looks played different attack sounds. "randomVariants": [{"model": "MONSTER_MODEL_CHAOSCASTLE_WIZARD", "soundSet": "Chaos Castle Wizard"}] keeps the wizard's sound; the code still draws the look until MN4.
  • Editor: a new Monster Sounds window edits the sets (files, events, maps), the monster's set and its variants' sets, and on a cue's event the cue's frames (Use the preview's frame, a new cue at the preview's frame; removing a cue's last event asks whether the cue goes too). Monster Preview shows the cues as tags under the frame slider (hover: frames and sound; click: Monster Sounds at the event), Add a sound at this frame, and a Variant switch for a monster with random variants. Monster Model shows the cues and the monster's set; Data Checks gets a Monster sound sets area.

Commits

  1. docs(monsters): the answers to Q-MN78 to Q-MN83, sound sets that monsters name — the design.
  2. feat(monsters): the sounds move into sound sets that the monsters name — Sounds.json, soundSet, randomVariants, cues; the game plays the set of the character's monster (CHARACTER::MonsterIndex) for the model it appears with; a model opens with the sounds of its monsters' sets; the one conversion (139 sets, 312 monsters name one, 12 models get cues); the editor and Data Checks; texts in English and German.
  3. test(monsters): sound sets, cues and random variants — the reader, writer and checks of Sounds.json and the shipped sets.
  4. docs(monsters): sound sets, cues and random variants — monster-data.md and the editor README.

Then main is merged in (only the two editor text files conflicted, both sides kept).

Comparison (commit 10)

A second one-time recorder ran every monster on 22 maps (those the sound sets name, Lorencia, Devil Square 1–4 and 5–7, Chaos Castle 1 and 7), with the SubType its creator gives it, the ranged Chaos Castle soldiers in both looks, near and far from the hero, two seeds and two frame rates, through SetPlayerStop, SetPlayerWalk, SetPlayerAttack, SetPlayerShock, SetPlayerDie (death effect armed and not) and the events of every animation at four frames (with the steps' 1.5 and 7.5). It wrote each sound by its file (or SOUND_* number), whether the monster was its object, and the next rand() and engine draw. Before the sound sets (on commit 8) and after them, 97,196 cases:

  • 87,948 identical, among them all of Death Centurion 1–7 and Illusion of Kundun 1–6 (their own sets play what the second sets played), the Imperial Guardian and Karutan steps on their cues, and the Chaos Castle elf and wizard looks.
  • 9,248 differ, all of one kind: a monster whose creator answers only on some maps (the Battle Castle, Crywolf, Kanturu, Barracks and Hunting Ground numbers, 104 … 491) is a Bull Fighter elsewhere, and there it no longer plays the Bull Fighter's sounds: that look is not one of its own (D62 plans to remove the Bull Fighter look). The server never sends these monsters on those maps.

Decided by me (sound sets)

  • A set is named after the English name of its first monster, without a trailing level number ("Chaos Castle 1" → "Chaos Castle"); the wizard look's set is "Chaos Castle Wizard". Models share a set only when their sound files, events and maps are fully alike: 139 sets.
  • Events name animations, on moments or cues, one of the three. frames, subType and notSubType are fatal and name what replaced them. Models.json stays format 2 (not released yet) and refuses its old sound fields.
  • A character plays its monster's set only with the entry's model or a random variant's model. A player (whose CHARACTER() starts as number 0) plays none; a monster that a map creator shows as a Bull Fighter on a map the server never sends it to no longer plays the Bull Fighter's sounds.
  • A model opens with the sets of every monster that uses it, so the Death Centurion's model still loads the ten files of both its sets.
  • Data Checks lists a missing set and a random variant whose look the code never draws as errors, an event's cue that the model of a monster that plays the set lacks and a set no monster plays as warnings.
  • A new cue is named after its animation and frame ("walk 4.50"), one frame long.

Review fixes (commits 14–25)

An xhigh review of this PR found 12 problems, all in the editor and the data checks (the game's sounds stay as compared). One commit each, each with a test that a mutation of the fix fails:

  1. Monster Preview marks the cue frames Monster Sounds edits while they are typed — the preview draws before Monster Sounds in a frame, so it never got Monster Sounds' draft, and Monster Model's draft came first for the cues too (Q-MN81's live marker never moved).
  2. Monster Sounds changes cues only on the model of a look that plays the shown set — with another set shown (picked in the list, a variant's set opened while the entry's look is shown, or the last monster's set), the cue rows changed the selected monster's model; they now say to select a monster of the set.
  3. The sound event rows no longer draw on with an event that a store freed — a store took the draft back at once, which freed the row's event and cue while the row went on reading and writing them. Drafts can now take the data back at the next frame (ReloadAfterStore::NextFrame); Monster Sounds' two drafts do.
  4. Add a file says when an id is not a SOUND_ id, and keeps the fields when nothing is stored (it used to add nothing and clear them).
  5. Two sounds of a set with one name are fatal, an id that sets load from different files an error — an event played the first of two sounds with one name, and an id played the file of whichever model opened last.
  6. Data Checks warns about a cue that a random variant's look lacks when the variant plays the entry's set.
  7. Monster Model removes a cue that no sound event plays on any more — nothing removed a cue before.
  8. The set of a monster's look is looked up once per change of the lists (SoundSetLinks), not with a scan of every model name at each sound and for every monster at each model open.
  9. Monster Sounds counts a model's monsters with the set links, not with a scan each frame; the count now includes the monsters that appear with the model as a random variant.
  10. The sound sets' limits and the frame text are defined once (MaxSoundChannels, MaxSoundChanceOneIn, PickSoundCount in MonsterSoundSet.h, SoundEventsEditor::FrameText).
  11. The cue frame inputs of Monster Sounds and Monster Preview keep the cue alike (KeepCueFrames, no bare 0.1f).
  12. Cue frames are checked as the floats the game keeps — two close numbers could be one float, a cue that never plays.

Then main is merged in again (only the two editor text files conflicted, both sides kept).

Testing

  • Debug editor build with all test targets; ctest -j 3 -E rml-theme: 1,798/1,798 pass with the sound sets (one GPU test skipped), 1,813/1,813 after merging main in (bundled_font_assets once hit a log file another test was writing and passed on its own), 1,825/1,825 with the review fixes, and 1,853/1,853 with main merged in again (bundled_font_assets again hit the editor log and passed on its own). The effect types recorder ran again on its own and passed (it took 94 s of its 120 s; with three tests at once it ran out). The RmlUi theme tests were left out: they stop on an RmlUi assert dialog in local Debug builds on this machine.
  • Tests of the sound slots part (commits 2 to 8): reading and writing on, heardAnywhere, atMonster and a pick with one sound, and their errors; moment events play only at their moments and animation events only in animations; heard anywhere far and on another map, at the monster; a pick with one sound in one draw of two; PlayMoment on a character; the shipped data (the Bull Fighter's moments, the Assassin's hit, the Balrog's walk, Queen Rainer's one-sound pick, the Dragon's loaded sounds); the game's stand, walk, attack, hit and death (and in Chaos Castle) on the Bull Fighter; Monster Preview's attack, hit and death; a skeleton's moments; a new BMD without slot sounds; format 2 refusing soundSlots; Play's pick.
  • Tests of the sound sets (commits 10 and 11): Sounds.json read and written back, the writer's layout, every wrong value with its set and field, the old event fields refused; Monsters.json's soundSet and randomVariants and their errors; Models.json's cues and the old sound fields refused; the checks between the files (a missing set, a variant look the code does not draw, a cue the model lacks, a set no monster plays); the shipped sets (load clean, written as the writer writes, the four SubType sets with their own files, the Chaos Castle variants, the twelve models' step cues, loaded alike); a character plays its monster's set for its own looks only (not a player, not another model), the wizard look its variant's; one set on the cues of two models; a model loads its monsters' sets (the Death Centurion's ten files, the wizard's variant set); undo, redo, rename and remove of sets, refused sets, an undo refused after a change past the history, Save writing Sounds.json; Data Checks sending set problems to Monster Sounds; the editor's cue toggles and new cue names, the shown look of a variant, Monster Sounds' model draft in its frame and the next, and the three windows drawing without ImGui errors. Each new test fails against a mutation of the code it covers (35 mutations before the sound sets, 23 for them).
  • Tests of the review fixes (commits 14 to 25): the preview's cues from Monster Sounds' draft in the frame after it drew and before Monster Model's; no cue context for another set, a variant's set or the last monster's; the drafts keeping a row's event and cue after a store and taking the data back the next frame; Add a file's reasons, a wrong id among them; two sounds with one name, an id loaded from two files (not for another case of one file); a variant's model in the cue warning; the unused cues; the set links (each look, a variant without its own set, a changed list, the sets and monsters of a model); the limits at and past their ends; the frame text; the cue frame clamp; frames that are one float or past the floats. 28 mutations, each killed.
  • clang-format 21.1.8 on the changed lines and cppcheck (CI's flags) on the 74 changed files, and on the 21 files the review fixes changed: clean.
  • In game: not tried yet.

The sound slots play at moments of the game, not with animations: when an
attack starts, the monster is hit, it dies, it is set to stand and at each
walk step. Q-MN76: the converted sound events name these moments (`on`)
and play where the slots play today, whatever the animation; any event may
use them. Q-MN77: the death moment plays in Chaos Castle too, next to the
explosion. The MNS phase and its details say how; the chain is six PRs
since the sounds PR joined it.
…nywhere and at the monster

What the sound slots do and the sound events could not (Q-MN76), so the
slots can become events:
- `on`: the moments attack, hit, death, stand and walk, where the slots
  play (SetPlayerAttack, SetPlayerShock, SetPlayerDie, SetPlayerStop,
  SetPlayerWalk), whatever the animation. An event names animations or
  moments.
- `subType` and `notSubType`: the second set of SubType 9.
- `heardAnywhere`: no 500-unit cut-off and no map list.
- `atMonster`: the character as PlayBuffer's object, which does not start
  the sound again while it plays.
- `pick` with one sound: the sound in one draw of two, else none (a slot
  pair whose second slot is empty).

SoundEvents::PlayMoment plays a character's events at a moment; Play and
the preview's PlayEvents take the situation (animation, frame, moment,
death effect, SubType, heard). The game plays nothing new yet: no data
names a moment.
…etons play none

The Skeleton monsters are the player model with a skeleton SubType, and
SetPlayerStop, SetPlayerWalk, SetPlayerAttack and SetPlayerDie played the
sound slots of their model as for a monster. Nothing sets the player
model's slots: BMD left them as the memory was, so a skeleton played a
sound by that value (in a Debug build sound 210 when set to stand and when
walking, next to its bone sound). A BMD now starts with no sound in any
slot, as the model loader sets them for a model without slots (P9).
The 74 models with sound slots get sound events instead (monsters design,
MNS, Q-MN75 to Q-MN77), 209 in all, in the order the slots played:
- idle on stand and walk, one time in 16 by rand_fps_check, then a pick
  (the Balrog only on stand: its bone sound comes first while it walks);
- attack on attack and hit (the Assassin's only on attack, as its slot
  played no hit sound);
- die on death;
- the second set of the Death Centurion and Shriker models with subType 9,
  the first with notSubType 9.
All are heard anywhere and played at the monster, as the slots were. A pair
whose second slot was empty is a pick with one sound.

The five places that played the slots play the events of their moment:
SetPlayerStop and SetPlayerWalk (a player still draws for its Fenrir and a
skeleton's bone sound), SetPlayerAttack, SetPlayerShock and SetPlayerDie,
whose death events play in Chaos Castle too, next to the explosion
(Q-MN77). Models.json is format 2 and refuses soundSlots; PlaySlotSound,
HasSlotSound, MonsterSlotSound, BMD::Sounds, the slot reader, writer and
checks, and Monster Model's slot rows go. Monster Preview plays the events
of the moment where it played a slot.

Comparison: a one-time recorder ran every monster on 22 maps (those the
sound events name, Lorencia, Devil Square 1-4 and 5-7, Chaos Castle 1 and
7), with its SubType and SubType 9 (or 0), near and far from the hero, two
seeds and two frame rates, through SetPlayerStop and SetPlayerWalk (24
calls each), SetPlayerAttack, SetPlayerShock, SetPlayerDie (death effect
armed and not) and the events of every animation at three frames, recording
each sound, whether the monster was its object, and the next rand() and
engine draw. On the commit before and on this one, 191,840 cases: the same
sounds in the same order with the same rand(), except
- the death in Chaos Castle, which plays the death sound too (Q-MN77);
- SubType 9 on a model without a second set, which plays its sounds now
  (only the Death Centurion and Illusion of Kundun have SubType 9);
- the stray slot sound of the models no entry opens (the skeletons and
  traps), which the commit before removed;
- fewer rand_fps_check draws for a monster without idle sounds (its events
  draw, the code no longer does), with the same sounds.
The Molt (a model without a body, whose sounds are not loaded) was left out
of the second run: numbering its sound files shifts the numbers of later
ones.
… and death play their moments

The Bull Fighter's walk plays its idle events (in a safe zone, where a walk
does not read the hero's equipment); its death in Chaos Castle plays its
death sound, then the explosion (Q-MN77); Monster Preview's shock plays the
hit moment's events and its death the death moment's.
…ations, by SubType, anywhere and at the monster

The sound events table gets what the events got (Q-MN76):
- When: a combo of the moments (attack, hit, death, stand, walk) and the
  animations; picking one of the other kind makes the event one of it, and
  the last one stays. It shows "attack1, attack2" or "on attack, hit".
- SubType: any, only n or not n (a new one names 9, the second set of the
  Death Centurion and Shriker models).
- Anywhere (heard at any distance and on every map) and At monster (the
  sound is not started again while it plays).
- Pick with one sound: the sound or none.

Texts in English and German.
monster-data.md: the model example and the field list without sound
slots; the sound events name animations or moments (a table of where the
game plays each), with subType and notSubType, heardAnywhere and atMonster
and a pick with one sound; format 2 refuses soundSlots; Monster Model's
When, SubType, Anywhere and At monster; Monster Preview plays the events of
the moment of each animation.
With "One of two" set, Play on a sound event always played the first
sound, so a pick of DSIDLE1 and DSIDLE2 never let the second be heard.
Play now draws which one, like the game does (one sound: that one each
time); without a pick it plays all of them together, as before. The
event's chance is still not drawn, so Play always makes a sound.
…ters name

Sound data moves out of the models into sound sets (Sounds.json) that every
monster names; frames become cues on the models, shown and edited from
Monster Preview and Monster Sounds; the Chaos Castle wizard look is a random
variant with its own set.
The monsters design's answers Q-MN78 to Q-MN83:

- Sounds.json holds the sound sets: their sound files, sound events and the
  maps they play on. Every monster names its set (soundSet); a model holds
  no sounds. The game plays the set of the character's monster
  (CHARACTER::MonsterIndex) for the model it appears with, so the Death
  Centurion and the Illusion of Kundun play their own sets and no event needs
  SubType. A model opens with the sounds of the sets its monsters use with
  it, which loads a sound where it loaded before.
- Frames become cues of the models ("step 1" = walk frames 1 to 2); an event
  plays on cues, so a set fits any model with its cues.
- The ranged Chaos Castle soldiers' wizard look is a random variant with its
  own set: the code still draws the look, the variant of the drawn model
  applies.
- One conversion: 139 sets from the 146 models' sounds (fully alike lists
  share one), 312 monsters name one, 12 models get their two step cues;
  Models.json format 2 refuses sounds, soundEvents and soundEventMaps, the
  sets' reader subType, notSubType and frames.
- Editor: Monster Sounds edits the sets, the monster's and its variants'
  sets, and on a cue's event the cue's frames (Use the preview's frame, a
  new cue at the preview's frame, removing a cue's last event asks about the
  cue); Monster Preview shows the cues as tags (hover: frames and sound;
  click: Monster Sounds at the event), Add a sound at this frame and a
  Variant switch; Monster Model shows the cues and the monster's set; Data
  Checks has the sound sets and what the files name of each other. Texts in
  English and German.
Sounds.json read and written back, the writer's layout, every wrong value
with its set and field, the old event fields refused (Q-MN78, Q-MN79); a
monster's set and its random variants' (Q-MN83); the checks between the
monster list, the model list and the sets; and the shipped sets: they load
clean, are written as the writer writes them, the four SubType sets have
their own files, the ranged Chaos Castle soldiers have the wizard variant,
twelve models have their step cues, and sets that share a sound id load it
alike.
monster-data.md: Sounds.json and its sound events (animations, cues or
moments), the monsters' soundSet and randomVariants, the models' cues, the
checks between the files, Monster Sounds, Monster Preview's cue tags, Add a
sound at this frame and Variant, and Save and Revert with three files. The
editor README lists MuMonsterSoundsUI.
@Mosch0512 Mosch0512 changed the title Sounds: the sound slots become sound events at moments (MNS) Sounds: sound events at moments, in sound sets that the monsters name (MNS) Oct 30, 2026
Main and this branch both added editor texts at the end of
Editor.en.resx and Editor.de.resx; both sides' texts are kept.
…s while they are typed

Monster Preview draws before Monster Sounds in a frame, so the draft that
Monster Sounds gave only in the frame it drew never reached the preview,
and Monster Model's draft came first for the cues too. The preview now
takes Monster Sounds' draft of this frame or the one before, and its cues
before Monster Model's (Q-MN81).
…that plays the shown set

The cue rows changed the cues of the selected monster's shown look also
while Monster Sounds showed another set: one picked in the list, a random
variant's set opened while the entry's look was shown, or the set of the
monster selected before, which stays shown when the new one names none. A
cue's frames then changed on a model the set's monsters do not have, and
"New cue at the preview's frame" moved the set's event onto a cue they
lack. The cue rows now take the look's model only while it plays the set
shown; otherwise they say to select a monster of the set.
…t a store freed

A sound event row kept its event, and a cue row its cue, while it stored a
change, and the store took the draft back from the data at once: the new
set replaced the draft's lists, the old event and cue were freed, and the
rest of the row read them and wrote the chance's clamp into freed memory.
The editor's drafts can now take the data back at the next frame
(ReloadAfterStore::NextFrame); Monster Sounds' set and model drafts do,
the other tools keep taking it back at once.
… the fields when nothing is stored

Add a file checked the file only. A sound with an id that is not a SOUND_
id was refused by the set's save without a word, and the file and id
fields were cleared anyway. The checks are one function now
(FindNewSoundProblem) that also checks the id; the button stays off with
the reason as its tooltip, and the fields are cleared only when the set
has the file.
…t sets load from different files an error

The sound set checks let a set have two sounds with one name (the same id,
or the same file without one), of which an event always played the first,
and let two sets load one SOUND_ id from different files, so that the id
played the file of whichever model opened last. The first is fatal now,
the second an error that names the set and file that loaded the id
first; Add a file refuses an id another sound of the set has.
…look lacks when it plays the entry's set

A random variant without a set of its own plays the entry's set on its own
model, but the check of the cues looked only at the entry's model: an
event on a cue the variant's model lacks never played for that look,
without a warning. The variant's model is checked with the entry's set
now.
… any more

An event moved from a cue to a moment or another cue left the cue on the
model, and no tool removed a cue: each "New cue at the preview's frame"
that was tried and left stayed in Models.json. Monster Model's Cues list
has Remove for a cue that no event of a set its monsters play names; a
cue in use says so in its tooltip.
…nge of the lists, not at each sound

Each sound of a monster (a walk step played two lookups) found the set by
turning the model names of the monster and its random variants into
slots, a scan of every model name each, and each model that opened did
that for every monster number. SoundSetLinks keeps the set of each look
of each monster and the monsters of each model, built again when the
monster list or the sound sets change; FindSoundSet and
ModelLoader::FindSoundSetsOfSlot use it.
…inks, not with a scan each frame

The cue rows' "a cue of the model, for N monsters" scanned every monster
number and turned each model name into a slot on every frame the window
drew. SoundSetLinks::CountMonstersWithModel gives the count from the
links, and it now also counts the monsters that appear with the model as
a random variant, which a change of its cues reaches too.
…ined once

The channels of a sound, a sound event's chance and the sounds of a pick
had their limits in the reader and again in Monster Sounds and the sound
event rows, and Monster Preview had a copy of the rows' frame text. The
limits are in MonsterSoundSet.h now (MaxSoundChannels,
MaxSoundChanceOneIn, PickSoundCount) and the frame text is
SoundEventsEditor::FrameText, so the editor cannot let through a value
the save refuses.
…Preview keep the cue alike

Add a sound at this frame kept its new cue 0.1 frames long with a bare
0.1f, while the cue rows of Monster Sounds named it MinCueFrames, so the
two could drift apart (CODING_RULES.md, 3). Both call
SoundEventsEditor::KeepCueFrames now.
The reader checked that a cue's first frame is below its second on the
file's numbers and then kept them as floats: two close numbers could be
one float, a cue that never plays, and a frame past the floats' range an
infinite one, without a problem. The frames are compared and checked as
floats now.
Main and this branch both added editor texts at the end of
Editor.en.resx and Editor.de.resx again; both sides' texts are kept.
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.

1 participant