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