1 //
2 // Copyright(C) 1993-1996 Id Software, Inc.
3 // Copyright(C) 1993-2008 Raven Software
4 // Copyright(C) 2005-2014 Simon Howard
5 //
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 
17 
18 // HEADER FILES ------------------------------------------------------------
19 
20 #include "h2def.h"
21 #include "m_misc.h"
22 #include "m_random.h"
23 #include "s_sound.h"
24 #include "i_swap.h"
25 #include "i_system.h"
26 #include "p_local.h"
27 
28 // MACROS ------------------------------------------------------------------
29 
30 #define MAX_SCRIPT_ARGS 3
31 #define SCRIPT_CONTINUE 0
32 #define SCRIPT_STOP 1
33 #define SCRIPT_TERMINATE 2
34 #define OPEN_SCRIPTS_BASE 1000
35 #define PRINT_BUFFER_SIZE 256
36 #define GAME_SINGLE_PLAYER 0
37 #define GAME_NET_COOPERATIVE 1
38 #define GAME_NET_DEATHMATCH 2
39 #define TEXTURE_TOP 0
40 #define TEXTURE_MIDDLE 1
41 #define TEXTURE_BOTTOM 2
42 
43 // TYPES -------------------------------------------------------------------
44 
45 typedef PACKED_STRUCT (
46 {
47     int marker;
48     int infoOffset;
49     int code;
50 }) acsHeader_t;
51 
52 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
53 
54 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
55 
56 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
57 
58 static void StartOpenACS(int number, int infoIndex, int offset);
59 static void ScriptFinished(int number);
60 static boolean TagBusy(int tag);
61 static boolean AddToACSStore(int map, int number, byte * args);
62 static int GetACSIndex(int number);
63 static void Push(int value);
64 static int Pop(void);
65 static int Top(void);
66 static void Drop(void);
67 
68 static int CmdNOP(void);
69 static int CmdTerminate(void);
70 static int CmdSuspend(void);
71 static int CmdPushNumber(void);
72 static int CmdLSpec1(void);
73 static int CmdLSpec2(void);
74 static int CmdLSpec3(void);
75 static int CmdLSpec4(void);
76 static int CmdLSpec5(void);
77 static int CmdLSpec1Direct(void);
78 static int CmdLSpec2Direct(void);
79 static int CmdLSpec3Direct(void);
80 static int CmdLSpec4Direct(void);
81 static int CmdLSpec5Direct(void);
82 static int CmdAdd(void);
83 static int CmdSubtract(void);
84 static int CmdMultiply(void);
85 static int CmdDivide(void);
86 static int CmdModulus(void);
87 static int CmdEQ(void);
88 static int CmdNE(void);
89 static int CmdLT(void);
90 static int CmdGT(void);
91 static int CmdLE(void);
92 static int CmdGE(void);
93 static int CmdAssignScriptVar(void);
94 static int CmdAssignMapVar(void);
95 static int CmdAssignWorldVar(void);
96 static int CmdPushScriptVar(void);
97 static int CmdPushMapVar(void);
98 static int CmdPushWorldVar(void);
99 static int CmdAddScriptVar(void);
100 static int CmdAddMapVar(void);
101 static int CmdAddWorldVar(void);
102 static int CmdSubScriptVar(void);
103 static int CmdSubMapVar(void);
104 static int CmdSubWorldVar(void);
105 static int CmdMulScriptVar(void);
106 static int CmdMulMapVar(void);
107 static int CmdMulWorldVar(void);
108 static int CmdDivScriptVar(void);
109 static int CmdDivMapVar(void);
110 static int CmdDivWorldVar(void);
111 static int CmdModScriptVar(void);
112 static int CmdModMapVar(void);
113 static int CmdModWorldVar(void);
114 static int CmdIncScriptVar(void);
115 static int CmdIncMapVar(void);
116 static int CmdIncWorldVar(void);
117 static int CmdDecScriptVar(void);
118 static int CmdDecMapVar(void);
119 static int CmdDecWorldVar(void);
120 static int CmdGoto(void);
121 static int CmdIfGoto(void);
122 static int CmdDrop(void);
123 static int CmdDelay(void);
124 static int CmdDelayDirect(void);
125 static int CmdRandom(void);
126 static int CmdRandomDirect(void);
127 static int CmdThingCount(void);
128 static int CmdThingCountDirect(void);
129 static int CmdTagWait(void);
130 static int CmdTagWaitDirect(void);
131 static int CmdPolyWait(void);
132 static int CmdPolyWaitDirect(void);
133 static int CmdChangeFloor(void);
134 static int CmdChangeFloorDirect(void);
135 static int CmdChangeCeiling(void);
136 static int CmdChangeCeilingDirect(void);
137 static int CmdRestart(void);
138 static int CmdAndLogical(void);
139 static int CmdOrLogical(void);
140 static int CmdAndBitwise(void);
141 static int CmdOrBitwise(void);
142 static int CmdEorBitwise(void);
143 static int CmdNegateLogical(void);
144 static int CmdLShift(void);
145 static int CmdRShift(void);
146 static int CmdUnaryMinus(void);
147 static int CmdIfNotGoto(void);
148 static int CmdLineSide(void);
149 static int CmdScriptWait(void);
150 static int CmdScriptWaitDirect(void);
151 static int CmdClearLineSpecial(void);
152 static int CmdCaseGoto(void);
153 static int CmdBeginPrint(void);
154 static int CmdEndPrint(void);
155 static int CmdPrintString(void);
156 static int CmdPrintNumber(void);
157 static int CmdPrintCharacter(void);
158 static int CmdPlayerCount(void);
159 static int CmdGameType(void);
160 static int CmdGameSkill(void);
161 static int CmdTimer(void);
162 static int CmdSectorSound(void);
163 static int CmdAmbientSound(void);
164 static int CmdSoundSequence(void);
165 static int CmdSetLineTexture(void);
166 static int CmdSetLineBlocking(void);
167 static int CmdSetLineSpecial(void);
168 static int CmdThingSound(void);
169 static int CmdEndPrintBold(void);
170 
171 static void ThingCount(int type, int tid);
172 
173 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
174 
175 // PUBLIC DATA DEFINITIONS -------------------------------------------------
176 
177 int ACScriptCount;
178 byte *ActionCodeBase;
179 static int ActionCodeSize;
180 acsInfo_t *ACSInfo;
181 int MapVars[MAX_ACS_MAP_VARS];
182 int WorldVars[MAX_ACS_WORLD_VARS];
183 acsstore_t ACSStore[MAX_ACS_STORE + 1]; // +1 for termination marker
184 
185 // PRIVATE DATA DEFINITIONS ------------------------------------------------
186 
187 static char EvalContext[64];
188 static acs_t *ACScript;
189 static unsigned int PCodeOffset;
190 static byte SpecArgs[8];
191 static int ACStringCount;
192 static char **ACStrings;
193 static char PrintBuffer[PRINT_BUFFER_SIZE];
194 static acs_t *NewScript;
195 
196 static int (*PCodeCmds[]) (void) =
197 {
198         CmdNOP,
199         CmdTerminate,
200         CmdSuspend,
201         CmdPushNumber,
202         CmdLSpec1,
203         CmdLSpec2,
204         CmdLSpec3,
205         CmdLSpec4,
206         CmdLSpec5,
207         CmdLSpec1Direct,
208         CmdLSpec2Direct,
209         CmdLSpec3Direct,
210         CmdLSpec4Direct,
211         CmdLSpec5Direct,
212         CmdAdd,
213         CmdSubtract,
214         CmdMultiply,
215         CmdDivide,
216         CmdModulus,
217         CmdEQ,
218         CmdNE,
219         CmdLT,
220         CmdGT,
221         CmdLE,
222         CmdGE,
223         CmdAssignScriptVar,
224         CmdAssignMapVar,
225         CmdAssignWorldVar,
226         CmdPushScriptVar,
227         CmdPushMapVar,
228         CmdPushWorldVar,
229         CmdAddScriptVar,
230         CmdAddMapVar,
231         CmdAddWorldVar,
232         CmdSubScriptVar,
233         CmdSubMapVar,
234         CmdSubWorldVar,
235         CmdMulScriptVar,
236         CmdMulMapVar,
237         CmdMulWorldVar,
238         CmdDivScriptVar,
239         CmdDivMapVar,
240         CmdDivWorldVar,
241         CmdModScriptVar,
242         CmdModMapVar,
243         CmdModWorldVar,
244         CmdIncScriptVar,
245         CmdIncMapVar,
246         CmdIncWorldVar,
247         CmdDecScriptVar,
248         CmdDecMapVar,
249         CmdDecWorldVar,
250         CmdGoto,
251         CmdIfGoto,
252         CmdDrop,
253         CmdDelay,
254         CmdDelayDirect,
255         CmdRandom,
256         CmdRandomDirect,
257         CmdThingCount,
258         CmdThingCountDirect,
259         CmdTagWait,
260         CmdTagWaitDirect,
261         CmdPolyWait,
262         CmdPolyWaitDirect,
263         CmdChangeFloor,
264         CmdChangeFloorDirect,
265         CmdChangeCeiling,
266         CmdChangeCeilingDirect,
267         CmdRestart,
268         CmdAndLogical,
269         CmdOrLogical,
270         CmdAndBitwise,
271         CmdOrBitwise,
272         CmdEorBitwise,
273         CmdNegateLogical,
274         CmdLShift,
275         CmdRShift,
276         CmdUnaryMinus,
277         CmdIfNotGoto,
278         CmdLineSide,
279         CmdScriptWait,
280         CmdScriptWaitDirect,
281         CmdClearLineSpecial,
282         CmdCaseGoto,
283         CmdBeginPrint,
284         CmdEndPrint,
285         CmdPrintString,
286         CmdPrintNumber,
287         CmdPrintCharacter,
288         CmdPlayerCount,
289         CmdGameType,
290         CmdGameSkill,
291         CmdTimer,
292         CmdSectorSound,
293         CmdAmbientSound,
294         CmdSoundSequence,
295         CmdSetLineTexture,
296         CmdSetLineBlocking,
297         CmdSetLineSpecial,
298         CmdThingSound,
299         CmdEndPrintBold,
300 };
301 
302 // CODE --------------------------------------------------------------------
303 
304 //==========================================================================
305 //
306 // ACSAssert
307 //
308 // Check that the given condition evaluates to true. If it does not, exit
309 // with an I_Error() printing the given message.
310 //
311 //==========================================================================
312 
ACSAssert(int condition,const char * fmt,...)313 static void ACSAssert(int condition, const char *fmt, ...)
314 {
315     char buf[128];
316     va_list args;
317 
318     if (condition)
319     {
320         return;
321     }
322 
323     va_start(args, fmt);
324     M_vsnprintf(buf, sizeof(buf), fmt, args);
325     va_end(args);
326     I_Error("ACS assertion failure: in %s: %s", EvalContext, buf);
327 }
328 
329 //==========================================================================
330 //
331 // ReadCodeInt
332 //
333 // Read a 32-bit value from the loaded ACS lump at the location pointed to
334 // by PCodeOffset, advancing PCodeOffset to the next value in the process.
335 //
336 //==========================================================================
337 
ReadCodeInt(void)338 static int ReadCodeInt(void)
339 {
340     int result;
341     int *ptr;
342 
343     ACSAssert(PCodeOffset + 3 < ActionCodeSize,
344               "unexpectedly reached end of ACS lump");
345 
346     ptr = (int *) (ActionCodeBase + PCodeOffset);
347     result = LONG(*ptr);
348     PCodeOffset += 4;
349 
350     return result;
351 }
352 
353 //==========================================================================
354 //
355 // ReadScriptVar
356 //
357 // Read a script variable index as an immediate value, validating the
358 // result is a valid script variable number.
359 //
360 //==========================================================================
361 
ReadScriptVar(void)362 static int ReadScriptVar(void)
363 {
364     int var = ReadCodeInt();
365     ACSAssert(var >= 0, "negative script variable: %d < 0", var);
366     ACSAssert(var < MAX_ACS_SCRIPT_VARS,
367               "invalid script variable: %d >= %d", var, MAX_ACS_SCRIPT_VARS);
368     return var;
369 }
370 
371 //==========================================================================
372 //
373 // ReadMapVar
374 //
375 // Read a map variable index as an immediate value, validating the
376 // result is a valid map variable number.
377 //
378 //==========================================================================
379 
ReadMapVar(void)380 static int ReadMapVar(void)
381 {
382     int var = ReadCodeInt();
383     ACSAssert(var >= 0, "negative map variable: %d < 0", var);
384     ACSAssert(var < MAX_ACS_MAP_VARS,
385               "invalid map variable: %d >= %d", var, MAX_ACS_MAP_VARS);
386     return var;
387 }
388 
389 //==========================================================================
390 //
391 // ReadWorldVar
392 //
393 // Read a world variable index as an immediate value, validating the
394 // result is a valid world variable number.
395 //
396 //==========================================================================
397 
ReadWorldVar(void)398 static int ReadWorldVar(void)
399 {
400     int var = ReadCodeInt();
401     ACSAssert(var >= 0, "negative world variable: %d < 0", var);
402     ACSAssert(var < MAX_ACS_WORLD_VARS,
403               "invalid world variable: %d >= %d", var, MAX_ACS_WORLD_VARS);
404     return var;
405 }
406 
407 //==========================================================================
408 //
409 // StringLookup
410 //
411 // Look up the given string in the strings table by index, validating that
412 // it is a valid string index.
413 //
414 //==========================================================================
415 
StringLookup(int string_index)416 static char *StringLookup(int string_index)
417 {
418     ACSAssert(string_index >= 0,
419               "negative string index: %d < 0", string_index);
420     ACSAssert(string_index < ACStringCount,
421               "invalid string index: %d >= %d", string_index, ACStringCount);
422     return ACStrings[string_index];
423 }
424 
425 //==========================================================================
426 //
427 // ReadOffset
428 //
429 // Read a lump offset value, validating that it is an offset within the
430 // range of the lump.
431 //
432 //==========================================================================
433 
ReadOffset(void)434 static int ReadOffset(void)
435 {
436     int offset = ReadCodeInt();
437     ACSAssert(offset >= 0, "negative lump offset %d", offset);
438     ACSAssert(offset < ActionCodeSize, "invalid lump offset: %d >= %d",
439               offset, ActionCodeSize);
440     return offset;
441 }
442 
443 //==========================================================================
444 //
445 // P_LoadACScripts
446 //
447 //==========================================================================
448 
P_LoadACScripts(int lump)449 void P_LoadACScripts(int lump)
450 {
451     int i, offset;
452     acsHeader_t *header;
453     acsInfo_t *info;
454 
455     ActionCodeBase = W_CacheLumpNum(lump, PU_LEVEL);
456     ActionCodeSize = W_LumpLength(lump);
457 
458     M_snprintf(EvalContext, sizeof(EvalContext),
459                "header parsing of lump #%d", lump);
460 
461     header = (acsHeader_t *) ActionCodeBase;
462     PCodeOffset = LONG(header->infoOffset);
463 
464     ACScriptCount = ReadCodeInt();
465 
466     if (ACScriptCount == 0)
467     {                           // Empty behavior lump
468         return;
469     }
470 
471     ACSInfo = Z_Malloc(ACScriptCount * sizeof(acsInfo_t), PU_LEVEL, 0);
472     memset(ACSInfo, 0, ACScriptCount * sizeof(acsInfo_t));
473     for (i = 0, info = ACSInfo; i < ACScriptCount; i++, info++)
474     {
475         info->number = ReadCodeInt();
476         info->offset = ReadOffset();
477         info->argCount = ReadCodeInt();
478 
479         if (info->argCount > MAX_SCRIPT_ARGS)
480         {
481             fprintf(stderr, "Warning: ACS script #%i has %i arguments, more "
482                             "than the maximum of %i. Enforcing limit.\n"
483                             "If you are seeing this message, please report "
484                             "the name of the WAD where you saw it.\n",
485                             i, info->argCount, MAX_SCRIPT_ARGS);
486             info->argCount = MAX_SCRIPT_ARGS;
487         }
488 
489         if (info->number >= OPEN_SCRIPTS_BASE)
490         {                       // Auto-activate
491             info->number -= OPEN_SCRIPTS_BASE;
492             StartOpenACS(info->number, i, info->offset);
493             info->state = ASTE_RUNNING;
494         }
495         else
496         {
497             info->state = ASTE_INACTIVE;
498         }
499     }
500 
501     ACStringCount = ReadCodeInt();
502     ACSAssert(ACStringCount >= 0, "negative string count %d", ACStringCount);
503     ACStrings = Z_Malloc(ACStringCount * sizeof(char *), PU_LEVEL, NULL);
504 
505     for (i=0; i<ACStringCount; ++i)
506     {
507         offset = ReadOffset();
508         ACStrings[i] = (char *) ActionCodeBase + offset;
509         ACSAssert(memchr(ACStrings[i], '\0', ActionCodeSize - offset) != NULL,
510                   "string %d missing terminating NUL", i);
511     }
512 
513     memset(MapVars, 0, sizeof(MapVars));
514 }
515 
516 //==========================================================================
517 //
518 // StartOpenACS
519 //
520 //==========================================================================
521 
StartOpenACS(int number,int infoIndex,int offset)522 static void StartOpenACS(int number, int infoIndex, int offset)
523 {
524     acs_t *script;
525 
526     script = Z_Malloc(sizeof(acs_t), PU_LEVSPEC, 0);
527     memset(script, 0, sizeof(acs_t));
528     script->number = number;
529 
530     // World objects are allotted 1 second for initialization
531     script->delayCount = 35;
532 
533     script->infoIndex = infoIndex;
534     script->ip = offset;
535     script->thinker.function = T_InterpretACS;
536     P_AddThinker(&script->thinker);
537 }
538 
539 //==========================================================================
540 //
541 // P_CheckACSStore
542 //
543 // Scans the ACS store and executes all scripts belonging to the current
544 // map.
545 //
546 //==========================================================================
547 
P_CheckACSStore(void)548 void P_CheckACSStore(void)
549 {
550     acsstore_t *store;
551 
552     for (store = ACSStore; store->map != 0; store++)
553     {
554         if (store->map == gamemap)
555         {
556             P_StartACS(store->script, 0, store->args, NULL, NULL, 0);
557             if (NewScript)
558             {
559                 NewScript->delayCount = 35;
560             }
561             store->map = -1;
562         }
563     }
564 }
565 
566 //==========================================================================
567 //
568 // P_StartACS
569 //
570 // Start an ACS script. The 'args' array should be at least MAX_SCRIPT_ARGS
571 // elements in length.
572 //
573 //==========================================================================
574 
575 static char ErrorMsg[128];
576 
P_StartACS(int number,int map,byte * args,mobj_t * activator,line_t * line,int side)577 boolean P_StartACS(int number, int map, byte * args, mobj_t * activator,
578                    line_t * line, int side)
579 {
580     int i;
581     acs_t *script;
582     int infoIndex;
583     aste_t *statePtr;
584 
585     NewScript = NULL;
586     if (map && map != gamemap)
587     {                           // Add to the script store
588         return AddToACSStore(map, number, args);
589     }
590     infoIndex = GetACSIndex(number);
591     if (infoIndex == -1)
592     {                           // Script not found
593         //I_Error("P_StartACS: Unknown script number %d", number);
594         M_snprintf(ErrorMsg, sizeof(ErrorMsg),
595                    "P_STARTACS ERROR: UNKNOWN SCRIPT %d", number);
596         P_SetMessage(&players[consoleplayer], ErrorMsg, true);
597     }
598     statePtr = &ACSInfo[infoIndex].state;
599     if (*statePtr == ASTE_SUSPENDED)
600     {                           // Resume a suspended script
601         *statePtr = ASTE_RUNNING;
602         return true;
603     }
604     if (*statePtr != ASTE_INACTIVE)
605     {                           // Script is already executing
606         return false;
607     }
608     script = Z_Malloc(sizeof(acs_t), PU_LEVSPEC, 0);
609     memset(script, 0, sizeof(acs_t));
610     script->number = number;
611     script->infoIndex = infoIndex;
612     script->activator = activator;
613     script->line = line;
614     script->side = side;
615     script->ip = ACSInfo[infoIndex].offset;
616     script->thinker.function = T_InterpretACS;
617     for (i = 0; i < MAX_SCRIPT_ARGS && i < ACSInfo[infoIndex].argCount; i++)
618     {
619         script->vars[i] = args[i];
620     }
621     *statePtr = ASTE_RUNNING;
622     P_AddThinker(&script->thinker);
623     NewScript = script;
624     return true;
625 }
626 
627 //==========================================================================
628 //
629 // AddToACSStore
630 //
631 //==========================================================================
632 
AddToACSStore(int map,int number,byte * args)633 static boolean AddToACSStore(int map, int number, byte * args)
634 {
635     int i;
636     int index;
637 
638     index = -1;
639     for (i = 0; ACSStore[i].map != 0; i++)
640     {
641         if (ACSStore[i].script == number && ACSStore[i].map == map)
642         {                       // Don't allow duplicates
643             return false;
644         }
645         if (index == -1 && ACSStore[i].map == -1)
646         {                       // Remember first empty slot
647             index = i;
648         }
649     }
650     if (index == -1)
651     {                           // Append required
652         if (i == MAX_ACS_STORE)
653         {
654             I_Error("AddToACSStore: MAX_ACS_STORE (%d) exceeded.",
655                     MAX_ACS_STORE);
656         }
657         index = i;
658         ACSStore[index + 1].map = 0;
659     }
660     ACSStore[index].map = map;
661     ACSStore[index].script = number;
662     memcpy(ACSStore[index].args, args, MAX_SCRIPT_ARGS);
663     return true;
664 }
665 
666 //==========================================================================
667 //
668 // P_StartLockedACS
669 //
670 //==========================================================================
671 
672 
P_StartLockedACS(line_t * line,byte * args,mobj_t * mo,int side)673 boolean P_StartLockedACS(line_t * line, byte * args, mobj_t * mo, int side)
674 {
675     int i;
676     int lock;
677     byte newArgs[5];
678     char LockedBuffer[80];
679 
680     extern char *TextKeyMessages[11];
681 
682     lock = args[4];
683     if (!mo->player)
684     {
685         return false;
686     }
687     if (lock)
688     {
689         if (!(mo->player->keys & (1 << (lock - 1))))
690         {
691             M_snprintf(LockedBuffer, sizeof(LockedBuffer),
692                        "YOU NEED THE %s\n", TextKeyMessages[lock - 1]);
693             P_SetMessage(mo->player, LockedBuffer, true);
694             S_StartSound(mo, SFX_DOOR_LOCKED);
695             return false;
696         }
697     }
698     for (i = 0; i < 4; i++)
699     {
700         newArgs[i] = args[i];
701     }
702     newArgs[4] = 0;
703     return P_StartACS(newArgs[0], newArgs[1], &newArgs[2], mo, line, side);
704 }
705 
706 //==========================================================================
707 //
708 // P_TerminateACS
709 //
710 //==========================================================================
711 
P_TerminateACS(int number,int map)712 boolean P_TerminateACS(int number, int map)
713 {
714     int infoIndex;
715 
716     infoIndex = GetACSIndex(number);
717     if (infoIndex == -1)
718     {                           // Script not found
719         return false;
720     }
721     if (ACSInfo[infoIndex].state == ASTE_INACTIVE
722         || ACSInfo[infoIndex].state == ASTE_TERMINATING)
723     {                           // States that disallow termination
724         return false;
725     }
726     ACSInfo[infoIndex].state = ASTE_TERMINATING;
727     return true;
728 }
729 
730 //==========================================================================
731 //
732 // P_SuspendACS
733 //
734 //==========================================================================
735 
P_SuspendACS(int number,int map)736 boolean P_SuspendACS(int number, int map)
737 {
738     int infoIndex;
739 
740     infoIndex = GetACSIndex(number);
741     if (infoIndex == -1)
742     {                           // Script not found
743         return false;
744     }
745     if (ACSInfo[infoIndex].state == ASTE_INACTIVE
746         || ACSInfo[infoIndex].state == ASTE_SUSPENDED
747         || ACSInfo[infoIndex].state == ASTE_TERMINATING)
748     {                           // States that disallow suspension
749         return false;
750     }
751     ACSInfo[infoIndex].state = ASTE_SUSPENDED;
752     return true;
753 }
754 
755 //==========================================================================
756 //
757 // P_Init
758 //
759 //==========================================================================
760 
P_ACSInitNewGame(void)761 void P_ACSInitNewGame(void)
762 {
763     memset(WorldVars, 0, sizeof(WorldVars));
764     memset(ACSStore, 0, sizeof(ACSStore));
765 }
766 
767 //==========================================================================
768 //
769 // T_InterpretACS
770 //
771 //==========================================================================
772 
T_InterpretACS(acs_t * script)773 void T_InterpretACS(acs_t * script)
774 {
775     int cmd;
776     int action;
777 
778     if (ACSInfo[script->infoIndex].state == ASTE_TERMINATING)
779     {
780         ACSInfo[script->infoIndex].state = ASTE_INACTIVE;
781         ScriptFinished(ACScript->number);
782         P_RemoveThinker(&ACScript->thinker);
783         return;
784     }
785     if (ACSInfo[script->infoIndex].state != ASTE_RUNNING)
786     {
787         return;
788     }
789     if (script->delayCount)
790     {
791         script->delayCount--;
792         return;
793     }
794     ACScript = script;
795     PCodeOffset = ACScript->ip;
796 
797     do
798     {
799         M_snprintf(EvalContext, sizeof(EvalContext), "script %d @0x%x",
800                    ACSInfo[script->infoIndex].number, PCodeOffset);
801         cmd = ReadCodeInt();
802         M_snprintf(EvalContext, sizeof(EvalContext), "script %d @0x%x, cmd=%d",
803                    ACSInfo[script->infoIndex].number, PCodeOffset, cmd);
804         ACSAssert(cmd >= 0, "negative ACS instruction %d", cmd);
805         ACSAssert(cmd < arrlen(PCodeCmds),
806                   "invalid ACS instruction %d (maybe this WAD is designed "
807                   "for an advanced source port and is not vanilla "
808                   "compatible)", cmd);
809         action = PCodeCmds[cmd]();
810     } while (action == SCRIPT_CONTINUE);
811 
812     ACScript->ip = PCodeOffset;
813 
814     if (action == SCRIPT_TERMINATE)
815     {
816         ACSInfo[script->infoIndex].state = ASTE_INACTIVE;
817         ScriptFinished(ACScript->number);
818         P_RemoveThinker(&ACScript->thinker);
819     }
820 }
821 
822 //==========================================================================
823 //
824 // P_TagFinished
825 //
826 //==========================================================================
827 
P_TagFinished(int tag)828 void P_TagFinished(int tag)
829 {
830     int i;
831 
832     if (TagBusy(tag) == true)
833     {
834         return;
835     }
836     for (i = 0; i < ACScriptCount; i++)
837     {
838         if (ACSInfo[i].state == ASTE_WAITINGFORTAG
839             && ACSInfo[i].waitValue == tag)
840         {
841             ACSInfo[i].state = ASTE_RUNNING;
842         }
843     }
844 }
845 
846 //==========================================================================
847 //
848 // P_PolyobjFinished
849 //
850 //==========================================================================
851 
P_PolyobjFinished(int po)852 void P_PolyobjFinished(int po)
853 {
854     int i;
855 
856     if (PO_Busy(po) == true)
857     {
858         return;
859     }
860     for (i = 0; i < ACScriptCount; i++)
861     {
862         if (ACSInfo[i].state == ASTE_WAITINGFORPOLY
863             && ACSInfo[i].waitValue == po)
864         {
865             ACSInfo[i].state = ASTE_RUNNING;
866         }
867     }
868 }
869 
870 //==========================================================================
871 //
872 // ScriptFinished
873 //
874 //==========================================================================
875 
ScriptFinished(int number)876 static void ScriptFinished(int number)
877 {
878     int i;
879 
880     for (i = 0; i < ACScriptCount; i++)
881     {
882         if (ACSInfo[i].state == ASTE_WAITINGFORSCRIPT
883             && ACSInfo[i].waitValue == number)
884         {
885             ACSInfo[i].state = ASTE_RUNNING;
886         }
887     }
888 }
889 
890 //==========================================================================
891 //
892 // TagBusy
893 //
894 //==========================================================================
895 
TagBusy(int tag)896 static boolean TagBusy(int tag)
897 {
898     int sectorIndex;
899 
900     sectorIndex = -1;
901     while ((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0)
902     {
903         if (sectors[sectorIndex].specialdata)
904         {
905             return true;
906         }
907     }
908     return false;
909 }
910 
911 //==========================================================================
912 //
913 // GetACSIndex
914 //
915 // Returns the index of a script number.  Returns -1 if the script number
916 // is not found.
917 //
918 //==========================================================================
919 
GetACSIndex(int number)920 static int GetACSIndex(int number)
921 {
922     int i;
923 
924     for (i = 0; i < ACScriptCount; i++)
925     {
926         if (ACSInfo[i].number == number)
927         {
928             return i;
929         }
930     }
931     return -1;
932 }
933 
934 //==========================================================================
935 //
936 // CheckACSPresent
937 //
938 // Placing Korax in a PWAD without extra steps will result in a crash in
939 // Vanilla because the relevant ACS scripts are not initialized
940 //
941 //==========================================================================
942 
CheckACSPresent(int number)943 void CheckACSPresent(int number)
944 {
945     if (GetACSIndex(number) == -1)
946     {
947         I_Error("Required ACS script %d not initialized", number);
948     }
949 }
950 
951 //==========================================================================
952 //
953 // Push
954 //
955 //==========================================================================
956 
Push(int value)957 static void Push(int value)
958 {
959     ACSAssert(ACScript->stackPtr < ACS_STACK_DEPTH,
960               "maximum stack depth exceeded: %d >= %d",
961               ACScript->stackPtr, ACS_STACK_DEPTH);
962     ACScript->stack[ACScript->stackPtr++] = value;
963 }
964 
965 //==========================================================================
966 //
967 // Pop
968 //
969 //==========================================================================
970 
Pop(void)971 static int Pop(void)
972 {
973     ACSAssert(ACScript->stackPtr > 0, "pop of empty stack");
974     return ACScript->stack[--ACScript->stackPtr];
975 }
976 
977 //==========================================================================
978 //
979 // Top
980 //
981 //==========================================================================
982 
Top(void)983 static int Top(void)
984 {
985     ACSAssert(ACScript->stackPtr > 0, "read from top of empty stack");
986     return ACScript->stack[ACScript->stackPtr - 1];
987 }
988 
989 //==========================================================================
990 //
991 // Drop
992 //
993 //==========================================================================
994 
Drop(void)995 static void Drop(void)
996 {
997     ACSAssert(ACScript->stackPtr > 0, "drop on empty stack");
998     ACScript->stackPtr--;
999 }
1000 
1001 //==========================================================================
1002 //
1003 // P-Code Commands
1004 //
1005 //==========================================================================
1006 
CmdNOP(void)1007 static int CmdNOP(void)
1008 {
1009     return SCRIPT_CONTINUE;
1010 }
1011 
CmdTerminate(void)1012 static int CmdTerminate(void)
1013 {
1014     return SCRIPT_TERMINATE;
1015 }
1016 
CmdSuspend(void)1017 static int CmdSuspend(void)
1018 {
1019     ACSInfo[ACScript->infoIndex].state = ASTE_SUSPENDED;
1020     return SCRIPT_STOP;
1021 }
1022 
CmdPushNumber(void)1023 static int CmdPushNumber(void)
1024 {
1025     Push(ReadCodeInt());
1026     return SCRIPT_CONTINUE;
1027 }
1028 
CmdLSpec1(void)1029 static int CmdLSpec1(void)
1030 {
1031     int special;
1032 
1033     special = ReadCodeInt();
1034     SpecArgs[0] = Pop();
1035     P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
1036                          ACScript->side, ACScript->activator);
1037     return SCRIPT_CONTINUE;
1038 }
1039 
CmdLSpec2(void)1040 static int CmdLSpec2(void)
1041 {
1042     int special;
1043 
1044     special = ReadCodeInt();
1045     SpecArgs[1] = Pop();
1046     SpecArgs[0] = Pop();
1047     P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
1048                          ACScript->side, ACScript->activator);
1049     return SCRIPT_CONTINUE;
1050 }
1051 
CmdLSpec3(void)1052 static int CmdLSpec3(void)
1053 {
1054     int special;
1055 
1056     special = ReadCodeInt();
1057     SpecArgs[2] = Pop();
1058     SpecArgs[1] = Pop();
1059     SpecArgs[0] = Pop();
1060     P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
1061                          ACScript->side, ACScript->activator);
1062     return SCRIPT_CONTINUE;
1063 }
1064 
CmdLSpec4(void)1065 static int CmdLSpec4(void)
1066 {
1067     int special;
1068 
1069     special = ReadCodeInt();
1070     SpecArgs[3] = Pop();
1071     SpecArgs[2] = Pop();
1072     SpecArgs[1] = Pop();
1073     SpecArgs[0] = Pop();
1074     P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
1075                          ACScript->side, ACScript->activator);
1076     return SCRIPT_CONTINUE;
1077 }
1078 
CmdLSpec5(void)1079 static int CmdLSpec5(void)
1080 {
1081     int special;
1082 
1083     special = ReadCodeInt();
1084     SpecArgs[4] = Pop();
1085     SpecArgs[3] = Pop();
1086     SpecArgs[2] = Pop();
1087     SpecArgs[1] = Pop();
1088     SpecArgs[0] = Pop();
1089     P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
1090                          ACScript->side, ACScript->activator);
1091     return SCRIPT_CONTINUE;
1092 }
1093 
CmdLSpec1Direct(void)1094 static int CmdLSpec1Direct(void)
1095 {
1096     int special;
1097 
1098     special = ReadCodeInt();
1099     SpecArgs[0] = ReadCodeInt();
1100     P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
1101                          ACScript->side, ACScript->activator);
1102     return SCRIPT_CONTINUE;
1103 }
1104 
CmdLSpec2Direct(void)1105 static int CmdLSpec2Direct(void)
1106 {
1107     int special;
1108 
1109     special = ReadCodeInt();
1110     SpecArgs[0] = ReadCodeInt();
1111     SpecArgs[1] = ReadCodeInt();
1112     P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
1113                          ACScript->side, ACScript->activator);
1114     return SCRIPT_CONTINUE;
1115 }
1116 
CmdLSpec3Direct(void)1117 static int CmdLSpec3Direct(void)
1118 {
1119     int special;
1120 
1121     special = ReadCodeInt();
1122     SpecArgs[0] = ReadCodeInt();
1123     SpecArgs[1] = ReadCodeInt();
1124     SpecArgs[2] = ReadCodeInt();
1125     P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
1126                          ACScript->side, ACScript->activator);
1127     return SCRIPT_CONTINUE;
1128 }
1129 
CmdLSpec4Direct(void)1130 static int CmdLSpec4Direct(void)
1131 {
1132     int special;
1133 
1134     special = ReadCodeInt();
1135     SpecArgs[0] = ReadCodeInt();
1136     SpecArgs[1] = ReadCodeInt();
1137     SpecArgs[2] = ReadCodeInt();
1138     SpecArgs[3] = ReadCodeInt();
1139     P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
1140                          ACScript->side, ACScript->activator);
1141     return SCRIPT_CONTINUE;
1142 }
1143 
CmdLSpec5Direct(void)1144 static int CmdLSpec5Direct(void)
1145 {
1146     int special;
1147 
1148     special = ReadCodeInt();
1149     SpecArgs[0] = ReadCodeInt();
1150     SpecArgs[1] = ReadCodeInt();
1151     SpecArgs[2] = ReadCodeInt();
1152     SpecArgs[3] = ReadCodeInt();
1153     SpecArgs[4] = ReadCodeInt();
1154     P_ExecuteLineSpecial(special, SpecArgs, ACScript->line,
1155                          ACScript->side, ACScript->activator);
1156     return SCRIPT_CONTINUE;
1157 }
1158 
CmdAdd(void)1159 static int CmdAdd(void)
1160 {
1161     Push(Pop() + Pop());
1162     return SCRIPT_CONTINUE;
1163 }
1164 
CmdSubtract(void)1165 static int CmdSubtract(void)
1166 {
1167     int operand2;
1168 
1169     operand2 = Pop();
1170     Push(Pop() - operand2);
1171     return SCRIPT_CONTINUE;
1172 }
1173 
CmdMultiply(void)1174 static int CmdMultiply(void)
1175 {
1176     Push(Pop() * Pop());
1177     return SCRIPT_CONTINUE;
1178 }
1179 
CmdDivide(void)1180 static int CmdDivide(void)
1181 {
1182     int operand2;
1183 
1184     operand2 = Pop();
1185     Push(Pop() / operand2);
1186     return SCRIPT_CONTINUE;
1187 }
1188 
CmdModulus(void)1189 static int CmdModulus(void)
1190 {
1191     int operand2;
1192 
1193     operand2 = Pop();
1194     Push(Pop() % operand2);
1195     return SCRIPT_CONTINUE;
1196 }
1197 
CmdEQ(void)1198 static int CmdEQ(void)
1199 {
1200     Push(Pop() == Pop());
1201     return SCRIPT_CONTINUE;
1202 }
1203 
CmdNE(void)1204 static int CmdNE(void)
1205 {
1206     Push(Pop() != Pop());
1207     return SCRIPT_CONTINUE;
1208 }
1209 
CmdLT(void)1210 static int CmdLT(void)
1211 {
1212     int operand2;
1213 
1214     operand2 = Pop();
1215     Push(Pop() < operand2);
1216     return SCRIPT_CONTINUE;
1217 }
1218 
CmdGT(void)1219 static int CmdGT(void)
1220 {
1221     int operand2;
1222 
1223     operand2 = Pop();
1224     Push(Pop() > operand2);
1225     return SCRIPT_CONTINUE;
1226 }
1227 
CmdLE(void)1228 static int CmdLE(void)
1229 {
1230     int operand2;
1231 
1232     operand2 = Pop();
1233     Push(Pop() <= operand2);
1234     return SCRIPT_CONTINUE;
1235 }
1236 
CmdGE(void)1237 static int CmdGE(void)
1238 {
1239     int operand2;
1240 
1241     operand2 = Pop();
1242     Push(Pop() >= operand2);
1243     return SCRIPT_CONTINUE;
1244 }
1245 
CmdAssignScriptVar(void)1246 static int CmdAssignScriptVar(void)
1247 {
1248     ACScript->vars[ReadScriptVar()] = Pop();
1249     return SCRIPT_CONTINUE;
1250 }
1251 
CmdAssignMapVar(void)1252 static int CmdAssignMapVar(void)
1253 {
1254     MapVars[ReadMapVar()] = Pop();
1255     return SCRIPT_CONTINUE;
1256 }
1257 
CmdAssignWorldVar(void)1258 static int CmdAssignWorldVar(void)
1259 {
1260     WorldVars[ReadWorldVar()] = Pop();
1261     return SCRIPT_CONTINUE;
1262 }
1263 
CmdPushScriptVar(void)1264 static int CmdPushScriptVar(void)
1265 {
1266     Push(ACScript->vars[ReadScriptVar()]);
1267     return SCRIPT_CONTINUE;
1268 }
1269 
CmdPushMapVar(void)1270 static int CmdPushMapVar(void)
1271 {
1272     Push(MapVars[ReadMapVar()]);
1273     return SCRIPT_CONTINUE;
1274 }
1275 
CmdPushWorldVar(void)1276 static int CmdPushWorldVar(void)
1277 {
1278     Push(WorldVars[ReadWorldVar()]);
1279     return SCRIPT_CONTINUE;
1280 }
1281 
CmdAddScriptVar(void)1282 static int CmdAddScriptVar(void)
1283 {
1284     ACScript->vars[ReadScriptVar()] += Pop();
1285     return SCRIPT_CONTINUE;
1286 }
1287 
CmdAddMapVar(void)1288 static int CmdAddMapVar(void)
1289 {
1290     MapVars[ReadMapVar()] += Pop();
1291     return SCRIPT_CONTINUE;
1292 }
1293 
CmdAddWorldVar(void)1294 static int CmdAddWorldVar(void)
1295 {
1296     WorldVars[ReadWorldVar()] += Pop();
1297     return SCRIPT_CONTINUE;
1298 }
1299 
CmdSubScriptVar(void)1300 static int CmdSubScriptVar(void)
1301 {
1302     ACScript->vars[ReadScriptVar()] -= Pop();
1303     return SCRIPT_CONTINUE;
1304 }
1305 
CmdSubMapVar(void)1306 static int CmdSubMapVar(void)
1307 {
1308     MapVars[ReadMapVar()] -= Pop();
1309     return SCRIPT_CONTINUE;
1310 }
1311 
CmdSubWorldVar(void)1312 static int CmdSubWorldVar(void)
1313 {
1314     WorldVars[ReadWorldVar()] -= Pop();
1315     return SCRIPT_CONTINUE;
1316 }
1317 
CmdMulScriptVar(void)1318 static int CmdMulScriptVar(void)
1319 {
1320     ACScript->vars[ReadScriptVar()] *= Pop();
1321     return SCRIPT_CONTINUE;
1322 }
1323 
CmdMulMapVar(void)1324 static int CmdMulMapVar(void)
1325 {
1326     MapVars[ReadMapVar()] *= Pop();
1327     return SCRIPT_CONTINUE;
1328 }
1329 
CmdMulWorldVar(void)1330 static int CmdMulWorldVar(void)
1331 {
1332     WorldVars[ReadWorldVar()] *= Pop();
1333     return SCRIPT_CONTINUE;
1334 }
1335 
CmdDivScriptVar(void)1336 static int CmdDivScriptVar(void)
1337 {
1338     ACScript->vars[ReadScriptVar()] /= Pop();
1339     return SCRIPT_CONTINUE;
1340 }
1341 
CmdDivMapVar(void)1342 static int CmdDivMapVar(void)
1343 {
1344     MapVars[ReadMapVar()] /= Pop();
1345     return SCRIPT_CONTINUE;
1346 }
1347 
CmdDivWorldVar(void)1348 static int CmdDivWorldVar(void)
1349 {
1350     WorldVars[ReadWorldVar()] /= Pop();
1351     return SCRIPT_CONTINUE;
1352 }
1353 
CmdModScriptVar(void)1354 static int CmdModScriptVar(void)
1355 {
1356     ACScript->vars[ReadScriptVar()] %= Pop();
1357     return SCRIPT_CONTINUE;
1358 }
1359 
CmdModMapVar(void)1360 static int CmdModMapVar(void)
1361 {
1362     MapVars[ReadMapVar()] %= Pop();
1363     return SCRIPT_CONTINUE;
1364 }
1365 
CmdModWorldVar(void)1366 static int CmdModWorldVar(void)
1367 {
1368     WorldVars[ReadWorldVar()] %= Pop();
1369     return SCRIPT_CONTINUE;
1370 }
1371 
CmdIncScriptVar(void)1372 static int CmdIncScriptVar(void)
1373 {
1374     ++ACScript->vars[ReadScriptVar()];
1375     return SCRIPT_CONTINUE;
1376 }
1377 
CmdIncMapVar(void)1378 static int CmdIncMapVar(void)
1379 {
1380     ++MapVars[ReadMapVar()];
1381     return SCRIPT_CONTINUE;
1382 }
1383 
CmdIncWorldVar(void)1384 static int CmdIncWorldVar(void)
1385 {
1386     ++WorldVars[ReadWorldVar()];
1387     return SCRIPT_CONTINUE;
1388 }
1389 
CmdDecScriptVar(void)1390 static int CmdDecScriptVar(void)
1391 {
1392     --ACScript->vars[ReadScriptVar()];
1393     return SCRIPT_CONTINUE;
1394 }
1395 
CmdDecMapVar(void)1396 static int CmdDecMapVar(void)
1397 {
1398     --MapVars[ReadMapVar()];
1399     return SCRIPT_CONTINUE;
1400 }
1401 
CmdDecWorldVar(void)1402 static int CmdDecWorldVar(void)
1403 {
1404     --WorldVars[ReadWorldVar()];
1405     return SCRIPT_CONTINUE;
1406 }
1407 
CmdGoto(void)1408 static int CmdGoto(void)
1409 {
1410     PCodeOffset = ReadOffset();
1411     return SCRIPT_CONTINUE;
1412 }
1413 
CmdIfGoto(void)1414 static int CmdIfGoto(void)
1415 {
1416     int offset;
1417 
1418     offset = ReadOffset();
1419 
1420     if (Pop() != 0)
1421     {
1422         PCodeOffset = offset;
1423     }
1424     return SCRIPT_CONTINUE;
1425 }
1426 
CmdDrop(void)1427 static int CmdDrop(void)
1428 {
1429     Drop();
1430     return SCRIPT_CONTINUE;
1431 }
1432 
CmdDelay(void)1433 static int CmdDelay(void)
1434 {
1435     ACScript->delayCount = Pop();
1436     return SCRIPT_STOP;
1437 }
1438 
CmdDelayDirect(void)1439 static int CmdDelayDirect(void)
1440 {
1441     ACScript->delayCount = ReadCodeInt();
1442     return SCRIPT_STOP;
1443 }
1444 
CmdRandom(void)1445 static int CmdRandom(void)
1446 {
1447     int low;
1448     int high;
1449 
1450     high = Pop();
1451     low = Pop();
1452     Push(low + (P_Random() % (high - low + 1)));
1453     return SCRIPT_CONTINUE;
1454 }
1455 
CmdRandomDirect(void)1456 static int CmdRandomDirect(void)
1457 {
1458     int low;
1459     int high;
1460 
1461     low = ReadCodeInt();
1462     high = ReadCodeInt();
1463     Push(low + (P_Random() % (high - low + 1)));
1464     return SCRIPT_CONTINUE;
1465 }
1466 
CmdThingCount(void)1467 static int CmdThingCount(void)
1468 {
1469     int tid;
1470 
1471     tid = Pop();
1472     ThingCount(Pop(), tid);
1473     return SCRIPT_CONTINUE;
1474 }
1475 
CmdThingCountDirect(void)1476 static int CmdThingCountDirect(void)
1477 {
1478     int type;
1479 
1480     type = ReadCodeInt();
1481     ThingCount(type, ReadCodeInt());
1482     return SCRIPT_CONTINUE;
1483 }
1484 
ThingCount(int type,int tid)1485 static void ThingCount(int type, int tid)
1486 {
1487     int count;
1488     int searcher;
1489     mobj_t *mobj;
1490     mobjtype_t moType;
1491     thinker_t *think;
1492 
1493     if (!(type + tid))
1494     {                           // Nothing to count
1495         return;
1496     }
1497     moType = TranslateThingType[type];
1498     count = 0;
1499     searcher = -1;
1500     if (tid)
1501     {                           // Count TID things
1502         while ((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL)
1503         {
1504             if (type == 0)
1505             {                   // Just count TIDs
1506                 count++;
1507             }
1508             else if (moType == mobj->type)
1509             {
1510                 if (mobj->flags & MF_COUNTKILL && mobj->health <= 0)
1511                 {               // Don't count dead monsters
1512                     continue;
1513                 }
1514                 count++;
1515             }
1516         }
1517     }
1518     else
1519     {                           // Count only types
1520         for (think = thinkercap.next; think != &thinkercap;
1521              think = think->next)
1522         {
1523             if (think->function != P_MobjThinker)
1524             {                   // Not a mobj thinker
1525                 continue;
1526             }
1527             mobj = (mobj_t *) think;
1528             if (mobj->type != moType)
1529             {                   // Doesn't match
1530                 continue;
1531             }
1532             if (mobj->flags & MF_COUNTKILL && mobj->health <= 0)
1533             {                   // Don't count dead monsters
1534                 continue;
1535             }
1536             count++;
1537         }
1538     }
1539     Push(count);
1540 }
1541 
CmdTagWait(void)1542 static int CmdTagWait(void)
1543 {
1544     ACSInfo[ACScript->infoIndex].waitValue = Pop();
1545     ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORTAG;
1546     return SCRIPT_STOP;
1547 }
1548 
CmdTagWaitDirect(void)1549 static int CmdTagWaitDirect(void)
1550 {
1551     ACSInfo[ACScript->infoIndex].waitValue = ReadCodeInt();
1552     ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORTAG;
1553     return SCRIPT_STOP;
1554 }
1555 
CmdPolyWait(void)1556 static int CmdPolyWait(void)
1557 {
1558     ACSInfo[ACScript->infoIndex].waitValue = Pop();
1559     ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORPOLY;
1560     return SCRIPT_STOP;
1561 }
1562 
CmdPolyWaitDirect(void)1563 static int CmdPolyWaitDirect(void)
1564 {
1565     ACSInfo[ACScript->infoIndex].waitValue = ReadCodeInt();
1566     ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORPOLY;
1567     return SCRIPT_STOP;
1568 }
1569 
CmdChangeFloor(void)1570 static int CmdChangeFloor(void)
1571 {
1572     int tag;
1573     int flat;
1574     int sectorIndex;
1575 
1576     flat = R_FlatNumForName(StringLookup(Pop()));
1577     tag = Pop();
1578     sectorIndex = -1;
1579     while ((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0)
1580     {
1581         sectors[sectorIndex].floorpic = flat;
1582     }
1583     return SCRIPT_CONTINUE;
1584 }
1585 
CmdChangeFloorDirect(void)1586 static int CmdChangeFloorDirect(void)
1587 {
1588     int tag;
1589     int flat;
1590     int sectorIndex;
1591 
1592     tag = ReadCodeInt();
1593     flat = R_FlatNumForName(StringLookup(ReadCodeInt()));
1594     sectorIndex = -1;
1595     while ((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0)
1596     {
1597         sectors[sectorIndex].floorpic = flat;
1598     }
1599     return SCRIPT_CONTINUE;
1600 }
1601 
CmdChangeCeiling(void)1602 static int CmdChangeCeiling(void)
1603 {
1604     int tag;
1605     int flat;
1606     int sectorIndex;
1607 
1608     flat = R_FlatNumForName(StringLookup(Pop()));
1609     tag = Pop();
1610     sectorIndex = -1;
1611     while ((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0)
1612     {
1613         sectors[sectorIndex].ceilingpic = flat;
1614     }
1615     return SCRIPT_CONTINUE;
1616 }
1617 
CmdChangeCeilingDirect(void)1618 static int CmdChangeCeilingDirect(void)
1619 {
1620     int tag;
1621     int flat;
1622     int sectorIndex;
1623 
1624     tag = ReadCodeInt();
1625     flat = R_FlatNumForName(StringLookup(ReadCodeInt()));
1626     sectorIndex = -1;
1627     while ((sectorIndex = P_FindSectorFromTag(tag, sectorIndex)) >= 0)
1628     {
1629         sectors[sectorIndex].ceilingpic = flat;
1630     }
1631     return SCRIPT_CONTINUE;
1632 }
1633 
CmdRestart(void)1634 static int CmdRestart(void)
1635 {
1636     PCodeOffset = ACSInfo[ACScript->infoIndex].offset;
1637     return SCRIPT_CONTINUE;
1638 }
1639 
CmdAndLogical(void)1640 static int CmdAndLogical(void)
1641 {
1642     Push(Pop() && Pop());
1643     return SCRIPT_CONTINUE;
1644 }
1645 
CmdOrLogical(void)1646 static int CmdOrLogical(void)
1647 {
1648     Push(Pop() || Pop());
1649     return SCRIPT_CONTINUE;
1650 }
1651 
CmdAndBitwise(void)1652 static int CmdAndBitwise(void)
1653 {
1654     Push(Pop() & Pop());
1655     return SCRIPT_CONTINUE;
1656 }
1657 
CmdOrBitwise(void)1658 static int CmdOrBitwise(void)
1659 {
1660     Push(Pop() | Pop());
1661     return SCRIPT_CONTINUE;
1662 }
1663 
CmdEorBitwise(void)1664 static int CmdEorBitwise(void)
1665 {
1666     Push(Pop() ^ Pop());
1667     return SCRIPT_CONTINUE;
1668 }
1669 
CmdNegateLogical(void)1670 static int CmdNegateLogical(void)
1671 {
1672     Push(!Pop());
1673     return SCRIPT_CONTINUE;
1674 }
1675 
CmdLShift(void)1676 static int CmdLShift(void)
1677 {
1678     int operand2;
1679 
1680     operand2 = Pop();
1681     Push(Pop() << operand2);
1682     return SCRIPT_CONTINUE;
1683 }
1684 
CmdRShift(void)1685 static int CmdRShift(void)
1686 {
1687     int operand2;
1688 
1689     operand2 = Pop();
1690     Push(Pop() >> operand2);
1691     return SCRIPT_CONTINUE;
1692 }
1693 
CmdUnaryMinus(void)1694 static int CmdUnaryMinus(void)
1695 {
1696     Push(-Pop());
1697     return SCRIPT_CONTINUE;
1698 }
1699 
CmdIfNotGoto(void)1700 static int CmdIfNotGoto(void)
1701 {
1702     int offset;
1703 
1704     offset = ReadOffset();
1705 
1706     if (Pop() == 0)
1707     {
1708         PCodeOffset = offset;
1709     }
1710     return SCRIPT_CONTINUE;
1711 }
1712 
CmdLineSide(void)1713 static int CmdLineSide(void)
1714 {
1715     Push(ACScript->side);
1716     return SCRIPT_CONTINUE;
1717 }
1718 
CmdScriptWait(void)1719 static int CmdScriptWait(void)
1720 {
1721     ACSInfo[ACScript->infoIndex].waitValue = Pop();
1722     ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORSCRIPT;
1723     return SCRIPT_STOP;
1724 }
1725 
CmdScriptWaitDirect(void)1726 static int CmdScriptWaitDirect(void)
1727 {
1728     ACSInfo[ACScript->infoIndex].waitValue = ReadCodeInt();
1729     ACSInfo[ACScript->infoIndex].state = ASTE_WAITINGFORSCRIPT;
1730     return SCRIPT_STOP;
1731 }
1732 
CmdClearLineSpecial(void)1733 static int CmdClearLineSpecial(void)
1734 {
1735     if (ACScript->line)
1736     {
1737         ACScript->line->special = 0;
1738     }
1739     return SCRIPT_CONTINUE;
1740 }
1741 
CmdCaseGoto(void)1742 static int CmdCaseGoto(void)
1743 {
1744     int value;
1745     int offset;
1746 
1747     value = ReadCodeInt();
1748     offset = ReadOffset();
1749 
1750     if (Top() == value)
1751     {
1752         PCodeOffset = offset;
1753         Drop();
1754     }
1755 
1756     return SCRIPT_CONTINUE;
1757 }
1758 
CmdBeginPrint(void)1759 static int CmdBeginPrint(void)
1760 {
1761     *PrintBuffer = 0;
1762     return SCRIPT_CONTINUE;
1763 }
1764 
CmdEndPrint(void)1765 static int CmdEndPrint(void)
1766 {
1767     player_t *player;
1768 
1769     if (ACScript->activator && ACScript->activator->player)
1770     {
1771         player = ACScript->activator->player;
1772     }
1773     else
1774     {
1775         player = &players[consoleplayer];
1776     }
1777     P_SetMessage(player, PrintBuffer, true);
1778     return SCRIPT_CONTINUE;
1779 }
1780 
CmdEndPrintBold(void)1781 static int CmdEndPrintBold(void)
1782 {
1783     int i;
1784 
1785     for (i = 0; i < maxplayers; i++)
1786     {
1787         if (playeringame[i])
1788         {
1789             P_SetYellowMessage(&players[i], PrintBuffer, true);
1790         }
1791     }
1792     return SCRIPT_CONTINUE;
1793 }
1794 
CmdPrintString(void)1795 static int CmdPrintString(void)
1796 {
1797     M_StringConcat(PrintBuffer, StringLookup(Pop()), sizeof(PrintBuffer));
1798     return SCRIPT_CONTINUE;
1799 }
1800 
CmdPrintNumber(void)1801 static int CmdPrintNumber(void)
1802 {
1803     char tempStr[16];
1804 
1805     M_snprintf(tempStr, sizeof(tempStr), "%d", Pop());
1806     M_StringConcat(PrintBuffer, tempStr, sizeof(PrintBuffer));
1807     return SCRIPT_CONTINUE;
1808 }
1809 
CmdPrintCharacter(void)1810 static int CmdPrintCharacter(void)
1811 {
1812     char tempStr[2];
1813 
1814     tempStr[0] = Pop();
1815     tempStr[1] = '\0';
1816     M_StringConcat(PrintBuffer, tempStr, sizeof(PrintBuffer));
1817 
1818     return SCRIPT_CONTINUE;
1819 }
1820 
CmdPlayerCount(void)1821 static int CmdPlayerCount(void)
1822 {
1823     int i;
1824     int count;
1825 
1826     count = 0;
1827     for (i = 0; i < maxplayers; i++)
1828     {
1829         count += playeringame[i];
1830     }
1831     Push(count);
1832     return SCRIPT_CONTINUE;
1833 }
1834 
CmdGameType(void)1835 static int CmdGameType(void)
1836 {
1837     int gametype;
1838 
1839     if (netgame == false)
1840     {
1841         gametype = GAME_SINGLE_PLAYER;
1842     }
1843     else if (deathmatch)
1844     {
1845         gametype = GAME_NET_DEATHMATCH;
1846     }
1847     else
1848     {
1849         gametype = GAME_NET_COOPERATIVE;
1850     }
1851     Push(gametype);
1852     return SCRIPT_CONTINUE;
1853 }
1854 
CmdGameSkill(void)1855 static int CmdGameSkill(void)
1856 {
1857     Push(gameskill);
1858     return SCRIPT_CONTINUE;
1859 }
1860 
CmdTimer(void)1861 static int CmdTimer(void)
1862 {
1863     Push(leveltime);
1864     return SCRIPT_CONTINUE;
1865 }
1866 
CmdSectorSound(void)1867 static int CmdSectorSound(void)
1868 {
1869     int volume;
1870     mobj_t *mobj;
1871 
1872     mobj = NULL;
1873     if (ACScript->line)
1874     {
1875         mobj = (mobj_t *) & ACScript->line->frontsector->soundorg;
1876     }
1877     volume = Pop();
1878     S_StartSoundAtVolume(mobj, S_GetSoundID(StringLookup(Pop())), volume);
1879     return SCRIPT_CONTINUE;
1880 }
1881 
CmdThingSound(void)1882 static int CmdThingSound(void)
1883 {
1884     int tid;
1885     int sound;
1886     int volume;
1887     mobj_t *mobj;
1888     int searcher;
1889 
1890     volume = Pop();
1891     sound = S_GetSoundID(StringLookup(Pop()));
1892     tid = Pop();
1893     searcher = -1;
1894     while ((mobj = P_FindMobjFromTID(tid, &searcher)) != NULL)
1895     {
1896         S_StartSoundAtVolume(mobj, sound, volume);
1897     }
1898     return SCRIPT_CONTINUE;
1899 }
1900 
CmdAmbientSound(void)1901 static int CmdAmbientSound(void)
1902 {
1903     int volume;
1904 
1905     volume = Pop();
1906     S_StartSoundAtVolume(NULL, S_GetSoundID(StringLookup(Pop())), volume);
1907     return SCRIPT_CONTINUE;
1908 }
1909 
CmdSoundSequence(void)1910 static int CmdSoundSequence(void)
1911 {
1912     mobj_t *mobj;
1913 
1914     mobj = NULL;
1915     if (ACScript->line)
1916     {
1917         mobj = (mobj_t *) & ACScript->line->frontsector->soundorg;
1918     }
1919     SN_StartSequenceName(mobj, StringLookup(Pop()));
1920     return SCRIPT_CONTINUE;
1921 }
1922 
CmdSetLineTexture(void)1923 static int CmdSetLineTexture(void)
1924 {
1925     line_t *line;
1926     int lineTag;
1927     int side;
1928     int position;
1929     int texture;
1930     int searcher;
1931 
1932     texture = R_TextureNumForName(StringLookup(Pop()));
1933     position = Pop();
1934     side = Pop();
1935     lineTag = Pop();
1936     searcher = -1;
1937     while ((line = P_FindLine(lineTag, &searcher)) != NULL)
1938     {
1939         if (position == TEXTURE_MIDDLE)
1940         {
1941             sides[line->sidenum[side]].midtexture = texture;
1942         }
1943         else if (position == TEXTURE_BOTTOM)
1944         {
1945             sides[line->sidenum[side]].bottomtexture = texture;
1946         }
1947         else
1948         {                       // TEXTURE_TOP
1949             sides[line->sidenum[side]].toptexture = texture;
1950         }
1951     }
1952     return SCRIPT_CONTINUE;
1953 }
1954 
CmdSetLineBlocking(void)1955 static int CmdSetLineBlocking(void)
1956 {
1957     line_t *line;
1958     int lineTag;
1959     boolean blocking;
1960     int searcher;
1961 
1962     blocking = Pop()? ML_BLOCKING : 0;
1963     lineTag = Pop();
1964     searcher = -1;
1965     while ((line = P_FindLine(lineTag, &searcher)) != NULL)
1966     {
1967         line->flags = (line->flags & ~ML_BLOCKING) | blocking;
1968     }
1969     return SCRIPT_CONTINUE;
1970 }
1971 
CmdSetLineSpecial(void)1972 static int CmdSetLineSpecial(void)
1973 {
1974     line_t *line;
1975     int lineTag;
1976     int special, arg1, arg2, arg3, arg4, arg5;
1977     int searcher;
1978 
1979     arg5 = Pop();
1980     arg4 = Pop();
1981     arg3 = Pop();
1982     arg2 = Pop();
1983     arg1 = Pop();
1984     special = Pop();
1985     lineTag = Pop();
1986     searcher = -1;
1987     while ((line = P_FindLine(lineTag, &searcher)) != NULL)
1988     {
1989         line->special = special;
1990         line->arg1 = arg1;
1991         line->arg2 = arg2;
1992         line->arg3 = arg3;
1993         line->arg4 = arg4;
1994         line->arg5 = arg5;
1995     }
1996     return SCRIPT_CONTINUE;
1997 }
1998