1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: p_acs.cpp 4542 2014-02-09 17:39:42Z dr_sean $
5 //
6 // Copyright (C) 1998-2006 by Randy Heit (ZDoom).
7 // Copyright (C) 2006-2014 by The Odamex Team.
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 // DESCRIPTION:
20 // [RH] p_acs.c: New file to handle ACS scripts
21 //
22 //-----------------------------------------------------------------------------
23 //
24
25 #include "z_zone.h"
26 #include "doomdef.h"
27 #include "p_local.h"
28 #include "p_spec.h"
29 #include "g_level.h"
30 #include "s_sound.h"
31 #include "p_acs.h"
32 #include "p_saveg.h"
33 #include "p_lnspec.h"
34 #include "m_random.h"
35 #include "doomstat.h"
36 #include "c_console.h"
37 #include "c_dispatch.h"
38 #include "s_sndseq.h"
39 #include "i_system.h"
40 #include "m_vectors.h"
41
42 #define CLAMPCOLOR(c) (EColorRange)((unsigned)(c)>CR_UNTRANSLATED?CR_UNTRANSLATED:(c))
43 #define LANGREGIONMASK MAKE_ID(0,0,0xff,0xff)
44
45 struct CallReturn
46 {
47 int ReturnAddress;
48 ScriptFunction *ReturnFunction;
49 BYTE bDiscardResult;
50 BYTE Pad[3];
51 };
52
53 static int Stack[STACK_SIZE];
54
55 static bool P_GetScriptGoing (AActor *who, line_t *where, int num, int *code,
56 int lineSide, int arg0, int arg1, int arg2, int always, bool delay);
57
58 struct FBehavior::ArrayInfo
59 {
60 int ArraySize;
61 SDWORD *Elements;
62 };
63
64 // Inventory shim for Doom.
65 #include "gi.h"
66
67 void SV_SendPlayerInfo(player_t &player);
68
DoClearInv(player_t * player)69 static void DoClearInv(player_t* player)
70 {
71 memset(player->weaponowned, 0, sizeof(player->weaponowned));
72 memset(player->powers, 0, sizeof(player->powers));
73 memset(player->cards, 0, sizeof(player->cards));
74 memset(player->ammo, 0, sizeof(player->ammo));
75
76 if (player->backpack)
77 {
78 player->backpack = false;
79 for (int i = 0; i < NUMAMMO; i++)
80 {
81 player->maxammo[i] /= 2;
82 }
83 }
84
85 player->pendingweapon = NUMWEAPONS;
86 }
87
ClearInventory(AActor * activator)88 static void ClearInventory(AActor* activator)
89 {
90 if (activator == NULL)
91 {
92 Players::iterator it;
93 for (it = players.begin();it != players.end();++it)
94 {
95 if (it->ingame() && !it->spectator)
96 DoClearInv(&(*it));
97 SV_SendPlayerInfo(*it);
98 }
99 }
100 else if (activator->player != NULL)
101 {
102 DoClearInv(activator->player);
103 SV_SendPlayerInfo(*(activator->player));
104 }
105 }
106
107 static const char* DoomAmmoNames[4] =
108 {
109 "Clip", "Shell", "Cell", "RocketAmmo"
110 };
111 static const char* DoomWeaponNames[9] =
112 {
113 "Fist", "Pistol", "Shotgun", "Chaingun", "RocketLauncher",
114 "PlasmaRifle", "BFG9000", "Chainsaw", "SuperShotgun"
115 };
116 static const char* DoomKeyNames[6] =
117 {
118 "BlueCard", "YellowCard", "RedCard",
119 "BlueSkull", "YellowSkull", "RedSkull"
120 };
121 static const char* DoomPowerNames[7] =
122 {
123 "InvulnerabilitySphere", "Berserk", "BlurSphere",
124 "RadSuit", "Allmap", "Infrared"
125 };
126
127 extern BOOL P_GiveAmmo(player_t *player, ammotype_t ammo, int num);
128 extern BOOL P_GiveWeapon(player_t *player, weapontype_t weapon, BOOL dropped);
129 extern void P_GiveCard(player_t *player, card_t card);
130 extern BOOL P_GivePower(player_t *player, int power);
131
GiveBackpack(player_t * player)132 static void GiveBackpack(player_t* player)
133 {
134 if (!player->backpack)
135 {
136 for (int i=0 ; i<NUMAMMO ; i++)
137 {
138 player->maxammo[i] *= 2;
139 }
140 player->backpack = true;
141 }
142 for (int i=0 ; i<NUMAMMO ; i++)
143 {
144 P_GiveAmmo(player, static_cast<ammotype_t>(i), 1);
145 }
146 SV_SendPlayerInfo(*player);
147 }
148
DoGiveInv(player_t * player,const char * type,int amount)149 static void DoGiveInv(player_t* player, const char* type, int amount)
150 {
151 weapontype_t savedpendingweap = player->pendingweapon;
152
153 // Give ammo
154 for (int i = 0; i < NUMAMMO; i++)
155 {
156 if (strcmp(DoomAmmoNames[i], type) == 0)
157 {
158 player->ammo[i] = MIN(player->ammo[i]+amount, player->maxammo[i]);
159 return;
160 }
161 }
162
163 // Give weapon
164 for (int i = 0; i < NUMWEAPONS; i++)
165 {
166 if (strcmp(DoomWeaponNames[i], type) == 0)
167 {
168 do
169 {
170 P_GiveWeapon(player, static_cast<weapontype_t>(i), false);
171 }
172 while (--amount > 0);
173
174 // Don't bring it up automatically
175 player->pendingweapon = savedpendingweap;
176 return;
177 }
178 }
179
180 // Give keycard
181 for (int i = 0; i < NUMCARDS; i++)
182 {
183 if (strcmp(DoomKeyNames[i], type) == 0)
184 {
185 do
186 {
187 P_GiveCard(player, static_cast<card_t>(i));
188 }
189 while (--amount > 0);
190 return;
191 }
192 }
193
194 // Give power
195 for (int i = 0; i < NUMPOWERS; i++)
196 {
197 if (strcmp(DoomPowerNames[i], type) == 0)
198 {
199 do
200 {
201 P_GivePower(player, i);
202 }
203 while (--amount > 0);
204 return;
205 }
206 }
207
208 // Give backpack
209 if (strcmp("Backpack", type) == 0)
210 {
211 do
212 {
213 GiveBackpack(player);
214 }
215 while (--amount > 0);
216 return;
217 }
218
219 // Unknown item.
220 Printf(PRINT_HIGH, "I don't know what %s is\n", type);
221 }
222
GiveInventory(AActor * activator,const char * type,int amount)223 static void GiveInventory(AActor* activator, const char* type, int amount)
224 {
225 if (amount <= 0)
226 {
227 }
228 if (activator == NULL)
229 {
230 for (int i = 0; i < MAXPLAYERS; ++i)
231 {
232 Players::iterator it;
233 for (it = players.begin();it != players.end();++it)
234 {
235 if (it->ingame() && !it->spectator) {
236 DoGiveInv(&(*it), type, amount);
237 SV_SendPlayerInfo(*it);
238 }
239 }
240 }
241 }
242 else if (activator->player != NULL)
243 {
244 DoGiveInv(activator->player, type, amount);
245 SV_SendPlayerInfo(*(activator->player));
246 }
247 }
248
249 extern void P_SwitchWeapon(player_t *player);
250
TakeWeapon(player_t * player,int weapon)251 static void TakeWeapon(player_t* player, int weapon)
252 {
253 player->weaponowned[weapon] = false;
254 if (player->readyweapon == weapon || player->pendingweapon == weapon)
255 {
256 P_SwitchWeapon(player);
257 }
258 SV_SendPlayerInfo(*player);
259 }
260
261 extern BOOL P_CheckAmmo (player_t *player);
262
TakeAmmo(player_t * player,int ammo,int amount)263 static void TakeAmmo(player_t* player, int ammo, int amount)
264 {
265 if (amount == 0)
266 {
267 player->ammo[ammo] = 0;
268 }
269 else
270 {
271 player->ammo[ammo] = MAX(player->ammo[ammo]-amount, 0);
272 }
273 if (player->pendingweapon != wp_nochange)
274 {
275 // Make sure we have the ammo for the weapon being switched to
276 weapontype_t readynow = player->readyweapon;
277 player->readyweapon = player->pendingweapon;
278 player->pendingweapon = wp_nochange;
279 if (P_CheckAmmo(player))
280 {
281 // There was enough ammo for the pending weapon, so keep switching
282 player->pendingweapon = player->readyweapon;
283 player->readyweapon = readynow;
284 }
285 else
286 {
287 player->pendingweapon = player->readyweapon = readynow;
288 P_CheckAmmo(player);
289 }
290 }
291 else
292 {
293 // Make sure we still have enough ammo for the current weapon
294 P_CheckAmmo(player);
295 }
296 SV_SendPlayerInfo(*player);
297 }
298
TakeBackpack(player_t * player)299 static void TakeBackpack(player_t* player)
300 {
301 if (!player->backpack)
302 return;
303
304 player->backpack = false;
305 for (int i = 0; i < NUMAMMO; ++i)
306 {
307 player->maxammo[i] /= 2;
308 if (player->ammo[i] > player->maxammo[i])
309 {
310 player->ammo[i] = player->maxammo[i];
311 }
312 }
313 SV_SendPlayerInfo(*player);
314 }
315
DoTakeInv(player_t * player,const char * type,int amount)316 static void DoTakeInv(player_t* player, const char* type, int amount)
317 {
318 int i;
319
320 for (i = 0; i < NUMAMMO; ++i)
321 {
322 if (strcmp(DoomAmmoNames[i], type) == 0)
323 {
324 TakeAmmo(player, i, amount);
325 return;
326 }
327 }
328 for (i = 0; i < NUMWEAPONS; ++i)
329 {
330 if (strcmp(DoomWeaponNames[i], type) == 0)
331 {
332 TakeWeapon(player, i);
333 return;
334 }
335 }
336 for (i = 0; i < NUMCARDS; ++i)
337 {
338 if (strcmp(DoomKeyNames[i], type) == 0)
339 {
340 player->cards[i] = 0;
341 }
342 }
343 if (strcmp("Backpack", type) == 0)
344 {
345 TakeBackpack(player);
346 }
347 }
348
TakeInventory(AActor * activator,const char * type,int amount)349 static void TakeInventory(AActor* activator, const char* type, int amount)
350 {
351 if (amount < 0)
352 {
353 }
354 if (activator == NULL)
355 {
356 Players::iterator it;
357 for (it = players.begin();it != players.end();++it)
358 {
359 if (it->ingame() && !it->spectator) {
360 DoTakeInv(&(*it), type, amount);
361 SV_SendPlayerInfo(*it);
362 }
363 }
364 }
365 else if (activator->player != NULL)
366 {
367 DoTakeInv(activator->player, type, amount);
368 SV_SendPlayerInfo(*(activator->player));
369 }
370 }
371
CheckInventory(AActor * activator,const char * type)372 static int CheckInventory(AActor* activator, const char* type)
373 {
374 if (activator == NULL || activator->player == NULL)
375 return 0;
376
377 player_t* player = activator->player;
378
379 for (int i = 0; i < NUMAMMO; ++i)
380 {
381 if (strcmp(DoomAmmoNames[i], type) == 0)
382 {
383 return player->ammo[i];
384 }
385 }
386 for (int i = 0; i < NUMWEAPONS; ++i)
387 {
388 if (strcmp(DoomWeaponNames[i], type) == 0)
389 {
390 return player->weaponowned[i] ? 1 : 0;
391 }
392 }
393 for (int i = 0; i < NUMCARDS; ++i)
394 {
395 if (strcmp(DoomKeyNames[i], type) == 0)
396 {
397 return player->cards[i] ? 1 : 0;
398 }
399 }
400 if (strcmp("Backpack", type) == 0)
401 {
402 return player->backpack ? 1 : 0;
403 }
404 return 0;
405 }
406
407 EXTERN_CVAR (sv_skill)
EXTERN_CVAR(sv_gametype)408 EXTERN_CVAR (sv_gametype)
409
410 //---- ACS lump manager ----//
411
412 FBehavior::FBehavior (BYTE *object, int len)
413 {
414 int i;
415
416 NumScripts = 0;
417 NumFunctions = 0;
418 NumArrays = 0;
419 Scripts = NULL;
420 Functions = NULL;
421 Arrays = NULL;
422 Chunks = NULL;
423
424 if (object[0] != 'A' || object[1] != 'C' || object[2] != 'S')
425 {
426 Format = ACS_Unknown;
427 return;
428 }
429
430 switch (object[3])
431 {
432 case 0:
433 Format = ACS_Old;
434 break;
435 case 'E':
436 Format = ACS_Enhanced;
437 break;
438 case 'e':
439 Format = ACS_LittleEnhanced;
440 break;
441 default:
442 Format = ACS_Unknown;
443 return;
444 }
445
446 Data = object;
447 DataSize = len;
448
449 if (Format == ACS_Old)
450 {
451 Chunks = object + len;
452 Scripts = object + ((DWORD *)object)[1];
453 NumScripts = ((DWORD *)Scripts)[0];
454 // Check for redesigned ACSE/ACSe
455 if (((DWORD *)object)[1] >= 6*4 &&
456 (((DWORD *)Scripts)[-1] == MAKE_ID('A','C','S','e') ||
457 ((DWORD *)Scripts)[-1] == MAKE_ID('A','C','S','E')))
458 {
459 Format = (((BYTE *)Scripts)[-1] == 'e') ? ACS_LittleEnhanced : ACS_Enhanced;
460 Chunks = object + ((DWORD *)Scripts)[-2];
461 // Forget about the compatibility cruft at the end of the lump
462 DataSize = ((DWORD *)object)[1] - 8;
463 }
464 else
465 {
466 Scripts += 4;
467 for (i = 0; i < NumScripts; ++i)
468 {
469 ScriptPtr2 ptr1 = *(ScriptPtr2 *)(Scripts + 12*i);
470 ScriptPtr *ptr2 = (ScriptPtr *)(Scripts + 8*i);
471 ptr2->Number = ptr1.Number % 1000;
472 ptr2->Type = ptr1.Number / 1000;
473 ptr2->ArgCount = ptr1.ArgCount;
474 ptr2->Address = ptr1.Address;
475 }
476 }
477 }
478 else
479 {
480 Chunks = object + ((DWORD *)object)[1];
481 }
482 if (Format != ACS_Old)
483 {
484 Scripts = FindChunk (MAKE_ID('S','P','T','R'));
485 if (object[3] != 0)
486 {
487 NumScripts = ((DWORD *)Scripts)[1] / 12;
488 Scripts += 8;
489 for (i = 0; i < NumScripts; ++i)
490 {
491 ScriptPtr1 ptr1 = *(ScriptPtr1 *)(Scripts + 12*i);
492 ScriptPtr *ptr2 = (ScriptPtr *)(Scripts + 8*i);
493 ptr2->Number = ptr1.Number;
494 ptr2->Type = ptr1.Type;
495 ptr2->ArgCount = ptr1.ArgCount;
496 ptr2->Address = ptr1.Address;
497 }
498 }
499 else
500 {
501 NumScripts = ((DWORD *)Scripts)[1] / 8;
502 Scripts += 8;
503 }
504 }
505
506 // Sort scripts, so we can use a binary search to find them
507 if (NumScripts > 0)
508 {
509 qsort (Scripts, NumScripts, 8, SortScripts);
510 }
511
512 if (Format == ACS_Old)
513 {
514 LanguageNeutral = ((DWORD *)Data)[1];
515 LanguageNeutral += ((DWORD *)(Data + LanguageNeutral))[0] * 12 + 4;
516 }
517 else
518 {
519 LanguageNeutral = FindLanguage (0, false);
520 PrepLocale (LanguageIDs[0], LanguageIDs[1], LanguageIDs[2], LanguageIDs[3]);
521 }
522
523 if (Format != ACS_Old)
524 {
525 DWORD *chunk;
526
527 Functions = FindChunk(MAKE_ID('F','U','N','C'));
528 if (Functions != NULL)
529 {
530 NumFunctions = LELONG(((DWORD *)Functions)[1]);
531 Functions += 8;
532 }
533
534 chunk = (DWORD *)FindChunk(MAKE_ID('M','I','N','I'));
535 if (chunk != NULL)
536 {
537 int numvars = LELONG(chunk[1])/4;
538 int firstvar = LELONG(chunk[2]);
539 for (i = 0; i < numvars; ++i)
540 {
541 level.vars[i+firstvar] = LELONG(chunk[3+i]);
542 }
543 }
544
545 chunk = (DWORD *)FindChunk(MAKE_ID('A','R','A','Y'));
546 if (chunk != NULL)
547 {
548 NumArrays = LELONG(chunk[1])/8;
549 Arrays = new ArrayInfo[NumArrays];
550 memset (Arrays, 0, sizeof(*Arrays)*NumArrays);
551 for (i = 0; i < NumArrays; ++i)
552 {
553 level.vars[LELONG(chunk[2+i*2])] = i;
554 Arrays[i].ArraySize = LELONG(chunk[3+i*2]);
555 Arrays[i].Elements = new SDWORD[Arrays[i].ArraySize];
556 memset(Arrays[i].Elements, 0, Arrays[i].ArraySize*sizeof(DWORD));
557 }
558 }
559
560 chunk = (DWORD *)FindChunk(MAKE_ID('A','I','N','I'));
561 while (chunk != NULL)
562 {
563 int arraynum = level.vars[LELONG(chunk[2])];
564 if ((unsigned)arraynum < (unsigned)NumArrays)
565 {
566 int initsize = MIN<int> (Arrays[arraynum].ArraySize, (LELONG(chunk[1])-4)/4);
567 SDWORD *elems = Arrays[arraynum].Elements;
568 for (i = 0; i < initsize; ++i)
569 {
570 elems[i] = LELONG(chunk[3+i]);
571 }
572 }
573 chunk = (DWORD *)NextChunk((BYTE *)chunk);
574 }
575 }
576
577 DPrintf ("Loaded %d scripts, %d Functions\n", NumScripts, NumFunctions);
578 }
579
~FBehavior()580 FBehavior::~FBehavior ()
581 {
582 // Object file is freed by the zone heap
583 if(Arrays != NULL)
584 {
585 for (int i = 0; i < NumArrays; ++i)
586 {
587 if (Arrays[i].Elements != NULL)
588 {
589 delete[] Arrays[i].Elements;
590 Arrays[i].Elements = NULL;
591 }
592 }
593 delete[] Arrays;
594 Arrays = NULL;
595 }
596 }
597
SortScripts(const void * a,const void * b)598 int STACK_ARGS FBehavior::SortScripts (const void *a, const void *b)
599 {
600 ScriptPtr *ptr1 = (ScriptPtr *)a;
601 ScriptPtr *ptr2 = (ScriptPtr *)b;
602 return ptr1->Number - ptr2->Number;
603 }
604
IsGood()605 bool FBehavior::IsGood ()
606 {
607 return Format != ACS_Unknown;
608 }
609
FindScript(int script) const610 int *FBehavior::FindScript (int script) const
611 {
612 const ScriptPtr *ptr = BinarySearch<ScriptPtr, WORD>
613 ((ScriptPtr *)Scripts, NumScripts, &ScriptPtr::Number, (WORD)script);
614
615 return ptr ? (int *)(ptr->Address + Data) : NULL;
616 }
617
GetFunction(int funcnum) const618 ScriptFunction *FBehavior::GetFunction (int funcnum) const
619 {
620 if ((unsigned)funcnum >= (unsigned)NumFunctions)
621 {
622 return NULL;
623 }
624 return (ScriptFunction *)Functions + funcnum;
625 }
626
GetArrayVal(int arraynum,int index) const627 int FBehavior::GetArrayVal (int arraynum, int index) const
628 {
629 if ((unsigned)arraynum >= (unsigned)NumArrays)
630 return 0;
631 const ArrayInfo *array = &Arrays[arraynum];
632 if ((unsigned)index >= (unsigned)array->ArraySize)
633 return 0;
634 return array->Elements[index];
635 }
636
SetArrayVal(int arraynum,int index,int value)637 void FBehavior::SetArrayVal (int arraynum, int index, int value)
638 {
639 if ((unsigned)arraynum >= (unsigned)NumArrays)
640 return;
641 const ArrayInfo *array = &Arrays[arraynum];
642 if ((unsigned)index >= (unsigned)array->ArraySize)
643 return;
644 array->Elements[index] = value;
645 }
646
FindChunk(DWORD id) const647 BYTE *FBehavior::FindChunk (DWORD id) const
648 {
649 BYTE *chunk = Chunks;
650
651 while (chunk != NULL && chunk < Data + DataSize)
652 {
653 if (((DWORD *)chunk)[0] == id)
654 {
655 return chunk;
656 }
657 chunk += ((DWORD *)chunk)[1] + 8;
658 }
659 return NULL;
660 }
661
NextChunk(BYTE * chunk) const662 BYTE *FBehavior::NextChunk (BYTE *chunk) const
663 {
664 DWORD id = *(DWORD *)chunk;
665 chunk += ((DWORD *)chunk)[1] + 8;
666 while (chunk != NULL && chunk < Data + DataSize)
667 {
668 if (((DWORD *)chunk)[0] == id)
669 {
670 return chunk;
671 }
672 chunk += ((DWORD *)chunk)[1] + 8;
673 }
674 return NULL;
675 }
676
LookupString(DWORD index,DWORD ofs) const677 const char *FBehavior::LookupString (DWORD index, DWORD ofs) const
678 {
679 if (Format == ACS_Old)
680 {
681 DWORD *list = (DWORD *)(Data + LanguageNeutral);
682
683 if (index >= list[0])
684 return NULL; // Out of range for this list;
685 return (const char *)(Data + list[1+index]);
686 }
687 else
688 {
689 if (ofs == 0)
690 {
691 ofs = LanguageNeutral;
692 if (ofs == 0)
693 {
694 return NULL;
695 }
696 }
697 DWORD *list = (DWORD *)(Data + ofs);
698
699 if (index >= list[1])
700 return NULL; // Out of range for this list
701 if (list[3+index] == 0)
702 return NULL; // Not defined in this list
703 return (const char *)(Data + ofs + list[3+index]);
704 }
705 }
706
LocalizeString(DWORD index) const707 const char *FBehavior::LocalizeString (DWORD index) const
708 {
709 if (Format != ACS_Old)
710 {
711 DWORD ofs = Localized;
712 const char *str = NULL;
713
714 while (ofs != 0 && (str = LookupString (index, ofs)) == NULL)
715 {
716 ofs = ((DWORD *)(Data + ofs))[2];
717 }
718 return str;
719 }
720 else
721 {
722 return LookupString (index);
723 }
724 }
725
PrepLocale(DWORD userpref,DWORD userdef,DWORD syspref,DWORD sysdef)726 void FBehavior::PrepLocale (DWORD userpref, DWORD userdef, DWORD syspref, DWORD sysdef)
727 {
728 BYTE *chunk;
729 DWORD *list;
730
731 // Clear away any existing links
732 for (chunk = Chunks; chunk < Data + DataSize; chunk += ((DWORD *)chunk)[1] + 8)
733 {
734 list = (DWORD *)chunk;
735 if (list[0] == MAKE_ID('S','T','R','L'))
736 {
737 list[4] = 0;
738 }
739 }
740 Localized = 0;
741
742 if (userpref)
743 AddLanguage (userpref);
744 if (userpref & LANGREGIONMASK)
745 AddLanguage (userpref & ~LANGREGIONMASK);
746 if (userdef)
747 AddLanguage (userdef);
748 if (userdef & LANGREGIONMASK)
749 AddLanguage (userdef & ~LANGREGIONMASK);
750 if (syspref)
751 AddLanguage (syspref);
752 if (syspref & LANGREGIONMASK)
753 AddLanguage (syspref & ~LANGREGIONMASK);
754 if (sysdef)
755 AddLanguage (sysdef);
756 if (sysdef & LANGREGIONMASK)
757 AddLanguage (sysdef & ~LANGREGIONMASK);
758 AddLanguage (MAKE_ID('e','n',0,0)); // Use English as a fallback
759 AddLanguage (0); // Failing that, use language independent strings
760 }
761
AddLanguage(DWORD langid)762 void FBehavior::AddLanguage (DWORD langid)
763 {
764 DWORD ofs, *ofsput;
765 DWORD *list;
766 BYTE *chunk;
767
768 // First, make sure language is not already inserted
769 ofsput = CheckIfInList (langid);
770 if (ofsput == NULL)
771 { // Already in list
772 return;
773 }
774
775 // Try to find an exact match first
776 ofs = FindLanguage (langid, false);
777 if (ofs != 0)
778 {
779 *ofsput = ofs;
780 return;
781 }
782
783 // If langid has no sublanguage, add all languages that match the major
784 // type, if not in list already
785 if ((langid & LANGREGIONMASK) == 0)
786 {
787 for (chunk = Chunks; chunk < Data + DataSize; chunk += ((DWORD *)chunk)[1] + 8)
788 {
789 list = (DWORD *)chunk;
790 if (list[0] != MAKE_ID('S','T','R','L'))
791 continue; // not a string list
792 if ((list[2] & ~LANGREGIONMASK) != langid)
793 continue; // wrong language
794 if (list[4] != 0)
795 continue; // definitely in language list
796 ofsput = CheckIfInList (list[2]);
797 if (ofsput != NULL)
798 *ofsput = chunk - Data + 8; // add to language list
799 }
800 }
801 }
802
CheckIfInList(DWORD langid)803 DWORD *FBehavior::CheckIfInList (DWORD langid)
804 {
805 DWORD ofs, *ofsput;
806 DWORD *list;
807
808 ofs = Localized;
809 ofsput = &Localized;
810 while (ofs != 0)
811 {
812 list = (DWORD *)(Data + ofs);
813 if (list[0] == langid)
814 return NULL;
815 ofsput = &list[2];
816 ofs = list[2];
817 }
818 return ofsput;
819 }
820
FindLanguage(DWORD langid,bool ignoreregion) const821 DWORD FBehavior::FindLanguage (DWORD langid, bool ignoreregion) const
822 {
823 BYTE *chunk;
824 DWORD *list;
825 DWORD langmask;
826
827 langmask = ignoreregion ? ~LANGREGIONMASK : ~0;
828
829 for (chunk = Chunks; chunk < Data + DataSize; chunk += ((DWORD *)chunk)[1] + 8)
830 {
831 list = (DWORD *)chunk;
832 if (list[0] == MAKE_ID('S','T','R','L') && (list[2] & langmask) == langid)
833 {
834 return chunk - Data + 8;
835 }
836 }
837 return 0;
838 }
839
StartTypedScripts(WORD type,AActor * activator) const840 void FBehavior::StartTypedScripts (WORD type, AActor *activator) const
841 {
842 ScriptPtr *ptr;
843 int i;
844
845 for (i = 0; i < NumScripts; ++i)
846 {
847 ptr = (ScriptPtr *)(Scripts + 8*i);
848 if (ptr->Type == type)
849 {
850 P_GetScriptGoing (activator, NULL, ptr->Number,
851 (int *)(ptr->Address + Data), 0, 0, 0, 0, 0, true);
852 }
853 }
854 }
855
856 //---- The ACS Interpreter ----//
857
858
859
860 #define NEXTWORD (LELONG(*pc++))
861 #define NEXTBYTE (fmt==ACS_LittleEnhanced?getbyte(pc):NEXTWORD)
862 #define STACK(a) (Stack[sp - (a)])
863 #define PushToStack(a) (Stack[sp++] = (a))
864
865 void strbin (char *str);
866
867 IMPLEMENT_SERIAL (DACSThinker, DThinker)
868
869 DACSThinker *DACSThinker::ActiveThinker = NULL;
870
DACSThinker()871 DACSThinker::DACSThinker ()
872 {
873 if (ActiveThinker)
874 {
875 I_Error ("Only one ACSThinker is allowed to exist at a time.\nCheck your code.");
876 }
877 else
878 {
879 ActiveThinker = this;
880 Scripts = NULL;
881 LastScript = NULL;
882 for (int i = 0; i < 1000; i++)
883 RunningScripts[i] = NULL;
884 }
885 }
886
~DACSThinker()887 DACSThinker::~DACSThinker ()
888 {
889 DLevelScript *script = Scripts;
890 while (script)
891 {
892 DLevelScript *next = script->next;
893 script->Destroy ();
894 script = next;
895 }
896 Scripts = NULL;
897 ActiveThinker = NULL;
898 }
899
Serialize(FArchive & arc)900 void DACSThinker::Serialize (FArchive &arc)
901 {
902 if (arc.IsStoring ())
903 {
904 arc << Scripts << LastScript;
905 for (int i = 0; i < 1000; i++)
906 {
907 if (RunningScripts[i])
908 arc << RunningScripts[i] << (WORD)i;
909 }
910 arc << (DLevelScript *)NULL;
911 }
912 else
913 {
914 arc >> Scripts >> LastScript;
915
916 WORD scriptnum;
917 DLevelScript *script;
918 arc >> script;
919 while (script)
920 {
921 arc >> scriptnum;
922 RunningScripts[scriptnum] = script;
923 arc >> script;
924 }
925 }
926 }
927
RunThink()928 void DACSThinker::RunThink ()
929 {
930 DLevelScript *script = Scripts;
931
932 while (script)
933 {
934 DLevelScript *next = script->next;
935 script->RunScript ();
936 script = next;
937 }
938 }
939
940 // FlashFader class - not sure where to put this so it goes here for now...
941 class DFlashFader : public DThinker
942 {
943 DECLARE_SERIAL (DFlashFader, DThinker)
944 public:
945 DFlashFader (float r1, float g1, float b1, float a1,
946 float r2, float g2, float b2, float a2,
947 float time, AActor *who);
948 ~DFlashFader ();
949 virtual void RunThink ();
950 virtual void DestroyedPointer(DObject *obj);
WhoFor()951 AActor *WhoFor() { return ForWho; }
952 void Cancel ();
953
954 protected:
955 float Blends[2][4];
956 int TotalTics;
957 int StartTic;
958 AActor *ForWho;
959
960 void SetBlend (float time);
961 DFlashFader ();
962 };
963
IMPLEMENT_SERIAL(DFlashFader,DThinker)964 IMPLEMENT_SERIAL(DFlashFader, DThinker)
965
966 DFlashFader::DFlashFader ()
967 {
968 }
969
DestroyedPointer(DObject * obj)970 void DFlashFader::DestroyedPointer(DObject *obj)
971 {
972 if(obj == ForWho)
973 ForWho = NULL;
974 }
975
DFlashFader(float r1,float g1,float b1,float a1,float r2,float g2,float b2,float a2,float time,AActor * who)976 DFlashFader::DFlashFader (float r1, float g1, float b1, float a1,
977 float r2, float g2, float b2, float a2,
978 float time, AActor *who)
979 : TotalTics ((int)(time*TICRATE)), StartTic (level.time), ForWho (who)
980 {
981 Blends[0][0]=r1; Blends[0][1]=g1; Blends[0][2]=b1; Blends[0][3]=a1;
982 Blends[1][0]=r2; Blends[1][1]=g2; Blends[1][2]=b2; Blends[1][3]=a2;
983 }
984
~DFlashFader()985 DFlashFader::~DFlashFader ()
986 {
987 SetBlend (1.f);
988 }
989
Serialize(FArchive & arc)990 void DFlashFader::Serialize (FArchive &arc)
991 {
992 Super::Serialize (arc);
993
994 if (arc.IsStoring ())
995 {
996 arc << TotalTics << StartTic << ForWho;
997
998 for (int i = 1; i >= 0; --i)
999 for (int j = 3; j >= 0; --j)
1000 arc << Blends[i][j];
1001 }
1002 else
1003 {
1004 arc >> TotalTics >> StartTic >> ForWho;
1005
1006 for (int i = 1; i >= 0; --i)
1007 for (int j = 3; j >= 0; --j)
1008 arc >> Blends[i][j];
1009 }
1010 }
1011
RunThink()1012 void DFlashFader::RunThink ()
1013 {
1014 if (ForWho == NULL || ForWho->player == NULL)
1015 {
1016 Destroy ();
1017 return;
1018 }
1019 if (level.time >= StartTic+TotalTics)
1020 {
1021 SetBlend (1.f);
1022 Destroy ();
1023 return;
1024 }
1025 SetBlend ((float)(level.time - StartTic) / (float)TotalTics);
1026 }
1027
SetBlend(float time)1028 void DFlashFader::SetBlend (float time)
1029 {
1030 if (ForWho == NULL || ForWho->player == NULL)
1031 {
1032 return;
1033 }
1034 player_t *player = ForWho->player;
1035 float iT = 1.f - time;
1036 player->BlendR = Blends[0][0]*iT + Blends[1][0]*time;
1037 player->BlendG = Blends[0][1]*iT + Blends[1][1]*time;
1038 player->BlendB = Blends[0][2]*iT + Blends[1][2]*time;
1039 player->BlendA = Blends[0][3]*iT + Blends[1][3]*time;
1040 }
1041
Cancel()1042 void DFlashFader::Cancel ()
1043 {
1044 TotalTics = level.time - StartTic;
1045 Blends[1][3] = 0.f;
1046 }
1047
1048 //---- Plane watchers ----//
1049
1050 class DPlaneWatcher : public DThinker
1051 {
1052 DECLARE_SERIAL (DPlaneWatcher, DThinker)
1053 public:
1054 DPlaneWatcher (AActor *it, line_t *line, int lineSide, bool ceiling,
1055 int tag, int height, int special,
1056 int arg0, int arg1, int arg2, int arg3, int arg4);
1057 virtual void RunThink ();
1058 virtual void DestroyedPointer(DObject *obj);
1059 private:
1060 sector_t *Sector;
1061 fixed_t WatchD, LastD;
1062 int Special, Arg0, Arg1, Arg2, Arg3, Arg4;
1063 AActor *Activator;
1064 line_t *Line;
1065 int LineSide;
1066 bool bCeiling;
1067
DPlaneWatcher()1068 DPlaneWatcher() {}
1069 };
1070
IMPLEMENT_SERIAL(DPlaneWatcher,DThinker)1071 IMPLEMENT_SERIAL(DPlaneWatcher, DThinker)
1072
1073 void DPlaneWatcher::DestroyedPointer(DObject *obj)
1074 {
1075 if(obj == Activator)
1076 Activator = NULL;
1077 }
1078
DPlaneWatcher(AActor * it,line_t * line,int lineSide,bool ceiling,int tag,int height,int special,int arg0,int arg1,int arg2,int arg3,int arg4)1079 DPlaneWatcher::DPlaneWatcher (AActor *it, line_t *line, int lineSide, bool ceiling,
1080 int tag, int height, int special,
1081 int arg0, int arg1, int arg2, int arg3, int arg4)
1082 : Special (special), Arg0 (arg0), Arg1 (arg1), Arg2 (arg2), Arg3 (arg3), Arg4 (arg4),
1083 Activator (it), Line (line), LineSide (lineSide), bCeiling (ceiling)
1084 {
1085 int secnum;
1086
1087 secnum = P_FindSectorFromTag (tag, -1);
1088 if (secnum >= 0)
1089 {
1090 Sector = §ors[secnum];
1091 if (bCeiling)
1092 {
1093 LastD = Sector->ceilingplane.d;
1094 P_ChangeCeilingHeight(Sector, height << FRACBITS);
1095 WatchD = Sector->ceilingplane.d;
1096 }
1097 else
1098 {
1099 LastD = Sector->floorplane.d;
1100 P_ChangeFloorHeight(Sector, height << FRACBITS);
1101 WatchD = Sector->floorplane.d;
1102 }
1103 }
1104 else
1105 {
1106 Sector = NULL;
1107 WatchD = LastD = 0;
1108 }
1109 }
1110
Serialize(FArchive & arc)1111 void DPlaneWatcher::Serialize (FArchive &arc)
1112 {
1113 Super::Serialize (arc);
1114
1115 if (arc.IsStoring ())
1116 {
1117 arc << Special << Arg0 << Arg1 << Arg2 << Arg3 << Arg4
1118 << Sector << bCeiling << WatchD << LastD << Activator
1119 << Line << LineSide << bCeiling;
1120 }
1121 else
1122 {
1123 arc >> Special >> Arg0 >> Arg1 >> Arg2 >> Arg3 >> Arg4
1124 >> Sector >> bCeiling >> WatchD >> LastD >> Activator
1125 >> Line >> LineSide >> bCeiling;
1126 }
1127 }
1128
RunThink()1129 void DPlaneWatcher::RunThink ()
1130 {
1131 if (Sector == NULL)
1132 {
1133 Destroy ();
1134 return;
1135 }
1136
1137 fixed_t newd;
1138
1139 if (bCeiling)
1140 {
1141 newd = Sector->ceilingplane.d;
1142 }
1143 else
1144 {
1145 newd = Sector->floorplane.d;
1146 }
1147
1148 if ((LastD < WatchD && newd >= WatchD) ||
1149 (LastD > WatchD && newd <= WatchD))
1150 {
1151 TeleportSide = LineSide;
1152 LineSpecials[Special] (Line, Activator, Arg0, Arg1, Arg2, Arg3, Arg4);
1153 Destroy ();
1154 }
1155 }
1156
1157
IMPLEMENT_SERIAL(DLevelScript,DObject)1158 IMPLEMENT_SERIAL (DLevelScript, DObject)
1159
1160 void *DLevelScript::operator new (size_t size)
1161 {
1162 return Z_Malloc (sizeof(DLevelScript), PU_LEVACS, 0);
1163 }
1164
operator delete(void * block)1165 void DLevelScript::operator delete (void *block)
1166 {
1167 Z_Free (block);
1168 }
1169
Serialize(FArchive & arc)1170 void DLevelScript::Serialize (FArchive &arc)
1171 {
1172 DWORD i;
1173
1174 Super::Serialize (arc);
1175
1176 if (arc.IsStoring ())
1177 {
1178 arc << next << prev
1179 << script
1180 << sp
1181 << state
1182 << statedata
1183 << activator
1184 << activationline
1185 << lineSide;
1186
1187 for (i = 0; i < LOCAL_SIZE; i++)
1188 arc << localvars[i];
1189
1190 i = level.behavior->PC2Ofs(pc);
1191 arc << i;
1192 }
1193 else
1194 {
1195 arc >> next >> prev
1196 >> script
1197 >> sp
1198 >> state
1199 >> statedata
1200 >> activator
1201 >> activationline
1202 >> lineSide;
1203
1204 for (i = 0; i < LOCAL_SIZE; i++)
1205 arc >> localvars[i];
1206
1207 arc >> i;
1208 pc = level.behavior->Ofs2PC (i);
1209 }
1210 }
1211
DLevelScript()1212 DLevelScript::DLevelScript ()
1213 {
1214 next = prev = NULL;
1215 if (DACSThinker::ActiveThinker == NULL)
1216 new DACSThinker;
1217 }
1218
1219
Unlink()1220 void DLevelScript::Unlink ()
1221 {
1222 DACSThinker *controller = DACSThinker::ActiveThinker;
1223 if (!controller)
1224 return;
1225
1226 if (controller->LastScript == this)
1227 controller->LastScript = prev;
1228 if (controller->Scripts == this)
1229 controller->Scripts = next;
1230 if (prev)
1231 prev->next = next;
1232 if (next)
1233 next->prev = prev;
1234 }
1235
Link()1236 void DLevelScript::Link ()
1237 {
1238 DACSThinker *controller = DACSThinker::ActiveThinker;
1239 if (!controller)
1240 return;
1241
1242 next = controller->Scripts;
1243 if (controller->Scripts)
1244 controller->Scripts->prev = this;
1245 prev = NULL;
1246 controller->Scripts = this;
1247 if (controller->LastScript == NULL)
1248 controller->LastScript = this;
1249 }
1250
PutLast()1251 void DLevelScript::PutLast ()
1252 {
1253 DACSThinker *controller = DACSThinker::ActiveThinker;
1254 if (!controller)
1255 return;
1256
1257 if (controller->LastScript == this)
1258 return;
1259
1260 Unlink ();
1261 if (controller->Scripts == NULL)
1262 {
1263 Link ();
1264 }
1265 else
1266 {
1267 if (controller->LastScript)
1268 controller->LastScript->next = this;
1269 prev = controller->LastScript;
1270 next = NULL;
1271 controller->LastScript = this;
1272 }
1273 }
1274
PutFirst()1275 void DLevelScript::PutFirst ()
1276 {
1277 DACSThinker *controller = DACSThinker::ActiveThinker;
1278 if (!controller)
1279 return;
1280
1281 if (controller->Scripts == this)
1282 return;
1283
1284 Unlink ();
1285 Link ();
1286 }
1287
Random(int min,int max)1288 int DLevelScript::Random (int min, int max)
1289 {
1290 int num1, num2, num3, num4;
1291 unsigned int num;
1292
1293 num1 = P_Random ();
1294 num2 = P_Random ();
1295 num3 = P_Random ();
1296 num4 = P_Random ();
1297
1298 num = ((num1 << 24) | (num2 << 16) | (num3 << 8) | num4);
1299 num %= (max - min + 1);
1300 num += min;
1301 return (int)num;
1302 }
1303
ThingCount(int type,int tid)1304 int DLevelScript::ThingCount (int type, int tid)
1305 {
1306 AActor *mobj = NULL;
1307 int count = 0;
1308
1309 if (type >= NumSpawnableThings)
1310 {
1311 return 0;
1312 }
1313 else if (type > 0)
1314 {
1315 type = SpawnableThings[type];
1316 if (type == 0)
1317 return 0;
1318 }
1319
1320 if (tid)
1321 {
1322 mobj = AActor::FindByTID (NULL, tid);
1323 while (mobj)
1324 {
1325 if ((type == 0) || (mobj->type == type && mobj->health > 0))
1326 count++;
1327 mobj = mobj->FindByTID (tid);
1328 }
1329 }
1330 else
1331 {
1332 AActor *actor;
1333 TThinkerIterator<AActor> iterator;
1334
1335 while ( (actor = iterator.Next ()) )
1336 {
1337 if (type == 0)
1338 {
1339 count++;
1340 }
1341 else if (actor->type == type && actor->health > 0)
1342 {
1343 count++;
1344 }
1345 }
1346 }
1347 return count;
1348 }
1349
ChangeFlat(int tag,int name,bool floorOrCeiling)1350 void DLevelScript::ChangeFlat (int tag, int name, bool floorOrCeiling)
1351 {
1352 int flat, secnum = -1;
1353 const char *flatname = level.behavior->LookupString (name);
1354
1355 if (flatname == NULL)
1356 return;
1357
1358 flat = R_FlatNumForName (flatname);
1359
1360 while ((secnum = P_FindSectorFromTag (tag, secnum)) >= 0)
1361 {
1362 if (floorOrCeiling == false)
1363 sectors[secnum].floorpic = flat;
1364 else
1365 sectors[secnum].ceilingpic = flat;
1366 }
1367 }
1368
1369 extern size_t P_NumPlayersInGame();
1370
CountPlayers()1371 int DLevelScript::CountPlayers()
1372 {
1373 return static_cast<int>(P_NumPlayersInGame());
1374 }
1375
SetLineTexture(int lineid,int side,int position,int name)1376 void DLevelScript::SetLineTexture (int lineid, int side, int position, int name)
1377 {
1378 int texture, linenum = -1;
1379 const char *texname = level.behavior->LookupString (name);
1380
1381 if (texname == NULL)
1382 return;
1383
1384 side = (side) ? 1 : 0;
1385
1386 texture = R_TextureNumForName (texname);
1387
1388 while ((linenum = P_FindLineFromID (lineid, linenum)) >= 0) {
1389 side_t *sidedef;
1390
1391 if (lines[linenum].sidenum[side] == R_NOSIDE)
1392 continue;
1393 sidedef = sides + lines[linenum].sidenum[side];
1394
1395 switch (position) {
1396 case TEXTURE_TOP:
1397 sidedef->toptexture = texture;
1398 break;
1399 case TEXTURE_MIDDLE:
1400 sidedef->midtexture = texture;
1401 break;
1402 case TEXTURE_BOTTOM:
1403 sidedef->bottomtexture = texture;
1404 break;
1405 default:
1406 break;
1407 }
1408
1409 }
1410 }
1411
1412 /*int DLevelScript::DoSpawn(int type, fixed_t x, fixed_t y, fixed_t z, int tid, int angle)
1413 {
1414 const char* typestr = level.behavior->LookupString(type);
1415 if (typestr == NULL)
1416 return 0;
1417 char name[64];
1418 name[0] = 'A';
1419 name[63] = 0;
1420 strncpy(name+1, typestr, 62);
1421
1422 const TypeInfo* info = TypeInfo::FindType(name);
1423 AActor* actor = NULL;
1424
1425 if (info != NULL)
1426 {
1427 actor = Spawn(info, x, y, z);
1428 if (actor != NULL)
1429 {
1430 if (P_TestMobjLocation(actor))
1431 {
1432 actor->angle = angle << 24;
1433 actor->tid = tid;
1434 actor->AddToHash();
1435 actor->flags |= MF_DROPPED; // Don't respawn
1436 }
1437 else
1438 {
1439 actor->Destroy();
1440 actor = NULL;
1441 }
1442 }
1443 }
1444 return (int)actor;
1445 }
1446
1447 int DLevelScript::DoSpawnSpot(int type, int spot, int tid, int angle)
1448 {
1449 FActorIterator iterator(tid);
1450 AActor* aspot;
1451 int spawned = 0;
1452
1453 while ((aspot = iterator.Next()))
1454 {
1455 spawned = DoSpawn(type, aspot->x, aspot->y, aspot->z, tid, angle);
1456 }
1457 return spawned;
1458 }*/
1459
DoFadeTo(int r,int g,int b,int a,fixed_t time)1460 void DLevelScript::DoFadeTo (int r, int g, int b, int a, fixed_t time)
1461 {
1462 Printf(PRINT_HIGH,"DoFadeRange now... \n");
1463 DoFadeRange (0, 0, 0, -1, r, g, b, a, time);
1464 }
1465
DoActualFadeRange(player_s * viewer,float ftime,bool fadingFrom,float fr1,float fg1,float fb1,float fa1,float fr2,float fg2,float fb2,float fa2)1466 static void DoActualFadeRange(player_s* viewer, float ftime, bool fadingFrom,
1467 float fr1, float fg1, float fb1, float fa1,
1468 float fr2, float fg2, float fb2, float fa2)
1469 {
1470 if (ftime <= 0.f)
1471 {
1472 viewer->BlendR = fr2;
1473 viewer->BlendG = fg2;
1474 viewer->BlendB = fb2;
1475 viewer->BlendA = fa2;
1476 }
1477 else
1478 {
1479 if (!fadingFrom)
1480 {
1481 if (viewer->BlendA <= 0.f)
1482 {
1483 fr1 = fr2;
1484 fg1 = fg2;
1485 fb1 = fb2;
1486 fa1 = 0.f;
1487 }
1488 else
1489 {
1490 fr1 = viewer->BlendR;
1491 fg1 = viewer->BlendG;
1492 fb1 = viewer->BlendB;
1493 fa1 = viewer->BlendA;
1494 }
1495 }
1496 new DFlashFader (fr1, fg1, fb1, fa1, fr2, fg2, fb2, fa2, ftime, viewer->mo);
1497 }
1498 }
1499
DoFadeRange(int r1,int g1,int b1,int a1,int r2,int g2,int b2,int a2,fixed_t time)1500 void DLevelScript::DoFadeRange(int r1, int g1, int b1, int a1,
1501 int r2, int g2, int b2, int a2, fixed_t time)
1502 {
1503 player_t *viewer;
1504 float ftime = (float)time / 65536.f;
1505 bool fadingFrom = a1 >= 0;
1506 float fr1 = 0.f, fg1 = 0.f, fb1 = 0.f, fa1 = 0.f;
1507 float fr2, fg2, fb2, fa2;
1508
1509 fr2 = (float)r2 / 255.f;
1510 fg2 = (float)g2 / 255.f;
1511 fb2 = (float)b2 / 255.f;
1512 fa2 = (float)a2 / 65536.f;
1513
1514 if (fadingFrom)
1515 {
1516 fr1 = (float)r1 / 255.f;
1517 fg1 = (float)g1 / 255.f;
1518 fb1 = (float)b1 / 255.f;
1519 fa1 = (float)a1 / 65536.f;
1520 }
1521
1522 if (activator != NULL)
1523 {
1524 viewer = activator->player;
1525 if (viewer == NULL)
1526 return;
1527 DoActualFadeRange(viewer, ftime, fadingFrom, fr1, fg1, fb1, fa1, fr2, fg2, fb2, fa2);
1528 }
1529 else
1530 {
1531 for (Players::iterator it = players.begin();it != players.end();++it)
1532 {
1533 if (it->ingame())
1534 DoActualFadeRange(&*it, ftime, fadingFrom, fr1, fg1, fb1, fa1, fr2, fg2, fb2, fa2);
1535 }
1536 }
1537 }
1538
1539
getbyte(int * & pc)1540 inline int getbyte (int *&pc)
1541 {
1542 int res = *(BYTE *)pc;
1543 pc = (int *)((BYTE *)pc+1);
1544 return res;
1545 }
1546
RunScript()1547 void DLevelScript::RunScript ()
1548 {
1549 DACSThinker *controller = DACSThinker::ActiveThinker;
1550 if (!controller)
1551 return;
1552
1553 TeleportSide = lineSide;
1554 int *locals = localvars;
1555 ScriptFunction *activeFunction = NULL;
1556
1557 switch (state)
1558 {
1559 case SCRIPT_Delayed:
1560 // Decrement the delay counter and enter state running
1561 // if it hits 0
1562 if (--statedata == 0)
1563 state = SCRIPT_Running;
1564 break;
1565
1566 case SCRIPT_TagWait:
1567 // Wait for tagged sector(s) to go inactive, then enter
1568 // state running
1569 {
1570 int secnum = -1;
1571
1572 while ((secnum = P_FindSectorFromTag (statedata, secnum)) >= 0)
1573 if (sectors[secnum].floordata || sectors[secnum].ceilingdata)
1574 return;
1575
1576 // If we got here, none of the tagged sectors were busy
1577 state = SCRIPT_Running;
1578 }
1579 break;
1580
1581 case SCRIPT_PolyWait:
1582 // Wait for polyobj(s) to stop moving, then enter state running
1583 if (!PO_Busy (statedata))
1584 {
1585 state = SCRIPT_Running;
1586 }
1587 break;
1588
1589 case SCRIPT_ScriptWaitPre:
1590 // Wait for a script to start running, then enter state scriptwait
1591 if (controller->RunningScripts[statedata])
1592 state = SCRIPT_ScriptWait;
1593 break;
1594
1595 case SCRIPT_ScriptWait:
1596 // Wait for a script to stop running, then enter state running
1597 if (controller->RunningScripts[statedata])
1598 return;
1599
1600 state = SCRIPT_Running;
1601 PutFirst ();
1602 break;
1603
1604 default:
1605 break;
1606 }
1607
1608 int *pc = this->pc;
1609 int sp = this->sp;
1610 const ACSFormat fmt = level.behavior->GetFormat();
1611 int runaway = 0; // used to prevent infinite loops
1612 int pcd;
1613 char work[4096], *workwhere = work;
1614 const char *lookup;
1615 int optstart = -1;
1616 int temp;
1617
1618 while (state == SCRIPT_Running)
1619 {
1620 if (++runaway > 500000)
1621 {
1622 Printf (PRINT_HIGH,"Runaway script %d terminated\n", script);
1623 state = SCRIPT_PleaseRemove;
1624 break;
1625 }
1626
1627 pcd = NEXTBYTE;
1628 switch (pcd)
1629 {
1630 default:
1631 Printf (PRINT_HIGH,"Unknown P-Code %d in script %d\n", pcd, script);
1632 // fall through
1633 case PCD_TERMINATE:
1634 state = SCRIPT_PleaseRemove;
1635 break;
1636
1637 case PCD_NOP:
1638 break;
1639
1640 case PCD_SUSPEND:
1641 state = SCRIPT_Suspended;
1642 break;
1643
1644 case PCD_PUSHNUMBER:
1645 PushToStack (NEXTWORD);
1646 break;
1647
1648 case PCD_PUSHBYTE:
1649 PushToStack (*(BYTE *)pc);
1650 pc = (int *)((BYTE *)pc + 1);
1651 break;
1652
1653 case PCD_PUSH2BYTES:
1654 Stack[sp] = ((BYTE *)pc)[0];
1655 Stack[sp+1] = ((BYTE *)pc)[1];
1656 sp += 2;
1657 pc = (int *)((BYTE *)pc + 2);
1658 break;
1659
1660 case PCD_PUSH3BYTES:
1661 Stack[sp] = ((BYTE *)pc)[0];
1662 Stack[sp+1] = ((BYTE *)pc)[1];
1663 Stack[sp+2] = ((BYTE *)pc)[2];
1664 sp += 3;
1665 pc = (int *)((BYTE *)pc + 3);
1666 break;
1667
1668 case PCD_PUSH4BYTES:
1669 Stack[sp] = ((BYTE *)pc)[0];
1670 Stack[sp+1] = ((BYTE *)pc)[1];
1671 Stack[sp+2] = ((BYTE *)pc)[2];
1672 Stack[sp+3] = ((BYTE *)pc)[3];
1673 sp += 4;
1674 pc = (int *)((BYTE *)pc + 4);
1675 break;
1676
1677 case PCD_PUSH5BYTES:
1678 Stack[sp] = ((BYTE *)pc)[0];
1679 Stack[sp+1] = ((BYTE *)pc)[1];
1680 Stack[sp+2] = ((BYTE *)pc)[2];
1681 Stack[sp+3] = ((BYTE *)pc)[3];
1682 Stack[sp+4] = ((BYTE *)pc)[4];
1683 sp += 5;
1684 pc = (int *)((BYTE *)pc + 5);
1685 break;
1686
1687 case PCD_PUSHBYTES:
1688 temp = *(BYTE *)pc;
1689 pc = (int *)((BYTE *)pc + temp + 1);
1690 for (temp = -temp; temp; temp++)
1691 {
1692 PushToStack (*((BYTE *)pc + temp));
1693 }
1694 break;
1695
1696 case PCD_DUP:
1697 Stack[sp] = Stack[sp-1];
1698 sp++;
1699 break;
1700
1701 case PCD_SWAP:
1702 std::swap(Stack[sp-2], Stack[sp-1]);
1703 break;
1704
1705 case PCD_LSPEC1:
1706 LineSpecials[NEXTBYTE] (activationline, activator,
1707 STACK(1), 0, 0, 0, 0);
1708 sp -= 1;
1709 break;
1710
1711 case PCD_LSPEC2:
1712 LineSpecials[NEXTBYTE] (activationline, activator,
1713 STACK(2), STACK(1), 0, 0, 0);
1714 sp -= 2;
1715 break;
1716
1717 case PCD_LSPEC3:
1718 LineSpecials[NEXTBYTE] (activationline, activator,
1719 STACK(3), STACK(2), STACK(1), 0, 0);
1720 sp -= 3;
1721 break;
1722
1723 case PCD_LSPEC4:
1724 LineSpecials[NEXTBYTE] (activationline, activator,
1725 STACK(4), STACK(3), STACK(2),
1726 STACK(1), 0);
1727 sp -= 4;
1728 break;
1729
1730 case PCD_LSPEC5:
1731 LineSpecials[NEXTBYTE] (activationline, activator,
1732 STACK(5), STACK(4), STACK(3),
1733 STACK(2), STACK(1));
1734 sp -= 5;
1735 break;
1736
1737 case PCD_LSPEC1DIRECT:
1738 temp = NEXTBYTE;
1739 LineSpecials[temp] (activationline, activator,
1740 pc[0], 0, 0, 0, 0);
1741 pc += 1;
1742 break;
1743
1744 case PCD_LSPEC2DIRECT:
1745 temp = NEXTBYTE;
1746 LineSpecials[temp] (activationline, activator,
1747 pc[0], pc[1], 0, 0, 0);
1748 pc += 2;
1749 break;
1750
1751 case PCD_LSPEC3DIRECT:
1752 temp = NEXTBYTE;
1753 LineSpecials[temp] (activationline, activator,
1754 pc[0], pc[1], pc[2], 0, 0);
1755 pc += 3;
1756 break;
1757
1758 case PCD_LSPEC4DIRECT:
1759 temp = NEXTBYTE;
1760 LineSpecials[temp] (activationline, activator,
1761 pc[0], pc[1], pc[2], pc[3], 0);
1762 pc += 4;
1763 break;
1764
1765 case PCD_LSPEC5DIRECT:
1766 temp = NEXTBYTE;
1767 LineSpecials[temp] (activationline, activator,
1768 pc[0], pc[1], pc[2], pc[3], pc[4]);
1769 pc += 5;
1770 break;
1771
1772 case PCD_LSPEC1DIRECTB:
1773 LineSpecials[((BYTE *)pc)[0]] (activationline, activator,
1774 ((BYTE *)pc)[1], 0, 0, 0, 0);
1775 pc = (int *)((BYTE *)pc + 2);
1776 break;
1777
1778 case PCD_LSPEC2DIRECTB:
1779 LineSpecials[((BYTE *)pc)[0]] (activationline, activator,
1780 ((BYTE *)pc)[1], ((BYTE *)pc)[2], 0, 0, 0);
1781 pc = (int *)((BYTE *)pc + 3);
1782 break;
1783
1784 case PCD_LSPEC3DIRECTB:
1785 LineSpecials[((BYTE *)pc)[0]] (activationline, activator,
1786 ((BYTE *)pc)[1], ((BYTE *)pc)[2], ((BYTE *)pc)[3], 0, 0);
1787 pc = (int *)((BYTE *)pc + 4);
1788 break;
1789
1790 case PCD_LSPEC4DIRECTB:
1791 LineSpecials[((BYTE *)pc)[0]] (activationline, activator,
1792 ((BYTE *)pc)[1], ((BYTE *)pc)[2], ((BYTE *)pc)[3],
1793 ((BYTE *)pc)[4], 0);
1794 pc = (int *)((BYTE *)pc + 5);
1795 break;
1796
1797 case PCD_LSPEC5DIRECTB:
1798 LineSpecials[((BYTE *)pc)[0]] (activationline, activator,
1799 ((BYTE *)pc)[1], ((BYTE *)pc)[2], ((BYTE *)pc)[3],
1800 ((BYTE *)pc)[4], ((BYTE *)pc)[5]);
1801 pc = (int *)((BYTE *)pc + 6);
1802 break;
1803
1804 case PCD_CALL:
1805 case PCD_CALLDISCARD:
1806 {
1807 int funcnum;
1808 int i;
1809 ScriptFunction *func;
1810
1811 funcnum = NEXTBYTE;
1812 func = level.behavior->GetFunction (funcnum);
1813 if (func == NULL)
1814 {
1815 Printf (PRINT_HIGH,"Function %d in script %d out of range\n", funcnum, script);
1816 state = SCRIPT_PleaseRemove;
1817 break;
1818 }
1819 if (sp + func->LocalCount + 32 > STACK_SIZE)
1820 { // 32 is the margin for the function's working space
1821 Printf (PRINT_HIGH,"Out of stack space in script %d\n", script);
1822 state = SCRIPT_PleaseRemove;
1823 break;
1824 }
1825 // The function's first argument is also its first local variable.
1826 locals = &Stack[sp - func->ArgCount];
1827 // Make space on the stack for any other variables the function uses.
1828 for (i = 0; i < func->LocalCount; ++i)
1829 {
1830 Stack[sp+i] = 0;
1831 }
1832 sp += i;
1833 ((CallReturn *)&Stack[sp])->ReturnAddress = level.behavior->PC2Ofs (pc);
1834 ((CallReturn *)&Stack[sp])->ReturnFunction = activeFunction;
1835 ((CallReturn *)&Stack[sp])->bDiscardResult = (pcd == PCD_CALLDISCARD);
1836 sp += sizeof(CallReturn)/sizeof(int);
1837 pc = level.behavior->Ofs2PC (func->Address);
1838 activeFunction = func;
1839 }
1840 break;
1841
1842 case PCD_RETURNVOID:
1843 case PCD_RETURNVAL:
1844 {
1845 int value;
1846 CallReturn *retState;
1847
1848 if (pcd == PCD_RETURNVAL)
1849 {
1850 value = Stack[--sp];
1851 }
1852 else
1853 {
1854 value = 0;
1855 }
1856 sp -= sizeof(CallReturn)/sizeof(int);
1857 retState = (CallReturn *)&Stack[sp];
1858 pc = level.behavior->Ofs2PC (retState->ReturnAddress);
1859 sp -= activeFunction->ArgCount + activeFunction->LocalCount;
1860 activeFunction = retState->ReturnFunction;
1861 if (activeFunction == NULL)
1862 {
1863 locals = localvars;
1864 }
1865 else
1866 {
1867 locals = &Stack[sp - activeFunction->ArgCount - activeFunction->LocalCount];
1868 }
1869 if (!retState->bDiscardResult)
1870 {
1871 Stack[sp++] = value;
1872 }
1873 }
1874 break;
1875
1876 case PCD_ADD:
1877 STACK(2) = STACK(2) + STACK(1);
1878 sp--;
1879 break;
1880
1881 case PCD_SUBTRACT:
1882 STACK(2) = STACK(2) - STACK(1);
1883 sp--;
1884 break;
1885
1886 case PCD_MULTIPLY:
1887 STACK(2) = STACK(2) * STACK(1);
1888 sp--;
1889 break;
1890
1891 case PCD_DIVIDE:
1892 STACK(2) = STACK(2) / STACK(1);
1893 sp--;
1894 break;
1895
1896 case PCD_MODULUS:
1897 STACK(2) = STACK(2) % STACK(1);
1898 sp--;
1899 break;
1900
1901 case PCD_EQ:
1902 STACK(2) = (STACK(2) == STACK(1));
1903 sp--;
1904 break;
1905
1906 case PCD_NE:
1907 STACK(2) = (STACK(2) != STACK(1));
1908 sp--;
1909 break;
1910
1911 case PCD_LT:
1912 STACK(2) = (STACK(2) < STACK(1));
1913 sp--;
1914 break;
1915
1916 case PCD_GT:
1917 STACK(2) = (STACK(2) > STACK(1));
1918 sp--;
1919 break;
1920
1921 case PCD_LE:
1922 STACK(2) = (STACK(2) <= STACK(1));
1923 sp--;
1924 break;
1925
1926 case PCD_GE:
1927 STACK(2) = (STACK(2) >= STACK(1));
1928 sp--;
1929 break;
1930
1931 case PCD_ASSIGNSCRIPTVAR:
1932 locals[NEXTBYTE] = STACK(1);
1933 sp--;
1934 break;
1935
1936
1937 case PCD_ASSIGNMAPVAR:
1938 level.vars[NEXTBYTE] = STACK(1);
1939 sp--;
1940 break;
1941
1942 case PCD_ASSIGNWORLDVAR:
1943 ACS_WorldVars[NEXTBYTE] = STACK(1);
1944 sp--;
1945 break;
1946
1947 case PCD_ASSIGNGLOBALVAR:
1948 ACS_GlobalVars[NEXTBYTE] = STACK(1);
1949 sp--;
1950 break;
1951
1952 case PCD_ASSIGNMAPARRAY:
1953 level.behavior->SetArrayVal (ACS_WorldVars[NEXTBYTE], STACK(2), STACK(1));
1954 sp -= 2;
1955 break;
1956
1957 case PCD_PUSHSCRIPTVAR:
1958 PushToStack (locals[NEXTBYTE]);
1959 break;
1960
1961 case PCD_PUSHMAPVAR:
1962 PushToStack (level.vars[NEXTBYTE]);
1963 break;
1964
1965 case PCD_PUSHWORLDVAR:
1966 PushToStack (ACS_WorldVars[NEXTBYTE]);
1967 break;
1968
1969 case PCD_PUSHGLOBALVAR:
1970 PushToStack (ACS_GlobalVars[NEXTBYTE]);
1971 break;
1972
1973 case PCD_PUSHMAPARRAY:
1974 STACK(1) = level.behavior->GetArrayVal (level.vars[NEXTBYTE], STACK(1));
1975 break;
1976
1977 case PCD_ADDSCRIPTVAR:
1978 locals[NEXTBYTE] += STACK(1);
1979 sp--;
1980 break;
1981
1982 case PCD_ADDMAPVAR:
1983 level.vars[NEXTBYTE] += STACK(1);
1984 sp--;
1985 break;
1986
1987 case PCD_ADDWORLDVAR:
1988 ACS_WorldVars[NEXTBYTE] += STACK(1);
1989 sp--;
1990 break;
1991
1992 case PCD_ADDGLOBALVAR:
1993 ACS_GlobalVars[NEXTBYTE] += STACK(1);
1994 sp--;
1995 break;
1996
1997 case PCD_ADDMAPARRAY:
1998 {
1999 int a = ACS_WorldVars[NEXTBYTE];
2000 int i = STACK(2);
2001 level.behavior->SetArrayVal (a, i,
2002 level.behavior->GetArrayVal (a, i) + STACK(1));
2003 sp -= 2;
2004 }
2005 break;
2006
2007 case PCD_SUBSCRIPTVAR:
2008 locals[NEXTBYTE] -= STACK(1);
2009 sp--;
2010 break;
2011
2012 case PCD_SUBMAPVAR:
2013 level.vars[NEXTBYTE] -= STACK(1);
2014 sp--;
2015 break;
2016
2017 case PCD_SUBWORLDVAR:
2018 ACS_WorldVars[NEXTBYTE] -= STACK(1);
2019 sp--;
2020 break;
2021
2022 case PCD_SUBGLOBALVAR:
2023 ACS_GlobalVars[NEXTBYTE] -= STACK(1);
2024 sp--;
2025 break;
2026
2027 case PCD_SUBMAPARRAY:
2028 {
2029 int a = ACS_WorldVars[NEXTBYTE];
2030 int i = STACK(2);
2031 level.behavior->SetArrayVal (a, i,
2032 level.behavior->GetArrayVal (a, i) - STACK(1));
2033 sp -= 2;
2034 }
2035 break;
2036
2037 case PCD_MULSCRIPTVAR:
2038 locals[NEXTBYTE] *= STACK(1);
2039 sp--;
2040 break;
2041
2042 case PCD_MULMAPVAR:
2043 level.vars[NEXTBYTE] *= STACK(1);
2044 sp--;
2045 break;
2046
2047 case PCD_MULWORLDVAR:
2048 ACS_WorldVars[NEXTBYTE] *= STACK(1);
2049 sp--;
2050 break;
2051
2052 case PCD_MULGLOBALVAR:
2053 ACS_GlobalVars[NEXTBYTE] *= STACK(1);
2054 sp--;
2055 break;
2056
2057 case PCD_MULMAPARRAY:
2058 {
2059 int a = ACS_WorldVars[NEXTBYTE];
2060 int i = STACK(2);
2061 level.behavior->SetArrayVal (a, i,
2062 level.behavior->GetArrayVal (a, i) * STACK(1));
2063 sp -= 2;
2064 }
2065 break;
2066
2067 case PCD_DIVSCRIPTVAR:
2068 locals[NEXTBYTE] /= STACK(1);
2069 sp--;
2070 break;
2071
2072 case PCD_DIVMAPVAR:
2073 level.vars[NEXTBYTE] /= STACK(1);
2074 sp--;
2075 break;
2076
2077 case PCD_DIVWORLDVAR:
2078 ACS_WorldVars[NEXTBYTE] /= STACK(1);
2079 sp--;
2080 break;
2081
2082 case PCD_DIVGLOBALVAR:
2083 ACS_GlobalVars[NEXTBYTE] /= STACK(1);
2084 sp--;
2085 break;
2086
2087 case PCD_DIVMAPARRAY:
2088 {
2089 int a = ACS_WorldVars[NEXTBYTE];
2090 int i = STACK(2);
2091 level.behavior->SetArrayVal (a, i,
2092 level.behavior->GetArrayVal (a, i) / STACK(1));
2093 sp -= 2;
2094 }
2095 break;
2096
2097 case PCD_MODSCRIPTVAR:
2098 locals[NEXTBYTE] %= STACK(1);
2099 sp--;
2100 break;
2101
2102 case PCD_MODMAPVAR:
2103 level.vars[NEXTBYTE] %= STACK(1);
2104 sp--;
2105 break;
2106
2107 case PCD_MODWORLDVAR:
2108 ACS_WorldVars[NEXTBYTE] %= STACK(1);
2109 sp--;
2110 break;
2111
2112 case PCD_MODGLOBALVAR:
2113 ACS_GlobalVars[NEXTBYTE] %= STACK(1);
2114 sp--;
2115 break;
2116
2117 case PCD_MODMAPARRAY:
2118 {
2119 int a = ACS_WorldVars[NEXTBYTE];
2120 int i = STACK(2);
2121 level.behavior->SetArrayVal (a, i,
2122 level.behavior->GetArrayVal (a, i) % STACK(1));
2123 sp -= 2;
2124 }
2125 break;
2126
2127 case PCD_INCSCRIPTVAR:
2128 ++locals[NEXTBYTE];
2129 break;
2130
2131 case PCD_INCMAPVAR:
2132 ++level.vars[NEXTBYTE];
2133 break;
2134
2135 case PCD_INCWORLDVAR:
2136 ++ACS_WorldVars[NEXTBYTE];
2137 break;
2138
2139 case PCD_INCGLOBALVAR:
2140 ++ACS_GlobalVars[NEXTBYTE];
2141 break;
2142
2143 case PCD_INCMAPARRAY:
2144 {
2145 int a = ACS_WorldVars[NEXTBYTE];
2146 int i = STACK(2);
2147 level.behavior->SetArrayVal (a, i,
2148 level.behavior->GetArrayVal (a, i) + 1);
2149 sp--;
2150 }
2151 break;
2152
2153 case PCD_DECSCRIPTVAR:
2154 --locals[NEXTBYTE];
2155 break;
2156
2157 case PCD_DECMAPVAR:
2158 --level.vars[NEXTBYTE];
2159 break;
2160
2161 case PCD_DECWORLDVAR:
2162 --ACS_WorldVars[NEXTBYTE];
2163 break;
2164
2165 case PCD_DECGLOBALVAR:
2166 --ACS_GlobalVars[NEXTBYTE];
2167 break;
2168
2169 case PCD_DECMAPARRAY:
2170 {
2171 int a = ACS_WorldVars[NEXTBYTE];
2172 int i = STACK(2);
2173 level.behavior->SetArrayVal (a, i,
2174 level.behavior->GetArrayVal (a, i) - 1);
2175 sp--;
2176 }
2177 break;
2178
2179 case PCD_GOTO:
2180 pc = level.behavior->Ofs2PC (*pc);
2181 break;
2182
2183 case PCD_IFGOTO:
2184 if (STACK(1))
2185 pc = level.behavior->Ofs2PC (*pc);
2186 else
2187 pc++;
2188 sp--;
2189 break;
2190
2191 case PCD_DROP:
2192 sp--;
2193 break;
2194
2195 case PCD_DELAY:
2196 state = SCRIPT_Delayed;
2197 statedata = STACK(1);
2198 sp--;
2199 break;
2200
2201 case PCD_DELAYDIRECT:
2202 state = SCRIPT_Delayed;
2203 statedata = NEXTWORD;
2204 break;
2205
2206 case PCD_DELAYDIRECTB:
2207 state = SCRIPT_Delayed;
2208 statedata = *(BYTE *)pc;
2209 pc = (int *)((BYTE *)pc + 1);
2210 break;
2211
2212 case PCD_RANDOM:
2213 STACK(2) = Random (STACK(2), STACK(1));
2214 sp--;
2215 break;
2216
2217 case PCD_RANDOMDIRECT:
2218 PushToStack (Random (pc[0], pc[1]));
2219 pc += 2;
2220 break;
2221
2222 case PCD_RANDOMDIRECTB:
2223 PushToStack (Random (((BYTE *)pc)[0], ((BYTE *)pc)[1]));
2224 pc = (int *)((BYTE *)pc + 2);
2225 break;
2226
2227 case PCD_THINGCOUNT:
2228 STACK(2) = ThingCount (STACK(2), STACK(1));
2229 sp--;
2230 break;
2231
2232 case PCD_THINGCOUNTDIRECT:
2233 PushToStack (ThingCount (pc[0], pc[1]));
2234 pc += 2;
2235 break;
2236
2237 case PCD_TAGWAIT:
2238 state = SCRIPT_TagWait;
2239 statedata = STACK(1);
2240 sp--;
2241 break;
2242
2243 case PCD_TAGWAITDIRECT:
2244 state = SCRIPT_TagWait;
2245 statedata = NEXTWORD;
2246 break;
2247
2248 case PCD_POLYWAIT:
2249 state = SCRIPT_PolyWait;
2250 statedata = STACK(1);
2251 sp--;
2252 break;
2253
2254 case PCD_POLYWAITDIRECT:
2255 state = SCRIPT_PolyWait;
2256 statedata = NEXTWORD;
2257 break;
2258
2259 case PCD_CHANGEFLOOR:
2260 ChangeFlat (STACK(2), STACK(1), 0);
2261 sp -= 2;
2262 break;
2263
2264 case PCD_CHANGEFLOORDIRECT:
2265 ChangeFlat (pc[0], pc[1], 0);
2266 pc += 2;
2267 break;
2268
2269 case PCD_CHANGECEILING:
2270 ChangeFlat (STACK(2), STACK(1), 1);
2271 sp -= 2;
2272 break;
2273
2274 case PCD_CHANGECEILINGDIRECT:
2275 ChangeFlat (pc[0], pc[1], 1);
2276 pc += 2;
2277 break;
2278
2279 case PCD_RESTART:
2280 pc = level.behavior->FindScript (script);
2281 break;
2282
2283 case PCD_ANDLOGICAL:
2284 STACK(2) = (STACK(2) && STACK(1));
2285 sp--;
2286 break;
2287
2288 case PCD_ORLOGICAL:
2289 STACK(2) = (STACK(2) || STACK(1));
2290 sp--;
2291 break;
2292
2293 case PCD_ANDBITWISE:
2294 STACK(2) = (STACK(2) & STACK(1));
2295 sp--;
2296 break;
2297
2298 case PCD_ORBITWISE:
2299 STACK(2) = (STACK(2) | STACK(1));
2300 sp--;
2301 break;
2302
2303 case PCD_EORBITWISE:
2304 STACK(2) = (STACK(2) ^ STACK(1));
2305 sp--;
2306 break;
2307
2308 case PCD_NEGATELOGICAL:
2309 STACK(1) = !STACK(1);
2310 break;
2311
2312 case PCD_LSHIFT:
2313 STACK(2) = (STACK(2) << STACK(1));
2314 sp--;
2315 break;
2316
2317 case PCD_RSHIFT:
2318 STACK(2) = (STACK(2) >> STACK(1));
2319 sp--;
2320 break;
2321
2322 case PCD_UNARYMINUS:
2323 STACK(1) = -STACK(1);
2324 break;
2325
2326 case PCD_IFNOTGOTO:
2327 if (!STACK(1))
2328 pc = level.behavior->Ofs2PC (*pc);
2329 else
2330 pc++;
2331 sp--;
2332 break;
2333
2334 case PCD_LINESIDE:
2335 PushToStack (lineSide);
2336 break;
2337
2338 case PCD_SCRIPTWAIT:
2339 statedata = STACK(1);
2340 if (controller->RunningScripts[statedata])
2341 state = SCRIPT_ScriptWait;
2342 else
2343 state = SCRIPT_ScriptWaitPre;
2344 sp--;
2345 PutLast ();
2346 break;
2347
2348 case PCD_SCRIPTWAITDIRECT:
2349 state = SCRIPT_ScriptWait;
2350 statedata = NEXTWORD;
2351 PutLast ();
2352 break;
2353
2354 case PCD_CLEARLINESPECIAL:
2355 if (activationline)
2356 activationline->special = 0;
2357 break;
2358
2359 case PCD_CASEGOTO:
2360 if (STACK(1) == NEXTWORD)
2361 {
2362 pc = level.behavior->Ofs2PC (*pc);
2363 sp--;
2364 }
2365 else
2366 {
2367 pc++;
2368 }
2369 break;
2370
2371 case PCD_BEGINPRINT:
2372 workwhere = work;
2373 work[0] = 0;
2374 break;
2375
2376 case PCD_PRINTSTRING:
2377 case PCD_PRINTLOCALIZED:
2378 lookup = (pcd == PCD_PRINTSTRING ?
2379 level.behavior->LookupString (STACK(1)) :
2380 level.behavior->LocalizeString (STACK(1)));
2381 if (lookup != NULL)
2382 {
2383 workwhere += sprintf (workwhere, "%s", lookup);
2384 }
2385 --sp;
2386 break;
2387
2388 case PCD_PRINTNUMBER:
2389 workwhere += sprintf (workwhere, "%d", STACK(1));
2390 --sp;
2391 break;
2392
2393 case PCD_PRINTCHARACTER:
2394 workwhere[0] = STACK(1);
2395 workwhere[1] = 0;
2396 workwhere++;
2397 --sp;
2398 break;
2399
2400 case PCD_PRINTFIXED:
2401 workwhere += sprintf (workwhere, "%g", FIXED2FLOAT(STACK(1)));
2402 --sp;
2403 break;
2404
2405 // [BC] Print activator's name
2406 // [RH] Fancied up a bit
2407 case PCD_PRINTNAME:
2408 {
2409 player_t *player = NULL;
2410
2411 if (STACK(1) == 0 || (unsigned)STACK(1) > MAXPLAYERS)
2412 {
2413 if (activator)
2414 {
2415 player = activator->player;
2416 }
2417 }
2418 else if (idplayer(STACK(1)).ingame())
2419 {
2420 player = &idplayer(STACK(1));
2421 }
2422 else
2423 {
2424 workwhere += sprintf (workwhere, "Player %d\n",
2425 STACK(1));
2426 sp--;
2427 break;
2428 }
2429 if (player)
2430 {
2431 workwhere += sprintf (workwhere, "%s",
2432 activator->player->userinfo.netname.c_str());
2433 }
2434 else if (activator)
2435 {
2436 workwhere += sprintf (workwhere, "%s",
2437 RUNTIME_TYPE(activator)->Name+1);
2438 }
2439 else
2440 {
2441 workwhere += sprintf (workwhere, " ");
2442 }
2443 sp--;
2444 }
2445 break;
2446
2447 case PCD_ENDPRINT:
2448 case PCD_ENDPRINTBOLD:
2449 //case PCD_MOREHUDMESSAGE:
2450 strbin (work);
2451 if (pcd != PCD_MOREHUDMESSAGE)
2452 {
2453 if (pcd == PCD_ENDPRINTBOLD || activator == NULL ||
2454 (activator->player->mo == consoleplayer().camera))
2455 {
2456 strbin (work);
2457 C_MidPrint (work);
2458 }
2459 }
2460 else
2461 {
2462 optstart = -1;
2463 }
2464 break;
2465
2466 /*case PCD_OPTHUDMESSAGE:
2467 optstart = sp;
2468 break;
2469
2470 case PCD_ENDHUDMESSAGE:
2471 case PCD_ENDHUDMESSAGEBOLD:
2472 if (optstart == -1)
2473 {
2474 optstart = sp;
2475 }
2476 if (pcd == PCD_ENDHUDMESSAGEBOLD || activator == NULL ||
2477 (activator->player->mo == consoleplayer().camera))
2478 {
2479 int type = Stack[optstart-6];
2480 int id = Stack[optstart-5];
2481 EColorRange color = CLAMPCOLOR(Stack[optstart-4]);
2482 float x = FIXED2FLOAT(Stack[optstart-3]);
2483 float y = FIXED2FLOAT(Stack[optstart-2]);
2484 float holdTime = FIXED2FLOAT(Stack[optstart-1]);
2485 DHUDMessage *msg;
2486
2487 switch (type)
2488 {
2489 default: // normal
2490 msg = new DHUDMessage (work, x, y, color, holdTime);
2491 break;
2492 case 1: // fade out
2493 {
2494 float fadeTime = (optstart < sp) ?
2495 FIXED2FLOAT(Stack[optstart]) : 0.5f;
2496 msg = new DHUDMessageFadeOut (work, x, y, color, holdTime, fadeTime);
2497 }
2498 break;
2499 case 2: // type on, then fade out
2500 {
2501 float typeTime = (optstart < sp) ?
2502 FIXED2FLOAT(Stack[optstart]) : 0.05f;
2503 float fadeTime = (optstart < sp-1) ?
2504 FIXED2FLOAT(Stack[optstart+1]) : 0.5f;
2505 msg = new DHUDMessageTypeOnFadeOut (work, x, y, color, typeTime, holdTime, fadeTime);
2506 }
2507 break;
2508 }
2509 StatusBar->AttachMessage (msg, id ? 0xff000000|id : 0);
2510 }
2511 sp = optstart-6;
2512 break;
2513 */
2514 /*case PCD_SETFONT:
2515 DoSetFont (STACK(1));
2516 sp--;
2517 break;
2518
2519 case PCD_SETFONTDIRECT:
2520 DoSetFont (pc[0]);
2521 pc++;
2522 break;
2523 */
2524 case PCD_PLAYERCOUNT:
2525 PushToStack (CountPlayers ());
2526 break;
2527
2528 case PCD_GAMETYPE:
2529 if (sv_gametype == 3)
2530 PushToStack (GAME_NET_CTF);
2531 else if (sv_gametype == 2)
2532 PushToStack (GAME_NET_TEAMDEATHMATCH);
2533 else if (sv_gametype == 1)
2534 PushToStack (GAME_NET_DEATHMATCH);
2535 else if (multiplayer)
2536 PushToStack (GAME_NET_COOPERATIVE);
2537 else
2538 PushToStack (GAME_SINGLE_PLAYER);
2539 break;
2540
2541 case PCD_GAMESKILL:
2542 PushToStack (sv_skill);
2543 break;
2544
2545 // [BC] Start ST PCD's
2546 case PCD_PLAYERHEALTH:
2547 if (activator)
2548 PushToStack (activator->health);
2549 break;
2550
2551 case PCD_PLAYERARMORPOINTS:
2552 if (activator && activator->player)
2553 PushToStack (activator->player->armorpoints);
2554 break;
2555
2556 case PCD_PLAYERFRAGS:
2557 if (activator && activator->player)
2558 PushToStack (activator->player->fragcount);
2559 break;
2560
2561 case PCD_MUSICCHANGE:
2562 lookup = level.behavior->LookupString (STACK(2));
2563 if (lookup != NULL)
2564 {
2565 S_ChangeMusic (lookup, STACK(1));
2566 }
2567 sp -= 2;
2568 break;
2569
2570 case PCD_SINGLEPLAYER:
2571 PushToStack (!netgame);
2572 break;
2573 // [BC] End ST PCD's
2574
2575 case PCD_TIMER:
2576 PushToStack (level.time);
2577 break;
2578
2579 case PCD_SECTORSOUND:
2580 lookup = level.behavior->LookupString (STACK(2));
2581 if (lookup != NULL)
2582 {
2583 if (activationline)
2584 {
2585 S_Sound (
2586 activationline->frontsector->soundorg,
2587 CHAN_BODY,
2588 lookup,
2589 (float)(STACK(1)) / 127.f,
2590 ATTN_NORM);
2591 }
2592 else
2593 {
2594 S_Sound (
2595 CHAN_BODY,
2596 lookup,
2597 (float)(STACK(1)) / 127.f,
2598 ATTN_NORM);
2599 }
2600 }
2601 sp -= 2;
2602 break;
2603
2604 case PCD_AMBIENTSOUND:
2605 lookup = level.behavior->LookupString (STACK(2));
2606 if (lookup != NULL)
2607 {
2608 S_Sound (CHAN_AUTO,
2609 lookup,
2610 (float)(STACK(1)) / 127.f, ATTN_NONE);
2611 }
2612 sp -= 2;
2613 break;
2614
2615 case PCD_LOCALAMBIENTSOUND:
2616 lookup = level.behavior->LookupString (STACK(2));
2617 if (lookup != NULL && consoleplayer().camera == activator)
2618 {
2619 S_Sound (CHAN_AUTO,
2620 lookup,
2621 (float)(STACK(1)) / 127.f, ATTN_NONE);
2622 }
2623 sp -= 2;
2624 break;
2625
2626 case PCD_ACTIVATORSOUND:
2627 lookup = level.behavior->LookupString (STACK(2));
2628 if (lookup != NULL)
2629 {
2630 S_Sound (activator, CHAN_AUTO,
2631 lookup,
2632 (float)(STACK(1)) / 127.f, ATTN_NORM);
2633 }
2634 sp -= 2;
2635 break;
2636
2637 case PCD_SOUNDSEQUENCE:
2638 lookup = level.behavior->LookupString (STACK(1));
2639 if (lookup != NULL)
2640 {
2641 if (activationline)
2642 {
2643 SN_StartSequence (
2644 activationline->frontsector,
2645 lookup);
2646 }
2647 }
2648 sp--;
2649 break;
2650
2651 case PCD_SETLINETEXTURE:
2652 SetLineTexture (STACK(4), STACK(3), STACK(2), STACK(1));
2653 sp -= 4;
2654 break;
2655
2656 case PCD_SETLINEBLOCKING:
2657 {
2658 int line = -1;
2659
2660 while ((line = P_FindLineFromID (STACK(2), line)) >= 0)
2661 {
2662 switch (STACK(1))
2663 {
2664 case BLOCK_NOTHING:
2665 lines[line].flags &= ~(ML_BLOCKING|ML_BLOCKEVERYTHING);
2666 break;
2667 case BLOCK_CREATURES:
2668 default:
2669 lines[line].flags &= ~ML_BLOCKEVERYTHING;
2670 lines[line].flags |= ML_BLOCKING;
2671 break;
2672 case BLOCK_EVERYTHING:
2673 lines[line].flags |= ML_BLOCKING|ML_BLOCKEVERYTHING;
2674 break;
2675 }
2676 }
2677
2678 sp -= 2;
2679 }
2680 break;
2681
2682 case PCD_SETLINEMONSTERBLOCKING:
2683 {
2684 int line = -1;
2685
2686 while ((line = P_FindLineFromID (STACK(2), line)) >= 0)
2687 {
2688 if (STACK(1))
2689 lines[line].flags |= ML_BLOCKMONSTERS;
2690 else
2691 lines[line].flags &= ~ML_BLOCKMONSTERS;
2692 }
2693
2694 sp -= 2;
2695 }
2696 break;
2697
2698 case PCD_SETLINESPECIAL:
2699 {
2700 int linenum = -1;
2701
2702 while ((linenum = P_FindLineFromID (STACK(7), linenum)) >= 0) {
2703 line_t *line = &lines[linenum];
2704
2705 line->special = STACK(6);
2706 line->args[0] = STACK(5);
2707 line->args[1] = STACK(4);
2708 line->args[2] = STACK(3);
2709 line->args[3] = STACK(2);
2710 line->args[4] = STACK(1);
2711 }
2712 sp -= 7;
2713 }
2714 break;
2715
2716 case PCD_SETTHINGSPECIAL:
2717 {
2718 FActorIterator iterator (STACK(7));
2719 AActor *actor;
2720
2721 while ( (actor = iterator.Next ()) )
2722 {
2723 actor->special = STACK(6);
2724 actor->args[0] = STACK(5);
2725 actor->args[1] = STACK(4);
2726 actor->args[2] = STACK(3);
2727 actor->args[3] = STACK(2);
2728 actor->args[4] = STACK(1);
2729 }
2730 sp -= 7;
2731 }
2732 break;
2733
2734 case PCD_THINGSOUND:
2735 lookup = level.behavior->LookupString (STACK(2));
2736 if (lookup != NULL)
2737 {
2738 FActorIterator iterator (STACK(3));
2739 AActor *spot;
2740
2741 while ( (spot = iterator.Next ()) )
2742 {
2743 S_Sound (spot, CHAN_BODY,
2744 lookup,
2745 (float)(STACK(1))/127.f, ATTN_NORM);
2746 }
2747 }
2748 sp -= 3;
2749 break;
2750
2751
2752 case PCD_FIXEDMUL:
2753 STACK(2) = FixedMul (STACK(2), STACK(1));
2754 sp--;
2755 break;
2756
2757 case PCD_FIXEDDIV:
2758 STACK(2) = FixedDiv (STACK(2), STACK(1));
2759 sp--;
2760 break;
2761
2762 case PCD_SETGRAVITY:
2763 level.gravity = (float)STACK(1) / 65536.f;
2764 sp--;
2765 break;
2766
2767 case PCD_SETGRAVITYDIRECT:
2768 level.gravity = (float)pc[0] / 65536.f;
2769 pc++;
2770 break;
2771
2772 case PCD_SETAIRCONTROL:
2773 level.aircontrol = STACK(1);
2774 sp--;
2775 G_AirControlChanged ();
2776 break;
2777
2778 case PCD_SETAIRCONTROLDIRECT:
2779 level.aircontrol = pc[0];
2780 pc++;
2781 G_AirControlChanged ();
2782 break;
2783
2784 /*case PCD_SPAWN:
2785 STACK(6) = DoSpawn (STACK(6), STACK(5), STACK(4), STACK(3), STACK(2), STACK(1));
2786 sp -= 5;
2787 break;
2788
2789 case PCD_SPAWNDIRECT:
2790 PushToStack (DoSpawn (pc[0], pc[1], pc[2], pc[3], pc[4], pc[5]));
2791 pc += 6;
2792 break;
2793
2794 case PCD_SPAWNSPOT:
2795 STACK(4) = DoSpawnSpot (STACK(4), STACK(3), STACK(2), STACK(1));
2796 sp -= 3;
2797 break;
2798
2799 case PCD_SPAWNSPOTDIRECT:
2800 PushToStack (DoSpawnSpot (pc[0], pc[1], pc[2], pc[3]));
2801 pc += 4;
2802 break;*/
2803
2804 case PCD_CLEARINVENTORY:
2805 ClearInventory (activator);
2806 break;
2807
2808 case PCD_GIVEINVENTORY:
2809 GiveInventory (activator, level.behavior->LookupString (STACK(2)), STACK(1));
2810 sp -= 2;
2811 break;
2812
2813 case PCD_GIVEINVENTORYDIRECT:
2814 GiveInventory (activator, level.behavior->LookupString (pc[0]), pc[1]);
2815 pc += 2;
2816 break;
2817
2818 case PCD_TAKEINVENTORY:
2819 TakeInventory (activator, level.behavior->LookupString (STACK(2)), STACK(1));
2820 sp -= 2;
2821 break;
2822
2823 case PCD_TAKEINVENTORYDIRECT:
2824 TakeInventory (activator, level.behavior->LookupString (pc[0]), pc[1]);
2825 pc += 2;
2826 break;
2827
2828 case PCD_CHECKINVENTORY:
2829 STACK(1) = CheckInventory (activator, level.behavior->LookupString (STACK(1)));
2830 break;
2831
2832 case PCD_CHECKINVENTORYDIRECT:
2833 PushToStack (CheckInventory (activator, level.behavior->LookupString (pc[0])));
2834 pc += 1;
2835 break;
2836
2837 case PCD_SETMUSIC:
2838 S_ChangeMusic (level.behavior->LookupString (STACK(3)), STACK(2));
2839 sp -= 3;
2840 break;
2841
2842 case PCD_SETMUSICDIRECT:
2843 S_ChangeMusic (level.behavior->LookupString (pc[0]), pc[1]);
2844 pc += 3;
2845 break;
2846
2847 case PCD_LOCALSETMUSIC:
2848 if (activator == consoleplayer().mo)
2849 {
2850 S_ChangeMusic (level.behavior->LookupString (STACK(3)), STACK(2));
2851 }
2852 sp -= 3;
2853 break;
2854
2855 case PCD_LOCALSETMUSICDIRECT:
2856 if (activator == consoleplayer().mo)
2857 {
2858 S_ChangeMusic (level.behavior->LookupString (pc[0]), pc[1]);
2859 }
2860 pc += 3;
2861 break;
2862
2863 case PCD_FADETO:
2864 DoFadeTo (STACK(5), STACK(4), STACK(3), STACK(2), STACK(1));
2865 sp -= 5;
2866 break;
2867
2868 case PCD_FADERANGE:
2869 DoFadeRange (STACK(9), STACK(8), STACK(7), STACK(6),
2870 STACK(5), STACK(4), STACK(3), STACK(2), STACK(1));
2871 sp -= 9;
2872 break;
2873
2874 case PCD_CANCELFADE:
2875 {
2876 TThinkerIterator<DFlashFader> iterator;
2877 DFlashFader *fader;
2878
2879 while ( (fader = iterator.Next()) )
2880 {
2881 if (activator == NULL || fader->WhoFor() == activator)
2882 {
2883 fader->Cancel ();
2884 }
2885 }
2886 }
2887 break;
2888
2889 /*case PCD_PLAYMOVIE:
2890 STACK(1) = I_PlayMovie (level.behavior->LookupString (STACK(1)));
2891 break;
2892 */
2893 case PCD_GETACTORX:
2894 case PCD_GETACTORY:
2895 case PCD_GETACTORZ:
2896 {
2897 AActor *actor;
2898
2899 if (STACK(1) == 0)
2900 {
2901 actor = activator;
2902 }
2903 else
2904 {
2905 FActorIterator iterator (STACK(1));
2906 actor = iterator.Next ();
2907 }
2908 if (actor == NULL)
2909 {
2910 STACK(1) = 0;
2911 }
2912 else
2913 {
2914 STACK(1) = (&actor->x)[pcd - PCD_GETACTORX];
2915 }
2916 }
2917 break;
2918
2919 case PCD_SETFLOORTRIGGER:
2920 new DPlaneWatcher (activator, activationline, lineSide, false, STACK(8),
2921 STACK(7), STACK(6), STACK(5), STACK(4), STACK(3), STACK(2), STACK(1));
2922 sp -= 8;
2923 break;
2924
2925 case PCD_SETCEILINGTRIGGER:
2926 new DPlaneWatcher (activator, activationline, lineSide, true, STACK(8),
2927 STACK(7), STACK(6), STACK(5), STACK(4), STACK(3), STACK(2), STACK(1));
2928 sp -= 8;
2929 break;
2930
2931 /*case PCD_STARTTRANSLATION:
2932 {
2933 int i = STACK(1);
2934 sp--;
2935 if (i >= 1 && i <= MAX_ACS_TRANSLATIONS)
2936 {
2937 translation = &translationtables[TRANSLATION_LevelScripted][i*256-256];
2938 for (i = 0; i < 256; ++i)
2939 {
2940 translation[i] = i;
2941 }
2942 }
2943 }
2944 break;
2945
2946 case PCD_TRANSLATIONRANGE1:
2947 { // translation using palette shifting
2948 int start = STACK(4);
2949 int end = STACK(3);
2950 int pal1 = STACK(2);
2951 int pal2 = STACK(1);
2952 fixed_t palcol, palstep;
2953 sp -= 4;
2954
2955 if (translation == NULL)
2956 {
2957 break;
2958 }
2959 if (start > end)
2960 {
2961 swap (start, end);
2962 swap (pal1, pal2);
2963 }
2964 else if (start == end)
2965 {
2966 translation[start] = pal1;
2967 break;
2968 }
2969 palcol = pal1 << FRACBITS;
2970 palstep = ((pal2 << FRACBITS) - palcol) / (end - start);
2971 for (int i = start; i <= end; palcol += palstep, ++i)
2972 {
2973 translation[i] = palcol >> FRACBITS;
2974 }
2975 }
2976 break;
2977
2978 case PCD_TRANSLATIONRANGE2:
2979 { // translation using RGB values
2980 // (would HSV be a good idea too?)
2981 int start = STACK(8);
2982 int end = STACK(7);
2983 fixed_t r1 = STACK(6) << FRACBITS;
2984 fixed_t g1 = STACK(5) << FRACBITS;
2985 fixed_t b1 = STACK(4) << FRACBITS;
2986 fixed_t r2 = STACK(3) << FRACBITS;
2987 fixed_t g2 = STACK(2) << FRACBITS;
2988 fixed_t b2 = STACK(1) << FRACBITS;
2989 fixed_t r, g, b;
2990 fixed_t rs, gs, bs;
2991 sp -= 8;
2992
2993 if (translation == NULL)
2994 {
2995 break;
2996 }
2997 if (start > end)
2998 {
2999 swap (start, end);
3000 r = r2;
3001 g = g2;
3002 b = b2;
3003 rs = r1 - r2;
3004 gs = g1 - g2;
3005 bs = b1 - b2;
3006 }
3007 else
3008 {
3009 r = r1;
3010 g = g1;
3011 b = b1;
3012 rs = r2 - r1;
3013 gs = g2 - g1;
3014 bs = b2 - b1;
3015 }
3016 if (start == end)
3017 {
3018 translation[start] = ColorMatcher.Pick
3019 (r >> FRACBITS, g >> FRACBITS, b >> FRACBITS);
3020 break;
3021 }
3022 rs /= (end - start);
3023 gs /= (end - start);
3024 bs /= (end - start);
3025 for (int i = start; i <= end; ++i)
3026 {
3027 translation[i] = ColorMatcher.Pick
3028 (r >> FRACBITS, g >> FRACBITS, b >> FRACBITS);
3029 r += rs;
3030 g += gs;
3031 b += bs;
3032 }
3033 }
3034 break;
3035
3036 case PCD_ENDTRANSLATION:
3037 // This might be useful for hardware rendering, but
3038 // for software it is superfluous.
3039 translation = NULL;
3040 break;
3041 */
3042
3043 case PCD_SIN:
3044 STACK(1) = finesine[(STACK(1)<<16)>>ANGLETOFINESHIFT];
3045 break;
3046
3047 case PCD_COS:
3048 STACK(1) = finecosine[(STACK(1)<<16)>>ANGLETOFINESHIFT];
3049 break;
3050
3051 case PCD_VECTORANGLE:
3052 STACK(2) = R_PointToAngle2 (0, 0, STACK(2), STACK(1)) >> 16;
3053 sp--;
3054 break;
3055
3056 /*case PCD_CHECKWEAPON:
3057 if (activator == NULL || activator->player == NULL)
3058 { // Non-players do not have ready weapons
3059 STACK(1) = 0;
3060 }
3061 else
3062 {
3063 STACK(1) = 0 == strcmp (level.behavior->LookupString (STACK(1)),
3064 wpnlev1info[activator->player->readyweapon]->type->Name+1);
3065 }
3066 break;
3067
3068 case PCD_SETWEAPON:
3069 if (activator == NULL || activator->player == NULL)
3070 {
3071 STACK(1) = 0;
3072 }
3073 else
3074 {
3075 int i;
3076
3077 for (i = 0; i < NUMWEAPONS; ++i)
3078 {
3079 if (0 == strcmp (level.behavior->LookupString (STACK(1)),
3080 wpnlev1info[i]->type->Name+1))
3081 {
3082 break;
3083 }
3084 }
3085 if (i >= NUMWEAPONS || !activator->player->weaponowned[i])
3086 {
3087 STACK(1) = 0;
3088 }
3089 else
3090 {
3091 STACK(1) = 1;
3092 if (activator->player->readyweapon != i)
3093 {
3094 activator->player->pendingweapon = (weapontype_t)i;
3095 }
3096 }
3097 }
3098 break;
3099 */
3100 }
3101 }
3102
3103 this->pc = pc;
3104 this->sp = sp;
3105
3106 if (state == SCRIPT_PleaseRemove)
3107 {
3108 Unlink ();
3109 if (!controller)
3110 return;
3111
3112 if (controller->RunningScripts[script] == this)
3113 controller->RunningScripts[script] = NULL;
3114 this->Destroy ();
3115 }
3116 }
3117
P_GetScriptGoing(AActor * who,line_t * where,int num,int * code,int lineSide,int arg0,int arg1,int arg2,int always,bool delay)3118 static bool P_GetScriptGoing (AActor *who, line_t *where, int num, int *code,
3119 int lineSide, int arg0, int arg1, int arg2, int always, bool delay)
3120 {
3121 DACSThinker *controller = DACSThinker::ActiveThinker;
3122
3123 if (controller && !always && controller->RunningScripts[num])
3124 {
3125 if (controller->RunningScripts[num]->GetState () == DLevelScript::SCRIPT_Suspended)
3126 {
3127 controller->RunningScripts[num]->SetState (DLevelScript::SCRIPT_Running);
3128 return true;
3129 }
3130 return false;
3131 }
3132
3133 new DLevelScript (who, where, num, code, lineSide, arg0, arg1, arg2, always, delay);
3134
3135 return true;
3136 }
3137
DLevelScript(AActor * who,line_t * where,int num,int * code,int lineside,int arg0,int arg1,int arg2,int always,bool delay)3138 DLevelScript::DLevelScript (AActor *who, line_t *where, int num, int *code, int lineside,
3139 int arg0, int arg1, int arg2, int always, bool delay)
3140 {
3141 if (DACSThinker::ActiveThinker == NULL)
3142 new DACSThinker;
3143
3144 script = num;
3145 sp = 0;
3146 localvars[0] = arg0;
3147 localvars[1] = arg1;
3148 localvars[2] = arg2;
3149 memset (localvars+3, 0, sizeof(localvars)-3*sizeof(int));
3150 pc = code;
3151 activator = who;
3152 activationline = where;
3153 lineSide = lineside;
3154 if (delay) {
3155 // From Hexen: Give the world some time to set itself up before
3156 // running open scripts.
3157 //script->state = SCRIPT_Delayed;
3158 //script->statedata = TICRATE;
3159 state = SCRIPT_Running;
3160 } else {
3161 state = SCRIPT_Running;
3162 }
3163
3164 if (!always)
3165 DACSThinker::ActiveThinker->RunningScripts[num] = this;
3166
3167 Link ();
3168
3169 DPrintf ("Script %d started.\n", num);
3170 }
3171
SetScriptState(int script,DLevelScript::EScriptState state)3172 static void SetScriptState (int script, DLevelScript::EScriptState state)
3173 {
3174 DACSThinker *controller = DACSThinker::ActiveThinker;
3175 if (!controller)
3176 return;
3177
3178 if (controller->RunningScripts[script])
3179 controller->RunningScripts[script]->SetState (state);
3180 }
3181
P_DoDeferedScripts(void)3182 void P_DoDeferedScripts (void)
3183 {
3184 acsdefered_t *def;
3185 int *scriptdata;
3186 AActor *gomo = NULL;
3187
3188 // Handle defered scripts in this step, too
3189 def = level.info->defered;
3190 while (def)
3191 {
3192 acsdefered_t *next = def->next;
3193 switch (def->type)
3194 {
3195 case acsdefered_t::defexecute:
3196 case acsdefered_t::defexealways:
3197 scriptdata = level.behavior->FindScript (def->script);
3198 if (scriptdata)
3199 {
3200 if ((unsigned)def->playernum < MAXPLAYERS && idplayer(def->playernum).ingame())
3201 gomo = idplayer(def->playernum).mo;
3202
3203 P_GetScriptGoing (gomo, NULL, def->script, scriptdata, 0, def->arg0, def->arg1, def->arg2, def->type == acsdefered_t::defexealways, true);
3204
3205 } else
3206 Printf (PRINT_HIGH,"P_DoDeferredScripts: Unknown script %d\n", def->script);
3207 break;
3208
3209 case acsdefered_t::defsuspend:
3210 SetScriptState (def->script, DLevelScript::SCRIPT_Suspended);
3211 DPrintf ("Defered suspend of script %d\n", def->script);
3212 break;
3213
3214 case acsdefered_t::defterminate:
3215 SetScriptState (def->script, DLevelScript::SCRIPT_PleaseRemove);
3216 DPrintf ("Defered terminate of script %d\n", def->script);
3217 break;
3218 }
3219 delete def;
3220 def = next;
3221 }
3222 level.info->defered = NULL;
3223 }
3224
addDefered(level_info_t * i,acsdefered_t::EType type,int script,int arg0,int arg1,int arg2,AActor * who)3225 static void addDefered (level_info_t *i, acsdefered_t::EType type, int script, int arg0, int arg1, int arg2, AActor *who)
3226 {
3227 if (i)
3228 {
3229 acsdefered_t *def = new acsdefered_s;
3230
3231 def->next = i->defered;
3232 def->type = type;
3233 def->script = script;
3234 def->arg0 = arg0;
3235 def->arg1 = arg1;
3236 def->arg2 = arg2;
3237 if (who != NULL && who->player != NULL)
3238 {
3239 def->playernum = who->player->id;
3240 }
3241 else
3242 {
3243 def->playernum = -1;
3244 }
3245 i->defered = def;
3246 DPrintf ("Script %d on map %s defered\n", script, i->mapname);
3247 }
3248 }
3249
P_StartScript(AActor * who,line_t * where,int script,char * map,int lineSide,int arg0,int arg1,int arg2,int always)3250 bool P_StartScript (AActor *who, line_t *where, int script, char *map, int lineSide,
3251 int arg0, int arg1, int arg2, int always)
3252 {
3253 if (!strnicmp (level.mapname, map, 8))
3254 {
3255 int *scriptdata;
3256
3257 if (level.behavior != NULL &&
3258 (scriptdata = level.behavior->FindScript (script)) != NULL)
3259 {
3260 return P_GetScriptGoing (who, where, script,
3261 scriptdata,
3262 lineSide, arg0, arg1, arg2, always, false);
3263 }
3264 else
3265 {
3266 Printf (PRINT_HIGH,"P_StartScript: Unknown script %d\n", script);
3267 }
3268 }
3269 else
3270 {
3271 addDefered (FindLevelInfo (map),
3272 always ? acsdefered_t::defexealways : acsdefered_t::defexecute,
3273 script, arg0, arg1, arg2, who);
3274 }
3275 return false;
3276 }
3277
P_SuspendScript(int script,char * map)3278 void P_SuspendScript (int script, char *map)
3279 {
3280 if (strnicmp (level.mapname, map, 8))
3281 addDefered (FindLevelInfo (map), acsdefered_t::defsuspend, script, 0, 0, 0, NULL);
3282 else
3283 SetScriptState (script, DLevelScript::SCRIPT_Suspended);
3284 }
3285
P_TerminateScript(int script,char * map)3286 void P_TerminateScript (int script, char *map)
3287 {
3288 if (strnicmp (level.mapname, map, 8))
3289 addDefered (FindLevelInfo (map), acsdefered_t::defterminate, script, 0, 0, 0, NULL);
3290 else
3291 SetScriptState (script, DLevelScript::SCRIPT_PleaseRemove);
3292 }
3293
strbin(char * str)3294 void strbin (char *str)
3295 {
3296 char *p = str, c;
3297 int i;
3298
3299 while ( (c = *p++) ) {
3300 if (c != '\\') {
3301 *str++ = c;
3302 } else {
3303 switch (*p) {
3304 case 'c':
3305 *str++ = '\x8a';
3306 break;
3307 case 'n':
3308 *str++ = '\n';
3309 break;
3310 case 't':
3311 *str++ = '\t';
3312 break;
3313 case 'r':
3314 *str++ = '\r';
3315 break;
3316 case '\n':
3317 break;
3318 case 'x':
3319 case 'X':
3320 c = 0;
3321 p++;
3322 for (i = 0; i < 2; i++) {
3323 c <<= 4;
3324 if (*p >= '0' && *p <= '9')
3325 c += *p-'0';
3326 else if (*p >= 'a' && *p <= 'f')
3327 c += 10 + *p-'a';
3328 else if (*p >= 'A' && *p <= 'F')
3329 c += 10 + *p-'A';
3330 else
3331 break;
3332 p++;
3333 }
3334 *str++ = c;
3335 break;
3336 case '0':
3337 case '1':
3338 case '2':
3339 case '3':
3340 case '4':
3341 case '5':
3342 case '6':
3343 case '7':
3344 c = 0;
3345 for (i = 0; i < 3; i++) {
3346 c <<= 3;
3347 if (*p >= '0' && *p <= '7')
3348 c += *p-'0';
3349 else
3350 break;
3351 p++;
3352 }
3353 *str++ = c;
3354 break;
3355 default:
3356 *str++ = *p;
3357 break;
3358 }
3359 p++;
3360 }
3361 }
3362 *str = 0;
3363 }
3364
operator <<(FArchive & arc,acsdefered_s * defer)3365 FArchive &operator<< (FArchive &arc, acsdefered_s *defer)
3366 {
3367 while (defer)
3368 {
3369 arc << (BYTE)1;
3370 arc << (BYTE)defer->type << defer->script
3371 << defer->arg0 << defer->arg1 << defer->arg2;
3372 defer = defer->next;
3373 }
3374 arc << (BYTE)0;
3375 return arc;
3376 }
3377
operator >>(FArchive & arc,acsdefered_s * & defertop)3378 FArchive &operator>> (FArchive &arc, acsdefered_s* &defertop)
3379 {
3380 acsdefered_s **defer = &defertop;
3381 BYTE inbyte;
3382
3383 arc >> inbyte;
3384 while (inbyte)
3385 {
3386 *defer = new acsdefered_s;
3387 arc >> inbyte;
3388 (*defer)->type = (acsdefered_s::EType)inbyte;
3389 arc >> (*defer)->script
3390 >> (*defer)->arg0 >> (*defer)->arg1 >> (*defer)->arg2;
3391 defer = &((*defer)->next);
3392 arc >> inbyte;
3393 }
3394 *defer = NULL;
3395 return arc;
3396 }
3397
3398
BEGIN_COMMAND(scriptstat)3399 BEGIN_COMMAND (scriptstat)
3400 {
3401 if (DACSThinker::ActiveThinker == NULL)
3402 {
3403 Printf (PRINT_HIGH,"No scripts are running.\n");
3404 }
3405 else
3406 {
3407 DACSThinker::ActiveThinker->DumpScriptStatus ();
3408 }
3409 }
END_COMMAND(scriptstat)3410 END_COMMAND (scriptstat)
3411
3412 void DACSThinker::DumpScriptStatus ()
3413 {
3414 static const char *stateNames[] =
3415 {
3416 "Running",
3417 "Suspended",
3418 "Delayed",
3419 "TagWait",
3420 "PolyWait",
3421 "ScriptWaitPre",
3422 "ScriptWait",
3423 "PleaseRemove"
3424 };
3425 DLevelScript *script = Scripts;
3426
3427 while (script != NULL)
3428 {
3429 Printf (PRINT_HIGH,"%d: %s\n", script->script, stateNames[script->state]);
3430 script = script->next;
3431 }
3432 }
3433
3434
3435 VERSION_CONTROL (p_acs_cpp, "$Id: p_acs.cpp 4542 2014-02-09 17:39:42Z dr_sean $")
3436
3437