1 //**************************************************************************
2 //**
3 //**	##   ##    ##    ##   ##   ####     ####   ###     ###
4 //**	##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
5 //**	 ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
6 //**	 ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
7 //**	  ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
8 //**	   #    ##    ##    #      ####     ####   ##       ##
9 //**
10 //**	$Id: p_acs.cpp 4297 2010-06-03 22:49:00Z firebrand_kh $
11 //**
12 //**	Copyright (C) 1999-2006 Jānis Legzdiņš
13 //**
14 //**	This program is free software; you can redistribute it and/or
15 //**  modify it under the terms of the GNU General Public License
16 //**  as published by the Free Software Foundation; either version 2
17 //**  of the License, or (at your option) any later version.
18 //**
19 //**	This program is distributed in the hope that it will be useful,
20 //**  but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //**  GNU General Public License for more details.
23 //**
24 //**************************************************************************
25 //**
26 //**	This file includes code from ZDoom, copyright 1998-2004 Randy Heit,
27 //**  all rights reserved, with the following licence:
28 //**
29 //** Redistribution and use in source and binary forms, with or without
30 //** modification, are permitted provided that the following conditions
31 //** are met:
32 //**
33 //** 1. Redistributions of source code must retain the above copyright
34 //**    notice, this list of conditions and the following disclaimer.
35 //** 2. Redistributions in binary form must reproduce the above copyright
36 //**    notice, this list of conditions and the following disclaimer in the
37 //**    documentation and/or other materials provided with the distribution.
38 //** 3. The name of the author may not be used to endorse or promote products
39 //**    derived from this software without specific prior written permission.
40 //**
41 //** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
42 //** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
43 //** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
44 //** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
45 //** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
46 //** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
47 //** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
48 //** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
49 //** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
50 //** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51 //**
52 //**************************************************************************
53 
54 // HEADER FILES ------------------------------------------------------------
55 
56 #include "gamedefs.h"
57 #include "sv_local.h"
58 #include "p_acs.h"
59 
60 // MACROS ------------------------------------------------------------------
61 
62 // TYPES -------------------------------------------------------------------
63 
64 //
65 //	Internal engine limits
66 //
67 enum
68 {
69 	MAX_ACS_SCRIPT_VARS	= 20,
70 	MAX_ACS_MAP_VARS	= 128,
71 };
72 
73 enum EAcsFormat
74 {
75 	ACS_Old,
76 	ACS_Enhanced,
77 	ACS_LittleEnhanced,
78 	ACS_Unknown
79 };
80 
81 //	Script flags.
82 enum
83 {
84 	SCRIPTF_Net = 0x0001	//	Safe to "puke" in multiplayer.
85 };
86 
87 struct VAcsHeader
88 {
89 	char		Marker[4];
90 	vint32		InfoOffset;
91 	vint32		Code;
92 };
93 
94 struct VAcsInfo
95 {
96 	vuint16		Number;
97 	vuint8		Type;
98 	vuint8		ArgCount;
99 	vuint8*		Address;
100 	vuint16		Flags;
101 	vuint16		VarCount;
102 	VAcs*		RunningScript;
103 };
104 
105 struct VAcsFunction
106 {
107 	vuint8		ArgCount;
108 	vuint8		LocalCount;
109 	vuint8		HasReturnValue;
110 	vuint8		ImportNum;
111 	vuint32		Address;
112 };
113 
114 //
115 //	A action code scripts object module - level's BEHAVIOR lump or library.
116 //
117 class VAcsObject
118 {
119 private:
120 	friend class VAcsLevel;
121 
122 	struct VArrayInfo
123 	{
124 		vint32		Size;
125 		vint32*		Data;
126 	};
127 
128 	EAcsFormat			Format;
129 
130 	vint32				LumpNum;
131 	vint32				LibraryID;
132 
133 	vint32				DataSize;
134 	vuint8*				Data;
135 
136 	vuint8*				Chunks;
137 
138 	vint32				NumScripts;
139 	VAcsInfo*			Scripts;
140 
141 	VAcsFunction*		Functions;
142 	vint32				NumFunctions;
143 
144 	vint32				NumStrings;
145 	char**				Strings;
146 	VName*				LowerCaseNames;
147 
148 	vint32				MapVarStore[MAX_ACS_MAP_VARS];
149 
150 	vint32				NumArrays;
151 	VArrayInfo*			ArrayStore;
152 	vint32				NumTotalArrays;
153 	VArrayInfo**		Arrays;
154 
155 	TArray<VAcsObject*>	Imports;
156 
157 	void LoadOldObject();
158 	void LoadEnhancedObject();
159 	void UnencryptStrings();
160 	int FindFunctionName(const char* Name) const;
161 	int FindMapVarName(const char* Name) const;
162 	int FindMapArray(const char* Name) const;
163 	int FindStringInChunk(vuint8* Chunk, const char* Name) const;
164 	vuint8* FindChunk(const char* id) const;
165 	vuint8* NextChunk(vuint8* prev) const;
166 	void Serialise(VStream& Strm);
167 	void StartTypedACScripts(int Type, int Arg1, int Arg2, int Arg3,
168 		VEntity* Activator, bool Always, bool RunNow);
169 
170 public:
171 	VAcsLevel*			Level;
172 	vint32*				MapVars[MAX_ACS_MAP_VARS];
173 
174 	VAcsObject(VAcsLevel* ALevel, int Lump);
175 	~VAcsObject();
176 
177 	vuint8* OffsetToPtr(int);
178 	int PtrToOffset(vuint8*);
GetFormat() const179 	EAcsFormat GetFormat() const
180 	{
181 		return Format;
182 	}
GetNumScripts() const183 	int GetNumScripts() const
184 	{
185 		return NumScripts;
186 	}
GetScriptInfo(int i)187 	VAcsInfo& GetScriptInfo(int i)
188 	{
189 		return Scripts[i];
190 	}
GetString(int i) const191 	VStr GetString(int i) const
192 	{
193 		if (i >= NumStrings)
194 		{
195 			return "";
196 		}
197 		VStr Ret = Strings[i];
198 		if (!Ret.IsValidUtf8())
199 		{
200 			Ret = Ret.Latin1ToUtf8();
201 		}
202 		return Ret;
203 	}
GetNameLowerCase(int i)204 	VName GetNameLowerCase(int i)
205 	{
206 		if (LowerCaseNames[i] == NAME_None)
207 		{
208 			LowerCaseNames[i] = *GetString(i).ToLower();
209 		}
210 		return LowerCaseNames[i];
211 	}
GetLibraryID() const212 	int GetLibraryID() const
213 	{
214 		return LibraryID;
215 	}
216 	VAcsInfo* FindScript(int Number) const;
217 	VAcsFunction* GetFunction(int funcnum, VAcsObject*& Object);
218 	int GetArrayVal(int ArrayIdx, int Index);
219 	void SetArrayVal(int ArrayIdx, int Index, int Value);
220 };
221 
222 struct VAcsCallReturn
223 {
224 	int				ReturnAddress;
225 	VAcsFunction*	ReturnFunction;
226 	VAcsObject*		ReturnObject;
227 	vuint8			bDiscardResult;
228 	vuint8			Pad[3];
229 };
230 
231 class VAcs : public VThinker
232 {
233 	DECLARE_CLASS(VAcs, VThinker, 0)
234 	NO_DEFAULT_CONSTRUCTOR(VAcs)
235 
236 	enum
237 	{
238 		ASTE_Running,
239 		ASTE_Suspended,
240 		ASTE_WaitingForTag,
241 		ASTE_WaitingForPoly,
242 		ASTE_WaitingForScriptStart,
243 		ASTE_WaitingForScript,
244 		ASTE_Terminating
245 	};
246 
247 	VEntity*		Activator;
248 	line_t*			line;
249 	vint32 			side;
250 	vint32 			number;
251 	VAcsInfo*		info;
252 	vuint8			State;
253 	float			DelayTime;
254 	vint32			WaitValue;
255 	vint32*			LocalVars;
256 	vuint8*			InstructionPointer;
257 	VAcsObject*		ActiveObject;
258 	int				HudWidth;
259 	int				HudHeight;
260 	VName			Font;
261 
262 	void Destroy();
263 	void Serialise(VStream&);
264 	void ClearReferences();
265 	int RunScript(float);
266 	void Tick(float);
267 
268 private:
269 	enum { ACS_STACK_DEPTH		= 4096 };
270 
271 	enum EScriptAction
272 	{
273 		SCRIPT_Continue,
274 		SCRIPT_Stop,
275 		SCRIPT_Terminate,
276 	};
277 
278 	//
279 	//	Constants used by scripts.
280 	//
281 
282 	enum EGameMode
283 	{
284 		GAME_SINGLE_PLAYER,
285 		GAME_NET_COOPERATIVE,
286 		GAME_NET_DEATHMATCH,
287 		GAME_TITLE_MAP
288 	};
289 
290 	enum ETexturePosition
291 	{
292 		TEXTURE_TOP,
293 		TEXTURE_MIDDLE,
294 		TEXTURE_BOTTOM
295 	};
296 
297 	enum
298 	{
299 		BLOCK_NOTHING,
300 		BLOCK_CREATURES,
301 		BLOCK_EVERYTHING,
302 		BLOCK_RAILING,
303 		BLOCK_PLAYERS,
304 	};
305 
306 	enum
307 	{
308 		LEVELINFO_PAR_TIME,
309 		LEVELINFO_CLUSTERNUM,
310 		LEVELINFO_LEVELNUM,
311 		LEVELINFO_TOTAL_SECRETS,
312 		LEVELINFO_FOUND_SECRETS,
313 		LEVELINFO_TOTAL_ITEMS,
314 		LEVELINFO_FOUND_ITEMS,
315 		LEVELINFO_TOTAL_MONSTERS,
316 		LEVELINFO_KILLED_MONSTERS,
317 		LEVELINFO_SUCK_TIME
318 	};
319 
320 	//	Flags for ReplaceTextures
321 	enum
322 	{
323 		NOT_BOTTOM			= 1,
324 		NOT_MIDDLE			= 2,
325 		NOT_TOP				= 4,
326 		NOT_FLOOR			= 8,
327 		NOT_CEILING			= 16,
328 	};
329 
330 	enum
331 	{
332 		HUDMSG_PLAIN,
333 		HUDMSG_FADEOUT,
334 		HUDMSG_TYPEON,
335 		HUDMSG_FADEINOUT,
336 
337 		HUDMSG_LOG			= 0x80000000,
338 		HUDMSG_COLORSTRING	= 0x40000000,
339 	};
340 
GetStr(int Index)341 	VStr GetStr(int Index)
342 	{
343 		return ActiveObject->Level->GetString(Index);
344 	}
GetName(int Index)345 	VName GetName(int Index)
346 	{
347 		return *ActiveObject->Level->GetString(Index);
348 	}
GetNameLowerCase(int Index)349 	VName GetNameLowerCase(int Index)
350 	{
351 		return ActiveObject->Level->GetNameLowerCase(Index);
352 	}
GetName8(int Index)353 	VName GetName8(int Index)
354 	{
355 		return VName(*ActiveObject->Level->GetString(Index),
356 			VName::AddLower8);
357 	}
358 
EntityFromTID(int TID,VEntity * Default)359 	VEntity* EntityFromTID(int TID, VEntity* Default)
360 	{
361 		if (!TID)
362 		{
363 			return Default;
364 		}
365 		else
366 		{
367 			return Level->FindMobjFromTID(TID, NULL);
368 		}
369 	}
370 	int FindSectorFromTag(int, int);
371 };
372 
373 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
374 
375 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
376 
377 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
378 
379 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
380 
381 // PUBLIC DATA DEFINITIONS -------------------------------------------------
382 
383 // PRIVATE DATA DEFINITIONS ------------------------------------------------
384 
IMPLEMENT_CLASS(V,Acs)385 IMPLEMENT_CLASS(V, Acs)
386 
387 // CODE --------------------------------------------------------------------
388 
389 //==========================================================================
390 //
391 //	VAcsObject::VAcsObject
392 //
393 //==========================================================================
394 
395 VAcsObject::VAcsObject(VAcsLevel* ALevel, int Lump)
396 : Level(ALevel)
397 {
398 	guard(VAcsObject::VAcsObject);
399 	Format = ACS_Unknown;
400 	LumpNum = Lump;
401 	LibraryID = 0;
402 	DataSize = 0;
403 	Data = NULL;
404 	Chunks = NULL;
405 	NumScripts = 0;
406 	Scripts = NULL;
407 	NumFunctions = 0;
408 	Functions = NULL;
409 	NumStrings = 0;
410 	Strings = NULL;
411 	LowerCaseNames = NULL;
412 	NumArrays = 0;
413 	ArrayStore = NULL;
414 	NumTotalArrays = 0;
415 	Arrays = NULL;
416 	memset(MapVarStore, 0, sizeof(MapVarStore));
417 
418 	if (Lump < 0)
419     {
420 		return;
421     }
422 	if (W_LumpLength(Lump) < (int)sizeof(VAcsHeader))
423     {
424 		GCon->Log("Behavior lump too small");
425 		return;
426     }
427 
428 	VStream* Strm = W_CreateLumpReaderNum(Lump);
429 	Data = new vuint8[Strm->TotalSize()];
430 	Strm->Serialise(Data, Strm->TotalSize());
431 	delete Strm;
432 	Strm = NULL;
433 	VAcsHeader* header = (VAcsHeader*)Data;
434 
435 	//	Check header.
436 	if (header->Marker[0] != 'A' || header->Marker[1] != 'C' ||
437 		header->Marker[2] != 'S')
438 	{
439 		return;
440 	}
441 	//	Determine format.
442 	switch (header->Marker[3])
443 	{
444 	case 0:
445 		Format = ACS_Old;
446 		break;
447 	case 'E':
448 		Format = ACS_Enhanced;
449 		break;
450 	case 'e':
451 		Format = ACS_LittleEnhanced;
452 		break;
453 	default:
454 		return;
455 	}
456 
457 	DataSize = W_LumpLength(Lump);
458 
459 	if (Format == ACS_Old)
460 	{
461 		vuint32 dirofs = LittleLong(header->InfoOffset);
462 		vuint8* pretag = Data + dirofs - 4;
463 
464 		Chunks = Data + DataSize;
465 		//	Check for redesigned ACSE/ACSe
466 		if (dirofs >= 6 * 4 && pretag[0] == 'A' &&
467 			pretag[1] == 'C' && pretag[2] == 'S' &&
468 			(pretag[3] == 'e' || pretag[3] == 'E'))
469 		{
470 			Format = (pretag[3] == 'e') ? ACS_LittleEnhanced : ACS_Enhanced;
471 			Chunks = Data + LittleLong(*(int*)(Data + dirofs - 8));
472 			//	Forget about the compatibility cruft at the end of the lump
473 			DataSize = dirofs - 8;
474 		}
475 	}
476 	else
477 	{
478 		Chunks = Data + LittleLong(header->InfoOffset);
479 	}
480 
481 	if (Format == ACS_Old)
482 	{
483 		LoadOldObject();
484 	}
485 	else
486 	{
487 		LoadEnhancedObject();
488 	}
489 	unguard;
490 }
491 
492 //==========================================================================
493 //
494 //	VAcsObject::~VAcsObject
495 //
496 //==========================================================================
497 
~VAcsObject()498 VAcsObject::~VAcsObject()
499 {
500 	guard(VAcsObject::~VAcsObject);
501 	delete[] Scripts;
502 	Scripts = NULL;
503 	delete[] Strings;
504 	Strings = NULL;
505 	delete[] LowerCaseNames;
506 	LowerCaseNames = NULL;
507 	for (int i = 0; i < NumArrays; i++)
508 	{
509 		delete[] ArrayStore[i].Data;
510 		ArrayStore[i].Data = NULL;
511 	}
512 	if (ArrayStore)
513 	{
514 		delete[] ArrayStore;
515 		ArrayStore = NULL;
516 	}
517 	if (Arrays)
518 	{
519 		delete[] Arrays;
520 		Arrays = NULL;
521 	}
522 	delete[] Data;
523 	Data = NULL;
524 	unguard;
525 }
526 
527 //==========================================================================
528 //
529 //	VAcsObject::LoadOldObject
530 //
531 //==========================================================================
532 
LoadOldObject()533 void VAcsObject::LoadOldObject()
534 {
535 	guard(VAcsObject::LoadOldObject);
536 	int i;
537 	int *buffer;
538 	VAcsInfo *info;
539 	VAcsHeader *header;
540 
541 	//	Add to loaded objects.
542 	LibraryID = Level->LoadedObjects.Append(this) << 16;
543 
544 	header = (VAcsHeader*)Data;
545 
546 	//	Load script info.
547 	buffer = (int*)(Data + LittleLong(header->InfoOffset));
548 	NumScripts = LittleLong(*buffer++);
549 	if (NumScripts == 0)
550 	{
551 		//	Empty behavior lump
552 		return;
553 	}
554 	Scripts = new VAcsInfo[NumScripts];
555 	memset(Scripts, 0, NumScripts * sizeof(VAcsInfo));
556 	for (i = 0, info = Scripts; i < NumScripts; i++, info++)
557 	{
558 		info->Number = LittleLong(*buffer) % 1000;
559 		info->Type = LittleLong(*buffer) / 1000;
560 		buffer++;
561 		info->Address = OffsetToPtr(LittleLong(*buffer++));
562 		info->ArgCount = LittleLong(*buffer++);
563 		info->Flags = 0;
564 		info->VarCount = MAX_ACS_SCRIPT_VARS;
565 	}
566 
567 	//	Load strings.
568 	NumStrings = LittleLong(*buffer++);
569 	Strings = new char*[NumStrings];
570 	LowerCaseNames = new VName[NumStrings];
571 	for (i = 0; i < NumStrings; i++)
572 	{
573 		Strings[i] = (char*)Data + LittleLong(buffer[i]);
574 		LowerCaseNames[i] = NAME_None;
575 	}
576 
577 	//	Set up map vars.
578 	memset(MapVarStore, 0, sizeof(MapVarStore));
579 	for (i = 0; i < MAX_ACS_MAP_VARS; i++)
580 	{
581 		MapVars[i] = &MapVarStore[i];
582 	}
583 	unguard;
584 }
585 
586 //==========================================================================
587 //
588 //	VAcsObject::LoadEnhancedObject
589 //
590 //==========================================================================
591 
LoadEnhancedObject()592 void VAcsObject::LoadEnhancedObject()
593 {
594 	guard(VAcsObject::LoadEnhancedObject);
595 	int i;
596 	int *buffer;
597 	VAcsInfo *info;
598 
599 	//	Load scripts.
600 	buffer = (int*)FindChunk("SPTR");
601 	if (Data[3] != 0)
602 	{
603 		NumScripts = LittleLong(buffer[1]) / 12;
604 		Scripts = new VAcsInfo[NumScripts];
605 		memset(Scripts, 0, NumScripts * sizeof(VAcsInfo));
606 		buffer += 2;
607 
608 		for (i = 0, info = Scripts; i < NumScripts; i++, info++)
609 		{
610 			info->Number = LittleShort(*(short*)buffer);
611 			info->Type = LittleShort(((short*)buffer)[1]);
612 			buffer++;
613 			info->Address = OffsetToPtr(LittleLong(*buffer++));
614 			info->ArgCount = LittleLong(*buffer++);
615 			info->Flags = 0;
616 			info->VarCount = MAX_ACS_SCRIPT_VARS;
617 		}
618 	}
619 	else
620 	{
621 		NumScripts = LittleLong(buffer[1]) / 8;
622 		Scripts = new VAcsInfo[NumScripts];
623 		memset(Scripts, 0, NumScripts * sizeof(VAcsInfo));
624 		buffer += 2;
625 
626 		for (i = 0, info = Scripts; i < NumScripts; i++, info++)
627 		{
628 			info->Number = LittleShort(*(short*)buffer);
629 			info->Type = ((vuint8*)buffer)[2];
630 			info->ArgCount = ((vuint8*)buffer)[3];
631 			buffer++;
632 			info->Address = OffsetToPtr(LittleLong(*buffer++));
633 			info->Flags = 0;
634 			info->VarCount = MAX_ACS_SCRIPT_VARS;
635 		}
636 	}
637 
638 	//	Load script flags.
639 	buffer = (int*)FindChunk("SFLG");
640 	if (buffer)
641 	{
642 		int count = LittleLong(buffer[1]) / 4;
643 		buffer += 2;
644 		for (int i = 0; i < count; i++, buffer++)
645 		{
646 			VAcsInfo* info = FindScript(LittleShort(((word*)buffer)[0]));
647 			if (info)
648 			{
649 				info->Flags = LittleShort(((word*)buffer)[1]);
650 			}
651 		}
652 	}
653 
654 	//	Load script var counts
655 	buffer = (int*)FindChunk("SVCT");
656 	if (buffer)
657 	{
658 		int count = LittleLong(buffer[1]) / 4;
659 		buffer += 2;
660 		for (i = 0; i < count; i++, buffer++)
661 		{
662 			VAcsInfo* info = FindScript(LittleShort(((word*)buffer)[0]));
663 			if (info)
664 			{
665 				info->VarCount = LittleShort(((word*)buffer)[1]);
666 				//	Make sure it's at least 3 so in SpawnScript we can safely
667 				// assign args to first 3 variables.
668 				if (info->VarCount < 3)
669 					info->VarCount = 3;
670 			}
671 		}
672 	}
673 
674 	//	Load functions.
675 	buffer = (int*)FindChunk("FUNC");
676 	if (buffer)
677 	{
678 		NumFunctions = LittleLong(buffer[1]) / 8;
679 		Functions = (VAcsFunction*)(buffer + 2);
680 		for (i = 0; i < NumFunctions; i++)
681 			Functions[i].Address = LittleLong(Functions[i].Address);
682 	}
683 
684 	//	Unencrypt strings.
685 	UnencryptStrings();
686 
687 	//	A temporary hack.
688 	buffer = (int*)FindChunk("STRL");
689 	if (buffer)
690 	{
691 		buffer += 2;
692 		NumStrings = LittleLong(buffer[1]);
693 		Strings = new char*[NumStrings];
694 		LowerCaseNames = new VName[NumStrings];
695 		for(i = 0; i < NumStrings; i++)
696 		{
697 			Strings[i] = (char*)buffer + LittleLong(buffer[i + 3]);
698 			LowerCaseNames[i] = NAME_None;
699 		}
700 	}
701 
702 	//	Initialise this object's map variable pointers to defaults. They can
703 	// be changed later once the imported modules are loaded.
704 	for (i = 0; i < MAX_ACS_MAP_VARS; i++)
705 	{
706 		MapVars[i] = &MapVarStore[i];
707 	}
708 
709 	//	Initialise this object's map variables.
710 	memset(MapVarStore, 0, sizeof(MapVarStore));
711 	buffer = (int*)FindChunk("MINI");
712 	while (buffer)
713 	{
714 		int numvars = LittleLong(buffer[1]) / 4 - 1;
715 		int firstvar = LittleLong(buffer[2]);
716 		for (i = 0; i < numvars; i++)
717 		{
718 			MapVarStore[firstvar + i] = LittleLong(buffer[3 + i]);
719 		}
720 		buffer = (int*)NextChunk((vuint8*)buffer);
721 	}
722 
723 	//	Create arrays.
724 	buffer = (int*)FindChunk("ARAY");
725 	if (buffer)
726 	{
727 		NumArrays = LittleLong(buffer[1]) / 8;
728 		ArrayStore = new VArrayInfo[NumArrays];
729 		memset(ArrayStore, 0, sizeof(*ArrayStore) * NumArrays);
730 		for (i = 0; i < NumArrays; ++i)
731 		{
732 			MapVarStore[LittleLong(buffer[2 + i * 2])] = i;
733 			ArrayStore[i].Size = LittleLong(buffer[3 + i * 2]);
734 			ArrayStore[i].Data = new vint32[ArrayStore[i].Size];
735 			memset(ArrayStore[i].Data, 0, ArrayStore[i].Size * sizeof(vint32));
736 		}
737 	}
738 
739 	//	Initialise arrays.
740 	buffer = (int*)FindChunk("AINI");
741 	while (buffer)
742 	{
743 		int arraynum = MapVarStore[LittleLong(buffer[2])];
744 		if ((unsigned)arraynum < (unsigned)NumArrays)
745 		{
746 			int initsize = (LittleLong(buffer[1]) - 4) / 4;
747 			if (initsize > ArrayStore[arraynum].Size)
748 				initsize = ArrayStore[arraynum].Size;
749 			int *elems = ArrayStore[arraynum].Data;
750 			for (i = 0; i < initsize; i++)
751 			{
752 				elems[i] = LittleLong(buffer[3 + i]);
753 			}
754 		}
755 		buffer = (int*)NextChunk((vuint8*)buffer);
756 	}
757 
758 	//	Start setting up array pointers.
759 	NumTotalArrays = NumArrays;
760 	buffer = (int*)FindChunk("AIMP");
761 	if (buffer)
762 	{
763 		NumTotalArrays += LittleLong(buffer[2]);
764 	}
765 	if (NumTotalArrays)
766 	{
767 		Arrays = new VArrayInfo*[NumTotalArrays];
768 		for (i = 0; i < NumArrays; ++i)
769 		{
770 			Arrays[i] = &ArrayStore[i];
771 		}
772 	}
773 
774 	//	Now that everything is set up, record this object as being among
775 	// the loaded objects. We need to do this before resolving any imports,
776 	// because an import might (indirectly) need to resolve exports in this
777 	// module. The only things that can be exported are functions and map
778 	// variables, which must already be present if they're exported, so this
779 	// is okay.
780 	LibraryID = Level->LoadedObjects.Append(this) << 16;
781 
782 	//	Tag the library ID to any map variables that are initialised with
783 	// strings.
784 	if (LibraryID)
785 	{
786 		buffer = (int*)FindChunk("MSTR");
787 		if (buffer)
788 		{
789 			for (i = 0; i < LittleLong(buffer[1]) / 4; i++)
790 			{
791 				MapVarStore[LittleLong(buffer[i + 2])] |= LibraryID;
792 			}
793 		}
794 
795 		buffer = (int*)FindChunk("ASTR");
796 		if (buffer)
797 		{
798 			for (i = 0; i < LittleLong(buffer[1]) / 4; i++)
799 			{
800 				int arraynum = MapVarStore[LittleLong(buffer[i + 2])];
801 				if ((unsigned)arraynum < (unsigned)NumArrays)
802 				{
803 					int *elems = ArrayStore[arraynum].Data;
804 					for (int j = ArrayStore[arraynum].Size; j > 0; j--, elems++)
805 					{
806 						*elems |= LibraryID;
807 					}
808 				}
809 			}
810 		}
811 	}
812 
813 	//	Library loading.
814 	buffer = (int*)FindChunk("LOAD");
815 	if (buffer)
816 	{
817 		const char* const parse = (char*)&buffer[2];
818 		for (i = 0; i < LittleLong(buffer[1]); i++)
819 		{
820 			if (parse[i])
821 			{
822 				VAcsObject* Object = NULL;
823 				int Lump = W_CheckNumForName(VName(&parse[i],
824 					VName::AddLower8), WADNS_ACSLibrary);
825 				if (Lump < 0)
826 				{
827 					GCon->Logf("Could not find ACS library %s.", &parse[i]);
828 				}
829 				else
830 				{
831 					Object = Level->LoadObject(Lump);
832 				}
833 				Imports.Append(Object);
834 				do ; while (parse[++i]);
835 			}
836 		}
837 
838 		//	Go through each imported object in order and resolve all
839 		// imported functions and map variables.
840 		for (i = 0; i < Imports.Num(); i++)
841 		{
842 			VAcsObject* lib = Imports[i];
843 			int j;
844 
845 			if (!lib)
846 				continue;
847 
848 			// Resolve functions
849 			buffer = (int*)FindChunk("FNAM");
850 			for (j = 0; j < NumFunctions; j++)
851 			{
852 				VAcsFunction *func = &Functions[j];
853 				if (func->Address != 0 || func->ImportNum != 0)
854 					continue;
855 
856 				int libfunc = lib->FindFunctionName((char*)(buffer + 2) +
857 					LittleLong(buffer[3 + j]));
858 				if (libfunc < 0)
859 					continue;
860 
861 				VAcsFunction* realfunc = &lib->Functions[libfunc];
862 				//	Make sure that the library really defines this
863 				// function. It might simply be importing it itself.
864 				if (realfunc->Address == 0 || realfunc->ImportNum != 0)
865 					continue;
866 
867 				func->Address = libfunc;
868 				func->ImportNum = i + 1;
869 				if (realfunc->ArgCount != func->ArgCount)
870 				{
871 					GCon->Logf("Function %s in %s has %d arguments. "
872 						"%s expects it to have %d.",
873 						(char *)(buffer + 2) + LittleLong(buffer[3 + j]),
874 						*W_LumpName(lib->LumpNum), realfunc->ArgCount,
875 						*W_LumpName(LumpNum), func->ArgCount);
876 					Format = ACS_Unknown;
877 				}
878 				//	The next two properties do not effect code compatibility,
879 				// so it is okay for them to be different in the imported
880 				// module than they are in this one, as long as we make sure
881 				// to use the real values.
882 				func->LocalCount = realfunc->LocalCount;
883 				func->HasReturnValue = realfunc->HasReturnValue;
884 			}
885 
886 			//	Resolve map variables.
887 			buffer = (int*)FindChunk("MIMP");
888 			if (buffer)
889 			{
890 				char* parse = (char*)&buffer[2];
891 				for (j = 0; j < LittleLong(buffer[1]); j++)
892 				{
893 					int varNum = LittleLong(*(int*)&parse[j]);
894 					j += 4;
895 					int impNum = lib->FindMapVarName(&parse[j]);
896 					if (impNum >= 0)
897 					{
898 						MapVars[varNum] = &lib->MapVarStore[impNum];
899 					}
900 					do ; while (parse[++j]);
901 				}
902 			}
903 
904 			// Resolve arrays
905 			if (NumTotalArrays > NumArrays)
906 			{
907 				buffer = (int*)FindChunk("AIMP");
908 				char* parse = (char*)&buffer[3];
909 				for (j = 0; j < LittleLong(buffer[2]); j++)
910 				{
911 					int varNum = LittleLong(*(int*)parse);
912 					parse += 4;
913 					int expectedSize = LittleLong(*(int*)parse);
914 					parse += 4;
915 					int impNum = lib->FindMapArray(parse);
916 					if (impNum >= 0)
917 					{
918 						Arrays[NumArrays + j] = &lib->ArrayStore[impNum];
919 						MapVarStore[varNum] = NumArrays + j;
920 						if (lib->ArrayStore[impNum].Size != expectedSize)
921 						{
922 							Format = ACS_Unknown;
923 							GCon->Logf("The array %s in %s has %ld elements, "
924 								"but %s expects it to only have %ld.",
925 								parse, *W_LumpName(lib->LumpNum),
926 								lib->ArrayStore[impNum].Size,
927 								*W_LumpName(LumpNum), expectedSize);
928 						}
929 					}
930 					do ; while (*++parse);
931 					++parse;
932 				}
933 			}
934 		}
935 	}
936 	unguard;
937 }
938 
939 //==========================================================================
940 //
941 //	VAcsObject::UnencryptStrings
942 //
943 //==========================================================================
944 
UnencryptStrings()945 void VAcsObject::UnencryptStrings()
946 {
947 	guard(VAcsObject::UnencryptStrings);
948 	vuint8 *prevchunk = NULL;
949 	vuint32* chunk = (vuint32*)FindChunk("STRE");
950 	while (chunk)
951 	{
952 		for (int strnum = 0; strnum < LittleLong(chunk[3]); strnum++)
953 		{
954 			int ofs = LittleLong(chunk[5 + strnum]);
955 			vuint8* data = (vuint8*)chunk + ofs + 8;
956 			vuint8 last;
957 			int p = (vuint8)(ofs * 157135);
958 			int i = 0;
959 			do
960 			{
961 				last = (data[i] ^= (vuint8)(p + (i >> 1)));
962 				i++;
963 			} while (last != 0);
964 		}
965 		prevchunk = (vuint8*)chunk;
966 		chunk = (vuint32*)NextChunk((vuint8*)chunk);
967 		prevchunk[3] = 'L';
968 	}
969 	if (prevchunk)
970 	{
971 		prevchunk[3] = 'L';
972 	}
973 	unguard;
974 }
975 
976 //==========================================================================
977 //
978 //	VAcsObject::FindFunctionName
979 //
980 //==========================================================================
981 
FindFunctionName(const char * Name) const982 int VAcsObject::FindFunctionName(const char* Name) const
983 {
984 	guard(VAcsObject::FindFunctionName);
985 	return FindStringInChunk(FindChunk("FNAM"), Name);
986 	unguard;
987 }
988 
989 //==========================================================================
990 //
991 //	VAcsObject::FindMapVarName
992 //
993 //==========================================================================
994 
FindMapVarName(const char * Name) const995 int VAcsObject::FindMapVarName(const char* Name) const
996 {
997 	guard(VAcsObject::FindMapVarName);
998 	return FindStringInChunk(FindChunk("MEXP"), Name);
999 	unguard;
1000 }
1001 
1002 //==========================================================================
1003 //
1004 //	VAcsObject::FindMapArray
1005 //
1006 //==========================================================================
1007 
FindMapArray(const char * Name) const1008 int VAcsObject::FindMapArray(const char* Name) const
1009 {
1010 	guard(VAcsObject::FindMapArray);
1011 	int var = FindMapVarName(Name);
1012 	if (var >= 0)
1013 	{
1014 		return MapVarStore[var];
1015 	}
1016 	return -1;
1017 	unguard;
1018 }
1019 
1020 //==========================================================================
1021 //
1022 //	VAcsObject::FindStringInChunk
1023 //
1024 //==========================================================================
1025 
FindStringInChunk(vuint8 * Chunk,const char * Name) const1026 int VAcsObject::FindStringInChunk(vuint8* Chunk, const char* Name) const
1027 {
1028 	guard(VAcsObject::FindStringInChunk);
1029 	if (Chunk)
1030 	{
1031 		int count = LittleLong(((int*)Chunk)[2]);
1032 		for (int i = 0; i < count; ++i)
1033 		{
1034 			if (!VStr::ICmp(Name, (char*)(Chunk + 8) +
1035 				LittleLong(((int*)Chunk)[3 + i])))
1036 			{
1037 				return i;
1038 			}
1039 		}
1040 	}
1041 	return -1;
1042 	unguard;
1043 }
1044 
1045 //==========================================================================
1046 //
1047 //	VAcsObject::FindChunk
1048 //
1049 //==========================================================================
1050 
FindChunk(const char * id) const1051 vuint8* VAcsObject::FindChunk(const char* id) const
1052 {
1053 	guard(VAcsObject::FindChunk);
1054 	vuint8* chunk = Chunks;
1055 	while (chunk && chunk < Data + DataSize)
1056 	{
1057 		if (*(int*)chunk == *(int*)id)
1058 		{
1059 			return chunk;
1060 		}
1061 		chunk = chunk + LittleLong(((int*)chunk)[1]) + 8;
1062 	}
1063 	return NULL;
1064 	unguard;
1065 }
1066 
1067 //==========================================================================
1068 //
1069 //	VAcsObject::NextChunk
1070 //
1071 //==========================================================================
1072 
NextChunk(vuint8 * prev) const1073 vuint8* VAcsObject::NextChunk(vuint8* prev) const
1074 {
1075 	guard(VAcsObject::NextChunk);
1076 	int id = *(int*)prev;
1077 	vuint8* chunk = prev + LittleLong(((int*)prev)[1]) + 8;
1078 	while (chunk && chunk < Data + DataSize)
1079 	{
1080 		if (*(int*)chunk == id)
1081 		{
1082 			return chunk;
1083 		}
1084 		chunk = chunk + LittleLong(((int*)chunk)[1]) + 8;
1085 	}
1086 	return NULL;
1087 	unguard;
1088 }
1089 
1090 //==========================================================================
1091 //
1092 //	VAcsObject::Serialise
1093 //
1094 //==========================================================================
1095 
Serialise(VStream & Strm)1096 void VAcsObject::Serialise(VStream& Strm)
1097 {
1098 	guard(VAcsObject::Serialise);
1099 	for (int i = 0; i < NumScripts; i++)
1100 	{
1101 		Strm << Scripts[i].RunningScript;
1102 	}
1103 	for (int i = 0; i < MAX_ACS_MAP_VARS; i++)
1104 	{
1105 		Strm << STRM_INDEX(MapVarStore[i]);
1106 	}
1107 	for (int i = 0; i < NumArrays; i++)
1108 	{
1109 		for (int j = 0; j < ArrayStore[i].Size; j++)
1110 		{
1111 			Strm << STRM_INDEX(ArrayStore[i].Data[j]);
1112 		}
1113 	}
1114 	unguard;
1115 }
1116 
1117 //==========================================================================
1118 //
1119 //	VAcsObject::OffsetToPtr
1120 //
1121 //==========================================================================
1122 
OffsetToPtr(int Offs)1123 vuint8* VAcsObject::OffsetToPtr(int Offs)
1124 {
1125 	if (Offs < 0 || Offs >= DataSize)
1126 		Host_Error("Bad offset in ACS file");
1127 	return Data + Offs;
1128 }
1129 
1130 //==========================================================================
1131 //
1132 //	VAcsObject::PtrToOffset
1133 //
1134 //==========================================================================
1135 
PtrToOffset(vuint8 * Ptr)1136 int VAcsObject::PtrToOffset(vuint8* Ptr)
1137 {
1138 	return Ptr - Data;
1139 }
1140 
1141 //==========================================================================
1142 //
1143 //	VAcsObject::FindScript
1144 //
1145 //==========================================================================
1146 
FindScript(int Number) const1147 VAcsInfo* VAcsObject::FindScript(int Number) const
1148 {
1149 	guard(VAcsObject::FindScript);
1150 	for (int i = 0; i < NumScripts; i++)
1151 	{
1152 		if (Scripts[i].Number == Number)
1153 		{
1154 			return Scripts + i;
1155 		}
1156 	}
1157 	return NULL;
1158 	unguard;
1159 }
1160 
1161 //==========================================================================
1162 //
1163 //	VAcsObject::GetFunction
1164 //
1165 //==========================================================================
1166 
GetFunction(int funcnum,VAcsObject * & Object)1167 VAcsFunction* VAcsObject::GetFunction(int funcnum,
1168 	VAcsObject*& Object)
1169 {
1170 	guard(VAcsObject::GetFunction);
1171 	if ((unsigned)funcnum >= (unsigned)NumFunctions)
1172 	{
1173 		return NULL;
1174 	}
1175 	VAcsFunction* Func = Functions + funcnum;
1176 	if (Func->ImportNum)
1177 	{
1178 		return Imports[Func->ImportNum - 1]->GetFunction(Func->Address,
1179 			Object);
1180 	}
1181 	Object = this;
1182 	return Func;
1183 	unguard;
1184 }
1185 
1186 //==========================================================================
1187 //
1188 //	VAcsObject::GetArrayVal
1189 //
1190 //==========================================================================
1191 
GetArrayVal(int ArrayIdx,int Index)1192 int VAcsObject::GetArrayVal(int ArrayIdx, int Index)
1193 {
1194 	guard(VAcsObject::GetArrayVal);
1195 	if ((unsigned)ArrayIdx >= (unsigned)NumTotalArrays)
1196 		return 0;
1197 	if ((unsigned)Index >= (unsigned)Arrays[ArrayIdx]->Size)
1198 		return 0;
1199 	return Arrays[ArrayIdx]->Data[Index];
1200 	unguard;
1201 }
1202 
1203 //==========================================================================
1204 //
1205 //	VAcsObject::SetArrayVal
1206 //
1207 //==========================================================================
1208 
SetArrayVal(int ArrayIdx,int Index,int Value)1209 void VAcsObject::SetArrayVal(int ArrayIdx, int Index, int Value)
1210 {
1211 	guard(VAcsObject::SetArrayVal);
1212 	if ((unsigned)ArrayIdx >= (unsigned)NumTotalArrays)
1213 		return;
1214 	if ((unsigned)Index >= (unsigned)Arrays[ArrayIdx]->Size)
1215 		return;
1216 	Arrays[ArrayIdx]->Data[Index] = Value;
1217 	unguard;
1218 }
1219 
1220 //==========================================================================
1221 //
1222 //	VAcsObject::StartTypedACScripts
1223 //
1224 //==========================================================================
1225 
StartTypedACScripts(int Type,int Arg1,int Arg2,int Arg3,VEntity * Activator,bool Always,bool RunNow)1226 void VAcsObject::StartTypedACScripts(int Type, int Arg1, int Arg2, int Arg3,
1227 	VEntity* Activator, bool Always, bool RunNow)
1228 {
1229 	guard(VAcsObject::StartTypedACScripts);
1230 	for (int i = 0; i < NumScripts; i++)
1231 	{
1232 		if (Scripts[i].Type == Type)
1233 		{
1234 			// Auto-activate
1235 			VAcs* Script = Level->SpawnScript(&Scripts[i], this, Activator,
1236 				NULL, 0, Arg1, Arg2, Arg3, Always, !RunNow);
1237 			if (RunNow)
1238 			{
1239 				Script->RunScript(host_frametime);
1240 			}
1241 		}
1242 	}
1243 	unguard;
1244 }
1245 
1246 //==========================================================================
1247 //
1248 //	VAcsLevel::VAcsLevel
1249 //
1250 //==========================================================================
1251 
VAcsLevel(VLevel * ALevel)1252 VAcsLevel::VAcsLevel(VLevel* ALevel)
1253 : XLevel(ALevel)
1254 {
1255 }
1256 
1257 //==========================================================================
1258 //
1259 //	VAcsLevel::~VAcsLevel
1260 //
1261 //==========================================================================
1262 
~VAcsLevel()1263 VAcsLevel::~VAcsLevel()
1264 {
1265 	guard(VAcsLevel::~VAcsLevel);
1266 	for (int i = 0; i < LoadedObjects.Num(); i++)
1267 	{
1268 		delete LoadedObjects[i];
1269 		LoadedObjects[i] = NULL;
1270 	}
1271 	LoadedObjects.Clear();
1272 	unguard;
1273 }
1274 
1275 //==========================================================================
1276 //
1277 //	VAcsLevel::LoadObject
1278 //
1279 //==========================================================================
1280 
LoadObject(int Lump)1281 VAcsObject* VAcsLevel::LoadObject(int Lump)
1282 {
1283 	guard(VAcsLevel::LoadObject);
1284 	for (int i = 0; i < LoadedObjects.Num(); i++)
1285 	{
1286 		if (LoadedObjects[i]->LumpNum == Lump)
1287 		{
1288 			return LoadedObjects[i];
1289 		}
1290 	}
1291 	return new VAcsObject(this, Lump);
1292 	unguard;
1293 }
1294 
1295 //==========================================================================
1296 //
1297 //	VAcsLevel::FindScript
1298 //
1299 //==========================================================================
1300 
FindScript(int Number,VAcsObject * & Object)1301 VAcsInfo* VAcsLevel::FindScript(int Number, VAcsObject*& Object)
1302 {
1303 	guard(VAcsLevel::FindScript);
1304 	for (int i = 0; i < LoadedObjects.Num(); i++)
1305 	{
1306 		VAcsInfo* Found = LoadedObjects[i]->FindScript(Number);
1307 		if (Found)
1308 		{
1309 			Object = LoadedObjects[i];
1310 			return Found;
1311 		}
1312 	}
1313 	return NULL;
1314 	unguard;
1315 }
1316 
1317 //==========================================================================
1318 //
1319 //	VAcsLevel::GetString
1320 //
1321 //==========================================================================
1322 
GetString(int Index)1323 VStr VAcsLevel::GetString(int Index)
1324 {
1325 	guard(VAcsLevel::GetString);
1326 	int ObjIdx = Index >> 16;
1327 	if (ObjIdx >= LoadedObjects.Num())
1328 	{
1329 		return "";
1330 	}
1331 	return LoadedObjects[ObjIdx]->GetString(Index & 0xffff);
1332 	unguard;
1333 }
1334 
1335 //==========================================================================
1336 //
1337 //	VAcsLevel::GetNameLowerCase
1338 //
1339 //==========================================================================
1340 
GetNameLowerCase(int Index)1341 VName VAcsLevel::GetNameLowerCase(int Index)
1342 {
1343 	guard(VAcsLevel::GetNameLowerCase);
1344 	int ObjIdx = Index >> 16;
1345 	if (ObjIdx >= LoadedObjects.Num())
1346 	{
1347 		return NAME_None;
1348 	}
1349 	return LoadedObjects[ObjIdx]->GetNameLowerCase(Index & 0xffff);
1350 	unguard;
1351 }
1352 
1353 //==========================================================================
1354 //
1355 //	VAcsLevel::GetObject
1356 //
1357 //==========================================================================
1358 
GetObject(int Index)1359 VAcsObject* VAcsLevel::GetObject(int Index)
1360 {
1361 	guard(VAcsLevel::GetObject);
1362 	if ((unsigned)Index >= (unsigned)LoadedObjects.Num())
1363 	{
1364 		return NULL;
1365 	}
1366 	return LoadedObjects[Index];
1367 	unguard;
1368 }
1369 
1370 //==========================================================================
1371 //
1372 //	VAcsLevel::StartTypedACScripts
1373 //
1374 //==========================================================================
1375 
StartTypedACScripts(int Type,int Arg1,int Arg2,int Arg3,VEntity * Activator,bool Always,bool RunNow)1376 void VAcsLevel::StartTypedACScripts(int Type, int Arg1, int Arg2, int Arg3,
1377 	VEntity* Activator, bool Always, bool RunNow)
1378 {
1379 	guard(VAcsLevel::StartTypedACScripts);
1380 	for (int i = 0; i < LoadedObjects.Num(); i++)
1381 	{
1382 		LoadedObjects[i]->StartTypedACScripts(Type, Arg1, Arg2, Arg3,
1383 			Activator, Always, RunNow);
1384 	}
1385 	unguard;
1386 }
1387 
1388 //==========================================================================
1389 //
1390 //	VAcsLevel::Serialise
1391 //
1392 //==========================================================================
1393 
Serialise(VStream & Strm)1394 void VAcsLevel::Serialise(VStream& Strm)
1395 {
1396 	guard(VAcsLevel::Serialise);
1397 	for (int i = 0; i < LoadedObjects.Num(); i++)
1398 	{
1399 		LoadedObjects[i]->Serialise(Strm);
1400 	}
1401 	unguard;
1402 }
1403 
1404 //==========================================================================
1405 //
1406 //	VAcsLevel::AddToACSStore
1407 //
1408 //==========================================================================
1409 
AddToACSStore(int Type,VName Map,int Number,int Arg1,int Arg2,int Arg3,VEntity * Activator)1410 bool VAcsLevel::AddToACSStore(int Type, VName Map, int Number, int Arg1,
1411 	int Arg2, int Arg3, VEntity* Activator)
1412 {
1413 	guard(VAcsLevel::AddToACSStore);
1414 	VAcsStore& S = XLevel->WorldInfo->Acs->Store.Alloc();
1415 	S.Map = Map;
1416 	S.Type = Type;
1417 	S.PlayerNum = Activator && Activator->Player ?
1418 		SV_GetPlayerNum(Activator->Player) : -1;
1419 	S.Script = Number;
1420 	S.Args[0] = Arg1;
1421 	S.Args[1] = Arg2;
1422 	S.Args[2] = Arg3;
1423 	return true;
1424 	unguard;
1425 }
1426 
1427 //==========================================================================
1428 //
1429 //	VAcsLevel::CheckAcsStore
1430 //
1431 //	Scans the ACS store and executes all scripts belonging to the current
1432 // map.
1433 //
1434 //==========================================================================
1435 
CheckAcsStore()1436 void VAcsLevel::CheckAcsStore()
1437 {
1438 	guard(VAcsLevel::CheckAcsStore);
1439 	for (int i = XLevel->WorldInfo->Acs->Store.Num() - 1; i >= 0; i--)
1440 	{
1441 		VAcsStore* store = &XLevel->WorldInfo->Acs->Store[i];
1442 		if (store->Map != XLevel->MapName)
1443 		{
1444 			continue;
1445 		}
1446 
1447 		VAcsObject* Object;
1448 		VAcsInfo* Info = FindScript(store->Script, Object);
1449 		if (!Info)
1450 		{
1451 			//	Script not found
1452 			GCon->Logf(NAME_Dev, "Start ACS ERROR: Unknown script %d", store->Script);
1453 		}
1454 		else
1455 		{
1456 			switch (store->Type)
1457 			{
1458 			case VAcsStore::Start:
1459 			case VAcsStore::StartAlways:
1460 				SpawnScript(Info, Object, store->PlayerNum >= 0 &&
1461 					GGameInfo->Players[store->PlayerNum] &&
1462 					(GGameInfo->Players[store->PlayerNum]->PlayerFlags &
1463 					VBasePlayer::PF_Spawned) ?
1464 					GGameInfo->Players[store->PlayerNum]->MO : NULL, NULL, 0,
1465 					store->Args[0], store->Args[1], store->Args[2],
1466 					store->Type == VAcsStore::StartAlways, true);
1467 				break;
1468 
1469 			case VAcsStore::Terminate:
1470 				if (!Info->RunningScript ||
1471 					Info->RunningScript->State == VAcs::ASTE_Terminating)
1472 				{
1473 					//	States that disallow termination
1474 					break;
1475 				}
1476 				Info->RunningScript->State = VAcs::ASTE_Terminating;
1477 				break;
1478 
1479 			case VAcsStore::Suspend:
1480 				if (!Info->RunningScript ||
1481 					Info->RunningScript->State == VAcs::ASTE_Suspended ||
1482 					Info->RunningScript->State == VAcs::ASTE_Terminating)
1483 				{
1484 					// States that disallow suspension
1485 					break;
1486 				}
1487 				Info->RunningScript->State = VAcs::ASTE_Suspended;
1488 				break;
1489 			}
1490 		}
1491 		XLevel->WorldInfo->Acs->Store.RemoveIndex(i);
1492 	}
1493 	unguard;
1494 }
1495 
1496 //==========================================================================
1497 //
1498 //	VAcsLevel::Start
1499 //
1500 //==========================================================================
1501 
Start(int Number,int MapNum,int Arg1,int Arg2,int Arg3,VEntity * Activator,line_t * Line,int Side,bool Always,bool WantResult,bool Net)1502 bool VAcsLevel::Start(int Number, int MapNum, int Arg1, int Arg2, int Arg3,
1503 	VEntity* Activator, line_t* Line, int Side, bool Always, bool WantResult,
1504 	bool Net)
1505 {
1506 	guard(VAcsLevel::Start);
1507 	if (MapNum)
1508 	{
1509 		VName Map = P_GetMapNameByLevelNum(MapNum);
1510 		if (Map != NAME_None && Map != XLevel->MapName)
1511 		{
1512 			// Add to the script store
1513 			return AddToACSStore(Always ? VAcsStore::StartAlways :
1514 				VAcsStore::Start, Map, Number, Arg1, Arg2, Arg3, Activator);
1515 		}
1516 	}
1517 
1518 	VAcsObject* Object;
1519 	VAcsInfo* Info = FindScript(Number, Object);
1520 	if (!Info)
1521 	{
1522 		//	Script not found
1523 		GCon->Logf(NAME_Dev, "Start ACS ERROR: Unknown script %d", Number);
1524 		return false;
1525 	}
1526 	if (Net && (GGameInfo->NetMode >= NM_DedicatedServer) &&
1527 		!(Info->Flags & SCRIPTF_Net))
1528 	{
1529 		GCon->Logf("%s tried to puke script %d",
1530 			*Activator->Player->PlayerName, Number);
1531 		return false;
1532 	}
1533 	VAcs* script = SpawnScript(Info, Object, Activator, Line, Side, Arg1,
1534 		Arg2, Arg3, Always, false);
1535 	if (WantResult)
1536 	{
1537 		return !!script->RunScript(host_frametime);
1538 	}
1539 	return true;
1540 	unguard;
1541 }
1542 
1543 //==========================================================================
1544 //
1545 //	VAcsLevel::Terminate
1546 //
1547 //==========================================================================
1548 
Terminate(int Number,int MapNum)1549 bool VAcsLevel::Terminate(int Number, int MapNum)
1550 {
1551 	guard(VAcsLevel::Terminate);
1552 	if (MapNum)
1553 	{
1554 		VName Map = P_GetMapNameByLevelNum(MapNum);
1555 		if (Map != NAME_None && Map != XLevel->MapName)
1556 		{
1557 			// Add to the script store
1558 			return AddToACSStore(VAcsStore::Terminate, Map, Number, 0, 0, 0, 0);
1559 		}
1560 	}
1561 
1562 	VAcsObject* Object;
1563 	VAcsInfo* Info = FindScript(Number, Object);
1564 	if (!Info)
1565 	{
1566 		//	Script not found
1567 		return false;
1568 	}
1569 	if (!Info->RunningScript ||
1570 		Info->RunningScript->State == VAcs::ASTE_Terminating)
1571 	{
1572 		//	States that disallow termination
1573 		return false;
1574 	}
1575 	Info->RunningScript->State = VAcs::ASTE_Terminating;
1576 	return true;
1577 	unguard;
1578 }
1579 
1580 //==========================================================================
1581 //
1582 //	VAcsLevel::Suspend
1583 //
1584 //==========================================================================
1585 
Suspend(int Number,int MapNum)1586 bool VAcsLevel::Suspend(int Number, int MapNum)
1587 {
1588 	guard(VAcsLevel::Suspend);
1589 	if (MapNum)
1590 	{
1591 		VName Map = P_GetMapNameByLevelNum(MapNum);
1592 		if (Map != NAME_None && Map != XLevel->MapName)
1593 		{
1594 			// Add to the script store
1595 			return AddToACSStore(VAcsStore::Suspend, Map, Number, 0, 0, 0, 0);
1596 		}
1597 	}
1598 
1599 	VAcsObject* Object;
1600 	VAcsInfo* Info = FindScript(Number, Object);
1601 	if (!Info)
1602 	{
1603 		//	Script not found.
1604 		return false;
1605 	}
1606 	if (!Info->RunningScript ||
1607 		Info->RunningScript->State == VAcs::ASTE_Suspended ||
1608 		Info->RunningScript->State == VAcs::ASTE_Terminating)
1609 	{
1610 		// States that disallow suspension
1611 		return false;
1612 	}
1613 	Info->RunningScript->State = VAcs::ASTE_Suspended;
1614 	return true;
1615 	unguard;
1616 }
1617 
1618 //==========================================================================
1619 //
1620 //	VAcsLevel::SpawnScript
1621 //
1622 //==========================================================================
1623 
SpawnScript(VAcsInfo * Info,VAcsObject * Object,VEntity * Activator,line_t * Line,int Side,int Arg1,int Arg2,int Arg3,bool Always,bool Delayed)1624 VAcs* VAcsLevel::SpawnScript(VAcsInfo* Info, VAcsObject* Object,
1625 	VEntity* Activator, line_t* Line, int Side, int Arg1, int Arg2, int Arg3,
1626 	bool Always, bool Delayed)
1627 {
1628 	guard(VAcsLevel::SpawnScript);
1629 	if (!Always && Info->RunningScript)
1630 	{
1631 		if (Info->RunningScript->State == VAcs::ASTE_Suspended)
1632 		{
1633 			//	Resume a suspended script
1634 			Info->RunningScript->State = VAcs::ASTE_Running;
1635 		}
1636 		//	Script is already executing
1637 		return Info->RunningScript;
1638 	}
1639 
1640 	VAcs* script = (VAcs*)XLevel->SpawnThinker(VAcs::StaticClass());
1641 	script->info = Info;
1642 	script->number = Info->Number;
1643 	script->InstructionPointer = Info->Address;
1644 	script->State = VAcs::ASTE_Running;
1645 	script->ActiveObject = Object;
1646 	script->Activator = Activator;
1647 	script->line = Line;
1648 	script->side = Side;
1649 	script->LocalVars = new vint32[Info->VarCount];
1650 	script->LocalVars[0] = Arg1;
1651 	script->LocalVars[1] = Arg2;
1652 	script->LocalVars[2] = Arg3;
1653 	memset(script->LocalVars + Info->ArgCount, 0,
1654 		(Info->VarCount - Info->ArgCount) * 4);
1655 	if (Delayed)
1656 	{
1657 		//	World objects are allotted 1 second for initialization.
1658 		//script->DelayTime = 1.0;
1659 	}
1660 	if (!Always)
1661 	{
1662 		Info->RunningScript = script;
1663 	}
1664 	return script;
1665 	unguard;
1666 }
1667 
1668 //==========================================================================
1669 //
1670 //	VAcsGrowingArray::VAcsGrowingArray
1671 //
1672 //==========================================================================
1673 
VAcsGrowingArray()1674 VAcsGrowingArray::VAcsGrowingArray()
1675 : Size(0)
1676 , Data(NULL)
1677 {
1678 }
1679 
1680 //==========================================================================
1681 //
1682 //	VAcsGrowingArray::Redim
1683 //
1684 //==========================================================================
1685 
Redim(int NewSize)1686 void VAcsGrowingArray::Redim(int NewSize)
1687 {
1688 	guard(VAcsGrowingArray::Redim);
1689 	if (!NewSize && Data)
1690 	{
1691 		delete[] Data;
1692 		Data = NULL;
1693 	}
1694 	else if (NewSize)
1695 	{
1696 		int* Temp = Data;
1697 		Data = new int[NewSize];
1698 		if (Temp)
1699 		{
1700 			memcpy(Data, Temp, Min(Size, NewSize) * sizeof(int));
1701 			delete[] Temp;
1702 			Temp = NULL;
1703 		}
1704 		//	Clear newly allocated elements.
1705 		if (NewSize > Size)
1706 		{
1707 			memset(Data + Size, 0, (NewSize - Size) * sizeof(int));
1708 		}
1709 	}
1710 	Size = NewSize;
1711 	unguard;
1712 }
1713 
1714 //==========================================================================
1715 //
1716 //	VAcsGrowingArray::SetElemVal
1717 //
1718 //==========================================================================
1719 
SetElemVal(int Index,int Value)1720 void VAcsGrowingArray::SetElemVal(int Index, int Value)
1721 {
1722 	guard(VAcsGrowingArray::SetElemVal);
1723 	if (Index >= Size)
1724 	{
1725 		Redim(Index + 1);
1726 	}
1727 	Data[Index] = Value;
1728 	unguard;
1729 }
1730 
1731 //==========================================================================
1732 //
1733 //	VAcsGrowingArray::GetElemVal
1734 //
1735 //==========================================================================
1736 
GetElemVal(int Index)1737 int VAcsGrowingArray::GetElemVal(int Index)
1738 {
1739 	guard(VAcsGrowingArray::GetElemVal);
1740 	if ((unsigned)Index >= (unsigned)Size)
1741 		return 0;
1742 	return Data[Index];
1743 	unguard;
1744 }
1745 
1746 //==========================================================================
1747 //
1748 //	VAcsGrowingArray::Serialise
1749 //
1750 //==========================================================================
1751 
Serialise(VStream & Strm)1752 void VAcsGrowingArray::Serialise(VStream& Strm)
1753 {
1754 	guard(VAcsGrowingArray::Serialise);
1755 	if (Strm.IsLoading())
1756 	{
1757 		int NewSize;
1758 		Strm << STRM_INDEX(NewSize);
1759 		Redim(NewSize);
1760 	}
1761 	else
1762 	{
1763 		Strm << STRM_INDEX(Size);
1764 	}
1765 	for (int i = 0; i < Size; i++)
1766 	{
1767 		Strm << STRM_INDEX(Data[i]);
1768 	}
1769 	unguard;
1770 }
1771 
1772 //==========================================================================
1773 //
1774 //	VAcs::Destroy
1775 //
1776 //==========================================================================
1777 
Destroy()1778 void VAcs::Destroy()
1779 {
1780 	guard(VAcs::Destroy);
1781 	if (LocalVars)
1782 	{
1783 		delete[] LocalVars;
1784 		LocalVars = NULL;
1785 	}
1786 	unguard;
1787 }
1788 
1789 //==========================================================================
1790 //
1791 //	VAcs::Serialise
1792 //
1793 //==========================================================================
1794 
Serialise(VStream & Strm)1795 void VAcs::Serialise(VStream& Strm)
1796 {
1797 	guard(VAcs::Serialise);
1798 	vint32 TmpInt;
1799 
1800 	Super::Serialise(Strm);
1801 	Strm << Activator;
1802 	if (Strm.IsLoading())
1803 	{
1804 		Strm << STRM_INDEX(TmpInt);
1805 		line = TmpInt == -1 ? NULL : &XLevel->Lines[TmpInt];
1806 	}
1807 	else
1808 	{
1809 		TmpInt = line ? line - XLevel->Lines : -1;
1810 		Strm << STRM_INDEX(TmpInt);
1811 	}
1812 	Strm << side
1813 		<< number
1814 		<< State
1815 		<< DelayTime
1816 		<< STRM_INDEX(WaitValue);
1817 	if (Strm.IsLoading())
1818 	{
1819 		Strm << STRM_INDEX(TmpInt);
1820 		ActiveObject = XLevel->Acs->GetObject(TmpInt);
1821 		Strm << STRM_INDEX(TmpInt);
1822 		InstructionPointer = ActiveObject->OffsetToPtr(TmpInt);
1823 		info = ActiveObject->FindScript(number);
1824 		LocalVars = new vint32[info->VarCount];
1825 	}
1826 	else
1827 	{
1828 		TmpInt = ActiveObject->GetLibraryID() >> 16;
1829 		Strm << STRM_INDEX(TmpInt);
1830 		TmpInt = ActiveObject->PtrToOffset(InstructionPointer);
1831 		Strm << STRM_INDEX(TmpInt);
1832 	}
1833 	for (int i = 0; i < info->VarCount; i++)
1834 	{
1835 		Strm << LocalVars[i];
1836 	}
1837 	Strm << HudWidth
1838 		<< HudHeight
1839 		<< Font;
1840 	unguard;
1841 }
1842 
1843 //==========================================================================
1844 //
1845 //	VAcs::ClearReferences
1846 //
1847 //==========================================================================
1848 
ClearReferences()1849 void VAcs::ClearReferences()
1850 {
1851 	guard(VAcs::ClearReferences);
1852 	Super::ClearReferences();
1853 	if (Activator && Activator->GetFlags() & _OF_CleanupRef)
1854 	{
1855 		Activator = NULL;
1856 	}
1857 	unguard;
1858 }
1859 
1860 //==========================================================================
1861 //
1862 //	VAcs::Tick
1863 //
1864 //==========================================================================
1865 
Tick(float DeltaTime)1866 void VAcs::Tick(float DeltaTime)
1867 {
1868 	guard(VAcs::Tick);
1869 	RunScript(DeltaTime);
1870 	unguard;
1871 }
1872 
1873 //==========================================================================
1874 //
1875 //	VAcs::RunScript
1876 //
1877 //==========================================================================
1878 
1879 #define STUB(cmd)	GCon->Log("Executing unimplemented ACS PCODE " #cmd);
1880 
1881 #ifdef __GNUC__
1882 #define USE_COMPUTED_GOTO 1
1883 #endif
1884 
1885 #if USE_COMPUTED_GOTO
1886 #define ACSVM_SWITCH(op)	goto *vm_labels[op];
1887 #define ACSVM_CASE(x)		Lbl_ ## x:
1888 #define ACSVM_BREAK \
1889 	if (fmt == ACS_LittleEnhanced) \
1890 	{ \
1891 		cmd = *ip; \
1892 		if (cmd >= 240) \
1893 		{ \
1894 			cmd = 240 + ((cmd - 240) << 8) + ip[1]; \
1895 			ip += 2; \
1896 		} \
1897 		else \
1898 		{ \
1899 			ip++; \
1900 		} \
1901 	} \
1902 	else \
1903 	{ \
1904 		cmd = READ_INT32(ip); \
1905 		ip += 4; \
1906 	} \
1907 	if ((vuint32)cmd >= PCODE_COMMAND_COUNT) \
1908 	{ \
1909 		goto LblDefault; \
1910 	} \
1911 	goto *vm_labels[cmd];
1912 #define ACSVM_BREAK_STOP	goto LblFuncStop;
1913 #define ACSVM_DEFAULT		LblDefault:
1914 #else
1915 #define ACSVM_SWITCH(op)	switch (cmd)
1916 #define ACSVM_CASE(op)		case op:
1917 #define ACSVM_BREAK			break
1918 #define ACSVM_BREAK_STOP	break
1919 #define ACSVM_DEFAULT		default:
1920 #endif
1921 
1922 #define READ_INT32(p)		((p)[0] | ((p)[1] << 8) | ((p)[2] << 16) | ((p)[3] << 24))
1923 #define READ_BYTE_OR_INT32	(fmt == ACS_LittleEnhanced ? *ip : READ_INT32(ip))
1924 #define INC_BYTE_OR_INT32	if (fmt == ACS_LittleEnhanced) ip++; else ip += 4
1925 
RunScript(float DeltaTime)1926 int VAcs::RunScript(float DeltaTime)
1927 {
1928 	guard(VAcs::RunScript);
1929 	VAcsObject* WaitObject;
1930 	if (State == ASTE_Terminating)
1931 	{
1932 		if (info->RunningScript == this)
1933 		{
1934 			info->RunningScript = NULL;
1935 		}
1936 		DestroyThinker();
1937 		return 1;
1938 	}
1939 	if (State == ASTE_WaitingForTag && !Level->eventTagBusy(WaitValue))
1940 	{
1941 		State = ASTE_Running;
1942 	}
1943 	if (State == ASTE_WaitingForPoly && !Level->eventPolyBusy(WaitValue))
1944 	{
1945 		State = ASTE_Running;
1946 	}
1947 	if (State == ASTE_WaitingForScriptStart &&
1948 		XLevel->Acs->FindScript(WaitValue, WaitObject) &&
1949 		XLevel->Acs->FindScript(WaitValue, WaitObject)->RunningScript)
1950 	{
1951 		State = ASTE_WaitingForScript;
1952 	}
1953 	if (State == ASTE_WaitingForScript &&
1954 		!XLevel->Acs->FindScript(WaitValue, WaitObject)->RunningScript)
1955 	{
1956 		State = ASTE_Running;
1957 	}
1958 	if (State != ASTE_Running)
1959 	{
1960 		return 1;
1961 	}
1962 	if (DelayTime)
1963 	{
1964 		DelayTime -= DeltaTime;
1965 		if (DelayTime < 0)
1966 			DelayTime = 0;
1967 		return 1;
1968 	}
1969 
1970 	//	Shortcuts
1971 	int* WorldVars = Level->World->Acs->WorldVars;
1972 	int* GlobalVars = Level->World->Acs->GlobalVars;
1973 	VAcsGrowingArray* WorldArrays = Level->World->Acs->WorldArrays;
1974 	VAcsGrowingArray* GlobalArrays = Level->World->Acs->GlobalArrays;
1975 
1976 	VStr PrintStr;
1977 	vint32 resultValue = 1;
1978 	vint32 stack[ACS_STACK_DEPTH];
1979 	vint32* optstart = NULL;
1980 	vint32* locals = LocalVars;
1981 	VAcsFunction* activeFunction = NULL;
1982 	EAcsFormat fmt = ActiveObject->GetFormat();
1983 	int action = SCRIPT_Continue;
1984 	vuint8* ip = InstructionPointer;
1985 	vint32* sp = stack;
1986 	VTextureTranslation* Translation = NULL;
1987 	do
1988 	{
1989 		vint32 cmd;
1990 
1991 #if USE_COMPUTED_GOTO
1992 		static void* vm_labels[] = {
1993 #define DECLARE_PCD(name)	&&Lbl_PCD_ ## name
1994 #include "p_acs.h"
1995 		0 };
1996 #endif
1997 
1998 		if (fmt == ACS_LittleEnhanced)
1999 		{
2000 			cmd = *ip;
2001 			if (cmd >= 240)
2002 			{
2003 				cmd = 240 + ((cmd - 240) << 8) + ip[1];
2004 				ip += 2;
2005 			}
2006 			else
2007 			{
2008 				ip++;
2009 			}
2010 		}
2011 		else
2012 		{
2013 			cmd = READ_INT32(ip);
2014 			ip += 4;
2015 		}
2016 
2017 		ACSVM_SWITCH(cmd)
2018 		{
2019 		//	Standard P-Code commands.
2020 		ACSVM_CASE(PCD_Nop)
2021 			ACSVM_BREAK;
2022 
2023 		ACSVM_CASE(PCD_Terminate)
2024 			action = SCRIPT_Terminate;
2025 			ACSVM_BREAK_STOP;
2026 
2027 		ACSVM_CASE(PCD_Suspend)
2028 			State = ASTE_Suspended;
2029 			action = SCRIPT_Stop;
2030 			ACSVM_BREAK_STOP;
2031 
2032 		ACSVM_CASE(PCD_PushNumber)
2033 			*sp = READ_INT32(ip);
2034 			ip += 4;
2035 			sp++;
2036 			ACSVM_BREAK;
2037 
2038 		ACSVM_CASE(PCD_LSpec1)
2039 			{
2040 				int special = READ_BYTE_OR_INT32;
2041 				INC_BYTE_OR_INT32;
2042 				Level->eventExecuteActionSpecial(special, sp[-1], 0, 0, 0, 0,
2043 					line, side, Activator);
2044 				sp--;
2045 			}
2046 			ACSVM_BREAK;
2047 
2048 		ACSVM_CASE(PCD_LSpec2)
2049 			{
2050 				int special = READ_BYTE_OR_INT32;
2051 				INC_BYTE_OR_INT32;
2052 				Level->eventExecuteActionSpecial(special, sp[-2], sp[-1], 0,
2053 					0, 0, line, side, Activator);
2054 				sp -= 2;
2055 			}
2056 			ACSVM_BREAK;
2057 
2058 		ACSVM_CASE(PCD_LSpec3)
2059 			{
2060 				int special = READ_BYTE_OR_INT32;
2061 				INC_BYTE_OR_INT32;
2062 				Level->eventExecuteActionSpecial(special, sp[-3], sp[-2],
2063 					sp[-1], 0, 0, line, side, Activator);
2064 				sp -= 3;
2065 			}
2066 			ACSVM_BREAK;
2067 
2068 		ACSVM_CASE(PCD_LSpec4)
2069 			{
2070 				int special = READ_BYTE_OR_INT32;
2071 				INC_BYTE_OR_INT32;
2072 				Level->eventExecuteActionSpecial(special, sp[-4], sp[-3],
2073 					sp[-2], sp[-1], 0, line, side, Activator);
2074 				sp -= 4;
2075 			}
2076 			ACSVM_BREAK;
2077 
2078 		ACSVM_CASE(PCD_LSpec5)
2079 			{
2080 				int special = READ_BYTE_OR_INT32;
2081 				INC_BYTE_OR_INT32;
2082 				Level->eventExecuteActionSpecial(special, sp[-5], sp[-4],
2083 					sp[-3], sp[-2], sp[-1], line, side, Activator);
2084 				sp -= 5;
2085 			}
2086 			ACSVM_BREAK;
2087 
2088 		ACSVM_CASE(PCD_LSpec1Direct)
2089 			{
2090 				int special = READ_BYTE_OR_INT32;
2091 				INC_BYTE_OR_INT32;
2092 				Level->eventExecuteActionSpecial(special, READ_INT32(ip), 0,
2093 					0, 0, 0, line, side, Activator);
2094 				ip += 4;
2095 			}
2096 			ACSVM_BREAK;
2097 
2098 		ACSVM_CASE(PCD_LSpec2Direct)
2099 			{
2100 				int special = READ_BYTE_OR_INT32;
2101 				INC_BYTE_OR_INT32;
2102 				Level->eventExecuteActionSpecial(special, READ_INT32(ip),
2103 					READ_INT32(ip + 4), 0, 0, 0, line, side, Activator);
2104 				ip += 8;
2105 			}
2106 			ACSVM_BREAK;
2107 
2108 		ACSVM_CASE(PCD_LSpec3Direct)
2109 			{
2110 				int special = READ_BYTE_OR_INT32;
2111 				INC_BYTE_OR_INT32;
2112 				Level->eventExecuteActionSpecial(special, READ_INT32(ip),
2113 					READ_INT32(ip + 4), READ_INT32(ip + 8), 0, 0, line, side,
2114 					Activator);
2115 				ip += 12;
2116 			}
2117 			ACSVM_BREAK;
2118 
2119 		ACSVM_CASE(PCD_LSpec4Direct)
2120 			{
2121 				int special = READ_BYTE_OR_INT32;
2122 				INC_BYTE_OR_INT32;
2123 				Level->eventExecuteActionSpecial(special, READ_INT32(ip),
2124 					READ_INT32(ip + 4), READ_INT32(ip + 8),
2125 					READ_INT32(ip + 12), 0, line, side, Activator);
2126 				ip += 16;
2127 			}
2128 			ACSVM_BREAK;
2129 
2130 		ACSVM_CASE(PCD_LSpec5Direct)
2131 			{
2132 				int special = READ_BYTE_OR_INT32;
2133 				INC_BYTE_OR_INT32;
2134 				Level->eventExecuteActionSpecial(special, READ_INT32(ip),
2135 					READ_INT32(ip + 4), READ_INT32(ip + 8),
2136 					READ_INT32(ip + 12), READ_INT32(ip + 16), line, side,
2137 					Activator);
2138 				ip += 20;
2139 			}
2140 			ACSVM_BREAK;
2141 
2142 		ACSVM_CASE(PCD_Add)
2143 			sp[-2] += sp[-1];
2144 			sp--;
2145 			ACSVM_BREAK;
2146 
2147 		ACSVM_CASE(PCD_Subtract)
2148 			sp[-2] -= sp[-1];
2149 			sp--;
2150 			ACSVM_BREAK;
2151 
2152 		ACSVM_CASE(PCD_Multiply)
2153 			sp[-2] *= sp[-1];
2154 			sp--;
2155 			ACSVM_BREAK;
2156 
2157 		ACSVM_CASE(PCD_Divide)
2158 			sp[-2] /= sp[-1];
2159 			sp--;
2160 			ACSVM_BREAK;
2161 
2162 		ACSVM_CASE(PCD_Modulus)
2163 			sp[-2] %= sp[-1];
2164 			sp--;
2165 			ACSVM_BREAK;
2166 
2167 		ACSVM_CASE(PCD_EQ)
2168 			sp[-2] = sp[-2] == sp[-1];
2169 			sp--;
2170 			ACSVM_BREAK;
2171 
2172 		ACSVM_CASE(PCD_NE)
2173 			sp[-2] = sp[-2] != sp[-1];
2174 			sp--;
2175 			ACSVM_BREAK;
2176 
2177 		ACSVM_CASE(PCD_LT)
2178 			sp[-2] = sp[-2] < sp[-1];
2179 			sp--;
2180 			ACSVM_BREAK;
2181 
2182 		ACSVM_CASE(PCD_GT)
2183 			sp[-2] = sp[-2] > sp[-1];
2184 			sp--;
2185 			ACSVM_BREAK;
2186 
2187 		ACSVM_CASE(PCD_LE)
2188 			sp[-2] = sp[-2] <= sp[-1];
2189 			sp--;
2190 			ACSVM_BREAK;
2191 
2192 		ACSVM_CASE(PCD_GE)
2193 			sp[-2] = sp[-2] >= sp[-1];
2194 			sp--;
2195 			ACSVM_BREAK;
2196 
2197 		ACSVM_CASE(PCD_AssignScriptVar)
2198 			locals[READ_BYTE_OR_INT32] = sp[-1];
2199 			INC_BYTE_OR_INT32;
2200 			sp--;
2201 			ACSVM_BREAK;
2202 
2203 		ACSVM_CASE(PCD_AssignMapVar)
2204 			*ActiveObject->MapVars[READ_BYTE_OR_INT32] = sp[-1];
2205 			INC_BYTE_OR_INT32;
2206 			sp--;
2207 			ACSVM_BREAK;
2208 
2209 		ACSVM_CASE(PCD_AssignWorldVar)
2210 			WorldVars[READ_BYTE_OR_INT32] = sp[-1];
2211 			INC_BYTE_OR_INT32;
2212 			sp--;
2213 			ACSVM_BREAK;
2214 
2215 		ACSVM_CASE(PCD_PushScriptVar)
2216 			*sp = locals[READ_BYTE_OR_INT32];
2217 			INC_BYTE_OR_INT32;
2218 			sp++;
2219 			ACSVM_BREAK;
2220 
2221 		ACSVM_CASE(PCD_PushMapVar)
2222 			*sp = *ActiveObject->MapVars[READ_BYTE_OR_INT32];
2223 			INC_BYTE_OR_INT32;
2224 			sp++;
2225 			ACSVM_BREAK;
2226 
2227 		ACSVM_CASE(PCD_PushWorldVar)
2228 			*sp = WorldVars[READ_BYTE_OR_INT32];
2229 			INC_BYTE_OR_INT32;
2230 			sp++;
2231 			ACSVM_BREAK;
2232 
2233 		ACSVM_CASE(PCD_AddScriptVar)
2234 			locals[READ_BYTE_OR_INT32] += sp[-1];
2235 			INC_BYTE_OR_INT32;
2236 			sp--;
2237 			ACSVM_BREAK;
2238 
2239 		ACSVM_CASE(PCD_AddMapVar)
2240 			*ActiveObject->MapVars[READ_BYTE_OR_INT32] += sp[-1];
2241 			INC_BYTE_OR_INT32;
2242 			sp--;
2243 			ACSVM_BREAK;
2244 
2245 		ACSVM_CASE(PCD_AddWorldVar)
2246 			WorldVars[READ_BYTE_OR_INT32] += sp[-1];
2247 			INC_BYTE_OR_INT32;
2248 			sp--;
2249 			ACSVM_BREAK;
2250 
2251 		ACSVM_CASE(PCD_SubScriptVar)
2252 			locals[READ_BYTE_OR_INT32] -= sp[-1];
2253 			INC_BYTE_OR_INT32;
2254 			sp--;
2255 			ACSVM_BREAK;
2256 
2257 		ACSVM_CASE(PCD_SubMapVar)
2258 			*ActiveObject->MapVars[READ_BYTE_OR_INT32] -= sp[-1];
2259 			INC_BYTE_OR_INT32;
2260 			sp--;
2261 			ACSVM_BREAK;
2262 
2263 		ACSVM_CASE(PCD_SubWorldVar)
2264 			WorldVars[READ_BYTE_OR_INT32] -= sp[-1];
2265 			INC_BYTE_OR_INT32;
2266 			sp--;
2267 			ACSVM_BREAK;
2268 
2269 		ACSVM_CASE(PCD_MulScriptVar)
2270 			locals[READ_BYTE_OR_INT32] *= sp[-1];
2271 			INC_BYTE_OR_INT32;
2272 			sp--;
2273 			ACSVM_BREAK;
2274 
2275 		ACSVM_CASE(PCD_MulMapVar)
2276 			*ActiveObject->MapVars[READ_BYTE_OR_INT32] *= sp[-1];
2277 			INC_BYTE_OR_INT32;
2278 			sp--;
2279 			ACSVM_BREAK;
2280 
2281 		ACSVM_CASE(PCD_MulWorldVar)
2282 			WorldVars[READ_BYTE_OR_INT32] *= sp[-1];
2283 			INC_BYTE_OR_INT32;
2284 			sp--;
2285 			ACSVM_BREAK;
2286 
2287 		ACSVM_CASE(PCD_DivScriptVar)
2288 			locals[READ_BYTE_OR_INT32] /= sp[-1];
2289 			INC_BYTE_OR_INT32;
2290 			sp--;
2291 			ACSVM_BREAK;
2292 
2293 		ACSVM_CASE(PCD_DivMapVar)
2294 			*ActiveObject->MapVars[READ_BYTE_OR_INT32] /= sp[-1];
2295 			INC_BYTE_OR_INT32;
2296 			sp--;
2297 			ACSVM_BREAK;
2298 
2299 		ACSVM_CASE(PCD_DivWorldVar)
2300 			WorldVars[READ_BYTE_OR_INT32] /= sp[-1];
2301 			INC_BYTE_OR_INT32;
2302 			sp--;
2303 			ACSVM_BREAK;
2304 
2305 		ACSVM_CASE(PCD_ModScriptVar)
2306 			locals[READ_BYTE_OR_INT32] %= sp[-1];
2307 			INC_BYTE_OR_INT32;
2308 			sp--;
2309 			ACSVM_BREAK;
2310 
2311 		ACSVM_CASE(PCD_ModMapVar)
2312 			*ActiveObject->MapVars[READ_BYTE_OR_INT32] %= sp[-1];
2313 			INC_BYTE_OR_INT32;
2314 			sp--;
2315 			ACSVM_BREAK;
2316 
2317 		ACSVM_CASE(PCD_ModWorldVar)
2318 			WorldVars[READ_BYTE_OR_INT32] %= sp[-1];
2319 			INC_BYTE_OR_INT32;
2320 			sp--;
2321 			ACSVM_BREAK;
2322 
2323 		ACSVM_CASE(PCD_IncScriptVar)
2324 			locals[READ_BYTE_OR_INT32]++;
2325 			INC_BYTE_OR_INT32;
2326 			ACSVM_BREAK;
2327 
2328 		ACSVM_CASE(PCD_IncMapVar)
2329 			(*ActiveObject->MapVars[READ_BYTE_OR_INT32])++;
2330 			INC_BYTE_OR_INT32;
2331 			ACSVM_BREAK;
2332 
2333 		ACSVM_CASE(PCD_IncWorldVar)
2334 			WorldVars[READ_BYTE_OR_INT32]++;
2335 			INC_BYTE_OR_INT32;
2336 			ACSVM_BREAK;
2337 
2338 		ACSVM_CASE(PCD_DecScriptVar)
2339 			locals[READ_BYTE_OR_INT32]--;
2340 			INC_BYTE_OR_INT32;
2341 			ACSVM_BREAK;
2342 
2343 		ACSVM_CASE(PCD_DecMapVar)
2344 			(*ActiveObject->MapVars[READ_BYTE_OR_INT32])--;
2345 			INC_BYTE_OR_INT32;
2346 			ACSVM_BREAK;
2347 
2348 		ACSVM_CASE(PCD_DecWorldVar)
2349 			WorldVars[READ_BYTE_OR_INT32]--;
2350 			INC_BYTE_OR_INT32;
2351 			ACSVM_BREAK;
2352 
2353 		ACSVM_CASE(PCD_Goto)
2354 			ip = ActiveObject->OffsetToPtr(READ_INT32(ip));
2355 			ACSVM_BREAK;
2356 
2357 		ACSVM_CASE(PCD_IfGoto)
2358 			if (sp[-1])
2359 			{
2360 				ip = ActiveObject->OffsetToPtr(READ_INT32(ip));
2361 			}
2362 			else
2363 			{
2364 				ip += 4;
2365 			}
2366 			sp--;
2367 			ACSVM_BREAK;
2368 
2369 		ACSVM_CASE(PCD_Drop)
2370 			sp--;
2371 			ACSVM_BREAK;
2372 
2373 		ACSVM_CASE(PCD_Delay)
2374 			DelayTime = float(sp[-1]) / 35.0;
2375 			sp--;
2376 			action = SCRIPT_Stop;
2377 			ACSVM_BREAK_STOP;
2378 
2379 		ACSVM_CASE(PCD_DelayDirect)
2380 			DelayTime = float(READ_INT32(ip)) / 35.0;
2381 			ip += 4;
2382 			action = SCRIPT_Stop;
2383 			ACSVM_BREAK_STOP;
2384 
2385 		ACSVM_CASE(PCD_Random)
2386 			sp[-2] = sp[-2] + (vint32)(Random() * (sp[-1] - sp[-2] + 1));
2387 			sp--;
2388 			ACSVM_BREAK;
2389 
2390 		ACSVM_CASE(PCD_RandomDirect)
2391 			*sp = READ_INT32(ip) + (vint32)(Random() * (READ_INT32(ip + 4) -
2392 				READ_INT32(ip) + 1));
2393 			ip += 8;
2394 			sp++;
2395 			ACSVM_BREAK;
2396 
2397 		ACSVM_CASE(PCD_ThingCount)
2398 			sp[-2] = Level->eventThingCount(sp[-2], NAME_None, sp[-1], -1);
2399 			sp--;
2400 			ACSVM_BREAK;
2401 
2402 		ACSVM_CASE(PCD_ThingCountDirect)
2403 			*sp = Level->eventThingCount(READ_INT32(ip), NAME_None,
2404 				READ_INT32(ip + 4), -1);
2405 			ip += 8;
2406 			sp++;
2407 			ACSVM_BREAK;
2408 
2409 		ACSVM_CASE(PCD_TagWait)
2410 			WaitValue = sp[-1];
2411 			State = ASTE_WaitingForTag;
2412 			sp--;
2413 			action = SCRIPT_Stop;
2414 			ACSVM_BREAK_STOP;
2415 
2416 		ACSVM_CASE(PCD_TagWaitDirect)
2417 			WaitValue = READ_INT32(ip);
2418 			State = ASTE_WaitingForTag;
2419 			ip += 4;
2420 			action = SCRIPT_Stop;
2421 			ACSVM_BREAK_STOP;
2422 
2423 		ACSVM_CASE(PCD_PolyWait)
2424 			WaitValue = sp[-1];
2425 			State = ASTE_WaitingForPoly;
2426 			sp--;
2427 			action = SCRIPT_Stop;
2428 			ACSVM_BREAK_STOP;
2429 
2430 		ACSVM_CASE(PCD_PolyWaitDirect)
2431 			WaitValue = READ_INT32(ip);
2432 			State = ASTE_WaitingForPoly;
2433 			ip += 4;
2434 			action = SCRIPT_Stop;
2435 			ACSVM_BREAK_STOP;
2436 
2437 		ACSVM_CASE(PCD_ChangeFloor)
2438 			{
2439 				int Flat = GTextureManager.NumForName(GetName8(sp[-1]),
2440 					TEXTYPE_Flat, true, true);
2441 				for  (int Idx = FindSectorFromTag(sp[-2], -1); Idx >= 0;
2442 					Idx = FindSectorFromTag(sp[-2], Idx))
2443 				{
2444 					XLevel->Sectors[Idx].floor.pic = Flat;
2445 				}
2446 				sp -= 2;
2447 			}
2448 			ACSVM_BREAK;
2449 
2450 		ACSVM_CASE(PCD_ChangeFloorDirect)
2451 			{
2452 				int Tag = READ_INT32(ip);
2453 				int Flat = GTextureManager.NumForName(GetName8(
2454 					READ_INT32(ip + 4)), TEXTYPE_Flat, true, true);
2455 				ip += 8;
2456 				for (int Idx = FindSectorFromTag(Tag, -1); Idx >= 0;
2457 					Idx = FindSectorFromTag(Tag, Idx))
2458 				{
2459 					XLevel->Sectors[Idx].floor.pic = Flat;
2460 				}
2461 			}
2462 			ACSVM_BREAK;
2463 
2464 		ACSVM_CASE(PCD_ChangeCeiling)
2465 			{
2466 				int Flat = GTextureManager.NumForName(GetName8(sp[-1]),
2467 					TEXTYPE_Flat, true, true);
2468 				for  (int Idx = FindSectorFromTag(sp[-2], -1); Idx >= 0;
2469 					Idx = FindSectorFromTag(sp[-2], Idx))
2470 				{
2471 					XLevel->Sectors[Idx].ceiling.pic = Flat;
2472 				}
2473 				sp -= 2;
2474 			}
2475 			ACSVM_BREAK;
2476 
2477 		ACSVM_CASE(PCD_ChangeCeilingDirect)
2478 			{
2479 				int Tag = READ_INT32(ip);
2480 				int Flat = GTextureManager.NumForName(GetName8(
2481 					READ_INT32(ip + 4)), TEXTYPE_Flat, true, true);
2482 				ip += 8;
2483 				for (int Idx = FindSectorFromTag(Tag, -1); Idx >= 0;
2484 					Idx = FindSectorFromTag(Tag, Idx))
2485 				{
2486 					XLevel->Sectors[Idx].ceiling.pic = Flat;
2487 				}
2488 			}
2489 			ACSVM_BREAK;
2490 
2491 		ACSVM_CASE(PCD_Restart)
2492 			ip = info->Address;
2493 			ACSVM_BREAK;
2494 
2495 		ACSVM_CASE(PCD_AndLogical)
2496 			sp[-2] = sp[-2] && sp[-1];
2497 			sp--;
2498 			ACSVM_BREAK;
2499 
2500 		ACSVM_CASE(PCD_OrLogical)
2501 			sp[-2] = sp[-2] || sp[-1];
2502 			sp--;
2503 			ACSVM_BREAK;
2504 
2505 		ACSVM_CASE(PCD_AndBitwise)
2506 			sp[-2] = sp[-2] & sp[-1];
2507 			sp--;
2508 			ACSVM_BREAK;
2509 
2510 		ACSVM_CASE(PCD_OrBitwise)
2511 			sp[-2] = sp[-2] | sp[-1];
2512 			sp--;
2513 			ACSVM_BREAK;
2514 
2515 		ACSVM_CASE(PCD_EorBitwise)
2516 			sp[-2] = sp[-2] ^ sp[-1];
2517 			sp--;
2518 			ACSVM_BREAK;
2519 
2520 		ACSVM_CASE(PCD_NegateLogical)
2521 			sp[-1] = !sp[-1];
2522 			ACSVM_BREAK;
2523 
2524 		ACSVM_CASE(PCD_LShift)
2525 			sp[-2] = sp[-2] << sp[-1];
2526 			sp--;
2527 			ACSVM_BREAK;
2528 
2529 		ACSVM_CASE(PCD_RShift)
2530 			sp[-2] = sp[-2] >> sp[-1];
2531 			sp--;
2532 			ACSVM_BREAK;
2533 
2534 		ACSVM_CASE(PCD_UnaryMinus)
2535 			sp[-1] = -sp[-1];
2536 			ACSVM_BREAK;
2537 
2538 		ACSVM_CASE(PCD_IfNotGoto)
2539 			if (!sp[-1])
2540 			{
2541 				ip = ActiveObject->OffsetToPtr(READ_INT32(ip));
2542 			}
2543 			else
2544 			{
2545 				ip += 4;
2546 			}
2547 			sp--;
2548 			ACSVM_BREAK;
2549 
2550 		ACSVM_CASE(PCD_LineSide)
2551 			*sp = side;
2552 			sp++;
2553 			ACSVM_BREAK;
2554 
2555 		ACSVM_CASE(PCD_ScriptWait)
2556 			WaitValue = sp[-1];
2557 			if (!XLevel->Acs->FindScript(WaitValue, WaitObject) ||
2558 				!XLevel->Acs->FindScript(WaitValue, WaitObject)->RunningScript)
2559 			{
2560 				State = ASTE_WaitingForScriptStart;
2561 			}
2562 			else
2563 			{
2564 				State = ASTE_WaitingForScript;
2565 			}
2566 			sp--;
2567 			action = SCRIPT_Stop;
2568 			ACSVM_BREAK_STOP;
2569 
2570 		ACSVM_CASE(PCD_ScriptWaitDirect)
2571 			WaitValue = READ_INT32(ip);
2572 			if (!XLevel->Acs->FindScript(WaitValue, WaitObject) ||
2573 				!XLevel->Acs->FindScript(WaitValue, WaitObject)->RunningScript)
2574 			{
2575 				State = ASTE_WaitingForScriptStart;
2576 			}
2577 			else
2578 			{
2579 				State = ASTE_WaitingForScript;
2580 			}
2581 			ip += 4;
2582 			action = SCRIPT_Stop;
2583 			ACSVM_BREAK_STOP;
2584 
2585 		ACSVM_CASE(PCD_ClearLineSpecial)
2586 			if (line)
2587 			{
2588 				line->special = 0;
2589 			}
2590 			ACSVM_BREAK;
2591 
2592 		ACSVM_CASE(PCD_CaseGoto)
2593 			if (sp[-1] == READ_INT32(ip))
2594 			{
2595 				ip = ActiveObject->OffsetToPtr(READ_INT32(ip + 4));
2596 				sp--;
2597 			}
2598 			else
2599 			{
2600 				ip += 8;
2601 			}
2602 			ACSVM_BREAK;
2603 
2604 		ACSVM_CASE(PCD_BeginPrint)
2605 			PrintStr.Clean();
2606 			ACSVM_BREAK;
2607 
2608 		ACSVM_CASE(PCD_EndPrint)
2609 			PrintStr = PrintStr.EvalEscapeSequences();
2610 			if (Activator && Activator->EntityFlags & VEntity::EF_IsPlayer)
2611 			{
2612 				Activator->Player->CentrePrintf(*PrintStr);
2613 			}
2614 			else
2615 			{
2616 				BroadcastCentrePrint(*PrintStr);
2617 			}
2618 			ACSVM_BREAK;
2619 
2620 		ACSVM_CASE(PCD_PrintString)
2621 			PrintStr += GetStr(sp[-1]);
2622 			sp--;
2623 			ACSVM_BREAK;
2624 
2625 		ACSVM_CASE(PCD_PrintNumber)
2626 			PrintStr +=  VStr(sp[-1]);
2627 			sp--;
2628 			ACSVM_BREAK;
2629 
2630 		ACSVM_CASE(PCD_PrintCharacter)
2631 			PrintStr += (char)sp[-1];
2632 			sp--;
2633 			ACSVM_BREAK;
2634 
2635 		ACSVM_CASE(PCD_PlayerCount)
2636 			sp[0] = 0;
2637 			for (int i = 0; i < MAXPLAYERS; i++)
2638 			{
2639 				if (Level->Game->Players[i])
2640 					sp[0]++;
2641 			}
2642 			sp++;
2643 			ACSVM_BREAK;
2644 
2645 		ACSVM_CASE(PCD_GameType)
2646 			if (GGameInfo->NetMode == NM_TitleMap)
2647 			{
2648 				*sp = GAME_TITLE_MAP;
2649 			}
2650 			else if (GGameInfo->NetMode == NM_Standalone)
2651 			{
2652 				*sp = GAME_SINGLE_PLAYER;
2653 			}
2654 			else if (deathmatch)
2655 			{
2656 				*sp = GAME_NET_DEATHMATCH;
2657 			}
2658 			else
2659 			{
2660 				*sp = GAME_NET_COOPERATIVE;
2661 			}
2662 			sp++;
2663 			ACSVM_BREAK;
2664 
2665 		ACSVM_CASE(PCD_GameSkill)
2666 			*sp = Level->World->SkillAcsReturn;
2667 			sp++;
2668 			ACSVM_BREAK;
2669 
2670 		ACSVM_CASE(PCD_Timer)
2671 			*sp = XLevel->TicTime;
2672 			sp++;
2673 			ACSVM_BREAK;
2674 
2675 		ACSVM_CASE(PCD_SectorSound)
2676 			Level->SectorStartSound(line ? line->frontsector : NULL,
2677 				GSoundManager->GetSoundID(GetName(sp[-2])), 0, sp[-1] / 127.0,
2678 				1.0);
2679 			sp -= 2;
2680 			ACSVM_BREAK;
2681 
2682 		ACSVM_CASE(PCD_AmbientSound)
2683 			StartSound(TVec(0, 0, 0), 0, GSoundManager->GetSoundID(
2684 				GetName(sp[-2])), 0, sp[-1] / 127.0, 0.0, false);
2685 			sp -= 2;
2686 			ACSVM_BREAK;
2687 
2688 		ACSVM_CASE(PCD_SoundSequence)
2689 			Level->SectorStartSequence(line ? line->frontsector : NULL,
2690 				GetName(sp[-1]), 0);
2691 			sp--;
2692 			ACSVM_BREAK;
2693 
2694 		ACSVM_CASE(PCD_SetLineTexture)
2695 			{
2696 				int Tex = GTextureManager.NumForName(GetName8(sp[-1]),
2697 					TEXTYPE_Wall, true, true);
2698 				int searcher = -1;
2699 				for (line_t *line = XLevel->FindLine(sp[-4], &searcher);
2700 					line != NULL; line = XLevel->FindLine(sp[-4], &searcher))
2701 				{
2702 					if (sp[-2] == TEXTURE_MIDDLE)
2703 					{
2704 						GLevel->Sides[line->sidenum[sp[-3]]].MidTexture = Tex;
2705 					}
2706 					else if (sp[-2] == TEXTURE_BOTTOM)
2707 					{
2708 						GLevel->Sides[line->sidenum[sp[-3]]].BottomTexture = Tex;
2709 					}
2710 					else
2711 					{
2712 						// TEXTURE_TOP
2713 						GLevel->Sides[line->sidenum[sp[-3]]].TopTexture = Tex;
2714 					}
2715 				}
2716 				sp -= 4;
2717 			}
2718 			ACSVM_BREAK;
2719 
2720 		ACSVM_CASE(PCD_SetLineBlocking)
2721 			{
2722 				int searcher = -1;
2723 				for (line_t* line = XLevel->FindLine(sp[-2], &searcher);
2724 					line != NULL; line = XLevel->FindLine(sp[-2], &searcher))
2725 				{
2726 					switch (sp[-1])
2727 					{
2728 					case BLOCK_NOTHING:
2729 						line->flags &= ~(ML_BLOCKING | ML_BLOCKEVERYTHING |
2730 							ML_RAILING | ML_BLOCKPLAYERS);
2731 						break;
2732 					case BLOCK_CREATURES:
2733 					default:
2734 						line->flags &= ~(ML_BLOCKEVERYTHING | ML_RAILING |
2735 							ML_BLOCKPLAYERS);
2736 						line->flags |= ML_BLOCKING;
2737 						break;
2738 					case BLOCK_EVERYTHING:
2739 						line->flags &= ~(ML_RAILING | ML_BLOCKPLAYERS);
2740 						line->flags |= ML_BLOCKING | ML_BLOCKEVERYTHING;
2741 						break;
2742 					case BLOCK_RAILING:
2743 						line->flags &= ~(ML_BLOCKEVERYTHING | ML_BLOCKPLAYERS);
2744 						line->flags |= ML_BLOCKING | ML_RAILING;
2745 						break;
2746 					case BLOCK_PLAYERS:
2747 						line->flags &= ~(ML_BLOCKING | ML_BLOCKEVERYTHING |
2748 							ML_RAILING);
2749 						line->flags |= ML_BLOCKPLAYERS;
2750 						break;
2751 					}
2752 				}
2753 				sp -= 2;
2754 			}
2755 			ACSVM_BREAK;
2756 
2757 		ACSVM_CASE(PCD_SetLineSpecial)
2758 			{
2759 				int searcher = -1;
2760 				for (line_t* line = XLevel->FindLine(sp[-7], &searcher);
2761 					line != NULL; line = XLevel->FindLine(sp[-7], &searcher))
2762 				{
2763 					line->special = sp[-6];
2764 					line->arg1 = sp[-5];
2765 					line->arg2 = sp[-4];
2766 					line->arg3 = sp[-3];
2767 					line->arg4 = sp[-2];
2768 					line->arg5 = sp[-1];
2769 				}
2770 				sp -= 7;
2771 			}
2772 			ACSVM_BREAK;
2773 
2774 		ACSVM_CASE(PCD_ThingSound)
2775 			{
2776 				VName sound = GetName(sp[-2]);
2777 				for (VEntity* mobj = Level->FindMobjFromTID(sp[-3], NULL);
2778 					mobj; mobj = Level->FindMobjFromTID(sp[-3], mobj))
2779 				{
2780 					mobj->StartSound(sound, 0, sp[-1] / 127.0, 1.0, false);
2781 				}
2782 				sp -= 3;
2783 			}
2784 			ACSVM_BREAK;
2785 
2786 		ACSVM_CASE(PCD_EndPrintBold)
2787 			PrintStr = PrintStr.EvalEscapeSequences();
2788 			BroadcastCentrePrint(*(VStr(TEXT_COLOUR_ESCAPE) + "+" + PrintStr));
2789 			ACSVM_BREAK;
2790 
2791 		//	Extended P-Code commands.
2792 		ACSVM_CASE(PCD_ActivatorSound)
2793 			if (Activator)
2794 			{
2795 				Activator->StartSound(GetName(sp[-2]), 0, sp[-1] / 127.0, 1.0, false);
2796 			}
2797 			else
2798 			{
2799 				StartSound(TVec(0, 0, 0), 0, GSoundManager->GetSoundID(
2800 					GetName(sp[-2])), 0, sp[-1] / 127.0, 1.0, false);
2801 			}
2802 			sp -= 2;
2803 			ACSVM_BREAK;
2804 
2805 		ACSVM_CASE(PCD_LocalAmbientSound)
2806 			if (Activator)
2807 			{
2808 				Activator->StartLocalSound(GetName(sp[-2]), 0, sp[-1] / 127.0,
2809 					1.0);
2810 			}
2811 			sp -= 2;
2812 			ACSVM_BREAK;
2813 
2814 		ACSVM_CASE(PCD_SetLineMonsterBlocking)
2815 			{
2816 				int searcher = -1;
2817 				for (line_t* line = XLevel->FindLine(sp[-2], &searcher);
2818 					line != NULL; line = XLevel->FindLine(sp[-2], &searcher))
2819 				{
2820 					if (sp[-1])
2821 						line->flags |= ML_BLOCKMONSTERS;
2822 					else
2823 						line->flags &= ~ML_BLOCKMONSTERS;
2824 				}
2825 				sp -= 2;
2826 			}
2827 			ACSVM_BREAK;
2828 
2829 		ACSVM_CASE(PCD_PlayerHealth)
2830 			if (Activator)
2831 			{
2832 				*sp = Activator->Health;
2833 			}
2834 			else
2835 			{
2836 				*sp = 0;
2837 			}
2838 			sp++;
2839 			ACSVM_BREAK;
2840 
2841 		ACSVM_CASE(PCD_PlayerArmorPoints)
2842 			if (Activator)
2843 			{
2844 				*sp = Activator->eventGetArmorPoints();
2845 			}
2846 			else
2847 			{
2848 				*sp = 0;
2849 			}
2850 			sp++;
2851 			ACSVM_BREAK;
2852 
2853 		ACSVM_CASE(PCD_PlayerFrags)
2854 			if (Activator && Activator->Player)
2855 			{
2856 				*sp = Activator->Player->Frags;
2857 			}
2858 			else
2859 			{
2860 				*sp = 0;
2861 			}
2862 			sp++;
2863 			ACSVM_BREAK;
2864 
2865 		ACSVM_CASE(PCD_PrintName)
2866 			{
2867 				VBasePlayer* Plr;
2868 				if (sp[-1] <= 0 || sp[-1] > MAXPLAYERS)
2869 				{
2870 					Plr = Activator ? Activator->Player : NULL;
2871 				}
2872 				else
2873 				{
2874 					Plr = Level->Game->Players[sp[-1] - 1];
2875 				}
2876 				if (Plr && (Plr->PlayerFlags & VBasePlayer::PF_Spawned))
2877 				{
2878 					PrintStr += Plr->PlayerName;
2879 				}
2880 				else if (Plr && !(Plr->PlayerFlags & VBasePlayer::PF_Spawned))
2881 				{
2882 					PrintStr += VStr("Player ") + VStr(sp[-1]);
2883 				}
2884 				else if (Activator)
2885 				{
2886 					PrintStr += Activator->GetClass()->GetName();
2887 				}
2888 				else
2889 				{
2890 					PrintStr += "Unknown";
2891 				}
2892 				sp--;
2893 			}
2894 			ACSVM_BREAK;
2895 
2896 		ACSVM_CASE(PCD_MusicChange)
2897 			Level->ChangeMusic(GetName8(sp[-2]));
2898 			sp -= 2;
2899 			ACSVM_BREAK;
2900 
2901 		ACSVM_CASE(PCD_SinglePlayer)
2902 			sp[-1] = GGameInfo->NetMode < NM_DedicatedServer;
2903 			sp++;
2904 			ACSVM_BREAK;
2905 
2906 		ACSVM_CASE(PCD_FixedMul)
2907 			sp[-2] = vint32((double)sp[-2] / (double)0x10000 * (double)sp[-1]);
2908 			sp--;
2909 			ACSVM_BREAK;
2910 
2911 		ACSVM_CASE(PCD_FixedDiv)
2912 			sp[-2] = vint32((double)sp[-2] / (double)sp[-1] * (double)0x10000);
2913 			sp--;
2914 			ACSVM_BREAK;
2915 
2916 		ACSVM_CASE(PCD_SetGravity)
2917 			Level->Gravity = ((float)sp[-1] / (float)0x10000) *
2918 				DEFAULT_GRAVITY / 800.0;
2919 			sp--;
2920 			ACSVM_BREAK;
2921 
2922 		ACSVM_CASE(PCD_SetGravityDirect)
2923 			Level->Gravity = ((float)READ_INT32(ip) / (float)0x10000) *
2924 				DEFAULT_GRAVITY / 800.0;
2925 			ip += 4;
2926 			ACSVM_BREAK;
2927 
2928 		ACSVM_CASE(PCD_SetAirControl)
2929 			Level->AirControl = float(sp[-1]) / 65536.0;
2930 			sp--;
2931 			ACSVM_BREAK;
2932 
2933 		ACSVM_CASE(PCD_SetAirControlDirect)
2934 			Level->AirControl = float(READ_INT32(ip)) / 65535.0;
2935 			ip += 4;
2936 			ACSVM_BREAK;
2937 
2938 		ACSVM_CASE(PCD_ClearInventory)
2939 			if (Activator)
2940 			{
2941 				Activator->eventClearInventory();
2942 			}
2943 			else
2944 			{
2945 				for (int i = 0; i < MAXPLAYERS; i++)
2946 				{
2947 					if (Level->Game->Players[i] &&
2948 						Level->Game->Players[i]->PlayerFlags & VBasePlayer::PF_Spawned)
2949 					{
2950 						Level->Game->Players[i]->MO->eventClearInventory();
2951 					}
2952 				}
2953 			}
2954 			ACSVM_BREAK;
2955 
2956 		ACSVM_CASE(PCD_GiveInventory)
2957 			if (Activator)
2958 			{
2959 				Activator->eventGiveInventory(GetNameLowerCase(sp[-2]),
2960 					sp[-1]);
2961 			}
2962 			else
2963 			{
2964 				for (int i = 0; i < MAXPLAYERS; i++)
2965 				{
2966 					if (Level->Game->Players[i] &&
2967 						Level->Game->Players[i]->PlayerFlags & VBasePlayer::PF_Spawned)
2968 					{
2969 						Level->Game->Players[i]->MO->eventGiveInventory(
2970 							GetNameLowerCase(sp[-2]), sp[-1]);
2971 					}
2972 				}
2973 			}
2974 			sp -= 2;
2975 			ACSVM_BREAK;
2976 
2977 		ACSVM_CASE(PCD_GiveInventoryDirect)
2978 			if (Activator)
2979 			{
2980 				Activator->eventGiveInventory(GetNameLowerCase(READ_INT32(ip)),
2981 					READ_INT32(ip + 4));
2982 			}
2983 			else
2984 			{
2985 				for (int i = 0; i < MAXPLAYERS; i++)
2986 				{
2987 					if (Level->Game->Players[i] &&
2988 						Level->Game->Players[i]->PlayerFlags & VBasePlayer::PF_Spawned)
2989 						Level->Game->Players[i]->MO->eventGiveInventory(
2990 							GetNameLowerCase(READ_INT32(ip)), READ_INT32(ip + 4));
2991 				}
2992 			}
2993 			ip += 8;
2994 			ACSVM_BREAK;
2995 
2996 		ACSVM_CASE(PCD_TakeInventory)
2997 			if (Activator)
2998 			{
2999 				Activator->eventTakeInventory(GetNameLowerCase(sp[-2]),
3000 					sp[-1]);
3001 			}
3002 			else
3003 			{
3004 				for (int i = 0; i < MAXPLAYERS; i++)
3005 				{
3006 					if (Level->Game->Players[i] &&
3007 						Level->Game->Players[i]->PlayerFlags & VBasePlayer::PF_Spawned)
3008 					{
3009 						Level->Game->Players[i]->MO->eventTakeInventory(
3010 							GetNameLowerCase(sp[-2]), sp[-1]);
3011 					}
3012 				}
3013 			}
3014 			sp -= 2;
3015 			ACSVM_BREAK;
3016 
3017 		ACSVM_CASE(PCD_TakeInventoryDirect)
3018 			if (Activator)
3019 			{
3020 				Activator->eventTakeInventory(GetNameLowerCase(READ_INT32(ip)),
3021 					READ_INT32(ip + 4));
3022 			}
3023 			else
3024 			{
3025 				for (int i = 0; i < MAXPLAYERS; i++)
3026 				{
3027 					if (Level->Game->Players[i] &&
3028 						Level->Game->Players[i]->PlayerFlags & VBasePlayer::PF_Spawned)
3029 						Level->Game->Players[i]->MO->eventTakeInventory(
3030 							GetNameLowerCase(READ_INT32(ip)),
3031 							READ_INT32(ip + 4));
3032 				}
3033 			}
3034 			ip += 8;
3035 			ACSVM_BREAK;
3036 
3037 		ACSVM_CASE(PCD_CheckInventory)
3038 			if (!Activator)
3039 			{
3040 				sp[-1] = 0;
3041 			}
3042 			else
3043 			{
3044 				sp[-1] = Activator->eventCheckInventory(GetNameLowerCase(
3045 					sp[-1]));
3046 			}
3047 			ACSVM_BREAK;
3048 
3049 		ACSVM_CASE(PCD_CheckInventoryDirect)
3050 			if (!Activator)
3051 			{
3052 				*sp = 0;
3053 			}
3054 			else
3055 			{
3056 				*sp = Activator->eventCheckInventory(GetNameLowerCase(
3057 					READ_INT32(ip)));
3058 			}
3059 			sp++;
3060 			ip += 4;
3061 			ACSVM_BREAK;
3062 
3063 		ACSVM_CASE(PCD_Spawn)
3064 			sp[-6] = Level->eventAcsSpawnThing(GetNameLowerCase(sp[-6]),
3065 				TVec(float(sp[-5]) / float(0x10000),
3066 				float(sp[-4]) / float(0x10000),
3067 				float(sp[-3]) / float(0x10000)),
3068 				sp[-2], float(sp[-1]) * 45.0 / 32.0);
3069 			sp -= 5;
3070 			ACSVM_BREAK;
3071 
3072 		ACSVM_CASE(PCD_SpawnDirect)
3073 			*sp = Level->eventAcsSpawnThing(GetNameLowerCase(READ_INT32(ip)),
3074 				TVec(float(READ_INT32(ip + 4)) / float(0x10000),
3075 				float(READ_INT32(ip + 8)) / float(0x10000),
3076 				float(READ_INT32(ip + 12)) / float(0x10000)),
3077 				READ_INT32(ip + 16), float(READ_INT32(ip + 20)) * 45.0 / 32.0);
3078 			sp++;
3079 			ACSVM_BREAK;
3080 
3081 		ACSVM_CASE(PCD_SpawnSpot)
3082 			sp[-4] = Level->eventAcsSpawnSpot(GetNameLowerCase(sp[-4]),
3083 				sp[-3], sp[-2], float(sp[-1]) * 45.0 / 32.0);
3084 			sp -= 3;
3085 			ACSVM_BREAK;
3086 
3087 		ACSVM_CASE(PCD_SpawnSpotDirect)
3088 			*sp = Level->eventAcsSpawnSpot(GetNameLowerCase(READ_INT32(ip)),
3089 				READ_INT32(ip + 4), READ_INT32(ip + 8),
3090 				float(READ_INT32(ip + 12)) * 45.0 / 32.0);
3091 			sp++;
3092 			ACSVM_BREAK;
3093 
3094 		ACSVM_CASE(PCD_SetMusic)
3095 			Level->ChangeMusic(GetName8(sp[-3]));
3096 			sp -= 3;
3097 			ACSVM_BREAK;
3098 
3099 		ACSVM_CASE(PCD_SetMusicDirect)
3100 			Level->ChangeMusic(GetName8(READ_INT32(ip)));
3101 			ip += 12;
3102 			ACSVM_BREAK;
3103 
3104 		ACSVM_CASE(PCD_LocalSetMusic)
3105 			if (Activator && Activator->EntityFlags & VEntity::EF_IsPlayer)
3106 			{
3107 				Activator->Player->eventClientChangeMusic(
3108 					GetNameLowerCase(sp[-3]), 0);
3109 			}
3110 			sp -= 3;
3111 			ACSVM_BREAK;
3112 
3113 		ACSVM_CASE(PCD_LocalSetMusicDirect)
3114 			if (Activator && Activator->EntityFlags & VEntity::EF_IsPlayer)
3115 			{
3116 				Activator->Player->eventClientChangeMusic(
3117 					GetNameLowerCase(READ_INT32(ip)), 0);
3118 			}
3119 			ip += 12;
3120 			ACSVM_BREAK;
3121 
3122 		ACSVM_CASE(PCD_PrintFixed)
3123 			PrintStr += VStr(float(sp[-1]) / float(0x10000));
3124 			sp--;
3125 			ACSVM_BREAK;
3126 
3127 		ACSVM_CASE(PCD_PrintLocalised)
3128 			PrintStr += GLanguage[GetName(sp[-1])];
3129 			sp--;
3130 			ACSVM_BREAK;
3131 
3132 		ACSVM_CASE(PCD_MoreHudMessage)
3133 			PrintStr = PrintStr.EvalEscapeSequences();
3134 			optstart = NULL;
3135 			ACSVM_BREAK;
3136 
3137 		ACSVM_CASE(PCD_OptHudMessage)
3138 			optstart = sp;
3139 			ACSVM_BREAK;
3140 
3141 		ACSVM_CASE(PCD_EndHudMessage)
3142 		ACSVM_CASE(PCD_EndHudMessageBold)
3143 			if (!optstart)
3144 			{
3145 				optstart = sp;
3146 			}
3147 			{
3148 				int Type = optstart[-6];
3149 				int Id = optstart[-5];
3150 				int Colour = optstart[-4];
3151 				VStr ColourName;
3152 				if (Type & HUDMSG_COLORSTRING)
3153 				{
3154 					ColourName = GetStr(optstart[-4]);
3155 					Colour = -1;
3156 				}
3157 				float x = (float)optstart[-3] / float(0x10000);
3158 				float y = (float)optstart[-2] / float(0x10000);
3159 				float HoldTime = (float)optstart[-1] / float(0x10000);
3160 				float Time1 = 0;
3161 				float Time2 = 0;
3162 				switch (Type & 0xffff)
3163 				{
3164 				case HUDMSG_FADEOUT:
3165 					Time1 = optstart < sp ?
3166 						(float)optstart[0] / float(0x10000) : 0.5;
3167 					break;
3168 				case HUDMSG_TYPEON:
3169 					Time1 = optstart < sp ?
3170 						(float)optstart[0] / float(0x10000) : 0.05;
3171 					Time2 = optstart < sp - 1 ?
3172 						(float)optstart[1] / float(0x10000) : 0.5;
3173 					break;
3174 				case HUDMSG_FADEINOUT:
3175 					Time1 = optstart < sp ?
3176 						(float)optstart[0] / float(0x10000) : 0.5;
3177 					Time2 = optstart < sp - 1 ?
3178 						(float)optstart[1] / float(0x10000) : 0.5;
3179 					break;
3180 				}
3181 				if (cmd != PCD_EndHudMessageBold && Activator &&
3182 					(Activator->EntityFlags & VEntity::EF_IsPlayer))
3183 				{
3184 					Activator->Player->eventClientHudMessage(PrintStr, Font,
3185 						Type, Id, Colour, ColourName, x, y, HudWidth,
3186 						HudHeight, HoldTime, Time1, Time2);
3187 				}
3188 				else
3189 				{
3190 					for (int i = 0; i < MAXPLAYERS; i++)
3191 					{
3192 						if (Level->Game->Players[i] &&
3193 							(Level->Game->Players[i]->PlayerFlags &
3194 							VBasePlayer::PF_Spawned))
3195 						{
3196 							Level->Game->Players[i]->eventClientHudMessage(
3197 								PrintStr, Font, Type, Id, Colour, ColourName,
3198 								x, y, HudWidth, HudHeight, HoldTime, Time1,
3199 								Time2);
3200 						}
3201 					}
3202 				}
3203 				sp = optstart - 6;
3204 			}
3205 			ACSVM_BREAK;
3206 
3207 		ACSVM_CASE(PCD_SetFont)
3208 			Font = *GetStr(sp[-1]).ToLower();
3209 			sp--;
3210 			ACSVM_BREAK;
3211 
3212 		ACSVM_CASE(PCD_SetFontDirect)
3213 			Font = *VStr(READ_INT32(ip)).ToLower();
3214 			ip += 4;
3215 			ACSVM_BREAK;
3216 
3217 		ACSVM_CASE(PCD_PushByte)
3218 			*sp = *ip;
3219 			sp++;
3220 			ip++;
3221 			ACSVM_BREAK;
3222 
3223 		ACSVM_CASE(PCD_LSpec1DirectB)
3224 			Level->eventExecuteActionSpecial(ip[0], ip[1], 0, 0, 0, 0, line,
3225 				side, Activator);
3226 			ip += 2;
3227 			ACSVM_BREAK;
3228 
3229 		ACSVM_CASE(PCD_LSpec2DirectB)
3230 			Level->eventExecuteActionSpecial(ip[0], ip[1], ip[2], 0, 0, 0,
3231 				line, side, Activator);
3232 			ip += 3;
3233 			ACSVM_BREAK;
3234 
3235 		ACSVM_CASE(PCD_LSpec3DirectB)
3236 			Level->eventExecuteActionSpecial(ip[0], ip[1], ip[2], ip[3], 0,
3237 				0, line, side, Activator);
3238 			ip += 4;
3239 			ACSVM_BREAK;
3240 
3241 		ACSVM_CASE(PCD_LSpec4DirectB)
3242 			Level->eventExecuteActionSpecial(ip[0], ip[1], ip[2], ip[3],
3243 				ip[4], 0, line, side, Activator);
3244 			ip += 5;
3245 			ACSVM_BREAK;
3246 
3247 		ACSVM_CASE(PCD_LSpec5DirectB)
3248 			Level->eventExecuteActionSpecial(ip[0], ip[1], ip[2], ip[3],
3249 				ip[4], ip[5], line, side, Activator);
3250 			ip += 6;
3251 			ACSVM_BREAK;
3252 
3253 		ACSVM_CASE(PCD_DelayDirectB)
3254 			DelayTime = float(*ip) / 35.0;
3255 			ip++;
3256 			action = SCRIPT_Stop;
3257 			ACSVM_BREAK_STOP;
3258 
3259 		ACSVM_CASE(PCD_RandomDirectB)
3260 			*sp = ip[0] + (vint32)(Random() * (ip[1] - ip[0] + 1));
3261 			ip += 2;
3262 			sp++;
3263 			ACSVM_BREAK;
3264 
3265 		ACSVM_CASE(PCD_PushBytes)
3266 			for (int i = 0; i < ip[0]; i++)
3267 				sp[i] = ip[i + 1];
3268 			sp += ip[0];
3269 			ip += ip[0] + 1;
3270 			ACSVM_BREAK;
3271 
3272 		ACSVM_CASE(PCD_Push2Bytes)
3273 			sp[0] = ip[0];
3274 			sp[1] = ip[1];
3275 			ip += 2;
3276 			sp += 2;
3277 			ACSVM_BREAK;
3278 
3279 		ACSVM_CASE(PCD_Push3Bytes)
3280 			sp[0] = ip[0];
3281 			sp[1] = ip[1];
3282 			sp[2] = ip[2];
3283 			ip += 3;
3284 			sp += 3;
3285 			ACSVM_BREAK;
3286 
3287 		ACSVM_CASE(PCD_Push4Bytes)
3288 			sp[0] = ip[0];
3289 			sp[1] = ip[1];
3290 			sp[2] = ip[2];
3291 			sp[3] = ip[3];
3292 			ip += 4;
3293 			sp += 4;
3294 			ACSVM_BREAK;
3295 
3296 		ACSVM_CASE(PCD_Push5Bytes)
3297 			sp[0] = ip[0];
3298 			sp[1] = ip[1];
3299 			sp[2] = ip[2];
3300 			sp[3] = ip[3];
3301 			sp[4] = ip[4];
3302 			ip += 5;
3303 			sp += 5;
3304 			ACSVM_BREAK;
3305 
3306 		ACSVM_CASE(PCD_SetThingSpecial)
3307 			{
3308 				for (VEntity* Ent = Level->FindMobjFromTID(sp[-7], NULL);
3309 					Ent; Ent = Level->FindMobjFromTID(sp[-7], Ent))
3310 				{
3311 					Ent->Special = sp[-6];
3312 					Ent->Args[0] = sp[-5];
3313 					Ent->Args[1] = sp[-4];
3314 					Ent->Args[2] = sp[-3];
3315 					Ent->Args[3] = sp[-2];
3316 					Ent->Args[4] = sp[-1];
3317 				}
3318 				sp -= 7;
3319 			}
3320 			ACSVM_BREAK;
3321 
3322 		ACSVM_CASE(PCD_AssignGlobalVar)
3323 			GlobalVars[READ_BYTE_OR_INT32] = sp[-1];
3324 			INC_BYTE_OR_INT32;
3325 			sp--;
3326 			ACSVM_BREAK;
3327 
3328 		ACSVM_CASE(PCD_PushGlobalVar)
3329 			*sp = GlobalVars[READ_BYTE_OR_INT32];
3330 			INC_BYTE_OR_INT32;
3331 			sp++;
3332 			ACSVM_BREAK;
3333 
3334 		ACSVM_CASE(PCD_AddGlobalVar)
3335 			GlobalVars[READ_BYTE_OR_INT32] += sp[-1];
3336 			INC_BYTE_OR_INT32;
3337 			sp--;
3338 			ACSVM_BREAK;
3339 
3340 		ACSVM_CASE(PCD_SubGlobalVar)
3341 			GlobalVars[READ_BYTE_OR_INT32] -= sp[-1];
3342 			INC_BYTE_OR_INT32;
3343 			sp--;
3344 			ACSVM_BREAK;
3345 
3346 		ACSVM_CASE(PCD_MulGlobalVar)
3347 			GlobalVars[READ_BYTE_OR_INT32] *= sp[-1];
3348 			INC_BYTE_OR_INT32;
3349 			sp--;
3350 			ACSVM_BREAK;
3351 
3352 		ACSVM_CASE(PCD_DivGlobalVar)
3353 			GlobalVars[READ_BYTE_OR_INT32] /= sp[-1];
3354 			INC_BYTE_OR_INT32;
3355 			sp--;
3356 			ACSVM_BREAK;
3357 
3358 		ACSVM_CASE(PCD_ModGlobalVar)
3359 			GlobalVars[READ_BYTE_OR_INT32] %= sp[-1];
3360 			INC_BYTE_OR_INT32;
3361 			sp--;
3362 			ACSVM_BREAK;
3363 
3364 		ACSVM_CASE(PCD_IncGlobalVar)
3365 			GlobalVars[READ_BYTE_OR_INT32]++;
3366 			INC_BYTE_OR_INT32;
3367 			ACSVM_BREAK;
3368 
3369 		ACSVM_CASE(PCD_DecGlobalVar)
3370 			GlobalVars[READ_BYTE_OR_INT32]--;
3371 			INC_BYTE_OR_INT32;
3372 			ACSVM_BREAK;
3373 
3374 		ACSVM_CASE(PCD_FadeTo)
3375 			Level->eventAcsFadeRange(0, 0, 0, -1, (float)sp[-5] / 255.0,
3376 				(float)sp[-4] / 255.0, (float)sp[-3] / 255.0,
3377 				(float)sp[-2] / 65536.0, (float)sp[-1] / 65536.0, Activator);
3378 			sp -= 5;
3379 			ACSVM_BREAK;
3380 
3381 		ACSVM_CASE(PCD_FadeRange)
3382 			Level->eventAcsFadeRange((float)sp[-9] / 255.0,
3383 				(float)sp[-8] / 255.0, (float)sp[-7] / 255.0,
3384 				(float)sp[-6] / 65536.0, (float)sp[-5] / 255.0,
3385 				(float)sp[-4] / 255.0, (float)sp[-3] / 255.0,
3386 				(float)sp[-2] / 65536.0, (float)sp[-1] / 65536.0, Activator);
3387 			sp -= 9;
3388 			ACSVM_BREAK;
3389 
3390 		ACSVM_CASE(PCD_CancelFade)
3391 			Level->eventAcsCancelFade(Activator);
3392 			ACSVM_BREAK;
3393 
3394 		ACSVM_CASE(PCD_PlayMovie)
3395 			STUB(PCD_PlayMovie)
3396 			//FIXME implement this
3397 			//sp[-1] - movie name, string
3398 			//Pushes result
3399 			sp[-1] = 0;
3400 			ACSVM_BREAK;
3401 
3402 		ACSVM_CASE(PCD_SetFloorTrigger)
3403 			Level->eventStartPlaneWatcher(Activator, line, side, false,
3404 				sp[-8], sp[-7], sp[-6], sp[-5], sp[-4], sp[-3], sp[-2],
3405 				sp[-1]);
3406 			sp -= 8;
3407 			ACSVM_BREAK;
3408 
3409 		ACSVM_CASE(PCD_SetCeilingTrigger)
3410 			Level->eventStartPlaneWatcher(Activator, line, side, true,
3411 				sp[-8], sp[-7], sp[-6], sp[-5], sp[-4], sp[-3], sp[-2],
3412 				sp[-1]);
3413 			sp -= 8;
3414 			ACSVM_BREAK;
3415 
3416 		ACSVM_CASE(PCD_GetActorX)
3417 			{
3418 				VEntity* Ent = EntityFromTID(sp[-1], Activator);
3419 				if (!Ent)
3420 				{
3421 					sp[-1] = 0;
3422 				}
3423 				else
3424 				{
3425 					sp[-1] = vint32(Ent->Origin.x * 0x10000);
3426 				}
3427 			}
3428 			ACSVM_BREAK;
3429 
3430 		ACSVM_CASE(PCD_GetActorY)
3431 			{
3432 				VEntity* Ent = EntityFromTID(sp[-1], Activator);
3433 				if (!Ent)
3434 				{
3435 					sp[-1] = 0;
3436 				}
3437 				else
3438 				{
3439 					sp[-1] = vint32(Ent->Origin.y * 0x10000);
3440 				}
3441 			}
3442 			ACSVM_BREAK;
3443 
3444 		ACSVM_CASE(PCD_GetActorZ)
3445 			{
3446 				VEntity* Ent = EntityFromTID(sp[-1], Activator);
3447 				if (!Ent)
3448 				{
3449 					sp[-1] = 0;
3450 				}
3451 				else
3452 				{
3453 					sp[-1] = vint32(Ent->Origin.z * 0x10000);
3454 				}
3455 			}
3456 			ACSVM_BREAK;
3457 
3458 		ACSVM_CASE(PCD_StartTranslation)
3459 			if (sp[-1] >= 1 && sp[-1] <= MAX_LEVEL_TRANSLATIONS)
3460 			{
3461 				while (XLevel->Translations.Num() < sp[-1])
3462 				{
3463 					XLevel->Translations.Append(NULL);
3464 				}
3465 				Translation = XLevel->Translations[sp[-1] - 1];
3466 				if (!Translation)
3467 				{
3468 					Translation = new VTextureTranslation;
3469 					XLevel->Translations[sp[-1] - 1] = Translation;
3470 				}
3471 				else
3472 				{
3473 					Translation->Clear();
3474 				}
3475 			}
3476 			sp--;
3477 			ACSVM_BREAK;
3478 
3479 		ACSVM_CASE(PCD_TranslationRange1)
3480 			if (Translation)
3481 			{
3482 				Translation->MapToRange(sp[-4], sp[-3], sp[-2], sp[-1]);
3483 			}
3484 			sp -= 4;
3485 			ACSVM_BREAK;
3486 
3487 		ACSVM_CASE(PCD_TranslationRange2)
3488 			if (Translation)
3489 			{
3490 				Translation->MapToColours(sp[-8], sp[-7], sp[-6], sp[-5],
3491 					sp[-4], sp[-3], sp[-2], sp[-1]);
3492 			}
3493 			sp -= 8;
3494 			ACSVM_BREAK;
3495 
3496 		ACSVM_CASE(PCD_EndTranslation)
3497 			//	Nothing to do here.
3498 			ACSVM_BREAK;
3499 
3500 		ACSVM_CASE(PCD_Call)
3501 		ACSVM_CASE(PCD_CallDiscard)
3502 			{
3503 				int funcnum;
3504 				int i;
3505 				VAcsObject* object = ActiveObject;
3506 
3507 				funcnum = READ_BYTE_OR_INT32;
3508 				INC_BYTE_OR_INT32;
3509 				VAcsFunction* func = ActiveObject->GetFunction(funcnum,
3510 					object);
3511 				if (!func)
3512 				{
3513 					GCon->Logf("Function %d in script %d out of range",
3514 						funcnum, number);
3515 					action = SCRIPT_Terminate;
3516 					ACSVM_BREAK_STOP;
3517 				}
3518 				if ((sp - stack) + func->LocalCount + 64 > ACS_STACK_DEPTH)
3519 				{
3520 					// 64 is the margin for the function's working space
3521 					GCon->Logf("Out of stack space in script %d", number);
3522 					action = SCRIPT_Terminate;
3523 					ACSVM_BREAK_STOP;
3524 				}
3525 				//	The function's first argument is also its first local
3526 				// variable.
3527 				locals = sp - func->ArgCount;
3528 				//	Make space on the stack for any other variables the
3529 				// function uses.
3530 				for (i = 0; i < func->LocalCount; i++)
3531 				{
3532 					sp[i] = 0;
3533 				}
3534 				sp += i;
3535 				((VAcsCallReturn*)sp)->ReturnAddress =
3536 					ActiveObject->PtrToOffset(ip);
3537 				((VAcsCallReturn*)sp)->ReturnFunction = activeFunction;
3538 				((VAcsCallReturn*)sp)->ReturnObject = ActiveObject;
3539 				((VAcsCallReturn*)sp)->bDiscardResult = (cmd == PCD_CallDiscard);
3540 				sp += sizeof(VAcsCallReturn) / sizeof(vint32);
3541 				ActiveObject = object;
3542 				ip = ActiveObject->OffsetToPtr(func->Address);
3543 				activeFunction = func;
3544 			}
3545 			ACSVM_BREAK;
3546 
3547 		ACSVM_CASE(PCD_ReturnVoid)
3548 		ACSVM_CASE(PCD_ReturnVal)
3549 			{
3550 				int value;
3551 				VAcsCallReturn* retState;
3552 
3553 				if (cmd == PCD_ReturnVal)
3554 				{
3555 					value = sp[-1];
3556 					sp--;
3557 				}
3558 				else
3559 				{
3560 					value = 0;
3561 				}
3562 				sp -= sizeof(VAcsCallReturn) / sizeof(vint32);
3563 				retState = (VAcsCallReturn*)sp;
3564 				sp -= activeFunction->ArgCount + activeFunction->LocalCount;
3565 				ActiveObject = retState->ReturnObject;
3566 				activeFunction = retState->ReturnFunction;
3567 				ip = ActiveObject->OffsetToPtr(retState->ReturnAddress);
3568 				fmt = ActiveObject->GetFormat();
3569 				if (!activeFunction)
3570 				{
3571 					locals = LocalVars;
3572 				}
3573 				else
3574 				{
3575 					locals = sp - activeFunction->ArgCount -
3576 						activeFunction->LocalCount - sizeof(VAcsCallReturn) /
3577 						sizeof(vint32);
3578 				}
3579 				if (!retState->bDiscardResult)
3580 				{
3581 					*sp = value;
3582 					sp++;
3583 				}
3584 			}
3585 			ACSVM_BREAK;
3586 
3587 		ACSVM_CASE(PCD_PushMapArray)
3588 			sp[-1] = ActiveObject->GetArrayVal(*ActiveObject->MapVars[
3589 				READ_BYTE_OR_INT32], sp[-1]);
3590 			INC_BYTE_OR_INT32;
3591 			ACSVM_BREAK;
3592 
3593 		ACSVM_CASE(PCD_AssignMapArray)
3594 			ActiveObject->SetArrayVal(*ActiveObject->MapVars[
3595 				READ_BYTE_OR_INT32], sp[-2], sp[-1]);
3596 			INC_BYTE_OR_INT32;
3597 			sp -= 2;
3598 			ACSVM_BREAK;
3599 
3600 		ACSVM_CASE(PCD_AddMapArray)
3601 			{
3602 				int ANum = *ActiveObject->MapVars[READ_BYTE_OR_INT32];
3603 				ActiveObject->SetArrayVal(ANum, sp[-2],
3604 					ActiveObject->GetArrayVal(ANum, sp[-2]) + sp[-1]);
3605 				INC_BYTE_OR_INT32;
3606 				sp -= 2;
3607 			}
3608 			ACSVM_BREAK;
3609 
3610 		ACSVM_CASE(PCD_SubMapArray)
3611 			{
3612 				int ANum = *ActiveObject->MapVars[READ_BYTE_OR_INT32];
3613 				ActiveObject->SetArrayVal(ANum, sp[-2],
3614 					ActiveObject->GetArrayVal(ANum, sp[-2]) - sp[-1]);
3615 				INC_BYTE_OR_INT32;
3616 				sp -= 2;
3617 			}
3618 			ACSVM_BREAK;
3619 
3620 		ACSVM_CASE(PCD_MulMapArray)
3621 			{
3622 				int ANum = *ActiveObject->MapVars[READ_BYTE_OR_INT32];
3623 				ActiveObject->SetArrayVal(ANum, sp[-2],
3624 					ActiveObject->GetArrayVal(ANum, sp[-2]) * sp[-1]);
3625 				INC_BYTE_OR_INT32;
3626 				sp -= 2;
3627 			}
3628 			ACSVM_BREAK;
3629 
3630 		ACSVM_CASE(PCD_DivMapArray)
3631 			{
3632 				int ANum = *ActiveObject->MapVars[READ_BYTE_OR_INT32];
3633 				ActiveObject->SetArrayVal(ANum, sp[-2],
3634 					ActiveObject->GetArrayVal(ANum, sp[-2]) / sp[-1]);
3635 				INC_BYTE_OR_INT32;
3636 				sp -= 2;
3637 			}
3638 			ACSVM_BREAK;
3639 
3640 		ACSVM_CASE(PCD_ModMapArray)
3641 			{
3642 				int ANum = *ActiveObject->MapVars[READ_BYTE_OR_INT32];
3643 				ActiveObject->SetArrayVal(ANum, sp[-2],
3644 					ActiveObject->GetArrayVal(ANum, sp[-2]) % sp[-1]);
3645 				INC_BYTE_OR_INT32;
3646 				sp -= 2;
3647 			}
3648 			ACSVM_BREAK;
3649 
3650 		ACSVM_CASE(PCD_IncMapArray)
3651 			{
3652 				int ANum = *ActiveObject->MapVars[READ_BYTE_OR_INT32];
3653 				ActiveObject->SetArrayVal(ANum, sp[-1],
3654 					ActiveObject->GetArrayVal(ANum, sp[-1]) + 1);
3655 				INC_BYTE_OR_INT32;
3656 				sp--;
3657 			}
3658 			ACSVM_BREAK;
3659 
3660 		ACSVM_CASE(PCD_DecMapArray)
3661 			{
3662 				int ANum = *ActiveObject->MapVars[READ_BYTE_OR_INT32];
3663 				ActiveObject->SetArrayVal(ANum, sp[-1],
3664 					ActiveObject->GetArrayVal(ANum, sp[-1]) - 1);
3665 				INC_BYTE_OR_INT32;
3666 				sp--;
3667 			}
3668 			ACSVM_BREAK;
3669 
3670 		ACSVM_CASE(PCD_Dup)
3671 			*sp = sp[-1];
3672 			sp++;
3673 			ACSVM_BREAK;
3674 
3675 		ACSVM_CASE(PCD_Swap)
3676 			{
3677 				int tmp = sp[-2];
3678 				sp[-2] = sp[-1];
3679 				sp[-1] = tmp;
3680 			}
3681 			ACSVM_BREAK;
3682 
3683 		ACSVM_CASE(PCD_Sin)
3684 			sp[-1] = vint32(msin(float(sp[-1]) * 360.0 / 0x10000) * 0x10000);
3685 			ACSVM_BREAK;
3686 
3687 		ACSVM_CASE(PCD_Cos)
3688 			sp[-1] = vint32(mcos(float(sp[-1]) * 360.0 / 0x10000) * 0x10000);
3689 			ACSVM_BREAK;
3690 
3691 		ACSVM_CASE(PCD_VectorAngle)
3692 			sp[-2] = vint32(matan(float(sp[-1]) / float(0x10000),
3693 				float(sp[-2]) / float(0x10000)) / 360.0 * 0x10000);
3694 			sp--;
3695 			ACSVM_BREAK;
3696 
3697 		ACSVM_CASE(PCD_CheckWeapon)
3698 			if (Activator)
3699 			{
3700 				sp[-1] = Activator->eventCheckNamedWeapon(GetNameLowerCase(
3701 					sp[-1]));
3702 			}
3703 			else
3704 			{
3705 				sp[-1] = 0;
3706 			}
3707 			ACSVM_BREAK;
3708 
3709 		ACSVM_CASE(PCD_SetWeapon)
3710 			if (Activator)
3711 			{
3712 				sp[-1] = Activator->eventSetNamedWeapon(GetNameLowerCase(
3713 					sp[-1]));
3714 			}
3715 			else
3716 			{
3717 				sp[-1] = 0;
3718 			}
3719 			ACSVM_BREAK;
3720 
3721 		ACSVM_CASE(PCD_TagString)
3722 			sp[-1] |= ActiveObject->GetLibraryID();
3723 			ACSVM_BREAK;
3724 
3725 		ACSVM_CASE(PCD_PushWorldArray)
3726 			sp[-1] = WorldArrays[READ_BYTE_OR_INT32].GetElemVal(sp[-1]);
3727 			INC_BYTE_OR_INT32;
3728 			ACSVM_BREAK;
3729 
3730 		ACSVM_CASE(PCD_AssignWorldArray)
3731 			WorldArrays[READ_BYTE_OR_INT32].SetElemVal(sp[-2], sp[-1]);
3732 			INC_BYTE_OR_INT32;
3733 			sp -= 2;
3734 			ACSVM_BREAK;
3735 
3736 		ACSVM_CASE(PCD_AddWorldArray)
3737 			{
3738 				int ANum = READ_BYTE_OR_INT32;
3739 				WorldArrays[ANum].SetElemVal(sp[-2],
3740 					WorldArrays[ANum].GetElemVal(sp[-2]) + sp[-1]);
3741 				INC_BYTE_OR_INT32;
3742 				sp -= 2;
3743 			}
3744 			ACSVM_BREAK;
3745 
3746 		ACSVM_CASE(PCD_SubWorldArray)
3747 			{
3748 				int ANum = READ_BYTE_OR_INT32;
3749 				WorldArrays[ANum].SetElemVal(sp[-2],
3750 					WorldArrays[ANum].GetElemVal(sp[-2]) - sp[-1]);
3751 				INC_BYTE_OR_INT32;
3752 				sp -= 2;
3753 			}
3754 			ACSVM_BREAK;
3755 
3756 		ACSVM_CASE(PCD_MulWorldArray)
3757 			{
3758 				int ANum = READ_BYTE_OR_INT32;
3759 				WorldArrays[ANum].SetElemVal(sp[-2],
3760 					WorldArrays[ANum].GetElemVal(sp[-2]) * sp[-1]);
3761 				INC_BYTE_OR_INT32;
3762 				sp -= 2;
3763 			}
3764 			ACSVM_BREAK;
3765 
3766 		ACSVM_CASE(PCD_DivWorldArray)
3767 			{
3768 				int ANum = READ_BYTE_OR_INT32;
3769 				WorldArrays[ANum].SetElemVal(sp[-2],
3770 					WorldArrays[ANum].GetElemVal(sp[-2]) / sp[-1]);
3771 				INC_BYTE_OR_INT32;
3772 				sp -= 2;
3773 			}
3774 			ACSVM_BREAK;
3775 
3776 		ACSVM_CASE(PCD_ModWorldArray)
3777 			{
3778 				int ANum = READ_BYTE_OR_INT32;
3779 				WorldArrays[ANum].SetElemVal(sp[-2],
3780 					WorldArrays[ANum].GetElemVal(sp[-2]) % sp[-1]);
3781 				INC_BYTE_OR_INT32;
3782 				sp -= 2;
3783 			}
3784 			ACSVM_BREAK;
3785 
3786 		ACSVM_CASE(PCD_IncWorldArray)
3787 			{
3788 				int ANum = READ_BYTE_OR_INT32;
3789 				WorldArrays[ANum].SetElemVal(sp[-1],
3790 					WorldArrays[ANum].GetElemVal(sp[-1]) + 1);
3791 				INC_BYTE_OR_INT32;
3792 				sp--;
3793 			}
3794 			ACSVM_BREAK;
3795 
3796 		ACSVM_CASE(PCD_DecWorldArray)
3797 			{
3798 				int ANum = READ_BYTE_OR_INT32;
3799 				WorldArrays[ANum].SetElemVal(sp[-1],
3800 					WorldArrays[ANum].GetElemVal(sp[-1]) - 1);
3801 				INC_BYTE_OR_INT32;
3802 				sp--;
3803 			}
3804 			ACSVM_BREAK;
3805 
3806 		ACSVM_CASE(PCD_PushGlobalArray)
3807 			sp[-1] = GlobalArrays[READ_BYTE_OR_INT32].GetElemVal(sp[-1]);
3808 			INC_BYTE_OR_INT32;
3809 			ACSVM_BREAK;
3810 
3811 		ACSVM_CASE(PCD_AssignGlobalArray)
3812 			GlobalArrays[READ_BYTE_OR_INT32].SetElemVal(sp[-2], sp[-1]);
3813 			INC_BYTE_OR_INT32;
3814 			sp -= 2;
3815 			ACSVM_BREAK;
3816 
3817 		ACSVM_CASE(PCD_AddGlobalArray)
3818 			{
3819 				int ANum = READ_BYTE_OR_INT32;
3820 				GlobalArrays[ANum].SetElemVal(sp[-2],
3821 					GlobalArrays[ANum].GetElemVal(sp[-2]) + sp[-1]);
3822 				INC_BYTE_OR_INT32;
3823 				sp -= 2;
3824 			}
3825 			ACSVM_BREAK;
3826 
3827 		ACSVM_CASE(PCD_SubGlobalArray)
3828 			{
3829 				int ANum = READ_BYTE_OR_INT32;
3830 				GlobalArrays[ANum].SetElemVal(sp[-2],
3831 					GlobalArrays[ANum].GetElemVal(sp[-2]) - sp[-1]);
3832 				INC_BYTE_OR_INT32;
3833 				sp -= 2;
3834 			}
3835 			ACSVM_BREAK;
3836 
3837 		ACSVM_CASE(PCD_MulGlobalArray)
3838 			{
3839 				int ANum = READ_BYTE_OR_INT32;
3840 				GlobalArrays[ANum].SetElemVal(sp[-2],
3841 					GlobalArrays[ANum].GetElemVal(sp[-2]) * sp[-1]);
3842 				INC_BYTE_OR_INT32;
3843 				sp -= 2;
3844 			}
3845 			ACSVM_BREAK;
3846 
3847 		ACSVM_CASE(PCD_DivGlobalArray)
3848 			{
3849 				int ANum = READ_BYTE_OR_INT32;
3850 				GlobalArrays[ANum].SetElemVal(sp[-2],
3851 					GlobalArrays[ANum].GetElemVal(sp[-2]) / sp[-1]);
3852 				INC_BYTE_OR_INT32;
3853 				sp -= 2;
3854 			}
3855 			ACSVM_BREAK;
3856 
3857 		ACSVM_CASE(PCD_ModGlobalArray)
3858 			{
3859 				int ANum = READ_BYTE_OR_INT32;
3860 				GlobalArrays[ANum].SetElemVal(sp[-2],
3861 					GlobalArrays[ANum].GetElemVal(sp[-2]) % sp[-1]);
3862 				INC_BYTE_OR_INT32;
3863 				sp -= 2;
3864 			}
3865 			ACSVM_BREAK;
3866 
3867 		ACSVM_CASE(PCD_IncGlobalArray)
3868 			{
3869 				int ANum = READ_BYTE_OR_INT32;
3870 				GlobalArrays[ANum].SetElemVal(sp[-1],
3871 					GlobalArrays[ANum].GetElemVal(sp[-1]) + 1);
3872 				INC_BYTE_OR_INT32;
3873 				sp--;
3874 			}
3875 			ACSVM_BREAK;
3876 
3877 		ACSVM_CASE(PCD_DecGlobalArray)
3878 			{
3879 				int ANum = READ_BYTE_OR_INT32;
3880 				GlobalArrays[ANum].SetElemVal(sp[-1],
3881 					GlobalArrays[ANum].GetElemVal(sp[-1]) - 1);
3882 				INC_BYTE_OR_INT32;
3883 				sp--;
3884 			}
3885 			ACSVM_BREAK;
3886 
3887 		ACSVM_CASE(PCD_SetMarineWeapon)
3888 			Level->eventSetMarineWeapon(sp[-2], sp[-1], Activator);
3889 			sp -= 2;
3890 			ACSVM_BREAK;
3891 
3892 		ACSVM_CASE(PCD_SetActorProperty)
3893 			if (!sp[-3])
3894 			{
3895 				if (Activator)
3896 				{
3897 					Activator->eventSetActorProperty(sp[-2], sp[-1], GetStr(sp[-1]));
3898 				}
3899 			}
3900 			else
3901 			{
3902 				for (VEntity* Ent = Level->FindMobjFromTID(sp[-3], NULL);
3903 					Ent; Ent = Level->FindMobjFromTID(sp[-3], Ent))
3904 				{
3905 					Ent->eventSetActorProperty(sp[-2], sp[-1], GetStr(sp[-1]));
3906 				}
3907 			}
3908 			sp -= 3;
3909 			ACSVM_BREAK;
3910 
3911 		ACSVM_CASE(PCD_GetActorProperty)
3912 			{
3913 				VEntity* Ent = EntityFromTID(sp[-2], NULL);
3914 				if (!Ent)
3915 				{
3916 					sp[-2] = 0;
3917 				}
3918 				else
3919 				{
3920 					sp[-2] = Ent->eventGetActorProperty(sp[-1]);
3921 				}
3922 			}
3923 			sp -= 1;
3924 			ACSVM_BREAK;
3925 
3926 		ACSVM_CASE(PCD_PlayerNumber)
3927 			*sp = Activator && (Activator->EntityFlags & VEntity::EF_IsPlayer) ?
3928 				SV_GetPlayerNum(Activator->Player) : -1;
3929 			sp++;
3930 			ACSVM_BREAK;
3931 
3932 		ACSVM_CASE(PCD_ActivatorTID)
3933 			*sp = Activator ? Activator->TID : 0;
3934 			sp++;
3935 			ACSVM_BREAK;
3936 
3937 		ACSVM_CASE(PCD_SetMarineSprite)
3938 			Level->eventSetMarineSprite(sp[-2], GetName(sp[-1]), Activator);
3939 			sp -= 2;
3940 			ACSVM_BREAK;
3941 
3942 		ACSVM_CASE(PCD_GetScreenWidth)
3943 			*sp = 640;
3944 			sp++;
3945 			ACSVM_BREAK;
3946 
3947 		ACSVM_CASE(PCD_GetScreenHeight)
3948 			*sp = 480;
3949 			sp++;
3950 			ACSVM_BREAK;
3951 
3952 		ACSVM_CASE(PCD_ThingProjectile2)
3953 			Level->eventEV_ThingProjectile(sp[-7], sp[-6], sp[-5], sp[-4],
3954 				sp[-3], sp[-2], sp[-1], NAME_None, Activator);
3955 			sp -= 7;
3956 			ACSVM_BREAK;
3957 
3958 		ACSVM_CASE(PCD_StrLen)
3959 			sp[-1] = GetStr(sp[-1]).Utf8Length();
3960 			ACSVM_BREAK;
3961 
3962 		ACSVM_CASE(PCD_SetHudSize)
3963 			HudWidth = abs(sp[-3]);
3964 			HudHeight = abs(sp[-2]);
3965 			if (sp[-1])
3966 			{
3967 				HudHeight = -HudHeight;
3968 			}
3969 			sp -= 3;
3970 			ACSVM_BREAK;
3971 
3972 		ACSVM_CASE(PCD_GetCvar)
3973 			sp[-1] = VCvar::GetInt(*GetStr(sp[-1]));
3974 			ACSVM_BREAK;
3975 
3976 		ACSVM_CASE(PCD_CaseGotoSorted)
3977 			//	The count and jump table are 4-byte aligned.
3978 			if (ActiveObject->PtrToOffset(ip) & 3)
3979 			{
3980 				ip += 4 - (ActiveObject->PtrToOffset(ip) & 3);
3981 			}
3982 			{
3983 				int numcases = READ_INT32(ip);
3984 				int min = 0, max = numcases - 1;
3985 				while (min <= max)
3986 				{
3987 					int mid = (min + max) / 2;
3988 					int caseval = READ_INT32(ip + 4 + mid * 8);
3989 					if (caseval == sp[-1])
3990 					{
3991 						ip = ActiveObject->OffsetToPtr(READ_INT32(ip + 8 + mid * 8));
3992 						sp--;
3993 						ACSVM_BREAK;
3994 					}
3995 					else if (caseval < sp[-1])
3996 					{
3997 						min = mid + 1;
3998 					}
3999 					else
4000 					{
4001 						max = mid - 1;
4002 					}
4003 				}
4004 				if (min > max)
4005 				{
4006 					// The case was not found, so go to the next instruction.
4007 					ip += 4 + numcases * 8;
4008 				}
4009 			}
4010 			ACSVM_BREAK;
4011 
4012 		ACSVM_CASE(PCD_SetResultValue)
4013 			resultValue = sp[-1];
4014 			sp--;
4015 			ACSVM_BREAK;
4016 
4017 		ACSVM_CASE(PCD_GetLineRowOffset)
4018 			*sp = line ? (vint32)XLevel->Sides[line->sidenum[0]].MidRowOffset : 0;
4019 			sp++;
4020 			ACSVM_BREAK;
4021 
4022 		ACSVM_CASE(PCD_GetActorFloorZ)
4023 			{
4024 				VEntity* Ent = EntityFromTID(sp[-1], Activator);
4025 				sp[-1] = Ent ? vint32(Ent->FloorZ * 0x10000) : 0;
4026 			}
4027 			ACSVM_BREAK;
4028 
4029 		ACSVM_CASE(PCD_GetActorAngle)
4030 			{
4031 				VEntity* Ent = EntityFromTID(sp[-1], Activator);
4032 				sp[-1] = Ent ? vint32(Ent->Angles.yaw * 0x10000 / 360) &
4033 					0xffff : 0;
4034 			}
4035 			ACSVM_BREAK;
4036 
4037 		ACSVM_CASE(PCD_GetSectorFloorZ)
4038 			{
4039 				int SNum = FindSectorFromTag(sp[-3], -1);
4040 				sp[-3] = SNum >= 0 ? vint32(XLevel->Sectors[SNum].floor.
4041 					GetPointZ(sp[-2], sp[-1]) * 0x10000) : 0;
4042 				sp -= 2;
4043 			}
4044 			ACSVM_BREAK;
4045 
4046 		ACSVM_CASE(PCD_GetSectorCeilingZ)
4047 			{
4048 				int SNum = FindSectorFromTag(sp[-3], -1);
4049 				sp[-3] = SNum >= 0 ? vint32(XLevel->Sectors[SNum].ceiling.
4050 					GetPointZ(sp[-2], sp[-1]) * 0x10000) : 0;
4051 				sp -= 2;
4052 			}
4053 			ACSVM_BREAK;
4054 
4055 		ACSVM_CASE(PCD_LSpec5Result)
4056 			sp[-5] = Level->eventExecuteActionSpecial(READ_BYTE_OR_INT32,
4057 				sp[-5], sp[-4], sp[-3], sp[-2], sp[-1], line, side,
4058 				Activator);
4059 			INC_BYTE_OR_INT32;
4060 			sp -= 4;
4061 			ACSVM_BREAK;
4062 
4063 		ACSVM_CASE(PCD_GetSigilPieces)
4064 			*sp = Activator ? Activator->eventGetSigilPieces() : 0;
4065 			sp++;
4066 			ACSVM_BREAK;
4067 
4068 		ACSVM_CASE(PCD_GetLevelInfo)
4069 			switch (sp[-1])
4070 			{
4071 			case LEVELINFO_PAR_TIME:
4072 				sp[-1] = Level->ParTime;
4073 				break;
4074 			case LEVELINFO_SUCK_TIME:
4075 				sp[-1] = Level->SuckTime;
4076 				break;
4077 			case LEVELINFO_CLUSTERNUM:
4078 				sp[-1] = Level->Cluster;
4079 				break;
4080 			case LEVELINFO_LEVELNUM:
4081 				sp[-1] = Level->LevelNum;
4082 				break;
4083 			case LEVELINFO_TOTAL_SECRETS:
4084 				sp[-1] = Level->TotalSecret;
4085 				break;
4086 			case LEVELINFO_FOUND_SECRETS:
4087 				sp[-1] = Level->CurrentSecret;
4088 				break;
4089 			case LEVELINFO_TOTAL_ITEMS:
4090 				sp[-1] = Level->TotalItems;
4091 				break;
4092 			case LEVELINFO_FOUND_ITEMS:
4093 				sp[-1] = Level->CurrentItems;
4094 				break;
4095 			case LEVELINFO_TOTAL_MONSTERS:
4096 				sp[-1] = Level->TotalKills;
4097 				break;
4098 			case LEVELINFO_KILLED_MONSTERS:
4099 				sp[-1] = Level->CurrentKills;
4100 				break;
4101 			default:
4102 				sp[-1] = 0;
4103 				break;
4104 			}
4105 			ACSVM_BREAK;
4106 
4107 		ACSVM_CASE(PCD_ChangeSky)
4108 			Level->Sky1Texture = GTextureManager.NumForName(GetName8(sp[-2]),
4109 				TEXTYPE_Wall, true, false);
4110 			Level->Sky2Texture = GTextureManager.NumForName(GetName8(sp[-1]),
4111 				TEXTYPE_Wall, true, false);
4112 			sp -= 2;
4113 			ACSVM_BREAK;
4114 
4115 		ACSVM_CASE(PCD_PlayerInGame)
4116 			sp[-1] = (sp[-1] < 0 || sp[-1] >= MAXPLAYERS) ? false :
4117 				(Level->Game->Players[sp[-1]] && (Level->Game->Players[
4118 				sp[-1]]->PlayerFlags & VBasePlayer::PF_Spawned));
4119 			ACSVM_BREAK;
4120 
4121 		ACSVM_CASE(PCD_PlayerIsBot)
4122 			sp[-1] = (sp[-1] < 0 || sp[-1] >= MAXPLAYERS) ? false :
4123 				Level->Game->Players[sp[-1]] && Level->Game->Players[
4124 				sp[-1]]->PlayerFlags & VBasePlayer::PF_Spawned &&
4125 				Level->Game->Players[sp[-1]]->PlayerFlags &
4126 				VBasePlayer::PF_IsBot;
4127 			ACSVM_BREAK;
4128 
4129 		ACSVM_CASE(PCD_SetCameraToTexture)
4130 			XLevel->SetCameraToTexture(EntityFromTID(sp[-3], Activator),
4131 				GetName8(sp[-2]), sp[-1]);
4132 			sp -= 3;
4133 			ACSVM_BREAK;
4134 
4135 		ACSVM_CASE(PCD_EndLog)
4136 			PrintStr = PrintStr.EvalEscapeSequences();
4137 			GCon->Log(PrintStr);
4138 			ACSVM_BREAK;
4139 
4140 		ACSVM_CASE(PCD_GetAmmoCapacity)
4141 			if (Activator)
4142 			{
4143 				sp[-1] = Activator->eventGetAmmoCapacity(GetNameLowerCase(
4144 					sp[-1]));
4145 			}
4146 			else
4147 			{
4148 				sp[-1] = 0;
4149 			}
4150 			ACSVM_BREAK;
4151 
4152 		ACSVM_CASE(PCD_SetAmmoCapacity)
4153 			if (Activator)
4154 			{
4155 				Activator->eventSetAmmoCapacity(GetNameLowerCase(sp[-2]),
4156 					sp[-1]);
4157 			}
4158 			ACSVM_BREAK;
4159 
4160 		ACSVM_CASE(PCD_PrintMapCharArray)
4161 			{
4162 				int ANum = *ActiveObject->MapVars[sp[-1]];
4163 				int Idx = sp[-2];
4164 				for (int c = ActiveObject->GetArrayVal(ANum, Idx); c;
4165 					c = ActiveObject->GetArrayVal(ANum, Idx))
4166 				{
4167 					PrintStr += (char)c;
4168 					Idx++;
4169 				}
4170 				sp -= 2;
4171 			}
4172 			ACSVM_BREAK;
4173 
4174 		ACSVM_CASE(PCD_PrintWorldCharArray)
4175 			{
4176 				int ANum = *ActiveObject->MapVars[sp[-1]];
4177 				int Idx = sp[-2];
4178 				for (int c = WorldArrays[ANum].GetElemVal(Idx); c;
4179 					c = WorldArrays[ANum].GetElemVal(Idx))
4180 				{
4181 					PrintStr += (char)c;
4182 					Idx++;
4183 				}
4184 				sp -= 2;
4185 			}
4186 			ACSVM_BREAK;
4187 
4188 		ACSVM_CASE(PCD_PrintGlobalCharArray)
4189 			{
4190 				int ANum = *ActiveObject->MapVars[sp[-1]];
4191 				int Idx = sp[-2];
4192 				for (int c = GlobalArrays[ANum].GetElemVal(Idx); c;
4193 					c = GlobalArrays[ANum].GetElemVal(Idx))
4194 				{
4195 					PrintStr += (char)c;
4196 					Idx++;
4197 				}
4198 				sp -= 2;
4199 			}
4200 			ACSVM_BREAK;
4201 
4202 		ACSVM_CASE(PCD_SetActorAngle)
4203 			if (!sp[-2])
4204 			{
4205 				if (Activator)
4206 				{
4207 					Activator->Angles.yaw = (float)(sp[-1] & 0xffff) * 360.0 / (float)0x10000;
4208 				}
4209 			}
4210 			else
4211 			{
4212 				for (VEntity* Ent = Level->FindMobjFromTID(sp[-2], NULL);
4213 					Ent; Ent = Level->FindMobjFromTID(sp[-2], Ent))
4214 				{
4215 					Ent->Angles.yaw = (float)(sp[-1] & 0xffff) * 360.0 / (float)0x10000;
4216 				}
4217 			}
4218 			sp -= 2;
4219 			ACSVM_BREAK;
4220 
4221 		ACSVM_CASE(PCD_SpawnProjectile)
4222 			Level->eventEV_ThingProjectile(sp[-7], 0, sp[-5], sp[-4], sp[-3],
4223 				sp[-2], sp[-1], GetNameLowerCase(sp[-6]), Activator);
4224 			sp -= 7;
4225 			ACSVM_BREAK;
4226 
4227 		ACSVM_CASE(PCD_GetSectorLightLevel)
4228 			{
4229 				int SNum = FindSectorFromTag(sp[-1], -1);
4230 				sp[-1] = SNum >= 0 ? XLevel->Sectors[SNum].params.lightlevel : 0;
4231 			}
4232 			ACSVM_BREAK;
4233 
4234 		ACSVM_CASE(PCD_GetActorCeilingZ)
4235 			{
4236 				VEntity* Ent = EntityFromTID(sp[-1], Activator);
4237 				sp[-1] = Ent ? vint32(Ent->CeilingZ * 0x10000) : 0;
4238 			}
4239 			ACSVM_BREAK;
4240 
4241 		ACSVM_CASE(PCD_SetActorPosition)
4242 			{
4243 				VEntity* Ent = EntityFromTID(sp[-5], Activator);
4244 				sp[-5] = Ent ? Ent->eventMoveThing(TVec(
4245 					(float)sp[-4] / (float)0x10000,
4246 					(float)sp[-3] / (float)0x10000,
4247 					(float)sp[-2] / (float)0x10000), !!sp[-1]) : 0;
4248 				sp -= 4;
4249 			}
4250 			ACSVM_BREAK;
4251 
4252 		ACSVM_CASE(PCD_ClearActorInventory)
4253 			{
4254 				for (VEntity* mobj = Level->FindMobjFromTID(sp[-1], NULL);
4255 					mobj; mobj = Level->FindMobjFromTID(sp[-1], mobj))
4256 				{
4257 					mobj->eventClearInventory();
4258 				}
4259 			}
4260 			sp--;
4261 			ACSVM_BREAK;
4262 
4263 		ACSVM_CASE(PCD_GiveActorInventory)
4264 			{
4265 				for (VEntity* mobj = Level->FindMobjFromTID(sp[-3], NULL);
4266 					mobj; mobj = Level->FindMobjFromTID(sp[-3], mobj))
4267 				{
4268 					mobj->eventGiveInventory(GetNameLowerCase(sp[-2]), sp[-1]);
4269 				}
4270 			}
4271 			sp -= 3;
4272 			ACSVM_BREAK;
4273 
4274 		ACSVM_CASE(PCD_TakeActorInventory)
4275 			{
4276 				int searcher = -1;
4277 				for (VEntity* mobj = Level->FindMobjFromTID(sp[-3], NULL);
4278 					mobj; mobj = Level->FindMobjFromTID(sp[-3], mobj))
4279 				{
4280 					mobj->eventTakeInventory(GetNameLowerCase(sp[-2]), sp[-1]);
4281 				}
4282 			}
4283 			sp -= 3;
4284 			ACSVM_BREAK;
4285 
4286 		ACSVM_CASE(PCD_CheckActorInventory)
4287 			{
4288 				VEntity* Ent = EntityFromTID(sp[-2], NULL);
4289 				if (!Ent)
4290 				{
4291 					sp[-2] = 0;
4292 				}
4293 				else
4294 				{
4295 					sp[-2] = Ent->eventCheckInventory(GetNameLowerCase(
4296 						sp[-1]));
4297 				}
4298 			}
4299 			sp--;
4300 			ACSVM_BREAK;
4301 
4302 		ACSVM_CASE(PCD_ThingCountName)
4303 			sp[-2] = Level->eventThingCount(0, GetNameLowerCase(sp[-2]),
4304 				sp[-1], -1);
4305 			sp--;
4306 			ACSVM_BREAK;
4307 
4308 		ACSVM_CASE(PCD_SpawnSpotFacing)
4309 			sp[-3] = Level->eventAcsSpawnSpotFacing(GetNameLowerCase(sp[-3]),
4310 				sp[-2], sp[-1]);
4311 			sp -= 2;
4312 			ACSVM_BREAK;
4313 
4314 		ACSVM_CASE(PCD_PlayerClass)
4315 			if (sp[-1] < 0 || sp[-1] >= MAXPLAYERS || !Level->Game->Players[sp[-1]] ||
4316 				!(Level->Game->Players[sp[-1]]->PlayerFlags & VBasePlayer::PF_Spawned))
4317 			{
4318 				sp[-1] = -1;
4319 			}
4320 			else
4321 			{
4322 				sp[-1] = Level->Game->Players[sp[-1]]->PClass;
4323 			}
4324 			ACSVM_BREAK;
4325 
4326 		ACSVM_CASE(PCD_AndScriptVar)
4327 			locals[READ_BYTE_OR_INT32] &= sp[-1];
4328 			INC_BYTE_OR_INT32;
4329 			sp--;
4330 			ACSVM_BREAK;
4331 
4332 		ACSVM_CASE(PCD_AndMapVar)
4333 			*ActiveObject->MapVars[READ_BYTE_OR_INT32] &= sp[-1];
4334 			INC_BYTE_OR_INT32;
4335 			sp--;
4336 			ACSVM_BREAK;
4337 
4338 		ACSVM_CASE(PCD_AndWorldVar)
4339 			WorldVars[READ_BYTE_OR_INT32] &= sp[-1];
4340 			INC_BYTE_OR_INT32;
4341 			sp--;
4342 			ACSVM_BREAK;
4343 
4344 		ACSVM_CASE(PCD_AndGlobalVar)
4345 			GlobalVars[READ_BYTE_OR_INT32] &= sp[-1];
4346 			INC_BYTE_OR_INT32;
4347 			sp--;
4348 			ACSVM_BREAK;
4349 
4350 		ACSVM_CASE(PCD_AndMapArray)
4351 			{
4352 				int ANum = *ActiveObject->MapVars[READ_BYTE_OR_INT32];
4353 				ActiveObject->SetArrayVal(ANum, sp[-2],
4354 					ActiveObject->GetArrayVal(ANum, sp[-2]) & sp[-1]);
4355 				INC_BYTE_OR_INT32;
4356 				sp -= 2;
4357 			}
4358 			ACSVM_BREAK;
4359 
4360 		ACSVM_CASE(PCD_AndWorldArray)
4361 			{
4362 				int ANum = READ_BYTE_OR_INT32;
4363 				WorldArrays[ANum].SetElemVal(sp[-2],
4364 					WorldArrays[ANum].GetElemVal(sp[-2]) & sp[-1]);
4365 				INC_BYTE_OR_INT32;
4366 				sp -= 2;
4367 			}
4368 			ACSVM_BREAK;
4369 
4370 		ACSVM_CASE(PCD_AndGlobalArray)
4371 			{
4372 				int ANum = READ_BYTE_OR_INT32;
4373 				GlobalArrays[ANum].SetElemVal(sp[-2],
4374 					GlobalArrays[ANum].GetElemVal(sp[-2]) & sp[-1]);
4375 				INC_BYTE_OR_INT32;
4376 				sp -= 2;
4377 			}
4378 			ACSVM_BREAK;
4379 
4380 		ACSVM_CASE(PCD_EOrScriptVar)
4381 			locals[READ_BYTE_OR_INT32] ^= sp[-1];
4382 			INC_BYTE_OR_INT32;
4383 			sp--;
4384 			ACSVM_BREAK;
4385 
4386 		ACSVM_CASE(PCD_EOrMapVar)
4387 			*ActiveObject->MapVars[READ_BYTE_OR_INT32] ^= sp[-1];
4388 			INC_BYTE_OR_INT32;
4389 			sp--;
4390 			ACSVM_BREAK;
4391 
4392 		ACSVM_CASE(PCD_EOrWorldVar)
4393 			WorldVars[READ_BYTE_OR_INT32] ^= sp[-1];
4394 			INC_BYTE_OR_INT32;
4395 			sp--;
4396 			ACSVM_BREAK;
4397 
4398 		ACSVM_CASE(PCD_EOrGlobalVar)
4399 			GlobalVars[READ_BYTE_OR_INT32] ^= sp[-1];
4400 			INC_BYTE_OR_INT32;
4401 			sp--;
4402 			ACSVM_BREAK;
4403 
4404 		ACSVM_CASE(PCD_EOrMapArray)
4405 			{
4406 				int ANum = *ActiveObject->MapVars[READ_BYTE_OR_INT32];
4407 				ActiveObject->SetArrayVal(ANum, sp[-2],
4408 					ActiveObject->GetArrayVal(ANum, sp[-2]) ^ sp[-1]);
4409 				INC_BYTE_OR_INT32;
4410 				sp -= 2;
4411 			}
4412 			ACSVM_BREAK;
4413 
4414 		ACSVM_CASE(PCD_EOrWorldArray)
4415 			{
4416 				int ANum = READ_BYTE_OR_INT32;
4417 				WorldArrays[ANum].SetElemVal(sp[-2],
4418 					WorldArrays[ANum].GetElemVal(sp[-2]) ^ sp[-1]);
4419 				INC_BYTE_OR_INT32;
4420 				sp -= 2;
4421 			}
4422 			ACSVM_BREAK;
4423 
4424 		ACSVM_CASE(PCD_EOrGlobalArray)
4425 			{
4426 				int ANum = READ_BYTE_OR_INT32;
4427 				GlobalArrays[ANum].SetElemVal(sp[-2],
4428 					GlobalArrays[ANum].GetElemVal(sp[-2]) ^ sp[-1]);
4429 				INC_BYTE_OR_INT32;
4430 				sp -= 2;
4431 			}
4432 			ACSVM_BREAK;
4433 
4434 		ACSVM_CASE(PCD_OrScriptVar)
4435 			locals[READ_BYTE_OR_INT32] |= sp[-1];
4436 			INC_BYTE_OR_INT32;
4437 			sp--;
4438 			ACSVM_BREAK;
4439 
4440 		ACSVM_CASE(PCD_OrMapVar)
4441 			*ActiveObject->MapVars[READ_BYTE_OR_INT32] |= sp[-1];
4442 			INC_BYTE_OR_INT32;
4443 			sp--;
4444 			ACSVM_BREAK;
4445 
4446 		ACSVM_CASE(PCD_OrWorldVar)
4447 			WorldVars[READ_BYTE_OR_INT32] |= sp[-1];
4448 			INC_BYTE_OR_INT32;
4449 			sp--;
4450 			ACSVM_BREAK;
4451 
4452 		ACSVM_CASE(PCD_OrGlobalVar)
4453 			GlobalVars[READ_BYTE_OR_INT32] |= sp[-1];
4454 			INC_BYTE_OR_INT32;
4455 			sp--;
4456 			ACSVM_BREAK;
4457 
4458 		ACSVM_CASE(PCD_OrMapArray)
4459 			{
4460 				int ANum = *ActiveObject->MapVars[READ_BYTE_OR_INT32];
4461 				ActiveObject->SetArrayVal(ANum, sp[-2],
4462 					ActiveObject->GetArrayVal(ANum, sp[-2]) | sp[-1]);
4463 				INC_BYTE_OR_INT32;
4464 				sp -= 2;
4465 			}
4466 			ACSVM_BREAK;
4467 
4468 		ACSVM_CASE(PCD_OrWorldArray)
4469 			{
4470 				int ANum = READ_BYTE_OR_INT32;
4471 				WorldArrays[ANum].SetElemVal(sp[-2],
4472 					WorldArrays[ANum].GetElemVal(sp[-2]) | sp[-1]);
4473 				INC_BYTE_OR_INT32;
4474 				sp -= 2;
4475 			}
4476 			ACSVM_BREAK;
4477 
4478 		ACSVM_CASE(PCD_OrGlobalArray)
4479 			{
4480 				int ANum = READ_BYTE_OR_INT32;
4481 				GlobalArrays[ANum].SetElemVal(sp[-2],
4482 					GlobalArrays[ANum].GetElemVal(sp[-2]) | sp[-1]);
4483 				INC_BYTE_OR_INT32;
4484 				sp -= 2;
4485 			}
4486 			ACSVM_BREAK;
4487 
4488 		ACSVM_CASE(PCD_LSScriptVar)
4489 			locals[READ_BYTE_OR_INT32] <<= sp[-1];
4490 			INC_BYTE_OR_INT32;
4491 			sp--;
4492 			ACSVM_BREAK;
4493 
4494 		ACSVM_CASE(PCD_LSMapVar)
4495 			*ActiveObject->MapVars[READ_BYTE_OR_INT32] <<= sp[-1];
4496 			INC_BYTE_OR_INT32;
4497 			sp--;
4498 			ACSVM_BREAK;
4499 
4500 		ACSVM_CASE(PCD_LSWorldVar)
4501 			WorldVars[READ_BYTE_OR_INT32] <<= sp[-1];
4502 			INC_BYTE_OR_INT32;
4503 			sp--;
4504 			ACSVM_BREAK;
4505 
4506 		ACSVM_CASE(PCD_LSGlobalVar)
4507 			GlobalVars[READ_BYTE_OR_INT32] <<= sp[-1];
4508 			INC_BYTE_OR_INT32;
4509 			sp--;
4510 			ACSVM_BREAK;
4511 
4512 		ACSVM_CASE(PCD_LSMapArray)
4513 			{
4514 				int ANum = *ActiveObject->MapVars[READ_BYTE_OR_INT32];
4515 				ActiveObject->SetArrayVal(ANum, sp[-2],
4516 					ActiveObject->GetArrayVal(ANum, sp[-2]) << sp[-1]);
4517 				INC_BYTE_OR_INT32;
4518 				sp -= 2;
4519 			}
4520 			ACSVM_BREAK;
4521 
4522 		ACSVM_CASE(PCD_LSWorldArray)
4523 			{
4524 				int ANum = READ_BYTE_OR_INT32;
4525 				WorldArrays[ANum].SetElemVal(sp[-2],
4526 					WorldArrays[ANum].GetElemVal(sp[-2]) << sp[-1]);
4527 				INC_BYTE_OR_INT32;
4528 				sp -= 2;
4529 			}
4530 			ACSVM_BREAK;
4531 
4532 		ACSVM_CASE(PCD_LSGlobalArray)
4533 			{
4534 				int ANum = READ_BYTE_OR_INT32;
4535 				GlobalArrays[ANum].SetElemVal(sp[-2],
4536 					GlobalArrays[ANum].GetElemVal(sp[-2]) << sp[-1]);
4537 				INC_BYTE_OR_INT32;
4538 				sp -= 2;
4539 			}
4540 			ACSVM_BREAK;
4541 
4542 		ACSVM_CASE(PCD_RSScriptVar)
4543 			locals[READ_BYTE_OR_INT32] >>= sp[-1];
4544 			INC_BYTE_OR_INT32;
4545 			sp--;
4546 			ACSVM_BREAK;
4547 
4548 		ACSVM_CASE(PCD_RSMapVar)
4549 			*ActiveObject->MapVars[READ_BYTE_OR_INT32] >>= sp[-1];
4550 			INC_BYTE_OR_INT32;
4551 			sp--;
4552 			ACSVM_BREAK;
4553 
4554 		ACSVM_CASE(PCD_RSWorldVar)
4555 			WorldVars[READ_BYTE_OR_INT32] >>= sp[-1];
4556 			INC_BYTE_OR_INT32;
4557 			sp--;
4558 			ACSVM_BREAK;
4559 
4560 		ACSVM_CASE(PCD_RSGlobalVar)
4561 			GlobalVars[READ_BYTE_OR_INT32] >>= sp[-1];
4562 			INC_BYTE_OR_INT32;
4563 			sp--;
4564 			ACSVM_BREAK;
4565 
4566 		ACSVM_CASE(PCD_RSMapArray)
4567 			{
4568 				int ANum = *ActiveObject->MapVars[READ_BYTE_OR_INT32];
4569 				ActiveObject->SetArrayVal(ANum, sp[-2],
4570 					ActiveObject->GetArrayVal(ANum, sp[-2]) >> sp[-1]);
4571 				INC_BYTE_OR_INT32;
4572 				sp -= 2;
4573 			}
4574 			ACSVM_BREAK;
4575 
4576 		ACSVM_CASE(PCD_RSWorldArray)
4577 			{
4578 				int ANum = READ_BYTE_OR_INT32;
4579 				WorldArrays[ANum].SetElemVal(sp[-2],
4580 					WorldArrays[ANum].GetElemVal(sp[-2]) >> sp[-1]);
4581 				INC_BYTE_OR_INT32;
4582 				sp -= 2;
4583 			}
4584 			ACSVM_BREAK;
4585 
4586 		ACSVM_CASE(PCD_RSGlobalArray)
4587 			{
4588 				int ANum = READ_BYTE_OR_INT32;
4589 				GlobalArrays[ANum].SetElemVal(sp[-2],
4590 					GlobalArrays[ANum].GetElemVal(sp[-2]) >> sp[-1]);
4591 				INC_BYTE_OR_INT32;
4592 				sp -= 2;
4593 			}
4594 			ACSVM_BREAK;
4595 
4596 		ACSVM_CASE(PCD_GetPlayerInfo)
4597 			STUB(PCD_GetPlayerInfo)
4598 			//sp[-2] - Player num
4599 			//sp[-1] - Info type
4600 			//Pushes result.
4601 			sp[-2] = -1;
4602 			sp--;
4603 			ACSVM_BREAK;
4604 
4605 		ACSVM_CASE(PCD_ChangeLevel)
4606 			STUB(PCD_ChangeLevel)
4607 			//sp[-4] - Level name
4608 			//sp[-3] - Position
4609 			//sp[-2] - Skill
4610 			//sp[-1] - Flags
4611 			sp -= 4;
4612 			ACSVM_BREAK;
4613 
4614 		ACSVM_CASE(PCD_SectorDamage)
4615 			Level->eventSectorDamage(sp[-5], sp[-4], GetName(sp[-3]),
4616 				GetNameLowerCase(sp[-2]), sp[-1]);
4617 			sp -= 5;
4618 			ACSVM_BREAK;
4619 
4620 		ACSVM_CASE(PCD_ReplaceTextures)
4621 			if (~sp[-1] & (NOT_TOP | NOT_MIDDLE | NOT_BOTTOM))
4622 			{
4623 				int FromTex = GTextureManager.NumForName(GetName8(sp[-3]),
4624 					TEXTYPE_Wall, true);
4625 				int ToTex = GTextureManager.NumForName(GetName8(sp[-2]),
4626 					TEXTYPE_Wall, true);
4627 				for (int i = 0; i < XLevel->NumSides; i++)
4628 				{
4629 					if (!(sp[-1] & NOT_TOP) &&
4630 						XLevel->Sides[i].TopTexture == FromTex)
4631 					{
4632 						XLevel->Sides[i].TopTexture = ToTex;
4633 					}
4634 					if (!(sp[-1] & NOT_MIDDLE) &&
4635 						XLevel->Sides[i].MidTexture == FromTex)
4636 					{
4637 						XLevel->Sides[i].MidTexture = ToTex;
4638 					}
4639 					if (!(sp[-1] & NOT_BOTTOM) &&
4640 						XLevel->Sides[i].BottomTexture == FromTex)
4641 					{
4642 						XLevel->Sides[i].BottomTexture = ToTex;
4643 					}
4644 				}
4645 			}
4646 			if (~sp[-1] & (NOT_FLOOR | NOT_CEILING))
4647 			{
4648 				int FromTex = GTextureManager.NumForName(GetName8(sp[-3]),
4649 					TEXTYPE_Flat, true);
4650 				int ToTex = GTextureManager.NumForName(GetName8(sp[-2]),
4651 					TEXTYPE_Flat, true);
4652 				for (int i = 0; i < XLevel->NumSectors; i++)
4653 				{
4654 					if (!(sp[-1] & NOT_FLOOR) &&
4655 						XLevel->Sectors[i].floor.pic == FromTex)
4656 					{
4657 						XLevel->Sectors[i].floor.pic = ToTex;
4658 					}
4659 					if (!(sp[-1] & NOT_CEILING) &&
4660 						XLevel->Sectors[i].ceiling.pic == FromTex)
4661 					{
4662 						XLevel->Sectors[i].ceiling.pic = ToTex;
4663 					}
4664 				}
4665 			}
4666 			sp -= 3;
4667 			ACSVM_BREAK;
4668 
4669 		ACSVM_CASE(PCD_NegateBinary)
4670 			sp[-1] = ~sp[-1];
4671 			ACSVM_BREAK;
4672 
4673 		ACSVM_CASE(PCD_GetActorPitch)
4674 			{
4675 				VEntity* Ent = EntityFromTID(sp[-1], Activator);
4676 				sp[-1] = Ent ? vint32(Ent->Angles.pitch * 0x10000 / 360) &
4677 					0xffff : 0;
4678 			}
4679 			ACSVM_BREAK;
4680 
4681 		ACSVM_CASE(PCD_SetActorPitch)
4682 			if (!sp[-2])
4683 			{
4684 				if (Activator)
4685 				{
4686 					Activator->Angles.pitch = AngleMod180(
4687 						(float)(sp[-1] & 0xffff) * 360.0 / (float)0x10000);
4688 				}
4689 			}
4690 			else
4691 			{
4692 				for (VEntity* Ent = Level->FindMobjFromTID(sp[-2], NULL);
4693 					Ent; Ent = Level->FindMobjFromTID(sp[-2], Ent))
4694 				{
4695 					Ent->Angles.pitch = AngleMod180(
4696 						(float)(sp[-1] & 0xffff) * 360.0 / (float)0x10000);
4697 				}
4698 			}
4699 			sp -= 2;
4700 			ACSVM_BREAK;
4701 
4702 		ACSVM_CASE(PCD_PrintBind)
4703 			STUB(PCD_PrintBind)
4704 			//sp[-1] - command (string)
4705 			sp--;
4706 			ACSVM_BREAK;
4707 
4708 		ACSVM_CASE(PCD_SetActorState)
4709 			{
4710 				TArray<VName> Names;
4711 				VMemberBase::StaticSplitStateLabel(GetStr(sp[-2]), Names);
4712 				if (!sp[-3])
4713 				{
4714 					VStateLabel* Lbl = !Activator ? NULL :
4715 						Activator->GetClass()->FindStateLabel(Names, !!sp[-1]);
4716 					if (Lbl && Lbl->State)
4717 					{
4718 						Activator->SetState(Lbl->State);
4719 						sp[-3] = 1;
4720 					}
4721 					else
4722 					{
4723 						sp[-3] = 0;
4724 					}
4725 				}
4726 				else
4727 				{
4728 					int Count = 0;
4729 					for (VEntity* Ent = Level->FindMobjFromTID(sp[-3], NULL);
4730 						Ent; Ent = Level->FindMobjFromTID(sp[-3], Ent))
4731 					{
4732 						VStateLabel* Lbl = Ent->GetClass()->FindStateLabel(
4733 							Names, !!sp[-1]);
4734 						if (Lbl && Lbl->State)
4735 						{
4736 							Ent->SetState(Lbl->State);
4737 							Count++;
4738 						}
4739 					}
4740 					sp[-3] = Count;
4741 				}
4742 				sp -= 2;
4743 			}
4744 			ACSVM_BREAK;
4745 
4746 		ACSVM_CASE(PCD_ThingDamage2)
4747 			sp[-3] = Level->eventDoThingDamage(sp[-3], sp[-2],
4748 				GetName(sp[-1]), Activator);
4749 			sp -= 2;
4750 			ACSVM_BREAK;
4751 
4752 		ACSVM_CASE(PCD_UseInventory)
4753 			if (Activator)
4754 			{
4755 				sp[-1] = Activator->eventUseInventoryName(GetNameLowerCase(
4756 					sp[-1]));
4757 			}
4758 			else
4759 			{
4760 				sp[-1] = 0;
4761 				for (int i = 0; i < MAXPLAYERS; i++)
4762 				{
4763 					if (Level->Game->Players[i] &&
4764 						Level->Game->Players[i]->PlayerFlags & VBasePlayer::PF_Spawned)
4765 					{
4766 						sp[-1] += Level->Game->Players[i]->MO->eventUseInventoryName(
4767 							GetNameLowerCase(sp[-1]));
4768 					}
4769 				}
4770 			}
4771 			ACSVM_BREAK;
4772 
4773 		ACSVM_CASE(PCD_UseActorInventory)
4774 			if (sp[-2])
4775 			{
4776 				int Ret = 0;
4777 				for (VEntity* Ent = Level->FindMobjFromTID(sp[-2], NULL);
4778 					Ent; Ent = Level->FindMobjFromTID(sp[-2], Ent))
4779 				{
4780 					Ret += Ent->eventUseInventoryName(GetNameLowerCase(sp[-1]));
4781 				}
4782 				sp[-2] = Ret;
4783 			}
4784 			else
4785 			{
4786 				sp[-1] = 0;
4787 				for (int i = 0; i < MAXPLAYERS; i++)
4788 				{
4789 					if (Level->Game->Players[i] &&
4790 						Level->Game->Players[i]->PlayerFlags & VBasePlayer::PF_Spawned)
4791 					{
4792 						sp[-1] += Level->Game->Players[i]->MO->eventUseInventoryName(
4793 							GetNameLowerCase(sp[-1]));
4794 					}
4795 				}
4796 			}
4797 			sp--;
4798 			ACSVM_BREAK;
4799 
4800 		ACSVM_CASE(PCD_CheckActorCeilingTexture)
4801 			{
4802 				VEntity* Ent = EntityFromTID(sp[-2], Activator);
4803 				if (Ent)
4804 				{
4805 					int Tex = GTextureManager.CheckNumForName(GetName8(sp[-1]),
4806 						TEXTYPE_Wall, true);
4807 					sp[-2] = Ent->Sector->ceiling.pic == Tex;
4808 				}
4809 				else
4810 				{
4811 					sp[-2] = 0;
4812 				}
4813 			}
4814 			sp--;
4815 			ACSVM_BREAK;
4816 
4817 		ACSVM_CASE(PCD_CheckActorFloorTexture)
4818 			{
4819 				VEntity* Ent = EntityFromTID(sp[-2], Activator);
4820 				if (Ent)
4821 				{
4822 					int Tex = GTextureManager.CheckNumForName(GetName8(sp[-1]),
4823 						TEXTYPE_Wall, true);
4824 					sp[-2] = Ent->Sector->floor.pic == Tex;
4825 				}
4826 				else
4827 				{
4828 					sp[-2] = 0;
4829 				}
4830 			}
4831 			sp--;
4832 			ACSVM_BREAK;
4833 
4834 		ACSVM_CASE(PCD_GetActorLightLevel)
4835 			{
4836 				VEntity* Ent = EntityFromTID(sp[-1], Activator);
4837 				if (Ent)
4838 				{
4839 					sp[-1] = Ent->Sector->params.lightlevel;
4840 				}
4841 				else
4842 				{
4843 					sp[-1] = 0;
4844 				}
4845 			}
4846 			ACSVM_BREAK;
4847 
4848 		ACSVM_CASE(PCD_SetMugShotState)
4849 			STUB(PCD_SetMugShotState)
4850 			//sp[-1] - state (string)
4851 			sp--;
4852 			ACSVM_BREAK;
4853 
4854 		ACSVM_CASE(PCD_ThingCountSector)
4855 			sp[-3] = Level->eventThingCount(sp[-3], NAME_None, sp[-2], sp[-1]);
4856 			sp -= 2;
4857 			ACSVM_BREAK;
4858 
4859 		ACSVM_CASE(PCD_ThingCountNameSector)
4860 			sp[-3] = Level->eventThingCount(0, GetNameLowerCase(sp[-3]),
4861 				sp[-2], sp[-1]);
4862 			sp -= 2;
4863 			ACSVM_BREAK;
4864 
4865 		ACSVM_CASE(PCD_CheckPlayerCamera)
4866 			if (sp[-1] < 0 || sp[-1] >= MAXPLAYERS ||
4867 				!Level->Game->Players[sp[-1]] ||
4868 				!(Level->Game->Players[sp[-1]]->PlayerFlags & VBasePlayer::PF_Spawned) ||
4869 				!Level->Game->Players[sp[-1]]->Camera)
4870 			{
4871 				sp[-1] = -1;
4872 			}
4873 			else
4874 			{
4875 				sp[-1] = Level->Game->Players[sp[-1]]->Camera->TID;
4876 			}
4877 			ACSVM_BREAK;
4878 
4879 		ACSVM_CASE(PCD_MorphActor)
4880 			if (sp[-7])
4881 			{
4882 				int searcher = -1;
4883 				int Res = 0;
4884 				for (VEntity* Ent = Level->FindMobjFromTID(sp[-7], NULL);
4885 					Ent; Ent = Level->FindMobjFromTID(sp[-7], Ent))
4886 				{
4887 					Res += Ent->eventMorphActor(GetNameLowerCase(sp[-6]),
4888 						GetNameLowerCase(sp[-5]), sp[-4] / 35.0, sp[-3],
4889 						GetNameLowerCase(sp[-2]), GetNameLowerCase(sp[-1]));
4890 				}
4891 				sp[-7] = Res;
4892 			}
4893 			else if (Activator)
4894 			{
4895 				sp[-7] = Activator->eventMorphActor(GetNameLowerCase(sp[-6]),
4896 						GetNameLowerCase(sp[-5]), sp[-4] / 35.0, sp[-3],
4897 						GetNameLowerCase(sp[-2]), GetNameLowerCase(sp[-1]));
4898 			}
4899 			else
4900 			{
4901 				sp[-7] = 0;
4902 			}
4903 			sp -= 6;
4904 			ACSVM_BREAK;
4905 
4906 		ACSVM_CASE(PCD_UnmorphActor)
4907 			if (sp[-2])
4908 			{
4909 				int searcher = -1;
4910 				int Res = 0;
4911 				for (VEntity* Ent = Level->FindMobjFromTID(sp[-2], NULL);
4912 					Ent; Ent = Level->FindMobjFromTID(sp[-2], Ent))
4913 				{
4914 					Res += Ent->eventUnmorphActor(Activator, sp[-1]);
4915 				}
4916 				sp[-2] = Res;
4917 			}
4918 			else if (Activator)
4919 			{
4920 				sp[-2] = Activator->eventUnmorphActor(Activator, sp[-1]);
4921 			}
4922 			else
4923 			{
4924 				sp[-2] = 0;
4925 			}
4926 			sp--;
4927 			ACSVM_BREAK;
4928 
4929 		ACSVM_CASE(PCD_GetPlayerInput)
4930 			if (sp[-2] < 0)
4931 			{
4932 				if (Activator && Activator->Player)
4933 				{
4934 					sp[-2] = Activator->Player->AcsGetInput(sp[-1]);
4935 				}
4936 				else
4937 				{
4938 					sp[-2] = 0;
4939 				}
4940 			}
4941 			else if (sp[-2] < MAXPLAYERS && Level->Game->Players[sp[-2]] &&
4942 				(Level->Game->Players[sp[-2]]->PlayerFlags & VBasePlayer::PF_Spawned))
4943 			{
4944 				sp[-2] = Level->Game->Players[sp[-2]]->AcsGetInput(sp[-1]);
4945 			}
4946 			else
4947 			{
4948 				sp[-2] = 0;
4949 			}
4950 			sp--;
4951 			ACSVM_BREAK;
4952 
4953 		ACSVM_CASE(PCD_ClassifyActor)
4954 			if (sp[-1])
4955 			{
4956 				VEntity* Ent = EntityFromTID(sp[-1], NULL);
4957 				if (Ent)
4958 				{
4959 					sp[-1] = Ent->eventClassifyActor();
4960 				}
4961 				else
4962 				{
4963 					//	None
4964 					sp[-1] = 0;
4965 				}
4966 			}
4967 			else
4968 			{
4969 				if (Activator)
4970 				{
4971 					sp[-1] = Activator->eventClassifyActor();
4972 				}
4973 				else
4974 				{
4975 					//	World
4976 					sp[-1] = 1;
4977 				}
4978 			}
4979 			ACSVM_BREAK;
4980 
4981 		ACSVM_CASE(PCD_PrintBinary)
4982 			{
4983 				vuint32 Val = sp[-1];
4984 				do
4985 				{
4986 					PrintStr += Val & 1 ? "1" : "0";
4987 					Val >>= 1;
4988 				}
4989 				while (Val);
4990 			}
4991 			sp--;
4992 			ACSVM_BREAK;
4993 
4994 		ACSVM_CASE(PCD_PrintHex)
4995 			PrintStr += va("%X", sp[-1]);
4996 			sp--;
4997 			ACSVM_BREAK;
4998 
4999 
5000 		//	These p-codes are not supported. They will terminate script.
5001 		ACSVM_CASE(PCD_PlayerBlueSkull)
5002 		ACSVM_CASE(PCD_PlayerRedSkull)
5003 		ACSVM_CASE(PCD_PlayerYellowSkull)
5004 		ACSVM_CASE(PCD_PlayerMasterSkull)
5005 		ACSVM_CASE(PCD_PlayerBlueCard)
5006 		ACSVM_CASE(PCD_PlayerRedCard)
5007 		ACSVM_CASE(PCD_PlayerYellowCard)
5008 		ACSVM_CASE(PCD_PlayerMasterCard)
5009 		ACSVM_CASE(PCD_PlayerBlackSkull)
5010 		ACSVM_CASE(PCD_PlayerSilverSkull)
5011 		ACSVM_CASE(PCD_PlayerGoldSkull)
5012 		ACSVM_CASE(PCD_PlayerBlackCard)
5013 		ACSVM_CASE(PCD_PlayerSilverCard)
5014 		ACSVM_CASE(PCD_PlayerOnTeam)
5015 		ACSVM_CASE(PCD_PlayerTeam)
5016 		ACSVM_CASE(PCD_PlayerExpert)
5017 		ACSVM_CASE(PCD_BlueTeamCount)
5018 		ACSVM_CASE(PCD_RedTeamCount)
5019 		ACSVM_CASE(PCD_BlueTeamScore)
5020 		ACSVM_CASE(PCD_RedTeamScore)
5021 		ACSVM_CASE(PCD_IsOneFlagCTF)
5022 		ACSVM_CASE(PCD_LSpec6)
5023 		ACSVM_CASE(PCD_LSpec6Direct)
5024 		ACSVM_CASE(PCD_Team2FragPoints)
5025 		ACSVM_CASE(PCD_ConsoleCommand)
5026 		ACSVM_CASE(PCD_SetStyle)
5027 		ACSVM_CASE(PCD_SetStyleDirect)
5028 		ACSVM_CASE(PCD_WriteToIni)
5029 		ACSVM_CASE(PCD_GetFromIni)
5030 		ACSVM_CASE(PCD_GrabInput)
5031 		ACSVM_CASE(PCD_SetMousePointer)
5032 		ACSVM_CASE(PCD_MoveMousePointer)
5033 			GCon->Logf(NAME_Dev, "Unsupported ACS p-code %d", cmd);
5034 			action = SCRIPT_Terminate;
5035 			ACSVM_BREAK_STOP;
5036 
5037 		ACSVM_DEFAULT
5038 			Host_Error("Illegal ACS opcode %d", cmd);
5039 		}
5040 	} while  (action == SCRIPT_Continue);
5041 #if USE_COMPUTED_GOTO
5042 LblFuncStop:
5043 #endif
5044 	InstructionPointer = ip;
5045 	if (action == SCRIPT_Terminate)
5046 	{
5047 		if (info->RunningScript == this)
5048 		{
5049 			info->RunningScript = NULL;
5050 		}
5051 		DestroyThinker();
5052 	}
5053 	return resultValue;
5054 	unguard;
5055 }
5056 
5057 //==========================================================================
5058 //
5059 //  FindSectorFromTag
5060 //
5061 //	RETURN NEXT SECTOR # THAT LINE TAG REFERS TO
5062 //
5063 //==========================================================================
5064 
FindSectorFromTag(int tag,int start)5065 int VAcs::FindSectorFromTag(int tag, int start)
5066 {
5067 	guard(VAcs::FindSectorFromTag);
5068 	for (int i = start + 1; i < XLevel->NumSectors; i++)
5069 		if (XLevel->Sectors[i].tag == tag)
5070 			return i;
5071 	return -1;
5072 	unguard;
5073 }
5074 
5075 //==========================================================================
5076 //
5077 //	VAcsGlobal::VAcsGlobal
5078 //
5079 //==========================================================================
5080 
VAcsGlobal()5081 VAcsGlobal::VAcsGlobal()
5082 {
5083 	memset(WorldVars, 0, sizeof(WorldVars));
5084 	memset(GlobalVars, 0, sizeof(GlobalVars));
5085 }
5086 
5087 //==========================================================================
5088 //
5089 //	operator <<
5090 //
5091 //==========================================================================
5092 
operator <<(VStream & Strm,VAcsStore & Store)5093 VStream& operator << (VStream& Strm, VAcsStore& Store)
5094 {
5095 	return Strm << Store.Map
5096 			<< Store.Type
5097 			<< Store.PlayerNum
5098 			<< STRM_INDEX(Store.Script)
5099 			<< STRM_INDEX(Store.Args[0])
5100 			<< STRM_INDEX(Store.Args[1])
5101 			<< STRM_INDEX(Store.Args[2]);
5102 }
5103 
5104 //==========================================================================
5105 //
5106 //	VAcsGlobal::Serialise
5107 //
5108 //==========================================================================
5109 
Serialise(VStream & Strm)5110 void VAcsGlobal::Serialise(VStream& Strm)
5111 {
5112 	guard(VAcsGlobal::Serialise);
5113 	for (int i = 0; i < MAX_ACS_WORLD_VARS; i++)
5114 	{
5115 		Strm << STRM_INDEX(WorldVars[i]);
5116 	}
5117 	for (int i = 0; i < MAX_ACS_GLOBAL_VARS; i++)
5118 	{
5119 		Strm << STRM_INDEX(GlobalVars[i]);
5120 	}
5121 	for (int i = 0; i < MAX_ACS_WORLD_VARS; i++)
5122 	{
5123 		WorldArrays[i].Serialise(Strm);
5124 	}
5125 	for (int i = 0; i < MAX_ACS_GLOBAL_VARS; i++)
5126 	{
5127 		GlobalArrays[i].Serialise(Strm);
5128 	}
5129 	Strm << Store;
5130 	unguard;
5131 }
5132 
5133 //==========================================================================
5134 //
5135 //	Script ACS methods
5136 //
5137 //==========================================================================
5138 
IMPLEMENT_FUNCTION(VLevel,StartACS)5139 IMPLEMENT_FUNCTION(VLevel, StartACS)
5140 {
5141 	P_GET_BOOL(WantResult);
5142 	P_GET_BOOL(Always);
5143 	P_GET_INT(side);
5144 	P_GET_PTR(line_t, line);
5145 	P_GET_REF(VEntity, activator);
5146 	P_GET_INT(arg3);
5147 	P_GET_INT(arg2);
5148 	P_GET_INT(arg1);
5149 	P_GET_INT(map);
5150 	P_GET_INT(num);
5151 	P_GET_SELF;
5152 	RET_BOOL(Self->Acs->Start(num, map, arg1, arg2, arg3, activator, line,
5153 		side, Always, WantResult));
5154 }
5155 
IMPLEMENT_FUNCTION(VLevel,SuspendACS)5156 IMPLEMENT_FUNCTION(VLevel, SuspendACS)
5157 {
5158 	P_GET_INT(map);
5159 	P_GET_INT(number);
5160 	P_GET_SELF;
5161 	RET_BOOL(Self->Acs->Suspend(number, map));
5162 }
5163 
IMPLEMENT_FUNCTION(VLevel,TerminateACS)5164 IMPLEMENT_FUNCTION(VLevel, TerminateACS)
5165 {
5166 	P_GET_INT(map);
5167 	P_GET_INT(number);
5168 	P_GET_SELF;
5169 	RET_BOOL(Self->Acs->Terminate(number, map));
5170 }
5171 
IMPLEMENT_FUNCTION(VLevel,StartTypedACScripts)5172 IMPLEMENT_FUNCTION(VLevel, StartTypedACScripts)
5173 {
5174 	P_GET_BOOL(RunNow);
5175 	P_GET_BOOL(Always);
5176 	P_GET_REF(VEntity, Activator);
5177 	P_GET_INT(Arg3);
5178 	P_GET_INT(Arg2);
5179 	P_GET_INT(Arg1);
5180 	P_GET_INT(Type);
5181 	P_GET_SELF;
5182 	Self->Acs->StartTypedACScripts(Type, Arg1, Arg2, Arg3, Activator, Always,
5183 		RunNow);
5184 }
5185 
5186 //==========================================================================
5187 //
5188 //	Puke
5189 //
5190 //==========================================================================
5191 
COMMAND(Puke)5192 COMMAND(Puke)
5193 {
5194 	guard(COMMAND Puke);
5195 	if (Source == SRC_Command)
5196 	{
5197 		ForwardToServer();
5198 		return;
5199 	}
5200 
5201 	if (Args.Num() < 2)
5202 	{
5203 		return;
5204 	}
5205 	int Script = atoi(*Args[1]);
5206 	if (Script == 0)
5207 	{
5208 		//	Script 0 is special
5209 		return;
5210 	}
5211 	int ScArgs[3];
5212 	for (int i = 0; i < 3; i++)
5213 	{
5214 		if (Args.Num() >= i + 3)
5215 		{
5216 			ScArgs[i] = atoi(*Args[i + 2]);
5217 		}
5218 		else
5219 		{
5220 			ScArgs[i] = 0;
5221 		}
5222 	}
5223 
5224 	Player->Level->XLevel->Acs->Start(abs(Script), 0, ScArgs[0], ScArgs[1],
5225 		ScArgs[2], GGameInfo->Players[0]->MO, NULL, 0, Script < 0, false, true);
5226 	unguard;
5227 }
5228