ScummVM API documentation
macs2.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_MACS2_H
23 #define MACS2_MACS2_H
24 
25 #include "advancedDetector.h"
26 #include "audio/audiostream.h"
27 #include "audio/mixer.h"
28 #include "common/array.h"
29 #include "common/error.h"
30 #include "common/file.h"
31 #include "common/fs.h"
32 #include "common/hashmap.h"
33 #include "common/memstream.h"
34 #include "common/random.h"
35 #include "common/scummsys.h"
36 #include "common/serializer.h"
37 #include "common/system.h"
38 #include "common/text-to-speech.h"
39 #include "common/util.h"
40 #include "engines/engine.h"
41 #include "graphics/palette.h"
42 #include "macs2/amiga_archive.h"
43 #include "macs2/events.h"
44 #include "macs2/macs2_constants.h"
45 #include "macs2/pathfinding.h"
46 #include "macs2/scriptexecutor.h"
47 #include "macs2/sprite.h"
48 #include "macs2/text.h"
49 
50 namespace Macs2 {
51 
53 public:
54  Common::Array<byte> _data;
55  int64 _pos = 0;
57  int _rate = 0x1F40;
58 
59  virtual ~MacsAudioStream() {}
60 
77  virtual int readBuffer(int16 *buffer, const int numSamples);
78 
80  virtual bool isStereo() const;
81 
83  virtual int getRate() const;
84 
94  virtual bool endOfData() const;
95 
96  virtual bool seek(const Audio::Timestamp &where);
97 
98  virtual Audio::Timestamp getLength() const;
99 };
100 
101 struct Macs2GameDescription;
102 class Music;
103 
104 struct AnimFrame : public Sprite {
105  int16 _offsetX = 0;
106  int16 _offsetY = 0;
107 
108  void readFromeFile(Common::File &file);
109  void readFromStream(Common::SeekableReadStream *stream);
110  bool pixelHit(const Common::Point &point) const;
111  Common::Point getBottomMiddleOffset(uint16 scale = 100) const;
112 };
113 
114 enum class MenuMode : uint16 {
115  Hidden = 0,
116  Main = 1,
117  Options = 2,
118  DialogueList = 4
119 };
120 
121 enum class OptionsSubMode : uint16 {
122  None = 0,
123  Save = 1,
124  Load = 2
125 };
126 
128 struct HudButton {
129  int16 x = 0;
130  int16 y = 0;
131  uint16 inactiveStep = 0;
132  uint16 activeStep = 0;
133  uint16 hoverStep = 0;
134  uint16 buttonId = 0; // 1=Walk, 2=Look, 3=Talk, 4=Use, 0x33=Options, ...
135  uint16 menuId = 0;
136  AnimFrame frame;
137  AnimFrame activeFrame;
138  AnimFrame hoverFrame;
139  Common::Array<uint8> animBlob;
140 };
141 
143  uint16 _x = 0;
144  uint16 _y = 0;
145  Common::Array<AnimFrame> _frames;
146  uint32 _frameIndex = 0;
147 };
148 
150  uint16 _x = 0;
151  uint16 _y = 0;
152  Common::Array<uint8> _blob;
154  Common::Array<uint8> _extraBlobs[8];
155  uint16 _activeExtraSlot = 0; // 0 = primary _blob; 1..8 = _extraBlobs[slot-1]
156  uint16 _unknown0C = 0; // +0x50F3: purpose unknown (word, read from file, not used at runtime)
157  uint8 _unknown0E = 0; // +0x50F5: purpose unknown (byte, read from file, not used at runtime)
158  uint8 _unknown0F = 0; // +0x50F6: purpose unknown (byte, read from file, not used at runtime)
159  Common::Array<uint8> &activeBlob() {
160  if (_activeExtraSlot >= 1 && _activeExtraSlot <= 8)
161  return _extraBlobs[_activeExtraSlot - 1];
162  return _blob;
163  }
164  const Common::Array<uint8> &activeBlob() const {
165  if (_activeExtraSlot >= 1 && _activeExtraSlot <= 8)
166  return _extraBlobs[_activeExtraSlot - 1];
167  return _blob;
168  }
169  AnimFrame getCurrentFrame();
170  static uint16 advanceAnimFrame(Common::Array<uint8> &blob, bool bpp6, uint16 bpp8);
172  static uint16 getCurrentPixelFrameNumber(const Common::Array<uint8> &blob);
173  static uint16 getAnimFrameCount(Common::Array<uint8> &blob);
174  // Mirrors (horizontally flips) all frames in an animation blob in-place.
175  // Matches binary decodeAnimBlob (1010:184d) which calls the row-flip at 1010:1319.
176  static void mirrorAnimBlob(Common::Array<uint8> &blob);
177 };
178 
196 struct AnimBlobView {
197  const Common::Array<uint8> &_blob;
198 
199  explicit AnimBlobView(const Common::Array<uint8> &blob) : _blob(blob) {}
200 
201  bool isValid() const {
202  return _blob.size() >= 14;
203  }
204 
205  uint16 sequencePosition() const {
206  return READ_LE_UINT16(&_blob[0x02]);
207  }
208 
209  uint16 repeatCounter() const {
210  return READ_LE_UINT16(&_blob[0x04]);
211  }
212 
213  uint16 loopStartPosition() const {
214  return READ_LE_UINT16(&_blob[0x06]);
215  }
216 
217  uint16 delayCounter() const {
218  return READ_LE_UINT16(&_blob[0x08]);
219  }
220 
221  uint16 sequenceLength() const {
222  return READ_LE_UINT16(&_blob[0x0A]) + 1;
223  }
224 
225  uint32 frameDataOffset() const {
226  return 0x0B + sequenceLength();
227  }
228 
229  uint16 frameCount() const {
230  const uint32 off = frameDataOffset();
231  if (off + 2 > _blob.size()) {
232  return 0;
233  }
234  return READ_LE_UINT16(&_blob[off]);
235  }
236 
237  // Get frame info at index (0-based). Returns false if out of bounds.
238  struct FrameInfo {
239  int16 offsetX;
240  int16 offsetY;
241  uint16 unknown;
242  uint16 width;
243  uint16 height;
244  const byte *pixels; // pointer into blob data
245  };
246 
247  bool getFrameInfo(uint16 index, FrameInfo &out) const {
248  uint32 pos = frameDataOffset() + 2; // skip frame count word
249  for (uint16 i = 0; i <= index; i++) {
250  if (pos + 10 > _blob.size()) {
251  return false;
252  }
253  const int16 ox = (int16)READ_LE_UINT16(&_blob[pos]);
254  const int16 oy = (int16)READ_LE_UINT16(&_blob[pos + 2]);
255  const uint16 unk = READ_LE_UINT16(&_blob[pos + 4]);
256  const uint16 w = READ_LE_UINT16(&_blob[pos + 6]);
257  const uint16 h = READ_LE_UINT16(&_blob[pos + 8]);
258  pos += 10;
259  if (w == 0 || h == 0 || pos + (uint32)w * h > _blob.size()) {
260  return false;
261  }
262  if (i == index) {
263  out = {ox, oy, unk, w, h, &_blob[pos]};
264  return true;
265  }
266  pos += (uint32)w * h;
267  }
268  return false;
269  }
270 };
271 
272 class Macs2Engine : public Engine, public Events {
273 private:
274  const ADGameDescription *_gameDescription;
275 
276  Music *_music = nullptr;
277 
278 protected:
279  // Engine APIs
280  Common::Error run() override;
281 
285  bool shouldQuit() const override {
286  return Engine::shouldQuit();
287  }
288 
289 public:
290  Graphics::ManagedSurface readRLEImage(int64 offs, Common::SeekableReadStream *stream);
291 
293  void readResourceFile();
295  enum class McsFileVersion {
296  Unknown = 0,
297  V1, // AHFFMACS0100
298  V2 // AHFFMACS0200
299  };
300  McsFileVersion detectMcsFileVersion(Common::SeekableReadStream &stream) const;
302  void loadResourceFileV1();
304  void loadResourceFileV2();
306  void bootstrapMcsActorsObjectsAndScene();
308  void softRestart();
309  const char *getResourceMcsFilename() const;
311  void readAmigaResources();
312  void applyAmigaUiPalette();
318  void installAmigaPortraitPalette(bool copyFromPlayfield);
319  void buildAmigaPanelRemapTable();
320  bool loadAmigaCursorResource(uint16 resourceId, AnimFrame &out);
321  bool loadAmigaMxffFont();
322  bool loadAmigaOverlayFont(uint8 resourceIndex);
323  bool loadAmigaOverlayFontResource(uint16 ffId);
330  bool loadAmigaSceneBackground(uint32 sceneResourceId);
331 
332  void initInventoryIconIndices();
333 
334  // Assumes that the stream is at the location of the number of background animations
335  void readBackgroundAnimations(Common::SeekableReadStream *stream);
336 
337  // Assumes that the stream is at the start of the right section
338  void readImageResources(Common::SeekableReadStream *stream);
339 
340 public:
341  Macs2Engine(OSystem *osystem, const ADGameDescription *gameDesc);
342  ~Macs2Engine() override;
343 
344  void changeScene(uint32 newSceneIndex, bool executeScript = true);
345 
346  Script::ScriptExecutor *_scriptExecutor = nullptr;
347  Graphics::ManagedSurface _sceneBackground;
348  Graphics::ManagedSurface _hotspotMap;
349 
350  // This is the depth map
351  Graphics::ManagedSurface _depthMap;
352  // Scene-load snapshot used to restore depth under background animation frame 0.
353  Graphics::ManagedSurface _sceneDepthMap;
354 
355  // Shadow/shading intensity map (scene+0x301B). Per-pixel values 0-32
356  // control character sprite darkening via the shading table.
357  // Only scenes with shadow regions have non-zero data.
358  Graphics::ManagedSurface _shadowMap;
359 
360  // _palVanilla: raw 6-bit VGA source. _pal: 8-bit display (darkened + expanded).
363 
364  Common::Array<Common::String> _debugOutput;
366 
367  Pathfinding _pathfinding;
368 
370  void updateBackgroundAnimationDepthMap(size_t animIndex);
371  void updateAllBackgroundAnimationDepthMaps();
372 
373  // This is the override list living at [5BD1]
374  // Savegames sync 16 words into indices 1..16 (array size 0x11 during sync).
375  // Runtime capacity is 0x21 for later growth after load.
376  Common::Array<uint16> _hotspotOverrides;
377 
378  Common::Array<Macs2::AnimFrame> _imageResources;
380  Common::Point _cursorHotspots[33];
381 
382  Text _text;
383  bool loadOverlayFont(uint8 resourceIndex, uint16 executingObjectID);
388  bool resolveResourceFileOffset(uint8 resourceIndex, uint16 executingObjectId, uint32 &outOffset) const;
390  bool loadSizedResourcePayload(uint8 resourceIndex, uint16 executingObjectId, Common::Array<uint8> &outPayload);
392  bool loadAhffAnimResource(uint8 resourceIndex, uint16 executingObjectId, Common::Array<uint8> &outBlob);
394  bool readMegaPicImage(Common::SeekableReadStream *stream, int width, int height, Graphics::ManagedSurface &out);
400  bool loadMaskFromResource(uint8 resourceIndex, uint16 executingObjectId, Graphics::ManagedSurface &dest,
401  int megapicW, int megapicH, bool upscaleHalfRes = false);
402 
404  struct DeltaStrip {
405  uint16 y = 0;
407  };
408  struct DeltaFrame {
410  };
411  struct DeltaSfxEvent {
412  uint16 frameIndex = 0;
413  bool duckMusic = false;
414  Common::String fileName;
415  };
416  struct DeltaAnimState {
417  bool loaded = false;
418  bool playing = false;
419  bool applyPaletteOnStart = false;
420  uint16 frameCount = 0;
421  uint16 startFrame = 0;
422  uint16 endFrame = 0;
423  uint16 speedTicks = 1;
424  uint16 tickCounter = 0;
425  uint16 currentFrame = 0;
426  uint16 clipMiX = 0;
427  uint16 clipMiY = 0;
428  uint16 clipMaX = 0;
429  uint16 clipMaY = 0;
433  void clear(int screenW, int screenH) {
434  loaded = false;
435  playing = false;
436  frameCount = 0;
437  startFrame = endFrame = currentFrame = 0;
438  speedTicks = 1;
439  tickCounter = 0;
440  applyPaletteOnStart = false;
441  frames.clear();
442  sfxEvents.clear();
443  clipMiX = clipMiY = 0;
444  clipMaX = (uint16)MAX(0, screenW - 1);
445  clipMaY = (uint16)MAX(0, screenH - 1);
447  }
448  };
449  DeltaAnimState _deltaAnim;
450  bool loadDeltaAnimResource(uint8 resourceIndex, uint16 executingObjectId, bool forceSkipSpeed1 = false);
451  void clearDeltaAnim();
452  bool startDeltaPlayback(uint16 startFrame, uint16 endFrame, uint16 speedTicks, bool applyPalette);
453  bool tickDeltaPlayback();
454  void applyDeltaFrameToBackground(const DeltaFrame &frame);
455  void playDeltaFrameSfx(uint16 displayFrame);
456  AnimFrame _animFrames[6];
457  // 6 flag/decoration animation frames at fixed file offset 0x6A5941, each followed by 6 padding bytes
458 
459  // Character shading remap (loadResourceFile @ 1008:2e8d -> scene+0x53D3).
460  // Indexed as (color - 0xC0) * 0x20 + shadowIntensity (drawSpriteTransparent @ 1010:0ed1).
461  Common::Array<byte> _shadingTable;
462  // Per-scene 256-byte UI pixel remap (changeScene @ 1008:2574, drawAnimFrameScaled @ 1010:1399).
463  Common::Array<byte> _panelRemapTable;
464 
465  // Map scene offsets from resource file (scene+0x5DDB, 256 entries x 4 bytes).
466  // Each entry is a file offset to a scene preview image for map mode.
467  uint32 _helpOffsets[256] = {0};
468 
469  Common::Array<BackgroundAnimation> _backgroundAnimations;
470  Common::Array<BackgroundAnimationBlob> _backgroundAnimationsBlobs;
471 
472  Common::SeekableReadStream *_fileStream = nullptr;
473  McsFileVersion _mcsFileVersion = McsFileVersion::Unknown;
475  uint32 _mcsDirectoryOffset = kMcsV1DirectoryOffset;
476 
478  bool _amigaNativePlayfieldPalette = false;
479  Common::Array<byte> _amigaLineCopperPal;
481  Macs2AmigaArchive *_amigaArchive = nullptr;
484  Common::Array<byte> _amigaPendingSceneStrings;
485 
488 
490  bool _bottomHudVisible = true;
491 
496  Graphics::ManagedSurface _hudMegapics[6];
497  bool _hudMegapicLoaded[6] = {};
498  Common::Array<HudButton> _hudButtons;
500  uint16 _panelTopY = 0;
501  uint16 _panelHeight = 0;
502  MenuMode _menuMode = MenuMode::Main;
503  OptionsSubMode _optionsSubMode = OptionsSubMode::None;
504  Script::MouseMode _savedMenuCursorMode = Script::MouseMode::Walk;
505  uint16 _inventScroll = 1;
506  uint16 _inventOriginX = 0;
507  uint16 _inventOriginY = 0;
508  uint16 _inventCols = 4;
509  uint16 _inventRows = 2;
510  uint16 _inventSlotW = 64;
511  uint16 _inventSlotH = 52;
512  uint16 _inventLayoutMode = 0;
518  uint16 _hudTextLayout[7] = {};
519  uint16 _hudTextRecolor[4] = {};
520  uint16 _saveListScroll = 1;
521  Common::Array<Common::String> _saveSlotNames;
523  uint16 _skipSpeed = 1;
524 
525  void setCursorMode(Script::MouseMode newMode);
526  void nextCursorMode();
527 
528  Common::Array<uint16> _hotspotColorTable;
529 
530  uint16 _walkDepthThresholdY;
531  uint16 _walkDepthScaleFactor;
532  uint16 _walkBaseSpeedPct;
533 
534  // Scene palette/animation mode at sceneData+0x5203:
535  // 1 = normal (no darkening, no bg animation)
536  // 2 = slow bg animation + palette darkening
537  // 3 = fast bg animation + palette darkening
538  uint16 _scenePaletteMode;
539  // Palette darken percentage (0-100) at sceneData+0x5205.
540  // Formula: displayedPal[i] = sourcePal[i] * (100 - _paletteDarkenPercent) / 100
541  uint16 _paletteDarkenPercent;
542 
543  void applyPaletteDarkening();
544  void readPalette(Common::SeekableReadStream *stream, Graphics::Palette &dest);
545  void expandPalette6To8(Graphics::Palette &pal);
546  // Palette quantization for g_wHelpButtonDisabled path (1000:103e).
547  // Histograms scene pixels, keeps 16 rarest colors (0..0xBF) plus UI range
548  // 0xC0..0xFF, remaps background + bg-anim blobs + palette via Manhattan RGB.
549  void applyScenePaletteEffect();
550 
551  // Gradual palette brighten effect for _scenePaletteMode == 2, matching the
552  // binary updateBackgroundAnimations (1008:2c05). Called from the game tick at
553  // the mode-gated rate. Despite the name it does NOT advance animation frames
554  // (that happens in the per-frame render); it decrements the darken percent
555  // toward 60 and reapplies the palette darkening.
556  void updateBackgroundAnimationPalette();
557 
558  // Binary g_bMovementFinishedFlag [1020:0000]: set by walkAlongPath on final arrival
559  // (orientation < 9), checked after all characters processed in drawAllCharacters.
560  bool _movementFinishedFlag = false;
561 
562  Common::Array<uint32> _sceneResourceOffsets;
563 
564  bool loadAnimationFromSceneData(uint16 objectIndex, uint16 slotIndex, uint8 arrayIndex, bool shouldMirror = false, uint16 executingScriptObjectId = 0);
565  bool loadObjectData(GameObject *obj);
566  void clearObjectRuntime(GameObject *obj);
567  // sortObjectsByDepth @ 1008:0d79 - inventory cursor reset + free object runtime blobs.
568  void sortObjectsByDepth(uint16 objectIndex);
569 
570  Music *getMusic() const { return _music; }
571  // Returns the Music volume (0-63) scaled by the user's music_volume setting
572  uint16 scaledMusicVolume(uint16 gameAttenuation) const;
577  uint16 _talkVol = 0;
584  void setCurrentSoundData(const Common::Array<uint8> &data, int rateHz = 0x1F40, int headerSkip = 2);
585  void clearCurrentSoundData();
586  bool hasCurrentSoundData() const { return !_currentSoundData.empty(); }
587  void playSample();
588  void stopSample();
589  bool isSamplePlaying() const;
595  void playAudioFile(const Common::Path &basename, bool speechBus);
596  void stopSpeech();
597  bool isSpeechPlaying() const;
598 
599  // Offset 50D3h - This is used in 0037:10C4 to terminate the loop
600  uint16 _numHotspots;
601 
602  // Reserved/unused scene data fields at scene+0x53C3..+0x53CF (4 dwords).
603  // Zeroed on scene change, saved/loaded, but never read or written with meaningful
604  // values by any game logic. Kept only for save/load format compatibility.
605  uint32 _sceneTimerParams[4] = {0};
606 
607  // Clip rect dirty flag [0xfec] - in the original DOS engine this signaled the VGA
608  // blitter to reset the clip region to full screen before the next partial-region
609  // updates (a dirty-rect optimization). Not used in ScummVM because we redraw the
610  // full backbuffer each frame via ManagedSurface. Kept only for save/load compatibility.
611  bool _clipRectDirty = false;
612 
613  uint16 getHotspotAtPoint(const Common::Point &p) const;
614 
615  Common::Array<uint16> inventoryIconIndices;
616  Common::Array<uint16> containerInventoryIconIndices;
617 
618  void runScriptExecutor(bool firstRun = false) {
619  _scriptExecutor->run(firstRun);
620  }
621 
622  bool _runScheduled = false;
623  // TODO: Feels like this should be more elegantly solved, also check how the game does this
624  // Is required for example after a scene change
625  bool _scheduledRunIsInitScene = false;
626 
627  // Game speed mode from original binary (g_wGameSpeedMode at 1020:0214).
628  // Cycled by Ctrl+T: 0=normal, 1=fast (no frame wait), 2=slow (wait for tick>=0x12).
629  uint16 _gameSpeedMode = 0;
630  void setGameSpeedMode(uint16 mode);
631 
632  // Input record/playback system from original binary.
633  // Original usage: MCSEXEC filename /rRecFile or /pPlayFile
634  // Record writes per-frame: mouseX(2), mouseY(2), buttons(2)
635  // Playback reads the same and also a leading frame-count word.
636  enum class InputMode { None,
637  Record,
638  Playback };
639  InputMode _inputMode = InputMode::None;
640  Common::WriteStream *_inputRecordStream = nullptr;
641  Common::SeekableReadStream *_inputPlaybackStream = nullptr;
642  uint32 _inputFrameCounter = 0;
643  uint32 _inputPlaybackEndFrame = 0;
644 
645  void startInputRecording(const Common::Path &filename);
646  void startInputPlayback(const Common::Path &filename);
647  void stopInputRecording();
648  void recordInputFrame(uint16 mouseX, uint16 mouseY, uint16 buttons);
649  bool readInputFrame(uint16 &mouseX, uint16 &mouseY, uint16 &buttons);
650 
651  Common::Array<uint8> _currentSoundData;
652  int _currentSoundRate = 0x1F40;
653  int _currentSoundHeaderSkip = 2;
658 
659  // Schedules a run of the script the next time the executor is ticked
660  void scheduleRun(bool initScene = false);
661 
662  Common::StringArray decodeStrings(Common::MemoryReadStream *stream, int offset, int numStrings, int sceneId = 0, int objectId = 0);
663 
664  // --- Translation support ---
666  Common::StringArray strings;
667  };
669  Common::HashMap<uint32, TranslationEntry> _objectTranslations;
670  Common::HashMap<Common::String, Common::String> _hotspotLabelTranslations;
672  void loadTranslation();
673  Common::String translateHotspotLabel(const Common::String &cp850Name) const;
675  Common::String translateUiLabel(const Common::String &source) const;
676  // Compute the sequential string index at the given byte offset in a string blob
677  int computeStringIndex(Common::MemoryReadStream *stream, int targetOffset);
678 
679  uint32 getFeatures() const;
680  Common::Platform getPlatform() const { return _gameDescription->platform; }
681  bool isAmiga() const { return getPlatform() == Common::kPlatformAmiga; }
682  Macs2AmigaArchive *getAmigaArchive() const { return _amigaArchive; }
683 
684  bool isDemo() const { return getFeatures() & ADGF_DEMO; }
685 
686  bool isV1() const { return _mcsFileVersion == McsFileVersion::V1; }
687  bool isV2() const { return _mcsFileVersion == McsFileVersion::V2; }
688 
690  uint32 getMcsDirectoryOffset() const { return _mcsDirectoryOffset; }
691  McsFileVersion getMcsFileVersion() const { return _mcsFileVersion; }
692  int screenWidth() const { return isV2() ? kWinScreenWidth : kScreenWidth; }
693  int screenWidthLast() const { return screenWidth() - 1; }
694  int gameHeight() const { return isV2() ? kWinGameHeight : kGameHeight; }
695  int gameHeightLast() const { return gameHeight() - 1; }
700  int screenHeight() {
701  if (isV2()) {
702  if (_panelTopY == 0 || _panelHeight == 0)
703  return kWinGameHeight;
704  return (int)_panelTopY + (int)_panelHeight;
705  }
706  if (enhancementEnabled(kEnhUIUX))
707  return gameHeight() + kUIHeight;
708  return gameHeight();
709  }
710  int screenHeightLast() { return screenHeight() - 1; }
711 
718  bool isBottomHudVisible() const { return _bottomHudVisible; }
719  void setBottomHudVisible(bool visible);
720 
722  bool hasNativeHudAssets() const {
723  return _panelTopY != 0 && _panelHeight != 0;
724  }
725 
726  // --- Layout / dialect facades ---
727 
729  uint32 timerTickMs() const { return isV2() ? 55 : 46; }
731  uint16 ticksPerGameFrame() const { return isV2() ? 1 : 2; }
732 
734  uint16 maxAnimSlots() const { return isV2() ? 38 : 21; }
736  uint16 maxOrientations() const { return maxAnimSlots() - 1; }
738  uint16 overloadAnimSlot() const { return maxAnimSlots(); }
739  static uint16 specialAnimSlotToAnimSlot(uint16 specialSlot);
742  uint16 maxHotspots() const { return isV2() ? 32 : 16; }
744  uint maxObjectResources() const { return 32; }
746  uint16 resolveAnimSlotIndex(const GameObject *obj) const;
747 
749  bool scriptValuesHaveHighWord() const { return isV2(); }
751  bool scriptVarIndexHasPaddingWord() const { return isV2(); }
753  int16 scaleScriptCoord(int16 coord) const { return isV2() ? (int16)(coord * 2) : coord; }
754 
756  int dialogPadW() const { return isAmiga() ? 8 : 18; }
757  int dialogPadH() const { return isAmiga() ? 8 : 16; }
758  int dialogTextInset() const { return isAmiga() ? 4 : 9; }
759  int dialogLineGap() const { return 2; }
760  int portraitBorderPad() const { return isAmiga() ? 2 : 13; }
761  int portraitContentInset() const { return isAmiga() ? 1 : 7; }
762  int portraitTextGap() const { return isAmiga() ? 10 : 18; }
768  int dialogLineHeight() const {
769  if (isAmiga())
770  return _text.amigaTextLinePitch ? (int)_text.amigaTextLinePitch : (int)_text._maxGlyphHeight;
771  return (int)_text._maxGlyphHeight + dialogLineGap();
772  }
773 
775  uint8 depthThresholdForY(int16 charY) const {
776  return isV2() ? (uint8)((uint16)charY >> 1) : (uint8)charY;
777  }
778 
780  void loadBootstrapResources();
785  bool loadSceneGraphics(uint32 sceneIndex);
787  bool loadSceneGraphicsV1(uint32 sceneIndex);
789  bool loadSceneGraphicsV2(uint32 sceneIndex);
790 
794  Common::String getGameId() const;
795 
796  bool hasFeature(EngineFeature f) const override {
797  return (f == kSupportsLoadingDuringRuntime) ||
798  (f == kSupportsSavingDuringRuntime) ||
799  (f == kSupportsReturnToLauncher) ||
800  (f == kSupportsChangingOptionsDuringRuntime);
801  };
802 
803  void syncSoundSettings() override;
804 
805  bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override {
806  return true;
807  }
808  bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override {
809  return true;
810  }
811 
812  Common::Error loadGameState(int slot) override;
813 
819  Common::Error syncGame(Common::Serializer &s);
820  Common::Error syncGameV1(Common::Serializer &s);
821  Common::Error syncGameV2(Common::Serializer &s);
822 
823  Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override {
824  Common::Serializer s(nullptr, stream);
825  return syncGame(s);
826  }
828  Common::Serializer s(stream, nullptr);
829  return syncGame(s);
830  }
831 
832  // Write a raw original-DOS-format save file ("SAVEGAME.N", N=0..9) with no
833  // ScummVM wrapper, so it can be loaded by the original game executable.
834  // The byte layout is produced directly by syncGame (binary-compatible with
835  // saveGameToFile at 1008:6859). Mirrors loadGameState(slot 100..109).
836  Common::Error saveOriginalGameState(int dosSlot);
837 
838  bool tick() override;
839 
840  void sayText(const Common::String &text,
841  Common::TextToSpeechManager::Action action = Common::TextToSpeechManager::INTERRUPT_NO_REPEAT,
842  uint16 speakerObjectId = 0) const;
843  void setTTSVoice(uint16 speakerObjectId) const;
844  void stopTextToSpeech() const;
845 
846  void getHotspotPositions(Common::Array<Graphics::HotspotInfo> &hotspots) override;
847  bool hotspotDirty() const override;
848  void rebuildHotspotSnapshot() const;
849 
852  uint16 index = 0;
853  Common::Point center;
854  };
856  uint16 index = 0;
857  Common::Point position;
858  uint16 orientation = 0;
859  };
860 
861  int currentSceneIndex = -1;
862  uint16 numHotspots = 0;
863  Common::Array<uint16> hotspotColorTable;
864  Common::Array<uint16> hotspotOverrides;
865  Common::Array<SceneHotspotEntry> sceneHotspots;
866  Common::Array<SceneObjectEntry> sceneObjects;
867  bool mapModeActive = false;
868  };
869  mutable HotspotSnapshot _hotspotSnapshot;
870 };
871 
872 extern Macs2Engine *g_engine;
874 Common::String lookupInteractionDisplayName(uint16 interactionId);
875 
876 } // End of namespace Macs2
877 
878 #endif // MACS2_MACS2H
Definition: managed_surface.h:51
virtual int readBuffer(int16 *buffer, const int numSamples)
bool canSaveGameStateCurrently(Common::U32String *msg=nullptr) override
Definition: macs2.h:808
Definition: str.h:59
Common::Array< byte > _amigaPendingSceneScript
Definition: macs2.h:483
uint8 depthThresholdForY(int16 charY) const
Definition: macs2.h:775
EngineFeature
Definition: engine.h:282
bool scriptValuesHaveHighWord() const
Definition: macs2.h:749
Definition: macs2.h:404
bool hasFeature(EngineFeature f) const override
Definition: macs2.h:796
Definition: stream.h:77
Audio::SoundHandle _currentSoundHandle
Definition: macs2.h:655
Definition: error.h:81
virtual Audio::Timestamp getLength() const
Definition: gameobjects.h:107
Definition: macs2.h:128
virtual bool isStereo() const
Definition: macs2.h:52
Definition: advancedDetector.h:164
void clear()
Definition: array.h:321
int dialogLineHeight() const
Definition: macs2.h:768
uint16 overloadAnimSlot() const
Definition: macs2.h:738
bool isBottomHudVisible() const
Definition: macs2.h:718
Definition: amiga_archive.h:100
uint16 maxAnimSlots() const
Definition: macs2.h:734
virtual int getRate() const
Definition: macs2.h:851
Definition: path.h:52
bool hasNativeHudAssets() const
Definition: macs2.h:722
uint16 maxHotspots() const
Definition: macs2.h:742
Definition: timestamp.h:83
Definition: music.h:61
Definition: stream.h:745
int _rate
Definition: macs2.h:57
Definition: events.h:131
Engine * g_engine
Definition: scriptexecutor.h:107
Definition: serializer.h:80
Definition: audiostream.h:212
bool shouldQuit() const override
Definition: macs2.h:285
Common::Error loadGameStream(Common::SeekableReadStream *stream) override
Definition: macs2.h:827
constexpr int PALETTE_COUNT
Definition: palette.h:42
Definition: macs2.h:142
Definition: mixer.h:49
Definition: text.h:40
uint32 getMcsDirectoryOffset() const
Definition: macs2.h:690
Audio::SoundHandle _speechSoundHandle
Definition: macs2.h:657
Definition: macs2.h:411
uint16 ticksPerGameFrame() const
Definition: macs2.h:731
Definition: macs2.h:408
Definition: hashmap.h:85
int16 scaleScriptCoord(int16 coord) const
Definition: macs2.h:753
Definition: ustr.h:57
Definition: file.h:47
static bool shouldQuit()
bool scriptVarIndexHasPaddingWord() const
Definition: macs2.h:751
Definition: macs2.h:272
McsFileVersion
Definition: macs2.h:295
Definition: rect.h:144
Definition: macs2.h:238
int screenHeight()
Definition: macs2.h:700
virtual bool endOfData() const
size_type size() const
Definition: array.h:316
bool canLoadGameStateCurrently(Common::U32String *msg=nullptr) override
Definition: macs2.h:805
Definition: memstream.h:43
Definition: sprite.h:30
Definition: pathfinding.h:57
int dialogPadW() const
Definition: macs2.h:756
uint16 maxOrientations() const
Definition: macs2.h:736
uint32 timerTickMs() const
Definition: macs2.h:729
uint maxObjectResources() const
Definition: macs2.h:744
Definition: macs2.h:104
Definition: macs2.h:855
Definition: macs2.h:416
T MAX(T a, T b)
Definition: util.h:66
Definition: system.h:166
Simple class for handling a palette data.
Definition: palette.h:61
Definition: actionbar.h:31
virtual bool seek(const Audio::Timestamp &where)
Definition: macs2.h:149
Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave=false) override
Definition: macs2.h:823
Definition: macs2.h:196
Add "-demo" to gameid.
Definition: advancedDetector.h:157
Common::Platform platform
Definition: advancedDetector.h:199
Definition: macs2.h:665
Definition: engine.h:149
Platform
Definition: platform.h:93
Common::String _ttsPreviousSaid
Definition: macs2.h:487