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 = &sectors[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(&sectors[i]);
1615 				if (!f->Move(
1616 					abs(dest - sectors[i].CenterFloor()),
1617 					sectors[i].floorplane.PointToDist (CenterSpot(&sectors[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 = &sectors[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(&sectors[i]);
1763 				if (!c->Move(
1764 					abs(dest - sectors[i].CenterCeiling()),
1765 					sectors[i].ceilingplane.PointToDist (CenterSpot(&sectors[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 = &sectors[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 = &sectors[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(&sectors[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 = &sectors[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 = &sectors[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=&sectors[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 = &sectors[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