1 // Emacs style mode select -*- C++ -*-
2 //---------------------------------------------------------------------------
3 //
4 // $Id: t_func.c 1564 2020-12-19 06:21:07Z wesleyjohnson $
5 //
6 // Copyright (C) 2000 Simon Howard
7 // Copyright (C) 2001-2016 by DooM Legacy Team.
8 //
9 // This program is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation; either version 2 of the License, or
12 // (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 //
23 // $Log: t_func.c,v $
24 // Revision 1.40  2005/11/07 22:54:39  iori_
25 // Kind of redundant unless we want a 1.43 release sometime.
26 //
27 // PlayerPitch - enabled setting the player's pitch
28 // ObjAngle - Enabled/Fixed setting the player's angle (was broken). May not work for MP..
29 // SectorEffect - kind of limited, but useful I guess. Incomplete (secret, dmg sectors)
30 //
31 // Revision 1.39  2005/05/21 08:41:23  iori_
32 // May 19, 2005 - PlayerArmor FS function;  1.43 can be compiled again.
33 //
34 // Revision 1.38  2004/09/17 23:04:48  darkwolf95
35 // playerkeysb (see comment), waittic and clocktic
36 //
37 // Revision 1.37  2004/08/26 10:53:51  iori_
38 // warpmap fs function
39 //
40 // Revision 1.36  2004/07/27 08:19:37  exl
41 // New fmod, fs functions, bugfix or 2, patrol nodes
42 //
43 // Revision 1.35  2004/03/06 17:25:04  darkwolf95
44 // SetObjPosition to work around spawning and removing objects
45 //
46 // Revision 1.34  2003/12/07 14:59:40  darkwolf95
47 // changed objstate to set state and return result of function, user can use other clues to find out what the state of an object is
48 //
49 // Revision 1.33  2003/11/21 16:15:27  darkwolf95
50 // small fix to resurrect
51 //
52 // Revision 1.32  2003/11/16 02:01:33  darkwolf95
53 // objheal(): so there's no pain state or noise; resurrect(): completely bring 'em back
54 //
55 // Revision 1.31  2003/11/15 22:04:42  darkwolf95
56 // Added objstate(), which is modifed from Exl's code and playerselwep() to complement playerweapon().
57 //
58 // Revision 1.30  2003/10/15 14:57:04  darkwolf95
59 // added ability to set with objangle()
60 //
61 // Revision 1.29  2003/10/08 15:13:02  darkwolf95
62 // Small fix - spawnmissile now returns mobj
63 //
64 // Revision 1.28  2003/08/23 14:07:26  darkwolf95
65 // added gameskill() and fixed setcamera pitch
66 //
67 // Revision 1.27  2003/07/23 17:26:36  darkwolf95
68 // SetLineTexture function for Fraggle Script
69 //
70 // Revision 1.26  2003/07/21 11:33:57  hurdler
71 // Revision 1.25  2003/07/13 13:16:15  hurdler
72 //
73 // Revision 1.24  2003/05/30 22:44:07  hurdler
74 // add checkcvar function to FS
75 //
76 // Revision 1.23  2003/05/26 18:02:29  darkwolf95
77 // added playeraddfrag, skincolor, testlocation and radiusattack functions
78 //
79 // Revision 1.22  2003/04/21 19:55:26  darkwolf95
80 // Added playdemo, spawnmissle, mapthings, objtype, mapthingnumexist, and playerweapon.
81 //
82 // Revision 1.21  2002/09/07 16:46:47  hurdler
83 // Fix respawning things bug using FS
84 //
85 // Revision 1.20  2002/07/28 17:11:33  hurdler
86 // Change version number to reflect the big changes since v.30
87 //
88 // Revision 1.19  2002/06/30 21:37:48  hurdler
89 // Ready for 1.32 beta 5 release
90 //
91 // Revision 1.18  2002/06/15 13:39:26  ssntails
92 //
93 // Revision 1.17  2002/06/14 02:20:06  ssntails
94 // New FS function (SoM Request)
95 //
96 // Revision 1.16  2002/05/19 19:44:44  hurdler
97 // Revision 1.14  2002/01/05 16:39:19  hurdler
98 //
99 // Revision 1.13  2002/01/05 00:58:10  hurdler
100 // fix compiling problem when not using hwrender
101 //
102 // Revision 1.12  2001/12/31 14:44:50  hurdler
103 // Last fix for beta 4
104 //
105 // Revision 1.11  2001/12/31 13:47:46  hurdler
106 // Add setcorona FS command and prepare the code for beta 4
107 //
108 // Revision 1.10  2001/12/28 16:57:30  hurdler
109 // Add setcorona command to FS
110 //
111 // Revision 1.9  2001/12/26 17:24:46  hurdler
112 // Update Linux version
113 //
114 // Revision 1.8  2001/08/14 00:36:26  hurdler
115 //
116 // Revision 1.7  2001/08/06 23:57:10  stroggonmeth
117 // Removed portal code, improved 3D floors in hardware mode.
118 //
119 // Revision 1.6  2001/04/30 17:19:24  stroggonmeth
120 // HW fix and misc. changes
121 //
122 // Revision 1.5  2001/03/21 18:24:56  stroggonmeth
123 // Misc changes and fixes. Code cleanup
124 //
125 // Revision 1.4  2001/03/13 22:14:20  stroggonmeth
126 // Long time no commit. 3D floors, FraggleScript, portals, ect.
127 //
128 // Revision 1.3  2000/11/09 17:56:20  stroggonmeth
129 // Hopefully fixed a few bugs and did a few optimizations.
130 //
131 // Revision 1.2  2000/11/04 16:23:44  bpereira
132 // Revision 1.1  2000/11/02 17:57:28  stroggonmeth
133 // FraggleScript files...
134 //
135 //
136 //--------------------------------------------------------------------------
137 //
138 // Functions
139 //
140 // functions are stored as variables(see variable.c), the
141 // value being a pointer to a 'handler' function for the
142 // function. Arguments are stored in an argc/argv-style list
143 //
144 // this module contains all the handler functions for the
145 // basic FraggleScript Functions.
146 //
147 // By Simon Howard
148 //
149 //---------------------------------------------------------------------------
150 
151 /* includes ************************/
152 
153 #include "doomincl.h"
154 #include "doomstat.h"
155 #include "command.h"
156 #include "d_main.h"
157 #include "g_game.h"
158 #include "hu_stuff.h"
159 #include "info.h"
160 #include "m_random.h"
161 #include "p_mobj.h"
162 #include "p_tick.h"
163 #include "p_spec.h"
164 //#include "p_hubs.h"
165 #include "p_inter.h"
166 #include "r_data.h"
167 #include "r_main.h"
168 #include "r_segs.h"
169 #include "r_defs.h"
170 #include "s_sound.h"
171 #include "w_wad.h"
172 #include "z_zone.h"
173 #include "p_local.h"
174 #include "p_setup.h"
175 #include "d_think.h"
176 #include "i_video.h"
177 
178 #include "t_parse.h"
179 #include "t_spec.h"
180 #include "t_script.h"
181 #include "t_oper.h"
182 #include "t_vari.h"
183 #include "t_func.h"
184 #include "t_array.h"
185 
186 #ifdef HWRENDER
187 #include "hardware/hw_light.h"
188   // lspr
189 #endif
190 
191 
192 fs_value_t  evaluate_expression(int start, int stop);
193 
194 
195 // array functions in t_array.c
196 void SF_NewArray(void);          // impls: array newarray(...)
197 void SF_NewEmptyArray(void);     // impls: array newemptyarray(...)
198 void SF_ArrayCopyInto(void);     // impls: void copyinto(array, array)
199 void SF_ArrayElementAt(void);    // impls: 'a elementat(array, int)
200 void SF_ArraySetElementAt(void); // impls: void setelementat(array, int, 'a)
201 void SF_ArrayLength(void);       // impls: int length(array)
202 
203 
204 // return a Z_Malloc string of the args( i1.. ) concatenated
205 // The Z_Malloc string has 3 extra chars allocated, to allow an append.
206 // The return string must be freed.
Z_cat_args(int i1)207 char *  Z_cat_args( int i1 )
208 {
209     int strsize = 0;
210     int i;
211 
212     for (i = i1; i < t_argc; i++)
213         strsize += strlen(stringvalue(t_argv[i]));
214 
215     char * tempstr = Z_Malloc(strsize + 4, PU_IN_USE, 0);
216     tempstr[0] = '\0';
217 
218     for (i = i1; i < t_argc; i++)
219     {
220         strcat(tempstr, stringvalue(t_argv[i])); // append
221     }
222 
223     return tempstr;
224 }
225 
226 // Some error handling
wrong_num_arg(const char * funcname,int num_args)227 void  wrong_num_arg( const char * funcname, int num_args )
228 {
229     script_error("%s: wrong num arg (%i)\n", funcname, num_args);
230 }
231 
missing_arg(const char * funcname,int min_num_args)232 void  missing_arg( const char * funcname, int min_num_args )
233 {
234     script_error("%s: missing arg (%i)\n", funcname, min_num_args);
235 }
236 
missing_arg_str(const char * funcname,const char * argstr)237 void  missing_arg_str( const char * funcname, const char * argstr )
238 {
239     script_error("%s: missing arg (%s)\n", funcname, argstr);
240 }
241 
242 static
player_not_in_game(const char * funcname,byte playernum)243 void  player_not_in_game( const char * funcname, byte playernum )
244 {
245     // [WDJ] No reason for script to fail because a player is not in game
246 #if 0
247     script_error("%s: player %i not in game\n", funcname, playernum);
248 #endif
249 }
250 
251 static
arg_playernum(fs_value_t * arg,const char * funcname)252 byte  arg_playernum( fs_value_t * arg, const char * funcname )
253 {
254     unsigned int playernum;
255     if ( arg->type == FSVT_mobj)
256     {
257         if (! arg->value.mobj->player)  goto mobj_err;
258         playernum = arg->value.mobj->player - players;
259     }
260     else
261         playernum = intvalue(*arg);
262 
263     if ( playernum >= MAXPLAYERS
264          || ! playeringame[playernum] )  goto player_err;
265     return playernum;
266 
267 mobj_err:
268     script_error("%s: mobj arg not a player!\n", funcname);
269     goto errexit;
270 
271 player_err:
272     player_not_in_game( funcname, playernum );
273     goto errexit;
274 
275 errexit:
276     return  255;  //  > MAXPLAYERS, indicates error
277 }
278 
279 
280 
281 
282 // functions. SF_ means Script Function not, well.. heh, me
283 
284         /////////// actually running a function /////////////
285 
286 /*******************
287   FUNCTIONS
288  *******************/
289 
290 // Actual handler functions for the script functions
291 
292 // arguments are evaluated and passed to the
293 // handler functions using 't_argc' and 't_argv'
294 // in a similar way to the way C does with command line options.
295 
296 // values can be returned from the functions using
297 // the variable 't_return'
298 
SF_Print(void)299 void SF_Print(void)
300 {
301     int i;
302 
303     if (!t_argc)
304         return;
305 
306     for (i = 0; i < t_argc; i++)
307     {
308         GenPrintf(EMSG_playmsg, "%s", stringvalue(t_argv[i]));
309     }
310 }
311 
312 // return a random number from 0 to 255
SF_Rnd(void)313 void SF_Rnd(void)
314 {
315     t_return.type = FSVT_int;
316     t_return.value.i = rand() % 256;
317 }
318 
319 // return a random number from 0 to 255
SF_PRnd(void)320 void SF_PRnd(void)
321 {
322     t_return.type = FSVT_int;
323     // Legacy Fragglescript use of P_Random, not in Doom.
324     t_return.value.i = PP_Random(pL_PRnd);
325 }
326 
327 // Find the next outermost
328 // loop we are currently in and return the section_t for it.
in_looping_section(void)329 fs_section_t * in_looping_section(void)
330 {
331     // deepest level loop we're in that has been found so far
332     fs_section_t * loopfnd = NULL;
333     int n;
334 
335     // check thru all the hashchains
336     for (n = 0; n < SECTIONSLOTS; n++)
337     {
338         fs_section_t *current = fs_current_script->sections[n];
339 
340         // check all the sections in this hashchain
341         while (current)
342         {
343             if (current->type == FSST_loop) // a loop?
344             {
345                 // check to see if it's a loop that we're inside
346                 if (fs_src_cp >= current->start && fs_src_cp <= current->end)
347                 {
348                     // a deeper nesting level than already found ?
349                     if (!loopfnd || (current->start > loopfnd->start))
350                         loopfnd = current; // save it
351                 }
352             }
353             current = current->next;
354         }
355     }
356 
357     return loopfnd;        // return the closest one found
358 }
359 
360 // "continue;" in FraggleScript is a function
SF_Continue(void)361 void SF_Continue(void)
362 {
363     fs_section_t *section;
364 
365     if (!(section = in_looping_section()))  goto err_notloop; // no loop found
366 
367     fs_src_cp = section->end;       // jump to the closing brace
368     return;
369 
370 err_notloop:
371     script_error("Continue: not in loop\n");
372     return;
373 }
374 
SF_Break(void)375 void SF_Break(void)
376 {
377     fs_section_t *section;
378 
379     if (!(section = in_looping_section()))  goto err_notloop;
380 
381     fs_src_cp = section->end + 1;   // jump out of the loop
382     return;
383 
384 err_notloop:
385     script_error("Break: not in loop\n");
386     return;
387 }
388 
389 // Goto( label )
SF_Goto(void)390 void SF_Goto(void)
391 {
392     if (t_argc != 1)  goto err_numarg;
393 
394     // check argument is a labelptr
395     if (t_argv[0].type != FSVT_label)  goto err_not_label;
396 
397     // go there then if everythings fine
398     fs_src_cp = t_argv[0].value.labelptr;
399 done:
400     return;
401 
402 err_numarg:
403     wrong_num_arg("Goto", 1);
404     goto done;
405 
406 err_not_label:
407     script_error("Goto: argument not a label\n");
408     goto done;
409 }
410 
SF_Return(void)411 void SF_Return(void)
412 {
413     fs_killscript = true;  // kill the script
414 }
415 
416 // Include( name )
SF_Include(void)417 void SF_Include(void)
418 {
419     char tempstr[9];
420 
421     if (t_argc != 1)  goto err_numarg;
422 
423     memset(tempstr, 0, 9);
424 
425     if (t_argv[0].type == FSVT_string)
426         strncpy(tempstr, t_argv[0].value.s, 8);
427     else
428         snprintf(tempstr, 8, "%s", stringvalue(t_argv[0]));
429     tempstr[8] = '\0';
430 
431     parse_include(tempstr);
432 done:
433     return;
434 
435 err_numarg:
436     wrong_num_arg("Include", 1);
437     goto done;
438 }
439 
SF_Input(void)440 void SF_Input(void)
441 {
442 #if 1
443    // [WDJ] was disabled in 143beta_macosx
444    // Doing gets() will probably freeze program until user cancels the input.
445    // If it does errmsg "not available in doom", then why do it, FIXME maybe ??
446         static char inputstr[128];
447         // [WDJ] NEVER use gets(), it can overrun the buffer, use fgets()
448         fgets(inputstr, 128, stdin);
449 
450         t_return.type = FSVT_string;
451         t_return.value.s = inputstr;
452 #endif
453     CONS_Printf("input() function not available in doom\a\n");
454 }
455 
SF_Beep(void)456 void SF_Beep(void)
457 {
458     CONS_Printf("\3");
459 }
460 
SF_Clock(void)461 void SF_Clock(void)
462 {
463     t_return.type = FSVT_int;
464     t_return.value.i = (gametic * 100) / 35;
465 }
466 
SF_ClockTic(void)467 void SF_ClockTic(void)
468 {
469     t_return.type = FSVT_int;
470     t_return.value.i = gametic;
471 }
472 
473     /**************** doom stuff ****************/
474 
SF_ExitLevel(void)475 void SF_ExitLevel(void)
476 {
477     G_ExitLevel();
478 }
479 
480 // 08/25/04 iori: warp(<skill>, <"map">, [reset 0|1]);
481 // Warp( skill, mapname, {reset} )
482 // skill= 1..5, reset= 0,1
SF_Warp(void)483 void SF_Warp(void)
484 {
485     int reset = 1;
486     int skill;
487 
488     if(t_argc < 2)  goto err_numarg;
489 
490     skill = t_argv[0].value.i;
491     if (skill < 1 || skill > 5)  goto err_skill;
492 
493     if(t_argc > 2)
494     {
495         reset = t_argv[2].value.i;
496         if(reset != 0 && reset != 1)  goto err_reset;
497     }
498     // skill 0..4
499     G_InitNew(skill - 1, t_argv[1].value.s, reset);
500 done:
501     return;
502 
503 err_numarg:
504     missing_arg("Warp", 2);  // 2, 3
505     goto done;
506 
507 err_skill:
508     script_error("Warp: Skill must be between 1 and 5.\n");
509     goto done;
510 
511 err_reset:
512     script_error("Warp: Reset must be either 0 or 1.\n");
513     goto done;
514 }
515 
516 // centered msg, default display time
517 // To all players
518 // Tip( tipstring ... )   upto 128 strings
SF_Tip(void)519 void SF_Tip(void)
520 {
521     if (fs_current_script->trigger->player == displayplayer_ptr)
522     {
523         char * tempstr = Z_cat_args(0);  // concat arg0, arg1, ...
524         HU_SetTip(tempstr, 53);
525         Z_Free(tempstr);
526     }
527     return;
528 }
529 
530 // SoM: Timed tip!
531 // To all players
532 // TimedTip( tiptime, tipstring, ... )   upto 127 strings
SF_TimedTip(void)533 void SF_TimedTip(void)
534 {
535     int tiptime;
536 
537     if (t_argc < 2)  goto err_numarg;
538 
539     tiptime = (intvalue(t_argv[0]) * 35) / 100;
540 
541     if (fs_current_script->trigger->player == displayplayer_ptr)
542     {
543         char * tempstr = Z_cat_args(1);  // concat arg1, arg2, ...
544         //debug_Printf("%s\n", tempstr);
545         HU_SetTip(tempstr, tiptime);
546         Z_Free(tempstr);
547     }
548 done:
549     return;
550 
551 err_numarg:
552     missing_arg("TimedTip", 2);  // 2 or more, upto 128
553     goto done;
554 }
555 
556 // tip to a particular player
557 // PlayerTip( playernum, tipstring, ... )  upto 127 strings
558 // No restriction on playernum, contrary to docs.
SF_PlayerTip(void)559 void SF_PlayerTip(void)
560 {
561     unsigned int plnum;
562 
563     if (t_argc < 2)  goto err_numarg;  // [WDJ] requires 2 args
564 
565     plnum = intvalue(t_argv[0]);
566     if ( plnum == consoleplayer )
567     {
568         char * tempstr = Z_cat_args(1);  // concat arg1, arg2, ...
569         //debug_Printf("%s\n", tempstr);
570         HU_SetTip(tempstr, 53);
571         Z_Free(tempstr);
572     }
573 done:
574     return;
575 
576 err_numarg:
577     missing_arg("PlayerTip", 2);  // 2 or more, upto 128
578     goto done;
579 }
580 
581 // message to all players
582 // Message( msgstring, ... )   upto 128 strings
SF_Message(void)583 void SF_Message(void)
584 {
585     if (fs_current_script->trigger->player == displayplayer_ptr)
586     {
587         char * tempstr = Z_cat_args(0);  // concat arg0, arg1, ...
588         GenPrintf(EMSG_playmsg, "%s\n", tempstr);
589         Z_Free(tempstr);
590     }
591 }
592 
593 
594 //DarkWolf95:July 28, 2003:Added unimplemented function
595 // Return skill 1..5
SF_GameSkill(void)596 void SF_GameSkill(void)
597 {
598     t_return.type = FSVT_int;
599     t_return.value.i = gameskill + 1;  //make 1-5, rather than 0-4
600 }
601 
602 // Returns what type of game is going on - Deathmatch, CoOp, or Single Player.
603 // Feature Requested by SoM! SSNTails 06-13-2002
604 // GameMode()
605 // Return 0=Single, 1=Coop, 2=Deathmatch
SF_GameMode(void)606 void SF_GameMode(void)
607 {
608     t_return.type = FSVT_int;
609 
610     if( deathmatch )    // deathmatch!
611         t_return.value.i = 2;
612     else if (netgame || multiplayer)    // Cooperative
613         t_return.value.i = 1;
614     else        // Single Player
615         t_return.value.i = 0;
616 
617     return;
618 }
619 
620 // message to a particular player
621 // PlayerMsg( playernum, msgstring, ... )  upto 127 strings
622 // No restriction on playernum, contrary to docs.
SF_PlayerMsg(void)623 void SF_PlayerMsg(void)
624 {
625     unsigned int plnum;
626 
627     if (t_argc < 2)  goto err_numarg;
628 
629     plnum = intvalue(t_argv[0]);
630     if( (plnum == displayplayer) || (plnum == displayplayer2) )
631     {
632         char * tempstr = Z_cat_args(1);  // concat arg1, arg2, ...
633         GenPrintf( (plnum == displayplayer)? EMSG_playmsg: EMSG_playmsg2,
634                    "%s\n", tempstr);
635         Z_Free(tempstr);
636     }
637 done:
638     return;
639 
640 err_numarg:
641     missing_arg("PlayerMsg", 2);  // 2 or more, upto 128
642     goto done;
643 }
644 
645 // PlayerInGame( playernum )
SF_PlayerInGame(void)646 void SF_PlayerInGame(void)
647 {
648     unsigned int plnum;
649 
650     if (t_argc != 1)  goto err_numarg;
651 
652     t_return.type = FSVT_int;
653     t_return.value.i = 0; // default
654 
655     plnum = intvalue(t_argv[0]);
656     if( plnum < MAXPLAYERS )  // [WDJ] safe limit
657         t_return.value.i = playeringame[plnum];
658 done:
659     return;
660 
661 err_numarg:
662     wrong_num_arg("PlayerInGame", 1);
663     goto done;
664 }
665 
666 // PlayerName( {playernum} )
SF_PlayerName(void)667 void SF_PlayerName(void)
668 {
669     unsigned int plnum;
670 
671     t_return.type = FSVT_string;
672     t_return.value.s = "";
673 
674     if (!t_argc)
675     {
676         // player that triggered script
677         player_t *pl = fs_current_script->trigger->player;
678         if (pl == NULL)  goto err_notplayer;
679         plnum = pl - players;
680     }
681     else
682     {
683         plnum = intvalue(t_argv[0]);
684     }
685 
686     if( plnum < MAXPLAYERS )  // [WDJ] safe limit
687         t_return.value.s = player_names[plnum];
688 done:
689     return;
690 
691 err_notplayer:
692     script_error("PlayerName: script not started by player\n");
693     goto done;
694 }
695 
696 // PlayerAddFrag( playernum1, {playernum2} )
SF_PlayerAddFrag(void)697 void SF_PlayerAddFrag(void)
698 {
699     unsigned int playernum1;
700 
701     if (t_argc < 1)  goto err_numarg;
702 
703     t_return.type = FSVT_int;
704     t_return.value.f = 0;  // default
705 
706     playernum1 = intvalue(t_argv[0]);
707     if ( playernum1 < MAXPLAYERS        // [WDJ] Safe limit
708          && playeringame[playernum1] )  // [WDJ] Only if player in game
709     {
710         if (t_argc == 1)
711         {
712             // player1 fragged
713             players[playernum1].addfrags++;
714             t_return.value.f = players[playernum1].addfrags;
715         }
716         else
717         {
718             // player1 fragged by player2
719             int playernum2 = intvalue(t_argv[1]);
720 
721             if ( playernum2 < MAXPLAYERS        // [WDJ] Safe limit
722                  && playeringame[playernum2] )  // [WDJ] Only if player in game
723             {
724                 players[playernum1].frags[playernum2]++;
725                 t_return.value.f = players[playernum1].frags[playernum2];
726             }
727         }
728     }
729 done:
730     return;
731 
732 err_numarg:
733     missing_arg_str("PlayerAddFrag", "1 or 2");
734     goto done;
735 }
736 
737 // player mobj
738 // PlayerObj( {playernum} )
SF_PlayerObj(void)739 void SF_PlayerObj(void)
740 {
741     unsigned int plnum;
742 
743     t_return.type = FSVT_mobj;
744     t_return.value.mobj = NULL;  // default
745 
746     if (!t_argc)
747     {
748         // player that triggered script
749         player_t *pl;
750         pl = fs_current_script->trigger->player;
751         if (pl == NULL)  goto err_notplayer;
752         plnum = pl - players;
753     }
754     else
755     {
756         plnum = intvalue(t_argv[0]);
757     }
758 
759     if ( plnum < MAXPLAYERS        // [WDJ] Safe limit
760          && playeringame[plnum] )  // [WDJ] Only if player in game
761         t_return.value.mobj = players[plnum].mo;
762 done:
763     return;
764 
765 err_notplayer:
766     script_error("PlayerObj: script not started by player\n");
767     goto done;
768 }
769 
770 // MobjIsPlayer( {mobj} )
SF_MobjIsPlayer(void)771 void SF_MobjIsPlayer(void)
772 {
773     t_return.type = FSVT_int;
774     if (t_argc == 0)
775     {
776         // if player triggered script
777         t_return.value.i = fs_current_script->trigger->player ? 1 : 0;
778     }
779     else
780     {
781         mobj_t * mobj = MobjForSvalue(t_argv[0]);
782         t_return.value.i = (mobj)? (mobj->player ? 1 : 0) : 0;
783     }
784     return;
785 }
786 
787 // Test or Set
788 // SkinColor( player, {colornum} )
SF_SkinColor(void)789 void SF_SkinColor(void)
790 {
791     byte playernum;
792 
793     if (t_argc < 1)  goto err_numarg;
794 
795     t_return.type = FSVT_int;
796     t_return.value.i = 0;  // default
797 
798     playernum = arg_playernum( &t_argv[0], "SkinColor" );
799     if( playernum >= MAXPLAYERS )  goto done;
800 
801     if(t_argc == 2)
802     {
803         // set skincolor
804         int colour = intvalue(t_argv[1]);
805 
806         if(colour > NUMSKINCOLORS-1)  // [WDJ] was NUMSKINCOLORS
807         {
808             script_error("SkinColor: skin colour %i > %i\n", colour, NUMSKINCOLORS-1);
809             goto done;
810         }
811 
812         P_SetPlayer_color( &players[playernum], colour );
813 
814         // Test for netplay and splitscreen usage.
815 #if 1
816         // Would automatically trigger a NetXCmd to other clients, but is that necessary ?
817         // Should not affect user settings.
818         if( playernum == displayplayer )
819         {
820             cv_playercolor[0].EV = colour;
821 //            Send_NameColor_pind( 0 );
822 	}
823         else  if( playernum == displayplayer2 )
824         {
825             cv_playercolor[1].EV = colour;
826 //            Send_NameColor_pind( 1 );
827 	}
828 #else
829         // This will affect user settings, and trigger a NetXCmd send to other clients.
830         if( playernum == displayplayer )
831             CV_SetValue (&cv_playercolor[0], colour);  // affects user config value
832         else  if( playernum == displayplayer2 )
833             CV_SetValue (&cv_playercolor[1], colour);  // affects user config value
834 #endif
835     }
836 
837     t_return.value.i = players[playernum].skincolor;
838 done:
839     return;
840 
841 err_numarg:
842     missing_arg_str("SkinColor", "1 or 2");
843     goto done;
844 }
845 
846 // Tests and modifies the usual keys 0..5
847 // PlayerKeys( player, keynum, {value} )
848 // [WDJ] keynum: 0..7, the usual 0..5, and additional keys 6,7
849 // value: 0,1
SF_PlayerKeys(void)850 void SF_PlayerKeys(void)
851 {
852     byte playernum;
853     int keynum;
854     byte keymask;
855 
856     if (t_argc < 2)   goto err_numarg;
857 
858     t_return.type = FSVT_int;
859     t_return.value.i = 0;  // default
860 
861     playernum = arg_playernum( &t_argv[0], "PlayerKeys" );
862     if( playernum >= MAXPLAYERS )  goto done;
863 
864     keynum = intvalue(t_argv[1]);
865     if (keynum > 7)  goto bad_keyvalue;  // [WDJ] was 5
866     keymask = (1 << keynum);
867 
868     if (t_argc == 2)
869     {
870         // test, player has key
871         t_return.value.i = (players[playernum].cards & keymask) ? 1 : 0;
872     }
873     else
874     {
875         // give or take key
876         int givetake = intvalue(t_argv[2]);
877         if (givetake)
878             players[playernum].cards |= keymask;  // give key
879         else
880             players[playernum].cards &= ~keymask;  // take key
881     }
882 
883 done:
884     return;
885 
886 err_numarg:
887     missing_arg_str("PlayerKeys", "2 or 3");
888     goto done;
889 
890 bad_keyvalue:
891     script_error("PlayerKeys: keynum must be 0..5! %i\n", keynum);
892     goto done;
893 }
894 
895 /*  DarkWolf95:September 17, 2004:playerkeysb
896 
897     Returns players[i].cards as a whole, since FS supports binary operators.
898     Also allows you to set upper two bits of cards (64 & 128).  Thus the user
899     can have two new boolean values to work with.  CTF, Runes, Tag...
900  */
901 
902 // Test or Set
903 // PlayerKeysByte(player, {newbyte})
SF_PlayerKeysByte(void)904 void SF_PlayerKeysByte(void)
905 {
906     byte playernum = 0;
907 
908     if (t_argc < 1)  goto err_numarg;
909 
910     t_return.type = FSVT_int;
911     t_return.value.i = 0;  // default
912 
913     // [WDJ] full player arg like other functions
914     playernum = arg_playernum( &t_argv[0], "PlayerKeysByte" );
915     if( playernum >= MAXPLAYERS )  goto done;
916 
917     if(t_argc == 2)
918     {
919         // set keys
920         unsigned int keybyte = intvalue(t_argv[1]);
921         if(keybyte > 255)  // don't overflow
922             keybyte = 0;
923 
924         players[playernum].cards = keybyte;  // set
925     }
926     t_return.value.i = players[playernum].cards;
927 
928 done:
929     return;
930 
931 err_numarg:
932     missing_arg_str("PlayerKeysByte", "1 or 2");
933     goto done;
934 }
935 
936 // iori 05/17/2005: playerarmor
937 // [WDJ] Test or Set
938 // PlayerArmor( player, {armor_value} )
SF_PlayerArmor(void)939 void SF_PlayerArmor(void)
940 {
941     int armor;
942     byte playernum;
943     player_t * player;
944 
945     if (t_argc < 1)  goto err_numarg;
946 
947     t_return.type = FSVT_int;
948     t_return.value.i = 0;  // default
949 
950     // [WDJ] full player arg like other functions
951     playernum = arg_playernum( &t_argv[0], "PlayerArmor" );
952     if( playernum >= MAXPLAYERS )  goto done;
953     player = & players[playernum];
954 
955     if ( t_argc == 2 )
956     {
957         // Set armor
958         armor = t_argv[1].value.i;
959         player->armorpoints = armor;
960         player->armortype = (armor>100)? 2 : 1;
961     }
962 
963     t_return.value.i = player->armorpoints;
964 done:
965     return;
966 
967 err_numarg:
968     missing_arg_str("PlayerArmor", "1 or 2");
969     goto done;
970 }
971 
972 // Test or Set
973 // PlayerAmmo( player, ammonum, {ammo_value} )
SF_PlayerAmmo(void)974 void SF_PlayerAmmo(void)
975 {
976     int ammonum;
977     byte playernum;
978     player_t * player;
979 
980     if (t_argc < 2)  goto err_numarg;
981 
982     t_return.type = FSVT_int;
983     t_return.value.i = 0;  // default
984 
985     playernum = arg_playernum( &t_argv[0], "PlayerAmmo" );
986     if( playernum >= MAXPLAYERS )  goto done;
987     player = & players[playernum];
988 
989     ammonum = intvalue(t_argv[1]);
990     if (ammonum >= NUMAMMO || ammonum < 0)  goto bad_ammo;
991 
992     if (t_argc == 3)
993     {
994         // set player ammo
995         int maxammo = player->maxammo[ammonum];
996         int newammo = intvalue(t_argv[2]);
997         newammo = (newammo > maxammo) ? maxammo : newammo;
998         player->ammo[ammonum] = newammo;
999     }
1000     t_return.value.i = player->ammo[ammonum];  // test ammo
1001 done:
1002     return;
1003 
1004 err_numarg:
1005     missing_arg_str("PlayerAmmo", "2 or 3");
1006     goto done;
1007 
1008 bad_ammo:
1009     script_error("PlayerAmmo: invalid ammonum %i\n", ammonum);
1010     goto done;
1011 }
1012 
1013 // Test or Set
1014 // MaxPlayerAmmo( player, ammonum, {ammo_value} )
SF_MaxPlayerAmmo(void)1015 void SF_MaxPlayerAmmo(void)
1016 {
1017     byte playernum;
1018     int ammonum;
1019     player_t * player;
1020 
1021     if (t_argc < 2)  goto err_numarg;
1022 
1023     t_return.type = FSVT_int;
1024     t_return.value.i = 0;  // default
1025 
1026     playernum = arg_playernum( &t_argv[0], "MaxPlayerAmmo" );
1027     if( playernum >= MAXPLAYERS )  goto done;
1028     player = & players[playernum];
1029 
1030     ammonum = intvalue(t_argv[1]);
1031     if (ammonum >= NUMAMMO || ammonum < 0)  goto bad_ammo;
1032 
1033     if (t_argc == 3)
1034     {
1035         // set player max ammo
1036         int newmax = intvalue(t_argv[2]);
1037         player->maxammo[ammonum] = newmax;
1038     }
1039     t_return.value.i = player->maxammo[ammonum]; // test player max ammo
1040 done:
1041     return;
1042 
1043 err_numarg:
1044     missing_arg_str("MaxPlayerAmmo", "2 or 3");
1045     goto done;
1046 
1047 bad_ammo:
1048     script_error("MaxPlayerAmmo: invalid ammonum %i\n", ammonum);
1049     goto done;
1050 }
1051 
1052 // Test or Set
1053 // PlayerWeapon(player, weaponnum, [give])
SF_PlayerWeapon(void)1054 void SF_PlayerWeapon(void)
1055 {
1056     int weaponnum;
1057     byte playernum;
1058     player_t * player;
1059 
1060     if (t_argc < 2)  goto err_numarg;
1061 
1062     t_return.type = FSVT_int;
1063     t_return.value.i = 0;  // default
1064 
1065     playernum = arg_playernum( &t_argv[0], "PlayerWeapon" );
1066     if( playernum >= MAXPLAYERS )  goto done;
1067     player = & players[playernum];
1068 
1069     weaponnum = intvalue(t_argv[1]);
1070     if (weaponnum >= NUMWEAPONS || weaponnum < 0)  goto bad_weapon;
1071 
1072     if (t_argc == 3)
1073     {
1074         // give or take weapon
1075         int newweapon = intvalue(t_argv[2]);
1076 
1077         if (newweapon != 0)  // boolean
1078             newweapon = 1;
1079 
1080         player->weaponowned[weaponnum] = newweapon;
1081     }
1082     t_return.value.i = player->weaponowned[weaponnum]; // test weapon
1083 done:
1084     return;
1085 
1086 err_numarg:
1087     missing_arg_str("PlayerWeapon", "2 or 3");
1088     goto done;
1089 
1090 bad_weapon:
1091     script_error("PlayerWeapon: invalid weaponnum %i\n", weaponnum);
1092     goto done;
1093 }
1094 
1095 // Test or Set
1096 // PlayerSelectedWeapon( player, {selected_weapon} )
SF_PlayerSelectedWeapon(void)1097 void SF_PlayerSelectedWeapon(void)
1098 {
1099     int weaponnum;
1100     byte playernum;
1101     player_t * player;
1102 
1103     if (!t_argc)  goto err_numarg;
1104 
1105     t_return.type = FSVT_int;
1106     t_return.value.i = 0;  // default
1107 
1108     playernum = arg_playernum( &t_argv[0], "PlayerSelectedWeapon" );
1109     if( playernum >= MAXPLAYERS )  goto done;
1110     player = & players[playernum];
1111 
1112     if(t_argc == 2)
1113     {
1114         weaponnum = intvalue(t_argv[1]);
1115         if (weaponnum >= NUMWEAPONS || weaponnum < 0)  goto bad_weapon;
1116         player->pendingweapon = weaponnum;
1117     }
1118     t_return.value.i = player->readyweapon;  // test weapon
1119 done:
1120     return;
1121 
1122 err_numarg:
1123     missing_arg_str("PlayerSelectedWeapon", "1 or 2");
1124     goto done;
1125 
1126 bad_weapon:
1127     script_error("PlayerSelectedWeapon: invalid weaponnum %i\n", weaponnum);
1128     goto done;
1129 }
1130 
1131 
1132 // Exl: Toxicfluff's pitchview function.
1133 // Returns a player's view pitch in a range useful for the FS trig functions
1134 // iori: added ability to modify player's pitch
1135 // Test or Set
1136 // PlayerPitch( player, {pitch} )
SF_PlayerPitch(void)1137 void SF_PlayerPitch(void)
1138 {
1139     byte playernum;
1140 
1141     if (!t_argc)  goto err_numarg;
1142 
1143     t_return.type = FSVT_fixed;
1144     t_return.value.f = 0;  // default
1145 
1146     // [WDJ] full player arg like other functions
1147     playernum = arg_playernum( &t_argv[0], "PlayerPitch" );
1148     if( playernum >= MAXPLAYERS )  goto done;
1149 
1150     if(t_argc == 2)
1151     {
1152         angle_t new_angle = FixedToAngle(fixedvalue(t_argv[1]));
1153         // Test for netplay and splitscreen usage.
1154         if( playernum == displayplayer )
1155             localaiming[0] = new_angle;
1156         else if( playernum == displayplayer2 )
1157             localaiming[1] = new_angle;
1158     }
1159 
1160     t_return.value.f = AngleToFixed(players[playernum].aiming);
1161 
1162 done:
1163     return;
1164 
1165 err_numarg:
1166     missing_arg_str("PlayerPitch", "1 or 2");
1167     goto done;
1168 }
1169 
1170 
1171 // Set player properties
1172 // This could (or rather, should) be expanded to support all players
1173 // Set
1174 // PlayerProperty( select, value )
SF_PlayerProperty(void)1175 void SF_PlayerProperty(void)
1176 {
1177     int arg_value;
1178 
1179     if (t_argc != 2)  goto err_numarg;
1180 
1181     arg_value = intvalue(t_argv[1]);
1182 
1183     switch(intvalue(t_argv[0]))
1184     {
1185        // Speed
1186      case 0:
1187         extramovefactor = arg_value;
1188         break;
1189 
1190      case 1:
1191         jumpgravity = arg_value * FRACUNIT / NEWTICRATERATIO;
1192         break;
1193 
1194      case 2:
1195         consoleplayer_ptr->locked = (arg_value)?  true :  false;
1196         break;
1197 
1198      default:
1199         script_error("PlayerProperty, invalid property specified\n");
1200         break;
1201     }
1202 done:
1203    return;
1204 
1205 err_numarg:
1206     wrong_num_arg("PlayerProperty", 2);
1207     goto done;
1208 }
1209 
1210 
1211 extern void SF_StartScript(void);   // in t_script.c
1212 extern void SF_ScriptRunning(void);
1213 extern void SF_Wait(void);
1214 extern void SF_WaitTic(void);
1215 extern void SF_TagWait(void);
1216 extern void SF_ScriptWait(void);
1217 
1218 /*********** Mobj code ***************/
1219 
1220 // Return playernum, or -1
1221 // Player( {mobj} )
SF_Player(void)1222 void SF_Player(void)
1223 {
1224     mobj_t *mo = t_argc ? MobjForSvalue(t_argv[0]) : fs_current_script->trigger;
1225 
1226     t_return.type = FSVT_int;
1227 
1228     if (mo && mo->player)  // [WDJ] check player
1229     {
1230         t_return.value.i = (unsigned int) (mo->player - players);
1231     }
1232     else
1233     {
1234         t_return.value.i = -1;  // programs may depende on it being negative
1235     }
1236 }
1237 
1238 // spawn an object: type, x, y, [angle]
1239 // Spawn( type, x, y, {angle}, {z} )
SF_Spawn(void)1240 void SF_Spawn(void)
1241 {
1242     mapthing_t * mthing;
1243     int x, y, z, objtype;
1244     angle_t angle = 0;
1245 
1246     if (t_argc < 3)  goto err_numarg;
1247 
1248     t_return.type = FSVT_mobj;
1249     t_return.value.mobj = NULL;  // default
1250 
1251     objtype = intvalue(t_argv[0]);
1252     x = intvalue(t_argv[1]) << FRACBITS;
1253     y = intvalue(t_argv[2]) << FRACBITS;
1254     if (t_argc >= 5)
1255         z = intvalue(t_argv[4]) << FRACBITS;
1256     else
1257     {
1258         // SoM: Check thing flags for spawn-on-ceiling types...
1259         z = R_PointInSubsector(x, y)->sector->floorheight;
1260     }
1261 
1262     if (t_argc >= 4)
1263         angle = intvalue(t_argv[3]) * (ANG45 / 45);
1264 
1265     // check invalid object to spawn
1266     if (objtype < 0 || objtype >= NUMMOBJTYPES)  goto err_objtype;
1267 
1268     t_return.value.mobj = P_SpawnMobj(x, y, z, objtype);
1269     t_return.value.mobj->angle = angle;
1270 
1271     // [WDJ] Only MF_SPECIAL things can respawn, and MF_COUNTKILL can nightmare
1272     // respawn, so only they need a spawnpoint.
1273     if (t_return.value.mobj->flags & (MF_SPECIAL|MF_COUNTKILL))
1274     {
1275         // create a unique mapthing for this spawn
1276         mthing = P_Get_Extra_Mapthing( MTF_FS_SPAWNED );  // [WDJ]
1277         t_return.value.mobj->spawnpoint = mthing;
1278         if (mthing)
1279         {
1280             //Hurdler: fix the crashing bug of respawning monster
1281             // 2002/9/7
1282             mthing->x = x >> FRACBITS;
1283             mthing->y = y >> FRACBITS;
1284             mthing->z = z >> FRACBITS;
1285             mthing->angle = angle >> FRACBITS;
1286             mthing->type = mobjinfo[objtype].doomednum;  // objtype;
1287             mthing->mobj = t_return.value.mobj;
1288         }
1289     }
1290 done:
1291     return;
1292 
1293 err_numarg:
1294     missing_arg_str("Spawn", "3, 4 or 5");
1295     goto done;
1296 
1297 err_objtype:
1298     script_error("Spawn: unknown object type: %i\n", objtype);
1299     goto done;
1300 }
1301 
1302 // [WDJ] Docs say: SpawnExplosion ( damage, spot, {source} )
1303 // but implemented as SpawnExplosion ( type, x, y, {z} )
SF_SpawnExplosion(void)1304 void SF_SpawnExplosion(void)
1305 {
1306     int type;
1307     fixed_t x, y, z;
1308     mobj_t *spawn;
1309 
1310     if (t_argc < 3)  goto err_numarg;
1311 
1312     type = intvalue(t_argv[0]);
1313     if (type < 0 || type >= NUMMOBJTYPES)  goto err_spawntype;
1314 
1315     x = fixedvalue(t_argv[1]);
1316     y = fixedvalue(t_argv[2]);
1317     if (t_argc > 3)
1318         z = fixedvalue(t_argv[3]);
1319     else
1320         z = R_PointInSubsector(x, y)->sector->floorheight;
1321 
1322     spawn = P_SpawnMobj(x, y, z, type);
1323     t_return.type = FSVT_int;
1324     t_return.value.i = P_SetMobjState(spawn, spawn->info->deathstate);
1325     if (spawn->info->deathsound)
1326         S_StartObjSound(spawn, spawn->info->deathsound);
1327 done:
1328     return;
1329 
1330 err_numarg:
1331     missing_arg_str("SpawnExplosion", "3 or 4");
1332     goto done;
1333 
1334 err_spawntype:
1335     script_error("SpawnExplosion: Invalid type number %i\n", type);
1336     goto done;
1337 }
1338 
1339 // RadiusAttack( location_mobj, source_mobj, damage )
SF_RadiusAttack(void)1340 void SF_RadiusAttack(void)
1341 {
1342     mobj_t *spot;
1343     mobj_t *source;
1344     int damage;
1345 
1346     if (t_argc != 3)  goto err_numarg;
1347 
1348     spot = MobjForSvalue(t_argv[0]);    // where
1349     source = MobjForSvalue(t_argv[1]);  // who gets blame
1350     damage = intvalue(t_argv[2]);
1351 
1352     if (spot && source)
1353     {
1354         P_RadiusAttack(spot, source, damage);
1355     }
1356 done:
1357     return;
1358 
1359 err_numarg:
1360     wrong_num_arg("RadiusAttack", 3);
1361     goto done;
1362 }
1363 
1364 // RemoveObj( mobj )
SF_RemoveObj(void)1365 void SF_RemoveObj(void)
1366 {
1367     mobj_t *mo;
1368 
1369     if (t_argc != 1)  goto err_numarg;
1370 
1371     mo = MobjForSvalue(t_argv[0]);
1372     if (mo)     // nullptr check
1373         P_RemoveMobj(mo);
1374 done:
1375     return;
1376 
1377 err_numarg:
1378     wrong_num_arg("RemoveObj", 1);
1379     goto done;
1380 }
1381 
1382 // KillObj( {mobj} )
SF_KillObj(void)1383 void SF_KillObj(void)
1384 {
1385     mobj_t *mo;
1386 
1387     if (t_argc)
1388         mo = MobjForSvalue(t_argv[0]);
1389     else
1390         mo = fs_current_script->trigger;   // default to trigger object
1391 
1392     if (mo)     // nullptr check
1393         P_KillMobj(mo, NULL, fs_current_script->trigger);  // kill it
1394 }
1395 
1396 // mobj x, y, z
1397 // ObjX( {mobj} )
SF_ObjX(void)1398 void SF_ObjX(void)
1399 {
1400     mobj_t *mo = t_argc ? MobjForSvalue(t_argv[0]) : fs_current_script->trigger;
1401 
1402     t_return.type = FSVT_fixed;
1403     t_return.value.f = mo ? mo->x : 0;  // null ptr check
1404 }
1405 
1406 // ObjY( {mobj} )
SF_ObjY(void)1407 void SF_ObjY(void)
1408 {
1409     mobj_t *mo = t_argc ? MobjForSvalue(t_argv[0]) : fs_current_script->trigger;
1410 
1411     t_return.type = FSVT_fixed;
1412     t_return.value.f = mo ? mo->y : 0;  // null ptr check
1413 }
1414 
1415 // ObjZ( {mobj} )
SF_ObjZ(void)1416 void SF_ObjZ(void)
1417 {
1418     mobj_t *mo = t_argc ? MobjForSvalue(t_argv[0]) : fs_current_script->trigger;
1419 
1420     t_return.type = FSVT_fixed;
1421     t_return.value.f = mo ? mo->z : 0;  // null ptr check
1422 }
1423 
1424 // SetObjPosition( mobj, x, {y}, {z} )
SF_SetObjPosition(void)1425 void SF_SetObjPosition(void)
1426 {
1427     mobj_t* mobj;
1428 
1429     if (t_argc < 2)  goto err_numarg;  // [WDJ] requires 2 arg
1430 
1431     mobj = MobjForSvalue(t_argv[0]);
1432     if( mobj )  // [WDJ]
1433     {
1434         P_UnsetThingPosition(mobj);
1435 
1436         mobj->x = intvalue(t_argv[1]) << FRACBITS;
1437 
1438         if(t_argc >= 3)
1439             mobj->y = intvalue(t_argv[2]) << FRACBITS;
1440         if(t_argc == 4)
1441             mobj->z = intvalue(t_argv[3]) << FRACBITS;
1442 
1443         P_SetThingPosition(mobj);
1444     }
1445 done:
1446     return;
1447 
1448 err_numarg:
1449     missing_arg_str("SetObjPosition", "2, 3 or 4");
1450     goto done;
1451 }
1452 
1453 
1454 // Resurrect( mobj )
SF_Resurrect(void)1455 void SF_Resurrect(void)
1456 {
1457     mobj_t *mo;
1458 
1459     if(t_argc != 1)  goto err_numarg;
1460 
1461     mo = MobjForSvalue(t_argv[0]);
1462     if( mo )  // [WDJ]
1463     {
1464         if(!mo->info->raisestate)  //Don't resurrect things that can't be resurrected
1465             goto done;
1466 
1467         P_SetMobjState (mo, mo->info->raisestate);
1468         if( demoversion<129 )
1469             mo->height <<= 2;
1470         else
1471         {
1472             mo->height = mo->info->height;
1473             mo->radius = mo->info->radius;
1474         }
1475 
1476         mo->flags = mo->info->flags;
1477         mo->health = mo->info->spawnhealth;
1478         mo->target = NULL;
1479     }
1480 done:
1481     return;
1482 
1483 err_numarg:
1484     wrong_num_arg("Resurrect", 1);
1485     goto done;
1486 }
1487 
1488 // TestLocation( {mobj} )
SF_TestLocation(void)1489 void SF_TestLocation(void)
1490 {
1491     mobj_t *mo = t_argc ? MobjForSvalue(t_argv[0]) : fs_current_script->trigger;
1492 
1493     t_return.type = FSVT_int;
1494     t_return.value.f = 0;  // default
1495 
1496     if (mo && P_TestMobjLocation(mo))
1497     {
1498         t_return.value.f = 1;
1499     }
1500 }
1501 
1502 // mobj angle
1503 // ObjAngle( {mobj}, {angle} )
SF_ObjAngle(void)1504 void SF_ObjAngle(void)
1505 {
1506     mobj_t *mo = t_argc ? MobjForSvalue(t_argv[0]) : fs_current_script->trigger;
1507 
1508     t_return.type = FSVT_fixed;
1509     t_return.value.f = 0;  // default
1510 
1511     if( mo )  // [WDJ]
1512     {
1513         if(t_argc > 1)
1514         {
1515             // set angle
1516             angle_t new_angle = FixedToAngle(fixedvalue(t_argv[1]));
1517             //iori: now able to change the player's angle, not just mobj's
1518             // Test for netplay and splitscreen usage.
1519             if(mo == consoleplayer_ptr->mo)
1520             {
1521                 localangle[0] = new_angle;
1522             }
1523             else if(displayplayer2_ptr && (mo == displayplayer2_ptr->mo))
1524             {
1525                 localangle[1] = new_angle;
1526             }
1527             else
1528             {
1529                 mo->angle = new_angle;
1530             }
1531         }
1532         t_return.value.f = (int)AngleToFixed(mo->angle);
1533     }
1534 }
1535 
1536 // CheckSight( obj1, {obj2} )
SF_CheckSight(void)1537 void SF_CheckSight(void)
1538 {
1539     mobj_t *obj1;
1540     mobj_t *obj2;
1541 
1542     if(!t_argc)  goto err_numarg;
1543 
1544     obj1 = MobjForSvalue(t_argv[0]);
1545     obj2 = (t_argc == 2) ? MobjForSvalue(t_argv[1]) : fs_current_script->trigger;
1546 
1547     t_return.type = FSVT_int;
1548     t_return.value.i = P_CheckSight(obj1, obj2);
1549 done:
1550     return;
1551 
1552 err_numarg:
1553     missing_arg_str("CheckSight", "1 or 2");
1554     goto done;
1555 }
1556 
1557 
1558 // Teleport( {mobj}, sector_tag )
SF_Teleport(void)1559 void SF_Teleport(void)
1560 {
1561     line_t sf_tmpline;  // temp teleport linedef
1562     mobj_t *mo;
1563 
1564     if (t_argc == 0)  goto err_numarg;
1565 
1566     if (t_argc == 1)       // 1 argument: sector tag
1567     {
1568         // teleport trigger mobj
1569         mo = fs_current_script->trigger;   // default to trigger
1570         sf_tmpline.tag = intvalue(t_argv[0]);
1571     }
1572     else
1573     {
1574         // teleport the arg mobj
1575         mo = MobjForSvalue(t_argv[0]);
1576         sf_tmpline.tag = intvalue(t_argv[1]);
1577     }
1578     sf_tmpline.dx = sf_tmpline.dy = 1;  // [WDJ] used by EV_Teleport
1579 
1580     if (mo)
1581         EV_Teleport(&sf_tmpline, 0, mo);
1582 done:
1583     return;
1584 
1585 err_numarg:
1586     missing_arg_str("Teleport", "1 or 2");
1587     goto done;
1588 }
1589 
1590 // SilentTeleport( {mobj}, sector_tag )
SF_SilentTeleport(void)1591 void SF_SilentTeleport(void)
1592 {
1593     line_t sf_tmpline;                // dummy line for teleport function
1594     mobj_t *mo;
1595 
1596     if (t_argc == 0)   goto err_numarg;
1597 
1598     if (t_argc == 1)       // 1 argument: sector tag
1599     {
1600         // teleport trigger mobj
1601         mo = fs_current_script->trigger;   // default to trigger
1602         sf_tmpline.tag = intvalue(t_argv[0]);
1603     }
1604     else
1605     {
1606         // teleport the arg mobj
1607         mo = MobjForSvalue(t_argv[0]);
1608         sf_tmpline.tag = intvalue(t_argv[1]);
1609     }
1610     sf_tmpline.dx = sf_tmpline.dy = 1;  // [WDJ] used by EV_SilentTeleport
1611 
1612     if (mo)
1613         EV_SilentTeleport(&sf_tmpline, 0, mo);
1614 done:
1615     return;
1616 
1617 err_numarg:
1618     missing_arg_str("SilentTeleport", "1 or 2");
1619     goto done;
1620 }
1621 
1622 // DamageObj( {mobj}, damage );
SF_DamageObj(void)1623 void SF_DamageObj(void)
1624 {
1625     mobj_t *mo;
1626     int damageamount;
1627 
1628     if (t_argc == 0)  goto err_numarg;
1629 
1630     if (t_argc == 1)       // 1 argument: damage trigger by amount
1631     {
1632         // damage the trigger mobj
1633         mo = fs_current_script->trigger;   // default to trigger
1634         damageamount = intvalue(t_argv[0]);
1635     }
1636     else
1637     {
1638         // damage the arg mobj
1639         mo = MobjForSvalue(t_argv[0]);
1640         damageamount = intvalue(t_argv[1]);
1641     }
1642 
1643     if (mo)
1644         P_DamageMobj(mo, NULL, fs_current_script->trigger, damageamount);
1645 done:
1646     return;
1647 
1648 err_numarg:
1649     missing_arg_str("DamageObj", "1 or 2");
1650     goto done;
1651 }
1652 
1653 
1654 // the tag number of the sector the thing is in
1655 // ObjSector( {mobj} )
SF_ObjSector(void)1656 void SF_ObjSector(void)
1657 {
1658     // use trigger object if not specified
1659     mobj_t *mo = t_argc ? MobjForSvalue(t_argv[0]) : fs_current_script->trigger;
1660 
1661     t_return.type = FSVT_int;
1662     // [WDJ] dsv4 map28 has buttons that hurt player, causes segfault here
1663     // when pressed after getting yellow key, mo with no subsector.
1664     t_return.value.i = (mo && mo->subsector) ? mo->subsector->sector->tag : 0;     // nullptr check
1665 }
1666 
1667 // the health number of an object
1668 // ObjHealth( {mobj} )
SF_ObjHealth(void)1669 void SF_ObjHealth(void)
1670 {
1671     // use trigger object if not specified
1672     mobj_t *mo = t_argc ? MobjForSvalue(t_argv[0]) : fs_current_script->trigger;
1673 
1674     t_return.type = FSVT_int;
1675     t_return.value.i = mo ? mo->health : 0;
1676 }
1677 
1678 // ObjDead( {mobj} )
SF_ObjDead(void)1679 void SF_ObjDead(void)
1680 {
1681     mobj_t *mo = t_argc ? MobjForSvalue(t_argv[0]) : fs_current_script->trigger;
1682 
1683     t_return.type = FSVT_int;
1684     if (mo && (mo->health <= 0 || mo->flags & MF_CORPSE))
1685         t_return.value.i = 1;
1686     else
1687         t_return.value.i = 0;
1688 }
1689 
1690 // Test or Set
1691 // ObjFlag( {mobj}, flagnum, {flagvalue} )
1692 // flagvalue= 0,1
SF_ObjFlag(void)1693 void SF_ObjFlag(void)
1694 {
1695     mobj_t *mo;
1696     int flagnum;
1697 
1698     if (t_argc == 0)  goto err_numarg;
1699 
1700     t_return.type = FSVT_int;
1701     t_return.value.i = 0;  // default
1702 
1703     if (t_argc == 1)       // use trigger, arg0 is flagnum
1704     {
1705         // test flags on trigger:
1706         mo = fs_current_script->trigger;
1707         flagnum = intvalue(t_argv[0]);
1708     }
1709     else
1710     {
1711         // test flags on arg mobj
1712         mo = MobjForSvalue(t_argv[0]);
1713         flagnum = intvalue(t_argv[1]);
1714     }
1715 
1716     if (mo && flagnum >= 0 && flagnum < 32)  // [WDJ]
1717     {
1718         uint32_t flagmsk = (1 << flagnum);
1719         if (t_argc == 3)
1720         {
1721             // set flags on arg mobj
1722             if( intvalue(t_argv[2]) )   // 0 or 1
1723                 mo->flags |= flagmsk;  // set the flag
1724             else
1725                 mo->flags &= ~flagmsk;  // clear the flag
1726             //P_UpdateThinker(&mo->thinker);     // update thinker
1727         }
1728 
1729         t_return.value.i = !!(mo->flags & flagmsk);  // test flag, to boolean
1730     }
1731 done:
1732     return;
1733 
1734 err_numarg:
1735     missing_arg_str("ObjFlag", "1, 2 or 3");
1736     goto done;
1737 }
1738 
1739 
1740 // Just copy n paste :>
1741 // Test or Set
1742 // ObjFlag2( {mobj}, flagnum, {flagvalue} )
1743 // flagvalue= 0,1
SF_ObjFlag2(void)1744 void SF_ObjFlag2(void)
1745 {
1746     mobj_t *mo;
1747     int flagnum;
1748 
1749     if (t_argc == 0)  goto err_numarg;
1750 
1751     t_return.type = FSVT_int;
1752     t_return.value.i = 0;  // default
1753 
1754     if (t_argc == 1)       // use trigger, arg0 is flagnum
1755     {
1756         // test flags on trigger:
1757         mo = fs_current_script->trigger;
1758         flagnum = intvalue(t_argv[0]);
1759     }
1760     else
1761     {
1762         // test flags on arg mobj
1763         mo = MobjForSvalue(t_argv[0]);
1764         flagnum = intvalue(t_argv[1]);
1765     }
1766 
1767     if (mo && flagnum >= 0 && flagnum < 32)  // [WDJ]
1768     {
1769         uint32_t flagmsk = (1 << flagnum);
1770         if (t_argc == 3)
1771         {
1772             // set flags on arg mobj
1773             if( intvalue(t_argv[2]) )   // 0 or 1
1774                 mo->flags2 |= flagmsk;  // set the flag
1775             else
1776                 mo->flags2 &= ~flagmsk;  // clear the flag
1777             //P_UpdateThinker(&mo->thinker);     // update thinker
1778         }
1779 
1780         t_return.value.i = !!(mo->flags2 & flagmsk);  // test flag, to boolean
1781     }
1782 done:
1783     return;
1784 
1785 err_numarg:
1786     missing_arg_str("ObjFlag2", "1, 2 or 3");
1787     goto done;
1788 }
1789 
1790 
1791 // Extra flags, too
SF_ObjEFlag(void)1792 void SF_ObjEFlag(void)
1793 {
1794     mobj_t *mo;
1795     int flagnum;
1796 
1797     if (t_argc == 0)  goto err_numarg;
1798 
1799     t_return.type = FSVT_int;
1800     t_return.value.i = 0;  // default
1801 
1802     if (t_argc == 1)       // use trigger, arg0 is flagnum
1803     {
1804         // test flags on trigger:
1805         mo = fs_current_script->trigger;
1806         flagnum = intvalue(t_argv[0]);
1807     }
1808     else
1809     {
1810         // test flags on arg mobj
1811         mo = MobjForSvalue(t_argv[0]);
1812         flagnum = intvalue(t_argv[1]);
1813     }
1814 
1815     if (mo && flagnum >= 0 && flagnum < 32)  // [WDJ]
1816     {
1817         uint32_t flagmsk = (1 << flagnum);
1818         if (t_argc == 3)
1819         {
1820             // set flags on arg mobj
1821             if( intvalue(t_argv[2]) )   // 0 or 1
1822                 mo->eflags |= flagmsk;  // set the flag
1823             else
1824                 mo->eflags &= ~flagmsk;  // clear the flag
1825             //P_UpdateThinker(&mo->thinker);     // update thinker
1826         }
1827 
1828         t_return.value.i = !!(mo->eflags & flagmsk);  // test flag, to boolean
1829     }
1830 done:
1831     return;
1832 
1833 err_numarg:
1834     missing_arg_str("ObjEFlag", "1, 2 or 3");
1835     goto done;
1836 }
1837 
1838 
1839 // apply momentum to a thing
1840 // PushThing( mobj, angle, force )
SF_PushThing(void)1841 void SF_PushThing(void)
1842 {
1843     mobj_t *mo;
1844     angle_t angle;
1845     fixed_t force;
1846 
1847     if (t_argc != 3)  goto err_numarg;
1848 
1849     mo = MobjForSvalue(t_argv[0]);
1850     if (!mo)  goto done;
1851 
1852     angle = FixedToAngle(fixedvalue(t_argv[1]));
1853     force = fixedvalue(t_argv[2]);
1854 
1855     mo->momx += FixedMul( cosine_ANG(angle), force);
1856     mo->momy += FixedMul( sine_ANG(angle), force);
1857 done:
1858     return;
1859 
1860 err_numarg:
1861     wrong_num_arg("PushThing", 3);
1862     goto done;
1863 }
1864 
1865 // Test or Set
1866 // ReactionTime( mobj, {reactiontime} )
SF_ReactionTime(void)1867 void SF_ReactionTime(void)
1868 {
1869     mobj_t *mo;
1870 
1871     if (t_argc < 1)  goto err_numarg;
1872 
1873     t_return.type = FSVT_int;
1874     t_return.value.i = 0;  // default
1875 
1876     mo = MobjForSvalue(t_argv[0]);
1877     if (!mo)  goto done;
1878 
1879     if (t_argc > 1)
1880     {   // set
1881         mo->reactiontime = (intvalue(t_argv[1]) * 35) / 100;
1882     }
1883 
1884     t_return.value.i = mo->reactiontime;  // test
1885 done:
1886     return;
1887 
1888 err_numarg:
1889     missing_arg_str("ReactionTime", "1 or 2");
1890     goto done;
1891 }
1892 
1893 // Sets a mobj's Target! >:)
1894 // Test or Set
1895 // MobjTarget( mobj, {target_mobj} )
SF_MobjTarget(void)1896 void SF_MobjTarget(void)
1897 {
1898     mobj_t *mo;
1899     mobj_t *target;
1900 
1901     if (t_argc < 1)  goto err_numarg;
1902 
1903     t_return.type = FSVT_mobj;
1904     t_return.value.mobj = NULL;  // default
1905 
1906     mo = MobjForSvalue(t_argv[0]);
1907     if (!mo)  goto done;
1908 
1909     if (t_argc >= 2)
1910     {   // set
1911         if (t_argv[1].type != FSVT_mobj && intvalue(t_argv[1]) == -1)
1912         {
1913             // Set target to NULL
1914             mo->target = NULL;
1915             P_SetMobjState(mo, mo->info->spawnstate);
1916         }
1917         else
1918         {
1919             target = MobjForSvalue(t_argv[1]);
1920 
1921             // Also remember node here
1922             if (target->type == MT_NODE)
1923                 mo->targetnode = target;
1924 
1925             mo->target = target;
1926             P_SetMobjState(mo, mo->info->seestate);
1927         }
1928     }
1929 
1930     t_return.value.mobj = mo->target;  // test
1931 done:
1932     return;
1933 
1934 err_numarg:
1935     missing_arg_str("MobjTarget", "1 or 2");
1936     goto done;
1937 }
1938 
1939 // MobjMomx( mobj )
SF_MobjMomx(void)1940 void SF_MobjMomx(void)
1941 {
1942     mobj_t *mo;
1943 
1944     if (t_argc < 1)  goto err_numarg;
1945 
1946     t_return.type = FSVT_fixed;
1947     t_return.value.f = 0;  // default
1948 
1949     mo = MobjForSvalue(t_argv[0]);
1950     if( mo )
1951     {
1952         if (t_argc > 1)
1953             mo->momx = fixedvalue(t_argv[1]);  // set
1954         t_return.value.f = mo->momx;  // test
1955     }
1956 done:
1957     return;
1958 
1959 err_numarg:
1960     missing_arg_str("MobjMomx", "1 or 2");
1961     goto done;
1962 }
1963 
1964 // MobjMomy( mobj )
SF_MobjMomy(void)1965 void SF_MobjMomy(void)
1966 {
1967     mobj_t *mo;
1968 
1969     if (t_argc < 1)  goto err_numarg;
1970 
1971     t_return.type = FSVT_fixed;
1972     t_return.value.f = 0;  // default
1973 
1974     mo = MobjForSvalue(t_argv[0]);
1975     if( mo )
1976     {
1977         if (t_argc > 1)
1978             mo->momy = fixedvalue(t_argv[1]);  // set
1979         t_return.value.f = mo->momy;  // test
1980     }
1981 done:
1982     return;
1983 
1984 err_numarg:
1985     missing_arg_str("MobjMomy", "1 or 2");
1986     goto done;
1987 }
1988 
1989 // MobjMomz( mobj )
SF_MobjMomz(void)1990 void SF_MobjMomz(void)
1991 {
1992     mobj_t *mo;
1993 
1994     if (t_argc < 1)  goto err_numarg;
1995 
1996     t_return.type = FSVT_fixed;
1997     t_return.value.f = 0;  // default
1998 
1999     mo = MobjForSvalue(t_argv[0]);
2000     if( mo )
2001     {
2002         if (t_argc > 1)
2003             mo->momz = fixedvalue(t_argv[1]);  // set
2004         t_return.value.f = mo->momz;  // test
2005     }
2006 done:
2007     return;
2008 
2009 err_numarg:
2010     missing_arg_str("MobjMomz", "1 or 2");
2011     goto done;
2012 }
2013 
2014 // SpawnMissile( mobj, target_mobj, missiletype )
SF_SpawnMissile(void)2015 void SF_SpawnMissile(void)
2016 {
2017     mobj_t *mobj;
2018     mobj_t *target;
2019     int objtype;
2020 
2021     if (t_argc != 3)  goto err_numarg;
2022 
2023     t_return.type = FSVT_mobj;
2024     t_return.value.mobj = NULL;  // default
2025 
2026     objtype = intvalue(t_argv[2]);
2027     if (objtype < 0 || objtype >= NUMMOBJTYPES)  goto err_objtype;
2028 
2029     mobj = MobjForSvalue(t_argv[0]);
2030     target = MobjForSvalue(t_argv[1]);
2031 
2032     t_return.value.mobj = P_SpawnMissile(mobj, target, objtype);
2033 done:
2034     return;
2035 
2036 err_numarg:
2037     wrong_num_arg("SpawnMissile", 3);
2038     goto done;
2039 
2040 err_objtype:
2041     script_error("SpawnMissile: unknown object type: %i\n", objtype);
2042     goto done;
2043 }
2044 
2045 
2046 // Exl: Modified by Tox to take a pitch parameter
2047 // LineAttack( mobj, angle, damage, {pitch})
SF_LineAttack(void)2048 void SF_LineAttack(void)
2049 {
2050     mobj_t * mo;
2051     angle_t aiming;
2052     int  damage, angle, slope;
2053     int	 short fixedtodeg = 182.033;
2054 
2055     if (t_argc < 3)  goto err_numarg; // [WDJ]
2056 
2057     mo = MobjForSvalue(t_argv[0]);
2058     if( !mo )  goto done;  // [WDJ]
2059 
2060     damage = intvalue(t_argv[2]);
2061     angle = (intvalue(t_argv[1]) * (ANG45 / 45));
2062 
2063     if(t_argc == 4)
2064     {
2065         aiming = fixedvalue(t_argv[3]) * fixedtodeg;
2066         slope = AIMINGTOSLOPE(aiming);
2067     }
2068     else
2069     {
2070         slope = P_AimLineAttack(mo, angle, MISSILERANGE, 0);  // auto aim
2071     }
2072 
2073     P_LineAttack(mo, angle, MISSILERANGE, slope, damage);
2074 done:
2075     return;
2076 
2077 err_numarg:
2078     missing_arg_str("LineAttack", "3 or 4");
2079     goto done;
2080 }
2081 
2082 
2083 //checks to see if a Map Thing Number exists; used to avoid script errors
2084 // MapthingNumExist( thingnum )
SF_MapthingNumExist(void)2085 void SF_MapthingNumExist(void)
2086 {
2087     int intval;
2088 
2089     if (t_argc != 1)  goto err_numarg;
2090 
2091     t_return.type = FSVT_int;
2092 
2093     intval = intvalue(t_argv[0]);
2094     if (intval < 0 || intval >= nummapthings || !mapthings[intval].mobj)
2095     {
2096         t_return.value.i = 0;
2097     }
2098     else
2099     {
2100         t_return.value.i = 1;
2101     }
2102 done:
2103     return;
2104 
2105 err_numarg:
2106     wrong_num_arg("MapthingNumExist", 1);
2107     goto done;
2108 }
2109 
2110 // Mapthings()
SF_Mapthings(void)2111 void SF_Mapthings(void)
2112 {
2113     t_return.type = FSVT_int;
2114     t_return.value.i = nummapthings;
2115 }
2116 
2117 // ObjType( {mobj} )
SF_ObjType(void)2118 void SF_ObjType(void)
2119 {
2120     // use trigger object if not specified
2121     mobj_t *mo = t_argc ? MobjForSvalue(t_argv[0]) : fs_current_script->trigger;
2122 
2123     t_return.type = FSVT_int;
2124     t_return.value.i = (mo)? mo->type : 0;  // [WDJ] check mo exist
2125 }
2126 
2127 
2128 // Exl: sets an object's properties (tox)
2129 // SetObjProperty( attribute, property_value, {mobj} )
SF_SetObjProperty(void)2130 void SF_SetObjProperty(void)
2131 {
2132     mobj_t * mo;
2133     int attrib;
2134     int32_t  value;  // int and fixed_t
2135 
2136     if (t_argc < 2)  goto err_numarg;
2137 
2138     attrib = intvalue(t_argv[0]);
2139     value  = intvalue(t_argv[1]);
2140   // FIXME fixed_t vals, syntax does not make sense (operate on MTs instead of mobjs...
2141 
2142     mo = (t_argc >= 3) ? MobjForSvalue(t_argv[2]) : fs_current_script->trigger;
2143     if( ! mo)  goto done;  // [WDJ]
2144 
2145     switch (attrib)
2146     {
2147     case 0:
2148       mo->info->radius = (value*FRACUNIT);
2149       break;
2150     case 1:
2151       mo->info->height = (value*FRACUNIT);
2152       break;
2153     case 2:
2154       mo->info->mass = value;
2155       break;
2156     case 3:
2157       mo->info->spawnhealth = value;
2158       break;
2159     case 4:
2160       mo->info->damage = value;
2161       break;
2162     case 5:
2163       mo->info->speed = value;
2164       break;
2165     case 6:
2166       mo->info->reactiontime = value;
2167       break;
2168     case 7:
2169       mo->info->painchance = value;
2170       break;
2171 
2172     case 8:
2173       mo->info->spawnstate = value;
2174       break;
2175     case 9:
2176       mo->info->seestate = value;
2177       break;
2178     case 10:
2179       mo->info->meleestate = value;
2180       break;
2181     case 11:
2182       mo->info->missilestate = value;
2183       break;
2184     case 12:
2185       mo->info->painstate = value;
2186       break;
2187     case 13:
2188       mo->info->deathstate = value;
2189       break;
2190     case 14:
2191       mo->info->xdeathstate = value;
2192       break;
2193     case 15:
2194       mo->info->crashstate = value;
2195       break;
2196     case 16:
2197       mo->info->raisestate = value;
2198       break;
2199 
2200     case 17:
2201       mo->info->seesound = value;
2202       break;
2203     case 18:
2204       mo->info->activesound = value;
2205       break;
2206     case 19:
2207       mo->info->attacksound = value;
2208       break;
2209     case 20:
2210       mo->info->painsound = value;
2211       break;
2212     case 21:
2213       mo->info->deathsound = value;
2214       break;
2215     default:
2216       script_error("SetObjProperty: invalid attribute %i\n", attrib);
2217       break;
2218     }
2219 done:
2220     return;
2221 
2222 err_numarg:
2223     missing_arg_str("SetObjProperty", "2 or 3");
2224     goto done;
2225 }
2226 
2227 
2228 // Exl: Returns an object's properties (tox)
2229 // GetObjProperty( {mobj}, attrib )
SF_GetObjProperty(void)2230 void SF_GetObjProperty(void)
2231 {
2232     int attrib = 0, retval = 0;	// might be used uninit
2233     mobj_t *mo = NULL;
2234 
2235     if(t_argc == 1)
2236     {
2237         mo = fs_current_script->trigger;
2238         attrib = intvalue(t_argv[0]);
2239     }
2240     else if(t_argc == 2)
2241     {
2242         mo = MobjForSvalue(t_argv[0]);
2243         attrib = intvalue(t_argv[1]);
2244     }
2245     else goto err_numarg;
2246 
2247     t_return.type = FSVT_int;
2248     t_return.value.i = 0;  // default
2249 
2250     if( !mo )  goto done;  // [WDJ]
2251 
2252     switch (attrib)
2253     {
2254      case 0:
2255         retval = mo->info->radius / FRACUNIT;
2256         break;
2257      case 1:
2258         retval = mo->info->height / FRACUNIT;
2259         break;
2260      case 2:
2261         retval = mo->info->mass;
2262         break;
2263      case 3:
2264         retval = mo->info->spawnhealth;
2265         break;
2266      case 4:
2267         retval = mo->info->damage;
2268         break;
2269      case 5:
2270         retval = mo->info->speed;
2271         break;
2272      case 6:
2273         retval = mo->info->reactiontime;
2274         break;
2275      case 7:
2276         retval = mo->info->painchance;
2277         break;
2278      case 8:
2279         retval = mo->info->spawnstate;
2280         break;
2281      case 9:
2282         retval = mo->info->seestate;
2283         break;
2284      case 10:
2285         retval = mo->info->meleestate;
2286         break;
2287      case 11:
2288         retval = mo->info->missilestate;
2289         break;
2290      case 12:
2291         retval = mo->info->painstate;
2292         break;
2293      case 13:
2294         retval = mo->info->deathstate;
2295         break;
2296      case 14:
2297         retval = mo->info->xdeathstate;
2298         break;
2299      case 15:
2300         retval = mo->info->crashstate;
2301         break;
2302      case 16:
2303         retval = mo->info->raisestate;
2304         break;
2305      case 17:
2306         retval = mo->info->seesound;
2307         break;
2308      case 18:
2309         retval = mo->info->activesound;
2310         break;
2311      case 19:
2312         retval = mo->info->attacksound;
2313         break;
2314      case 20:
2315         retval = mo->info->painsound;
2316         break;
2317      case 21:
2318         retval = mo->info->deathsound;
2319         break;
2320      default:
2321         script_error("GetObjProperty: invalid attribute %i\n", attrib);
2322         return;
2323     }
2324     t_return.value.i = retval;
2325 done:
2326     return;
2327 
2328 err_numarg:
2329     missing_arg_str("GetObjProperty", "1 or 2");
2330     goto done;
2331 }
2332 
2333 
2334 //DarkWolf95:November 15, 2003: Adaptation of Exl's code
2335 //DarkWolf95:December 7, 2003: Change to set only
2336 // Set
2337 // ObjState( {mobj}, state )
SF_ObjState(void)2338 void SF_ObjState(void)
2339 {
2340     int      state, newstate;
2341     mobj_t * mo;
2342 
2343     t_return.type = FSVT_int;
2344     t_return.value.i = 0;  // default
2345 
2346     if(t_argc == 1)
2347     {
2348         mo = fs_current_script->trigger;
2349         state = intvalue(t_argv[0]);
2350     }
2351     else if(t_argc == 2)
2352     {
2353         mo = MobjForSvalue(t_argv[0]);
2354         state = intvalue(t_argv[1]);
2355     }
2356     else goto err_numarg;
2357     if( ! mo )  goto done; // [WDJ]
2358 
2359     switch (state)
2360     {
2361      case 8:
2362         newstate = mo->info->spawnstate;
2363         break;
2364      case 9:
2365         newstate = mo->info->seestate;
2366         break;
2367      case 10:
2368         newstate = mo->info->meleestate;
2369         break;
2370      case 11:
2371         newstate = mo->info->missilestate;
2372         break;
2373      case 12:
2374         newstate = mo->info->painstate;
2375         break;
2376      case 13:
2377         newstate = mo->info->deathstate;
2378         break;
2379      case 14:
2380         newstate = mo->info->xdeathstate;
2381         break;
2382      case 15:
2383         newstate = mo->info->crashstate;
2384         break;
2385      case 16:
2386         newstate = mo->info->raisestate;
2387         break;
2388      default:
2389         script_error("ObjState: invalid state\n");
2390         return;
2391     }
2392 
2393     t_return.value.i = P_SetMobjState(mo, newstate);
2394 done:
2395     return;
2396 
2397 err_numarg:
2398     missing_arg_str("ObjState", "1 or 2");
2399     goto done;
2400 }
2401 
2402 
2403 // HealObj( {mobj}, {health} )
SF_HealObj(void)2404 void SF_HealObj(void)
2405 {
2406     mobj_t *mo;
2407     int heal = 0;
2408 
2409     switch(t_argc)
2410     {
2411      case 0:
2412         // Heal trigger to default health
2413         mo = fs_current_script->trigger;
2414         heal = mo->info->spawnhealth;
2415         break;
2416      case 1:
2417         // Heal arg mobj to default health
2418         mo = MobjForSvalue(t_argv[0]);
2419         heal = mo->info->spawnhealth;
2420         break;
2421      case 2:
2422         // Heal arg mobj to given health
2423         mo = MobjForSvalue(t_argv[0]);
2424         heal = intvalue(t_argv[1]);
2425         break;
2426      default:
2427         goto err_numarg;
2428     }
2429 
2430     if( mo ) // [WDJ]
2431         mo->health = heal;
2432 done:
2433     return;
2434 
2435 err_numarg:
2436     missing_arg_str("HealObj", "0, 1 or 2");
2437     goto done;
2438 }
2439 
2440 
2441 // Set next node mobj
2442 // SetNodeNext( mobj, next_mobj )
SF_SetNodeNext(void)2443 void SF_SetNodeNext(void)
2444 {
2445     mobj_t* mo;  // Affected
2446     mobj_t* nextmo;
2447 
2448     if (t_argc != 2)  goto err_numarg;
2449 
2450     mo = MobjForSvalue(t_argv[0]);
2451     if( !mo )  goto done;  // [WDJ]
2452 
2453     nextmo = MobjForSvalue(t_argv[1]);
2454     if( !nextmo )  goto done;  // [WDJ]
2455 
2456     // Check if both mobjs are NODE
2457     if (mo->type != MT_NODE || nextmo->type != MT_NODE)  goto err_notnode;
2458     mo->nextnode = nextmo;
2459 done:
2460     return;
2461 
2462 err_numarg:
2463     wrong_num_arg("SetNodeNext", 2);
2464     goto done;
2465 
2466 err_notnode:
2467     script_error("SetNodeNext: mobj is not a node\n");
2468     goto done;
2469 }
2470 
2471 
2472 // Set the time to wait at a node
2473 // SetNodePause( mobj, waittime )
SF_SetNodePause(void)2474 void SF_SetNodePause(void)
2475 {
2476     mobj_t* mo;
2477 
2478     if (t_argc != 2)  goto err_numarg;
2479 
2480     mo = MobjForSvalue(t_argv[0]);
2481     if( !mo )  goto done;  // [WDJ]
2482     if (mo->type != MT_NODE)  goto err_notnode;
2483     mo->nodewait = t_argv[1].value.i;
2484 done:
2485     return;
2486 
2487 err_numarg:
2488     wrong_num_arg("SetNodePause", 2);
2489     goto done;
2490 
2491 err_notnode:
2492     script_error("SetNodePause: mobj is not a node\n");
2493     goto done;
2494 }
2495 
2496 
2497 // Run a script when touching a node
2498 // SetNodeScript( mobj, scriptnum )
SF_SetNodeScript(void)2499 void SF_SetNodeScript(void)
2500 {
2501     mobj_t*  mo;
2502     int      sn;
2503 
2504     if (t_argc != 2)  goto err_numarg;
2505 
2506     mo = MobjForSvalue(t_argv[0]);
2507     if( !mo )  goto done;  // [WDJ]
2508     if (mo->type != MT_NODE)  goto err_notnode;
2509 
2510     sn = intvalue(t_argv[1]);
2511 
2512     // Check if the script is defined
2513     if(!fs_levelscript.children[sn])  goto err_notscript;
2514     mo->nodescript = sn + 1;  // +1 because 0 = none
2515 done:
2516     return;
2517 
2518 err_numarg:
2519     wrong_num_arg("SetNodeScript", 2);
2520     goto done;
2521 
2522 err_notnode:
2523     script_error("SetNodeScript: mobj is not a node\n");
2524     goto done;
2525 
2526 err_notscript:
2527     script_error("SetNodeScript: script not defined\n");
2528     goto done;
2529 }
2530 
2531 
2532 
2533 /****************** Trig *********************/
2534 
2535 // PointToAngle( x1, y1, x2, y2 )
SF_PointToAngle(void)2536 void SF_PointToAngle(void)
2537 {
2538     angle_t angle;
2539     int x1, y1, x2, y2;
2540 
2541     if (t_argc != 4)   goto err_numarg;
2542 
2543     x1 = intvalue(t_argv[0]) << FRACBITS;
2544     y1 = intvalue(t_argv[1]) << FRACBITS;
2545     x2 = intvalue(t_argv[2]) << FRACBITS;
2546     y2 = intvalue(t_argv[3]) << FRACBITS;
2547 
2548     angle = R_PointToAngle2(x1, y1, x2, y2);
2549 
2550     t_return.type = FSVT_fixed;
2551     t_return.value.f = AngleToFixed(angle);
2552 done:
2553     return;
2554 
2555 err_numarg:
2556     wrong_num_arg("PointToAngle", 4);
2557     goto done;
2558 }
2559 
2560 // PointToDist( x1, y1, x2, y2 )
SF_PointToDist(void)2561 void SF_PointToDist(void)
2562 {
2563     int dist;
2564     int x1, x2, y1, y2;
2565 
2566     if (t_argc != 4)   goto err_numarg;
2567 
2568     x1 = intvalue(t_argv[0]) << FRACBITS;
2569     y1 = intvalue(t_argv[1]) << FRACBITS;
2570     x2 = intvalue(t_argv[2]) << FRACBITS;
2571     y2 = intvalue(t_argv[3]) << FRACBITS;
2572 
2573     dist = R_PointToDist2(x1, y1, x2, y2);
2574     t_return.type = FSVT_fixed;
2575     t_return.value.f = dist;
2576 done:
2577     return;
2578 
2579 err_numarg:
2580     wrong_num_arg("PointToDist", 4);
2581     goto done;
2582 }
2583 
2584 /************* Camera functions ***************/
2585 
2586 camera_t script_camera = { NULL, NULL, 0, 0, 0, 0 };
2587 boolean script_camera_on;
2588 
2589 // SetCamera(obj, [angle], [viewheight], [pitch])
2590 // pitch= -50..50
SF_SetCamera(void)2591 void SF_SetCamera(void)
2592 {
2593     mobj_t * mo;
2594 
2595     if (t_argc < 1)  goto err_numarg;
2596 
2597 #if 0
2598     // [WDJ] Docs: returns void
2599     t_return.type = FSVT_fixed;
2600     t_return.value.f = 0; // default
2601 #endif
2602 
2603     mo = MobjForSvalue(t_argv[0]);
2604     if (!mo)  goto done;
2605 
2606     if (script_camera.mo != mo)
2607     {
2608         if (script_camera.mo)
2609             script_camera.mo->angle = script_camera.startangle;
2610 
2611         script_camera.startangle = mo->angle;
2612     }
2613 
2614     script_camera.mo = mo;
2615     script_camera.mo->angle = (t_argc < 2) ? mo->angle : FixedToAngle(fixedvalue(t_argv[1]));
2616     script_camera.mo->z = (t_argc < 3) ?
2617      (((mo->subsector)? mo->subsector->sector->floorheight : 0) + (41 << FRACBITS))
2618      : fixedvalue(t_argv[2]);
2619 
2620     angle_t aiming = (t_argc < 4) ? 0 : FixedToAngle(fixedvalue(t_argv[3]));
2621     script_camera.aiming = G_ClipAimingPitch(aiming);
2622 
2623     script_camera_on = true;
2624 
2625 #if 0
2626     // [WDJ] Docs: returns void
2627     t_return.value.f = script_camera.aiming; // FIXME really?
2628 #endif
2629 done:
2630     return;
2631 
2632 err_numarg:
2633     missing_arg_str("SetCamera", "1, 2, 3 or 4");
2634 }
2635 
2636 // ClearCamera( )
SF_ClearCamera(void)2637 void SF_ClearCamera(void)
2638 {
2639     script_camera_on = false;
2640     if ( ! script_camera.mo )  goto done;
2641 
2642     script_camera.mo->angle = script_camera.startangle;
2643     script_camera.mo = NULL;
2644 done:
2645     return;
2646 
2647 #if 0
2648    // [WDJ] Docs: no error  (and it is unnecessary)
2649 err_camera:
2650     script_error("Clearcamera: called without setcamera.\n");
2651     goto done;
2652 #endif
2653 }
2654 
2655 // MoveCamera(cameraobj, targetobj, targetheight, movespeed, targetangle, anglespeed)
SF_MoveCamera(void)2656 void SF_MoveCamera(void)
2657 {
2658     fixed_t x, y, z;
2659     fixed_t xdist, ydist, zdist, xydist, movespeed;
2660     fixed_t xstep, ystep, zstep, targetheight;
2661     angle_t anglespeed, anglestep = 0, angledist, targetangle, mobjangle, bigangle, smallangle;
2662     // I have to use floats for the math where angles are divided by fixed
2663     // values.
2664     double fangledist, fanglestep, fmovestep;
2665     int angledir = 0;
2666     mobj_t *camera;
2667     mobj_t *target;
2668     int moved = 0; // camera moved
2669     int quad1, quad2;
2670 
2671 #if 1
2672     // Phobiata.wad has at least one movecamera with 7 parameters
2673     // movecamera( 154, 157, 780, 6, 270, 0.5, -30 )
2674     if (t_argc < 6)  goto err_numarg;
2675     if (t_argc > 6)
2676         I_SoftError( "movecamera: wrong num arg (%i), extra ignored\n", t_argc );
2677 #else
2678     // This is correct, but causes Phobiata.wad to fail.
2679     if (t_argc != 6)  goto err_numarg;
2680 #endif
2681 
2682     camera = MobjForSvalue(t_argv[0]);
2683     if( ! camera )  goto err_nomobj;
2684     target = MobjForSvalue(t_argv[1]);
2685     if( ! target )  goto err_nomobj;
2686     targetheight = fixedvalue(t_argv[2]);
2687     movespeed = fixedvalue(t_argv[3]);
2688     targetangle = FixedToAngle(fixedvalue(t_argv[4]));
2689     anglespeed = FixedToAngle(fixedvalue(t_argv[5]));
2690 
2691     // Figure out how big the step will be
2692     xdist = target->x - camera->x;
2693     ydist = target->y - camera->y;
2694     zdist = targetheight - camera->z;
2695 
2696     // Angle checking...
2697     //    90
2698     //   Q1|Q0
2699     //180--+--0
2700     //   Q2|Q3
2701     //    270
2702     quad1 = targetangle / ANG90;
2703     quad2 = camera->angle / ANG90;
2704     bigangle = targetangle > camera->angle ? targetangle : camera->angle;
2705     smallangle = targetangle < camera->angle ? targetangle : camera->angle;
2706     if ((quad1 > quad2 && quad1 - 1 == quad2) || (quad2 > quad1 && quad2 - 1 == quad1) || quad1 == quad2)
2707     {
2708         angledist = bigangle - smallangle;
2709         angledir = targetangle > camera->angle ? 1 : -1;
2710     }
2711     else
2712     {
2713         if (quad2 == 3 && quad1 == 0)
2714         {
2715             angledist = (bigangle + ANG180) - (smallangle + ANG180);
2716             angledir = 1;
2717         }
2718         else if (quad1 == 3 && quad2 == 0)
2719         {
2720             angledist = (bigangle + ANG180) - (smallangle + ANG180);
2721             angledir = -1;
2722         }
2723         else
2724         {
2725             angledist = bigangle - smallangle;
2726             if (angledist > ANG180)
2727             {
2728                 angledist = (bigangle + ANG180) - (smallangle + ANG180);
2729                 angledir = targetangle > camera->angle ? -1 : 1;
2730             }
2731             else
2732                 angledir = targetangle > camera->angle ? 1 : -1;
2733         }
2734     }
2735 
2736     //debug_Printf("angle: cam=%i, target=%i; dir: %i; quads: 1=%i, 2=%i\n", camera->angle / ANGLE_1, targetangle / ANGLE_1, angledir, quad1, quad2);
2737     // set the step variables based on distance and speed...
2738     mobjangle = R_PointToAngle2(camera->x, camera->y, target->x, target->y);
2739 
2740     if (movespeed)
2741     {
2742         xydist = R_PointToDist2(camera->x, camera->y, target->x, target->y);
2743         xstep = FixedMul( cosine_ANG(mobjangle), movespeed);
2744         ystep = FixedMul( sine_ANG(mobjangle), movespeed);
2745         if (xydist)
2746             zstep = FixedDiv(zdist, FixedDiv(xydist, movespeed));
2747         else
2748             zstep = zdist > 0 ? movespeed : -movespeed;
2749 
2750         if (xydist && !anglespeed)
2751         {
2752 #if 1
2753             // [WDJ] Without using ANGLE_1, which has a significant round-off error.
2754             fangledist = ((double) angledist * 45.0f / ANG45);
2755             fmovestep = ((double) FixedDiv(xydist, movespeed) / FRACUNIT);
2756             if (fmovestep)
2757                 fanglestep = (fangledist / fmovestep);
2758             else
2759                 fanglestep = 360.0f;
2760 
2761             //debug_Printf("fstep: %f, fdist: %f, fmspeed: %f, ms: %i\n", fanglestep, fangledist, fmovestep, FixedDiv(xydist, movespeed) >> FRACBITS);
2762 
2763             anglestep = (fanglestep * ANG45 / 45.0f);
2764 #else
2765             // [WDJ] ANGLE_1 (from Heretic) has a significant round-off error.
2766             fangledist = ((double) angledist / ANGLE_1);
2767             fmovestep = ((double) FixedDiv(xydist, movespeed) / FRACUNIT);
2768             if (fmovestep)
2769                 fanglestep = (fangledist / fmovestep);
2770             else
2771                 fanglestep = 360;
2772 
2773             //debug_Printf("fstep: %f, fdist: %f, fmspeed: %f, ms: %i\n", fanglestep, fangledist, fmovestep, FixedDiv(xydist, movespeed) >> FRACBITS);
2774 
2775             anglestep = (fanglestep * ANGLE_1);
2776 #endif
2777         }
2778         else
2779             anglestep = anglespeed;
2780 
2781         if (abs(xstep) >= (abs(xdist) - 1))
2782             x = target->x;
2783         else
2784         {
2785             x = camera->x + xstep;
2786             moved = 1;
2787         }
2788 
2789         if (abs(ystep) >= (abs(ydist) - 1))
2790             y = target->y;
2791         else
2792         {
2793             y = camera->y + ystep;
2794             moved = 1;
2795         }
2796 
2797         if (abs(zstep) >= abs(zdist) - 1)
2798             z = targetheight;
2799         else
2800         {
2801             z = camera->z + zstep;
2802             moved = 1;
2803         }
2804     }
2805     else
2806     {
2807         // instantaneous
2808         x = camera->x;
2809         y = camera->y;
2810         z = camera->z;
2811     }
2812 
2813     if (anglestep >= angledist)
2814         camera->angle = targetangle;
2815     else
2816     {
2817         if (angledir == 1)
2818         {
2819             moved = 1;
2820             camera->angle += anglestep;
2821         }
2822         else if (angledir == -1)
2823         {
2824             moved = 1;
2825             camera->angle -= anglestep;
2826         }
2827     }
2828 
2829     if (x != camera->x || y != camera->y)
2830     {
2831        if ( !P_TryMove(camera, x, y, true) )
2832        {
2833 #if 1
2834           // [WDJ] force it past the obstruction, no errors,
2835           // keep game playable, do not police script during gameplay
2836           camera->x = x;
2837           camera->y = y;
2838 #else
2839           goto err_move;
2840 #endif
2841        }
2842     }
2843     camera->z = z;
2844 
2845 done:
2846     t_return.type = FSVT_int;
2847     t_return.value.i = moved;
2848     return;
2849 
2850 err_numarg:
2851     wrong_num_arg("MoveCamera", 6);
2852     goto done;
2853 
2854 err_nomobj:
2855     script_error("MoveCamera: Invalid mobj\n");
2856     goto done;
2857 
2858 #if 0
2859 err_move:
2860     // [WDJ] Docs: no error
2861     // [WDJ] Questionable, not even good for debugging path
2862 //    script_error("MoveCamera: Illegal camera move\n");
2863     moved = 0;  // to exit loop
2864     goto done;
2865 #endif
2866 }
2867 
2868 /*********** sounds ******************/
2869 
2870 // start sound from thing
2871 // StartSound( mobj, sound_lump_name )
SF_StartSound(void)2872 void SF_StartSound(void)
2873 {
2874     mobj_t *mo;
2875 
2876     if (t_argc != 2)  goto err_numarg;
2877     if (t_argv[1].type != FSVT_string)  goto err_notsound;
2878 
2879     mo = MobjForSvalue(t_argv[0]);
2880     if (!mo)  goto done;
2881 
2882     S_StartXYZSoundName((xyz_t*)&(mo->x), mo, t_argv[1].value.s);
2883 done:
2884     return;
2885 
2886 err_numarg:
2887     wrong_num_arg("StartSound", 2);
2888     goto done;
2889 
2890 err_notsound:
2891     script_error("StartSound: sound lump argument not a string!\n");
2892     goto done;
2893 }
2894 
2895 // start sound from sector
2896 // StartSectorSound( tagnum, sound_lump_name )
SF_StartSectorSound(void)2897 void SF_StartSectorSound(void)
2898 {
2899     int secnum;
2900     uint16_t tagnum;
2901 
2902     if (t_argc != 2)  goto err_numarg;
2903     if (t_argv[1].type != FSVT_string)  goto err_notsound;
2904 
2905     tagnum = intvalue(t_argv[0]);  // sector tag
2906 
2907     // check on existing tagged sector
2908     secnum = P_FindSectorFromTag(tagnum, -1);
2909     if (secnum < 0)  goto err_nosector;
2910 
2911     secnum = -1; // init for search FindSector
2912     while ((secnum = P_FindSectorFromTag(tagnum, secnum)) >= 0)
2913     {
2914         // all sectors with the tagnum
2915         sector_t * sector = &sectors[secnum];
2916         S_StartXYZSoundName(&sector->soundorg, NULL, t_argv[1].value.s);
2917     }
2918 done:
2919     return;
2920 
2921 err_numarg:
2922     wrong_num_arg("StartSectorSound", 2);
2923     goto done;
2924 
2925 err_notsound:
2926     script_error("StartSectorSound: sound lump argument not a string!\n");
2927     goto done;
2928 
2929 err_nosector:
2930     script_error("StartSectorSound: sector not found with tagnum %i\n", tagnum);
2931     goto done;
2932 }
2933 
2934 // AmbiantSound( sound_lump_name )
SF_AmbiantSound(void)2935 void SF_AmbiantSound(void)
2936 {
2937     if (t_argc != 1)  goto err_numarg;
2938     if (t_argv[0].type != FSVT_string)  goto err_notsound;
2939 
2940     S_StartXYZSoundName(NULL, NULL, t_argv[0].value.s);
2941 done:
2942     return;
2943 
2944 err_numarg:
2945     wrong_num_arg("AmbiantSound", 1);
2946     goto done;
2947 
2948 err_notsound:
2949     script_error("AmbiantSound: sound lump argument not a string!\n");
2950     goto done;
2951 }
2952 
2953 /************* Sector functions ***************/
2954 
2955 // Not in Docs
2956 // [WDJ] Does not match code: SectorEffect(tagnum, [effect])
2957 // [WDJ] Does not test, no need for return value
2958 // Set, no return value
2959 // SectorEffect(tagnum, effect)
SF_SectorEffect(void)2960 void SF_SectorEffect(void)
2961 {
2962     sector_t * sector;
2963     int select, secnum;
2964     uint16_t tagnum;
2965 
2966     if (t_argc != 2)  goto err_numarg;  // [WDJ]
2967 
2968     tagnum = intvalue(t_argv[0]);
2969     select = intvalue(t_argv[1]);
2970 
2971     secnum = -1; // init for search FindSector
2972     // silent when no sectors found
2973     while ((secnum = P_FindSectorFromTag(tagnum, secnum)) >= 0)
2974     {
2975         // all sectors with the tagnum
2976         sector = &sectors[secnum];
2977 
2978         switch (select)
2979         {
2980           case 1:
2981             // FLICKERING LIGHTS
2982             P_SpawnLightFlash (sector);
2983             break;
2984 
2985           case 2:
2986             // STROBE FAST
2987             P_SpawnStrobeFlash(sector,FASTDARK,0);
2988             break;
2989 
2990           case 3:
2991             // STROBE SLOW
2992             P_SpawnStrobeFlash(sector,SLOWDARK,0);
2993             break;
2994 
2995           case 8:
2996             // GLOWING LIGHT
2997             P_SpawnGlowingLight(sector);
2998             break;
2999 
3000           //case 9: SECRET SECTOR
3001 
3002           case 10:
3003             // DOOR CLOSE IN 30 SECONDS
3004             P_SpawnDoorCloseIn30 (sector);
3005             break;
3006 
3007           case 12:
3008             // SYNC STROBE SLOW
3009             P_SpawnStrobeFlash (sector, SLOWDARK, 1);
3010             break;
3011 
3012           case 13:
3013             // SYNC STROBE FAST
3014             P_SpawnStrobeFlash (sector, FASTDARK, 1);
3015             break;
3016 
3017           case 14:
3018             // DOOR RAISE IN 5 MINUTES
3019             P_SpawnDoorRaiseIn5Mins (sector, secnum);
3020             break;
3021 
3022           case 17:
3023             //LIGHT FLICKERS RANDOMLY
3024             P_SpawnFireFlicker(sector);
3025             break;
3026         }
3027     }
3028 
3029 #if 0
3030     // [WDJ] Why would this need to return an input arg
3031     t_return.type = FSVT_int;
3032     t_return.value.i = select;
3033 #endif
3034 done:
3035     return;
3036 
3037 err_numarg:
3038     wrong_num_arg("SectorEffect", 1);
3039     goto done;
3040 }
3041 
3042 // floor height of sector
3043 // Test or Set
3044 // FloorHeight( tagnum, {floorheight}, {crush} )
3045 // Return Test floorheight
3046 // Return when Set floorheight: 1=default, 0=crushing
SF_FloorHeight(void)3047 void SF_FloorHeight(void)
3048 {
3049     int returnval = 1;  // When Set floorheight: 1=default, 0=crushing
3050     int secnum;
3051     uint16_t tagnum;
3052 
3053     if (!t_argc)  goto err_numarg;
3054 
3055     t_return.type = FSVT_int;
3056 
3057     tagnum = intvalue(t_argv[0]);
3058 
3059     // check on existing tagged sector
3060     secnum = P_FindSectorFromTag(tagnum, -1);
3061     if (secnum < 0)  goto err_nosector;
3062 
3063     if (t_argc > 1)     // > 1: set floorheight
3064     {
3065         // set floorheight
3066         fixed_t arg_flrheight = fixedvalue(t_argv[1]);  // set floorheight
3067         boolean arg_crush = (t_argc == 3) ? intvalue(t_argv[2]) : false;
3068 
3069         // set all sectors with tag
3070         secnum = -1;
3071         while ((secnum = P_FindSectorFromTag(tagnum, secnum)) >= 0)
3072         {
3073             sector_t * sec = &sectors[secnum];
3074             result_e res =
3075              T_MovePlane(sec,
3076                          abs(arg_flrheight - sec->floorheight), // speed
3077                          arg_flrheight, arg_crush, 0,  // dest, crush, move floor
3078                          (arg_flrheight > sec->floorheight) ? 1 : -1); // direction
3079             if ( res == MP_crushed)
3080                 returnval = 0;  // signal crushing
3081         }
3082     }
3083     else
3084     {
3085         // get floorheight
3086         returnval = sectors[secnum].floorheight >> FRACBITS;
3087     }
3088 
3089     // return floorheight, or crushing
3090     t_return.value.i = returnval;
3091 done:
3092     return;
3093 
3094 err_numarg:
3095     missing_arg_str("FloorHeight", "1, 2 or 3");
3096     goto done;
3097 
3098 err_nosector:
3099     script_error("FloorHeight: sector not found with tagnum %i\n", tagnum);
3100     goto done;
3101 }
3102 
3103 // MoveFloor( tagnum, destheight, {speed} )
SF_MoveFloor(void)3104 void SF_MoveFloor(void)
3105 {
3106     int secnum;
3107     int platspeed;
3108     fixed_t destheight;
3109     uint16_t tagnum;
3110 
3111     if (t_argc < 2)  goto err_numarg;
3112 
3113     tagnum = intvalue(t_argv[0]);
3114     destheight = intvalue(t_argv[1]) << FRACBITS;
3115     platspeed = FLOORSPEED;
3116     if(t_argc > 2)
3117         platspeed *= intvalue(t_argv[2]);  // speed multiplier
3118 
3119     // move all sectors with tag
3120     secnum = -1;  // init search
3121     while ((secnum = P_FindSectorFromTag(tagnum, secnum)) >= 0)
3122     {
3123         sector_t * sec = &sectors[secnum];
3124         floormove_t * mfloor;
3125 
3126         // Don't start a second thinker on the same floor
3127         if (P_SectorActive( S_floor_special, sec))
3128             continue;
3129 
3130         mfloor = Z_Malloc(sizeof(floormove_t), PU_LEVSPEC, 0);
3131         P_AddThinker(&mfloor->thinker);
3132         sec->floordata = mfloor;
3133         mfloor->thinker.function.acp1 = (actionf_p1) T_MoveFloor;
3134         mfloor->type = -1;       // not done by line
3135         mfloor->crush = false;
3136 
3137         mfloor->direction = (destheight < sec->floorheight) ? -1 : 1;
3138         mfloor->sector = sec;
3139         mfloor->speed = platspeed;
3140         mfloor->floordestheight = destheight;
3141     }
3142 done:
3143     return;
3144 
3145 err_numarg:
3146     missing_arg_str("MoveFloor", "2 or 3");
3147     goto done;
3148 }
3149 
3150 // ceiling height of sector
3151 // CeilingHeight( tagnum, {ceilheight}, {crush} )
3152 // Return Test ceilingheight
3153 // Return when Set ceilingheight: 1=default, 0=crushing
SF_CeilingHeight(void)3154 void SF_CeilingHeight(void)
3155 {
3156     int returnval = 1;  // When Set ceilingheight: 1=default, 0=crushing
3157     int secnum;
3158     uint16_t tagnum;
3159 
3160     if (!t_argc)  goto err_numarg;
3161 
3162     t_return.type = FSVT_int;
3163 
3164     tagnum = intvalue(t_argv[0]);
3165 
3166     // check on existing tagged sector
3167     secnum = P_FindSectorFromTag(tagnum, -1);
3168     if (secnum < 0)  goto err_nosector;
3169 
3170     if (t_argc > 1)     // > 1: set ceilheight
3171     {
3172         // set ceilingheight
3173         fixed_t arg_cheight = fixedvalue(t_argv[1]);  // set ceilingheight
3174         boolean arg_crush = (t_argc == 3) ? intvalue(t_argv[2]) : false;
3175 
3176         // set all sectors with tag
3177         secnum = -1;
3178         while ((secnum = P_FindSectorFromTag(tagnum, secnum)) >= 0)
3179         {
3180             sector_t * sec = &sectors[secnum];
3181             result_e res =
3182              T_MovePlane(sec,
3183                          abs(arg_cheight - sec->ceilingheight), // speed
3184                          arg_cheight, arg_crush, 1,  // dest, crush, move ceiling
3185                          (arg_cheight > sec->ceilingheight) ? 1 : -1); // direction
3186             if ( res == MP_crushed)
3187                 returnval = 0;  // signal crushing
3188         }
3189     }
3190     else
3191     {
3192         // get ceilingheight
3193         returnval = sectors[secnum].ceilingheight >> FRACBITS;
3194     }
3195 
3196     // return floorheight, or crushing
3197     t_return.value.i = returnval;
3198 done:
3199     return;
3200 
3201 err_numarg:
3202     missing_arg_str("CeilingHeight", "1, 2 or 3");
3203     goto done;
3204 
3205 err_nosector:
3206     script_error("CeilingHeight: sector not found with tagnum %i\n", tagnum);
3207     goto done;
3208 }
3209 
3210 // MoveCeiling( tagnum, destheight, {speed} )
SF_MoveCeiling(void)3211 void SF_MoveCeiling(void)
3212 {
3213     int secnum;
3214     int platspeed;
3215     fixed_t destheight;
3216     uint16_t tagnum;
3217 
3218     if (t_argc < 2)  goto err_numarg;
3219 
3220     tagnum = intvalue(t_argv[0]);
3221     destheight = intvalue(t_argv[1]) << FRACBITS;
3222     platspeed = FLOORSPEED;
3223     if(t_argc > 2)
3224         platspeed *= intvalue(t_argv[2]);  // speed multiplier
3225 
3226     // move all sectors with tag
3227     secnum = -1;  // init search
3228     while ((secnum = P_FindSectorFromTag(tagnum, secnum)) >= 0)
3229     {
3230         sector_t * sec = &sectors[secnum];
3231         ceiling_t * mceiling;
3232 
3233         // Don't start a second thinker on the same floor
3234         if (P_SectorActive( S_ceiling_special, sec))
3235             continue;
3236 
3237         mceiling = Z_Malloc(sizeof(ceiling_t), PU_LEVSPEC, 0);
3238         P_AddThinker(&mceiling->thinker);
3239         sec->ceilingdata = mceiling;
3240         mceiling->thinker.function.acp1 = (actionf_p1) T_MoveCeiling;
3241         mceiling->type = CT_genCeiling;     // not done by line
3242         mceiling->crush = false;
3243 
3244         mceiling->direction = destheight < sec->ceilingheight ? -1 : 1;
3245         mceiling->sector = sec;
3246         mceiling->speed = platspeed;
3247         // just set top and bottomheight the same
3248         mceiling->topheight = mceiling->bottomheight = destheight;
3249 
3250         mceiling->tag = sec->tag;
3251         P_AddActiveCeiling(mceiling);
3252     }
3253 done:
3254     return;
3255 
3256 err_numarg:
3257     missing_arg_str("MoveCeiling", "2 or 3");
3258     goto done;
3259 }
3260 
3261 // Test or Set
3262 // Lightlevel( tagnum, {lightlevel} )
SF_LightLevel(void)3263 void SF_LightLevel(void)
3264 {
3265     sector_t *sector;
3266     int secnum;
3267     uint16_t tagnum;
3268 
3269     if (!t_argc)  goto err_numarg;
3270 
3271     t_return.type = FSVT_int;
3272 
3273     tagnum = intvalue(t_argv[0]);
3274 
3275     // check on existing tagged sector
3276     secnum = P_FindSectorFromTag(tagnum, -1);
3277     if (secnum < 0)  goto err_nosector;
3278     sector = &sectors[secnum];  // first sector for return value
3279 
3280     if (t_argc > 1)     // > 1: set
3281     {
3282         // set all sectors with tag
3283         lightlev_t arg_light = intvalue(t_argv[1]);
3284         secnum = -1;  // init search
3285         while ((secnum = P_FindSectorFromTag(tagnum, secnum)) >= 0)
3286         {
3287             // all sectors with tagnum
3288             sectors[secnum].lightlevel = arg_light;
3289         }
3290     }
3291 
3292     // return lightlevel
3293     t_return.value.i = sector->lightlevel;
3294 done:
3295     return;
3296 
3297 err_numarg:
3298     missing_arg_str("LightLevel", "1 or 2");
3299     goto done;
3300 
3301 err_nosector:
3302     script_error("LightLevel: sector not found with tagnum %i\n", tagnum);
3303     goto done;
3304 }
3305 
3306 // Set
3307 // Lightlevel( tagnum, lightlevel, {speed} )
SF_FadeLight(void)3308 void SF_FadeLight(void)
3309 {
3310     int sectag, destlevel, speed = 1;
3311 
3312     if (t_argc < 2)  goto err_numarg;
3313 
3314     sectag = intvalue(t_argv[0]);
3315     destlevel = intvalue(t_argv[1]);
3316     speed = (t_argc > 2) ? intvalue(t_argv[2]) : 1;
3317 
3318     P_FadeLight(sectag, destlevel, speed);
3319 done:
3320     return;
3321 
3322 err_numarg:
3323     missing_arg_str("FadeLight", "2 or 3");
3324     goto done;
3325 }
3326 
3327 // Test or Set
3328 // FloorTexture( tagnum, {flatname} )
SF_FloorTexture(void)3329 void SF_FloorTexture(void)
3330 {
3331     sector_t *sector;
3332     int secnum;
3333     uint16_t tagnum;
3334 
3335     if (!t_argc)  goto err_numarg;
3336 
3337     t_return.type = FSVT_string;
3338     t_return.value.s = "";  // default
3339 
3340     tagnum = intvalue(t_argv[0]);
3341 
3342     // check on existing tagged sector
3343     secnum = P_FindSectorFromTag(tagnum, -1);
3344     if (secnum < 0)  goto err_nosector;
3345     sector = &sectors[secnum];  // first sector for return value
3346 
3347     if (t_argc > 1)
3348     {
3349         // set texture
3350         lumpnum_t picnum = R_FlatNumForName(t_argv[1].value.s);
3351             // when flat not found, defaults to first flat
3352 
3353         // set all sectors with tag
3354         secnum = -1;  // init search
3355         while ((secnum = P_FindSectorFromTag(tagnum, secnum)) >= 0)
3356         {
3357             sectors[secnum].floorpic = picnum;
3358         }
3359     }
3360 
3361     t_return.value.s = P_FlatNameForNum(sector->floorpic);
3362 done:
3363     return;
3364 
3365 err_numarg:
3366     missing_arg_str("FloorTexture", "1 or 2");
3367     goto done;
3368 
3369 err_nosector:
3370     script_error("FloorTexture: sector not found with tagnum %i\n", tagnum);
3371     goto done;
3372 }
3373 
3374 // Test or Set
3375 // SectorColormap( tagnum, {mapnum} )
3376 // mapnum= -1 removes the colormap
SF_SectorColormap(void)3377 void SF_SectorColormap(void)
3378 {
3379     sector_t *sector;
3380     int secnum;
3381     uint16_t tagnum;
3382 
3383     if (!t_argc)  goto err_numarg;
3384 
3385     t_return.type = FSVT_string;
3386     t_return.value.s = "";  // default
3387 
3388     tagnum = intvalue(t_argv[0]);
3389 
3390     // check on existing tagged sector
3391     secnum = P_FindSectorFromTag(tagnum, -1);
3392     if (secnum < 0)  goto err_nosector;
3393     sector = &sectors[secnum];  // first sector for return value
3394 
3395     if (t_argc > 1)
3396     {
3397         int mapnum = R_ColormapNumForName(t_argv[1].value.s);
3398 
3399         // set all sectors with tag
3400         secnum = -1; // init search
3401         while ((secnum = P_FindSectorFromTag(tagnum, secnum)) >= 0)
3402         {
3403             sector_t * sec = &sectors[secnum];
3404             if (mapnum == -1)
3405             {
3406                 // remove colormap
3407                 sec->midmap = 0;
3408                 sec->model = SM_normal;
3409                 sec->modelsec = 0;
3410             }
3411             else
3412             {
3413                 // set colormap
3414                 sec->midmap = mapnum;
3415                 sec->model = SM_colormap;
3416                 sec->modelsec = 0;
3417             }
3418         }
3419     }
3420 
3421     t_return.value.s = R_ColormapNameForNum(sector->midmap);
3422 done:
3423     return;
3424 
3425 err_numarg:
3426     missing_arg_str("SectorColormap", "1 or 2");
3427     goto done;
3428 
3429 err_nosector:
3430     script_error("SectorColormap: sector not found with tagnum %i\n", tagnum);
3431     goto done;
3432 }
3433 
3434 // Test or Set
3435 // CeilingTexture( tagnum, {flatname} )
SF_CeilingTexture(void)3436 void SF_CeilingTexture(void)
3437 {
3438     sector_t *sector;
3439     int secnum;
3440     uint16_t tagnum;
3441 
3442     if (!t_argc)  goto err_numarg;
3443 
3444     t_return.type = FSVT_string;
3445     t_return.value.s = "";  // default
3446 
3447     tagnum = intvalue(t_argv[0]);
3448 
3449     // check on existing tagged sector
3450     secnum = P_FindSectorFromTag(tagnum, -1);
3451     if (secnum < 0)  goto err_nosector;
3452     sector = &sectors[secnum];  // first sector for return value
3453 
3454     if (t_argc > 1)
3455     {
3456         // set texture
3457         int picnum = R_FlatNumForName(t_argv[1].value.s);
3458             // when flat not found, defaults to first flat
3459 
3460         // set all sectors with tag
3461         secnum = -1; // init search
3462         while ((secnum = P_FindSectorFromTag(tagnum, secnum)) >= 0)
3463         {
3464             sectors[secnum].ceilingpic = picnum;
3465         }
3466     }
3467 
3468     t_return.value.s = P_FlatNameForNum(sector->ceilingpic);
3469 done:
3470     return;
3471 
3472 err_numarg:
3473     missing_arg_str("CeilingTexture", "1 or 2");
3474     goto done;
3475 
3476 err_nosector:
3477     script_error("CeilingTexture: sector not found with tagnum %i\n", tagnum);
3478     goto done;
3479 }
3480 
SF_ChangeHubLevel(void)3481 void SF_ChangeHubLevel(void)
3482 {
3483 /*  uint16_t tagnum;
3484 
3485   if(!t_argc)
3486     {
3487       script_error("hub level to go to not specified!\n");
3488       return;
3489     }
3490   if(t_argv[0].type != FSVT_string)
3491     {
3492       script_error("level argument is not a string!\n");
3493       return;
3494     }
3495 
3496   // second argument is tag num for 'seamless' travel
3497   if(t_argc > 1)
3498     tagnum = intvalue(t_argv[1]);
3499   else
3500     tagnum = -1;
3501 
3502   P_SavePlayerPosition(fs_current_script->trigger->player, tagnum);
3503   P_ChangeHubLevel(t_argv[0].value.s);*/
3504 }
3505 
3506 // for start map: start new game on a particular skill
3507 // StartSkill( skill )
3508 // skill = 1..5
SF_StartSkill(void)3509 void SF_StartSkill(void)
3510 {
3511     int skill;
3512 
3513     if (t_argc != 1)  goto err_numarg;
3514 
3515     // -1: 1-5 is how we normally see skills
3516     // 0-4 is how doom sees them
3517 
3518     skill = intvalue(t_argv[0]) - 1;
3519 
3520     // skill 0..4
3521     G_DeferedInitNew(skill, G_BuildMapName(1, 1), false);
3522 done:
3523     return;
3524 
3525 err_numarg:
3526     wrong_num_arg("StartSkill", 1);
3527     goto done;
3528 }
3529 
3530 //////////////////////////////////////////////////////////////////////////
3531 //
3532 // Doors
3533 //
3534 
3535 // OpenDoor(tagnum, [delay], [speed])
SF_OpenDoor(void)3536 void SF_OpenDoor(void)
3537 {
3538     int speed;
3539     int wait_time;
3540     uint16_t tagnum;
3541 
3542     if (t_argc < 1)  goto err_numarg;
3543 
3544     // got sector tag
3545     tagnum = intvalue(t_argv[0]);
3546 
3547     // door wait time
3548     if (t_argc > 1)     // door wait time
3549         wait_time = (intvalue(t_argv[1]) * 35) / 100;
3550     else
3551         wait_time = 0;  // 0= stay open
3552 
3553     // door speed
3554     if (t_argc > 2)
3555         speed = intvalue(t_argv[2]);
3556     else
3557         speed = 1;      // 1= normal speed
3558 
3559     EV_OpenDoor(tagnum, speed, wait_time);
3560 done:
3561     return;
3562 
3563 err_numarg:
3564     missing_arg_str("OpenDoor", "1, 2, or 3");
3565     goto done;
3566 }
3567 
3568 // CloseDoor(tagnum, [speed])
SF_CloseDoor(void)3569 void SF_CloseDoor(void)
3570 {
3571     int speed;
3572     uint16_t tagnum;
3573 
3574     if (t_argc < 1)  goto err_numarg;
3575 
3576     // got sector tag
3577     tagnum = intvalue(t_argv[0]);
3578 
3579     // door speed
3580     if (t_argc > 1)
3581         speed = intvalue(t_argv[1]);
3582     else
3583         speed = 1;      // 1= normal speed
3584 
3585     EV_CloseDoor(tagnum, speed);
3586 done:
3587     return;
3588 
3589 err_numarg:
3590     missing_arg_str("CloseDoor", "1 or 2");
3591     goto done;
3592 }
3593 
3594 // play demo, internal lump or external file
3595 // PlayDemo( lumpname )
SF_PlayDemo(void)3596 void SF_PlayDemo(void)
3597 {
3598     if (t_argc != 1)  goto err_numarg;
3599     if (t_argv[0].type != FSVT_string)  goto err_notlump;
3600 
3601     G_DoPlayDemo(t_argv[0].value.s);
3602     // if name is lump, then play lump,
3603     // otherwise it adds ".lmp" and reads demo file
3604 done:
3605     return;
3606 
3607 err_numarg:
3608     wrong_num_arg("PlayDemo", 1);
3609     goto done;
3610 
3611 err_notlump:
3612     script_error("PlayDemo: not a lump name\n");
3613     goto done;
3614 }
3615 
3616 // run console cmd
3617 // RunCommand( cmdstring, ... )   upto 128 strings
SF_RunCommand(void)3618 void SF_RunCommand(void)
3619 {
3620     char * tempstr = Z_cat_args(0);  // concat arg0, arg1, ...
3621     // [WDJ] May be too long to terminate with va( "%s\n", tempstr ).
3622     // Z_cat_args allocates 3 extra chars for termination.
3623     strcat(tempstr, "\n" );
3624     COM_BufAddText( tempstr );
3625     Z_Free(tempstr);
3626 }
3627 
3628 // return the (string) value of a cvar
SF_CheckCVar(void)3629 void SF_CheckCVar(void)
3630 {
3631     consvar_t *cvar;
3632 
3633     if (t_argc != 1)  goto err_numarg;
3634 
3635     t_return.type = FSVT_string;
3636 
3637     if ((cvar = CV_FindVar(stringvalue(t_argv[0]))))
3638     {
3639         t_return.value.s = cvar->string;
3640     }
3641     else
3642     {
3643         t_return.value.s = "";
3644     }
3645 done:
3646     return;
3647 
3648 err_numarg:
3649     wrong_num_arg("CheckCVar", 1);
3650     goto done;
3651 }
3652 
3653 
3654 //DarkWolf95:July 23, 2003:Return/Set LineTexture Yay!
3655 // Set
3656 // SetLineTexture( tagnum, texturename, sidenum, sectionflags )
3657 // sidenum: 0, 1
3658 // sectionflags: 1 = top 2 = mid 4 = bot
SF_SetLineTexture(void)3659 void SF_SetLineTexture(void)
3660 {
3661     int linenum;
3662     unsigned int side;
3663     int sectionflags;
3664 //    line_t *line;
3665     uint16_t tagnum;
3666 
3667     if(t_argc != 4)  goto err_numarg;
3668 
3669     tagnum = intvalue(t_argv[0]);
3670 
3671     // check on existing line
3672     linenum = P_FindLineFromTag(tagnum, -1);
3673     if(linenum < 0)  goto err_noline;
3674 //    line = &lines[linenum];  // for return value
3675 
3676     side = (unsigned) intvalue(t_argv[2]);
3677     if( side > 1 )  side = 1;  // easier than an error
3678     sectionflags = intvalue(t_argv[3]);
3679 
3680     {
3681       // set textures
3682       short picnum = R_TextureNumForName(t_argv[1].value.s);
3683         // returns 0=no-texture, or default texture, otherwise valid
3684 
3685       // set all sectors with tag
3686       linenum = -1; // init search
3687       while (( linenum = P_FindLineFromTag(tagnum, linenum)) >= 0)
3688       {
3689           line_t * linep = &lines[linenum];
3690           if (linep->sidenum[side] == NULL_INDEX)
3691           {
3692 #if 0
3693               // [WDJ] Unnecessary error, error is not in script
3694               script_error("nonexistant side\n");
3695               goto done;
3696 #endif
3697           }
3698           else
3699           {
3700               side_t * sidep = & sides[linep->sidenum[side]];
3701               // textures, 0=no-texture, otherwise valid
3702               if(sectionflags & 1)
3703                  sidep->toptexture = picnum;
3704               if(sectionflags & 2)
3705                  sidep->midtexture = picnum;
3706               if(sectionflags & 4)
3707                  sidep->bottomtexture = picnum;
3708           }
3709       }
3710     }
3711 done:
3712     return;
3713 
3714 err_numarg:
3715     wrong_num_arg("SetLineTexture", 1);
3716     goto done;
3717 
3718 err_noline:
3719     script_error("line not found with tagnum %i\n", tagnum);
3720     goto done;
3721 }
3722 
3723 // Imitate a linedef trigger of any linedef type
3724 // LineTrigger( special, {tagnum} )
3725 // With tagnum, or with tag of 0
SF_LineTrigger(void)3726 void SF_LineTrigger(void)
3727 {
3728     line_t sf_tmpline;
3729 
3730     if (!t_argc)  goto err_numarg;
3731 
3732     sf_tmpline.special = intvalue(t_argv[0]);
3733     sf_tmpline.tag = (t_argc>1)? intvalue(t_argv[1]) : 0;
3734     // [WDJ] used by P_UseSpecialLine and functions in p_genlin
3735     sf_tmpline.flags = 0;
3736     sf_tmpline.sidenum[0] = NULL_INDEX;
3737     sf_tmpline.sidenum[1] = NULL_INDEX;
3738     sf_tmpline.backsector = NULL;
3739     sf_tmpline.frontsector = NULL;
3740 
3741     P_UseSpecialLine(fs_run_trigger, &sf_tmpline, 0);      // Try using it
3742     P_CrossSpecialLine(&sf_tmpline, 0, fs_run_trigger); // Try crossing it
3743 done:
3744     return;
3745 
3746 err_numarg:
3747     missing_arg_str("LineTrigger", "1 or 2");
3748     goto done;
3749 }
3750 
3751 // Test or Set
3752 // LineFlag( linenum, {flagnum}, {flagvalue} )
3753 // flagnum: flag bit position, 0..31
3754 // flagvalue: 0, 1
SF_LineFlag(void)3755 void SF_LineFlag(void)
3756 {
3757     line_t *line;
3758     int linenum;
3759     int flagnum;
3760     uint32_t flagmsk;
3761 
3762     if (t_argc < 2)  goto err_numarg;
3763 
3764     t_return.type = FSVT_int;
3765 
3766     linenum = intvalue(t_argv[0]);
3767     if (linenum < 0 || linenum > numlines)  goto err_noline;
3768     line = & lines[linenum];  // for test, set, clear
3769 
3770     flagnum = intvalue(t_argv[1]);
3771     if (flagnum < 0 || flagnum > 32)  goto err_flagnum;
3772     flagmsk = (1 << flagnum);
3773 
3774     if (t_argc > 2)
3775     {   // Clear or Set flags
3776         line->flags &= ~flagmsk;
3777         if (intvalue(t_argv[2]))
3778             line->flags |= flagmsk;
3779     }
3780 
3781     t_return.value.i = line->flags & flagmsk;
3782 done:
3783     return;
3784 
3785 err_numarg:
3786     missing_arg_str("LineFlag", "1 or 2");
3787     goto done;
3788 
3789 err_noline:
3790     script_error("LineFlag: invalid line number %i\n", linenum);
3791     goto done;
3792 
3793 err_flagnum:
3794     script_error("LineFlag: invalid flag number %i\n", flagnum);
3795     goto done;
3796 }
3797 
3798 // ChangeMusic( namestring )
SF_ChangeMusic(void)3799 void SF_ChangeMusic(void)
3800 {
3801     if (t_argc != 1)  goto err_numarg;
3802     if (t_argv[0].type != FSVT_string)  goto err_notstring;
3803 
3804     S_ChangeMusicName(t_argv[0].value.s, 1);
3805 done:
3806     return;
3807 
3808 err_numarg:
3809     wrong_num_arg("ChangeMusic", 1);
3810     goto done;
3811 
3812 err_notstring:
3813     script_error("ChangeMusic: require music name string\n");
3814     goto done;
3815 }
3816 
3817 // SoM: Max and Min math functions.
3818 // Max( v1, v2 )
SF_Max(void)3819 void SF_Max(void)
3820 {
3821     fixed_t n1, n2;
3822 
3823     if (t_argc != 2)  goto err_numarg;
3824 
3825     n1 = fixedvalue(t_argv[0]);
3826     n2 = fixedvalue(t_argv[1]);
3827 
3828     t_return.type = FSVT_fixed;
3829     t_return.value.f = n1 > n2 ? n1 : n2;
3830 done:
3831     return;
3832 
3833 err_numarg:
3834     wrong_num_arg("Max", 2);
3835     goto done;
3836 }
3837 
3838 // Min( v1, v2 )
SF_Min(void)3839 void SF_Min(void)
3840 {
3841     fixed_t n1, n2;
3842 
3843     if (t_argc != 2)  goto err_numarg;
3844 
3845     n1 = fixedvalue(t_argv[0]);
3846     n2 = fixedvalue(t_argv[1]);
3847 
3848     t_return.type = FSVT_fixed;
3849     t_return.value.f = n1 < n2 ? n1 : n2;
3850 done:
3851     return;
3852 
3853 err_numarg:
3854     wrong_num_arg("Min", 2);
3855     goto done;
3856 }
3857 
3858 // Abs( v1 )
SF_Abs(void)3859 void SF_Abs(void)
3860 {
3861     fixed_t n1;
3862 
3863     if (t_argc != 1)  goto err_numarg;
3864 
3865     n1 = fixedvalue(t_argv[0]);
3866 
3867     t_return.type = FSVT_fixed;
3868 //    t_return.value.f = n1 < 0 ? n1 * -1 : n1;
3869     t_return.value.f = abs(n1);
3870 done:
3871     return;
3872 
3873 err_numarg:
3874     wrong_num_arg("Abs", 1);
3875     goto done;
3876 }
3877 
3878 //Hurdler: some new math functions
double2fixed(double t)3879 fixed_t double2fixed(double t)
3880 {
3881     double fl = floor(t);
3882     return ((int) fl << 16) | (int) ((t - fl) * 65536.0);
3883 }
3884 
3885 // Sin( angle )
3886 // angle: radians
SF_Sin(void)3887 void SF_Sin(void)
3888 {
3889     if (t_argc != 1)  goto err_numarg;
3890 
3891     {
3892         fixed_t n1 = fixedvalue(t_argv[0]);
3893         t_return.type = FSVT_fixed;
3894         t_return.value.f = double2fixed(sin(FIXED_TO_FLOAT(n1)));
3895     }
3896 done:
3897     return;
3898 
3899 err_numarg:
3900     wrong_num_arg("Sin", 1);
3901     goto done;
3902 }
3903 
3904 // ASin( v1 )
3905 // Return angle: radians
SF_ASin(void)3906 void SF_ASin(void)
3907 {
3908     if (t_argc != 1)  goto err_numarg;
3909 
3910     {
3911         fixed_t n1 = fixedvalue(t_argv[0]);
3912         t_return.type = FSVT_fixed;
3913         t_return.value.f = double2fixed(asin(FIXED_TO_FLOAT(n1)));
3914     }
3915 done:
3916     return;
3917 
3918 err_numarg:
3919     wrong_num_arg("ASin", 1);
3920     goto done;
3921 }
3922 
3923 // Cos( angle )
3924 // angle: radians
SF_Cos(void)3925 void SF_Cos(void)
3926 {
3927     if (t_argc != 1)  goto err_numarg;
3928 
3929     {
3930         fixed_t n1 = fixedvalue(t_argv[0]);
3931         t_return.type = FSVT_fixed;
3932         t_return.value.f = double2fixed(cos(FIXED_TO_FLOAT(n1)));
3933     }
3934 done:
3935     return;
3936 
3937 err_numarg:
3938     wrong_num_arg("Cos", 1);
3939     goto done;
3940 }
3941 
3942 // ACos( v1 )
3943 // Return angle: radians
SF_ACos(void)3944 void SF_ACos(void)
3945 {
3946     if (t_argc != 1)  goto err_numarg;
3947 
3948     {
3949         fixed_t n1 = fixedvalue(t_argv[0]);
3950         t_return.type = FSVT_fixed;
3951         t_return.value.f = double2fixed(acos(FIXED_TO_FLOAT(n1)));
3952     }
3953 done:
3954     return;
3955 
3956 err_numarg:
3957     wrong_num_arg("ACos", 1);
3958     goto done;
3959 }
3960 
3961 // Tan( angle )
3962 // angle: radians
SF_Tan(void)3963 void SF_Tan(void)
3964 {
3965     if (t_argc != 1)  goto err_numarg;
3966 
3967     {
3968         fixed_t n1 = fixedvalue(t_argv[0]);
3969         t_return.type = FSVT_fixed;
3970         t_return.value.f = double2fixed(tan(FIXED_TO_FLOAT(n1)));
3971     }
3972 done:
3973     return;
3974 
3975 err_numarg:
3976     wrong_num_arg("Tan", 1);
3977     goto done;
3978 }
3979 
3980 // ATan( v1 )
3981 // Return angle: radians
SF_ATan(void)3982 void SF_ATan(void)
3983 {
3984     if (t_argc != 1)  goto err_numarg;
3985 
3986     {
3987         fixed_t n1 = fixedvalue(t_argv[0]);
3988         t_return.type = FSVT_fixed;
3989         t_return.value.f = double2fixed(atan(FIXED_TO_FLOAT(n1)));
3990     }
3991 done:
3992     return;
3993 
3994 err_numarg:
3995     wrong_num_arg("ATan", 1);
3996     goto done;
3997 }
3998 
SF_Exp(void)3999 void SF_Exp(void)
4000 {
4001     if (t_argc != 1)  goto err_numarg;
4002 
4003     {
4004         fixed_t n1 = fixedvalue(t_argv[0]);
4005         t_return.type = FSVT_fixed;
4006         t_return.value.f = double2fixed(exp(FIXED_TO_FLOAT(n1)));
4007     }
4008 done:
4009     return;
4010 
4011 err_numarg:
4012     wrong_num_arg("Exp", 1);
4013     goto done;
4014 }
4015 
4016 // Log( v1 )
SF_Log(void)4017 void SF_Log(void)
4018 {
4019     if (t_argc != 1)  goto err_numarg;
4020 
4021     {
4022         fixed_t n1 = fixedvalue(t_argv[0]);
4023         t_return.type = FSVT_fixed;
4024         t_return.value.f = double2fixed(log(FIXED_TO_FLOAT(n1)));
4025     }
4026 done:
4027     return;
4028 
4029 err_numarg:
4030     wrong_num_arg("Log", 1);
4031     goto done;
4032 }
4033 
SF_Sqrt(void)4034 void SF_Sqrt(void)
4035 {
4036     if (t_argc != 1)  goto err_numarg;
4037 
4038     {
4039         fixed_t n1 = fixedvalue(t_argv[0]);
4040         t_return.type = FSVT_fixed;
4041         t_return.value.f = double2fixed(sqrt(FIXED_TO_FLOAT(n1)));
4042     }
4043 done:
4044     return;
4045 
4046 err_numarg:
4047     wrong_num_arg("Sqrt", 1);
4048     goto done;
4049 }
4050 
4051 // Floor( v1 )
SF_Floor(void)4052 void SF_Floor(void)
4053 {
4054     if (t_argc != 1)  goto err_numarg;
4055 
4056     {
4057         fixed_t n1 = fixedvalue(t_argv[0]);
4058         t_return.type = FSVT_fixed;
4059         t_return.value.f = n1 & 0xffFF0000;
4060     }
4061 done:
4062     return;
4063 
4064 err_numarg:
4065     wrong_num_arg("Floor", 1);
4066     goto done;
4067 }
4068 
4069 // Pow( v1, v2 )
SF_Pow(void)4070 void SF_Pow(void)
4071 {
4072     fixed_t n1, n2;
4073 
4074     if (t_argc != 2)  goto err_numarg;
4075 
4076     n1 = fixedvalue(t_argv[0]);
4077     n2 = fixedvalue(t_argv[1]);
4078 
4079     t_return.type = FSVT_fixed;
4080     t_return.value.f = double2fixed(pow(FIXED_TO_FLOAT(n1), FIXED_TO_FLOAT(n2)));
4081 done:
4082     return;
4083 
4084 err_numarg:
4085     wrong_num_arg("Pow", 2);
4086     goto done;
4087 }
4088 
4089 
4090 
4091 
4092 // Type forcing functions -- useful with arrays et al
4093 
4094 // MobjValue( mobj )
SF_MobjValue(void)4095 void SF_MobjValue(void)
4096 {
4097     if(t_argc != 1)  goto err_numarg;
4098     t_return.type = FSVT_mobj;
4099     t_return.value.mobj = MobjForSvalue(t_argv[0]);
4100 done:
4101     return;
4102 
4103 err_numarg:
4104     wrong_num_arg("MobjValue", 1);
4105     goto done;
4106 }
4107 
4108 // StringValue( v )
SF_StringValue(void)4109 void SF_StringValue(void)
4110 {
4111    if(t_argc != 1)  goto err_numarg;
4112    t_return.type = FSVT_string;
4113    t_return.value.s = Z_Strdup(stringvalue(t_argv[0]), PU_LEVEL, 0);
4114 done:
4115     return;
4116 
4117 err_numarg:
4118     wrong_num_arg("StringValue", 1);
4119     goto done;
4120 }
4121 
4122 // IntValue( v1 )
SF_IntValue(void)4123 void SF_IntValue(void)
4124 {
4125    if(t_argc != 1)  goto err_numarg;
4126    t_return.type = FSVT_int;
4127    t_return.value.i = intvalue(t_argv[0]);
4128 done:
4129     return;
4130 
4131 err_numarg:
4132     wrong_num_arg("IntValue", 1);
4133     goto done;
4134 }
4135 
4136 // FixedValue( v1 )
SF_FixedValue(void)4137 void SF_FixedValue(void)
4138 {
4139    if(t_argc != 1)  goto err_numarg;
4140    t_return.type = FSVT_fixed;
4141    t_return.value.f = fixedvalue(t_argv[0]);
4142 done:
4143     return;
4144 
4145 err_numarg:
4146     wrong_num_arg("FixedValue", 1);
4147     goto done;
4148 }
4149 
4150 
4151 
4152 
4153 //////////////////////////////////////////////////////////////////////////
4154 // FraggleScript HUD graphics
4155 //////////////////////////////////////////////////////////////////////////
4156 
4157 // alias createpic
4158 // NewHUPic( lumpname, x, y )
4159 // Return pic_handle
SF_NewHUPic(void)4160 void SF_NewHUPic(void)
4161 {
4162     if (t_argc != 3)  goto err_numarg;
4163     t_return.type = FSVT_int;
4164     t_return.value.i =
4165       HU_Get_FSPic( W_GetNumForName(stringvalue(t_argv[0])),
4166                     intvalue(t_argv[1]), intvalue(t_argv[2]));
4167 done:
4168     return;
4169 
4170 err_numarg:
4171     wrong_num_arg("NewHUPic", 3);
4172     goto done;
4173 }
4174 
4175 // alias deletehupic
4176 // DeleteHUPic( pic_handle )
SF_DeleteHUPic(void)4177 void SF_DeleteHUPic(void)
4178 {
4179     int handle;
4180 
4181     if (t_argc != 1)  goto err_numarg;
4182 
4183     handle = intvalue(t_argv[0]);
4184     if (HU_Delete_FSPic(handle) == -1)  goto err_delete;
4185 done:
4186     return;
4187 
4188 err_numarg:
4189     wrong_num_arg("DeleteHUPic", 1);
4190     goto done;
4191 
4192 err_delete:
4193     script_error("DeleteHUPic: invalid sfpic handle %i\n", handle);
4194     goto done;
4195 }
4196 
4197 // alias modifyhupic, modifypic
4198 // ModifyHUPic( handle, lumpname, x, y )
SF_ModifyHUPic(void)4199 void SF_ModifyHUPic(void)
4200 {
4201     int handle;
4202 
4203     if (t_argc != 4)  goto err_numarg;
4204 
4205     handle = intvalue(t_argv[0]);
4206     if (HU_Modify_FSPic(handle, W_GetNumForName(stringvalue(t_argv[1])),
4207                        intvalue(t_argv[2]), intvalue(t_argv[3])) == -1)
4208         goto err_handle;
4209 done:
4210     return;
4211 
4212 err_numarg:
4213     wrong_num_arg("ModifyHUPic", 4);
4214     goto done;
4215 
4216 err_handle:
4217     script_error("ModifyHUPic: invalid sfpic handle %i\n", handle);
4218     goto done;
4219 }
4220 
4221 // alias setpicvisible
4222 // SetHUPicDisplay( handle, drawenable )
4223 // drawenable: 0,1
SF_SetHUPicDisplay(void)4224 void SF_SetHUPicDisplay(void)
4225 {
4226     int handle;
4227 
4228     if (t_argc != 2)  goto err_numarg;
4229 
4230     handle = intvalue(t_argv[0]);
4231     if (HU_FS_Display(handle, intvalue(t_argv[1]) > 0 ? 1 : 0) == -1)
4232         goto err_handle;
4233 done:
4234     return;
4235 
4236 err_numarg:
4237     wrong_num_arg("SetHUPicDisplay", 4);
4238     goto done;
4239 
4240 err_handle:
4241     script_error("SetHUPicDisplay: invalid sfpic handle %i\n", handle);
4242     goto done;
4243 }
4244 
4245 // Hurdler: I'm enjoying FS capability :)
4246 
4247 
4248 // Debug color setting.
4249 //#define SHOW_COLOR_SETTING
4250 
4251 // [WDJ] Rewritten to process Hex ARGB and RGB string, any length, any machine.
String_to_RGBA(const char * s)4252 uint32_t String_to_RGBA( const char *s)
4253 {
4254     // [WDJ] Handles any length hex value, and no macros.
4255     RGBA_t   valrgb;  // Cannot trust byte order of rgba component.
4256     uint32_t valhex = 0;
4257     // Convert string to hex
4258     while( *s ) {
4259        register byte v = *s;
4260        if( v >= '0' && v <= '9' )
4261        {
4262            v -= '0';
4263        }else if( v >= 'A' && v <= 'F' )
4264        {
4265            v -= ('A' - 10);
4266        }else if( v >= 'a' && v <= 'f' )
4267        {
4268            v -= ('a' - 10);
4269        }
4270        else
4271          break;  // Not a hex digit
4272 
4273        valhex = (valhex<<4) + v;
4274        s++;
4275     }
4276     // Convert hex to RGB bytes.
4277     // Macro UINT2RGBA does not work, gives wrong colors.
4278     // Was read as A R G B
4279     valrgb.s.blue = valhex;  // LSB byte
4280     valrgb.s.green = valhex>>8;
4281     valrgb.s.red = valhex>>16;
4282     valrgb.s.alpha = valhex>>24;
4283     return valrgb.rgba;
4284 }
4285 
4286 
4287 // SetCorona( id, attribute, value )
4288 // id: corona id number
4289 // attribute: corona attribute selection
4290 //
4291 // SetCorona( id, type, xoffset, yoffset, color, radius, dynamic_color, dynamic_radius )
4292 // id: corona id number
4293 // type: bits (
4294 //  CORONA_SPR=0x01,    // emit corona
4295 //  DYNLIGHT_SPR=0x02,  // dynamic light
4296 //  LIGHT_SPR=0x03, // CORONA_SPR|DYNLIGHT_SPR
4297 //  ROCKET_SPR=0x13 // CORONA_SPR|DYNLIGHT_SPR with random radius
4298 //  )
4299 // color: rgba color
4300 // radius: corona size
4301 // dynamic_color: rgba wall lighting color
4302 // dynamic_radius: wall lighting size
SF_SetCorona(void)4303 void SF_SetCorona(void)
4304 {
4305     int num;
4306     spr_light_t * sl;
4307 
4308     if (t_argc != 3 && t_argc != 7)   goto err_numarg;
4309 
4310     num = t_argv[0].value.i;    // which corona we want to modify
4311     if( num >= NUMLIGHTS )
4312         return;
4313 
4314     sl = & sprite_light[num];
4315 
4316     //this function accept 2 kinds of parameters
4317     if (t_argc == 3)
4318     {
4319         int what = t_argv[1].value.i;   // what we want to modify (type, color, offset,...)
4320         int ival = t_argv[2].value.i;   // new value
4321         double fval = FIXED_TO_FLOAT( t_argv[2].value.f );  // fixed param
4322 
4323         // The fragglescript corona sets.
4324         switch (what)
4325         {
4326             case 0:  // CORONA_TYPE is int
4327                 // Set sprite light corona lights.
4328                 sl->splgt_flags = ival;
4329                 sl->impl_flags |= SLI_type_set;  // a type was set
4330                 break;
4331             case 1:  // CORONA_OFFX is fixed
4332                 sl->light_xoffset = fval;  // unused
4333                 break;
4334             case 2:  // CORONA_OFFY is fixed
4335                 sl->light_yoffset = fval;
4336                 break;
4337             case 3:  // CORONA_COLOR is (string or int)
4338                 sl->corona_color.rgba = (t_argv[2].type == FSVT_string)?
4339                     String_to_RGBA(t_argv[2].value.s)
4340                     : ival;
4341 
4342 #ifdef SHOW_COLOR_SETTING
4343                 // Show the corona color setting.
4344                 if( devparm > 2 )
4345                 {
4346                     if(t_argv[2].type == FSVT_string)
4347                        debug_Printf( "CORONA_COLOR = %s, rgba=%x\n",
4348                                   t_argv[2].value.s, sl->corona_color.rgba );
4349                     else
4350                        debug_Printf( "CORONA_COLOR = %x, rgba=%x\n",
4351                                   ival, sl->corona_color.rgba );
4352                 }
4353 #endif
4354                 // Phobiata fix. Color with no alpha, uses default of 0xff.
4355                 if( sl->corona_color.s.alpha == 0 )
4356                 {
4357                     sl->corona_color.s.alpha = 0xff;  // previous default
4358                 }
4359 
4360                 // Chex newmaps fix. The flags are set 0 for Chex1.
4361                 // If CORONA_COLOR is set, then corona should be enabled.
4362                 if( sl->splgt_flags == 0 )
4363                 {
4364                     sl->splgt_flags = SPLGT_dynamic|SPLGT_corona|SPLT_light;  // firefly light
4365                 }
4366                 break;
4367             case 4:  // CORONA_SIZE is fixed
4368                 sl->corona_radius = fval;
4369                 break;
4370             case 5:  // LIGHT_COLOR is (string or int)
4371                 sl->dynamic_color.rgba = (t_argv[2].type == FSVT_string)?
4372                     String_to_RGBA(t_argv[2].value.s)
4373                     : ival;
4374                 // 0 means off, dynamic_alpha has not ever been defaulted
4375 
4376 #ifdef SHOW_COLOR_SETTING
4377                 // Show the dynamic color setting.
4378                 if( devparm > 2 )
4379                 {
4380                     if(t_argv[2].type == FSVT_string)
4381                        debug_Printf( "LIGHT_COLOR = %s, rgba=%x\n",
4382                                   t_argv[2].value.s, sl->dynamic_color.rgba );
4383                     else
4384                        debug_Printf( "LIGHT_COLOR = %x, rgba=%x\n",
4385                                   ival, sl->dynamic_color.rgba );
4386                 }
4387 #endif
4388                 break;
4389             case 6:  // LIGHT_SIZE is fixed
4390                 sl->dynamic_radius = fval;
4391                 // According to usage and init this is squared-radius.
4392                 sl->dynamic_sqrradius = fval * fval;
4393                 break;
4394             default:
4395                 I_SoftError("SetCorona: what %i\n", what);
4396                 break;
4397         }
4398     }
4399     else
4400     {
4401         // Set all fields of sprite corona light.
4402         sl->splgt_flags = t_argv[1].value.i;
4403         sl->impl_flags |= SLI_type_set;  // a type was set
4404         sl->light_xoffset = FIXED_TO_FLOAT(t_argv[2].value.f);  // unused
4405         sl->light_yoffset = FIXED_TO_FLOAT(t_argv[3].value.f);
4406         sl->corona_color.rgba = (t_argv[4].type == FSVT_string)?
4407             String_to_RGBA(t_argv[4].value.s)
4408             : t_argv[4].value.i;
4409 
4410         sl->corona_radius = FIXED_TO_FLOAT(t_argv[5].value.f);
4411 
4412         // Phobiata fix. Color with no alpha, uses default of 0xff.
4413         if( sl->corona_color.s.alpha == 0 )
4414         {
4415             sl->corona_color.s.alpha = 0xff;  // previous default
4416         }
4417 
4418         sl->dynamic_color.rgba = (t_argv[6].type == FSVT_string)?
4419             String_to_RGBA(t_argv[6].value.s)
4420             : t_argv[6].value.i;
4421         // 0 means off, dynamic_alpha has not ever been defaulted
4422 
4423         sl->dynamic_radius = FIXED_TO_FLOAT(t_argv[7].value.f);
4424         // According to usage and init this is squared-radius.
4425         sl->dynamic_sqrradius = sl->dynamic_radius * sl->dynamic_radius;
4426 
4427 #ifdef SHOW_COLOR_SETTING
4428         // Show the corona color setting.
4429         if( devparm > 2 )
4430         {
4431             if(t_argv[4].type == FSVT_string)
4432                debug_Printf( "CORONA_COLOR = %s, rgba=%x\n",
4433                           t_argv[4].value.s, sl->corona_color.rgba );
4434             else
4435                debug_Printf( "CORONA_COLOR = %x, rgba=%x\n",
4436                           t_argv[4].value.i, sl->corona_color.rgba );
4437             if(t_argv[2].type == FSVT_string)
4438                debug_Printf( "LIGHT_COLOR = %s, rgba=%x\n",
4439                           t_argv[6].value.s, sl->dynamic_color.rgba );
4440             else
4441                debug_Printf( "LIGHT_COLOR = %x, rgba=%x\n",
4442                           t_argv[6].value.i, sl->dynamic_color.rgba );
4443         }
4444 #endif
4445     }
4446 
4447     sl->impl_flags |= (SLI_changed | SLI_corona_set);  // trigger check for missing settings
4448     return;
4449 
4450 err_numarg:
4451     missing_arg_str("SetCorona", "3 or 7");
4452     return;
4453 }
4454 
4455 
4456 // Background color fades for FS
4457 uint32_t  fs_fadecolor;
4458 int       fs_fadealpha;
4459 
4460 
4461 // Background color fades
4462 // SetFade( red, green, blue, alpha )
SF_SetFade(void)4463 void SF_SetFade(void)
4464 {
4465     if (t_argc != 4)   goto err_numarg;
4466 
4467     // Calculate the background color value
4468     // Before was (R,G,B,A) format, which got reversed to (A,B,G,R).
4469 //    fadecolor = (256 * b) + (65536 * g) + (16777216 * r);
4470     fs_fadecolor = RGBA( t_argv[0].value.i, t_argv[1].value.i, t_argv[2].value.i, 0);
4471     fs_fadealpha = t_argv[3].value.i;
4472 done:
4473     return;
4474 
4475 err_numarg:
4476     wrong_num_arg("SetFade", 4);
4477     goto done;
4478 }
4479 
4480 
4481 //////////////////////////////////////////////////////////////////////////
4482 //
4483 // Init Functions
4484 //
4485 
4486 //extern int fov; // r_main.c
4487 int fov;
4488 
T_Init_functions(void)4489 void T_Init_functions(void)
4490 {
4491     // add all the functions
4492     add_game_int("consoleplayer", &consoleplayer);
4493     add_game_int("displayplayer", &displayplayer);
4494     add_game_int("fov", &fov);
4495     add_game_int("zoom", &fov); //SoM: BAKWARDS COMPATABILITY!
4496     add_game_mobj("trigger", &fs_trigger_obj);
4497 
4498     // important C-emulating stuff
4499     new_function("break", SF_Break);
4500     new_function("continue", SF_Continue);
4501     new_function("return", SF_Return);
4502     new_function("goto", SF_Goto);
4503     new_function("include", SF_Include);
4504 
4505     // standard FraggleScript functions
4506     new_function("print", SF_Print);
4507     new_function("rnd", SF_Rnd);
4508     new_function("prnd", SF_PRnd);
4509     new_function("input", SF_Input);    // Hurdler: TODO: document this function
4510     new_function("beep", SF_Beep);
4511     new_function("clock", SF_Clock);
4512     new_function("clocktic", SF_ClockTic);
4513     new_function("wait", SF_Wait);
4514     new_function("waittic", SF_WaitTic);
4515     new_function("tagwait", SF_TagWait);
4516     new_function("scriptwait", SF_ScriptWait);
4517     new_function("startscript", SF_StartScript);
4518     new_function("scriptrunning", SF_ScriptRunning);
4519 
4520     // doom stuff
4521     new_function("startskill", SF_StartSkill);
4522     new_function("exitlevel", SF_ExitLevel);
4523     new_function("warp", SF_Warp);
4524     new_function("tip", SF_Tip);
4525     new_function("timedtip", SF_TimedTip);
4526     new_function("message", SF_Message);
4527     new_function("gameskill", SF_GameSkill);
4528     new_function("gamemode", SF_GameMode);      // SoM Request SSNTails 06-13-2002
4529 
4530     // player stuff
4531     new_function("playermsg", SF_PlayerMsg);
4532     new_function("playertip", SF_PlayerTip);
4533     new_function("playeringame", SF_PlayerInGame);
4534     new_function("playername", SF_PlayerName);
4535     new_function("playeraddfrag", SF_PlayerAddFrag);
4536     new_function("playerobj", SF_PlayerObj);
4537     new_function("isobjplayer", SF_MobjIsPlayer);
4538     new_function("isplayerobj", SF_MobjIsPlayer);       // Hurdler: due to backward and eternity compatibility
4539     new_function("skincolor", SF_SkinColor);
4540     new_function("playerkeys", SF_PlayerKeys);
4541     new_function("playerkeysb", SF_PlayerKeysByte);
4542     new_function("playerarmor", SF_PlayerArmor);
4543     new_function("playerammo", SF_PlayerAmmo);
4544     new_function("maxplayerammo", SF_MaxPlayerAmmo);
4545     new_function("playerweapon", SF_PlayerWeapon);
4546     new_function("playerselwep", SF_PlayerSelectedWeapon);
4547     new_function("playerpitch", SF_PlayerPitch);
4548     new_function("playerproperty", SF_PlayerProperty);
4549 
4550     // mobj stuff
4551     new_function("spawn", SF_Spawn);
4552     new_function("spawnexplosion", SF_SpawnExplosion);
4553     new_function("radiusattack", SF_RadiusAttack);
4554     new_function("kill", SF_KillObj);
4555     new_function("removeobj", SF_RemoveObj);
4556     new_function("objx", SF_ObjX);
4557     new_function("objy", SF_ObjY);
4558     new_function("objz", SF_ObjZ);
4559     new_function("testlocation", SF_TestLocation);
4560     new_function("teleport", SF_Teleport);
4561     new_function("silentteleport", SF_SilentTeleport);
4562     new_function("damageobj", SF_DamageObj);
4563     new_function("healobj", SF_HealObj);
4564     new_function("player", SF_Player);
4565     new_function("objsector", SF_ObjSector);
4566     new_function("objflag", SF_ObjFlag);
4567     new_function("objflag2", SF_ObjFlag2);
4568     new_function("objeflag", SF_ObjEFlag);
4569     new_function("pushobj", SF_PushThing);
4570     new_function("pushthing", SF_PushThing);    // Hurdler: due to backward and eternity compatibility
4571     new_function("objangle", SF_ObjAngle);
4572     new_function("checksight", SF_CheckSight);
4573     new_function("objhealth", SF_ObjHealth);
4574     new_function("objdead", SF_ObjDead);
4575     new_function("objreactiontime", SF_ReactionTime);
4576     new_function("reactiontime", SF_ReactionTime);      // Hurdler: due to backward and eternity compatibility
4577     new_function("objtarget", SF_MobjTarget);
4578     new_function("objmomx", SF_MobjMomx);
4579     new_function("objmomy", SF_MobjMomy);
4580     new_function("objmomz", SF_MobjMomz);
4581     new_function("spawnmissile", SF_SpawnMissile);
4582     new_function("mapthings", SF_Mapthings);
4583     new_function("objtype", SF_ObjType);
4584     new_function("mapthingnumexist", SF_MapthingNumExist);
4585     new_function("objstate", SF_ObjState);
4586     new_function("resurrect", SF_Resurrect);
4587     new_function("lineattack", SF_LineAttack);
4588     new_function("setobjposition", SF_SetObjPosition);
4589     new_function("setobjproperty", SF_SetObjProperty);
4590     new_function("getobjproperty", SF_GetObjProperty);
4591     new_function("setnodenext", SF_SetNodeNext);
4592     new_function("setnodewait", SF_SetNodePause);
4593     new_function("setnodescript", SF_SetNodeScript);
4594 
4595     // sector stuff
4596     new_function("sectoreffect", SF_SectorEffect);
4597     new_function("floorheight", SF_FloorHeight);
4598     new_function("floortext", SF_FloorTexture);
4599     new_function("floortexture", SF_FloorTexture);      // Hurdler: due to backward and eternity compatibility
4600     new_function("movefloor", SF_MoveFloor);
4601     new_function("ceilheight", SF_CeilingHeight);
4602     new_function("ceilingheight", SF_CeilingHeight);    // Hurdler: due to backward and eternity compatibility
4603     new_function("moveceil", SF_MoveCeiling);
4604     new_function("moveceiling", SF_MoveCeiling);        // Hurdler: due to backward and eternity compatibility
4605     new_function("ceiltext", SF_CeilingTexture);
4606     new_function("ceilingtexture", SF_CeilingTexture);  // Hurdler: due to backward and eternity compatibility
4607     new_function("lightlevel", SF_LightLevel);
4608     new_function("fadelight", SF_FadeLight);
4609     new_function("colormap", SF_SectorColormap);
4610 
4611     // cameras!
4612     new_function("setcamera", SF_SetCamera);
4613     new_function("clearcamera", SF_ClearCamera);
4614     new_function("movecamera", SF_MoveCamera);
4615 
4616     // trig functions
4617     new_function("pointtoangle", SF_PointToAngle);
4618     new_function("pointtodist", SF_PointToDist);
4619 
4620     // sound functions
4621     new_function("startsound", SF_StartSound);
4622     new_function("startsectorsound", SF_StartSectorSound);
4623     new_function("startambiantsound", SF_AmbiantSound);
4624     new_function("ambientsound", SF_AmbiantSound);      // Hurdler: due to backward and eternity compatibility
4625     new_function("changemusic", SF_ChangeMusic);
4626 
4627     // hubs!
4628     new_function("changehublevel", SF_ChangeHubLevel);  // Hurdler: TODO: document this function
4629 
4630     // doors
4631     new_function("opendoor", SF_OpenDoor);
4632     new_function("closedoor", SF_CloseDoor);
4633 
4634     new_function("playdemo", SF_PlayDemo);
4635     new_function("runcommand", SF_RunCommand);
4636     new_function("checkcvar", SF_CheckCVar);
4637     new_function("setlinetexture", SF_SetLineTexture);
4638     new_function("linetrigger", SF_LineTrigger);
4639     new_function("lineflag", SF_LineFlag);
4640 
4641     new_function("max", SF_Max);
4642     new_function("min", SF_Min);
4643     new_function("abs", SF_Abs);
4644 
4645     //Hurdler: new math functions
4646     new_function("sin", SF_Sin);
4647     new_function("asin", SF_ASin);
4648     new_function("cos", SF_Cos);
4649     new_function("acos", SF_ACos);
4650     new_function("tan", SF_Tan);
4651     new_function("atan", SF_ATan);
4652     new_function("exp", SF_Exp);
4653     new_function("log", SF_Log);
4654     new_function("sqrt", SF_Sqrt);
4655     new_function("floor", SF_Floor);
4656     new_function("pow", SF_Pow);
4657 
4658     // forced coercion functions
4659     new_function("mobjvalue", SF_MobjValue);
4660     new_function("stringvalue", SF_StringValue);
4661     new_function("intvalue", SF_IntValue);
4662     new_function("fixedvalue", SF_FixedValue);
4663 
4664     // HU Graphics
4665     new_function("newhupic", SF_NewHUPic);
4666     new_function("createpic", SF_NewHUPic);
4667     new_function("deletehupic", SF_DeleteHUPic);
4668     new_function("modifyhupic", SF_ModifyHUPic);
4669     new_function("modifypic", SF_ModifyHUPic);
4670     new_function("sethupicdisplay", SF_SetHUPicDisplay);
4671     new_function("setpicvisible", SF_SetHUPicDisplay);
4672 
4673     // Arrays
4674     new_function("newarray", SF_NewArray);
4675     new_function("newemptyarray", SF_NewEmptyArray);
4676     new_function("copyinto", SF_ArrayCopyInto);
4677     new_function("elementat", SF_ArrayElementAt);
4678     new_function("setelementat", SF_ArraySetElementAt);
4679     new_function("length", SF_ArrayLength);
4680 
4681     // Hurdler's stuff :)
4682 #ifdef HWRENDER
4683     new_function("setcorona", SF_SetCorona);
4684     new_function("setfade", SF_SetFade);
4685 #endif
4686 }
4687 
4688