[Scummvm-git-logs] scummvm master -> 62980da2baf35f7fa6d818b3306dfb0a83649a97
mgerhardy
noreply at scummvm.org
Sun Aug 23 19:14:04 UTC 2026
This automated email contains information about 18 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
e56e4430c1 MACS2: implemented soft restart from action bar
8222ad0a06 MACS2: removed duplicated version check
29ff99203f MACS2: prepare for v2 loading
e2f7dd8e1c MACS2: load v2 scene images
e14828f78a MACS2: read v2 resource file layout
158e5d307f MACS2: this data is not available for v2, too
314fdbcf4b MACS2: more v2 data points connected
cbf51f43ea MACS2: support v2 scaling for shading tables
80404bd795 MACS2: removed magic numbers
7fa9a6deeb MACS2: fixed member name
bee5a75f26 MACS2: replaced magic numbers
9905b340f2 MACS2: removed magic numbers
88cfefbc7f MACS2: renamed variables - they are no constants anymore
8ff8fa25e6 MACS2: reduced scope
7fa2422e40 MACS2: minor opcode cleanup and fixed max hotspots
f97da85669 MACS2: ignore enhancement here if there is a native action bar
959699743c MACS2: fixed cursor hotspots
62980da2ba MACS2: fixed v2 map input
Commit: e56e4430c1d91a864e0655779b2662d414f1f26f
https://github.com/scummvm/scummvm/commit/e56e4430c1d91a864e0655779b2662d414f1f26f
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-08-23T20:53:56+02:00
Commit Message:
MACS2: implemented soft restart from action bar
Changed paths:
engines/macs2/actionbar.cpp
engines/macs2/macs2.cpp
engines/macs2/macs2.h
diff --git a/engines/macs2/actionbar.cpp b/engines/macs2/actionbar.cpp
index 9874e9b76e4..3baefde36aa 100644
--- a/engines/macs2/actionbar.cpp
+++ b/engines/macs2/actionbar.cpp
@@ -845,11 +845,8 @@ bool ActionBar::handleClickNative(const Common::Point &pos) {
g_engine->_optionsSubMode = 2;
refreshSaveSlotNames();
} else if (id == 0x20) {
- // Soft restart requires dialect-v2 reinit; not available until that loader lands.
- debugC(1, kDebugScript, "ActionBar: restart button ignored (no soft-restart yet)");
- g_engine->_menuMode = 1;
- g_engine->_optionsSubMode = 0;
- g_engine->setCursorMode(Script::MouseMode::PanelCursor);
+ g_engine->softRestart();
+ return true;
} else if (id == 0x21) {
::GUI::MessageDialog quitDialog(
Common::U32String("Quit the game?"),
diff --git a/engines/macs2/macs2.cpp b/engines/macs2/macs2.cpp
index 2f920291468..e204bfb1fc9 100644
--- a/engines/macs2/macs2.cpp
+++ b/engines/macs2/macs2.cpp
@@ -361,6 +361,71 @@ void Macs2Engine::readExecutable() {
exeFileStream->read(containerInventoryIconIndices.data(), 12);
}
+void Macs2Engine::softRestart() {
+ getMusic()->stopMusic();
+ stopSample();
+ stopSpeech();
+ clearDeltaAnim();
+ _skipSpeed = 1;
+ _menuMode = 1;
+ _optionsSubMode = 0;
+ _inventScroll = 1;
+ _saveListScroll = 1;
+
+ if (_scriptExecutor != nullptr) {
+ _scriptExecutor->_waitForDeltaAnim = false;
+ _scriptExecutor->_waitForDeltaSpeed = false;
+ _scriptExecutor->_waitForPcmSound = false;
+ _scriptExecutor->_waitForMusicControl = false;
+ _scriptExecutor->_waitForAdlibReady = false;
+ _scriptExecutor->_waitForObjectAnimStep = false;
+ _scriptExecutor->_waitForSpecialAnimStep = false;
+ _scriptExecutor->_waitingForUiClick = false;
+ _scriptExecutor->endFrameWait();
+ _scriptExecutor->releaseObjectStream();
+ }
+
+ View1 *currentView = (View1 *)findView("View1");
+ if (currentView != nullptr) {
+ for (Character *c : currentView->_characters)
+ delete c;
+ currentView->_characters.clear();
+ currentView->flushPendingCharacterDeletes();
+ currentView->_inventoryItems.clear();
+ currentView->_activeInventoryItem = nullptr;
+ currentView->_isShowingDialoguePanel = false;
+ currentView->_isDialogueChoiceInputActive = false;
+ currentView->_isShowingTextBox = false;
+ currentView->currentSpeechActData = SpeechActData();
+ }
+
+ for (uint i = 0; i < GameObjects::instance()._objects.size(); i++)
+ delete GameObjects::instance()._objects[i];
+ GameObjects::instance()._objects.clear();
+
+ delete Scenes::instance()._currentSceneScript;
+ delete Scenes::instance()._currentSceneStrings;
+ Scenes::instance()._currentSceneScript = nullptr;
+ Scenes::instance()._currentSceneStrings = nullptr;
+ Scenes::instance()._currentSceneSpecialAnimOffsets.clear();
+
+ _backgroundAnimations.clear();
+ _backgroundAnimationsBlobs.clear();
+ clearDeltaAnim();
+
+ delete _fileStream;
+ _fileStream = nullptr;
+
+ readResourceFile();
+
+ if (currentView != nullptr) {
+ currentView->_backgroundSurface.copyFrom(_sceneBackground);
+ currentView->_paletteDirty = true;
+ currentView->redraw();
+ }
+ runScriptExecutor();
+}
+
void Macs2Engine::loadBootstrapResources() {
if (isAmiga())
readAmigaResources();
diff --git a/engines/macs2/macs2.h b/engines/macs2/macs2.h
index f55bcd9f747..a3a9e2d1465 100644
--- a/engines/macs2/macs2.h
+++ b/engines/macs2/macs2.h
@@ -294,6 +294,8 @@ public:
McsFileVersion detectMcsFileVersion(Common::SeekableReadStream &stream) const;
/** Load AHFFMACS0100 layout (loadResourceFile @ 1008:2e8d). */
void loadResourceFileV1();
+ /** Soft restart (options button 0x20 / Macs2PretReInit). */
+ void softRestart();
const char *getResourceMcsFilename() const;
/** Amiga: open DataA/Mdir, load OO objects as GameObjects, cursors, and scene stubs. */
void readAmigaResources();
Commit: 8222ad0a0626bb801dadc33efcd7ba1b7bd468b0
https://github.com/scummvm/scummvm/commit/8222ad0a0626bb801dadc33efcd7ba1b7bd468b0
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-08-23T20:53:57+02:00
Commit Message:
MACS2: removed duplicated version check
Changed paths:
engines/macs2/macs2.cpp
diff --git a/engines/macs2/macs2.cpp b/engines/macs2/macs2.cpp
index e204bfb1fc9..5f4cd19894e 100644
--- a/engines/macs2/macs2.cpp
+++ b/engines/macs2/macs2.cpp
@@ -158,17 +158,12 @@ void Macs2Engine::readResourceFile() {
file->read(fileData, size);
delete file;
_fileStream = new Common::MemoryReadStream(fileData, (uint32)size, DisposeAfterUse::YES);
+ loadResourceFileV1();
} else {
delete file;
error("readResourceFile(): unrecognized MCS magic in %s", mcsName);
}
}
-
- _mcsFileVersion = detectMcsFileVersion(*_fileStream);
- if (_mcsFileVersion != McsFileVersion::V1)
- error("readResourceFile(): unrecognized MCS magic (expected %s)", kMcsMagicV1);
-
- loadResourceFileV1();
}
void Macs2Engine::loadResourceFileV1() {
Commit: 29ff99203f5a3279761bde488205972735793101
https://github.com/scummvm/scummvm/commit/29ff99203f5a3279761bde488205972735793101
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-08-23T20:53:57+02:00
Commit Message:
MACS2: prepare for v2 loading
Changed paths:
engines/macs2/macs2.cpp
engines/macs2/macs2.h
diff --git a/engines/macs2/macs2.cpp b/engines/macs2/macs2.cpp
index 5f4cd19894e..6fda5df6e6e 100644
--- a/engines/macs2/macs2.cpp
+++ b/engines/macs2/macs2.cpp
@@ -611,6 +611,10 @@ bool Macs2Engine::loadSceneGraphics(uint32 sceneIndex) {
return true;
}
+ return loadSceneGraphicsV1(sceneIndex);
+}
+
+bool Macs2Engine::loadSceneGraphicsV1(uint32 sceneIndex) {
const uint32 newSceneIndex = sceneIndex;
// Background image
diff --git a/engines/macs2/macs2.h b/engines/macs2/macs2.h
index a3a9e2d1465..2f26eac5718 100644
--- a/engines/macs2/macs2.h
+++ b/engines/macs2/macs2.h
@@ -828,6 +828,8 @@ public:
* Called from changeScene; Amiga uses native MXMM from DataA.
*/
bool loadSceneGraphics(uint32 sceneIndex);
+ /** AHFFMACS0100 scene package (RLE maps). */
+ bool loadSceneGraphicsV1(uint32 sceneIndex);
/**
* Returns the game Id
Commit: e2f7dd8e1c64115f51841e85aece143cef4c3fdd
https://github.com/scummvm/scummvm/commit/e2f7dd8e1c64115f51841e85aece143cef4c3fdd
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-08-23T20:53:57+02:00
Commit Message:
MACS2: load v2 scene images
Changed paths:
engines/macs2/macs2.cpp
engines/macs2/macs2.h
diff --git a/engines/macs2/macs2.cpp b/engines/macs2/macs2.cpp
index 6fda5df6e6e..4722b9d3fe3 100644
--- a/engines/macs2/macs2.cpp
+++ b/engines/macs2/macs2.cpp
@@ -611,6 +611,9 @@ bool Macs2Engine::loadSceneGraphics(uint32 sceneIndex) {
return true;
}
+ if (isV2())
+ return loadSceneGraphicsV2(sceneIndex);
+
return loadSceneGraphicsV1(sceneIndex);
}
@@ -785,6 +788,177 @@ bool Macs2Engine::loadSceneGraphicsV1(uint32 sceneIndex) {
return true;
}
+bool Macs2Engine::loadSceneGraphicsV2(uint32 sceneIndex) {
+ if (_fileStream == nullptr)
+ return false;
+
+ Common::SeekableReadStream *stream = _fileStream;
+ stream->seek(_mcsDirectoryOffset + 0xC * sceneIndex - 0xC, SEEK_SET);
+ const uint32 bgImageOffset = stream->readUint32LE();
+ (void)stream->readUint32LE();
+ (void)stream->readUint32LE();
+
+ if (bgImageOffset == 0 || bgImageOffset >= (uint32)stream->size())
+ return false;
+
+ stream->seek(bgImageOffset, SEEK_SET);
+ if (!readMegaPicImage(stream, kWinScreenWidth, kWinGameHeight, _sceneBackground))
+ return false;
+
+ stream->read(_palVanilla, 0x300);
+ memcpy(_pal, _palVanilla, 0x300);
+ for (int i = 0; i < 256 * 3; i++)
+ _pal[i] = (_pal[i] * 259 + 33) >> 6;
+
+ if (_panelRemapTable.size() != 0x100)
+ _panelRemapTable.resize(0x100);
+ stream->read(_panelRemapTable.data(), 0x100);
+ stream->readByte();
+ stream->readByte();
+ stream->readByte();
+ _shadingTable.clear();
+ _shadingTable.resize(0x2000, 0);
+ if (stream->read(_shadingTable.data(), 0x2000) != 0x2000)
+ return false;
+
+ Graphics::ManagedSurface depthFull;
+ if (!readMegaPicImage(stream, kWinScreenWidth, kWinGameHeight, depthFull))
+ return false;
+ _depthMap.copyFrom(depthFull);
+
+ auto upscaleHalfRes = [](const Graphics::ManagedSurface &half, Graphics::ManagedSurface &full) {
+ full.create(kWinScreenWidth, kWinGameHeight, Graphics::PixelFormat::createFormatCLUT8());
+ for (int y = 0; y < half.h; y++) {
+ for (int x = 0; x < half.w; x++) {
+ const byte p = half.getPixel(x, y);
+ const int dx = x * 2;
+ const int dy = y * 2;
+ full.setPixel(dx, dy, p);
+ full.setPixel(dx + 1, dy, p);
+ full.setPixel(dx, dy + 1, p);
+ full.setPixel(dx + 1, dy + 1, p);
+ }
+ }
+ };
+
+ Graphics::ManagedSurface half;
+ if (!readMegaPicImage(stream, kScreenWidth, kGameHeight, half))
+ return false;
+ upscaleHalfRes(half, _pathfindingMap);
+
+ if (!readMegaPicImage(stream, kScreenWidth, kGameHeight, half))
+ return false;
+ upscaleHalfRes(half, _shadowMap);
+
+ if (!readMegaPicImage(stream, kScreenWidth, kGameHeight, half))
+ return false;
+ upscaleHalfRes(half, _hotspotMap);
+
+ pathfindingPoints.clear();
+ for (int i = 0; i < 16; i++) {
+ PathfindingPoint current;
+ current._index = i;
+ current._position.x = (int16)(stream->readUint16LE() << 1);
+ current._position.y = (int16)(stream->readUint16LE() << 1);
+ uint8 adj[8];
+ stream->read(adj, 8);
+ stream->skip(8);
+ const uint16 numConnections = stream->readUint16LE();
+ current._adjacentPoints.clear();
+ for (uint16 j = 0; j < numConnections && j < 4; j++)
+ current._adjacentPoints.push_back(adj[j]);
+ pathfindingPoints.push_back(current);
+ }
+ stream->skip(0x2c0 - 0x160);
+
+ _numHotspots = stream->readUint16LE();
+ _hotspotColorTable.clear();
+ _hotspotColorTable.resize(0x40 / sizeof(uint16));
+ stream->read(_hotspotColorTable.data(), 0x40);
+
+ const uint16 numBackgroundAnimations = stream->readUint16LE();
+ _backgroundAnimations.clear();
+ _backgroundAnimationsBlobs.clear();
+ _backgroundAnimations.resize(numBackgroundAnimations);
+ _backgroundAnimationsBlobs.resize(numBackgroundAnimations);
+ for (uint16 i = 0; i < numBackgroundAnimations; i++) {
+ BackgroundAnimationBlob ¤tBlob = _backgroundAnimationsBlobs[i];
+ BackgroundAnimation ¤t = _backgroundAnimations[i];
+ const uint16 halfX = stream->readUint16LE();
+ const uint16 halfY = stream->readUint16LE();
+ const uint32 animSize = stream->readUint32LE();
+ currentBlob._blob.clear();
+ if (animSize > 0 && animSize < 0x1000000) {
+ currentBlob._blob.resize(animSize);
+ if (stream->read(currentBlob._blob.data(), animSize) != animSize)
+ return false;
+ }
+ currentBlob._unknown0C = stream->readUint16LE();
+ (void)stream->readByte();
+ const uint8 flagX = stream->readByte();
+ const uint8 flagY = stream->readByte();
+ currentBlob._unknown0E = stream->readByte();
+ (void)stream->readByte();
+
+ uint16 x = (uint16)(halfX << 1);
+ uint16 y = (uint16)(halfY << 1);
+ if (flagX)
+ x = (uint16)(x + 1);
+ if (flagY)
+ y = (uint16)(y + 1);
+ current._x = x;
+ current._y = y;
+ currentBlob._x = x;
+ currentBlob._y = y;
+
+ AnimBlobView blobView(currentBlob._blob);
+ const uint16 numFrames = blobView.isValid() ? blobView.sequenceLength() : 0;
+ current._frameIndex = 0;
+ current._frames.resize(numFrames);
+ const uint16 actualFrameCount = blobView.isValid() ? blobView.frameCount() : 0;
+ for (uint16 j = 0; j < actualFrameCount && j < numFrames; j++) {
+ AnimBlobView::FrameInfo fi;
+ if (!blobView.getFrameInfo(j, fi))
+ break;
+ current._frames[j]._width = fi.width;
+ current._frames[j]._height = fi.height;
+ current._frames[j]._data.resize((uint)fi.width * (uint)fi.height);
+ memcpy(current._frames[j]._data.data(), fi.pixels, (uint)fi.width * (uint)fi.height);
+ }
+ }
+
+ _numPathfindingPoints = stream->readUint16LE();
+ if (_numPathfindingPoints == 0 || _numPathfindingPoints > 16)
+ _numPathfindingPoints = 16;
+ (void)stream->readUint16LE();
+ (void)stream->readUint16LE();
+ _walkDepthThresholdY = (uint16)(stream->readUint16LE() << 1);
+ _walkDepthScaleFactor = stream->readUint16LE();
+ _walkBaseSpeedPct = stream->readUint16LE();
+ _scenePaletteMode = stream->readUint16LE();
+ _paletteDarkenPercent = stream->readUint16LE();
+
+ _mapImageFileOffset = 0;
+ _mapSubSceneTableFilePos = 0;
+
+ stream->seek(_mcsDirectoryOffset + 0xC * sceneIndex - 0x8, SEEK_SET);
+ const uint32 scriptBlobOffset = stream->readUint32LE();
+ _sceneResourceOffsets.clear();
+ clearDeltaAnim();
+ if (scriptBlobOffset != 0 && scriptBlobOffset < (uint32)stream->size()) {
+ const int64 saved = stream->pos();
+ stream->seek(scriptBlobOffset, SEEK_SET);
+ _sceneResourceOffsets.resize(0x200 / 4);
+ if (stream->read(_sceneResourceOffsets.data(), 0x200) != 0x200)
+ _sceneResourceOffsets.clear();
+ stream->seek(saved, SEEK_SET);
+ }
+
+ applyPaletteDarkening();
+ return true;
+}
+
+
void Macs2Engine::changeScene(uint32 newSceneIndex, bool executeScript) {
// Release old scene resources
_backgroundAnimations.clear();
diff --git a/engines/macs2/macs2.h b/engines/macs2/macs2.h
index 2f26eac5718..af39f8610fd 100644
--- a/engines/macs2/macs2.h
+++ b/engines/macs2/macs2.h
@@ -830,6 +830,8 @@ public:
bool loadSceneGraphics(uint32 sceneIndex);
/** AHFFMACS0100 scene package (RLE maps). */
bool loadSceneGraphicsV1(uint32 sceneIndex);
+ /** AHFFMACS0200 ReadyModule scene package (MegaPic + half-res masks). */
+ bool loadSceneGraphicsV2(uint32 sceneIndex);
/**
* Returns the game Id
Commit: e14828f78a867e376cadfbe967453ef72a472daa
https://github.com/scummvm/scummvm/commit/e14828f78a867e376cadfbe967453ef72a472daa
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-08-23T20:53:57+02:00
Commit Message:
MACS2: read v2 resource file layout
Changed paths:
engines/macs2/gameobjects.h
engines/macs2/macs2.cpp
engines/macs2/macs2.h
diff --git a/engines/macs2/gameobjects.h b/engines/macs2/gameobjects.h
index 4064f1dcf34..92f91956833 100644
--- a/engines/macs2/gameobjects.h
+++ b/engines/macs2/gameobjects.h
@@ -141,6 +141,8 @@ public:
// Runtime +0x186: per-object flag loaded from file. When set, character sprites
// are scaled based on Y position (perspective depth scaling).
bool _hasScaling = false;
+ // V2: half-res anim data drawn at 2x
+ bool _hasDoubleResAnim = false;
// Runtime field +0x231: "frozen/attached" flag. Set by scriptSetObjectBounds (opcode 0x35).
// When set, the object cannot be walked (opcode 0x11 returns error 0x1F)
// and walkAlongPath skips movement for this object.
diff --git a/engines/macs2/macs2.cpp b/engines/macs2/macs2.cpp
index 4722b9d3fe3..20396da4143 100644
--- a/engines/macs2/macs2.cpp
+++ b/engines/macs2/macs2.cpp
@@ -70,7 +70,7 @@ void resetCharacterWalkPath(Character *character) {
Macs2Engine *g_engine;
-Graphics::ManagedSurface Macs2Engine::readRLEImage(int64 offs, Common::MemoryReadStream *stream) {
+Graphics::ManagedSurface Macs2Engine::readRLEImage(int64 offs, Common::SeekableReadStream *stream) {
stream->seek(offs);
Graphics::ManagedSurface result;
@@ -159,10 +159,33 @@ void Macs2Engine::readResourceFile() {
delete file;
_fileStream = new Common::MemoryReadStream(fileData, (uint32)size, DisposeAfterUse::YES);
loadResourceFileV1();
+ } else if (_mcsFileVersion == McsFileVersion::V2) {
+ _mcsDirectoryOffset = kMcsV2DirectoryOffset;
+ debugC(1, kDebugFilePath, "MCS %s: AHFFMACS0200, directory @ 0x%x", mcsName, _mcsDirectoryOffset);
+ file->seek(0, SEEK_SET);
+ _fileStream = file; // large archives stay file-backed
+ loadResourceFileV2();
+ _scriptExecutor->setOpcodeTable(Script::ScriptExecutor::kV2OpcodeTable,
+ Script::ScriptExecutor::kV2OpcodeTableSize);
} else {
delete file;
error("readResourceFile(): unrecognized MCS magic in %s", mcsName);
}
+
+ // Initialize border sprites from cursor image array entries at fixed indices.
+ // Original loadResourceFile (1008:2e8d) calls changeScene(g_wCurrentSceneIndex) before
+ // returning, ensuring all scene data (pathfinding maps, depth map, palette, background)
+ // is loaded before the game loop processes any input.
+ // The original allocates the 0x75E0-byte scene data buffer (which includes space for
+ // all RLE-decoded maps) before calling changeScene. Create the surfaces here.
+ const int sw = screenWidth();
+ const int gh = gameHeight();
+ _sceneBackground.create(sw, gh, Graphics::PixelFormat::createFormatCLUT8());
+ _depthMap.create(sw, gh, Graphics::PixelFormat::createFormatCLUT8());
+ _pathfindingMap.create(sw, gh, Graphics::PixelFormat::createFormatCLUT8());
+ _shadowMap.create(sw, gh, Graphics::PixelFormat::createFormatCLUT8());
+ _hotspotMap.create(sw, gh, Graphics::PixelFormat::createFormatCLUT8());
+ changeScene(Scenes::instance()._currentSceneIndex);
}
}
@@ -208,13 +231,259 @@ void Macs2Engine::loadResourceFileV1() {
}
_fileStream->seek(kMcsV1ActorIndexOffset, SEEK_SET);
- Scenes::instance()._currentActorIndex = _fileStream->readUint16LE();
+ bootstrapMcsActorsObjectsAndScene();
+}
+
+void Macs2Engine::loadResourceFileV2() {
+ // File layout after Directory:
+ // 0x300 sprite palette
+ // 4 words -> ShowText recolor colors
+ // panelTopY + panelHeight
+ // 6 megapics (flag word; if nonzero, panelH rows of RLE)
+ // button count + per-button metadata + inline anim blobs
+ // inventory grid + text layout words
+ // TalkVol + Font1 + SysFont + 0x400 map offsets
+ _shadingTable.clear();
+ _shadingTable.resize(0x800, 0);
+ numGlyphs = 0;
+ numPanelGlyphs = 0;
+ memset(_mapSceneOffsets, 0, sizeof(_mapSceneOffsets));
+ _imageResources.clear();
+ _imageResources.resize(33);
+ for (int i = 0; i < 33; i++)
+ _cursorHotspots[i] = Common::Point(0, 0);
+ _hudButtons.clear();
+ for (int i = 0; i < 6; i++) {
+ _hudMegapicLoaded[i] = false;
+ _hudMegapics[i].free();
+ }
+ _panelTopY = 0;
+ _panelHeight = 0;
+ _menuMode = 1;
+ _optionsSubMode = 0;
+ _savedMenuCursorMode = Script::MouseMode::Walk;
+ _inventScroll = 1;
+ memset(_hudTextLayout, 0, sizeof(_hudTextLayout));
+ memset(_hudTextRecolor, 0, sizeof(_hudTextRecolor));
+ _talkVol = 0;
+
+ if (_fileStream == nullptr)
+ return;
+
+ _fileStream->seek(_mcsDirectoryOffset + 0x3000, SEEK_SET);
+ _fileStream->read(_palVanilla, 0x300);
+ memcpy(_pal, _palVanilla, 0x300);
+
+ for (int i = 0; i < 4; i++)
+ _hudTextRecolor[i] = _fileStream->readUint16LE();
+
+ _panelTopY = _fileStream->readUint16LE();
+ _panelHeight = _fileStream->readUint16LE();
+ if (_panelTopY == 0 || _panelHeight == 0) {
+ warning("readGlobalAssetsV2: invalid panel geometry %u+%u", _panelTopY, _panelHeight);
+ _panelTopY = 280;
+ _panelHeight = 146;
+ }
+
+ for (int i = 0; i < 6; i++) {
+ const uint16 flag = _fileStream->readUint16LE();
+ if (flag == 0)
+ continue;
+ if (!readMegaPicImage(_fileStream, kWinScreenWidth, _panelHeight, _hudMegapics[i])) {
+ warning("readGlobalAssetsV2: failed loading UI megapic %d", i);
+ return;
+ }
+ _hudMegapicLoaded[i] = true;
+ }
+
+ const uint16 buttonCount = _fileStream->readUint16LE();
+ struct CursorMap {
+ uint16 cid;
+ uint16 mouseNr;
+ bool active;
+ };
+ static const CursorMap kCursorMap[] = {
+ {0x6E, 0x16, true},
+ {0x6F, 0x16, false},
+ {0x69, 0x13, true},
+ {0x6A, 0x13, false},
+ {0x64, 0x14, true},
+ {0x65, 0x14, false},
+ {0x6B, 0x15, true},
+ {0x6C, 0x15, false},
+ {0x66, 0x17, true},
+ {0x67, 0x17, false},
+ {0x68, 0x19, true},
+ {0x6D, 0x1A, true},
+ };
+
+ auto extractAnimStepFrame = [](Common::Array<uint8> blob, uint16 step, AnimFrame &out) -> bool {
+ if (blob.empty() || step == 0)
+ return false;
+ const uint32 offset = BackgroundAnimationBlob::advanceAnimFrame(blob, true, (uint16)(step + 0x64));
+ if (offset == 0 || offset + 10 > blob.size())
+ return false;
+ const uint32 frameOff = offset + 6;
+ out._width = READ_LE_UINT16(&blob[frameOff]);
+ out._height = READ_LE_UINT16(&blob[frameOff + 2]);
+ if (out._width == 0 || out._width > 640 || out._height == 0 || out._height > 400)
+ return false;
+ const uint32 pix = (uint32)out._width * (uint32)out._height;
+ if (frameOff + 4 + pix > blob.size())
+ return false;
+ out._data.resize(pix);
+ memcpy(out._data.data(), &blob[frameOff + 4], pix);
+ return true;
+ };
+
+ auto loadBigAnimFirstFrame = [&](int64 animStart, AnimFrame &out) -> bool {
+ _fileStream->seek(animStart + 10, SEEK_SET);
+ const uint16 local8 = _fileStream->readUint16LE();
+ _fileStream->seek(animStart + local8 + 0x0E, SEEK_SET);
+ _fileStream->skip(6);
+ out._width = _fileStream->readUint16LE();
+ out._height = _fileStream->readUint16LE();
+ if (out._width == 0 || out._width > 640 || out._height == 0 || out._height > 400)
+ return false;
+ out._data.resize((uint)out._width * (uint)out._height);
+ return _fileStream->read(out._data.data(), out._data.size()) == out._data.size();
+ };
+
+ for (uint16 b = 1; b <= buttonCount && !_fileStream->eos(); b++) {
+ HudButton button;
+ button.x = (int16)_fileStream->readUint16LE();
+ button.y = (int16)_fileStream->readUint16LE();
+ button.inactiveStep = _fileStream->readUint16LE();
+ button.activeStep = _fileStream->readUint16LE();
+ button.hoverStep = _fileStream->readUint16LE();
+ button.buttonId = _fileStream->readUint16LE();
+ button.menuId = _fileStream->readUint16LE();
+ const uint32 animSize = _fileStream->readUint32LE();
+ if (animSize == 0 || animSize > 0x1000000 || _fileStream->eos())
+ break;
+
+ const int64 animStart = _fileStream->pos();
+ Common::Array<uint8> animBlob;
+ animBlob.resize(animSize);
+ if (_fileStream->read(animBlob.data(), animSize) != animSize)
+ break;
+
+ AnimFrame frame;
+ bool gotFrame = extractAnimStepFrame(animBlob, button.inactiveStep ? button.inactiveStep : 1, frame);
+ if (!gotFrame)
+ gotFrame = loadBigAnimFirstFrame(animStart, frame);
+
+ AnimFrame activeFrame;
+ const bool gotActive = (button.activeStep != 0 && button.activeStep != button.inactiveStep) && extractAnimStepFrame(animBlob, button.activeStep, activeFrame);
+ AnimFrame hoverFrame;
+ const bool gotHover = (button.hoverStep != 0 && button.hoverStep != button.inactiveStep) && extractAnimStepFrame(animBlob, button.hoverStep, hoverFrame);
+
+ if (button.menuId == 7 && gotFrame) {
+ uint16 mouseNr = 0;
+ bool prefer = false;
+ for (const CursorMap &entry : kCursorMap) {
+ if (entry.cid == button.buttonId) {
+ mouseNr = entry.mouseNr;
+ prefer = entry.active;
+ break;
+ }
+ }
+ const int slot = (int)mouseNr - 1;
+ if (mouseNr != 0 && slot >= 0 && slot < 33) {
+ const bool empty = _imageResources[slot]._data.empty();
+ if (empty || prefer) {
+ _imageResources[slot] = prefer && gotActive ? activeFrame : frame;
+ _cursorHotspots[slot] = Common::Point(button.x, button.y);
+ }
+ }
+ } else if (gotFrame) {
+ button.animBlob = Common::move(animBlob);
+ button.frame = Common::move(frame);
+ if (gotActive)
+ button.activeFrame = Common::move(activeFrame);
+ if (gotHover)
+ button.hoverFrame = Common::move(hoverFrame);
+ _hudButtons.push_back(Common::move(button));
+ }
+
+ _fileStream->seek(animStart + (int64)animSize, SEEK_SET);
+ }
+
+ _inventOriginX = _fileStream->readUint16LE();
+ _inventOriginY = _fileStream->readUint16LE();
+ _inventCols = _fileStream->readUint16LE();
+ _inventRows = _fileStream->readUint16LE();
+ _inventSlotW = _fileStream->readUint16LE();
+ _inventSlotH = _fileStream->readUint16LE();
+ _inventLayoutMode = _fileStream->readUint16LE();
+ for (int i = 0; i < 7; i++)
+ _hudTextLayout[i] = _fileStream->readUint16LE();
+
+ if (_inventCols == 0)
+ _inventCols = 4;
+ if (_inventRows == 0)
+ _inventRows = 2;
+
+ _talkVol = _fileStream->readUint16LE();
+ auto loadSizedFont = [&](GlyphData *out, uint16 &outCount, uint16 &outMaxH) -> bool {
+ outCount = 0;
+ outMaxH = 0;
+ const uint32 fontSize = _fileStream->readUint32LE();
+ if (fontSize == 0 || fontSize > 0x100000)
+ return false;
+ const int64 fontStart = _fileStream->pos();
+ const uint16 glyphCount = _fileStream->readUint16LE();
+ if (glyphCount == 0 || glyphCount > 256) {
+ _fileStream->seek(fontStart + (int64)fontSize, SEEK_SET);
+ return false;
+ }
+ for (uint16 i = 0; i < glyphCount; i++) {
+ out[i].readFromMemory(_fileStream);
+ outMaxH = MAX(outMaxH, out[i]._height);
+ }
+ outCount = glyphCount;
+ _fileStream->seek(fontStart + (int64)fontSize, SEEK_SET);
+ return true;
+ };
+ if (!loadSizedFont(_glyphs, numGlyphs, maxGlyphHeight))
+ warning("readGlobalAssetsV2: failed loading Font1");
+ if (!loadSizedFont(_panelGlyphs, numPanelGlyphs, maxPanelGlyphHeight))
+ warning("readGlobalAssetsV2: failed loading SysFont");
+
+ for (int i = 0; i < 256; i++)
+ _mapSceneOffsets[i] = _fileStream->readUint32LE();
+
+ _saveListScroll = 1;
+ _saveSlotNames.clear();
+
+ uint installed = 0;
+ for (uint i = 0; i < _imageResources.size(); i++) {
+ if (!_imageResources[i]._data.empty())
+ installed++;
+ }
+ uint megas = 0;
+ for (int i = 0; i < 6; i++) {
+ if (_hudMegapicLoaded[i])
+ megas++;
+ }
+ debugC(1, kDebugFilePath,
+ "readGlobalAssetsV2: panel=%u+%u megapics=%u buttons=%u cursors=%u invent=%ux%u @(%u,%u) fonts=%u/%u",
+ _panelTopY, _panelHeight, megas, (uint)_hudButtons.size(), installed,
+ _inventCols, _inventRows, _inventOriginX, _inventOriginY,
+ numGlyphs, numPanelGlyphs);
+ _fileStream->seek(kMcsV2ActorIndexOffset, SEEK_SET);
+ bootstrapMcsActorsObjectsAndScene();
+}
+
+void Macs2Engine::bootstrapMcsActorsObjectsAndScene() {
+ Scenes &scenes = Scenes::instance();
+ scenes._currentActorIndex = _fileStream->readUint16LE();
uint16 firstSceneIndex = _fileStream->readUint16LE();
- Scenes::instance()._currentSceneIndex = firstSceneIndex;
- Scenes::instance()._currentSceneScript = Scenes::instance().readSceneScript(firstSceneIndex, _fileStream);
- Scenes::instance()._currentSceneStrings = Scenes::instance().readSceneStrings(firstSceneIndex, _fileStream);
- Scenes::instance()._currentSceneSpecialAnimOffsets = Scenes::instance().readSpecialAnimsOffsets(firstSceneIndex, _fileStream);
- _scriptExecutor->setScript(Scenes::instance()._currentSceneScript);
+ scenes._currentSceneIndex = firstSceneIndex;
+ scenes._currentSceneScript = scenes.readSceneScript(firstSceneIndex, _fileStream);
+ scenes._currentSceneStrings = scenes.readSceneStrings(firstSceneIndex, _fileStream);
+ scenes._currentSceneSpecialAnimOffsets = scenes.readSpecialAnimsOffsets(firstSceneIndex, _fileStream);
+ _scriptExecutor->setScript(scenes._currentSceneScript);
// Load object data (512 entries max, matching original loadResourceFile)
// Original allocates all 512 slots, then frees unused ones. We pre-fill with nullptr.
@@ -235,44 +504,63 @@ void Macs2Engine::loadResourceFileV1() {
gameObject->_dataOffset = objectOffset;
// Object header (ReadyObject / initGameObject): x, y, scene, orientation, vertical scale
- uint16 x = _fileStream->readUint16LE();
+ uint16 x = _fileStream->readUint16LE(); // TODO: use _engine->scaleScriptCoord
uint16 y = _fileStream->readUint16LE();
+ if (isV2()) {
+ x = (uint16)(x << 1);
+ y = (uint16)(y << 1);
+ }
gameObject->_position = Common::Point(x, y);
gameObject->_sceneIndex = _fileStream->readUint16LE();
gameObject->_orientation = _fileStream->readUint16LE();
gameObject->_verticalOffsetScale = _fileStream->readUint16LE();
const uint16 animSlotCount = maxAnimSlots();
- for (int j = 1; j <= (int)animSlotCount; j++) {
- // Per-slot: animID, sourceKey, dataSize, data, speed, mirrorFlag, discarded byte
- _fileStream->readUint16LE(); // runtime+0x24: animation slot ID (editor metadata)
- uint16 blobSourceKey = _fileStream->readUint16LE();
- uint32 dataSize = _fileStream->readUint32LE();
- uint8 *data = new uint8[dataSize];
- _fileStream->read(data, dataSize);
- gameObject->_blobs.push_back(Common::Array<uint8>(data, dataSize));
- delete[] data;
- gameObject->_blobSourceKeys.push_back(blobSourceKey);
- uint16 blobSpeed = _fileStream->readUint16LE();
- gameObject->_blobWalkSpeeds.push_back(blobSpeed);
- uint16 blobMirrorFlag = _fileStream->readByte();
- _fileStream->readByte(); // slot loaded flag (runtime-only, discarded from file)
- gameObject->_blobMirrorFlags.push_back(blobMirrorFlag != 0);
-
- if (blobMirrorFlag != 0) {
- debugC(kDebugScript, "Object %.4x need to mirror blob %4.x", i, j);
- if (dataSize > 0) {
+ if (isV2()) {
+ // ReadyObject: lead word, then slots; payload filled later by loadObjectData.
+ _fileStream->readUint16LE();
+ for (int j = 0; j < (int)animSlotCount; j++) {
+ _fileStream->readUint16LE(); // animID
+ _fileStream->readUint16LE(); // sourceKey
+ uint32 dataSize = _fileStream->readUint32LE();
+ if (dataSize > 0)
+ _fileStream->skip(dataSize);
+ _fileStream->readUint16LE(); // speed
+ _fileStream->readByte(); // mirror
+ _fileStream->readByte(); // pad
+ gameObject->_blobs.push_back(Common::Array<uint8>());
+ gameObject->_blobSourceKeys.push_back(0);
+ gameObject->_blobWalkSpeeds.push_back(0);
+ gameObject->_blobMirrorFlags.push_back(false);
+ }
+ _fileStream->readByte();
+ gameObject->_hasShading = _fileStream->readByte() != 0;
+ gameObject->_hasScaling = _fileStream->readByte() != 0;
+ gameObject->_hasDoubleResAnim = _fileStream->readByte() != 0;
+ } else {
+ for (int j = 1; j <= (int)animSlotCount; j++) {
+ _fileStream->readUint16LE(); // animID
+ uint16 blobSourceKey = _fileStream->readUint16LE();
+ uint32 dataSize = _fileStream->readUint32LE();
+ uint8 *data = new uint8[dataSize];
+ _fileStream->read(data, dataSize);
+ gameObject->_blobs.push_back(Common::Array<uint8>(data, dataSize));
+ delete[] data;
+ gameObject->_blobSourceKeys.push_back(blobSourceKey);
+ uint16 blobSpeed = _fileStream->readUint16LE();
+ gameObject->_blobWalkSpeeds.push_back(blobSpeed);
+ uint16 blobMirrorFlag = _fileStream->readByte();
+ _fileStream->readByte();
+ gameObject->_blobMirrorFlags.push_back(blobMirrorFlag != 0);
+ if (blobMirrorFlag != 0 && dataSize > 0)
BackgroundAnimationBlob::mirrorAnimBlob(gameObject->_blobs.back());
- }
}
+ _fileStream->readByte();
+ gameObject->_hasShading = _fileStream->readByte() != 0;
+ gameObject->_hasScaling = _fileStream->readByte() != 0;
}
- // Per-object flags after anim slots (loadObjectData -> runtime+0x184..+0x186)
- _fileStream->readByte(); // hasInventoryIcon (derived from slot 0x13)
- gameObject->_hasShading = _fileStream->readByte() != 0; // runtime+0x185
- gameObject->_hasScaling = _fileStream->readByte() != 0; // runtime+0x186
- // Object SCRIPT ptr in directory (+0x17F8). Zero SCRIPT keeps the object (no script table).
- const uint32 scriptPtrOffset = kMcsV1DirectoryOffset + kMcsV1ObjectScriptPtrRel + (uint32)i * 0xC;
+ const uint32 scriptPtrOffset = dir + kMcsV1ObjectScriptPtrRel + (uint32)i * 0xC;
_fileStream->seek(scriptPtrOffset, SEEK_SET);
objectOffset = _fileStream->readUint32LE();
@@ -294,27 +582,17 @@ void Macs2Engine::loadResourceFileV1() {
for (uint r = 0; r < maxObjRes; r++) {
gameObject->_resourceOffsets[r] = _fileStream->readUint32LE();
}
+ if (isV2()) {
+ _fileStream->skip(0x200 - maxObjRes * 4);
+ _fileStream->readUint16LE();
+ _fileStream->readUint16LE();
+ }
uint16 scriptLength = _fileStream->readUint16LE();
gameObject->_script.resize(scriptLength);
_fileStream->read(gameObject->_script.data(), scriptLength);
GameObjects::instance()._objects[i - 1] = gameObject;
}
-
- // Initialize border sprites from cursor image array entries at fixed indices.
- // Original loadResourceFile (1008:2e8d) calls changeScene(g_wCurrentSceneIndex) before
- // returning, ensuring all scene data (pathfinding maps, depth map, palette, background)
- // is loaded before the game loop processes any input.
- // The original allocates the 0x75E0-byte scene data buffer (which includes space for
- // all RLE-decoded maps) before calling changeScene. Create the surfaces here.
- const int sw = screenWidth();
- const int gh = gameHeight();
- _sceneBackground.create(sw, gh, Graphics::PixelFormat::createFormatCLUT8());
- _depthMap.create(sw, gh, Graphics::PixelFormat::createFormatCLUT8());
- _pathfindingMap.create(sw, gh, Graphics::PixelFormat::createFormatCLUT8());
- _shadowMap.create(sw, gh, Graphics::PixelFormat::createFormatCLUT8());
- _hotspotMap.create(sw, gh, Graphics::PixelFormat::createFormatCLUT8());
- changeScene(Scenes::instance()._currentSceneIndex);
}
void Macs2Engine::readExecutable() {
@@ -428,7 +706,7 @@ void Macs2Engine::loadBootstrapResources() {
readResourceFile();
}
-void Macs2Engine::readBackgroundAnimations(Common::MemoryReadStream *stream) {
+void Macs2Engine::readBackgroundAnimations(Common::SeekableReadStream *stream) {
// changeScene (1008:2574): background animation loading at scene+0x50F5.
// Per-entry runtime struct (0x10 bytes stride):
// +0x00: X position (word)
@@ -485,7 +763,7 @@ void Macs2Engine::readBackgroundAnimations(Common::MemoryReadStream *stream) {
}
}
-void Macs2Engine::readImageResources(Common::MemoryReadStream *stream) {
+void Macs2Engine::readImageResources(Common::SeekableReadStream *stream) {
// l0037_3355: Read 33 entries, preserving index alignment (zero-length = empty placeholder).
// Binary uses g_pCursorImageArray[index] directly; indices must match.
for (int i = 0; i < 0x21; i++) {
@@ -2750,6 +3028,10 @@ bool Macs2Engine::loadObjectData(GameObject *obj) {
_fileStream->readByte(); // runtime+0x184 hasInventoryIcon (derived from slot 0x13 in C++)
obj->_hasShading = _fileStream->readByte() != 0;
obj->_hasScaling = _fileStream->readByte() != 0;
+ if (isV2())
+ obj->_hasDoubleResAnim = _fileStream->readByte() != 0;
+ else
+ obj->_hasDoubleResAnim = false;
if (obj->_blobs.size() > 0x11 && !obj->_blobs[0x11].empty()) {
const uint16 frameCount = BackgroundAnimationBlob::getAnimFrameCount(obj->_blobs[0x11]);
@@ -2988,7 +3270,7 @@ void GlyphData::readFromeFile(Common::File &file) {
file.read(_data.data(), _width * _height);
}
-void GlyphData::readFromMemory(Common::MemoryReadStream *stream) {
+void GlyphData::readFromMemory(Common::SeekableReadStream *stream) {
_ascii = stream->readByte();
_width = stream->readUint16LE();
_height = stream->readUint16LE();
@@ -3003,7 +3285,7 @@ void AnimFrame::readFromeFile(Common::File &file) {
file.read(_data.data(), _width * _height);
}
-void AnimFrame::readFromStream(Common::MemoryReadStream *stream) {
+void AnimFrame::readFromStream(Common::SeekableReadStream *stream) {
_width = stream->readUint16LE();
_height = stream->readUint16LE();
_data.resize(_width * _height);
diff --git a/engines/macs2/macs2.h b/engines/macs2/macs2.h
index af39f8610fd..894a921bfb7 100644
--- a/engines/macs2/macs2.h
+++ b/engines/macs2/macs2.h
@@ -109,7 +109,7 @@ struct GlyphData : public Sprite {
char _ascii = 0;
void readFromeFile(Common::File &file);
- void readFromMemory(Common::MemoryReadStream *stream);
+ void readFromMemory(Common::SeekableReadStream *stream);
};
struct AnimFrame : public Sprite {
@@ -117,7 +117,7 @@ struct AnimFrame : public Sprite {
int16 _offsetY = 0;
void readFromeFile(Common::File &file);
- void readFromStream(Common::MemoryReadStream *stream);
+ void readFromStream(Common::SeekableReadStream *stream);
bool pixelHit(const Common::Point &point) const;
Common::Point getBottomMiddleOffset(uint16 scale = 100) const;
};
@@ -281,7 +281,7 @@ protected:
}
public:
- Graphics::ManagedSurface readRLEImage(int64 offs, Common::MemoryReadStream *stream);
+ Graphics::ManagedSurface readRLEImage(int64 offs, Common::SeekableReadStream *stream);
/** Open primary MCS archive, check magic, load v1 or v2 layout. */
void readResourceFile();
@@ -294,6 +294,10 @@ public:
McsFileVersion detectMcsFileVersion(Common::SeekableReadStream &stream) const;
/** Load AHFFMACS0100 layout (loadResourceFile @ 1008:2e8d). */
void loadResourceFileV1();
+ /** Load AHFFMACS0200 layout */
+ void loadResourceFileV2();
+ /** Shared actor/scene/object directory load after dialect-specific globals */
+ void bootstrapMcsActorsObjectsAndScene();
/** Soft restart (options button 0x20 / Macs2PretReInit). */
void softRestart();
const char *getResourceMcsFilename() const;
@@ -332,10 +336,10 @@ public:
void readExecutable();
// Assumes that the stream is at the location of the number of background animations
- void readBackgroundAnimations(Common::MemoryReadStream *stream);
+ void readBackgroundAnimations(Common::SeekableReadStream *stream);
// Assumes that the stream is at the start of the right section
- void readImageResources(Common::MemoryReadStream *stream);
+ void readImageResources(Common::SeekableReadStream *stream);
public:
Macs2Engine(OSystem *osystem, const ADGameDescription *gameDesc);
@@ -410,6 +414,8 @@ public:
Common::Array<uint16> _hotspotOverrides;
Common::Array<Macs2::AnimFrame> _imageResources;
+ /** Per-cursor hotspot from native HUD button metadata (v2); (0,0) = use center. */
+ Common::Point _cursorHotspots[33];
GlyphData _glyphs[256];
GlyphData _panelGlyphs[256]; // Font 2: clean sans-serif font used by save/load panel
@@ -513,7 +519,7 @@ public:
Common::Array<BackgroundAnimation> _backgroundAnimations;
Common::Array<BackgroundAnimationBlob> _backgroundAnimationsBlobs;
- Common::MemoryReadStream *_fileStream = nullptr;
+ Common::SeekableReadStream *_fileStream = nullptr;
McsFileVersion _mcsFileVersion = McsFileVersion::Unknown;
/** Absolute file offset of the 0x3000-byte scene/object directory. */
uint32 _mcsDirectoryOffset = kMcsV1DirectoryOffset;
Commit: 158e5d307f322ac20877b31626c206519d4ef416
https://github.com/scummvm/scummvm/commit/158e5d307f322ac20877b31626c206519d4ef416
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-08-23T20:53:57+02:00
Commit Message:
MACS2: this data is not available for v2, too
Changed paths:
engines/macs2/macs2.cpp
diff --git a/engines/macs2/macs2.cpp b/engines/macs2/macs2.cpp
index 20396da4143..3e7a94707f8 100644
--- a/engines/macs2/macs2.cpp
+++ b/engines/macs2/macs2.cpp
@@ -599,7 +599,7 @@ void Macs2Engine::readExecutable() {
inventoryIconIndices.resize(6);
containerInventoryIconIndices.resize(6);
- if (isAmiga()) {
+ if (isAmiga() || isV2()) {
for (uint i = 0; i < 6; i++) {
inventoryIconIndices[i] = (uint16)(i + 1);
containerInventoryIconIndices[i] = (uint16)(i + 1);
Commit: 314fdbcf4b3673029c479454077b76ba499834f8
https://github.com/scummvm/scummvm/commit/314fdbcf4b3673029c479454077b76ba499834f8
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-08-23T20:53:57+02:00
Commit Message:
MACS2: more v2 data points connected
Changed paths:
engines/macs2/macs2.cpp
engines/macs2/scriptexecutor.cpp
engines/macs2/view1.cpp
diff --git a/engines/macs2/macs2.cpp b/engines/macs2/macs2.cpp
index 3e7a94707f8..66e19db8f84 100644
--- a/engines/macs2/macs2.cpp
+++ b/engines/macs2/macs2.cpp
@@ -1374,6 +1374,14 @@ void Macs2Engine::changeScene(uint32 newSceneIndex, bool executeScript) {
if (!loadSceneGraphics(newSceneIndex))
error("changeScene(): Failed to load scene graphics for scene %u", newSceneIndex);
+ // V2 starts with the main DisplayMenu bar visible; scene
+ // scripts (e.g. world map / overview map) may hideActionBar during isSceneInit
+ if (isV2()) {
+ _menuMode = 1;
+ _optionsSubMode = 0;
+ _bottomHudVisible = true;
+ }
+
// Refresh characters
View1 *currentView = (View1 *)findView("View1");
if (!currentView) {
diff --git a/engines/macs2/scriptexecutor.cpp b/engines/macs2/scriptexecutor.cpp
index bfa6c360783..5682710e879 100644
--- a/engines/macs2/scriptexecutor.cpp
+++ b/engines/macs2/scriptexecutor.cpp
@@ -3895,6 +3895,7 @@ OpcodeResult ScriptExecutor::scriptLoadDistanceMask() {
debugC(kDebugScript, "SCRIPT::loadDistanceMask(index=%u)", resourceIndex);
clearScriptError();
scriptSkipOpcodeRemainder(0x69);
+ // Depth mask is full playfield resolution on both dialects.
if (!_engine->loadMaskFromResource(resourceIndex, _executingScriptObjectId, _engine->_depthMap,
_engine->screenWidth(), _engine->gameHeight(), false))
warning("loadDistanceMask: failed resource %u", resourceIndex);
@@ -3906,8 +3907,13 @@ OpcodeResult ScriptExecutor::scriptLoadAreaMask() {
debugC(kDebugScript, "SCRIPT::loadAreaMask(index=%u)", resourceIndex);
clearScriptError();
scriptSkipOpcodeRemainder(0x6A);
+ // V2 stores hotspot/path/shadow masks at half res (320x200) and upscales
+ // depth above is already full 640x400
+ const bool halfRes = _engine->isV2();
+ const int w = halfRes ? kScreenWidth : _engine->screenWidth();
+ const int h = halfRes ? kGameHeight : _engine->gameHeight();
if (!_engine->loadMaskFromResource(resourceIndex, _executingScriptObjectId, _engine->_hotspotMap,
- _engine->screenWidth(), _engine->gameHeight(), false))
+ w, h, halfRes))
warning("loadAreaMask: failed resource %u", resourceIndex);
return OpcodeResult::Continue;
}
@@ -3917,8 +3923,11 @@ OpcodeResult ScriptExecutor::scriptLoadWalkMask() {
debugC(kDebugScript, "SCRIPT::loadWalkMask(index=%u)", resourceIndex);
clearScriptError();
scriptSkipOpcodeRemainder(0x6B);
+ const bool halfRes = _engine->isV2();
+ const int w = halfRes ? kScreenWidth : _engine->screenWidth();
+ const int h = halfRes ? kGameHeight : _engine->gameHeight();
if (!_engine->loadMaskFromResource(resourceIndex, _executingScriptObjectId, _engine->_pathfindingMap,
- _engine->screenWidth(), _engine->gameHeight(), false))
+ w, h, halfRes))
warning("loadWalkMask: failed resource %u", resourceIndex);
return OpcodeResult::Continue;
}
@@ -3928,8 +3937,11 @@ OpcodeResult ScriptExecutor::scriptLoadShadowMask() {
debugC(kDebugScript, "SCRIPT::loadShadowMask(index=%u)", resourceIndex);
clearScriptError();
scriptSkipOpcodeRemainder(0x6C);
+ const bool halfRes = _engine->isV2();
+ const int w = halfRes ? kScreenWidth : _engine->screenWidth();
+ const int h = halfRes ? kGameHeight : _engine->gameHeight();
if (!_engine->loadMaskFromResource(resourceIndex, _executingScriptObjectId, _engine->_shadowMap,
- _engine->screenWidth(), _engine->gameHeight(), false))
+ w, h, halfRes))
warning("loadShadowMask: failed resource %u", resourceIndex);
return OpcodeResult::Continue;
}
diff --git a/engines/macs2/view1.cpp b/engines/macs2/view1.cpp
index fcb467ef2bf..bb76c160290 100644
--- a/engines/macs2/view1.cpp
+++ b/engines/macs2/view1.cpp
@@ -555,16 +555,32 @@ void View1::drawBackgroundAnimations(Graphics::ManagedSurface &s) {
}
// Binary drawAllCharacters (1008:929c): drawAnimFrame(2, y, x+1, blob) - one
// advanceAnimFrame(save=1, mode=2) per frame, not a separate tick advance.
- uint16 frameStart = BackgroundAnimationBlob::advanceAnimFrame(blob, true, 2);
- int16 frameOffsetX = (int16)READ_LE_UINT16(&blob[frameStart]);
- int16 frameOffsetY = (int16)READ_LE_UINT16(&blob[frameStart + 2]);
+ const uint32 frameStart = BackgroundAnimationBlob::advanceAnimFrame(blob, true, 2);
+ if (frameStart == 0 || frameStart + 10 > blob.size())
+ continue;
+ const int16 frameOffsetX = (int16)READ_LE_UINT16(&blob[frameStart]);
+ const int16 frameOffsetY = (int16)READ_LE_UINT16(&blob[frameStart + 2]);
AnimFrame currentFrame;
currentFrame._width = READ_LE_UINT16(&blob[frameStart + 6]);
currentFrame._height = READ_LE_UINT16(&blob[frameStart + 8]);
- currentFrame._data.resize(currentFrame._width * currentFrame._height);
- memcpy(currentFrame._data.data(), &blob[frameStart + 10],
- currentFrame._width * currentFrame._height);
- drawSprite(current._x + 1 + frameOffsetX, current._y + frameOffsetY, currentFrame, s, false);
+ const uint32 pix = (uint32)currentFrame._width * (uint32)currentFrame._height;
+ // V2 blobs can be >64KB; reject corrupt/oversized frame headers.
+ if (currentFrame._width == 0 || currentFrame._height == 0 ||
+ currentFrame._width > 640 || currentFrame._height > 400 ||
+ frameStart + 10 + pix > blob.size()) {
+ continue;
+ }
+ currentFrame._data.resize(pix);
+ memcpy(currentFrame._data.data(), &blob[frameStart + 10], pix);
+ if (g_engine->isV2()) {
+ const int16 ox = (int16)(frameOffsetX << 1);
+ const int16 oy = (int16)(frameOffsetY << 1);
+ drawSpriteTransparent(0, 0, 200, current._x + 1 + ox, current._y + oy,
+ currentFrame._width, currentFrame._height,
+ currentFrame._data.data(), s);
+ } else {
+ drawSprite(current._x + 1 + frameOffsetX, current._y + frameOffsetY, currentFrame, s, false);
+ }
}
}
@@ -915,6 +931,7 @@ void View1::closeScriptActionBar(Script::MouseMode &outSavedCursorMode) {
void View1::enterMapMode() {
// Binary handleInput end-block when scene+0x61db != 0 (1008:e8bf): fade, load map
// from scene+0x5DDB (_mapSceneOffsets[0]), set cursor 0x18 (PanelUse).
+ // this path is the DOS help-map overlay
uint32 helpOffset = g_engine->_mapSceneOffsets[0];
if (helpOffset == 0 || helpOffset >= (uint32)g_engine->_fileStream->size()) {
return;
@@ -1885,11 +1902,12 @@ bool View1::handleInput(const MouseDownMessage &msg) {
return true;
}
if (hasPersistentActionBar()) {
- if (shouldShowActionBar()) {
+ const bool canCycleVerbs = shouldShowActionBar() || g_engine->hasNativeHudAssets();
+ if (canCycleVerbs) {
g_engine->nextCursorMode();
_activeInventoryItem = nullptr;
g_engine->_scriptExecutor->_interactedInventoryItemId = 0;
- if (_actionBar)
+ if (_actionBar && shouldShowActionBar())
_actionBar->syncActiveVerbFromCursorMode();
updateCursor();
presentFrame();
@@ -2293,6 +2311,9 @@ void View1::draw() {
Graphics::ManagedSurface fullScreen(*g_events->getScreen(), Common::Rect(0, 0, sw, sh));
if (shouldShowActionBar()) {
_actionBar->draw(fullScreen);
+ } else if (g_engine->hasNativeHudAssets() && g_engine->_menuMode == 0) {
+ // hideActionBar / overview map: leave playfield pixels alone so
+ // scene art (and hotspots) remain visible in the former panel band.
} else {
const int top = actionBarTopY();
if (top >= 0 && top < sh)
@@ -2735,7 +2756,9 @@ void View1::drawAllCharacters(Graphics::ManagedSurface *surface, bool fullUpdate
// drawAllCharacters @ 1008:93f8-9440 (inlined; not a separate EXE function)
int32 depthOffset = ((int32)charY - (int32)g_engine->_walkDepthThresholdY) *
(int32)g_engine->_walkDepthScaleFactor / 100;
- const uint16 scalingFactor = (uint16)((int32)g_engine->_walkBaseSpeedPct + depthOffset);
+ uint16 scalingFactor = (uint16)((int32)g_engine->_walkBaseSpeedPct + depthOffset);
+ if (obj->_hasDoubleResAnim)
+ scalingFactor = (uint16)(scalingFactor * 2);
if (obj->_index == 1) {
_scalingValues.characterY = (uint16)charY;
_scalingValues.scalingFactor = scalingFactor;
@@ -2747,6 +2770,8 @@ void View1::drawAllCharacters(Graphics::ManagedSurface *surface, bool fullUpdate
if (Macs2Engine::isWalkabilityBlocking((uint16)walkabilityOffset))
walkabilityOffset = 0;
}
+ if (g_engine->isV2())
+ walkabilityOffset = (int16)(walkabilityOffset << 1);
if (obj->_verticalOffsetScale != 0)
walkabilityOffset = (scalingFactor * obj->_verticalOffsetScale) / 100;
@@ -2759,16 +2784,27 @@ void View1::drawAllCharacters(Graphics::ManagedSurface *surface, bool fullUpdate
uint16 frameWidth;
uint16 frameHeight;
+ int16 offsetX = frame._offsetX;
+ int16 offsetY = frame._offsetY;
+ // Frame header offsets are authored in half-res when +0x2e3 is set.
+ if (obj->_hasDoubleResAnim) {
+ offsetX = (int16)(offsetX << 1);
+ offsetY = (int16)(offsetY << 1);
+ }
if (obj->_hasScaling) {
frameWidth = (frame._width * scalingFactor) / 100;
frameHeight = (frame._height * scalingFactor) / 100;
+ } else if (obj->_hasDoubleResAnim) {
+ // exact 2x blit of half-res anim data.
+ frameWidth = (uint16)(frame._width << 1);
+ frameHeight = (uint16)(frame._height << 1);
} else {
frameWidth = frame._width;
frameHeight = frame._height;
}
- const int16 drawX = charX - (frameWidth >> 1) + frame._offsetX;
- const int16 drawY = (charY - frameHeight) - walkabilityOffset + frame._offsetY;
+ const int16 drawX = charX - (frameWidth >> 1) + offsetX + (int16)obj->_objectAdjust1;
+ const int16 drawY = (charY - frameHeight) - walkabilityOffset + offsetY + (int16)obj->_objectAdjust2;
const uint8 depthThreshold = g_engine->depthThresholdForY(charY);
const byte *pixelData = frame._data.data();
Commit: cbf51f43ea6c72fa3df7ba2607fca13cbc2516e3
https://github.com/scummvm/scummvm/commit/cbf51f43ea6c72fa3df7ba2607fca13cbc2516e3
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-08-23T20:53:57+02:00
Commit Message:
MACS2: support v2 scaling for shading tables
Changed paths:
engines/macs2/view1.cpp
engines/macs2/view1.h
diff --git a/engines/macs2/view1.cpp b/engines/macs2/view1.cpp
index bb76c160290..c8d537457c3 100644
--- a/engines/macs2/view1.cpp
+++ b/engines/macs2/view1.cpp
@@ -2810,12 +2810,18 @@ void View1::drawAllCharacters(Graphics::ManagedSurface *surface, bool fullUpdate
// drawAllCharacters @ 1008:9573-9754: drawAnimFrame / drawAnimFrameShaded / drawAnimFrameDepth
const bool clipGameArea = hasPersistentActionBar();
+ const bool useMaskedShading = g_engine->isV2() && (obj->_hasScaling || obj->_hasShading);
if (obj->_hasScaling) {
drawSpriteTransparent(shadingTableOffset, depthThreshold, scalingFactor,
- drawX, drawY, frame._width, frame._height, pixelData, *surface);
+ drawX, drawY, frame._width, frame._height, pixelData, *surface,
+ useMaskedShading);
+ } else if (obj->_hasDoubleResAnim) {
+ drawSpriteTransparent(obj->_hasShading ? shadingTableOffset : 0, depthThreshold, 200,
+ drawX, drawY, frame._width, frame._height, pixelData, *surface,
+ useMaskedShading);
} else if (obj->_hasShading) {
drawSpriteScaled(shadingTableOffset, depthThreshold, drawX, drawY,
- frame._width, frame._height, pixelData, *surface);
+ frame._width, frame._height, pixelData, *surface, useMaskedShading);
} else {
drawSprite(drawX, drawY, frame._width, frame._height,
const_cast<byte *>(pixelData), *surface, false, false, 0, clipGameArea);
@@ -3126,7 +3132,26 @@ void View1::drawSpriteFitted(const Common::Rect &bounds, const Sprite &sprite, G
}
}
-static byte applyShadingTable(byte color, int shadingTableOffset) {
+static byte applyShadingTable(byte color, int shadingTableOffset, byte bgColor, bool useMaskedShading) {
+ if (g_engine->_shadingTable.empty())
+ return color;
+
+ if (useMaskedShading) {
+ const uint intensity = (uint)CLIP(shadingTableOffset, 0, 0x1f);
+ if (color == 1) {
+ const uint idx = (uint)bgColor * 0x20u + intensity;
+ if (idx < g_engine->_shadingTable.size())
+ return g_engine->_shadingTable[idx];
+ return color;
+ }
+ if (intensity == 0)
+ return color;
+ const uint idx = (uint)color * 0x20u + intensity;
+ if (idx >= g_engine->_shadingTable.size())
+ return color;
+ return g_engine->_shadingTable[idx];
+ }
+
if (shadingTableOffset == 0)
return color;
// drawSpriteTransparent @ 1010:0fba: (color - 0xC0) * 0x20 + shadingTableOffset + scene+0x53D3
@@ -3141,7 +3166,7 @@ static byte applyShadingTable(byte color, int shadingTableOffset) {
// drawSpriteScaled @ 1010:102b
void View1::drawSpriteScaled(int shadingTableOffset, uint8 depthThreshold, int16 drawX, int16 drawY,
uint16 srcWidth, uint16 srcHeight, const byte *srcPixels,
- Graphics::ManagedSurface &s) {
+ Graphics::ManagedSurface &s, bool useMaskedShading) {
int screenY = drawY;
int srcRow = 0;
int remainingRows = srcHeight;
@@ -3153,8 +3178,11 @@ void View1::drawSpriteScaled(int shadingTableOffset, uint8 depthThreshold, int16
const uint8 bgDepth = g_engine->_depthMap.getPixel(screenX, screenY);
if (bgDepth < depthThreshold) {
const uint8 color = srcPixels[srcRow + srcX];
- if (color != 0)
- s.setPixel(screenX, screenY, applyShadingTable(color, shadingTableOffset));
+ if (color != 0) {
+ const byte bg = s.getPixel(screenX, screenY);
+ s.setPixel(screenX, screenY,
+ applyShadingTable(color, shadingTableOffset, bg, useMaskedShading));
+ }
}
}
screenX++;
@@ -3169,7 +3197,7 @@ void View1::drawSpriteScaled(int shadingTableOffset, uint8 depthThreshold, int16
// drawSpriteTransparent @ 1010:0ed1
void View1::drawSpriteTransparent(int shadingTableOffset, uint8 depthThreshold, uint16 scalingFactor,
int16 drawX, int16 drawY, uint16 srcWidth, uint16 srcHeight,
- const byte *srcPixels, Graphics::ManagedSurface &s) {
+ const byte *srcPixels, Graphics::ManagedSurface &s, bool useMaskedShading) {
int screenY = drawY;
int srcRowOffset = 0;
int remainingRows = (int)srcHeight;
@@ -3187,7 +3215,9 @@ void View1::drawSpriteTransparent(int shadingTableOffset, uint8 depthThreshold,
const uint8 color = *srcPtr;
if (color != 0 && screenX >= 0 && screenX < s.w &&
g_engine->_depthMap.getPixel(screenX, screenY) < depthThreshold) {
- s.setPixel(screenX, screenY, applyShadingTable(color, shadingTableOffset));
+ const byte bg = s.getPixel(screenX, screenY);
+ s.setPixel(screenX, screenY,
+ applyShadingTable(color, shadingTableOffset, bg, useMaskedShading));
}
screenX++;
diff --git a/engines/macs2/view1.h b/engines/macs2/view1.h
index 1b5351f7642..b1ee2f18784 100644
--- a/engines/macs2/view1.h
+++ b/engines/macs2/view1.h
@@ -192,11 +192,11 @@ private:
// drawSpriteTransparent @ 1010:0ed1 (drawAnimFrameDepth @ 1010:172c)
void drawSpriteTransparent(int shadingTableOffset, uint8 depthThreshold, uint16 scalingFactor,
int16 drawX, int16 drawY, uint16 srcWidth, uint16 srcHeight,
- const byte *srcPixels, Graphics::ManagedSurface &s);
+ const byte *srcPixels, Graphics::ManagedSurface &s, bool useMaskedShading = false);
// drawSpriteScaled @ 1010:102b (drawAnimFrameShaded @ 1010:1785)
void drawSpriteScaled(int shadingTableOffset, uint8 depthThreshold, int16 drawX, int16 drawY,
uint16 srcWidth, uint16 srcHeight, const byte *srcPixels,
- Graphics::ManagedSurface &s);
+ Graphics::ManagedSurface &s, bool useMaskedShading = false);
// Set by action bar map button on press; enterMapMode() runs on panel release.
bool _pendingMapOpen = false;
Commit: 80404bd7956ab2242b73ebf05d65dd28c89e31bf
https://github.com/scummvm/scummvm/commit/80404bd7956ab2242b73ebf05d65dd28c89e31bf
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-08-23T21:11:42+02:00
Commit Message:
MACS2: removed magic numbers
Changed paths:
engines/macs2/actionbar.cpp
engines/macs2/macs2.cpp
engines/macs2/view1.cpp
diff --git a/engines/macs2/actionbar.cpp b/engines/macs2/actionbar.cpp
index 3baefde36aa..e8e00fa31b8 100644
--- a/engines/macs2/actionbar.cpp
+++ b/engines/macs2/actionbar.cpp
@@ -120,7 +120,7 @@ void ActionBar::syncActiveVerbFromCursorMode() {
return;
}
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i < ARRAYSIZE(kVerbs); i++) {
if (kVerbs[i].mode == mode) {
_activeVerbIndex = i;
return;
@@ -202,7 +202,7 @@ void ActionBar::drawSentenceLine(Graphics::ManagedSurface &s) {
}
void ActionBar::drawVerbBar(Graphics::ManagedSurface &s) {
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i < ARRAYSIZE(kVerbs); i++) {
const Common::Rect r = getVerbRect(i);
const bool isActive = (i == _activeVerbIndex);
const bool isHovered = (i == _hoveredVerb);
@@ -298,7 +298,7 @@ bool ActionBar::handleClick(const Common::Point &pos, bool scriptsRunning) {
}
bool ActionBar::handleClickScumm(const Common::Point &pos, bool scriptsRunning) {
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i < ARRAYSIZE(kVerbs); i++) {
if (getVerbRect(i).contains(pos)) {
_activeVerbIndex = i;
g_engine->setCursorMode(kVerbs[i].mode);
@@ -410,7 +410,7 @@ void ActionBar::handleMouseMoveScumm(const Common::Point &pos) {
_hoveredScrollButton = -1;
clearSentenceObject();
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i < ARRAYSIZE(kVerbs); i++) {
if (getVerbRect(i).contains(pos)) {
_hoveredVerb = i;
break;
diff --git a/engines/macs2/macs2.cpp b/engines/macs2/macs2.cpp
index 66e19db8f84..9d060783d12 100644
--- a/engines/macs2/macs2.cpp
+++ b/engines/macs2/macs2.cpp
@@ -416,8 +416,9 @@ void Macs2Engine::loadResourceFileV2() {
_inventSlotW = _fileStream->readUint16LE();
_inventSlotH = _fileStream->readUint16LE();
_inventLayoutMode = _fileStream->readUint16LE();
- for (int i = 0; i < 7; i++)
+ for (int i = 0; i < ARRAYSIZE(_hudTextLayout); i++) {
_hudTextLayout[i] = _fileStream->readUint16LE();
+ }
if (_inventCols == 0)
_inventCols = 4;
diff --git a/engines/macs2/view1.cpp b/engines/macs2/view1.cpp
index c8d537457c3..ac3e39d009e 100644
--- a/engines/macs2/view1.cpp
+++ b/engines/macs2/view1.cpp
@@ -1334,7 +1334,7 @@ bool View1::handleInventoryClick(const MouseDownMessage &msg) {
return true;
}
- for (int i = 0; i < 6; i++) {
+ for (int i = 0; i < (int)_inventoryButtonLocations.size(); i++) {
const Common::Rect ¤t = _inventoryButtonLocations[i];
if (!current.contains(msg._pos)) {
continue;
@@ -1482,7 +1482,7 @@ bool View1::handleContainerInventoryClick(const MouseDownMessage &msg) {
return true;
}
- for (int i = 0; i < 6; i++) {
+ for (int i = 0; i < (int)_inventoryButtonLocations.size(); i++) {
const Common::Rect ¤t = _inventoryButtonLocations[i];
if (!current.contains(msg._pos)) {
continue;
@@ -2270,7 +2270,7 @@ void View1::draw() {
}
if (_uiPanelState == kUiPanelInventory && g_engine->enhancementEnabled(kEnhUIUX)) {
- for (int i = 0; i < 6; i++) {
+ for (int i = 0; i < (int)_inventoryButtonLocations.size(); i++) {
if (_inventoryButtonLocations[i].contains(mousePos)) {
static const char *const buttonNames[] = {
"Schauen", "Benutzen", "Hoch", "Runter", "Ablegen", "Schliessen"};
@@ -2291,7 +2291,7 @@ void View1::draw() {
}
if (_uiPanelState == kUiPanelSaveLoad && g_engine->enhancementEnabled(kEnhUIUX)) {
- for (int i = 0; i < 7; i++) {
+ for (int i = 0; i < ARRAYSIZE(_saveLoadButtonRects); i++) {
if (_saveLoadButtonRects[i].contains(mousePos)) {
static const char *const buttonNames[] = {
"Laden", "Speichern", "Musik an/aus",
Commit: 7fa9a6deeb1c7e274fb616bdf676c018d1280479
https://github.com/scummvm/scummvm/commit/7fa9a6deeb1c7e274fb616bdf676c018d1280479
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-08-23T21:11:42+02:00
Commit Message:
MACS2: fixed member name
Changed paths:
engines/macs2/amiga_resources.cpp
engines/macs2/debugtools.cpp
engines/macs2/macs2.cpp
engines/macs2/macs2.h
engines/macs2/view1.cpp
diff --git a/engines/macs2/amiga_resources.cpp b/engines/macs2/amiga_resources.cpp
index 04105dcb47b..75b398a86ad 100644
--- a/engines/macs2/amiga_resources.cpp
+++ b/engines/macs2/amiga_resources.cpp
@@ -166,7 +166,7 @@ bool Macs2Engine::loadAmigaSceneBackground(uint32 sceneResourceId) {
// load_scene_mxmm @ 00221d90). Without nodes, calculatePath always fails and
// walkAlongPath cancels with finalDest=current - waitForWalk then completes
// immediately while the actor is still short of the script target.
- pathfindingPoints.clear();
+ _pathfindingPoints.clear();
_numPathfindingPoints = 0;
uint16 numPfPoints = 0;
Common::Array<AmigaPathfindingNode> pfNodes;
@@ -183,7 +183,7 @@ bool Macs2Engine::loadAmigaSceneBackground(uint32 sceneResourceId) {
if (pfNodes[i].adjacent[j] != 0)
current._adjacentPoints.push_back(pfNodes[i].adjacent[j]);
}
- pathfindingPoints.push_back(current);
+ _pathfindingPoints.push_back(current);
}
}
diff --git a/engines/macs2/debugtools.cpp b/engines/macs2/debugtools.cpp
index 50019205a44..1a19ddd66f7 100644
--- a/engines/macs2/debugtools.cpp
+++ b/engines/macs2/debugtools.cpp
@@ -1325,7 +1325,7 @@ static void showSceneMapsWindow() {
if (view) {
// Draw pathfinding point nodes and connections
for (int i = 0; i < 16; i++) {
- PathfindingPoint &pt = g_engine->pathfindingPoints[i];
+ PathfindingPoint &pt = g_engine->_pathfindingPoints[i];
if (pt._position.x >= 0 && pt._position.x < kScreenWidth && pt._position.y >= 0 && pt._position.y < kGameHeight) {
// Draw cross at node
for (int d = -2; d <= 2; d++) {
@@ -1341,7 +1341,7 @@ static void showSceneMapsWindow() {
for (uint8 adj : pt._adjacentPoints) {
if (adj == 0 || adj > 16)
continue;
- PathfindingPoint &other = g_engine->pathfindingPoints[adj - 1];
+ PathfindingPoint &other = g_engine->_pathfindingPoints[adj - 1];
overlayComposite.drawLine(pt._position.x, pt._position.y, other._position.x, other._position.y, 0xFE);
}
}
@@ -1409,7 +1409,7 @@ static void showSceneMapsWindow() {
ImDrawList *dl = ImGui::GetWindowDrawList();
ImVec2 imgOrigin = ImGui::GetItemRectMin();
for (int i = 0; i < 16; i++) {
- PathfindingPoint &pt = g_engine->pathfindingPoints[i];
+ PathfindingPoint &pt = g_engine->_pathfindingPoints[i];
if (pt._position.x >= 0 && pt._position.x < kScreenWidth && pt._position.y >= 0 && pt._position.y < kGameHeight) {
char buf[4];
snprintf(buf, sizeof(buf), "%d", i);
@@ -1534,7 +1534,7 @@ static void showSceneMapsWindow() {
ImGui::Text("_walkDepthThresholdY=%u _walkDepthScaleFactor=%u _walkBaseSpeedPct=%u",
g_engine->_walkDepthThresholdY, g_engine->_walkDepthScaleFactor, g_engine->_walkBaseSpeedPct);
ImGui::Text("Pathfinding points: %u Path nodes: %u",
- (uint)g_engine->pathfindingPoints.size(), (uint)g_engine->_path.size());
+ (uint)g_engine->_pathfindingPoints.size(), (uint)g_engine->_path.size());
// Node detail table
if (ImGui::CollapsingHeader("Node Graph", ImGuiTreeNodeFlags_DefaultOpen)) {
@@ -1542,8 +1542,8 @@ static void showSceneMapsWindow() {
Character *protagonist = view ? view->getCharacterByIndex(Scenes::instance()._currentActorIndex) : nullptr;
Common::Point charPos = protagonist ? protagonist->getPosition() : Common::Point(0, 0);
- for (int i = 0; i < (int)g_engine->pathfindingPoints.size(); i++) {
- const PathfindingPoint &pt = g_engine->pathfindingPoints[i];
+ for (int i = 0; i < (int)g_engine->_pathfindingPoints.size(); i++) {
+ const PathfindingPoint &pt = g_engine->_pathfindingPoints[i];
// Check reachability from character
bool reachable = protagonist && g_engine->isPathWalkable(charPos.y, charPos.x, pt._position.y, pt._position.x);
// Check if node is in current path
diff --git a/engines/macs2/macs2.cpp b/engines/macs2/macs2.cpp
index 9d060783d12..960a94aefbe 100644
--- a/engines/macs2/macs2.cpp
+++ b/engines/macs2/macs2.cpp
@@ -1002,7 +1002,7 @@ bool Macs2Engine::loadSceneGraphicsV1(uint32 sceneIndex) {
// Pretty sure that this is the pathfinding points. We address them starting
// Load pathfinding nodes (16 entries x 10 bytes at scene+0x5023)
- pathfindingPoints.clear();
+ _pathfindingPoints.clear();
for (int i = 0; i < 16; i++) {
PathfindingPoint current;
current._index = i;
@@ -1014,7 +1014,7 @@ bool Macs2Engine::loadSceneGraphicsV1(uint32 sceneIndex) {
current._adjacentPoints.clear();
for (uint16 j = 0; j < numConnections && j < 4; j++)
current._adjacentPoints.push_back(adj[j]);
- pathfindingPoints.push_back(current);
+ _pathfindingPoints.push_back(current);
}
_numHotspots = _fileStream->readUint16LE();
@@ -1133,7 +1133,7 @@ bool Macs2Engine::loadSceneGraphicsV2(uint32 sceneIndex) {
return false;
upscaleHalfRes(half, _hotspotMap);
- pathfindingPoints.clear();
+ _pathfindingPoints.clear();
for (int i = 0; i < 16; i++) {
PathfindingPoint current;
current._index = i;
@@ -1146,7 +1146,7 @@ bool Macs2Engine::loadSceneGraphicsV2(uint32 sceneIndex) {
current._adjacentPoints.clear();
for (uint16 j = 0; j < numConnections && j < 4; j++)
current._adjacentPoints.push_back(adj[j]);
- pathfindingPoints.push_back(current);
+ _pathfindingPoints.push_back(current);
}
stream->skip(0x2c0 - 0x160);
@@ -2104,8 +2104,8 @@ int Macs2Engine::euclideanDistance(const Common::Point &a, const Common::Point &
// Binary walkableDistance (1008:1293): distance between two nodes IF walkable, else 0x500.
// Uses binary search on precomputed squared-distance table (scene+0x61DC) for O(log n) sqrt.
int Macs2Engine::walkableDistance(int nodeA, int nodeB) {
- const Common::Point &a = pathfindingPoints[nodeA - 1]._position;
- const Common::Point &b = pathfindingPoints[nodeB - 1]._position;
+ const Common::Point &a = _pathfindingPoints[nodeA - 1]._position;
+ const Common::Point &b = _pathfindingPoints[nodeB - 1]._position;
if (!isPathWalkable(a.y, a.x, b.y, b.x))
return 0x500;
// Binary search for integer sqrt(dx^2 + dy^2), matching binary at 1008:1293
@@ -2139,7 +2139,7 @@ int Macs2Engine::computeMinCostToReachable(int nodeIndex, int prevNode, uint16 a
visitedStack[visitedCount] = nodeIndex;
int result;
- const Common::Point &nodePos = pathfindingPoints[nodeIndex - 1]._position;
+ const Common::Point &nodePos = _pathfindingPoints[nodeIndex - 1]._position;
if (reachable[nodeIndex]) {
// Terminal: return walkable distance from this node to finalDest
@@ -2168,7 +2168,7 @@ int Macs2Engine::computeMinCostToReachable(int nodeIndex, int prevNode, uint16 a
int bestCost = 0x7777;
int bestAdj = 0;
- const PathfindingPoint &pt = pathfindingPoints[nodeIndex - 1];
+ const PathfindingPoint &pt = _pathfindingPoints[nodeIndex - 1];
int adjCount = (int)pt._adjacentPoints.size();
if (adjCount > 0) {
diff --git a/engines/macs2/macs2.h b/engines/macs2/macs2.h
index 894a921bfb7..551fbe25dbb 100644
--- a/engines/macs2/macs2.h
+++ b/engines/macs2/macs2.h
@@ -380,8 +380,7 @@ public:
Common::Array<PathfindingAreaOverride> _pathfindingOverrides;
// Area override table at scene+value*5+0x4EA8 (for getAreaAtPoint)
uint16 _areaOverrides[AREA_OVERRIDE_COUNT] = {0};
- uint16 _pathfindingPoints[32];
- Common::Array<PathfindingPoint> pathfindingPoints;
+ Common::Array<PathfindingPoint> _pathfindingPoints;
Common::Array<Common::Point> _path;
bool getPathfindingOverride(uint16 index, uint16 &result);
diff --git a/engines/macs2/view1.cpp b/engines/macs2/view1.cpp
index ac3e39d009e..d3262a1fd64 100644
--- a/engines/macs2/view1.cpp
+++ b/engines/macs2/view1.cpp
@@ -797,17 +797,17 @@ void View1::drawPathfindingPoints(Graphics::ManagedSurface &s) {
yOffset = xData._height / 2;
}
for (int i = 0; i < 16; i++) {
- PathfindingPoint ¤t = g_engine->pathfindingPoints[i];
+ PathfindingPoint ¤t = g_engine->_pathfindingPoints[i];
renderString(current._position.x - xOffset, current._position.y - yOffset, "x");
Common::String number = Common::String::format("%u", i);
renderString(current._position.x - xOffset + 10, current._position.y - yOffset + 10, number.c_str());
for (uint8 adjacentIndex : current._adjacentPoints) {
- if (adjacentIndex >= g_engine->pathfindingPoints.size()) {
+ if (adjacentIndex >= g_engine->_pathfindingPoints.size()) {
continue;
}
- PathfindingPoint &other = g_engine->pathfindingPoints[adjacentIndex - 1];
+ PathfindingPoint &other = g_engine->_pathfindingPoints[adjacentIndex - 1];
s.drawLine(current._position.x, current._position.y, other._position.x, other._position.y, 0xFFFFFFFF);
}
}
@@ -3636,7 +3636,7 @@ bool Character::calculatePath(Common::Point target) {
// scene[i + 0x50C2] = isPathWalkable(finalDest, node[i])
bool reachable[MAX_NODES + 1] = {};
for (int i = 1; i <= nodeCount; i++) {
- const Common::Point &nodePos = g_engine->pathfindingPoints[i - 1]._position;
+ const Common::Point &nodePos = g_engine->_pathfindingPoints[i - 1]._position;
reachable[i] = g_engine->isPathWalkable(target.y, target.x, nodePos.y, nodePos.x);
}
@@ -3644,7 +3644,7 @@ bool Character::calculatePath(Common::Point target) {
int bestCost = 0x7777;
int bestNode = 0;
for (int i = 1; i <= nodeCount; i++) {
- const Common::Point &nodePos = g_engine->pathfindingPoints[i - 1]._position;
+ const Common::Point &nodePos = g_engine->_pathfindingPoints[i - 1]._position;
int costToDest = g_engine->euclideanDistance(nodePos, target);
int costToChar = g_engine->euclideanDistance(nodePos, charPos);
if (costToDest + costToChar < bestCost) {
@@ -3681,7 +3681,7 @@ bool Character::calculatePath(Common::Point target) {
_path.push_back(bestNode);
int currentNode = bestNode;
while (!reachable[currentNode]) {
- const PathfindingPoint &curPt = g_engine->pathfindingPoints[currentNode - 1];
+ const PathfindingPoint &curPt = g_engine->_pathfindingPoints[currentNode - 1];
int localBestCost = 0x7777;
int nextNode = currentNode;
for (uint a = 0; a < curPt._adjacentPoints.size(); a++) {
@@ -3701,8 +3701,8 @@ bool Character::calculatePath(Common::Point target) {
// Step 4: Validate path - consecutive nodes must be walkable to each other
for (uint i = 0; i + 1 < _path.size(); i++) {
- const Common::Point &p1 = g_engine->pathfindingPoints[_path[i + 1] - 1]._position;
- const Common::Point &p2 = g_engine->pathfindingPoints[_path[i] - 1]._position;
+ const Common::Point &p1 = g_engine->_pathfindingPoints[_path[i + 1] - 1]._position;
+ const Common::Point &p2 = g_engine->_pathfindingPoints[_path[i] - 1]._position;
if (!g_engine->isPathWalkable(p1.y, p1.x, p2.y, p2.x)) {
// Path invalid - abort, go directly to target
_path.clear();
@@ -3717,14 +3717,14 @@ bool Character::calculatePath(Common::Point target) {
// is actually the character position.
_currentPathIndex = 0;
while (_currentPathIndex + 1 < (int16)_path.size()) {
- const Common::Point &nextNodePos = g_engine->pathfindingPoints[_path[_currentPathIndex + 1] - 1]._position;
+ const Common::Point &nextNodePos = g_engine->_pathfindingPoints[_path[_currentPathIndex + 1] - 1]._position;
if (!g_engine->isPathWalkable(nextNodePos.y, nextNodePos.x, charPos.y, charPos.x))
break;
_currentPathIndex++;
}
// Set immediate target to the current path node
- const Common::Point &firstTarget = g_engine->pathfindingPoints[_path[_currentPathIndex] - 1]._position;
+ const Common::Point &firstTarget = g_engine->_pathfindingPoints[_path[_currentPathIndex] - 1]._position;
_targetPosition = firstTarget;
return true;
}
@@ -3735,7 +3735,7 @@ bool Character::canNodeConnectSourceToTarget(uint16 nodeIndex, const Common::Poi
// 1. Node must be able to see the target
// 2. Flood-fill connected component from node
// 3. Some node in component must see target AND some node must be seen from source
- const Common::Point &nodePos = g_engine->pathfindingPoints[nodeIndex - 1]._position;
+ const Common::Point &nodePos = g_engine->_pathfindingPoints[nodeIndex - 1]._position;
if (!g_engine->isPathWalkable(nodePos.y, nodePos.x, target.y, target.x))
return false;
@@ -3749,7 +3749,7 @@ bool Character::canNodeConnectSourceToTarget(uint16 nodeIndex, const Common::Poi
for (int i = 1; i <= nodeCount; i++) {
if (!visited[i])
continue;
- const Common::Point &p = g_engine->pathfindingPoints[i - 1]._position;
+ const Common::Point &p = g_engine->_pathfindingPoints[i - 1]._position;
if (g_engine->isPathWalkable(p.y, p.x, target.y, target.x))
anySeesTarget = true;
if (g_engine->isPathWalkable(charPos.y, charPos.x, p.y, p.x))
@@ -3764,7 +3764,7 @@ void Character::floodFillConnectedNodes(int nodeIndex, bool *visited, int nodeCo
if (visited[nodeIndex])
return;
visited[nodeIndex] = true;
- const PathfindingPoint &pt = g_engine->pathfindingPoints[nodeIndex - 1];
+ const PathfindingPoint &pt = g_engine->_pathfindingPoints[nodeIndex - 1];
for (uint i = 0; i < pt._adjacentPoints.size(); i++) {
floodFillConnectedNodes(pt._adjacentPoints[i], visited, nodeCount);
}
@@ -3802,7 +3802,7 @@ bool Character::walkAlongPath() {
// Binary: if (pathNodeIndex != 0) posX/Y = nodeCoords[pathNodes[pathNodeIndex]]
if (_currentPathIndex >= 0 && _currentPathIndex < (int16)_path.size()) {
const uint16 snapIdx = _path[_currentPathIndex];
- const Common::Point &snapPos = g_engine->pathfindingPoints[snapIdx - 1]._position;
+ const Common::Point &snapPos = g_engine->_pathfindingPoints[snapIdx - 1]._position;
_gameObject->_position = snapPos;
}
_currentPathIndex++;
@@ -3816,7 +3816,7 @@ bool Character::walkAlongPath() {
return false; // No more path segments after this
}
const uint16 nodeIdx = _path[_currentPathIndex];
- const Common::Point &nodePos = g_engine->pathfindingPoints[nodeIdx - 1]._position;
+ const Common::Point &nodePos = g_engine->_pathfindingPoints[nodeIdx - 1]._position;
_targetPosition = nodePos;
_stepDeltaX = abs(_targetPosition.x - _gameObject->_position.x);
_stepDeltaY = abs(_targetPosition.y - _gameObject->_position.y);
Commit: bee5a75f2689edeb2a5e7bcd7f56b8b23276d8b6
https://github.com/scummvm/scummvm/commit/bee5a75f2689edeb2a5e7bcd7f56b8b23276d8b6
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-08-23T21:11:42+02:00
Commit Message:
MACS2: replaced magic numbers
Changed paths:
engines/macs2/debugtools.cpp
engines/macs2/macs2.cpp
engines/macs2/music.cpp
engines/macs2/saveload.cpp
diff --git a/engines/macs2/debugtools.cpp b/engines/macs2/debugtools.cpp
index 1a19ddd66f7..0e37a09cf7b 100644
--- a/engines/macs2/debugtools.cpp
+++ b/engines/macs2/debugtools.cpp
@@ -26,6 +26,7 @@
#include "common/str-enc.h"
#include "common/system.h"
#include "common/ustr.h"
+#include "common/util.h"
#include "macs2/detection.h"
#include "macs2/gameobjects.h"
#include "macs2/macs2.h"
@@ -2050,7 +2051,7 @@ static void showSoundWindow() {
// Channel selector
static int selectedVoice = 0;
if (ImGui::BeginCombo("Voice", Common::String::format("Voice %d", selectedVoice).c_str())) {
- for (int i = 0; i < 9; i++) {
+ for (int i = 0; i < ARRAYSIZE(ds.voices); i++) {
bool selected = (selectedVoice == i);
const char *label = ds.voices[i].active
? Common::String::format("Voice %d [CH%d N%02X]", i, ds.voices[i].channel, ds.voices[i].note).c_str()
@@ -2080,7 +2081,7 @@ static void showSoundWindow() {
// All voices overview
ImGui::Separator();
ImGui::TextUnformatted("All Voices:");
- for (int i = 0; i < 9; i++) {
+ for (int i = 0; i < ARRAYSIZE(ds.regHistory); i++) {
float voiceData[Music::kDebugRingSize];
for (int j = 0; j < Music::kDebugRingSize; j++) {
voiceData[j] = ds.regHistory[i][(ringPos + j) % Music::kDebugRingSize];
diff --git a/engines/macs2/macs2.cpp b/engines/macs2/macs2.cpp
index 960a94aefbe..d969deb11e9 100644
--- a/engines/macs2/macs2.cpp
+++ b/engines/macs2/macs2.cpp
@@ -250,11 +250,14 @@ void Macs2Engine::loadResourceFileV2() {
memset(_mapSceneOffsets, 0, sizeof(_mapSceneOffsets));
_imageResources.clear();
_imageResources.resize(33);
- for (int i = 0; i < 33; i++)
+ for (int i = 0; i < ARRAYSIZE(_cursorHotspots); i++) {
_cursorHotspots[i] = Common::Point(0, 0);
+ }
_hudButtons.clear();
- for (int i = 0; i < 6; i++) {
+ for (int i = 0; i < ARRAYSIZE(_hudMegapicLoaded); i++) {
_hudMegapicLoaded[i] = false;
+ }
+ for (int i = 0; i < ARRAYSIZE(_hudMegapics); i++) {
_hudMegapics[i].free();
}
_panelTopY = 0;
@@ -274,8 +277,9 @@ void Macs2Engine::loadResourceFileV2() {
_fileStream->read(_palVanilla, 0x300);
memcpy(_pal, _palVanilla, 0x300);
- for (int i = 0; i < 4; i++)
+ for (int i = 0; i < ARRAYSIZE(_hudTextRecolor); i++) {
_hudTextRecolor[i] = _fileStream->readUint16LE();
+ }
_panelTopY = _fileStream->readUint16LE();
_panelHeight = _fileStream->readUint16LE();
@@ -285,7 +289,7 @@ void Macs2Engine::loadResourceFileV2() {
_panelHeight = 146;
}
- for (int i = 0; i < 6; i++) {
+ for (int i = 0; i < ARRAYSIZE(_hudMegapicLoaded); i++) {
const uint16 flag = _fileStream->readUint16LE();
if (flag == 0)
continue;
@@ -451,7 +455,7 @@ void Macs2Engine::loadResourceFileV2() {
if (!loadSizedFont(_panelGlyphs, numPanelGlyphs, maxPanelGlyphHeight))
warning("readGlobalAssetsV2: failed loading SysFont");
- for (int i = 0; i < 256; i++)
+ for (int i = 0; i < ARRAYSIZE(_mapSceneOffsets); i++)
_mapSceneOffsets[i] = _fileStream->readUint32LE();
_saveListScroll = 1;
@@ -463,7 +467,7 @@ void Macs2Engine::loadResourceFileV2() {
installed++;
}
uint megas = 0;
- for (int i = 0; i < 6; i++) {
+ for (int i = 0; i < ARRAYSIZE(_hudMegapicLoaded); i++) {
if (_hudMegapicLoaded[i])
megas++;
}
diff --git a/engines/macs2/music.cpp b/engines/macs2/music.cpp
index 5722afcc8f4..77eda940e1a 100644
--- a/engines/macs2/music.cpp
+++ b/engines/macs2/music.cpp
@@ -491,7 +491,7 @@ void Music::updateDebugState() {
_debug.masterVolume = _masterVolume;
_debug.numOplChannels = _numOplChannels;
- for (int i = 0; i < 9; i++) {
+ for (int i = 0; i < ARRAYSIZE(_debug.voices); i++) {
_debug.voices[i].note = _voiceNote[i];
_debug.voices[i].channel = _voiceMidiChannel[i];
_debug.voices[i].active = (_voiceAge[i] == 0);
diff --git a/engines/macs2/saveload.cpp b/engines/macs2/saveload.cpp
index c8c83fce90d..5e10aa61177 100644
--- a/engines/macs2/saveload.cpp
+++ b/engines/macs2/saveload.cpp
@@ -19,6 +19,7 @@
*
*/
+#include "common/util.h"
#include "macs2/gameobjects.h"
#include "macs2/macs2.h"
#include "macs2/view1.h"
@@ -372,10 +373,11 @@ Common::Error Macs2Engine::syncGame(Common::Serializer &s) {
_hotspotOverrides[i + 1] = val;
}
}
- if (s.isLoading() && _hotspotOverrides.size() < 0x21)
+ if (s.isLoading() && _hotspotOverrides.size() < 0x21) {
_hotspotOverrides.resize(0x21, 0xFFFF);
+ }
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i < ARRAYSIZE(_sceneTimerParams); i++) {
s.syncAsUint32LE(_sceneTimerParams[i]);
}
Commit: 9905b340f2ab5a16bab1155c15bafd3faf0ed816
https://github.com/scummvm/scummvm/commit/9905b340f2ab5a16bab1155c15bafd3faf0ed816
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-08-23T21:11:42+02:00
Commit Message:
MACS2: removed magic numbers
Changed paths:
engines/macs2/view1.cpp
diff --git a/engines/macs2/view1.cpp b/engines/macs2/view1.cpp
index d3262a1fd64..d66044f6e86 100644
--- a/engines/macs2/view1.cpp
+++ b/engines/macs2/view1.cpp
@@ -851,16 +851,16 @@ void View1::drawPath(Graphics::ManagedSurface &s) {
}
void View1::layoutActionBarButtons() {
+ _mainMenuButtonLocations.resize(9);
uint16 maxW = 0, maxH = 0;
- for (int i = 0; i < 9 && i < (int)g_engine->_imageResources.size(); i++) {
+ for (int i = 0; i < (int)_mainMenuButtonLocations.size() && i < (int)g_engine->_imageResources.size(); i++) {
maxW = MAX(maxW, g_engine->_imageResources[i]._width);
maxH = MAX(maxH, g_engine->_imageResources[i]._height);
}
const uint16 btnW = maxW + 6;
const uint16 btnH = maxH + 6;
- _mainMenuButtonLocations.resize(9);
- for (int i = 0; i < 9; i++) {
+ for (int i = 0; i < (int)_mainMenuButtonLocations.size(); i++) {
const int col = i % 3;
const int row = i / 3;
const uint16 cellX = _mainMenuRect.left + 4 + col * (btnW + 4);
@@ -959,7 +959,7 @@ void View1::drawMainMenu(Graphics::ManagedSurface &s) {
drawBorderSide(Common::Point(_mainMenuRect.left, _mainMenuRect.top), Common::Point(_mainMenuRect.width(), _mainMenuRect.height()), s);
drawNinePatchBorder(Common::Point(_mainMenuRect.left, _mainMenuRect.top), Common::Point(_mainMenuRect.width(), _mainMenuRect.height()), kBorderRaised, false, false, s);
- for (int i = 0; i < 9 && i < (int)g_engine->_imageResources.size(); i++) {
+ for (int i = 0; i < (int)_mainMenuButtonLocations.size() && i < (int)g_engine->_imageResources.size(); i++) {
const Common::Rect &cell = _mainMenuButtonLocations[i];
const bool pressed = (_clickedButtonIndex == (uint16)(i + 1));
const BorderStyle &border = pressed ? kBorderPressed : kBorderRaised;
Commit: 88cfefbc7fda14c5a2084cb0e26c840a496bcc34
https://github.com/scummvm/scummvm/commit/88cfefbc7fda14c5a2084cb0e26c840a496bcc34
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-08-23T21:11:43+02:00
Commit Message:
MACS2: renamed variables - they are no constants anymore
Changed paths:
engines/macs2/events.cpp
diff --git a/engines/macs2/events.cpp b/engines/macs2/events.cpp
index a9acb40fbfb..02d5795d69a 100644
--- a/engines/macs2/events.cpp
+++ b/engines/macs2/events.cpp
@@ -53,8 +53,8 @@ void Events::runGame() {
// ISR counter exceeds 1 (every 2 ticks) -> ~10.8fps / ~92.6ms.
// Tick period / ticks-per-frame come from engine facades for later platforms.
uint32 lastTickTime = g_system->getMillis();
- const uint32 kTimerTickMs = g_engine->timerTickMs();
- const uint16 kNormalTicksPerGameFrame = g_engine->ticksPerGameFrame();
+ const uint32 timerTickMs = g_engine->timerTickMs();
+ const uint16 normalTicksPerGameFrame = g_engine->ticksPerGameFrame();
uint16 timerTickCounter = 0;
Common::Event e;
@@ -76,10 +76,10 @@ void Events::runGame() {
// Accumulate timer ticks based on elapsed wall-clock time
const uint32 elapsed = currentMillis - lastTickTime;
- if (elapsed >= kTimerTickMs) {
- const uint16 ticks = elapsed / kTimerTickMs;
+ if (elapsed >= timerTickMs) {
+ const uint16 ticks = elapsed / timerTickMs;
timerTickCounter += ticks;
- lastTickTime += ticks * kTimerTickMs;
+ lastTickTime += ticks * timerTickMs;
}
bool doTick = false;
@@ -91,7 +91,7 @@ void Events::runGame() {
doTick = (timerTickCounter >= 0x12); // TODO: this is running at different speed compared to running this in dosbox
break;
default: // normal: every kNormalTicksPerGameFrame timer ticks
- doTick = (timerTickCounter >= kNormalTicksPerGameFrame);
+ doTick = (timerTickCounter >= normalTicksPerGameFrame);
break;
}
Commit: 8ff8fa25e669c424db68f6defbc3f81280f8a50b
https://github.com/scummvm/scummvm/commit/8ff8fa25e669c424db68f6defbc3f81280f8a50b
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-08-23T21:11:43+02:00
Commit Message:
MACS2: reduced scope
Changed paths:
engines/macs2/gameobjects.cpp
diff --git a/engines/macs2/gameobjects.cpp b/engines/macs2/gameobjects.cpp
index e9451a29217..107c009f7b1 100644
--- a/engines/macs2/gameobjects.cpp
+++ b/engines/macs2/gameobjects.cpp
@@ -438,9 +438,8 @@ const Common::Array<uint8> *Macs2::GameObject::getAnimSlotBlob(uint16 slot) cons
bool Macs2::GameObject::isAnimSlotLoaded(uint16 orient) const {
const uint16 overloadSlot = g_engine->overloadAnimSlot();
- const uint16 maxOrient = g_engine->maxOrientations();
if (g_engine->isV2()) {
- for (uint i = 0; i < 5; i++) {
+ for (uint i = 0; i < ARRAYSIZE(_specialAnimTriggers); i++) {
const uint16 trig = _specialAnimTriggers[i];
if ((int16)trig >= 0 && trig == orient) {
const uint16 animSlot = Macs2Engine::specialAnimSlotToAnimSlot(i + 1);
@@ -458,6 +457,7 @@ bool Macs2::GameObject::isAnimSlotLoaded(uint16 orient) const {
const Common::Array<uint8> *blob = getAnimSlotBlob(overloadSlot);
return blob != nullptr && !blob->empty();
}
+ const uint16 maxOrient = g_engine->maxOrientations();
if (orient < 1 || orient > maxOrient)
return false;
const uint slot = orient - 1;
Commit: 7fa2422e40737c0b51bb774620d3671565fe678c
https://github.com/scummvm/scummvm/commit/7fa2422e40737c0b51bb774620d3671565fe678c
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-08-23T21:11:43+02:00
Commit Message:
MACS2: minor opcode cleanup and fixed max hotspots
Changed paths:
engines/macs2/macs2.cpp
engines/macs2/macs2.h
engines/macs2/scriptexecutor.cpp
diff --git a/engines/macs2/macs2.cpp b/engines/macs2/macs2.cpp
index d969deb11e9..f3d42f3b583 100644
--- a/engines/macs2/macs2.cpp
+++ b/engines/macs2/macs2.cpp
@@ -1423,6 +1423,8 @@ void Macs2Engine::changeScene(uint32 newSceneIndex, bool executeScript) {
// Binary changeScene (1008:2574): loadObjectData for scene objects except current actor.
GameObject *actorObject = GameObjects::getObjectByIndex(Scenes::instance()._currentActorIndex);
if (actorObject != nullptr && actorObject->_sceneIndex == newSceneIndex) {
+ if (isV2())
+ loadObjectData(actorObject);
Character *actorChar = new Character();
actorChar->_gameObject = actorObject;
currentView->_characters.push_back(actorChar);
@@ -3056,6 +3058,8 @@ bool Macs2Engine::loadObjectData(GameObject *obj) {
}
obj->_overloadAnimTriggerDirection = 0x7FFF;
+ for (uint i = 0; i < ARRAYSIZE(obj->_specialAnimTriggers); i++)
+ obj->_specialAnimTriggers[i] = 0x7FFF;
obj->_useOverloadAnimation = false;
obj->_overloadAnimation.clear();
obj->_snapToTarget = false;
@@ -3171,7 +3175,7 @@ uint16 Macs2Engine::resolveAnimSlotIndex(const GameObject *obj) const {
if (obj == nullptr)
return 0;
if (isV2()) {
- for (uint i = 0; i < 5; i++) {
+ for (uint i = 0; i < ARRAYSIZE(obj->_specialAnimTriggers); i++) {
const uint16 trig = obj->_specialAnimTriggers[i];
if ((int16)trig >= 0 && trig == obj->_orientation)
return specialAnimSlotToAnimSlot(i + 1);
diff --git a/engines/macs2/macs2.h b/engines/macs2/macs2.h
index 551fbe25dbb..720c079152a 100644
--- a/engines/macs2/macs2.h
+++ b/engines/macs2/macs2.h
@@ -789,7 +789,8 @@ public:
uint16 overloadAnimSlot() const { return maxAnimSlots(); }
static uint16 specialAnimSlotToAnimSlot(uint16 specialSlot);
/** Scene hotspot override table entries (1-based inclusive max). */
- uint16 maxHotspots() const { return 0x10; }
+ /** Hotspot remap table indices (1-based). DOS scene+0x5BD1: 16; V2 ActModule+0x6161: 32. */
+ uint16 maxHotspots() const { return isV2() ? 0x20 : 0x10; }
/** Per-object resource offset table entries. */
uint maxObjectResources() const { return 32; }
/** Anim slot used for the current orientation (overload-direction rule). */
diff --git a/engines/macs2/scriptexecutor.cpp b/engines/macs2/scriptexecutor.cpp
index 5682710e879..1f8ddd0415f 100644
--- a/engines/macs2/scriptexecutor.cpp
+++ b/engines/macs2/scriptexecutor.cpp
@@ -27,6 +27,7 @@
#include "common/memstream.h"
#include "common/path.h"
#include "common/system.h"
+#include "common/util.h"
#include "engines/enhancements.h"
#include "macs2/amiga_archive.h"
#include "macs2/amiga_decode.h"
@@ -2185,19 +2186,19 @@ OpcodeResult Script::ScriptExecutor::scriptSetDirection() {
return OpcodeResult::Continue;
}
if (_engine->isV2()) {
- if (specialSlot < 1 || specialSlot > 5) {
+ if (specialSlot < 1 || specialSlot > ARRAYSIZE(object->_specialAnimTriggers)) {
setScriptError(0x2e);
return OpcodeResult::Continue;
}
- // Binary: clear any other special slot that already uses this direction.
- for (uint i = 0; i < 5; i++) {
+ // clear any other slot that already uses this direction to 0.
+ for (uint i = 0; i < ARRAYSIZE(object->_specialAnimTriggers); i++) {
if (object->_specialAnimTriggers[i] == value)
object->_specialAnimTriggers[i] = 0;
}
object->_specialAnimTriggers[specialSlot - 1] = value;
- return OpcodeResult::Continue;
+ } else {
+ object->_overloadAnimTriggerDirection = value;
}
- object->_overloadAnimTriggerDirection = value;
return OpcodeResult::Continue;
}
@@ -2237,13 +2238,13 @@ OpcodeResult Script::ScriptExecutor::scriptStopAnimation() {
obj->_useOverloadAnimation = false;
obj->_overloadAnimation.clear();
}
- return OpcodeResult::Continue;
+ } else {
+ obj->_overloadAnimTriggerDirection = 0x7FFF;
+ obj->_useOverloadAnimation = false;
+ obj->_overloadAnimation.clear();
+ if (obj->_blobs.size() > 20)
+ obj->_blobs[20].clear();
}
- obj->_overloadAnimTriggerDirection = 0x7FFF;
- obj->_useOverloadAnimation = false;
- obj->_overloadAnimation.clear();
- if (obj->_blobs.size() > 20)
- obj->_blobs[20].clear();
return OpcodeResult::Continue;
}
@@ -2541,7 +2542,8 @@ OpcodeResult Script::ScriptExecutor::scriptSetHotspotOverride() {
clearScriptError();
const uint16 maxHotspot = _engine->maxHotspots();
- if (v1 < 1 || v1 > maxHotspot || v2 < 1 || v2 > maxHotspot) {
+ // v1 validates both ids; v2 only rejects an out-of-range source
+ if (v1 < 1 || v1 > maxHotspot || (!_engine->isV2() && (v2 < 1 || v2 > maxHotspot))) {
setScriptError(0x1e);
return OpcodeResult::Continue;
}
Commit: f97da856698a7bd5a90d8d285b9d006bd9f16b9b
https://github.com/scummvm/scummvm/commit/f97da856698a7bd5a90d8d285b9d006bd9f16b9b
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-08-23T21:11:43+02:00
Commit Message:
MACS2: ignore enhancement here if there is a native action bar
Changed paths:
engines/macs2/view1.cpp
diff --git a/engines/macs2/view1.cpp b/engines/macs2/view1.cpp
index d66044f6e86..c60164a88b5 100644
--- a/engines/macs2/view1.cpp
+++ b/engines/macs2/view1.cpp
@@ -444,8 +444,9 @@ void View1::updateCursor(const byte *palette) {
// The array has 33 entries (indices 0-32). Cursor modes 0x13-0x1A map to entries 18-25.
int mode = (int)g_engine->_scriptExecutor->_cursorMode - 1;
- // SCUMM-style UI: gameplay verbs share the walk cursor; the sentence line shows the active verb.
- if (hasPersistentActionBar()) {
+ // SCUMM-style enhancement only: gameplay verbs share the walk cursor; the sentence
+ // line shows the active verb. Native V2 HUD must keep per-MouseNr graphics.
+ if (g_engine->enhancementEnabled(kEnhUIUX) && !g_engine->hasNativeHudAssets()) {
const Script::MouseMode cursorMode = g_engine->_scriptExecutor->_cursorMode;
switch (cursorMode) {
case Script::MouseMode::Talk:
Commit: 959699743c6c4b2e4a8d059c4c0c63d0dc97aa89
https://github.com/scummvm/scummvm/commit/959699743c6c4b2e4a8d059c4c0c63d0dc97aa89
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-08-23T21:11:43+02:00
Commit Message:
MACS2: fixed cursor hotspots
Changed paths:
engines/macs2/actionbar.cpp
engines/macs2/view1.cpp
diff --git a/engines/macs2/actionbar.cpp b/engines/macs2/actionbar.cpp
index e8e00fa31b8..6870c42b0c1 100644
--- a/engines/macs2/actionbar.cpp
+++ b/engines/macs2/actionbar.cpp
@@ -129,11 +129,11 @@ void ActionBar::syncActiveVerbFromCursorMode() {
}
bool ActionBar::useScummSkin() const {
- return g_engine->enhancementEnabled(kEnhUIUX);
+ return !useNativeSkin() && g_engine->enhancementEnabled(kEnhUIUX);
}
bool ActionBar::useNativeSkin() const {
- return !useScummSkin() && g_engine->hasNativeHudAssets();
+ return g_engine->hasNativeHudAssets();
}
int ActionBar::gameAreaBottomY() const {
diff --git a/engines/macs2/view1.cpp b/engines/macs2/view1.cpp
index c60164a88b5..01e2176295e 100644
--- a/engines/macs2/view1.cpp
+++ b/engines/macs2/view1.cpp
@@ -489,7 +489,16 @@ void View1::updateCursor(const byte *palette) {
rgbaCursor[i] = rgbaCursorFormat.RGBToColor(paletteEntry[0], paletteEntry[1], paletteEntry[2]);
}
- CursorMan.replaceCursor(rgbaCursor.data(), width, height, width >> 1, height >> 1, 0, &rgbaCursorFormat);
+ int hotX = width >> 1;
+ int hotY = height >> 1;
+ if (mode >= 0 && mode < ARRAYSIZE(g_engine->_cursorHotspots)) {
+ const Common::Point &hot = g_engine->_cursorHotspots[mode];
+ if (hot.x != 0 || hot.y != 0) {
+ hotX = hot.x;
+ hotY = hot.y;
+ }
+ }
+ CursorMan.replaceCursor(rgbaCursor.data(), width, height, hotX, hotY, 0, &rgbaCursorFormat);
// Enable a cursor palette so the backend won't re-blit the cursor on
// every screen palette change. The macs2 engine uses RGBA cursors with
// baked-in palette colors, so the cursor palette content is irrelevant -
Commit: 62980da2baf35f7fa6d818b3306dfb0a83649a97
https://github.com/scummvm/scummvm/commit/62980da2baf35f7fa6d818b3306dfb0a83649a97
Author: Martin Gerhardy (martin.gerhardy at gmail.com)
Date: 2026-08-23T21:11:43+02:00
Commit Message:
MACS2: fixed v2 map input
Changed paths:
engines/macs2/scriptexecutor.cpp
diff --git a/engines/macs2/scriptexecutor.cpp b/engines/macs2/scriptexecutor.cpp
index 1f8ddd0415f..e2b268c7a73 100644
--- a/engines/macs2/scriptexecutor.cpp
+++ b/engines/macs2/scriptexecutor.cpp
@@ -1252,8 +1252,12 @@ OpcodeResult Script::ScriptExecutor::scriptChangeScene() {
currentView->startFadingWithSpeed(transitionSpeed);
}
- // Binary step 8: set cursor to Walk (0x16) after scene change
- _engine->setCursorMode(Script::MouseMode::Walk);
+ // V1 scriptChangeScene (1008:ad6e) forces Walk (0x16) after init
+ // V2 does not: it leaves it at the value scripts left it (typically via opcode 0x67 setCursorType)
+ // Overview/world-map init sets Use (0x15) so hotspot special 0x01 matches; forcing Walk here would
+ // send left-clicks through the walk path instead
+ if (!_engine->isV2())
+ _engine->setCursorMode(Script::MouseMode::Walk);
_interactedObjectID = 0;
_interactedInventoryItemId = 0;
// Binary: after init completes synchronously, terminate outer script context and
More information about the Scummvm-git-logs
mailing list