-
Notifications
You must be signed in to change notification settings - Fork 0
/
MusicHolder.es
427 lines (381 loc) · 13.4 KB
/
MusicHolder.es
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
/* Copyright (c) 2002-2012 Croteam Ltd.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as published by
the Free Software Foundation
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
222
%{
#include "StdH.h"
#include "Entities/EnemyBase.h"
#include "Entities/EnemySpawner.h"
#include "Entities/Trigger.h"
#include "Entities/Woman.h"
%}
enum MusicType {
0 MT_LIGHT "light",
1 MT_MEDIUM "medium",
2 MT_HEAVY "heavy",
3 MT_EVENT "event",
4 MT_CONTINUOUS "continuous",
};
event EChangeMusic {
enum MusicType mtType,
CTFileName fnMusic,
FLOAT fVolume,
BOOL bForceStart,
};
%{
#define MUSIC_VOLUMEMIN 0.02f // minimum volume (considered off)
#define MUSIC_VOLUMEMAX 0.98f // maximum volume (considered full)
float FadeInFactor(TIME fFadeTime)
{
return (float) pow(MUSIC_VOLUMEMAX/MUSIC_VOLUMEMIN, 1/(fFadeTime/_pTimer->TickQuantum));
}
float FadeOutFactor(TIME fFadeTime)
{
return (float) pow(MUSIC_VOLUMEMIN/MUSIC_VOLUMEMAX, 1/(fFadeTime/_pTimer->TickQuantum));
}
%}
class CMusicHolder : CRationalEntity {
name "MusicHolder";
thumbnail "Thumbnails\\MusicHolder.tbn";
features "HasName", "IsTargetable", "IsImportant";
properties:
1 CTString m_strName "" = "MusicHolder",
2 FLOAT m_fScoreMedium "Score Medium" = 100.0f,
3 FLOAT m_fScoreHeavy "Score Heavy" = 1000.0f,
10 CTFileName m_fnMusic0 "Music Light" 'M' = CTFILENAME(""),
11 CTFileName m_fnMusic1 "Music Medium" = CTFILENAME(""),
12 CTFileName m_fnMusic2 "Music Heavy" = CTFILENAME(""),
13 CTFileName m_fnMusic3 = CTFILENAME(""), // event music
14 CTFileName m_fnMusic4 = CTFILENAME(""), // continuous music
20 FLOAT m_fVolume0 "Volume Light" 'V' = 1.0f,
21 FLOAT m_fVolume1 "Volume Medium" = 1.0f,
22 FLOAT m_fVolume2 "Volume Heavy" = 1.0f,
23 FLOAT m_fVolume3 = 1.0f, // event volume
24 FLOAT m_fVolume4 = 1.0f, // continuous volume
// internals
100 CEntityPointer m_penBoss, // current boss if any
102 CEntityPointer m_penCounter, // enemy counter for wave-fight progress display
104 INDEX m_ctEnemiesInWorld = 0, // count of total enemies in world
105 CEntityPointer m_penRespawnMarker, // respawn marker for coop
106 INDEX m_ctSecretsInWorld = 0, // count of total secrets in world
101 FLOAT m_tmFade = 1.0f, // music cross-fade speed
103 enum MusicType m_mtCurrentMusic = MT_LIGHT, // current active channel
// for cross-fade purposes
110 FLOAT m_fCurrentVolume0a = 1.0f,
210 FLOAT m_fCurrentVolume0b = 1.0f,
111 FLOAT m_fCurrentVolume1a = 1.0f,
211 FLOAT m_fCurrentVolume1b = 1.0f,
112 FLOAT m_fCurrentVolume2a = 1.0f,
212 FLOAT m_fCurrentVolume2b = 1.0f,
113 FLOAT m_fCurrentVolume3a = 1.0f,
213 FLOAT m_fCurrentVolume3b = 1.0f,
114 FLOAT m_fCurrentVolume4a = 1.0f,
214 FLOAT m_fCurrentVolume4b = 1.0f,
// the music channels
120 CSoundObject m_soMusic0a,
220 CSoundObject m_soMusic0b,
121 CSoundObject m_soMusic1a,
221 CSoundObject m_soMusic1b,
122 CSoundObject m_soMusic2a,
222 CSoundObject m_soMusic2b,
123 CSoundObject m_soMusic3a,
223 CSoundObject m_soMusic3b,
124 CSoundObject m_soMusic4a,
224 CSoundObject m_soMusic4b,
// next free subchannel markers (all starts at subchannel 1(b), first switch goes to subchannel 0(a))
130 INDEX m_iSubChannel0 = 1,
131 INDEX m_iSubChannel1 = 1,
132 INDEX m_iSubChannel2 = 1,
133 INDEX m_iSubChannel3 = 1,
134 INDEX m_iSubChannel4 = 1,
{
// array of enemies that make fuss
CDynamicContainer<CEntity> m_cenFussMakers;
}
components:
1 model MODEL_MARKER "Models\\Editor\\MusicHolder.mdl",
2 texture TEXTURE_MARKER "Models\\Editor\\MusicHolder.tex"
functions:
// count enemies in current world
void CountEnemies(void)
{
m_ctEnemiesInWorld = 0;
m_ctSecretsInWorld = 0;
// for each entity in the world
{FOREACHINDYNAMICCONTAINER(GetWorld()->wo_cenEntities, CEntity, iten) {
CEntity *pen = iten;
// if enemybase
if (IsDerivedFromClass(pen, "Enemy Base")) {
CEnemyBase *penEnemy = (CEnemyBase *)pen;
// if not template
if (!penEnemy->m_bTemplate) {
// count one
m_ctEnemiesInWorld++;
// if this is a woman kamikaze carrier, add another one to count
if (IsOfClass(pen, "Woman")) {
if (((CWoman *)&*pen)->m_bKamikazeCarrier) { m_ctEnemiesInWorld++; }
}
}
// if spawner
} else if (IsDerivedFromClass(pen, "Enemy Spawner")) {
CEnemySpawner *penSpawner = (CEnemySpawner *)pen;
// if not teleporting
if (penSpawner->m_estType!=EST_TELEPORTER) {
// add total count
m_ctEnemiesInWorld+=penSpawner->m_ctTotal;
// if this spawner points to a woman kamikaze carrier template, increase count once more
if (penSpawner->m_penTarget) {
if (IsOfClass(penSpawner->m_penTarget, "Woman")) {
if (((CWoman *)&*penSpawner->m_penTarget)->m_bKamikazeCarrier) { m_ctEnemiesInWorld+=penSpawner->m_ctTotal; }
}
}
}
// if trigger
} else if (IsDerivedFromClass(pen, "Trigger")) {
CTrigger *penTrigger = (CTrigger *)pen;
// if has score
if (penTrigger->m_fScore>0) {
// it counts as a secret
m_ctSecretsInWorld++;
}
}
}}
}
// check for stale fuss-makers
void CheckOldFussMakers(void)
{
TIME tmNow = _pTimer->CurrentTick();
TIME tmTooOld = tmNow-10.0f;
CDynamicContainer<CEntity> cenOldFussMakers;
// for each fussmaker
{FOREACHINDYNAMICCONTAINER(m_cenFussMakers, CEntity, itenFussMaker) {
CEnemyBase & enFussMaker = (CEnemyBase&)*itenFussMaker;
// if haven't done fuss for too long
if (enFussMaker.m_tmLastFussTime<tmTooOld) {
// add to old fuss makers
cenOldFussMakers.Add(&enFussMaker);
}
}}
// for each old fussmaker
{FOREACHINDYNAMICCONTAINER(cenOldFussMakers, CEntity, itenOldFussMaker) {
CEnemyBase &enOldFussMaker = (CEnemyBase&)*itenOldFussMaker;
// remove from fuss
enOldFussMaker.RemoveFromFuss();
}}
}
// get total score of all active fuss makers
INDEX GetFussMakersScore(void) {
INDEX iScore = 0;
{FOREACHINDYNAMICCONTAINER(m_cenFussMakers, CEntity, itenFussMaker) {
CEnemyBase &enFussMaker = (CEnemyBase&)*itenFussMaker;
iScore += enFussMaker.m_iScore;
}}
return iScore;
}
// change given music channel
void ChangeMusicChannel(enum MusicType mtType, const CTFileName &fnNewMusic, FLOAT fNewVolume)
{
INDEX &iSubChannel = (&m_iSubChannel0)[mtType];
// take next sub-channel if needed
if (fnNewMusic!="") {
iSubChannel = (iSubChannel+1)%2;
}
// find channel's variables
FLOAT &fVolume = (&m_fVolume0)[mtType];
CSoundObject &soMusic = (&m_soMusic0a)[mtType*2+iSubChannel];
FLOAT &fCurrentVolume = (&m_fCurrentVolume0a)[mtType*2+iSubChannel];
// setup looping/non looping flags
ULONG ulFlags;
if (mtType==MT_EVENT) {
ulFlags = SOF_MUSIC;
} else {
ulFlags = SOF_MUSIC|SOF_LOOP|SOF_NONGAME;
}
// remember volumes
fVolume = fNewVolume;
// start new music file if needed
if (fnNewMusic!="") {
PlaySound( soMusic, fnNewMusic, ulFlags);
// initially, not playing
fCurrentVolume = MUSIC_VOLUMEMIN;
soMusic.Pause();
soMusic.SetVolume(fCurrentVolume, fCurrentVolume);
}
}
// fade out one channel
void FadeOutChannel(INDEX iChannel, INDEX iSubChannel)
{
// find channel's variables
FLOAT &fVolume = (&m_fVolume0)[iChannel];
CSoundObject &soMusic = (&m_soMusic0a)[iChannel*2+iSubChannel];
FLOAT &fCurrentVolume = (&m_fCurrentVolume0a)[iChannel*2+iSubChannel];
// do nothing, if music is not playing
if( !soMusic.IsPlaying()) { return; }
// do nothing, if music is already paused
if( soMusic.IsPaused()) { return; }
// if minimum volume reached
if( fCurrentVolume<MUSIC_VOLUMEMIN) {
// pause music
soMusic.Pause();
} else {
// music isn't even faded yet, so continue on fading it out
fCurrentVolume *= FadeOutFactor( m_tmFade);
soMusic.SetVolume( fCurrentVolume*fVolume, fCurrentVolume*fVolume);
}
}
// fade in one channel
void FadeInChannel(INDEX iChannel, INDEX iSubChannel)
{
// find channel's variables
FLOAT &fVolume = (&m_fVolume0)[iChannel];
CSoundObject &soMusic = (&m_soMusic0a)[iChannel*2+iSubChannel];
FLOAT &fCurrentVolume = (&m_fCurrentVolume0a)[iChannel*2+iSubChannel];
// do nothing, if music is not playing
if( !soMusic.IsPlaying()) { return; }
// resume music if needed
if( soMusic.IsPaused()) {
soMusic.Resume();
}
// fade in music if needed
if( fCurrentVolume<MUSIC_VOLUMEMAX) {
fCurrentVolume *= FadeInFactor( m_tmFade);
fCurrentVolume = ClampUp( fCurrentVolume, 1.0f);
}
soMusic.SetVolume( fCurrentVolume*fVolume, fCurrentVolume*fVolume);
}
// fade one channel in or out
void CrossFadeOneChannel(enum MusicType mtType)
{
INDEX iSubChannelActive = (&m_iSubChannel0)[mtType];
INDEX iSubChannelInactive = (iSubChannelActive+1)%2;
// if it is current channel
if (mtType==m_mtCurrentMusic) {
// fade in active subchannel
FadeInChannel(mtType, iSubChannelActive);
// fade out inactive subchannel
FadeOutChannel(mtType, iSubChannelInactive);
// if it is not current channel
} else {
// fade it out
FadeOutChannel(mtType, 0);
FadeOutChannel(mtType, 1);
}
}
procedures:
// initialize music
Main(EVoid) {
// init as model
InitAsEditorModel();
SetPhysicsFlags(EPF_MODEL_IMMATERIAL);
SetCollisionFlags(ECF_IMMATERIAL);
// set appearance
SetModel(MODEL_MARKER);
SetModelMainTexture(TEXTURE_MARKER);
// wait for game to start
autowait(_pTimer->TickQuantum);
// prepare initial music channel values
ChangeMusicChannel(MT_LIGHT, m_fnMusic0, m_fVolume0);
ChangeMusicChannel(MT_MEDIUM, m_fnMusic1, m_fVolume1);
ChangeMusicChannel(MT_HEAVY, m_fnMusic2, m_fVolume2);
ChangeMusicChannel(MT_EVENT, m_fnMusic3, m_fVolume3);
ChangeMusicChannel(MT_CONTINUOUS, m_fnMusic4, m_fVolume4);
// start with light music
m_mtCurrentMusic = MT_LIGHT;
m_fCurrentVolume0a = MUSIC_VOLUMEMAX*0.98f;
m_tmFade = 0.01f;
CrossFadeOneChannel(MT_LIGHT);
// must react after enemyspawner and all enemies, but before player for proper enemy counting
// (total wait is two ticks so far)
autowait(_pTimer->TickQuantum);
// count enemies in current world
CountEnemies();
// main loop
while(TRUE) {
// wait a bit
wait(0.1f) {
on (ETimer) : {
stop;
};
// if music is to be changed
on (EChangeMusic ecm) : {
// change parameters
ChangeMusicChannel(ecm.mtType, ecm.fnMusic, ecm.fVolume);
// if force started
if (ecm.bForceStart) {
// set as current music
m_mtCurrentMusic = ecm.mtType;
}
// stop waiting
stop;
}
}
// check fuss
CheckOldFussMakers();
// get total score of all active fuss makers
FLOAT fFussScore = GetFussMakersScore();
// if event is on
if (m_mtCurrentMusic==MT_EVENT) {
// if event has ceased playing
if (!m_soMusic3a.IsPlaying() && !m_soMusic3b.IsPlaying()) {
// switch to light music
m_mtCurrentMusic=MT_LIGHT;
}
}
// if heavy fight is on
if (m_mtCurrentMusic==MT_HEAVY) {
// if no more fuss
if (fFussScore<=0.0f) {
// switch to no fight
m_mtCurrentMusic=MT_LIGHT;
}
// if medium fight is on
} else if (m_mtCurrentMusic==MT_MEDIUM) {
// if no more fuss
if (fFussScore<=0.0f) {
// switch to no fight
m_mtCurrentMusic=MT_LIGHT;
// if larger fuss
} else if (fFussScore>=m_fScoreHeavy) {
// switch to heavy fight
m_mtCurrentMusic=MT_HEAVY;
}
// if no fight is on
} else if (m_mtCurrentMusic==MT_LIGHT) {
// if heavy fuss
if (fFussScore>=m_fScoreHeavy) {
// switch to heavy fight
m_mtCurrentMusic=MT_HEAVY;
// if medium fuss
} else if (fFussScore>=m_fScoreMedium) {
// switch to medium fight
m_mtCurrentMusic=MT_MEDIUM;
}
}
// setup fade speed depending on music type
if (m_mtCurrentMusic==MT_LIGHT) {
m_tmFade = 2.0f;
} else if (m_mtCurrentMusic==MT_MEDIUM) {
m_tmFade = 1.0f;
} else if (m_mtCurrentMusic==MT_HEAVY) {
m_tmFade = 1.0f;
} else if (m_mtCurrentMusic==MT_EVENT || m_mtCurrentMusic==MT_CONTINUOUS) {
m_tmFade = 0.5f;
}
// fade all channels
CrossFadeOneChannel(MT_LIGHT);
CrossFadeOneChannel(MT_MEDIUM);
CrossFadeOneChannel(MT_HEAVY);
CrossFadeOneChannel(MT_EVENT);
CrossFadeOneChannel(MT_CONTINUOUS);
}
return;
}
};