[Scummvm-git-logs] scummvm master -> cff4e56a26e74746e9fc1e5c0f634bfe31584a95
neuromancer
noreply at scummvm.org
Sat Aug 22 10:00:03 UTC 2026
This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
534c32850a COLONY: added varios commands to test goals and game ending
d8df792d60 COLONY: adjusted message displaying to match the original sources
cff4e56a26 SCUMM: RA: manually initialize _vm->_actionMap
Commit: 534c32850a7a8ecbb470e5daca4cc9fafe9a65a0
https://github.com/scummvm/scummvm/commit/534c32850a7a8ecbb470e5daca4cc9fafe9a65a0
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-08-22T11:59:36+02:00
Commit Message:
COLONY: added varios commands to test goals and game ending
Changed paths:
engines/colony/colony.h
engines/colony/console.cpp
engines/colony/console.h
engines/colony/intro.cpp
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index ab065095e88..b159fe44698 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -898,6 +898,7 @@ private:
void takeOff();
void fullOfStars();
void gameOver(bool kill);
+ void gameOver(bool kill, int savedCryos);
int countSavedCryos() const;
void playAnimation();
void updateAnimation();
diff --git a/engines/colony/console.cpp b/engines/colony/console.cpp
index e64cb2f89bd..a109fe08579 100644
--- a/engines/colony/console.cpp
+++ b/engines/colony/console.cpp
@@ -74,10 +74,47 @@ const char *featureTypeName(int type) {
}
}
+const char *reactorStateName(int state) {
+ switch (state) {
+ case 0: return "closed, core installed";
+ case 1: return "open, core installed";
+ case 2: return "open, core removed";
+ default: return "unknown";
+ }
+}
+
+const char *reactorPowerName(int power) {
+ switch (power) {
+ case 0: return "off";
+ case 1: return "emergency";
+ case 2: return "full";
+ default: return "unknown";
+ }
+}
+
+struct NumberedTeleporter {
+ int x;
+ int y;
+ int destination;
+};
+
+// MAP.7 contains the four working booths. Their destinations establish the
+// original numbering: 1 -> 2 -> 3 -> 4 -> nowhere.
+const NumberedTeleporter kNumberedTeleporters[] = {
+ {9, 3, 2},
+ {3, 3, 3},
+ {3, 9, 4},
+ {9, 9, 0}
+};
+
Debugger::Debugger(ColonyEngine *vm) : GUI::Debugger(), _vm(vm) {
registerCmd("teleport", WRAP_METHOD(Debugger, cmdTeleport));
+ registerCmd("teleporter", WRAP_METHOD(Debugger, cmdTeleporter));
+ registerCmd("teleporters", WRAP_METHOD(Debugger, cmdTeleporters));
registerCmd("pos", WRAP_METHOD(Debugger, cmdPos));
registerCmd("info", WRAP_METHOD(Debugger, cmdInfo));
+ registerCmd("goals", WRAP_METHOD(Debugger, cmdGoals));
+ registerCmd("win", WRAP_METHOD(Debugger, cmdWin));
registerCmd("robots", WRAP_METHOD(Debugger, cmdRobots));
registerCmd("map", WRAP_METHOD(Debugger, cmdMap));
registerCmd("give", WRAP_METHOD(Debugger, cmdGive));
@@ -89,6 +126,192 @@ Debugger::Debugger(ColonyEngine *vm) : GUI::Debugger(), _vm(vm) {
registerCmd("spawn", WRAP_METHOD(Debugger, cmdSpawn));
}
+void Debugger::postEnter() {
+ GUI::Debugger::postEnter();
+
+ if (_pendingEnding == 0)
+ return;
+
+ const int ending = _pendingEnding;
+ _pendingEnding = 0;
+ const bool destroyPlanet = ending <= 3;
+ const int savedCryos = (ending == 1 || ending == 4) ? 6 :
+ ((ending == 2 || ending == 5) ? 1 : 0);
+ _vm->gameOver(destroyPlanet, savedCryos);
+}
+
+bool Debugger::getTeleporterLocation(int number, int &level, int &x, int &y) const {
+ const NumberedTeleporter &teleporter = kNumberedTeleporters[number - 1];
+ level = 7;
+ x = teleporter.x;
+ y = teleporter.y;
+
+ for (uint i = 0; i < _vm->_patches.size(); i++) {
+ const PatchEntry &patch = _vm->_patches[i];
+ if (patch.type == kObjTeleport && patch.from.level == 7 &&
+ patch.from.xindex == teleporter.x && patch.from.yindex == teleporter.y) {
+ level = patch.to.level;
+ x = patch.to.xindex;
+ y = patch.to.yindex;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Debugger::cmdTeleporters(int argc, const char **argv) {
+ debugPrintf("Numbered teleporter series (1 -> 2 -> 3 -> 4):\n");
+ for (uint i = 0; i < ARRAYSIZE(kNumberedTeleporters); i++) {
+ int level, x, y;
+ getTeleporterLocation(i + 1, level, x, y);
+ if (level == 100) {
+ debugPrintf(" %u: carried by the forklift", i + 1);
+ } else {
+ debugPrintf(" %u: level %d at (%d, %d)", i + 1, level, x, y);
+ }
+
+ if (kNumberedTeleporters[i].destination != 0)
+ debugPrintf(" -> %d\n", kNumberedTeleporters[i].destination);
+ else
+ debugPrintf(" -> nowhere\n");
+ }
+ return true;
+}
+
+bool Debugger::cmdTeleporter(int argc, const char **argv) {
+ if (argc != 2) {
+ debugPrintf("Usage: teleporter <number>\n");
+ debugPrintf("Recalls working teleporter 1-4 into the cell in front of the player.\n");
+ debugPrintf("Use 'teleporters' to list their current locations.\n");
+ return true;
+ }
+
+ char *end = nullptr;
+ const long number = strtol(argv[1], &end, 10);
+ if (!argv[1][0] || *end != '\0' || number < 1 || number > (long)ARRAYSIZE(kNumberedTeleporters)) {
+ debugPrintf("Invalid teleporter number '%s' (must be 1-4)\n", argv[1]);
+ return true;
+ }
+
+ if (_vm->_gameMode != kModeColony || _vm->_level < 1 || _vm->_level > 7) {
+ debugPrintf("Must be in colony mode to recall a teleporter\n");
+ return true;
+ }
+
+ int level, x, y;
+ const bool relocated = getTeleporterLocation((int)number, level, x, y);
+ if (level != 100 && (level < 1 || level > 7 || x < 0 || x > 31 || y < 0 || y > 31)) {
+ debugPrintf("Teleporter %ld has an invalid location: level %d at (%d, %d)\n",
+ number, level, x, y);
+ return true;
+ }
+
+ const int facingX = _vm->_cost[_vm->_me.ang];
+ const int facingY = _vm->_sint[_vm->_me.ang];
+ const bool horizontal = ABS(facingX) >= ABS(facingY);
+ const int stepX = horizontal ? (facingX >= 0 ? 1 : -1) : 0;
+ const int stepY = horizontal ? 0 : (facingY >= 0 ? 1 : -1);
+ const int targetX = _vm->_me.xindex + stepX;
+ const int targetY = _vm->_me.yindex + stepY;
+ if (targetX < 1 || targetX > 30 || targetY < 1 || targetY > 30) {
+ debugPrintf("Target cell (%d, %d) is out of bounds\n", targetX, targetY);
+ return true;
+ }
+
+ if (level == _vm->_level && x == targetX && y == targetY) {
+ debugPrintf("Teleporter %ld is already in front of the player at (%d, %d)\n",
+ number, targetX, targetY);
+ return false;
+ }
+
+ if (_vm->_robotArray[targetX][targetY] != 0) {
+ debugPrintf("Target cell (%d, %d) is already occupied\n", targetX, targetY);
+ return true;
+ }
+
+ bool blocked = false;
+ if (stepX > 0)
+ blocked = (_vm->_wall[targetX][targetY] & 2) != 0;
+ else if (stepX < 0)
+ blocked = (_vm->_wall[_vm->_me.xindex][_vm->_me.yindex] & 2) != 0;
+ else if (stepY > 0)
+ blocked = (_vm->_wall[targetX][targetY] & 1) != 0;
+ else
+ blocked = (_vm->_wall[_vm->_me.xindex][_vm->_me.yindex] & 1) != 0;
+ if (blocked) {
+ debugPrintf("A wall blocks target cell (%d, %d)\n", targetX, targetY);
+ return true;
+ }
+
+ if (!relocated && _vm->_patches.size() >= 100) {
+ debugPrintf("Cannot recall teleporter %ld: relocation table is full\n", number);
+ return true;
+ }
+
+ int oldObject = -1;
+ if (level == _vm->_level) {
+ for (uint i = 0; i < _vm->_objects.size(); i++) {
+ const Thing &obj = _vm->_objects[i];
+ if (obj.alive && obj.type == kObjTeleport &&
+ obj.where.xindex == x && obj.where.yindex == y) {
+ oldObject = i;
+ break;
+ }
+ }
+ }
+
+ if (oldObject >= 0) {
+ if (_vm->_robotArray[x][y] == oldObject + 1)
+ _vm->_robotArray[x][y] = 0;
+ _vm->_objects[oldObject].alive = 0;
+ }
+
+ const int targetXLoc = (targetX << 8) + 128;
+ const int targetYLoc = (targetY << 8) + 128;
+ if (!_vm->createObject(kObjTeleport, targetXLoc, targetYLoc, 0)) {
+ if (oldObject >= 0) {
+ _vm->_objects[oldObject].alive = 1;
+ _vm->_robotArray[x][y] = oldObject + 1;
+ }
+ debugPrintf("Failed to place teleporter %ld at (%d, %d)\n", number, targetX, targetY);
+ return true;
+ }
+
+ PassPatch from = {};
+ from.level = level;
+ from.xindex = x;
+ from.yindex = y;
+
+ PassPatch to = {};
+ to.level = _vm->_level;
+ to.xindex = targetX;
+ to.yindex = targetY;
+ to.xloc = targetXLoc;
+ to.yloc = targetYLoc;
+ to.ang = 0;
+
+ uint8 mapdata[5] = {6, kObjTeleport - kBaseObject, 0, 0, 0};
+ const int destination = kNumberedTeleporters[number - 1].destination;
+ if (destination != 0) {
+ mapdata[2] = 7;
+ mapdata[3] = kNumberedTeleporters[destination - 1].x;
+ mapdata[4] = kNumberedTeleporters[destination - 1].y;
+ }
+ _vm->newPatch(kObjTeleport, from, to, mapdata);
+
+ if (level == 100 && _vm->_carryType == kObjTeleport) {
+ _vm->_carryType = 0;
+ if (_vm->_fl == 2)
+ _vm->_fl = 1;
+ }
+ _vm->_bumpedObject = 0;
+ _vm->setPlayerCellMarker();
+
+ debugPrintf("Recalled teleporter %ld to level %d at (%d, %d)\n",
+ number, _vm->_level, targetX, targetY);
+ return false;
+}
+
bool Debugger::cmdTeleport(int argc, const char **argv) {
if (argc < 2) {
debugPrintf("Usage: teleport <level> [<x> <y>]\n");
@@ -190,6 +413,63 @@ bool Debugger::cmdInfo(int argc, const char **argv) {
return true;
}
+bool Debugger::cmdGoals(int argc, const char **argv) {
+ if (argc != 1) {
+ debugPrintf("Usage: goals\n");
+ return true;
+ }
+
+ const int savedCryos = _vm->countSavedCryos();
+ debugPrintf("=== Mission Goals ===\n");
+ debugPrintf("Cryogenic chambers recovered: %d / 6%s\n",
+ savedCryos, savedCryos == 6 ? " (complete)" : "");
+
+ const char *reactorNames[] = {"Ship", "Colony"};
+ for (int i = 0; i < 2; i++) {
+ debugPrintf("%s reactor: %s; power %s (%d)\n",
+ reactorNames[i], reactorStateName(_vm->_coreState[i]),
+ reactorPowerName(_vm->_corePower[i]), _vm->_corePower[i]);
+ }
+
+ if (_vm->_fl == 2 && _vm->_carryType == kObjReactor) {
+ debugPrintf("Reactor core in forklift: yes; power %s (%d)\n",
+ reactorPowerName(_vm->_corePower[2]), _vm->_corePower[2]);
+ } else {
+ debugPrintf("Reactor core in forklift: no\n");
+ }
+ return true;
+}
+
+bool Debugger::cmdWin(int argc, const char **argv) {
+ static const char *const endingDescriptions[] = {
+ "planet destroyed, all 6 cryos recovered",
+ "planet destroyed, some cryos recovered",
+ "planet destroyed, no cryos recovered",
+ "planet spared, all 6 cryos recovered",
+ "planet spared, some cryos recovered",
+ "planet spared, no cryos recovered"
+ };
+
+ if (argc != 2) {
+ debugPrintf("Usage: win <ending>\n");
+ for (uint i = 0; i < ARRAYSIZE(endingDescriptions); i++)
+ debugPrintf(" %u: %s\n", i + 1, endingDescriptions[i]);
+ return true;
+ }
+
+ char *end = nullptr;
+ const long ending = strtol(argv[1], &end, 10);
+ if (!argv[1][0] || *end != '\0' || ending < 1 || ending > (long)ARRAYSIZE(endingDescriptions)) {
+ debugPrintf("Invalid ending number '%s' (must be 1-6)\n", argv[1]);
+ return true;
+ }
+
+ debugPrintf("Playing ending %ld: %s\n", ending, endingDescriptions[ending - 1]);
+ _pendingEnding = (int)ending;
+ detach();
+ return false;
+}
+
bool Debugger::cmdRobots(int argc, const char **argv) {
int count = 0;
for (int i = 0; i < (int)_vm->_objects.size(); i++) {
diff --git a/engines/colony/console.h b/engines/colony/console.h
index 8a2b0630d9a..b7cb9af782c 100644
--- a/engines/colony/console.h
+++ b/engines/colony/console.h
@@ -40,9 +40,15 @@ public:
private:
ColonyEngine *_vm = nullptr;
+ int _pendingEnding = 0;
+ void postEnter() override;
bool cmdTeleport(int argc, const char **argv);
+ bool cmdTeleporter(int argc, const char **argv);
+ bool cmdTeleporters(int argc, const char **argv);
bool cmdPos(int argc, const char **argv);
bool cmdInfo(int argc, const char **argv);
+ bool cmdGoals(int argc, const char **argv);
+ bool cmdWin(int argc, const char **argv);
bool cmdRobots(int argc, const char **argv);
bool cmdMap(int argc, const char **argv);
bool cmdGive(int argc, const char **argv);
@@ -52,6 +58,7 @@ private:
bool cmdColony(int argc, const char **argv);
bool cmdForklift(int argc, const char **argv);
bool cmdSpawn(int argc, const char **argv);
+ bool getTeleporterLocation(int number, int &level, int &x, int &y) const;
};
}
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index e50c9161447..b8d54a892f1 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -1458,6 +1458,10 @@ void ColonyEngine::fullOfStars() {
}
void ColonyEngine::gameOver(bool kill) {
+ gameOver(kill, countSavedCryos());
+}
+
+void ColonyEngine::gameOver(bool kill, int savedCryos) {
Common::Rect savedScreenR = _screenR;
Common::Rect savedClip = _clip;
int savedCenterX = _centerX;
@@ -1473,7 +1477,6 @@ void ColonyEngine::gameOver(bool kill) {
CursorMan.setDefaultArrowCursor(true);
CursorMan.showMouse(true);
- const int savedCryos = countSavedCryos();
int textEntry;
if (kill)
Commit: d8df792d60051d1329b0dc4bfec6e26a25eb6157
https://github.com/scummvm/scummvm/commit/d8df792d60051d1329b0dc4bfec6e26a25eb6157
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-08-22T11:59:36+02:00
Commit Message:
COLONY: adjusted message displaying to match the original sources
Changed paths:
engines/colony/animation.cpp
engines/colony/colony.cpp
engines/colony/colony.h
engines/colony/interaction.cpp
engines/colony/intro.cpp
engines/colony/movement.cpp
engines/colony/sound.cpp
engines/colony/sound.h
engines/colony/ui.cpp
diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 02607ade341..fa1cffc3b5e 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -605,6 +605,7 @@ void ColonyEngine::playAnimation() {
needsDraw = true;
} else if (event.type == Common::EVENT_LBUTTONDOWN) {
const Common::Point pt = eventMouseToLogical(event.mouse);
+ _messageSourceRect = Common::Rect();
if (handleColonyCoderClick(pt)) {
needsDraw = true;
} else if (!_animExitStrip.isEmpty() && _animExitStrip.contains(pt)) {
@@ -617,6 +618,7 @@ void ColonyEngine::playAnimation() {
int item = whichSprite(pt);
if (item > 0) {
handleAnimationClick(item);
+ _messageSourceRect = Common::Rect();
needsDraw = true;
}
}
@@ -692,6 +694,7 @@ void ColonyEngine::playAnimation() {
}
if (item > 0) {
+ _messageSourceRect = Common::Rect();
handleAnimationClick(item);
needsDraw = true;
}
@@ -1390,6 +1393,8 @@ int ColonyEngine::whichSprite(const Common::Point &p) {
debugC(1, kColonyDebugAnimation, "Sprite %d HIT. type=%d frozen=%d Frame %d, Sprite %d. Box: (%d,%d,%d,%d)",
i + 1, ls->type, ls->frozen, cnum, spriteIdx, r.left, r.top, r.right, r.bottom);
+ r.translate(ox, oy);
+ _messageSourceRect = r;
return i + 1;
}
diff --git a/engines/colony/colony.cpp b/engines/colony/colony.cpp
index 4842a06a2aa..7586914bf0b 100644
--- a/engines/colony/colony.cpp
+++ b/engines/colony/colony.cpp
@@ -1357,4 +1357,63 @@ bool ColonyEngine::waitForInput() {
return false;
}
+bool ColonyEngine::waitForMessageInput() {
+ // Ignore the input that opened the message.
+ _moveForward = _moveBackward = false;
+ _strafeLeft = _strafeRight = false;
+ _rotateLeft = _rotateRight = false;
+ _sprint = false;
+
+ Common::EventManager *eventMan = _system->getEventManager();
+ auto handleSystemEvent = [&](const Common::Event &event) {
+ if (event.type == Common::EVENT_QUIT || event.type == Common::EVENT_RETURN_TO_LAUNCHER) {
+ quitGame();
+ return false;
+ }
+ if (event.type == Common::EVENT_SCREEN_CHANGED)
+ _gfx->computeScreenViewport();
+ return true;
+ };
+
+ while (eventMan->getButtonState() && !shouldQuit()) {
+ Common::Event event;
+ while (eventMan->pollEvent(event)) {
+ if (!handleSystemEvent(event))
+ return false;
+ }
+ _system->updateScreen();
+ _system->delayMillis(10);
+ }
+
+ {
+ Common::Event event;
+ while (eventMan->pollEvent(event)) {
+ if (!handleSystemEvent(event))
+ return false;
+ }
+ }
+
+ eventMan->purgeMouseEvents();
+ eventMan->purgeKeyboardEvents();
+
+ while (!shouldQuit()) {
+ Common::Event event;
+ while (eventMan->pollEvent(event)) {
+ if (!handleSystemEvent(event))
+ return false;
+
+ if (event.type == Common::EVENT_LBUTTONDOWN ||
+ event.type == Common::EVENT_RBUTTONDOWN ||
+ event.type == Common::EVENT_KEYDOWN ||
+ event.type == Common::EVENT_CUSTOM_ENGINE_ACTION_START) {
+ return true;
+ }
+ }
+ _system->updateScreen();
+ _system->delayMillis(10);
+ }
+
+ return false;
+}
+
} // End of namespace Colony
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index b159fe44698..a8178fd0114 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -496,6 +496,7 @@ public:
bool checkSkipRequested();
bool checkClickRequested();
bool waitForInput();
+ bool waitForMessageInput();
void checkCenter();
void fallThroughHole();
void playTunnelEffect(bool falling);
@@ -864,6 +865,7 @@ private:
bool _animExitInside = false;
Common::Rect _animExitStrip;
Common::Rect _animExitButton;
+ Common::Rect _messageSourceRect;
int _coderPick[4] = {};
int _coderCursor = 0;
int _coderPressed = -1;
@@ -891,7 +893,7 @@ private:
bool makeStars(const Common::Rect &r, int btn);
bool makeBlackHole();
bool makePlanet();
- bool timeSquare(const Common::String &str, const Graphics::Font *macFont = nullptr);
+ bool timeSquare(const Common::String &str, const Graphics::Font *macFont = nullptr, bool gameOver = false);
bool drawPict(int resID);
bool loadAnimation(const Common::String &name);
void deleteAnimation();
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index 212836f6c66..15d7aac0a82 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -82,7 +82,6 @@ void ColonyEngine::interactWithObject(int objNum) {
playAnimation();
break;
default:
- inform("IT DOES NOT SEEM TO BE WORKING.", true);
break;
}
break;
@@ -97,7 +96,6 @@ void ColonyEngine::interactWithObject(int objNum) {
playAnimation();
break;
default:
- inform("PROJECTOR OFFLINE", true);
break;
}
break;
@@ -167,16 +165,13 @@ void ColonyEngine::interactWithObject(int objNum) {
case kObjToilet:
case kObjPToilet:
_sound->play(Sound::kToilet);
- inform("IT'S A TOILET.", true);
break;
case kObjTub:
_sound->play(Sound::kBath);
- inform("A BATHTUB. NO TIME FOR A SOAK.", true);
break;
case kObjSink:
_sound->play(Sound::kSink);
- inform("A SINK. IT'S DRY.", true);
break;
case kObjTV:
if (_level == 1)
diff --git a/engines/colony/intro.cpp b/engines/colony/intro.cpp
index b8d54a892f1..dc00baa3688 100644
--- a/engines/colony/intro.cpp
+++ b/engines/colony/intro.cpp
@@ -38,7 +38,6 @@
#include "graphics/macgui/mactext.h"
#include "graphics/macgui/macwindowmanager.h"
#include "graphics/managed_surface.h"
-#include "gui/message.h"
#include "image/pict.h"
#include "colony/colony.h"
@@ -1050,7 +1049,7 @@ bool ColonyEngine::makePlanet() {
return false;
}
-bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *macFont) {
+bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *macFont, bool gameOver) {
// Original: TimeSquare() in intro.c
// Mac and DOS use different presentation here. DOS is a monochrome/gray
// warning band with 16-pixel blits and white text; Mac uses the colorful
@@ -1059,13 +1058,18 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
_gfx->clear(_gfx->black());
Graphics::DosFont dosFont;
- const Graphics::Font *font = macFont ? macFont : (const Graphics::Font *)&dosFont;
+ Graphics::MacFont systemFont(Graphics::kMacFontSystem, 12);
+ const bool macStyle = isMacRenderMode();
+ const Graphics::Font *fallbackMacFont = macStyle && _wm && _wm->_fontMan ?
+ _wm->_fontMan->getFont(systemFont) : nullptr;
+ const Graphics::Font *font = macStyle ? (macFont ? macFont : fallbackMacFont) : nullptr;
+ if (!font)
+ font = &dosFont;
int swidth = font->getStringWidth(str);
int centery = _height / 2 - 10;
- const bool bwMac = (macFont && !isMacColorMode());
- const bool macStyle = (macFont != nullptr);
+ const bool bwMac = macStyle && !isMacColorMode();
const uint32 grayIndex = 160;
const uint32 textIndex = 176;
const Common::Rect textBand(0, centery + 1, _width, centery + 16);
@@ -1112,7 +1116,7 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
// DOS uses 16-pixel blits of a black text box; Mac scrolls smoothly.
int targetX = (_width - swidth) / 2;
const int startX = macStyle ? _width : (_width + 16);
- const int stepX = macStyle ? 2 : 16;
+ const int stepX = macStyle ? 1 : 16;
const int endX = macStyle ? -swidth : (-swidth - 16);
const uint32 scrollDelayMs = macStyle ? 8 : (1000 / 60);
@@ -1126,38 +1130,34 @@ bool ColonyEngine::timeSquare(const Common::String &str, const Graphics::Font *m
_system->delayMillis(scrollDelayMs);
}
- // Phase 2: Klaxon flash â original intro.c lines 312-322.
- // DOS does 4 full klaxon cycles here; Mac uses the longer 6-flash variant.
+ // Wait for each cue before the next flash.
_sound->stop();
_gfx->setXorMode(true);
const int klaxonCount = macStyle ? 6 : 4;
- const uint32 dosKlaxonFlashMs = 12 * 1000 / 60;
for (int i = 0; i < klaxonCount; i++) {
- if (checkSkipRequested()) {
- _gfx->setXorMode(false);
- return true;
+ while (_sound->isPlaying() && !shouldQuit()) {
+ if (checkSkipRequested()) {
+ _gfx->setXorMode(false);
+ return true;
+ }
+ _system->delayMillis(10);
}
+ _sound->stop();
- // InvertRect(&invrt) â XOR the text band
+ _sound->play(macStyle && gameOver ? Sound::kChime : Sound::kKlaxon);
_gfx->fillRect(textBand, 0xFFFFFFFF);
_gfx->copyToScreen();
-
- _sound->play(Sound::kKlaxon);
- if (macStyle) {
- // Keep the snappier Mac timing.
- _system->delayMillis(200);
- } else {
- // At modern frame rates, waiting for the synthesized klaxon to end
- // drags these warning cards out too long. Keep a short fixed flash.
- _system->delayMillis(dosKlaxonFlashMs);
+ if (!_sound->isPlaying())
+ _system->delayMillis(100);
+ }
+ while (_sound->isPlaying() && !shouldQuit()) {
+ if (checkSkipRequested()) {
+ _gfx->setXorMode(false);
+ return true;
}
+ _system->delayMillis(10);
}
_gfx->setXorMode(false);
- if (macStyle) {
- // Wait for last klaxon to finish
- while (_sound->isPlaying() && !shouldQuit())
- _system->delayMillis(10);
- }
_sound->stop();
// Phase 3: Mac resumes Mars here; DOS scrolls out silently.
@@ -1299,12 +1299,6 @@ void ColonyEngine::terminateGame(bool blowup) {
_gfx->clear(_gfx->black());
_gfx->copyToScreen();
- const char *msg[] = {
- "YOU HAVE BEEN TERMINATED",
- nullptr
- };
- printMessage(msg, true);
-
_screenR = savedScreenR;
_clip = savedClip;
_centerX = savedCenterX;
@@ -1333,28 +1327,71 @@ void ColonyEngine::terminateGame(bool blowup) {
return;
}
+ const char *msg[] = {
+ " YOU HAVE BEEN TERMINATED! ",
+ " Type 'n' to start a new game. ",
+ " Type 'l' to load a game. ",
+ " Type 'q' to quit the game. ",
+ nullptr
+ };
+ Common::EventManager *eventMan = _system->getEventManager();
+
while (!shouldQuit()) {
- Common::U32StringArray altButtons;
- altButtons.push_back(_("Load Game"));
- altButtons.push_back(_("Quit"));
- GUI::MessageDialog prompt(_("You have been terminated."), _("New Game"), altButtons);
+ printMessage(msg, false);
+ eventMan->purgeKeyboardEvents();
+ int choice = 0;
+ while (!choice && !shouldQuit()) {
+ Common::Event event;
+ while (eventMan->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_QUIT:
+ case Common::EVENT_RETURN_TO_LAUNCHER:
+ choice = 'q';
+ break;
+ case Common::EVENT_KEYDOWN:
+ if (event.kbd.keycode == Common::KEYCODE_n)
+ choice = 'n';
+ else if (event.kbd.keycode == Common::KEYCODE_l)
+ choice = 'l';
+ else if (event.kbd.keycode == Common::KEYCODE_q)
+ choice = 'q';
+ break;
+ case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
+ // Q is mapped to rotate left.
+ if (event.customType == kActionRotateLeft || event.customType == kActionEscape)
+ choice = 'q';
+ break;
+ case Common::EVENT_SCREEN_CHANGED:
+ _gfx->computeScreenViewport();
+ printMessage(msg, false);
+ break;
+ default:
+ break;
+ }
+ }
+ _system->updateScreen();
+ _system->delayMillis(10);
+ }
- switch (runDialog(prompt)) {
- case GUI::kMessageOK:
+ switch (choice) {
+ case 'n':
+ inform("New Game!", false);
startNewGame();
_mouseLocked = savedMouseLocked;
updateMouseCapture(true);
return;
- case GUI::kMessageAlt:
+ case 'l':
if (loadGameDialog()) {
_mouseLocked = savedMouseLocked;
updateMouseCapture(true);
return;
}
break;
- default:
+ case 'q':
quitGame();
return;
+ default:
+ break;
}
}
}
@@ -1510,36 +1547,68 @@ void ColonyEngine::gameOver(bool kill, int savedCryos) {
_gfx->copyToScreen();
doText(textEntry, 2);
- _gfx->clear(_gfx->black());
- _gfx->copyToScreen();
- _sound->play(Sound::kStars4);
- makeStars(_screenR, 0);
- _sound->stop();
+ auto playFinalExplosion = [&]() {
+ _gfx->clear(_gfx->black());
+ _gfx->copyToScreen();
+ _sound->play(Sound::kExplode);
+ if (_sound->isPlaying()) {
+ while (_sound->isPlaying() && !shouldQuit()) {
+ _gfx->clear(_gfx->white());
+ _gfx->copyToScreen();
+ _system->delayMillis(50);
+ _gfx->clear(_gfx->black());
+ _gfx->copyToScreen();
+ _system->delayMillis(50);
+ }
+ } else {
+ for (int i = 0; i < 4; i++) {
+ _gfx->clear((i & 1) ? _gfx->black() : _gfx->white());
+ _gfx->copyToScreen();
+ _system->delayMillis(50);
+ }
+ }
+ _sound->stop();
+ };
- _gfx->clear(_gfx->black());
- _gfx->copyToScreen();
- timeSquare("...THE END...", nullptr);
+ if (isMacRenderMode()) {
+ _gfx->clear(_gfx->black());
+ _gfx->copyToScreen();
+ _sound->play(Sound::kMars, true);
+ makeStars(_screenR, 0);
- _gfx->clear(_gfx->black());
- _gfx->copyToScreen();
- _sound->play(Sound::kExplode);
- if (_sound->isPlaying()) {
- while (_sound->isPlaying() && !shouldQuit()) {
- _gfx->clear(_gfx->white());
- _gfx->copyToScreen();
- _system->delayMillis(50);
- _gfx->clear(_gfx->black());
- _gfx->copyToScreen();
- _system->delayMillis(50);
+ Graphics::MacFONTFont *macFont = nullptr;
+ if (_resMan) {
+ const uint16 fontResID = 24332; // FOND 190, 12pt
+ Common::SeekableReadStream *fontStream = _resMan->getResource(MKTAG('N', 'F', 'N', 'T'), fontResID);
+ if (!fontStream)
+ fontStream = _resMan->getResource(MKTAG('F', 'O', 'N', 'T'), fontResID);
+ if (fontStream) {
+ macFont = new Graphics::MacFONTFont();
+ if (!macFont->loadFont(*fontStream)) {
+ delete macFont;
+ macFont = nullptr;
+ }
+ delete fontStream;
+ }
}
+ timeSquare("...THE END...", macFont, true);
+ delete macFont;
+
+ _gfx->clear(_gfx->black());
+ _gfx->copyToScreen();
+ makeStars(_screenR, 0);
+ _sound->stop();
+ playFinalExplosion();
} else {
- for (int i = 0; i < 4; i++) {
- _gfx->clear((i & 1) ? _gfx->black() : _gfx->white());
- _gfx->copyToScreen();
- _system->delayMillis(50);
- }
+ // DOS uses Inform here, not TimeSquare.
+ _gfx->clear(_gfx->black());
+ _gfx->copyToScreen();
+ _sound->play(Sound::kStars4);
+ makeStars(_screenR, 0);
+ _sound->stop();
+ playFinalExplosion();
+ inform("THE END", true);
}
- _sound->stop();
_screenR = savedScreenR;
_clip = savedClip;
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 297ab2ad35b..3df6164f05f 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -912,10 +912,8 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
case kWallFeatureElevator: {
if (pobject != &_me)
return 0;
- if (_corePower[1] == 0) {
- inform("ELEVATOR HAS NO POWER.", true);
+ if (_corePower[1] == 0)
return 0;
- }
// DOS DoElevator: play elevator animation with floor selection
if (!loadAnimation("elev"))
diff --git a/engines/colony/sound.cpp b/engines/colony/sound.cpp
index ae342bc7530..82d232786e0 100644
--- a/engines/colony/sound.cpp
+++ b/engines/colony/sound.cpp
@@ -194,6 +194,7 @@ void Sound::playPCSpeaker(int soundID) {
}
break;
case kChime:
+ case kDiDit:
queueTick(4649, 7);
queueTick(3690, 7);
queueTick(3103, 7);
@@ -359,6 +360,7 @@ bool Sound::playMacSound(int soundID, bool loop) {
case kPShot: resID = 27539; break; // PLANETSHOT
case kTest: resID = 25795; break;
case kDit: resID = 1516; break;
+ case kDiDit: resID = 4274; break;
case kSink: resID = 2920; break;
case kClatter: resID = 11208; break;
case kStop: resID = 29382; break; // FULLSTOP
diff --git a/engines/colony/sound.h b/engines/colony/sound.h
index a83212f3238..60e4ec147f4 100644
--- a/engines/colony/sound.h
+++ b/engines/colony/sound.h
@@ -62,6 +62,7 @@ public:
kPShot,
kTest,
kDit,
+ kDiDit,
kSink,
kClatter,
kStop,
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index c8729abbd50..28b718ec27a 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -31,6 +31,7 @@
#include "common/macresman.h"
#include "common/system.h"
#include "common/util.h"
+#include "graphics/cursorman.h"
#include "graphics/fontman.h"
#include "graphics/fonts/dosfont.h"
#include "graphics/macgui/macfontmanager.h"
@@ -45,11 +46,72 @@
namespace Colony {
-bool drawMacTextPopup(Graphics::MacWindowManager *wm, Renderer *gfx,
+enum MacTextPopupStyle {
+ kMacTextWindow,
+ kMacInformWindow
+};
+
+static Graphics::ManagedSurface *captureMessageBackground(Renderer *gfx, int width, int height) {
+ if (!gfx)
+ return nullptr;
+
+ Graphics::Surface *screenshot = gfx->getScreenshot();
+ if (!screenshot)
+ return nullptr;
+
+ Graphics::ManagedSurface *saved = new Graphics::ManagedSurface();
+ saved->create(width, height, screenshot->format);
+ saved->blitFrom(*screenshot, Common::Rect(screenshot->w, screenshot->h), Common::Rect(width, height));
+ screenshot->free();
+ delete screenshot;
+ return saved;
+}
+
+static void restoreMessageBackground(Renderer *gfx, Graphics::ManagedSurface *saved) {
+ if (!saved)
+ return;
+ if (gfx) {
+ gfx->drawSurface(&saved->rawSurface(), 0, 0);
+ gfx->copyToScreen();
+ }
+ saved->free();
+ delete saved;
+}
+
+static void animateMacZoom(Renderer *gfx, OSystem *system, const Common::Rect &from, const Common::Rect &to) {
+ if (!gfx || !system || from.isEmpty() || to.isEmpty())
+ return;
+
+ const bool cursorWasVisible = CursorMan.isVisible();
+ CursorMan.showMouse(false);
+ gfx->setXorMode(true);
+ const int steps = 8;
+ for (int i = 0; i <= steps; ++i) {
+ Common::Rect r;
+ r.left = (from.left * (steps - i) + to.left * i) / steps;
+ r.top = (from.top * (steps - i) + to.top * i) / steps;
+ r.right = (from.right * (steps - i) + to.right * i) / steps;
+ r.bottom = (from.bottom * (steps - i) + to.bottom * i) / steps;
+ gfx->drawRect(r, 0xFFFFFFFF);
+ gfx->copyToScreen();
+ system->delayMillis(12);
+ gfx->drawRect(r, 0xFFFFFFFF);
+ }
+ gfx->setXorMode(false);
+ gfx->copyToScreen();
+ CursorMan.showMouse(cursorWasVisible);
+}
+
+static bool drawMacTextPopup(Graphics::MacWindowManager *wm, Renderer *gfx,
int screenWidth, int screenHeight, int centerX, int centerY,
- const Common::Array<Common::String> &lines, Graphics::TextAlign align, bool macColor) {
+ const Common::Array<Common::String> &lines, Graphics::TextAlign align, bool macColor,
+ int visibleLineCount = -1, Common::Rect *popupBounds = nullptr,
+ bool measureOnly = false, MacTextPopupStyle style = kMacTextWindow) {
if (!gfx || lines.empty())
return false;
+ uint visibleLines = lines.size();
+ if (visibleLineCount >= 0 && (uint)visibleLineCount < visibleLines)
+ visibleLines = visibleLineCount;
Graphics::MacFont systemFont(Graphics::kMacFontSystem, 12);
const Graphics::Font *font = (wm && wm->_fontMan) ? wm->_fontMan->getFont(systemFont) : nullptr;
@@ -65,15 +127,17 @@ bool drawMacTextPopup(Graphics::MacWindowManager *wm, Renderer *gfx,
textWidth = MAX<int>(textWidth, font->getStringWidth(lines[i]));
const int fontHeight = MAX<int>(1, font->getFontHeight());
- const int fontLeading = MAX<int>(0, font->getFontLeading());
- const int topPad = 8;
- const int bottomPad = 8;
- const int sidePad = 12;
- const int lineGap = MAX<int>(2, fontLeading);
- const int lineStep = fontHeight + lineGap;
- int popupWidth = CLIP<int>(textWidth + sidePad * 2, 96, MAX<int>(96, screenWidth - 16));
- int popupHeight = topPad + bottomPad + fontHeight +
- MAX<int>(0, (int)lines.size() - 1) * lineStep;
+ const int sidePad = 8;
+ const int lineStep = align == Graphics::kTextAlignCenter ? 18 : 20;
+ const int contentWidth = style == kMacInformWindow ? 370 : textWidth + 16;
+ const int contentHeight = style == kMacInformWindow ? 72 : 4 + ((int)lines.size() + 1) * 18;
+ Common::Rect contentRect;
+ if (style == kMacInformWindow)
+ contentRect = Common::Rect(56, 60, 56 + contentWidth, 60 + contentHeight);
+ else
+ contentRect = Common::Rect(centerX - contentWidth / 2, centerY - contentHeight / 2,
+ centerX - contentWidth / 2 + contentWidth, centerY - contentHeight / 2 + contentHeight);
+
Common::Rect bounds(8, 24, screenWidth - 8, screenHeight - 8);
if (wm) {
Graphics::MacWindowBorder border;
@@ -81,14 +145,12 @@ bool drawMacTextPopup(Graphics::MacWindowManager *wm, Renderer *gfx,
border.setBorderType(Graphics::kWindowWindow);
if (border.hasBorder(Graphics::kWindowBorderActive) && border.hasOffsets()) {
const Graphics::BorderOffsets &offsets = border.getOffset();
- popupWidth = MAX<int>(border.getMinWidth(Graphics::kWindowBorderActive),
- textWidth + sidePad * 2 + offsets.left + offsets.right);
- popupHeight = MAX<int>(border.getMinHeight(Graphics::kWindowBorderActive),
- topPad + bottomPad + fontHeight + MAX<int>(0, (int)lines.size() - 1) * lineStep +
- offsets.top + offsets.bottom);
-
- Common::Rect r(centerX - popupWidth / 2, centerY - popupHeight / 2,
- centerX - popupWidth / 2 + popupWidth, centerY - popupHeight / 2 + popupHeight);
+ const int popupWidth = MAX<int>(border.getMinWidth(Graphics::kWindowBorderActive),
+ contentWidth + offsets.left + offsets.right);
+ const int popupHeight = MAX<int>(border.getMinHeight(Graphics::kWindowBorderActive),
+ contentHeight + offsets.top + offsets.bottom);
+ Common::Rect r(contentRect.left - offsets.left, contentRect.top - offsets.top,
+ contentRect.left - offsets.left + popupWidth, contentRect.top - offsets.top + popupHeight);
if (r.left < bounds.left)
r.translate(bounds.left - r.left, 0);
if (r.right > bounds.right)
@@ -97,6 +159,10 @@ bool drawMacTextPopup(Graphics::MacWindowManager *wm, Renderer *gfx,
r.translate(0, bounds.top - r.top);
if (r.bottom > bounds.bottom)
r.translate(0, bounds.bottom - r.bottom);
+ if (popupBounds)
+ *popupBounds = r;
+ if (measureOnly)
+ return true;
Graphics::ManagedSurface popup;
popup.create(popupWidth, popupHeight, wm->_pixelformat);
@@ -108,9 +174,10 @@ bool drawMacTextPopup(Graphics::MacWindowManager *wm, Renderer *gfx,
border.blitBorderInto(popup, Graphics::kWindowBorderActive);
const int textX = inner.left + sidePad;
- const int textY = inner.top + topPad;
+ const int textY = style == kMacInformWindow ?
+ inner.top + (inner.height() - fontHeight) / 2 : inner.top + MAX<int>(0, 14 - fontHeight);
const int textW = MAX<int>(1, inner.width() - sidePad * 2);
- for (uint i = 0; i < lines.size(); ++i)
+ for (uint i = 0; i < visibleLines; ++i)
font->drawString(&popup, lines[i], textX, textY + (int)i * lineStep, textW, wm->_colorBlack, align);
gfx->drawSurface(&popup.rawSurface(), r.left, r.top);
@@ -120,8 +187,7 @@ bool drawMacTextPopup(Graphics::MacWindowManager *wm, Renderer *gfx,
}
}
- Common::Rect r(centerX - popupWidth / 2, centerY - popupHeight / 2,
- centerX - popupWidth / 2 + popupWidth, centerY - popupHeight / 2 + popupHeight);
+ Common::Rect r = contentRect;
if (r.left < bounds.left)
r.translate(bounds.left - r.left, 0);
if (r.right > bounds.right)
@@ -130,6 +196,10 @@ bool drawMacTextPopup(Graphics::MacWindowManager *wm, Renderer *gfx,
r.translate(0, bounds.top - r.top);
if (r.bottom > bounds.bottom)
r.translate(0, bounds.bottom - r.bottom);
+ if (popupBounds)
+ *popupBounds = r;
+ if (measureOnly)
+ return true;
const uint32 colBlack = macColor ? packRGB(0, 0, 0) : 0;
const uint32 colWhite = macColor ? packRGB(255, 255, 255) : 15;
@@ -148,8 +218,9 @@ bool drawMacTextPopup(Graphics::MacWindowManager *wm, Renderer *gfx,
const int textLeft = r.left + sidePad;
const int textRight = r.right - sidePad;
const int textCenter = (textLeft + textRight) / 2;
- const int startY = r.top + topPad;
- for (uint i = 0; i < lines.size(); ++i) {
+ const int startY = style == kMacInformWindow ?
+ r.top + (r.height() - fontHeight) / 2 : r.top + MAX<int>(0, 14 - fontHeight);
+ for (uint i = 0; i < visibleLines; ++i) {
const int y = startY + (int)i * lineStep;
if (align == Graphics::kTextAlignCenter)
gfx->drawString(font, lines[i], textCenter, y, colBlack, Graphics::kTextAlignCenter);
@@ -1415,6 +1486,22 @@ void ColonyEngine::drawCrosshair() {
}
void ColonyEngine::inform(const char *text, bool hold) {
+ if (isMacRenderMode()) {
+ Common::Array<Common::String> lines;
+ lines.push_back(text);
+ Graphics::ManagedSurface *background = hold ? captureMessageBackground(_gfx, _width, _height) : nullptr;
+ if (drawMacTextPopup(_wm, _gfx, _width, _height, _centerX, _centerY, lines,
+ Graphics::kTextAlignCenter, isMacColorMode(), -1, nullptr, false, kMacInformWindow)) {
+ if (hold) {
+ waitForMessageInput();
+ restoreMessageBackground(_gfx, background);
+ }
+ return;
+ }
+ restoreMessageBackground(_gfx, background);
+ background = nullptr;
+ }
+
const char *msg[3];
msg[0] = text;
msg[1] = hold ? "-Press Any Key to Continue-" : nullptr;
@@ -1436,21 +1523,21 @@ void ColonyEngine::printMessage(const char *text[], bool hold) {
numLines++;
}
+ Graphics::ManagedSurface *background = hold ? captureMessageBackground(_gfx, _width, _height) : nullptr;
if (isMacRenderMode() && drawMacTextPopup(_wm, _gfx,
_width, _height, _centerX, _centerY, lines, Graphics::kTextAlignCenter, isMacColorMode())) {
- if (hold)
- waitForInput();
+ if (hold) {
+ waitForMessageInput();
+ restoreMessageBackground(_gfx, background);
+ }
return;
}
- int pxPerInchX = 72;
- int pxPerInchY = 72;
-
Common::Rect rr;
- rr.top = _centerY - (numLines + 1) * (pxPerInchY / 4);
- rr.bottom = _centerY + (numLines + 1) * (pxPerInchY / 4);
- rr.left = _centerX - width / 2 - (pxPerInchX / 2);
- rr.right = _centerX + width / 2 + (pxPerInchX / 2);
+ rr.top = _centerY - (numLines + 1) * _pQy;
+ rr.bottom = _centerY + (numLines + 1) * _pQy;
+ rr.left = _centerX - width / 2 - 2 * _pQx;
+ rr.right = _centerX + width / 2 + 2 * _pQx;
_gfx->fillDitherRect(_screenR, 0, 15);
makeMessageRect(rr);
@@ -1458,8 +1545,8 @@ void ColonyEngine::printMessage(const char *text[], bool hold) {
int start;
int step;
if (numLines > 1) {
- start = rr.top + (pxPerInchY / 4) * 2;
- step = (rr.height() - (pxPerInchY / 4) * 4) / (numLines - 1);
+ start = rr.top + _pQy * 2;
+ step = (rr.height() - _pQy * 4) / (numLines - 1);
} else {
start = (rr.top + rr.bottom) / 2;
step = 0;
@@ -1471,8 +1558,10 @@ void ColonyEngine::printMessage(const char *text[], bool hold) {
_gfx->copyToScreen();
- if (hold)
- waitForInput();
+ if (hold) {
+ waitForMessageInput();
+ restoreMessageBackground(_gfx, background);
+ }
}
void ColonyEngine::makeMessageRect(Common::Rect &rr) {
@@ -1484,6 +1573,11 @@ void ColonyEngine::makeMessageRect(Common::Rect &rr) {
}
void ColonyEngine::doText(int entry, int center) {
+ Common::Rect messageSource = _messageSourceRect;
+ _messageSourceRect = Common::Rect();
+ if (messageSource.isEmpty())
+ messageSource = _screenR;
+
Common::SeekableReadStream *file = Common::MacResManager::openFileOrDataFork(Common::Path("T.DAT"));
if (!file)
file = Common::MacResManager::openFileOrDataFork(Common::Path("Tdata"));
@@ -1502,7 +1596,7 @@ void ColonyEngine::doText(int entry, int center) {
file->seek(4 + entry * 8);
uint32 offset = file->readUint32BE();
uint16 ch = file->readUint16BE();
- file->readUint16BE(); // lines (unused)
+ uint16 textLineCount = file->readUint16BE();
if (ch == 0) {
delete file;
@@ -1525,14 +1619,96 @@ void ColonyEngine::doText(int entry, int center) {
int start = 0;
for (int i = 0; i < ch; i++) {
if (p[i] == '\r' || p[i] == '\n') {
- p[i] = 0;
- if (p[start])
- lineArray.push_back(&p[start]);
+ lineArray.push_back(Common::String(&p[start], i - start));
+ if (p[i] == '\r' && i + 1 < ch && p[i + 1] == '\n')
+ i++;
start = i + 1;
}
}
- if (start < ch && p[start])
- lineArray.push_back(&p[start]);
+ if (start < ch)
+ lineArray.push_back(Common::String(&p[start], ch - start));
+
+ // Preserve indexed blank lines without adding a trailing empty field.
+ if (textLineCount > 0) {
+ lineArray.resize(textLineCount);
+ }
+
+ Graphics::ManagedSurface *background = captureMessageBackground(_gfx, _width, _height);
+ auto restoreBackground = [&]() {
+ if (background) {
+ restoreMessageBackground(_gfx, background);
+ background = nullptr;
+ } else {
+ _gfx->fillRect(_screenR, 0);
+ _gfx->copyToScreen();
+ }
+ };
+
+ auto waitForLineSound = [&]() {
+ while (_sound->isPlaying() && !shouldQuit()) {
+ Common::Event event;
+ while (_system->getEventManager()->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_QUIT:
+ case Common::EVENT_RETURN_TO_LAUNCHER:
+ quitGame();
+ return false;
+ case Common::EVENT_SCREEN_CHANGED:
+ _gfx->computeScreenViewport();
+ break;
+ default:
+ break;
+ }
+ }
+ _system->updateScreen();
+ _system->delayMillis(10);
+ }
+ return !shouldQuit();
+ };
+
+ if (isMacRenderMode()) {
+ const Graphics::TextAlign align = center == 1 ?
+ Graphics::kTextAlignCenter : Graphics::kTextAlignLeft;
+ Common::Rect popupBounds;
+
+ if (drawMacTextPopup(_wm, _gfx, _width, _height, _centerX, _centerY,
+ lineArray, align, isMacColorMode(), 0, &popupBounds, true)) {
+ animateMacZoom(_gfx, _system, messageSource, popupBounds);
+
+ if (center == 2) {
+ // PlayDiDit waits for the previous cue before each line.
+ drawMacTextPopup(_wm, _gfx, _width, _height, _centerX, _centerY,
+ lineArray, align, isMacColorMode(), 0);
+ for (uint i = 0; i < lineArray.size(); ++i) {
+ if (!waitForLineSound()) {
+ restoreBackground();
+ delete[] page;
+ return;
+ }
+ _sound->play(Sound::kDiDit);
+ drawMacTextPopup(_wm, _gfx, _width, _height, _centerX, _centerY,
+ lineArray, align, isMacColorMode(), i + 1);
+ }
+ } else {
+ drawMacTextPopup(_wm, _gfx, _width, _height, _centerX, _centerY,
+ lineArray, align, isMacColorMode());
+ }
+
+ waitForMessageInput();
+ restoreBackground();
+ if (!shouldQuit())
+ animateMacZoom(_gfx, _system, popupBounds, messageSource);
+ delete[] page;
+ return;
+ }
+ }
+
+ const bool cursorWasVisible = CursorMan.isVisible();
+ CursorMan.showMouse(false);
+ auto finishDosMessage = [&]() {
+ restoreBackground();
+ CursorMan.showMouse(cursorWasVisible);
+ };
Graphics::DosFont font;
int width = 0;
@@ -1542,30 +1718,16 @@ void ColonyEngine::doText(int entry, int center) {
width = w;
}
const char *kpress = "-Press Any Key to Continue-";
- const char *kmore = "-More-";
int kw = font.getStringWidth(kpress);
if (kw > width)
width = kw;
width += 12;
int lineheight = 14;
- int maxlines = (_screenR.height() / lineheight) - 2;
+ int maxlines = MAX(1, (_screenR.height() / lineheight) - 1);
if (maxlines > (int)lineArray.size())
maxlines = lineArray.size();
- if (isMacRenderMode()) {
- Common::Array<Common::String> popupLines;
- for (int i = 0; i < maxlines; ++i)
- popupLines.push_back(lineArray[i]);
- popupLines.push_back((int)lineArray.size() > maxlines ? kmore : kpress);
- if (drawMacTextPopup(_wm, _gfx, _width, _height, _centerX, _centerY, popupLines,
- center == 1 ? Graphics::kTextAlignCenter : Graphics::kTextAlignLeft, isMacColorMode())) {
- waitForInput();
- delete[] page;
- return;
- }
- }
-
// DOS DOTEXT.C: r positioned at (cX ± wdth, cY ± ((maxlines+1)*7 + 4))
// then offset by (+3,+3) for shadow. 3 nested FrameRects shrinking by 1.
const int halfH = ((maxlines + 1) * (lineheight / 2)) + 4;
@@ -1585,13 +1747,20 @@ void ColonyEngine::doText(int entry, int center) {
}
_gfx->fillRect(r, 15);
_gfx->drawRect(r, 0);
+ if (center == 2)
+ _gfx->copyToScreen();
// Draw first page of text
for (int i = 0; i < maxlines; i++) {
_gfx->drawString(&font, lineArray[i], r.left + 3, r.top + 4 + i * lineheight, 0);
if (center == 2) {
+ _gfx->copyToScreen();
_sound->play(Sound::kDit);
- _system->delayMillis(20);
+ if (!waitForLineSound()) {
+ finishDosMessage();
+ delete[] page;
+ return;
+ }
}
}
@@ -1600,28 +1769,44 @@ void ColonyEngine::doText(int entry, int center) {
_gfx->drawString(&font, hasMore ? "-Press Any Key For More...-" : kpress,
(r.left + r.right) / 2, r.top + 6 + maxlines * lineheight, 0, Graphics::kTextAlignCenter);
_gfx->copyToScreen();
- waitForInput();
+ if (!waitForMessageInput()) {
+ finishDosMessage();
+ delete[] page;
+ return;
+ }
// Second page: if text was truncated, show remainder
// DOS DOTEXT.C: starts from maxlines-1 (repeats last line of page 1 for context)
if (hasMore) {
_gfx->fillRect(r, 15);
_gfx->drawRect(r, 0);
+ if (center == 2)
+ _gfx->copyToScreen();
int pageStart = maxlines - 1;
for (int i = pageStart; i < (int)lineArray.size() && (i - pageStart) < maxlines; i++) {
_gfx->drawString(&font, lineArray[i], r.left + 3,
r.top + 6 + (1 + i - pageStart) * lineheight, 0);
if (center == 2) {
+ _gfx->copyToScreen();
_sound->play(Sound::kDit);
- _system->delayMillis(20);
+ if (!waitForLineSound()) {
+ finishDosMessage();
+ delete[] page;
+ return;
+ }
}
}
_gfx->drawString(&font, kpress,
(r.left + r.right) / 2, r.top + 6 + maxlines * lineheight, 0, Graphics::kTextAlignCenter);
_gfx->copyToScreen();
- waitForInput();
+ if (!waitForMessageInput()) {
+ finishDosMessage();
+ delete[] page;
+ return;
+ }
}
+ finishDosMessage();
delete[] page;
}
Commit: cff4e56a26e74746e9fc1e5c0f634bfe31584a95
https://github.com/scummvm/scummvm/commit/cff4e56a26e74746e9fc1e5c0f634bfe31584a95
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-08-22T11:59:36+02:00
Commit Message:
SCUMM: RA: manually initialize _vm->_actionMap
Changed paths:
engines/scumm/insane/rebel1/rebel.cpp
engines/scumm/insane/rebel2/rebel.cpp
diff --git a/engines/scumm/insane/rebel1/rebel.cpp b/engines/scumm/insane/rebel1/rebel.cpp
index be44fd47b57..3f042b3b229 100644
--- a/engines/scumm/insane/rebel1/rebel.cpp
+++ b/engines/scumm/insane/rebel1/rebel.cpp
@@ -462,6 +462,9 @@ void InsaneRebel1::loadLocalizedUiStrings() {
InsaneRebel1::InsaneRebel1(ScummEngine_v7 *scumm) : Insane(), _vm(scumm) {
Insane::_vm = scumm;
+ // Rebel Assault skips ScummEngine::resetScumm(), which normally clears this state.
+ for (int i = 0; i < kScummActionCount; i++)
+ _vm->_actionMap[i] = false;
_screenWidth = 384;
_screenHeight = 242;
diff --git a/engines/scumm/insane/rebel2/rebel.cpp b/engines/scumm/insane/rebel2/rebel.cpp
index f7d3b1cb10f..d449a8fd3da 100644
--- a/engines/scumm/insane/rebel2/rebel.cpp
+++ b/engines/scumm/insane/rebel2/rebel.cpp
@@ -161,6 +161,9 @@ bool InsaneRebel2::isSkippableVideoState() const {
InsaneRebel2::InsaneRebel2(ScummEngine_v7 *scumm) {
_vm = scumm;
+ // Rebel Assault II skips ScummEngine::resetScumm(), which normally clears this state.
+ for (int i = 0; i < kScummActionCount; i++)
+ _vm->_actionMap[i] = false;
_smush_roadrashRip = nullptr;
_smush_roadrsh2Rip = nullptr;
More information about the Scummvm-git-logs
mailing list