1 // Emacs style mode select -*- C++ -*-
2 //---------------------------------------------------------------------------
3 //
4 // Copyright(C) 2000 Simon Howard
5 // Copyright(C) 2005-2008 Christoph Oelckers
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 2 of the License, or
10 // (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 //
21 //--------------------------------------------------------------------------
22 //
23 // Functions
24 //
25 // functions are stored as variables(see variable.c), the
26 // value being a pointer to a 'handler' function for the
27 // function. Arguments are stored in an argc/argv-style list
28 //
29 // this module contains all the handler functions for the
30 // basic FraggleScript Functions.
31 //
32 // By Simon Howard
33 //
34 //---------------------------------------------------------------------------
35 //
36 // FraggleScript is from SMMU which is under the GPL. Technically,
37 // therefore, combining the FraggleScript code with the non-free
38 // ZDoom code is a violation of the GPL.
39 //
40 // As this may be a problem for you, I hereby grant an exception to my
41 // copyright on the SMMU source (including FraggleScript). You may use
42 // any code from SMMU in (G)ZDoom, provided that:
43 //
44 // * For any binary release of the port, the source code is also made
45 // available.
46 // * The copyright notice is kept on any file containing my code.
47 //
48 //
49
50 #include "templates.h"
51 #include "p_local.h"
52 #include "t_script.h"
53 #include "s_sound.h"
54 #include "p_lnspec.h"
55 #include "m_random.h"
56 #include "c_console.h"
57 #include "c_dispatch.h"
58 #include "d_player.h"
59 #include "a_doomglobal.h"
60 #include "w_wad.h"
61 #include "gi.h"
62 #include "zstring.h"
63 #include "i_system.h"
64 #include "doomstat.h"
65 #include "g_level.h"
66 #include "v_palette.h"
67 #include "v_font.h"
68 #include "r_data/colormaps.h"
69 #include "farchive.h"
70 #include "p_setup.h"
71
72 static FRandom pr_script("FScript");
73
74
75 #define AngleToFixed(x) ((((double) x) / ((double) ANG45/45)) * FRACUNIT)
76 #define FixedToAngle(x) ((((double) x) / FRACUNIT) * ANG45/45)
77
78 // functions. FParser::SF_ means Script Function not, well.. heh, me
79
80 /////////// actually running a function /////////////
81
82 //==========================================================================
83 //
84 // The Doom actors in their original order
85 //
86 //==========================================================================
87
88 static const char * const ActorNames_init[]=
89 {
90 "DoomPlayer",
91 "ZombieMan",
92 "ShotgunGuy",
93 "Archvile",
94 "ArchvileFire",
95 "Revenant",
96 "RevenantTracer",
97 "RevenantTracerSmoke",
98 "Fatso",
99 "FatShot",
100 "ChaingunGuy",
101 "DoomImp",
102 "Demon",
103 "Spectre",
104 "Cacodemon",
105 "BaronOfHell",
106 "BaronBall",
107 "HellKnight",
108 "LostSoul",
109 "SpiderMastermind",
110 "Arachnotron",
111 "Cyberdemon",
112 "PainElemental",
113 "WolfensteinSS",
114 "CommanderKeen",
115 "BossBrain",
116 "BossEye",
117 "BossTarget",
118 "SpawnShot",
119 "SpawnFire",
120 "ExplosiveBarrel",
121 "DoomImpBall",
122 "CacodemonBall",
123 "Rocket",
124 "PlasmaBall",
125 "BFGBall",
126 "ArachnotronPlasma",
127 "BulletPuff",
128 "Blood",
129 "TeleportFog",
130 "ItemFog",
131 "TeleportDest",
132 "BFGExtra",
133 "GreenArmor",
134 "BlueArmor",
135 "HealthBonus",
136 "ArmorBonus",
137 "BlueCard",
138 "RedCard",
139 "YellowCard",
140 "YellowSkull",
141 "RedSkull",
142 "BlueSkull",
143 "Stimpack",
144 "Medikit",
145 "Soulsphere",
146 "InvulnerabilitySphere",
147 "Berserk",
148 "BlurSphere",
149 "RadSuit",
150 "Allmap",
151 "Infrared",
152 "Megasphere",
153 "Clip",
154 "ClipBox",
155 "RocketAmmo",
156 "RocketBox",
157 "Cell",
158 "CellPack",
159 "Shell",
160 "ShellBox",
161 "Backpack",
162 "BFG9000",
163 "Chaingun",
164 "Chainsaw",
165 "RocketLauncher",
166 "PlasmaRifle",
167 "Shotgun",
168 "SuperShotgun",
169 "TechLamp",
170 "TechLamp2",
171 "Column",
172 "TallGreenColumn",
173 "ShortGreenColumn",
174 "TallRedColumn",
175 "ShortRedColumn",
176 "SkullColumn",
177 "HeartColumn",
178 "EvilEye",
179 "FloatingSkull",
180 "TorchTree",
181 "BlueTorch",
182 "GreenTorch",
183 "RedTorch",
184 "ShortBlueTorch",
185 "ShortGreenTorch",
186 "ShortRedTorch",
187 "Slalagtite",
188 "TechPillar",
189 "CandleStick",
190 "Candelabra",
191 "BloodyTwitch",
192 "Meat2",
193 "Meat3",
194 "Meat4",
195 "Meat5",
196 "NonsolidMeat2",
197 "NonsolidMeat4",
198 "NonsolidMeat3",
199 "NonsolidMeat5",
200 "NonsolidTwitch",
201 "DeadCacodemon",
202 "DeadMarine",
203 "DeadZombieMan",
204 "DeadDemon",
205 "DeadLostSoul",
206 "DeadDoomImp",
207 "DeadShotgunGuy",
208 "GibbedMarine",
209 "GibbedMarineExtra",
210 "HeadsOnAStick",
211 "Gibs",
212 "HeadOnAStick",
213 "HeadCandles",
214 "DeadStick",
215 "LiveStick",
216 "BigTree",
217 "BurningBarrel",
218 "HangNoGuts",
219 "HangBNoBrain",
220 "HangTLookingDown",
221 "HangTSkull",
222 "HangTLookingUp",
223 "HangTNoBrain",
224 "ColonGibs",
225 "SmallBloodPool",
226 "BrainStem",
227 "PointPusher",
228 "PointPuller",
229 };
230
231 static const PClass * ActorTypes[countof(ActorNames_init)];
232
233 //==========================================================================
234 //
235 // Some functions that take care of the major differences between the class
236 // based actor system from ZDoom and Doom's index based one
237 //
238 //==========================================================================
239
240 //==========================================================================
241 //
242 // Gets an actor class
243 // Input can be either a class name, an actor variable or a Doom index
244 // Doom index is only supported for the original things up to MBF
245 //
246 //==========================================================================
T_GetMobjType(svalue_t arg)247 const PClass * T_GetMobjType(svalue_t arg)
248 {
249 const PClass * PClass=NULL;
250
251 if (arg.type==svt_string)
252 {
253 PClass=PClass::FindClass(arg.string);
254
255 // invalid object to spawn
256 if(!PClass) script_error("unknown object type: %s\n", arg.string.GetChars());
257 }
258 else if (arg.type==svt_mobj)
259 {
260 AActor * mo = actorvalue(arg);
261 if (mo) PClass = mo->GetClass();
262 }
263 else
264 {
265 int objtype = intvalue(arg);
266 if (objtype>=0 && objtype<int(countof(ActorTypes))) PClass=ActorTypes[objtype];
267 else PClass=NULL;
268
269 // invalid object to spawn
270 if(!PClass) script_error("unknown object type: %i\n", objtype);
271 }
272 return PClass;
273 }
274
275 //==========================================================================
276 //
277 // Gets a player index
278 // Input can be either an actor variable or an index value
279 //
280 //==========================================================================
T_GetPlayerNum(const svalue_t & arg)281 static int T_GetPlayerNum(const svalue_t &arg)
282 {
283 int playernum;
284 if(arg.type == svt_mobj)
285 {
286 if(!actorvalue(arg) || !arg.value.mobj->player)
287 {
288 // I prefer this not to make an error.
289 // This way a player function used for a non-player
290 // object will just do nothing
291 //script_error("mobj not a player!\n");
292 return -1;
293 }
294 playernum = int(arg.value.mobj->player - players);
295 }
296 else
297 playernum = intvalue(arg);
298
299 if(playernum < 0 || playernum > MAXPLAYERS)
300 {
301 return -1;
302 }
303 if(!playeringame[playernum]) // no error, just return -1
304 {
305 return -1;
306 }
307 return playernum;
308 }
309
310 //==========================================================================
311 //
312 // Finds a sector from a tag. This has been extended to allow looking for
313 // sectors directly by passing a negative value
314 //
315 //==========================================================================
316 class FSSectorTagIterator : public FSectorTagIterator
317 {
318 public:
FSSectorTagIterator(int tag)319 FSSectorTagIterator(int tag)
320 : FSectorTagIterator(tag)
321 {
322 if (tag < 0)
323 {
324 searchtag = INT_MIN;
325 start = tag == -32768? 0 : -tag < numsectors? -tag : -1;
326 }
327 }
328 };
329
T_FindFirstSectorFromTag(int tagnum)330 inline int T_FindFirstSectorFromTag(int tagnum)
331 {
332 FSSectorTagIterator it(tagnum);
333 return it.Next();
334 }
335
336
337 //==========================================================================
338 //
339 // Get an ammo type
340 // Input can be either a class name or a Doom index
341 // Doom index is only supported for the 4 original ammo types
342 //
343 //==========================================================================
T_GetAmmo(const svalue_t & t)344 static const PClass * T_GetAmmo(const svalue_t &t)
345 {
346 const char * p;
347
348 if (t.type==svt_string)
349 {
350 p=stringvalue(t);
351 }
352 else
353 {
354 // backwards compatibility with Legacy.
355 // allow only Doom's standard types here!
356 static const char * DefAmmo[]={"Clip","Shell","Cell","RocketAmmo"};
357 int ammonum = intvalue(t);
358 if(ammonum < 0 || ammonum >= 4)
359 {
360 script_error("ammo number out of range: %i", ammonum);
361 return NULL;
362 }
363 p=DefAmmo[ammonum];
364 }
365 const PClass * am=PClass::FindClass(p);
366 if (!am->IsDescendantOf(RUNTIME_CLASS(AAmmo)))
367 {
368 script_error("unknown ammo type : %s", p);
369 return NULL;
370 }
371 return am;
372
373 }
374
375 //==========================================================================
376 //
377 // Finds a sound in the sound table and adds a new entry if it isn't defined
378 // It's too bad that this is necessary but FS doesn't know about this kind
379 // of sound management.
380 //
381 //==========================================================================
T_FindSound(const char * name)382 static FSoundID T_FindSound(const char * name)
383 {
384 char buffer[40];
385 FSoundID so=S_FindSound(name);
386
387 if (so>0) return so;
388
389 // Now it gets dirty!
390
391 if (gameinfo.gametype & GAME_DoomStrifeChex)
392 {
393 mysnprintf(buffer, countof(buffer), "DS%.35s", name);
394 if (Wads.CheckNumForName(buffer, ns_sounds)<0) strcpy(buffer, name);
395 }
396 else
397 {
398 strcpy(buffer, name);
399 if (Wads.CheckNumForName(buffer, ns_sounds)<0) mysnprintf(buffer, countof(buffer), "DS%.35s", name);
400 }
401
402 int id = S_AddSound(name, buffer);
403 S_HashSounds();
404 return FSoundID(id);
405 }
406
407
408 //==========================================================================
409 //
410 // Creates a string out of a print argument list. This version does not
411 // have any length restrictions like the original FS versions had.
412 //
413 //==========================================================================
GetFormatString(int startarg)414 FString FParser::GetFormatString(int startarg)
415 {
416 FString fmt="";
417 for(int i=startarg; i<t_argc; i++) fmt += stringvalue(t_argv[i]);
418 return fmt;
419 }
420
CheckArgs(int cnt)421 bool FParser::CheckArgs(int cnt)
422 {
423 if (t_argc<cnt)
424 {
425 script_error("Insufficient parameters for '%s'\n", t_func.GetChars());
426 return false;
427 }
428 return true;
429 }
430 //==========================================================================
431
432 //FUNCTIONS
433
434 // the actual handler functions for the
435 // functions themselves
436
437 // arguments are evaluated and passed to the
438 // handler functions using 't_argc' and 't_argv'
439 // in a similar way to the way C does with command
440 // line options.
441
442 // values can be returned from the functions using
443 // the variable 't_return'
444 //
445 //==========================================================================
446
447 //==========================================================================
448 //
449 // prints some text to the console and the notify buffer
450 //
451 //==========================================================================
SF_Print(void)452 void FParser::SF_Print(void)
453 {
454 Printf(PRINT_HIGH, "%s\n", GetFormatString(0).GetChars());
455 }
456
457
458 //==========================================================================
459 //
460 // return a random number from 0 to 255
461 //
462 //==========================================================================
SF_Rnd(void)463 void FParser::SF_Rnd(void)
464 {
465 t_return.type = svt_int;
466 t_return.value.i = pr_script();
467 }
468
469 //==========================================================================
470 //
471 // looping section. using the rover, find the highest level
472 // loop we are currently in and return the DFsSection for it.
473 //
474 //==========================================================================
475
looping_section()476 DFsSection *FParser::looping_section()
477 {
478 DFsSection *best = NULL; // highest level loop we're in
479 // that has been found so far
480 int n;
481
482 // check thru all the hashchains
483 SDWORD rover_index = Script->MakeIndex(Rover);
484
485 for(n=0; n<SECTIONSLOTS; n++)
486 {
487 DFsSection *current = Script->sections[n];
488
489 // check all the sections in this hashchain
490 while(current)
491 {
492 // a loop?
493
494 if(current->type == st_loop)
495 // check to see if it's a loop that we're inside
496 if(rover_index >= current->start_index && rover_index <= current->end_index)
497 {
498 // a higher nesting level than the best one so far?
499 if(!best || (current->start_index > best->start_index))
500 best = current; // save it
501 }
502 current = current->next;
503 }
504 }
505
506 return best; // return the best one found
507 }
508
509 //==========================================================================
510 //
511 // "continue;" in FraggleScript is a function
512 //
513 //==========================================================================
514
SF_Continue(void)515 void FParser::SF_Continue(void)
516 {
517 DFsSection *section;
518
519 if(!(section = looping_section()) ) // no loop found
520 {
521 script_error("continue() not in loop\n");
522 return;
523 }
524
525 Rover = Script->SectionEnd(section); // jump to the closing brace
526 }
527
528 //==========================================================================
529 //
530 //
531 //
532 //==========================================================================
533
SF_Break(void)534 void FParser::SF_Break(void)
535 {
536 DFsSection *section;
537
538 if(!(section = looping_section()) )
539 {
540 script_error("break() not in loop\n");
541 return;
542 }
543
544 Rover = Script->SectionEnd(section) + 1; // jump out of the loop
545 }
546
547 //==========================================================================
548 //
549 //
550 //
551 //==========================================================================
552
SF_Goto(void)553 void FParser::SF_Goto(void)
554 {
555 if (CheckArgs(1))
556 {
557 // check argument is a labelptr
558
559 if(t_argv[0].type != svt_label)
560 {
561 script_error("goto argument not a label\n");
562 return;
563 }
564
565 // go there then if everythings fine
566 Rover = Script->LabelValue(t_argv[0]);
567 }
568 }
569
570 //==========================================================================
571 //
572 //
573 //
574 //==========================================================================
575
SF_Return(void)576 void FParser::SF_Return(void)
577 {
578 throw CFsTerminator();
579 }
580
581 //==========================================================================
582 //
583 //
584 //
585 //==========================================================================
586
SF_Include(void)587 void FParser::SF_Include(void)
588 {
589 char tempstr[12];
590
591 if (CheckArgs(1))
592 {
593 if(t_argv[0].type == svt_string)
594 {
595 strncpy(tempstr, t_argv[0].string, 8);
596 tempstr[8]=0;
597 }
598 else
599 mysnprintf(tempstr, countof(tempstr), "%i", (int)t_argv[0].value.i);
600
601 Script->ParseInclude(tempstr);
602 }
603 }
604
605 //==========================================================================
606 //
607 //
608 //
609 //==========================================================================
610
SF_Input(void)611 void FParser::SF_Input(void)
612 {
613 Printf(PRINT_BOLD,"input() function not available in doom\n");
614 }
615
616 //==========================================================================
617 //
618 //
619 //
620 //==========================================================================
621
SF_Beep(void)622 void FParser::SF_Beep(void)
623 {
624 S_Sound(CHAN_AUTO, "misc/chat", 1.0f, ATTN_IDLE);
625 }
626
627 //==========================================================================
628 //
629 //
630 //
631 //==========================================================================
632
SF_Clock(void)633 void FParser::SF_Clock(void)
634 {
635 t_return.type = svt_int;
636 t_return.value.i = (gametic*100)/TICRATE;
637 }
638
639 /**************** doom stuff ****************/
640
641 //==========================================================================
642 //
643 //
644 //
645 //==========================================================================
646
SF_ExitLevel(void)647 void FParser::SF_ExitLevel(void)
648 {
649 G_ExitLevel(0, false);
650 }
651
652 //==========================================================================
653 //
654 //
655 //
656 //==========================================================================
657
SF_Tip(void)658 void FParser::SF_Tip(void)
659 {
660 if (t_argc>0 && Script->trigger &&
661 Script->trigger->CheckLocalView(consoleplayer))
662 {
663 C_MidPrint(SmallFont, GetFormatString(0).GetChars());
664 }
665 }
666
667 //==========================================================================
668 //
669 // FParser::SF_TimedTip
670 //
671 // Implements: void timedtip(int clocks, ...)
672 //
673 //==========================================================================
674
EXTERN_CVAR(Float,con_midtime)675 EXTERN_CVAR(Float, con_midtime)
676
677 void FParser::SF_TimedTip(void)
678 {
679 if (CheckArgs(2))
680 {
681 float saved = con_midtime;
682 con_midtime = intvalue(t_argv[0])/100.0f;
683 C_MidPrint(SmallFont, GetFormatString(1).GetChars());
684 con_midtime=saved;
685 }
686 }
687
688
689 //==========================================================================
690 //
691 // tip to a particular player
692 //
693 //==========================================================================
694
SF_PlayerTip(void)695 void FParser::SF_PlayerTip(void)
696 {
697 if (CheckArgs(1))
698 {
699 int plnum = T_GetPlayerNum(t_argv[0]);
700 if (plnum!=-1 && players[plnum].mo->CheckLocalView(consoleplayer))
701 {
702 C_MidPrint(SmallFont, GetFormatString(1).GetChars());
703 }
704 }
705 }
706
707 //==========================================================================
708 //
709 // message player
710 //
711 //==========================================================================
712
SF_Message(void)713 void FParser::SF_Message(void)
714 {
715 if (t_argc>0 && Script->trigger &&
716 Script->trigger->CheckLocalView(consoleplayer))
717 {
718 Printf(PRINT_HIGH, "%s\n", GetFormatString(0).GetChars());
719 }
720 }
721
722 //==========================================================================
723 //
724 // message to a particular player
725 //
726 //==========================================================================
727
SF_PlayerMsg(void)728 void FParser::SF_PlayerMsg(void)
729 {
730 if (CheckArgs(1))
731 {
732 int plnum = T_GetPlayerNum(t_argv[0]);
733 if (plnum!=-1 && players[plnum].mo->CheckLocalView(consoleplayer))
734 {
735 Printf(PRINT_HIGH, "%s\n", GetFormatString(1).GetChars());
736 }
737 }
738 }
739
740 //==========================================================================
741 //
742 //
743 //
744 //==========================================================================
745
SF_PlayerInGame(void)746 void FParser::SF_PlayerInGame(void)
747 {
748 if (CheckArgs(1))
749 {
750 int plnum = T_GetPlayerNum(t_argv[0]);
751
752 if (plnum!=-1)
753 {
754 t_return.type = svt_int;
755 t_return.value.i = playeringame[plnum];
756 }
757 }
758 }
759
760 //==========================================================================
761 //
762 //
763 //
764 //==========================================================================
765
SF_PlayerName(void)766 void FParser::SF_PlayerName(void)
767 {
768 int plnum;
769
770 if(!t_argc)
771 {
772 player_t *pl=NULL;
773 if (Script->trigger) pl = Script->trigger->player;
774 if(pl) plnum = int(pl - players);
775 else plnum=-1;
776 }
777 else
778 plnum = T_GetPlayerNum(t_argv[0]);
779
780 if(plnum !=-1)
781 {
782 t_return.type = svt_string;
783 t_return.string = players[plnum].userinfo.GetName();
784 }
785 else
786 {
787 script_error("script not started by player\n");
788 }
789 }
790
791 //==========================================================================
792 //
793 // object being controlled by player
794 //
795 //==========================================================================
796
SF_PlayerObj(void)797 void FParser::SF_PlayerObj(void)
798 {
799 int plnum;
800
801 if(!t_argc)
802 {
803 player_t *pl=NULL;
804 if (Script->trigger) pl = Script->trigger->player;
805 if(pl) plnum = int(pl - players);
806 else plnum=-1;
807 }
808 else
809 plnum = T_GetPlayerNum(t_argv[0]);
810
811 if(plnum !=-1)
812 {
813 t_return.type = svt_mobj;
814 t_return.value.mobj = players[plnum].mo;
815 }
816 else
817 {
818 script_error("script not started by player\n");
819 }
820 }
821
822 //==========================================================================
823 //
824 //
825 //
826 //==========================================================================
827
SF_Player(void)828 void FParser::SF_Player(void)
829 {
830 // use trigger object if not specified
831 AActor *mo;
832 if(t_argc)
833 {
834 mo = actorvalue(t_argv[0]);
835 }
836 else
837 {
838 mo = Script->trigger;
839 }
840
841 t_return.type = svt_int;
842
843 if(mo && mo->player) // haleyjd: added mo->player
844 {
845 t_return.value.i = (int)(mo->player - players);
846 }
847 else
848 {
849 t_return.value.i = -1;
850 }
851 }
852
853 //==========================================================================
854 //
855 // FParser::SF_Spawn
856 //
857 // Implements: mobj spawn(int type, int x, int y, [int angle], [int z], [bool zrel])
858 //
859 //==========================================================================
860
SF_Spawn(void)861 void FParser::SF_Spawn(void)
862 {
863 int x, y, z;
864 const PClass *PClass;
865 angle_t angle = 0;
866
867 if (CheckArgs(3))
868 {
869 if (!(PClass=T_GetMobjType(t_argv[0]))) return;
870
871 x = fixedvalue(t_argv[1]);
872 y = fixedvalue(t_argv[2]);
873
874 if(t_argc >= 5)
875 {
876 z = fixedvalue(t_argv[4]);
877 // [Graf Zahl] added option of spawning with a relative z coordinate
878 if(t_argc > 5)
879 {
880 if (intvalue(t_argv[5])) z+=P_PointInSector(x, y)->floorplane.ZatPoint(x,y);
881 }
882 }
883 else
884 {
885 // Legacy compatibility is more important than correctness.
886 z = ONFLOORZ;// (GetDefaultByType(PClass)->flags & MF_SPAWNCEILING) ? ONCEILINGZ : ONFLOORZ;
887 }
888
889 if(t_argc >= 4)
890 {
891 angle = intvalue(t_argv[3]) * (SQWORD)ANG45 / 45;
892 }
893
894 t_return.type = svt_mobj;
895 t_return.value.mobj = Spawn(PClass, x, y, z, ALLOW_REPLACE);
896
897 if (t_return.value.mobj)
898 {
899 t_return.value.mobj->angle = angle;
900
901 if (!DFraggleThinker::ActiveThinker->nocheckposition)
902 {
903 if (!P_TestMobjLocation(t_return.value.mobj))
904 {
905 if (t_return.value.mobj->flags&MF_COUNTKILL) level.total_monsters--;
906 if (t_return.value.mobj->flags&MF_COUNTITEM) level.total_items--;
907 t_return.value.mobj->Destroy();
908 t_return.value.mobj = NULL;
909 }
910 }
911 }
912 }
913 }
914
915 //==========================================================================
916 //
917 //
918 //
919 //==========================================================================
920
SF_RemoveObj(void)921 void FParser::SF_RemoveObj(void)
922 {
923 if (CheckArgs(1))
924 {
925 AActor * mo = actorvalue(t_argv[0]);
926 if(mo) // nullptr check
927 {
928 mo->ClearCounters();
929 mo->Destroy();
930 }
931 }
932 }
933
934 //==========================================================================
935 //
936 //
937 //
938 //==========================================================================
939
SF_KillObj(void)940 void FParser::SF_KillObj(void)
941 {
942 // use trigger object if not specified
943 AActor *mo;
944 if(t_argc)
945 {
946 mo = actorvalue(t_argv[0]);
947 }
948 else
949 {
950 mo = Script->trigger; // default to trigger object
951 }
952
953 if(mo)
954 {
955 // ensure the thing can be killed
956 mo->flags|=MF_SHOOTABLE;
957 mo->flags2&=~(MF2_INVULNERABLE|MF2_DORMANT);
958 // [GrafZahl] This called P_KillMobj directly
959 // which is a very bad thing to do!
960 P_DamageMobj(mo, NULL, NULL, mo->health, NAME_Massacre);
961 }
962 }
963
964
965 //==========================================================================
966 //
967 //
968 //
969 //==========================================================================
970
SF_ObjX(void)971 void FParser::SF_ObjX(void)
972 {
973 // use trigger object if not specified
974 AActor *mo;
975 if(t_argc)
976 {
977 mo = actorvalue(t_argv[0]);
978 }
979 else
980 {
981 mo = Script->trigger;
982 }
983
984 t_return.type = svt_fixed; // haleyjd: SoM's fixed-point fix
985 t_return.value.f = mo ? mo->X() : 0; // null ptr check
986 }
987
988 //==========================================================================
989 //
990 //
991 //
992 //==========================================================================
993
SF_ObjY(void)994 void FParser::SF_ObjY(void)
995 {
996 // use trigger object if not specified
997 AActor *mo;
998 if(t_argc)
999 {
1000 mo = actorvalue(t_argv[0]);
1001 }
1002 else
1003 {
1004 mo = Script->trigger;
1005 }
1006
1007 t_return.type = svt_fixed; // haleyjd
1008 t_return.value.f = mo ? mo->Y() : 0; // null ptr check
1009 }
1010
1011 //==========================================================================
1012 //
1013 //
1014 //
1015 //==========================================================================
1016
SF_ObjZ(void)1017 void FParser::SF_ObjZ(void)
1018 {
1019 // use trigger object if not specified
1020 AActor *mo;
1021 if(t_argc)
1022 {
1023 mo = actorvalue(t_argv[0]);
1024 }
1025 else
1026 {
1027 mo = Script->trigger;
1028 }
1029
1030 t_return.type = svt_fixed; // haleyjd
1031 t_return.value.f = mo ? mo->Z() : 0; // null ptr check
1032 }
1033
1034
1035 //==========================================================================
1036 //
1037 //
1038 //
1039 //==========================================================================
1040
SF_ObjAngle(void)1041 void FParser::SF_ObjAngle(void)
1042 {
1043 // use trigger object if not specified
1044 AActor *mo;
1045 if(t_argc)
1046 {
1047 mo = actorvalue(t_argv[0]);
1048 }
1049 else
1050 {
1051 mo = Script->trigger;
1052 }
1053
1054 t_return.type = svt_fixed; // haleyjd: fixed-point -- SoM again :)
1055 t_return.value.f = mo ? (fixed_t)AngleToFixed(mo->angle) : 0; // null ptr check
1056 }
1057
1058
1059 //==========================================================================
1060 //
1061 //
1062 //
1063 //==========================================================================
1064
1065 // teleport: object, sector_tag
SF_Teleport(void)1066 void FParser::SF_Teleport(void)
1067 {
1068 int tag;
1069 AActor *mo;
1070
1071 if (CheckArgs(1))
1072 {
1073 if(t_argc == 1) // 1 argument: sector tag
1074 {
1075 mo = Script->trigger; // default to trigger
1076 tag = intvalue(t_argv[0]);
1077 }
1078 else // 2 or more
1079 { // teleport a given object
1080 mo = actorvalue(t_argv[0]);
1081 tag = intvalue(t_argv[1]);
1082 }
1083
1084 if(mo)
1085 EV_Teleport(0, tag, NULL, 0, mo, TELF_DESTFOG | TELF_SOURCEFOG);
1086 }
1087 }
1088
1089 //==========================================================================
1090 //
1091 //
1092 //
1093 //==========================================================================
1094
SF_SilentTeleport(void)1095 void FParser::SF_SilentTeleport(void)
1096 {
1097 int tag;
1098 AActor *mo;
1099
1100 if (CheckArgs(1))
1101 {
1102 if(t_argc == 1) // 1 argument: sector tag
1103 {
1104 mo = Script->trigger; // default to trigger
1105 tag = intvalue(t_argv[0]);
1106 }
1107 else // 2 or more
1108 { // teleport a given object
1109 mo = actorvalue(t_argv[0]);
1110 tag = intvalue(t_argv[1]);
1111 }
1112
1113 if(mo)
1114 EV_Teleport(0, tag, NULL, 0, mo, TELF_KEEPORIENTATION);
1115 }
1116 }
1117
1118 //==========================================================================
1119 //
1120 //
1121 //
1122 //==========================================================================
1123
SF_DamageObj(void)1124 void FParser::SF_DamageObj(void)
1125 {
1126 AActor *mo;
1127 int damageamount;
1128
1129 if (CheckArgs(1))
1130 {
1131 if(t_argc == 1) // 1 argument: damage trigger by amount
1132 {
1133 mo = Script->trigger; // default to trigger
1134 damageamount = intvalue(t_argv[0]);
1135 }
1136 else // 2 or more
1137 { // damage a given object
1138 mo = actorvalue(t_argv[0]);
1139 damageamount = intvalue(t_argv[1]);
1140 }
1141
1142 if(mo)
1143 P_DamageMobj(mo, NULL, Script->trigger, damageamount, NAME_None);
1144 }
1145 }
1146
1147 //==========================================================================
1148 //
1149 //
1150 //
1151 //==========================================================================
1152
1153 // the tag number of the sector the thing is in
SF_ObjSector(void)1154 void FParser::SF_ObjSector(void)
1155 {
1156 // use trigger object if not specified
1157 AActor *mo;
1158 if(t_argc)
1159 {
1160 mo = actorvalue(t_argv[0]);
1161 }
1162 else
1163 {
1164 mo = Script->trigger;
1165 }
1166
1167 t_return.type = svt_int;
1168 t_return.value.i = mo ? tagManager.GetFirstSectorTag(mo->Sector) : 0; // nullptr check
1169 }
1170
1171 //==========================================================================
1172 //
1173 //
1174 //
1175 //==========================================================================
1176
1177 // the health number of an object
SF_ObjHealth(void)1178 void FParser::SF_ObjHealth(void)
1179 {
1180 // use trigger object if not specified
1181 AActor *mo;
1182 if(t_argc)
1183 {
1184 mo = actorvalue(t_argv[0]);
1185 }
1186 else
1187 {
1188 mo = Script->trigger;
1189 }
1190
1191 t_return.type = svt_int;
1192 t_return.value.i = mo ? mo->health : 0;
1193 }
1194
1195 //==========================================================================
1196 //
1197 //
1198 //
1199 //==========================================================================
1200
SF_ObjFlag(void)1201 void FParser::SF_ObjFlag(void)
1202 {
1203 AActor *mo;
1204 int flagnum;
1205
1206 if (CheckArgs(1))
1207 {
1208 if(t_argc == 1) // use trigger, 1st is flag
1209 {
1210 // use trigger:
1211 mo = Script->trigger;
1212 flagnum = intvalue(t_argv[0]);
1213 }
1214 else if(t_argc == 2) // specified object
1215 {
1216 mo = actorvalue(t_argv[0]);
1217 flagnum = intvalue(t_argv[1]);
1218 }
1219 else // >= 3 : SET flags
1220 {
1221 mo = actorvalue(t_argv[0]);
1222 flagnum = intvalue(t_argv[1]);
1223
1224 if(mo && flagnum<26) // nullptr check
1225 {
1226 DWORD tempflags = mo->flags;
1227
1228 // remove old bit
1229 tempflags &= ~(1 << flagnum);
1230
1231 // make the new flag
1232 tempflags |= (!!intvalue(t_argv[2])) << flagnum;
1233 mo->flags = ActorFlags::FromInt (tempflags);
1234 }
1235 }
1236 t_return.type = svt_int;
1237 if (mo && flagnum<26)
1238 {
1239 t_return.value.i = !!(mo->flags & ActorFlags::FromInt(1 << flagnum));
1240 }
1241 else t_return.value.i = 0;
1242 }
1243 }
1244
1245 //==========================================================================
1246 //
1247 //
1248 //
1249 //==========================================================================
1250
1251 // apply momentum to a thing
SF_PushThing(void)1252 void FParser::SF_PushThing(void)
1253 {
1254 if (CheckArgs(3))
1255 {
1256 AActor * mo = actorvalue(t_argv[0]);
1257 if(!mo) return;
1258
1259 angle_t angle = (angle_t)FixedToAngle(fixedvalue(t_argv[1]));
1260 fixed_t force = fixedvalue(t_argv[2]);
1261
1262 P_ThrustMobj(mo, angle, force);
1263 }
1264 }
1265
1266 //==========================================================================
1267 //
1268 // FParser::SF_ReactionTime -- useful for freezing things
1269 //
1270 //==========================================================================
1271
1272
SF_ReactionTime(void)1273 void FParser::SF_ReactionTime(void)
1274 {
1275 if (CheckArgs(1))
1276 {
1277 AActor *mo = actorvalue(t_argv[0]);
1278
1279 if(t_argc > 1)
1280 {
1281 if(mo) mo->reactiontime = (intvalue(t_argv[1]) * TICRATE) / 100;
1282 }
1283
1284 t_return.type = svt_int;
1285 t_return.value.i = mo ? mo->reactiontime : 0;
1286 }
1287 }
1288
1289 //==========================================================================
1290 //
1291 // FParser::SF_MobjTarget -- sets a thing's target field
1292 //
1293 //==========================================================================
1294
1295 // Sets a mobj's Target! >:)
SF_MobjTarget(void)1296 void FParser::SF_MobjTarget(void)
1297 {
1298 AActor* mo;
1299 AActor* target;
1300
1301 if (CheckArgs(1))
1302 {
1303 mo = actorvalue(t_argv[0]);
1304 if(t_argc > 1)
1305 {
1306 target = actorvalue(t_argv[1]);
1307 if(mo && target && mo->SeeState) // haleyjd: added target check -- no NULL allowed
1308 {
1309 mo->target=target;
1310 mo->SetState(mo->SeeState);
1311 mo->flags|=MF_JUSTHIT;
1312 }
1313 }
1314
1315 t_return.type = svt_mobj;
1316 t_return.value.mobj = mo ? mo->target : NULL;
1317 }
1318 }
1319
1320 //==========================================================================
1321 //
1322 // FParser::SF_MobjMomx, MobjMomy, MobjMomz -- momentum functions
1323 //
1324 //==========================================================================
1325
SF_MobjMomx(void)1326 void FParser::SF_MobjMomx(void)
1327 {
1328 AActor* mo;
1329
1330 if (CheckArgs(1))
1331 {
1332 mo = actorvalue(t_argv[0]);
1333 if(t_argc > 1)
1334 {
1335 if(mo)
1336 mo->velx = fixedvalue(t_argv[1]);
1337 }
1338
1339 t_return.type = svt_fixed;
1340 t_return.value.f = mo ? mo->velx : 0;
1341 }
1342 }
1343
1344 //==========================================================================
1345 //
1346 //
1347 //
1348 //==========================================================================
1349
SF_MobjMomy(void)1350 void FParser::SF_MobjMomy(void)
1351 {
1352 AActor* mo;
1353
1354 if (CheckArgs(1))
1355 {
1356 mo = actorvalue(t_argv[0]);
1357 if(t_argc > 1)
1358 {
1359 if(mo)
1360 mo->vely = fixedvalue(t_argv[1]);
1361 }
1362
1363 t_return.type = svt_fixed;
1364 t_return.value.f = mo ? mo->vely : 0;
1365 }
1366 }
1367
1368 //==========================================================================
1369 //
1370 //
1371 //
1372 //==========================================================================
1373
SF_MobjMomz(void)1374 void FParser::SF_MobjMomz(void)
1375 {
1376 AActor* mo;
1377
1378 if (CheckArgs(1))
1379 {
1380 mo = actorvalue(t_argv[0]);
1381 if(t_argc > 1)
1382 {
1383 if(mo)
1384 mo->velz = fixedvalue(t_argv[1]);
1385 }
1386
1387 t_return.type = svt_fixed;
1388 t_return.value.f = mo ? mo->velz : 0;
1389 }
1390 }
1391
1392
1393 //==========================================================================
1394 //
1395 //
1396 //
1397 //==========================================================================
1398
1399 /****************** Trig *********************/
1400
SF_PointToAngle(void)1401 void FParser::SF_PointToAngle(void)
1402 {
1403 if (CheckArgs(4))
1404 {
1405 fixed_t x1 = fixedvalue(t_argv[0]);
1406 fixed_t y1 = fixedvalue(t_argv[1]);
1407 fixed_t x2 = fixedvalue(t_argv[2]);
1408 fixed_t y2 = fixedvalue(t_argv[3]);
1409
1410 angle_t angle = R_PointToAngle2(x1, y1, x2, y2);
1411
1412 t_return.type = svt_fixed;
1413 t_return.value.f = (fixed_t)AngleToFixed(angle);
1414 }
1415 }
1416
1417
1418 //==========================================================================
1419 //
1420 //
1421 //
1422 //==========================================================================
1423
SF_PointToDist(void)1424 void FParser::SF_PointToDist(void)
1425 {
1426 if (CheckArgs(4))
1427 {
1428 // Doing this in floating point is actually faster with modern computers!
1429 double x = floatvalue(t_argv[2]) - floatvalue(t_argv[0]);
1430 double y = floatvalue(t_argv[3]) - floatvalue(t_argv[1]);
1431
1432 t_return.type = svt_fixed;
1433 t_return.value.f = FLOAT2FIXED(sqrt(x*x+y*y));
1434 }
1435 }
1436
1437
1438 //==========================================================================
1439 //
1440 // setcamera(obj, [angle], [height], [pitch])
1441 //
1442 // [GrafZahl] This is a complete rewrite.
1443 // Although both Eternity and Legacy implement this function
1444 // they are mutually incompatible with each other and with ZDoom...
1445 //
1446 //==========================================================================
1447
SF_SetCamera(void)1448 void FParser::SF_SetCamera(void)
1449 {
1450 angle_t angle;
1451 player_t * player;
1452 AActor * newcamera;
1453
1454 if (CheckArgs(1))
1455 {
1456 player=Script->trigger->player;
1457 if (!player) player=&players[0];
1458
1459 newcamera = actorvalue(t_argv[0]);
1460 if(!newcamera)
1461 {
1462 script_error("invalid location object for camera\n");
1463 return; // nullptr check
1464 }
1465
1466 angle = t_argc < 2 ? newcamera->angle : (fixed_t)FixedToAngle(fixedvalue(t_argv[1]));
1467
1468 newcamera->special1=newcamera->angle;
1469 newcamera->special2=newcamera->Z();
1470 newcamera->SetZ(t_argc < 3 ? (newcamera->Z() + (41 << FRACBITS)) : (intvalue(t_argv[2]) << FRACBITS));
1471 newcamera->angle = angle;
1472 if(t_argc < 4) newcamera->pitch = 0;
1473 else
1474 {
1475 fixed_t pitch = fixedvalue(t_argv[3]);
1476 if(pitch < -50*FRACUNIT) pitch = -50*FRACUNIT;
1477 if(pitch > 50*FRACUNIT) pitch = 50*FRACUNIT;
1478 newcamera->pitch=(angle_t)((pitch/65536.0f)*(ANGLE_45/45.0f)*(20.0f/32.0f));
1479 }
1480 player->camera=newcamera;
1481 }
1482 }
1483
1484 //==========================================================================
1485 //
1486 //
1487 //
1488 //==========================================================================
1489
SF_ClearCamera(void)1490 void FParser::SF_ClearCamera(void)
1491 {
1492 player_t * player;
1493 player=Script->trigger->player;
1494 if (!player) player=&players[0];
1495
1496 AActor * cam=player->camera;
1497 if (cam)
1498 {
1499 player->camera=player->mo;
1500 cam->angle=cam->special1;
1501 cam->SetZ(cam->special2);
1502 }
1503
1504 }
1505
1506
1507
1508 /*********** sounds ******************/
1509
1510 //==========================================================================
1511 //
1512 //
1513 //
1514 //==========================================================================
1515
1516 // start sound from thing
SF_StartSound(void)1517 void FParser::SF_StartSound(void)
1518 {
1519 AActor *mo;
1520
1521 if (CheckArgs(2))
1522 {
1523 mo = actorvalue(t_argv[0]);
1524
1525 if (mo)
1526 {
1527 S_Sound(mo, CHAN_BODY, T_FindSound(stringvalue(t_argv[1])), 1, ATTN_NORM);
1528 }
1529 }
1530 }
1531
1532 //==========================================================================
1533 //
1534 //
1535 //
1536 //==========================================================================
1537
1538 // start sound from sector
SF_StartSectorSound(void)1539 void FParser::SF_StartSectorSound(void)
1540 {
1541 sector_t *sector;
1542 int tagnum;
1543
1544 if (CheckArgs(2))
1545 {
1546 tagnum = intvalue(t_argv[0]);
1547
1548 int i=-1;
1549 FSSectorTagIterator itr(tagnum);
1550 while ((i = itr.Next()) >= 0)
1551 {
1552 sector = §ors[i];
1553 S_Sound(sector, CHAN_BODY, T_FindSound(stringvalue(t_argv[1])), 1.0f, ATTN_NORM);
1554 }
1555 }
1556 }
1557
1558 /************* Sector functions ***************/
1559
1560 //DMover::EResult P_MoveFloor (sector_t * m_Sector, fixed_t speed, fixed_t dest, int crush, int direction, int flags=0);
1561 //DMover::EResult P_MoveCeiling (sector_t * m_Sector, fixed_t speed, fixed_t dest, int crush, int direction, int flags=0);
1562
1563 class DFloorChanger : public DFloor
1564 {
1565 public:
DFloorChanger(sector_t * sec)1566 DFloorChanger(sector_t * sec)
1567 : DFloor(sec) {}
1568
Move(fixed_t speed,fixed_t dest,int crush,int direction)1569 bool Move(fixed_t speed, fixed_t dest, int crush, int direction)
1570 {
1571 bool res = DMover::crushed != MoveFloor(speed, dest, crush, direction, false);
1572 Destroy();
1573 m_Sector->floordata=NULL;
1574 StopInterpolation(true);
1575 m_Sector=NULL;
1576 return res;
1577 }
1578 };
1579
1580
1581 //==========================================================================
1582 //
1583 //
1584 //
1585 //==========================================================================
1586
1587 // floor height of sector
SF_FloorHeight(void)1588 void FParser::SF_FloorHeight(void)
1589 {
1590 int tagnum;
1591 int secnum;
1592 fixed_t dest;
1593 int returnval = 1; // haleyjd: SoM's fixes
1594
1595 if (CheckArgs(1))
1596 {
1597 tagnum = intvalue(t_argv[0]);
1598
1599 if(t_argc > 1) // > 1: set floor height
1600 {
1601 int i;
1602 int crush = (t_argc >= 3) ? intvalue(t_argv[2]) : false;
1603
1604 i = -1;
1605 dest = fixedvalue(t_argv[1]);
1606
1607 // set all sectors with tag
1608
1609 FSSectorTagIterator itr(tagnum);
1610 while ((i = itr.Next()) >= 0)
1611 {
1612 if (sectors[i].floordata) continue; // don't move floors that are active!
1613
1614 DFloorChanger * f = new DFloorChanger(§ors[i]);
1615 if (!f->Move(
1616 abs(dest - sectors[i].CenterFloor()),
1617 sectors[i].floorplane.PointToDist (CenterSpot(§ors[i]), dest),
1618 crush? 10:-1,
1619 (dest > sectors[i].CenterFloor()) ? 1 : -1))
1620 {
1621 returnval = 0;
1622 }
1623 }
1624 }
1625 else
1626 {
1627 secnum = T_FindFirstSectorFromTag(tagnum);
1628 if(secnum < 0)
1629 {
1630 script_error("sector not found with tagnum %i\n", tagnum);
1631 return;
1632 }
1633 returnval = sectors[secnum].CenterFloor() >> FRACBITS;
1634 }
1635
1636 // return floor height
1637
1638 t_return.type = svt_int;
1639 t_return.value.i = returnval;
1640 }
1641 }
1642
1643
1644 //=============================================================================
1645 //
1646 //
1647 //=============================================================================
1648 class DMoveFloor : public DFloor
1649 {
1650 public:
DMoveFloor(sector_t * sec,int moveheight,int _m_Direction,int crush,int movespeed)1651 DMoveFloor(sector_t * sec,int moveheight,int _m_Direction,int crush,int movespeed)
1652 : DFloor(sec)
1653 {
1654 m_Type = floorRaiseByValue;
1655 m_Crush = crush;
1656 m_Speed=movespeed;
1657 m_Direction = _m_Direction;
1658 m_FloorDestDist = moveheight;
1659
1660 // Do not interpolate instant movement floors.
1661 fixed_t movedist = abs(-sec->floorplane.d - moveheight);
1662 if (m_Speed >= movedist)
1663 {
1664 StopInterpolation(true);
1665 }
1666
1667 StartFloorSound();
1668 }
1669 };
1670
1671
1672
1673 //==========================================================================
1674 //
1675 //
1676 //
1677 //==========================================================================
1678
SF_MoveFloor(void)1679 void FParser::SF_MoveFloor(void)
1680 {
1681 int secnum = -1;
1682 sector_t *sec;
1683 int tagnum, platspeed = 1, destheight, crush;
1684
1685 if (CheckArgs(2))
1686 {
1687 tagnum = intvalue(t_argv[0]);
1688 destheight = intvalue(t_argv[1]) * FRACUNIT;
1689 platspeed = t_argc > 2 ? fixedvalue(t_argv[2]) : FRACUNIT;
1690 crush = (t_argc > 3 ? intvalue(t_argv[3]) : -1);
1691
1692 // move all sectors with tag
1693
1694 FSSectorTagIterator itr(tagnum);
1695 while ((secnum = itr.Next()) >= 0)
1696 {
1697 sec = §ors[secnum];
1698 // Don't start a second thinker on the same floor
1699 if (sec->floordata) continue;
1700
1701 new DMoveFloor(sec,sec->floorplane.PointToDist(CenterSpot(sec),destheight),
1702 destheight < sec->CenterFloor() ? -1:1,crush,platspeed);
1703 }
1704 }
1705 }
1706
1707 //==========================================================================
1708 //
1709 //
1710 //
1711 //==========================================================================
1712
1713 class DCeilingChanger : public DCeiling
1714 {
1715 public:
DCeilingChanger(sector_t * sec)1716 DCeilingChanger(sector_t * sec)
1717 : DCeiling(sec) {}
1718
Move(fixed_t speed,fixed_t dest,int crush,int direction)1719 bool Move(fixed_t speed, fixed_t dest, int crush, int direction)
1720 {
1721 bool res = DMover::crushed != MoveCeiling(speed, dest, crush, direction, false);
1722 Destroy();
1723 m_Sector->ceilingdata=NULL;
1724 StopInterpolation (true);
1725 m_Sector=NULL;
1726 return res;
1727 }
1728 };
1729
1730 //==========================================================================
1731 //
1732 //
1733 //
1734 //==========================================================================
1735
1736 // ceiling height of sector
SF_CeilingHeight(void)1737 void FParser::SF_CeilingHeight(void)
1738 {
1739 fixed_t dest;
1740 int secnum;
1741 int tagnum;
1742 int returnval = 1;
1743
1744 if (CheckArgs(1))
1745 {
1746 tagnum = intvalue(t_argv[0]);
1747
1748 if(t_argc > 1) // > 1: set ceilheight
1749 {
1750 int i;
1751 int crush = (t_argc >= 3) ? intvalue(t_argv[2]) : false;
1752
1753 i = -1;
1754 dest = fixedvalue(t_argv[1]);
1755
1756 // set all sectors with tag
1757 FSSectorTagIterator itr(tagnum);
1758 while ((i = itr.Next()) >= 0)
1759 {
1760 if (sectors[i].ceilingdata) continue; // don't move ceilings that are active!
1761
1762 DCeilingChanger * c = new DCeilingChanger(§ors[i]);
1763 if (!c->Move(
1764 abs(dest - sectors[i].CenterCeiling()),
1765 sectors[i].ceilingplane.PointToDist (CenterSpot(§ors[i]), dest),
1766 crush? 10:-1,
1767 (dest > sectors[i].CenterCeiling()) ? 1 : -1))
1768 {
1769 returnval = 0;
1770 }
1771 }
1772 }
1773 else
1774 {
1775 secnum = T_FindFirstSectorFromTag(tagnum);
1776 if(secnum < 0)
1777 {
1778 script_error("sector not found with tagnum %i\n", tagnum);
1779 return;
1780 }
1781 returnval = sectors[secnum].CenterCeiling() >> FRACBITS;
1782 }
1783
1784 // return ceiling height
1785 t_return.type = svt_int;
1786 t_return.value.i = returnval;
1787 }
1788 }
1789
1790
1791 //==========================================================================
1792 //
1793 //
1794 //
1795 //==========================================================================
1796
1797 class DMoveCeiling : public DCeiling
1798 {
1799 public:
1800
DMoveCeiling(sector_t * sec,int tag,fixed_t destheight,fixed_t speed,int silent,int crush)1801 DMoveCeiling(sector_t * sec,int tag,fixed_t destheight,fixed_t speed,int silent,int crush)
1802 : DCeiling(sec)
1803 {
1804 m_Crush = crush;
1805 m_Speed2 = m_Speed = m_Speed1 = speed;
1806 m_Silent = silent;
1807 m_Type = DCeiling::ceilLowerByValue; // doesn't really matter as long as it's no special value
1808 m_Tag=tag;
1809 vertex_t * spot=CenterSpot(sec);
1810 m_TopHeight=m_BottomHeight=sec->ceilingplane.PointToDist(spot,destheight);
1811 m_Direction=destheight>sec->GetPlaneTexZ(sector_t::ceiling)? 1:-1;
1812
1813 // Do not interpolate instant movement ceilings.
1814 fixed_t movedist = abs(sec->ceilingplane.d - m_BottomHeight);
1815 if (m_Speed >= movedist)
1816 {
1817 StopInterpolation (true);
1818 m_Silent=2;
1819 }
1820 PlayCeilingSound();
1821 }
1822 };
1823
1824
1825 //==========================================================================
1826 //
1827 //
1828 //
1829 //==========================================================================
1830
SF_MoveCeiling(void)1831 void FParser::SF_MoveCeiling(void)
1832 {
1833 int secnum = -1;
1834 sector_t *sec;
1835 int tagnum, platspeed = 1, destheight;
1836 int crush;
1837 int silent;
1838
1839 if (CheckArgs(2))
1840 {
1841 tagnum = intvalue(t_argv[0]);
1842 destheight = intvalue(t_argv[1]) * FRACUNIT;
1843 platspeed = /*FLOORSPEED **/ (t_argc > 2 ? fixedvalue(t_argv[2]) : FRACUNIT);
1844 crush=t_argc>3 ? intvalue(t_argv[3]):-1;
1845 silent=t_argc>4 ? intvalue(t_argv[4]):1;
1846
1847 // move all sectors with tag
1848 FSSectorTagIterator itr(tagnum);
1849 while ((secnum = itr.Next()) >= 0)
1850 {
1851 sec = §ors[secnum];
1852
1853 // Don't start a second thinker on the same floor
1854 if (sec->ceilingdata) continue;
1855 new DMoveCeiling(sec, tagnum, destheight, platspeed, silent, crush);
1856 }
1857 }
1858 }
1859
1860 //==========================================================================
1861 //
1862 //
1863 //
1864 //==========================================================================
1865
SF_LightLevel(void)1866 void FParser::SF_LightLevel(void)
1867 {
1868 sector_t *sector;
1869 int secnum;
1870 int tagnum;
1871
1872 if (CheckArgs(1))
1873 {
1874 tagnum = intvalue(t_argv[0]);
1875
1876 // argv is sector tag
1877 secnum = T_FindFirstSectorFromTag(tagnum);
1878
1879 if(secnum < 0)
1880 {
1881 return;
1882 }
1883
1884 sector = §ors[secnum];
1885
1886 if(t_argc > 1) // > 1: set light level
1887 {
1888 int i = -1;
1889
1890 // set all sectors with tag
1891 FSSectorTagIterator itr(tagnum);
1892 while ((i = itr.Next()) >= 0)
1893 {
1894 sectors[i].SetLightLevel(intvalue(t_argv[1]));
1895 }
1896 }
1897
1898 // return lightlevel
1899 t_return.type = svt_int;
1900 t_return.value.i = sector->lightlevel;
1901 }
1902 }
1903
1904
1905
1906 //==========================================================================
1907 //
1908 // Simple light fade - locks lightingdata. For FParser::SF_FadeLight
1909 //
1910 //==========================================================================
1911 class DLightLevel : public DLighting
1912 {
1913 DECLARE_CLASS (DLightLevel, DLighting)
1914
1915 unsigned char destlevel;
1916 unsigned char speed;
1917
DLightLevel()1918 DLightLevel() {}
1919
1920 public:
1921
1922 DLightLevel(sector_t * s,int destlevel,int speed);
1923 void Serialize (FArchive &arc);
1924 void Tick ();
Destroy()1925 void Destroy() { Super::Destroy(); m_Sector->lightingdata=NULL; }
1926 };
1927
1928
1929
IMPLEMENT_CLASS(DLightLevel)1930 IMPLEMENT_CLASS (DLightLevel)
1931
1932 void DLightLevel::Serialize (FArchive &arc)
1933 {
1934 Super::Serialize (arc);
1935 arc << destlevel << speed;
1936 if (arc.IsLoading()) m_Sector->lightingdata=this;
1937 }
1938
1939
1940 //==========================================================================
1941 // sf 13/10/99:
1942 //
1943 // T_LightFade()
1944 //
1945 // Just fade the light level in a sector to a new level
1946 //
1947 //==========================================================================
1948
Tick()1949 void DLightLevel::Tick()
1950 {
1951 Super::Tick();
1952 if(m_Sector->lightlevel < destlevel)
1953 {
1954 // increase the lightlevel
1955 if(m_Sector->lightlevel + speed >= destlevel)
1956 {
1957 // stop changing light level
1958 m_Sector->SetLightLevel(destlevel); // set to dest lightlevel
1959 Destroy();
1960 }
1961 else
1962 {
1963 m_Sector->ChangeLightLevel(speed);
1964 }
1965 }
1966 else
1967 {
1968 // decrease lightlevel
1969 if(m_Sector->lightlevel - speed <= destlevel)
1970 {
1971 // stop changing light level
1972 m_Sector->SetLightLevel(destlevel); // set to dest lightlevel
1973 Destroy();
1974 }
1975 else
1976 {
1977 m_Sector->ChangeLightLevel(-speed);
1978 }
1979 }
1980 }
1981
1982 //==========================================================================
1983 //
1984 //==========================================================================
DLightLevel(sector_t * s,int _destlevel,int _speed)1985 DLightLevel::DLightLevel(sector_t * s,int _destlevel,int _speed) : DLighting(s)
1986 {
1987 destlevel=_destlevel;
1988 speed=_speed;
1989 s->lightingdata=this;
1990 }
1991
1992 //==========================================================================
1993 // sf 13/10/99:
1994 //
1995 // P_FadeLight()
1996 //
1997 // Fade all the lights in sectors with a particular tag to a new value
1998 //
1999 //==========================================================================
SF_FadeLight(void)2000 void FParser::SF_FadeLight(void)
2001 {
2002 int sectag, destlevel, speed = 1;
2003 int i;
2004
2005 if (CheckArgs(2))
2006 {
2007 sectag = intvalue(t_argv[0]);
2008 destlevel = intvalue(t_argv[1]);
2009 speed = t_argc>2 ? intvalue(t_argv[2]) : 1;
2010
2011 FSectorTagIterator it(sectag);
2012 while ((i = it.Next()) >= 0)
2013 {
2014 if (!sectors[i].lightingdata) new DLightLevel(§ors[i],destlevel,speed);
2015 }
2016 }
2017 }
2018
2019 //==========================================================================
2020 //
2021 //
2022 //
2023 //==========================================================================
SF_FloorTexture(void)2024 void FParser::SF_FloorTexture(void)
2025 {
2026 int tagnum, secnum;
2027 sector_t *sector;
2028
2029 if (CheckArgs(1))
2030 {
2031 tagnum = intvalue(t_argv[0]);
2032
2033 // argv is sector tag
2034 secnum = T_FindFirstSectorFromTag(tagnum);
2035
2036 if(secnum < 0)
2037 { script_error("sector not found with tagnum %i\n", tagnum); return;}
2038
2039 sector = §ors[secnum];
2040
2041 if(t_argc > 1)
2042 {
2043 int i = -1;
2044 FTextureID picnum = TexMan.GetTexture(t_argv[1].string, FTexture::TEX_Flat, FTextureManager::TEXMAN_Overridable);
2045
2046 // set all sectors with tag
2047 FSSectorTagIterator itr(tagnum);
2048 while ((i = itr.Next()) >= 0)
2049 {
2050 sectors[i].SetTexture(sector_t::floor, picnum);
2051 }
2052 }
2053
2054 t_return.type = svt_string;
2055 FTexture * tex = TexMan[sector->GetTexture(sector_t::floor)];
2056 t_return.string = tex? tex->Name : "";
2057 }
2058 }
2059
2060 //==========================================================================
2061 //
2062 //
2063 //
2064 //==========================================================================
2065
SF_SectorColormap(void)2066 void FParser::SF_SectorColormap(void)
2067 {
2068 // This doesn't work properly and it never will.
2069 // Whatever was done here originally, it is incompatible
2070 // with Boom and ZDoom and doesn't work properly in Legacy either.
2071
2072 // Making it no-op is probably the best thing one can do in this case.
2073
2074 /*
2075 int tagnum, secnum;
2076 sector_t *sector;
2077 int c=2;
2078 int i = -1;
2079
2080 if(t_argc<2)
2081 { script_error("insufficient arguments to function\n"); return; }
2082
2083 tagnum = intvalue(t_argv[0]);
2084
2085 // argv is sector tag
2086 secnum = T_FindFirstSectorFromTag(tagnum);
2087
2088 if(secnum < 0)
2089 { script_error("sector not found with tagnum %i\n", tagnum); return;}
2090
2091 sector = §ors[secnum];
2092
2093 if (t_argv[1].type==svt_string)
2094 {
2095 DWORD cm = R_ColormapNumForName(t_argv[1].value.s);
2096
2097 FSSectorTagIterator itr(tagnum);
2098 while ((i = itr.Next()) >= 0)
2099 {
2100 sectors[i].midmap=cm;
2101 sectors[i].heightsec=§ors[i];
2102 }
2103 }
2104 */
2105 }
2106
2107
2108 //==========================================================================
2109 //
2110 //
2111 //
2112 //==========================================================================
2113
SF_CeilingTexture(void)2114 void FParser::SF_CeilingTexture(void)
2115 {
2116 int tagnum, secnum;
2117 sector_t *sector;
2118
2119 if (CheckArgs(1))
2120 {
2121 tagnum = intvalue(t_argv[0]);
2122
2123 // argv is sector tag
2124 secnum = T_FindFirstSectorFromTag(tagnum);
2125
2126 if(secnum < 0)
2127 { script_error("sector not found with tagnum %i\n", tagnum); return;}
2128
2129 sector = §ors[secnum];
2130
2131 if(t_argc > 1)
2132 {
2133 int i = -1;
2134 FTextureID picnum = TexMan.GetTexture(t_argv[1].string, FTexture::TEX_Flat, FTextureManager::TEXMAN_Overridable);
2135
2136 // set all sectors with tag
2137 FSSectorTagIterator itr(tagnum);
2138 while ((i = itr.Next()) >= 0)
2139 {
2140 sectors[i].SetTexture(sector_t::ceiling, picnum);
2141 }
2142 }
2143
2144 t_return.type = svt_string;
2145 FTexture * tex = TexMan[sector->GetTexture(sector_t::ceiling)];
2146 t_return.string = tex? tex->Name : "";
2147 }
2148 }
2149
2150 //==========================================================================
2151 //
2152 //
2153 //
2154 //==========================================================================
2155
SF_ChangeHubLevel(void)2156 void FParser::SF_ChangeHubLevel(void)
2157 {
2158 I_Error("FS hub system permanently disabled\n");
2159 }
2160
2161 // for start map: start new game on a particular skill
SF_StartSkill(void)2162 void FParser::SF_StartSkill(void)
2163 {
2164 I_Error("startskill is not supported by this implementation!\n");
2165 }
2166
2167 //==========================================================================
2168 //
2169 // Doors
2170 //
2171 // opendoor(sectag, [delay], [speed])
2172 //
2173 //==========================================================================
2174
SF_OpenDoor(void)2175 void FParser::SF_OpenDoor(void)
2176 {
2177 int speed, wait_time;
2178 int sectag;
2179
2180 if (CheckArgs(1))
2181 {
2182 // got sector tag
2183 sectag = intvalue(t_argv[0]);
2184 if (sectag==0) return; // tag 0 not allowed
2185
2186 // door wait time
2187 if(t_argc > 1) wait_time = (intvalue(t_argv[1]) * TICRATE) / 100;
2188 else wait_time = 0; // 0= stay open
2189
2190 // door speed
2191 if(t_argc > 2) speed = intvalue(t_argv[2]);
2192 else speed = 1; // 1= normal speed
2193
2194 EV_DoDoor(wait_time? DDoor::doorRaise:DDoor::doorOpen,NULL,NULL,sectag,2*FRACUNIT*clamp(speed,1,127),wait_time,0,0);
2195 }
2196 }
2197
2198 //==========================================================================
2199 //
2200 //
2201 //
2202 //==========================================================================
2203
SF_CloseDoor(void)2204 void FParser::SF_CloseDoor(void)
2205 {
2206 int speed;
2207 int sectag;
2208
2209 if (CheckArgs(1))
2210 {
2211 // got sector tag
2212 sectag = intvalue(t_argv[0]);
2213 if (sectag==0) return; // tag 0 not allowed
2214
2215 // door speed
2216 if(t_argc > 1) speed = intvalue(t_argv[1]);
2217 else speed = 1; // 1= normal speed
2218
2219 EV_DoDoor(DDoor::doorClose,NULL,NULL,sectag,2*FRACUNIT*clamp(speed,1,127),0,0,0);
2220 }
2221 }
2222
2223 //==========================================================================
2224 //
2225 //
2226 //
2227 //==========================================================================
2228
2229 // run console cmd
SF_RunCommand(void)2230 void FParser::SF_RunCommand(void)
2231 {
2232 FS_EmulateCmd(GetFormatString(0).LockBuffer());
2233 }
2234
2235 //==========================================================================
2236 //
2237 //
2238 //
2239 //==========================================================================
2240
SF_LineTrigger()2241 void FParser::SF_LineTrigger()
2242 {
2243 if (CheckArgs(1))
2244 {
2245 line_t line;
2246 maplinedef_t mld;
2247 mld.special=intvalue(t_argv[0]);
2248 mld.tag=t_argc > 1 ? intvalue(t_argv[1]) : 0;
2249 P_TranslateLineDef(&line, &mld);
2250 P_ExecuteSpecial(line.special, NULL, Script->trigger, false,
2251 line.args[0],line.args[1],line.args[2],line.args[3],line.args[4]);
2252 }
2253 }
2254
2255 //==========================================================================
2256 //
2257 //
2258 //
2259 //==========================================================================
FS_ChangeMusic(const char * string)2260 bool FS_ChangeMusic(const char * string)
2261 {
2262 char buffer[40];
2263
2264 if (Wads.CheckNumForName(string, ns_music)<0 || !S_ChangeMusic(string,true))
2265 {
2266 // Retry with O_ prepended to the music name, then with D_
2267 mysnprintf(buffer, countof(buffer), "O_%s", string);
2268 if (Wads.CheckNumForName(buffer, ns_music)<0 || !S_ChangeMusic(buffer,true))
2269 {
2270 mysnprintf(buffer, countof(buffer), "D_%s", string);
2271 if (Wads.CheckNumForName(buffer, ns_music)<0)
2272 {
2273 S_ChangeMusic(NULL, 0);
2274 return false;
2275 }
2276 else S_ChangeMusic(buffer,true);
2277 }
2278 }
2279 return true;
2280 }
2281
SF_ChangeMusic(void)2282 void FParser::SF_ChangeMusic(void)
2283 {
2284 if (CheckArgs(1))
2285 {
2286 FS_ChangeMusic(stringvalue(t_argv[0]));
2287 }
2288 }
2289
2290
2291 //==========================================================================
2292 //
2293 // FParser::SF_SetLineBlocking()
2294 //
2295 // Sets a line blocking or unblocking
2296 //
2297 //==========================================================================
SF_SetLineBlocking(void)2298 void FParser::SF_SetLineBlocking(void)
2299 {
2300 static unsigned short blocks[]={0,ML_BLOCKING,ML_BLOCKEVERYTHING};
2301
2302 if (CheckArgs(2))
2303 {
2304 int blocking=intvalue(t_argv[1]);
2305 if (blocking>=0 && blocking<=2)
2306 {
2307 blocking=blocks[blocking];
2308 int tag=intvalue(t_argv[0]);
2309 FLineIdIterator itr(tag);
2310 int i;
2311 while ((i = itr.Next()) >= 0)
2312 {
2313 lines[i].flags = (lines[i].flags & ~(ML_BLOCKING | ML_BLOCKEVERYTHING)) | blocking;
2314 }
2315 }
2316 }
2317 }
2318
2319 //==========================================================================
2320 //
2321 // similar, but monster blocking
2322 //
2323 //==========================================================================
2324
SF_SetLineMonsterBlocking(void)2325 void FParser::SF_SetLineMonsterBlocking(void)
2326 {
2327 if (CheckArgs(2))
2328 {
2329 int blocking = intvalue(t_argv[1]) ? ML_BLOCKMONSTERS : 0;
2330 int tag=intvalue(t_argv[0]);
2331
2332 FLineIdIterator itr(tag);
2333 int i;
2334 while ((i = itr.Next()) >= 0)
2335 {
2336 lines[i].flags = (lines[i].flags & ~ML_BLOCKMONSTERS) | blocking;
2337 }
2338 }
2339 }
2340
2341
2342
2343 //==========================================================================
2344 //
2345 //FParser::SF_SetLineTexture
2346 //
2347 // #2 in a not-so-long line of ACS-inspired functions
2348 // This one is *much* needed, IMO
2349 //
2350 // Eternity: setlinetexture(tag, side, position, texture)
2351 // Legacy: setlinetexture(tag, texture, side, sections)
2352 //
2353 //==========================================================================
2354
SF_SetLineTexture(void)2355 void FParser::SF_SetLineTexture(void)
2356 {
2357 int tag;
2358 int side;
2359 int position;
2360 const char *texture;
2361 FTextureID texturenum;
2362 int i;
2363
2364 if (CheckArgs(4))
2365 {
2366 tag = intvalue(t_argv[0]);
2367
2368 // the eternity version
2369 if (t_argv[3].type==svt_string)
2370 {
2371 side = intvalue(t_argv[1]);
2372 if(side < 0 || side > 1)
2373 {
2374 script_error("invalid side number for texture change\n");
2375 return;
2376 }
2377
2378 position = intvalue(t_argv[2]);
2379 if(position < 1 || position > 3)
2380 {
2381 script_error("invalid position for texture change\n");
2382 return;
2383 }
2384 position=3-position;
2385
2386 texture = stringvalue(t_argv[3]);
2387 texturenum = TexMan.GetTexture(texture, FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable);
2388
2389 FLineIdIterator itr(tag);
2390 while ((i = itr.Next()) >= 0)
2391 {
2392 // bad sidedef, Hexen just SEGV'd here!
2393 if (lines[i].sidedef[side] != NULL)
2394 {
2395 if (position >= 0 && position <= 2)
2396 {
2397 lines[i].sidedef[side]->SetTexture(position, texturenum);
2398 }
2399 }
2400 }
2401 }
2402 else // and an improved legacy version
2403 {
2404 FTextureID picnum = TexMan.GetTexture(t_argv[1].string, FTexture::TEX_Wall, FTextureManager::TEXMAN_Overridable);
2405 side = !!intvalue(t_argv[2]);
2406 int sections = intvalue(t_argv[3]);
2407
2408 // set all sectors with tag
2409 FLineIdIterator itr(tag);
2410 while ((i = itr.Next()) >= 0)
2411 {
2412 side_t *sided = lines[i].sidedef[side];
2413 if(sided != NULL)
2414 {
2415 if(sections & 1) sided->SetTexture(side_t::top, picnum);
2416 if(sections & 2) sided->SetTexture(side_t::mid, picnum);
2417 if(sections & 4) sided->SetTexture(side_t::bottom, picnum);
2418 }
2419 }
2420 }
2421 }
2422 }
2423
2424
2425 //==========================================================================
2426 //
2427 // SoM: Max, Min, Abs math functions.
2428 //
2429 //==========================================================================
2430
SF_Max(void)2431 void FParser::SF_Max(void)
2432 {
2433 fixed_t n1, n2;
2434
2435 if (CheckArgs(2))
2436 {
2437 n1 = fixedvalue(t_argv[0]);
2438 n2 = fixedvalue(t_argv[1]);
2439
2440 t_return.type = svt_fixed;
2441 t_return.value.f = (n1 > n2) ? n1 : n2;
2442 }
2443 }
2444
2445
2446 //==========================================================================
2447 //
2448 //
2449 //
2450 //==========================================================================
2451
SF_Min(void)2452 void FParser::SF_Min(void)
2453 {
2454 fixed_t n1, n2;
2455
2456 if (CheckArgs(1))
2457 {
2458 n1 = fixedvalue(t_argv[0]);
2459 n2 = fixedvalue(t_argv[1]);
2460
2461 t_return.type = svt_fixed;
2462 t_return.value.f = (n1 < n2) ? n1 : n2;
2463 }
2464 }
2465
2466
2467 //==========================================================================
2468 //
2469 //
2470 //
2471 //==========================================================================
2472
SF_Abs(void)2473 void FParser::SF_Abs(void)
2474 {
2475 fixed_t n1;
2476
2477 if (CheckArgs(1))
2478 {
2479 n1 = fixedvalue(t_argv[0]);
2480
2481 t_return.type = svt_fixed;
2482 t_return.value.f = (n1 < 0) ? -n1 : n1;
2483 }
2484 }
2485
2486 //==========================================================================
2487 //
2488 // FParser::SF_Gameskill, FParser::SF_Gamemode
2489 //
2490 // Access functions are more elegant for these than variables,
2491 // especially for the game mode, which doesn't exist as a numeric
2492 // variable already.
2493 //
2494 //==========================================================================
2495
SF_Gameskill(void)2496 void FParser::SF_Gameskill(void)
2497 {
2498 t_return.type = svt_int;
2499 t_return.value.i = G_SkillProperty(SKILLP_ACSReturn) + 1; // +1 for the user skill value
2500 }
2501
2502 //==========================================================================
2503 //
2504 //
2505 //
2506 //==========================================================================
2507
SF_Gamemode(void)2508 void FParser::SF_Gamemode(void)
2509 {
2510 t_return.type = svt_int;
2511 if(!multiplayer)
2512 {
2513 t_return.value.i = 0; // single-player
2514 }
2515 else if(!deathmatch)
2516 {
2517 t_return.value.i = 1; // cooperative
2518 }
2519 else
2520 t_return.value.i = 2; // deathmatch
2521 }
2522
2523 //==========================================================================
2524 //
2525 // FParser::SF_IsPlayerObj()
2526 //
2527 // A function suggested by SoM to help the script coder prevent
2528 // exceptions related to calling player functions on non-player
2529 // objects.
2530 //
2531 //==========================================================================
2532
SF_IsPlayerObj(void)2533 void FParser::SF_IsPlayerObj(void)
2534 {
2535 // use trigger object if not specified
2536 AActor *mo;
2537 if(t_argc)
2538 {
2539 mo = actorvalue(t_argv[0]);
2540 }
2541 else
2542 {
2543 mo = Script->trigger;
2544 }
2545
2546 t_return.type = svt_int;
2547 t_return.value.i = (mo && mo->player) ? 1 : 0;
2548 }
2549
2550 //============================================================================
2551 //
2552 // Inventory stuff - mostly new to GZDoom
2553 //
2554 // all the original functions are still supported but they have not
2555 // been expanded from their original and are limited as a result
2556 //
2557 // Since FraggleScript is rather hard coded to the original inventory
2558 // handling of Doom this is quite messy.
2559 //
2560 //============================================================================
2561
2562
2563 //============================================================================
2564 //
2565 // DoGiveInv
2566 //
2567 // Gives an item to a single actor.
2568 //
2569 //============================================================================
2570
FS_GiveInventory(AActor * actor,const char * type,int amount)2571 static void FS_GiveInventory (AActor *actor, const char * type, int amount)
2572 {
2573 if (amount <= 0)
2574 {
2575 return;
2576 }
2577 if (strcmp (type, "Armor") == 0)
2578 {
2579 type = "BasicArmorPickup";
2580 }
2581 const PClass * info = PClass::FindClass (type);
2582 if (info == NULL || !info->IsDescendantOf (RUNTIME_CLASS(AInventory)))
2583 {
2584 Printf ("Unknown inventory item: %s\n", type);
2585 return;
2586 }
2587
2588 AWeapon *savedPendingWeap = actor->player != NULL? actor->player->PendingWeapon : NULL;
2589 bool hadweap = actor->player != NULL ? actor->player->ReadyWeapon != NULL : true;
2590
2591 AInventory *item = static_cast<AInventory *>(Spawn (info, 0,0,0, NO_REPLACE));
2592
2593 // This shouldn't count for the item statistics!
2594 item->ClearCounters();
2595 if (info->IsDescendantOf (RUNTIME_CLASS(ABasicArmorPickup)) ||
2596 info->IsDescendantOf (RUNTIME_CLASS(ABasicArmorBonus)))
2597 {
2598 static_cast<ABasicArmorPickup*>(item)->SaveAmount *= amount;
2599 }
2600 else
2601 {
2602 item->Amount = amount;
2603 }
2604 if (!item->CallTryPickup (actor))
2605 {
2606 item->Destroy ();
2607 }
2608 // If the item was a weapon, don't bring it up automatically
2609 // unless the player was not already using a weapon.
2610 if (savedPendingWeap != NULL && hadweap)
2611 {
2612 actor->player->PendingWeapon = savedPendingWeap;
2613 }
2614 }
2615
2616 //============================================================================
2617 //
2618 // DoTakeInv
2619 //
2620 // Takes an item from a single actor.
2621 //
2622 //============================================================================
2623
FS_TakeInventory(AActor * actor,const char * type,int amount)2624 static void FS_TakeInventory (AActor *actor, const char * type, int amount)
2625 {
2626 if (strcmp (type, "Armor") == 0)
2627 {
2628 type = "BasicArmor";
2629 }
2630 if (amount <= 0)
2631 {
2632 return;
2633 }
2634 const PClass * info = PClass::FindClass (type);
2635 if (info == NULL)
2636 {
2637 return;
2638 }
2639
2640 AInventory *item = actor->FindInventory (info);
2641 if (item != NULL)
2642 {
2643 item->Amount -= amount;
2644 if (item->Amount <= 0)
2645 {
2646 // If it's not ammo, destroy it. Ammo needs to stick around, even
2647 // when it's zero for the benefit of the weapons that use it and
2648 // to maintain the maximum ammo amounts a backpack might have given.
2649 if (item->GetClass()->ParentClass != RUNTIME_CLASS(AAmmo))
2650 {
2651 item->Destroy ();
2652 }
2653 else
2654 {
2655 item->Amount = 0;
2656 }
2657 }
2658 }
2659 }
2660
2661 //============================================================================
2662 //
2663 // CheckInventory
2664 //
2665 // Returns how much of a particular item an actor has.
2666 //
2667 //============================================================================
2668
FS_CheckInventory(AActor * activator,const char * type)2669 static int FS_CheckInventory (AActor *activator, const char *type)
2670 {
2671 if (activator == NULL)
2672 return 0;
2673
2674 if (strcmp (type, "Armor") == 0)
2675 {
2676 type = "BasicArmor";
2677 }
2678 else if (strcmp (type, "Health") == 0)
2679 {
2680 return activator->health;
2681 }
2682
2683 const PClass *info = PClass::FindClass (type);
2684 AInventory *item = activator->FindInventory (info);
2685 return item ? item->Amount : 0;
2686 }
2687
2688
2689 //==========================================================================
2690 //
2691 // This function is just kept for backwards compatibility
2692 // and intentionally limited to thr standard keys!
2693 // Use Give/Take/CheckInventory instead!
2694 //
2695 //==========================================================================
2696
SF_PlayerKeys(void)2697 void FParser::SF_PlayerKeys(void)
2698 {
2699 static const char * const DoomKeys[]={"BlueCard", "YellowCard", "RedCard", "BlueSkull", "YellowSkull", "RedSkull"};
2700 int playernum, keynum, givetake;
2701 const char * keyname;
2702
2703 if (CheckArgs(2))
2704 {
2705 playernum=T_GetPlayerNum(t_argv[0]);
2706 if (playernum==-1) return;
2707
2708 keynum = intvalue(t_argv[1]);
2709 if(keynum < 0 || keynum >= 6)
2710 {
2711 script_error("key number out of range: %i\n", keynum);
2712 return;
2713 }
2714 keyname=DoomKeys[keynum];
2715
2716 if(t_argc == 2)
2717 {
2718 t_return.type = svt_int;
2719 t_return.value.i = FS_CheckInventory(players[playernum].mo, keyname);
2720 return;
2721 }
2722 else
2723 {
2724 givetake = intvalue(t_argv[2]);
2725 if(givetake) FS_GiveInventory(players[playernum].mo, keyname, 1);
2726 else FS_TakeInventory(players[playernum].mo, keyname, 1);
2727 t_return.type = svt_int;
2728 t_return.value.i = 0;
2729 }
2730 }
2731 }
2732
2733 //==========================================================================
2734 //
2735 // This function is just kept for backwards compatibility and intentionally limited!
2736 // Use Give/Take/CheckInventory instead!
2737 //
2738 //==========================================================================
2739
SF_PlayerAmmo(void)2740 void FParser::SF_PlayerAmmo(void)
2741 {
2742 int playernum, amount;
2743 const PClass * ammotype;
2744
2745 if (CheckArgs(2))
2746 {
2747 playernum=T_GetPlayerNum(t_argv[0]);
2748 if (playernum==-1) return;
2749
2750 ammotype=T_GetAmmo(t_argv[1]);
2751 if (!ammotype) return;
2752
2753 if(t_argc >= 3)
2754 {
2755 AInventory * iammo = players[playernum].mo->FindInventory(ammotype);
2756 amount = intvalue(t_argv[2]);
2757 if(amount < 0) amount = 0;
2758 if (iammo) iammo->Amount = amount;
2759 else players[playernum].mo->GiveAmmo(ammotype, amount);
2760 }
2761
2762 t_return.type = svt_int;
2763 AInventory * iammo = players[playernum].mo->FindInventory(ammotype);
2764 if (iammo) t_return.value.i = iammo->Amount;
2765 else t_return.value.i = 0;
2766 }
2767 }
2768
2769
2770 //==========================================================================
2771 //
2772 //
2773 //
2774 //==========================================================================
2775
SF_MaxPlayerAmmo()2776 void FParser::SF_MaxPlayerAmmo()
2777 {
2778 int playernum, amount;
2779 const PClass * ammotype;
2780
2781 if (CheckArgs(2))
2782 {
2783 playernum=T_GetPlayerNum(t_argv[0]);
2784 if (playernum==-1) return;
2785
2786 ammotype=T_GetAmmo(t_argv[1]);
2787 if (!ammotype) return;
2788
2789 if(t_argc == 2)
2790 {
2791 }
2792 else if(t_argc >= 3)
2793 {
2794 AAmmo * iammo = (AAmmo*)players[playernum].mo->FindInventory(ammotype);
2795 amount = intvalue(t_argv[2]);
2796 if(amount < 0) amount = 0;
2797 if (!iammo)
2798 {
2799 iammo = static_cast<AAmmo *>(Spawn (ammotype, 0, 0, 0, NO_REPLACE));
2800 iammo->Amount = 0;
2801 iammo->AttachToOwner (players[playernum].mo);
2802 }
2803 iammo->MaxAmount = amount;
2804
2805
2806 for (AInventory *item = players[playernum].mo->Inventory; item != NULL; item = item->Inventory)
2807 {
2808 if (item->IsKindOf(RUNTIME_CLASS(ABackpackItem)))
2809 {
2810 if (t_argc>=4) amount = intvalue(t_argv[3]);
2811 else amount*=2;
2812 break;
2813 }
2814 }
2815 iammo->BackpackMaxAmount=amount;
2816 }
2817
2818 t_return.type = svt_int;
2819 AInventory * iammo = players[playernum].mo->FindInventory(ammotype);
2820 if (iammo) t_return.value.i = iammo->MaxAmount;
2821 else t_return.value.i = ((AAmmo*)GetDefaultByType(ammotype))->MaxAmount;
2822 }
2823 }
2824
2825 //==========================================================================
2826 //
2827 // This function is just kept for backwards compatibility and
2828 // intentionally limited to the standard weapons!
2829 // Use Give/Take/CheckInventory instead!
2830 //
2831 //==========================================================================
2832
SF_PlayerWeapon()2833 void FParser::SF_PlayerWeapon()
2834 {
2835 static const char * const WeaponNames[]={
2836 "Fist", "Pistol", "Shotgun", "Chaingun", "RocketLauncher",
2837 "PlasmaRifle", "BFG9000", "Chainsaw", "SuperShotgun" };
2838
2839
2840 int playernum;
2841 int weaponnum;
2842 int newweapon;
2843
2844 if (CheckArgs(2))
2845 {
2846 playernum=T_GetPlayerNum(t_argv[0]);
2847 weaponnum = intvalue(t_argv[1]);
2848 if (playernum==-1) return;
2849 if (weaponnum<0 || weaponnum>9)
2850 {
2851 script_error("weaponnum out of range! %s\n", weaponnum);
2852 return;
2853 }
2854 const PClass * ti = PClass::FindClass(WeaponNames[weaponnum]);
2855 if (!ti)
2856 {
2857 script_error("incompatibility in playerweapon\n", weaponnum);
2858 return;
2859 }
2860
2861 if (t_argc == 2)
2862 {
2863 AActor * wp = players[playernum].mo->FindInventory(ti);
2864 t_return.type = svt_int;
2865 t_return.value.i = wp!=NULL;;
2866 return;
2867 }
2868 else
2869 {
2870 AActor * wp = players[playernum].mo->FindInventory(ti);
2871
2872 newweapon = !!intvalue(t_argv[2]);
2873 if (!newweapon)
2874 {
2875 if (wp)
2876 {
2877 wp->Destroy();
2878 // If the weapon is active pick a replacement. Legacy didn't do this!
2879 if (players[playernum].PendingWeapon==wp) players[playernum].PendingWeapon=WP_NOCHANGE;
2880 if (players[playernum].ReadyWeapon==wp)
2881 {
2882 players[playernum].ReadyWeapon=NULL;
2883 players[playernum].mo->PickNewWeapon(NULL);
2884 }
2885 }
2886 }
2887 else
2888 {
2889 if (!wp)
2890 {
2891 AWeapon * pw=players[playernum].PendingWeapon;
2892 players[playernum].mo->GiveInventoryType(ti);
2893 players[playernum].PendingWeapon=pw;
2894 }
2895 }
2896
2897 t_return.type = svt_int;
2898 t_return.value.i = !!newweapon;
2899 return;
2900 }
2901 }
2902 }
2903
2904 //==========================================================================
2905 //
2906 // This function is just kept for backwards compatibility and
2907 // intentionally limited to the standard weapons!
2908 //
2909 //==========================================================================
2910
SF_PlayerSelectedWeapon()2911 void FParser::SF_PlayerSelectedWeapon()
2912 {
2913 int playernum;
2914 int weaponnum;
2915
2916
2917 static const char * const WeaponNames[]={
2918 "Fist", "Pistol", "Shotgun", "Chaingun", "RocketLauncher",
2919 "PlasmaRifle", "BFG9000", "Chainsaw", "SuperShotgun" };
2920
2921
2922 if (CheckArgs(1))
2923 {
2924 playernum=T_GetPlayerNum(t_argv[0]);
2925
2926 if(t_argc == 2)
2927 {
2928 weaponnum = intvalue(t_argv[1]);
2929
2930 if (weaponnum<0 || weaponnum>=9)
2931 {
2932 script_error("weaponnum out of range! %s\n", weaponnum);
2933 return;
2934 }
2935 const PClass * ti = PClass::FindClass(WeaponNames[weaponnum]);
2936 if (!ti)
2937 {
2938 script_error("incompatibility in playerweapon\n", weaponnum);
2939 return;
2940 }
2941
2942 players[playernum].PendingWeapon = (AWeapon*)players[playernum].mo->FindInventory(ti);
2943
2944 }
2945 t_return.type = svt_int;
2946 for(int i=0;i<9;i++)
2947 {
2948 if (players[playernum].ReadyWeapon->GetClass ()->TypeName == FName(WeaponNames[i]))
2949 {
2950 t_return.value.i=i;
2951 break;
2952 }
2953 }
2954 }
2955 }
2956
2957 //==========================================================================
2958 //
2959 // new for GZDoom: named inventory handling
2960 //
2961 //==========================================================================
2962
SF_GiveInventory(void)2963 void FParser::SF_GiveInventory(void)
2964 {
2965 int playernum, count;
2966
2967 if (CheckArgs(2))
2968 {
2969 playernum=T_GetPlayerNum(t_argv[0]);
2970 if (playernum==-1) return;
2971
2972 if(t_argc == 2) count=1;
2973 else count=intvalue(t_argv[2]);
2974 FS_GiveInventory(players[playernum].mo, stringvalue(t_argv[1]), count);
2975 t_return.type = svt_int;
2976 t_return.value.i = 0;
2977 }
2978 }
2979
2980 //==========================================================================
2981 //
2982 // new for GZDoom: named inventory handling
2983 //
2984 //==========================================================================
2985
SF_TakeInventory(void)2986 void FParser::SF_TakeInventory(void)
2987 {
2988 int playernum, count;
2989
2990 if (CheckArgs(2))
2991 {
2992 playernum=T_GetPlayerNum(t_argv[0]);
2993 if (playernum==-1) return;
2994
2995 if(t_argc == 2) count=32767;
2996 else count=intvalue(t_argv[2]);
2997 FS_TakeInventory(players[playernum].mo, stringvalue(t_argv[1]), count);
2998 t_return.type = svt_int;
2999 t_return.value.i = 0;
3000 }
3001 }
3002
3003 //==========================================================================
3004 //
3005 // new for GZDoom: named inventory handling
3006 //
3007 //==========================================================================
3008
SF_CheckInventory(void)3009 void FParser::SF_CheckInventory(void)
3010 {
3011 int playernum;
3012
3013 if (CheckArgs(2))
3014 {
3015 playernum=T_GetPlayerNum(t_argv[0]);
3016 if (playernum==-1)
3017 {
3018 t_return.value.i = 0;
3019 return;
3020 }
3021 t_return.type = svt_int;
3022 t_return.value.i = FS_CheckInventory(players[playernum].mo, stringvalue(t_argv[1]));
3023 }
3024 }
3025
3026 //==========================================================================
3027 //
3028 //
3029 //
3030 //==========================================================================
3031
SF_SetWeapon()3032 void FParser::SF_SetWeapon()
3033 {
3034 if (CheckArgs(2))
3035 {
3036 int playernum=T_GetPlayerNum(t_argv[0]);
3037 if (playernum!=-1)
3038 {
3039 AInventory *item = players[playernum].mo->FindInventory (PClass::FindClass (stringvalue(t_argv[1])));
3040
3041 if (item == NULL || !item->IsKindOf (RUNTIME_CLASS(AWeapon)))
3042 {
3043 }
3044 else if (players[playernum].ReadyWeapon == item)
3045 {
3046 // The weapon is already selected, so setweapon succeeds by default,
3047 // but make sure the player isn't switching away from it.
3048 players[playernum].PendingWeapon = WP_NOCHANGE;
3049 t_return.value.i = 1;
3050 }
3051 else
3052 {
3053 AWeapon *weap = static_cast<AWeapon *> (item);
3054
3055 if (weap->CheckAmmo (AWeapon::EitherFire, false))
3056 {
3057 // There's enough ammo, so switch to it.
3058 t_return.value.i = 1;
3059 players[playernum].PendingWeapon = weap;
3060 }
3061 }
3062 }
3063 t_return.value.i = 0;
3064 }
3065 }
3066
3067 //==========================================================================
3068 //
3069 // movecamera(camera, targetobj, targetheight, movespeed, targetangle, anglespeed)
3070 //
3071 //==========================================================================
3072
SF_MoveCamera(void)3073 void FParser::SF_MoveCamera(void)
3074 {
3075 fixed_t x, y, z;
3076 fixed_t zdist, xydist, movespeed;
3077 fixed_t xstep, ystep, zstep, targetheight;
3078 angle_t anglespeed, anglestep, angledist, targetangle,
3079 mobjangle, bigangle, smallangle;
3080
3081 // I have to use floats for the math where angles are divided
3082 // by fixed values.
3083 double fangledist, fanglestep, fmovestep;
3084 int angledir;
3085 AActor* target;
3086 int moved;
3087 int quad1, quad2;
3088 AActor * cam;
3089
3090 angledir = moved = 0;
3091
3092 if (CheckArgs(6))
3093 {
3094 cam = actorvalue(t_argv[0]);
3095
3096 target = actorvalue(t_argv[1]);
3097 if(!cam || !target)
3098 {
3099 script_error("invalid target for camera\n"); return;
3100 }
3101
3102 targetheight = fixedvalue(t_argv[2]);
3103 movespeed = fixedvalue(t_argv[3]);
3104 targetangle = (angle_t)FixedToAngle(fixedvalue(t_argv[4]));
3105 anglespeed = (angle_t)FixedToAngle(fixedvalue(t_argv[5]));
3106
3107 // figure out how big one step will be
3108 fixedvec2 dist = cam->Vec2To(target);
3109 zdist = targetheight - cam->Z();
3110
3111 // Angle checking...
3112 // 90
3113 // Q1|Q0
3114 //180--+--0
3115 // Q2|Q3
3116 // 270
3117 quad1 = targetangle / ANG90;
3118 quad2 = cam->angle / ANG90;
3119 bigangle = targetangle > cam->angle ? targetangle : cam->angle;
3120 smallangle = targetangle < cam->angle ? targetangle : cam->angle;
3121 if((quad1 > quad2 && quad1 - 1 == quad2) || (quad2 > quad1 && quad2 - 1 == quad1) ||
3122 quad1 == quad2)
3123 {
3124 angledist = bigangle - smallangle;
3125 angledir = targetangle > cam->angle ? 1 : -1;
3126 }
3127 else
3128 {
3129 angle_t diff180 = (bigangle + ANG180) - (smallangle + ANG180);
3130
3131 if(quad2 == 3 && quad1 == 0)
3132 {
3133 angledist = diff180;
3134 angledir = 1;
3135 }
3136 else if(quad1 == 3 && quad2 == 0)
3137 {
3138 angledist = diff180;
3139 angledir = -1;
3140 }
3141 else
3142 {
3143 angledist = bigangle - smallangle;
3144 if(angledist > ANG180)
3145 {
3146 angledist = diff180;
3147 angledir = targetangle > cam->angle ? -1 : 1;
3148 }
3149 else
3150 angledir = targetangle > cam->angle ? 1 : -1;
3151 }
3152 }
3153
3154 // set step variables based on distance and speed
3155 mobjangle = cam->AngleTo(target);
3156 xydist = cam->Distance2D(target);
3157
3158 xstep = FixedMul(finecosine[mobjangle >> ANGLETOFINESHIFT], movespeed);
3159 ystep = FixedMul(finesine[mobjangle >> ANGLETOFINESHIFT], movespeed);
3160
3161 if(xydist && movespeed)
3162 zstep = FixedDiv(zdist, FixedDiv(xydist, movespeed));
3163 else
3164 zstep = zdist > 0 ? movespeed : -movespeed;
3165
3166 if(xydist && movespeed && !anglespeed)
3167 {
3168 fangledist = ((double)angledist / (ANG45/45));
3169 fmovestep = ((double)FixedDiv(xydist, movespeed) / FRACUNIT);
3170 if(fmovestep)
3171 fanglestep = fangledist / fmovestep;
3172 else
3173 fanglestep = 360;
3174
3175 anglestep =(angle_t) (fanglestep * (ANG45/45));
3176 }
3177 else
3178 anglestep = anglespeed;
3179
3180 if(abs(xstep) >= (abs(dist.x) - 1))
3181 x = cam->X() + dist.x;
3182 else
3183 {
3184 x = cam->X() + xstep;
3185 moved = 1;
3186 }
3187
3188 if(abs(ystep) >= (abs(dist.y) - 1))
3189 y = cam->Y() + dist.y;
3190 else
3191 {
3192 y = cam->Y() + ystep;
3193 moved = 1;
3194 }
3195
3196 if(abs(zstep) >= (abs(zdist) - 1))
3197 z = targetheight;
3198 else
3199 {
3200 z = cam->Z() + zstep;
3201 moved = 1;
3202 }
3203
3204 if(anglestep >= angledist)
3205 cam->angle = targetangle;
3206 else
3207 {
3208 if(angledir == 1)
3209 {
3210 cam->angle += anglestep;
3211 moved = 1;
3212 }
3213 else if(angledir == -1)
3214 {
3215 cam->angle -= anglestep;
3216 moved = 1;
3217 }
3218 }
3219
3220 cam->radius=8;
3221 cam->height=8;
3222 if ((x != cam->X() || y != cam->Y()) && !P_TryMove(cam, x, y, true))
3223 {
3224 Printf("Illegal camera move to (%f, %f)\n", x/65536.f, y/65536.f);
3225 return;
3226 }
3227 cam->SetZ(z);
3228
3229 t_return.type = svt_int;
3230 t_return.value.i = moved;
3231 }
3232 }
3233
3234
3235
3236 //==========================================================================
3237 //
3238 // FParser::SF_ObjAwaken
3239 //
3240 // Implements: void objawaken([mobj mo])
3241 //
3242 //==========================================================================
3243
SF_ObjAwaken(void)3244 void FParser::SF_ObjAwaken(void)
3245 {
3246 // use trigger object if not specified
3247 AActor *mo;
3248 if(t_argc)
3249 {
3250 mo = actorvalue(t_argv[0]);
3251 }
3252 else
3253 {
3254 mo = Script->trigger;
3255 }
3256
3257 if(mo)
3258 {
3259 mo->Activate(Script->trigger);
3260 }
3261 }
3262
3263 //==========================================================================
3264 //
3265 // FParser::SF_AmbientSound
3266 //
3267 // Implements: void ambientsound(string name)
3268 //
3269 //==========================================================================
3270
SF_AmbientSound(void)3271 void FParser::SF_AmbientSound(void)
3272 {
3273 if (CheckArgs(1))
3274 {
3275 S_Sound(CHAN_AUTO, T_FindSound(stringvalue(t_argv[0])), 1, ATTN_NORM);
3276 }
3277 }
3278
3279
3280 //==========================================================================
3281 //
3282 // FParser::SF_ExitSecret
3283 //
3284 // Implements: void exitsecret()
3285 //
3286 //==========================================================================
3287
SF_ExitSecret(void)3288 void FParser::SF_ExitSecret(void)
3289 {
3290 G_SecretExitLevel(0);
3291 }
3292
3293
3294 //==========================================================================
3295 //
3296 // Type forcing functions -- useful with arrays et al
3297 //
3298 //==========================================================================
3299
SF_MobjValue(void)3300 void FParser::SF_MobjValue(void)
3301 {
3302 if (CheckArgs(1))
3303 {
3304 t_return.type = svt_mobj;
3305 t_return.value.mobj = actorvalue(t_argv[0]);
3306 }
3307 }
3308
SF_StringValue(void)3309 void FParser::SF_StringValue(void)
3310 {
3311 if (CheckArgs(1))
3312 {
3313 t_return.type = svt_string;
3314 if (t_argv[0].type == svt_string)
3315 {
3316 t_return.string = t_argv[0].string;
3317 }
3318 else
3319 {
3320 t_return.string = stringvalue(t_argv[0]);
3321 }
3322 }
3323 }
3324
SF_IntValue(void)3325 void FParser::SF_IntValue(void)
3326 {
3327 if (CheckArgs(1))
3328 {
3329 t_return.type = svt_int;
3330 t_return.value.i = intvalue(t_argv[0]);
3331 }
3332 }
3333
SF_FixedValue(void)3334 void FParser::SF_FixedValue(void)
3335 {
3336 if (CheckArgs(1))
3337 {
3338 t_return.type = svt_fixed;
3339 t_return.value.f = fixedvalue(t_argv[0]);
3340 }
3341 }
3342
3343
3344 //==========================================================================
3345 //
3346 // Starting here are functions present in Legacy but not Eternity.
3347 //
3348 //==========================================================================
3349
SF_SpawnExplosion()3350 void FParser::SF_SpawnExplosion()
3351 {
3352 fixed_t x, y, z;
3353 AActor* spawn;
3354 const PClass * PClass;
3355
3356 if (CheckArgs(3))
3357 {
3358 if (!(PClass=T_GetMobjType(t_argv[0]))) return;
3359
3360 x = fixedvalue(t_argv[1]);
3361 y = fixedvalue(t_argv[2]);
3362 if(t_argc > 3)
3363 z = fixedvalue(t_argv[3]);
3364 else
3365 z = P_PointInSector(x, y)->floorplane.ZatPoint(x,y);
3366
3367 spawn = Spawn (PClass, x, y, z, ALLOW_REPLACE);
3368 t_return.type = svt_int;
3369 t_return.value.i=0;
3370 if (spawn)
3371 {
3372 spawn->ClearCounters();
3373 t_return.value.i = spawn->SetState(spawn->FindState(NAME_Death));
3374 if(spawn->DeathSound) S_Sound (spawn, CHAN_BODY, spawn->DeathSound, 1, ATTN_NORM);
3375 }
3376 }
3377 }
3378
3379 //==========================================================================
3380 //
3381 //
3382 //
3383 //==========================================================================
3384
SF_RadiusAttack()3385 void FParser::SF_RadiusAttack()
3386 {
3387 AActor *spot;
3388 AActor *source;
3389 int damage;
3390
3391 if (CheckArgs(3))
3392 {
3393 spot = actorvalue(t_argv[0]);
3394 source = actorvalue(t_argv[1]);
3395 damage = intvalue(t_argv[2]);
3396
3397 if (spot && source)
3398 {
3399 P_RadiusAttack(spot, source, damage, damage, NAME_None, RADF_HURTSOURCE);
3400 }
3401 }
3402 }
3403
3404 //==========================================================================
3405 //
3406 //
3407 //
3408 //==========================================================================
3409
SF_SetObjPosition()3410 void FParser::SF_SetObjPosition()
3411 {
3412 AActor* mobj;
3413
3414 if (CheckArgs(2))
3415 {
3416 mobj = actorvalue(t_argv[0]);
3417
3418 if (!mobj) return;
3419
3420 mobj->SetOrigin(
3421 fixedvalue(t_argv[1]),
3422 (t_argc >= 3)? fixedvalue(t_argv[2]) : mobj->Y(),
3423 (t_argc >= 4)? fixedvalue(t_argv[3]) : mobj->Z(), false);
3424 }
3425 }
3426
3427 //==========================================================================
3428 //
3429 //
3430 //
3431 //==========================================================================
3432
SF_TestLocation()3433 void FParser::SF_TestLocation()
3434 {
3435 // use trigger object if not specified
3436 AActor *mo;
3437 if(t_argc)
3438 {
3439 mo = actorvalue(t_argv[0]);
3440 }
3441 else
3442 {
3443 mo = Script->trigger;
3444 }
3445
3446 if (mo)
3447 {
3448 t_return.type = svt_int;
3449 t_return.value.f = !!P_TestMobjLocation(mo);
3450 }
3451 }
3452
3453 //==========================================================================
3454 //
3455 //
3456 //
3457 //==========================================================================
3458
SF_HealObj()3459 void FParser::SF_HealObj() //no pain sound
3460 {
3461 // use trigger object if not specified
3462 AActor *mo;
3463 if(t_argc)
3464 {
3465 mo = actorvalue(t_argv[0]);
3466 }
3467 else
3468 {
3469 mo = Script->trigger;
3470 }
3471
3472 if(t_argc < 2)
3473 {
3474 mo->health = mo->GetDefault()->health;
3475 if(mo->player) mo->player->health = mo->health;
3476 }
3477
3478 else if (t_argc == 2)
3479 {
3480 mo->health += intvalue(t_argv[1]);
3481 if(mo->player) mo->player->health = mo->health;
3482 }
3483
3484 else
3485 script_error("invalid number of arguments for objheal");
3486 }
3487
3488
3489 //==========================================================================
3490 //
3491 //
3492 //
3493 //==========================================================================
3494
SF_ObjDead()3495 void FParser::SF_ObjDead()
3496 {
3497 // use trigger object if not specified
3498 AActor *mo;
3499 if(t_argc)
3500 {
3501 mo = actorvalue(t_argv[0]);
3502 }
3503 else
3504 {
3505 mo = Script->trigger;
3506 }
3507
3508 t_return.type = svt_int;
3509 if(mo && (mo->health <= 0 || mo->flags&MF_CORPSE))
3510 t_return.value.i = 1;
3511 else
3512 t_return.value.i = 0;
3513 }
3514
3515 //==========================================================================
3516 //
3517 //
3518 //
3519 //==========================================================================
3520
SF_SpawnMissile()3521 void FParser::SF_SpawnMissile()
3522 {
3523 AActor *mobj;
3524 AActor *target;
3525 const PClass * PClass;
3526
3527 if (CheckArgs(3))
3528 {
3529 if (!(PClass=T_GetMobjType(t_argv[2]))) return;
3530
3531 mobj = actorvalue(t_argv[0]);
3532 target = actorvalue(t_argv[1]);
3533 if (mobj && target) P_SpawnMissile(mobj, target, PClass);
3534 }
3535 }
3536
3537 //==========================================================================
3538 //
3539 //checks to see if a Map Thing Number exists; used to avoid script errors
3540 //
3541 //==========================================================================
3542
SF_MapThingNumExist()3543 void FParser::SF_MapThingNumExist()
3544 {
3545 TArray<TObjPtr<AActor> > &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings;
3546
3547 int intval;
3548
3549 if (CheckArgs(1))
3550 {
3551 intval = intvalue(t_argv[0]);
3552
3553 if (intval < 0 || intval >= int(SpawnedThings.Size()) || !SpawnedThings[intval])
3554 {
3555 t_return.type = svt_int;
3556 t_return.value.i = 0;
3557 }
3558 else
3559 {
3560 // Inventory items in the player's inventory have to be considered non-present.
3561 if (SpawnedThings[intval]->IsKindOf(RUNTIME_CLASS(AInventory)) &&
3562 barrier_cast<AInventory*>(SpawnedThings[intval])->Owner != NULL)
3563 {
3564 t_return.value.i = 0;
3565 }
3566 else
3567 {
3568 t_return.value.i = 1;
3569 }
3570 t_return.type = svt_int;
3571 }
3572 }
3573 }
3574
3575 //==========================================================================
3576 //
3577 //
3578 //
3579 //==========================================================================
3580
SF_MapThings()3581 void FParser::SF_MapThings()
3582 {
3583 TArray<TObjPtr<AActor> > &SpawnedThings = DFraggleThinker::ActiveThinker->SpawnedThings;
3584
3585 t_return.type = svt_int;
3586 t_return.value.i = SpawnedThings.Size();
3587 }
3588
3589
3590 //==========================================================================
3591 //
3592 //
3593 //
3594 //==========================================================================
3595
SF_ObjState()3596 void FParser::SF_ObjState()
3597 {
3598 int state;
3599 AActor *mo = NULL;
3600
3601 if (CheckArgs(1))
3602 {
3603 if(t_argc == 1)
3604 {
3605 mo = Script->trigger;
3606 state = intvalue(t_argv[0]);
3607 }
3608
3609 else if(t_argc == 2)
3610 {
3611 mo = actorvalue(t_argv[0]);
3612 state = intvalue(t_argv[1]);
3613 }
3614
3615 if (mo)
3616 {
3617 static ENamedName statenames[]= {
3618 NAME_Spawn, NAME_See, NAME_Missile, NAME_Melee,
3619 NAME_Pain, NAME_Death, NAME_Raise, NAME_XDeath, NAME_Crash };
3620
3621 if (state <1 || state > 9)
3622 {
3623 script_error("objstate: invalid state");
3624 return;
3625 }
3626
3627 t_return.type = svt_int;
3628 t_return.value.i = mo->SetState(mo->FindState(statenames[state]));
3629 }
3630 }
3631 }
3632
3633
3634 //==========================================================================
3635 //
3636 // only here for Legacy maps. The implementation of this function
3637 // is completely useless.
3638 //
3639 //==========================================================================
3640
SF_LineFlag()3641 void FParser::SF_LineFlag()
3642 {
3643 line_t* line;
3644 int linenum;
3645 int flagnum;
3646
3647 if (CheckArgs(2))
3648 {
3649 linenum = intvalue(t_argv[0]);
3650 if(linenum < 0 || linenum > numlines)
3651 {
3652 script_error("LineFlag: Invalid line number.\n");
3653 return;
3654 }
3655
3656 line = lines + linenum;
3657
3658 flagnum = intvalue(t_argv[1]);
3659 if(flagnum < 0 || (flagnum > 8 && flagnum!=15))
3660 {
3661 script_error("LineFlag: Invalid flag number.\n");
3662 return;
3663 }
3664
3665 if(t_argc > 2)
3666 {
3667 line->flags &= ~(1 << flagnum);
3668 if(intvalue(t_argv[2]))
3669 line->flags |= (1 << flagnum);
3670 }
3671
3672 t_return.type = svt_int;
3673 t_return.value.i = line->flags & (1 << flagnum);
3674 }
3675 }
3676
3677
3678 //==========================================================================
3679 //
3680 //
3681 //
3682 //==========================================================================
3683
SF_PlayerAddFrag()3684 void FParser::SF_PlayerAddFrag()
3685 {
3686 int playernum1;
3687 int playernum2;
3688
3689 if (CheckArgs(1))
3690 {
3691 if (t_argc == 1)
3692 {
3693 playernum1 = T_GetPlayerNum(t_argv[0]);
3694
3695 players[playernum1].fragcount++;
3696
3697 t_return.type = svt_int;
3698 t_return.value.f = players[playernum1].fragcount;
3699 }
3700
3701 else
3702 {
3703 playernum1 = T_GetPlayerNum(t_argv[0]);
3704 playernum2 = T_GetPlayerNum(t_argv[1]);
3705
3706 players[playernum1].frags[playernum2]++;
3707
3708 t_return.type = svt_int;
3709 t_return.value.f = players[playernum1].frags[playernum2];
3710 }
3711 }
3712 }
3713
3714 //==========================================================================
3715 //
3716 //
3717 //
3718 //==========================================================================
3719
SF_SkinColor()3720 void FParser::SF_SkinColor()
3721 {
3722 // Ignoring it for now.
3723 }
3724
SF_PlayDemo()3725 void FParser::SF_PlayDemo()
3726 {
3727 // Ignoring it for now.
3728 }
3729
SF_CheckCVar()3730 void FParser::SF_CheckCVar()
3731 {
3732 // can't be done so return 0.
3733 }
3734 //==========================================================================
3735 //
3736 //
3737 //
3738 //==========================================================================
3739
SF_Resurrect()3740 void FParser::SF_Resurrect()
3741 {
3742
3743 AActor *mo;
3744
3745 if (CheckArgs(1))
3746 {
3747 mo = actorvalue(t_argv[0]);
3748
3749 FState * state = mo->FindState(NAME_Raise);
3750 if (!state) //Don't resurrect things that can't be resurrected
3751 return;
3752
3753 mo->SetState(state);
3754 mo->height = mo->GetDefault()->height;
3755 mo->radius = mo->GetDefault()->radius;
3756 mo->flags = mo->GetDefault()->flags;
3757 mo->flags2 = mo->GetDefault()->flags2;
3758 mo->flags3 = mo->GetDefault()->flags3;
3759 mo->flags4 = mo->GetDefault()->flags4;
3760 mo->flags5 = mo->GetDefault()->flags5;
3761 mo->health = mo->GetDefault()->health;
3762 mo->target = NULL;
3763 }
3764 }
3765
3766 //==========================================================================
3767 //
3768 //
3769 //
3770 //==========================================================================
3771
SF_LineAttack()3772 void FParser::SF_LineAttack()
3773 {
3774 AActor *mo;
3775 int damage, angle, slope;
3776
3777 if (CheckArgs(3))
3778 {
3779 mo = actorvalue(t_argv[0]);
3780 damage = intvalue(t_argv[2]);
3781
3782 angle = (intvalue(t_argv[1]) * (ANG45 / 45));
3783 slope = P_AimLineAttack(mo, angle, MISSILERANGE);
3784
3785 P_LineAttack(mo, angle, MISSILERANGE, slope, damage, NAME_Hitscan, NAME_BulletPuff);
3786 }
3787 }
3788
3789 //==========================================================================
3790 //
3791 // This is a lousy hack. It only works for the standard actors
3792 // and it is quite inefficient.
3793 //
3794 //==========================================================================
3795
SF_ObjType()3796 void FParser::SF_ObjType()
3797 {
3798 // use trigger object if not specified
3799 AActor *mo;
3800 if(t_argc)
3801 {
3802 mo = actorvalue(t_argv[0]);
3803 }
3804 else
3805 {
3806 mo = Script->trigger;
3807 }
3808
3809 if (mo != NULL)
3810 {
3811 for (unsigned int i = 0; i < countof(ActorTypes); i++) if (mo->GetClass() == ActorTypes[i])
3812 {
3813 t_return.type = svt_int;
3814 t_return.value.i = i;
3815 return;
3816 }
3817 }
3818 t_return.type = svt_int;
3819 t_return.value.i = -1;
3820 }
3821
3822 //==========================================================================
3823 //
3824 // some new math functions
3825 //
3826 //==========================================================================
3827
double2fixed(double t)3828 inline fixed_t double2fixed(double t)
3829 {
3830 return (fixed_t)(t*65536.0);
3831 }
3832
3833
3834
SF_Sin()3835 void FParser::SF_Sin()
3836 {
3837 if (CheckArgs(1))
3838 {
3839 t_return.type = svt_fixed;
3840 t_return.value.f = double2fixed(sin(floatvalue(t_argv[0])));
3841 }
3842 }
3843
3844
SF_ASin()3845 void FParser::SF_ASin()
3846 {
3847 if (CheckArgs(1))
3848 {
3849 t_return.type = svt_fixed;
3850 t_return.value.f = double2fixed(asin(floatvalue(t_argv[0])));
3851 }
3852 }
3853
3854
SF_Cos()3855 void FParser::SF_Cos()
3856 {
3857 if (CheckArgs(1))
3858 {
3859 t_return.type = svt_fixed;
3860 t_return.value.f = double2fixed(cos(floatvalue(t_argv[0])));
3861 }
3862 }
3863
3864
SF_ACos()3865 void FParser::SF_ACos()
3866 {
3867 if (CheckArgs(1))
3868 {
3869 t_return.type = svt_fixed;
3870 t_return.value.f = double2fixed(acos(floatvalue(t_argv[0])));
3871 }
3872 }
3873
3874
SF_Tan()3875 void FParser::SF_Tan()
3876 {
3877 if (CheckArgs(1))
3878 {
3879 t_return.type = svt_fixed;
3880 t_return.value.f = double2fixed(tan(floatvalue(t_argv[0])));
3881 }
3882 }
3883
3884
SF_ATan()3885 void FParser::SF_ATan()
3886 {
3887 if (CheckArgs(1))
3888 {
3889 t_return.type = svt_fixed;
3890 t_return.value.f = double2fixed(atan(floatvalue(t_argv[0])));
3891 }
3892 }
3893
3894
SF_Exp()3895 void FParser::SF_Exp()
3896 {
3897 if (CheckArgs(1))
3898 {
3899 t_return.type = svt_fixed;
3900 t_return.value.f = double2fixed(exp(floatvalue(t_argv[0])));
3901 }
3902 }
3903
SF_Log()3904 void FParser::SF_Log()
3905 {
3906 if (CheckArgs(1))
3907 {
3908 t_return.type = svt_fixed;
3909 t_return.value.f = double2fixed(log(floatvalue(t_argv[0])));
3910 }
3911 }
3912
3913
SF_Sqrt()3914 void FParser::SF_Sqrt()
3915 {
3916 if (CheckArgs(1))
3917 {
3918 t_return.type = svt_fixed;
3919 t_return.value.f = double2fixed(sqrt(floatvalue(t_argv[0])));
3920 }
3921 }
3922
3923
SF_Floor()3924 void FParser::SF_Floor()
3925 {
3926 if (CheckArgs(1))
3927 {
3928 t_return.type = svt_fixed;
3929 t_return.value.f = fixedvalue(t_argv[0]) & 0xffFF0000;
3930 }
3931 }
3932
3933
SF_Pow()3934 void FParser::SF_Pow()
3935 {
3936 if (CheckArgs(2))
3937 {
3938 t_return.type = svt_fixed;
3939 t_return.value.f = double2fixed(pow(floatvalue(t_argv[0]), floatvalue(t_argv[1])));
3940 }
3941 }
3942
3943 //==========================================================================
3944 //
3945 // HUD pics (not operational yet!)
3946 //
3947 //==========================================================================
3948
3949
3950 int HU_GetFSPic(FTextureID lumpnum, int xpos, int ypos);
3951 int HU_DeleteFSPic(unsigned int handle);
3952 int HU_ModifyFSPic(unsigned int handle, FTextureID lumpnum, int xpos, int ypos);
3953 int HU_FSDisplay(unsigned int handle, bool newval);
3954
SF_NewHUPic()3955 void FParser::SF_NewHUPic()
3956 {
3957 if (CheckArgs(3))
3958 {
3959 t_return.type = svt_int;
3960 t_return.value.i = HU_GetFSPic(
3961 TexMan.GetTexture(stringvalue(t_argv[0]), FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny),
3962 intvalue(t_argv[1]), intvalue(t_argv[2]));
3963 }
3964 }
3965
SF_DeleteHUPic()3966 void FParser::SF_DeleteHUPic()
3967 {
3968 if (CheckArgs(1))
3969 {
3970 if (HU_DeleteFSPic(intvalue(t_argv[0])) == -1)
3971 script_error("deletehupic: Invalid sfpic handle: %i\n", intvalue(t_argv[0]));
3972 }
3973 }
3974
SF_ModifyHUPic()3975 void FParser::SF_ModifyHUPic()
3976 {
3977 if (t_argc != 4)
3978 {
3979 script_error("modifyhupic: invalid number of arguments\n");
3980 return;
3981 }
3982
3983 if (HU_ModifyFSPic(intvalue(t_argv[0]),
3984 TexMan.GetTexture(stringvalue(t_argv[0]), FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny),
3985 intvalue(t_argv[2]), intvalue(t_argv[3])) == -1)
3986 {
3987 script_error("modifyhypic: invalid sfpic handle %i\n", intvalue(t_argv[0]));
3988 }
3989 return;
3990 }
3991
SF_SetHUPicDisplay()3992 void FParser::SF_SetHUPicDisplay()
3993 {
3994 if (t_argc != 2)
3995 {
3996 script_error("sethupicdisplay: invalud number of arguments\n");
3997 return;
3998 }
3999
4000 if (HU_FSDisplay(intvalue(t_argv[0]), intvalue(t_argv[1]) > 0 ? 1 : 0) == -1)
4001 script_error("sethupicdisplay: invalid pic handle %i\n", intvalue(t_argv[0]));
4002 }
4003
4004
4005 //==========================================================================
4006 //
4007 // Yet to be made operational.
4008 //
4009 //==========================================================================
4010
SF_SetCorona(void)4011 void FParser::SF_SetCorona(void)
4012 {
4013 if(t_argc != 3)
4014 {
4015 script_error("incorrect arguments to function\n");
4016 return;
4017 }
4018
4019 int num = t_argv[0].value.i; // which corona we want to modify
4020 int what = t_argv[1].value.i; // what we want to modify (type, color, offset,...)
4021 int ival = t_argv[2].value.i; // the value of what we modify
4022 double fval = ((double) t_argv[2].value.f / FRACUNIT);
4023
4024 /*
4025 switch (what)
4026 {
4027 case 0:
4028 lspr[num].type = ival;
4029 break;
4030 case 1:
4031 lspr[num].light_xoffset = fval;
4032 break;
4033 case 2:
4034 lspr[num].light_yoffset = fval;
4035 break;
4036 case 3:
4037 if (t_argv[2].type == svt_string)
4038 lspr[num].corona_color = String2Hex(t_argv[2].value.s);
4039 else
4040 memcpy(&lspr[num].corona_color, &ival, sizeof(int));
4041 break;
4042 case 4:
4043 lspr[num].corona_radius = fval;
4044 break;
4045 case 5:
4046 if (t_argv[2].type == svt_string)
4047 lspr[num].dynamic_color = String2Hex(t_argv[2].value.s);
4048 else
4049 memcpy(&lspr[num].dynamic_color, &ival, sizeof(int));
4050 break;
4051 case 6:
4052 lspr[num].dynamic_radius = fval;
4053 lspr[num].dynamic_sqrradius = sqrt(lspr[num].dynamic_radius);
4054 break;
4055 default:
4056 CONS_Printf("Error in setcorona\n");
4057 break;
4058 }
4059 */
4060
4061 // no use for this!
4062 t_return.type = svt_int;
4063 t_return.value.i = 0;
4064 }
4065
4066 //==========================================================================
4067 //
4068 // new for GZDoom: Call a Hexen line special (deprecated, superseded by direct use)
4069 //
4070 //==========================================================================
4071
SF_Ls()4072 void FParser::SF_Ls()
4073 {
4074 int args[5]={0,0,0,0,0};
4075 int spc;
4076
4077 if (CheckArgs(1))
4078 {
4079 spc=intvalue(t_argv[0]);
4080 for(int i=0;i<5;i++)
4081 {
4082 if (t_argc>=i+2) args[i]=intvalue(t_argv[i+1]);
4083 }
4084 if (spc>=0 && spc<256)
4085 P_ExecuteSpecial(spc, NULL,Script->trigger,false, args[0],args[1],args[2],args[3],args[4]);
4086 }
4087 }
4088
4089
4090 //==========================================================================
4091 //
4092 // new for GZDoom: Gets the levelnum
4093 //
4094 //==========================================================================
4095
SF_LevelNum()4096 void FParser::SF_LevelNum()
4097 {
4098 t_return.type = svt_int;
4099 t_return.value.f = level.levelnum;
4100 }
4101
4102
4103 //==========================================================================
4104 //
4105 // new for GZDoom
4106 //
4107 //==========================================================================
4108
SF_MobjRadius(void)4109 void FParser::SF_MobjRadius(void)
4110 {
4111 AActor* mo;
4112
4113 if (CheckArgs(1))
4114 {
4115 mo = actorvalue(t_argv[0]);
4116 if(t_argc > 1)
4117 {
4118 if(mo)
4119 mo->radius = fixedvalue(t_argv[1]);
4120 }
4121
4122 t_return.type = svt_fixed;
4123 t_return.value.f = mo ? mo->radius : 0;
4124 }
4125 }
4126
4127
4128 //==========================================================================
4129 //
4130 // new for GZDoom
4131 //
4132 //==========================================================================
4133
SF_MobjHeight(void)4134 void FParser::SF_MobjHeight(void)
4135 {
4136 AActor* mo;
4137
4138 if (CheckArgs(1))
4139 {
4140 mo = actorvalue(t_argv[0]);
4141 if(t_argc > 1)
4142 {
4143 if(mo)
4144 mo->height = fixedvalue(t_argv[1]);
4145 }
4146
4147 t_return.type = svt_fixed;
4148 t_return.value.f = mo ? mo->height : 0;
4149 }
4150 }
4151
4152
4153 //==========================================================================
4154 //
4155 // new for GZDoom
4156 //
4157 //==========================================================================
4158
SF_ThingCount(void)4159 void FParser::SF_ThingCount(void)
4160 {
4161 const PClass *pClass;
4162 AActor * mo;
4163 int count=0;
4164 bool replacemented = false;
4165
4166
4167 if (CheckArgs(1))
4168 {
4169 pClass=T_GetMobjType(t_argv[0]);
4170 if (!pClass) return;
4171 // If we want to count map items we must consider actor replacement
4172 pClass = pClass->ActorInfo->GetReplacement()->Class;
4173
4174 again:
4175 TThinkerIterator<AActor> it;
4176
4177 if (t_argc<2 || intvalue(t_argv[1])==0 || pClass->IsDescendantOf(RUNTIME_CLASS(AInventory)))
4178 {
4179 while ((mo=it.Next()))
4180 {
4181 if (mo->IsA(pClass))
4182 {
4183 if (!mo->IsKindOf (RUNTIME_CLASS(AInventory)) ||
4184 static_cast<AInventory *>(mo)->Owner == NULL)
4185 {
4186 count++;
4187 }
4188 }
4189 }
4190 }
4191 else
4192 {
4193 while ((mo=it.Next()))
4194 {
4195 if (mo->IsA(pClass) && mo->health>0) count++;
4196 }
4197 }
4198 if (!replacemented)
4199 {
4200 // Again, with decorate replacements
4201 replacemented = true;
4202 PClass *newkind = pClass->ActorInfo->GetReplacement()->Class;
4203 if (newkind != pClass)
4204 {
4205 pClass = newkind;
4206 goto again;
4207 }
4208 }
4209 t_return.type = svt_int;
4210 t_return.value.i = count;
4211 }
4212 }
4213
4214 //==========================================================================
4215 //
4216 // new for GZDoom: Sets a sector color
4217 //
4218 //==========================================================================
4219
SF_SetColor(void)4220 void FParser::SF_SetColor(void)
4221 {
4222 int tagnum, secnum;
4223 int c=2;
4224 int i = -1;
4225 PalEntry color=0;
4226
4227 if (CheckArgs(2))
4228 {
4229 tagnum = intvalue(t_argv[0]);
4230
4231 secnum = T_FindFirstSectorFromTag(tagnum);
4232
4233 if(secnum < 0)
4234 {
4235 return;
4236 }
4237
4238 if(t_argc >1 && t_argc<4)
4239 {
4240 color=intvalue(t_argv[1]);
4241 }
4242 else if (t_argc>=4)
4243 {
4244 color.r=intvalue(t_argv[1]);
4245 color.g=intvalue(t_argv[2]);
4246 color.b=intvalue(t_argv[3]);
4247 color.a=0;
4248 }
4249 else return;
4250
4251 // set all sectors with tag
4252 FSSectorTagIterator itr(tagnum);
4253 while ((i = itr.Next()) >= 0)
4254 {
4255 sectors[i].ColorMap = GetSpecialLights (color, sectors[i].ColorMap->Fade, 0);
4256 }
4257 }
4258 }
4259
4260
4261 //==========================================================================
4262 //
4263 // Spawns a projectile at a map spot
4264 //
4265 //==========================================================================
4266
SF_SpawnShot2(void)4267 void FParser::SF_SpawnShot2(void)
4268 {
4269 AActor *source = NULL;
4270 const PClass * PClass;
4271 int z=0;
4272
4273 // t_argv[0] = type to spawn
4274 // t_argv[1] = source mobj, optional, -1 to default
4275 // shoots at source's angle
4276
4277 if (CheckArgs(2))
4278 {
4279 if(t_argv[1].type == svt_int && t_argv[1].value.i < 0)
4280 source = Script->trigger;
4281 else
4282 source = actorvalue(t_argv[1]);
4283
4284 if (t_argc>2) z=fixedvalue(t_argv[2]);
4285
4286 if(!source) return;
4287
4288 if (!(PClass=T_GetMobjType(t_argv[0]))) return;
4289
4290 t_return.type = svt_mobj;
4291
4292 AActor *mo = Spawn (PClass, source->PosPlusZ(z), ALLOW_REPLACE);
4293 if (mo)
4294 {
4295 S_Sound (mo, CHAN_VOICE, mo->SeeSound, 1, ATTN_NORM);
4296 mo->target = source;
4297 P_ThrustMobj(mo, mo->angle = source->angle, mo->Speed);
4298 if (!P_CheckMissileSpawn(mo, source->radius)) mo = NULL;
4299 }
4300 t_return.value.mobj = mo;
4301 }
4302 }
4303
4304
4305
4306 //==========================================================================
4307 //
4308 // new for GZDoom
4309 //
4310 //==========================================================================
4311
SF_KillInSector()4312 void FParser::SF_KillInSector()
4313 {
4314 if (CheckArgs(1))
4315 {
4316 TThinkerIterator<AActor> it;
4317 AActor * mo;
4318 int tag=intvalue(t_argv[0]);
4319
4320 while ((mo=it.Next()))
4321 {
4322 if (mo->flags3&MF3_ISMONSTER && tagManager.SectorHasTag(mo->Sector, tag)) P_DamageMobj(mo, NULL, NULL, 1000000, NAME_Massacre);
4323 }
4324 }
4325 }
4326
4327 //==========================================================================
4328 //
4329 // new for GZDoom: Sets a sector's type
4330 //
4331 //==========================================================================
4332
SF_SectorType(void)4333 void FParser::SF_SectorType(void)
4334 {
4335 // I don't think this was ever used publicly so I'm not going to bother fixing it.
4336 }
4337
4338 //==========================================================================
4339 //
4340 // new for GZDoom: Sets a new line trigger type (Doom format!)
4341 // (Sure, this is not particularly useful. But having it made it possible
4342 // to fix a few annoying bugs in some old maps ;) )
4343 //
4344 //==========================================================================
4345
SF_SetLineTrigger()4346 void FParser::SF_SetLineTrigger()
4347 {
4348 int i,id,spec,tag(0);
4349
4350 if (CheckArgs(2))
4351 {
4352 id=intvalue(t_argv[0]);
4353 spec=intvalue(t_argv[1]);
4354 if (t_argc>2) tag=intvalue(t_argv[2]);
4355 FLineIdIterator itr(id);
4356 while ((i = itr.Next()) >= 0)
4357 {
4358 maplinedef_t mld;
4359 mld.special = spec;
4360 mld.tag = tag;
4361 mld.flags = 0;
4362 int f = lines[i].flags;
4363 P_TranslateLineDef(&lines[i], &mld);
4364 lines[i].flags = (lines[i].flags & (ML_MONSTERSCANACTIVATE | ML_REPEAT_SPECIAL | ML_SPAC_MASK | ML_FIRSTSIDEONLY)) |
4365 (f & ~(ML_MONSTERSCANACTIVATE | ML_REPEAT_SPECIAL | ML_SPAC_MASK | ML_FIRSTSIDEONLY));
4366
4367 }
4368 }
4369 }
4370
4371
4372 //==========================================================================
4373 //
4374 //
4375 //
4376 //==========================================================================
4377
SF_ChangeTag()4378 void FParser::SF_ChangeTag()
4379 {
4380 // Development garbage!
4381 }
4382
4383
4384 //==========================================================================
4385 //
4386 //
4387 //
4388 //==========================================================================
4389
SF_WallGlow()4390 void FParser::SF_WallGlow()
4391 {
4392 // Development garbage!
4393 }
4394
4395
4396 //==========================================================================
4397 //
4398 // new for GZDoom: Call a Hexen line special
4399 //
4400 //==========================================================================
4401
RunLineSpecial(const FLineSpecial * spec)4402 void FParser::RunLineSpecial(const FLineSpecial *spec)
4403 {
4404
4405 if (CheckArgs(spec->min_args))
4406 {
4407 int args[5];
4408 for(int i=0;i<5;i++)
4409 {
4410 if (t_argc>i) args[i]=intvalue(t_argv[i]);
4411 else args[i] = 0;
4412 }
4413 t_return.value.i = P_ExecuteSpecial(spec->number, NULL,Script->trigger,false, args[0],args[1],args[2],args[3],args[4]);
4414 }
4415 }
4416
4417
4418 //==========================================================================
4419 //
4420 //
4421 //
4422 //==========================================================================
4423
SaveCurrentScript()4424 DRunningScript *FParser::SaveCurrentScript()
4425 {
4426 DFraggleThinker *th = DFraggleThinker::ActiveThinker;
4427 if (th)
4428 {
4429 DRunningScript *runscr = new DRunningScript(Script->trigger, Script, Script->MakeIndex(Rover));
4430
4431 // hook into chain at start
4432 th->AddRunningScript(runscr);
4433 return runscr;
4434 }
4435 return NULL;
4436 }
4437
4438 //==========================================================================
4439 //
4440 // script function
4441 //
4442 //==========================================================================
4443
SF_Wait()4444 void FParser::SF_Wait()
4445 {
4446 DRunningScript *runscr;
4447
4448 if(t_argc != 1)
4449 {
4450 script_error("incorrect arguments to function\n");
4451 return;
4452 }
4453
4454 runscr = SaveCurrentScript();
4455
4456 runscr->wait_type = wt_delay;
4457
4458 runscr->wait_data = (intvalue(t_argv[0]) * TICRATE) / 100;
4459 throw CFsTerminator();
4460 }
4461
4462 //==========================================================================
4463 //
4464 // wait for sector with particular tag to stop moving
4465 //
4466 //==========================================================================
4467
SF_TagWait()4468 void FParser::SF_TagWait()
4469 {
4470 DRunningScript *runscr;
4471
4472 if(t_argc != 1)
4473 {
4474 script_error("incorrect arguments to function\n");
4475 return;
4476 }
4477
4478 runscr = SaveCurrentScript();
4479
4480 runscr->wait_type = wt_tagwait;
4481 runscr->wait_data = intvalue(t_argv[0]);
4482 throw CFsTerminator();
4483 }
4484
4485 //==========================================================================
4486 //
4487 // wait for a script to finish
4488 //
4489 //==========================================================================
4490
SF_ScriptWait()4491 void FParser::SF_ScriptWait()
4492 {
4493 DRunningScript *runscr;
4494
4495 if(t_argc != 1)
4496 {
4497 script_error("insufficient arguments to function\n");
4498 return;
4499 }
4500
4501 runscr = SaveCurrentScript();
4502
4503 runscr->wait_type = wt_scriptwait;
4504 runscr->wait_data = intvalue(t_argv[0]);
4505 throw CFsTerminator();
4506 }
4507
4508 //==========================================================================
4509 //
4510 // haleyjd 05/23/01: wait for a script to start (zdoom-inspired)
4511 //
4512 //==========================================================================
4513
SF_ScriptWaitPre()4514 void FParser::SF_ScriptWaitPre()
4515 {
4516 DRunningScript *runscr;
4517
4518 if(t_argc != 1)
4519 {
4520 script_error("insufficient arguments to function\n");
4521 return;
4522 }
4523
4524 runscr = SaveCurrentScript();
4525 runscr->wait_type = wt_scriptwaitpre;
4526 runscr->wait_data = intvalue(t_argv[0]);
4527 throw CFsTerminator();
4528 }
4529
4530 //==========================================================================
4531 //
4532 // start a new script
4533 //
4534 //==========================================================================
4535
SF_StartScript()4536 void FParser::SF_StartScript()
4537 {
4538 if(t_argc != 1)
4539 {
4540 script_error("incorrect arguments to function\n");
4541 return;
4542 }
4543
4544 int snum = intvalue(t_argv[0]);
4545
4546 if(snum < 0 || snum >= MAXSCRIPTS)
4547 {
4548 script_error("script number %d out of range\n",snum);
4549 return;
4550 }
4551
4552 DFraggleThinker *th = DFraggleThinker::ActiveThinker;
4553 if (th)
4554 {
4555
4556 DFsScript *script = th->LevelScript->children[snum];
4557
4558 if(!script)
4559 {
4560 script_error("script %i not defined\n", snum);
4561 }
4562
4563 DRunningScript *runscr = new DRunningScript(Script->trigger, script, 0);
4564 // hook into chain at start
4565 th->AddRunningScript(runscr);
4566 }
4567 }
4568
4569 //==========================================================================
4570 //
4571 // checks if a script is running
4572 //
4573 //==========================================================================
4574
SF_ScriptRunning()4575 void FParser::SF_ScriptRunning()
4576 {
4577 DRunningScript *current;
4578 int snum = 0;
4579
4580 if(t_argc < 1)
4581 {
4582 script_error("not enough arguments to function\n");
4583 return;
4584 }
4585
4586 snum = intvalue(t_argv[0]);
4587
4588 for(current = DFraggleThinker::ActiveThinker->RunningScripts->next; current; current=current->next)
4589 {
4590 if(current->script->scriptnum == snum)
4591 {
4592 // script found so return
4593 t_return.type = svt_int;
4594 t_return.value.i = 1;
4595 return;
4596 }
4597 }
4598
4599 // script not found
4600 t_return.type = svt_int;
4601 t_return.value.i = 0;
4602 }
4603
4604
4605 //==========================================================================
4606 //
4607 // Init Functions
4608 //
4609 //==========================================================================
4610
4611 static int zoom=1; // Dummy - no longer needed!
4612
init_functions(void)4613 void init_functions(void)
4614 {
4615 for(unsigned i=0;i<countof(ActorNames_init);i++)
4616 {
4617 ActorTypes[i]=PClass::FindClass(ActorNames_init[i]);
4618 }
4619
4620 DFsScript * gscr = global_script;
4621
4622 // add all the functions
4623 gscr->NewVariable("consoleplayer", svt_pInt)->value.pI = &consoleplayer;
4624 gscr->NewVariable("displayplayer", svt_pInt)->value.pI = &consoleplayer;
4625 gscr->NewVariable("zoom", svt_pInt)->value.pI = &zoom;
4626 gscr->NewVariable("fov", svt_pInt)->value.pI = &zoom;
4627 gscr->NewVariable("trigger", svt_pMobj)->value.pMobj = &trigger_obj;
4628
4629 // Create constants for all existing line specials
4630 for(int i=0; i<256; i++)
4631 {
4632 const FLineSpecial *ls = LineSpecialsInfo[i];
4633
4634 if (ls != NULL && ls->max_args >= 0) // specials with max args set to -1 can only be used in a map and are of no use hee.
4635 {
4636 gscr->NewVariable(ls->name, svt_linespec)->value.ls = ls;
4637 }
4638 }
4639
4640 // important C-emulating stuff
4641 gscr->NewFunction("break", &FParser::SF_Break);
4642 gscr->NewFunction("continue", &FParser::SF_Continue);
4643 gscr->NewFunction("return", &FParser::SF_Return);
4644 gscr->NewFunction("goto", &FParser::SF_Goto);
4645 gscr->NewFunction("include", &FParser::SF_Include);
4646
4647 // standard FraggleScript functions
4648 gscr->NewFunction("print", &FParser::SF_Print);
4649 gscr->NewFunction("rnd", &FParser::SF_Rnd); // Legacy uses a normal rand() call for this which is extremely dangerous.
4650 gscr->NewFunction("prnd", &FParser::SF_Rnd); // I am mapping rnd and prnd to the same named RNG which should eliminate any problem
4651 gscr->NewFunction("input", &FParser::SF_Input);
4652 gscr->NewFunction("beep", &FParser::SF_Beep);
4653 gscr->NewFunction("clock", &FParser::SF_Clock);
4654 gscr->NewFunction("wait", &FParser::SF_Wait);
4655 gscr->NewFunction("tagwait", &FParser::SF_TagWait);
4656 gscr->NewFunction("scriptwait", &FParser::SF_ScriptWait);
4657 gscr->NewFunction("startscript", &FParser::SF_StartScript);
4658 gscr->NewFunction("scriptrunning", &FParser::SF_ScriptRunning);
4659
4660 // doom stuff
4661 gscr->NewFunction("startskill", &FParser::SF_StartSkill);
4662 gscr->NewFunction("exitlevel", &FParser::SF_ExitLevel);
4663 gscr->NewFunction("tip", &FParser::SF_Tip);
4664 gscr->NewFunction("timedtip", &FParser::SF_TimedTip);
4665 gscr->NewFunction("message", &FParser::SF_Message);
4666 gscr->NewFunction("gameskill", &FParser::SF_Gameskill);
4667 gscr->NewFunction("gamemode", &FParser::SF_Gamemode);
4668
4669 // player stuff
4670 gscr->NewFunction("playermsg", &FParser::SF_PlayerMsg);
4671 gscr->NewFunction("playertip", &FParser::SF_PlayerTip);
4672 gscr->NewFunction("playeringame", &FParser::SF_PlayerInGame);
4673 gscr->NewFunction("playername", &FParser::SF_PlayerName);
4674 gscr->NewFunction("playeraddfrag", &FParser::SF_PlayerAddFrag);
4675 gscr->NewFunction("playerobj", &FParser::SF_PlayerObj);
4676 gscr->NewFunction("isplayerobj", &FParser::SF_IsPlayerObj);
4677 gscr->NewFunction("isobjplayer", &FParser::SF_IsPlayerObj);
4678 gscr->NewFunction("skincolor", &FParser::SF_SkinColor);
4679 gscr->NewFunction("playerkeys", &FParser::SF_PlayerKeys);
4680 gscr->NewFunction("playerammo", &FParser::SF_PlayerAmmo);
4681 gscr->NewFunction("maxplayerammo", &FParser::SF_MaxPlayerAmmo);
4682 gscr->NewFunction("playerweapon", &FParser::SF_PlayerWeapon);
4683 gscr->NewFunction("playerselwep", &FParser::SF_PlayerSelectedWeapon);
4684
4685 // mobj stuff
4686 gscr->NewFunction("spawn", &FParser::SF_Spawn);
4687 gscr->NewFunction("spawnexplosion", &FParser::SF_SpawnExplosion);
4688 gscr->NewFunction("radiusattack", &FParser::SF_RadiusAttack);
4689 gscr->NewFunction("kill", &FParser::SF_KillObj);
4690 gscr->NewFunction("removeobj", &FParser::SF_RemoveObj);
4691 gscr->NewFunction("objx", &FParser::SF_ObjX);
4692 gscr->NewFunction("objy", &FParser::SF_ObjY);
4693 gscr->NewFunction("objz", &FParser::SF_ObjZ);
4694 gscr->NewFunction("testlocation", &FParser::SF_TestLocation);
4695 gscr->NewFunction("teleport", &FParser::SF_Teleport);
4696 gscr->NewFunction("silentteleport", &FParser::SF_SilentTeleport);
4697 gscr->NewFunction("damageobj", &FParser::SF_DamageObj);
4698 gscr->NewFunction("healobj", &FParser::SF_HealObj);
4699 gscr->NewFunction("player", &FParser::SF_Player);
4700 gscr->NewFunction("objsector", &FParser::SF_ObjSector);
4701 gscr->NewFunction("objflag", &FParser::SF_ObjFlag);
4702 gscr->NewFunction("pushobj", &FParser::SF_PushThing);
4703 gscr->NewFunction("pushthing", &FParser::SF_PushThing);
4704 gscr->NewFunction("objangle", &FParser::SF_ObjAngle);
4705 gscr->NewFunction("objhealth", &FParser::SF_ObjHealth);
4706 gscr->NewFunction("objdead", &FParser::SF_ObjDead);
4707 gscr->NewFunction("reactiontime", &FParser::SF_ReactionTime);
4708 gscr->NewFunction("objreactiontime", &FParser::SF_ReactionTime);
4709 gscr->NewFunction("objtarget", &FParser::SF_MobjTarget);
4710 gscr->NewFunction("objmomx", &FParser::SF_MobjMomx);
4711 gscr->NewFunction("objmomy", &FParser::SF_MobjMomy);
4712 gscr->NewFunction("objmomz", &FParser::SF_MobjMomz);
4713
4714 gscr->NewFunction("spawnmissile", &FParser::SF_SpawnMissile);
4715 gscr->NewFunction("mapthings", &FParser::SF_MapThings);
4716 gscr->NewFunction("objtype", &FParser::SF_ObjType);
4717 gscr->NewFunction("mapthingnumexist", &FParser::SF_MapThingNumExist);
4718 gscr->NewFunction("objstate", &FParser::SF_ObjState);
4719 gscr->NewFunction("resurrect", &FParser::SF_Resurrect);
4720 gscr->NewFunction("lineattack", &FParser::SF_LineAttack);
4721 gscr->NewFunction("setobjposition", &FParser::SF_SetObjPosition);
4722
4723 // sector stuff
4724 gscr->NewFunction("floorheight", &FParser::SF_FloorHeight);
4725 gscr->NewFunction("floortext", &FParser::SF_FloorTexture);
4726 gscr->NewFunction("floortexture", &FParser::SF_FloorTexture); // haleyjd: alias
4727 gscr->NewFunction("movefloor", &FParser::SF_MoveFloor);
4728 gscr->NewFunction("ceilheight", &FParser::SF_CeilingHeight);
4729 gscr->NewFunction("ceilingheight", &FParser::SF_CeilingHeight); // haleyjd: alias
4730 gscr->NewFunction("moveceil", &FParser::SF_MoveCeiling);
4731 gscr->NewFunction("moveceiling", &FParser::SF_MoveCeiling); // haleyjd: aliases
4732 gscr->NewFunction("ceilingtexture", &FParser::SF_CeilingTexture);
4733 gscr->NewFunction("ceiltext", &FParser::SF_CeilingTexture); // haleyjd: wrong
4734 gscr->NewFunction("lightlevel", &FParser::SF_LightLevel); // handler - was
4735 gscr->NewFunction("fadelight", &FParser::SF_FadeLight); // &FParser::SF_FloorTexture!
4736 gscr->NewFunction("colormap", &FParser::SF_SectorColormap);
4737
4738 // cameras!
4739 gscr->NewFunction("setcamera", &FParser::SF_SetCamera);
4740 gscr->NewFunction("clearcamera", &FParser::SF_ClearCamera);
4741 gscr->NewFunction("movecamera", &FParser::SF_MoveCamera);
4742
4743 // trig functions
4744 gscr->NewFunction("pointtoangle", &FParser::SF_PointToAngle);
4745 gscr->NewFunction("pointtodist", &FParser::SF_PointToDist);
4746
4747 // sound functions
4748 gscr->NewFunction("startsound", &FParser::SF_StartSound);
4749 gscr->NewFunction("startsectorsound", &FParser::SF_StartSectorSound);
4750 gscr->NewFunction("ambientsound", &FParser::SF_AmbientSound);
4751 gscr->NewFunction("startambiantsound", &FParser::SF_AmbientSound); // Legacy's incorrectly spelled name!
4752 gscr->NewFunction("changemusic", &FParser::SF_ChangeMusic);
4753
4754 // hubs!
4755 gscr->NewFunction("changehublevel", &FParser::SF_ChangeHubLevel);
4756
4757 // doors
4758 gscr->NewFunction("opendoor", &FParser::SF_OpenDoor);
4759 gscr->NewFunction("closedoor", &FParser::SF_CloseDoor);
4760
4761 // HU Graphics
4762 gscr->NewFunction("newhupic", &FParser::SF_NewHUPic);
4763 gscr->NewFunction("createpic", &FParser::SF_NewHUPic);
4764 gscr->NewFunction("deletehupic", &FParser::SF_DeleteHUPic);
4765 gscr->NewFunction("modifyhupic", &FParser::SF_ModifyHUPic);
4766 gscr->NewFunction("modifypic", &FParser::SF_ModifyHUPic);
4767 gscr->NewFunction("sethupicdisplay", &FParser::SF_SetHUPicDisplay);
4768 gscr->NewFunction("setpicvisible", &FParser::SF_SetHUPicDisplay);
4769
4770 //
4771 gscr->NewFunction("playdemo", &FParser::SF_PlayDemo);
4772 gscr->NewFunction("runcommand", &FParser::SF_RunCommand);
4773 gscr->NewFunction("checkcvar", &FParser::SF_CheckCVar);
4774 gscr->NewFunction("setlinetexture", &FParser::SF_SetLineTexture);
4775 gscr->NewFunction("linetrigger", &FParser::SF_LineTrigger);
4776 gscr->NewFunction("lineflag", &FParser::SF_LineFlag);
4777
4778 //Hurdler: new math functions
4779 gscr->NewFunction("max", &FParser::SF_Max);
4780 gscr->NewFunction("min", &FParser::SF_Min);
4781 gscr->NewFunction("abs", &FParser::SF_Abs);
4782
4783 gscr->NewFunction("sin", &FParser::SF_Sin);
4784 gscr->NewFunction("asin", &FParser::SF_ASin);
4785 gscr->NewFunction("cos", &FParser::SF_Cos);
4786 gscr->NewFunction("acos", &FParser::SF_ACos);
4787 gscr->NewFunction("tan", &FParser::SF_Tan);
4788 gscr->NewFunction("atan", &FParser::SF_ATan);
4789 gscr->NewFunction("exp", &FParser::SF_Exp);
4790 gscr->NewFunction("log", &FParser::SF_Log);
4791 gscr->NewFunction("sqrt", &FParser::SF_Sqrt);
4792 gscr->NewFunction("floor", &FParser::SF_Floor);
4793 gscr->NewFunction("pow", &FParser::SF_Pow);
4794
4795 // Eternity extensions
4796 gscr->NewFunction("setlineblocking", &FParser::SF_SetLineBlocking);
4797 gscr->NewFunction("setlinetrigger", &FParser::SF_SetLineTrigger);
4798 gscr->NewFunction("setlinemnblock", &FParser::SF_SetLineMonsterBlocking);
4799 gscr->NewFunction("scriptwaitpre", &FParser::SF_ScriptWaitPre);
4800 gscr->NewFunction("exitsecret", &FParser::SF_ExitSecret);
4801 gscr->NewFunction("objawaken", &FParser::SF_ObjAwaken);
4802
4803 // forced coercion functions
4804 gscr->NewFunction("mobjvalue", &FParser::SF_MobjValue);
4805 gscr->NewFunction("stringvalue", &FParser::SF_StringValue);
4806 gscr->NewFunction("intvalue", &FParser::SF_IntValue);
4807 gscr->NewFunction("fixedvalue", &FParser::SF_FixedValue);
4808
4809 // new for GZDoom
4810 gscr->NewFunction("spawnshot2", &FParser::SF_SpawnShot2);
4811 gscr->NewFunction("setcolor", &FParser::SF_SetColor);
4812 gscr->NewFunction("sectortype", &FParser::SF_SectorType);
4813 gscr->NewFunction("wallglow", &FParser::SF_WallGlow);
4814 gscr->NewFunction("objradius", &FParser::SF_MobjRadius);
4815 gscr->NewFunction("objheight", &FParser::SF_MobjHeight);
4816 gscr->NewFunction("thingcount", &FParser::SF_ThingCount);
4817 gscr->NewFunction("killinsector", &FParser::SF_KillInSector);
4818 gscr->NewFunction("changetag", &FParser::SF_ChangeTag);
4819 gscr->NewFunction("levelnum", &FParser::SF_LevelNum);
4820
4821 // new inventory
4822 gscr->NewFunction("giveinventory", &FParser::SF_GiveInventory);
4823 gscr->NewFunction("takeinventory", &FParser::SF_TakeInventory);
4824 gscr->NewFunction("checkinventory", &FParser::SF_CheckInventory);
4825 gscr->NewFunction("setweapon", &FParser::SF_SetWeapon);
4826
4827 gscr->NewFunction("ls", &FParser::SF_Ls); // execute Hexen type line special
4828
4829 // Dummies - shut up warnings
4830 gscr->NewFunction("setcorona", &FParser::SF_SetCorona);
4831 }
4832
4833