@futdevpro/fsm-dynamo 1.16.31 → 1.16.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/build/_modules/game/_collections/audio-scale.util.d.ts +57 -0
  2. package/build/_modules/game/_collections/audio-scale.util.d.ts.map +1 -0
  3. package/build/_modules/game/_collections/audio-scale.util.js +105 -0
  4. package/build/_modules/game/_collections/audio-scale.util.js.map +1 -0
  5. package/build/_modules/game/_enums/audio-channel.enum.d.ts +8 -4
  6. package/build/_modules/game/_enums/audio-channel.enum.d.ts.map +1 -1
  7. package/build/_modules/game/_enums/audio-channel.enum.js +8 -4
  8. package/build/_modules/game/_enums/audio-channel.enum.js.map +1 -1
  9. package/build/_modules/game/_models/audio-mixer-config.interface.d.ts +37 -0
  10. package/build/_modules/game/_models/audio-mixer-config.interface.d.ts.map +1 -0
  11. package/build/_modules/game/_models/audio-mixer-config.interface.js +3 -0
  12. package/build/_modules/game/_models/audio-mixer-config.interface.js.map +1 -0
  13. package/build/_modules/game/_models/audio-mixer-state.interface.d.ts +19 -0
  14. package/build/_modules/game/_models/audio-mixer-state.interface.d.ts.map +1 -0
  15. package/build/_modules/game/_models/audio-mixer-state.interface.js +3 -0
  16. package/build/_modules/game/_models/audio-mixer-state.interface.js.map +1 -0
  17. package/build/_modules/game/_models/audio-mixer.control-model.d.ts +123 -0
  18. package/build/_modules/game/_models/audio-mixer.control-model.d.ts.map +1 -0
  19. package/build/_modules/game/_models/audio-mixer.control-model.js +312 -0
  20. package/build/_modules/game/_models/audio-mixer.control-model.js.map +1 -0
  21. package/build/_modules/game/_models/audio-scale.interface.d.ts +18 -0
  22. package/build/_modules/game/_models/audio-scale.interface.d.ts.map +1 -0
  23. package/build/_modules/game/_models/audio-scale.interface.js +3 -0
  24. package/build/_modules/game/_models/audio-scale.interface.js.map +1 -0
  25. package/build/_modules/game/index.d.ts +5 -0
  26. package/build/_modules/game/index.d.ts.map +1 -1
  27. package/build/_modules/game/index.js +5 -0
  28. package/build/_modules/game/index.js.map +1 -1
  29. package/package.json +1 -1
  30. package/src/_modules/game/_collections/audio-scale.util.spec.ts +140 -0
  31. package/src/_modules/game/_collections/audio-scale.util.ts +129 -0
  32. package/src/_modules/game/_enums/audio-channel.enum.ts +8 -4
  33. package/src/_modules/game/_models/audio-mixer-config.interface.ts +45 -0
  34. package/src/_modules/game/_models/audio-mixer-state.interface.ts +23 -0
  35. package/src/_modules/game/_models/audio-mixer.control-model.spec.ts +306 -0
  36. package/src/_modules/game/_models/audio-mixer.control-model.ts +378 -0
  37. package/src/_modules/game/_models/audio-scale.interface.ts +21 -0
  38. package/src/_modules/game/index.ts +5 -0
@@ -0,0 +1,129 @@
1
+
2
+
3
+ import { DyFM_AudioScale } from '../_models/audio-scale.interface';
4
+
5
+
6
+ /**
7
+ * Skala-konverziok az audio-mixerhez: a projekt sajat szamabrazolasa (pl. `0-100` egesz)
8
+ * es a lejatszo API-k altal vart `0..1` kozott.
9
+ *
10
+ * **Miert statikus util:** nincs allapota — tiszta fuggvenyek, a `DyFM_Math` mintajara.
11
+ *
12
+ * **Hasznalat:**
13
+ * ```typescript
14
+ * const percent = DyFM_AudioScale_Util.getPercentScale();
15
+ * DyFM_AudioScale_Util.toUnit(72, percent); // 0.72 -> ezt varja a HTMLAudio / GainNode
16
+ * DyFM_AudioScale_Util.fromUnit(0.725, percent); // 73 -> ezt tarolja / mutatja a projekt
17
+ * ```
18
+ */
19
+ export class DyFM_AudioScale_Util {
20
+
21
+ /** Alapertelmezett hangero-arany (a skala maximumanak hanyada), ha nincs jobb ertek. */
22
+ static readonly defaultRatio: number = 0.8;
23
+
24
+ /**
25
+ * A `0..1` lebegopontos skala — a Prototyper prototipusai (es a round-1
26
+ * `DyFM_AudioMixer_Settings`) ezt hasznaljak. Minden hivas UJ objektumot ad vissza, hogy a
27
+ * hivo szabadon mutalhassa.
28
+ */
29
+ static getUnitScale(): DyFM_AudioScale {
30
+ return {
31
+ max: 1,
32
+ integer: false,
33
+ };
34
+ }
35
+
36
+ /**
37
+ * A `0-100` egesz skala — a Hero Coordinator ezt tarolja a localStorage-ban
38
+ * (`clampVolume` = `Math.round`). Minden hivas UJ objektumot ad vissza.
39
+ */
40
+ static getPercentScale(): DyFM_AudioScale {
41
+ return {
42
+ max: 100,
43
+ integer: true,
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Sajat skalan levo erteket `0..1`-re normalizal, es a tartomanyba szorit.
49
+ * Ervenytelen bemenet (NaN, nem-szam, null/undefined) eseten a `fallbackUnit`-ot adja —
50
+ * igy egy serult save-payload sem tud NaN-t juttatni a lejatszoba
51
+ * (`HTMLAudioElement.volume = NaN` dobna, a `GainNode.gain.value = NaN` elnemitana).
52
+ */
53
+ static toUnit(
54
+ value: number,
55
+ scale: DyFM_AudioScale,
56
+ fallbackUnit: number = DyFM_AudioScale_Util.defaultRatio,
57
+ ): number {
58
+ const max: number = this.getSafeMax(scale);
59
+
60
+ if (typeof value !== 'number' || Number.isNaN(value)) {
61
+ return this.clampUnit(fallbackUnit);
62
+ }
63
+
64
+ return this.clampUnit(value / max);
65
+ }
66
+
67
+ /**
68
+ * `0..1` erteket a sajat skalara alakit vissza. Egesz skalanal (`integer: true`) kerekit,
69
+ * igy a `0-100`-as UI-nak nem kell tortszamokkal dolgoznia.
70
+ */
71
+ static fromUnit(unit: number, scale: DyFM_AudioScale): number {
72
+ const max: number = this.getSafeMax(scale);
73
+ const safeUnit: number = typeof unit !== 'number' || Number.isNaN(unit)
74
+ ? 0
75
+ : this.clampUnit(unit);
76
+ const scaled: number = safeUnit * max;
77
+
78
+ return scale && scale.integer === true ? Math.round(scaled) : scaled;
79
+ }
80
+
81
+ /**
82
+ * Egy erteket a sajat skala `0..max` tartomanyaba szorit (egesz skalanal kerekit is).
83
+ *
84
+ * Ervenytelen bemenetre (NaN, nem-szam) a `fallbackScaleValue`-t adja — **a sajat skalan**.
85
+ * Alapertelmezesben `0`; a mixer viszont a csatorna DEFAULT-jat adja at, mert egy serult
86
+ * mentesbol jobb a hallhato alapertek, mint a nema 0 (a felhasznalo eszre sem venne).
87
+ */
88
+ static clampToScale(
89
+ value: number,
90
+ scale: DyFM_AudioScale,
91
+ fallbackScaleValue: number = 0,
92
+ ): number {
93
+ if (typeof value !== 'number' || Number.isNaN(value)) {
94
+ const safeFallback: number = typeof fallbackScaleValue === 'number'
95
+ && !Number.isNaN(fallbackScaleValue)
96
+ ? fallbackScaleValue
97
+ : 0;
98
+
99
+ return this.fromUnit(this.toUnit(safeFallback, scale, 0), scale);
100
+ }
101
+
102
+ return this.fromUnit(this.toUnit(value, scale, 0), scale);
103
+ }
104
+
105
+ /** Egy `0..1` erteket a tartomanyba szorit. */
106
+ static clampUnit(value: number): number {
107
+ if (typeof value !== 'number' || Number.isNaN(value)) {
108
+ return 0;
109
+ }
110
+
111
+ if (value < 0) {
112
+ return 0;
113
+ }
114
+
115
+ return 1 < value ? 1 : value;
116
+ }
117
+
118
+ /**
119
+ * A skala maximuma vedve: a hianyzo / nem-pozitiv / NaN maximumot `1`-re cseréli, hogy a
120
+ * nullaval valo osztas es a NaN-terjedes kizart legyen.
121
+ */
122
+ private static getSafeMax(scale: DyFM_AudioScale): number {
123
+ if (!scale || typeof scale.max !== 'number' || Number.isNaN(scale.max) || scale.max <= 0) {
124
+ return 1;
125
+ }
126
+
127
+ return scale.max;
128
+ }
129
+ }
@@ -1,11 +1,15 @@
1
1
 
2
2
 
3
3
  /**
4
- * A jatek-audio mixer csatornai.
4
+ * A jatek-audio mixer **klasszikus** csatorna-keszlete.
5
5
  *
6
- * A flotta NEGY jateka egymastol fuggetlenul ugyanezt a negy csatornat vezette be
7
- * (Prototyper: Room Cats / Cell Stage / Portalhold, plusz a Hero Coordinator) ez az
8
- * enum a kanonikus nevezektan, amire mindegyik ratert.
6
+ * A Prototyper HAROM prototipusa (Room Cats / Cell Stage / Portalhold) egymastol fuggetlenul
7
+ * ugyanezt a negy csatornat vezette be ez az enum az o kanonikus nevezektanuk.
8
+ *
9
+ * **FIGYELEM (2026-08-06 helyesbites):** ez NEM flotta-univerzalis. A **Hero Coordinator**
10
+ * csatornai `master` / `music` / **`dialog`** / **`interface`**, es `0-100` egeszet tarol.
11
+ * Ha a projekted csatorna-keszlete elter, NE ezt az enumot hasznald, hanem a
12
+ * `DyFM_AudioMixer_ControlModel`-t sajat config-gal (csatorna- es skala-agnosztikus).
9
13
  *
10
14
  * A `master` NEM onallo hangsav: minden csatorna effektiv hangereje a sajat erteke
11
15
  * SZOROZVA a master-rel (lasd `DyFM_AudioMixer_Util.getEffectiveVolume`).
@@ -0,0 +1,45 @@
1
+
2
+
3
+ import { DyFM_AudioScale } from './audio-scale.interface';
4
+
5
+
6
+ /**
7
+ * Egy jatek audio-mixerenek **deklaracioja**: milyen csatornai vannak, milyen skalan, es mik
8
+ * az alapertelmezesek.
9
+ *
10
+ * **Miert nem fix csatorna-keszlet:** a `DyFM_AudioMixer_Settings` (round-1) negy fix mezot ad
11
+ * (`music` / `ambience` / `effects`), ami a Prototyper harom prototipusara igaz — de a Hero
12
+ * Coordinator csatornai `music` / `dialog` / `interface`. Egy fix alak ott csatorna-vesztessel
13
+ * jarna. A config-alapu modell mindkettot kiszolgalja, es a projekt sajat csatorna-enumja
14
+ * marad a nevek forrasa.
15
+ *
16
+ * **A `master` nem szerepel a `channels` listaban** — kulon kezelt, mert minden csatornara
17
+ * szorzodik.
18
+ *
19
+ * **Hasznalat:**
20
+ * ```typescript
21
+ * const mixer = new DyFM_AudioMixer_ControlModel({
22
+ * channels: ['music', 'dialog', 'interface'],
23
+ * scale: DyFM_AudioScale_Util.getPercentScale(),
24
+ * defaults: { music: 36, dialog: 84, interface: 68 },
25
+ * defaultMaster: 72,
26
+ * });
27
+ * ```
28
+ */
29
+ export interface DyFM_AudioMixer_Config {
30
+
31
+ /** A projekt sajat csatorna-nevei. A `master` NEM tartozik ide (kulon kezelt). */
32
+ channels: string[];
33
+
34
+ /** A csatorna-ertekek skalaja (`unit` = 0..1, `percent` = 0-100 egesz). */
35
+ scale: DyFM_AudioScale;
36
+
37
+ /** Alapertelmezett csatorna-ertekek a **sajat skalan**. A fel nem soroltak a `defaultChannelVolume`-ot kapjak. */
38
+ defaults?: Record<string, number>;
39
+
40
+ /** Alapertelmezett master-ertek a **sajat skalan**. Default: a skala maximumanak 80%-a. */
41
+ defaultMaster?: number;
42
+
43
+ /** Alapertelmezes a `defaults`-ban fel nem sorolt csatornakra (**sajat skala**). Default: a maximum 80%-a. */
44
+ defaultChannelVolume?: number;
45
+ }
@@ -0,0 +1,23 @@
1
+
2
+
3
+ /**
4
+ * Egy audio-mixer **allapota** — a projekt sajat skalajan tarolva, JSON-serializalhatoan.
5
+ *
6
+ * A `channels` nyitott rekord: a kulcsok a `DyFM_AudioMixer_Config.channels` nevei. Igy egy
7
+ * jatek annyi es olyan nevu csatornat hasznalhat, amennyit akar, a bedrock-modell valtoztatasa
8
+ * nelkul.
9
+ *
10
+ * **Mentheto:** ez az alak kerulhet kozvetlenul a save-payloadba vagy a localStorage-ba.
11
+ * Hianyos/regi payloadbol a `DyFM_AudioMixer_ControlModel.merge()` allit elo teljes allapotot.
12
+ */
13
+ export interface DyFM_AudioMixer_State {
14
+
15
+ /** Fo-hangero a **sajat skalan** — minden csatornara szorzodik. */
16
+ master: number;
17
+
18
+ /** Csatorna-nev -> hangero a **sajat skalan**. */
19
+ channels: Record<string, number>;
20
+
21
+ /** Globalis nemitas — ha `true`, minden effektiv hangero 0. */
22
+ muted: boolean;
23
+ }
@@ -0,0 +1,306 @@
1
+
2
+ import { DyFM_AudioMixer_Settings } from './audio-mixer-settings.interface';
3
+ import { DyFM_AudioMixer_State } from './audio-mixer-state.interface';
4
+ import { DyFM_AudioMixer_ControlModel } from './audio-mixer.control-model';
5
+
6
+
7
+ /** A Hero Coordinator VALODI mixer-alakja (0-100 egesz, dialog + interface csatorna). */
8
+ function createHeroCoordinatorMixer(): DyFM_AudioMixer_ControlModel {
9
+ return DyFM_AudioMixer_ControlModel.createPercent(
10
+ ['music', 'dialog', 'interface'],
11
+ { music: 36, dialog: 84, interface: 68 },
12
+ 72,
13
+ );
14
+ }
15
+
16
+
17
+ describe('DyFM_AudioMixer_ControlModel', (): void => {
18
+
19
+ describe('konstruktor-validacio', (): void => {
20
+
21
+ it('ures csatorna-listara dob', (): void => {
22
+ expect((): void => {
23
+ new DyFM_AudioMixer_ControlModel({ channels: [], scale: { max: 1, integer: false } });
24
+ }).toThrow();
25
+ });
26
+
27
+ it('a fenntartott "master" csatorna-nevre dob', (): void => {
28
+ expect((): void => {
29
+ DyFM_AudioMixer_ControlModel.createPercent(['master', 'music']);
30
+ }).toThrow();
31
+ });
32
+
33
+ it('duplikalt csatorna-nevre dob', (): void => {
34
+ expect((): void => {
35
+ DyFM_AudioMixer_ControlModel.createPercent(['music', 'music']);
36
+ }).toThrow();
37
+ });
38
+
39
+ it('a config masolatat tarolja (a kesobbi mutacio nem hat vissza)', (): void => {
40
+ const channels: string[] = ['music', 'dialog'];
41
+ const mixer: DyFM_AudioMixer_ControlModel = DyFM_AudioMixer_ControlModel.createPercent(channels);
42
+
43
+ channels.push('interface');
44
+
45
+ expect(mixer.getChannels()).toEqual(['music', 'dialog']);
46
+ });
47
+ });
48
+
49
+ describe('getDefaults', (): void => {
50
+
51
+ it('a config szerinti alapertekeket adja a SAJAT skalan', (): void => {
52
+ const state: DyFM_AudioMixer_State = createHeroCoordinatorMixer().getDefaults();
53
+
54
+ expect(state.master).toBe(72);
55
+ expect(state.channels['music']).toBe(36);
56
+ expect(state.channels['dialog']).toBe(84);
57
+ expect(state.channels['interface']).toBe(68);
58
+ expect(state.muted).toBe(false);
59
+ });
60
+
61
+ it('a fel nem sorolt csatornanak kozos default jut', (): void => {
62
+ const mixer: DyFM_AudioMixer_ControlModel = DyFM_AudioMixer_ControlModel.createPercent(
63
+ ['music', 'ui'],
64
+ { music: 20 },
65
+ 90,
66
+ );
67
+ const state: DyFM_AudioMixer_State = mixer.getDefaults();
68
+
69
+ expect(state.channels['music']).toBe(20);
70
+ expect(state.channels['ui']).toBe(80);
71
+ });
72
+
73
+ it('minden hivas UJ objektumot ad (nincs megosztott referencia)', (): void => {
74
+ const mixer: DyFM_AudioMixer_ControlModel = createHeroCoordinatorMixer();
75
+ const first: DyFM_AudioMixer_State = mixer.getDefaults();
76
+ const second: DyFM_AudioMixer_State = mixer.getDefaults();
77
+
78
+ first.channels['music'] = 1;
79
+
80
+ expect(second.channels['music']).toBe(36);
81
+ });
82
+ });
83
+
84
+ describe('merge', (): void => {
85
+
86
+ it('hianyzo allapotbol a defaultot adja', (): void => {
87
+ const mixer: DyFM_AudioMixer_ControlModel = createHeroCoordinatorMixer();
88
+
89
+ expect(mixer.merge(null)).toEqual(mixer.getDefaults());
90
+ expect(mixer.merge(undefined)).toEqual(mixer.getDefaults());
91
+ });
92
+
93
+ it('reszleges payloadot kiegeszit, a meglevot megtartja', (): void => {
94
+ const mixer: DyFM_AudioMixer_ControlModel = createHeroCoordinatorMixer();
95
+ const merged: DyFM_AudioMixer_State = mixer.merge({ channels: { dialog: 10 } });
96
+
97
+ expect(merged.channels['dialog']).toBe(10);
98
+ expect(merged.channels['music']).toBe(36);
99
+ expect(merged.master).toBe(72);
100
+ });
101
+
102
+ it('a NEM deklaralt csatornat eldobja (eltavolitott csatorna nem szivarog vissza)', (): void => {
103
+ const mixer: DyFM_AudioMixer_ControlModel = createHeroCoordinatorMixer();
104
+ const merged: DyFM_AudioMixer_State = mixer.merge({
105
+ channels: { music: 50, legacyAmbience: 99 } as Record<string, number>,
106
+ });
107
+
108
+ expect(merged.channels['music']).toBe(50);
109
+ expect(merged.channels['legacyAmbience']).toBeUndefined();
110
+ });
111
+
112
+ it('a tartomanyon kivuli erteket beszoritja', (): void => {
113
+ const mixer: DyFM_AudioMixer_ControlModel = createHeroCoordinatorMixer();
114
+ const merged: DyFM_AudioMixer_State = mixer.merge({
115
+ master: 500,
116
+ channels: { music: -20 },
117
+ });
118
+
119
+ expect(merged.master).toBe(100);
120
+ expect(merged.channels['music']).toBe(0);
121
+ });
122
+
123
+ it('a SERULT (NaN) erteket a csatorna DEFAULT-jara viszi, nem 0-ra (nem nemit el csendben)', (): void => {
124
+ const mixer: DyFM_AudioMixer_ControlModel = createHeroCoordinatorMixer();
125
+ const merged: DyFM_AudioMixer_State = mixer.merge({
126
+ master: NaN,
127
+ channels: { dialog: NaN, interface: 'loud' as unknown as number },
128
+ });
129
+
130
+ expect(merged.master).toBe(72);
131
+ expect(merged.channels['dialog']).toBe(84);
132
+ expect(merged.channels['interface']).toBe(68);
133
+ });
134
+ });
135
+
136
+ describe('getEffectiveVolume', (): void => {
137
+
138
+ it('MINDIG 0..1-et ad, fuggetlenul a 0-100 tarolastol', (): void => {
139
+ const mixer: DyFM_AudioMixer_ControlModel = createHeroCoordinatorMixer();
140
+ const state: DyFM_AudioMixer_State = mixer.getDefaults();
141
+
142
+ expect(mixer.getEffectiveVolume(state, 'interface')).toBeCloseTo(0.68 * 0.72, 10);
143
+ expect(mixer.getEffectiveVolume(state, 'dialog')).toBeCloseTo(0.84 * 0.72, 10);
144
+ });
145
+
146
+ it('a "master" nevre a master sajat erteket adja (nem negyzetre emelve)', (): void => {
147
+ const mixer: DyFM_AudioMixer_ControlModel = createHeroCoordinatorMixer();
148
+ const state: DyFM_AudioMixer_State = mixer.getDefaults();
149
+
150
+ expect(mixer.getEffectiveVolume(state, 'master')).toBeCloseTo(0.72, 10);
151
+ });
152
+
153
+ it('nemitva minden csatorna 0', (): void => {
154
+ const mixer: DyFM_AudioMixer_ControlModel = createHeroCoordinatorMixer();
155
+ const state: DyFM_AudioMixer_State = mixer.withMuted(mixer.getDefaults(), true);
156
+
157
+ expect(mixer.getEffectiveVolume(state, 'music')).toBe(0);
158
+ expect(mixer.getEffectiveVolume(state, 'master')).toBe(0);
159
+ });
160
+
161
+ it('ismeretlen csatornara DOB (elgepeles nem nemul el csendben)', (): void => {
162
+ const mixer: DyFM_AudioMixer_ControlModel = createHeroCoordinatorMixer();
163
+ const state: DyFM_AudioMixer_State = mixer.getDefaults();
164
+
165
+ expect((): void => {
166
+ mixer.getEffectiveVolume(state, 'ambience');
167
+ }).toThrow();
168
+ });
169
+
170
+ it('serult allapotbol sem ad NaN-t', (): void => {
171
+ const mixer: DyFM_AudioMixer_ControlModel = createHeroCoordinatorMixer();
172
+ const broken: DyFM_AudioMixer_State = {
173
+ master: NaN,
174
+ channels: { music: NaN, dialog: NaN, interface: NaN },
175
+ muted: false,
176
+ };
177
+
178
+ expect(Number.isNaN(mixer.getEffectiveVolume(broken, 'music'))).toBe(false);
179
+ });
180
+ });
181
+
182
+ describe('immutable update-ek', (): void => {
183
+
184
+ it('withChannelVolume UJ objektumot ad, az eredetit nem valtoztatja', (): void => {
185
+ const mixer: DyFM_AudioMixer_ControlModel = createHeroCoordinatorMixer();
186
+ const state: DyFM_AudioMixer_State = mixer.getDefaults();
187
+ const next: DyFM_AudioMixer_State = mixer.withChannelVolume(state, 'music', 40);
188
+
189
+ expect(next.channels['music']).toBe(40);
190
+ expect(state.channels['music']).toBe(36);
191
+ expect(next).not.toBe(state);
192
+ });
193
+
194
+ it('withChannelVolume ismeretlen csatornara dob', (): void => {
195
+ const mixer: DyFM_AudioMixer_ControlModel = createHeroCoordinatorMixer();
196
+
197
+ expect((): void => {
198
+ mixer.withChannelVolume(mixer.getDefaults(), 'effects', 10);
199
+ }).toThrow();
200
+ });
201
+
202
+ it('withMasterVolume a sajat skalan szorit es kerekit', (): void => {
203
+ const mixer: DyFM_AudioMixer_ControlModel = createHeroCoordinatorMixer();
204
+ const next: DyFM_AudioMixer_State = mixer.withMasterVolume(mixer.getDefaults(), 120);
205
+
206
+ expect(next.master).toBe(100);
207
+ });
208
+
209
+ it('withMuted UJ objektumot ad', (): void => {
210
+ const mixer: DyFM_AudioMixer_ControlModel = createHeroCoordinatorMixer();
211
+ const state: DyFM_AudioMixer_State = mixer.getDefaults();
212
+ const next: DyFM_AudioMixer_State = mixer.withMuted(state, true);
213
+
214
+ expect(next.muted).toBe(true);
215
+ expect(state.muted).toBe(false);
216
+ });
217
+ });
218
+
219
+ describe('isEqual', (): void => {
220
+
221
+ it('ertek szerint hasonlit, nem referencia szerint', (): void => {
222
+ const mixer: DyFM_AudioMixer_ControlModel = createHeroCoordinatorMixer();
223
+
224
+ expect(mixer.isEqual(mixer.getDefaults(), mixer.getDefaults())).toBe(true);
225
+ });
226
+
227
+ it('egy csatorna elteresere is false', (): void => {
228
+ const mixer: DyFM_AudioMixer_ControlModel = createHeroCoordinatorMixer();
229
+ const changed: DyFM_AudioMixer_State = mixer.withChannelVolume(mixer.getDefaults(), 'dialog', 1);
230
+
231
+ expect(mixer.isEqual(mixer.getDefaults(), changed)).toBe(false);
232
+ });
233
+ });
234
+
235
+ describe('round-1 kompatibilitas (createClassic + hidak)', (): void => {
236
+
237
+ it('a classic preset a round-1 csatornakat adja', (): void => {
238
+ expect(DyFM_AudioMixer_ControlModel.createClassic().getChannels())
239
+ .toEqual(['music', 'ambience', 'effects']);
240
+ });
241
+
242
+ it('fromClassicSettings atveszi a fix alakot', (): void => {
243
+ const legacy: DyFM_AudioMixer_Settings = {
244
+ master: 0.5, music: 0.4, ambience: 0.3, effects: 0.2, muted: true,
245
+ };
246
+ const state: DyFM_AudioMixer_State = DyFM_AudioMixer_ControlModel.fromClassicSettings(legacy);
247
+
248
+ expect(state.master).toBeCloseTo(0.5, 10);
249
+ expect(state.channels['ambience']).toBeCloseTo(0.3, 10);
250
+ expect(state.muted).toBe(true);
251
+ });
252
+
253
+ it('oda-vissza konverzio ertekhu (classic -> state -> classic)', (): void => {
254
+ const mixer: DyFM_AudioMixer_ControlModel = DyFM_AudioMixer_ControlModel.createClassic();
255
+ const legacy: DyFM_AudioMixer_Settings = {
256
+ master: 0.65, music: 0.4, ambience: 0.3, effects: 0.9, muted: false,
257
+ };
258
+ const roundTrip: DyFM_AudioMixer_Settings = mixer.toClassicSettings(
259
+ DyFM_AudioMixer_ControlModel.fromClassicSettings(legacy),
260
+ );
261
+
262
+ expect(roundTrip).toEqual(legacy);
263
+ });
264
+
265
+ it('a peldany-metodus a SAJAT skalarol normalizal (0-100 -> 0..1)', (): void => {
266
+ const mixer: DyFM_AudioMixer_ControlModel = DyFM_AudioMixer_ControlModel.createPercent(
267
+ ['music', 'ambience', 'effects'],
268
+ { music: 40, ambience: 30, effects: 90 },
269
+ 65,
270
+ );
271
+ const classic: DyFM_AudioMixer_Settings = mixer.toClassicSettings(mixer.getDefaults());
272
+
273
+ expect(classic.master).toBeCloseTo(0.65, 10);
274
+ expect(classic.music).toBeCloseTo(0.4, 10);
275
+ expect(classic.effects).toBeCloseTo(0.9, 10);
276
+ });
277
+
278
+ it('a classic mixer effektiv hangereje egyezik a round-1 szamitassal (csatorna * master)', (): void => {
279
+ const mixer: DyFM_AudioMixer_ControlModel = DyFM_AudioMixer_ControlModel.createClassic();
280
+ const state: DyFM_AudioMixer_State = DyFM_AudioMixer_ControlModel.fromClassicSettings({
281
+ master: 0.5, music: 0.4, ambience: 0.3, effects: 0.2, muted: false,
282
+ });
283
+
284
+ expect(mixer.getEffectiveVolume(state, 'music')).toBeCloseTo(0.2, 10);
285
+ });
286
+ });
287
+
288
+ describe('Hero Coordinator regresszio-vedelem', (): void => {
289
+
290
+ it('a 0-100 tarolas ertekhuen tulel egy merge-ciklust (NINCS adat-migracio)', (): void => {
291
+ const mixer: DyFM_AudioMixer_ControlModel = createHeroCoordinatorMixer();
292
+ const persisted: DyFM_AudioMixer_State = {
293
+ master: 72, channels: { music: 36, dialog: 84, interface: 68 }, muted: false,
294
+ };
295
+
296
+ expect(mixer.merge(JSON.parse(JSON.stringify(persisted)))).toEqual(persisted);
297
+ });
298
+
299
+ it('a dialog es interface csatorna NEM vesz el (a round-1 fix alak hibaja)', (): void => {
300
+ const mixer: DyFM_AudioMixer_ControlModel = createHeroCoordinatorMixer();
301
+
302
+ expect(mixer.hasChannel('dialog')).toBe(true);
303
+ expect(mixer.hasChannel('interface')).toBe(true);
304
+ });
305
+ });
306
+ });