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 = §ors[secnum];
2916 S_StartXYZSoundName(§or->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 = §ors[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 = §ors[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 = §ors[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 = §ors[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 = §ors[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 = §ors[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 = §ors[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 = §ors[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 = §ors[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 = §ors[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