[Scummvm-git-logs] scummvm master -> c937dc9f3762f532770b89705db95d6d8efe0a85
neuromancer
noreply at scummvm.org
Mon Aug 17 19:06:03 UTC 2026
This automated email contains information about 6 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
84e34fb7fe COLONY: added missing teleport code and features
ff2bb42f74 COLONY: specific icon for teleport in minimap
ecf28674b9 COLONY: implemented warp doors
00bad4ec1d COLONY: restored additonal door animations
e0138d8b73 COLONY: specific icon for forklift in minimap
c937dc9f37 COLONY: better object discovery in minimap
Commit: 84e34fb7fe98bd0a603ac1f5dcbc6e663f8074cf
https://github.com/scummvm/scummvm/commit/84e34fb7fe98bd0a603ac1f5dcbc6e663f8074cf
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-08-17T21:05:37+02:00
Commit Message:
COLONY: added missing teleport code and features
Changed paths:
engines/colony/animation.cpp
engines/colony/colony.h
engines/colony/interaction.cpp
engines/colony/map.cpp
engines/colony/movement.cpp
diff --git a/engines/colony/animation.cpp b/engines/colony/animation.cpp
index 0e5e3a28625..80e658234e7 100644
--- a/engines/colony/animation.cpp
+++ b/engines/colony/animation.cpp
@@ -197,6 +197,7 @@ const AnimColorEntry kAnimColors[] = {
{ "vanity", kBMC_Vanity, ARRAYSIZE(kBMC_Vanity) },
{ "reactor", kBMC_Reactor, ARRAYSIZE(kBMC_Reactor) },
{ "security", kBMC_Security, ARRAYSIZE(kBMC_Security) },
+ { "tele", kBMC_Teleport, ARRAYSIZE(kBMC_Teleport) },
{ "teleporter", kBMC_Teleport, ARRAYSIZE(kBMC_Teleport) },
{ "teleporter2", kBMC_Teleport, ARRAYSIZE(kBMC_Teleport) },
{ "slides", kBMC_Creatures, ARRAYSIZE(kBMC_Creatures) },
@@ -283,6 +284,7 @@ bool ColonyEngine::loadAnimation(const Common::String &name) {
{ "elev", "elevator" },
{ "slides", "slideshow" },
{ "lift", "lifter" },
+ { "tele", "teleporter" },
{ nullptr, nullptr }
};
@@ -1449,6 +1451,8 @@ void ColonyEngine::handleAnimationClick(int item) {
handleAirlockClick(item);
} else if (_animationName == "elev" || _animationName == "elevator" || _animationName == "elevator2") {
handleElevatorClick(item);
+ } else if (_animationName == "tele" || _animationName == "teleporter2") {
+ handleTeleportClick(item);
} else if (_animationName == "controls") {
handleControlsClick(item);
} else if (_animationName == "forklift") {
@@ -2026,6 +2030,72 @@ void ColonyEngine::handleElevatorClick(int item) {
}
}
+// DoTeleport(): the screen inverts while the transport sound runs.
+void ColonyEngine::flashTeleportBooth() {
+ int ox = _screenR.left + (_screenR.width() - 416) / 2;
+ ox = (ox / 8) * 8;
+ const int oy = _screenR.top + (_screenR.height() - 264) / 2;
+ const Common::Rect booth(ox, oy, ox + 416, oy + 264);
+
+ for (int i = 0; i < 8; i++) {
+ _gfx->fillRect(booth, (i & 1) ? _gfx->black() : _gfx->white());
+ _gfx->copyToScreen();
+ responsiveAnimationDelay(_system, 60);
+ }
+ drawAnimation();
+ _gfx->copyToScreen();
+}
+
+// DoTeleport(): stepping in and closing the door are separate clicks, and the
+// player can leave either stage without being transported.
+void ColonyEngine::handleTeleportClick(int item) {
+ if (getPlatform() == Common::kPlatformMacintosh) {
+ if (_animationName == "teleporter2") {
+ if (item == 2 && !_teleportDone) {
+ _sound->play(Sound::kTeleport);
+ flashTeleportBooth();
+ _teleportDone = true;
+ teleportPlayer();
+ } else if (item == 1 && objectState(2) == 6) {
+ _animationRunning = false;
+ }
+ return;
+ }
+
+ if (item == 1) {
+ if (!loadAnimation("teleporter2")) {
+ _animationRunning = false;
+ return;
+ }
+ setObjectState(2, 6);
+ drawAnimation();
+ _gfx->copyToScreen();
+ }
+ return;
+ }
+
+ // DOS keeps both stages in one file.
+ if (!_teleportInside) {
+ if (item == 3) {
+ _teleportInside = true;
+ setObjectOnOff(3, false);
+ setObjectOnOff(1, false);
+ drawAnimation();
+ _gfx->copyToScreen();
+ }
+ return;
+ }
+
+ if (item == 4 && !_teleportDone) {
+ _sound->play(Sound::kTeleport);
+ flashTeleportBooth();
+ _teleportDone = true;
+ teleportPlayer();
+ } else if (item == 2) {
+ _animationRunning = false;
+ }
+}
+
void ColonyEngine::handleControlsClick(int item) {
switch (item) {
case 4: // Accelerator
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 1bc24cdaac9..c7ab80a88ab 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -484,6 +484,7 @@ public:
void clearPlayerCellMarker();
void setPlayerCellMarker();
bool playerIntersectsObjectFootprint(const Thing &obj, int xloc, int yloc) const;
+ bool playerStartsInsideObject(int rnum) const;
void cCommand(int xnew, int ynew, bool allowInteraction);
bool scrollInfo(const Graphics::Font *macFont = nullptr);
bool checkSkipRequested();
@@ -587,6 +588,7 @@ private:
uint32 _lastAnimUpdate = 0;
uint32 _lastWarningChimeTime = 0;
uint32 _lastCollisionSoundTime = 0;
+ int _bumpedObject = 0;
int _action0 = 0, _action1 = 0;
int _creature = 0;
bool _allGrow = false;
@@ -744,6 +746,9 @@ private:
bool patchMapFrom(const PassPatch &from, uint8 *mapdata);
void exitForklift();
void dropCarriedObject();
+ bool stepOutOfCell();
+ bool exitTeleport();
+ void teleportPlayer();
bool setDoorState(int x, int y, int direction, int state);
int openAdjacentDoors(int x, int y);
int goToDestination(const uint8 *map, Locate *pobject);
@@ -840,6 +845,10 @@ private:
int _airlockY = -1;
int _airlockDirection = -1;
bool _airlockTerminate = false;
+ int _teleportX = -1;
+ int _teleportY = -1;
+ bool _teleportInside = false;
+ bool _teleportDone = false;
void playIntro();
bool makeStars(const Common::Rect &r, int btn);
@@ -879,6 +888,8 @@ private:
void handleDoorClick(int item);
void handleAirlockClick(int item);
void handleElevatorClick(int item);
+ void handleTeleportClick(int item);
+ void flashTeleportBooth();
void handleControlsClick(int item);
void dolSprite(int index);
void moveObject(int index);
diff --git a/engines/colony/interaction.cpp b/engines/colony/interaction.cpp
index 16752c5d8d3..b0d89a3c6da 100644
--- a/engines/colony/interaction.cpp
+++ b/engines/colony/interaction.cpp
@@ -145,40 +145,13 @@ void ColonyEngine::interactWithObject(int objNum) {
}
break;
}
- // fl==0 or fl==2: use the teleporter
- _sound->play(Sound::kTeleport);
- int targetLevelRaw = _mapData[x][y][4][2];
- int targetX = _action0;
- int targetY = _action1;
- // Check if this teleporter was relocated via patch
- PassPatch tp;
- tp.level = _level;
- tp.xindex = x;
- tp.yindex = y;
- uint8 patchData[5];
- if (patchMapTo(tp, patchData)) {
- targetLevelRaw = patchData[2];
- targetX = patchData[3];
- targetY = patchData[4];
- }
- const int targetLevel = (targetLevelRaw == 0) ? _level : targetLevelRaw;
- if (targetLevel >= 100 || targetX <= 0 || targetX >= 31 || targetY <= 0 || targetY >= 31) {
- inform("TELEPORTER INITIALIZATION FAILED", true);
- break;
- }
- if (targetLevel != _level)
- loadMap(targetLevel);
- const int oldX = _me.xindex;
- const int oldY = _me.yindex;
- _me.xindex = targetX;
- _me.yindex = targetY;
- _me.xloc = (targetX << 8) + 128;
- _me.yloc = (targetY << 8) + 128;
- _me.ang = _me.look;
- if (oldX >= 0 && oldX < 32 && oldY >= 0 && oldY < 32 &&
- _robotArray[oldX][oldY] == kMeNum)
- _robotArray[oldX][oldY] = 0;
- setPlayerCellMarker();
+ // fl==0 or fl==2: step into the booth
+ _teleportX = x;
+ _teleportY = y;
+ _teleportInside = false;
+ _teleportDone = false;
+ if (loadAnimation("tele"))
+ playAnimation();
break;
}
case kObjDrawer:
@@ -312,6 +285,52 @@ void ColonyEngine::interactWithObject(int objNum) {
}
}
+// GANIMATE.C TelePorted(): runs when the booth door closes, never on contact.
+void ColonyEngine::teleportPlayer() {
+ const int x = CLIP<int>(_teleportX, 0, 30);
+ const int y = CLIP<int>(_teleportY, 0, 30);
+
+ uint8 mapdata[5] = {};
+ PassPatch from = {};
+ from.level = (uint8)_level;
+ from.xindex = (uint8)x;
+ from.yindex = (uint8)y;
+
+ PassPatch to = {};
+ if (patchMapTo(from, mapdata)) {
+ // Carried here; it kept the destination it was built with.
+ to.level = mapdata[2];
+ to.xindex = mapdata[3];
+ to.yindex = mapdata[4];
+ } else {
+ to.level = _mapData[x][y][4][2];
+ to.xindex = _mapData[x][y][4][3];
+ to.yindex = _mapData[x][y][4][4];
+ }
+
+ // The booth at the far end may itself have been carried elsewhere.
+ if (!patchMapFrom(to, mapdata)) {
+ mapdata[2] = (uint8)to.level;
+ mapdata[3] = (uint8)to.xindex;
+ mapdata[4] = (uint8)to.yindex;
+ }
+
+ goToDestination(mapdata, &_me);
+
+ if (!exitTeleport()) {
+ // Text 65: no way out of the booth
+ doText(65, 0);
+ terminateGame(false);
+ return;
+ }
+
+ setPlayerCellMarker();
+
+ // Wired to nowhere, or to the eighth dimension: the player is scattered.
+ if (mapdata[2] == 100 || !(mapdata[2] || mapdata[3] || mapdata[4]))
+ terminateGame(true);
+}
+
// shoot.c SetPower(): adjust player's 3 power levels and update display.
// p0=weapons delta, p1=life delta, p2=armor delta.
void ColonyEngine::setPower(int p0, int p1, int p2) {
diff --git a/engines/colony/map.cpp b/engines/colony/map.cpp
index 3de42026f6d..ed7263eeffe 100644
--- a/engines/colony/map.cpp
+++ b/engines/colony/map.cpp
@@ -164,6 +164,7 @@ void ColonyEngine::loadMap(int mnum) {
delete[] buffer;
_dynamicObjectBase = kStaticObjectStartIndex;
_robotNum = MAX<int>(_robotNum, (int)_objects.size() + 1);
+ _bumpedObject = 0; // object numbers are per-level
_level = mnum;
_me.type = kMeNum;
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 31f1149f83f..76eea90edcc 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -348,6 +348,15 @@ void ColonyEngine::setPlayerCellMarker() {
_robotArray[_me.xindex][_me.yindex] = kMeNum;
}
+// CHCKWALL.C never blocks a move that stays inside the cell you already occupy.
+// Footprints let the player be in one, so they must be able to walk back out.
+bool ColonyEngine::playerStartsInsideObject(int rnum) const {
+ if (rnum <= 0 || rnum > (int)_objects.size())
+ return false;
+ const Thing &obj = _objects[rnum - 1];
+ return obj.alive && playerIntersectsObjectFootprint(obj, _me.xloc, _me.yloc);
+}
+
bool ColonyEngine::playerIntersectsObjectFootprint(const Thing &obj, int xloc, int yloc) const {
ObjectFootprint fp;
if (!objectFootprintForType(obj.type, fp))
@@ -550,7 +559,7 @@ int ColonyEngine::checkwall(int xnew, int ynew, Locate *pobject) {
if (yind2 == pobject->yindex) {
if (pobject == &_me) {
const int rnum = occupiedObjectAt(xnew, ynew, xind2, yind2, pobject);
- if (rnum)
+ if (rnum && !playerStartsInsideObject(rnum))
return rnum;
}
pobject->dx = xnew - pobject->xloc;
@@ -1301,19 +1310,96 @@ void ColonyEngine::cCommand(int xnew, int ynew, bool allowInteraction) {
const int oldYIndex = _me.yindex;
const bool sameCellAttempt = ((xnew >> 8) == oldXIndex && (ynew >> 8) == oldYIndex);
const int robot = checkwall(xnew, ynew, &_me);
- if (robot > 0 && allowInteraction)
- interactWithObject(robot);
- else if (robot)
- playCollisionSound();
- else if (sameCellAttempt && _me.xindex == oldXIndex && _me.yindex == oldYIndex &&
- (_me.xloc != xnew || _me.yloc != ynew))
- playCollisionSound();
+ if (robot > 0 && allowInteraction) {
+ // CCommand() ran once per key event; movement here is continuous, so latch
+ // the object until contact breaks or its message reopens every frame.
+ if (robot != _bumpedObject) {
+ _bumpedObject = robot;
+ interactWithObject(robot);
+ } else {
+ playCollisionSound();
+ }
+ } else {
+ _bumpedObject = 0;
+ if (robot)
+ playCollisionSound();
+ else if (sameCellAttempt && _me.xindex == oldXIndex && _me.yindex == oldYIndex &&
+ (_me.xloc != xnew || _me.yloc != ynew))
+ playCollisionSound();
+ }
setPlayerCellMarker();
_suppressCollisionSound = false;
}
+// DOS Forward(): inch ahead along _me.ang until the player leaves the cell.
+bool ColonyEngine::stepOutOfCell() {
+ const int xindex = _me.xindex;
+ const int yindex = _me.yindex;
+
+ // clampToWalls() can pin the player short of the boundary, so cap the walk.
+ int guard = 16;
+ _me.type = 2; // temporary small collision type
+ while (_me.xindex == xindex && _me.yindex == yindex) {
+ if (--guard < 0 || checkwall(_me.xloc + _cost[_me.ang], _me.yloc + _sint[_me.ang], &_me)) {
+ _sound->play(Sound::kChime);
+ _me.type = kMeNum;
+ return false;
+ }
+ }
+ _me.type = kMeNum;
+ return true;
+}
+
+// DOS ExitTeleport(): walk clear of the arrival booth, trying each quarter turn,
+// then leave a booth behind. False = all four directions blocked.
+bool ColonyEngine::exitTeleport() {
+ const int xloc = _me.xloc;
+ const int yloc = _me.yloc;
+ const int xindex = _me.xindex;
+ const int yindex = _me.yindex;
+
+ // goToDestination() stamped this cell as the player's; occupiedObjectAt()
+ // would read that marker back as a blocker.
+ clearPlayerCellMarker();
+
+ _me.ang = 48;
+ bool out = false;
+ for (int tries = 0; tries < 4 && !out; tries++) {
+ out = stepOutOfCell();
+ if (!out) {
+ _me.xloc = xloc;
+ _me.yloc = yloc;
+ _me.xindex = xindex;
+ _me.yindex = yindex;
+ _me.ang += 64;
+ }
+ }
+ if (!out)
+ return false;
+
+ if (xindex < 0 || xindex >= 32 || yindex < 0 || yindex >= 32)
+ return true;
+
+ // The original always rebuilds the booth because it reloads the map; the port
+ // keeps its object table, so relink an existing one instead of duplicating it.
+ if (_robotArray[xindex][yindex] != 0)
+ return true;
+
+ for (uint i = 0; i < _objects.size() && i < 255; i++) {
+ const Thing &obj = _objects[i];
+ if (obj.alive && obj.type == kObjTeleport &&
+ obj.where.xindex == xindex && obj.where.yindex == yindex) {
+ _robotArray[xindex][yindex] = (uint8)(i + 1);
+ return true;
+ }
+ }
+
+ createObject(kObjTeleport, (xindex << 8) + 128, (yindex << 8) + 128, 0);
+ return true;
+}
+
// DOS ExitFL(): step back one cell and drop the forklift.
void ColonyEngine::exitForklift() {
if (_fl != 1)
Commit: ff2bb42f744008d43c85b1fcff62860a35fe4b3a
https://github.com/scummvm/scummvm/commit/ff2bb42f744008d43c85b1fcff62860a35fe4b3a
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-08-17T21:05:37+02:00
Commit Message:
COLONY: specific icon for teleport in minimap
Changed paths:
engines/colony/colony.h
engines/colony/ui.cpp
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index c7ab80a88ab..05368a7989d 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -770,6 +770,7 @@ private:
void drawAutomap();
void changeAutomapZoom(bool zoomIn);
void drawAutomapCryoMarker(int x, int y, int halfSize, uint32 color, const Common::Rect &clip);
+ void drawAutomapTeleportMarker(int x, int y, int halfSize, uint32 color, const Common::Rect &clip);
void markVisited();
void automapCellCorner(int dx, int dy, int xloc, int yloc, int lExt, int tsin, int tcos, int ccx, int ccy, int &sx, int &sy);
void automapDrawWall(const Common::Rect &vp, int x1, int y1, int x2, int y2, uint32 color);
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index a43434a82de..df443477442 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -1003,6 +1003,27 @@ void ColonyEngine::drawAutomapCryoMarker(int x, int y, int halfSize, uint32 colo
_gfx->fillEllipse(x, y, cr, cr, color);
}
+// Octagon with a waistband, echoing the booth shape drawn in the 3D view.
+void ColonyEngine::drawAutomapTeleportMarker(int x, int y, int halfSize, uint32 color, const Common::Rect &clip) {
+ if (x < clip.left || x >= clip.right || y < clip.top || y >= clip.bottom)
+ return;
+
+ const int r = MAX(halfSize, 3);
+ const int k = MAX(r * 2 / 5, 1);
+ const int dx[8] = { -k, k, r, r, k, -k, -r, -r };
+ const int dy[8] = { -r, -r, -k, k, r, r, k, -k };
+ for (int i = 0; i < 8; i++) {
+ int x1 = x + dx[i], y1 = y + dy[i];
+ int x2 = x + dx[(i + 1) & 7], y2 = y + dy[(i + 1) & 7];
+ if (clipLineToRect(x1, y1, x2, y2, clip))
+ _gfx->drawLine(x1, y1, x2, y2, color);
+ }
+
+ int bx1 = x - r, by1 = y, bx2 = x + r, by2 = y;
+ if (clipLineToRect(bx1, by1, bx2, by2, clip))
+ _gfx->drawLine(bx1, by1, bx2, by2, color);
+}
+
void ColonyEngine::drawAutomap() {
if (_level < 1 || _level > 8)
return;
@@ -1045,6 +1066,7 @@ void ColonyEngine::drawAutomap() {
const int markerR = scaleR(isMac ? 5 : 3, 2);
const int foodR = scaleR(isMac ? 3 : 2, 1);
const int cryoR = scaleR(isMac ? 7 : 5, 3);
+ const int teleR = scaleR(isMac ? 6 : 4, 3);
for (int dy = -radius; dy <= radius; dy++) {
for (int dx = -radius; dx <= radius; dx++) {
@@ -1072,12 +1094,15 @@ void ColonyEngine::drawAutomap() {
const int mx = (x0 + x2) >> 1;
const int my = (y0 + y2) >> 1;
- // Cryo pods are static, so they map beyond the robot/egg radar range.
+ // Cryo pods and teleporters are static, so they map beyond the
+ // robot/egg radar range.
const bool inRadar = (ABS(dx) <= 6 && ABS(dy) <= 6);
const uint8 rnum = _robotArray[cx][cy];
if (rnum > 0 && rnum != kMeNum && rnum <= _objects.size() && _objects[rnum - 1].alive) {
if (_objects[rnum - 1].type == kObjCryo)
drawAutomapCryoMarker(mx, my, cryoR, lineColor, vp);
+ else if (_objects[rnum - 1].type == kObjTeleport)
+ drawAutomapTeleportMarker(mx, my, teleR, lineColor, vp);
else if (inRadar)
drawMiniMapMarker(mx, my, markerR, lineColor, isMac, &vp);
}
Commit: ecf28674b9e318587931569cf7100eece8e3f13c
https://github.com/scummvm/scummvm/commit/ecf28674b9e318587931569cf7100eece8e3f13c
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-08-17T21:05:37+02:00
Commit Message:
COLONY: implemented warp doors
Changed paths:
engines/colony/movement.cpp
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index 76eea90edcc..d064f95731e 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -746,6 +746,10 @@ int ColonyEngine::goToDestination(const uint8 *map, Locate *pobject) {
if (targetMap == 0 && targetX == 0 && targetY == 0)
return 1;
+ // GOTOMAP.C GoTo(): robots never follow a destination.
+ if (pobject->type == 0)
+ return 0;
+
if (targetMap == 127) {
if (pobject != &_me)
return 0;
@@ -814,8 +818,10 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
switch (map[0]) {
case kWallFeatureDoor:
+ // GOTOMAP.C OpenDoor(): a door carries a destination like any other
+ // feature, so opening one can warp instead of stepping through.
if (map[1] == 0)
- return 1; // already open pass through
+ return goToDestination(map, pobject);
if (pobject != &_me)
return 0; // robots can't open doors
// DOS DoDoor: play door animation, player clicks handle to open
@@ -833,7 +839,7 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
playAnimation();
if (_animationResult) {
setDoorState(fromX, fromY, direction, 0);
- return 1; // pass through
+ return goToDestination(map, pobject);
}
return 0; // player didn't open the door
}
Commit: 00bad4ec1d3bf12fee823e52443aa9c0a066e1a1
https://github.com/scummvm/scummvm/commit/00bad4ec1d3bf12fee823e52443aa9c0a066e1a1
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-08-17T21:05:37+02:00
Commit Message:
COLONY: restored additonal door animations
Changed paths:
engines/colony/colony.h
engines/colony/movement.cpp
engines/colony/sound.cpp
engines/colony/sound.h
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 05368a7989d..0b083a64472 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -493,6 +493,8 @@ public:
void checkCenter();
void fallThroughHole();
void playTunnelEffect(bool falling);
+ int rideTunnel(const uint8 *map, Locate *pobject);
+ void doDnStairs();
void doText(int entry, int center);
void inform(const char *text, bool hold);
diff --git a/engines/colony/movement.cpp b/engines/colony/movement.cpp
index d064f95731e..678e125e5bc 100644
--- a/engines/colony/movement.cpp
+++ b/engines/colony/movement.cpp
@@ -880,19 +880,32 @@ int ColonyEngine::tryPassThroughFeature(int fromX, int fromY, int direction, Loc
}
case kWallFeatureUpStairs:
- case kWallFeatureDnStairs:
- case kWallFeatureTunnel: {
+ case kWallFeatureDnStairs: {
if (pobject != &_me)
- return 0; // robots don't use stairs/tunnels
+ return 0; // robots don't use stairs
+
+ // UpStairs(): the forklift cannot be driven up a staircase.
+ if (map[0] == kWallFeatureUpStairs && _fl)
+ return 0;
- // Play appropriate sound
- if (map[0] == kWallFeatureDnStairs)
- _sound->play(Sound::kClatter);
const int result = goToDestination(map, pobject);
+ if (map[0] == kWallFeatureDnStairs && _fl)
+ doDnStairs();
if (result == 2) {
debugC(1, kColonyDebugMove, "Level change via %s: level=%d pos=(%d,%d)",
- map[0] == kWallFeatureUpStairs ? "upstairs" :
- map[0] == kWallFeatureDnStairs ? "downstairs" : "tunnel",
+ map[0] == kWallFeatureUpStairs ? "upstairs" : "downstairs",
+ _level, pobject->xindex, pobject->yindex);
+ }
+ return result;
+ }
+
+ case kWallFeatureTunnel: {
+ if (pobject != &_me)
+ return 0;
+
+ const int result = rideTunnel(map, pobject);
+ if (result == 2) {
+ debugC(1, kColonyDebugMove, "Level change via tunnel: level=%d pos=(%d,%d)",
_level, pobject->xindex, pobject->yindex);
}
return result;
@@ -1071,11 +1084,12 @@ void ColonyEngine::playTunnelEffect(bool falling) {
int counter = falling ? 2 : kTunnelST[0];
int spd = 180 / counter;
- _sound->play(Sound::kTunnel2);
+ // Tunnel1 runs under the ride, Tunnel2 on arrival.
+ _sound->play(Sound::kTunnel1);
for (int remaining = tunnelFrames; remaining > 0 && !shouldQuit(); ) {
if (!_sound->isPlaying())
- _sound->play(Sound::kTunnel2);
+ _sound->play(Sound::kTunnel1);
if (macColor) {
fillTunnelPattern(_gfx, effectRect, fillFg, fillBg, _macColors[tunnelColor].pattern);
@@ -1187,6 +1201,28 @@ void ColonyEngine::playTunnelEffect(bool falling) {
}
_sound->stop();
+ _sound->play(Sound::kTunnel2);
+}
+
+// TUNNEL.C tunnel(FALSE): ride the subway, then arrive at the far station.
+int ColonyEngine::rideTunnel(const uint8 *map, Locate *pobject) {
+ const bool hasDestination = (map[2] || map[3] || map[4]);
+
+ playTunnelEffect(false);
+ const int result = goToDestination(map, pobject);
+
+ if (!hasDestination) {
+ terminateGame(false);
+ return 0;
+ }
+ return result;
+}
+
+// DoDnStairs(): only the forklift clatters down the steps.
+void ColonyEngine::doDnStairs() {
+ _sound->play(Sound::kClatter);
+ _gfx->fillRect(_screenR, _gfx->black());
+ _gfx->copyToScreen();
}
void ColonyEngine::fallThroughHole() {
diff --git a/engines/colony/sound.cpp b/engines/colony/sound.cpp
index 1c79e25bdb2..ae342bc7530 100644
--- a/engines/colony/sound.cpp
+++ b/engines/colony/sound.cpp
@@ -364,6 +364,7 @@ bool Sound::playMacSound(int soundID, bool loop) {
case kStop: resID = 29382; break; // FULLSTOP
case kTeleport: resID = 9757; break;
case kSlug: resID = 8347; break;
+ case kTunnel1: resID = 16403; break;
case kTunnel2: resID = 17354; break;
case kLift: resID = 28521; break;
case kGlass: resID = 19944; break;
diff --git a/engines/colony/sound.h b/engines/colony/sound.h
index e09be141b08..a83212f3238 100644
--- a/engines/colony/sound.h
+++ b/engines/colony/sound.h
@@ -67,6 +67,7 @@ public:
kStop,
kTeleport,
kSlug,
+ kTunnel1,
kTunnel2,
kLift,
kDrop,
Commit: e0138d8b73a9b21fe23461fc197646607b7f7d3d
https://github.com/scummvm/scummvm/commit/e0138d8b73a9b21fe23461fc197646607b7f7d3d
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-08-17T21:05:37+02:00
Commit Message:
COLONY: specific icon for forklift in minimap
Changed paths:
engines/colony/colony.h
engines/colony/ui.cpp
diff --git a/engines/colony/colony.h b/engines/colony/colony.h
index 0b083a64472..1776207a826 100644
--- a/engines/colony/colony.h
+++ b/engines/colony/colony.h
@@ -773,6 +773,7 @@ private:
void changeAutomapZoom(bool zoomIn);
void drawAutomapCryoMarker(int x, int y, int halfSize, uint32 color, const Common::Rect &clip);
void drawAutomapTeleportMarker(int x, int y, int halfSize, uint32 color, const Common::Rect &clip);
+ void drawAutomapForkliftMarker(int x, int y, int halfSize, uint32 color, const Common::Rect &clip);
void markVisited();
void automapCellCorner(int dx, int dy, int xloc, int yloc, int lExt, int tsin, int tcos, int ccx, int ccy, int &sx, int &sy);
void automapDrawWall(const Common::Rect &vp, int x1, int y1, int x2, int y2, uint32 color);
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index df443477442..5b6f1ed7295 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -1024,6 +1024,26 @@ void ColonyEngine::drawAutomapTeleportMarker(int x, int y, int halfSize, uint32
_gfx->drawLine(bx1, by1, bx2, by2, color);
}
+// Open-topped carriage: the only marker with a gap, so it reads at any zoom.
+void ColonyEngine::drawAutomapForkliftMarker(int x, int y, int halfSize, uint32 color, const Common::Rect &clip) {
+ if (x < clip.left || x >= clip.right || y < clip.top || y >= clip.bottom)
+ return;
+
+ const int r = MAX(halfSize, 3);
+ const int seg[4][4] = {
+ { -r, -r, -r, r },
+ { r, -r, r, r },
+ { -r, r, r, r },
+ { -r, 0, r, 0 }
+ };
+ for (int i = 0; i < 4; i++) {
+ int x1 = x + seg[i][0], y1 = y + seg[i][1];
+ int x2 = x + seg[i][2], y2 = y + seg[i][3];
+ if (clipLineToRect(x1, y1, x2, y2, clip))
+ _gfx->drawLine(x1, y1, x2, y2, color);
+ }
+}
+
void ColonyEngine::drawAutomap() {
if (_level < 1 || _level > 8)
return;
@@ -1067,6 +1087,7 @@ void ColonyEngine::drawAutomap() {
const int foodR = scaleR(isMac ? 3 : 2, 1);
const int cryoR = scaleR(isMac ? 7 : 5, 3);
const int teleR = scaleR(isMac ? 6 : 4, 3);
+ const int forkR = scaleR(isMac ? 6 : 4, 3);
for (int dy = -radius; dy <= radius; dy++) {
for (int dx = -radius; dx <= radius; dx++) {
@@ -1094,8 +1115,8 @@ void ColonyEngine::drawAutomap() {
const int mx = (x0 + x2) >> 1;
const int my = (y0 + y2) >> 1;
- // Cryo pods and teleporters are static, so they map beyond the
- // robot/egg radar range.
+ // Cryo pods, teleporters and the forklift are static, so they map
+ // beyond the robot/egg radar range.
const bool inRadar = (ABS(dx) <= 6 && ABS(dy) <= 6);
const uint8 rnum = _robotArray[cx][cy];
if (rnum > 0 && rnum != kMeNum && rnum <= _objects.size() && _objects[rnum - 1].alive) {
@@ -1103,6 +1124,8 @@ void ColonyEngine::drawAutomap() {
drawAutomapCryoMarker(mx, my, cryoR, lineColor, vp);
else if (_objects[rnum - 1].type == kObjTeleport)
drawAutomapTeleportMarker(mx, my, teleR, lineColor, vp);
+ else if (_objects[rnum - 1].type == kObjForkLift)
+ drawAutomapForkliftMarker(mx, my, forkR, lineColor, vp);
else if (inRadar)
drawMiniMapMarker(mx, my, markerR, lineColor, isMac, &vp);
}
Commit: c937dc9f3762f532770b89705db95d6d8efe0a85
https://github.com/scummvm/scummvm/commit/c937dc9f3762f532770b89705db95d6d8efe0a85
Author: neuromancer (gustavo.grieco at gmail.com)
Date: 2026-08-17T21:05:37+02:00
Commit Message:
COLONY: better object discovery in minimap
Changed paths:
engines/colony/ui.cpp
diff --git a/engines/colony/ui.cpp b/engines/colony/ui.cpp
index 5b6f1ed7295..229580198ee 100644
--- a/engines/colony/ui.cpp
+++ b/engines/colony/ui.cpp
@@ -937,26 +937,14 @@ void ColonyEngine::automapDrawWall(const Common::Rect &vp, int x1, int y1, int x
}
}
+// Own side only: a feature opens from the cell that records it, so the far side
+// is a plain wall with nothing to use.
int ColonyEngine::automapWallFeature(int fx, int fy, int dir) {
- if (fx >= 0 && fx < 31 && fy >= 0 && fy < 31) {
- int feat = _mapData[fx][fy][dir][0];
- if (isPassableFeature(feat))
- return feat;
- }
- int nx = fx, ny = fy, opp = -1;
- switch (dir) {
- case kDirNorth: ny = fy + 1; opp = kDirSouth; break;
- case kDirSouth: ny = fy - 1; opp = kDirNorth; break;
- case kDirEast: nx = fx + 1; opp = kDirWest; break;
- case kDirWest: nx = fx - 1; opp = kDirEast; break;
- default: return 0;
- }
- if (nx >= 0 && nx < 31 && ny >= 0 && ny < 31) {
- int feat = _mapData[nx][ny][opp][0];
- if (isPassableFeature(feat))
- return feat;
- }
- return 0;
+ if (fx < 0 || fx >= 31 || fy < 0 || fy >= 31)
+ return 0;
+
+ const int feat = _mapData[fx][fy][dir][0];
+ return isPassableFeature(feat) ? feat : 0;
}
void ColonyEngine::automapDrawWallWithFeature(const Common::Rect &vp, int wx1, int wy1, int wx2, int wy2, int feat, int lExt, uint32 color) {
@@ -970,6 +958,21 @@ void ColonyEngine::automapDrawWallWithFeature(const Common::Rect &vp, int wx1, i
if (plen > 0) {
const int tx = (ppx * tickLen) / plen;
const int ty = (ppy * tickLen) / plen;
+ if (feat == kWallFeatureUpStairs || feat == kWallFeatureDnStairs) {
+ // A flight of steps, as on a floor plan.
+ const int mx = (wx1 + wx2) / 2;
+ const int my = (wy1 + wy2) / 2;
+ const int hx = (wx2 - wx1) / 4;
+ const int hy = (wy2 - wy1) / 4;
+ for (int i = -2; i <= 2; i++) {
+ if (i == 0)
+ continue;
+ const int ox = mx + tx * i;
+ const int oy = my + ty * i;
+ automapDrawWall(vp, ox - hx, oy - hy, ox + hx, oy + hy, color);
+ }
+ return;
+ }
const int ax = wx1 + (wx2 - wx1) / 4;
const int ay = wy1 + (wy2 - wy1) / 4;
const int bx = wx1 + 3 * (wx2 - wx1) / 4;
More information about the Scummvm-git-logs
mailing list