1 /** @file interpreter.cpp  Action Code Script (ACS), interpreter.
2  *
3  * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  * @authors Copyright © 2005-2015 Daniel Swanson <danij@dengine.net>
5  * @authors Copyright © 1999 Activision
6  *
7  * @par License
8  * GPL: http://www.gnu.org/licenses/gpl.html
9  *
10  * <small>This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by the
12  * Free Software Foundation; either version 2 of the License, or (at your
13  * option) any later version. This program is distributed in the hope that it
14  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16  * Public License for more details. You should have received a copy of the GNU
17  * General Public License along with this program; if not, write to the Free
18  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19  * 02110-1301 USA</small>
20  */
21 
22 #include "acs/interpreter.h"
23 
24 #include <de/Log>
25 #include "acs/system.h"
26 #include "dmu_lib.h"
27 #include "g_common.h"
28 #include "gamesession.h"
29 #include "player.h"
30 #include "p_map.h"
31 #include "p_saveg.h"
32 #include "p_saveio.h"
33 #include "p_sound.h"
34 
35 using namespace de;
36 
37 int acs::Interpreter::currentScriptNumber = -1;
38 
39 namespace internal
40 {
41     /// Status to return from ACScript command functions.
42     enum CommandResult
43     {
44         Continue,
45         Stop,
46         Terminate
47     };
48 
49     typedef CommandResult (*CommandFunc) (acs::Interpreter &);
50 
51 /// Helper macro for declaring ACScript command functions.
52 #define ACS_COMMAND(Name) CommandResult cmd##Name(acs::Interpreter &interp)
53 
54     static String printBuffer;
55 
56 #ifdef __JHEXEN__
57     static byte specArgs[5];
58 
ACS_COMMAND(NOP)59     ACS_COMMAND(NOP)
60     {
61         DENG2_UNUSED(interp);
62         return Continue;
63     }
64 
ACS_COMMAND(Terminate)65     ACS_COMMAND(Terminate)
66     {
67         DENG2_UNUSED(interp);
68         return Terminate;
69     }
70 
ACS_COMMAND(Suspend)71     ACS_COMMAND(Suspend)
72     {
73         interp.script().setState(acs::Script::Suspended);
74         return Stop;
75     }
76 
ACS_COMMAND(PushNumber)77     ACS_COMMAND(PushNumber)
78     {
79         interp.locals.push(DD_LONG(*interp.pcodePtr++));
80         return Continue;
81     }
82 
ACS_COMMAND(LSpec1)83     ACS_COMMAND(LSpec1)
84     {
85         int special = DD_LONG(*interp.pcodePtr++);
86         specArgs[0] = interp.locals.pop();
87         P_ExecuteLineSpecial(special, specArgs, interp.line, interp.side, interp.activator);
88 
89         return Continue;
90     }
91 
ACS_COMMAND(LSpec2)92     ACS_COMMAND(LSpec2)
93     {
94         int special = DD_LONG(*interp.pcodePtr++);
95         specArgs[1] = interp.locals.pop();
96         specArgs[0] = interp.locals.pop();
97         P_ExecuteLineSpecial(special, specArgs, interp.line, interp.side, interp.activator);
98 
99         return Continue;
100     }
101 
ACS_COMMAND(LSpec3)102     ACS_COMMAND(LSpec3)
103     {
104         int special = DD_LONG(*interp.pcodePtr++);
105         specArgs[2] = interp.locals.pop();
106         specArgs[1] = interp.locals.pop();
107         specArgs[0] = interp.locals.pop();
108         P_ExecuteLineSpecial(special, specArgs, interp.line, interp.side, interp.activator);
109 
110         return Continue;
111     }
112 
ACS_COMMAND(LSpec4)113     ACS_COMMAND(LSpec4)
114     {
115         int special = DD_LONG(*interp.pcodePtr++);
116         specArgs[3] = interp.locals.pop();
117         specArgs[2] = interp.locals.pop();
118         specArgs[1] = interp.locals.pop();
119         specArgs[0] = interp.locals.pop();
120         P_ExecuteLineSpecial(special, specArgs, interp.line, interp.side, interp.activator);
121 
122         return Continue;
123     }
124 
ACS_COMMAND(LSpec5)125     ACS_COMMAND(LSpec5)
126     {
127         int special = DD_LONG(*interp.pcodePtr++);
128         specArgs[4] = interp.locals.pop();
129         specArgs[3] = interp.locals.pop();
130         specArgs[2] = interp.locals.pop();
131         specArgs[1] = interp.locals.pop();
132         specArgs[0] = interp.locals.pop();
133         P_ExecuteLineSpecial(special, specArgs, interp.line, interp.side,
134                              interp.activator);
135 
136         return Continue;
137     }
138 
ACS_COMMAND(LSpec1Direct)139     ACS_COMMAND(LSpec1Direct)
140     {
141         int special = DD_LONG(*interp.pcodePtr++);
142         specArgs[0] = DD_LONG(*interp.pcodePtr++);
143         P_ExecuteLineSpecial(special, specArgs, interp.line, interp.side,
144                              interp.activator);
145 
146         return Continue;
147     }
148 
ACS_COMMAND(LSpec2Direct)149     ACS_COMMAND(LSpec2Direct)
150     {
151         int special = DD_LONG(*interp.pcodePtr++);
152         specArgs[0] = DD_LONG(*interp.pcodePtr++);
153         specArgs[1] = DD_LONG(*interp.pcodePtr++);
154         P_ExecuteLineSpecial(special, specArgs, interp.line, interp.side,
155                              interp.activator);
156 
157         return Continue;
158     }
159 
ACS_COMMAND(LSpec3Direct)160     ACS_COMMAND(LSpec3Direct)
161     {
162         int special = DD_LONG(*interp.pcodePtr++);
163         specArgs[0] = DD_LONG(*interp.pcodePtr++);
164         specArgs[1] = DD_LONG(*interp.pcodePtr++);
165         specArgs[2] = DD_LONG(*interp.pcodePtr++);
166         P_ExecuteLineSpecial(special, specArgs, interp.line, interp.side,
167                              interp.activator);
168 
169         return Continue;
170     }
171 
ACS_COMMAND(LSpec4Direct)172     ACS_COMMAND(LSpec4Direct)
173     {
174         int special = DD_LONG(*interp.pcodePtr++);
175         specArgs[0] = DD_LONG(*interp.pcodePtr++);
176         specArgs[1] = DD_LONG(*interp.pcodePtr++);
177         specArgs[2] = DD_LONG(*interp.pcodePtr++);
178         specArgs[3] = DD_LONG(*interp.pcodePtr++);
179         P_ExecuteLineSpecial(special, specArgs, interp.line, interp.side,
180                              interp.activator);
181 
182         return Continue;
183     }
184 
ACS_COMMAND(LSpec5Direct)185     ACS_COMMAND(LSpec5Direct)
186     {
187         int special = DD_LONG(*interp.pcodePtr++);
188         specArgs[0] = DD_LONG(*interp.pcodePtr++);
189         specArgs[1] = DD_LONG(*interp.pcodePtr++);
190         specArgs[2] = DD_LONG(*interp.pcodePtr++);
191         specArgs[3] = DD_LONG(*interp.pcodePtr++);
192         specArgs[4] = DD_LONG(*interp.pcodePtr++);
193         P_ExecuteLineSpecial(special, specArgs, interp.line, interp.side,
194                              interp.activator);
195 
196         return Continue;
197     }
198 
ACS_COMMAND(Add)199     ACS_COMMAND(Add)
200     {
201         interp.locals.push(interp.locals.pop() + interp.locals.pop());
202         return Continue;
203     }
204 
ACS_COMMAND(Subtract)205     ACS_COMMAND(Subtract)
206     {
207         int operand2 = interp.locals.pop();
208         interp.locals.push(interp.locals.pop() - operand2);
209         return Continue;
210     }
211 
ACS_COMMAND(Multiply)212     ACS_COMMAND(Multiply)
213     {
214         interp.locals.push(interp.locals.pop() * interp.locals.pop());
215         return Continue;
216     }
217 
ACS_COMMAND(Divide)218     ACS_COMMAND(Divide)
219     {
220         int operand2 = interp.locals.pop();
221         interp.locals.push(interp.locals.pop() / operand2);
222         return Continue;
223     }
224 
ACS_COMMAND(Modulus)225     ACS_COMMAND(Modulus)
226     {
227         int operand2 = interp.locals.pop();
228         interp.locals.push(interp.locals.pop() % operand2);
229         return Continue;
230     }
231 
ACS_COMMAND(EQ)232     ACS_COMMAND(EQ)
233     {
234         interp.locals.push(interp.locals.pop() == interp.locals.pop());
235         return Continue;
236     }
237 
ACS_COMMAND(NE)238     ACS_COMMAND(NE)
239     {
240         interp.locals.push(interp.locals.pop() != interp.locals.pop());
241         return Continue;
242     }
243 
ACS_COMMAND(LT)244     ACS_COMMAND(LT)
245     {
246         int operand2 = interp.locals.pop();
247         interp.locals.push(interp.locals.pop() < operand2);
248         return Continue;
249     }
250 
ACS_COMMAND(GT)251     ACS_COMMAND(GT)
252     {
253         int operand2 = interp.locals.pop();
254         interp.locals.push(interp.locals.pop() > operand2);
255         return Continue;
256     }
257 
ACS_COMMAND(LE)258     ACS_COMMAND(LE)
259     {
260         int operand2 = interp.locals.pop();
261         interp.locals.push(interp.locals.pop() <= operand2);
262         return Continue;
263     }
264 
ACS_COMMAND(GE)265     ACS_COMMAND(GE)
266     {
267         int operand2 = interp.locals.pop();
268         interp.locals.push(interp.locals.pop() >= operand2);
269         return Continue;
270     }
271 
ACS_COMMAND(AssignScriptVar)272     ACS_COMMAND(AssignScriptVar)
273     {
274         interp.args[DD_LONG(*interp.pcodePtr++)] = interp.locals.pop();
275         return Continue;
276     }
277 
ACS_COMMAND(AssignMapVar)278     ACS_COMMAND(AssignMapVar)
279     {
280         interp.scriptSys().mapVars[DD_LONG(*interp.pcodePtr++)] = interp.locals.pop();
281         return Continue;
282     }
283 
ACS_COMMAND(AssignWorldVar)284     ACS_COMMAND(AssignWorldVar)
285     {
286         interp.scriptSys().worldVars[DD_LONG(*interp.pcodePtr++)] = interp.locals.pop();
287         return Continue;
288     }
289 
ACS_COMMAND(PushScriptVar)290     ACS_COMMAND(PushScriptVar)
291     {
292         interp.locals.push(interp.args[DD_LONG(*interp.pcodePtr++)]);
293         return Continue;
294     }
295 
ACS_COMMAND(PushMapVar)296     ACS_COMMAND(PushMapVar)
297     {
298         interp.locals.push(interp.scriptSys().mapVars[DD_LONG(*interp.pcodePtr++)]);
299         return Continue;
300     }
301 
ACS_COMMAND(PushWorldVar)302     ACS_COMMAND(PushWorldVar)
303     {
304         interp.locals.push(interp.scriptSys().worldVars[DD_LONG(*interp.pcodePtr++)]);
305         return Continue;
306     }
307 
ACS_COMMAND(AddScriptVar)308     ACS_COMMAND(AddScriptVar)
309     {
310         interp.args[DD_LONG(*interp.pcodePtr++)] += interp.locals.pop();
311         return Continue;
312     }
313 
ACS_COMMAND(AddMapVar)314     ACS_COMMAND(AddMapVar)
315     {
316         interp.scriptSys().mapVars[DD_LONG(*interp.pcodePtr++)] += interp.locals.pop();
317         return Continue;
318     }
319 
ACS_COMMAND(AddWorldVar)320     ACS_COMMAND(AddWorldVar)
321     {
322         interp.scriptSys().worldVars[DD_LONG(*interp.pcodePtr++)] += interp.locals.pop();
323         return Continue;
324     }
325 
ACS_COMMAND(SubScriptVar)326     ACS_COMMAND(SubScriptVar)
327     {
328         interp.args[DD_LONG(*interp.pcodePtr++)] -= interp.locals.pop();
329         return Continue;
330     }
331 
ACS_COMMAND(SubMapVar)332     ACS_COMMAND(SubMapVar)
333     {
334         interp.scriptSys().mapVars[DD_LONG(*interp.pcodePtr++)] -= interp.locals.pop();
335         return Continue;
336     }
337 
ACS_COMMAND(SubWorldVar)338     ACS_COMMAND(SubWorldVar)
339     {
340         interp.scriptSys().worldVars[DD_LONG(*interp.pcodePtr++)] -= interp.locals.pop();
341         return Continue;
342     }
343 
ACS_COMMAND(MulScriptVar)344     ACS_COMMAND(MulScriptVar)
345     {
346         interp.args[DD_LONG(*interp.pcodePtr++)] *= interp.locals.pop();
347         return Continue;
348     }
349 
ACS_COMMAND(MulMapVar)350     ACS_COMMAND(MulMapVar)
351     {
352         interp.scriptSys().mapVars[DD_LONG(*interp.pcodePtr++)] *= interp.locals.pop();
353         return Continue;
354     }
355 
ACS_COMMAND(MulWorldVar)356     ACS_COMMAND(MulWorldVar)
357     {
358         interp.scriptSys().worldVars[DD_LONG(*interp.pcodePtr++)] *= interp.locals.pop();
359         return Continue;
360     }
361 
ACS_COMMAND(DivScriptVar)362     ACS_COMMAND(DivScriptVar)
363     {
364         interp.args[DD_LONG(*interp.pcodePtr++)] /= interp.locals.pop();
365         return Continue;
366     }
367 
ACS_COMMAND(DivMapVar)368     ACS_COMMAND(DivMapVar)
369     {
370         interp.scriptSys().mapVars[DD_LONG(*interp.pcodePtr++)] /= interp.locals.pop();
371         return Continue;
372     }
373 
ACS_COMMAND(DivWorldVar)374     ACS_COMMAND(DivWorldVar)
375     {
376         interp.scriptSys().worldVars[DD_LONG(*interp.pcodePtr++)] /= interp.locals.pop();
377         return Continue;
378     }
379 
ACS_COMMAND(ModScriptVar)380     ACS_COMMAND(ModScriptVar)
381     {
382         interp.args[DD_LONG(*interp.pcodePtr++)] %= interp.locals.pop();
383         return Continue;
384     }
385 
ACS_COMMAND(ModMapVar)386     ACS_COMMAND(ModMapVar)
387     {
388         interp.scriptSys().mapVars[DD_LONG(*interp.pcodePtr++)] %= interp.locals.pop();
389         return Continue;
390     }
391 
ACS_COMMAND(ModWorldVar)392     ACS_COMMAND(ModWorldVar)
393     {
394         interp.scriptSys().worldVars[DD_LONG(*interp.pcodePtr++)] %= interp.locals.pop();
395         return Continue;
396     }
397 
ACS_COMMAND(IncScriptVar)398     ACS_COMMAND(IncScriptVar)
399     {
400         interp.args[DD_LONG(*interp.pcodePtr++)]++;
401         return Continue;
402     }
403 
ACS_COMMAND(IncMapVar)404     ACS_COMMAND(IncMapVar)
405     {
406         interp.scriptSys().mapVars[DD_LONG(*interp.pcodePtr++)]++;
407         return Continue;
408     }
409 
ACS_COMMAND(IncWorldVar)410     ACS_COMMAND(IncWorldVar)
411     {
412         interp.scriptSys().worldVars[DD_LONG(*interp.pcodePtr++)]++;
413         return Continue;
414     }
415 
ACS_COMMAND(DecScriptVar)416     ACS_COMMAND(DecScriptVar)
417     {
418         interp.args[DD_LONG(*interp.pcodePtr++)]--;
419         return Continue;
420     }
421 
ACS_COMMAND(DecMapVar)422     ACS_COMMAND(DecMapVar)
423     {
424         interp.scriptSys().mapVars[DD_LONG(*interp.pcodePtr++)]--;
425         return Continue;
426     }
427 
ACS_COMMAND(DecWorldVar)428     ACS_COMMAND(DecWorldVar)
429     {
430         interp.scriptSys().worldVars[DD_LONG(*interp.pcodePtr++)]--;
431         return Continue;
432     }
433 
ACS_COMMAND(Goto)434     ACS_COMMAND(Goto)
435     {
436         interp.pcodePtr = (int const *) (interp.scriptSys().module().pcode().constData() + DD_LONG(*interp.pcodePtr));
437         return Continue;
438     }
439 
ACS_COMMAND(IfGoto)440     ACS_COMMAND(IfGoto)
441     {
442         if(interp.locals.pop())
443         {
444             interp.pcodePtr = (int const *) (interp.scriptSys().module().pcode().constData() + DD_LONG(*interp.pcodePtr));
445         }
446         else
447         {
448             interp.pcodePtr++;
449         }
450         return Continue;
451     }
452 
ACS_COMMAND(Drop)453     ACS_COMMAND(Drop)
454     {
455         interp.locals.drop();
456         return Continue;
457     }
458 
ACS_COMMAND(Delay)459     ACS_COMMAND(Delay)
460     {
461         interp.delayCount = interp.locals.pop();
462         return Stop;
463     }
464 
ACS_COMMAND(DelayDirect)465     ACS_COMMAND(DelayDirect)
466     {
467         interp.delayCount = DD_LONG(*interp.pcodePtr++);
468         return Stop;
469     }
470 
ACS_COMMAND(Random)471     ACS_COMMAND(Random)
472     {
473         int high = interp.locals.pop();
474         int low  = interp.locals.pop();
475         interp.locals.push(low + (P_Random() % (high - low + 1)));
476         return Continue;
477     }
478 
ACS_COMMAND(RandomDirect)479     ACS_COMMAND(RandomDirect)
480     {
481         int low  = DD_LONG(*interp.pcodePtr++);
482         int high = DD_LONG(*interp.pcodePtr++);
483         interp.locals.push(low + (P_Random() % (high - low + 1)));
484         return Continue;
485     }
486 
ACS_COMMAND(ThingCount)487     ACS_COMMAND(ThingCount)
488     {
489         int tid  = interp.locals.pop();
490         int type = interp.locals.pop();
491         // Anything to count?
492         if(type + tid)
493         {
494             interp.locals.push(P_MobjCount(type, tid));
495         }
496         return Continue;
497     }
498 
ACS_COMMAND(ThingCountDirect)499     ACS_COMMAND(ThingCountDirect)
500     {
501         int type = DD_LONG(*interp.pcodePtr++);
502         int tid  = DD_LONG(*interp.pcodePtr++);
503         // Anything to count?
504         if(type + tid)
505         {
506             interp.locals.push(P_MobjCount(type, tid));
507         }
508         return Continue;
509     }
510 
ACS_COMMAND(TagWait)511     ACS_COMMAND(TagWait)
512     {
513         interp.script().waitForSector(interp.locals.pop());
514         return Stop;
515     }
516 
ACS_COMMAND(TagWaitDirect)517     ACS_COMMAND(TagWaitDirect)
518     {
519         interp.script().waitForSector(DD_LONG(*interp.pcodePtr++));
520         return Stop;
521     }
522 
ACS_COMMAND(PolyWait)523     ACS_COMMAND(PolyWait)
524     {
525         interp.script().waitForPolyobj(interp.locals.pop());
526         return Stop;
527     }
528 
ACS_COMMAND(PolyWaitDirect)529     ACS_COMMAND(PolyWaitDirect)
530     {
531         interp.script().waitForPolyobj(DD_LONG(*interp.pcodePtr++));
532         return Stop;
533     }
534 
ACS_COMMAND(ChangeFloor)535     ACS_COMMAND(ChangeFloor)
536     {
537         AutoStr *path = Str_PercentEncode(AutoStr_FromTextStd(interp.scriptSys().module().constant(interp.locals.pop()).toUtf8().constData()));
538         uri_s *uri = Uri_NewWithPath3("Flats", Str_Text(path));
539 
540         world_Material *mat = (world_Material *) P_ToPtr(DMU_MATERIAL, Materials_ResolveUri(uri));
541         Uri_Delete(uri);
542 
543         int tag = interp.locals.pop();
544 
545         if(iterlist_t *list = P_GetSectorIterListForTag(tag, false))
546         {
547             IterList_SetIteratorDirection(list, ITERLIST_FORWARD);
548             IterList_RewindIterator(list);
549 
550             Sector *sec;
551             while((sec = (Sector *) IterList_MoveIterator(list)))
552             {
553                 P_SetPtrp(sec, DMU_FLOOR_MATERIAL, mat);
554             }
555         }
556 
557         return Continue;
558     }
559 
ACS_COMMAND(ChangeFloorDirect)560     ACS_COMMAND(ChangeFloorDirect)
561     {
562         int tag = DD_LONG(*interp.pcodePtr++);
563 
564         AutoStr *path = Str_PercentEncode(AutoStr_FromTextStd(interp.scriptSys().module().constant(DD_LONG(*interp.pcodePtr++)).toUtf8().constData()));
565         uri_s *uri = Uri_NewWithPath3("Flats", Str_Text(path));
566 
567         world_Material *mat = (world_Material *) P_ToPtr(DMU_MATERIAL, Materials_ResolveUri(uri));
568         Uri_Delete(uri);
569 
570         if(iterlist_t *list = P_GetSectorIterListForTag(tag, false))
571         {
572             IterList_SetIteratorDirection(list, ITERLIST_FORWARD);
573             IterList_RewindIterator(list);
574 
575             Sector *sec;
576             while((sec = (Sector *) IterList_MoveIterator(list)))
577             {
578                 P_SetPtrp(sec, DMU_FLOOR_MATERIAL, mat);
579             }
580         }
581 
582         return Continue;
583     }
584 
ACS_COMMAND(ChangeCeiling)585     ACS_COMMAND(ChangeCeiling)
586     {
587         AutoStr *path = Str_PercentEncode(AutoStr_FromTextStd(interp.scriptSys().module().constant(interp.locals.pop()).toUtf8().constData()));
588         uri_s *uri = Uri_NewWithPath3("Flats", Str_Text(path));
589 
590         world_Material *mat = (world_Material *) P_ToPtr(DMU_MATERIAL, Materials_ResolveUri(uri));
591         Uri_Delete(uri);
592 
593         int tag = interp.locals.pop();
594 
595         if(iterlist_t *list = P_GetSectorIterListForTag(tag, false))
596         {
597             IterList_SetIteratorDirection(list, ITERLIST_FORWARD);
598             IterList_RewindIterator(list);
599 
600             Sector *sec;
601             while((sec = (Sector *) IterList_MoveIterator(list)))
602             {
603                 P_SetPtrp(sec, DMU_CEILING_MATERIAL, mat);
604             }
605         }
606 
607         return Continue;
608     }
609 
ACS_COMMAND(ChangeCeilingDirect)610     ACS_COMMAND(ChangeCeilingDirect)
611     {
612         int tag = DD_LONG(*interp.pcodePtr++);
613 
614         AutoStr *path = Str_PercentEncode(AutoStr_FromTextStd(interp.scriptSys().module().constant(DD_LONG(*interp.pcodePtr++)).toUtf8().constData()));
615         uri_s *uri = Uri_NewWithPath3("Flats", Str_Text(path));
616 
617         world_Material *mat = (world_Material *) P_ToPtr(DMU_MATERIAL, Materials_ResolveUri(uri));
618         Uri_Delete(uri);
619 
620         if(iterlist_t *list = P_GetSectorIterListForTag(tag, false))
621         {
622             IterList_SetIteratorDirection(list, ITERLIST_FORWARD);
623             IterList_RewindIterator(list);
624 
625             Sector *sec;
626             while((sec = (Sector *) IterList_MoveIterator(list)))
627             {
628                 P_SetPtrp(sec, DMU_CEILING_MATERIAL, mat);
629             }
630         }
631 
632         return Continue;
633     }
634 
ACS_COMMAND(Restart)635     ACS_COMMAND(Restart)
636     {
637         interp.pcodePtr = interp.script().entryPoint().pcodePtr;
638         return Continue;
639     }
640 
ACS_COMMAND(AndLogical)641     ACS_COMMAND(AndLogical)
642     {
643         interp.locals.push(interp.locals.pop() && interp.locals.pop());
644         return Continue;
645     }
646 
ACS_COMMAND(OrLogical)647     ACS_COMMAND(OrLogical)
648     {
649         interp.locals.push(interp.locals.pop() || interp.locals.pop());
650         return Continue;
651     }
652 
ACS_COMMAND(AndBitwise)653     ACS_COMMAND(AndBitwise)
654     {
655         interp.locals.push(interp.locals.pop() & interp.locals.pop());
656         return Continue;
657     }
658 
ACS_COMMAND(OrBitwise)659     ACS_COMMAND(OrBitwise)
660     {
661         interp.locals.push(interp.locals.pop() | interp.locals.pop());
662         return Continue;
663     }
664 
ACS_COMMAND(EorBitwise)665     ACS_COMMAND(EorBitwise)
666     {
667         interp.locals.push(interp.locals.pop() ^ interp.locals.pop());
668         return Continue;
669     }
670 
ACS_COMMAND(NegateLogical)671     ACS_COMMAND(NegateLogical)
672     {
673         interp.locals.push(!interp.locals.pop());
674         return Continue;
675     }
676 
ACS_COMMAND(LShift)677     ACS_COMMAND(LShift)
678     {
679         int operand2 = interp.locals.pop();
680         interp.locals.push(interp.locals.pop() << operand2);
681         return Continue;
682     }
683 
ACS_COMMAND(RShift)684     ACS_COMMAND(RShift)
685     {
686         int operand2 = interp.locals.pop();
687         interp.locals.push(interp.locals.pop() >> operand2);
688         return Continue;
689     }
690 
ACS_COMMAND(UnaryMinus)691     ACS_COMMAND(UnaryMinus)
692     {
693         interp.locals.push(-interp.locals.pop());
694         return Continue;
695     }
696 
ACS_COMMAND(IfNotGoto)697     ACS_COMMAND(IfNotGoto)
698     {
699         if(interp.locals.pop())
700         {
701             interp.pcodePtr++;
702         }
703         else
704         {
705             interp.pcodePtr = (int const *) (interp.scriptSys().module().pcode().constData() + DD_LONG(*interp.pcodePtr));
706         }
707         return Continue;
708     }
709 
ACS_COMMAND(LineSide)710     ACS_COMMAND(LineSide)
711     {
712         interp.locals.push(interp.side);
713         return Continue;
714     }
715 
ACS_COMMAND(ScriptWait)716     ACS_COMMAND(ScriptWait)
717     {
718         interp.script().waitForScript(interp.locals.pop());
719         return Stop;
720     }
721 
ACS_COMMAND(ScriptWaitDirect)722     ACS_COMMAND(ScriptWaitDirect)
723     {
724         interp.script().waitForScript(DD_LONG(*interp.pcodePtr++));
725         return Stop;
726     }
727 
ACS_COMMAND(ClearLineSpecial)728     ACS_COMMAND(ClearLineSpecial)
729     {
730         if(interp.line)
731         {
732             P_ToXLine(interp.line)->special = 0;
733         }
734         return Continue;
735     }
736 
ACS_COMMAND(CaseGoto)737     ACS_COMMAND(CaseGoto)
738     {
739         if(interp.locals.top() == DD_LONG(*interp.pcodePtr++))
740         {
741             interp.pcodePtr = (int const *) (interp.scriptSys().module().pcode().constData() + DD_LONG(*interp.pcodePtr));
742             interp.locals.drop();
743         }
744         else
745         {
746             interp.pcodePtr++;
747         }
748         return Continue;
749     }
750 
ACS_COMMAND(BeginPrint)751     ACS_COMMAND(BeginPrint)
752     {
753         DENG2_UNUSED(interp);
754         printBuffer.clear();
755         return Continue;
756     }
757 
ACS_COMMAND(EndPrint)758     ACS_COMMAND(EndPrint)
759     {
760         if(interp.activator && interp.activator->player)
761         {
762             P_SetMessage(interp.activator->player, printBuffer.toUtf8().constData());
763         }
764         else
765         {
766             // Send to everybody.
767             for(int i = 0; i < MAXPLAYERS; ++i)
768             {
769                 if(players[i].plr->inGame)
770                 {
771                     P_SetMessage(&players[i], printBuffer.toUtf8().constData());
772                 }
773             }
774         }
775 
776         return Continue;
777     }
778 
ACS_COMMAND(EndPrintBold)779     ACS_COMMAND(EndPrintBold)
780     {
781         DENG2_UNUSED(interp);
782         for(int i = 0; i < MAXPLAYERS; ++i)
783         {
784             if(players[i].plr->inGame)
785             {
786                 P_SetYellowMessage(&players[i], printBuffer.toUtf8().constData());
787             }
788         }
789         return Continue;
790     }
791 
ACS_COMMAND(PrintString)792     ACS_COMMAND(PrintString)
793     {
794         printBuffer += interp.scriptSys().module().constant(interp.locals.pop());
795         return Continue;
796     }
797 
ACS_COMMAND(PrintNumber)798     ACS_COMMAND(PrintNumber)
799     {
800         printBuffer += String::number(interp.locals.pop());
801         return Continue;
802     }
803 
ACS_COMMAND(PrintCharacter)804     ACS_COMMAND(PrintCharacter)
805     {
806         char ch[2];
807         ch[0] = interp.locals.pop();
808         ch[1] = 0;
809         printBuffer += String(ch);
810         return Continue;
811     }
812 
ACS_COMMAND(PlayerCount)813     ACS_COMMAND(PlayerCount)
814     {
815         int count = 0;
816         for(int i = 0; i < MAXPLAYERS; ++i)
817         {
818             count += players[i].plr->inGame;
819         }
820         interp.locals.push(count);
821         return Continue;
822     }
823 
ACS_COMMAND(GameType)824     ACS_COMMAND(GameType)
825     {
826         int gametype;
827 
828         if(!IS_NETGAME)
829         {
830             gametype = 0; // singleplayer
831         }
832         else if(gfw_Rule(deathmatch))
833         {
834             gametype = 2; // deathmatch
835         }
836         else
837         {
838             gametype = 1; // cooperative
839         }
840         interp.locals.push(gametype);
841 
842         return Continue;
843     }
844 
ACS_COMMAND(GameSkill)845     ACS_COMMAND(GameSkill)
846     {
847         interp.locals.push((int)gfw_Rule(skill));
848         return Continue;
849     }
850 
ACS_COMMAND(Timer)851     ACS_COMMAND(Timer)
852     {
853         interp.locals.push(mapTime);
854         return Continue;
855     }
856 
ACS_COMMAND(SectorSound)857     ACS_COMMAND(SectorSound)
858     {
859         mobj_t *emitter = nullptr;
860         if(interp.line)
861         {
862             auto *sector = (Sector *) P_GetPtrp(interp.line, DMU_FRONT_SECTOR);
863             emitter = (mobj_t *) P_GetPtrp(sector, DMU_EMITTER);
864         }
865         int volume = interp.locals.pop();
866 
867         S_StartSoundAtVolume(S_GetSoundID(interp.scriptSys().module().constant(interp.locals.pop()).toUtf8().constData()),
868                              emitter, volume / 127.0f);
869         return Continue;
870     }
871 
ACS_COMMAND(ThingSound)872     ACS_COMMAND(ThingSound)
873     {
874         int volume   = interp.locals.pop();
875         int sound    = S_GetSoundID(interp.scriptSys().module().constant(interp.locals.pop()).toUtf8().constData());
876         int tid      = interp.locals.pop();
877         int searcher = -1;
878 
879         mobj_t *emitter;
880         while(sound && (emitter = P_FindMobjFromTID(tid, &searcher)))
881         {
882             S_StartSoundAtVolume(sound, emitter, volume / 127.0f);
883         }
884 
885         return Continue;
886     }
887 
ACS_COMMAND(AmbientSound)888     ACS_COMMAND(AmbientSound)
889     {
890         mobj_t *emitter = nullptr; // For 3D positioning.
891         mobj_t *plrMo   = players[DISPLAYPLAYER].plr->mo;
892 
893         int volume = interp.locals.pop();
894 
895         // If we are playing 3D sounds, create a temporary source mobj for the sound.
896         if(Con_GetInteger("sound-3d") && plrMo)
897         {
898             // SpawnMobj calls P_Random. We don't want that the random generator gets
899             // out of sync.
900             if((emitter = P_SpawnMobjXYZ(MT_CAMERA,
901                                          plrMo->origin[VX] + ((M_Random() - 127) * 2),
902                                          plrMo->origin[VY] + ((M_Random() - 127) * 2),
903                                          plrMo->origin[VZ] + ((M_Random() - 127) * 2),
904                                          0, 0)))
905             {
906                 emitter->tics = 5 * TICSPERSEC; // Five seconds should be enough.
907             }
908         }
909 
910         int sound = S_GetSoundID(interp.scriptSys().module().constant(interp.locals.pop()).toUtf8().constData());
911         S_StartSoundAtVolume(sound, emitter, volume / 127.0f);
912 
913         return Continue;
914     }
915 
ACS_COMMAND(SoundSequence)916     ACS_COMMAND(SoundSequence)
917     {
918         mobj_t *emitter = nullptr;
919         if(interp.line)
920         {
921             auto *sector = (Sector *) P_GetPtrp(interp.line, DMU_FRONT_SECTOR);
922             emitter = (mobj_t *) P_GetPtrp(sector, DMU_EMITTER);
923         }
924         SN_StartSequenceName(emitter, interp.scriptSys().module().constant(interp.locals.pop()).toUtf8().constData());
925 
926         return Continue;
927     }
928 
ACS_COMMAND(SetLineTexture)929     ACS_COMMAND(SetLineTexture)
930     {
931 #define TEXTURE_TOP 0
932 #define TEXTURE_MIDDLE 1
933 #define TEXTURE_BOTTOM 2
934 
935         AutoStr *path = Str_PercentEncode(AutoStr_FromTextStd(interp.scriptSys().module().constant(interp.locals.pop()).toUtf8().constData()));
936         uri_s *uri = Uri_NewWithPath3("Textures", Str_Text(path));
937 
938         world_Material *mat = (world_Material *) P_ToPtr(DMU_MATERIAL, Materials_ResolveUri(uri));
939         Uri_Delete(uri);
940 
941         int position = interp.locals.pop();
942         int side     = interp.locals.pop();
943         int lineTag  = interp.locals.pop();
944 
945         if(iterlist_t *list = P_GetLineIterListForTag(lineTag, false))
946         {
947             IterList_SetIteratorDirection(list, ITERLIST_FORWARD);
948             IterList_RewindIterator(list);
949 
950             Line *line;
951             while((line = (Line *) IterList_MoveIterator(list)))
952             {
953                 Side *sdef = (Side *) P_GetPtrp(line, (side == 0? DMU_FRONT : DMU_BACK));
954 
955                 if(position == TEXTURE_MIDDLE)
956                 {
957                     P_SetPtrp(sdef, DMU_MIDDLE_MATERIAL, mat);
958                 }
959                 else if(position == TEXTURE_BOTTOM)
960                 {
961                     P_SetPtrp(sdef, DMU_BOTTOM_MATERIAL, mat);
962                 }
963                 else // TEXTURE_TOP
964                 {
965                     P_SetPtrp(sdef, DMU_TOP_MATERIAL, mat);
966                 }
967             }
968         }
969 
970         return Continue;
971 
972 #undef TEXTURE_BOTTOM
973 #undef TEXTURE_MIDDLE
974 #undef TEXTURE_TOP
975     }
976 
ACS_COMMAND(SetLineBlocking)977     ACS_COMMAND(SetLineBlocking)
978     {
979         int lineFlags = interp.locals.pop()? DDLF_BLOCKING : 0;
980         int lineTag   = interp.locals.pop();
981 
982         if(iterlist_t *list = P_GetLineIterListForTag(lineTag, false))
983         {
984             IterList_SetIteratorDirection(list, ITERLIST_FORWARD);
985             IterList_RewindIterator(list);
986 
987             Line *line;
988             while((line = (Line *) IterList_MoveIterator(list)))
989             {
990                 P_SetIntp(line, DMU_FLAGS, (P_GetIntp(line, DMU_FLAGS) & ~DDLF_BLOCKING) | lineFlags);
991             }
992         }
993 
994         return Continue;
995     }
996 
ACS_COMMAND(SetLineSpecial)997     ACS_COMMAND(SetLineSpecial)
998     {
999         int arg5    = interp.locals.pop();
1000         int arg4    = interp.locals.pop();
1001         int arg3    = interp.locals.pop();
1002         int arg2    = interp.locals.pop();
1003         int arg1    = interp.locals.pop();
1004         int special = interp.locals.pop();
1005         int lineTag = interp.locals.pop();
1006 
1007         if(iterlist_t *list = P_GetLineIterListForTag(lineTag, false))
1008         {
1009             IterList_SetIteratorDirection(list, ITERLIST_FORWARD);
1010             IterList_RewindIterator(list);
1011 
1012             Line *line;
1013             while((line = (Line *) IterList_MoveIterator(list)))
1014             {
1015                 xline_t *xline = P_ToXLine(line);
1016 
1017                 xline->special = special;
1018                 xline->arg1 = arg1;
1019                 xline->arg2 = arg2;
1020                 xline->arg3 = arg3;
1021                 xline->arg4 = arg4;
1022                 xline->arg5 = arg5;
1023             }
1024         }
1025 
1026         return Continue;
1027     }
1028 
findCommand(int name)1029     static CommandFunc const &findCommand(int name)
1030     {
1031         static CommandFunc const cmds[] =
1032         {
1033             cmdNOP, cmdTerminate, cmdSuspend, cmdPushNumber, cmdLSpec1, cmdLSpec2,
1034             cmdLSpec3, cmdLSpec4, cmdLSpec5, cmdLSpec1Direct, cmdLSpec2Direct,
1035             cmdLSpec3Direct, cmdLSpec4Direct, cmdLSpec5Direct, cmdAdd,
1036             cmdSubtract, cmdMultiply, cmdDivide, cmdModulus, cmdEQ, cmdNE,
1037             cmdLT, cmdGT, cmdLE, cmdGE, cmdAssignScriptVar, cmdAssignMapVar,
1038             cmdAssignWorldVar, cmdPushScriptVar, cmdPushMapVar,
1039             cmdPushWorldVar, cmdAddScriptVar, cmdAddMapVar, cmdAddWorldVar,
1040             cmdSubScriptVar, cmdSubMapVar, cmdSubWorldVar, cmdMulScriptVar,
1041             cmdMulMapVar, cmdMulWorldVar, cmdDivScriptVar, cmdDivMapVar,
1042             cmdDivWorldVar, cmdModScriptVar, cmdModMapVar, cmdModWorldVar,
1043             cmdIncScriptVar, cmdIncMapVar, cmdIncWorldVar, cmdDecScriptVar,
1044             cmdDecMapVar, cmdDecWorldVar, cmdGoto, cmdIfGoto, cmdDrop,
1045             cmdDelay, cmdDelayDirect, cmdRandom, cmdRandomDirect,
1046             cmdThingCount, cmdThingCountDirect, cmdTagWait, cmdTagWaitDirect,
1047             cmdPolyWait, cmdPolyWaitDirect, cmdChangeFloor,
1048             cmdChangeFloorDirect, cmdChangeCeiling, cmdChangeCeilingDirect,
1049             cmdRestart, cmdAndLogical, cmdOrLogical, cmdAndBitwise,
1050             cmdOrBitwise, cmdEorBitwise, cmdNegateLogical, cmdLShift,
1051             cmdRShift, cmdUnaryMinus, cmdIfNotGoto, cmdLineSide, cmdScriptWait,
1052             cmdScriptWaitDirect, cmdClearLineSpecial, cmdCaseGoto,
1053             cmdBeginPrint, cmdEndPrint, cmdPrintString, cmdPrintNumber,
1054             cmdPrintCharacter, cmdPlayerCount, cmdGameType, cmdGameSkill,
1055             cmdTimer, cmdSectorSound, cmdAmbientSound, cmdSoundSequence,
1056             cmdSetLineTexture, cmdSetLineBlocking, cmdSetLineSpecial,
1057             cmdThingSound, cmdEndPrintBold
1058         };
1059         static int const numCmds = sizeof(cmds) / sizeof(cmds[0]);
1060         if(name >= 0 && name < numCmds) return cmds[name];
1061         /// @throw Error  Invalid command name specified.
1062         throw Error("acs::Interpreter::findCommand", "Unknown command #" + String::number(name));
1063     }
1064 
1065 #endif  // __JHEXEN__
1066 
1067 } // namespace internal
1068 
1069 namespace acs {
1070 
1071 using namespace internal;
1072 
newThinker(Script & script,Script::Args const & scriptArgs,mobj_t * activator,Line * line,int side,int delayCount)1073 thinker_t *Interpreter::newThinker(Script &script, Script::Args const &scriptArgs,
1074     mobj_t *activator, Line *line, int side, int delayCount)
1075 {
1076     Module::EntryPoint const &ep = script.entryPoint();
1077 
1078     Interpreter *th = (Interpreter *) Z_Calloc(sizeof(*th), PU_MAP, nullptr);
1079     th->thinker.function = (thinkfunc_t) acs_Interpreter_Think;
1080 
1081     th->_script    = &script;
1082     th->pcodePtr   = ep.pcodePtr;
1083     th->delayCount = delayCount;
1084     th->activator  = activator;
1085     th->line       = line;
1086     th->side       = side;
1087     th->delayCount = delayCount;
1088 
1089     for(int i = 0; i < ep.scriptArgCount; ++i)
1090     {
1091         th->args[i] = scriptArgs[i];
1092     }
1093 
1094     Thinker_Add(&th->thinker);
1095 
1096     return &th->thinker;
1097 }
1098 
think()1099 void Interpreter::think()
1100 {
1101 #ifdef __JHEXEN__
1102     int action = (script().state() == Script::Terminating? Terminate : Continue);
1103 
1104     if(script().isRunning())
1105     {
1106         if(delayCount)
1107         {
1108             delayCount--;
1109             return;
1110         }
1111 
1112         currentScriptNumber = script().entryPoint().scriptNumber;
1113 
1114         while((action = findCommand(DD_LONG(*pcodePtr++))(*this)) == Continue)
1115         {}
1116 
1117         currentScriptNumber = -1;
1118     }
1119 
1120     if(action == Terminate)
1121     {
1122         // This script has now finished - notify interested parties.
1123         /// @todo Use a de::Observers -based mechanism for this.
1124         script().setState(Script::Inactive);
1125 
1126         // Notify any scripts which are waiting for this script to finish.
1127         scriptSys().forAllScripts([this] (Script &otherScript)
1128         {
1129             otherScript.resumeIfWaitingForScript(script());
1130             return LoopContinue;
1131         });
1132 
1133         Thinker_Remove(&thinker);
1134     }
1135 #endif
1136 }
1137 
scriptSys() const1138 System &Interpreter::scriptSys() const
1139 {
1140     return gfw_Session()->acsSystem();
1141 }
1142 
script() const1143 Script &Interpreter::script() const
1144 {
1145     DENG2_ASSERT(_script);
1146     return *_script;
1147 }
1148 
push(int value)1149 void Interpreter::Stack::push(int value)
1150 {
1151     if (height >= ACS_INTERPRETER_SCRIPT_STACK_DEPTH)
1152     {
1153         LOG_SCR_ERROR("acs::Interpreter::Stack::push: Overflow");
1154         return;
1155     }
1156     values[height++] = value;
1157 }
1158 
pop()1159 int Interpreter::Stack::pop()
1160 {
1161     if (height <= 0)
1162     {
1163         LOG_SCR_ERROR("acs::Interpreter::Stack::pop: Underflow");
1164         return 0;
1165     }
1166     return values[--height];
1167 }
1168 
top() const1169 int Interpreter::Stack::top() const
1170 {
1171     if (height == 0)
1172     {
1173         LOG_SCR_ERROR("acs::Interpreter::Stack::top: Underflow");
1174         return 0;
1175     }
1176     return values[height - 1];
1177 }
1178 
drop()1179 void Interpreter::Stack::drop()
1180 {
1181     if(height == 0)
1182         LOG_SCR_ERROR("acs::Interpreter::Stack::drop: Underflow");
1183     height--;
1184 }
1185 
write(MapStateWriter * msw) const1186 void Interpreter::write(MapStateWriter *msw) const
1187 {
1188     writer_s *writer = msw->writer();
1189 
1190     Writer_WriteByte(writer, 2); // Write a version byte.
1191 
1192     Writer_WriteInt32(writer, msw->serialIdFor(activator));
1193     Writer_WriteInt32(writer, P_ToIndex(line));
1194     Writer_WriteInt32(writer, side);
1195     Writer_WriteInt32(writer, script().entryPoint().scriptNumber);
1196     Writer_WriteInt32(writer, delayCount);
1197     for(int i = 0; i < ACS_INTERPRETER_SCRIPT_STACK_DEPTH; ++i)
1198     {
1199         Writer_WriteInt32(writer, locals.values[i]);
1200     }
1201     Writer_WriteInt32(writer, locals.height);
1202     for(int i = 0; i < ACS_INTERPRETER_MAX_SCRIPT_ARGS; ++i)
1203     {
1204         Writer_WriteInt32(writer, args[i]);
1205     }
1206     Writer_WriteInt32(writer, ((dbyte const *)pcodePtr) - (dbyte const *)scriptSys().module().pcode().constData());
1207 }
1208 
read(MapStateReader * msr)1209 int Interpreter::read(MapStateReader *msr)
1210 {
1211     reader_s *reader = msr->reader();
1212     int mapVersion = msr->mapVersion();
1213 
1214     if(mapVersion >= 4)
1215     {
1216         // Note: the thinker class byte has already been read.
1217         int ver = Reader_ReadByte(reader); // version byte.
1218 
1219         // Activator.
1220         activator = INT2PTR(mobj_t, Reader_ReadInt32(reader));
1221         activator = msr->mobj(PTR2INT(activator), &activator);
1222 
1223         // Line.
1224         int lineIndex = Reader_ReadInt32(reader);
1225         if(lineIndex >= 0)
1226         {
1227             line = (Line *) P_ToPtr(DMU_LINE, lineIndex);
1228             DENG2_ASSERT(line);
1229         }
1230         else
1231         {
1232             line = nullptr;
1233         }
1234 
1235         // Side index.
1236         side = Reader_ReadInt32(reader);
1237 
1238         // Script number.
1239         int scriptNumber = Reader_ReadInt32(reader);
1240         _script = &scriptSys().script(scriptNumber);
1241 
1242         // Obsolete ignored value in the old format?
1243         if(ver < 2)
1244         {
1245             /*infoIndex =*/ Reader_ReadInt32(reader);
1246         }
1247 
1248         delayCount = Reader_ReadInt32(reader);
1249 
1250         for(int i = 0; i < ACS_INTERPRETER_SCRIPT_STACK_DEPTH; ++i)
1251         {
1252             locals.values[i] = Reader_ReadInt32(reader);
1253         }
1254         locals.height = Reader_ReadInt32(reader);
1255 
1256         for(int i = 0; i < ACS_INTERPRETER_MAX_SCRIPT_ARGS; ++i)
1257         {
1258             args[i] = Reader_ReadInt32(reader);
1259         }
1260 
1261         pcodePtr = (int const *) (scriptSys().module().pcode().constData() + Reader_ReadInt32(reader));
1262     }
1263     else
1264     {
1265         // Its in the old pre V4 format which serialized acs_t
1266         // Padding at the start (an old thinker_t struct)
1267         byte junk[16]; // sizeof thinker_t
1268         Reader_Read(reader, junk, 16);
1269 
1270         // Start of used data members.
1271         activator  = INT2PTR(mobj_t, Reader_ReadInt32(reader));
1272         activator  = msr->mobj(PTR2INT(activator), &activator);
1273 
1274         int temp = Reader_ReadInt32(reader);
1275         if(temp >= 0)
1276         {
1277             line = (Line *) P_ToPtr(DMU_LINE, temp);
1278             DENG2_ASSERT(line);
1279         }
1280         else
1281         {
1282             line = nullptr;
1283         }
1284 
1285         side       = Reader_ReadInt32(reader);
1286         _script    = &scriptSys().script(Reader_ReadInt32(reader));
1287         /*infoIndex  =*/ Reader_ReadInt32(reader);
1288         delayCount = Reader_ReadInt32(reader);
1289 
1290         for(int i = 0; i < ACS_INTERPRETER_SCRIPT_STACK_DEPTH; ++i)
1291         {
1292             locals.values[i] = Reader_ReadInt32(reader);
1293         }
1294         locals.height = Reader_ReadInt32(reader);
1295 
1296         for(int i = 0; i < ACS_INTERPRETER_MAX_SCRIPT_ARGS; ++i)
1297         {
1298             args[i] = Reader_ReadInt32(reader);
1299         }
1300 
1301         pcodePtr = (int const *) (scriptSys().module().pcode().constData() + Reader_ReadInt32(reader));
1302     }
1303 
1304     thinker.function = (thinkfunc_t) acs_Interpreter_Think;
1305 
1306     return true; // Add this thinker.
1307 }
1308 
1309 }  // namespace acs
1310 
acs_Interpreter_Think(acs_Interpreter * interp)1311 void acs_Interpreter_Think(acs_Interpreter *interp)
1312 {
1313     DENG2_ASSERT(interp);
1314     reinterpret_cast<acs::Interpreter *>(interp)->think();
1315 }
1316