[Scummvm-git-logs] scummvm master -> 142460fcb2c7c63f522af6a54af6531cae62dff6
npjg
noreply at scummvm.org
Sun Jan 26 19:33:16 UTC 2025
This automated email contains information about 10 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
d9d8f43807 MEDIASTATION: Run context exit event handler when branching to screen
d8ea01f259 MEDIASTATION: Implement remaining comparison operators
4a57bbf375 MEDIASTATION: Implement remaining math operators
1e247d272d MEDIASTATION: Implement remaining logical operators
7c0cae08f3 MEDIASTATION: Activate any assets marked active on context load
fd7552dc4c MEDIASTATION: Add support for calling methods on arrays (collections)
19eb4a84a6 MEDIASTATION: Centralize time event hander processing
ca602eabc2 MEDIASTATION: Clean up Sound asset
8e37b9a596 MEDIASTATION: Implement hotspot enter/exit handling logic
142460fcb2 MEDIASTATION: Stub out more script methods
Commit: d9d8f438077d7f1265bd3de2afbfb9bd149c3170
https://github.com/scummvm/scummvm/commit/d9d8f438077d7f1265bd3de2afbfb9bd149c3170
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-01-26T14:03:43-05:00
Commit Message:
MEDIASTATION: Run context exit event handler when branching to screen
Changed paths:
engines/mediastation/mediastation.cpp
engines/mediastation/mediastation.h
diff --git a/engines/mediastation/mediastation.cpp b/engines/mediastation/mediastation.cpp
index b64b3693041..4ccc276c0b9 100644
--- a/engines/mediastation/mediastation.cpp
+++ b/engines/mediastation/mediastation.cpp
@@ -324,7 +324,19 @@ Operand MediaStationEngine::callMethod(BuiltInMethod methodId, Common::Array<Ope
}
void MediaStationEngine::branchToScreen(uint32 contextId) {
+ if (_currentContext != nullptr) {
+ EventHandler *exitEvent = _currentContext->_screenAsset->_eventHandlers.getValOrDefault(kExitEvent);
+ if (exitEvent != nullptr) {
+ debugC(5, kDebugScript, "Executing context exit event handler");
+ exitEvent->execute(_currentContext->_screenAsset->_id);
+ } else {
+ debugC(5, kDebugScript, "No context exit event handler");
+ }
+ }
+
Context *context = loadContext(contextId);
+ _currentContext = context;
+
if (context->_screenAsset != nullptr) {
// TODO: Make the screen an asset just like everything else so we can
// run event handlers with runEventHandlerIfExists.
diff --git a/engines/mediastation/mediastation.h b/engines/mediastation/mediastation.h
index a086d70b7c6..87a1955d0ce 100644
--- a/engines/mediastation/mediastation.h
+++ b/engines/mediastation/mediastation.h
@@ -86,6 +86,7 @@ public:
Graphics::Screen *_screen = nullptr;
Audio::Mixer *_mixer = nullptr;
+ Context *_currentContext = nullptr;
// All Media Station titles run at 640x480.
const uint16 SCREEN_WIDTH = 640;
Commit: d8ea01f2594322dd0b2d4114470353a0679478cb
https://github.com/scummvm/scummvm/commit/d8ea01f2594322dd0b2d4114470353a0679478cb
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-01-26T14:03:43-05:00
Commit Message:
MEDIASTATION: Implement remaining comparison operators
Changed paths:
engines/mediastation/mediascript/codechunk.cpp
engines/mediastation/mediascript/operand.cpp
engines/mediastation/mediascript/operand.h
diff --git a/engines/mediastation/mediascript/codechunk.cpp b/engines/mediastation/mediascript/codechunk.cpp
index aa1ebd401e8..fb0d20f14f7 100644
--- a/engines/mediastation/mediascript/codechunk.cpp
+++ b/engines/mediastation/mediascript/codechunk.cpp
@@ -193,6 +193,58 @@ Operand CodeChunk::executeNextStatement() {
return returnValue;
}
+ case kOpcodeNotEquals: {
+ debugCN(5, kDebugScript, "\n lhs: ");
+ Operand value1 = executeNextStatement();
+ debugCN(5, kDebugScript, " rhs: ");
+ Operand value2 = executeNextStatement();
+
+ // TODO: Confirm this is the correct value type?
+ Operand returnValue(kOperandTypeLiteral1);
+ bool notEqual = !(value1 == value2);
+ returnValue.putInteger(static_cast<uint>(notEqual));
+ return returnValue;
+ }
+
+ case kOpcodeLessThan: {
+ debugCN(5, kDebugScript, "\n lhs: ");
+ Operand value1 = executeNextStatement();
+ debugCN(5, kDebugScript, " rhs: ");
+ Operand value2 = executeNextStatement();
+
+ // TODO: Confirm this is the correct value type?
+ Operand returnValue(kOperandTypeLiteral1);
+ bool lessThan = (value1 < value2);
+ returnValue.putInteger(static_cast<uint>(lessThan));
+ return returnValue;
+ }
+
+ case kOpcodeGreaterThan: {
+ debugCN(5, kDebugScript, "\n lhs: ");
+ Operand value1 = executeNextStatement();
+ debugCN(5, kDebugScript, " rhs: ");
+ Operand value2 = executeNextStatement();
+
+ // TODO: Confirm this is the correct value type?
+ Operand returnValue(kOperandTypeLiteral1);
+ bool greaterThan = (value1 > value2);
+ returnValue.putInteger(static_cast<uint>(greaterThan));
+ return returnValue;
+ }
+
+ case kOpcodeLessThanOrEqualTo: {
+ debugCN(5, kDebugScript, "\n lhs: ");
+ Operand value1 = executeNextStatement();
+ debugCN(5, kDebugScript, " rhs: ");
+ Operand value2 = executeNextStatement();
+
+ // TODO: Confirm this is the correct value type?
+ Operand returnValue(kOperandTypeLiteral1);
+ bool lessThanOrEqualTo = (value1 < value2) || (value1 == value2);
+ returnValue.putInteger(static_cast<uint>(lessThanOrEqualTo));
+ return returnValue;
+ }
+
case kOpcodeGreaterThanOrEqualTo: {
debugCN(5, kDebugScript, "\n lhs: ");
Operand value1 = executeNextStatement();
@@ -201,7 +253,7 @@ Operand CodeChunk::executeNextStatement() {
// TODO: Confirm this is the correct value type?
Operand returnValue(kOperandTypeLiteral1);
- bool greaterThanOrEqualTo = value1 >= value2;
+ bool greaterThanOrEqualTo = (value1 > value2) || (value1 == value2);
returnValue.putInteger(static_cast<uint>(greaterThanOrEqualTo));
return returnValue;
}
diff --git a/engines/mediastation/mediascript/operand.cpp b/engines/mediastation/mediascript/operand.cpp
index 367dd36724c..0b47ac8bf38 100644
--- a/engines/mediastation/mediascript/operand.cpp
+++ b/engines/mediastation/mediascript/operand.cpp
@@ -270,9 +270,10 @@ Operand Operand::getLiteralValue() const {
bool Operand::operator==(const Operand &other) const {
Operand lhs = getLiteralValue();
Operand rhs = other.getLiteralValue();
- // TODO: Maybe some better type checking here. If the types being compared end up being incompatible, the respective get
- // method on the rhs will raise the error. But better might be checking
- // both before we try getting values to report a more descriptive error.
+ // TODO: Maybe some better type checking here. If the types being compared
+ // end up being incompatible, the respective get method on the rhs will
+ // raise the error. But better might be checking both before we try getting
+ // values to report a more descriptive error.
switch (lhs.getType()) {
case kOperandTypeLiteral1:
case kOperandTypeLiteral2:
@@ -293,7 +294,7 @@ bool Operand::operator==(const Operand &other) const {
}
}
-bool Operand::operator>=(const Operand &other) const {
+bool Operand::operator<(const Operand &other) const {
Operand lhs = getLiteralValue();
Operand rhs = other.getLiteralValue();
// If the types being compared end up being incompatible, the respective get
@@ -301,14 +302,33 @@ bool Operand::operator>=(const Operand &other) const {
switch (lhs.getType()) {
case kOperandTypeLiteral1:
case kOperandTypeLiteral2:
- return lhs.getInteger() >= rhs.getInteger();
+ return lhs.getInteger() < rhs.getInteger();
case kOperandTypeFloat1:
case kOperandTypeFloat2:
- return lhs.getDouble() >= rhs.getDouble();
+ return lhs.getDouble() < rhs.getDouble();
default:
- error("Operand::operator>=(): Unsupported operand types %d and %d", static_cast<uint>(lhs.getType()), static_cast<uint>(rhs.getType()));
+ error("Operand::operator<(): Unsupported operand types %d and %d", static_cast<uint>(lhs.getType()), static_cast<uint>(rhs.getType()));
+ }
+}
+
+bool Operand::operator>(const Operand &other) const {
+ Operand lhs = getLiteralValue();
+ Operand rhs = other.getLiteralValue();
+ // If the types being compared end up being incompatible, the respective get
+ // method on the rhs will raise the error.
+ switch (lhs.getType()) {
+ case kOperandTypeLiteral1:
+ case kOperandTypeLiteral2:
+ return lhs.getInteger() > rhs.getInteger();
+
+ case kOperandTypeFloat1:
+ case kOperandTypeFloat2:
+ return lhs.getDouble() > rhs.getDouble();
+
+ default:
+ error("Operand::operator>(): Unsupported operand types %d and %d", static_cast<uint>(lhs.getType()), static_cast<uint>(rhs.getType()));
}
}
diff --git a/engines/mediastation/mediascript/operand.h b/engines/mediastation/mediascript/operand.h
index 4c8b1c35135..8e6a96b472f 100644
--- a/engines/mediastation/mediascript/operand.h
+++ b/engines/mediastation/mediascript/operand.h
@@ -62,7 +62,9 @@ public:
Operand getLiteralValue() const;
bool operator==(const Operand &other) const;
- bool operator>=(const Operand &other) const;
+ bool operator<(const Operand &other) const;
+ bool operator>(const Operand &other) const;
+
bool operator||(const Operand &other) const;
Operand operator-(const Operand &other) const;
Commit: 4a57bbf375ce4015957140dca28aee1d33546cf7
https://github.com/scummvm/scummvm/commit/4a57bbf375ce4015957140dca28aee1d33546cf7
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-01-26T14:03:43-05:00
Commit Message:
MEDIASTATION: Implement remaining math operators
And re-order two that already existed so it reads nicely.
Changed paths:
engines/mediastation/mediascript/codechunk.cpp
engines/mediastation/mediascript/operand.cpp
engines/mediastation/mediascript/operand.h
diff --git a/engines/mediastation/mediascript/codechunk.cpp b/engines/mediastation/mediascript/codechunk.cpp
index fb0d20f14f7..b1f15f48436 100644
--- a/engines/mediastation/mediascript/codechunk.cpp
+++ b/engines/mediastation/mediascript/codechunk.cpp
@@ -143,23 +143,6 @@ Operand CodeChunk::executeNextStatement() {
return returnValue;
}
- case kOpcodeSubtract: {
- debugCN(5, kDebugScript, "\n lhs: ");
- Operand value1 = executeNextStatement();
- debugCN(5, kDebugScript, " rhs: ");
- Operand value2 = executeNextStatement();
-
- Operand returnValue = value1 - value2;
- return returnValue;
- }
-
- case kOpcodeNegate: {
- Operand value = executeNextStatement();
- debugCN(5, kDebugScript, " value: ");
-
- return -value;
- }
-
case kOpcodeIfElse: {
debugCN(5, kDebugScript, "\n condition: ");
Operand condition = executeNextStatement();
@@ -258,6 +241,63 @@ Operand CodeChunk::executeNextStatement() {
return returnValue;
}
+ case kOpcodeAdd: {
+ debugCN(5, kDebugScript, "\n lhs: ");
+ Operand value1 = executeNextStatement();
+ debugCN(5, kDebugScript, " rhs: ");
+ Operand value2 = executeNextStatement();
+
+ Operand returnValue = value1 + value2;
+ return returnValue;
+ }
+
+ case kOpcodeSubtract: {
+ debugCN(5, kDebugScript, "\n lhs: ");
+ Operand value1 = executeNextStatement();
+ debugCN(5, kDebugScript, " rhs: ");
+ Operand value2 = executeNextStatement();
+
+ Operand returnValue = value1 - value2;
+ return returnValue;
+ }
+
+ case kOpcodeMultiply: {
+ debugCN(5, kDebugScript, "\n lhs: ");
+ Operand value1 = executeNextStatement();
+ debugCN(5, kDebugScript, " rhs: ");
+ Operand value2 = executeNextStatement();
+
+ Operand returnValue = value1 * value2;
+ return returnValue;
+ }
+
+ case kOpcodeDivide: {
+ debugCN(5, kDebugScript, "\n lhs: ");
+ Operand value1 = executeNextStatement();
+ debugCN(5, kDebugScript, " rhs: ");
+ Operand value2 = executeNextStatement();
+
+ Operand returnValue = value1 / value2;
+ return returnValue;
+ }
+
+ case kOpcodeModulo: {
+ debugCN(5, kDebugScript, "\n lhs: ");
+ Operand value1 = executeNextStatement();
+ debugCN(5, kDebugScript, " rhs: ");
+ Operand value2 = executeNextStatement();
+
+ Operand returnValue = value1 % value2;
+ return returnValue;
+ }
+
+ case kOpcodeNegate: {
+ Operand value = executeNextStatement();
+ debugCN(5, kDebugScript, " value: ");
+
+ return -value;
+ }
+
default: {
error("CodeChunk::getNextStatement(): Got unknown opcode %s (%d)", opcodeToStr(opcode), static_cast<uint>(opcode));
}
diff --git a/engines/mediastation/mediascript/operand.cpp b/engines/mediastation/mediascript/operand.cpp
index 0b47ac8bf38..c4e42001116 100644
--- a/engines/mediastation/mediascript/operand.cpp
+++ b/engines/mediastation/mediascript/operand.cpp
@@ -347,18 +347,119 @@ bool Operand::operator||(const Operand &other) const {
}
}
+Operand Operand::operator+(const Operand &other) const {
+ Operand lhs = getLiteralValue();
+ Operand rhs = other.getLiteralValue();
+ Operand returnValue(lhs.getType());
+ // If the types being compared end up being incompatible, the respective get
+ // method on the rhs will raise the error.
+ switch (lhs.getType()) {
+ case kOperandTypeLiteral1:
+ case kOperandTypeLiteral2:
+ returnValue.putInteger(lhs.getInteger() + rhs.getInteger());
+ return returnValue;
+
+ case kOperandTypeFloat1:
+ case kOperandTypeFloat2:
+ returnValue.putDouble(lhs.getDouble() + rhs.getDouble());
+ return returnValue;
+
+ default:
+ error("Operand::operator+(): Unsupported operand types %s and %s", operandTypeToStr(lhs.getType()), operandTypeToStr(rhs.getType()));
+ }
+}
+
Operand Operand::operator-(const Operand &other) const {
- Operand returnValue;
- if (this->_type == kOperandTypeLiteral1 && other._type == kOperandTypeLiteral1) {
- returnValue._type = kOperandTypeLiteral1;
- returnValue._u.i = this->_u.i - other._u.i;
- } else if (this->_type == kOperandTypeFloat1 && other._type == kOperandTypeFloat1) {
- returnValue._type = kOperandTypeFloat1;
- returnValue._u.d = this->_u.d - other._u.d;
- } else {
- error("Operand::operator-(): Unsupported operand types %d and %d", static_cast<uint>(this->_type), static_cast<uint>(other._type));
+ Operand lhs = getLiteralValue();
+ Operand rhs = other.getLiteralValue();
+ Operand returnValue(lhs.getType());
+ // If the types being compared end up being incompatible, the respective get
+ // method on the rhs will raise the error.
+ switch (lhs.getType()) {
+ case kOperandTypeLiteral1:
+ case kOperandTypeLiteral2:
+ returnValue.putInteger(lhs.getInteger() - rhs.getInteger());
+ return returnValue;
+
+ case kOperandTypeFloat1:
+ case kOperandTypeFloat2:
+ returnValue.putDouble(lhs.getDouble() - rhs.getDouble());
+ return returnValue;
+
+ default:
+ error("Operand::operator-(): Unsupported operand types %s and %s", operandTypeToStr(lhs.getType()), operandTypeToStr(rhs.getType()));
+ }
+}
+
+Operand Operand::operator*(const Operand &other) const {
+ Operand lhs = getLiteralValue();
+ Operand rhs = other.getLiteralValue();
+ Operand returnValue(lhs.getType());
+ // If the types being compared end up being incompatible, the respective get
+ // method on the rhs will raise the error.
+ switch (lhs.getType()) {
+ case kOperandTypeLiteral1:
+ case kOperandTypeLiteral2:
+ returnValue.putInteger(lhs.getInteger() * rhs.getInteger());
+ return returnValue;
+
+ case kOperandTypeFloat1:
+ case kOperandTypeFloat2:
+ returnValue.putDouble(lhs.getDouble() * rhs.getDouble());
+ return returnValue;
+
+ default:
+ error("Operand::operator*(): Unsupported operand types %s and %s", operandTypeToStr(lhs.getType()), operandTypeToStr(rhs.getType()));
+ }
+}
+
+Operand Operand::operator/(const Operand &other) const {
+ Operand lhs = getLiteralValue();
+ Operand rhs = other.getLiteralValue();
+ Operand returnValue(lhs.getType());
+ // If the types being compared end up being incompatible, the respective get
+ // method on the rhs will raise the error.
+ switch (lhs.getType()) {
+ case kOperandTypeLiteral1:
+ case kOperandTypeLiteral2:
+ if (rhs.getInteger() == 0) {
+ error("Operand::operator/(): Attempted divide by zero");
+ }
+ // Standard integer divison here.
+ returnValue.putInteger(lhs.getInteger() / rhs.getInteger());
+ return returnValue;
+
+ case kOperandTypeFloat1:
+ case kOperandTypeFloat2:
+ if (rhs.getDouble() == 0) {
+ error("Operand::operator/(): Attempted divide by zero");
+ }
+ returnValue.putDouble(lhs.getDouble() / rhs.getDouble());
+ return returnValue;
+
+ default:
+ error("Operand::operator/(): Unsupported operand types %s and %s", operandTypeToStr(lhs.getType()), operandTypeToStr(rhs.getType()));
+ }
+}
+
+Operand Operand::operator%(const Operand &other) const {
+ Operand lhs = getLiteralValue();
+ Operand rhs = other.getLiteralValue();
+ Operand returnValue(lhs.getType());
+ // If the types being compared end up being incompatible, the respective get
+ // method on the rhs will raise the error.
+ switch (lhs.getType()) {
+ case kOperandTypeLiteral1:
+ case kOperandTypeLiteral2:
+ if (rhs.getInteger() == 0) {
+ error("Operand::operator%%(): Attempted mod by zero");
+ }
+ returnValue.putInteger(lhs.getInteger() % rhs.getInteger());
+ return returnValue;
+
+ default:
+ error("Operand::operator/(): Unsupported operand types %s and %s", operandTypeToStr(lhs.getType()), operandTypeToStr(rhs.getType()));
}
- return returnValue;
}
Operand Operand::operator-() const {
diff --git a/engines/mediastation/mediascript/operand.h b/engines/mediastation/mediascript/operand.h
index 8e6a96b472f..3511c39fb66 100644
--- a/engines/mediastation/mediascript/operand.h
+++ b/engines/mediastation/mediascript/operand.h
@@ -67,7 +67,11 @@ public:
bool operator||(const Operand &other) const;
+ Operand operator+(const Operand &other) const;
Operand operator-(const Operand &other) const;
+ Operand operator*(const Operand &other) const;
+ Operand operator/(const Operand &other) const;
+ Operand operator%(const Operand &other) const;
Operand operator-() const;
private:
Commit: 1e247d272d43b12505846254a1d8fb35646b7055
https://github.com/scummvm/scummvm/commit/1e247d272d43b12505846254a1d8fb35646b7055
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-01-26T14:03:43-05:00
Commit Message:
MEDIASTATION: Implement remaining logical operators
Changed paths:
engines/mediastation/mediascript/codechunk.cpp
engines/mediastation/mediascript/operand.cpp
engines/mediastation/mediascript/operand.h
engines/mediastation/mediascript/scriptconstants.cpp
engines/mediastation/mediascript/scriptconstants.h
diff --git a/engines/mediastation/mediascript/codechunk.cpp b/engines/mediastation/mediascript/codechunk.cpp
index b1f15f48436..02fdc467dca 100644
--- a/engines/mediastation/mediascript/codechunk.cpp
+++ b/engines/mediastation/mediascript/codechunk.cpp
@@ -143,6 +143,28 @@ Operand CodeChunk::executeNextStatement() {
return returnValue;
}
+ case kOpcodeNot: {
+ debugCN(5, kDebugScript, "\n value: ");
+ Operand value = executeNextStatement();
+
+ Operand returnValue(kOperandTypeLiteral1);
+ bool logicalNot = !(static_cast<bool>(value.getInteger()));
+ returnValue.putInteger(static_cast<uint>(logicalNot));
+ return returnValue;
+ }
+
+ case kOpcodeAnd: {
+ debugCN(5, kDebugScript, "\n value: ");
+ Operand value1 = executeNextStatement();
+ debugCN(5, kDebugScript, " rhs: ");
+ Operand value2 = executeNextStatement();
+
+ Operand returnValue(kOperandTypeLiteral1);
+ bool logicalAnd = (value1 && value2);
+ returnValue.putInteger(static_cast<uint>(logicalAnd));
+ return returnValue;
+ }
+
case kOpcodeIfElse: {
debugCN(5, kDebugScript, "\n condition: ");
Operand condition = executeNextStatement();
diff --git a/engines/mediastation/mediascript/operand.cpp b/engines/mediastation/mediascript/operand.cpp
index c4e42001116..c9dd125d915 100644
--- a/engines/mediastation/mediascript/operand.cpp
+++ b/engines/mediastation/mediascript/operand.cpp
@@ -347,6 +347,35 @@ bool Operand::operator||(const Operand &other) const {
}
}
+bool Operand::operator!() const {
+ Operand literalValue = getLiteralValue();
+ // If the types being compared end up being incompatible, the respective get
+ // method on the rhs will raise the error.
+ switch (literalValue.getType()) {
+ case kOperandTypeLiteral1:
+ case kOperandTypeLiteral2:
+ return !literalValue.getInteger();
+
+ default:
+ error("Operand::operator!(): Unsupported operand type %d", static_cast<uint>(literalValue.getType()));
+ }
+}
+
+bool Operand::operator&&(const Operand &other) const {
+ Operand lhs = getLiteralValue();
+ Operand rhs = other.getLiteralValue();
+ // If the types being compared end up being incompatible, the respective get
+ // method on the rhs will raise the error.
+ switch (lhs.getType()) {
+ case kOperandTypeLiteral1:
+ case kOperandTypeLiteral2:
+ return lhs.getInteger() && rhs.getInteger();
+
+ default:
+ error("Operand::operator&&(): Unsupported operand types %s and %s", operandTypeToStr(lhs.getType()), operandTypeToStr(rhs.getType()));
+ }
+}
+
Operand Operand::operator+(const Operand &other) const {
Operand lhs = getLiteralValue();
Operand rhs = other.getLiteralValue();
diff --git a/engines/mediastation/mediascript/operand.h b/engines/mediastation/mediascript/operand.h
index 3511c39fb66..45ab73ca832 100644
--- a/engines/mediastation/mediascript/operand.h
+++ b/engines/mediastation/mediascript/operand.h
@@ -66,6 +66,8 @@ public:
bool operator>(const Operand &other) const;
bool operator||(const Operand &other) const;
+ bool operator!() const;
+ bool operator&&(const Operand &other) const;
Operand operator+(const Operand &other) const;
Operand operator-(const Operand &other) const;
diff --git a/engines/mediastation/mediascript/scriptconstants.cpp b/engines/mediastation/mediascript/scriptconstants.cpp
index 3a88cf20ce7..55ec25bc4fa 100644
--- a/engines/mediastation/mediascript/scriptconstants.cpp
+++ b/engines/mediastation/mediascript/scriptconstants.cpp
@@ -46,6 +46,8 @@ const char *opcodeToStr(Opcode opcode) {
return "AssignVariable";
case kOpcodeOr:
return "Or";
+ case kOpcodeNot:
+ return "Not";
case kOpcodeAnd:
return "And";
case kOpcodeEquals:
diff --git a/engines/mediastation/mediascript/scriptconstants.h b/engines/mediastation/mediascript/scriptconstants.h
index afe0c5ffd39..2eb9f906c23 100644
--- a/engines/mediastation/mediascript/scriptconstants.h
+++ b/engines/mediastation/mediascript/scriptconstants.h
@@ -36,6 +36,7 @@ enum Opcode {
kOpcodeIfElse = 202,
kOpcodeAssignVariable = 203,
kOpcodeOr = 204,
+ kOpcodeNot = 205,
kOpcodeAnd = 206,
kOpcodeEquals = 207,
kOpcodeNotEquals = 208,
Commit: 7c0cae08f3fe2d52f8583db1d6c8894f5a6381a5
https://github.com/scummvm/scummvm/commit/7c0cae08f3fe2d52f8583db1d6c8894f5a6381a5
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-01-26T14:03:43-05:00
Commit Message:
MEDIASTATION: Activate any assets marked active on context load
Changed paths:
engines/mediastation/assets/image.cpp
engines/mediastation/assets/image.h
engines/mediastation/assets/sprite.cpp
engines/mediastation/assets/sprite.h
engines/mediastation/context.cpp
engines/mediastation/context.h
engines/mediastation/mediastation.cpp
diff --git a/engines/mediastation/assets/image.cpp b/engines/mediastation/assets/image.cpp
index 89fe6266b36..edaa22a75c9 100644
--- a/engines/mediastation/assets/image.cpp
+++ b/engines/mediastation/assets/image.cpp
@@ -24,6 +24,12 @@
namespace MediaStation {
+Image::Image(AssetHeader *header) : Asset(header) {
+ if (header->_startup == kAssetStartupActive) {
+ _isActive = true;
+ }
+}
+
Image::~Image() {
delete _bitmap;
_bitmap = nullptr;
diff --git a/engines/mediastation/assets/image.h b/engines/mediastation/assets/image.h
index 4a0763f9e27..a950e207b88 100644
--- a/engines/mediastation/assets/image.h
+++ b/engines/mediastation/assets/image.h
@@ -33,7 +33,7 @@ namespace MediaStation {
class Image : public Asset {
public:
- Image(AssetHeader *header) : Asset(header) {};
+ Image(AssetHeader *header);
virtual ~Image() override;
virtual void readChunk(Chunk &chunk) override;
diff --git a/engines/mediastation/assets/sprite.cpp b/engines/mediastation/assets/sprite.cpp
index f8f5b246926..201987b6dea 100644
--- a/engines/mediastation/assets/sprite.cpp
+++ b/engines/mediastation/assets/sprite.cpp
@@ -66,6 +66,12 @@ uint32 SpriteFrame::index() {
return _bitmapHeader->_index;
}
+Sprite::Sprite(AssetHeader *header) : Asset(header) {
+ if (header->_startup == kAssetStartupActive) {
+ _isActive = true;
+ }
+}
+
Sprite::~Sprite() {
for (SpriteFrame *frame : _frames) {
delete frame;
diff --git a/engines/mediastation/assets/sprite.h b/engines/mediastation/assets/sprite.h
index ca41f754918..c1a3114fffb 100644
--- a/engines/mediastation/assets/sprite.h
+++ b/engines/mediastation/assets/sprite.h
@@ -60,7 +60,7 @@ private:
class Sprite : public Asset {
public:
- Sprite(AssetHeader *header) : Asset(header) {};
+ Sprite(AssetHeader *header);
~Sprite();
virtual Operand callMethod(BuiltInMethod methodId, Common::Array<Operand> &args) override;
diff --git a/engines/mediastation/context.cpp b/engines/mediastation/context.cpp
index 845b9baf531..f475ba05e40 100644
--- a/engines/mediastation/context.cpp
+++ b/engines/mediastation/context.cpp
@@ -103,6 +103,14 @@ Function *Context::getFunctionById(uint functionId) {
return _functions.getValOrDefault(functionId);
}
+void Context::registerActiveAssets() {
+ for (auto it = _assets.begin(); it != _assets.end(); ++it) {
+ if (it->_value->isActive()) {
+ g_engine->addPlayingAsset(it->_value);
+ }
+ }
+}
+
bool Context::readPreamble() {
uint16 signature = _stream->readUint16LE();
if (signature != 0x4949) { // "II"
diff --git a/engines/mediastation/context.h b/engines/mediastation/context.h
index 6b3e19b7746..618c3c9d310 100644
--- a/engines/mediastation/context.h
+++ b/engines/mediastation/context.h
@@ -64,6 +64,7 @@ public:
Asset *getAssetById(uint assetId);
Asset *getAssetByChunkReference(uint chunkReference);
Function *getFunctionById(uint functionId);
+ void registerActiveAssets();
private:
Common::HashMap<uint, Asset *> _assets;
diff --git a/engines/mediastation/mediastation.cpp b/engines/mediastation/mediastation.cpp
index 4ccc276c0b9..4f84cac81ba 100644
--- a/engines/mediastation/mediastation.cpp
+++ b/engines/mediastation/mediastation.cpp
@@ -272,6 +272,7 @@ Context *MediaStationEngine::loadContext(uint32 contextId) {
_screen->setPalette(*context->_palette);
}
+ context->registerActiveAssets();
_loadedContexts.setVal(contextId, context);
return context;
}
Commit: fd7552dc4c4eaf495e0031322de3303c7330a2df
https://github.com/scummvm/scummvm/commit/fd7552dc4c4eaf495e0031322de3303c7330a2df
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-01-26T14:29:06-05:00
Commit Message:
MEDIASTATION: Add support for calling methods on arrays (collections)
Changed paths:
engines/mediastation/mediascript/codechunk.cpp
engines/mediastation/mediascript/operand.cpp
engines/mediastation/mediascript/operand.h
engines/mediastation/mediascript/scriptconstants.cpp
engines/mediastation/mediascript/scriptconstants.h
engines/mediastation/mediascript/variable.cpp
engines/mediastation/mediascript/variable.h
diff --git a/engines/mediastation/mediascript/codechunk.cpp b/engines/mediastation/mediascript/codechunk.cpp
index 02fdc467dca..8247b5e05f2 100644
--- a/engines/mediastation/mediascript/codechunk.cpp
+++ b/engines/mediastation/mediascript/codechunk.cpp
@@ -486,6 +486,12 @@ Operand CodeChunk::callBuiltInMethod(BuiltInMethod method, Operand self, Common:
}
}
+ case kOperandTypeCollection: {
+ Collection *collection = literalSelf.getCollection();
+ Operand returnValue = collection->callMethod(method, args);
+ return returnValue;
+ }
+
default:
error("CodeChunk::callBuiltInMethod(): Attempt to call method on unsupported operand type %s (%d)",
operandTypeToStr(literalType), static_cast<uint>(literalType));
diff --git a/engines/mediastation/mediascript/operand.cpp b/engines/mediastation/mediascript/operand.cpp
index c9dd125d915..d6e09f12ff5 100644
--- a/engines/mediastation/mediascript/operand.cpp
+++ b/engines/mediastation/mediascript/operand.cpp
@@ -257,6 +257,45 @@ uint32 Operand::getAssetId() {
}
}
+void Operand::putCollection(Collection *collection) {
+ switch (_type) {
+ case kOperandTypeCollection: {
+ _u.collection = collection;
+ break;
+ }
+
+ case kOperandTypeVariableDeclaration: {
+ assert(_u.variable->_type == kVariableTypeCollection);
+ _u.variable->_value.collection = collection;
+ break;
+ }
+
+ default: {
+ error("Operand::putCollection(): Attempt to put collection into operand type %s (%d)",
+ operandTypeToStr(_type), static_cast<uint>(_type));
+ }
+ }
+}
+
+Collection *Operand::getCollection() {
+ switch (_type) {
+ case kOperandTypeCollection: {
+ return _u.collection;
+ }
+
+ case kOperandTypeVariableDeclaration: {
+ assert(_u.variable->_type == kVariableTypeCollection);
+ return _u.variable->_value.collection;
+ break;
+ }
+
+ default: {
+ error("Operand::getCollection(): Attempt to get collection from operand type %s (%d)",
+ operandTypeToStr(_type), static_cast<uint>(_type));
+ }
+ }
+}
+
Operand Operand::getLiteralValue() const {
// This function dereferences any variable to get the actual
// "direct" value (a literal asset ID or otherwise).
diff --git a/engines/mediastation/mediascript/operand.h b/engines/mediastation/mediascript/operand.h
index 45ab73ca832..f66b49d1916 100644
--- a/engines/mediastation/mediascript/operand.h
+++ b/engines/mediastation/mediascript/operand.h
@@ -59,6 +59,9 @@ public:
Asset *getAsset();
uint32 getAssetId();
+ void putCollection(Collection *collection);
+ Collection *getCollection();
+
Operand getLiteralValue() const;
bool operator==(const Operand &other) const;
@@ -85,6 +88,7 @@ private:
Variable *variable;
int i;
double d;
+ Collection *collection;
} _u;
};
diff --git a/engines/mediastation/mediascript/scriptconstants.cpp b/engines/mediastation/mediascript/scriptconstants.cpp
index 55ec25bc4fa..1aaee142b51 100644
--- a/engines/mediastation/mediascript/scriptconstants.cpp
+++ b/engines/mediastation/mediascript/scriptconstants.cpp
@@ -211,6 +211,10 @@ const char *builtInMethodToStr(BuiltInMethod method) {
return "Sort";
case kDeleteAtMethod:
return "DeleteAt";
+ case kJumbleMethod:
+ return "Jumble";
+ case kDeleteFirstMethod:
+ return "DeleteFirst";
case kOpenLensMethod:
return "OpenLens";
case kCloseLensMethod:
@@ -320,6 +324,8 @@ const char *operandTypeToStr(OperandType type) {
return "AssetId";
case kOperandTypeVariableDeclaration:
return "VariableDeclaration";
+ case kOperandTypeCollection:
+ return "Collection";
case kOperandTypeFunction:
return "Function";
default:
diff --git a/engines/mediastation/mediascript/scriptconstants.h b/engines/mediastation/mediascript/scriptconstants.h
index 2eb9f906c23..3e9d03547be 100644
--- a/engines/mediastation/mediascript/scriptconstants.h
+++ b/engines/mediastation/mediascript/scriptconstants.h
@@ -157,6 +157,8 @@ enum BuiltInMethod {
kSeekMethod = 256, // PARAMS: 1
kSortMethod = 266, // PARAMS: 0
kDeleteAtMethod = 258, // PARAMS: 1
+ kJumbleMethod = 255, // PARAMS: 0
+ kDeleteFirstMethod = 250, // PARAMS: 0
// PRINTER METHODS.
kOpenLensMethod = 346, // PARAMS: 0
@@ -244,6 +246,7 @@ enum OperandType {
kOperandTypeDollarSignVariable = 155,
kOperandTypeAssetId = 156,
kOperandTypeVariableDeclaration = 158,
+ kOperandTypeCollection = 159,
kOperandTypeFunction = 160
};
const char *operandTypeToStr(OperandType type);
diff --git a/engines/mediastation/mediascript/variable.cpp b/engines/mediastation/mediascript/variable.cpp
index afc375eb59f..bc9be29cc47 100644
--- a/engines/mediastation/mediascript/variable.cpp
+++ b/engines/mediastation/mediascript/variable.cpp
@@ -28,21 +28,49 @@
namespace MediaStation {
+Operand Collection::callMethod(BuiltInMethod method, Common::Array<Operand> &args) {
+ switch (method) {
+ case kIsEmptyMethod: {
+ // This is a built-in method that checks if a collection is empty.
+ // We can just check the size of the collection.
+ Operand returnValue(kOperandTypeLiteral1);
+ returnValue.putInteger(empty());
+ return returnValue;
+ }
+
+ case kAppendMethod: {
+ for (Operand arg : args) {
+ push_back(arg);
+ }
+ return Operand();
+ }
+
+ case kDeleteFirstMethod: {
+ Operand returnValue = remove_at(0);
+ return returnValue;
+ }
+
+ default: {
+ error("Collection::callMethod(): Attempt to call unimplemented method %s (%d)", builtInMethodToStr(method), method);
+ }
+ }
+}
+
Variable::Variable(Chunk &chunk, bool readId) {
if (readId) {
_id = Datum(chunk).u.i;
}
_type = static_cast<VariableType>(Datum(chunk).u.i);
- debugC(5, kDebugLoading, "Variable::Variable(): id = 0x%x, type %s (%d) (@0x%llx)",
+ debugC(1, kDebugLoading, "Variable::Variable(): id = %d, type %s (%d) (@0x%llx)",
_id, variableTypeToStr(_type), static_cast<uint>(_type), static_cast<long long int>(chunk.pos()));
switch ((VariableType)_type) {
case kVariableTypeCollection: {
uint totalItems = Datum(chunk).u.i;
- _value.collection = new Common::Array<Variable *>;
+ _value.collection = new Collection;
for (uint i = 0; i < totalItems; i++) {
debugC(7, kDebugLoading, "Variable::Variable(): %s: Value %d of %d", variableTypeToStr(_type), i, totalItems);
- Variable *variableDeclaration = new Variable(chunk, readId = false);
- _value.collection->push_back(variableDeclaration);
+ Variable variable = Variable(chunk, readId = false);
+ _value.collection->push_back(variable.getValue());
}
break;
}
@@ -97,9 +125,7 @@ Variable::Variable(Chunk &chunk, bool readId) {
Variable::~Variable() {
switch (_type) {
case kVariableTypeCollection: {
- for (Variable *variable : *(_value.collection)) {
- delete variable;
- }
+ _value.collection->clear();
delete _value.collection;
break;
}
@@ -122,9 +148,9 @@ Operand Variable::getValue() {
}
case kVariableTypeCollection: {
- // TODO: Determine if any scripts actually try to do this.
- error("Variable::getValue(): Returning a collection is not implemented");
- break;
+ Operand returnValue(kOperandTypeCollection);
+ returnValue.putCollection(_value.collection);
+ return returnValue;
}
case kVariableTypeString: {
@@ -214,20 +240,4 @@ void Variable::putValue(Operand value) {
}
}
-Operand Variable::callMethod(BuiltInMethod method, Common::Array<Operand> &args) {
- switch (_type) {
- case kVariableTypeCollection: {
- // TODO: This is just a warning for now so we can get past the
- // IBM/Crayola opening screen.
- warning("Variable::callMethod(): Calling method on a collection not implemented yet");
- return Operand();
- break;
- }
-
- default: {
- error("Variable::callMethod(): Calling method on unknown variable type %s (%d)", variableTypeToStr(_type), static_cast<uint>(_type));
- }
- }
-}
-
} // End of namespace MediaStation
diff --git a/engines/mediastation/mediascript/variable.h b/engines/mediastation/mediascript/variable.h
index 2163970a548..691f011f70b 100644
--- a/engines/mediastation/mediascript/variable.h
+++ b/engines/mediastation/mediascript/variable.h
@@ -33,13 +33,18 @@ namespace MediaStation {
class Operand;
+class Collection : public Common::Array<Operand> {
+public:
+ Operand callMethod(BuiltInMethod method, Common::Array<Operand> &args);
+};
+
class Variable {
public:
uint32 _id = 0;
VariableType _type = kVariableTypeEmpty;
union {
Common::String *string;
- Common::Array<Variable *> *collection;
+ Collection *collection;
bool b;
int i;
double d;
@@ -51,7 +56,6 @@ public:
Operand getValue();
void putValue(Operand value);
- Operand callMethod(BuiltInMethod method, Common::Array<Operand> &args);
~Variable();
};
Commit: 19eb4a84a65b16e6d1f0d80561156330a52221da
https://github.com/scummvm/scummvm/commit/19eb4a84a65b16e6d1f0d80561156330a52221da
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-01-26T14:29:06-05:00
Commit Message:
MEDIASTATION: Centralize time event hander processing
Changed paths:
engines/mediastation/asset.cpp
engines/mediastation/asset.h
engines/mediastation/assets/movie.cpp
engines/mediastation/assets/movie.h
engines/mediastation/assets/timer.cpp
diff --git a/engines/mediastation/asset.cpp b/engines/mediastation/asset.cpp
index 9949df5a4a2..f28e39d94c2 100644
--- a/engines/mediastation/asset.cpp
+++ b/engines/mediastation/asset.cpp
@@ -45,6 +45,27 @@ int Asset::zIndex() const {
return _header->_zIndex;
}
+void Asset::processTimeEventHandlers() {
+ if (!_isActive) {
+ warning("Asset::processTimeEventHandlers(): Attempted to process time event handlers while asset %d is not playing", _header->_id);
+ return;
+ }
+
+ // TODO: Replace with a queue.
+ uint currentTime = g_system->getMillis();
+ for (EventHandler *timeEvent : _header->_timeHandlers) {
+ double timeEventInFractionalSeconds = timeEvent->_argumentValue.u.f;
+ uint timeEventInMilliseconds = timeEventInFractionalSeconds * 1000;
+ bool timeEventAlreadyProcessed = timeEventInMilliseconds < _lastProcessedTime;
+ bool timeEventNeedsToBeProcessed = timeEventInMilliseconds <= currentTime - _startTime;
+ if (!timeEventAlreadyProcessed && timeEventNeedsToBeProcessed) {
+ debugC(5, kDebugScript, "Asset::processTimeEventHandlers(): Running On Time handler for time %d ms", timeEventInMilliseconds);
+ timeEvent->execute(_header->_id);
+ }
+ }
+ _lastProcessedTime = currentTime - _startTime;
+}
+
void Asset::runEventHandlerIfExists(EventType eventType) {
EventHandler *eventHandler = _header->_eventHandlers.getValOrDefault(eventType);
if (eventHandler != nullptr) {
diff --git a/engines/mediastation/asset.h b/engines/mediastation/asset.h
index 0576152f540..f746618d666 100644
--- a/engines/mediastation/asset.h
+++ b/engines/mediastation/asset.h
@@ -24,6 +24,7 @@
#include "common/keyboard.h"
+#include "mediastation/mediastation.h"
#include "mediastation/subfile.h"
#include "mediastation/chunk.h"
#include "mediastation/mediascript/scriptconstants.h"
@@ -57,6 +58,7 @@ public:
virtual void readChunk(Chunk &chunk);
virtual void readSubfile(Subfile &subfile, Chunk &chunk);
+ void processTimeEventHandlers();
void runEventHandlerIfExists(EventType eventType);
void runKeyDownEventHandlerIfExists(Common::KeyState keyState);
diff --git a/engines/mediastation/assets/movie.cpp b/engines/mediastation/assets/movie.cpp
index d8f7729992e..96fc0e24760 100644
--- a/engines/mediastation/assets/movie.cpp
+++ b/engines/mediastation/assets/movie.cpp
@@ -244,26 +244,6 @@ void Movie::process() {
drawNextFrame();
}
-void Movie::processTimeEventHandlers() {
- if (!_isActive) {
- warning("Movie::processTimeEventHandlers(): Attempted to process time event handlers while movie is not playing");
- return;
- }
-
- uint currentTime = g_system->getMillis();
- for (EventHandler *timeEvent : _header->_timeHandlers) {
- double timeEventInFractionalSeconds = timeEvent->_argumentValue.u.f;
- uint timeEventInMilliseconds = timeEventInFractionalSeconds * 1000;
- bool timeEventAlreadyProcessed = timeEventInMilliseconds < _lastProcessedTime;
- bool timeEventNeedsToBeProcessed = timeEventInMilliseconds <= currentTime - _startTime;
- if (!timeEventAlreadyProcessed && timeEventNeedsToBeProcessed) {
- debugC(5, kDebugScript, "Movie::processTimeEventHandlers(): Running On Time handler for movie time %d ms (real movie time: %d ms)", timeEventInMilliseconds, currentTime - _startTime);
- timeEvent->execute(_header->_id);
- }
- }
- _lastProcessedTime = currentTime - _startTime;
-}
-
bool Movie::drawNextFrame() {
// TODO: We'll need to support persistent frames in movies too. Do movies
// have the same distinction between spatialShow and timePlay that sprites
diff --git a/engines/mediastation/assets/movie.h b/engines/mediastation/assets/movie.h
index ec57c473ea6..f81f40b44c2 100644
--- a/engines/mediastation/assets/movie.h
+++ b/engines/mediastation/assets/movie.h
@@ -115,7 +115,6 @@ private:
// Internal helper functions.
bool drawNextFrame();
- void processTimeEventHandlers();
};
} // End of namespace MediaStation
diff --git a/engines/mediastation/assets/timer.cpp b/engines/mediastation/assets/timer.cpp
index dda6eb6b3f3..5af7507b856 100644
--- a/engines/mediastation/assets/timer.cpp
+++ b/engines/mediastation/assets/timer.cpp
@@ -87,27 +87,7 @@ void Timer::timeStop() {
}
void Timer::process() {
- if (!_isActive) {
- error("Timer::processTimeEventHandlers(): Attempted to process time event handlers while not playing");
- return;
- }
-
- uint currentTime = g_system->getMillis();
- //uint movieTime = currentTime - _startTime;
- debugC(7, kDebugScript, "** Timer %d: ON TIME Event Handlers **", _header->_id);
- for (EventHandler *timeEvent : _header->_timeHandlers) {
- double timeEventInFractionalSeconds = timeEvent->_argumentValue.u.f;
- uint timeEventInMilliseconds = timeEventInFractionalSeconds * 1000;
- bool timeEventAlreadyProcessed = timeEventInMilliseconds < _lastProcessedTime;
- bool timeEventNeedsToBeProcessed = timeEventInMilliseconds <= currentTime - _startTime;
- if (!timeEventAlreadyProcessed && timeEventNeedsToBeProcessed) {
- // TODO: What happens when we try re-run the timer when itʻs already
- // running? Seems like this would cause re-entrancy issues.
- timeEvent->execute(_header->_id);
- }
- }
- debugC(7, kDebugScript, "** Timer %d: End ON TIME Event Handlers **", _header->_id);
- _lastProcessedTime = currentTime - _startTime;
+ processTimeEventHandlers();
}
} // End of namespace MediaStation
Commit: ca602eabc25c9bca1d93e4c42ca2d0970a179dc3
https://github.com/scummvm/scummvm/commit/ca602eabc25c9bca1d93e4c42ca2d0970a179dc3
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-01-26T14:29:06-05:00
Commit Message:
MEDIASTATION: Clean up Sound asset
Changed paths:
engines/mediastation/assets/sound.cpp
engines/mediastation/assets/sound.h
diff --git a/engines/mediastation/assets/sound.cpp b/engines/mediastation/assets/sound.cpp
index 43037a6e1d9..f7170b01b82 100644
--- a/engines/mediastation/assets/sound.cpp
+++ b/engines/mediastation/assets/sound.cpp
@@ -19,10 +19,12 @@
*
*/
+#include "audio/decoders/raw.h"
#include "audio/decoders/adpcm.h"
#include "mediastation/debugchannels.h"
#include "mediastation/assets/sound.h"
+#include "mediastation/mediastation.h"
namespace MediaStation {
@@ -33,50 +35,67 @@ Sound::Sound(AssetHeader *header) : Asset(header) {
}
Sound::~Sound() {
- delete _samples;
- _samples = nullptr;
- //for (Audio::SeekableAudioStream *stream : _streams) {
- // delete stream;
- //}
+ for (Audio::SeekableAudioStream *stream : _streams) {
+ delete stream;
+ }
+ _streams.clear();
+}
+
+void Sound::process() {
+ processTimeEventHandlers();
+ if (!g_engine->_mixer->isSoundHandleActive(_handle)) {
+ _isActive = false;
+ _startTime = 0;
+ _lastProcessedTime = 0;
+ _handle = Audio::SoundHandle();
+
+ runEventHandlerIfExists(kSoundEndEvent);
+ }
}
Operand Sound::callMethod(BuiltInMethod methodId, Common::Array<Operand> &args) {
switch (methodId) {
- default: {
- error("Got unimplemented method ID %d", methodId);
+ case kTimePlayMethod: {
+ assert(args.empty());
+ timePlay();
+ return Operand();
}
+
+ case kTimeStopMethod: {
+ assert(args.empty());
+ timeStop();
+ return Operand();
}
-}
-void Sound::process() {
- // TODO: Process more playing.
+ default: {
+ error("Sound::callMethod(): Got unimplemented method %s (%d)", builtInMethodToStr(methodId), methodId);
+ }
+ }
}
void Sound::readChunk(Chunk &chunk) {
- // TODO: Can we read the chunk directly into the audio stream?
- debugC(5, kDebugLoading, "Sound::readChunk(): (encoding = 0x%x) Reading audio chunk (@0x%llx)", static_cast<uint>(_encoding), static_cast<long long int>(chunk.pos()));
byte *buffer = (byte *)malloc(chunk._length);
chunk.read((void *)buffer, chunk._length);
-
- switch (_encoding) {
- case SoundEncoding::PCM_S16LE_MONO_22050: {
- // Audio::SeekableAudioStream *stream = Audio::makeRawStream(buffer, chunk.length, Sound::RATE, Sound::FLAGS, DisposeAfterUse::NO);
- //_streams.push_back(stream);
+ Audio::SeekableAudioStream *stream = nullptr;
+ switch (_header->_soundEncoding) {
+ case SoundEncoding::PCM_S16LE_MONO_22050:
+ stream = Audio::makeRawStream(buffer, chunk._length, 22050, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN, DisposeAfterUse::NO);
break;
- }
- case SoundEncoding::IMA_ADPCM_S16LE_MONO_22050: {
- // TODO: Support ADPCM decoding.
- // Audio::SeekableAudioStream *stream = nullptr; // Audio::makeADPCMStream(buffer, chunk.length, DisposeAfterUse::NO, Audio::ADPCMType::kADPCMMSIma, Sound::RATE, 1, 4);
- //_streams.push_back(stream);
+ case SoundEncoding::IMA_ADPCM_S16LE_MONO_22050:
+ // TODO: The interface here is different. We can't pass in the
+ // buffers directly. We have to make a stream first.
+ // stream = Audio::makeADPCMStream(buffer, chunk.length,
+ // DisposeAfterUse::NO, Audio::ADPCMType::kADPCMMSIma, 22050, 1,
+ // 4);
+ warning("Sound::readSubfile(): ADPCM decoding not implemented yet");
+ chunk.skip(chunk.bytesRemaining());
break;
- }
- default: {
- error("Sound::readChunk(): Unknown audio encoding 0x%x", static_cast<uint>(_encoding));
- break;
- }
+ default:
+ error("Sound::readChunk(): Unknown audio encoding 0x%x", static_cast<uint>(_header->_soundEncoding));
}
+ _streams.push_back(stream);
debugC(5, kDebugLoading, "Sound::readChunk(): Finished reading audio chunk (@0x%llx)", static_cast<long long int>(chunk.pos()));
}
@@ -96,4 +115,45 @@ void Sound::readSubfile(Subfile &subfile, Chunk &chunk) {
}
}
+void Sound::timePlay() {
+ if (_isActive) {
+ warning("Sound::timePlay(): Attempt to play a sound that is already playing");
+ return;
+ }
+ _isActive = true;
+ g_engine->addPlayingAsset(this);
+
+ _startTime = g_system->getMillis();
+ _lastProcessedTime = 0;
+ _handle = Audio::SoundHandle();
+
+ runEventHandlerIfExists(kSoundBeginEvent);
+
+ if (!_streams.empty()) {
+ Audio::QueuingAudioStream *audio = Audio::makeQueuingAudioStream(22050, false);
+ for (Audio::SeekableAudioStream *stream : _streams) {
+ stream->rewind();
+ audio->queueAudioStream(stream, DisposeAfterUse::NO);
+ }
+ g_engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, audio, -1, Audio::Mixer::kMaxChannelVolume, DisposeAfterUse::YES);
+ audio->finish();
+ }
+}
+
+void Sound::timeStop() {
+ if (!_isActive) {
+ warning("Sound::timeStop(): Attempt to stop a sound that isn't playing");
+ return;
+ }
+
+ _isActive = false;
+ _startTime = 0;
+ _lastProcessedTime = 0;
+
+ g_engine->_mixer->stopHandle(_handle);
+ _handle = Audio::SoundHandle();
+
+ runEventHandlerIfExists(kSoundStoppedEvent);
}
+
+} // End of namespace MediaStation
diff --git a/engines/mediastation/assets/sound.h b/engines/mediastation/assets/sound.h
index edbfa36bb4e..adba464e98c 100644
--- a/engines/mediastation/assets/sound.h
+++ b/engines/mediastation/assets/sound.h
@@ -19,12 +19,10 @@
*
*/
-#ifndef MEDIASTATION_SOUND_H
-#define MEDIASTATION_SOUND_H
+#ifndef MEDIASTATION_ASSETS_SOUND_H
+#define MEDIASTATION_ASSETS_SOUND_H
-#include "audio/mixer.h"
#include "audio/audiostream.h"
-#include "audio/decoders/raw.h"
#include "mediastation/asset.h"
#include "mediastation/chunk.h"
@@ -37,12 +35,7 @@ namespace MediaStation {
class Sound : public Asset {
public:
- // For standalone Sound assets.
Sound(AssetHeader *header);
-
- // For sounds that are part of a movie.
- // TODO: Since these aren't Assets they should be handled elsewhere.
- //Sound(AssetHeader::SoundEncoding encoding);
~Sound();
virtual Operand callMethod(BuiltInMethod methodId, Common::Array<Operand> &args) override;
@@ -51,14 +44,14 @@ public:
virtual void readChunk(Chunk& chunk) override;
virtual void readSubfile(Subfile &subFile, Chunk &chunk) override;
- // All Media Station audio is signed 16-bit little-endian mono at 22050 Hz.
- // Some defaults must be overridden in the flags.
- static const uint RATE = 22050;
- static const byte FLAGS = Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN;
-
private:
SoundEncoding _encoding;
- byte *_samples = nullptr;
+ Audio::SoundHandle _handle;
+ Common::Array<Audio::SeekableAudioStream *> _streams;
+
+ // Script method implementations
+ void timePlay();
+ void timeStop();
};
} // End of namespace MediaStation
Commit: 8e37b9a59695c15ddac5e22334be5f769f981f9e
https://github.com/scummvm/scummvm/commit/8e37b9a59695c15ddac5e22334be5f769f981f9e
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-01-26T14:29:06-05:00
Commit Message:
MEDIASTATION: Implement hotspot enter/exit handling logic
Changed paths:
engines/mediastation/assets/hotspot.cpp
engines/mediastation/assets/hotspot.h
engines/mediastation/mediastation.cpp
engines/mediastation/mediastation.h
diff --git a/engines/mediastation/assets/hotspot.cpp b/engines/mediastation/assets/hotspot.cpp
index c06cf852cb2..7bc08218d22 100644
--- a/engines/mediastation/assets/hotspot.cpp
+++ b/engines/mediastation/assets/hotspot.cpp
@@ -20,6 +20,7 @@
*/
#include "mediastation/assets/hotspot.h"
+#include "mediastation/mediastation.h"
namespace MediaStation {
@@ -29,11 +30,50 @@ Hotspot::Hotspot(AssetHeader *header) : Asset(header) {
}
}
+bool Hotspot::isInside(const Common::Point &pointToCheck) {
+ // No sense checking the polygon if we're not even in the bbox.
+ if (!_header->_boundingBox->contains(pointToCheck)) {
+ return false;
+ }
+
+ // We're in the bbox, but there might not be a polygon to check.
+ if (_header->_mouseActiveArea.empty()) {
+ return true;
+ }
+
+ // Polygon intersection code adapted from HADESCH engine, might need more
+ // refinement once more testing is possible.
+ Common::Point point = pointToCheck - Common::Point(_header->_boundingBox->left, _header->_boundingBox->top);
+ int rcross = 0; // Number of right-side overlaps
+
+ // Each edge is checked whether it cuts the outgoing stream from the point
+ Common::Array<Common::Point *> _polygon = _header->_mouseActiveArea;
+ for (unsigned i = 0; i < _polygon.size(); i++) {
+ const Common::Point &edgeStart = *_polygon[i];
+ const Common::Point &edgeEnd = *_polygon[(i + 1) % _polygon.size()];
+
+ // A vertex is a point? Then it lies on one edge of the polygon
+ if (point == edgeStart)
+ return true;
+
+ if ((edgeStart.y > point.y) != (edgeEnd.y > point.y)) {
+ int term1 = (edgeStart.x - point.x) * (edgeEnd.y - point.y) - (edgeEnd.x - point.x) * (edgeStart.y - point.y);
+ int term2 = (edgeEnd.y - point.y) - (edgeStart.y - edgeEnd.y);
+ if ((term1 > 0) == (term2 >= 0))
+ rcross++;
+ }
+ }
+
+ // The point is strictly inside the polygon if and only if the number of overlaps is odd
+ return ((rcross % 2) == 1);
+}
+
Operand Hotspot::callMethod(BuiltInMethod methodId, Common::Array<Operand> &args) {
switch (methodId) {
case kMouseActivateMethod: {
assert(args.empty());
_isActive = true;
+ g_engine->addPlayingAsset(this);
return Operand();
}
@@ -44,7 +84,7 @@ Operand Hotspot::callMethod(BuiltInMethod methodId, Common::Array<Operand> &args
}
default: {
- error("Got unimplemented method ID %d", methodId);
+ error("Hotspot::callMethod(): Got unimplemented method ID %d", methodId);
}
}
}
diff --git a/engines/mediastation/assets/hotspot.h b/engines/mediastation/assets/hotspot.h
index 6818ffd000e..d72b0237555 100644
--- a/engines/mediastation/assets/hotspot.h
+++ b/engines/mediastation/assets/hotspot.h
@@ -34,6 +34,8 @@ public:
Hotspot(AssetHeader *header);
virtual ~Hotspot() override = default;
+ bool isInside(const Common::Point &pointToCheck);
+
virtual Operand callMethod(BuiltInMethod methodId, Common::Array<Operand> &args) override;
};
diff --git a/engines/mediastation/mediastation.cpp b/engines/mediastation/mediastation.cpp
index 4f84cac81ba..966cb1b1756 100644
--- a/engines/mediastation/mediastation.cpp
+++ b/engines/mediastation/mediastation.cpp
@@ -28,6 +28,7 @@
#include "mediastation/boot.h"
#include "mediastation/context.h"
#include "mediastation/asset.h"
+#include "mediastation/assets/hotspot.h"
#include "mediastation/assets/movie.h"
#include "mediastation/mediascript/scriptconstants.h"
@@ -186,9 +187,31 @@ void MediaStationEngine::processEvents() {
return;
}
+ case Common::EVENT_MOUSEMOVE: {
+ Asset *hotspot = findAssetToAcceptMouseEvents(e.mouse);
+ if (hotspot != nullptr) {
+ if (_currentHotspot == nullptr) {
+ _currentHotspot = hotspot;
+ debugC(5, kDebugEvents, "EVENT_MOUSEMOVE (%d, %d): Entered hotspot %d", e.mouse.x, e.mouse.y, hotspot->getHeader()->_id);
+ hotspot->runEventHandlerIfExists(kMouseEnteredEvent);
+ } else if (_currentHotspot == hotspot) {
+ // We are still in the same hotspot.
+ } else {
+ _currentHotspot->runEventHandlerIfExists(kMouseExitedEvent);
+ _currentHotspot = hotspot;
+ debugC(5, kDebugEvents, "EVENT_MOUSEMOVE (%d, %d): Exited hotspot %d", e.mouse.x, e.mouse.y, hotspot->getHeader()->_id);
+ hotspot->runEventHandlerIfExists(kMouseEnteredEvent);
+ }
+ debugC(5, kDebugEvents, "EVENT_MOUSEMOVE (%d, %d): Sent to hotspot %d", e.mouse.x, e.mouse.y, hotspot->getHeader()->_id);
+ hotspot->runEventHandlerIfExists(kMouseMovedEvent);
+ } else {
+ _currentHotspot = nullptr;
+ }
+ break;
+ }
+
case Common::EVENT_KEYDOWN: {
- // TODO: Reading the current mouse position for hotspots might not
- // be right, need to verify.
+ // Even though this is a keydown event, we need to look at the mouse position.
Common::Point mousePos = g_system->getEventManager()->getMousePos();
Asset *hotspot = findAssetToAcceptMouseEvents(mousePos);
if (hotspot != nullptr) {
@@ -381,19 +404,8 @@ Asset *MediaStationEngine::findAssetToAcceptMouseEvents(Common::Point point) {
int lowestZIndex = INT_MAX;
for (Asset *asset : _assetsPlaying) {
- // TODO: Currently only hotspots are found, but other asset types can
- // likely get mouse events too.
if (asset->type() == kAssetTypeHotspot) {
- Common::Rect *boundingBox = asset->getHeader()->_boundingBox;
- if (boundingBox == nullptr) {
- error("Hotspot %d has no bounding box", asset->getHeader()->_id);
- }
-
- if (!asset->isActive()) {
- continue;
- }
-
- if (boundingBox->contains(point)) {
+ if (asset->isActive() && static_cast<Hotspot *>(asset)->isInside(point)) {
if (asset->zIndex() < lowestZIndex) {
lowestZIndex = asset->zIndex();
intersectingAsset = asset;
diff --git a/engines/mediastation/mediastation.h b/engines/mediastation/mediastation.h
index 87a1955d0ce..c4a1ae2abc0 100644
--- a/engines/mediastation/mediastation.h
+++ b/engines/mediastation/mediastation.h
@@ -103,6 +103,7 @@ private:
Boot *_boot = nullptr;
Common::Array<Asset *> _assetsPlaying;
Common::HashMap<uint, Context *> _loadedContexts;
+ Asset *_currentHotspot = nullptr;
Context *loadContext(uint32 contextId);
void setPaletteFromHeader(AssetHeader *header);
Commit: 142460fcb2c7c63f522af6a54af6531cae62dff6
https://github.com/scummvm/scummvm/commit/142460fcb2c7c63f522af6a54af6531cae62dff6
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-01-26T14:29:06-05:00
Commit Message:
MEDIASTATION: Stub out more script methods
Changed paths:
engines/mediastation/assets/image.cpp
engines/mediastation/assets/movie.cpp
engines/mediastation/assets/path.cpp
engines/mediastation/assets/sprite.cpp
diff --git a/engines/mediastation/assets/image.cpp b/engines/mediastation/assets/image.cpp
index edaa22a75c9..b0d16c4a272 100644
--- a/engines/mediastation/assets/image.cpp
+++ b/engines/mediastation/assets/image.cpp
@@ -21,6 +21,7 @@
#include "mediastation/mediastation.h"
#include "mediastation/assets/image.h"
+#include "mediastation/debugchannels.h"
namespace MediaStation {
@@ -52,6 +53,13 @@ Operand Image::callMethod(BuiltInMethod methodId, Common::Array<Operand> &args)
break;
}
+ case kSetDissolveFactorMethod: {
+ assert(args.size() == 1);
+ warning("Image::callMethod(): setDissolveFactor not implemented yet");
+ return Operand();
+ }
+
+
default: {
error("Image::callMethod(): Got unimplemented method ID %d", methodId);
}
diff --git a/engines/mediastation/assets/movie.cpp b/engines/mediastation/assets/movie.cpp
index 96fc0e24760..2da51c9964c 100644
--- a/engines/mediastation/assets/movie.cpp
+++ b/engines/mediastation/assets/movie.cpp
@@ -186,6 +186,24 @@ Operand Movie::callMethod(BuiltInMethod methodId, Common::Array<Operand> &args)
return Operand();
}
+ case kSpatialShowMethod: {
+ assert(args.empty());
+ warning("Movie::callMethod(): spatialShow not implemented");
+ return Operand();
+ }
+
+ case kTimeStopMethod: {
+ assert(args.empty());
+ timeStop();
+ return Operand();
+ }
+
+ case kSpatialHideMethod: {
+ assert(args.empty());
+ warning("Movie::callMethod(): spatialHide not implemented");
+ return Operand();
+ }
+
default: {
error("Got unimplemented method ID %d", methodId);
}
diff --git a/engines/mediastation/assets/path.cpp b/engines/mediastation/assets/path.cpp
index e03f472a7ff..9a8dae48747 100644
--- a/engines/mediastation/assets/path.cpp
+++ b/engines/mediastation/assets/path.cpp
@@ -50,6 +50,12 @@ Operand Path::callMethod(BuiltInMethod methodId, Common::Array<Operand> &args) {
return returnValue;
}
+ case kSetDissolveFactorMethod: {
+ assert(args.size() == 1);
+ warning("Path::callMethod(): setDissolveFactor not implemented yet");
+ return Operand();
+ }
+
default: {
error("Got unimplemented method ID %d", methodId);
}
diff --git a/engines/mediastation/assets/sprite.cpp b/engines/mediastation/assets/sprite.cpp
index 201987b6dea..f3aae059dae 100644
--- a/engines/mediastation/assets/sprite.cpp
+++ b/engines/mediastation/assets/sprite.cpp
@@ -87,6 +87,18 @@ Operand Sprite::callMethod(BuiltInMethod methodId, Common::Array<Operand> &args)
return Operand();
}
+ case kSpatialHideMethod: {
+ assert(args.empty());
+ _isActive = false;
+ return Operand();
+ }
+
+ case kTimeStopMethod: {
+ assert(args.empty());
+ _isActive = false;
+ return Operand();
+ }
+
case kTimePlayMethod: {
assert(args.size() == 0);
timePlay();
More information about the Scummvm-git-logs
mailing list