1 // Emacs style mode select -*- C++ -*-
2 //----------------------------------------------------------------------------
3 //
4 // $Id: t_script.c 1474 2019-10-15 12:34:14Z wesleyjohnson $
5 //
6 // Copyright(C) 2000 Simon Howard
7 // Copyright (C) 2001-2011 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_script.c,v $
24 // Revision 1.3 2004/09/17 23:04:49 darkwolf95
25 // playerkeysb (see comment), waittic and clocktic
26 //
27 // Revision 1.2 2001/03/13 22:14:20 stroggonmeth
28 // Long time no commit. 3D floors, FraggleScript, portals, ect.
29 //
30 // Revision 1.1 2000/11/02 17:57:28 stroggonmeth
31 // FraggleScript files...
32 //
33 //
34 //--------------------------------------------------------------------------
35 //
36 // scripting.
37 //
38 // delayed scripts, running scripts, console cmds etc in here
39 // the interface between FraggleScript and the rest of the game
40 //
41 // By Simon Howard
42 //
43 //----------------------------------------------------------------------------
44
45 #include "doomincl.h"
46 #include "doomstat.h"
47 #include "command.h"
48 //#include "c_net.h"
49 //#include "c_runcmd.h"
50 #include "g_game.h"
51 #include "r_state.h"
52 #include "p_info.h"
53 #include "p_mobj.h"
54 #include "p_spec.h"
55 #include "p_setup.h"
56 #include "w_wad.h"
57 #include "z_zone.h"
58
59 #include "t_script.h"
60 #include "t_parse.h"
61 #include "t_vari.h"
62 #include "t_func.h"
63
64 void clear_runningscripts( void );
65
66 // script tree:
67 //
68 // global_script
69 // / \.
70 // hubscript thingscript
71 // / \ / \.
72 // levelscript [levelscript] ... scripts ...
73 // / \ / \.
74 // ... scripts... ... scripts ...
75 //
76
77 // the level script is just the stuff put in the wad,
78 // which the other scripts are derivatives of
79 script_t fs_levelscript;
80
81
82
83 // the thing script
84 //script_t thingscript;
85
86 // the individual scripts
87 //script_t *scripts[MAXSCRIPTS]; // the scripts
88
89 runningscript_t fs_runningscripts; // first in chain
90
91 mobj_t * fs_run_trigger; // the trigger parameter on RunScript
92
93 // T_Init()
94 //
95 // called at program start
96
T_Init_FS(void)97 void T_Init_FS( void )
98 {
99 T_Init_variables();
100 T_Init_functions();
101 }
102
103 //
104 // T_Clear_Scripts()
105 //
106 // called at level start, clears all scripts
107 //
T_Clear_Scripts(void)108 void T_Clear_Scripts( void )
109 {
110 int i;
111
112 // stop runningscripts
113 clear_runningscripts();
114
115 // clear the levelscript
116 fs_levelscript.data = Z_Malloc(5, PU_LEVEL, 0); // empty data
117 fs_levelscript.data[0] = '\0';
118
119 fs_levelscript.scriptnum = -1;
120 fs_levelscript.parent = &hub_script;
121
122 // clear levelscript variables
123
124 for(i=0; i<VARIABLESLOTS; i++)
125 {
126 fs_levelscript.variables[i] = NULL;
127 }
128 }
129
T_LoadThingScript(void)130 void T_LoadThingScript( void )
131 {
132 /* char *scriptlump;
133 lumpnum_t lumpnum;
134 int lumplen;
135
136 if(thingscript.data)
137 Z_Free(thingscript.data);
138
139 // load lump into thingscript.data
140
141 // get lumpnum, lumplen
142
143 lumpnum = W_CheckNumForName("THINGSCR");
144 if( ! VALID_LUMP(lumpnum) )
145 return;
146
147 lumplen = W_LumpLength(lumpnum);
148
149 // alloc space for lump and copy lump data into it
150
151 thingscript.data = Z_Malloc(lumplen+10, PU_STATIC, 0);
152 scriptlump = W_CacheLumpNum(lumpnum, PU_CACHE);
153
154 memcpy(thingscript.data, scriptlump, lumplen);
155
156 // add '\0' to end of string
157
158 thingscript.data[lumplen] = '\0';
159
160 // preprocess script
161
162 preprocess(&thingscript);
163
164 // run script
165
166 thingscript.trigger = players[0].mo;
167 run_script(&thingscript); */
168 }
169
170
171
T_PreprocessScripts(void)172 void T_PreprocessScripts( void )
173 {
174 // run the levelscript first
175 // get the other scripts
176
177 // levelscript started by player 0 'superplayer'
178 fs_levelscript.trigger = players[0].mo;
179
180 preprocess(&fs_levelscript);
181 run_script(&fs_levelscript);
182
183 // load and run the thing script
184 T_LoadThingScript();
185 }
186
187
188
T_RunScript(int scriptnum,mobj_t * t_trigger)189 void T_RunScript(int scriptnum, mobj_t * t_trigger )
190 {
191 script_t *script;
192
193 if(scriptnum<0 || scriptnum>=MAXSCRIPTS) return;
194
195 // use the level's child script[ scriptnum ]
196 script = fs_levelscript.children[scriptnum];
197 if(!script) return;
198
199 fs_run_trigger = t_trigger;
200 script->trigger = t_trigger; // save trigger in script
201
202 run_script(script);
203 }
204
205
206 #if 0
207 // T_RunThingScript:
208 // identical to T_RunScript but runs a script
209 // from the thingscript list rather than the
210 // levelscript list
211
212 void T_RunThingScript(int n, mobj_t * t_trigger )
213 {
214 /* script_t *script;
215
216 if(scriptnum<0 || scriptnum>=MAXSCRIPTS) return;
217
218 // use the things's script[ scriptnum ]
219 script = thingscript.children[scriptnum];
220 if(!script) return;
221
222 fs_run_trigger = t_trigger;
223 script->trigger = t_trigger; // save trigger in script
224
225 run_script(script);*/
226 }
227 #endif
228
229
230
231 // console scripting debugging commands
232
COM_T_DumpScript_f(void)233 void COM_T_DumpScript_f (void)
234 {
235 script_t *script;
236
237 if(COM_Argc() < 2)
238 {
239 CONS_Printf("usage: T_DumpScript <scriptnum>\n");
240 return;
241 }
242
243 if(!strcmp(COM_Argv(1), "global"))
244 script = &fs_levelscript;
245 else
246 script = fs_levelscript.children[atoi(COM_Argv(1))];
247
248 if(!script)
249 {
250 CONS_Printf("script '%s' not defined.\n", COM_Argv(1));
251 return;
252 }
253
254 CONS_Printf("%s\n", script->data);
255 }
256
257
258
COM_T_RunScript_f(void)259 void COM_T_RunScript_f (void)
260 {
261 int sn;
262
263 if(COM_Argc() < 2)
264 {
265 CONS_Printf("Usage: T_RunScript <script>\n");
266 return;
267 }
268
269 sn = atoi(COM_Argv(1));
270
271 if(!fs_levelscript.children[sn])
272 {
273 CONS_Printf("script not defined\n");
274 return;
275 }
276
277 T_RunScript(sn, consoleplayer_ptr->mo );
278 }
279
280
281
282 /************************
283 PAUSING SCRIPTS
284 ************************/
285
286 runningscript_t *freelist=NULL; // maintain a freelist for speed
287
288 // Does not return NULL
new_runningscript(void)289 runningscript_t * new_runningscript( void )
290 {
291 // check the freelist
292 if(freelist)
293 {
294 runningscript_t *returnv=freelist;
295 freelist = freelist->next;
296 return returnv;
297 }
298
299 // alloc static: can be used in other levels too
300 return Z_Malloc(sizeof(runningscript_t), PU_STATIC, 0);
301 }
302
303
304
free_runningscript(runningscript_t * runscr)305 static void free_runningscript(runningscript_t *runscr)
306 {
307 // add to freelist
308 runscr->next = freelist;
309 freelist = runscr;
310 }
311
312
313 // Return true when finished, false while waiting
wait_finished(runningscript_t * script)314 static boolean wait_finished(runningscript_t *script)
315 {
316 switch(script->wait_type)
317 {
318 case WT_none: break; // always finished
319 case WT_scriptwait: // waiting for script to finish
320 {
321 runningscript_t *current;
322 for(current = fs_runningscripts.next; current; current = current->next)
323 {
324 if(current == script) continue; // ignore this script
325 if(current->script->scriptnum == script->wait_data)
326 goto ret_wait; // script still running
327 }
328 }
329 break; // finished
330
331 case WT_delay: // just count down
332 {
333 return --script->wait_data <= 0;
334 }
335
336 case WT_tagwait:
337 {
338 int secnum = -1;
339
340 while ((secnum = P_FindSectorFromTag(script->wait_data, secnum)) >= 0)
341 {
342 sector_t *sec = §ors[secnum];
343 if(sec->floordata || sec->ceilingdata || sec->lightingdata)
344 goto ret_wait; // not finished
345 }
346 }
347 break; // finished
348
349 default: break;
350 }
351 return true; // default finished
352
353 ret_wait:
354 return false; // waiting
355 }
356
357
358
359
T_DelayedScripts(void)360 void T_DelayedScripts( void )
361 {
362 runningscript_t *current, *next;
363 int i;
364
365 if(!info_scripts) return; // no level scripts
366
367 current = fs_runningscripts.next;
368
369 while(current)
370 {
371 if(wait_finished(current))
372 {
373 // copy out the script variables from the
374 // runningscript_t
375
376 for(i=0; i<VARIABLESLOTS; i++)
377 current->script->variables[i] = current->variables[i];
378 current->script->trigger = current->trigger; // copy trigger
379
380 // continue the script
381
382 continue_script(current->script, current->savepoint);
383
384 // unhook from chain and free
385
386 current->prev->next = current->next;
387 if(current->next) current->next->prev = current->prev;
388 next = current->next; // save before freeing
389 free_runningscript(current);
390 }
391 else
392 next = current->next;
393 current = next; // continue to next in chain
394 }
395
396 }
397
398
T_SaveCurrentScript(void)399 static runningscript_t * T_SaveCurrentScript( void )
400 {
401 runningscript_t *runscr;
402 int i;
403
404 runscr = new_runningscript();
405 runscr->script = fs_current_script;
406 runscr->savepoint = fs_src_cp;
407
408 // leave to other functions to set wait_type: default to WT_none
409 runscr->wait_type = WT_none;
410
411 // hook into chain at start
412 runscr->next = fs_runningscripts.next;
413 runscr->prev = &fs_runningscripts;
414 runscr->prev->next = runscr;
415 if(runscr->next)
416 runscr->next->prev = runscr;
417
418 // save the script variables
419 for(i=0; i<VARIABLESLOTS; i++)
420 {
421 runscr->variables[i] = fs_current_script->variables[i];
422
423 // remove all the variables from the script variable list
424 // to prevent them being removed when the script stops
425
426 while(fs_current_script->variables[i]
427 && fs_current_script->variables[i]->type != FSVT_label)
428 {
429 fs_current_script->variables[i] =
430 fs_current_script->variables[i]->next;
431 }
432 }
433 runscr->trigger = fs_current_script->trigger; // save trigger
434
435 fs_killscript = true; // stop the script
436
437 return runscr;
438 }
439
440
441
442
443 // script function
SF_Wait(void)444 void SF_Wait( void )
445 {
446 runningscript_t *runscr;
447
448 if(t_argc != 1) goto err_numarg;
449
450 runscr = T_SaveCurrentScript();
451
452 runscr->wait_type = WT_delay;
453 runscr->wait_data = (intvalue(t_argv[0]) * 35) / 100;
454 done:
455 return;
456
457 err_numarg:
458 wrong_num_arg( "Wait", 1);
459 goto done;
460 }
461
462 //if you want to wait on tics instead of "real" time
SF_WaitTic(void)463 void SF_WaitTic( void )
464 {
465 runningscript_t *runscr;
466
467 if(t_argc != 1) goto err_numarg;
468
469 runscr = T_SaveCurrentScript();
470
471 runscr->wait_type = WT_delay;
472 runscr->wait_data = intvalue(t_argv[0]);
473 done:
474 return;
475
476 err_numarg:
477 wrong_num_arg( "WaitTic", 1);
478 goto done;
479 }
480
481 // wait for sector with particular tag to stop moving
SF_TagWait(void)482 void SF_TagWait( void )
483 {
484 runningscript_t *runscr;
485
486 if(t_argc != 1) goto err_numarg;
487
488 runscr = T_SaveCurrentScript();
489
490 runscr->wait_type = WT_tagwait;
491 runscr->wait_data = intvalue(t_argv[0]);
492 done:
493 return;
494
495 err_numarg:
496 wrong_num_arg( "TagWait", 1);
497 goto done;
498 }
499
500
501
502
503 // wait for a script to finish
SF_ScriptWait(void)504 void SF_ScriptWait( void )
505 {
506 runningscript_t *runscr;
507
508 if(t_argc != 1) goto err_numarg;
509
510 runscr = T_SaveCurrentScript();
511
512 runscr->wait_type = WT_scriptwait;
513 runscr->wait_data = intvalue(t_argv[0]);
514 done:
515 return;
516
517 err_numarg:
518 wrong_num_arg( "ScriptWait", 1);
519 goto done;
520 }
521
522
523
524
525 //extern mobj_t * fs_trigger_obj; // in t_func.c
526
SF_StartScript(void)527 void SF_StartScript( void )
528 {
529 runningscript_t *runscr;
530 script_t *script;
531 int i, snum;
532
533 if(t_argc != 1) goto err_numarg;
534
535 snum = intvalue(t_argv[0]);
536 script = fs_levelscript.children[snum];
537 if(!script) goto err_noscript;
538
539 runscr = new_runningscript();
540 runscr->script = script;
541 runscr->savepoint = script->data; // start at beginning
542 runscr->wait_type = WT_none; // start straight away
543
544 // hook into chain at start
545
546 runscr->next = fs_runningscripts.next;
547 runscr->prev = &fs_runningscripts;
548 runscr->prev->next = runscr;
549 if(runscr->next)
550 runscr->next->prev = runscr;
551
552 // save the script variables
553 for(i=0; i<VARIABLESLOTS; i++)
554 {
555 runscr->variables[i] = script->variables[i];
556
557 // in case we are starting another current_script:
558 // remove all the variables from the script variable list
559 // we only start with the basic labels
560 while(runscr->variables[i] &&
561 runscr->variables[i]->type != FSVT_label)
562 runscr->variables[i] =
563 runscr->variables[i]->next;
564 }
565 // copy trigger
566 runscr->trigger = fs_current_script->trigger;
567 done:
568 return;
569
570 err_numarg:
571 wrong_num_arg( "StartScript", 1);
572 goto done;
573
574 err_noscript:
575 script_error("StartScript: script %i not defined\n", snum);
576 goto done;
577 }
578
579
580
581 // int ScriptRunning( int scriptnumber )
582 // Return 1 when script (scriptnumber) is running
SF_ScriptRunning(void)583 void SF_ScriptRunning( void )
584 {
585 runningscript_t *current;
586 int snum;
587
588 if(t_argc != 1) goto err_numarg;
589
590 snum = intvalue(t_argv[0]);
591
592 t_return.type = FSVT_int;
593 t_return.value.i = 0; // default, script not found
594
595 for(current=fs_runningscripts.next; current; current=current->next)
596 {
597 if(current->script->scriptnum == snum)
598 {
599 t_return.value.i = 1; // found, and running
600 goto done;
601 }
602 }
603
604 done:
605 return;
606
607 err_numarg:
608 wrong_num_arg( "ScriptRunning", 1);
609 goto done;
610 }
611
612
613
614
615 // running scripts
616
COM_T_Running_f(void)617 void COM_T_Running_f (void)
618 {
619 runningscript_t *current;
620
621 current = fs_runningscripts.next;
622
623 CONS_Printf("running scripts\n");
624
625 if(!current)
626 CONS_Printf("no running scripts.\n");
627
628 while(current)
629 {
630 CONS_Printf("%i:", current->script->scriptnum);
631 switch(current->wait_type)
632 {
633 case WT_none:
634 CONS_Printf("waiting for nothing?\n");
635 break;
636 case WT_delay:
637 CONS_Printf("delay %i tics\n", current->wait_data);
638 break;
639 case WT_tagwait:
640 CONS_Printf("waiting for tag %i\n", current->wait_data);
641 break;
642 case WT_scriptwait:
643 CONS_Printf("waiting for script %i\n", current->wait_data);
644 break;
645 default:
646 CONS_Printf("unknown wait type \n");
647 break;
648 }
649 current = current->next;
650 }
651 }
652
653
654
655
clear_runningscripts(void)656 void clear_runningscripts( void )
657 {
658 runningscript_t *runscr, *next;
659
660 runscr = fs_runningscripts.next;
661
662 // free the whole chain
663 while(runscr)
664 {
665 next = runscr->next;
666 free_runningscript(runscr);
667 runscr = next;
668 }
669 fs_runningscripts.next = NULL;
670 }
671
672
MobjForSvalue(fs_value_t svalue)673 mobj_t * MobjForSvalue(fs_value_t svalue)
674 {
675 int intval;
676
677 if(svalue.type == FSVT_mobj)
678 return svalue.value.mobj;
679
680 // this requires some creativity. We use the intvalue
681 // as the thing number of a thing in the level.
682
683 intval = intvalue(svalue);
684 if(intval < 0 || intval >= nummapthings || !mapthings[intval].mobj)
685 goto err_mapthing;
686
687 return mapthings[intval].mobj;
688
689 err_mapthing:
690 script_error("no levelthing %i\n", intval);
691 return NULL;
692 }
693
694
695
696
697
698 /*********************
699 ADD SCRIPT
700 *********************/
701
702 // when the level is first loaded, all the
703 // scripts are simply stored in the levelscript.
704 // before the level starts, this script is
705 // preprocessed and run like any other. This allows
706 // the individual scripts to be derived from the
707 // levelscript. When the interpreter detects the
708 // 'script' keyword this function is called
709
spec_script(void)710 void spec_script( void )
711 {
712 int scriptnum;
713 int datasize;
714 script_t *script;
715
716 if(!fs_current_section) goto err_nosection;
717
718 // presume that the first token is "script"
719 if(num_tokens < 2) goto err_numtokens;
720
721 scriptnum = intvalue(evaluate_expression(1, num_tokens-1));
722 if(scriptnum < 0) goto err_scriptnum;
723
724 script = Z_Malloc(sizeof(script_t), PU_LEVEL, 0);
725
726 // add to scripts list of parent
727 fs_current_script->children[scriptnum] = script;
728
729 // copy script data
730 // workout script size: -2 to ignore { and }
731 datasize = fs_current_section->end - fs_current_section->start - 2;
732
733 // alloc extra 10 for safety
734 script->data = Z_Malloc(datasize+10, PU_LEVEL, 0);
735
736 // copy from parent script (fs_levelscript)
737 // ignore first char which is {
738 memcpy(script->data, fs_current_section->start+1, datasize);
739 // tack on a 0 to end the string
740 script->data[datasize] = '\0';
741
742 script->scriptnum = scriptnum;
743 script->parent = fs_current_script; // remember parent
744
745 // preprocess the script now
746 preprocess(script);
747
748 // restore current_script: usefully stored in new script
749 fs_current_script = script->parent;
750
751 // fs_src_cp may also be changed, but is changed below anyway
752
753 // we dont want to run the script, only add it
754 // jump past the script in parsing
755 fs_src_cp = fs_current_section->end + 1;
756 done:
757 return;
758
759 err_nosection:
760 script_error("need separators for script\n");
761 goto done;
762
763 err_numtokens:
764 script_error("need script number\n");
765 goto done;
766
767 err_scriptnum:
768 script_error("invalid script number\n");
769 goto done;
770 }
771
772
773
774
775 /****** scripting command list *******/
776
T_Register_Commands(void)777 void T_Register_Commands( void )
778 {
779 #ifdef FRAGGLESCRIPT
780 COM_AddCommand("fs_dumpscript", COM_T_DumpScript_f, CC_fs);
781 COM_AddCommand("fs_runscript", COM_T_RunScript_f, CC_fs);
782 COM_AddCommand("fs_running", COM_T_Running_f, CC_fs);
783 // for old wads with bind keys (like Chex newmaps)
784 COM_AddCommand("t_runscript", COM_T_RunScript_f, CC_fs);
785 COM_AddCommand("t_running", COM_T_Running_f, CC_fs);
786 // COM_AddCommand("t_dumpscript", COM_T_DumpScript_f, CC_fs);
787 #endif
788 }
789
790