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 = &sectors[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