[Scummvm-git-logs] scummvm master -> 8a42b59f4c20e235bd39f73da1a7ea6247889328
Scorpeg
noreply at scummvm.org
Thu Jul 16 14:00:06 UTC 2026
This automated email contains information about 8 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
0a85596fed PHOENIXVR: Add Messenger Louvre plugins
8af2829821 PHOENIXVR: Improve Messenger Louvre handling
5bf3a727cf PHOENIXVR: Add Messenger game variants
d0071eae92 PHOENIXVR: Fix Messenger save return handling
be7795688f PHOENIXVR: Keep script sounds playing during movies
b1aa7f5199 PHOENIXVR: Fix Messenger inventory overlay positions
74708adda3 PHOENIXVR: Tidy Messenger Louvre helpers
8a42b59f4c PHOENIXVR: Add debugger variable command
Commit: 0a85596fed1e133c485fecbad624f4cd9c8feb92
https://github.com/scummvm/scummvm/commit/0a85596fed1e133c485fecbad624f4cd9c8feb92
Author: Scorp (scorp at mrs.mn)
Date: 2026-07-16T16:58:37+03:00
Commit Message:
PHOENIXVR: Add Messenger Louvre plugins
Changed paths:
engines/phoenixvr/commands.h
engines/phoenixvr/detection_tables.h
engines/phoenixvr/phoenixvr.cpp
engines/phoenixvr/phoenixvr.h
diff --git a/engines/phoenixvr/commands.h b/engines/phoenixvr/commands.h
index 6600e76004f..a797f766b70 100644
--- a/engines/phoenixvr/commands.h
+++ b/engines/phoenixvr/commands.h
@@ -24,6 +24,7 @@
#include "common/debug.h"
#include "common/stream.h"
+#include "common/system.h"
#include "common/textconsole.h"
#include "phoenixvr/phoenixvr.h"
#include "phoenixvr/script.h"
@@ -31,6 +32,430 @@
namespace PhoenixVR {
namespace {
+constexpr int kMessengerInventorySlots = 12;
+constexpr int kMessengerDisplayedInventorySlots = 8;
+constexpr int kLouvreChestSize = 128;
+
+struct LouvrePluginState {
+ int objectChest[kLouvreChestSize];
+ int monde4;
+ uint32 rolloverBlockedUntil;
+ bool initialized;
+};
+
+LouvrePluginState g_louvrePluginState;
+
+void initLouvrePluginState() {
+ if (g_louvrePluginState.initialized)
+ return;
+
+ for (int i = 0; i < kLouvreChestSize; ++i)
+ g_louvrePluginState.objectChest[i] = 0;
+ g_louvrePluginState.monde4 = 0;
+ g_louvrePluginState.rolloverBlockedUntil = 0;
+ g_louvrePluginState.initialized = true;
+}
+
+Common::String messengerInventorySlotName(int slot) {
+ return Common::String::format("Pos%d", slot);
+}
+
+int messengerInventorySlot(int slot) {
+ if (slot < 1 || slot > kMessengerInventorySlots)
+ return 0;
+
+ return g_engine->getVariable(messengerInventorySlotName(slot));
+}
+
+void setMessengerInventorySlot(int slot, int objectId) {
+ if (slot >= 1 && slot <= kMessengerInventorySlots)
+ g_engine->setVariable(messengerInventorySlotName(slot), objectId);
+}
+
+int messengerSpecialObjectSlot(int objectId) {
+ switch (objectId) {
+ case 100:
+ return 10;
+ case 200:
+ return 11;
+ case 300:
+ return 9;
+ case 400:
+ return 12;
+ default:
+ return 0;
+ }
+}
+
+Common::String louvreObjectImage(int objectId, int suffix) {
+ return Common::String::format("Obj%02d%02d.bmp", objectId / 100, suffix);
+}
+
+void drawLouvreImage(const Common::String &image, int x, int y) {
+ g_engine->drawArchiveImage(image, x, y);
+}
+
+void drawLouvreText(int textId, uint16 color = 1987) {
+ static const Common::Rect kTextRect(62, 402, 354, 466);
+ drawLouvreImage("EffaceText.bmp", kTextRect.left, kTextRect.top);
+ g_engine->clearArchiveText(kTextRect);
+ g_engine->drawArchiveText(textId, kTextRect, 14, true, color);
+}
+
+void clearLouvreText() {
+ static const Common::Rect kTextRect(62, 402, 354, 466);
+ drawLouvreImage("EffaceText.bmp", kTextRect.left, kTextRect.top);
+ g_engine->clearArchiveText(kTextRect);
+}
+
+bool louvreTextBlocked() {
+ return g_louvrePluginState.rolloverBlockedUntil > g_system->getMillis();
+}
+
+bool louvreObjectCanView(int objectId) {
+ static const int kViewableObjects[] = {
+ 3000, 3100, 4200, 4300, 5500, 5900, 7600, 7700, 7800, 8500, 8600, 8800};
+
+ for (int viewableObject : kViewableObjects)
+ if (viewableObject == objectId)
+ return true;
+ return false;
+}
+
+int louvreCombineObjects(int selectedObjectId, int objectId) {
+ struct CombineEntry {
+ int left;
+ int right;
+ int result;
+ };
+ static const CombineEntry kCombineTable[] = {
+ {500, 1200, 1300},
+ {500, 600, 1400},
+ {500, 700, 9600},
+ {9600, 600, 1500},
+ {1400, 700, 1500},
+ {3000, 2900, 3100},
+ {2300, 2200, 3500},
+ {2100, 2200, 3500},
+ {2100, 2300, 3500},
+ {3500, 2100, 3600},
+ {3500, 2200, 3600},
+ {3500, 2300, 3600},
+ {1800, 2600, 3700},
+ {1800, 1700, 3700},
+ {2600, 1700, 3700},
+ {3700, 1800, 3800},
+ {3700, 2600, 3800},
+ {3700, 1700, 3800},
+ {4200, 3400, 4300},
+ {4400, 6100, 6200},
+ {4400, 6000, 6300},
+ {6200, 6000, 6400},
+ {6300, 6100, 6400},
+ {7700, 3400, 7800},
+ {6900, 8200, 9800},
+ {9300, 7000, 9900}};
+
+ for (const CombineEntry &entry : kCombineTable) {
+ if ((entry.left == selectedObjectId && entry.right == objectId) ||
+ (entry.left == objectId && entry.right == selectedObjectId)) {
+ if (entry.result == 3500) {
+ g_engine->setVariable("RedPastryA", selectedObjectId);
+ g_engine->setVariable("RedPastryB", objectId);
+ } else if (entry.result == 3700) {
+ g_engine->setVariable("YellowSeedA", selectedObjectId);
+ g_engine->setVariable("YellowSeedB", objectId);
+ }
+ return entry.result;
+ }
+ }
+
+ return 0;
+}
+
+int louvreSeparateObject(int objectId, int *parts) {
+ struct SeparateEntry {
+ int object;
+ int parts[3];
+ };
+ static const SeparateEntry kSeparateTable[] = {
+ {1300, {500, 1200, 0}},
+ {1400, {500, 600, 0}},
+ {1500, {1400, 700, 0}},
+ {3100, {3000, 2900, 0}},
+ {3600, {3500, 2100, 0}},
+ {3800, {3700, 1800, 0}},
+ {4300, {4200, 3400, 0}},
+ {6200, {4400, 6100, 0}},
+ {6300, {4400, 6000, 0}},
+ {6400, {6200, 6000, 0}},
+ {7800, {7700, 3400, 0}},
+ {9600, {500, 700, 0}},
+ {9800, {6900, 8200, 0}},
+ {9900, {9300, 7000, 0}},
+ };
+
+ for (const SeparateEntry &entry : kSeparateTable) {
+ if (entry.object == objectId) {
+ Common::copy(entry.parts, entry.parts + ARRAYSIZE(entry.parts), parts);
+ return entry.parts[2] != 0 ? 3 : 2;
+ }
+ }
+
+ return 0;
+}
+
+bool louvreObjectCanCombine(int objectId) {
+ static const int kCombinableObjects[] = {
+ 500, 600, 700, 1200, 1300, 1400, 1500, 1700, 1800, 2100, 2200, 2300, 2600, 2900,
+ 3000, 3100, 3400, 3500, 3600, 3700, 3800, 4200, 4300, 4400, 6000, 6100, 6200,
+ 6300, 6400, 6900, 7000, 7700, 7800, 8200, 9300, 9600, 9800, 9900};
+
+ for (int combinableObject : kCombinableObjects)
+ if (combinableObject == objectId)
+ return true;
+ return false;
+}
+
+int louvreObjectActionMask(int objectId) {
+ if (objectId == 0)
+ return 0;
+ if (messengerSpecialObjectSlot(objectId) != 0)
+ return 1;
+
+ int mask = 1;
+ if (louvreObjectCanView(objectId))
+ mask |= 2;
+ if (louvreObjectCanCombine(objectId))
+ mask |= 4;
+ return mask;
+}
+
+int louvreSelectedObjectActionMask() {
+ int selection = g_engine->getVariable("Selection");
+ if (selection > 100)
+ selection -= 100;
+ return louvreObjectActionMask(messengerInventorySlot(selection));
+}
+
+void drawLouvreActionButtons() {
+ static const int kActionButtonX[] = {21, 22, 25, 24};
+ static const int kActionButtonY[] = {145, 191, 270, 318};
+ static const int kActionValues[] = {1, 2, 4, 8};
+
+ int mask = louvreSelectedObjectActionMask();
+ int currentAction = g_engine->getVariable("CurrentAction");
+ for (uint i = 0; i < ARRAYSIZE(kActionValues); ++i) {
+ int button = i + 1;
+ if ((mask & kActionValues[i]) != 0)
+ button += 10;
+ if (currentAction == kActionValues[i])
+ button += 20;
+ drawLouvreImage(Common::String::format("Bout%04d.bmp", button), kActionButtonX[i], kActionButtonY[i]);
+ }
+}
+
+void drawLouvreSelectionMarker(int slot, int imageNumber, bool selected) {
+ static const Common::Point kSlotSelectedPos[kMessengerInventorySlots + 1] = {
+ Common::Point(273, 15),
+ Common::Point(389, 82),
+ Common::Point(482, 83),
+ Common::Point(574, 84),
+ Common::Point(456, 170),
+ Common::Point(556, 178),
+ Common::Point(476, 258),
+ Common::Point(567, 268),
+ Common::Point(552, 352),
+ Common::Point(8, 32),
+ Common::Point(93, 32),
+ Common::Point(182, 27),
+ Common::Point(264, 28)};
+
+ if (slot >= 1 && slot <= kMessengerInventorySlots && imageNumber > 0)
+ drawLouvreImage(Common::String::format(selected ? "Sel%04d.bmp" : "DSel%04d.bmp", imageNumber), kSlotSelectedPos[slot].x, kSlotSelectedPos[slot].y);
+}
+
+void drawLouvreSelectedObject(int objectSlot) {
+ static const int kSelectedObjectX = 103;
+ static const int kSelectedObjectY = 123;
+
+ initLouvrePluginState();
+ if (objectSlot > 100)
+ objectSlot -= 100;
+ if (objectSlot < 1 || objectSlot > kMessengerInventorySlots)
+ return;
+
+ int objectId = messengerInventorySlot(objectSlot);
+ if (objectId == 0)
+ return;
+
+ drawLouvreImage(louvreObjectImage(objectId, 0), kSelectedObjectX, kSelectedObjectY);
+ clearLouvreText();
+ drawLouvreActionButtons();
+ drawLouvreSelectionMarker(objectSlot, objectSlot, true);
+}
+
+void clearLouvreSelection() {
+ int selection = g_engine->getVariable("Selection");
+ int slot = selection > 100 ? selection - 100 : selection;
+
+ g_engine->setVariable("Selection", 0);
+ drawLouvreImage(louvreObjectImage(0, 0), 103, 123);
+ clearLouvreText();
+ drawLouvreActionButtons();
+ drawLouvreSelectionMarker(slot, selection, false);
+}
+
+void setLouvreSelectedSlot(int slot, int flags) {
+ int previousSelection = g_engine->getVariable("Selection");
+ int previousSlot = previousSelection > 100 ? previousSelection - 100 : previousSelection;
+ int targetSlot = slot > 100 ? slot - 100 : slot;
+
+ if (previousSelection == slot && (flags & 2) == 0) {
+ clearLouvreSelection();
+ return;
+ }
+
+ g_engine->setVariable("Selection", slot);
+ drawLouvreSelectionMarker(previousSlot, previousSelection, false);
+ drawLouvreSelectionMarker(targetSlot, slot, true);
+
+ if ((slot < 100 || (flags & 2) != 0) && (flags & 5) == 0)
+ drawLouvreSelectedObject(slot);
+}
+
+Common::String louvreNumberedImageName(const char *prefix, int value) {
+ return Common::String::format("%s%04d", prefix, value);
+}
+
+int louvreChestLastUsedIndex() {
+ for (int i = kLouvreChestSize - 1; i >= 0; --i)
+ if (g_louvrePluginState.objectChest[i] != 0)
+ return i + 1;
+ return 0;
+}
+
+void drawLouvreChestScrollButtons() {
+ static const Common::Point kUpButtonPos(191, 17);
+ static const Common::Point kDownButtonPos(198, 375);
+
+ int offset = g_engine->getVariable("CoffreOffset");
+ int count = louvreChestLastUsedIndex();
+ drawLouvreImage(offset <= 0 ? "CBou0001" : "CBou0011", kUpButtonPos.x, kUpButtonPos.y);
+ drawLouvreImage(offset >= count - 1 || offset >= 124 ? "CBou0002" : "CBou0012", kDownButtonPos.x, kDownButtonPos.y);
+}
+
+void drawLouvreChestSlot(int slot) {
+ static const Common::Point kChestImagePos[] = {
+ Common::Point(80, 82), Common::Point(80, 145), Common::Point(80, 210), Common::Point(80, 273)};
+ static const Common::Rect kChestTextRect[] = {
+ Common::Rect(138, 80, 324, 134),
+ Common::Rect(138, 145, 324, 199),
+ Common::Rect(138, 208, 324, 262),
+ Common::Rect(138, 272, 324, 326)};
+
+ int index = g_engine->getVariable("CoffreOffset") + slot - 1;
+ int objectId = (index >= 0 && index < kLouvreChestSize) ? g_louvrePluginState.objectChest[index] : 0;
+ drawLouvreImage(louvreNumberedImageName("Cof", objectId + 1), kChestImagePos[slot - 1].x, kChestImagePos[slot - 1].y);
+ drawLouvreImage(louvreNumberedImageName("CText", slot), kChestTextRect[slot - 1].left, kChestTextRect[slot - 1].top);
+ g_engine->clearArchiveText(kChestTextRect[slot - 1]);
+ if (objectId != 0)
+ g_engine->drawArchiveText(objectId, kChestTextRect[slot - 1], 8, false, 1987);
+}
+
+void drawLouvreChest() {
+ for (int slot = 1; slot < 5; ++slot)
+ drawLouvreChestSlot(slot);
+}
+
+void drawLouvreChestSelection(int slot) {
+ static const Common::Point kChestSelectionPos[] = {
+ Common::Point(69, 70), Common::Point(70, 134), Common::Point(70, 199), Common::Point(70, 262)};
+
+ int current = g_engine->getVariable("CoffreSelect");
+ if (current != 0 && current <= 4) {
+ drawLouvreImage(louvreNumberedImageName("CDel", current), kChestSelectionPos[current - 1].x, kChestSelectionPos[current - 1].y);
+ drawLouvreImage(louvreNumberedImageName("CDel", current + 1), kChestSelectionPos[current].x, kChestSelectionPos[current].y);
+ }
+
+ if (slot == 0 || current == slot) {
+ g_engine->setVariable("CoffreSelect", 0);
+ return;
+ }
+
+ drawLouvreImage(louvreNumberedImageName("Cel", slot), kChestSelectionPos[slot - 1].x, kChestSelectionPos[slot - 1].y);
+ drawLouvreImage(louvreNumberedImageName("Cel", slot + 1), kChestSelectionPos[slot].x, kChestSelectionPos[slot].y);
+ g_engine->setVariable("CoffreSelect", slot);
+}
+
+void drawLouvreInventorySlotObject(int slot, int objectId) {
+ static const Common::Point kInventoryObjectPos[] = {
+ Common::Point(381, 28),
+ Common::Point(477, 28),
+ Common::Point(565, 28),
+ Common::Point(452, 116),
+ Common::Point(549, 123),
+ Common::Point(468, 204),
+ Common::Point(558, 212),
+ Common::Point(548, 300),
+ Common::Point(18, 13),
+ Common::Point(104, 20),
+ Common::Point(193, 12),
+ Common::Point(273, 15)};
+
+ if (slot >= 1 && slot <= kMessengerInventorySlots && (slot <= kMessengerDisplayedInventorySlots || objectId != 0))
+ drawLouvreImage(louvreNumberedImageName("Obj", objectId + 1), kInventoryObjectPos[slot - 1].x, kInventoryObjectPos[slot - 1].y);
+}
+
+void drawLouvreInventoryObjects(bool includeSpecialSlots) {
+ for (int slot = 1; slot < (includeSpecialSlots ? 13 : 9); ++slot)
+ drawLouvreInventorySlotObject(slot, messengerInventorySlot(slot));
+}
+
+void updateLouvreChestScroll(int direction) {
+ int offset = g_engine->getVariable("CoffreOffset");
+ int count = louvreChestLastUsedIndex();
+ if (direction == 1)
+ offset -= 3;
+ else if (direction == 2)
+ offset += 3;
+
+ offset = CLIP(offset, 0, MIN(124, MAX(0, count - 1)));
+ if (offset != g_engine->getVariable("CoffreOffset")) {
+ g_engine->setVariable("CoffreOffset", offset);
+ drawLouvreChest();
+ }
+ drawLouvreChestScrollButtons();
+}
+
+void exchangeLouvreChestObject(int chestSlot, int inventorySlot) {
+ if (chestSlot == 0)
+ chestSlot = g_engine->getVariable("CoffreSelect");
+ if (inventorySlot == 0)
+ inventorySlot = g_engine->getVariable("Selection");
+ if (inventorySlot > 100)
+ inventorySlot -= 100;
+
+ if (chestSlot < 1 || chestSlot > 4 || inventorySlot < 1 || inventorySlot > kMessengerInventorySlots)
+ return;
+
+ int chestIndex = g_engine->getVariable("CoffreOffset") + chestSlot - 1;
+ if (chestIndex < 0 || chestIndex >= kLouvreChestSize)
+ return;
+ if (louvreChestLastUsedIndex() == 0)
+ g_engine->setVariable("CoffreOffset", 0);
+
+ int inventoryObject = messengerInventorySlot(inventorySlot);
+ int chestObject = g_louvrePluginState.objectChest[chestIndex];
+ g_louvrePluginState.objectChest[chestIndex] = inventoryObject;
+ setMessengerInventorySlot(inventorySlot, chestObject);
+ drawLouvreInventorySlotObject(inventorySlot, chestObject);
+ drawLouvreChestSlot(chestSlot);
+ drawLouvreChestSelection(0);
+ g_engine->setVariable("Selection", 0);
+ updateLouvreChestScroll(0);
+}
+
struct MultiCD_Use_Install_Path : public Script::Command {
Common::String path;
@@ -242,9 +667,9 @@ struct GetMonde4 : public Script::Command {
GetMonde4(const Common::Array<Common::String> &args) : var(args[0]), negativeVar(args[1]) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("GetMonde4 %s %s", var.c_str(), negativeVar.c_str());
- g_engine->setVariable(var, 0);
- g_engine->setVariable(negativeVar, 1);
+ initLouvrePluginState();
+ g_engine->setVariable(var, g_louvrePluginState.monde4);
+ g_engine->setVariable(negativeVar, g_louvrePluginState.monde4 == 0);
}
};
@@ -253,7 +678,8 @@ struct SetMonde4 : public Script::Command {
SetMonde4(const Common::Array<Common::String> &args) : value(atoi(args[0].c_str())) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("SetMonde4 %d", value);
+ initLouvrePluginState();
+ g_louvrePluginState.monde4 = value;
}
};
@@ -264,7 +690,31 @@ struct AddObject : public Script::Command {
AddObject(const Common::Array<Common::String> &args) : object(atoi(args[0].c_str())), var(args[1]), negativeVar(args[2]) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("AddObject %d %s %s", object, var.c_str(), negativeVar.c_str());
+ initLouvrePluginState();
+
+ g_engine->setVariable(var, 1);
+ g_engine->setVariable(negativeVar, 0);
+ g_engine->setVariable("AddObjectOk", 0);
+
+ int slot = messengerSpecialObjectSlot(object);
+ if (slot != 0) {
+ setMessengerInventorySlot(slot, object);
+ g_engine->setVariable("Selection", slot);
+ return;
+ }
+
+ for (slot = 1; slot < 9; ++slot) {
+ if (messengerInventorySlot(slot) == 0) {
+ setMessengerInventorySlot(slot, object);
+ g_engine->setVariable("Selection", slot);
+ return;
+ }
+ }
+
+ g_engine->setVariable("Selection", 0);
+ g_engine->setVariable("AddObjectOk", 1);
+ g_engine->setVariable(var, 0);
+ g_engine->setVariable(negativeVar, 1);
}
};
@@ -273,7 +723,13 @@ struct AddCoffreObject : public Script::Command {
AddCoffreObject(const Common::Array<Common::String> &args) : object(atoi(args[0].c_str())) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("AddCoffreObject %d", object);
+ initLouvrePluginState();
+ for (int i = 0; i < kLouvreChestSize; ++i) {
+ if (g_louvrePluginState.objectChest[i] == 0) {
+ g_louvrePluginState.objectChest[i] = object;
+ return;
+ }
+ }
}
};
@@ -284,16 +740,29 @@ struct IsPresent : public Script::Command {
IsPresent(const Common::Array<Common::String> &args) : object(atoi(args[0].c_str())), var(args[1]), negativeVar(args[2]) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("IsPresent %d %s %s", object, var.c_str(), negativeVar.c_str());
+ initLouvrePluginState();
+ bool found = false;
+ for (int i = 1; i <= kMessengerInventorySlots; ++i) {
+ if (messengerInventorySlot(i) == object) {
+ found = true;
+ break;
+ }
+ }
+ g_engine->setVariable(var, found);
+ g_engine->setVariable(negativeVar, !found);
}
};
struct RemoveObject : public Script::Command {
- int object;
+ Common::String object;
- RemoveObject(const Common::Array<Common::String> &args) : object(atoi(args[0].c_str())) {}
+ RemoveObject(const Common::Array<Common::String> &args) : object(args[0]) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("RemoveObject %d", object);
+ initLouvrePluginState();
+ int objectId = Script::Command::valueOf(object);
+ for (int i = 1; i <= kMessengerInventorySlots; ++i)
+ if (messengerInventorySlot(i) == objectId)
+ setMessengerInventorySlot(i, 0);
}
};
@@ -347,7 +816,32 @@ struct Select : public Script::Command {
Select(const Common::Array<Common::String> &args) : value(atoi(args[0].c_str())), arg0(args[1]), arg1(args[2]) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("Select %d %s %s", value, arg0.c_str(), arg1.c_str());
+ initLouvrePluginState();
+ g_engine->setVariable(arg1, 0);
+
+ int objectId = messengerInventorySlot(value);
+ if (objectId == 0)
+ return;
+
+ if (g_engine->getVariable("CurrentAction") == 4) {
+ g_engine->setVariable(arg0, 1);
+ g_engine->setVariable(arg1, 0);
+
+ int selectedSlot = g_engine->getVariable("Selection") - 100;
+ int selectedObjectId = messengerInventorySlot(selectedSlot);
+ int combinedObjectId = louvreCombineObjects(selectedObjectId, objectId);
+ if (combinedObjectId != 0) {
+ g_engine->setVariable(arg1, 1);
+ g_engine->setVariable("CurrentAction", 0);
+ setMessengerInventorySlot(selectedSlot, combinedObjectId);
+ setMessengerInventorySlot(value, 0);
+ drawLouvreActionButtons();
+ setLouvreSelectedSlot(selectedSlot, 0);
+ }
+ return;
+ }
+
+ setLouvreSelectedSlot(value, 0);
}
};
@@ -358,35 +852,98 @@ struct DoAction : public Script::Command {
DoAction(const Common::Array<Common::String> &args) : value(atoi(args[0].c_str())), arg(args[1]) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("DoAction %d %s", value, arg.c_str());
+ initLouvrePluginState();
+ g_engine->setVariable(arg, 0);
+
+ int selection = g_engine->getVariable("Selection");
+ if (selection > 100)
+ selection -= 100;
+ if (selection == 0)
+ return;
+
+ int objectId = messengerInventorySlot(selection);
+ int actionMask = louvreSelectedObjectActionMask();
+
+ int currentAction = g_engine->getVariable("CurrentAction");
+ if (!((currentAction == value || currentAction == 0) && (actionMask & value) != 0))
+ return;
+
+ if (currentAction != 0) {
+ setLouvreSelectedSlot(selection, 4);
+ g_engine->setVariable("CurrentAction", 0);
+ drawLouvreActionButtons();
+ return;
+ }
+
+ g_engine->setVariable(arg, 1);
+ switch (value) {
+ case 1:
+ g_engine->setVariable("Inventaire", objectId);
+ break;
+ case 2:
+ if (louvreObjectCanView(objectId))
+ g_engine->setVariable(arg, objectId);
+ break;
+ case 4:
+ g_engine->setVariable("CurrentAction", value);
+ setLouvreSelectedSlot(selection + 100, 0);
+ break;
+ case 8:
+ g_engine->setVariable("CurrentAction", value);
+ drawLouvreActionButtons();
+ break;
+ default:
+ break;
+ }
}
};
struct IsHere : public Script::Command {
IsHere(const Common::Array<Common::String> &args) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("IsHere");
+ int comefrom = g_engine->getVariable("comefrom");
+ if (comefrom > 10000000)
+ comefrom -= 10000000;
+
+ Common::String value = Common::String::format("%d", comefrom);
+ if (value.size() >= 3) {
+ Common::String name = Common::String::format("P0%cS%c%c", value[0], value[1], value[2]);
+ g_engine->playAnimation(name, "y", 20, 20);
+ }
}
};
struct InitCoffre : public Script::Command {
InitCoffre(const Common::Array<Common::String> &args) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("InitCoffre");
+ initLouvrePluginState();
+ int compacted[kLouvreChestSize];
+ Common::fill(compacted, compacted + kLouvreChestSize, 0);
+
+ int dst = 0;
+ for (int i = 0; i < kLouvreChestSize; ++i)
+ if (g_louvrePluginState.objectChest[i] != 0)
+ compacted[dst++] = g_louvrePluginState.objectChest[i];
+
+ Common::copy(compacted, compacted + kLouvreChestSize, g_louvrePluginState.objectChest);
}
};
struct LoadCoffre : public Script::Command {
LoadCoffre(const Common::Array<Common::String> &args) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("LoadCoffre");
+ initLouvrePluginState();
+ for (int i = 0; i < kLouvreChestSize; ++i)
+ g_louvrePluginState.objectChest[i] = g_engine->getVariable(Common::String::format("coffre%d", i + 1));
}
};
struct SaveCoffre : public Script::Command {
SaveCoffre(const Common::Array<Common::String> &args) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("SaveCoffre");
+ initLouvrePluginState();
+ for (int i = 0; i < kLouvreChestSize; ++i)
+ g_engine->setVariable(Common::String::format("coffre%d", i + 1), g_louvrePluginState.objectChest[i]);
}
};
@@ -459,14 +1016,20 @@ struct TestCible : public Script::Command {
struct AfficheCoffre : public Script::Command {
AfficheCoffre(const Common::Array<Common::String> &args) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("AfficheCoffre");
+ initLouvrePluginState();
+ drawLouvreChest();
}
};
struct AfficheSelection : public Script::Command {
AfficheSelection(const Common::Array<Common::String> &args) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("AfficheSelection");
+ initLouvrePluginState();
+ int selection = g_engine->getVariable("Selection");
+ if (selection > 100)
+ selection -= 100;
+ if (selection != 0)
+ drawLouvreSelectedObject(selection);
}
};
@@ -474,7 +1037,8 @@ struct AffichePorteF : public Script::Command {
int value;
AffichePorteF(const Common::Array<Common::String> &args) : value(atoi(args[0].c_str())) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("AffichePorteF %d", value);
+ initLouvrePluginState();
+ drawLouvreInventoryObjects(true);
}
};
@@ -483,7 +1047,14 @@ struct SelectPorteF : public Script::Command {
Common::String var;
SelectPorteF(const Common::Array<Common::String> &args) : value(atoi(args[0].c_str())), var(args[1]) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("SelectPorteF %d %s", value, var.c_str());
+ initLouvrePluginState();
+ g_engine->setVariable(var, 0);
+ if (g_engine->getVariable("CoffreSelect") != 0) {
+ exchangeLouvreChestObject(0, value);
+ } else if (messengerInventorySlot(value) != 0) {
+ setLouvreSelectedSlot(value, 1);
+ }
+ g_engine->setVariable(var, g_engine->getVariable("Selection") != 0);
}
};
@@ -492,7 +1063,16 @@ struct SelectCoffre : public Script::Command {
Common::String var;
SelectCoffre(const Common::Array<Common::String> &args) : value(atoi(args[0].c_str())), var(args[1]) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("SelectCoffre %d %s", value, var.c_str());
+ initLouvrePluginState();
+ g_engine->setVariable(var, 0);
+ if (g_engine->getVariable("Selection") != 0) {
+ exchangeLouvreChestObject(value, 0);
+ } else {
+ int index = g_engine->getVariable("CoffreOffset") + value - 1;
+ if (index >= 0 && index < kLouvreChestSize && g_louvrePluginState.objectChest[index] != 0)
+ drawLouvreChestSelection(value);
+ }
+ g_engine->setVariable(var, g_engine->getVariable("CoffreSelect") != 0);
}
};
@@ -565,7 +1145,8 @@ struct Set_Global_Pan : public Script::Command {
Set_Global_Pan(const Common::Array<Common::String> &args) : arg(args[0]) {}
void exec(Script::ExecutionContext &ctx) const override {
auto value = valueOf(arg);
- warning("set_global_pan %s -> %d", arg.c_str(), value);
+ debug("set_global_pan %s -> %d", arg.c_str(), value);
+ g_engine->setGlobalPan(value);
}
};
@@ -750,16 +1331,70 @@ struct LoadSave_Set_Context_Label : public Script::Command {
};
struct Discocier : public Script::Command {
- Discocier(const Common::Array<Common::String> &args) {}
+ Common::String var;
+ Discocier(const Common::Array<Common::String> &args) : var(args[0]) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("Discocier");
+ initLouvrePluginState();
+ g_engine->setVariable(var, 0);
+
+ int selection = g_engine->getVariable("Selection");
+ int slot = selection > 100 ? selection - 100 : selection;
+ int objectId = messengerInventorySlot(slot);
+
+ int parts[3] = {0, 0, 0};
+ int partCount = 0;
+ if (objectId == 3500) {
+ partCount = 2;
+ parts[0] = g_engine->getVariable("RedPastryA");
+ parts[1] = g_engine->getVariable("RedPastryB");
+ } else if (objectId == 3700) {
+ partCount = 2;
+ parts[0] = g_engine->getVariable("YellowSeedA");
+ parts[1] = g_engine->getVariable("YellowSeedB");
+ } else {
+ partCount = louvreSeparateObject(objectId, parts);
+ }
+
+ g_engine->setVariable("CurrentAction", 0);
+ int emptySlots = 0;
+ for (int i = 1; i < 9; ++i)
+ if (messengerInventorySlot(i) == 0)
+ ++emptySlots;
+
+ if (partCount > emptySlots + 1) {
+ drawLouvreActionButtons();
+ drawLouvreText(28, 63687);
+ g_louvrePluginState.rolloverBlockedUntil = g_system->getMillis() + 5000;
+ return;
+ }
+
+ if (partCount == 0)
+ return;
+
+ setMessengerInventorySlot(slot, 0);
+ for (int i = partCount - 1; i >= 0; --i) {
+ for (int dstSlot = 1; dstSlot < 9; ++dstSlot) {
+ if (messengerInventorySlot(dstSlot) == 0) {
+ setMessengerInventorySlot(dstSlot, parts[i]);
+ break;
+ }
+ }
+ }
+
+ setLouvreSelectedSlot(g_engine->getVariable("Selection"), 2);
+ drawLouvreInventoryObjects(true);
+ g_engine->setVariable(var, 1);
}
};
struct Reset : public Script::Command {
Reset(const Common::Array<Common::String> &args) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("Reset");
+ initLouvrePluginState();
+ for (int i = 0; i < kLouvreChestSize; ++i)
+ g_louvrePluginState.objectChest[i] = 0;
+ for (int i = 1; i <= kMessengerInventorySlots; ++i)
+ setMessengerInventorySlot(i, 0);
}
};
@@ -773,14 +1408,28 @@ struct Restart : public Script::Command {
struct MemoryRelease : public Script::Command {
MemoryRelease(const Common::Array<Common::String> &args) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("MemoryRelease");
+ debug("MemoryRelease");
}
};
struct DrawTextSelection : public Script::Command {
DrawTextSelection(const Common::Array<Common::String> &args) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("DrawTextSelection");
+ initLouvrePluginState();
+ if (louvreTextBlocked())
+ return;
+
+ static const Common::Rect kTextRect(62, 402, 354, 466);
+ clearLouvreText();
+
+ int selection = g_engine->getVariable("Selection");
+ if (selection > 100)
+ selection -= 100;
+ if (selection != 0) {
+ int objectId = messengerInventorySlot(selection);
+ if (objectId != 0)
+ g_engine->drawArchiveText(objectId, kTextRect, 14, true, 1987);
+ }
}
};
@@ -789,7 +1438,15 @@ struct CarteDestination : public Script::Command {
Common::String varY;
CarteDestination(const Common::Array<Common::String> &args) : varX(args[0]), varY(args[1]) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("CarteDestination %s %s", varX.c_str(), varY.c_str());
+ int comefrom = g_engine->getVariable("comefrom");
+ if (comefrom > 10000000)
+ comefrom -= 10000000;
+
+ Common::String value = Common::String::format("%d", comefrom);
+ if (value.size() >= 3) {
+ g_engine->setVariable(varX, value[0] - '0');
+ g_engine->setVariable(varY, atoi(value.c_str() + 1));
+ }
}
};
@@ -797,7 +1454,8 @@ struct Scroll : public Script::Command {
int value;
Scroll(const Common::Array<Common::String> &args) : value(atoi(args[0].c_str())) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("Scroll");
+ initLouvrePluginState();
+ updateLouvreChestScroll(value);
}
};
@@ -831,7 +1489,16 @@ struct PorteFRollover : public Script::Command {
PorteFRollover(const Common::Array<Common::String> &args) : arg(atoi(args[0].c_str())) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("PorteFRollover %d", arg);
+ initLouvrePluginState();
+ if (louvreTextBlocked())
+ return;
+
+ if (arg == 28) {
+ drawLouvreText(arg, 63687);
+ g_louvrePluginState.rolloverBlockedUntil = g_system->getMillis() + 5000;
+ } else {
+ drawLouvreText(arg);
+ }
}
};
@@ -849,6 +1516,24 @@ struct LoadVariable : public Script::Command {
}
};
+struct Init2 : public Script::Command {
+ Common::String var;
+ Init2(const Common::Array<Common::String> &args) : var(args[0]) {}
+ void exec(Script::ExecutionContext &ctx) const override {
+ g_engine->setVariable(var, 0);
+ }
+};
+
+struct Init : public Script::Command {
+ int value;
+ Common::String var;
+ Init(const Common::Array<Common::String> &args) : value(atoi(args[0].c_str())), var(args[1]) {}
+ void exec(Script::ExecutionContext &ctx) const override {
+ initLouvrePluginState();
+ g_engine->setVariable(var, value == 2 ? 1 : 0);
+ }
+};
+
struct End : public Script::Command {
End(const Common::Array<Common::String> &args) {}
void exec(Script::ExecutionContext &ctx) const override {
@@ -875,6 +1560,8 @@ struct End : public Script::Command {
E(ExeDemo) \
E(GetMonde4) \
E(SetMonde4) \
+ E(Init) \
+ E(Init2) \
E(IsPresent) \
E(KillTimer) \
E(InitCoffre) \
@@ -1170,7 +1857,8 @@ struct PlayRandomSound : public PlaySound {
PlayRandomSound(Common::String s, int v, int u, int l) : PlaySound(Common::move(s), v, l), unk(u) {}
void exec(Script::ExecutionContext &ctx) const override {
- warning("PlayRandomSound %s %d %d %d", sound.c_str(), volume, unk, loops);
+ debug("PlayRandomSound %s %d %d %d", sound.c_str(), volume, unk, loops);
+ PlaySound::exec(ctx);
}
};
diff --git a/engines/phoenixvr/detection_tables.h b/engines/phoenixvr/detection_tables.h
index e08aba75f11..b34396d3ef3 100644
--- a/engines/phoenixvr/detection_tables.h
+++ b/engines/phoenixvr/detection_tables.h
@@ -215,7 +215,7 @@ const ADGameDescription gameDescriptions[] = {
"textes.txt", "23f577d1201bc3024ca49cb11f9f7347", 5261),
Common::EN_ANY,
Common::kPlatformWindows,
- ADGF_DROPPLATFORM | ADGF_UNSUPPORTED,
+ ADGF_DROPPLATFORM,
GUIO1(GUIO_NONE)
},
diff --git a/engines/phoenixvr/phoenixvr.cpp b/engines/phoenixvr/phoenixvr.cpp
index d2d631c9de3..f79657d2d47 100644
--- a/engines/phoenixvr/phoenixvr.cpp
+++ b/engines/phoenixvr/phoenixvr.cpp
@@ -719,6 +719,7 @@ bool PhoenixVREngine::goToWarp(const Common::String &warp, bool savePrev) {
_nextWarp = _script->getWarp(warp);
_hoverIndex = -1;
+ _messengerInventoryHover = -1;
if (savePrev) {
assert(_warpIdx >= 0);
_prevWarp = _warpIdx;
@@ -804,6 +805,11 @@ int PhoenixVREngine::getVariable(const Common::String &name) const {
return _variables.getValOrDefault(name, 0);
}
+static int8 panToBalance(int pan) {
+ pan = CLIP(pan, 0, 255);
+ return static_cast<int8>((pan * 254 + 127) / 255 - 127);
+}
+
void PhoenixVREngine::playSound(const Common::String &sound, Audio::Mixer::SoundType type, uint8 volume, int loops, bool spatial, float angle) {
const bool music = type == Audio::Mixer::kMusicSoundType;
debug("play sound %s %d %d, music: %d, 3d: %d, angle: %g", sound.c_str(), volume, loops, music, spatial, angle);
@@ -825,7 +831,7 @@ void PhoenixVREngine::playSound(const Common::String &sound, Audio::Mixer::Sound
_currentMusicVolume = volume;
}
- _mixer->playStream(type, &h, Audio::makeWAVStream(stream.release(), DisposeAfterUse::YES), -1, volume);
+ _mixer->playStream(type, &h, Audio::makeWAVStream(stream.release(), DisposeAfterUse::YES), -1, volume, spatial ? 0 : panToBalance(_globalPan));
if (loops < 0 || music)
_mixer->loopChannel(h);
Common::SharedPtr<Video::Subtitles> subtitles;
@@ -997,6 +1003,50 @@ void PhoenixVREngine::resetLockKey() {
_prevWarp = -1; // original game does only this o_O
}
+void PhoenixVREngine::drawArchiveImage(const Common::String &image, int x, int y) {
+ const Graphics::Surface *surface = _arn ? _arn->get(image) : nullptr;
+ if (!surface && !image.contains('.'))
+ surface = _arn ? _arn->get(image + ".bmp") : nullptr;
+ if (!surface) {
+ warning("can't find archive image %s", image.c_str());
+ return;
+ }
+
+ _screen->simpleBlitFrom(*surface, Common::Point(x, y));
+
+ Common::Point pos(x, y);
+ for (ArchiveImage &archiveImage : _archiveImages) {
+ if (archiveImage.pos == pos) {
+ archiveImage.image = image;
+ return;
+ }
+ }
+
+ _archiveImages.push_back({image, pos});
+}
+
+void PhoenixVREngine::drawArchiveText(int textId, const Common::Rect &dstRect, int size, bool bold, uint16 color) {
+ for (TextState &archiveText : _archiveTexts) {
+ if (archiveText.rect == dstRect) {
+ archiveText = {textId, dstRect, size, bold, color};
+ paintText(archiveText);
+ return;
+ }
+ }
+
+ _archiveTexts.push_back({textId, dstRect, size, bold, color});
+ paintText(_archiveTexts.back());
+}
+
+void PhoenixVREngine::clearArchiveText(const Common::Rect &dstRect) {
+ for (uint i = 0; i < _archiveTexts.size(); ++i) {
+ if (_archiveTexts[i].rect == dstRect) {
+ _archiveTexts.remove_at(i);
+ return;
+ }
+ }
+}
+
void PhoenixVREngine::showImageOverlay(const Common::String &image, int x, int y) {
debug("AfficheImage %s %d %d", image.c_str(), x, y);
_imageOverlay.reset();
@@ -1197,32 +1247,57 @@ void PhoenixVREngine::renderTimer() {
void PhoenixVREngine::renderVR(float dt) {
_vr.render(_screen, _angleX.angle(), _angleY.angle(), _fov, dt, _showRegions ? _regSet.get() : nullptr);
- if (_textId >= 0 && _textes.contains(_textId)) {
- const Graphics::Font *font = getFont(_textSize, _textBold);
- if (font) {
- Common::Array<Common::U32String> lines;
- font->wordWrapText(_textes.getVal(_textId), _textRect.width(), lines, Graphics::kWordWrapDefault);
-
- const int fontH = font->getFontHeight();
- int textW = 0;
- for (const Common::U32String &line : lines)
- textW = MAX(textW, font->getStringWidth(line));
-
- byte r, g, b;
- _rgb565.colorToRGB(_textColor, r, g, b);
- const uint32 textColor = _screen->format.RGBToColor(r, g, b);
- const int16 textX = _textRect.left + (_textRect.width() - textW) / 2;
- const int16 textY = _textRect.top + (_textRect.height() - fontH * lines.size()) / 2;
- for (uint i = 0; i < lines.size(); ++i) {
- const int16 lineX = textX + (textW - font->getStringWidth(lines[i])) / 2;
- font->drawString(_screen, lines[i], lineX, textY + i * fontH, _textRect.right - lineX, textColor, Graphics::kTextAlignLeft);
- }
- }
- }
+ paintText(_rolloverText);
+ renderArchiveImages();
+ renderArchiveTexts();
renderImageOverlay();
renderTimer();
}
+void PhoenixVREngine::renderArchiveImages() {
+ for (const ArchiveImage &archiveImage : _archiveImages) {
+ const Graphics::Surface *surface = _arn ? _arn->get(archiveImage.image) : nullptr;
+ if (!surface && !archiveImage.image.contains('.'))
+ surface = _arn ? _arn->get(archiveImage.image + ".bmp") : nullptr;
+ if (!surface)
+ continue;
+
+ _screen->simpleBlitFrom(*surface, archiveImage.pos);
+ }
+}
+
+void PhoenixVREngine::renderArchiveTexts() {
+ for (const TextState &archiveText : _archiveTexts)
+ paintText(archiveText);
+}
+
+void PhoenixVREngine::paintText(const TextState &textState) {
+ auto *font = getFont(textState.size, textState.bold);
+ if (!font || !_textes.contains(textState.textId))
+ return;
+
+ Common::Array<Common::U32String> lines;
+ font->wordWrapText(_textes.getVal(textState.textId), textState.rect.width(), lines, Graphics::kWordWrapDefault);
+ if (lines.empty())
+ return;
+
+ int fontH = font->getFontHeight();
+ int textW = 0;
+ for (uint i = 0; i < lines.size(); ++i) {
+ textW = MAX(textW, font->getStringWidth(lines[i]));
+ }
+
+ byte r, g, b;
+ _rgb565.colorToRGB(textState.color, r, g, b);
+ uint32 textColor = _screen->format.RGBToColor(r, g, b);
+ int16 dstX = textState.rect.left + (textState.rect.width() - textW) / 2;
+ int16 dstY = textState.rect.top + (textState.rect.height() - fontH * lines.size()) / 2;
+ for (uint i = 0; i < lines.size(); ++i) {
+ int x = (textW - font->getStringWidth(lines[i])) / 2;
+ font->drawString(_screen, lines[i], dstX + x, dstY + i * fontH, textW, textColor, Graphics::kTextAlignLeft);
+ }
+}
+
void PhoenixVREngine::renderImageOverlay() {
if (_imageOverlay)
_screen->simpleBlitFrom(*_imageOverlay, _imageOverlayPos);
@@ -1314,17 +1389,17 @@ void PhoenixVREngine::rollover(int textId, RolloverType type) {
if (!_textes.contains(textId)) {
debug("text cleared");
- _textId = -1;
+ _rolloverText = TextState();
return;
}
auto &text = _textes.getVal(textId);
debug("rollover %s, %s font size: %d, bold: %d, color: %02x", dstRect.toString().c_str(), text.encode(Common::kUtf8).c_str(), size, bold, color);
- _textId = textId;
- _textRect = dstRect;
- _textSize = size;
- _textBold = bold;
- _textColor = color;
+ _rolloverText = TextState(textId, dstRect, size, bold, color);
+}
+
+void PhoenixVREngine::clearText() {
+ _rolloverText = TextState();
}
void PhoenixVREngine::tick(float dt) {
@@ -1370,7 +1445,9 @@ void PhoenixVREngine::tick(float dt) {
goToWarp(_script->getInitScript()->vrFile);
}
if (_nextWarp >= 0) {
- _textId = -1;
+ _rolloverText = TextState();
+ _archiveImages.clear();
+ _archiveTexts.clear();
_warpIdx = _nextWarp;
_warp = _script->getWarp(_nextWarp);
debug("warp %d -> %s %s", _nextWarp, _warp->vrFile.c_str(), _warp->testFile.c_str());
@@ -1423,6 +1500,7 @@ void PhoenixVREngine::tick(float dt) {
Graphics::ManagedSurface *cursor = nullptr;
auto &cursors = _cursors[_warpIdx];
bool anyMatched = false;
+ int messengerInventoryHover = -1;
for (int i = 0, n = cursors.size(); i != n; ++i) {
auto *region = getRegion(i);
if (!region)
@@ -1430,6 +1508,8 @@ void PhoenixVREngine::tick(float dt) {
if (_vr.isVR() ? region->contains3D(currentVRPos()) : region->contains2D(_mousePos.x, _mousePos.y)) {
anyMatched = true;
+ if (gameIdMatches("messenger") && _warp && _warp->vrFile.equalsIgnoreCase("portef.vr") && i >= 0 && i < 12)
+ messengerInventoryHover = i;
auto test = _warp->getTest(i);
if (test && test->hover == 1 && _hoverIndex < 0) {
@@ -1452,6 +1532,29 @@ void PhoenixVREngine::tick(float dt) {
}
}
+ if (messengerInventoryHover != _messengerInventoryHover) {
+ _messengerInventoryHover = messengerInventoryHover;
+ if (gameIdMatches("messenger") && _warp && _warp->vrFile.equalsIgnoreCase("portef.vr")) {
+ const Common::Rect textRect(62, 402, 354, 466);
+ drawArchiveImage("EffaceText.bmp", textRect.left, textRect.top);
+ clearArchiveText(textRect);
+
+ int textId = 0;
+ if (_messengerInventoryHover >= 0)
+ textId = getVariable(Common::String::format("Pos%d", _messengerInventoryHover + 1));
+ else {
+ int selection = getVariable("Selection");
+ if (selection > 100)
+ selection -= 100;
+ if (selection >= 1 && selection <= 12)
+ textId = getVariable(Common::String::format("Pos%d", selection));
+ }
+
+ if (textId != 0)
+ drawArchiveText(textId, textRect, 14, true, 1987);
+ }
+ }
+
if (!cursor)
cursor = loadCursor(anyMatched ? _defaultCursor[1] : _defaultCursor[0]);
if (cursor) {
@@ -2086,6 +2189,16 @@ void PhoenixVREngine::setGlobalVolume(int volume) {
syncSoundSettings();
}
+void PhoenixVREngine::setGlobalPan(int pan) {
+ _globalPan = CLIP(pan, 0, 255);
+ int8 balance = panToBalance(_globalPan);
+
+ for (auto &kv : _sounds) {
+ if (!kv._value.spatial && _mixer->isSoundHandleActive(kv._value.handle))
+ _mixer->setChannelBalance(kv._value.handle, balance);
+ }
+}
+
void PhoenixVREngine::syncSoundSettings() {
int musicVolume = ConfMan.getInt("music_volume");
int sfxVolume = ConfMan.getInt("sfx_volume");
diff --git a/engines/phoenixvr/phoenixvr.h b/engines/phoenixvr/phoenixvr.h
index 32ed68d1fde..7ef22297fe4 100644
--- a/engines/phoenixvr/phoenixvr.h
+++ b/engines/phoenixvr/phoenixvr.h
@@ -204,11 +204,16 @@ public:
void loadVariables();
void rollover(int textId, RolloverType type);
+ void clearText();
void showWaves();
void restart();
bool setNextLevel();
void setGlobalVolume(int vol);
+ void setGlobalPan(int pan);
+ void drawArchiveImage(const Common::String &image, int x, int y);
+ void drawArchiveText(int textId, const Common::Rect &dstRect, int size, bool bold, uint16 color);
+ void clearArchiveText(const Common::Rect &dstRect);
void showImageOverlay(const Common::String &image, int x, int y);
void stopImageOverlay();
void updateStage();
@@ -217,6 +222,22 @@ public:
void testCible(const Common::String &insideVar, const Common::String &outsideVar);
private:
+ struct ArchiveImage {
+ Common::String image;
+ Common::Point pos;
+ };
+
+ struct TextState {
+ TextState() : textId(-1), size(0), bold(false), color(0) {}
+ TextState(int textId_, const Common::Rect &rect_, int size_, bool bold_, uint16 color_) : textId(textId_), rect(rect_), size(size_), bold(bold_), color(color_) {}
+
+ int textId;
+ Common::Rect rect;
+ int size;
+ bool bold;
+ uint16 color;
+ };
+
static Common::String removeDrive(const Common::String &path);
Common::SeekableReadStream *open(const Common::String &name, Common::String *origName = nullptr);
Common::SeekableReadStream *tryOpen(const Common::Path &name, Common::String *origName);
@@ -230,6 +251,9 @@ private:
void tickTimer(float dt);
void loadNextScript();
void renderVR(float dt);
+ void renderArchiveImages();
+ void renderArchiveTexts();
+ void paintText(const TextState &textState);
void renderImageOverlay();
void renderTimer();
void renderFade(int color);
@@ -253,6 +277,7 @@ private:
int _nextWarp = -1;
int _prevWarp = -1;
int _hoverIndex = -1;
+ int _messengerInventoryHover = -1;
int _nextTest = -1;
struct KeyCodeHash : public Common::UnaryFunction<Common::KeyCode, uint> {
@@ -282,6 +307,7 @@ private:
Common::String _defaultCursor[2];
Common::String _currentMusic;
int _currentMusicVolume = 0;
+ int _globalPan = 128;
VR _vr;
float _fov;
@@ -307,13 +333,11 @@ private:
Common::ScopedPtr<Graphics::Font> _regularFonts[kFontSizeCount];
Common::ScopedPtr<Graphics::Font> _boldFonts[kFontSizeCount];
- int _textId = -1;
- Common::Rect _textRect;
- int _textSize = 0;
- bool _textBold = false;
- uint16 _textColor = 0;
+ TextState _rolloverText;
Common::ScopedPtr<Graphics::ManagedSurface> _imageOverlay;
Common::Point _imageOverlayPos;
+ Common::Array<ArchiveImage> _archiveImages;
+ Common::Array<TextState> _archiveTexts;
bool _cibleActive = false;
uint32 _cibleStartMillis = 0;
int _ciblePeriodSeconds = 0;
Commit: 8af2829821e2fe245588842043d008d296bb7d49
https://github.com/scummvm/scummvm/commit/8af2829821e2fe245588842043d008d296bb7d49
Author: Scorp (scorp at mrs.mn)
Date: 2026-07-16T16:58:37+03:00
Commit Message:
PHOENIXVR: Improve Messenger Louvre handling
Changed paths:
engines/phoenixvr/commands.h
engines/phoenixvr/phoenixvr.cpp
engines/phoenixvr/script.cpp
engines/phoenixvr/script.h
diff --git a/engines/phoenixvr/commands.h b/engines/phoenixvr/commands.h
index a797f766b70..ffb4526f480 100644
--- a/engines/phoenixvr/commands.h
+++ b/engines/phoenixvr/commands.h
@@ -217,6 +217,14 @@ bool louvreObjectCanCombine(int objectId) {
return false;
}
+bool louvreObjectCanSeparate(int objectId) {
+ if (objectId == 3500 || objectId == 3700)
+ return true;
+
+ int parts[3] = {0, 0, 0};
+ return louvreSeparateObject(objectId, parts) != 0;
+}
+
int louvreObjectActionMask(int objectId) {
if (objectId == 0)
return 0;
@@ -228,6 +236,8 @@ int louvreObjectActionMask(int objectId) {
mask |= 2;
if (louvreObjectCanCombine(objectId))
mask |= 4;
+ if (louvreObjectCanSeparate(objectId))
+ mask |= 8;
return mask;
}
@@ -295,14 +305,16 @@ void drawLouvreSelectedObject(int objectSlot) {
drawLouvreSelectionMarker(objectSlot, objectSlot, true);
}
-void clearLouvreSelection() {
+void clearLouvreSelection(bool clearObjectPreview = true) {
int selection = g_engine->getVariable("Selection");
int slot = selection > 100 ? selection - 100 : selection;
g_engine->setVariable("Selection", 0);
- drawLouvreImage(louvreObjectImage(0, 0), 103, 123);
- clearLouvreText();
- drawLouvreActionButtons();
+ if (clearObjectPreview) {
+ drawLouvreImage(louvreObjectImage(0, 0), 103, 123);
+ clearLouvreText();
+ drawLouvreActionButtons();
+ }
drawLouvreSelectionMarker(slot, selection, false);
}
@@ -312,7 +324,7 @@ void setLouvreSelectedSlot(int slot, int flags) {
int targetSlot = slot > 100 ? slot - 100 : slot;
if (previousSelection == slot && (flags & 2) == 0) {
- clearLouvreSelection();
+ clearLouvreSelection((flags & 1) == 0);
return;
}
@@ -360,7 +372,7 @@ void drawLouvreChestSlot(int slot) {
drawLouvreImage(louvreNumberedImageName("CText", slot), kChestTextRect[slot - 1].left, kChestTextRect[slot - 1].top);
g_engine->clearArchiveText(kChestTextRect[slot - 1]);
if (objectId != 0)
- g_engine->drawArchiveText(objectId, kChestTextRect[slot - 1], 8, false, 1987);
+ g_engine->drawArchiveText(objectId, kChestTextRect[slot - 1], 12, false, 1987);
}
void drawLouvreChest() {
@@ -370,7 +382,7 @@ void drawLouvreChest() {
void drawLouvreChestSelection(int slot) {
static const Common::Point kChestSelectionPos[] = {
- Common::Point(69, 70), Common::Point(70, 134), Common::Point(70, 199), Common::Point(70, 262)};
+ Common::Point(69, 70), Common::Point(70, 134), Common::Point(70, 199), Common::Point(70, 262), Common::Point(70, 326)};
int current = g_engine->getVariable("CoffreSelect");
if (current != 0 && current <= 4) {
@@ -422,6 +434,7 @@ void updateLouvreChestScroll(int direction) {
offset = CLIP(offset, 0, MIN(124, MAX(0, count - 1)));
if (offset != g_engine->getVariable("CoffreOffset")) {
+ g_engine->setVariable("CoffreSelect", 0);
g_engine->setVariable("CoffreOffset", offset);
drawLouvreChest();
}
@@ -777,7 +790,7 @@ struct Cmp : public Script::Command {
if (args.size() == 5) {
arg1 = args[4];
} else {
- uint opLength = (op.size() > 1 && op[1] == '=') ? 2 : 1;
+ uint opLength = (op.size() > 1 && (op[1] == '=' || op[1] == '>')) ? 2 : 1;
arg1 = op.substr(opLength);
op = op.substr(0, opLength);
}
@@ -790,7 +803,7 @@ struct Cmp : public Script::Command {
auto value1 = valueOf(arg1);
if (op == "==") {
r = value0 == value1;
- } else if (op == "!=") {
+ } else if (op == "!=" || op == "<>") {
r = value0 != value1;
} else if (op == "<") {
r = value0 < value1;
@@ -835,6 +848,8 @@ struct Select : public Script::Command {
g_engine->setVariable("CurrentAction", 0);
setMessengerInventorySlot(selectedSlot, combinedObjectId);
setMessengerInventorySlot(value, 0);
+ drawLouvreInventorySlotObject(selectedSlot, combinedObjectId);
+ drawLouvreInventorySlotObject(value, 0);
drawLouvreActionButtons();
setLouvreSelectedSlot(selectedSlot, 0);
}
@@ -1038,7 +1053,7 @@ struct AffichePorteF : public Script::Command {
AffichePorteF(const Common::Array<Common::String> &args) : value(atoi(args[0].c_str())) {}
void exec(Script::ExecutionContext &ctx) const override {
initLouvrePluginState();
- drawLouvreInventoryObjects(true);
+ drawLouvreInventoryObjects(value != 0);
}
};
@@ -1443,10 +1458,10 @@ struct CarteDestination : public Script::Command {
comefrom -= 10000000;
Common::String value = Common::String::format("%d", comefrom);
- if (value.size() >= 3) {
+ if (value.size() >= 1)
g_engine->setVariable(varX, value[0] - '0');
- g_engine->setVariable(varY, atoi(value.c_str() + 1));
- }
+ if (value.size() >= 3)
+ g_engine->setVariable(varY, atoi(value.substr(1, 2).c_str()));
}
};
@@ -1684,12 +1699,17 @@ struct GoSub : public Script::Command {
GoSub(const Common::String &l) : label(l) {}
void exec(Script::ExecutionContext &ctx) const override {
debug("gosub %s", label.c_str());
- assert(ctx.scope);
- auto *labelPtr = ctx.scope->findLabel(label);
- assert(labelPtr);
+ auto *scope = ctx.rootScope ? ctx.rootScope : ctx.scope;
+ assert(scope);
+ auto *labelPtr = scope->findLabel(label);
+ if (!labelPtr) {
+ warning("gosub: label %s not found", label.c_str());
+ return;
+ }
Script::ExecutionContext sub = {};
sub.subroutine = true;
- ctx.scope->exec(sub, labelPtr->offset);
+ sub.rootScope = scope;
+ scope->exec(sub, labelPtr->offset);
}
};
@@ -1706,12 +1726,10 @@ struct Return : public Script::Command {
Return() {}
void exec(Script::ExecutionContext &ctx) const override {
ctx.running = false;
- if (ctx.subroutine) {
+ if (ctx.subroutine)
debug("return to caller");
- } else {
- debug("return to previous warp");
- g_engine->returnToWarp();
- }
+ else
+ debug("return from script");
}
};
diff --git a/engines/phoenixvr/phoenixvr.cpp b/engines/phoenixvr/phoenixvr.cpp
index f79657d2d47..0af66729aca 100644
--- a/engines/phoenixvr/phoenixvr.cpp
+++ b/engines/phoenixvr/phoenixvr.cpp
@@ -1220,7 +1220,7 @@ void PhoenixVREngine::tickTimer(float dt) {
}
void PhoenixVREngine::renderTimer() {
- if (_timerFlags == 0 || !_showTimer || !_arn)
+ if ((_timerFlags & 4) == 0 || !_showTimer || !_arn)
return;
auto timerBg = _arn->get("cadre.bmp");
auto timerFg = _arn->get("cadreB.bmp");
@@ -1230,7 +1230,7 @@ void PhoenixVREngine::renderTimer() {
// Necronomicon has timer in scripts, but does not contain bitmaps for timers.
Common::Rect bgRect{320, 16, 632, 44};
Common::Rect fgRect{333, 23, 619, 38};
- if (gameIdMatches("dracula2")) {
+ if (gameIdMatches("dracula2") || gameIdMatches("messenger")) {
bgRect = Common::Rect(165, 15, 474, 48);
fgRect = Common::Rect(177, 15, 461, 48);
}
@@ -1293,8 +1293,8 @@ void PhoenixVREngine::paintText(const TextState &textState) {
int16 dstX = textState.rect.left + (textState.rect.width() - textW) / 2;
int16 dstY = textState.rect.top + (textState.rect.height() - fontH * lines.size()) / 2;
for (uint i = 0; i < lines.size(); ++i) {
- int x = (textW - font->getStringWidth(lines[i])) / 2;
- font->drawString(_screen, lines[i], dstX + x, dstY + i * fontH, textW, textColor, Graphics::kTextAlignLeft);
+ int16 lineX = dstX + (textW - font->getStringWidth(lines[i])) / 2;
+ font->drawString(_screen, lines[i], lineX, dstY + i * fontH, textState.rect.right - lineX, textColor, Graphics::kTextAlignLeft);
}
}
@@ -1444,7 +1444,7 @@ void PhoenixVREngine::tick(float dt) {
loadNextScript();
goToWarp(_script->getInitScript()->vrFile);
}
- if (_nextWarp >= 0) {
+ while (_nextWarp >= 0) {
_rolloverText = TextState();
_archiveImages.clear();
_archiveTexts.clear();
@@ -1486,7 +1486,6 @@ void PhoenixVREngine::tick(float dt) {
else
warning("no default script!");
_restarted = false;
- return;
}
if (_nextTest >= 0) {
@@ -1501,7 +1500,8 @@ void PhoenixVREngine::tick(float dt) {
auto &cursors = _cursors[_warpIdx];
bool anyMatched = false;
int messengerInventoryHover = -1;
- for (int i = 0, n = cursors.size(); i != n; ++i) {
+ int regionCount = _regSet ? _regSet->size() : 0;
+ for (int i = 0, n = MAX<int>(regionCount, cursors.size()); i != n; ++i) {
auto *region = getRegion(i);
if (!region)
continue;
@@ -1518,15 +1518,17 @@ void PhoenixVREngine::tick(float dt) {
executeTest(i);
}
- auto &name = cursors[i];
- if (!cursor) {
+ if (!cursor && i < static_cast<int>(cursors.size())) {
+ auto &name = cursors[i];
cursor = loadCursor(name);
}
} else if (i == _hoverIndex) {
debug("leaving hover region");
- auto leave = _warp->getTest(i + 1);
+ auto leave = _warp->getTest(i - 1);
+ if (!leave || leave->hover != 2)
+ leave = _warp->getTest(i + 1);
if (leave && leave->hover == 2) {
- executeTest(i + 1);
+ executeTest(leave->idx);
}
_hoverIndex = -1;
}
@@ -1715,8 +1717,12 @@ Common::Error PhoenixVREngine::run() {
}
} break;
case Common::EVENT_RBUTTONUP: {
- if (!_hasFocus || _prevWarp != -1)
+ if (!_hasFocus)
break;
+ if (_prevWarp != -1) {
+ returnToWarp();
+ break;
+ }
debug("right click");
auto &rclick = _lockKey[12];
if (!rclick.empty())
@@ -1740,7 +1746,8 @@ Common::Error PhoenixVREngine::run() {
if (_warpIdx < 0)
break;
auto &cursors = _cursors[_warpIdx];
- for (uint i = 0, n = cursors.size(); i != n; ++i) {
+ int regionCount = _regSet ? _regSet->size() : 0;
+ for (uint i = 0, n = MAX<int>(regionCount, cursors.size()); i != n; ++i) {
auto *region = getRegion(i);
if (!region)
continue;
diff --git a/engines/phoenixvr/script.cpp b/engines/phoenixvr/script.cpp
index 55fd722dd9c..f8e8c37b756 100644
--- a/engines/phoenixvr/script.cpp
+++ b/engines/phoenixvr/script.cpp
@@ -273,8 +273,11 @@ public:
return CommandPtr(new SetCursor(Common::move(var), Common::move(warp), idx));
}
int value = 0;
- if (maybe('='))
- value = nextInt();
+ if (maybe('=')) {
+ skip();
+ if (!atEnd() && peek() != '!')
+ value = nextInt();
+ }
return CommandPtr(new SetVar(Common::move(var), value));
} else if (keyword("not")) {
auto var = nextWord();
@@ -298,6 +301,8 @@ void Script::Scope::exec(ExecutionContext &ctx) const {
void Script::Scope::exec(ExecutionContext &ctx, uint offset) const {
auto oldScope = ctx.scope;
+ if (!ctx.rootScope)
+ ctx.rootScope = this;
ctx.scope = this;
for (uint i = offset, n = commands.size(); i < n; ++i) {
if (!ctx.running)
@@ -448,7 +453,15 @@ Script::~Script() {
int Script::getWarp(const Common::String &name) const {
auto it = _warpsIndex.find(name);
- return it != _warpsIndex.end() ? it->_value : -1;
+ if (it != _warpsIndex.end())
+ return it->_value;
+
+ for (uint i = 0; i < _warps.size(); ++i) {
+ if (_warps[i]->vrFile.equalsIgnoreCase(name))
+ return i;
+ }
+
+ return -1;
}
Script::ConstWarpPtr Script::getWarp(int idx) const {
diff --git a/engines/phoenixvr/script.h b/engines/phoenixvr/script.h
index dc7e93ef0dd..37032b20e31 100644
--- a/engines/phoenixvr/script.h
+++ b/engines/phoenixvr/script.h
@@ -56,6 +56,7 @@ public:
bool running = true;
bool subroutine = false;
const Scope *scope = nullptr;
+ const Scope *rootScope = nullptr;
};
struct Command {
virtual ~Command() {}
Commit: 5bf3a727cf49d827af1bf46604f58d73153fd9c8
https://github.com/scummvm/scummvm/commit/5bf3a727cf49d827af1bf46604f58d73153fd9c8
Author: Scorp (scorp at mrs.mn)
Date: 2026-07-16T16:58:37+03:00
Commit Message:
PHOENIXVR: Add Messenger game variants
Changed paths:
engines/phoenixvr/detection_tables.h
diff --git a/engines/phoenixvr/detection_tables.h b/engines/phoenixvr/detection_tables.h
index b34396d3ef3..3a78be90771 100644
--- a/engines/phoenixvr/detection_tables.h
+++ b/engines/phoenixvr/detection_tables.h
@@ -211,11 +211,38 @@ const ADGameDescription gameDescriptions[] = {
{"messenger",
nullptr,
- AD_ENTRY2s("script.pak", "1e0f9cb47bc203e9e2983b03ffa85174", 185,
- "textes.txt", "23f577d1201bc3024ca49cb11f9f7347", 5261),
+ AD_ENTRY5s("script.pak", "1e0f9cb47bc203e9e2983b03ffa85174", 185,
+ "textes.txt", "23f577d1201bc3024ca49cb11f9f7347", 5261,
+ "cd1/Data/interface.vr", "110c55142ec95dcbdc0dd86a68013d2a", 188422,
+ "cd1/Data/Script1.pak", "e0cf6ed93ea338dc91d639c6b70b75c1", 54920,
+ "cd2/Data/Script2.pak", "26fe454a74da294ff622cdc5663f2c8c", 82085),
Common::EN_ANY,
Common::kPlatformWindows,
- ADGF_DROPPLATFORM,
+ ADGF_DROPPLATFORM | ADGF_CD | ADGF_UNSTABLE,
+ GUIO1(GUIO_NONE)
+ },
+
+ {"messenger",
+ nullptr,
+ AD_ENTRY4s("script.pak", "0c9c4b2bfa3b1399b89f6b62063a2e80", 210,
+ "cd1/Data/interface.vr", "c128d4c10122ae50ad50e2d3c4ccfbc5", 218081,
+ "cd1/Data/Script.pak", "b8bf0acf2202f1929ed34305346303d9", 54752,
+ "cd2/Data/Script2.pak", "01bccbbb33e901c9addfa0de51d1bea3", 81955),
+ Common::RU_RUS,
+ Common::kPlatformWindows,
+ ADGF_DROPPLATFORM | ADGF_CD | ADGF_UNSTABLE,
+ GUIO1(GUIO_NONE)
+ },
+
+ {"messenger",
+ nullptr,
+ AD_ENTRY4s("script.pak", "0c9c4b2bfa3b1399b89f6b62063a2e80", 210,
+ "cd1/Data/interface.vr", "a11ca0314af1d863bbd289e3af4a52c9", 214009,
+ "cd1/Data/Script.pak", "62980f95be48448d738747a3cac9b2b2", 54944,
+ "cd2/Data/Script2.pak", "ea95c1be10f24c4ad5ad4cb4ad130699", 82104),
+ Common::FR_FRA,
+ Common::kPlatformWindows,
+ ADGF_DROPPLATFORM | ADGF_CD | ADGF_UNSTABLE,
GUIO1(GUIO_NONE)
},
Commit: d0071eae929c99ebc59cdc57082f814cc1ed9c5a
https://github.com/scummvm/scummvm/commit/d0071eae929c99ebc59cdc57082f814cc1ed9c5a
Author: Scorp (scorp at mrs.mn)
Date: 2026-07-16T16:58:37+03:00
Commit Message:
PHOENIXVR: Fix Messenger save return handling
Changed paths:
engines/phoenixvr/commands.h
engines/phoenixvr/detection_tables.h
engines/phoenixvr/phoenixvr.cpp
diff --git a/engines/phoenixvr/commands.h b/engines/phoenixvr/commands.h
index ffb4526f480..fed4a7f43a7 100644
--- a/engines/phoenixvr/commands.h
+++ b/engines/phoenixvr/commands.h
@@ -1726,10 +1726,12 @@ struct Return : public Script::Command {
Return() {}
void exec(Script::ExecutionContext &ctx) const override {
ctx.running = false;
- if (ctx.subroutine)
+ if (ctx.subroutine) {
debug("return to caller");
- else
- debug("return from script");
+ } else {
+ debug("return to previous warp");
+ g_engine->returnToWarp();
+ }
}
};
diff --git a/engines/phoenixvr/detection_tables.h b/engines/phoenixvr/detection_tables.h
index 3a78be90771..5e23dd36937 100644
--- a/engines/phoenixvr/detection_tables.h
+++ b/engines/phoenixvr/detection_tables.h
@@ -214,8 +214,8 @@ const ADGameDescription gameDescriptions[] = {
AD_ENTRY5s("script.pak", "1e0f9cb47bc203e9e2983b03ffa85174", 185,
"textes.txt", "23f577d1201bc3024ca49cb11f9f7347", 5261,
"cd1/Data/interface.vr", "110c55142ec95dcbdc0dd86a68013d2a", 188422,
- "cd1/Data/Script1.pak", "e0cf6ed93ea338dc91d639c6b70b75c1", 54920,
- "cd2/Data/Script2.pak", "26fe454a74da294ff622cdc5663f2c8c", 82085),
+ "cd1/Data/Script1.pak", "f599fc4da33515e414ddb97d8ef9010c", 54920,
+ "cd2/Data/Script2.pak", "d7af7fa917912626c9d13503a2a1178c", 82085),
Common::EN_ANY,
Common::kPlatformWindows,
ADGF_DROPPLATFORM | ADGF_CD | ADGF_UNSTABLE,
diff --git a/engines/phoenixvr/phoenixvr.cpp b/engines/phoenixvr/phoenixvr.cpp
index 0af66729aca..59556cd76b9 100644
--- a/engines/phoenixvr/phoenixvr.cpp
+++ b/engines/phoenixvr/phoenixvr.cpp
@@ -2065,8 +2065,7 @@ Common::Error PhoenixVREngine::saveGameStream(Common::WriteStream *slot, bool is
state.dibHeader.assign(dib.getData(), dib.getData() + dib.size());
state.thumbnail.assign(thumbnailPixels, thumbnailPixels + thumbnailSize);
- state.state = Common::move(_capturedState);
- _capturedState.clear();
+ state.state = _capturedState;
state.save(*slot);
return Common::kNoError;
Commit: be7795688fbc0974682c1e06fdb76413b7140506
https://github.com/scummvm/scummvm/commit/be7795688fbc0974682c1e06fdb76413b7140506
Author: Scorp (scorp at mrs.mn)
Date: 2026-07-16T16:58:37+03:00
Commit Message:
PHOENIXVR: Keep script sounds playing during movies
Changed paths:
engines/phoenixvr/phoenixvr.cpp
diff --git a/engines/phoenixvr/phoenixvr.cpp b/engines/phoenixvr/phoenixvr.cpp
index 59556cd76b9..6531ebbc2c2 100644
--- a/engines/phoenixvr/phoenixvr.cpp
+++ b/engines/phoenixvr/phoenixvr.cpp
@@ -937,7 +937,6 @@ void PhoenixVREngine::playMovie(const Common::String &movie) {
return;
}
- _mixer->pauseAll(true);
_system->lockMouse(false);
dec->start();
@@ -987,7 +986,6 @@ void PhoenixVREngine::playMovie(const Common::String &movie) {
if (subtitles)
g_system->hideOverlay();
_system->lockMouse(_vr.isVR());
- _mixer->pauseAll(false);
}
void PhoenixVREngine::playAnimation(const Common::String &name, const Common::String &var, int varValue, float speed) {
Commit: b1aa7f51996382b18765c7a7037710a6fe5f5314
https://github.com/scummvm/scummvm/commit/b1aa7f51996382b18765c7a7037710a6fe5f5314
Author: Scorp (scorp at mrs.mn)
Date: 2026-07-16T16:58:38+03:00
Commit Message:
PHOENIXVR: Fix Messenger inventory overlay positions
Changed paths:
engines/phoenixvr/commands.h
diff --git a/engines/phoenixvr/commands.h b/engines/phoenixvr/commands.h
index fed4a7f43a7..f9073e828b5 100644
--- a/engines/phoenixvr/commands.h
+++ b/engines/phoenixvr/commands.h
@@ -266,8 +266,7 @@ void drawLouvreActionButtons() {
}
void drawLouvreSelectionMarker(int slot, int imageNumber, bool selected) {
- static const Common::Point kSlotSelectedPos[kMessengerInventorySlots + 1] = {
- Common::Point(273, 15),
+ static const Common::Point kSlotSelectedPos[kMessengerInventorySlots] = {
Common::Point(389, 82),
Common::Point(482, 83),
Common::Point(574, 84),
@@ -282,7 +281,7 @@ void drawLouvreSelectionMarker(int slot, int imageNumber, bool selected) {
Common::Point(264, 28)};
if (slot >= 1 && slot <= kMessengerInventorySlots && imageNumber > 0)
- drawLouvreImage(Common::String::format(selected ? "Sel%04d.bmp" : "DSel%04d.bmp", imageNumber), kSlotSelectedPos[slot].x, kSlotSelectedPos[slot].y);
+ drawLouvreImage(Common::String::format(selected ? "Sel%04d.bmp" : "DSel%04d.bmp", imageNumber), kSlotSelectedPos[slot - 1].x, kSlotSelectedPos[slot - 1].y);
}
void drawLouvreSelectedObject(int objectSlot) {
Commit: 74708adda352eb7dfde5f0f69b8b13c40965a100
https://github.com/scummvm/scummvm/commit/74708adda352eb7dfde5f0f69b8b13c40965a100
Author: Scorp (scorp at mrs.mn)
Date: 2026-07-16T16:58:38+03:00
Commit Message:
PHOENIXVR: Tidy Messenger Louvre helpers
Changed paths:
engines/phoenixvr/commands.h
engines/phoenixvr/phoenixvr.cpp
engines/phoenixvr/phoenixvr.h
diff --git a/engines/phoenixvr/commands.h b/engines/phoenixvr/commands.h
index f9073e828b5..ae04ba7790b 100644
--- a/engines/phoenixvr/commands.h
+++ b/engines/phoenixvr/commands.h
@@ -35,6 +35,10 @@ namespace {
constexpr int kMessengerInventorySlots = 12;
constexpr int kMessengerDisplayedInventorySlots = 8;
constexpr int kLouvreChestSize = 128;
+const Common::Rect kLouvreTextRect(62, 402, 354, 466);
+const uint16 kLouvreTextColor = 1987;
+const uint16 kLouvreWarningTextColor = 63687;
+constexpr uint32 kLouvreTextBlockMillis = 5000;
struct LouvrePluginState {
int objectChest[kLouvreChestSize];
@@ -60,6 +64,10 @@ Common::String messengerInventorySlotName(int slot) {
return Common::String::format("Pos%d", slot);
}
+int louvreSelectionSlot(int selection) {
+ return selection > 100 ? selection - 100 : selection;
+}
+
int messengerInventorySlot(int slot) {
if (slot < 1 || slot > kMessengerInventorySlots)
return 0;
@@ -73,18 +81,13 @@ void setMessengerInventorySlot(int slot, int objectId) {
}
int messengerSpecialObjectSlot(int objectId) {
- switch (objectId) {
- case 100:
- return 10;
- case 200:
- return 11;
- case 300:
- return 9;
- case 400:
- return 12;
- default:
- return 0;
+ static const int kSpecialObjects[] = {300, 100, 200, 400};
+
+ for (uint i = 0; i < ARRAYSIZE(kSpecialObjects); ++i) {
+ if (kSpecialObjects[i] == objectId)
+ return i + 9;
}
+ return 0;
}
Common::String louvreObjectImage(int objectId, int suffix) {
@@ -95,33 +98,39 @@ void drawLouvreImage(const Common::String &image, int x, int y) {
g_engine->drawArchiveImage(image, x, y);
}
-void drawLouvreText(int textId, uint16 color = 1987) {
- static const Common::Rect kTextRect(62, 402, 354, 466);
- drawLouvreImage("EffaceText.bmp", kTextRect.left, kTextRect.top);
- g_engine->clearArchiveText(kTextRect);
- g_engine->drawArchiveText(textId, kTextRect, 14, true, color);
+void drawLouvreText(int textId, uint16 color = kLouvreTextColor) {
+ drawLouvreImage("EffaceText.bmp", kLouvreTextRect.left, kLouvreTextRect.top);
+ g_engine->clearArchiveText(kLouvreTextRect);
+ g_engine->drawArchiveText(textId, kLouvreTextRect, 14, true, color);
}
void clearLouvreText() {
- static const Common::Rect kTextRect(62, 402, 354, 466);
- drawLouvreImage("EffaceText.bmp", kTextRect.left, kTextRect.top);
- g_engine->clearArchiveText(kTextRect);
+ drawLouvreImage("EffaceText.bmp", kLouvreTextRect.left, kLouvreTextRect.top);
+ g_engine->clearArchiveText(kLouvreTextRect);
}
bool louvreTextBlocked() {
return g_louvrePluginState.rolloverBlockedUntil > g_system->getMillis();
}
-bool louvreObjectCanView(int objectId) {
- static const int kViewableObjects[] = {
- 3000, 3100, 4200, 4300, 5500, 5900, 7600, 7700, 7800, 8500, 8600, 8800};
+void drawBlockedLouvreText(int textId) {
+ drawLouvreText(textId, kLouvreWarningTextColor);
+ g_louvrePluginState.rolloverBlockedUntil = g_system->getMillis() + kLouvreTextBlockMillis;
+}
- for (int viewableObject : kViewableObjects)
- if (viewableObject == objectId)
+template<size_t N>
+bool louvreContainsObject(const int (&objects)[N], int objectId) {
+ for (int object : objects)
+ if (object == objectId)
return true;
return false;
}
+bool louvreObjectCanView(int objectId) {
+ static const int kViewableObjects[] = {3000, 3100, 4200, 4300, 5500, 5900, 7600, 7700, 7800, 8500, 8600, 8800};
+ return louvreContainsObject(kViewableObjects, objectId);
+}
+
int louvreCombineObjects(int selectedObjectId, int objectId) {
struct CombineEntry {
int left;
@@ -210,11 +219,7 @@ bool louvreObjectCanCombine(int objectId) {
500, 600, 700, 1200, 1300, 1400, 1500, 1700, 1800, 2100, 2200, 2300, 2600, 2900,
3000, 3100, 3400, 3500, 3600, 3700, 3800, 4200, 4300, 4400, 6000, 6100, 6200,
6300, 6400, 6900, 7000, 7700, 7800, 8200, 9300, 9600, 9800, 9900};
-
- for (int combinableObject : kCombinableObjects)
- if (combinableObject == objectId)
- return true;
- return false;
+ return louvreContainsObject(kCombinableObjects, objectId);
}
bool louvreObjectCanSeparate(int objectId) {
@@ -242,10 +247,7 @@ int louvreObjectActionMask(int objectId) {
}
int louvreSelectedObjectActionMask() {
- int selection = g_engine->getVariable("Selection");
- if (selection > 100)
- selection -= 100;
- return louvreObjectActionMask(messengerInventorySlot(selection));
+ return louvreObjectActionMask(messengerInventorySlot(louvreSelectionSlot(g_engine->getVariable("Selection"))));
}
void drawLouvreActionButtons() {
@@ -289,8 +291,7 @@ void drawLouvreSelectedObject(int objectSlot) {
static const int kSelectedObjectY = 123;
initLouvrePluginState();
- if (objectSlot > 100)
- objectSlot -= 100;
+ objectSlot = louvreSelectionSlot(objectSlot);
if (objectSlot < 1 || objectSlot > kMessengerInventorySlots)
return;
@@ -306,7 +307,7 @@ void drawLouvreSelectedObject(int objectSlot) {
void clearLouvreSelection(bool clearObjectPreview = true) {
int selection = g_engine->getVariable("Selection");
- int slot = selection > 100 ? selection - 100 : selection;
+ int slot = louvreSelectionSlot(selection);
g_engine->setVariable("Selection", 0);
if (clearObjectPreview) {
@@ -319,8 +320,8 @@ void clearLouvreSelection(bool clearObjectPreview = true) {
void setLouvreSelectedSlot(int slot, int flags) {
int previousSelection = g_engine->getVariable("Selection");
- int previousSlot = previousSelection > 100 ? previousSelection - 100 : previousSelection;
- int targetSlot = slot > 100 ? slot - 100 : slot;
+ int previousSlot = louvreSelectionSlot(previousSelection);
+ int targetSlot = louvreSelectionSlot(slot);
if (previousSelection == slot && (flags & 2) == 0) {
clearLouvreSelection((flags & 1) == 0);
@@ -445,8 +446,7 @@ void exchangeLouvreChestObject(int chestSlot, int inventorySlot) {
chestSlot = g_engine->getVariable("CoffreSelect");
if (inventorySlot == 0)
inventorySlot = g_engine->getVariable("Selection");
- if (inventorySlot > 100)
- inventorySlot -= 100;
+ inventorySlot = louvreSelectionSlot(inventorySlot);
if (chestSlot < 1 || chestSlot > 4 || inventorySlot < 1 || inventorySlot > kMessengerInventorySlots)
return;
@@ -869,9 +869,7 @@ struct DoAction : public Script::Command {
initLouvrePluginState();
g_engine->setVariable(arg, 0);
- int selection = g_engine->getVariable("Selection");
- if (selection > 100)
- selection -= 100;
+ int selection = louvreSelectionSlot(g_engine->getVariable("Selection"));
if (selection == 0)
return;
@@ -1039,9 +1037,7 @@ struct AfficheSelection : public Script::Command {
AfficheSelection(const Common::Array<Common::String> &args) {}
void exec(Script::ExecutionContext &ctx) const override {
initLouvrePluginState();
- int selection = g_engine->getVariable("Selection");
- if (selection > 100)
- selection -= 100;
+ int selection = louvreSelectionSlot(g_engine->getVariable("Selection"));
if (selection != 0)
drawLouvreSelectedObject(selection);
}
@@ -1352,7 +1348,7 @@ struct Discocier : public Script::Command {
g_engine->setVariable(var, 0);
int selection = g_engine->getVariable("Selection");
- int slot = selection > 100 ? selection - 100 : selection;
+ int slot = louvreSelectionSlot(selection);
int objectId = messengerInventorySlot(slot);
int parts[3] = {0, 0, 0};
@@ -1377,8 +1373,7 @@ struct Discocier : public Script::Command {
if (partCount > emptySlots + 1) {
drawLouvreActionButtons();
- drawLouvreText(28, 63687);
- g_louvrePluginState.rolloverBlockedUntil = g_system->getMillis() + 5000;
+ drawBlockedLouvreText(28);
return;
}
@@ -1433,16 +1428,13 @@ struct DrawTextSelection : public Script::Command {
if (louvreTextBlocked())
return;
- static const Common::Rect kTextRect(62, 402, 354, 466);
clearLouvreText();
- int selection = g_engine->getVariable("Selection");
- if (selection > 100)
- selection -= 100;
+ int selection = louvreSelectionSlot(g_engine->getVariable("Selection"));
if (selection != 0) {
int objectId = messengerInventorySlot(selection);
if (objectId != 0)
- g_engine->drawArchiveText(objectId, kTextRect, 14, true, 1987);
+ g_engine->drawArchiveText(objectId, kLouvreTextRect, 14, true, kLouvreTextColor);
}
}
};
@@ -1508,8 +1500,7 @@ struct PorteFRollover : public Script::Command {
return;
if (arg == 28) {
- drawLouvreText(arg, 63687);
- g_louvrePluginState.rolloverBlockedUntil = g_system->getMillis() + 5000;
+ drawBlockedLouvreText(arg);
} else {
drawLouvreText(arg);
}
diff --git a/engines/phoenixvr/phoenixvr.cpp b/engines/phoenixvr/phoenixvr.cpp
index 6531ebbc2c2..8a748e631e9 100644
--- a/engines/phoenixvr/phoenixvr.cpp
+++ b/engines/phoenixvr/phoenixvr.cpp
@@ -1001,10 +1001,15 @@ void PhoenixVREngine::resetLockKey() {
_prevWarp = -1; // original game does only this o_O
}
-void PhoenixVREngine::drawArchiveImage(const Common::String &image, int x, int y) {
+const Graphics::Surface *PhoenixVREngine::findArchiveImage(const Common::String &image) const {
const Graphics::Surface *surface = _arn ? _arn->get(image) : nullptr;
if (!surface && !image.contains('.'))
surface = _arn ? _arn->get(image + ".bmp") : nullptr;
+ return surface;
+}
+
+void PhoenixVREngine::drawArchiveImage(const Common::String &image, int x, int y) {
+ const Graphics::Surface *surface = findArchiveImage(image);
if (!surface) {
warning("can't find archive image %s", image.c_str());
return;
@@ -1049,9 +1054,7 @@ void PhoenixVREngine::showImageOverlay(const Common::String &image, int x, int y
debug("AfficheImage %s %d %d", image.c_str(), x, y);
_imageOverlay.reset();
- const Graphics::Surface *surface = _arn ? _arn->get(image) : nullptr;
- if (!surface && !image.contains('.'))
- surface = _arn ? _arn->get(image + ".bmp") : nullptr;
+ const Graphics::Surface *surface = findArchiveImage(image);
if (!surface) {
warning("can't find image overlay %s", image.c_str());
return;
@@ -1254,9 +1257,7 @@ void PhoenixVREngine::renderVR(float dt) {
void PhoenixVREngine::renderArchiveImages() {
for (const ArchiveImage &archiveImage : _archiveImages) {
- const Graphics::Surface *surface = _arn ? _arn->get(archiveImage.image) : nullptr;
- if (!surface && !archiveImage.image.contains('.'))
- surface = _arn ? _arn->get(archiveImage.image + ".bmp") : nullptr;
+ const Graphics::Surface *surface = findArchiveImage(archiveImage.image);
if (!surface)
continue;
diff --git a/engines/phoenixvr/phoenixvr.h b/engines/phoenixvr/phoenixvr.h
index 7ef22297fe4..5eb688bcef8 100644
--- a/engines/phoenixvr/phoenixvr.h
+++ b/engines/phoenixvr/phoenixvr.h
@@ -250,6 +250,7 @@ private:
void tick(float dt);
void tickTimer(float dt);
void loadNextScript();
+ const Graphics::Surface *findArchiveImage(const Common::String &image) const;
void renderVR(float dt);
void renderArchiveImages();
void renderArchiveTexts();
Commit: 8a42b59f4c20e235bd39f73da1a7ea6247889328
https://github.com/scummvm/scummvm/commit/8a42b59f4c20e235bd39f73da1a7ea6247889328
Author: Scorp (scorp at mrs.mn)
Date: 2026-07-16T16:58:38+03:00
Commit Message:
PHOENIXVR: Add debugger variable command
Changed paths:
engines/phoenixvr/console.cpp
engines/phoenixvr/console.h
diff --git a/engines/phoenixvr/console.cpp b/engines/phoenixvr/console.cpp
index 2f7b5e20ed1..31a6c48c415 100644
--- a/engines/phoenixvr/console.cpp
+++ b/engines/phoenixvr/console.cpp
@@ -27,6 +27,7 @@ namespace PhoenixVR {
Console::Console() : GUI::Debugger() {
registerCmd("warp", WRAP_METHOD(Console, cmdWarp));
registerCmd("script", WRAP_METHOD(Console, cmdScript));
+ registerCmd("var", WRAP_METHOD(Console, cmdVar));
registerCmd("stop_all_sounds", WRAP_METHOD(Console, cmdStopAllSounds));
registerCmd("next_level", WRAP_METHOD(Console, cmdNextLevel));
}
@@ -52,6 +53,22 @@ bool Console::cmdScript(int argc, const char **argv) {
return false;
}
+bool Console::cmdVar(int argc, const char **argv) {
+ if (argc < 2) {
+ debugPrintf("var <name> [value]\n");
+ return true;
+ }
+
+ if (argc >= 3) {
+ const int value = atoi(argv[2]);
+ g_engine->declareVariable(argv[1]);
+ g_engine->setVariable(argv[1], value);
+ }
+
+ debugPrintf("%s = %d\n", argv[1], g_engine->getVariable(argv[1]));
+ return true;
+}
+
bool Console::cmdStopAllSounds(int argc, const char **argv) {
g_engine->stopAllSounds();
return false;
diff --git a/engines/phoenixvr/console.h b/engines/phoenixvr/console.h
index 2fdd7fd8ce0..6ef00d29424 100644
--- a/engines/phoenixvr/console.h
+++ b/engines/phoenixvr/console.h
@@ -31,6 +31,7 @@ class Console : public GUI::Debugger {
private:
bool cmdWarp(int argc, const char **argv);
bool cmdScript(int argc, const char **argv);
+ bool cmdVar(int argc, const char **argv);
bool cmdStopAllSounds(int argc, const char **argv);
bool cmdNextLevel(int argc, const char **argv);
More information about the Scummvm-git-logs
mailing list