ScummVM API documentation
scriptexecutor.h
1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #ifndef MACS2_SCRIPTEXECUTOR_H
23 #define MACS2_SCRIPTEXECUTOR_H
24 
25 #include "common/array.h"
26 #include "common/rect.h"
27 #include "common/scummsys.h"
28 #include "common/str-array.h"
29 
30 namespace Common {
31 class MemoryReadStream;
32 }
33 
34 namespace Macs2 {
35 class Macs2Engine;
36 class Character;
37 class GameObject;
38 
39 namespace Script {
40 
42  uint16 a;
43  uint16 b;
44 };
45 // Order of cursors when loading from the file is
46 // { Talk = 0, Look = 1, Touch = 2, Walk = 3};
47 // Cursor mode values from setCursorMode (1008:3ea5) and handleInput.
48 // Each mode indexes into the cursor image array at _PTR_LOOP_1020_075a
49 // (16 bytes per entry: data ptr, size, width, height, hotspot x/y).
50 enum class MouseMode {
51  Talk = 0x13,
52  Look = 0x14,
53  Use = 0x15,
54  Walk = 0x16,
55  UseInventory = 0x17,
56  PanelUse = 0x18, // Used in map mode
57  PanelCursor = 0x19, // Used when action bar is open
58  Disabled = 0x1A // Hidden/disabled cursor
59 };
60 
61 enum class ExecutorState {
62  // We are not executing anything at the moment
63  Idle,
64  // We are in the middle of executing
65  Executing,
66  // Executing but paused until a callback happens
67  WaitingForCallback
68 };
69 
70 enum class ExecutionResult {
71  // We have finished executing the script
72  ScriptFinished,
73 
74  // We are now waiting for a callback
75  WaitingForCallback
76 };
77 
78 enum class ScriptExecutionState {
79  // We are executing the scene script
80  ExecutingSceneScript,
81 
82  // We are executing the other scripts
83  ExecutingOtherScripts
84 };
85 
100 private:
101 #ifdef DEMACS2
102 public:
103 #endif
104  void scriptSetVar();
105  void scriptSetVarOr();
106  void scriptIfTrue();
107  void scriptIfFalse();
108  bool scriptCompare();
109  void scriptIfInteraction();
110  void scriptEndIf();
111  void scriptElse();
112  void scriptNop09();
113  void scriptPrintStringLeft();
114  void scriptMoveObject();
115  ExecutionResult scriptChangeScene();
116  ExecutionResult scriptShowDialogue();
117  void scriptWalkToPosition();
118  ExecutionResult scriptWaitForWalk();
119  void scriptSkipWord();
120  void scriptClearDialogueChoices();
121  void scriptAddDialogueChoice();
122  ExecutionResult scriptShowDialogueChoice();
123  ExecutionResult scriptDismissPanel();
124  void scriptWalkToAndPickup();
125  bool scriptSetPickupFrames();
126  void scriptSetupObject();
127  void scriptSetSkippable();
128  void scriptClearSkippable();
129  void scriptPlayAnimation();
130  void scriptTestPathfinding();
131  void scriptSetYOffset();
132  void scriptSetMotion();
133  bool scriptSetOrientation();
134  void scriptMoveToPosition();
135  void scriptAddValues();
136  void scriptSubValues();
137  void scriptLoadSpecialAnim();
138  void scriptSetDirection();
139  void scriptStopAnimation();
140  void scriptOpenInventory();
141  void scriptLoadObjectAnim();
142  void scriptCheckObjectData();
143  void scriptCheckInventory();
144  void scriptSetSnapToTarget();
145  void scriptTestObjectAnimFrame();
146  void scriptPrintStringRight();
147  void scriptSetPaletteDarkness();
148  void scriptSetObjectShading();
149  void scriptSetObjectScaling();
150  void scriptSetHotspotOverride();
151  void scriptSetObjectBounds();
152  void scriptDismissAllPanels();
153  void scriptResetToSceneScript();
154  void scriptLoadOverlayFont();
155  void scriptAddOverlayTextEntry();
156  void scriptClearOverlayText();
157  void scriptFadeToBlack();
158  void scriptLoadPcmSound();
159  void scriptPlayPcmSound();
160  bool scriptWaitForSound();
161  void scriptStopPcmSound();
162  void scriptLoadMusicSlot();
163  void scriptPlayMusicSlot();
164  void scriptStopMusicSlot();
165  bool scriptWaitForMusic();
166  void scriptFreeMusicSlot();
167  void scriptGetObjectX();
168  void scriptGetObjectY();
169  void scriptGetObjectField8();
170  void scriptGetObjectOrientation();
171  void scriptClearActorInventory();
172  void scriptSetPathfindingRemap();
173  bool scriptWaitForAdlib();
174  void scriptSkipUntil14();
175  void scriptChangeAnimation();
176  void scriptFrameWait();
177  void scriptSetPathfinding();
178  void scriptTestSceneAnimFrame();
179  void scriptEndOverlayText();
180  void scriptFadeFromBlack();
181  void scriptFreePcmSound();
182 
183  inline void scriptUnimplementedOpcode(const char *source, uint16 opcode) {
184  debug("Unimplemented opcode (%s): %.2x.", source, opcode);
185  }
186 #ifdef DEMACS2
187 private:
188 #endif
189 
190  // State variables from here
191 
192  // Overall state
193  ExecutorState _state = ExecutorState::Idle;
194 
195  // Currently executed script
196  Common::MemoryReadStream *_stream = nullptr;
197 
198  // [1014h] global - current assumption is that this is set when we run
199  // the script for the scene initialization and reset when we run when the
200  // scene is active
201  bool _isSceneInitRun = false;
202 
203  // [1012h] global - current assumption is that this guards script runs that
204  // [1012h] g_wRepeatRunFlag - set during the repeat script pass
205  // that runs after scene init to process object scripts
206  bool _repeatRunFlag = false;
207 
208  // Binary runs init and repeat as separate runScriptExecutor calls inside
209  // scriptChangeScene. Init pass = scene script (isSceneInit) + object scripts.
210  // Repeat pass = second call with RepeatRunFlag=1 only.
211  bool _initPassComplete = false;
212  bool _deferredRepeatAfterInit = false;
213  bool _terminateOuterScriptBeforeRepeat = false;
214 
215  uint16 _executingObjectIndex = 0;
216 
217  ScriptExecutionState _scriptExecutionState = ScriptExecutionState::ExecutingSceneScript;
218 
219  // Returns true if the object is relevant to be executing in the current scene
220  bool isRelevantObject(const GameObject *obj) const;
221 
222  // Handles the next step of execution based on the current state.
223  // Can be run right after a previous step or be called after execution was paused
224  // Needs to update the state to be valid again
225  void step();
226 
227  // Depending on the current state, chooses the next script to run
228  // and adjusts the state
229  // Should return true if a new script was loaded
230  bool loadNextScript();
231 
232  bool _isTimerActive = false;
233  uint32 _timerEndMillis = 0;
234  bool _isFrameWaitActive = false;
235  uint16 _frameWaitTicksRemaining = 0;
236 
237  // We use this array to gather the dialogue choices as they come in
238 
239  Common::String identifyScriptOpcode(uint8 opcode, uint8 opcode2);
240  Common::String identifyHelperOpcode(uint8 opcode, uint16 value);
241 
242  byte readByte();
243  uint16 readUint16();
244 
245  // We use this to keep track of whether we have read all bytes we should have read
246  uint32 _expectedEndLocation = 0;
247  uint8 _lastOpcode = 0;
248  uint32 _lastOpcodeStreamPos = 0;
249 
250  // scriptSkipBlock: skips nested opcode blocks
251  void scriptSkipBlock();
252  // scriptSkipAlternate: alternate skip (for opcode 8)
253  void scriptSkipAlternate();
254 
255  bool loadIndexedResource(Common::Array<uint8> &outData, uint8 resourceIndex, uint16 objectTableOffset = 0x189);
256  bool loadSoundResource(Common::Array<uint8> &outData, uint8 resourceIndex);
257  bool loadMusicResource(Common::Array<uint8> &outData, uint8 resourceIndex);
258 
259  // scriptReadValue: reads a typed value from the script stream
260  void scriptReadValuePair(uint16 &out1, uint16 &out2);
261 
262  // Combines both 16 bit values into a 32 bit value
263  uint32 scriptReadValue32();
264 
265  // Returns only the first of the two 16 bit values
266  uint16 scriptReadValue16();
267 
268  // Saves the given value in a script variable
269  void scriptSaveVariableHelper(uint32 value);
270 
271  void scriptPrintString(bool alignRight = false);
272 
273  Common::StringArray _debugBuffer;
274  bool _lastOpcodeTriggeredSkip = false;
275  void beginBuffering();
276  void endBuffering(bool shouldMark = false);
277 
278  // g_wExecutingScriptObjectId [0F92h]: 0 when executing the scene script,
279  // 1..0x200 the object index whose script is running, > 0x200 when the
280  // object iteration in run() is exhausted (or a script terminated the run,
281  // e.g. opcode 0x29). The binary drives the whole script-selection flow off
282  // this single value; there is no separate scene-vs-object state flag.
283  uint16 _executingScriptObjectId = 0;
284 
285 public:
286  ScriptExecutor();
287  ~ScriptExecutor();
288 
289  void setIdle() { _state = ExecutorState::Idle; }
290 
291  Common::Array<uint16> _dialogueChoiceScriptIndices;
292  Common::Array<Common::StringArray> _dialogueChoices;
293 
294  // Binary scriptOpenInventory (1008:c3e6) / handleInput panel-3 close (1008:e8bf):
295  // saved script stream state restored when container inventory closes.
296  uint32 _savedOpenInventoryScriptPos = 0;
297  uint32 _savedOpenInventoryScriptEndPos = 0;
298  uint16 _savedOpenInventoryExecutingObjectId = 0;
299  // Legacy alias used by saveload; kept in sync with _savedOpenInventoryScriptPos.
300  uint32 _secondaryInventoryLocation = 0;
301  bool _hasPendingExternalInventoryResume = false;
302  uint16 _externalInventorySourceObjectID = 0;
303  MouseMode _savedExternalInventoryMouseMode = MouseMode::Use;
304 
305  // Determines if we actually look something up when looking up 9F4D FF 27
306  // in the obstacles/pathfinding map
307  // Should be 1 while we execute a "character stopped walking" script,
308  // otherwise 0
309  bool _pathWalkableResult = false;
310  bool _isRepeatRun = false;
311 
312  // Scene data [di+53B7h] - TODO: Confirm that we use a script variable as well as this thing
313  int _chosenDialogueOption = 0;
314  uint16 _dialogueSpeakerObjectID = 0;
315 
316  // 0x2000 bytes / 4 bytes per var = 2048 variables max (indices 1-0x800).
317  // All zeroed on init by memsetBytes in loadResourceFile.
319 
320  // g_wCursorMode (1020:0fe6): the active cursor mode (0x13..0x1A).
321  MouseMode _cursorMode = MouseMode::Walk;
322  MouseMode _cursorModeBeforeWait = MouseMode::Walk;
323  // Set by opcodes 0x0A/0x0D/0x17 while waiting for the player to dismiss text UI.
324  // Binary keeps a clickable cursor for those waits; step() must not force hourglass.
325  bool _waitingForUiClick = false;
326 
327  uint16 _interactedObjectID = 0;
328  // g_wInteractedInventoryItemId [1026h]: inventory item involved in a
329  // use-inventory-item-on-object interaction (0x400 + item object index).
330  uint16 _interactedInventoryItemId = 0;
331  uint16 _walkTargetObjectIndex = 0;
332  // Script click state: set by handleInput when user clicks during script execution.
333  // Saved/restored by scriptOpenInventory across UI interactions.
334  uint16 _scriptClickFlag = 0;
335  uint16 _scriptClickX = 0;
336  uint16 _scriptClickY = 0;
337  uint16 _scriptClickResult = 0;
338  uint16 _savedScriptClickFlag = 0;
339  uint16 _savedScriptClickX = 0;
340  uint16 _savedScriptClickY = 0;
341  uint16 _savedScriptClickResult = 0;
342 
343  // Is set to true in opcode 2C if an object is inside another target object
344  bool _inventoryCheckResult = false;
345  bool _animBlobRangeTestResult = false;
346  bool _soundEnabled = true;
347  // [102Ah] - separate from soundEnabled (sound). Controls whether the
348  // player can fast-forward the current script section via button 8.
349  // Set by opcode 0x1C, cleared by opcode 0x1D.
350  bool _scriptSkippable = false;
351  bool _musicEnabled = true;
352  bool _soundSystemActive = true;
353  bool _overlayTextStageActive = false;
354  bool _inventoryActionFlag = false;
355  bool _inventoryCombineFlag = false;
356  Common::Array<uint8> _musicSlots[2];
357  uint16 _activeMusicSlot = 0;
358  uint16 _musicControlMode = 0;
359  // g_wMusicControlStep: per-tick volume fade step for the music fade in gameTick.
360  uint16 _musicControlStep = 0;
361  uint16 _musicControlVolume = 0;
362  // g_wWaitForPcmSound [100Ch] (opcode 0x41), g_wWaitForMusicControl [100Eh]
363  // (opcode 0x47), g_wWaitForAdlibReady [1010h] (opcode 0x4E): wait flags
364  // polled by the gameTick wait cascade (View1::tick).
365  bool _waitForPcmSound = false;
366  bool _waitForMusicControl = false;
367  bool _waitForAdlibReady = false;
368  bool _debugPaused = false;
369  bool _pickupInProgress = false;
370  uint16 _pickupActorObjectID = 0;
371  uint16 _pickupTargetObjectID = 0;
372 
373  bool _isRunningScript = false;
374  // Mutex indicating if the A3D2 function is active
375  bool _isSkipping = false;
376 
377  Macs2::Macs2Engine *_engine = nullptr;
378 
379  // Button 8 skip from handleInput (1008:e8bf)
380  bool skipToEndOfSkippableSection();
381 
382  // Implements a lookup in the "areas" map
383  uint16 getAreaAtPoint(uint16 x, uint16 y);
384 
385  void setVariableValue(uint16 index, uint16 a, uint16 b);
386 
387  void setVariableValue(uint16 index, uint32 value);
388 
389  Common::Point getCharPosition();
390 
391  // executeOpcodes (1008:db56): the opcode dispatch loop for the current script.
392  ExecutionResult executeOpcodes();
393 
394  // Will execute the script and any object scripts until execution should be stopped
395  // TODO: Consider if we should let the executor also figure out where to get the
396  // first script from
397  void run(bool firstRun = false);
398 
399  void setScript(Common::MemoryReadStream *stream);
400  void releaseObjectStream();
401 
402  void setCurrentSceneScriptAt(uint32 offset);
403 
404  void tick();
405 
406  void startTimer(uint32 duration);
407  void endTimer();
408  void startFrameWait(uint16 duration);
409  void endFrameWait();
410 
411  // True when script execution is paused on any wait opcode (frame, walk, dialogue, etc.).
412  bool isScriptWaitDeferred() const {
413  return _state == ExecutorState::WaitingForCallback ||
414  _frameWaitTicksRemaining != 0 || _walkTargetObjectIndex != 0 ||
415  _waitForPcmSound || _waitForMusicControl || _waitForAdlibReady;
416  }
417 
418  bool isExecuting() const {
419  return _state != ExecutorState::Idle;
420  }
421 
422  uint32 getScriptPosition() const;
423  // Returns the position of the last executed/executing opcode (for debugger highlight)
424  uint32 getDebugOpcodePosition() const;
425  bool isWaitingForCallback() const { return _state == ExecutorState::WaitingForCallback; }
426  uint32 getScriptEndPosition() const;
427  uint16 getExecutingObjectId() const { return _executingObjectIndex; }
428  void setExecutingObjectId(uint16 id) { _executingObjectIndex = id; }
429  uint16 getFrameWaitCounter() const { return _frameWaitTicksRemaining; }
430  void setFrameWaitCounter(uint16 val) { _frameWaitTicksRemaining = val; }
431  bool isFrameWaitActive() const { return _isFrameWaitActive; }
432  bool getRepeatRunFlag() const { return _repeatRunFlag; }
433  void setRepeatRunFlag(bool val) { _repeatRunFlag = val; }
434  uint32 getVariableValue(int index) const;
435 
436  // Plate / walk debugging: log actor position, area, walkability, script waits.
437  void debugLogActorWalkState(const char *context);
438 
439  // Computes the read-only runtime value for a type 0xFF special
440  // (FF:value), inlined in scriptReadValue in the original binary
441  uint32 getSpecialValue(uint16 value);
442 
443  // Returns true if the save/load menu can be opened (no blocking state)
444  bool canOpenSaveMenu() const {
445  return !_isSceneInitRun && !_isFrameWaitActive && !_isTimerActive;
446  }
447 
448  // g_wScriptErrorCode (1020:0f86): non-zero halts opcode dispatch (1008:db56).
449  uint16 _scriptErrorCode = 0;
450  Character *getOrCreateCharacter(uint16 objectID);
451  void saveWalkRuntime(const Character *c, GameObject *o);
452  void restoreWalkRuntime(Character *c, const GameObject *o);
453  void clearStoredWalkRuntime(GameObject *o);
454  void seedMoveToPositionState(GameObject *object, Character *c, const Common::Point &target, uint16 targetVerticalOffset);
455  void seedMotionState(GameObject *object, Character *c, uint16 targetVerticalOffset, uint16 verticalOffsetDelta, uint16 motionDistance);
456  void saveOpenInventoryScriptContext();
457  void restoreOpenInventoryScriptContext();
458  void setScriptError(uint16 code);
459  bool hasScriptError() const { return _scriptErrorCode != 0; }
460  void clearScriptError() { _scriptErrorCode = 0; }
461  uint16 getScriptErrorCode() const { return _scriptErrorCode; }
462 
463  // Debug globals PTR_LOOP_1020_06c2 / PTR_LOOP_1020_06c4 (saved on script halt).
464  uint32 _errorScriptPosition = 0;
465  uint16 _errorScriptContext = 0;
466  void recordScriptErrorPosition();
467 
468  // Binary scriptChangeScene (1008:ad6e) synchronous init/repeat script passes.
469  void beginSceneEntryInitPass();
470  void finishSceneEntryRepeatPass(bool terminateOuterScript);
471  void runSceneEntryScriptPasses();
472  void runSceneScriptPass(bool initRun, bool repeatRun);
473 
474  // Binary executeOpcodes (1008:db56): blocking waits save cursor then set 0x1A.
475  void enterBlockingWaitCursor();
476  void clearScriptUiWaitState();
477 
478  // Resets the script to the beginning.
479  // Confirmed: runScriptExecutor (1008:e3e7) sets position=0 on fresh runs
480  // (g_wScriptIsExecuting==0). Mid-execution pauses resume from current position.
481  void rewind();
482 };
483 
484 } // namespace Script
485 } // namespace Macs2
486 
487 #endif
Definition: str.h:59
Definition: gameobjects.h:86
Definition: scriptexecutor.h:99
Definition: view1.h:55
void debug(MSVC_PRINTF const char *s,...) GCC_PRINTF(1
Definition: macs2.h:235
Definition: scriptexecutor.h:41
Definition: algorithm.h:29
Definition: rect.h:144
Definition: memstream.h:43
Definition: debugtools.h:25