[Scummvm-git-logs] scummvm master -> faaf89389bd03ff9d8d820fa853ad7cfa35b3576
sluicebox
noreply at scummvm.org
Fri Jan 31 05:18:41 UTC 2025
This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
5c30bfb1b1 AGI: Update misc AGIv1 opcode details
faaf89389b AGI: Implement near.water opcode
Commit: 5c30bfb1b1ac8d2bf036d50cc0d3a2748682557b
https://github.com/scummvm/scummvm/commit/5c30bfb1b1ac8d2bf036d50cc0d3a2748682557b
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2025-01-30T21:14:03-08:00
Commit Message:
AGI: Update misc AGIv1 opcode details
Changed paths:
engines/agi/op_cmd.cpp
engines/agi/opcodes.cpp
diff --git a/engines/agi/op_cmd.cpp b/engines/agi/op_cmd.cpp
index 2bd64b652d7..7319bdb2d16 100644
--- a/engines/agi/op_cmd.cpp
+++ b/engines/agi/op_cmd.cpp
@@ -41,16 +41,10 @@ void cmdIncrement(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
uint16 varNr = parameter[0];
byte varVal = vm->getVar(varNr);
- if (vm->getVersion() < 0x2000) {
- if (varVal < 0xf0) {
- varVal++;
- vm->setVar(varNr, varVal);
- }
- } else {
- if (varVal != 0xff) {
- varVal++;
- vm->setVar(varNr, varVal);
- }
+ byte maxValue = (vm->getVersion() < 0x2000) ? 0xf0 : 0xff;
+ if (varVal < maxValue) {
+ varVal++;
+ vm->setVar(varNr, varVal);
}
}
@@ -577,7 +571,9 @@ void cmdNormalCycle(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
ScreenObjEntry *screenObj = &state->screenObjTable[objectNr];
screenObj->cycle = kCycleNormal;
- screenObj->flags |= fCycling;
+ if (vm->getVersion() >= 0x2000) {
+ screenObj->flags |= fCycling;
+ }
}
void cmdReverseCycle(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
@@ -585,7 +581,9 @@ void cmdReverseCycle(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
ScreenObjEntry *screenObj = &state->screenObjTable[objectNr];
screenObj->cycle = kCycleReverse;
- screenObj->flags |= fCycling;
+ if (vm->getVersion() >= 0x2000) {
+ screenObj->flags |= fCycling;
+ }
}
void cmdSetDir(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
diff --git a/engines/agi/opcodes.cpp b/engines/agi/opcodes.cpp
index 237b03295f8..a6af4a95dd4 100644
--- a/engines/agi/opcodes.cpp
+++ b/engines/agi/opcodes.cpp
@@ -63,7 +63,7 @@ static const AgiOpCodeDefinitionEntry opCodesV1[] = {
{ "subv", "vv", &cmdSubV }, // 08
{ "load.view", "n", &cmdLoadView }, // 09
{ "animate.obj", "n", &cmdAnimateObj }, // 0A
- { "new.room", "n", &cmdNewRoom }, // 0B
+ { "new.room", "n", &cmdNewRoom }, // 0B TODO
{ "draw.pic", "v", &cmdDrawPicV1 }, // 0C
{ "print", "s", &cmdPrint }, // 0D TODO
{ "status", "", &cmdStatus }, // 0E TODO
@@ -133,22 +133,22 @@ static const AgiOpCodeDefinitionEntry opCodesV1[] = {
{ "show.obj", "n", &cmdShowObj }, // 4E # show.obj (KQ2)
{ "load.logics", "n", &cmdLoadLogic }, // 4F # load.global.logics
{ "display", "nnns", &cmdDisplay }, // 50 TODO: 4 vs 3 args
- { "prevent.input???", "", &cmdUnknown }, // 51
- { "...", "", &cmdUnknown }, // 52 # nop
+ { "prevent.input", "", &cmdUnknown }, // 51 TODO: disables input by clearing a global, reset on new.room
+ { "...", "", &cmdUnknown }, // 52 nop, 0 args
{ "text.screen", "n", &cmdUnknown }, // 53
{ "graphics", "", &cmdUnknown }, // 54
{ "stop.motion", "", &cmdStopMotion }, // 55
{ "discard.view", "n", &cmdDiscardView }, // 56
{ "discard.pic", "v", &cmdDiscardPic }, // 57
{ "set.item.view", "nn", &cmdSetItemView }, // 58
- { "...", "", &cmdUnknown }, // 59 # reverse.cycle, unused in KQ2 or BC
+ { "reverse.cycle", "n", &cmdReverseCycle }, // 59
{ "last.cel", "nv", &cmdLastCel }, // 5A
{ "set.cel.v", "nv", &cmdSetCelF }, // 5B
- { "...", "", &cmdUnknown }, // 5C # normal.cycle, unused in KQ2 or BC
- { "load.view", "n", &cmdLoadView }, // 5D
- { "...", "", &cmdUnknown }, // 5E unused in KQ2 or BC
+ { "normal.cycle", "n", &cmdNormalCycle }, // 5C
+ { "load.view", "n", &cmdLoadView }, // 5D duplicate opcode: same table entry as 09
+ { "...", "n", &cmdUnknown }, // 5E nop, 1 arg
{ "...", "", &cmdUnknown }, // 5F BC script 102 when attempting to fill flask
- { "set.bit", "nv", &cmdSetBit }, // 60 BC
+ { "set.bit", "nv", &cmdSetBit }, // 60
{ "clear.bit", "nv", &cmdClearBit }, // 61
{ "set.upper.left", "nn", &cmdSetUpperLeft } // 62 BC Apple II
};
Commit: faaf89389bd03ff9d8d820fa853ad7cfa35b3576
https://github.com/scummvm/scummvm/commit/faaf89389bd03ff9d8d820fa853ad7cfa35b3576
Author: sluicebox (22204938+sluicebox at users.noreply.github.com)
Date: 2025-01-30T21:14:57-08:00
Commit Message:
AGI: Implement near.water opcode
Used by Black Cauldron AGIv1 when filling flask
Changed paths:
engines/agi/agi.h
engines/agi/checks.cpp
engines/agi/op_cmd.cpp
engines/agi/opcodes.cpp
engines/agi/opcodes.h
diff --git a/engines/agi/agi.h b/engines/agi/agi.h
index a46f38d18d0..b003b839966 100644
--- a/engines/agi/agi.h
+++ b/engines/agi/agi.h
@@ -900,6 +900,8 @@ public:
void fixPosition(ScreenObjEntry *screenObj);
void updatePosition();
int getDirection(int16 objX, int16 objY, int16 destX, int16 destY, int16 stepSize);
+ byte egoNearWater(byte limit);
+ int16 nearWater(ScreenObjEntry &screenObj, byte direction, int16 x, int16 y, byte limit);
bool _keyHoldMode;
Common::KeyCode _keyHoldModeLastKey;
diff --git a/engines/agi/checks.cpp b/engines/agi/checks.cpp
index 31a8904becc..639b929728c 100644
--- a/engines/agi/checks.cpp
+++ b/engines/agi/checks.cpp
@@ -351,4 +351,145 @@ void AgiEngine::fixPosition(ScreenObjEntry *screenObj) {
debugC(4, kDebugLevelSprites, "view table entry #%d position adjusted to (%d,%d)", screenObj->objectNr, screenObj->xPos, screenObj->yPos);
}
+/**
+ * Tests if ego is facing nearby water without obstacles. Used by opcode 5F in
+ * Black Cauldron AGIv1 to test if the water flask can be filled. Removed from
+ * the interpreter in AGIv2 and replaced with position tests in game scripts.
+ *
+ * Returns distance to water or 250 if water is not found or is blocked.
+ */
+byte AgiEngine::egoNearWater(byte limit) {
+ ScreenObjEntry &ego = _game.screenObjTable[SCREENOBJECTS_EGO_ENTRY];
+ int16 x1 = ego.xPos;
+ int16 x2 = 0;
+ byte direction;
+
+ switch (ego.currentLoopNr) {
+ case 0: // right
+ direction = 3;
+ x1 += ego.xSize;
+ break;
+ case 1: // left
+ direction = 7;
+ break;
+ case 2: // down
+ direction = 5;
+ x1 += (ego.xSize / 2);
+ break;
+ case 3: // up
+ direction = 1;
+ x2 = x1 + ego.xSize;
+ x1--;
+ break;
+ default: // unhandled in original
+ return 250; // no water
+ }
+
+ int16 distance = -1; // uninitialized in original
+ while (x1 != 0) {
+ distance = nearWater(ego, direction, x1, ego.yPos, limit);
+ if (distance != -1) {
+ break;
+ }
+ x1 = x2;
+ x2 = 0;
+ }
+
+ if (distance == -1) {
+ return 250; // no water
+ }
+
+ // adjust ego positions for collision check
+ int16 prevPrevX = ego.xPos_prev;
+ int16 prevPrevY = ego.yPos_prev;
+ ego.xPos_prev = ego.xPos;
+ ego.yPos_prev = ego.yPos;
+ switch (direction) {
+ case 1: // up
+ ego.yPos -= distance;
+ break;
+ case 3: // right
+ ego.xPos += (x1 - ego.xSize);
+ break;
+ case 5: // down
+ ego.yPos += distance;
+ break;
+ case 7: // left
+ ego.xPos -= distance;
+ break;
+ default:
+ break;
+ }
+
+ if (!checkCollision(&ego)) {
+ if (_game.block.active) {
+ if (!(ego.flags & fIgnoreBlocks)) {
+ if (checkBlock(ego.xPos, ego.yPos)) {
+ distance = 250; // no water
+ }
+ }
+ }
+ } else {
+ distance = 250; // no water
+ }
+
+ // restore ego positions
+ ego.xPos = ego.xPos_prev;
+ ego.yPos = ego.yPos_prev;
+ ego.xPos_prev = prevPrevX;
+ ego.yPos_prev = prevPrevY;
+
+ return distance;
+}
+
+/**
+ * Tests if a screen object is near water in a given direction.
+ *
+ * Returns the distance to water or -1 if water is not found or is blocked.
+ *
+ * Note that the original contains a bug that scans left when facing right.
+ * We do not implement this bug. In Black Cauldron AGIv1, it prevents filling
+ * the flask when facing right unless ego is on or facing away from water.
+ */
+int16 AgiEngine::nearWater(ScreenObjEntry &screenObj, byte direction, int16 x, int16 y, byte limit) {
+ int16 dx = 0;
+ int16 dy = 0;
+ switch (direction) {
+ case 1: dy = -1; break;
+ case 3: dx = 1; break;
+ case 5: dy = 1; break;
+ case 7: dx = -1; break;
+ default: break;
+ }
+
+ for (int16 i = 0; i <= limit; i++) {
+ if (!(0 <= x && x < SCRIPT_WIDTH && 0 <= y && y < SCRIPT_HEIGHT)) {
+ break;
+ }
+
+ byte priority = _gfx->getPriority(x, y);
+ x += dx;
+ y += dy;
+
+ // water found?
+ if (priority == 3) {
+ return i;
+ }
+
+ if (screenObj.priority != 15) {
+ // is blocked by priority 0?
+ if (priority == 0) {
+ break;
+ }
+
+ // is blocked by priority 1?
+ if (priority == 1 && !(screenObj.flags & fIgnoreBlocks)) {
+ break;
+ }
+ }
+ }
+
+ return -1; // water not found
+}
+
} // End of namespace Agi
diff --git a/engines/agi/op_cmd.cpp b/engines/agi/op_cmd.cpp
index 7319bdb2d16..2f918270495 100644
--- a/engines/agi/op_cmd.cpp
+++ b/engines/agi/op_cmd.cpp
@@ -2297,6 +2297,14 @@ void cmdNewRoomVV1(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
vm->setVar(13, 1);
}
+void cmdNearWater(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
+ byte limit = parameter[0];
+ byte varNr = parameter[1];
+
+ byte distance = vm->egoNearWater(limit);
+ vm->setVar(varNr, distance);
+}
+
void cmdSetBit(AgiGame *state, AgiEngine *vm, uint8 *parameter) {
uint16 bit = parameter[0];
uint16 varNr = parameter[1];
diff --git a/engines/agi/opcodes.cpp b/engines/agi/opcodes.cpp
index a6af4a95dd4..676ce925b2d 100644
--- a/engines/agi/opcodes.cpp
+++ b/engines/agi/opcodes.cpp
@@ -147,7 +147,7 @@ static const AgiOpCodeDefinitionEntry opCodesV1[] = {
{ "normal.cycle", "n", &cmdNormalCycle }, // 5C
{ "load.view", "n", &cmdLoadView }, // 5D duplicate opcode: same table entry as 09
{ "...", "n", &cmdUnknown }, // 5E nop, 1 arg
- { "...", "", &cmdUnknown }, // 5F BC script 102 when attempting to fill flask
+ { "near.water", "nv", &cmdNearWater }, // 5F BC script 102 when attempting to fill flask
{ "set.bit", "nv", &cmdSetBit }, // 60
{ "clear.bit", "nv", &cmdClearBit }, // 61
{ "set.upper.left", "nn", &cmdSetUpperLeft } // 62 BC Apple II
diff --git a/engines/agi/opcodes.h b/engines/agi/opcodes.h
index 26bf778eb49..fb0bd9f0e89 100644
--- a/engines/agi/opcodes.h
+++ b/engines/agi/opcodes.h
@@ -225,6 +225,7 @@ void cmdAdjEgoMoveToXY(AgiGame *state, AgiEngine *vm, uint8 *p);
void cmdSetSpeed(AgiGame *state, AgiEngine *vm, uint8 *p);
void cmdSetItemView(AgiGame *state, AgiEngine *vm, uint8 *p);
void cmdCallV1(AgiGame *state, AgiEngine *vm, uint8 *p);
+void cmdNearWater(AgiGame *state, AgiEngine *vm, uint8 *p);
void cmdSetBit(AgiGame *state, AgiEngine *vm, uint8 *p);
void cmdClearBit(AgiGame *state, AgiEngine *vm, uint8 *p);
void cmdUnknown(AgiGame *state, AgiEngine *vm, uint8 *p);
More information about the Scummvm-git-logs
mailing list