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