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