1 //**************************************************************************
2 //**
3 //** sn_sonix.c : Heretic 2 : Raven Software, Corp.
4 //**
5 //** $RCSfile: sn_sonix.c,v $
6 //** $Revision: 1.17 $
7 //** $Date: 95/10/05 18:25:44 $
8 //** $Author: paul $
9 //**
10 //**************************************************************************
11
12 #include <string.h>
13 #include <stdio.h>
14
15 #include "doomtype.h"
16 #include "doomstat.h"
17 #include "sc_man.h"
18 #include "m_random.h"
19 #include "s_sound.h"
20 #include "s_sndseq.h"
21 #include "w_wad.h"
22 #include "i_system.h"
23 #include "cmdlib.h"
24 #include "p_local.h"
25 #include "po_man.h"
26 #include "gi.h"
27 #include "templates.h"
28 #include "c_dispatch.h"
29 #include "g_level.h"
30 #include "farchive.h"
31
32 // MACROS ------------------------------------------------------------------
33
34 #define GetCommand(a) ((a) & 255)
35 #define GetData(a) (SDWORD(a) >> 8 )
36 #define MakeCommand(a,b) ((a) | ((b) << 8))
37 #define HexenPlatSeq(a) (a)
38 #define HexenDoorSeq(a) ((a) | 0x40)
39 #define HexenEnvSeq(a) ((a) | 0x80)
40 #define HexenLastSeq (0xff)
41
42 #define TIME_REFERENCE level.time
43
44 // TYPES -------------------------------------------------------------------
45
46 typedef enum
47 {
48 SS_STRING_PLAY,
49 SS_STRING_PLAYUNTILDONE,
50 SS_STRING_PLAYTIME,
51 SS_STRING_PLAYREPEAT,
52 SS_STRING_PLAYLOOP,
53 SS_STRING_DELAY,
54 SS_STRING_DELAYONCE,
55 SS_STRING_DELAYRAND,
56 SS_STRING_VOLUME,
57 SS_STRING_VOLUMEREL,
58 SS_STRING_VOLUMERAND,
59 SS_STRING_END,
60 SS_STRING_STOPSOUND,
61 SS_STRING_ATTENUATION,
62 SS_STRING_NOSTOPCUTOFF,
63 SS_STRING_SLOT,
64 SS_STRING_RANDOMSEQUENCE,
65 SS_STRING_RESTART,
66 // These must be last and in the same order as they appear in seqtype_t
67 SS_STRING_PLATFORM,
68 SS_STRING_DOOR,
69 SS_STRING_ENVIRONMENT
70 } ssstrings_t;
71
72 typedef enum
73 {
74 SS_CMD_NONE,
75 SS_CMD_PLAY,
76 SS_CMD_WAITUNTILDONE, // used by PLAYUNTILDONE
77 SS_CMD_PLAYTIME,
78 SS_CMD_PLAYREPEAT,
79 SS_CMD_PLAYLOOP,
80 SS_CMD_DELAY,
81 SS_CMD_DELAYRAND,
82 SS_CMD_VOLUME,
83 SS_CMD_VOLUMEREL,
84 SS_CMD_VOLUMERAND,
85 SS_CMD_STOPSOUND,
86 SS_CMD_ATTENUATION,
87 SS_CMD_RANDOMSEQUENCE,
88 SS_CMD_BRANCH,
89 SS_CMD_LAST2NOP,
90 SS_CMD_SELECT,
91 SS_CMD_END
92 } sscmds_t;
93
94 struct hexenseq_t
95 {
96 ENamedName Name;
97 BYTE Seqs[4];
98 };
99
100 class DSeqActorNode : public DSeqNode
101 {
102 DECLARE_CLASS(DSeqActorNode, DSeqNode)
103 HAS_OBJECT_POINTERS
104 public:
105 DSeqActorNode(AActor *actor, int sequence, int modenum);
106 void Destroy();
107 void Serialize(FArchive &arc);
MakeSound(int loop,FSoundID id)108 void MakeSound(int loop, FSoundID id)
109 {
110 S_Sound(m_Actor, CHAN_BODY|loop, id, clamp(m_Volume, 0.f, 1.f), m_Atten);
111 }
IsPlaying()112 bool IsPlaying()
113 {
114 return S_IsActorPlayingSomething (m_Actor, CHAN_BODY, m_CurrentSoundID);
115 }
Source()116 void *Source()
117 {
118 return m_Actor;
119 }
SpawnChild(int seqnum)120 DSeqNode *SpawnChild(int seqnum)
121 {
122 return SN_StartSequence (m_Actor, seqnum, SEQ_NOTRANS, m_ModeNum, true);
123 }
124 private:
DSeqActorNode()125 DSeqActorNode() {}
126 TObjPtr<AActor> m_Actor;
127 };
128
129 class DSeqPolyNode : public DSeqNode
130 {
131 DECLARE_CLASS(DSeqPolyNode, DSeqNode)
132 public:
133 DSeqPolyNode(FPolyObj *poly, int sequence, int modenum);
134 void Destroy();
135 void Serialize(FArchive &arc);
MakeSound(int loop,FSoundID id)136 void MakeSound(int loop, FSoundID id)
137 {
138 S_Sound (m_Poly, CHAN_BODY|loop, id, clamp(m_Volume, 0.f, 1.f), m_Atten);
139 }
IsPlaying()140 bool IsPlaying()
141 {
142 return S_GetSoundPlayingInfo (m_Poly, m_CurrentSoundID);
143 }
Source()144 void *Source()
145 {
146 return m_Poly;
147 }
SpawnChild(int seqnum)148 DSeqNode *SpawnChild (int seqnum)
149 {
150 return SN_StartSequence (m_Poly, seqnum, SEQ_NOTRANS, m_ModeNum, true);
151 }
152 private:
DSeqPolyNode()153 DSeqPolyNode () {}
154 FPolyObj *m_Poly;
155 };
156
157 class DSeqSectorNode : public DSeqNode
158 {
159 DECLARE_CLASS(DSeqSectorNode, DSeqNode)
160 public:
161 DSeqSectorNode(sector_t *sec, int chan, int sequence, int modenum);
162 void Destroy();
163 void Serialize(FArchive &arc);
MakeSound(int loop,FSoundID id)164 void MakeSound(int loop, FSoundID id)
165 {
166 Channel = (Channel & 7) | CHAN_AREA | loop;
167 S_Sound(m_Sector, Channel, id, clamp(m_Volume, 0.f, 1.f), m_Atten);
168 }
IsPlaying()169 bool IsPlaying()
170 {
171 return S_GetSoundPlayingInfo (m_Sector, m_CurrentSoundID);
172 }
Source()173 void *Source()
174 {
175 return m_Sector;
176 }
SpawnChild(int seqnum)177 DSeqNode *SpawnChild(int seqnum)
178 {
179 return SN_StartSequence (m_Sector, Channel, seqnum, SEQ_NOTRANS, m_ModeNum, true);
180 }
181 int Channel;
182 private:
DSeqSectorNode()183 DSeqSectorNode() {}
184 sector_t *m_Sector;
185 };
186
187 // When destroyed, destroy the sound sequences too.
188 struct FSoundSequencePtrArray : public TArray<FSoundSequence *>
189 {
~FSoundSequencePtrArrayFSoundSequencePtrArray190 ~FSoundSequencePtrArray()
191 {
192 for (unsigned int i = 0; i < Size(); ++i)
193 {
194 M_Free((*this)[i]);
195 }
196 }
197 };
198
199 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
200
201 static void AssignTranslations (FScanner &sc, int seq, seqtype_t type);
202 static void AssignHexenTranslations (void);
203 static void AddSequence (int curseq, FName seqname, FName slot, int stopsound, const TArray<DWORD> &ScriptTemp);
204 static int FindSequence (const char *searchname);
205 static int FindSequence (FName seqname);
206 static bool TwiddleSeqNum (int &sequence, seqtype_t type);
207
208 // PUBLIC DATA DEFINITIONS -------------------------------------------------
209
210 FSoundSequencePtrArray Sequences;
211 int ActiveSequences;
212 DSeqNode *DSeqNode::SequenceListHead;
213
214 // PRIVATE DATA DEFINITIONS ------------------------------------------------
215
216 static const char *SSStrings[] = {
217 "play",
218 "playuntildone",
219 "playtime",
220 "playrepeat",
221 "playloop",
222 "delay",
223 "delayonce",
224 "delayrand",
225 "volume",
226 "volumerel",
227 "volumerand",
228 "end",
229 "stopsound",
230 "attenuation",
231 "nostopcutoff",
232 "slot",
233 "randomsequence",
234 "restart",
235 // These must be last and in the same order as they appear in seqtype_t
236 "platform",
237 "door",
238 "environment",
239 NULL
240 };
241
242 struct SSAttenuation
243 {
244 const char *name;
245 float value;
246 };
247
248 static const SSAttenuation Attenuations[] = {
249 { "none", ATTN_NONE },
250 { "normal", ATTN_NORM },
251 { "idle", ATTN_IDLE },
252 { "static", ATTN_STATIC },
253 { "surround", ATTN_NONE },
254 { NULL, 0}
255 };
256
257 static const hexenseq_t HexenSequences[] = {
258 { NAME_Platform, { HexenPlatSeq(0), HexenPlatSeq(1), HexenPlatSeq(3), HexenLastSeq } },
259 { NAME_PlatformMetal, { HexenPlatSeq(2), HexenLastSeq } },
260 { NAME_Silence, { HexenPlatSeq(4), HexenDoorSeq(4), HexenLastSeq } },
261 { NAME_Lava, { HexenPlatSeq(5), HexenDoorSeq(5), HexenLastSeq } },
262 { NAME_Water, { HexenPlatSeq(6), HexenDoorSeq(6), HexenLastSeq } },
263 { NAME_Ice, { HexenPlatSeq(7), HexenDoorSeq(7), HexenLastSeq } },
264 { NAME_Earth, { HexenPlatSeq(8), HexenDoorSeq(8), HexenLastSeq } },
265 { NAME_PlatformMetal2, { HexenPlatSeq(9), HexenLastSeq } },
266 { NAME_DoorNormal, { HexenDoorSeq(0), HexenLastSeq } },
267 { NAME_DoorHeavy, { HexenDoorSeq(1), HexenLastSeq } },
268 { NAME_DoorMetal, { HexenDoorSeq(2), HexenLastSeq } },
269 { NAME_DoorCreak, { HexenDoorSeq(3), HexenLastSeq } },
270 { NAME_DoorMetal2, { HexenDoorSeq(9), HexenLastSeq } },
271 { NAME_Wind, { HexenEnvSeq(10), HexenLastSeq } },
272 { NAME_None, {0} }
273 };
274
275 static int SeqTrans[64*3];
276
277 static FRandom pr_sndseq ("SndSeq");
278
279 // CODE --------------------------------------------------------------------
280
SerializeSequences(FArchive & arc)281 void DSeqNode::SerializeSequences (FArchive &arc)
282 {
283 arc << SequenceListHead;
284 }
285
286 IMPLEMENT_POINTY_CLASS (DSeqNode)
DECLARE_POINTER(m_ChildSeqNode)287 DECLARE_POINTER(m_ChildSeqNode)
288 DECLARE_POINTER(m_ParentSeqNode)
289 DECLARE_POINTER(m_Next)
290 DECLARE_POINTER(m_Prev)
291 END_POINTERS
292
293 DSeqNode::DSeqNode ()
294 : m_SequenceChoices(0)
295 {
296 m_Next = m_Prev = m_ChildSeqNode = m_ParentSeqNode = NULL;
297 }
298
Serialize(FArchive & arc)299 void DSeqNode::Serialize (FArchive &arc)
300 {
301 int seqOffset;
302 unsigned int i;
303
304 Super::Serialize (arc);
305 if (arc.IsStoring ())
306 {
307 seqOffset = (int)SN_GetSequenceOffset (m_Sequence, m_SequencePtr);
308 arc << seqOffset
309 << m_DelayUntilTic
310 << m_Volume
311 << m_Atten
312 << m_ModeNum
313 << m_Next
314 << m_Prev
315 << m_ChildSeqNode
316 << m_ParentSeqNode
317 << m_CurrentSoundID
318 << Sequences[m_Sequence]->SeqName;
319
320 arc.WriteCount (m_SequenceChoices.Size());
321 for (i = 0; i < m_SequenceChoices.Size(); ++i)
322 {
323 arc << Sequences[m_SequenceChoices[i]]->SeqName;
324 }
325 }
326 else
327 {
328 FName seqName;
329 int delayTics = 0;
330 FSoundID id;
331 float volume;
332 float atten = ATTN_NORM;
333 int seqnum;
334 unsigned int numchoices;
335
336 arc << seqOffset
337 << delayTics
338 << volume
339 << atten
340 << m_ModeNum
341 << m_Next
342 << m_Prev
343 << m_ChildSeqNode
344 << m_ParentSeqNode
345 << id
346 << seqName;
347
348 seqnum = FindSequence (seqName);
349 if (seqnum >= 0)
350 {
351 ActivateSequence (seqnum);
352 }
353 else
354 {
355 I_Error ("Unknown sound sequence '%s'\n", seqName.GetChars());
356 // Can I just Destroy() here instead of erroring out?
357 }
358
359 ChangeData (seqOffset, delayTics - TIME_REFERENCE, volume, id);
360
361 numchoices = arc.ReadCount();
362 m_SequenceChoices.Resize(numchoices);
363 for (i = 0; i < numchoices; ++i)
364 {
365 arc << seqName;
366 m_SequenceChoices[i] = FindSequence (seqName);
367 }
368 }
369 }
370
Destroy()371 void DSeqNode::Destroy()
372 {
373 // If this sequence was launched by a parent sequence, advance that
374 // sequence now.
375 if (m_ParentSeqNode != NULL && m_ParentSeqNode->m_ChildSeqNode == this)
376 {
377 m_ParentSeqNode->m_SequencePtr++;
378 m_ParentSeqNode->m_ChildSeqNode = NULL;
379 m_ParentSeqNode = NULL;
380 }
381 if (SequenceListHead == this)
382 {
383 SequenceListHead = m_Next;
384 GC::WriteBarrier(m_Next);
385 }
386 if (m_Prev)
387 {
388 m_Prev->m_Next = m_Next;
389 GC::WriteBarrier(m_Prev, m_Next);
390 }
391 if (m_Next)
392 {
393 m_Next->m_Prev = m_Prev;
394 GC::WriteBarrier(m_Next, m_Prev);
395 }
396 ActiveSequences--;
397 Super::Destroy();
398 }
399
StopAndDestroy()400 void DSeqNode::StopAndDestroy ()
401 {
402 if (m_ChildSeqNode != NULL)
403 {
404 m_ChildSeqNode->StopAndDestroy();
405 }
406 Destroy();
407 }
408
AddChoice(int seqnum,seqtype_t type)409 void DSeqNode::AddChoice (int seqnum, seqtype_t type)
410 {
411 if (TwiddleSeqNum (seqnum, type))
412 {
413 m_SequenceChoices.Push (seqnum);
414 }
415 }
416
GetSequenceName() const417 FName DSeqNode::GetSequenceName () const
418 {
419 return Sequences[m_Sequence]->SeqName;
420 }
421
422 IMPLEMENT_POINTY_CLASS (DSeqActorNode)
DECLARE_POINTER(m_Actor)423 DECLARE_POINTER (m_Actor)
424 END_POINTERS
425
426 void DSeqActorNode::Serialize (FArchive &arc)
427 {
428 Super::Serialize (arc);
429 arc << m_Actor;
430 }
431
IMPLEMENT_CLASS(DSeqPolyNode)432 IMPLEMENT_CLASS (DSeqPolyNode)
433
434 void DSeqPolyNode::Serialize (FArchive &arc)
435 {
436 Super::Serialize (arc);
437 arc << m_Poly;
438 }
439
IMPLEMENT_CLASS(DSeqSectorNode)440 IMPLEMENT_CLASS (DSeqSectorNode)
441
442 void DSeqSectorNode::Serialize (FArchive &arc)
443 {
444 Super::Serialize (arc);
445 arc << m_Sector << Channel;
446 }
447
448 //==========================================================================
449 //
450 // AssignTranslations
451 //
452 //==========================================================================
453
AssignTranslations(FScanner & sc,int seq,seqtype_t type)454 static void AssignTranslations (FScanner &sc, int seq, seqtype_t type)
455 {
456 sc.Crossed = false;
457
458 while (sc.GetString() && !sc.Crossed)
459 {
460 if (IsNum(sc.String))
461 {
462 SeqTrans[(atoi(sc.String) & 63) + type * 64] = seq;
463 }
464 }
465 sc.UnGet();
466 }
467
468 //==========================================================================
469 //
470 // AssignHexenTranslations
471 //
472 //==========================================================================
473
AssignHexenTranslations(void)474 static void AssignHexenTranslations (void)
475 {
476 unsigned int i, j, seq;
477
478 for (i = 0; HexenSequences[i].Name != NAME_None; i++)
479 {
480 for (seq = 0; seq < Sequences.Size(); seq++)
481 {
482 if (Sequences[seq] != NULL && HexenSequences[i].Name == Sequences[seq]->SeqName)
483 break;
484 }
485 if (seq == Sequences.Size())
486 continue;
487
488 for (j = 0; j < 4 && HexenSequences[i].Seqs[j] != HexenLastSeq; j++)
489 {
490 int trans;
491
492 if (HexenSequences[i].Seqs[j] & 0x40)
493 {
494 trans = 64 * SEQ_DOOR;
495 }
496 else if (HexenSequences[i].Seqs[j] & 0x80)
497 trans = 64 * SEQ_ENVIRONMENT;
498 else
499 trans = 64 * SEQ_PLATFORM;
500
501 SeqTrans[trans + (HexenSequences[i].Seqs[j] & 0x3f)] = seq;
502 }
503 }
504 }
505
506 //==========================================================================
507 //
508 // S_ClearSndSeq
509 //
510 //==========================================================================
511
S_ClearSndSeq()512 void S_ClearSndSeq()
513 {
514 for (unsigned int i = 0; i < Sequences.Size(); i++)
515 {
516 if (Sequences[i])
517 {
518 M_Free(Sequences[i]);
519 }
520 }
521 Sequences.Clear();
522 }
523
524 //==========================================================================
525 //
526 // S_ParseSndSeq
527 //
528 //==========================================================================
529
S_ParseSndSeq(int levellump)530 void S_ParseSndSeq (int levellump)
531 {
532 TArray<DWORD> ScriptTemp;
533 int lastlump, lump;
534 char seqtype = ':';
535 FName seqname;
536 FName slot;
537 int stopsound;
538 int delaybase;
539 float volumebase;
540 int curseq = -1;
541 fixed_t val;
542
543 // First free the old SNDSEQ data. This allows us to reload this for each level
544 // and specify a level specific SNDSEQ lump!
545 S_ClearSndSeq();
546
547 // be gone, compiler warnings
548 stopsound = 0;
549
550 memset (SeqTrans, -1, sizeof(SeqTrans));
551 lastlump = 0;
552
553 while (((lump = Wads.FindLump ("SNDSEQ", &lastlump)) != -1 || levellump != -1) && levellump != -2)
554 {
555 if (lump == -1)
556 {
557 lump = levellump;
558 levellump = -2;
559 }
560 FScanner sc(lump);
561 while (sc.GetString ())
562 {
563 if (*sc.String == ':' || *sc.String == '[')
564 {
565 if (curseq != -1)
566 {
567 sc.ScriptError ("S_ParseSndSeq: Nested Script Error");
568 }
569 seqname = sc.String + 1;
570 seqtype = sc.String[0];
571 for (curseq = 0; curseq < (int)Sequences.Size(); curseq++)
572 {
573 if (Sequences[curseq] != NULL && Sequences[curseq]->SeqName == seqname)
574 {
575 M_Free (Sequences[curseq]);
576 Sequences[curseq] = NULL;
577 break;
578 }
579 }
580 if (curseq == (int)Sequences.Size())
581 {
582 Sequences.Push (NULL);
583 }
584 ScriptTemp.Clear();
585 stopsound = 0;
586 slot = NAME_None;
587 if (seqtype == '[')
588 {
589 sc.SetCMode (true);
590 ScriptTemp.Push (0); // to be filled when definition is complete
591 }
592 continue;
593 }
594 if (curseq == -1)
595 {
596 continue;
597 }
598 if (seqtype == '[')
599 {
600 if (sc.String[0] == ']')
601 { // End of this definition
602 ScriptTemp[0] = MakeCommand(SS_CMD_SELECT, (ScriptTemp.Size()-1)/2);
603 AddSequence (curseq, seqname, slot, stopsound, ScriptTemp);
604 curseq = -1;
605 sc.SetCMode (false);
606 }
607 else
608 { // Add a selection
609 sc.UnGet();
610 if (sc.CheckNumber())
611 {
612 ScriptTemp.Push (sc.Number);
613 sc.MustGetString();
614 ScriptTemp.Push (FName(sc.String));
615 }
616 else
617 {
618 seqtype_t seqtype = seqtype_t(sc.MustMatchString (SSStrings + SS_STRING_PLATFORM));
619 AssignTranslations (sc, curseq, seqtype);
620 }
621 }
622 continue;
623 }
624 switch (sc.MustMatchString (SSStrings))
625 {
626 case SS_STRING_PLAYUNTILDONE:
627 sc.MustGetString ();
628 ScriptTemp.Push(MakeCommand(SS_CMD_PLAY, S_FindSound (sc.String)));
629 ScriptTemp.Push(MakeCommand(SS_CMD_WAITUNTILDONE, 0));
630 break;
631
632 case SS_STRING_PLAY:
633 sc.MustGetString ();
634 ScriptTemp.Push(MakeCommand(SS_CMD_PLAY, S_FindSound (sc.String)));
635 break;
636
637 case SS_STRING_PLAYTIME:
638 sc.MustGetString ();
639 ScriptTemp.Push(MakeCommand(SS_CMD_PLAY, S_FindSound (sc.String)));
640 sc.MustGetNumber ();
641 ScriptTemp.Push(MakeCommand(SS_CMD_DELAY, sc.Number));
642 break;
643
644 case SS_STRING_PLAYREPEAT:
645 sc.MustGetString ();
646 ScriptTemp.Push(MakeCommand (SS_CMD_PLAYREPEAT, S_FindSound (sc.String)));
647 break;
648
649 case SS_STRING_PLAYLOOP:
650 sc.MustGetString ();
651 ScriptTemp.Push(MakeCommand (SS_CMD_PLAYLOOP, S_FindSound (sc.String)));
652 sc.MustGetNumber ();
653 ScriptTemp.Push(sc.Number);
654 break;
655
656 case SS_STRING_DELAY:
657 sc.MustGetNumber ();
658 ScriptTemp.Push(MakeCommand(SS_CMD_DELAY, sc.Number));
659 break;
660
661 case SS_STRING_DELAYONCE:
662 sc.MustGetNumber ();
663 ScriptTemp.Push(MakeCommand(SS_CMD_DELAY, sc.Number));
664 ScriptTemp.Push(MakeCommand(SS_CMD_LAST2NOP, 0));
665 break;
666
667 case SS_STRING_DELAYRAND:
668 sc.MustGetNumber ();
669 delaybase = sc.Number;
670 ScriptTemp.Push(MakeCommand(SS_CMD_DELAYRAND, sc.Number));
671 sc.MustGetNumber ();
672 ScriptTemp.Push(MAX(1, sc.Number - delaybase + 1));
673 break;
674
675 case SS_STRING_VOLUME: // volume is in range 0..100
676 sc.MustGetFloat ();
677 ScriptTemp.Push(MakeCommand(SS_CMD_VOLUME, int(sc.Float * (FRACUNIT/100.f))));
678 break;
679
680 case SS_STRING_VOLUMEREL:
681 sc.MustGetFloat ();
682 ScriptTemp.Push(MakeCommand(SS_CMD_VOLUMEREL, int(sc.Float * (FRACUNIT/100.f))));
683 break;
684
685 case SS_STRING_VOLUMERAND:
686 sc.MustGetFloat ();
687 volumebase = float(sc.Float);
688 ScriptTemp.Push(MakeCommand(SS_CMD_VOLUMERAND, int(sc.Float * (FRACUNIT/100.f))));
689 sc.MustGetFloat ();
690 ScriptTemp.Push(int((sc.Float - volumebase) * (256/100.f)));
691 break;
692
693 case SS_STRING_STOPSOUND:
694 sc.MustGetString ();
695 stopsound = S_FindSound (sc.String);
696 ScriptTemp.Push(MakeCommand(SS_CMD_STOPSOUND, 0));
697 break;
698
699 case SS_STRING_NOSTOPCUTOFF:
700 stopsound = -1;
701 ScriptTemp.Push(MakeCommand(SS_CMD_STOPSOUND, 0));
702 break;
703
704 case SS_STRING_ATTENUATION:
705 if (sc.CheckFloat())
706 {
707 val = FLOAT2FIXED(sc.Float);
708 }
709 else
710 {
711 sc.MustGetString ();
712 val = sc.MustMatchString(&Attenuations[0].name, sizeof(Attenuations[0])) << FRACBITS;
713 }
714 ScriptTemp.Push(MakeCommand(SS_CMD_ATTENUATION, val));
715 break;
716
717 case SS_STRING_RANDOMSEQUENCE:
718 ScriptTemp.Push(MakeCommand(SS_CMD_RANDOMSEQUENCE, 0));
719 break;
720
721 case SS_STRING_RESTART:
722 ScriptTemp.Push(MakeCommand(SS_CMD_BRANCH, ScriptTemp.Size()));
723 break;
724
725 case SS_STRING_END:
726 AddSequence (curseq, seqname, slot, stopsound, ScriptTemp);
727 curseq = -1;
728 break;
729
730 case SS_STRING_PLATFORM:
731 AssignTranslations (sc, curseq, SEQ_PLATFORM);
732 break;
733
734 case SS_STRING_DOOR:
735 AssignTranslations (sc, curseq, SEQ_DOOR);
736 break;
737
738 case SS_STRING_ENVIRONMENT:
739 AssignTranslations (sc, curseq, SEQ_ENVIRONMENT);
740 break;
741
742 case SS_STRING_SLOT:
743 sc.MustGetString();
744 slot = sc.String;
745 break;
746 }
747 }
748 if (curseq > 0)
749 {
750 sc.ScriptError("End of file encountered before the final sequence ended.");
751 }
752 }
753
754 if (gameinfo.gametype == GAME_Hexen)
755 AssignHexenTranslations ();
756 }
757
AddSequence(int curseq,FName seqname,FName slot,int stopsound,const TArray<DWORD> & ScriptTemp)758 static void AddSequence (int curseq, FName seqname, FName slot, int stopsound, const TArray<DWORD> &ScriptTemp)
759 {
760 Sequences[curseq] = (FSoundSequence *)M_Malloc (sizeof(FSoundSequence) + sizeof(DWORD)*ScriptTemp.Size());
761 Sequences[curseq]->SeqName = seqname;
762 Sequences[curseq]->Slot = slot;
763 Sequences[curseq]->StopSound = FSoundID(stopsound);
764 memcpy (Sequences[curseq]->Script, &ScriptTemp[0], sizeof(DWORD)*ScriptTemp.Size());
765 Sequences[curseq]->Script[ScriptTemp.Size()] = MakeCommand(SS_CMD_END, 0);
766 }
767
DSeqNode(int sequence,int modenum)768 DSeqNode::DSeqNode (int sequence, int modenum)
769 : m_ModeNum(modenum), m_SequenceChoices(0)
770 {
771 ActivateSequence (sequence);
772 if (!SequenceListHead)
773 {
774 SequenceListHead = this;
775 m_Next = m_Prev = NULL;
776 }
777 else
778 {
779 SequenceListHead->m_Prev = this; GC::WriteBarrier(SequenceListHead->m_Prev, this);
780 m_Next = SequenceListHead; GC::WriteBarrier(this, SequenceListHead);
781 SequenceListHead = this;
782 m_Prev = NULL;
783 }
784 GC::WriteBarrier(this);
785 m_ParentSeqNode = m_ChildSeqNode = NULL;
786 }
787
ActivateSequence(int sequence)788 void DSeqNode::ActivateSequence (int sequence)
789 {
790 m_SequencePtr = Sequences[sequence]->Script;
791 m_Sequence = sequence;
792 m_DelayUntilTic = 0;
793 m_StopSound = Sequences[sequence]->StopSound;
794 m_CurrentSoundID = 0;
795 m_Volume = 1; // Start at max volume...
796 m_Atten = ATTN_IDLE; // ...and idle attenuation
797
798 ActiveSequences++;
799 }
800
DSeqActorNode(AActor * actor,int sequence,int modenum)801 DSeqActorNode::DSeqActorNode (AActor *actor, int sequence, int modenum)
802 : DSeqNode (sequence, modenum),
803 m_Actor (actor)
804 {
805 }
806
DSeqPolyNode(FPolyObj * poly,int sequence,int modenum)807 DSeqPolyNode::DSeqPolyNode (FPolyObj *poly, int sequence, int modenum)
808 : DSeqNode (sequence, modenum),
809 m_Poly (poly)
810 {
811 }
812
DSeqSectorNode(sector_t * sec,int chan,int sequence,int modenum)813 DSeqSectorNode::DSeqSectorNode (sector_t *sec, int chan, int sequence, int modenum)
814 : DSeqNode (sequence, modenum),
815 Channel (chan),
816 m_Sector (sec)
817 {
818 }
819
820 //==========================================================================
821 //
822 // SN_StartSequence
823 //
824 //==========================================================================
825
TwiddleSeqNum(int & sequence,seqtype_t type)826 static bool TwiddleSeqNum (int &sequence, seqtype_t type)
827 {
828 if (type < SEQ_NUMSEQTYPES)
829 {
830 // [GrafZahl] Needs some range checking:
831 // Sector_ChangeSound doesn't do it so this makes invalid sequences play nothing.
832 if (sequence >= 0 && sequence < 64)
833 {
834 sequence = SeqTrans[sequence + type * 64];
835 }
836 else
837 {
838 return false;
839 }
840 }
841
842 return ((size_t)sequence < Sequences.Size() && Sequences[sequence] != NULL);
843 }
844
SN_StartSequence(AActor * actor,int sequence,seqtype_t type,int modenum,bool nostop)845 DSeqNode *SN_StartSequence (AActor *actor, int sequence, seqtype_t type, int modenum, bool nostop)
846 {
847 if (!nostop)
848 {
849 SN_StopSequence (actor); // Stop any previous sequence
850 }
851 if (TwiddleSeqNum (sequence, type))
852 {
853 return new DSeqActorNode (actor, sequence, modenum);
854 }
855 return NULL;
856 }
857
SN_StartSequence(sector_t * sector,int chan,int sequence,seqtype_t type,int modenum,bool nostop)858 DSeqNode *SN_StartSequence (sector_t *sector, int chan, int sequence, seqtype_t type, int modenum, bool nostop)
859 {
860 if (!nostop)
861 {
862 SN_StopSequence (sector, chan);
863 }
864 if (TwiddleSeqNum (sequence, type))
865 {
866 return new DSeqSectorNode (sector, chan, sequence, modenum);
867 }
868 return NULL;
869 }
870
SN_StartSequence(FPolyObj * poly,int sequence,seqtype_t type,int modenum,bool nostop)871 DSeqNode *SN_StartSequence (FPolyObj *poly, int sequence, seqtype_t type, int modenum, bool nostop)
872 {
873 if (!nostop)
874 {
875 SN_StopSequence (poly);
876 }
877 if (TwiddleSeqNum (sequence, type))
878 {
879 return new DSeqPolyNode (poly, sequence, modenum);
880 }
881 return NULL;
882 }
883
884 //==========================================================================
885 //
886 // SN_StartSequence (named)
887 //
888 //==========================================================================
889
SN_StartSequence(AActor * actor,const char * seqname,int modenum)890 DSeqNode *SN_StartSequence (AActor *actor, const char *seqname, int modenum)
891 {
892 int seqnum = FindSequence(seqname);
893 if (seqnum >= 0)
894 {
895 return SN_StartSequence (actor, seqnum, SEQ_NOTRANS, modenum);
896 }
897 return NULL;
898 }
899
SN_StartSequence(AActor * actor,FName seqname,int modenum)900 DSeqNode *SN_StartSequence (AActor *actor, FName seqname, int modenum)
901 {
902 int seqnum = FindSequence(seqname);
903 if (seqnum >= 0)
904 {
905 return SN_StartSequence (actor, seqnum, SEQ_NOTRANS, modenum);
906 }
907 return NULL;
908 }
909
SN_StartSequence(sector_t * sec,int chan,const char * seqname,int modenum)910 DSeqNode *SN_StartSequence (sector_t *sec, int chan, const char *seqname, int modenum)
911 {
912 int seqnum = FindSequence(seqname);
913 if (seqnum >= 0)
914 {
915 return SN_StartSequence (sec, chan, seqnum, SEQ_NOTRANS, modenum);
916 }
917 return NULL;
918 }
919
SN_StartSequence(sector_t * sec,int chan,FName seqname,int modenum)920 DSeqNode *SN_StartSequence (sector_t *sec, int chan, FName seqname, int modenum)
921 {
922 int seqnum = FindSequence(seqname);
923 if (seqnum >= 0)
924 {
925 return SN_StartSequence (sec, chan, seqnum, SEQ_NOTRANS, modenum);
926 }
927 return NULL;
928 }
929
SN_StartSequence(FPolyObj * poly,const char * seqname,int modenum)930 DSeqNode *SN_StartSequence (FPolyObj *poly, const char *seqname, int modenum)
931 {
932 int seqnum = FindSequence(seqname);
933 if (seqnum >= 0)
934 {
935 return SN_StartSequence (poly, seqnum, SEQ_NOTRANS, modenum);
936 }
937 return NULL;
938 }
939
FindSequence(const char * searchname)940 static int FindSequence (const char *searchname)
941 {
942 FName seqname(searchname, true);
943
944 if (seqname != NAME_None)
945 {
946 return FindSequence(seqname);
947 }
948 return -1;
949 }
950
FindSequence(FName seqname)951 static int FindSequence (FName seqname)
952 {
953 for (int i = Sequences.Size(); i-- > 0; )
954 {
955 if (Sequences[i] != NULL && seqname == Sequences[i]->SeqName)
956 {
957 return i;
958 }
959 }
960 return -1;
961 }
962
963 //==========================================================================
964 //
965 // SN_CheckSequence
966 //
967 // Returns the sound sequence playing in the sector on the given channel,
968 // if any.
969 //
970 //==========================================================================
971
SN_CheckSequence(sector_t * sector,int chan)972 DSeqNode *SN_CheckSequence(sector_t *sector, int chan)
973 {
974 for (DSeqNode *node = DSeqNode::FirstSequence(); node; )
975 {
976 DSeqNode *next = node->NextSequence();
977 if (node->Source() == sector)
978 {
979 assert(node->IsKindOf(RUNTIME_CLASS(DSeqSectorNode)));
980 if ((static_cast<DSeqSectorNode *>(node)->Channel & 7) == chan)
981 {
982 return node;
983 }
984 }
985 node = next;
986 }
987 return NULL;
988 }
989
990 //==========================================================================
991 //
992 // SN_StopSequence
993 //
994 //==========================================================================
995
SN_StopSequence(AActor * actor)996 void SN_StopSequence (AActor *actor)
997 {
998 SN_DoStop (actor);
999 }
1000
SN_StopSequence(sector_t * sector,int chan)1001 void SN_StopSequence (sector_t *sector, int chan)
1002 {
1003 DSeqNode *node = SN_CheckSequence(sector, chan);
1004 if (node != NULL)
1005 {
1006 node->StopAndDestroy();
1007 }
1008 }
1009
SN_StopSequence(FPolyObj * poly)1010 void SN_StopSequence (FPolyObj *poly)
1011 {
1012 SN_DoStop (poly);
1013 }
1014
SN_DoStop(void * source)1015 void SN_DoStop (void *source)
1016 {
1017 DSeqNode *node;
1018
1019 for (node = DSeqNode::FirstSequence(); node; )
1020 {
1021 DSeqNode *next = node->NextSequence();
1022 if (node->Source() == source)
1023 {
1024 node->StopAndDestroy ();
1025 }
1026 node = next;
1027 }
1028 }
1029
Destroy()1030 void DSeqActorNode::Destroy ()
1031 {
1032 if (m_StopSound >= 0)
1033 S_StopSound (m_Actor, CHAN_BODY);
1034 if (m_StopSound >= 1)
1035 MakeSound (0, m_StopSound);
1036 Super::Destroy();
1037 }
1038
Destroy()1039 void DSeqSectorNode::Destroy ()
1040 {
1041 if (m_StopSound >= 0)
1042 S_StopSound (m_Sector, Channel & 7);
1043 if (m_StopSound >= 1)
1044 MakeSound (0, m_StopSound);
1045 Super::Destroy();
1046 }
1047
Destroy()1048 void DSeqPolyNode::Destroy ()
1049 {
1050 if (m_StopSound >= 0)
1051 S_StopSound (m_Poly, CHAN_BODY);
1052 if (m_StopSound >= 1)
1053 MakeSound (0, m_StopSound);
1054 Super::Destroy();
1055 }
1056
1057 //==========================================================================
1058 //
1059 // SN_IsMakingLoopingSound
1060 //
1061 //==========================================================================
1062
SN_IsMakingLoopingSound(sector_t * sector)1063 bool SN_IsMakingLoopingSound (sector_t *sector)
1064 {
1065 DSeqNode *node;
1066
1067 for (node = DSeqNode::FirstSequence (); node; )
1068 {
1069 DSeqNode *next = node->NextSequence();
1070 if (node->Source() == (void *)sector)
1071 {
1072 return !!(static_cast<DSeqSectorNode *>(node)->Channel & CHAN_LOOP);
1073 }
1074 node = next;
1075 }
1076 return false;
1077 }
1078
1079 //==========================================================================
1080 //
1081 // SN_UpdateActiveSequences
1082 //
1083 //==========================================================================
1084
Tick()1085 void DSeqNode::Tick ()
1086 {
1087 if (TIME_REFERENCE < m_DelayUntilTic)
1088 {
1089 return;
1090 }
1091 for (;;)
1092 {
1093 switch (GetCommand(*m_SequencePtr))
1094 {
1095 case SS_CMD_NONE:
1096 m_SequencePtr++;
1097 break;
1098
1099 case SS_CMD_PLAY:
1100 if (!IsPlaying())
1101 {
1102 m_CurrentSoundID = FSoundID(GetData(*m_SequencePtr));
1103 MakeSound (0, m_CurrentSoundID);
1104 }
1105 m_SequencePtr++;
1106 break;
1107
1108 case SS_CMD_WAITUNTILDONE:
1109 if (!IsPlaying())
1110 {
1111 m_SequencePtr++;
1112 m_CurrentSoundID = 0;
1113 }
1114 else
1115 {
1116 return;
1117 }
1118 break;
1119
1120 case SS_CMD_PLAYREPEAT:
1121 if (!IsPlaying())
1122 {
1123 // Does not advance sequencePtr, so it will repeat as necessary.
1124 m_CurrentSoundID = FSoundID(GetData(*m_SequencePtr));
1125 MakeSound (CHAN_LOOP, m_CurrentSoundID);
1126 }
1127 return;
1128
1129 case SS_CMD_PLAYLOOP:
1130 // Like SS_CMD_PLAYREPEAT, sequencePtr is not advanced, so this
1131 // command will repeat until the sequence is stopped.
1132 m_CurrentSoundID = FSoundID(GetData(m_SequencePtr[0]));
1133 MakeSound (0, m_CurrentSoundID);
1134 m_DelayUntilTic = TIME_REFERENCE + m_SequencePtr[1];
1135 return;
1136
1137 case SS_CMD_RANDOMSEQUENCE:
1138 // If there's nothing to choose from, then there's nothing to do here.
1139 if (m_SequenceChoices.Size() == 0)
1140 {
1141 m_SequencePtr++;
1142 }
1143 else if (m_ChildSeqNode == NULL)
1144 {
1145 int choice = pr_sndseq() % m_SequenceChoices.Size();
1146 m_ChildSeqNode = SpawnChild (m_SequenceChoices[choice]);
1147 GC::WriteBarrier(this, m_ChildSeqNode);
1148 if (m_ChildSeqNode == NULL)
1149 { // Failed, so skip to next instruction.
1150 m_SequencePtr++;
1151 }
1152 else
1153 { // Copy parameters to the child and link it to this one.
1154 m_ChildSeqNode->m_Volume = m_Volume;
1155 m_ChildSeqNode->m_Atten = m_Atten;
1156 m_ChildSeqNode->m_ParentSeqNode = this;
1157 return;
1158 }
1159 }
1160 else
1161 {
1162 // If we get here, then the child sequence is playing, and it
1163 // will advance our sequence pointer for us when it finishes.
1164 return;
1165 }
1166 break;
1167
1168 case SS_CMD_DELAY:
1169 m_DelayUntilTic = TIME_REFERENCE + GetData(*m_SequencePtr);
1170 m_SequencePtr++;
1171 m_CurrentSoundID = 0;
1172 return;
1173
1174 case SS_CMD_DELAYRAND:
1175 m_DelayUntilTic = TIME_REFERENCE + GetData(m_SequencePtr[0]) + pr_sndseq(m_SequencePtr[1]);
1176 m_SequencePtr += 2;
1177 m_CurrentSoundID = 0;
1178 return;
1179
1180 case SS_CMD_VOLUME:
1181 m_Volume = GetData(*m_SequencePtr) / float(FRACUNIT);
1182 m_SequencePtr++;
1183 break;
1184
1185 case SS_CMD_VOLUMEREL:
1186 // like SS_CMD_VOLUME, but the new volume is added to the old volume
1187 m_Volume += GetData(*m_SequencePtr) / float(FRACUNIT);
1188 m_SequencePtr++;
1189 break;
1190
1191 case SS_CMD_VOLUMERAND:
1192 // like SS_CMD_VOLUME, but the new volume is chosen randomly from a range
1193 m_Volume = GetData(m_SequencePtr[0]) / float(FRACUNIT) + (pr_sndseq() % m_SequencePtr[1]) / 255.f;
1194 m_SequencePtr += 2;
1195 break;
1196
1197 case SS_CMD_STOPSOUND:
1198 // Wait until something else stops the sequence
1199 return;
1200
1201 case SS_CMD_ATTENUATION:
1202 m_Atten = FIXED2FLOAT(GetData(*m_SequencePtr));
1203 m_SequencePtr++;
1204 break;
1205
1206 case SS_CMD_BRANCH:
1207 m_SequencePtr -= GetData(*m_SequencePtr);
1208 break;
1209
1210 case SS_CMD_SELECT:
1211 { // Completely transfer control to the choice matching m_ModeNum.
1212 // If no match is found, then just advance to the next command
1213 // in this sequence, which should be SS_CMD_END.
1214 int numchoices = GetData(*m_SequencePtr++);
1215 int i;
1216
1217 for (i = 0; i < numchoices; ++i)
1218 {
1219 if (m_SequencePtr[i*2] == m_ModeNum)
1220 {
1221 int seqnum = FindSequence (ENamedName(m_SequencePtr[i*2+1]));
1222 if (seqnum >= 0)
1223 { // Found a match, and it's a good one too.
1224 ActiveSequences--;
1225 ActivateSequence (seqnum);
1226 break;
1227 }
1228 }
1229 }
1230 if (i == numchoices)
1231 { // No match (or no good match) was found.
1232 m_SequencePtr += numchoices * 2;
1233 }
1234 }
1235 break;
1236
1237 case SS_CMD_LAST2NOP:
1238 *(m_SequencePtr - 1) = MakeCommand(SS_CMD_NONE, 0);
1239 *m_SequencePtr = MakeCommand(SS_CMD_NONE, 0);
1240 m_SequencePtr++;
1241 break;
1242
1243 case SS_CMD_END:
1244 Destroy ();
1245 return;
1246
1247 default:
1248 Printf ("Corrupted sound sequence: %s\n", Sequences[m_Sequence]->SeqName.GetChars());
1249 Destroy ();
1250 return;
1251 }
1252 }
1253 }
1254
SN_UpdateActiveSequences(void)1255 void SN_UpdateActiveSequences (void)
1256 {
1257 DSeqNode *node;
1258
1259 if (!ActiveSequences || paused)
1260 { // No sequences currently playing/game is paused
1261 return;
1262 }
1263 for (node = DSeqNode::FirstSequence(); node; node = node->NextSequence())
1264 {
1265 node->Tick ();
1266 }
1267 }
1268
1269 //==========================================================================
1270 //
1271 // SN_StopAllSequences
1272 //
1273 //==========================================================================
1274
SN_StopAllSequences(void)1275 void SN_StopAllSequences (void)
1276 {
1277 DSeqNode *node;
1278
1279 for (node = DSeqNode::FirstSequence(); node; )
1280 {
1281 DSeqNode *next = node->NextSequence();
1282 node->m_StopSound = 0; // don't play any stop sounds
1283 node->Destroy ();
1284 node = next;
1285 }
1286 }
1287
1288 //==========================================================================
1289 //
1290 // SN_GetSequenceOffset
1291 //
1292 //==========================================================================
1293
SN_GetSequenceOffset(int sequence,SDWORD * sequencePtr)1294 ptrdiff_t SN_GetSequenceOffset (int sequence, SDWORD *sequencePtr)
1295 {
1296 return sequencePtr - Sequences[sequence]->Script;
1297 }
1298
1299 //==========================================================================
1300 //
1301 // SN_GetSequenceSlot
1302 //
1303 //==========================================================================
1304
SN_GetSequenceSlot(int sequence,seqtype_t type)1305 FName SN_GetSequenceSlot (int sequence, seqtype_t type)
1306 {
1307 if (TwiddleSeqNum (sequence, type))
1308 {
1309 return Sequences[sequence]->Slot;
1310 }
1311 return NAME_None;
1312 }
1313
1314 //==========================================================================
1315 //
1316 // SN_MarkPrecacheSounds
1317 //
1318 // Marks all sounds played by this sequence for precaching.
1319 //
1320 //==========================================================================
1321
SN_MarkPrecacheSounds(int sequence,seqtype_t type)1322 void SN_MarkPrecacheSounds(int sequence, seqtype_t type)
1323 {
1324 if (TwiddleSeqNum(sequence, type))
1325 {
1326 FSoundSequence *seq = Sequences[sequence];
1327
1328 seq->StopSound.MarkUsed();
1329 for (int i = 0; GetCommand(seq->Script[i]) != SS_CMD_END; ++i)
1330 {
1331 int cmd = GetCommand(seq->Script[i]);
1332 if (cmd == SS_CMD_PLAY || cmd == SS_CMD_PLAYREPEAT || cmd == SS_CMD_PLAYLOOP)
1333 {
1334 FSoundID(GetData(seq->Script[i])).MarkUsed();
1335 }
1336 }
1337 }
1338 }
1339
1340 //==========================================================================
1341 //
1342 // SN_ChangeNodeData
1343 //
1344 // nodeNum zero is the first node
1345 //==========================================================================
1346
SN_ChangeNodeData(int nodeNum,int seqOffset,int delayTics,float volume,int currentSoundID)1347 void SN_ChangeNodeData (int nodeNum, int seqOffset, int delayTics, float volume,
1348 int currentSoundID)
1349 {
1350 int i;
1351 DSeqNode *node;
1352
1353 i = 0;
1354 node = DSeqNode::FirstSequence();
1355 while (node && i < nodeNum)
1356 {
1357 node = node->NextSequence();
1358 i++;
1359 }
1360 if (!node)
1361 { // reached the end of the list before finding the nodeNum-th node
1362 return;
1363 }
1364 node->ChangeData (seqOffset, delayTics, volume, currentSoundID);
1365 }
1366
ChangeData(int seqOffset,int delayTics,float volume,FSoundID currentSoundID)1367 void DSeqNode::ChangeData (int seqOffset, int delayTics, float volume, FSoundID currentSoundID)
1368 {
1369 m_DelayUntilTic = TIME_REFERENCE + delayTics;
1370 m_Volume = volume;
1371 m_SequencePtr += seqOffset;
1372 m_CurrentSoundID = currentSoundID;
1373 }
1374
1375 //==========================================================================
1376 //
1377 // FindMode
1378 //
1379 // Finds the sequence this sequence and mode combination selects.
1380 //
1381 //==========================================================================
1382
FindMode(int seqnum,int mode)1383 static int FindMode(int seqnum, int mode)
1384 {
1385 if (seqnum <= 0)
1386 { // Sequence does not exist.
1387 return seqnum;
1388 }
1389 FSoundSequence *seq = Sequences[seqnum];
1390 if (GetCommand(seq->Script[0]) != SS_CMD_SELECT)
1391 { // This sequence doesn't select any others.
1392 return seqnum;
1393 }
1394 // Search for the desired mode amongst the selections
1395 int nummodes = GetData(seq->Script[0]);
1396 for (int i = 0; i < nummodes; ++i)
1397 {
1398 if (seq->Script[1 + i*2] == mode)
1399 {
1400 return FindSequence(ENamedName(seq->Script[1 + i*2 + 1]));
1401 }
1402 }
1403 // The mode isn't selected, which means it stays on this sequence.
1404 return seqnum;
1405 }
1406
1407 //==========================================================================
1408 //
1409 // FindModeDeep
1410 //
1411 // Finds the final sequence this sequence and mode combination selects.
1412 //
1413 //==========================================================================
1414
FindModeDeep(int seqnum,int mode)1415 static int FindModeDeep(int seqnum, int mode)
1416 {
1417 int newseqnum = FindMode(seqnum, mode);
1418
1419 while (newseqnum != seqnum)
1420 {
1421 seqnum = newseqnum;
1422 newseqnum = FindMode(seqnum, mode);
1423 }
1424 return seqnum;
1425 }
1426
1427 //==========================================================================
1428 //
1429 // SN_AreModesSame
1430 //
1431 // Returns true if mode1 and mode2 represent the same sequence.
1432 //
1433 //==========================================================================
1434
SN_AreModesSame(int seqnum,seqtype_t type,int mode1,int mode2)1435 bool SN_AreModesSame(int seqnum, seqtype_t type, int mode1, int mode2)
1436 {
1437 if (mode1 == mode2)
1438 { // Obviously they're the same.
1439 return true;
1440 }
1441 if (TwiddleSeqNum(seqnum, type))
1442 {
1443 int mode1seq = FindModeDeep(seqnum, mode1);
1444 int mode2seq = FindModeDeep(seqnum, mode2);
1445 return mode1seq == mode2seq;
1446 }
1447 // The sequence doesn't exist, so that makes both modes equally nonexistant.
1448 return true;
1449 }
1450
SN_AreModesSame(const char * name,int mode1,int mode2)1451 bool SN_AreModesSame(const char *name, int mode1, int mode2)
1452 {
1453 int seqnum = FindSequence(name);
1454 if (seqnum >= 0)
1455 {
1456 return SN_AreModesSame(seqnum, SEQ_NOTRANS, mode1, mode2);
1457 }
1458 // The sequence doesn't exist, so that makes both modes equally nonexistant.
1459 return true;
1460 }
1461
1462 //==========================================================================
1463 //
1464 // CCMD playsequence
1465 //
1466 // Causes the player to play a sound sequence.
1467 //==========================================================================
1468
CCMD(playsequence)1469 CCMD(playsequence)
1470 {
1471 if (argv.argc() < 2 || argv.argc() > 3)
1472 {
1473 Printf ("Usage: playsequence <sound sequence name> [choice number]\n");
1474 }
1475 else
1476 {
1477 SN_StartSequence (players[consoleplayer].mo, argv[1], argv.argc() > 2 ? atoi(argv[2]) : 0);
1478 }
1479 }
1480