diff options
194 files changed, 9690 insertions, 1970 deletions
@@ -62,6 +62,9 @@ For a more comprehensive changelog of the latest experimental code, see: - Fixed taskbar support on Windows 10 onwards. - Fixed keymapping for non-QWERTY keyboards. + Linux port: + - Added basic support for the snap packaging system. + 1.8.1 (2016-05-25) New ports: - Added Nintendo 3DS port. diff --git a/common/debug.cpp b/common/debug.cpp index ce34a00445..c61fc63dea 100644 --- a/common/debug.cpp +++ b/common/debug.cpp @@ -120,6 +120,18 @@ bool DebugManager::isDebugChannelEnabled(uint32 channel) { } // End of namespace Common +bool debugLevelSet(int level) { + return level <= gDebugLevel; +} + +bool debugChannelSet(int level, uint32 debugChannels) { + if (gDebugLevel != 11) + if (level > gDebugLevel || !(DebugMan.isDebugChannelEnabled(debugChannels))) + return false; + + return true; +} + #ifndef DISABLE_TEXT_CONSOLE diff --git a/common/debug.h b/common/debug.h index 00bad81fa6..883a0bf29d 100644 --- a/common/debug.h +++ b/common/debug.h @@ -31,11 +31,10 @@ inline void debug(const char *s, ...) {} inline void debug(int level, const char *s, ...) {} inline void debugN(const char *s, ...) {} inline void debugN(int level, const char *s, ...) {} -inline void debugC(int level, uint32 engineChannel, const char *s, ...) {} -inline void debugC(uint32 engineChannel, const char *s, ...) {} -inline void debugCN(int level, uint32 engineChannel, const char *s, ...) {} -inline void debugCN(uint32 engineChannel, const char *s, ...) {} - +inline void debugC(int level, uint32 debugChannels, const char *s, ...) {} +inline void debugC(uint32 debugChannels, const char *s, ...) {} +inline void debugCN(int level, uint32 debugChannels, const char *s, ...) {} +inline void debugCN(uint32 debugChannels, const char *s, ...) {} #else @@ -111,6 +110,18 @@ void debugCN(uint32 debugChannels, const char *s, ...) GCC_PRINTF(2, 3); #endif /** + * Returns true if the debug level is set to the specified level + */ +bool debugLevelSet(int level); + +/** + * Returns true if the debug level and channel are active + * + * @see enableDebugChannel + */ +bool debugChannelSet(int level, uint32 debugChannels); + +/** * The debug level. Initially set to -1, indicating that no debug output * should be shown. Positive values usually imply an increasing number of * debug output shall be generated, the higher the value, the more verbose the diff --git a/devtools/create_project/xcode/create_project.xcodeproj/project.pbxproj b/devtools/create_project/xcode/create_project.xcodeproj/project.pbxproj index 4f06a5e469..55266a875f 100644 --- a/devtools/create_project/xcode/create_project.xcodeproj/project.pbxproj +++ b/devtools/create_project/xcode/create_project.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 076583601D660492006CBB9B /* cmake.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0765835E1D660492006CBB9B /* cmake.cpp */; }; F9A66C691396D4DF00CEE494 /* codeblocks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A66C5F1396D4DF00CEE494 /* codeblocks.cpp */; }; F9A66C6A1396D4DF00CEE494 /* create_project.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A66C621396D4DF00CEE494 /* create_project.cpp */; }; F9A66C6B1396D4DF00CEE494 /* msbuild.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9A66C651396D4DF00CEE494 /* msbuild.cpp */; }; @@ -41,6 +42,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0765835E1D660492006CBB9B /* cmake.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = cmake.cpp; path = ../cmake.cpp; sourceTree = "<group>"; }; + 0765835F1D660492006CBB9B /* cmake.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = cmake.h; path = ../cmake.h; sourceTree = "<group>"; }; F9A66C271396D36100CEE494 /* create_project */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = create_project; sourceTree = BUILT_PRODUCTS_DIR; }; F9A66C491396D47500CEE494 /* installer.vbs */ = {isa = PBXFileReference; lastKnownFileType = text; name = installer.vbs; path = ../scripts/installer.vbs; sourceTree = "<group>"; }; F9A66C4A1396D47500CEE494 /* postbuild.cmd */ = {isa = PBXFileReference; lastKnownFileType = text; name = postbuild.cmd; path = ../scripts/postbuild.cmd; sourceTree = "<group>"; }; @@ -76,6 +79,8 @@ F9A66C1C1396D36100CEE494 = { isa = PBXGroup; children = ( + 0765835E1D660492006CBB9B /* cmake.cpp */, + 0765835F1D660492006CBB9B /* cmake.h */, F9A66C861396E2F500CEE494 /* xcode.cpp */, F9A66C841396E2D800CEE494 /* xcode.h */, F9A66C6D1396D4E800CEE494 /* visualstudio.cpp */, @@ -169,6 +174,7 @@ F9A66C6A1396D4DF00CEE494 /* create_project.cpp in Sources */, F9A66C6B1396D4DF00CEE494 /* msbuild.cpp in Sources */, F9A66C6C1396D4DF00CEE494 /* msvc.cpp in Sources */, + 076583601D660492006CBB9B /* cmake.cpp in Sources */, F9A66C6F1396D4E800CEE494 /* visualstudio.cpp in Sources */, F9A66C871396E2F500CEE494 /* xcode.cpp in Sources */, ); diff --git a/engines/director/dib.cpp b/engines/director/dib.cpp index 8c54ba5363..04665e7d34 100644 --- a/engines/director/dib.cpp +++ b/engines/director/dib.cpp @@ -62,7 +62,7 @@ void DIBDecoder::loadPalette(Common::SeekableReadStream &stream) { uint16 steps = stream.size() / 6; uint16 index = (steps * 3) - 1; _paletteColorCount = steps; - _palette = new byte[index]; + _palette = new byte[index + 1]; for (uint8 i = 0; i < steps; i++) { _palette[index - 2] = stream.readByte(); diff --git a/engines/director/director.cpp b/engines/director/director.cpp index 469aeb80cb..ddc5adccdd 100644 --- a/engines/director/director.cpp +++ b/engines/director/director.cpp @@ -24,6 +24,7 @@ #include "common/config-manager.h" #include "common/debug.h" +#include "common/debug-channels.h" #include "common/scummsys.h" #include "common/error.h" #include "common/events.h" @@ -49,6 +50,9 @@ namespace Director { DirectorEngine::DirectorEngine(OSystem *syst, const DirectorGameDescription *gameDesc) : Engine(syst), _gameDescription(gameDesc), _rnd("director") { + DebugMan.addDebugChannel(kDebugLingoExec, "lingoexec", "Lingo Execution"); + DebugMan.addDebugChannel(kDebugLingoCompile, "lingocompile", "Lingo Compilation"); + if (!_mixer->isReady()) error("Sound initialization failed"); @@ -137,7 +141,8 @@ Common::HashMap<Common::String, Score *> DirectorEngine::loadMMMNames(Common::St Common::FSList movies; Common::HashMap<Common::String, Score *> nameMap; - directory.getChildren(movies, Common::FSNode::kListFilesOnly); + if (!directory.getChildren(movies, Common::FSNode::kListFilesOnly)) + return nameMap; if (!movies.empty()) { for (Common::FSList::const_iterator i = movies.begin(); i != movies.end(); ++i) { diff --git a/engines/director/lingo/lingo-builtins.cpp b/engines/director/lingo/lingo-builtins.cpp index d3545a7d3b..6738d4b707 100644 --- a/engines/director/lingo/lingo-builtins.cpp +++ b/engines/director/lingo/lingo-builtins.cpp @@ -32,25 +32,29 @@ static struct BuiltinProto { bool parens; } builtins[] = { // Math - { "abs", Lingo::b_abs, 1, 1, true }, // D2 - { "atan", Lingo::b_atan, 1, 1, true }, // D4 - { "cos", Lingo::b_cos, 1, 1, true }, // D4 - { "exp", Lingo::b_exp, 1, 1, true }, // D4 - { "float", Lingo::b_float, 1, 1, true }, // D4 - { "integer",Lingo::b_integer, 1, 1, true }, - { "integerp",Lingo::b_integerp, 1, 1, true }, - { "log", Lingo::b_log, 1, 1, true }, // D4 - { "pi", Lingo::b_pi, 0, 0, true }, // D4 - { "power", Lingo::b_power, 2, 2, true }, // D4 - { "random", Lingo::b_random, 1, 1, true }, // D2 - { "sin", Lingo::b_sin, 1, 1, true }, - { "sqrt", Lingo::b_sqrt, 1, 1, true }, // D2 - { "tan", Lingo::b_tan, 1, 1, true }, // D4 + { "abs", Lingo::b_abs, 1, 1, true }, // D2 + { "atan", Lingo::b_atan, 1, 1, true }, // D4 + { "cos", Lingo::b_cos, 1, 1, true }, // D4 + { "exp", Lingo::b_exp, 1, 1, true }, // D4 + { "float", Lingo::b_float, 1, 1, true }, // D4 + { "integer", Lingo::b_integer, 1, 1, true }, + { "integerp", Lingo::b_integerp, 1, 1, true }, + { "log", Lingo::b_log, 1, 1, true }, // D4 + { "pi", Lingo::b_pi, 0, 0, true }, // D4 + { "power", Lingo::b_power, 2, 2, true }, // D4 + { "random", Lingo::b_random, 1, 1, true }, // D2 + { "sin", Lingo::b_sin, 1, 1, true }, + { "sqrt", Lingo::b_sqrt, 1, 1, true }, // D2 + { "tan", Lingo::b_tan, 1, 1, true }, // D4 // String - { "chars", Lingo::b_chars, 3, 3, true }, // D2 - { "charToNum", Lingo::b_charToNum, 1, 1, true }, // D2 - { "length", Lingo::b_length, 1, 1, true }, // D2 - { "string", Lingo::b_string, 1, 1, true }, // D2 + { "chars", Lingo::b_chars, 3, 3, true }, // D2 + { "charToNum", Lingo::b_charToNum, 1, 1, true }, // D2 + { "length", Lingo::b_length, 1, 1, true }, // D2 + { "numToChar", Lingo::b_numToChar, 1, 1, true }, // D2 + { "offset", Lingo::b_offset, 2, 2, true }, // D2 + { "string", Lingo::b_string, 1, 1, true }, // D2 + { "stringp", Lingo::b_stringp, 1, 1, true }, // D2 + { "value", Lingo::b_value, 1, 1, true }, // D2 // Files { "closeDA", Lingo::b_closeDA, 0, 0, false }, // D2 { "closeResFile", Lingo::b_closeResFile, 0, 1, false }, // D2 @@ -86,8 +90,10 @@ static struct BuiltinProto { { "ilk", Lingo::b_ilk, 1, 2, true }, // D4 // put // D2 // set // D2 + { "objectp", Lingo::b_objectp, 1, 1, true }, { "showGlobals", Lingo::b_showGlobals, 0, 0, false }, // D2 { "showLocals", Lingo::b_showLocals, 0, 0, false }, // D2 + { "symbolp", Lingo::b_symbolp, 1, 1, true }, // D2 // Score { "constrainH", Lingo::b_constrainH, 2, 2, true }, // D2 { "constrainV", Lingo::b_constrainV, 2, 2, true }, // D2 @@ -95,12 +101,14 @@ static struct BuiltinProto { // go // D2 { "installMenu", Lingo::b_installMenu, 1, 1, false }, // D2 { "label", Lingo::b_label, 1, 1, true }, // D2 + { "marker", Lingo::b_marker, 1, 1, true }, // D2 { "moveableSprite", Lingo::b_moveableSprite,0, 0, false }, // D2 { "puppetPalette", Lingo::b_puppetPalette, -1,0, false }, // D2 { "puppetSound", Lingo::b_puppetSound, -1,0, false }, // D2 { "puppetSprite", Lingo::b_puppetSprite, -1,0, false }, // D2 { "puppetTempo", Lingo::b_puppetTempo, 1, 1, false }, // D2 { "puppetTransition",Lingo::b_puppetTransition,-1,0, false },// D2 + { "rollOver", Lingo::b_rollOver, 1, 1, true }, // D2 { "spriteBox", Lingo::b_spriteBox, -1,0, false }, // D2 { "updateStage", Lingo::b_updateStage, 0, 0, false }, // D2 { "zoomBox", Lingo::b_zoomBox, -1,0, false }, // D2 @@ -136,6 +144,8 @@ void Lingo::initBuiltIns() { sym->u.bltin = blt->func; _handlers[blt->name] = sym; + + _functions[(void *)sym->u.s] = new FuncDesc(blt->name, ""); } } @@ -341,12 +351,48 @@ void Lingo::b_length(int nargs) { g_lingo->push(d); } +void Lingo::b_numToChar(int nargs) { + Datum d = g_lingo->pop(); + + d.toInt(); + + g_lingo->push(Datum((char)d.u.i)); +} + +void Lingo::b_offset(int nargs) { + Datum target = g_lingo->pop(); + Datum source = g_lingo->pop(); + + target.toString(); + source.toString(); + + warning("STUB: b_offset()"); + + g_lingo->push(Datum(0)); +} + void Lingo::b_string(int nargs) { Datum d = g_lingo->pop(); d.toString(); g_lingo->push(d); } +void Lingo::b_stringp(int nargs) { + Datum d = g_lingo->pop(); + int res = (d.type == STRING) ? 1 : 0; + d.toInt(); + d.u.i = res; + g_lingo->push(d); +} + +void Lingo::b_value(int nargs) { + Datum d = g_lingo->pop(); + d.toInt(); + warning("STUB: b_value()"); + g_lingo->push(d); +} + + /////////////////// // Files /////////////////// @@ -499,7 +545,7 @@ void Lingo::b_alert(int nargs) { d.toString(); - warning("STUB: b_alert"); + warning("STUB: b_alert(%s)", d.u.s->c_str()); delete d.u.s; } @@ -510,6 +556,14 @@ void Lingo::b_cursor(int nargs) { warning("STUB: b_cursor(%d)", d.u.i); } +void Lingo::b_objectp(int nargs) { + Datum d = g_lingo->pop(); + int res = (d.type == OBJECT) ? 1 : 0; + d.toInt(); + d.u.i = res; + g_lingo->push(d); +} + void Lingo::b_showGlobals(int nargs) { warning("STUB: b_showGlobals"); } @@ -518,6 +572,13 @@ void Lingo::b_showLocals(int nargs) { warning("STUB: b_showLocals"); } +void Lingo::b_symbolp(int nargs) { + Datum d = g_lingo->pop(); + int res = (d.type == SYMBOL) ? 1 : 0; + d.toInt(); + d.u.i = res; + g_lingo->push(d); +} /////////////////// @@ -564,6 +625,14 @@ void Lingo::b_label(int nargs) { g_lingo->push(Datum(0)); } +void Lingo::b_marker(int nargs) { + Datum d = g_lingo->pop(); + d.toInt(); + warning("STUB: b_marker(%d)", d.u.i); + + g_lingo->push(Datum(0)); +} + void Lingo::b_moveableSprite(int nargs) { Datum d = g_lingo->pop(); warning("STUB: b_moveableSprite(%d)", d.u.i); @@ -602,6 +671,13 @@ void Lingo::b_puppetTransition(int nargs) { g_lingo->dropStack(nargs); } +void Lingo::b_rollOver(int nargs) { + Datum d = g_lingo->pop(); + warning("STUB: b_puppetTempo(%d)", d.u.i); + + g_lingo->push(Datum(0)); +} + void Lingo::b_spriteBox(int nargs) { g_lingo->printStubWithArglist("b_spriteBox", nargs); @@ -720,10 +796,11 @@ void Lingo::factoryCall(Common::String &name, int nargs) { s = name + "-" + *method.u.s; + debugC(3, kDebugLingoExec, "Stack size before call: %d", _stack.size()); call(s, nargs); + debugC(3, kDebugLingoExec, "Stack size after call: %d", _stack.size()); - if (method.u.s->compareToIgnoreCase("mNew")) { - warning("Got mNew method"); + if (!method.u.s->compareToIgnoreCase("mNew")) { Datum d; d.type = OBJECT; diff --git a/engines/director/lingo/lingo-code.cpp b/engines/director/lingo/lingo-code.cpp index 6072977d07..8712f0990c 100644 --- a/engines/director/lingo/lingo-code.cpp +++ b/engines/director/lingo/lingo-code.cpp @@ -51,6 +51,69 @@ namespace Director { +static struct FuncDescr { + const inst func; + const char *name; + const char *args; +} funcDescr[] = { + { 0, "STOP", "" }, + { Lingo::c_xpop, "c_xpop", "" }, + { Lingo::c_printtop, "c_printtop", "" }, + { Lingo::c_constpush, "c_constpush", "i" }, + { Lingo::c_voidpush, "c_voidpush", "" }, + { Lingo::c_fconstpush, "c_fconstpush", "f" }, + { Lingo::c_stringpush, "c_stringpush", "s" }, + { Lingo::c_varpush, "c_varpush", "s" }, + { Lingo::c_assign, "c_assign", "" }, + { Lingo::c_eval, "c_eval", "s" }, + { Lingo::c_theentitypush,"c_theentitypush","ii" }, // entity, field + { Lingo::c_theentityassign,"c_theentityassign","ii" }, + { Lingo::c_swap, "c_swap", "" }, + { Lingo::c_add, "c_add", "" }, + { Lingo::c_sub, "c_sub", "" }, + { Lingo::c_mul, "c_mul", "" }, + { Lingo::c_div, "c_div", "" }, + { Lingo::c_negate, "c_negate", "" }, + { Lingo::c_ampersand, "c_ampersand", "" }, + { Lingo::c_concat, "c_concat", "" }, + { Lingo::c_contains, "c_contains", "" }, + { Lingo::c_starts, "c_starts", "" }, + { Lingo::c_intersects, "c_intersects", "" }, + { Lingo::c_within, "c_within", "" }, + { Lingo::c_and, "c_and", "" }, + { Lingo::c_or, "c_or", "" }, + { Lingo::c_not, "c_not", "" }, + { Lingo::c_eq, "c_eq", "" }, + { Lingo::c_neq, "c_neq", "" }, + { Lingo::c_gt, "c_gt", "" }, + { Lingo::c_lt, "c_lt", "" }, + { Lingo::c_ge, "c_ge", "" }, + { Lingo::c_le, "c_le", "" }, + { Lingo::c_repeatwhilecode,"c_repeatwhilecode","oo" }, + { Lingo::c_repeatwithcode,"c_repeatwithcode","ooooos" }, + { Lingo::c_ifcode, "c_ifcode", "oooi" }, + { Lingo::c_whencode, "c_whencode", "os" }, + { Lingo::c_goto, "c_goto", "" }, + { Lingo::c_gotoloop, "c_gotoloop", "" }, + { Lingo::c_gotonext, "c_gotonext", "" }, + { Lingo::c_gotoprevious,"c_gotoprevious","" }, + { Lingo::c_play, "c_play", "" }, + { Lingo::c_playdone, "c_playdone", "" }, + { Lingo::c_call, "c_call", "si" }, + { Lingo::c_procret, "c_procret", "" }, + { Lingo::c_global, "c_global", "s" }, + { Lingo::c_open, "c_open", "" }, + { 0, 0, 0 } +}; + +void Lingo::initFuncs() { + Symbol sym; + for (FuncDescr *fnc = funcDescr; fnc->name; fnc++) { + sym.u.func = fnc->func; + _functions[(void *)sym.u.s] = new FuncDesc(fnc->name, fnc->args); + } +} + void Lingo::push(Datum d) { _stack.push_back(d); } @@ -108,6 +171,9 @@ void Lingo::c_printtop(void) { case SYMBOL: warning("%s", d.type2str(true)); break; + case OBJECT: + warning("#%s", d.u.s->c_str()); + break; default: warning("--unknown--"); } @@ -201,8 +267,11 @@ void Lingo::c_assign() { delete d2.u.arr; } else if (d2.type == SYMBOL) { d1.u.sym->u.i = d2.u.i; + } else if (d2.type == OBJECT) { + d1.u.sym->u.s = d2.u.s; } else { warning("c_assign: unhandled type: %s", d2.type2str()); + d1.u.sym->u.s = d2.u.s; } d1.u.sym->type = d2.type; @@ -633,24 +702,38 @@ void Lingo::c_ifcode() { int end = READ_UINT32(&(*g_lingo->_currentScript)[savepc + 2]); int skipEnd = READ_UINT32(&(*g_lingo->_currentScript)[savepc + 3]); - debug(8, "executing cond (have to %s end)", skipEnd ? "skip" : "execute"); + debugC(8, kDebugLingoExec, "executing cond (have to %s end)", skipEnd ? "skip" : "execute"); g_lingo->execute(savepc + 4); /* condition */ d = g_lingo->pop(); if (d.toInt()) { - debug(8, "executing then"); + debugC(8, kDebugLingoExec, "executing then"); g_lingo->execute(then); } else if (elsep) { /* else part? */ - debug(8, "executing else"); + debugC(8, kDebugLingoExec, "executing else"); g_lingo->execute(elsep); } if (!g_lingo->_returning && !skipEnd) { g_lingo->_pc = end; /* next stmt */ - debug(8, "executing end"); - } else - debug(8, "Skipped end"); + debugC(8, kDebugLingoExec, "executing end"); + } else { + debugC(8, kDebugLingoExec, "Skipped end"); + } +} + +void Lingo::c_whencode() { + Datum d; + int start = g_lingo->_pc; + int end = READ_UINT32(&(*g_lingo->_currentScript)[start]); + Common::String eventname((char *)&(*g_lingo->_currentScript)[start]); + + start += g_lingo->calcStringAlignment(eventname.c_str()); + + warning("STUB: c_whencode([%5d][%5d], %s)", start, end, eventname.c_str()); + + g_lingo->_pc = end; } //************************ @@ -727,7 +810,7 @@ void Lingo::call(Common::String &name, int nargs) { if (!g_lingo->_handlers.contains(name)) { Symbol *s = g_lingo->lookupVar(name.c_str(), false); if (s && s->type == OBJECT) { - debug(3, "Dereferencing object reference: %s to %s", name.c_str(), s->u.s->c_str()); + debugC(3, kDebugLingoExec, "Dereferencing object reference: %s to %s", name.c_str(), s->u.s->c_str()); name = *s->u.s; } } @@ -781,6 +864,7 @@ void Lingo::call(Common::String &name, int nargs) { g_lingo->push(d); } + debugC(5, kDebugLingoExec, "Pushing frame %d", g_lingo->_callstack.size() + 1); CFrame *fp = new CFrame; fp->sp = sym; @@ -806,7 +890,10 @@ void Lingo::c_procret() { return; } + debugC(5, kDebugLingoExec, "Popping frame %d", g_lingo->_callstack.size() + 1); + CFrame *fp = g_lingo->_callstack.back(); + g_lingo->_callstack.pop_back(); g_lingo->_currentScript = fp->retscript; g_lingo->_pc = fp->retpc; diff --git a/engines/director/lingo/lingo-codegen.cpp b/engines/director/lingo/lingo-codegen.cpp index bf96802bb7..32ddea47ac 100644 --- a/engines/director/lingo/lingo-codegen.cpp +++ b/engines/director/lingo/lingo-codegen.cpp @@ -53,17 +53,77 @@ namespace Director { void Lingo::execute(int pc) { for(_pc = pc; (*_currentScript)[_pc] != STOP && !_returning;) { + Common::String instr = decodeInstruction(_pc); + + debugC(1, kDebugLingoExec, "[%3d]: %s", _pc, instr.c_str()); + + Common::String stack("Stack: "); for (uint i = 0; i < _stack.size(); i++) { - debugN(5, "%d ", _stack[i].u.i); + Datum d = _stack[i]; + d.toString(); + stack += Common::String::format("<%s> ", d.u.s->c_str()); } - debug(5, "%s", ""); + debugC(5, kDebugLingoExec, "%s", stack.c_str()); _pc++; (*((*_currentScript)[_pc - 1]))(); } } +Common::String Lingo::decodeInstruction(int pc, int *newPc) { + Symbol sym; + Common::String res; + + sym.u.func = (*_currentScript)[pc++]; + if (_functions.contains((void *)sym.u.s)) { + res = _functions[(void *)sym.u.s]->name; + const char *pars = _functions[(void *)sym.u.s]->proto; + inst i; + + while (*pars) { + switch (*pars++) { + case 'i': + { + i = (*_currentScript)[pc++]; + int v = READ_UINT32(&i); + + res += Common::String::format(" %d", v); + break; + } + case 'o': + { + i = (*_currentScript)[pc++]; + int v = READ_UINT32(&i); + + res += Common::String::format(" [%5d]", v); + break; + } + case 's': + { + char *s = (char *)&(*_currentScript)[pc]; + pc += calcStringAlignment(s); + + res += Common::String::format(" \"%s\"", s); + break; + } + default: + warning("decodeInstruction: Unknown parameter type: %c", pars[-1]); + } + + if (*pars) + res += ','; + } + } else { + res = "<unknown>"; + } + + if (newPc) + *newPc = pc; + + return res; +} + Symbol *Lingo::lookupVar(const char *name, bool create, bool putInGlobalList) { Symbol *sym; @@ -129,7 +189,7 @@ void Lingo::define(Common::String &name, int start, int nargs, Common::String *p if (prefix) name = *prefix + "-" + name; - debug(3, "define(\"%s\", %d, %d, %d)", name.c_str(), start, _currentScript->size() - 1, nargs); + debugC(3, kDebugLingoCompile, "define(\"%s\", %d, %d, %d)", name.c_str(), start, _currentScript->size() - 1, nargs); if (!_handlers.contains(name)) { // Create variable if it was not defined sym = new Symbol; @@ -221,7 +281,7 @@ int Lingo::codeFunc(Common::String *s, int numpar) { if (s->equalsIgnoreCase("me")) { if (!g_lingo->_currentFactory.empty()) { g_lingo->codeString(g_lingo->_currentFactory.c_str()); - debug(2, "Repaced 'me' with %s", g_lingo->_currentFactory.c_str()); + debugC(2, kDebugLingoCompile, "Replaced 'me' with %s", g_lingo->_currentFactory.c_str()); } else { warning("'me' out of factory method"); g_lingo->codeString(s->c_str()); diff --git a/engines/director/lingo/lingo-gr.cpp b/engines/director/lingo/lingo-gr.cpp index 63128058ed..be5b3eb571 100644 --- a/engines/director/lingo/lingo-gr.cpp +++ b/engines/director/lingo/lingo-gr.cpp @@ -486,18 +486,18 @@ union yyalloc #endif /* YYFINAL -- State number of the termination state. */ -#define YYFINAL 87 +#define YYFINAL 88 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 921 +#define YYLAST 931 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 83 /* YYNNTS -- Number of nonterminals. */ -#define YYNNTS 35 +#define YYNNTS 36 /* YYNRULES -- Number of rules. */ -#define YYNRULES 124 +#define YYNRULES 125 /* YYNRULES -- Number of states. */ -#define YYNSTATES 259 +#define YYNSTATES 261 /* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ #define YYUNDEFTOK 2 @@ -551,69 +551,69 @@ static const yytype_uint16 yyprhs[] = { 0, 0, 3, 7, 9, 12, 14, 15, 17, 19, 21, 23, 25, 30, 35, 40, 46, 51, 56, 62, - 64, 66, 68, 70, 79, 91, 104, 109, 118, 130, - 142, 149, 160, 171, 172, 176, 179, 181, 184, 186, - 193, 195, 201, 203, 207, 211, 214, 218, 220, 222, - 223, 224, 225, 228, 231, 233, 235, 237, 239, 244, - 246, 248, 251, 253, 257, 261, 265, 269, 273, 277, - 281, 285, 289, 293, 297, 300, 304, 308, 312, 316, - 319, 322, 326, 331, 336, 339, 341, 343, 345, 348, - 351, 354, 356, 359, 364, 367, 369, 373, 376, 379, - 382, 385, 389, 392, 395, 397, 401, 404, 407, 410, - 414, 417, 418, 427, 430, 431, 440, 441, 443, 447, - 452, 453, 457, 458, 460 + 64, 66, 68, 70, 79, 91, 104, 108, 117, 129, + 141, 148, 159, 170, 171, 175, 178, 180, 183, 185, + 192, 194, 200, 202, 206, 210, 213, 217, 219, 221, + 222, 223, 224, 227, 230, 234, 236, 238, 240, 242, + 247, 249, 251, 254, 256, 260, 264, 268, 272, 276, + 280, 284, 288, 292, 296, 300, 303, 307, 311, 315, + 319, 322, 325, 329, 334, 339, 342, 344, 346, 348, + 351, 354, 357, 359, 362, 367, 370, 372, 376, 379, + 382, 385, 388, 392, 395, 398, 400, 404, 407, 410, + 413, 417, 420, 421, 430, 433, 434, 443, 444, 446, + 450, 455, 456, 460, 461, 463 }; /* YYRHS -- A `-1'-separated list of the rules' RHS. */ static const yytype_int8 yyrhs[] = { 84, 0, -1, 84, 85, 86, -1, 86, -1, 1, - 85, -1, 76, -1, -1, 111, -1, 105, -1, 116, - -1, 87, -1, 89, -1, 40, 104, 33, 21, -1, - 42, 21, 70, 104, -1, 42, 13, 70, 104, -1, - 42, 14, 104, 70, 104, -1, 42, 21, 44, 104, - -1, 42, 13, 44, 104, -1, 42, 14, 104, 44, - 104, -1, 104, -1, 105, -1, 88, -1, 90, -1, + 85, -1, 76, -1, -1, 112, -1, 106, -1, 117, + -1, 87, -1, 89, -1, 40, 105, 33, 21, -1, + 42, 21, 70, 105, -1, 42, 13, 70, 105, -1, + 42, 14, 105, 70, 105, -1, 42, 21, 44, 105, + -1, 42, 13, 44, 105, -1, 42, 14, 105, 44, + 105, -1, 105, -1, 106, -1, 88, -1, 90, -1, 97, 77, 96, 78, 103, 102, 27, 41, -1, 98, - 70, 104, 102, 44, 104, 102, 103, 102, 27, 41, - -1, 98, 70, 104, 102, 24, 44, 104, 102, 103, - 102, 27, 41, -1, 45, 21, 43, 104, -1, 99, - 96, 43, 85, 103, 102, 27, 32, -1, 99, 96, - 43, 85, 103, 102, 48, 103, 102, 27, 32, -1, - 99, 96, 43, 85, 103, 102, 101, 92, 102, 27, - 32, -1, 99, 96, 43, 101, 88, 102, -1, 99, - 96, 43, 101, 88, 102, 48, 101, 88, 102, -1, - 99, 96, 43, 101, 88, 102, 93, 102, 91, 102, - -1, -1, 48, 101, 88, -1, 92, 95, -1, 95, - -1, 93, 94, -1, 94, -1, 100, 96, 43, 101, - 89, 102, -1, 93, -1, 100, 96, 43, 103, 102, - -1, 104, -1, 104, 70, 104, -1, 77, 96, 78, - -1, 41, 47, -1, 41, 46, 21, -1, 32, -1, - 26, -1, -1, -1, -1, 103, 85, -1, 103, 89, - -1, 12, -1, 15, -1, 22, -1, 17, -1, 21, - 77, 117, 78, -1, 21, -1, 13, -1, 14, 104, - -1, 87, -1, 104, 71, 104, -1, 104, 72, 104, - -1, 104, 73, 104, -1, 104, 74, 104, -1, 104, - 79, 104, -1, 104, 80, 104, -1, 104, 60, 104, - -1, 104, 55, 104, -1, 104, 56, 104, -1, 104, - 61, 104, -1, 104, 62, 104, -1, 63, 104, -1, - 104, 81, 104, -1, 104, 64, 104, -1, 104, 65, - 104, -1, 104, 66, 104, -1, 71, 104, -1, 72, - 104, -1, 77, 104, 78, -1, 67, 104, 68, 104, - -1, 67, 104, 69, 104, -1, 40, 104, -1, 107, - -1, 110, -1, 28, -1, 30, 106, -1, 19, 104, - -1, 18, 104, -1, 18, -1, 20, 117, -1, 51, - 104, 46, 104, -1, 51, 104, -1, 21, -1, 106, - 82, 21, -1, 31, 34, -1, 31, 37, -1, 31, - 39, -1, 31, 108, -1, 31, 108, 109, -1, 31, - 109, -1, 29, 104, -1, 104, -1, 38, 36, 104, - -1, 36, 104, -1, 52, 53, -1, 52, 108, -1, - 52, 108, 109, -1, 52, 109, -1, -1, 35, 21, - 112, 101, 114, 85, 115, 103, -1, 49, 21, -1, - -1, 50, 21, 113, 101, 114, 85, 115, 103, -1, - -1, 21, -1, 114, 82, 21, -1, 114, 85, 82, - 21, -1, -1, 21, 101, 117, -1, -1, 104, -1, - 117, 82, 104, -1 + 70, 105, 102, 44, 105, 102, 103, 102, 27, 41, + -1, 98, 70, 105, 102, 24, 44, 105, 102, 103, + 102, 27, 41, -1, 104, 105, 102, -1, 99, 96, + 43, 85, 103, 102, 27, 32, -1, 99, 96, 43, + 85, 103, 102, 48, 103, 102, 27, 32, -1, 99, + 96, 43, 85, 103, 102, 101, 92, 102, 27, 32, + -1, 99, 96, 43, 101, 88, 102, -1, 99, 96, + 43, 101, 88, 102, 48, 101, 88, 102, -1, 99, + 96, 43, 101, 88, 102, 93, 102, 91, 102, -1, + -1, 48, 101, 88, -1, 92, 95, -1, 95, -1, + 93, 94, -1, 94, -1, 100, 96, 43, 101, 89, + 102, -1, 93, -1, 100, 96, 43, 103, 102, -1, + 105, -1, 105, 70, 105, -1, 77, 96, 78, -1, + 41, 47, -1, 41, 46, 21, -1, 32, -1, 26, + -1, -1, -1, -1, 103, 85, -1, 103, 89, -1, + 45, 21, 43, -1, 12, -1, 15, -1, 22, -1, + 17, -1, 21, 77, 118, 78, -1, 21, -1, 13, + -1, 14, 105, -1, 87, -1, 105, 71, 105, -1, + 105, 72, 105, -1, 105, 73, 105, -1, 105, 74, + 105, -1, 105, 79, 105, -1, 105, 80, 105, -1, + 105, 60, 105, -1, 105, 55, 105, -1, 105, 56, + 105, -1, 105, 61, 105, -1, 105, 62, 105, -1, + 63, 105, -1, 105, 81, 105, -1, 105, 64, 105, + -1, 105, 65, 105, -1, 105, 66, 105, -1, 71, + 105, -1, 72, 105, -1, 77, 105, 78, -1, 67, + 105, 68, 105, -1, 67, 105, 69, 105, -1, 40, + 105, -1, 108, -1, 111, -1, 28, -1, 30, 107, + -1, 19, 105, -1, 18, 105, -1, 18, -1, 20, + 118, -1, 51, 105, 46, 105, -1, 51, 105, -1, + 21, -1, 107, 82, 21, -1, 31, 34, -1, 31, + 37, -1, 31, 39, -1, 31, 109, -1, 31, 109, + 110, -1, 31, 110, -1, 29, 105, -1, 105, -1, + 38, 36, 105, -1, 36, 105, -1, 52, 53, -1, + 52, 109, -1, 52, 109, 110, -1, 52, 110, -1, + -1, 35, 21, 113, 101, 115, 85, 116, 103, -1, + 49, 21, -1, -1, 50, 21, 114, 101, 115, 85, + 116, 103, -1, -1, 21, -1, 115, 82, 21, -1, + 115, 85, 82, 21, -1, -1, 21, 101, 118, -1, + -1, 105, -1, 118, 82, 105, -1 }; /* YYRLINE[YYN] -- source line where rule number YYN was defined. */ @@ -621,17 +621,17 @@ static const yytype_uint16 yyrline[] = { 0, 103, 103, 104, 105, 108, 113, 114, 115, 116, 117, 118, 121, 127, 133, 141, 149, 155, 163, 172, - 173, 175, 176, 181, 192, 208, 220, 225, 232, 241, - 250, 260, 270, 281, 282, 285, 286, 289, 290, 293, - 301, 302, 310, 311, 312, 314, 316, 322, 328, 335, - 337, 339, 340, 341, 344, 345, 348, 351, 355, 358, - 362, 369, 375, 376, 377, 378, 379, 380, 381, 382, - 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, - 393, 394, 395, 396, 399, 400, 401, 402, 404, 405, - 408, 411, 414, 415, 416, 419, 420, 431, 432, 433, - 434, 437, 440, 445, 446, 449, 450, 453, 454, 457, - 460, 490, 490, 496, 499, 499, 504, 505, 506, 507, - 509, 513, 521, 522, 523 + 173, 175, 176, 181, 192, 208, 220, 228, 235, 244, + 253, 263, 273, 284, 285, 288, 289, 292, 293, 296, + 304, 305, 313, 314, 315, 317, 319, 325, 331, 338, + 340, 342, 343, 344, 347, 353, 354, 357, 360, 364, + 367, 371, 378, 384, 385, 386, 387, 388, 389, 390, + 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, + 401, 402, 403, 404, 405, 408, 409, 410, 411, 413, + 414, 417, 420, 423, 424, 425, 428, 429, 440, 441, + 442, 443, 446, 449, 454, 455, 458, 459, 462, 463, + 466, 469, 499, 499, 505, 508, 508, 513, 514, 515, + 516, 518, 522, 530, 531, 532 }; #endif @@ -655,9 +655,9 @@ static const char *const yytname[] = "programline", "asgn", "stmtoneliner", "stmt", "ifstmt", "elsestmtoneliner", "elseifstmt", "elseifstmtoneliner", "elseifstmtoneliner1", "elseifstmt1", "cond", "repeatwhile", - "repeatwith", "if", "elseif", "begin", "end", "stmtlist", "expr", "func", - "globallist", "gotofunc", "gotoframe", "gotomovie", "playfunc", "defn", - "@1", "@2", "argdef", "argstore", "macro", "arglist", 0 + "repeatwith", "if", "elseif", "begin", "end", "stmtlist", "when", "expr", + "func", "globallist", "gotofunc", "gotoframe", "gotomovie", "playfunc", + "defn", "@1", "@2", "argdef", "argstore", "macro", "arglist", 0 }; #endif @@ -686,14 +686,14 @@ static const yytype_uint8 yyr1[] = 88, 89, 89, 89, 89, 89, 89, 90, 90, 90, 90, 90, 90, 91, 91, 92, 92, 93, 93, 94, 95, 95, 96, 96, 96, 97, 98, 99, 100, 101, - 102, 103, 103, 103, 104, 104, 104, 104, 104, 104, - 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, - 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, - 104, 104, 104, 104, 105, 105, 105, 105, 105, 105, - 105, 105, 105, 105, 105, 106, 106, 107, 107, 107, - 107, 107, 107, 108, 108, 109, 109, 110, 110, 110, - 110, 112, 111, 111, 113, 111, 114, 114, 114, 114, - 115, 116, 117, 117, 117 + 102, 103, 103, 103, 104, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 107, 107, 108, 108, + 108, 108, 108, 108, 109, 109, 110, 110, 111, 111, + 111, 111, 113, 112, 112, 114, 112, 115, 115, 115, + 115, 116, 117, 118, 118, 118 }; /* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ @@ -701,17 +701,17 @@ static const yytype_uint8 yyr2[] = { 0, 2, 3, 1, 2, 1, 0, 1, 1, 1, 1, 1, 4, 4, 4, 5, 4, 4, 5, 1, - 1, 1, 1, 8, 11, 12, 4, 8, 11, 11, + 1, 1, 1, 8, 11, 12, 3, 8, 11, 11, 6, 10, 10, 0, 3, 2, 1, 2, 1, 6, 1, 5, 1, 3, 3, 2, 3, 1, 1, 0, - 0, 0, 2, 2, 1, 1, 1, 1, 4, 1, - 1, 2, 1, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 2, 3, 3, 3, 3, 2, - 2, 3, 4, 4, 2, 1, 1, 1, 2, 2, - 2, 1, 2, 4, 2, 1, 3, 2, 2, 2, - 2, 3, 2, 2, 1, 3, 2, 2, 2, 3, - 2, 0, 8, 2, 0, 8, 0, 1, 3, 4, - 0, 3, 0, 1, 3 + 0, 0, 2, 2, 3, 1, 1, 1, 1, 4, + 1, 1, 2, 1, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, + 2, 2, 3, 4, 4, 2, 1, 1, 1, 2, + 2, 2, 1, 2, 4, 2, 1, 3, 2, 2, + 2, 2, 3, 2, 2, 1, 3, 2, 2, 2, + 3, 2, 0, 8, 2, 0, 8, 0, 1, 3, + 4, 0, 3, 0, 1, 3 }; /* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state @@ -719,259 +719,263 @@ static const yytype_uint8 yyr2[] = means the default is an error. */ static const yytype_uint8 yydefact[] = { - 0, 0, 54, 60, 0, 55, 57, 91, 0, 122, - 49, 56, 87, 0, 0, 47, 0, 0, 0, 0, + 0, 0, 55, 61, 0, 56, 58, 92, 0, 123, + 49, 57, 88, 0, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 3, 62, 21, 11, 22, 0, 0, 0, 19, - 8, 85, 86, 7, 9, 5, 4, 59, 0, 62, - 61, 90, 89, 123, 92, 122, 122, 95, 88, 0, - 97, 0, 98, 0, 99, 104, 100, 102, 111, 84, - 0, 45, 0, 0, 0, 0, 113, 114, 94, 107, - 108, 110, 74, 0, 79, 80, 0, 1, 6, 0, - 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, + 0, 3, 63, 21, 11, 22, 0, 0, 0, 0, + 19, 8, 86, 87, 7, 9, 5, 4, 60, 0, + 63, 62, 91, 90, 124, 93, 123, 123, 96, 89, + 0, 98, 0, 99, 0, 100, 105, 101, 103, 112, + 85, 0, 45, 0, 0, 0, 0, 114, 115, 95, + 108, 109, 111, 75, 0, 80, 81, 0, 1, 6, + 0, 0, 0, 0, 42, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 121, 0, 103, 106, 0, 101, 49, 0, - 46, 0, 0, 0, 0, 0, 0, 49, 0, 109, - 0, 0, 81, 2, 0, 50, 0, 0, 49, 0, - 70, 71, 69, 72, 73, 76, 77, 78, 63, 64, - 65, 66, 67, 68, 75, 124, 58, 96, 105, 116, - 12, 17, 14, 0, 0, 16, 13, 26, 116, 93, - 82, 83, 51, 0, 44, 51, 0, 43, 117, 0, - 18, 15, 0, 50, 0, 0, 50, 50, 20, 0, - 120, 120, 52, 53, 0, 0, 50, 49, 30, 118, - 0, 51, 51, 0, 50, 51, 0, 51, 0, 48, - 49, 50, 38, 0, 119, 112, 115, 23, 51, 50, - 27, 50, 50, 40, 36, 0, 0, 37, 33, 0, - 50, 0, 0, 35, 0, 0, 50, 49, 50, 49, - 0, 0, 0, 0, 49, 31, 0, 32, 0, 0, - 24, 28, 29, 50, 34, 50, 25, 41, 39 + 0, 0, 0, 0, 122, 0, 104, 107, 0, 102, + 49, 0, 46, 0, 0, 0, 0, 0, 54, 49, + 0, 110, 0, 0, 82, 2, 0, 50, 0, 0, + 49, 0, 26, 71, 72, 70, 73, 74, 77, 78, + 79, 64, 65, 66, 67, 68, 69, 76, 125, 59, + 97, 106, 117, 12, 17, 14, 0, 0, 16, 13, + 117, 94, 83, 84, 51, 0, 44, 51, 0, 43, + 118, 0, 18, 15, 0, 50, 0, 0, 50, 50, + 20, 0, 121, 121, 52, 53, 0, 0, 50, 49, + 30, 119, 0, 51, 51, 0, 50, 51, 0, 51, + 0, 48, 49, 50, 38, 0, 120, 113, 116, 23, + 51, 50, 27, 50, 50, 40, 36, 0, 0, 37, + 33, 0, 50, 0, 0, 35, 0, 0, 50, 49, + 50, 49, 0, 0, 0, 0, 49, 31, 0, 32, + 0, 0, 24, 28, 29, 50, 34, 50, 25, 41, + 39 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { - -1, 30, 192, 31, 49, 33, 193, 35, 238, 222, - 223, 212, 224, 92, 36, 37, 38, 213, 248, 173, - 183, 39, 188, 58, 41, 66, 67, 42, 43, 118, - 127, 179, 201, 44, 54 + -1, 30, 194, 31, 50, 33, 195, 35, 240, 224, + 225, 214, 226, 93, 36, 37, 38, 215, 250, 142, + 185, 39, 40, 190, 59, 42, 67, 68, 43, 44, + 120, 129, 181, 203, 45, 55 }; /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ -#define YYPACT_NINF -198 +#define YYPACT_NINF -195 static const yytype_int16 yypact[] = { - 243, -53, -198, -198, 385, -198, -198, 385, 385, 385, - 791, -198, -198, 18, 573, -198, 22, 385, 7, 6, - 26, 31, 36, 385, 613, 385, 385, 385, 385, 385, - 4, -198, 5, -198, -198, -198, -48, -8, 512, 769, - -198, -198, -198, -198, -198, -198, -198, -14, 385, -198, - 769, 769, 769, 769, -21, 385, 385, -198, -4, 385, - -198, 385, -198, 38, -198, 769, 8, -198, -198, 151, - 54, -198, -37, 385, -29, 39, -198, -198, 649, -198, - 8, -198, 840, 671, 840, 840, 720, -198, 341, 512, - 385, 512, 40, 747, 385, 385, 385, 385, 385, 385, - 385, 385, 385, 385, 385, 385, 385, 385, 385, 151, - 385, -57, -21, 63, 769, 769, 385, -198, -198, 64, - -198, 385, 385, 627, 385, 385, 385, -198, 385, -198, - 385, 385, -198, -198, 9, 769, 14, 693, -53, 385, - 769, 769, 769, 769, 769, 769, 769, 769, 818, 818, - 840, 840, 769, 769, 769, 769, -198, -198, 769, 65, - -198, 769, 769, 385, 385, 769, 769, 769, 65, 769, - 769, 769, -198, -12, -198, -198, 529, 769, -198, -58, - 769, 769, -58, 402, 49, 385, 402, -198, -198, 73, - 13, 13, -198, -198, 71, 385, 769, -11, -17, -198, - 78, -198, -198, 60, 769, -198, 72, -198, 77, -198, - -198, 77, -198, 512, -198, 402, 402, -198, -198, 402, - -198, 402, 77, 77, -198, 512, 529, -198, 57, 67, - 402, 79, 80, -198, 81, 68, -198, -198, -198, -198, - 85, 74, 84, 87, -16, -198, 529, -198, 468, 76, - -198, -198, -198, 402, -198, -198, -198, -198, -198 + 254, -59, -195, -195, 364, -195, -195, 364, 364, 364, + 801, -195, -195, 14, 552, -195, 23, 364, 10, 6, + 30, 32, 39, 364, 592, 364, 364, 364, 364, 364, + 4, -195, 5, -195, -195, -195, -51, 1, 491, 364, + 779, -195, -195, -195, -195, -195, -195, -195, -5, 364, + -195, 779, 779, 779, 779, -8, 364, 364, -195, -3, + 364, -195, 364, -195, 37, -195, 779, 12, -195, -195, + 606, 61, -195, -31, 364, -30, 40, -195, -195, 659, + -195, 12, -195, 850, 681, 850, 850, 730, -195, 320, + 491, 364, 491, 41, 757, 779, 364, 364, 364, 364, + 364, 364, 364, 364, 364, 364, 364, 364, 364, 364, + 364, 606, 364, -57, -8, 64, 779, 779, 364, -195, + -195, 65, -195, 364, 364, 637, 364, 364, -195, -195, + 364, -195, 364, 364, -195, -195, 15, 779, 18, 703, + -59, 364, -195, 779, 779, 779, 779, 779, 779, 779, + 779, 828, 828, 850, 850, 779, 779, 779, 779, -195, + -195, 779, 67, -195, 779, 779, 364, 364, 779, 779, + 67, 779, 779, 779, -195, -1, -195, -195, 508, 779, + -195, -58, 779, 779, -58, 381, 46, 364, 381, -195, + -195, 76, 17, 17, -195, -195, 74, 364, 779, -20, + -11, -195, 81, -195, -195, 62, 779, -195, 72, -195, + 79, -195, -195, 79, -195, 491, -195, 381, 381, -195, + -195, 381, -195, 381, 79, 79, -195, 491, 508, -195, + 58, 66, 381, 80, 83, -195, 86, 71, -195, -195, + -195, -195, 88, 75, 85, 87, -17, -195, 508, -195, + 447, 77, -195, -195, -195, 381, -195, -195, -195, -195, + -195 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { - -198, -198, 12, 25, 2, -170, 0, -198, -198, -198, - -78, -197, -101, -61, -198, -198, -198, -186, -9, 113, - -167, 41, 3, -198, -198, 98, -7, -198, -198, -198, - -198, -45, -67, -198, 16 + -195, -195, 11, 19, 2, -170, 0, -195, -195, -195, + -79, -191, -102, -61, -195, -195, -195, -194, -9, -12, + -171, -195, 38, 3, -195, -195, 99, -13, -195, -195, + -195, -195, -46, -67, -195, 13 }; /* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule which number is the opposite. If zero, do what YYDEFACT says. If YYTABLE_NINF, syntax error. */ -#define YYTABLE_NINF -60 +#define YYTABLE_NINF -61 static const yytype_int16 yytable[] = { - 34, 56, 32, 40, 87, -10, 187, 121, 186, 209, - -51, -51, 184, 46, 227, 124, 206, 81, 45, 72, - 73, 156, 225, 45, 189, 110, 227, 74, 134, 89, - 136, 210, 185, 122, 215, 216, 225, 207, 219, 57, - 221, 125, 88, 68, 61, 50, 63, 75, 51, 52, - 53, 230, 76, 70, 71, 65, 236, 77, 69, 117, - -51, 110, 90, 55, 78, 65, 82, 83, 84, 85, - 86, 111, 112, 129, 116, 120, 254, 253, 113, 93, - 45, -10, 126, 138, 157, 160, 178, 172, 34, 109, - 32, 40, 174, 195, 199, 200, 53, 53, 203, 214, - 114, 217, 115, 209, 220, 237, 241, 242, 243, 159, - 239, 244, 249, 133, 123, 250, 251, 256, 168, 252, - 211, 233, 80, 182, 202, 0, 0, 0, 0, 176, - 93, 135, 137, 0, 0, 140, 141, 142, 143, 144, - 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, - 175, 155, 229, 0, 0, 0, 0, 158, 0, 0, - 0, 0, 161, 162, 235, 165, 166, 167, 0, 169, - 0, 170, 171, 0, 0, 0, 0, 0, 0, 0, - 177, 0, 0, 0, 119, 0, 0, 0, 208, 0, - 0, 190, 0, 0, 191, 0, 0, 0, 0, 0, - 0, 226, 0, 0, 180, 181, 94, 95, 0, 0, - 0, 96, 97, 98, 0, 99, 100, 101, 0, 0, - 0, 0, 102, 103, 104, 105, 196, 0, 246, 0, - 106, 107, 108, 0, 0, 0, 204, 0, 0, 0, - 0, 0, 0, -6, 1, 0, 0, 0, 255, 0, - 0, 0, 0, 0, 93, 2, 3, 4, 5, 0, - 6, 7, 8, 9, 10, 11, 93, 0, 0, 0, - 0, 12, 0, 13, 14, 15, 0, 0, 16, 0, - 0, 0, 0, 17, 18, 19, 0, 0, 20, 0, - 0, 0, 21, 22, 23, 24, 194, 0, 0, 197, - 198, 0, 0, 0, 0, 0, 25, 0, 0, 205, - 26, 0, 0, 0, 27, 28, 0, 218, 0, -6, - 29, 0, 0, 0, 228, 0, 0, 0, 0, 0, - 0, 0, 231, 0, 232, 234, 0, 0, 0, 0, - 0, 0, 0, 240, 0, 0, 0, 0, 0, 245, - 0, 247, 0, 2, 3, 4, 5, 0, 6, 7, - 8, 9, 10, 11, 0, 0, 257, 0, 258, 12, - 0, 13, 14, 15, 0, 0, 16, 0, 0, 0, - 0, 17, 18, 19, 0, 0, 20, 0, 0, 0, - 21, 22, 23, 24, 0, 0, 0, 2, 3, 4, - 5, 0, 6, 0, 25, 0, 47, 11, 26, 0, - 0, 0, 27, 28, 2, 3, 4, 5, 29, 6, - 7, 8, 9, 47, 11, 48, 0, 19, 0, 0, - 12, 0, 13, 14, 15, 0, 0, 0, 0, 0, - 0, 0, 17, 18, 19, 0, 0, 20, 25, 0, - 0, 0, 26, 23, 24, 0, 27, 28, 0, 0, - 0, 0, 29, 0, 0, 25, 0, 0, 0, 26, - 0, 0, 0, 27, 28, 0, 0, 0, 45, 29, - 2, 3, 4, 5, 0, 6, 7, 8, 9, 47, - 11, 0, 0, 0, 0, 0, 12, 0, 13, 14, - 15, 0, 0, 0, 0, 0, 0, 0, 17, 18, - 19, 0, 0, 20, 0, 0, 0, 0, 0, 23, - 24, 0, 0, 0, 2, 3, 4, 5, 0, 6, - 0, 25, 0, 47, 11, 26, 0, 0, 0, 27, - 28, 2, 3, 4, 5, 29, 6, 7, 8, 9, - 47, 11, 48, 0, 19, 0, 0, 12, 0, 13, - 14, 0, 0, 0, 0, 0, 0, 0, 0, 17, - 0, 19, 0, 0, 0, 25, 0, 0, 0, 26, - 23, 24, 0, 27, 28, 2, 3, 4, 5, 91, - 6, 0, 25, 0, 47, 11, 26, 0, 0, 0, - 27, 28, 59, 0, 0, 0, 29, 60, 0, 61, - 62, 63, 64, 48, 0, 19, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 2, 3, 4, 5, 0, - 6, 0, 0, 0, 47, 11, 25, 0, 0, 0, - 26, 0, 59, 0, 27, 28, 0, 0, 0, 61, - 29, 63, 0, 48, 0, 19, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 79, 0, 0, 0, - 0, 163, 0, 0, 0, 0, 25, 0, 0, 0, - 26, 0, 94, 95, 27, 28, 0, 96, 97, 98, - 29, 99, 100, 101, 0, 128, 0, 164, 102, 103, - 104, 105, 0, 0, 94, 95, 106, 107, 108, 96, - 97, 98, 0, 99, 100, 101, 0, 0, 0, 0, - 102, 103, 104, 105, 0, 0, 94, 95, 106, 107, - 108, 96, 97, 98, 0, 99, 100, 101, 0, 130, - 131, 0, 102, 103, 104, 105, 0, 0, 94, 95, - 106, 107, 108, 96, 97, 98, 0, 99, 100, 101, - 0, 0, 0, 139, 102, 103, 104, 105, 0, 0, - 0, 132, 106, 107, 108, 94, 95, 0, 0, 0, - 96, 97, 98, 0, 99, 100, 101, 0, 0, 0, - 0, 102, 103, 104, 105, 0, 0, 0, 132, 106, - 107, 108, 94, 95, 0, 0, 0, 96, 97, 98, - 0, 99, 100, 101, 0, 0, 0, 139, 102, 103, - 104, 105, 0, 0, 94, 95, 106, 107, 108, 96, - 97, 98, 0, 99, 100, 101, 0, 0, 0, 0, - 102, 103, 104, 105, 0, 0, -59, -59, 106, 107, - 108, -59, -59, -59, 0, -59, -59, -59, 0, 0, - 0, 0, 0, 0, -59, -59, 0, 0, 55, 0, - -59, -59, -59, 94, 95, 0, 0, 0, 96, 97, - 98, 0, 99, 100, 101, 0, 0, 0, 0, 0, - 0, 104, 105, 0, 0, 94, 95, 106, 107, 108, - 96, 97, 98, 0, 99, 100, 101, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, - 107, 108 + 34, 57, 32, 41, 88, -10, 188, 208, 189, -51, + -51, 82, 47, 123, 126, 211, 227, 46, 46, 73, + 74, 159, 229, 186, 191, 112, 90, 75, 209, 136, + 227, 138, 217, 218, 229, 58, 221, 212, 223, 124, + 127, 89, 51, 187, 69, 52, 53, 54, 62, 232, + 64, 76, 66, 77, 119, 70, 71, 72, 238, -51, + 78, 79, 66, 83, 84, 85, 86, 87, 131, 113, + 114, 91, 56, 118, 112, 255, 94, 95, 256, 115, + 46, -10, 122, 128, 140, 160, 163, 111, 180, 34, + 197, 32, 41, 174, 54, 54, 176, 201, 116, 202, + 117, 205, 216, 219, 222, 211, 239, 243, 135, 241, + 244, 162, 125, 245, 246, 251, 252, 253, 258, 254, + 170, 213, 235, 81, 184, 175, 204, 0, 94, 137, + 139, 178, 0, 0, 143, 144, 145, 146, 147, 148, + 149, 150, 151, 152, 153, 154, 155, 156, 157, 0, + 158, 177, 0, 0, 231, 0, 161, 0, 0, 0, + 0, 164, 165, 0, 168, 169, 237, 0, 171, 0, + 172, 173, 0, 196, 0, 0, 199, 200, 0, 179, + 0, 0, 0, 0, 0, 0, 207, 0, 0, 0, + 210, 0, 192, 0, 220, 193, 0, 0, 0, 0, + 0, 230, 0, 228, 182, 183, 0, 0, 0, 233, + 0, 234, 236, 0, 0, 0, 0, 0, 0, 0, + 242, 0, 0, 0, 0, 198, 247, 0, 249, 0, + 248, 0, 0, 0, 0, 206, 0, 0, 0, 0, + 0, 0, 0, 259, 0, 260, 0, 0, 0, 0, + 257, 0, 0, 94, -6, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 94, 2, 3, 4, 5, + 0, 6, 7, 8, 9, 10, 11, 0, 0, 0, + 0, 0, 12, 0, 13, 14, 15, 0, 0, 16, + 0, 0, 0, 0, 17, 18, 19, 0, 0, 20, + 0, 0, 0, 21, 22, 23, 24, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 25, 0, 0, + 0, 26, 0, 0, 0, 27, 28, 0, 0, 0, + -6, 29, 2, 3, 4, 5, 0, 6, 7, 8, + 9, 10, 11, 0, 0, 0, 0, 0, 12, 0, + 13, 14, 15, 0, 0, 16, 0, 0, 0, 0, + 17, 18, 19, 0, 0, 20, 0, 0, 0, 21, + 22, 23, 24, 0, 0, 0, 2, 3, 4, 5, + 0, 6, 0, 25, 0, 48, 11, 26, 0, 0, + 0, 27, 28, 2, 3, 4, 5, 29, 6, 7, + 8, 9, 48, 11, 49, 0, 19, 0, 0, 12, + 0, 13, 14, 15, 0, 0, 0, 0, 0, 0, + 0, 17, 18, 19, 0, 0, 20, 25, 0, 0, + 0, 26, 23, 24, 0, 27, 28, 0, 0, 0, + 0, 29, 0, 0, 25, 0, 0, 0, 26, 0, + 0, 0, 27, 28, 0, 0, 0, 46, 29, 2, + 3, 4, 5, 0, 6, 7, 8, 9, 48, 11, + 0, 0, 0, 0, 0, 12, 0, 13, 14, 15, + 0, 0, 0, 0, 0, 0, 0, 17, 18, 19, + 0, 0, 20, 0, 0, 0, 0, 0, 23, 24, + 0, 0, 0, 2, 3, 4, 5, 0, 6, 0, + 25, 0, 48, 11, 26, 0, 0, 0, 27, 28, + 2, 3, 4, 5, 29, 6, 7, 8, 9, 48, + 11, 49, 0, 19, 0, 0, 12, 0, 13, 14, + 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, + 19, 0, 0, 0, 25, 0, 0, 0, 26, 23, + 24, 0, 27, 28, 2, 3, 4, 5, 92, 6, + 0, 25, 0, 48, 11, 26, 0, 0, 0, 27, + 28, 60, 0, 0, 0, 29, 61, 0, 62, 63, + 64, 65, 49, 0, 19, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 3, 4, 5, 0, 6, + 0, 0, 0, 48, 11, 25, 0, 0, 0, 26, + 0, 60, 0, 27, 28, 0, 0, 0, 62, 29, + 64, 0, 49, 0, 19, 0, 0, 0, 0, 121, + 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 25, 0, 0, 0, 26, + 0, 96, 97, 27, 28, 0, 98, 99, 100, 29, + 101, 102, 103, 0, 0, 0, 0, 104, 105, 106, + 107, 166, 0, 0, 0, 108, 109, 110, 0, 0, + 0, 0, 96, 97, 0, 0, 0, 98, 99, 100, + 0, 101, 102, 103, 0, 130, 0, 167, 104, 105, + 106, 107, 0, 0, 96, 97, 108, 109, 110, 98, + 99, 100, 0, 101, 102, 103, 0, 0, 0, 0, + 104, 105, 106, 107, 0, 0, 96, 97, 108, 109, + 110, 98, 99, 100, 0, 101, 102, 103, 0, 132, + 133, 0, 104, 105, 106, 107, 0, 0, 96, 97, + 108, 109, 110, 98, 99, 100, 0, 101, 102, 103, + 0, 0, 0, 141, 104, 105, 106, 107, 0, 0, + 0, 134, 108, 109, 110, 96, 97, 0, 0, 0, + 98, 99, 100, 0, 101, 102, 103, 0, 0, 0, + 0, 104, 105, 106, 107, 0, 0, 0, 134, 108, + 109, 110, 96, 97, 0, 0, 0, 98, 99, 100, + 0, 101, 102, 103, 0, 0, 0, 141, 104, 105, + 106, 107, 0, 0, 96, 97, 108, 109, 110, 98, + 99, 100, 0, 101, 102, 103, 0, 0, 0, 0, + 104, 105, 106, 107, 0, 0, -60, -60, 108, 109, + 110, -60, -60, -60, 0, -60, -60, -60, 0, 0, + 0, 0, 0, 0, -60, -60, 0, 0, 56, 0, + -60, -60, -60, 96, 97, 0, 0, 0, 98, 99, + 100, 0, 101, 102, 103, 0, 0, 0, 0, 0, + 0, 106, 107, 0, 0, 96, 97, 108, 109, 110, + 98, 99, 100, 0, 101, 102, 103, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 108, + 109, 110 }; static const yytype_int16 yycheck[] = { - 0, 10, 0, 0, 0, 0, 176, 44, 175, 26, - 26, 27, 24, 1, 211, 44, 27, 24, 76, 13, - 14, 78, 208, 76, 82, 82, 223, 21, 89, 77, - 91, 48, 44, 70, 201, 202, 222, 48, 205, 21, - 207, 70, 30, 21, 36, 4, 38, 21, 7, 8, - 9, 218, 21, 46, 47, 14, 226, 21, 17, 66, - 76, 82, 70, 77, 23, 24, 25, 26, 27, 28, - 29, 55, 56, 80, 36, 21, 246, 244, 82, 38, - 76, 76, 43, 43, 21, 21, 21, 78, 88, 48, - 88, 88, 78, 44, 21, 82, 55, 56, 27, 21, - 59, 41, 61, 26, 32, 48, 27, 27, 27, 118, - 43, 43, 27, 88, 73, 41, 32, 41, 127, 32, - 198, 222, 24, 168, 191, -1, -1, -1, -1, 138, - 89, 90, 91, -1, -1, 94, 95, 96, 97, 98, - 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, - 138, 110, 213, -1, -1, -1, -1, 116, -1, -1, - -1, -1, 121, 122, 225, 124, 125, 126, -1, 128, - -1, 130, 131, -1, -1, -1, -1, -1, -1, -1, - 139, -1, -1, -1, 33, -1, -1, -1, 197, -1, - -1, 179, -1, -1, 182, -1, -1, -1, -1, -1, - -1, 210, -1, -1, 163, 164, 55, 56, -1, -1, - -1, 60, 61, 62, -1, 64, 65, 66, -1, -1, - -1, -1, 71, 72, 73, 74, 185, -1, 237, -1, - 79, 80, 81, -1, -1, -1, 195, -1, -1, -1, - -1, -1, -1, 0, 1, -1, -1, -1, 248, -1, - -1, -1, -1, -1, 213, 12, 13, 14, 15, -1, - 17, 18, 19, 20, 21, 22, 225, -1, -1, -1, - -1, 28, -1, 30, 31, 32, -1, -1, 35, -1, - -1, -1, -1, 40, 41, 42, -1, -1, 45, -1, - -1, -1, 49, 50, 51, 52, 183, -1, -1, 186, - 187, -1, -1, -1, -1, -1, 63, -1, -1, 196, - 67, -1, -1, -1, 71, 72, -1, 204, -1, 76, - 77, -1, -1, -1, 211, -1, -1, -1, -1, -1, - -1, -1, 219, -1, 221, 222, -1, -1, -1, -1, - -1, -1, -1, 230, -1, -1, -1, -1, -1, 236, - -1, 238, -1, 12, 13, 14, 15, -1, 17, 18, - 19, 20, 21, 22, -1, -1, 253, -1, 255, 28, - -1, 30, 31, 32, -1, -1, 35, -1, -1, -1, - -1, 40, 41, 42, -1, -1, 45, -1, -1, -1, - 49, 50, 51, 52, -1, -1, -1, 12, 13, 14, - 15, -1, 17, -1, 63, -1, 21, 22, 67, -1, - -1, -1, 71, 72, 12, 13, 14, 15, 77, 17, - 18, 19, 20, 21, 22, 40, -1, 42, -1, -1, - 28, -1, 30, 31, 32, -1, -1, -1, -1, -1, - -1, -1, 40, 41, 42, -1, -1, 45, 63, -1, - -1, -1, 67, 51, 52, -1, 71, 72, -1, -1, - -1, -1, 77, -1, -1, 63, -1, -1, -1, 67, - -1, -1, -1, 71, 72, -1, -1, -1, 76, 77, - 12, 13, 14, 15, -1, 17, 18, 19, 20, 21, - 22, -1, -1, -1, -1, -1, 28, -1, 30, 31, - 32, -1, -1, -1, -1, -1, -1, -1, 40, 41, - 42, -1, -1, 45, -1, -1, -1, -1, -1, 51, - 52, -1, -1, -1, 12, 13, 14, 15, -1, 17, + 0, 10, 0, 0, 0, 0, 177, 27, 178, 26, + 27, 24, 1, 44, 44, 26, 210, 76, 76, 13, + 14, 78, 213, 24, 82, 82, 77, 21, 48, 90, + 224, 92, 203, 204, 225, 21, 207, 48, 209, 70, + 70, 30, 4, 44, 21, 7, 8, 9, 36, 220, + 38, 21, 14, 21, 67, 17, 46, 47, 228, 76, + 21, 23, 24, 25, 26, 27, 28, 29, 81, 56, + 57, 70, 77, 36, 82, 246, 38, 39, 248, 82, + 76, 76, 21, 43, 43, 21, 21, 49, 21, 89, + 44, 89, 89, 78, 56, 57, 78, 21, 60, 82, + 62, 27, 21, 41, 32, 26, 48, 27, 89, 43, + 27, 120, 74, 27, 43, 27, 41, 32, 41, 32, + 129, 200, 224, 24, 170, 137, 193, -1, 90, 91, + 92, 140, -1, -1, 96, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107, 108, 109, 110, -1, + 112, 140, -1, -1, 215, -1, 118, -1, -1, -1, + -1, 123, 124, -1, 126, 127, 227, -1, 130, -1, + 132, 133, -1, 185, -1, -1, 188, 189, -1, 141, + -1, -1, -1, -1, -1, -1, 198, -1, -1, -1, + 199, -1, 181, -1, 206, 184, -1, -1, -1, -1, + -1, 213, -1, 212, 166, 167, -1, -1, -1, 221, + -1, 223, 224, -1, -1, -1, -1, -1, -1, -1, + 232, -1, -1, -1, -1, 187, 238, -1, 240, -1, + 239, -1, -1, -1, -1, 197, -1, -1, -1, -1, + -1, -1, -1, 255, -1, 257, -1, -1, -1, -1, + 250, -1, -1, 215, 0, 1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 227, 12, 13, 14, 15, + -1, 17, 18, 19, 20, 21, 22, -1, -1, -1, + -1, -1, 28, -1, 30, 31, 32, -1, -1, 35, + -1, -1, -1, -1, 40, 41, 42, -1, -1, 45, + -1, -1, -1, 49, 50, 51, 52, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 63, -1, -1, + -1, 67, -1, -1, -1, 71, 72, -1, -1, -1, + 76, 77, 12, 13, 14, 15, -1, 17, 18, 19, + 20, 21, 22, -1, -1, -1, -1, -1, 28, -1, + 30, 31, 32, -1, -1, 35, -1, -1, -1, -1, + 40, 41, 42, -1, -1, 45, -1, -1, -1, 49, + 50, 51, 52, -1, -1, -1, 12, 13, 14, 15, + -1, 17, -1, 63, -1, 21, 22, 67, -1, -1, + -1, 71, 72, 12, 13, 14, 15, 77, 17, 18, + 19, 20, 21, 22, 40, -1, 42, -1, -1, 28, + -1, 30, 31, 32, -1, -1, -1, -1, -1, -1, + -1, 40, 41, 42, -1, -1, 45, 63, -1, -1, + -1, 67, 51, 52, -1, 71, 72, -1, -1, -1, + -1, 77, -1, -1, 63, -1, -1, -1, 67, -1, + -1, -1, 71, 72, -1, -1, -1, 76, 77, 12, + 13, 14, 15, -1, 17, 18, 19, 20, 21, 22, + -1, -1, -1, -1, -1, 28, -1, 30, 31, 32, + -1, -1, -1, -1, -1, -1, -1, 40, 41, 42, + -1, -1, 45, -1, -1, -1, -1, -1, 51, 52, + -1, -1, -1, 12, 13, 14, 15, -1, 17, -1, + 63, -1, 21, 22, 67, -1, -1, -1, 71, 72, + 12, 13, 14, 15, 77, 17, 18, 19, 20, 21, + 22, 40, -1, 42, -1, -1, 28, -1, 30, 31, + -1, -1, -1, -1, -1, -1, -1, -1, 40, -1, + 42, -1, -1, -1, 63, -1, -1, -1, 67, 51, + 52, -1, 71, 72, 12, 13, 14, 15, 77, 17, -1, 63, -1, 21, 22, 67, -1, -1, -1, 71, - 72, 12, 13, 14, 15, 77, 17, 18, 19, 20, - 21, 22, 40, -1, 42, -1, -1, 28, -1, 30, - 31, -1, -1, -1, -1, -1, -1, -1, -1, 40, - -1, 42, -1, -1, -1, 63, -1, -1, -1, 67, - 51, 52, -1, 71, 72, 12, 13, 14, 15, 77, - 17, -1, 63, -1, 21, 22, 67, -1, -1, -1, - 71, 72, 29, -1, -1, -1, 77, 34, -1, 36, - 37, 38, 39, 40, -1, 42, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 12, 13, 14, 15, -1, - 17, -1, -1, -1, 21, 22, 63, -1, -1, -1, - 67, -1, 29, -1, 71, 72, -1, -1, -1, 36, - 77, 38, -1, 40, -1, 42, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 53, -1, -1, -1, - -1, 44, -1, -1, -1, -1, 63, -1, -1, -1, - 67, -1, 55, 56, 71, 72, -1, 60, 61, 62, - 77, 64, 65, 66, -1, 46, -1, 70, 71, 72, + 72, 29, -1, -1, -1, 77, 34, -1, 36, 37, + 38, 39, 40, -1, 42, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 12, 13, 14, 15, -1, 17, + -1, -1, -1, 21, 22, 63, -1, -1, -1, 67, + -1, 29, -1, 71, 72, -1, -1, -1, 36, 77, + 38, -1, 40, -1, 42, -1, -1, -1, -1, 33, + -1, -1, -1, -1, -1, 53, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 63, -1, -1, -1, 67, + -1, 55, 56, 71, 72, -1, 60, 61, 62, 77, + 64, 65, 66, -1, -1, -1, -1, 71, 72, 73, + 74, 44, -1, -1, -1, 79, 80, 81, -1, -1, + -1, -1, 55, 56, -1, -1, -1, 60, 61, 62, + -1, 64, 65, 66, -1, 46, -1, 70, 71, 72, 73, 74, -1, -1, 55, 56, 79, 80, 81, 60, 61, 62, -1, 64, 65, 66, -1, -1, -1, -1, 71, 72, 73, 74, -1, -1, 55, 56, 79, 80, @@ -1005,28 +1009,29 @@ static const yytype_uint8 yystos[] = 21, 22, 28, 30, 31, 32, 35, 40, 41, 42, 45, 49, 50, 51, 52, 63, 67, 71, 72, 77, 84, 86, 87, 88, 89, 90, 97, 98, 99, 104, - 105, 107, 110, 111, 116, 76, 85, 21, 40, 87, - 104, 104, 104, 104, 117, 77, 101, 21, 106, 29, - 34, 36, 37, 38, 39, 104, 108, 109, 21, 104, - 46, 47, 13, 14, 21, 21, 21, 21, 104, 53, - 108, 109, 104, 104, 104, 104, 104, 0, 85, 77, - 70, 77, 96, 104, 55, 56, 60, 61, 62, 64, - 65, 66, 71, 72, 73, 74, 79, 80, 81, 104, - 82, 117, 117, 82, 104, 104, 36, 109, 112, 33, - 21, 44, 70, 104, 44, 70, 43, 113, 46, 109, - 68, 69, 78, 86, 96, 104, 96, 104, 43, 70, - 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, - 104, 104, 104, 104, 104, 104, 78, 21, 104, 101, - 21, 104, 104, 44, 70, 104, 104, 104, 101, 104, - 104, 104, 78, 102, 78, 85, 101, 104, 21, 114, - 104, 104, 114, 103, 24, 44, 103, 88, 105, 82, - 85, 85, 85, 89, 102, 44, 104, 102, 102, 21, - 82, 115, 115, 27, 104, 102, 27, 48, 101, 26, - 48, 93, 94, 100, 21, 103, 103, 41, 102, 103, - 32, 103, 92, 93, 95, 100, 101, 94, 102, 96, - 103, 102, 102, 95, 102, 96, 88, 48, 91, 43, - 102, 27, 27, 27, 43, 102, 101, 102, 101, 27, - 41, 32, 32, 103, 88, 89, 41, 102, 102 + 105, 106, 108, 111, 112, 117, 76, 85, 21, 40, + 87, 105, 105, 105, 105, 118, 77, 101, 21, 107, + 29, 34, 36, 37, 38, 39, 105, 109, 110, 21, + 105, 46, 47, 13, 14, 21, 21, 21, 21, 105, + 53, 109, 110, 105, 105, 105, 105, 105, 0, 85, + 77, 70, 77, 96, 105, 105, 55, 56, 60, 61, + 62, 64, 65, 66, 71, 72, 73, 74, 79, 80, + 81, 105, 82, 118, 118, 82, 105, 105, 36, 110, + 113, 33, 21, 44, 70, 105, 44, 70, 43, 114, + 46, 110, 68, 69, 78, 86, 96, 105, 96, 105, + 43, 70, 102, 105, 105, 105, 105, 105, 105, 105, + 105, 105, 105, 105, 105, 105, 105, 105, 105, 78, + 21, 105, 101, 21, 105, 105, 44, 70, 105, 105, + 101, 105, 105, 105, 78, 102, 78, 85, 101, 105, + 21, 115, 105, 105, 115, 103, 24, 44, 103, 88, + 106, 82, 85, 85, 85, 89, 102, 44, 105, 102, + 102, 21, 82, 116, 116, 27, 105, 102, 27, 48, + 101, 26, 48, 93, 94, 100, 21, 103, 103, 41, + 102, 103, 32, 103, 92, 93, 95, 100, 101, 94, + 102, 96, 103, 102, 102, 95, 102, 96, 88, 48, + 91, 43, 102, 27, 27, 27, 43, 102, 101, 102, + 101, 27, 41, 32, 32, 103, 88, 89, 41, 102, + 102 }; #define yyerrok (yyerrstatus = 0) @@ -1986,12 +1991,15 @@ yyreduce: case 26: #line 220 "engines/director/lingo/lingo-gr.y" { - g_lingo->code1(g_lingo->c_ifcode); + inst end = 0; + WRITE_UINT32(&end, (yyvsp[(3) - (3)].code)); + g_lingo->code1(STOP); + (*g_lingo->_currentScript)[(yyvsp[(1) - (3)].code) + 1] = end; ;} break; case 27: -#line 225 "engines/director/lingo/lingo-gr.y" +#line 228 "engines/director/lingo/lingo-gr.y" { inst then = 0, end = 0; WRITE_UINT32(&then, (yyvsp[(5) - (8)].code)); @@ -2002,7 +2010,7 @@ yyreduce: break; case 28: -#line 232 "engines/director/lingo/lingo-gr.y" +#line 235 "engines/director/lingo/lingo-gr.y" { inst then = 0, else1 = 0, end = 0; WRITE_UINT32(&then, (yyvsp[(5) - (11)].code)); @@ -2015,7 +2023,7 @@ yyreduce: break; case 29: -#line 241 "engines/director/lingo/lingo-gr.y" +#line 244 "engines/director/lingo/lingo-gr.y" { inst then = 0, else1 = 0, end = 0; WRITE_UINT32(&then, (yyvsp[(5) - (11)].code)); @@ -2028,7 +2036,7 @@ yyreduce: break; case 30: -#line 250 "engines/director/lingo/lingo-gr.y" +#line 253 "engines/director/lingo/lingo-gr.y" { inst then = 0, else1 = 0, end = 0; WRITE_UINT32(&then, (yyvsp[(4) - (6)].code)); @@ -2042,7 +2050,7 @@ yyreduce: break; case 31: -#line 260 "engines/director/lingo/lingo-gr.y" +#line 263 "engines/director/lingo/lingo-gr.y" { inst then = 0, else1 = 0, end = 0; WRITE_UINT32(&then, (yyvsp[(4) - (10)].code)); @@ -2056,7 +2064,7 @@ yyreduce: break; case 32: -#line 270 "engines/director/lingo/lingo-gr.y" +#line 273 "engines/director/lingo/lingo-gr.y" { inst then = 0, else1 = 0, end = 0; WRITE_UINT32(&then, (yyvsp[(4) - (10)].code)); @@ -2070,17 +2078,17 @@ yyreduce: break; case 33: -#line 281 "engines/director/lingo/lingo-gr.y" +#line 284 "engines/director/lingo/lingo-gr.y" { (yyval.code) = 0; ;} break; case 34: -#line 282 "engines/director/lingo/lingo-gr.y" +#line 285 "engines/director/lingo/lingo-gr.y" { (yyval.code) = (yyvsp[(2) - (3)].code); ;} break; case 39: -#line 293 "engines/director/lingo/lingo-gr.y" +#line 296 "engines/director/lingo/lingo-gr.y" { inst then = 0; WRITE_UINT32(&then, (yyvsp[(4) - (6)].code)); @@ -2090,7 +2098,7 @@ yyreduce: break; case 41: -#line 302 "engines/director/lingo/lingo-gr.y" +#line 305 "engines/director/lingo/lingo-gr.y" { inst then = 0; WRITE_UINT32(&then, (yyvsp[(4) - (5)].code)); @@ -2100,22 +2108,22 @@ yyreduce: break; case 42: -#line 310 "engines/director/lingo/lingo-gr.y" +#line 313 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(STOP); ;} break; case 43: -#line 311 "engines/director/lingo/lingo-gr.y" +#line 314 "engines/director/lingo/lingo-gr.y" { g_lingo->code2(g_lingo->c_eq, STOP); ;} break; case 45: -#line 314 "engines/director/lingo/lingo-gr.y" +#line 317 "engines/director/lingo/lingo-gr.y" { (yyval.code) = g_lingo->code3(g_lingo->c_repeatwhilecode, STOP, STOP); ;} break; case 46: -#line 316 "engines/director/lingo/lingo-gr.y" +#line 319 "engines/director/lingo/lingo-gr.y" { (yyval.code) = g_lingo->code3(g_lingo->c_repeatwithcode, STOP, STOP); g_lingo->code3(STOP, STOP, STOP); @@ -2124,7 +2132,7 @@ yyreduce: break; case 47: -#line 322 "engines/director/lingo/lingo-gr.y" +#line 325 "engines/director/lingo/lingo-gr.y" { (yyval.code) = g_lingo->code1(g_lingo->c_ifcode); g_lingo->code3(STOP, STOP, STOP); @@ -2133,7 +2141,7 @@ yyreduce: break; case 48: -#line 328 "engines/director/lingo/lingo-gr.y" +#line 331 "engines/director/lingo/lingo-gr.y" { inst skipEnd; WRITE_UINT32(&skipEnd, 1); // We have to skip end to avoid multiple executions @@ -2143,64 +2151,73 @@ yyreduce: break; case 49: -#line 335 "engines/director/lingo/lingo-gr.y" +#line 338 "engines/director/lingo/lingo-gr.y" { (yyval.code) = g_lingo->_currentScript->size(); ;} break; case 50: -#line 337 "engines/director/lingo/lingo-gr.y" +#line 340 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(STOP); (yyval.code) = g_lingo->_currentScript->size(); ;} break; case 51: -#line 339 "engines/director/lingo/lingo-gr.y" +#line 342 "engines/director/lingo/lingo-gr.y" { (yyval.code) = g_lingo->_currentScript->size(); ;} break; case 54: -#line 344 "engines/director/lingo/lingo-gr.y" - { (yyval.code) = g_lingo->codeConst((yyvsp[(1) - (1)].i)); ;} +#line 347 "engines/director/lingo/lingo-gr.y" + { + (yyval.code) = g_lingo->code1(g_lingo->c_whencode); + g_lingo->code1(STOP); + g_lingo->codeString((yyvsp[(2) - (3)].s)->c_str()); + delete (yyvsp[(2) - (3)].s); ;} break; case 55: -#line 345 "engines/director/lingo/lingo-gr.y" +#line 353 "engines/director/lingo/lingo-gr.y" + { (yyval.code) = g_lingo->codeConst((yyvsp[(1) - (1)].i)); ;} + break; + + case 56: +#line 354 "engines/director/lingo/lingo-gr.y" { (yyval.code) = g_lingo->code1(g_lingo->c_fconstpush); g_lingo->codeFloat((yyvsp[(1) - (1)].f)); ;} break; - case 56: -#line 348 "engines/director/lingo/lingo-gr.y" + case 57: +#line 357 "engines/director/lingo/lingo-gr.y" { (yyval.code) = g_lingo->code1(g_lingo->c_stringpush); g_lingo->codeString((yyvsp[(1) - (1)].s)->c_str()); ;} break; - case 57: -#line 351 "engines/director/lingo/lingo-gr.y" + case 58: +#line 360 "engines/director/lingo/lingo-gr.y" { (yyval.code) = g_lingo->code1(g_lingo->_handlers[*(yyvsp[(1) - (1)].s)]->u.func); g_lingo->codeConst(0); // Put dummy value delete (yyvsp[(1) - (1)].s); ;} break; - case 58: -#line 355 "engines/director/lingo/lingo-gr.y" + case 59: +#line 364 "engines/director/lingo/lingo-gr.y" { (yyval.code) = g_lingo->codeFunc((yyvsp[(1) - (4)].s), (yyvsp[(3) - (4)].narg)); delete (yyvsp[(1) - (4)].s); ;} break; - case 59: -#line 358 "engines/director/lingo/lingo-gr.y" + case 60: +#line 367 "engines/director/lingo/lingo-gr.y" { (yyval.code) = g_lingo->code1(g_lingo->c_eval); g_lingo->codeString((yyvsp[(1) - (1)].s)->c_str()); delete (yyvsp[(1) - (1)].s); ;} break; - case 60: -#line 362 "engines/director/lingo/lingo-gr.y" + case 61: +#line 371 "engines/director/lingo/lingo-gr.y" { (yyval.code) = g_lingo->codeConst(0); // Put dummy id g_lingo->code1(g_lingo->c_theentitypush); @@ -2210,8 +2227,8 @@ yyreduce: g_lingo->code2(e, f); ;} break; - case 61: -#line 369 "engines/director/lingo/lingo-gr.y" + case 62: +#line 378 "engines/director/lingo/lingo-gr.y" { (yyval.code) = g_lingo->code1(g_lingo->c_theentitypush); inst e = 0, f = 0; @@ -2220,237 +2237,237 @@ yyreduce: g_lingo->code2(e, f); ;} break; - case 63: -#line 376 "engines/director/lingo/lingo-gr.y" + case 64: +#line 385 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_add); ;} break; - case 64: -#line 377 "engines/director/lingo/lingo-gr.y" + case 65: +#line 386 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_sub); ;} break; - case 65: -#line 378 "engines/director/lingo/lingo-gr.y" + case 66: +#line 387 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_mul); ;} break; - case 66: -#line 379 "engines/director/lingo/lingo-gr.y" + case 67: +#line 388 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_div); ;} break; - case 67: -#line 380 "engines/director/lingo/lingo-gr.y" + case 68: +#line 389 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_gt); ;} break; - case 68: -#line 381 "engines/director/lingo/lingo-gr.y" + case 69: +#line 390 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_lt); ;} break; - case 69: -#line 382 "engines/director/lingo/lingo-gr.y" + case 70: +#line 391 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_neq); ;} break; - case 70: -#line 383 "engines/director/lingo/lingo-gr.y" + case 71: +#line 392 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_ge); ;} break; - case 71: -#line 384 "engines/director/lingo/lingo-gr.y" + case 72: +#line 393 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_le); ;} break; - case 72: -#line 385 "engines/director/lingo/lingo-gr.y" + case 73: +#line 394 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_and); ;} break; - case 73: -#line 386 "engines/director/lingo/lingo-gr.y" + case 74: +#line 395 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_or); ;} break; - case 74: -#line 387 "engines/director/lingo/lingo-gr.y" + case 75: +#line 396 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_not); ;} break; - case 75: -#line 388 "engines/director/lingo/lingo-gr.y" + case 76: +#line 397 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_ampersand); ;} break; - case 76: -#line 389 "engines/director/lingo/lingo-gr.y" + case 77: +#line 398 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_concat); ;} break; - case 77: -#line 390 "engines/director/lingo/lingo-gr.y" + case 78: +#line 399 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_contains); ;} break; - case 78: -#line 391 "engines/director/lingo/lingo-gr.y" + case 79: +#line 400 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_starts); ;} break; - case 79: -#line 392 "engines/director/lingo/lingo-gr.y" + case 80: +#line 401 "engines/director/lingo/lingo-gr.y" { (yyval.code) = (yyvsp[(2) - (2)].code); ;} break; - case 80: -#line 393 "engines/director/lingo/lingo-gr.y" + case 81: +#line 402 "engines/director/lingo/lingo-gr.y" { (yyval.code) = (yyvsp[(2) - (2)].code); g_lingo->code1(g_lingo->c_negate); ;} break; - case 81: -#line 394 "engines/director/lingo/lingo-gr.y" + case 82: +#line 403 "engines/director/lingo/lingo-gr.y" { (yyval.code) = (yyvsp[(2) - (3)].code); ;} break; - case 82: -#line 395 "engines/director/lingo/lingo-gr.y" + case 83: +#line 404 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_intersects); ;} break; - case 83: -#line 396 "engines/director/lingo/lingo-gr.y" + case 84: +#line 405 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_within); ;} break; - case 84: -#line 399 "engines/director/lingo/lingo-gr.y" + case 85: +#line 408 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_printtop); ;} break; - case 87: -#line 402 "engines/director/lingo/lingo-gr.y" + case 88: +#line 411 "engines/director/lingo/lingo-gr.y" { g_lingo->codeConst(0); // Push fake value on stack g_lingo->code1(g_lingo->c_procret); ;} break; - case 89: -#line 405 "engines/director/lingo/lingo-gr.y" + case 90: +#line 414 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->_handlers[*(yyvsp[(1) - (2)].s)]->u.func); delete (yyvsp[(1) - (2)].s); ;} break; - case 90: -#line 408 "engines/director/lingo/lingo-gr.y" + case 91: +#line 417 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->_handlers[*(yyvsp[(1) - (2)].s)]->u.func); delete (yyvsp[(1) - (2)].s); ;} break; - case 91: -#line 411 "engines/director/lingo/lingo-gr.y" + case 92: +#line 420 "engines/director/lingo/lingo-gr.y" { g_lingo->code2(g_lingo->c_voidpush, g_lingo->_handlers[*(yyvsp[(1) - (1)].s)]->u.func); delete (yyvsp[(1) - (1)].s); ;} break; - case 92: -#line 414 "engines/director/lingo/lingo-gr.y" + case 93: +#line 423 "engines/director/lingo/lingo-gr.y" { g_lingo->codeFunc((yyvsp[(1) - (2)].s), (yyvsp[(2) - (2)].narg)); ;} break; - case 93: -#line 415 "engines/director/lingo/lingo-gr.y" + case 94: +#line 424 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_open); ;} break; - case 94: -#line 416 "engines/director/lingo/lingo-gr.y" + case 95: +#line 425 "engines/director/lingo/lingo-gr.y" { g_lingo->code2(g_lingo->c_voidpush, g_lingo->c_open); ;} break; - case 95: -#line 419 "engines/director/lingo/lingo-gr.y" + case 96: +#line 428 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_global); g_lingo->codeString((yyvsp[(1) - (1)].s)->c_str()); delete (yyvsp[(1) - (1)].s); ;} break; - case 96: -#line 420 "engines/director/lingo/lingo-gr.y" + case 97: +#line 429 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_global); g_lingo->codeString((yyvsp[(3) - (3)].s)->c_str()); delete (yyvsp[(3) - (3)].s); ;} break; - case 97: -#line 431 "engines/director/lingo/lingo-gr.y" + case 98: +#line 440 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_gotoloop); ;} break; - case 98: -#line 432 "engines/director/lingo/lingo-gr.y" + case 99: +#line 441 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_gotonext); ;} break; - case 99: -#line 433 "engines/director/lingo/lingo-gr.y" + case 100: +#line 442 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_gotoprevious); ;} break; - case 100: -#line 434 "engines/director/lingo/lingo-gr.y" + case 101: +#line 443 "engines/director/lingo/lingo-gr.y" { g_lingo->codeConst(1); g_lingo->code1(g_lingo->c_goto); ;} break; - case 101: -#line 437 "engines/director/lingo/lingo-gr.y" + case 102: +#line 446 "engines/director/lingo/lingo-gr.y" { g_lingo->codeConst(3); g_lingo->code1(g_lingo->c_goto); ;} break; - case 102: -#line 440 "engines/director/lingo/lingo-gr.y" + case 103: +#line 449 "engines/director/lingo/lingo-gr.y" { g_lingo->codeConst(2); g_lingo->code1(g_lingo->c_goto); ;} break; - case 107: -#line 453 "engines/director/lingo/lingo-gr.y" + case 108: +#line 462 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_playdone); ;} break; - case 108: -#line 454 "engines/director/lingo/lingo-gr.y" + case 109: +#line 463 "engines/director/lingo/lingo-gr.y" { g_lingo->codeConst(1); g_lingo->code1(g_lingo->c_play); ;} break; - case 109: -#line 457 "engines/director/lingo/lingo-gr.y" + case 110: +#line 466 "engines/director/lingo/lingo-gr.y" { g_lingo->codeConst(3); g_lingo->code1(g_lingo->c_play); ;} break; - case 110: -#line 460 "engines/director/lingo/lingo-gr.y" + case 111: +#line 469 "engines/director/lingo/lingo-gr.y" { g_lingo->codeConst(2); g_lingo->code1(g_lingo->c_play); ;} break; - case 111: -#line 490 "engines/director/lingo/lingo-gr.y" + case 112: +#line 499 "engines/director/lingo/lingo-gr.y" { g_lingo->_indef = true; g_lingo->_currentFactory.clear(); ;} break; - case 112: -#line 491 "engines/director/lingo/lingo-gr.y" + case 113: +#line 500 "engines/director/lingo/lingo-gr.y" { g_lingo->codeConst(0); // Push fake value on stack g_lingo->code1(g_lingo->c_procret); @@ -2458,53 +2475,53 @@ yyreduce: g_lingo->_indef = false; ;} break; - case 113: -#line 496 "engines/director/lingo/lingo-gr.y" + case 114: +#line 505 "engines/director/lingo/lingo-gr.y" { g_lingo->codeFactory(*(yyvsp[(2) - (2)].s)); ;} break; - case 114: -#line 499 "engines/director/lingo/lingo-gr.y" + case 115: +#line 508 "engines/director/lingo/lingo-gr.y" { g_lingo->_indef = true; ;} break; - case 115: -#line 500 "engines/director/lingo/lingo-gr.y" + case 116: +#line 509 "engines/director/lingo/lingo-gr.y" { - g_lingo->code1(STOP); + g_lingo->code1(g_lingo->c_procret); g_lingo->define(*(yyvsp[(2) - (8)].s), (yyvsp[(4) - (8)].code), (yyvsp[(5) - (8)].narg) + 1, &g_lingo->_currentFactory); g_lingo->_indef = false; ;} break; - case 116: -#line 504 "engines/director/lingo/lingo-gr.y" + case 117: +#line 513 "engines/director/lingo/lingo-gr.y" { (yyval.narg) = 0; ;} break; - case 117: -#line 505 "engines/director/lingo/lingo-gr.y" + case 118: +#line 514 "engines/director/lingo/lingo-gr.y" { g_lingo->codeArg((yyvsp[(1) - (1)].s)); (yyval.narg) = 1; ;} break; - case 118: -#line 506 "engines/director/lingo/lingo-gr.y" + case 119: +#line 515 "engines/director/lingo/lingo-gr.y" { g_lingo->codeArg((yyvsp[(3) - (3)].s)); (yyval.narg) = (yyvsp[(1) - (3)].narg) + 1; ;} break; - case 119: -#line 507 "engines/director/lingo/lingo-gr.y" + case 120: +#line 516 "engines/director/lingo/lingo-gr.y" { g_lingo->codeArg((yyvsp[(4) - (4)].s)); (yyval.narg) = (yyvsp[(1) - (4)].narg) + 1; ;} break; - case 120: -#line 509 "engines/director/lingo/lingo-gr.y" + case 121: +#line 518 "engines/director/lingo/lingo-gr.y" { g_lingo->codeArgStore(); ;} break; - case 121: -#line 513 "engines/director/lingo/lingo-gr.y" + case 122: +#line 522 "engines/director/lingo/lingo-gr.y" { g_lingo->code1(g_lingo->c_call); g_lingo->codeString((yyvsp[(1) - (3)].s)->c_str()); @@ -2513,24 +2530,24 @@ yyreduce: g_lingo->code1(numpar); ;} break; - case 122: -#line 521 "engines/director/lingo/lingo-gr.y" + case 123: +#line 530 "engines/director/lingo/lingo-gr.y" { (yyval.narg) = 0; ;} break; - case 123: -#line 522 "engines/director/lingo/lingo-gr.y" + case 124: +#line 531 "engines/director/lingo/lingo-gr.y" { (yyval.narg) = 1; ;} break; - case 124: -#line 523 "engines/director/lingo/lingo-gr.y" + case 125: +#line 532 "engines/director/lingo/lingo-gr.y" { (yyval.narg) = (yyvsp[(1) - (3)].narg) + 1; ;} break; /* Line 1267 of yacc.c. */ -#line 2534 "engines/director/lingo/lingo-gr.cpp" +#line 2551 "engines/director/lingo/lingo-gr.cpp" default: break; } YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); @@ -2744,6 +2761,6 @@ yyreturn: } -#line 526 "engines/director/lingo/lingo-gr.y" +#line 535 "engines/director/lingo/lingo-gr.y" diff --git a/engines/director/lingo/lingo-gr.y b/engines/director/lingo/lingo-gr.y index ea66bc6fd9..f74244e5fd 100644 --- a/engines/director/lingo/lingo-gr.y +++ b/engines/director/lingo/lingo-gr.y @@ -90,7 +90,7 @@ void yyerror(char *s) { %token tCONCAT tCONTAINS tSTARTS %token tSPRITE tINTERSECTS tWITHIN -%type<code> asgn begin elseif elsestmtoneliner end expr if repeatwhile repeatwith stmtlist +%type<code> asgn begin elseif elsestmtoneliner end expr if when repeatwhile repeatwith stmtlist %type<narg> argdef arglist %right '=' @@ -217,8 +217,11 @@ stmt: stmtoneliner (*g_lingo->_currentScript)[$1 + 3] = body; /* body of loop */ (*g_lingo->_currentScript)[$1 + 4] = inc; /* increment */ (*g_lingo->_currentScript)[$1 + 5] = end; } /* end, if cond fails */ - | tWHEN ID tTHEN expr { - g_lingo->code1(g_lingo->c_ifcode); + | when expr end { + inst end = 0; + WRITE_UINT32(&end, $3); + g_lingo->code1(STOP); + (*g_lingo->_currentScript)[$1 + 1] = end; } ; @@ -341,6 +344,12 @@ stmtlist: /* nothing */ { $$ = g_lingo->_currentScript->size(); } | stmtlist stmt ; +when: tWHEN ID tTHEN { + $$ = g_lingo->code1(g_lingo->c_whencode); + g_lingo->code1(STOP); + g_lingo->codeString($2->c_str()); + delete $2; } + expr: INT { $$ = g_lingo->codeConst($1); } | FLOAT { $$ = g_lingo->code1(g_lingo->c_fconstpush); @@ -498,7 +507,7 @@ defn: tMACRO ID { g_lingo->_indef = true; g_lingo->_currentFactory.clear(); } } | tMETHOD ID { g_lingo->_indef = true; } begin argdef nl argstore stmtlist { - g_lingo->code1(STOP); + g_lingo->code1(g_lingo->c_procret); g_lingo->define(*$2, $4, $5 + 1, &g_lingo->_currentFactory); g_lingo->_indef = false; } ; argdef: /* nothing */ { $$ = 0; } diff --git a/engines/director/lingo/lingo-lex.cpp b/engines/director/lingo/lingo-lex.cpp index 5fbd8d8653..6953c2b96f 100644 --- a/engines/director/lingo/lingo-lex.cpp +++ b/engines/director/lingo/lingo-lex.cpp @@ -364,8 +364,8 @@ static void yy_fatal_error (yyconst char msg[] ); *yy_cp = '\0'; \ (yy_c_buf_p) = yy_cp; -#define YY_NUM_RULES 56 -#define YY_END_OF_BUFFER 57 +#define YY_NUM_RULES 57 +#define YY_END_OF_BUFFER 58 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info @@ -373,28 +373,30 @@ struct yy_trans_info flex_int32_t yy_verify; flex_int32_t yy_nxt; }; -static yyconst flex_int16_t yy_accept[191] = +static yyconst flex_int16_t yy_accept[199] = { 0, - 0, 0, 57, 55, 3, 53, 53, 55, 55, 52, - 52, 52, 51, 52, 52, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, - 49, 49, 2, 2, 3, 53, 0, 0, 0, 0, - 0, 54, 48, 1, 50, 51, 47, 45, 46, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 18, - 8, 49, 49, 49, 49, 49, 49, 49, 27, 49, - 29, 49, 49, 49, 49, 49, 49, 49, 49, 39, - 49, 49, 2, 2, 0, 1, 50, 4, 49, 49, - 49, 49, 12, 49, 49, 49, 49, 0, 49, 49, - - 49, 49, 49, 49, 26, 49, 49, 49, 32, 49, - 34, 49, 49, 49, 49, 49, 49, 0, 49, 6, - 7, 11, 14, 49, 49, 49, 0, 49, 20, 21, - 49, 49, 49, 25, 28, 30, 49, 49, 49, 49, - 0, 38, 43, 49, 41, 10, 49, 49, 15, 49, - 17, 49, 22, 49, 24, 49, 49, 49, 49, 37, - 44, 49, 0, 49, 49, 16, 49, 23, 49, 33, - 40, 35, 0, 42, 0, 49, 13, 49, 49, 0, - 9, 5, 49, 31, 0, 49, 0, 19, 36, 0 + 0, 0, 58, 56, 3, 54, 54, 56, 56, 53, + 53, 53, 52, 53, 53, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 2, 2, 3, 54, 0, 0, 0, 0, + 0, 55, 49, 1, 51, 52, 48, 46, 47, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 18, + 8, 50, 50, 50, 50, 50, 50, 50, 27, 50, + 29, 50, 50, 50, 50, 50, 50, 50, 50, 40, + 50, 50, 2, 2, 0, 1, 51, 4, 50, 50, + 50, 50, 12, 50, 50, 50, 50, 0, 50, 50, + + 50, 50, 50, 50, 26, 50, 50, 50, 32, 50, + 34, 50, 50, 50, 50, 50, 50, 0, 50, 6, + 7, 11, 14, 50, 50, 50, 0, 50, 20, 21, + 50, 50, 50, 25, 28, 30, 50, 50, 50, 50, + 0, 39, 44, 50, 42, 10, 50, 50, 15, 50, + 17, 50, 22, 50, 24, 50, 50, 50, 50, 38, + 38, 45, 50, 0, 50, 50, 16, 50, 23, 50, + 33, 41, 35, 0, 38, 43, 0, 50, 13, 50, + 50, 0, 38, 9, 5, 50, 31, 0, 38, 50, + 0, 0, 19, 37, 0, 0, 36, 0 + } ; static yyconst flex_int32_t yy_ec[256] = @@ -407,12 +409,12 @@ static yyconst flex_int32_t yy_ec[256] = 11, 11, 11, 11, 11, 11, 11, 7, 1, 12, 13, 14, 1, 1, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 24, 25, 26, 27, 28, 29, - 24, 30, 31, 32, 33, 34, 35, 36, 37, 24, - 1, 1, 1, 7, 38, 1, 39, 40, 41, 42, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 24, + 1, 1, 1, 7, 39, 1, 40, 41, 42, 43, - 43, 44, 45, 46, 47, 24, 24, 48, 49, 50, - 51, 52, 24, 53, 54, 55, 56, 57, 58, 59, - 60, 24, 1, 1, 1, 1, 1, 1, 1, 1, + 44, 45, 46, 47, 48, 24, 24, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 62, 24, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -429,130 +431,139 @@ static yyconst flex_int32_t yy_ec[256] = 1, 1, 1, 1, 1 } ; -static yyconst flex_int32_t yy_meta[61] = +static yyconst flex_int32_t yy_meta[63] = { 0, 1, 2, 3, 3, 2, 1, 1, 1, 1, 1, 4, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 4, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 4, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 + 5, 5 } ; -static yyconst flex_int16_t yy_base[196] = +static yyconst flex_int16_t yy_base[204] = { 0, - 0, 59, 203, 452, 63, 67, 71, 75, 167, 452, - 157, 154, 52, 68, 143, 56, 0, 56, 57, 67, - 72, 68, 68, 69, 85, 102, 102, 104, 80, 119, - 113, 120, 173, 177, 181, 452, 185, 189, 193, 102, - 99, 452, 452, 0, 80, 129, 452, 452, 452, 0, - 91, 120, 165, 118, 126, 146, 184, 187, 171, 96, - 0, 172, 177, 189, 178, 177, 178, 184, 0, 188, - 0, 202, 199, 188, 192, 192, 199, 220, 219, 0, - 226, 208, 251, 262, 217, 0, 78, 0, 219, 227, - 230, 239, 0, 228, 229, 242, 256, 273, 257, 250, - - 251, 255, 263, 256, 0, 262, 253, 258, 0, 274, - 0, 271, 267, 304, 271, 274, 273, 284, 299, 0, - 0, 0, 0, 279, 297, 308, 297, 296, 0, 0, - 301, 304, 314, 0, 0, 0, 311, 320, 305, 307, - 156, 0, 0, 322, 319, 341, 321, 320, 0, 326, - 452, 322, 0, 327, 0, 328, 329, 344, 336, 370, - 0, 343, 375, 344, 341, 0, 345, 0, 348, 0, - 0, 0, 380, 0, 363, 355, 0, 372, 360, 372, - 452, 0, 363, 0, 394, 366, 398, 0, 400, 452, - 431, 433, 438, 442, 446 - + 0, 61, 156, 509, 65, 69, 73, 77, 148, 509, + 104, 99, 54, 70, 91, 58, 0, 58, 59, 69, + 74, 70, 70, 71, 88, 105, 105, 110, 82, 118, + 117, 130, 177, 181, 185, 509, 189, 193, 197, 106, + 94, 509, 509, 0, 82, 132, 509, 509, 509, 0, + 84, 119, 169, 116, 120, 174, 186, 191, 180, 171, + 0, 176, 182, 194, 181, 180, 181, 186, 0, 202, + 0, 208, 205, 193, 198, 197, 204, 225, 225, 0, + 233, 214, 258, 270, 223, 0, 80, 0, 225, 226, + 237, 234, 0, 233, 234, 242, 260, 278, 265, 257, + + 258, 265, 271, 263, 0, 270, 260, 264, 0, 281, + 0, 279, 273, 303, 280, 289, 293, 297, 303, 0, + 0, 0, 0, 296, 307, 317, 301, 303, 0, 0, + 308, 309, 320, 0, 0, 0, 319, 329, 313, 314, + 347, 0, 0, 331, 332, 202, 333, 328, 0, 337, + 509, 331, 0, 347, 0, 340, 339, 347, 341, 301, + 372, 0, 349, 383, 357, 351, 0, 363, 0, 359, + 0, 0, 0, 393, 399, 0, 374, 367, 0, 392, + 373, 390, 409, 509, 0, 379, 0, 413, 420, 384, + 422, 436, 0, 441, 408, 442, 446, 509, 488, 490, + + 495, 499, 503 } ; -static yyconst flex_int16_t yy_def[196] = +static yyconst flex_int16_t yy_def[204] = { 0, - 190, 1, 190, 190, 190, 190, 190, 190, 191, 190, - 190, 190, 190, 190, 190, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 190, 190, 190, 190, 190, 190, 190, 190, - 191, 190, 190, 193, 190, 190, 190, 190, 190, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 190, 190, 190, 193, 190, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 190, 192, 192, - - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 190, 192, 192, - 192, 192, 192, 192, 192, 192, 190, 192, 192, 192, - 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, - 194, 192, 192, 192, 192, 190, 192, 192, 192, 192, - 190, 192, 192, 192, 192, 192, 192, 192, 192, 194, - 192, 192, 190, 192, 192, 192, 192, 192, 192, 192, - 192, 192, 190, 192, 190, 192, 192, 192, 192, 190, - 190, 192, 192, 192, 190, 192, 195, 192, 195, 0, - 190, 190, 190, 190, 190 - + 198, 1, 198, 198, 198, 198, 198, 198, 199, 198, + 198, 198, 198, 198, 198, 200, 200, 200, 200, 200, + 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, + 200, 200, 198, 198, 198, 198, 198, 198, 198, 198, + 199, 198, 198, 201, 198, 198, 198, 198, 198, 200, + 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, + 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, + 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, + 200, 200, 198, 198, 198, 201, 198, 200, 200, 200, + 200, 200, 200, 200, 200, 200, 200, 198, 200, 200, + + 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, + 200, 200, 200, 200, 200, 200, 200, 198, 200, 200, + 200, 200, 200, 200, 200, 200, 198, 200, 200, 200, + 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, + 202, 200, 200, 200, 200, 198, 200, 200, 200, 200, + 198, 200, 200, 200, 200, 200, 200, 200, 200, 202, + 202, 200, 200, 198, 200, 200, 200, 200, 200, 200, + 200, 200, 200, 198, 202, 200, 198, 200, 200, 200, + 200, 198, 202, 198, 200, 200, 200, 198, 202, 200, + 203, 198, 200, 203, 198, 198, 203, 0, 198, 198, + + 198, 198, 198 } ; -static yyconst flex_int16_t yy_nxt[513] = +static yyconst flex_int16_t yy_nxt[572] = { 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 4, 13, 14, 10, 15, 16, 17, 18, 19, 20, 21, - 22, 17, 23, 17, 24, 25, 26, 27, 28, 29, - 30, 31, 17, 17, 32, 17, 17, 17, 16, 17, - 18, 19, 20, 21, 22, 17, 23, 24, 25, 26, - 27, 28, 29, 30, 31, 17, 17, 32, 17, 17, - 33, 45, 46, 34, 35, 36, 36, 37, 38, 39, - 39, 38, 38, 39, 39, 38, 37, 36, 36, 37, - 47, 48, 51, 52, 53, 40, 57, 61, 87, 40, - 87, 54, 59, 55, 62, 60, 63, 98, 75, 64, - - 98, 58, 56, 65, 42, 51, 52, 53, 88, 40, - 57, 61, 66, 40, 54, 59, 55, 62, 60, 63, - 67, 69, 75, 64, 58, 56, 85, 65, 72, 68, - 70, 71, 88, 73, 79, 66, 74, 76, 45, 46, - 80, 81, 82, 93, 67, 69, 89, 77, 92, 85, - 78, 72, 68, 70, 71, 49, 73, 141, 79, 74, - 141, 76, 44, 80, 43, 81, 82, 93, 94, 89, - 77, 92, 42, 78, 83, 36, 36, 84, 84, 36, - 36, 84, 35, 36, 36, 37, 37, 36, 36, 37, - 38, 90, 94, 38, 38, 39, 39, 38, 97, 91, - - 95, 96, 190, 99, 100, 101, 106, 40, 190, 102, - 103, 40, 190, 104, 90, 105, 107, 108, 190, 109, - 110, 97, 91, 111, 95, 96, 99, 100, 112, 101, - 106, 40, 102, 103, 113, 40, 104, 114, 105, 117, - 107, 108, 109, 110, 115, 120, 111, 118, 116, 190, - 119, 112, 83, 36, 36, 84, 121, 122, 113, 123, - 124, 114, 117, 84, 36, 36, 84, 125, 115, 120, - 118, 126, 116, 119, 98, 128, 132, 98, 130, 121, - 131, 122, 123, 124, 129, 133, 190, 134, 135, 136, - 125, 137, 138, 139, 145, 126, 140, 143, 144, 128, - - 132, 130, 146, 131, 127, 141, 148, 129, 141, 133, - 134, 135, 136, 147, 137, 149, 138, 139, 145, 140, - 143, 144, 150, 190, 151, 152, 146, 127, 153, 148, - 142, 154, 155, 156, 157, 190, 158, 147, 159, 149, - 161, 162, 163, 164, 168, 163, 150, 151, 152, 165, - 166, 153, 167, 142, 154, 169, 155, 156, 157, 158, - 170, 159, 171, 178, 161, 162, 172, 164, 168, 174, - 176, 173, 165, 166, 173, 167, 163, 177, 169, 163, - 179, 173, 181, 170, 173, 182, 171, 178, 183, 172, - 184, 185, 174, 176, 186, 187, 188, 175, 187, 187, - - 177, 190, 187, 179, 190, 190, 181, 180, 182, 190, - 190, 190, 183, 184, 190, 185, 190, 186, 190, 188, - 190, 175, 190, 190, 190, 190, 190, 190, 190, 190, - 180, 41, 41, 190, 41, 41, 50, 50, 86, 86, - 190, 86, 86, 160, 190, 190, 160, 189, 190, 190, - 189, 3, 190, 190, 190, 190, 190, 190, 190, 190, - 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, - 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, - 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, - 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, - - 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, - 190, 190 + 22, 17, 23, 17, 24, 25, 26, 27, 28, 17, + 29, 30, 31, 17, 17, 32, 17, 17, 17, 16, + 17, 18, 19, 20, 21, 22, 17, 23, 24, 25, + 26, 27, 28, 17, 29, 30, 31, 17, 17, 32, + 17, 17, 33, 45, 46, 34, 35, 36, 36, 37, + 38, 39, 39, 38, 38, 39, 39, 38, 37, 36, + 36, 37, 47, 48, 51, 52, 53, 40, 57, 61, + 87, 40, 87, 54, 59, 55, 62, 60, 63, 42, + + 75, 88, 64, 49, 58, 56, 65, 44, 51, 52, + 53, 43, 40, 57, 61, 66, 40, 54, 59, 55, + 62, 60, 63, 67, 69, 75, 88, 64, 58, 56, + 85, 65, 68, 70, 72, 71, 76, 93, 79, 66, + 73, 45, 46, 74, 80, 89, 77, 92, 67, 69, + 78, 81, 82, 42, 85, 198, 68, 70, 72, 71, + 198, 76, 93, 79, 73, 198, 198, 74, 80, 89, + 77, 92, 98, 198, 78, 98, 81, 82, 83, 36, + 36, 84, 84, 36, 36, 84, 35, 36, 36, 37, + 37, 36, 36, 37, 38, 90, 94, 38, 38, 39, + + 39, 38, 95, 164, 91, 96, 164, 97, 99, 100, + 101, 40, 198, 102, 103, 40, 198, 104, 105, 90, + 106, 94, 107, 108, 198, 109, 110, 95, 91, 111, + 96, 97, 99, 100, 112, 101, 40, 102, 103, 113, + 40, 104, 105, 114, 120, 106, 117, 107, 108, 109, + 110, 115, 122, 111, 118, 116, 198, 119, 112, 83, + 36, 36, 84, 121, 113, 123, 124, 125, 114, 120, + 117, 84, 36, 36, 84, 126, 115, 122, 118, 98, + 116, 119, 98, 128, 198, 130, 132, 121, 131, 123, + 124, 125, 129, 133, 198, 134, 135, 136, 137, 138, + + 126, 139, 174, 140, 141, 174, 143, 141, 128, 130, + 127, 132, 131, 144, 145, 146, 129, 147, 133, 134, + 135, 136, 137, 148, 138, 149, 139, 140, 151, 142, + 143, 150, 198, 152, 127, 153, 154, 144, 155, 145, + 146, 156, 147, 157, 198, 158, 159, 148, 141, 162, + 149, 141, 151, 142, 163, 165, 150, 152, 166, 153, + 154, 167, 168, 155, 169, 172, 156, 170, 157, 158, + 159, 171, 173, 174, 162, 176, 174, 198, 161, 163, + 165, 180, 166, 178, 164, 167, 168, 164, 179, 169, + 172, 170, 181, 184, 174, 171, 173, 174, 185, 176, + + 174, 175, 161, 174, 187, 177, 180, 178, 186, 188, + 174, 190, 179, 174, 191, 193, 181, 191, 184, 198, + 182, 192, 185, 191, 192, 175, 191, 196, 187, 183, + 177, 198, 198, 186, 188, 190, 198, 192, 198, 193, + 192, 189, 198, 197, 182, 198, 197, 197, 198, 198, + 197, 198, 196, 183, 198, 198, 198, 198, 198, 198, + 198, 198, 198, 195, 198, 189, 198, 198, 198, 198, + 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, + 198, 198, 198, 198, 198, 198, 198, 195, 41, 41, + 198, 41, 41, 50, 50, 86, 86, 198, 86, 86, + + 160, 198, 198, 160, 194, 198, 198, 194, 3, 198, + 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, + 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, + 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, + 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, + 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, + 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, + 198 } ; -static yyconst flex_int16_t yy_chk[513] = +static yyconst flex_int16_t yy_chk[572] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -560,57 +571,63 @@ static yyconst flex_int16_t yy_chk[513] = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 2, 13, 13, 2, 5, 5, 5, 5, 6, 6, - 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, - 14, 14, 16, 18, 19, 6, 21, 23, 87, 7, - 45, 20, 22, 20, 23, 22, 24, 60, 29, 25, - - 60, 21, 20, 25, 41, 16, 18, 19, 51, 6, - 21, 23, 25, 7, 20, 22, 20, 23, 22, 24, - 26, 27, 29, 25, 21, 20, 40, 25, 28, 26, - 27, 27, 51, 28, 31, 25, 28, 30, 46, 46, - 31, 32, 32, 55, 26, 27, 52, 30, 54, 40, - 30, 28, 26, 27, 27, 15, 28, 141, 31, 28, - 141, 30, 12, 31, 11, 32, 32, 55, 56, 52, - 30, 54, 9, 30, 33, 33, 33, 33, 34, 34, - 34, 34, 35, 35, 35, 35, 37, 37, 37, 37, - 38, 53, 56, 38, 39, 39, 39, 39, 59, 53, - - 57, 58, 3, 62, 63, 64, 70, 38, 0, 65, - 66, 39, 0, 67, 53, 68, 72, 73, 0, 74, - 75, 59, 53, 76, 57, 58, 62, 63, 77, 64, - 70, 38, 65, 66, 78, 39, 67, 79, 68, 82, - 72, 73, 74, 75, 81, 90, 76, 85, 81, 0, - 89, 77, 83, 83, 83, 83, 91, 92, 78, 94, - 95, 79, 82, 84, 84, 84, 84, 96, 81, 90, - 85, 97, 81, 89, 98, 99, 102, 98, 100, 91, - 101, 92, 94, 95, 99, 103, 0, 104, 106, 107, - 96, 108, 110, 112, 117, 97, 113, 115, 116, 99, - - 102, 100, 118, 101, 98, 114, 124, 99, 114, 103, - 104, 106, 107, 119, 108, 125, 110, 112, 117, 113, - 115, 116, 126, 0, 127, 128, 118, 98, 131, 124, - 114, 132, 133, 137, 138, 0, 139, 119, 140, 125, - 144, 145, 146, 147, 154, 146, 126, 127, 128, 148, - 150, 131, 152, 114, 132, 156, 133, 137, 138, 139, - 157, 140, 158, 167, 144, 145, 159, 147, 154, 162, - 164, 160, 148, 150, 160, 152, 163, 165, 156, 163, - 169, 173, 175, 157, 173, 176, 158, 167, 178, 159, - 179, 180, 162, 164, 183, 185, 186, 163, 185, 187, - - 165, 189, 187, 169, 189, 0, 175, 173, 176, 0, - 0, 0, 178, 179, 0, 180, 0, 183, 0, 186, - 0, 163, 0, 0, 0, 0, 0, 0, 0, 0, - 173, 191, 191, 0, 191, 191, 192, 192, 193, 193, - 0, 193, 193, 194, 0, 0, 194, 195, 0, 0, - 195, 190, 190, 190, 190, 190, 190, 190, 190, 190, - 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, - 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, - 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, - 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, - - 190, 190, 190, 190, 190, 190, 190, 190, 190, 190, - 190, 190 + 1, 1, 2, 13, 13, 2, 5, 5, 5, 5, + 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, + 8, 8, 14, 14, 16, 18, 19, 6, 21, 23, + 87, 7, 45, 20, 22, 20, 23, 22, 24, 41, + + 29, 51, 25, 15, 21, 20, 25, 12, 16, 18, + 19, 11, 6, 21, 23, 25, 7, 20, 22, 20, + 23, 22, 24, 26, 27, 29, 51, 25, 21, 20, + 40, 25, 26, 27, 28, 27, 30, 55, 31, 25, + 28, 46, 46, 28, 31, 52, 30, 54, 26, 27, + 30, 32, 32, 9, 40, 3, 26, 27, 28, 27, + 0, 30, 55, 31, 28, 0, 0, 28, 31, 52, + 30, 54, 60, 0, 30, 60, 32, 32, 33, 33, + 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, + 37, 37, 37, 37, 38, 53, 56, 38, 39, 39, + + 39, 39, 57, 146, 53, 58, 146, 59, 62, 63, + 64, 38, 0, 65, 66, 39, 0, 67, 68, 53, + 70, 56, 72, 73, 0, 74, 75, 57, 53, 76, + 58, 59, 62, 63, 77, 64, 38, 65, 66, 78, + 39, 67, 68, 79, 90, 70, 82, 72, 73, 74, + 75, 81, 92, 76, 85, 81, 0, 89, 77, 83, + 83, 83, 83, 91, 78, 94, 95, 96, 79, 90, + 82, 84, 84, 84, 84, 97, 81, 92, 85, 98, + 81, 89, 98, 99, 0, 100, 102, 91, 101, 94, + 95, 96, 99, 103, 0, 104, 106, 107, 108, 110, + + 97, 112, 160, 113, 114, 160, 115, 114, 99, 100, + 98, 102, 101, 116, 117, 118, 99, 119, 103, 104, + 106, 107, 108, 124, 110, 125, 112, 113, 127, 114, + 115, 126, 0, 128, 98, 131, 132, 116, 133, 117, + 118, 137, 119, 138, 0, 139, 140, 124, 141, 144, + 125, 141, 127, 114, 145, 147, 126, 128, 148, 131, + 132, 150, 152, 133, 154, 158, 137, 156, 138, 139, + 140, 157, 159, 161, 144, 163, 161, 0, 141, 145, + 147, 168, 148, 165, 164, 150, 152, 164, 166, 154, + 158, 156, 170, 177, 174, 157, 159, 174, 178, 163, + + 175, 161, 141, 175, 181, 164, 168, 165, 180, 182, + 183, 186, 166, 183, 188, 190, 170, 188, 177, 0, + 174, 189, 178, 191, 189, 161, 191, 195, 181, 175, + 164, 0, 0, 180, 182, 186, 0, 192, 0, 190, + 192, 183, 194, 196, 174, 194, 196, 197, 0, 0, + 197, 0, 195, 175, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 192, 0, 183, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 192, 199, 199, + 0, 199, 199, 200, 200, 201, 201, 0, 201, 201, + + 202, 0, 0, 202, 203, 0, 0, 203, 198, 198, + 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, + 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, + 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, + 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, + 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, + 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, + 198 } ; static yy_state_type yy_last_accepting_state; @@ -684,7 +701,7 @@ static void countnl() { g_lingo->_colnumber = strlen(p); } -#line 688 "engines/director/lingo/lingo-lex.cpp" +#line 705 "engines/director/lingo/lingo-lex.cpp" #define INITIAL 0 @@ -872,7 +889,7 @@ YY_DECL #line 69 "engines/director/lingo/lingo-lex.l" -#line 876 "engines/director/lingo/lingo-lex.cpp" +#line 893 "engines/director/lingo/lingo-lex.cpp" if ( !(yy_init) ) { @@ -926,13 +943,13 @@ yy_match: while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 191 ) + if ( yy_current_state >= 199 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; ++yy_cp; } - while ( yy_base[yy_current_state] != 452 ); + while ( yy_base[yy_current_state] != 509 ); yy_find_action: yy_act = yy_accept[yy_current_state]; @@ -1139,6 +1156,18 @@ YY_RULE_SETUP { count(); + yylval.e[0] = g_lingo->_theEntities["sqrt"]->entity; + yylval.e[1] = 0; // No field + + return THEENTITYWITHID; + } + YY_BREAK +case 37: +YY_RULE_SETUP +#line 115 "engines/director/lingo/lingo-lex.l" +{ + count(); + const char *ptr = &yytext[4]; // Skip 'the ' while (*ptr == ' ' || *ptr == '\t') ptr++; @@ -1177,9 +1206,9 @@ YY_RULE_SETUP warning("Unhandled the entity %s", ptr); } YY_BREAK -case 37: +case 38: YY_RULE_SETUP -#line 147 "engines/director/lingo/lingo-lex.l" +#line 155 "engines/director/lingo/lingo-lex.l" { count(); @@ -1200,64 +1229,64 @@ YY_RULE_SETUP warning("Unhandled the entity %s", ptr); } YY_BREAK -case 38: +case 39: YY_RULE_SETUP -#line 166 "engines/director/lingo/lingo-lex.l" +#line 174 "engines/director/lingo/lingo-lex.l" { count(); return tTHEN; } YY_BREAK -case 39: +case 40: YY_RULE_SETUP -#line 167 "engines/director/lingo/lingo-lex.l" +#line 175 "engines/director/lingo/lingo-lex.l" { count(); return tTO; } YY_BREAK -case 40: +case 41: YY_RULE_SETUP -#line 168 "engines/director/lingo/lingo-lex.l" +#line 176 "engines/director/lingo/lingo-lex.l" { count(); return tSPRITE; } YY_BREAK -case 41: +case 42: YY_RULE_SETUP -#line 169 "engines/director/lingo/lingo-lex.l" +#line 177 "engines/director/lingo/lingo-lex.l" { count(); return tWITH; } YY_BREAK -case 42: +case 43: YY_RULE_SETUP -#line 170 "engines/director/lingo/lingo-lex.l" +#line 178 "engines/director/lingo/lingo-lex.l" { count(); return tWITHIN; } YY_BREAK -case 43: +case 44: YY_RULE_SETUP -#line 171 "engines/director/lingo/lingo-lex.l" +#line 179 "engines/director/lingo/lingo-lex.l" { count(); return tWHEN; } YY_BREAK -case 44: +case 45: YY_RULE_SETUP -#line 172 "engines/director/lingo/lingo-lex.l" +#line 180 "engines/director/lingo/lingo-lex.l" { count(); return tWHILE; } YY_BREAK -case 45: +case 46: YY_RULE_SETUP -#line 174 "engines/director/lingo/lingo-lex.l" +#line 182 "engines/director/lingo/lingo-lex.l" { count(); return tNEQ; } YY_BREAK -case 46: +case 47: YY_RULE_SETUP -#line 175 "engines/director/lingo/lingo-lex.l" +#line 183 "engines/director/lingo/lingo-lex.l" { count(); return tGE; } YY_BREAK -case 47: +case 48: YY_RULE_SETUP -#line 176 "engines/director/lingo/lingo-lex.l" +#line 184 "engines/director/lingo/lingo-lex.l" { count(); return tLE; } YY_BREAK -case 48: +case 49: YY_RULE_SETUP -#line 177 "engines/director/lingo/lingo-lex.l" +#line 185 "engines/director/lingo/lingo-lex.l" { count(); return tCONCAT; } YY_BREAK -case 49: +case 50: YY_RULE_SETUP -#line 179 "engines/director/lingo/lingo-lex.l" +#line 187 "engines/director/lingo/lingo-lex.l" { count(); yylval.s = new Common::String(yytext); @@ -1285,43 +1314,43 @@ YY_RULE_SETUP return ID; } YY_BREAK -case 50: +case 51: YY_RULE_SETUP -#line 205 "engines/director/lingo/lingo-lex.l" +#line 213 "engines/director/lingo/lingo-lex.l" { count(); yylval.f = atof(yytext); return FLOAT; } YY_BREAK -case 51: +case 52: YY_RULE_SETUP -#line 206 "engines/director/lingo/lingo-lex.l" +#line 214 "engines/director/lingo/lingo-lex.l" { count(); yylval.i = strtol(yytext, NULL, 10); return INT; } YY_BREAK -case 52: +case 53: YY_RULE_SETUP -#line 207 "engines/director/lingo/lingo-lex.l" +#line 215 "engines/director/lingo/lingo-lex.l" { count(); return *yytext; } YY_BREAK -case 53: -/* rule 53 can match eol */ +case 54: +/* rule 54 can match eol */ YY_RULE_SETUP -#line 208 "engines/director/lingo/lingo-lex.l" +#line 216 "engines/director/lingo/lingo-lex.l" { return '\n'; } YY_BREAK -case 54: +case 55: YY_RULE_SETUP -#line 209 "engines/director/lingo/lingo-lex.l" +#line 217 "engines/director/lingo/lingo-lex.l" { count(); yylval.s = new Common::String(&yytext[1]); yylval.s->deleteLastChar(); return STRING; } YY_BREAK -case 55: +case 56: YY_RULE_SETUP -#line 210 "engines/director/lingo/lingo-lex.l" +#line 218 "engines/director/lingo/lingo-lex.l" YY_BREAK -case 56: +case 57: YY_RULE_SETUP -#line 212 "engines/director/lingo/lingo-lex.l" +#line 220 "engines/director/lingo/lingo-lex.l" ECHO; YY_BREAK -#line 1325 "engines/director/lingo/lingo-lex.cpp" +#line 1354 "engines/director/lingo/lingo-lex.cpp" case YY_STATE_EOF(INITIAL): yyterminate(); @@ -1614,7 +1643,7 @@ static int yy_get_next_buffer (void) while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 191 ) + if ( yy_current_state >= 199 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; @@ -1642,11 +1671,11 @@ static int yy_get_next_buffer (void) while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 191 ) + if ( yy_current_state >= 199 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; - yy_is_jam = (yy_current_state == 190); + yy_is_jam = (yy_current_state == 198); return yy_is_jam ? 0 : yy_current_state; } @@ -2321,7 +2350,7 @@ void yyfree (void * ptr ) #define YYTABLES_NAME "yytables" -#line 212 "engines/director/lingo/lingo-lex.l" +#line 220 "engines/director/lingo/lingo-lex.l" diff --git a/engines/director/lingo/lingo-lex.l b/engines/director/lingo/lingo-lex.l index 8c89a99764..3fca137e38 100644 --- a/engines/director/lingo/lingo-lex.l +++ b/engines/director/lingo/lingo-lex.l @@ -104,6 +104,14 @@ whitespace [\t ] (?i:repeat) { count(); return tREPEAT; } (?i:set) { count(); return tSET; } (?i:starts) { count(); return tSTARTS; } +(?i:the[ \t]+sqrt[\t ]+of[\t ]+) { + count(); + + yylval.e[0] = g_lingo->_theEntities["sqrt"]->entity; + yylval.e[1] = 0; // No field + + return THEENTITYWITHID; + } (?i:the[ \t]+[[:alpha:]]+[\t ]+of[\t ]+[[:alpha:]]+) { count(); diff --git a/engines/director/lingo/lingo-the.cpp b/engines/director/lingo/lingo-the.cpp index 3b00c092ec..760576ef03 100644 --- a/engines/director/lingo/lingo-the.cpp +++ b/engines/director/lingo/lingo-the.cpp @@ -28,47 +28,59 @@ class Sprite; TheEntity entities[] = { { kTheCast, "cast", true }, - { kTheClickOn, "clickOn", false }, + { kTheClickOn, "clickOn", false }, // D2 function { kTheColorDepth, "colorDepth", false }, - { kTheColorQD, "colorQD", false }, - { kTheCommandDown, "commandDown", false }, - { kTheControlDown, "controlDown", false }, - { kTheDoubleClick, "doubleClick", false }, + { kTheColorQD, "colorQD", false }, // D2 f + { kTheCommandDown, "commandDown", false }, // D2 f + { kTheControlDown, "controlDown", false }, // D2 f + { kTheDoubleClick, "doubleClick", false }, // D2 f { kTheExitLock, "exitlock", false }, { kTheFloatPrecision, "floatPrecision", false }, - { kTheFrame, "frame", false }, - { kTheFreeBlock, "freeBlock", false }, - { kTheFreeBytes, "freeBytes", false }, + { kTheFrame, "frame", false }, // D2 f + { kTheFreeBlock, "freeBlock", false }, // D2 f + { kTheFreeBytes, "freeBytes", false }, // D2 f { kTheItemDelimiter, "itemDelimiter", false }, - { kTheKey, "key", false }, - { kTheKeyCode, "keyCode", false }, - { kTheLastClick, "lastClick", false }, - { kTheLastEvent, "lastEvent", false }, + { kTheKey, "key", false }, // D2 f + { kTheKeyCode, "keyCode", false }, // D2 f + { kTheLastClick, "lastClick", false }, // D2 f + { kTheLastEvent, "lastEvent", false }, // D2 f { kTheLastFrame, "lastFrame", false }, + { kTheLastKey, "lastKey", false }, // D2 f + { kTheLastRoll, "lastRoll", false }, // D2 f + { kTheMachineType, "machineType", false }, // D2 f + { kTheMemorySize, "memorySize", false }, // D2 f { kTheMenu, "menu", true }, { kTheMenus, "menus", false }, { kTheMenuItem, "menuitem", true }, { kTheMenuItems, "menuitems", false }, - { kTheMouseDown, "mouseDown", false }, + { kTheMouseDown, "mouseDown", false }, // D2 f { kTheMouseDownScript, "mouseDownScript", false }, - { kTheMouseH, "mouseh", false }, - { kTheMouseUp, "mouseUp", false }, + { kTheMouseH, "mouseH", false }, // D2 f + { kTheMouseUp, "mouseUp", false }, // D2 f { kTheMouseUpScript, "mouseUpScript", false }, - { kTheMouseV, "mousev", false }, - { kTheMovie, "movie", false }, + { kTheMouseV, "mouseV", false }, // D2 f + { kTheMovie, "movie", false }, // D2 f { kTheMultiSound, "multiSound", false }, - { kTheOptionDown, "optionDown", false }, - { kThePathName, "pathname", false }, + { kTheOptionDown, "optionDown", false }, // D2 f + { kThePathName, "pathName", false }, // D2 f + { kThePauseState, "pauseState", false }, // D2 f { kThePerFrameHook, "perframehook", false }, { kThePreloadEventAbort,"preloadEventAbort",false }, + { kTheResult, "result", false }, // D2 f { kTheRightMouseDown, "rightMouseDown", false }, { kTheRightMouseUp, "rightMouseUp", false }, { kTheRomanLingo, "romanLingo", false }, - { kTheShiftDown, "shiftDown", false }, + { kTheSelection, "selection", false }, // D2 f + { kTheShiftDown, "shiftDown", false }, // D2 f { kTheSprite, "sprite", true }, + { kTheSqrt, "sqrt", false }, // D2 f { kTheStage, "stage", false }, - { kTheStillDown, "stillDown", false }, - { kTheTicks, "ticks", false }, + { kTheStageBottom, "stageBottom", false }, // D2 f + { kTheStageLeft, "stageLeft", false }, // D2 f + { kTheStageRight, "stageRight", false }, // D2 f + { kTheStageTop, "stageTop", false }, // D2 f + { kTheStillDown, "stillDown", false }, // D2 f + { kTheTicks, "ticks", false }, // D2 f { kTheTimeoutLength, "timeoutlength", false }, { kTheTimer, "timer", false }, { kTheWindow, "window", false }, @@ -313,6 +325,7 @@ Datum Lingo::getTheEntity(int entity, Datum &id, int field) { break; case kTheCast: d = getTheCast(id, field); + break; case kThePerFrameHook: warning("STUB: getting the perframehook"); break; @@ -320,6 +333,11 @@ Datum Lingo::getTheEntity(int entity, Datum &id, int field) { d.type = INT; d.u.i = _floatPrecision; break; + case kTheSqrt: + id.toFloat(); + d.type = FLOAT; + d.u.f = sqrt(id.u.f); + break; default: warning("Unprocessed getting field %d of entity %d", field, entity); d.type = VOID; @@ -466,8 +484,8 @@ Datum Lingo::getTheCast(Datum &id1, int field) { return d; } else { warning("The cast %d found", id); - return d; } + cast = _vm->_currentScore->_casts[id]; castInfo = _vm->_currentScore->_castsInfo[id]; diff --git a/engines/director/lingo/lingo-the.h b/engines/director/lingo/lingo-the.h index 7a27c0ad84..468be64140 100644 --- a/engines/director/lingo/lingo-the.h +++ b/engines/director/lingo/lingo-the.h @@ -52,10 +52,15 @@ enum TheEntityType { kTheClickOn, kTheDoubleClick, kTheLastClick, - kTheLastFrame, kTheLastEvent, + kTheLastFrame, + kTheLastKey, + kTheLastRoll, + kTheMachineType, + kTheMemorySize, kTheMouseDown, kTheMouseUp, + kThePauseState, kTheRightMouseUp, kTheRightMouseDown, kTheStillDown, @@ -63,7 +68,10 @@ enum TheEntityType { kTheKeyCode, kTheControlDown, kTheCommandDown, + kTheResult, + kTheSelection, kTheShiftDown, + kTheSqrt, kTheOptionDown, kTheColorDepth, @@ -74,7 +82,11 @@ enum TheEntityType { kTheMultiSound, kThePreloadEventAbort, kTheRomanLingo, - kTheStage + kTheStage, + kTheStageBottom, + kTheStageLeft, + kTheStageRight, + kTheStageTop }; enum TheFieldType { diff --git a/engines/director/lingo/lingo.cpp b/engines/director/lingo/lingo.cpp index f3faf975fe..d37da42670 100644 --- a/engines/director/lingo/lingo.cpp +++ b/engines/director/lingo/lingo.cpp @@ -86,6 +86,7 @@ Lingo::Lingo(DirectorEngine *vm) : _vm(vm) { _eventHandlerTypes[t->handler] = t->name; initBuiltIns(); + initFuncs(); initTheEntities(); _currentScript = 0; @@ -120,17 +121,17 @@ const char *Lingo::findNextDefinition(const char *s) { return NULL; if (!strncmp(res, "macro ", 6)) { - warning("See macro"); + debugC(3, kDebugLingoCompile, "See macro"); return res; } if (!strncmp(res, "factory ", 8)) { - warning("See factory"); + debugC(3, kDebugLingoCompile, "See factory"); return res; } if (!strncmp(res, "method ", 7)) { - warning("See method"); + debugC(3, kDebugLingoCompile, "See method"); return res; } @@ -142,7 +143,7 @@ const char *Lingo::findNextDefinition(const char *s) { } void Lingo::addCode(const char *code, ScriptType type, uint16 id) { - debug(2, "Add code \"%s\" for type %d with id %d", code, type, id); + debugC(2, kDebugLingoCompile, "Add code \"%s\" for type %d with id %d", code, type, id); if (_scripts[type].contains(id)) { delete _scripts[type][id]; @@ -176,10 +177,18 @@ void Lingo::addCode(const char *code, ScriptType type, uint16 id) { else _inFactory = false; - debug(2, "Code chunk:\n#####\n%s#####", chunk.c_str()); + debugC(2, kDebugLingoCompile, "Code chunk:\n#####\n%s#####", chunk.c_str()); parse(chunk.c_str()); + if (debugChannelSet(3, kDebugLingoCompile)) { + int pc = 0; + while (pc < _currentScript->size()) { + Common::String instr = decodeInstruction(pc, &pc); + debugC(3, kDebugLingoCompile, "[%5d] %s", pc, instr.c_str()); + } + } + _currentScript->clear(); begin = end; @@ -187,7 +196,7 @@ void Lingo::addCode(const char *code, ScriptType type, uint16 id) { _hadError = true; // HACK: This is for preventing test execution - debug(2, "Code chunk:\n#####\n%s#####", begin); + debugC(2, kDebugLingoCompile, "Code chunk:\n#####\n%s#####", begin); parse(begin); } else { parse(code); @@ -197,8 +206,16 @@ void Lingo::addCode(const char *code, ScriptType type, uint16 id) { _inFactory = false; - if (_currentScript->size() && !_hadError) - Common::hexdump((byte *)&_currentScript->front(), _currentScript->size() * sizeof(inst)); + if (debugChannelSet(3, kDebugLingoCompile)) { + if (_currentScript->size() && !_hadError) + Common::hexdump((byte *)&_currentScript->front(), _currentScript->size() * sizeof(inst)); + + int pc = 0; + while (pc < _currentScript->size()) { + Common::String instr = decodeInstruction(pc, &pc); + debugC(3, kDebugLingoCompile, "[%5d] %s", pc, instr.c_str()); + } + } } void Lingo::executeScript(ScriptType type, uint16 id) { @@ -207,7 +224,7 @@ void Lingo::executeScript(ScriptType type, uint16 id) { return; } - debug(2, "Executing script type: %d, id: %d", type, id); + debugC(2, kDebugLingoExec, "Executing script type: %d, id: %d", type, id); _currentScript = _scripts[type][id]; _pc = 0; @@ -224,7 +241,7 @@ void Lingo::processEvent(LEvent event, int entityId) { if (!_eventHandlerTypes.contains(event)) error("processEvent: Unknown event %d for entity %d", event, entityId); - debug(2, "processEvent(%s) for %d", _eventHandlerTypes[event], entityId); + debug(2, "STUB: processEvent(%s) for %d", _eventHandlerTypes[event], entityId); } int Lingo::alignTypes(Datum &d1, Datum &d2) { @@ -284,6 +301,15 @@ Common::String *Datum::toString() { delete s; s = u.s; break; + case OBJECT: + *s = Common::String::format("#%s", u.s->c_str()); + break; + case VOID: + *s = "#void"; + break; + case VAR: + *s = Common::String::format("var: #%s", u.sym->name); + break; default: warning("Incorrect operation toString() for type: %s", type2str()); } @@ -312,6 +338,10 @@ const char *Datum::type2str(bool isk) { return isk ? "#point" : "POINT"; case SYMBOL: return isk ? "#symbol" : "SYMBOL"; + case OBJECT: + return isk ? "#object" : "OBJECT"; + case VAR: + return isk ? "#var" : "VAR"; default: snprintf(res, 20, "-- (%d) --", type); return res; @@ -373,7 +403,7 @@ void Lingo::runTests() { Common::sort(fileList.begin(), fileList.end()); - for (int i = 0; i < fileList.size(); i++) { + for (uint i = 0; i < fileList.size(); i++) { Common::SeekableReadStream *const stream = SearchMan.createReadStreamForMember(fileList[i]); if (stream) { uint size = stream->size(); @@ -382,7 +412,7 @@ void Lingo::runTests() { stream->read(script, size); - warning("Compiling file %s of size %d, id: %d", fileList[i].c_str(), size, counter); + debugC(2, kDebugLingoCompile, "Compiling file %s of size %d, id: %d", fileList[i].c_str(), size, counter); _hadError = false; addCode(script, kMovieScript, counter); @@ -390,7 +420,7 @@ void Lingo::runTests() { if (!_hadError) executeScript(kMovieScript, counter); else - warning("Skipping execution"); + debugC(2, kDebugLingoCompile, "Skipping execution"); free(script); diff --git a/engines/director/lingo/lingo.h b/engines/director/lingo/lingo.h index 8af20f2a15..71708cf910 100644 --- a/engines/director/lingo/lingo.h +++ b/engines/director/lingo/lingo.h @@ -35,6 +35,11 @@ namespace Director { +enum { + kDebugLingoExec = 1 << 0, + kDebugLingoCompile = 1 << 1 +}; + enum LEvent { kEventPrepareMovie, kEventStartMovie, @@ -78,6 +83,30 @@ typedef void (*inst)(void); typedef Common::Array<inst> ScriptData; typedef Common::Array<double> FloatArray; +struct FuncDesc { + Common::String name; + const char *proto; + + FuncDesc(Common::String n, const char *p) { name = n; proto = p; } +}; + +struct Pointer_EqualTo { + bool operator()(const void *x, const void *y) const { return x == y; } +}; + +struct Pointer_Hash { + uint operator()(const void *x) const { +#ifdef SCUMM_64BITS + uint64 v = (uint64)x; + return (v >> 32) ^ (v & 0xffffffff); +#else + return (uint)x; +#endif + } +}; + +typedef Common::HashMap<void *, FuncDesc *, Pointer_Hash, Pointer_EqualTo> FuncHash; + struct Symbol { /* symbol table entry */ char *name; int type; @@ -151,10 +180,12 @@ public: void addCode(const char *code, ScriptType type, uint16 id); void executeScript(ScriptType type, uint16 id); + Common::String decodeInstruction(int pc, int *newPC = NULL); void processEvent(LEvent event, int entityId); void initBuiltIns(); + void initFuncs(); void initTheEntities(); Common::String *toLowercaseMac(Common::String *s); @@ -236,6 +267,7 @@ public: static void c_repeatwhilecode(); static void c_repeatwithcode(); static void c_ifcode(); + static void c_whencode(); static void c_eq(); static void c_neq(); static void c_gt(); @@ -283,26 +315,34 @@ public: static void b_chars(int nargs); static void b_charToNum(int nargs); static void b_length(int nargs); + static void b_numToChar(int nargs); + static void b_offset(int nargs); static void b_string(int nargs); + static void b_stringp(int nargs); static void b_ilk(int nargs); static void b_alert(int nargs); static void b_cursor(int nargs); + static void b_objectp(int nargs); static void b_printFrom(int nargs); static void b_showGlobals(int nargs); static void b_showLocals(int nargs); + static void b_symbolp(int nargs); + static void b_value(int nargs); static void b_constrainH(int nargs); static void b_constrainV(int nargs); static void b_editableText(int nargs); static void b_installMenu(int nargs); static void b_label(int nargs); + static void b_marker(int nargs); static void b_moveableSprite(int nargs); static void b_puppetPalette(int nargs); static void b_puppetSound(int nargs); static void b_puppetSprite(int nargs); static void b_puppetTempo(int nargs); static void b_puppetTransition(int nargs); + static void b_rollOver(int nargs); static void b_spriteBox(int nargs); static void b_updateStage(int nargs); static void b_zoomBox(int nargs); @@ -398,6 +438,8 @@ private: SymbolHash _globalvars; SymbolHash *_localvars; + FuncHash _functions; + int _pc; StackData _stack; diff --git a/engines/director/lingo/tests/math.lingo b/engines/director/lingo/tests/math.lingo index 6f8ecc374f..f38b061b6a 100644 --- a/engines/director/lingo/tests/math.lingo +++ b/engines/director/lingo/tests/math.lingo @@ -20,3 +20,5 @@ updatestage put (1024/4096)*100 -- 0 put (1024/4096)*100.0 -- 0.0 put ((1024*1.0)/4096)*100.0 -- 25.0 + +put the sqrt of 9 diff --git a/engines/director/score.cpp b/engines/director/score.cpp index 6587270641..2cdff841b2 100644 --- a/engines/director/score.cpp +++ b/engines/director/score.cpp @@ -188,9 +188,6 @@ Score::~Score() { if (_movieArchive) _movieArchive->close(); - delete _surface; - delete _trailSurface; - delete _font; delete _movieArchive; @@ -201,7 +198,7 @@ void Score::loadPalette(Common::SeekableSubReadStreamEndian &stream) { uint16 steps = stream.size() / 6; uint16 index = (steps * 3) - 1; uint16 _paletteColorCount = steps; - byte *_palette = new byte[index]; + byte *_palette = new byte[index + 1]; for (uint8 i = 0; i < steps; i++) { _palette[index - 2] = stream.readByte(); @@ -892,8 +889,6 @@ Frame::Frame(const Frame &frame) { } Frame::~Frame() { - delete[] &_sprites; - delete[] &_drawRects; delete _palette; } @@ -977,6 +972,7 @@ void Frame::readMainChannels(Common::SeekableSubReadStreamEndian &stream, uint16 if (stream.readUint16()) readPaletteInfo(stream); offset += 16; + break; default: offset++; stream.readByte(); @@ -1611,7 +1607,6 @@ Sprite::Sprite(const Sprite &sprite) { Sprite::~Sprite() { delete _cast; - delete &_startPoint; } } //End of namespace Director diff --git a/engines/director/sound.cpp b/engines/director/sound.cpp index 5f6d435392..d6c78a51ea 100644 --- a/engines/director/sound.cpp +++ b/engines/director/sound.cpp @@ -36,6 +36,12 @@ DirectorSound::DirectorSound() { _mixer = g_system->getMixer(); } +DirectorSound::~DirectorSound() { + delete _sound1; + delete _sound2; + delete _scriptSound; +} + void DirectorSound::playWAV(Common::String filename, uint8 soundChannel) { Common::File *file = new Common::File(); diff --git a/engines/director/sound.h b/engines/director/sound.h index 87a989c596..4327b63310 100644 --- a/engines/director/sound.h +++ b/engines/director/sound.h @@ -39,6 +39,7 @@ private: public: DirectorSound(); + ~DirectorSound(); void playWAV(Common::String filename, uint8 channelID); void playAIFF(Common::String filename, uint8 channelID); diff --git a/engines/fullpipe/fullpipe.h b/engines/fullpipe/fullpipe.h index d9ab2aaa85..2012d7a344 100644 --- a/engines/fullpipe/fullpipe.h +++ b/engines/fullpipe/fullpipe.h @@ -48,7 +48,7 @@ namespace Fullpipe { enum FullpipeGameFeatures { }; -enum AccessDebugChannels { +enum { kDebugPathfinding = 1 << 0, kDebugDrawing = 1 << 1, kDebugLoading = 1 << 2, diff --git a/engines/fullpipe/gfx.cpp b/engines/fullpipe/gfx.cpp index dd8d8b246d..174f66a3c8 100644 --- a/engines/fullpipe/gfx.cpp +++ b/engines/fullpipe/gfx.cpp @@ -1122,18 +1122,20 @@ void Bitmap::copier(uint32 *dest, byte *src, int len, int32 *palette, bool cb05_ } Bitmap *Bitmap::reverseImage(bool flip) { + Bitmap *b = new Bitmap(this); + if (flip) - _flipping = Graphics::FLIP_H; - else - _flipping = Graphics::FLIP_NONE; + b->_flipping ^= Graphics::FLIP_H; - return this; + return b; } Bitmap *Bitmap::flipVertical() { - _flipping = Graphics::FLIP_V; + Bitmap *b = new Bitmap(this); + + b->_flipping ^= Graphics::FLIP_V; - return this; + return b; } void Bitmap::drawShaded(int type, int x, int y, byte *palette, int alpha) { diff --git a/engines/fullpipe/statics.cpp b/engines/fullpipe/statics.cpp index 45cf743e51..552a17e3eb 100644 --- a/engines/fullpipe/statics.cpp +++ b/engines/fullpipe/statics.cpp @@ -96,7 +96,7 @@ Common::Point *StepArray::getPoint(Common::Point *point, int index, int offset) } bool StepArray::gotoNextPoint() { - if (_currPointIndex < _maxPointIndex) { + if (_currPointIndex < _maxPointIndex - 1) { _currPointIndex++; return true; } else { diff --git a/engines/mohawk/riven_sound.cpp b/engines/mohawk/riven_sound.cpp index dd45f94ad3..10a23a0719 100644 --- a/engines/mohawk/riven_sound.cpp +++ b/engines/mohawk/riven_sound.cpp @@ -200,7 +200,7 @@ void RivenSoundManager::addAmbientSounds(const SLSTRecord &record) { } void RivenSoundManager::setTargetVolumes(const SLSTRecord &record) { - for (uint i = 0; i < _ambientSounds.sounds.size(); i++) { + for (uint i = 0; i < record.volumes.size(); i++) { _ambientSounds.sounds[i].targetVolume = record.volumes[i] * record.globalVolume / 256; _ambientSounds.sounds[i].targetBalance = record.balances[i]; } diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index 3aaf13efdb..b20ed3f8be 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -54,7 +54,6 @@ #include "sci/graphics/frameout.h" #include "sci/graphics/paint32.h" #include "video/coktel_decoder.h" -#include "sci/video/robot_decoder.h" #endif #include "common/file.h" @@ -266,8 +265,6 @@ void Console::postEnter() { #ifdef ENABLE_SCI32 } else if (_videoFile.hasSuffix(".vmd")) { videoDecoder = new Video::AdvancedVMDDecoder(); - } else if (_videoFile.hasSuffix(".rbt")) { - videoDecoder = new RobotDecoder(_engine->getPlatform() == Common::kPlatformMacintosh); } else if (_videoFile.hasSuffix(".duk")) { duckMode = true; videoDecoder = new Video::AVIDecoder(); diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp index f5797dc106..ad2b0f31a5 100644 --- a/engines/sci/detection.cpp +++ b/engines/sci/detection.cpp @@ -565,8 +565,8 @@ const ADGameDescription *SciMetaEngine::fallbackDetect(const FileMap &allFiles, // the file should be over 10MB, as it contains all the game speech and is usually // around 450MB+. The size check is for some floppy game versions like KQ6 floppy, which // also have a small resource.aud file - if (allFiles.contains("resource.aud") || allFiles.contains("audio001.002")) { - Common::FSNode file = allFiles.contains("resource.aud") ? allFiles["resource.aud"] : allFiles["audio001.002"]; + if (allFiles.contains("resource.aud") || allFiles.contains("resaud.001") || allFiles.contains("audio001.002")) { + Common::FSNode file = allFiles.contains("resource.aud") ? allFiles["resource.aud"] : (allFiles.contains("resaud.001") ? allFiles["resaud.001"] : allFiles["audio001.002"]); Common::SeekableReadStream *tmpStream = file.createReadStream(); if (tmpStream->size() > 10 * 1024 * 1024) { // We got a CD version, so set the CD flag accordingly diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h index de342a3afc..eda6bfae64 100644 --- a/engines/sci/detection_tables.h +++ b/engines/sci/detection_tables.h @@ -22,15 +22,7 @@ namespace Sci { -#define GAMEOPTION_PREFER_DIGITAL_SFX GUIO_GAMEOPTIONS1 -#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS2 -#define GAMEOPTION_FB01_MIDI GUIO_GAMEOPTIONS3 -#define GAMEOPTION_JONES_CDAUDIO GUIO_GAMEOPTIONS4 -#define GAMEOPTION_KQ6_WINDOWS_CURSORS GUIO_GAMEOPTIONS5 -#define GAMEOPTION_SQ4_SILVER_CURSORS GUIO_GAMEOPTIONS6 -#define GAMEOPTION_EGA_UNDITHER GUIO_GAMEOPTIONS7 -#define GAMEOPTION_HIGH_RESOLUTION_GRAPHICS GUIO_GAMEOPTIONS8 -#define GAMEOPTION_ENABLE_BLACK_LINED_VIDEO GUIO_GAMEOPTIONS9 +#include "sci/sci.h" // SCI3 games have a different script format (in CSC files) and are currently unsupported #define ENABLE_SCI3_GAMES @@ -836,7 +828,10 @@ static const struct ADGameDescription SciGameDescriptions[] = { AD_LISTEND}, Common::ES_ESP, Common::kPlatformWindows, ADGF_CD | ADGF_UNSTABLE, GUIO_GK1_CD }, - // Gabriel Knight - English Macintosh + // Gabriel Knight - English Macintosh (Floppy!) + // This version is hi-res ONLY, so it should NOT get GAMEOPTION_HIGH_RESOLUTION_GRAPHICS + // (which is meant for enforcing hi-res graphics), but instead hi-res mode should be enabled all the time. + // Confirmed by [md5] and originally by clone2727. {"gk1", "", { {"Data1", 0, "044d3bcd7e5b5bb0393d954ade8053fe", 5814918}, {"Data2", 0, "99a0c63febf9e44e12a00f99c00eae0f", 6685352}, diff --git a/engines/sci/engine/kernel.h b/engines/sci/engine/kernel.h index b02a7e545a..90f582c291 100644 --- a/engines/sci/engine/kernel.h +++ b/engines/sci/engine/kernel.h @@ -421,6 +421,14 @@ reg_t kStubNull(EngineState *s, int argc, reg_t *argv); #ifdef ENABLE_SCI32 // SCI2 Kernel Functions +reg_t kSetCursor32(EngineState *s, int argc, reg_t *argv); +reg_t kSetNowSeen32(EngineState *s, int argc, reg_t *argv); +reg_t kBaseSetter32(EngineState *s, int argc, reg_t *argv); +reg_t kShakeScreen32(EngineState *s, int argc, reg_t *argv); +reg_t kPlatform32(EngineState *s, int argc, reg_t *argv); +reg_t kGlobalToLocal32(EngineState *s, int argc, reg_t *argv); +reg_t kLocalToGlobal32(EngineState *s, int argc, reg_t *argv); + reg_t kDoAudio32(EngineState *s, int argc, reg_t *argv); reg_t kDoAudioInit(EngineState *s, int argc, reg_t *argv); reg_t kDoAudioWaitForPlay(EngineState *s, int argc, reg_t *argv); @@ -441,10 +449,24 @@ reg_t kDoAudioFade(EngineState *s, int argc, reg_t *argv); reg_t kDoAudioHasSignal(EngineState *s, int argc, reg_t *argv); reg_t kDoAudioSetLoop(EngineState *s, int argc, reg_t *argv); +reg_t kRobot(EngineState *s, int argc, reg_t *argv); +reg_t kRobotOpen(EngineState *s, int argc, reg_t *argv); +reg_t kRobotShowFrame(EngineState *s, int argc, reg_t *argv); +reg_t kRobotGetFrameSize(EngineState *s, int argc, reg_t *argv); +reg_t kRobotPlay(EngineState *s, int argc, reg_t *argv); +reg_t kRobotGetIsFinished(EngineState *s, int argc, reg_t *argv); +reg_t kRobotGetIsPlaying(EngineState *s, int argc, reg_t *argv); +reg_t kRobotClose(EngineState *s, int argc, reg_t *argv); +reg_t kRobotGetCue(EngineState *s, int argc, reg_t *argv); +reg_t kRobotPause(EngineState *s, int argc, reg_t *argv); +reg_t kRobotGetFrameNo(EngineState *s, int argc, reg_t *argv); +reg_t kRobotSetPriority(EngineState *s, int argc, reg_t *argv); + reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv); reg_t kPlayVMDOpen(EngineState *s, int argc, reg_t *argv); reg_t kPlayVMDInit(EngineState *s, int argc, reg_t *argv); reg_t kPlayVMDClose(EngineState *s, int argc, reg_t *argv); +reg_t kPlayVMDGetStatus(EngineState *s, int argc, reg_t *argv); reg_t kPlayVMDPlayUntilEvent(EngineState *s, int argc, reg_t *argv); reg_t kPlayVMDShowCursor(EngineState *s, int argc, reg_t *argv); reg_t kPlayVMDSetBlackoutArea(EngineState *s, int argc, reg_t *argv); @@ -587,9 +609,9 @@ reg_t kTextWidth(EngineState *s, int argc, reg_t *argv); reg_t kSave(EngineState *s, int argc, reg_t *argv); reg_t kAutoSave(EngineState *s, int argc, reg_t *argv); reg_t kList(EngineState *s, int argc, reg_t *argv); -reg_t kRobot(EngineState *s, int argc, reg_t *argv); -reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv); reg_t kCD(EngineState *s, int argc, reg_t *argv); +reg_t kCheckCD(EngineState *s, int argc, reg_t *argv); +reg_t kGetSavedCD(EngineState *s, int argc, reg_t *argv); reg_t kAddPicAt(EngineState *s, int argc, reg_t *argv); reg_t kAddBefore(EngineState *s, int argc, reg_t *argv); reg_t kMoveToFront(EngineState *s, int argc, reg_t *argv); diff --git a/engines/sci/engine/kernel_tables.h b/engines/sci/engine/kernel_tables.h index 76c24b09e3..68611b0dee 100644 --- a/engines/sci/engine/kernel_tables.h +++ b/engines/sci/engine/kernel_tables.h @@ -396,6 +396,13 @@ static const SciKernelMapSubEntry kBitmap_subops[] = { }; // version, subId, function-mapping, signature, workarounds +static const SciKernelMapSubEntry kCD_subops[] = { + { SIG_SINCE_SCI21MID, 0, MAP_CALL(CheckCD), "(i)", NULL }, + { SIG_SINCE_SCI21MID, 1, MAP_CALL(GetSavedCD), "", NULL }, + SCI_SUBOPENTRY_TERMINATOR +}; + +// version, subId, function-mapping, signature, workarounds static const SciKernelMapSubEntry kList_subops[] = { { SIG_SINCE_SCI21, 0, MAP_CALL(NewList), "", NULL }, { SIG_SINCE_SCI21, 1, MAP_CALL(DisposeList), "l", NULL }, @@ -406,8 +413,8 @@ static const SciKernelMapSubEntry kList_subops[] = { { SIG_SINCE_SCI21, 6, MAP_CALL(NextNode), "n", NULL }, { SIG_SINCE_SCI21, 7, MAP_CALL(PrevNode), "n", NULL }, { SIG_SINCE_SCI21, 8, MAP_CALL(NodeValue), "[n0]", NULL }, - { SIG_SINCE_SCI21, 9, MAP_CALL(AddAfter), "lnn.", NULL }, - { SIG_SINCE_SCI21, 10, MAP_CALL(AddToFront), "ln.", NULL }, + { SIG_SINCE_SCI21, 9, MAP_CALL(AddAfter), "lnn(.)", NULL }, + { SIG_SINCE_SCI21, 10, MAP_CALL(AddToFront), "ln(.)", NULL }, { SIG_SINCE_SCI21, 11, MAP_CALL(AddToEnd), "ln(.)", NULL }, { SIG_SINCE_SCI21, 12, MAP_CALL(AddBefore), "ln.", NULL }, { SIG_SINCE_SCI21, 13, MAP_CALL(MoveToFront), "ln", NULL }, @@ -451,6 +458,7 @@ static const SciKernelMapSubEntry kPlayVMD_subops[] = { { SIG_SINCE_SCI21, 0, MAP_CALL(PlayVMDOpen), "r(i)(i)", NULL }, { SIG_SINCE_SCI21, 1, MAP_CALL(PlayVMDInit), "ii(i)(i)(ii)", NULL }, { SIG_SINCE_SCI21, 6, MAP_CALL(PlayVMDClose), "", NULL }, + { SIG_SINCE_SCI21, 10, MAP_CALL(PlayVMDGetStatus), "", NULL }, { SIG_SINCE_SCI21, 14, MAP_CALL(PlayVMDPlayUntilEvent), "i(i)(i)", NULL }, { SIG_SINCE_SCI21, 16, MAP_CALL(PlayVMDShowCursor), "i", NULL }, { SIG_SINCE_SCI21, 17, MAP_DUMMY(PlayVMDStartBlob), "", NULL }, @@ -461,6 +469,22 @@ static const SciKernelMapSubEntry kPlayVMD_subops[] = { }; // version, subId, function-mapping, signature, workarounds +static const SciKernelMapSubEntry kRobot_subops[] = { + { SIG_SINCE_SCI21, 0, MAP_CALL(RobotOpen), "ioiii(i)", NULL }, + { SIG_SINCE_SCI21, 1, MAP_CALL(RobotShowFrame), "i(ii)", NULL }, + { SIG_SINCE_SCI21, 2, MAP_CALL(RobotGetFrameSize), "r", NULL }, + { SIG_SINCE_SCI21, 4, MAP_CALL(RobotPlay), "", NULL }, + { SIG_SINCE_SCI21, 5, MAP_CALL(RobotGetIsFinished), "", NULL }, + { SIG_SINCE_SCI21, 6, MAP_CALL(RobotGetIsPlaying), "", NULL }, + { SIG_SINCE_SCI21, 7, MAP_CALL(RobotClose), "", NULL }, + { SIG_SINCE_SCI21, 8, MAP_CALL(RobotGetCue), "o", NULL }, + { SIG_SINCE_SCI21, 10, MAP_CALL(RobotPause), "", NULL }, + { SIG_SINCE_SCI21, 11, MAP_CALL(RobotGetFrameNo), "", NULL }, + { SIG_SINCE_SCI21, 12, MAP_CALL(RobotSetPriority), "i", NULL }, + SCI_SUBOPENTRY_TERMINATOR +}; + +// version, subId, function-mapping, signature, workarounds static const SciKernelMapSubEntry kRemapColors_subops[] = { { SIG_SCI32, 0, MAP_CALL(RemapColorsOff), "(i)", NULL }, { SIG_SCI32, 1, MAP_CALL(RemapColorsByRange), "iiii(i)", NULL }, @@ -569,7 +593,10 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(Animate), SIG_EVERYWHERE, "(l0)(i)", NULL, NULL }, { MAP_CALL(AssertPalette), SIG_EVERYWHERE, "i", NULL, NULL }, { MAP_CALL(AvoidPath), SIG_EVERYWHERE, "ii(.*)", NULL, NULL }, - { MAP_CALL(BaseSetter), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(BaseSetter), SIG_SCI16, SIGFOR_ALL, "o", NULL, NULL }, +#ifdef ENABLE_SCI32 + { "BaseSetter", kBaseSetter32, SIG_SCI32, SIGFOR_ALL, "o", NULL, NULL }, +#endif { MAP_CALL(CanBeHere), SIG_EVERYWHERE, "o(l)", NULL, NULL }, { MAP_CALL(CantBeHere), SIG_SCI16, SIGFOR_ALL, "o(l)", NULL, NULL }, #ifdef ENABLE_SCI32 @@ -638,8 +665,10 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(GetSaveDir), SIG_EVERYWHERE, "", NULL, NULL }, { MAP_CALL(GetSaveFiles), SIG_EVERYWHERE, "rrr", NULL, NULL }, { MAP_CALL(GetTime), SIG_EVERYWHERE, "(i)", NULL, NULL }, - { MAP_CALL(GlobalToLocal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL }, - { MAP_CALL(GlobalToLocal), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(GlobalToLocal), SIG_SCI16, SIGFOR_ALL, "o", NULL, NULL }, +#ifdef ENABLE_SCI32 + { "GlobalToLocal", kGlobalToLocal32, SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL }, +#endif { MAP_CALL(Graph), SIG_EVERYWHERE, NULL, kGraph_subops, NULL }, { MAP_CALL(HaveMouse), SIG_EVERYWHERE, "", NULL, NULL }, { MAP_CALL(HiliteControl), SIG_EVERYWHERE, "o", NULL, NULL }, @@ -650,8 +679,10 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(Joystick), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop { MAP_CALL(LastNode), SIG_EVERYWHERE, "l", NULL, NULL }, { MAP_CALL(Load), SIG_EVERYWHERE, "ii(i*)", NULL, NULL }, - { MAP_CALL(LocalToGlobal), SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL }, - { MAP_CALL(LocalToGlobal), SIG_EVERYWHERE, "o", NULL, NULL }, + { MAP_CALL(LocalToGlobal), SIG_SCI16, SIGFOR_ALL, "o", NULL, NULL }, +#ifdef ENABLE_SCI32 + { "LocalToGlobal", kLocalToGlobal32, SIG_SCI32, SIGFOR_ALL, "oo", NULL, NULL }, +#endif { MAP_CALL(Lock), SIG_EVERYWHERE, "ii(i)", NULL, NULL }, { MAP_CALL(MapKeyToDir), SIG_EVERYWHERE, "o", NULL, NULL }, { MAP_CALL(Memory), SIG_EVERYWHERE, "i(.*)", NULL, kMemory_workarounds }, // subop @@ -676,7 +707,10 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(Palette), SIG_EVERYWHERE, "i(.*)", kPalette_subops, NULL }, { MAP_CALL(Parse), SIG_EVERYWHERE, "ro", NULL, NULL }, { MAP_CALL(PicNotValid), SIG_EVERYWHERE, "(i)", NULL, NULL }, - { MAP_CALL(Platform), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_CALL(Platform), SIG_SCI16, SIGFOR_ALL, "(.*)", NULL, NULL }, +#ifdef ENABLE_SCI32 + { "Platform", kPlatform32, SIG_SCI32, SIGFOR_ALL, "(i)", NULL, NULL }, +#endif { MAP_CALL(Portrait), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, // subop { MAP_CALL(PrevNode), SIG_EVERYWHERE, "n", NULL, NULL }, { MAP_CALL(PriCoord), SIG_EVERYWHERE, "i", NULL, NULL }, @@ -693,22 +727,26 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_CALL(Said), SIG_EVERYWHERE, "[r0]", NULL, NULL }, { MAP_CALL(SaveGame), SIG_EVERYWHERE, "[r0]i[r0](r0)", NULL, NULL }, { MAP_CALL(ScriptID), SIG_EVERYWHERE, "[io](i)", NULL, NULL }, - { MAP_CALL(SetCursor), SIG_SINCE_SCI21, SIGFOR_ALL, "i(i)([io])(i*)", NULL, NULL }, - // TODO: SCI2.1 may supply an object optionally (mother goose sci21 right on startup) - find out why { MAP_CALL(SetCursor), SIG_SCI11, SIGFOR_ALL, "i(i)(i)(i)(iiiiii)", NULL, NULL }, - { MAP_CALL(SetCursor), SIG_EVERYWHERE, "i(i)(i)(i)(i)", NULL, kSetCursor_workarounds }, + { MAP_CALL(SetCursor), SIG_SCI16, SIGFOR_ALL, "i(i)(i)(i)(i)", NULL, kSetCursor_workarounds }, +#ifdef ENABLE_SCI32 + { "SetCursor", kSetCursor32, SIG_SCI32, SIGFOR_ALL, "i(i)(i)(i)", NULL, kSetCursor_workarounds }, +#endif { MAP_CALL(SetDebug), SIG_EVERYWHERE, "(i*)", NULL, NULL }, { MAP_CALL(SetJump), SIG_EVERYWHERE, "oiii", NULL, NULL }, { MAP_CALL(SetMenu), SIG_EVERYWHERE, "i(.*)", NULL, NULL }, { MAP_CALL(SetNowSeen), SIG_SCI16, SIGFOR_ALL, "o(i)", NULL, NULL }, #ifdef ENABLE_SCI32 - { MAP_CALL(SetNowSeen), SIG_SCI32, SIGFOR_ALL, "o", NULL, NULL }, + { "SetNowSeen", kSetNowSeen32, SIG_SCI32, SIGFOR_ALL, "o", NULL, NULL }, #endif { MAP_CALL(SetPort), SIG_EVERYWHERE, "i(iiiii)(i)", NULL, kSetPort_workarounds }, { MAP_CALL(SetQuitStr), SIG_EVERYWHERE, "r", NULL, NULL }, { MAP_CALL(SetSynonyms), SIG_EVERYWHERE, "o", NULL, NULL }, { MAP_CALL(SetVideoMode), SIG_EVERYWHERE, "i", NULL, NULL }, - { MAP_CALL(ShakeScreen), SIG_EVERYWHERE, "(i)(i)", NULL, NULL }, + { MAP_CALL(ShakeScreen), SIG_SCI16, SIGFOR_ALL, "(i)(i)", NULL, NULL }, +#ifdef ENABLE_SCI32 + { "ShakeScreen", kShakeScreen32, SIG_SCI32, SIGFOR_ALL, "i(i)", NULL, NULL }, +#endif { MAP_CALL(ShowMovie), SIG_SCI16, SIGFOR_ALL, "(.*)", NULL, NULL }, #ifdef ENABLE_SCI32 { "ShowMovie", kShowMovie32, SIG_SCI32, SIGFOR_DOS, "ri(i)(i)", NULL, NULL }, @@ -840,12 +878,12 @@ static SciKernelMapEntry s_kernelMap[] = { { MAP_DUMMY(PointSize), SIG_EVERYWHERE, "(.*)", NULL, NULL }, // SCI2.1 Kernel Functions - { MAP_CALL(CD), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_CALL(CD), SIG_SINCE_SCI21MID, SIGFOR_ALL, "(.*)", kCD_subops, NULL }, { MAP_CALL(IsOnMe), SIG_EVERYWHERE, "iioi", NULL, NULL }, { MAP_CALL(List), SIG_SINCE_SCI21, SIGFOR_ALL, "(.*)", kList_subops, NULL }, { MAP_CALL(MulDiv), SIG_EVERYWHERE, "iii", NULL, NULL }, { MAP_CALL(PlayVMD), SIG_EVERYWHERE, "(.*)", kPlayVMD_subops, NULL }, - { MAP_EMPTY(Robot), SIG_EVERYWHERE, "(.*)", NULL, NULL }, + { MAP_CALL(Robot), SIG_EVERYWHERE, "(.*)", kRobot_subops, NULL }, { MAP_CALL(Save), SIG_EVERYWHERE, "i(.*)", kSave_subops, NULL }, { MAP_CALL(Text), SIG_SINCE_SCI21MID, SIGFOR_ALL, "i(.*)", kText_subops, NULL }, { MAP_CALL(AddPicAt), SIG_EVERYWHERE, "oiii(i)(i)", NULL, NULL }, diff --git a/engines/sci/engine/kevent.cpp b/engines/sci/engine/kevent.cpp index d7a716a504..9250e0fc13 100644 --- a/engines/sci/engine/kevent.cpp +++ b/engines/sci/engine/kevent.cpp @@ -34,6 +34,9 @@ #include "sci/graphics/coordadjuster.h" #include "sci/graphics/cursor.h" #include "sci/graphics/maciconbar.h" +#ifdef ENABLE_SCI32 +#include "sci/graphics/frameout.h" +#endif namespace Sci { @@ -58,10 +61,7 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { if (g_debug_simulated_key && (mask & SCI_EVENT_KEYBOARD)) { // In case we use a simulated event we query the current mouse position mousePos = g_sci->_gfxCursor->getPosition(); -#ifdef ENABLE_SCI32 - if (getSciVersion() >= SCI_VERSION_2_1_EARLY) - g_sci->_gfxCoordAdjuster->fromDisplayToScript(mousePos.y, mousePos.x); -#endif + // Limit the mouse cursor position, if necessary g_sci->_gfxCursor->refreshPosition(); @@ -86,11 +86,14 @@ reg_t kGetEvent(EngineState *s, int argc, reg_t *argv) { #ifdef ENABLE_SCI32 if (getSciVersion() >= SCI_VERSION_2) mousePos = curEvent.mousePosSci; - else + else { #endif mousePos = curEvent.mousePos; - // Limit the mouse cursor position, if necessary - g_sci->_gfxCursor->refreshPosition(); + // Limit the mouse cursor position, if necessary + g_sci->_gfxCursor->refreshPosition(); +#ifdef ENABLE_SCI32 + } +#endif if (g_sci->getVocabulary()) g_sci->getVocabulary()->parser_event = NULL_REG; // Invalidate parser event @@ -281,14 +284,13 @@ reg_t kMapKeyToDir(EngineState *s, int argc, reg_t *argv) { reg_t kGlobalToLocal(EngineState *s, int argc, reg_t *argv) { reg_t obj = argv[0]; - reg_t planeObject = argc > 1 ? argv[1] : NULL_REG; // SCI32 SegManager *segMan = s->_segMan; if (obj.getSegment()) { int16 x = readSelectorValue(segMan, obj, SELECTOR(x)); int16 y = readSelectorValue(segMan, obj, SELECTOR(y)); - g_sci->_gfxCoordAdjuster->kernelGlobalToLocal(x, y, planeObject); + g_sci->_gfxCoordAdjuster->kernelGlobalToLocal(x, y); writeSelectorValue(segMan, obj, SELECTOR(x), x); writeSelectorValue(segMan, obj, SELECTOR(y), y); @@ -300,14 +302,13 @@ reg_t kGlobalToLocal(EngineState *s, int argc, reg_t *argv) { reg_t kLocalToGlobal(EngineState *s, int argc, reg_t *argv) { reg_t obj = argv[0]; - reg_t planeObject = argc > 1 ? argv[1] : NULL_REG; // SCI32 SegManager *segMan = s->_segMan; if (obj.getSegment()) { int16 x = readSelectorValue(segMan, obj, SELECTOR(x)); int16 y = readSelectorValue(segMan, obj, SELECTOR(y)); - g_sci->_gfxCoordAdjuster->kernelLocalToGlobal(x, y, planeObject); + g_sci->_gfxCoordAdjuster->kernelLocalToGlobal(x, y); writeSelectorValue(segMan, obj, SELECTOR(x), x); writeSelectorValue(segMan, obj, SELECTOR(y), y); @@ -322,4 +323,52 @@ reg_t kJoystick(EngineState *s, int argc, reg_t *argv) { return NULL_REG; } +#ifdef ENABLE_SCI32 +reg_t kGlobalToLocal32(EngineState *s, int argc, reg_t *argv) { + const reg_t result = argv[0]; + const reg_t planeObj = argv[1]; + + bool visible = true; + Plane *plane = g_sci->_gfxFrameout->getVisiblePlanes().findByObject(planeObj); + if (plane == nullptr) { + plane = g_sci->_gfxFrameout->getPlanes().findByObject(planeObj); + visible = false; + } + if (plane == nullptr) { + error("kGlobalToLocal: Plane %04x:%04x not found", PRINT_REG(planeObj)); + } + + const int16 x = readSelectorValue(s->_segMan, result, SELECTOR(x)) - plane->_gameRect.left; + const int16 y = readSelectorValue(s->_segMan, result, SELECTOR(y)) - plane->_gameRect.top; + + writeSelectorValue(s->_segMan, result, SELECTOR(x), x); + writeSelectorValue(s->_segMan, result, SELECTOR(y), y); + + return make_reg(0, visible); +} + +reg_t kLocalToGlobal32(EngineState *s, int argc, reg_t *argv) { + const reg_t result = argv[0]; + const reg_t planeObj = argv[1]; + + bool visible = true; + Plane *plane = g_sci->_gfxFrameout->getVisiblePlanes().findByObject(planeObj); + if (plane == nullptr) { + plane = g_sci->_gfxFrameout->getPlanes().findByObject(planeObj); + visible = false; + } + if (plane == nullptr) { + error("kLocalToGlobal: Plane %04x:%04x not found", PRINT_REG(planeObj)); + } + + const int16 x = readSelectorValue(s->_segMan, result, SELECTOR(x)) + plane->_gameRect.left; + const int16 y = readSelectorValue(s->_segMan, result, SELECTOR(y)) + plane->_gameRect.top; + + writeSelectorValue(s->_segMan, result, SELECTOR(x), x); + writeSelectorValue(s->_segMan, result, SELECTOR(y), y); + + return make_reg(0, visible); +} +#endif + } // End of namespace Sci diff --git a/engines/sci/engine/kfile.cpp b/engines/sci/engine/kfile.cpp index 3bcadd143c..a3a97eb7ee 100644 --- a/engines/sci/engine/kfile.cpp +++ b/engines/sci/engine/kfile.cpp @@ -40,6 +40,9 @@ #include "sci/engine/savegame.h" #include "sci/sound/audio.h" #include "sci/console.h" +#ifdef ENABLE_SCI32 +#include "sci/resource.h" +#endif namespace Sci { @@ -196,26 +199,25 @@ reg_t kValidPath(EngineState *s, int argc, reg_t *argv) { #ifdef ENABLE_SCI32 reg_t kCD(EngineState *s, int argc, reg_t *argv) { - // TODO: Stub - switch (argv[0].toUint16()) { - case 0: - if (argc == 1) { - // Check if a disc is in the drive - return TRUE_REG; - } else { - // Check if the specified disc is in the drive - // and return the current disc number. We just - // return the requested disc number. - return argv[1]; - } - case 1: - // Return the current CD number - return make_reg(0, 1); - default: - warning("CD(%d)", argv[0].toUint16()); + if (!s) + return make_reg(0, getSciVersion()); + error("not supposed to call this"); +} + +reg_t kCheckCD(EngineState *s, int argc, reg_t *argv) { + const int16 cdNo = argc > 0 ? argv[0].toSint16() : 0; + + if (cdNo) { + g_sci->getResMan()->findDisc(cdNo); } - return NULL_REG; + return make_reg(0, g_sci->getResMan()->getCurrentDiscNo()); +} + +reg_t kGetSavedCD(EngineState *s, int argc, reg_t *argv) { + // TODO: This is wrong, CD number needs to be available prior to + // the save game being loaded + return make_reg(0, g_sci->getResMan()->getCurrentDiscNo()); } #endif diff --git a/engines/sci/engine/kgraphics.cpp b/engines/sci/engine/kgraphics.cpp index cae5a09789..d375a27954 100644 --- a/engines/sci/engine/kgraphics.cpp +++ b/engines/sci/engine/kgraphics.cpp @@ -579,17 +579,8 @@ reg_t kBaseSetter(EngineState *s, int argc, reg_t *argv) { } reg_t kSetNowSeen(EngineState *s, int argc, reg_t *argv) { -#ifdef ENABLE_SCI32 - if (getSciVersion() >= SCI_VERSION_2) { - g_sci->_gfxFrameout->kernelSetNowSeen(argv[0]); - return NULL_REG; - } else { -#endif - g_sci->_gfxCompare->kernelSetNowSeen(argv[0]); - return s->r_acc; -#ifdef ENABLE_SCI32 - } -#endif + g_sci->_gfxCompare->kernelSetNowSeen(argv[0]); + return s->r_acc; } reg_t kPalette(EngineState *s, int argc, reg_t *argv) { diff --git a/engines/sci/engine/kgraphics32.cpp b/engines/sci/engine/kgraphics32.cpp index e458109cc2..13a209ea55 100644 --- a/engines/sci/engine/kgraphics32.cpp +++ b/engines/sci/engine/kgraphics32.cpp @@ -37,8 +37,6 @@ #include "sci/graphics/cache.h" #include "sci/graphics/compare.h" #include "sci/graphics/controls16.h" -#include "sci/graphics/coordadjuster.h" -#include "sci/graphics/cursor.h" #include "sci/graphics/palette.h" #include "sci/graphics/paint16.h" #include "sci/graphics/picture.h" @@ -48,6 +46,7 @@ #include "sci/graphics/text16.h" #include "sci/graphics/view.h" #ifdef ENABLE_SCI32 +#include "sci/graphics/cursor32.h" #include "sci/graphics/celobj32.h" #include "sci/graphics/controls32.h" #include "sci/graphics/font.h" // TODO: remove once kBitmap is moved in a separate class @@ -64,6 +63,107 @@ namespace Sci { extern void showScummVMDialog(const Common::String &message); +reg_t kBaseSetter32(EngineState *s, int argc, reg_t *argv) { + reg_t object = argv[0]; + + const GuiResourceId viewId = readSelectorValue(s->_segMan, object, SELECTOR(view)); + const int16 loopNo = readSelectorValue(s->_segMan, object, SELECTOR(loop)); + const int16 celNo = readSelectorValue(s->_segMan, object, SELECTOR(cel)); + const int16 x = readSelectorValue(s->_segMan, object, SELECTOR(x)); + const int16 y = readSelectorValue(s->_segMan, object, SELECTOR(y)); + + CelObjView celObj(viewId, loopNo, celNo); + + const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + + const Ratio scaleX(scriptWidth, celObj._scaledWidth); + const Ratio scaleY(scriptHeight, celObj._scaledHeight); + + int16 brLeft; + + if (celObj._mirrorX) { + brLeft = x - ((celObj._width - celObj._displace.x) * scaleX).toInt(); + } else { + brLeft = x - (celObj._displace.x * scaleX).toInt(); + } + + const int16 brRight = brLeft + (celObj._width * scaleX).toInt() - 1; + + writeSelectorValue(s->_segMan, object, SELECTOR(brLeft), brLeft); + writeSelectorValue(s->_segMan, object, SELECTOR(brRight), brRight); + writeSelectorValue(s->_segMan, object, SELECTOR(brBottom), y + 1); + writeSelectorValue(s->_segMan, object, SELECTOR(brTop), y + 1 - readSelectorValue(s->_segMan, object, SELECTOR(yStep))); + + return s->r_acc; +} + +reg_t kSetNowSeen32(EngineState *s, int argc, reg_t *argv) { + const bool found = g_sci->_gfxFrameout->kernelSetNowSeen(argv[0]); + + // NOTE: MGDX is assumed to use the older kSetNowSeen since it was + // released before SQ6, but this has not been verified since it cannot be + // disassembled at the moment (Phar Lap Windows-only release) + if (getSciVersion() <= SCI_VERSION_2_1_EARLY || + g_sci->getGameId() == GID_SQ6 || + g_sci->getGameId() == GID_MOTHERGOOSEHIRES) { + + if (!found) { + error("kSetNowSeen: Unable to find screen item %04x:%04x", PRINT_REG(argv[0])); + } + return s->r_acc; + } + + if (!found) { + warning("kSetNowSeen: Unable to find screen item %04x:%04x", PRINT_REG(argv[0])); + } + + return make_reg(0, found); +} + +reg_t kSetCursor32(EngineState *s, int argc, reg_t *argv) { + switch (argc) { + case 1: { + if (argv[0].toSint16() == -2) { + g_sci->_gfxCursor32->clearRestrictedArea(); + } else { + if (argv[0].isNull()) { + g_sci->_gfxCursor32->hide(); + } else { + g_sci->_gfxCursor32->show(); + } + } + break; + } + case 2: { + const Common::Point position(argv[0].toSint16(), argv[1].toSint16()); + g_sci->_gfxCursor32->setPosition(position); + break; + } + case 3: { + g_sci->_gfxCursor32->setView(argv[0].toUint16(), argv[1].toSint16(), argv[2].toSint16()); + break; + } + case 4: { + const Common::Rect restrictRect(argv[0].toSint16(), + argv[1].toSint16(), + argv[2].toSint16() + 1, + argv[3].toSint16() + 1); + g_sci->_gfxCursor32->setRestrictedArea(restrictRect); + break; + } + default: + error("kSetCursor: Invalid number of arguments (%d)", argc); + } + + return s->r_acc; +} + +reg_t kShakeScreen32(EngineState *s, int argc, reg_t *argv) { + g_sci->_gfxFrameout->shakeScreen(argv[0].toSint16(), (ShakeDirection)argv[1].toSint16()); + return s->r_acc; +} + reg_t kIsHiRes(EngineState *s, int argc, reg_t *argv) { const Buffer &buffer = g_sci->_gfxFrameout->getCurrentBuffer(); if (buffer.screenWidth < 640 || buffer.screenHeight < 400) @@ -266,7 +366,7 @@ reg_t kMessageBox(EngineState *s, int argc, reg_t *argv) { * effect */ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) { - ShowStyleType type = (ShowStyleType)argv[0].toUint16(); + const uint16 type = argv[0].toUint16(); reg_t planeObj = argv[1]; int16 seconds = argv[2].toSint16(); // NOTE: This value seems to indicate whether the transition is an @@ -301,6 +401,10 @@ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) { divisions = argc > 9 ? argv[9].toSint16() : -1; } + if ((getSciVersion() < SCI_VERSION_2_1_MIDDLE && g_sci->getGameId() != GID_KQ7 && type == 15) || type > 15) { + error("Illegal show style %d for plane %04x:%04x", type, PRINT_REG(planeObj)); + } + // TODO: Reuse later for SCI2 and SCI3 implementation and then discard // warning("kSetShowStyle: effect %d, plane: %04x:%04x (%s), sec: %d, " // "dir: %d, prio: %d, animate: %d, ref frame: %d, black screen: %d, " @@ -312,7 +416,7 @@ reg_t kSetShowStyle(EngineState *s, int argc, reg_t *argv) { // NOTE: The order of planeObj and showStyle are reversed // because this is how SCI3 called the corresponding method // on the KernelMgr - g_sci->_gfxTransitions32->kernelSetShowStyle(argc, planeObj, type, seconds, back, priority, animate, refFrame, pFadeArray, divisions, blackScreen); + g_sci->_gfxTransitions32->kernelSetShowStyle(argc, planeObj, (ShowStyleType)type, seconds, back, priority, animate, refFrame, pFadeArray, divisions, blackScreen); return s->r_acc; } diff --git a/engines/sci/engine/kmisc.cpp b/engines/sci/engine/kmisc.cpp index c99540967c..448d7bb8a3 100644 --- a/engines/sci/engine/kmisc.cpp +++ b/engines/sci/engine/kmisc.cpp @@ -540,57 +540,46 @@ enum kSciPlatforms { kSciPlatformWindows = 2 }; -enum kPlatformOps { - kPlatformUnk0 = 0, - kPlatformCDSpeed = 1, - kPlatformColorDepth = 2, - kPlatformCDCheck = 3, - kPlatformGetPlatform = 4, - kPlatformUnk5 = 5, - kPlatformIsHiRes = 6, - kPlatformIsItWindows = 7 -}; - reg_t kPlatform(EngineState *s, int argc, reg_t *argv) { + enum Operation { + kPlatformUnknown = 0, + kPlatformGetPlatform = 4, + kPlatformUnknown5 = 5, + kPlatformIsHiRes = 6, + kPlatformWin311OrHigher = 7 + }; + bool isWindows = g_sci->getPlatform() == Common::kPlatformWindows; - if (argc == 0 && getSciVersion() < SCI_VERSION_2) { + if (argc == 0) { // This is called in KQ5CD with no parameters, where it seems to do some // graphics driver check. This kernel function didn't have subfunctions // then. If 0 is returned, the game functions normally, otherwise all // the animations show up like a slideshow (e.g. in the intro). So we - // return 0. However, the behavior changed for kPlatform with no - // parameters in SCI32. + // return 0. return NULL_REG; } + if (g_sci->forceHiresGraphics()) { + // force Windows platform, so that hires-graphics are enabled + isWindows = true; + } + uint16 operation = (argc == 0) ? 0 : argv[0].toUint16(); switch (operation) { - case kPlatformCDSpeed: - // TODO: Returns CD Speed? - warning("STUB: kPlatform(CDSpeed)"); - break; - case kPlatformColorDepth: - // Always returns 2 - return make_reg(0, /* 256-color */ 2); - case kPlatformCDCheck: - // TODO: Some sort of CD check? - warning("STUB: kPlatform(CDCheck)"); - break; - case kPlatformUnk0: + case kPlatformUnknown: // For Mac versions, kPlatform(0) with other args has more functionality if (g_sci->getPlatform() == Common::kPlatformMacintosh && argc > 1) return kMacPlatform(s, argc - 1, argv + 1); // Otherwise, fall through case kPlatformGetPlatform: return make_reg(0, (isWindows) ? kSciPlatformWindows : kSciPlatformDOS); - case kPlatformUnk5: + case kPlatformUnknown5: // This case needs to return the opposite of case 6 to get hires graphics - return make_reg(0, !ConfMan.getBool("enable_high_resolution_graphics")); + return make_reg(0, !isWindows); case kPlatformIsHiRes: - return make_reg(0, ConfMan.getBool("enable_high_resolution_graphics")); - case kPlatformIsItWindows: + case kPlatformWin311OrHigher: return make_reg(0, isWindows); default: error("Unsupported kPlatform operation %d", operation); @@ -599,6 +588,37 @@ reg_t kPlatform(EngineState *s, int argc, reg_t *argv) { return NULL_REG; } +#ifdef ENABLE_SCI32 +reg_t kPlatform32(EngineState *s, int argc, reg_t *argv) { + enum Operation { + kGetPlatform = 0, + kGetCDSpeed = 1, + kGetColorDepth = 2, + kGetCDDrive = 3 + }; + + const Operation operation = argc > 0 ? (Operation)argv[0].toSint16() : kGetPlatform; + + switch (operation) { + case kGetPlatform: + switch (g_sci->getPlatform()) { + case Common::kPlatformDOS: + return make_reg(0, kSciPlatformDOS); + case Common::kPlatformWindows: + return make_reg(0, kSciPlatformWindows); + default: + error("Unknown platform %d", g_sci->getPlatform()); + } + case kGetColorDepth: + return make_reg(0, /* 256 color */ 2); + case kGetCDSpeed: + case kGetCDDrive: + default: + return make_reg(0, 0); + } +} +#endif + reg_t kEmpty(EngineState *s, int argc, reg_t *argv) { // Placeholder for empty kernel functions which are still called from the // engine scripts (like the empty kSetSynonyms function in SCI1.1). This diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp index 86d8a4b817..b539c84f5d 100644 --- a/engines/sci/engine/kvideo.cpp +++ b/engines/sci/engine/kvideo.cpp @@ -72,11 +72,10 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) { uint16 y = (screenHeight - height) / 2; bool skipVideo = false; - EngineState *s = g_sci->getEngineState(); if (videoDecoder->hasDirtyPalette()) { - const byte *palette = videoDecoder->getPalette() + s->_vmdPalStart * 3; - g_system->getPaletteManager()->setPalette(palette, s->_vmdPalStart, s->_vmdPalEnd - s->_vmdPalStart); + const byte *palette = videoDecoder->getPalette(); + g_system->getPaletteManager()->setPalette(palette, 0, 255); } while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) { @@ -85,7 +84,7 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) { if (frame) { if (scaleBuffer) { - // TODO: Probably should do aspect ratio correction in e.g. GK1 Windows + // TODO: Probably should do aspect ratio correction in KQ6 g_sci->_gfxScreen->scale2x((const byte *)frame->getPixels(), scaleBuffer, videoDecoder->getWidth(), videoDecoder->getHeight(), bytesPerPixel); g_system->copyRectToScreen(scaleBuffer, pitch, x, y, width, height); } else { @@ -93,8 +92,8 @@ void playVideo(Video::VideoDecoder *videoDecoder, VideoState videoState) { } if (videoDecoder->hasDirtyPalette()) { - const byte *palette = videoDecoder->getPalette() + s->_vmdPalStart * 3; - g_system->getPaletteManager()->setPalette(palette, s->_vmdPalStart, s->_vmdPalEnd - s->_vmdPalStart); + const byte *palette = videoDecoder->getPalette(); + g_system->getPaletteManager()->setPalette(palette, 0, 255); } g_system->updateScreen(); @@ -226,6 +225,80 @@ reg_t kShowMovie32(EngineState *s, int argc, reg_t *argv) { return s->r_acc; } +reg_t kRobot(EngineState *s, int argc, reg_t *argv) { + if (!s) + return make_reg(0, getSciVersion()); + error("not supposed to call this"); +} + +reg_t kRobotOpen(EngineState *s, int argc, reg_t *argv) { + const GuiResourceId robotId = argv[0].toUint16(); + const reg_t plane = argv[1]; + const int16 priority = argv[2].toSint16(); + const int16 x = argv[3].toSint16(); + const int16 y = argv[4].toSint16(); + const int16 scale = argc > 5 ? argv[5].toSint16() : 128; + g_sci->_video32->getRobotPlayer().open(robotId, plane, priority, x, y, scale); + return make_reg(0, 0); +} +reg_t kRobotShowFrame(EngineState *s, int argc, reg_t *argv) { + const uint16 frameNo = argv[0].toUint16(); + const uint16 newX = argc > 1 ? argv[1].toUint16() : (uint16)RobotDecoder::kUnspecified; + const uint16 newY = argc > 1 ? argv[2].toUint16() : (uint16)RobotDecoder::kUnspecified; + g_sci->_video32->getRobotPlayer().showFrame(frameNo, newX, newY, RobotDecoder::kUnspecified); + return s->r_acc; +} + +reg_t kRobotGetFrameSize(EngineState *s, int argc, reg_t *argv) { + Common::Rect frameRect; + const uint16 numFramesTotal = g_sci->_video32->getRobotPlayer().getFrameSize(frameRect); + + reg_t *outRect = s->_segMan->derefRegPtr(argv[0], 4); + outRect[0] = make_reg(0, frameRect.left); + outRect[1] = make_reg(0, frameRect.top); + outRect[2] = make_reg(0, frameRect.right - 1); + outRect[3] = make_reg(0, frameRect.bottom - 1); + + return make_reg(0, numFramesTotal); +} + +reg_t kRobotPlay(EngineState *s, int argc, reg_t *argv) { + g_sci->_video32->getRobotPlayer().resume(); + return s->r_acc; +} + +reg_t kRobotGetIsFinished(EngineState *s, int argc, reg_t *argv) { + return make_reg(0, g_sci->_video32->getRobotPlayer().getStatus() == RobotDecoder::kRobotStatusEnd); +} + +reg_t kRobotGetIsPlaying(EngineState *s, int argc, reg_t *argv) { + return make_reg(0, g_sci->_video32->getRobotPlayer().getStatus() == RobotDecoder::kRobotStatusPlaying); +} + +reg_t kRobotClose(EngineState *s, int argc, reg_t *argv) { + g_sci->_video32->getRobotPlayer().close(); + return s->r_acc; +} + +reg_t kRobotGetCue(EngineState *s, int argc, reg_t *argv) { + writeSelectorValue(s->_segMan, argv[0], SELECTOR(signal), g_sci->_video32->getRobotPlayer().getCue()); + return s->r_acc; +} + +reg_t kRobotPause(EngineState *s, int argc, reg_t *argv) { + g_sci->_video32->getRobotPlayer().pause(); + return s->r_acc; +} + +reg_t kRobotGetFrameNo(EngineState *s, int argc, reg_t *argv) { + return make_reg(0, g_sci->_video32->getRobotPlayer().getFrameNo()); +} + +reg_t kRobotSetPriority(EngineState *s, int argc, reg_t *argv) { + g_sci->_video32->getRobotPlayer().setPriority(argv[0].toSint16()); + return s->r_acc; +} + reg_t kShowMovieWin(EngineState *s, int argc, reg_t *argv) { if (!s) return make_reg(0, getSciVersion()); @@ -352,6 +425,10 @@ reg_t kPlayVMDClose(EngineState *s, int argc, reg_t *argv) { return make_reg(0, g_sci->_video32->getVMDPlayer().close()); } +reg_t kPlayVMDGetStatus(EngineState *s, int argc, reg_t *argv) { + return make_reg(0, g_sci->_video32->getVMDPlayer().getStatus()); +} + reg_t kPlayVMDPlayUntilEvent(EngineState *s, int argc, reg_t *argv) { const VMDPlayer::EventFlags flags = (VMDPlayer::EventFlags)argv[0].toUint16(); const int16 lastFrameNo = argc > 1 ? argv[1].toSint16() : -1; diff --git a/engines/sci/engine/savegame.cpp b/engines/sci/engine/savegame.cpp index 7804b7892d..eeddda8390 100644 --- a/engines/sci/engine/savegame.cpp +++ b/engines/sci/engine/savegame.cpp @@ -48,6 +48,7 @@ #include "sci/sound/music.h" #ifdef ENABLE_SCI32 +#include "sci/graphics/cursor32.h" #include "sci/graphics/frameout.h" #include "sci/graphics/palette32.h" #include "sci/graphics/remap32.h" @@ -889,6 +890,29 @@ void GfxRemap32::saveLoadWithSerializer(Common::Serializer &s) { _needsUpdate = true; } } + +void GfxCursor32::saveLoadWithSerializer(Common::Serializer &s) { + if (s.getVersion() < 37) { + return; + } + + s.syncAsSint32LE(_hideCount); + s.syncAsSint16LE(_restrictedArea.left); + s.syncAsSint16LE(_restrictedArea.top); + s.syncAsSint16LE(_restrictedArea.right); + s.syncAsSint16LE(_restrictedArea.bottom); + s.syncAsUint16LE(_cursorInfo.resourceId); + s.syncAsUint16LE(_cursorInfo.loopNo); + s.syncAsUint16LE(_cursorInfo.celNo); + + if (s.isLoading()) { + hide(); + setView(_cursorInfo.resourceId, _cursorInfo.loopNo, _cursorInfo.celNo); + if (!_hideCount) { + show(); + } + } +} #endif void GfxPorts::saveLoadWithSerializer(Common::Serializer &s) { diff --git a/engines/sci/engine/savegame.h b/engines/sci/engine/savegame.h index 1bf66864e8..51dbbedd87 100644 --- a/engines/sci/engine/savegame.h +++ b/engines/sci/engine/savegame.h @@ -37,7 +37,7 @@ struct EngineState; * * Version - new/changed feature * ============================= - * 37 - Segment entry data changed to pointers + * 37 - Segment entry data changed to pointers, SCI32 cursor * 36 - SCI32 bitmap segment * 35 - SCI32 remap * 34 - SCI32 palettes, and store play time in ticks diff --git a/engines/sci/engine/seg_manager.h b/engines/sci/engine/seg_manager.h index acebecea97..8ed1c3a143 100644 --- a/engines/sci/engine/seg_manager.h +++ b/engines/sci/engine/seg_manager.h @@ -30,8 +30,7 @@ #include "sci/engine/vm_types.h" #include "sci/engine/segment.h" #ifdef ENABLE_SCI32 -// TODO: Baaaad? -#include "sci/graphics/celobj32.h" +#include "sci/graphics/celobj32.h" // kLowResX, kLowResY #endif namespace Sci { diff --git a/engines/sci/engine/state.cpp b/engines/sci/engine/state.cpp index 2c85907628..a338beffc9 100644 --- a/engines/sci/engine/state.cpp +++ b/engines/sci/engine/state.cpp @@ -121,9 +121,6 @@ void EngineState::reset(bool isRestoring) { _videoState.reset(); _syncedAudioOptions = false; - - _vmdPalStart = 0; - _vmdPalEnd = 256; } void EngineState::speedThrottler(uint32 neededSleep) { diff --git a/engines/sci/engine/state.h b/engines/sci/engine/state.h index dd8d76f002..baa912b60e 100644 --- a/engines/sci/engine/state.h +++ b/engines/sci/engine/state.h @@ -203,7 +203,6 @@ public: // TODO: Excise video code from the state manager VideoState _videoState; - uint16 _vmdPalStart, _vmdPalEnd; bool _syncedAudioOptions; /** diff --git a/engines/sci/engine/workarounds.cpp b/engines/sci/engine/workarounds.cpp index 9b3b329418..bc1390864e 100644 --- a/engines/sci/engine/workarounds.cpp +++ b/engines/sci/engine/workarounds.cpp @@ -329,7 +329,7 @@ const SciWorkaroundEntry uninitializedReadWorkarounds[] = { { GID_PEPPER, -1, 894, 0, "Package", "doVerb", NULL, 3, { WORKAROUND_FAKE, 0 } }, // using the hand on the book in the inventory - bug #5154 { GID_PEPPER, 150, 928, 0, "Narrator", "startText", NULL, 0, { WORKAROUND_FAKE, 0 } }, // happens during the non-interactive demo of Pepper { GID_PQ4, -1, 25, 0, "iconToggle", "select", NULL, 1, { WORKAROUND_FAKE, 0 } }, // when toggling the icon bar to auto-hide or not - { GID_PQSWAT, -1, 64950, 0, "View", "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Using the menu in the beginning + { GID_PQSWAT, -1, 64950, 0, NULL, "handleEvent", NULL, 0, { WORKAROUND_FAKE, 0 } }, // Using any menus in-game { GID_QFG1, -1, 210, 0, "Encounter", "init", sig_uninitread_qfg1_1, 0, { WORKAROUND_FAKE, 0 } }, // qfg1/hq1: going to the brigands hideout { GID_QFG1VGA, 16, 16, 0, "lassoFailed", "changeState", NULL, -1, { WORKAROUND_FAKE, 0 } }, // qfg1vga: casting the "fetch" spell in the screen with the flowers, temps 0 and 1 - bug #5309 { GID_QFG1VGA, -1, 210, 0, "Encounter", "init", sig_uninitread_qfg1vga_1, 0, { WORKAROUND_FAKE, 0 } }, // qfg1vga: going to the brigands hideout - bug #5515 @@ -688,6 +688,7 @@ const SciWorkaroundEntry kPaletteUnsetFlag_workarounds[] = { // gameID, room,script,lvl, object-name, method-name, local-call-signature, index, workaround const SciWorkaroundEntry kSetCursor_workarounds[] = { { GID_KQ5, -1, 768, 0, "KQCursor", "init", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // CD: gets called with 4 additional "900d" parameters + { GID_MOTHERGOOSEHIRES,0, 0, -1, "MG", "setCursor", NULL, 0, { WORKAROUND_STILLCALL, 0 } }, // At the start of the game, an object is passed as the cel number SCI_WORKAROUNDENTRY_TERMINATOR }; diff --git a/engines/sci/event.cpp b/engines/sci/event.cpp index 4ad2a0cfa3..b267d2ebc2 100644 --- a/engines/sci/event.cpp +++ b/engines/sci/event.cpp @@ -30,6 +30,7 @@ #include "sci/engine/state.h" #include "sci/engine/kernel.h" #ifdef ENABLE_SCI32 +#include "sci/graphics/cursor32.h" #include "sci/graphics/frameout.h" #endif #include "sci/graphics/screen.h" @@ -168,9 +169,17 @@ SciEvent EventManager::getScummVMEvent() { if (getSciVersion() >= SCI_VERSION_2) { const Buffer &screen = g_sci->_gfxFrameout->getCurrentBuffer(); + if (ev.type == Common::EVENT_MOUSEMOVE) { + // This will clamp `mousePos` according to the restricted zone, + // so any cursor or screen item associated with the mouse position + // does not bounce when it hits the edge (or ignore the edge) + g_sci->_gfxCursor32->deviceMoved(mousePos); + } + Common::Point mousePosSci = mousePos; mulru(mousePosSci, Ratio(screen.scriptWidth, screen.screenWidth), Ratio(screen.scriptHeight, screen.screenHeight)); noEvent.mousePosSci = input.mousePosSci = mousePosSci; + } else { #endif g_sci->_gfxScreen->adjustBackUpscaledCoordinates(mousePos.y, mousePos.x); diff --git a/engines/sci/graphics/cache.cpp b/engines/sci/graphics/cache.cpp index fb1f557ad6..9c77f31a14 100644 --- a/engines/sci/graphics/cache.cpp +++ b/engines/sci/graphics/cache.cpp @@ -95,10 +95,20 @@ int16 GfxCache::kernelViewGetCelHeight(GuiResourceId viewId, int16 loopNo, int16 } int16 GfxCache::kernelViewGetLoopCount(GuiResourceId viewId) { +#ifdef ENABLE_SCI32 + if (getSciVersion() >= SCI_VERSION_2) { + return CelObjView::getNumLoops(viewId); + } +#endif return getView(viewId)->getLoopCount(); } int16 GfxCache::kernelViewGetCelCount(GuiResourceId viewId, int16 loopNo) { +#ifdef ENABLE_SCI32 + if (getSciVersion() >= SCI_VERSION_2) { + return CelObjView::getNumCels(viewId, loopNo); + } +#endif return getView(viewId)->getCelCount(loopNo); } diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp index 311684d595..d053fa2eef 100644 --- a/engines/sci/graphics/celobj32.cpp +++ b/engines/sci/graphics/celobj32.cpp @@ -45,7 +45,7 @@ void CelScaler::activateScaleTables(const Ratio &scaleX, const Ratio &scaleY) { } } - int i = 1 - _activeIndex; + const int i = 1 - _activeIndex; _activeIndex = i; CelScalerTable &table = _scaleTables[i]; @@ -65,7 +65,7 @@ void CelScaler::activateScaleTables(const Ratio &scaleX, const Ratio &scaleY) { void CelScaler::buildLookupTable(int *table, const Ratio &ratio, const int size) { int value = 0; int remainder = 0; - int num = ratio.getNumerator(); + const int num = ratio.getNumerator(); for (int i = 0; i < size; ++i) { *table++ = value; remainder += ratio.getDenominator(); @@ -164,8 +164,8 @@ struct SCALER_Scale { const byte *_row; READER _reader; int16 _x; - static int16 _valuesX[1024]; - static int16 _valuesY[1024]; + static int16 _valuesX[4096]; + static int16 _valuesY[4096]; SCALER_Scale(const CelObj &celObj, const Common::Rect &targetRect, const Common::Point &scaledPosition, const Ratio scaleX, const Ratio scaleY) : _row(nullptr), @@ -204,7 +204,7 @@ struct SCALER_Scale { if (g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth == kLowResX) { const int16 unscaledX = (scaledPosition.x / scaleX).toInt(); if (FLIP) { - int lastIndex = celObj._width - 1; + const int lastIndex = celObj._width - 1; for (int16 x = targetRect.left; x < targetRect.right; ++x) { _valuesX[x] = lastIndex - (table->valuesX[x] - unscaledX); } @@ -220,7 +220,7 @@ struct SCALER_Scale { } } else { if (FLIP) { - int lastIndex = celObj._width - 1; + const int lastIndex = celObj._width - 1; for (int16 x = 0; x < targetRect.width(); ++x) { _valuesX[targetRect.left + x] = lastIndex - table->valuesX[x]; } @@ -249,9 +249,9 @@ struct SCALER_Scale { }; template<bool FLIP, typename READER> -int16 SCALER_Scale<FLIP, READER>::_valuesX[1024]; +int16 SCALER_Scale<FLIP, READER>::_valuesX[4096]; template<bool FLIP, typename READER> -int16 SCALER_Scale<FLIP, READER>::_valuesY[1024]; +int16 SCALER_Scale<FLIP, READER>::_valuesY[4096]; #pragma mark - #pragma mark CelObj - Resource readers @@ -261,7 +261,7 @@ private: #ifndef NDEBUG const int16 _sourceHeight; #endif - byte *_pixels; + const byte *_pixels; const int16 _sourceWidth; public: @@ -270,7 +270,7 @@ public: _sourceHeight(celObj._height), #endif _sourceWidth(celObj._width) { - byte *resource = celObj.getResPointer(); + const byte *resource = celObj.getResPointer(); _pixels = resource + READ_SCI11ENDIAN_UINT32(resource + celObj._celHeaderOffset + 24); } @@ -282,8 +282,8 @@ public: struct READER_Compressed { private: - byte *_resource; - byte _buffer[1024]; + const byte *const _resource; + byte _buffer[4096]; uint32 _controlOffset; uint32 _dataOffset; uint32 _uncompressedDataOffset; @@ -301,7 +301,7 @@ public: _maxWidth(maxWidth) { assert(maxWidth <= celObj._width); - byte *celHeader = _resource + celObj._celHeaderOffset; + const byte *const celHeader = _resource + celObj._celHeaderOffset; _dataOffset = READ_SCI11ENDIAN_UINT32(celHeader + 24); _uncompressedDataOffset = READ_SCI11ENDIAN_UINT32(celHeader + 28); _controlOffset = READ_SCI11ENDIAN_UINT32(celHeader + 32); @@ -311,14 +311,14 @@ public: assert(y >= 0 && y < _sourceHeight); if (y != _y) { // compressed data segment for row - byte *row = _resource + _dataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + y * 4); + const byte *row = _resource + _dataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + y * 4); // uncompressed data segment for row - byte *literal = _resource + _uncompressedDataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + _sourceHeight * 4 + y * 4); + const byte *literal = _resource + _uncompressedDataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + _sourceHeight * 4 + y * 4); uint8 length; for (int16 i = 0; i < _maxWidth; i += length) { - byte controlByte = *row++; + const byte controlByte = *row++; length = controlByte; // Run-length encoded @@ -581,7 +581,7 @@ void CelObj::submitPalette() const { int CelObj::_nextCacheId = 1; CelCache *CelObj::_cache = nullptr; -int CelObj::searchCache(const CelInfo32 &celInfo, int *nextInsertIndex) const { +int CelObj::searchCache(const CelInfo32 &celInfo, int *const nextInsertIndex) const { *nextInsertIndex = -1; int oldestId = _nextCacheId + 1; int oldestIndex = 0; @@ -791,6 +791,49 @@ void CelObj::scaleDrawUncompNoMD(Buffer &target, const Ratio &scaleX, const Rati #pragma mark - #pragma mark CelObjView +int16 CelObjView::getNumLoops(const GuiResourceId viewId) { + const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, viewId), false); + + if (!resource) { + return 0; + } + + assert(resource->size >= 3); + return resource->data[2]; +} + +int16 CelObjView::getNumCels(const GuiResourceId viewId, const int16 loopNo) { + const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, viewId), false); + + if (!resource) { + return 0; + } + + const byte *const data = resource->data; + + const uint16 loopCount = data[2]; + if (loopNo >= loopCount || loopNo < 0) { + return 0; + } + + const uint16 viewHeaderSize = READ_SCI11ENDIAN_UINT16(data); + const uint8 loopHeaderSize = data[12]; + const uint8 viewHeaderFieldSize = 2; + +#ifndef NDEBUG + const byte *const dataMax = data + resource->size; +#endif + const byte *loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * loopNo); + assert(loopHeader + 3 <= dataMax); + + if ((int8)loopHeader[0] != -1) { + loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * (int8)loopHeader[0]); + assert(loopHeader >= data && loopHeader + 3 <= dataMax); + } + + return loopHeader[2]; +} + CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int16 celNo) { _info.type = kCelTypeView; _info.resourceId = viewId; @@ -801,7 +844,7 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int _transparent = true; int cacheInsertIndex; - int cacheIndex = searchCache(_info, &cacheInsertIndex); + const int cacheIndex = searchCache(_info, &cacheInsertIndex); if (cacheIndex != -1) { CelCacheEntry &entry = (*_cache)[cacheIndex]; const CelObjView *const cachedCelObj = dynamic_cast<CelObjView *>(entry.celObj); @@ -817,15 +860,14 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int // generates view resource metadata for both SCI16 and SCI32 // implementations - Resource *resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, viewId), false); + const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, viewId), false); // NOTE: SCI2.1/SQ6 just silently returns here. if (!resource) { - warning("View resource %d not loaded", viewId); - return; + error("View resource %d not found", viewId); } - byte *data = resource->data; + const byte *const data = resource->data; _scaledWidth = READ_SCI11ENDIAN_UINT16(data + 14); _scaledHeight = READ_SCI11ENDIAN_UINT16(data + 16); @@ -844,7 +886,7 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int } } - uint16 loopCount = data[2]; + const uint16 loopCount = data[2]; if (_info.loopNo >= loopCount) { _info.loopNo = loopCount - 1; } @@ -859,7 +901,7 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int const uint8 loopHeaderSize = data[12]; const uint8 viewHeaderFieldSize = 2; - byte *loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * _info.loopNo); + const byte *loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * _info.loopNo); if ((int8)loopHeader[0] != -1) { if (loopHeader[1] == 1) { @@ -874,10 +916,14 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int _info.celNo = celCount - 1; } + if (_info.celNo < 0) { + error("Cel is less than 0!"); + } + _hunkPaletteOffset = READ_SCI11ENDIAN_UINT32(data + 8); _celHeaderOffset = READ_SCI11ENDIAN_UINT32(loopHeader + 12) + (data[13] * _info.celNo); - byte *celHeader = data + _celHeaderOffset; + const byte *const celHeader = data + _celHeaderOffset; _width = READ_SCI11ENDIAN_UINT16(celHeader); _height = READ_SCI11ENDIAN_UINT16(celHeader + 2); @@ -906,7 +952,7 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int } bool CelObjView::analyzeUncompressedForRemap() const { - byte *pixels = getResPointer() + READ_SCI11ENDIAN_UINT32(getResPointer() + _celHeaderOffset + 24); + const byte *pixels = getResPointer() + READ_SCI11ENDIAN_UINT32(getResPointer() + _celHeaderOffset + 24); for (int i = 0; i < _width * _height; ++i) { const byte pixel = pixels[i]; if ( @@ -923,7 +969,7 @@ bool CelObjView::analyzeUncompressedForRemap() const { bool CelObjView::analyzeForRemap() const { READER_Compressed reader(*this, _width); for (int y = 0; y < _height; y++) { - const byte *curRow = reader.getRow(y); + const byte *const curRow = reader.getRow(y); for (int x = 0; x < _width; x++) { const byte pixel = curRow[x]; if ( @@ -948,7 +994,7 @@ CelObjView *CelObjView::duplicate() const { } byte *CelObjView::getResPointer() const { - const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, _info.resourceId), false); + Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, _info.resourceId), false); if (resource == nullptr) { error("Failed to load view %d from resource manager", _info.resourceId); } @@ -969,7 +1015,7 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) { _remap = false; int cacheInsertIndex; - int cacheIndex = searchCache(_info, &cacheInsertIndex); + const int cacheIndex = searchCache(_info, &cacheInsertIndex); if (cacheIndex != -1) { CelCacheEntry &entry = (*_cache)[cacheIndex]; const CelObjPic *const cachedCelObj = dynamic_cast<CelObjPic *>(entry.celObj); @@ -981,15 +1027,14 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) { return; } - Resource *resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypePic, picId), false); + const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypePic, picId), false); // NOTE: SCI2.1/SQ6 just silently returns here. if (!resource) { - warning("Pic resource %d not loaded", picId); - return; + error("Pic resource %d not found", picId); } - byte *data = resource->data; + const byte *const data = resource->data; _celCount = data[2]; @@ -1000,7 +1045,7 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) { _celHeaderOffset = READ_SCI11ENDIAN_UINT16(data) + (READ_SCI11ENDIAN_UINT16(data + 4) * _info.celNo); _hunkPaletteOffset = READ_SCI11ENDIAN_UINT32(data + 6); - byte *celHeader = data + _celHeaderOffset; + const byte *const celHeader = data + _celHeaderOffset; _width = READ_SCI11ENDIAN_UINT16(celHeader); _height = READ_SCI11ENDIAN_UINT16(celHeader + 2); @@ -1012,8 +1057,8 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) { _relativePosition.x = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 38); _relativePosition.y = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 40); - uint16 sizeFlag1 = READ_SCI11ENDIAN_UINT16(data + 10); - uint16 sizeFlag2 = READ_SCI11ENDIAN_UINT16(data + 12); + const uint16 sizeFlag1 = READ_SCI11ENDIAN_UINT16(data + 10); + const uint16 sizeFlag2 = READ_SCI11ENDIAN_UINT16(data + 12); if (sizeFlag2) { _scaledWidth = sizeFlag1; @@ -1032,7 +1077,7 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) { if (celHeader[10] & 128) { // NOTE: This is correct according to SCI2.1/SQ6/DOS; // the engine re-reads the byte value as a word value - uint16 flags = READ_SCI11ENDIAN_UINT16(celHeader + 10); + const uint16 flags = READ_SCI11ENDIAN_UINT16(celHeader + 10); _transparent = flags & 1 ? true : false; _remap = flags & 2 ? true : false; } else { @@ -1047,8 +1092,8 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) { } bool CelObjPic::analyzeUncompressedForSkip() const { - byte *resource = getResPointer(); - byte *pixels = resource + READ_SCI11ENDIAN_UINT32(resource + _celHeaderOffset + 24); + const byte *const resource = getResPointer(); + const byte *const pixels = resource + READ_SCI11ENDIAN_UINT32(resource + _celHeaderOffset + 24); for (int i = 0; i < _width * _height; ++i) { uint8 pixel = pixels[i]; if (pixel == _transparentColor) { @@ -1060,7 +1105,7 @@ bool CelObjPic::analyzeUncompressedForSkip() const { } void CelObjPic::draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const bool mirrorX) { - Ratio square; + const Ratio square; _drawMirrored = mirrorX; drawTo(target, targetRect, scaledPosition, square, square); } @@ -1088,15 +1133,21 @@ CelObjMem::CelObjMem(const reg_t bitmapObject) { _celHeaderOffset = 0; _transparent = true; - SciBitmap &bitmap = *g_sci->getEngineState()->_segMan->lookupBitmap(bitmapObject); - _width = bitmap.getWidth(); - _height = bitmap.getHeight(); - _displace = bitmap.getDisplace(); - _transparentColor = bitmap.getSkipColor(); - _scaledWidth = bitmap.getScaledWidth(); - _scaledHeight = bitmap.getScaledHeight(); - _hunkPaletteOffset = bitmap.getHunkPaletteOffset(); - _remap = bitmap.getRemap(); + SciBitmap *bitmap = g_sci->getEngineState()->_segMan->lookupBitmap(bitmapObject); + + // NOTE: SSCI did no error checking here at all. + if (!bitmap) { + error("Bitmap %04x:%04x not found", PRINT_REG(bitmapObject)); + } + + _width = bitmap->getWidth(); + _height = bitmap->getHeight(); + _displace = bitmap->getDisplace(); + _transparentColor = bitmap->getSkipColor(); + _scaledWidth = bitmap->getScaledWidth(); + _scaledHeight = bitmap->getScaledHeight(); + _hunkPaletteOffset = bitmap->getHunkPaletteOffset(); + _remap = bitmap->getRemap(); } CelObjMem *CelObjMem::duplicate() const { diff --git a/engines/sci/graphics/celobj32.h b/engines/sci/graphics/celobj32.h index eb6ce3a3c9..21e86d03e0 100644 --- a/engines/sci/graphics/celobj32.h +++ b/engines/sci/graphics/celobj32.h @@ -147,7 +147,7 @@ struct CelScalerTable { * the correct column to read from the source bitmap * when drawing a scaled version of the source bitmap. */ - int valuesX[1024]; + int valuesX[4096]; /** * The ratio used to generate the x-values. @@ -159,7 +159,7 @@ struct CelScalerTable { * the correct row to read from a source bitmap when * drawing a scaled version of the source bitmap. */ - int valuesY[1024]; + int valuesY[4096]; /** * The ratio used to generate the y-values. @@ -400,7 +400,7 @@ public: * Reads the pixel at the given coordinates. This method * is valid only for CelObjView and CelObjPic. */ - virtual uint8 readPixel(uint16 x, uint16 y, bool mirrorX) const; + virtual uint8 readPixel(const uint16 x, const uint16 y, const bool mirrorX) const; /** * Submits the palette from this cel to the palette @@ -505,6 +505,9 @@ public: using CelObj::draw; + static int16 getNumLoops(const GuiResourceId viewId); + static int16 getNumCels(const GuiResourceId viewId, const int16 loopNo); + /** * Draws the cel to the target buffer using the * positioning, mirroring, and scaling information from diff --git a/engines/sci/graphics/compare.cpp b/engines/sci/graphics/compare.cpp index 130416ff60..36026a8134 100644 --- a/engines/sci/graphics/compare.cpp +++ b/engines/sci/graphics/compare.cpp @@ -37,7 +37,7 @@ namespace Sci { -GfxCompare::GfxCompare(SegManager *segMan, GfxCache *cache, GfxScreen *screen, GfxCoordAdjuster *coordAdjuster) +GfxCompare::GfxCompare(SegManager *segMan, GfxCache *cache, GfxScreen *screen, GfxCoordAdjuster16 *coordAdjuster) : _segMan(segMan), _cache(cache), _screen(screen), _coordAdjuster(coordAdjuster) { } diff --git a/engines/sci/graphics/compare.h b/engines/sci/graphics/compare.h index c7005980d0..dd65b90bea 100644 --- a/engines/sci/graphics/compare.h +++ b/engines/sci/graphics/compare.h @@ -34,7 +34,7 @@ class Screen; */ class GfxCompare { public: - GfxCompare(SegManager *segMan, GfxCache *cache, GfxScreen *screen, GfxCoordAdjuster *coordAdjuster); + GfxCompare(SegManager *segMan, GfxCache *cache, GfxScreen *screen, GfxCoordAdjuster16 *coordAdjuster); ~GfxCompare(); uint16 kernelOnControl(byte screenMask, const Common::Rect &rect); @@ -50,7 +50,7 @@ private: SegManager *_segMan; GfxCache *_cache; GfxScreen *_screen; - GfxCoordAdjuster *_coordAdjuster; + GfxCoordAdjuster16 *_coordAdjuster; uint16 isOnControl(uint16 screenMask, const Common::Rect &rect); diff --git a/engines/sci/graphics/coordadjuster.cpp b/engines/sci/graphics/coordadjuster.cpp index 93dff10382..2f22d191d0 100644 --- a/engines/sci/graphics/coordadjuster.cpp +++ b/engines/sci/graphics/coordadjuster.cpp @@ -32,9 +32,6 @@ namespace Sci { -GfxCoordAdjuster::GfxCoordAdjuster() { -} - GfxCoordAdjuster16::GfxCoordAdjuster16(GfxPorts *ports) : _ports(ports) { } @@ -83,53 +80,4 @@ Common::Rect GfxCoordAdjuster16::pictureGetDisplayArea() { return displayArea; } -#ifdef ENABLE_SCI32 -GfxCoordAdjuster32::GfxCoordAdjuster32(SegManager *segMan) - : _segMan(segMan) { - _scriptsRunningWidth = 0; - _scriptsRunningHeight = 0; -} - -GfxCoordAdjuster32::~GfxCoordAdjuster32() { -} - -void GfxCoordAdjuster32::kernelGlobalToLocal(int16 &x, int16 &y, reg_t planeObject) { - uint16 planeTop = readSelectorValue(_segMan, planeObject, SELECTOR(top)); - uint16 planeLeft = readSelectorValue(_segMan, planeObject, SELECTOR(left)); - - y -= planeTop; - x -= planeLeft; -} -void GfxCoordAdjuster32::kernelLocalToGlobal(int16 &x, int16 &y, reg_t planeObject) { - uint16 planeTop = readSelectorValue(_segMan, planeObject, SELECTOR(top)); - uint16 planeLeft = readSelectorValue(_segMan, planeObject, SELECTOR(left)); - - x += planeLeft; - y += planeTop; -} - -void GfxCoordAdjuster32::setScriptsResolution(uint16 width, uint16 height) { - _scriptsRunningWidth = width; - _scriptsRunningHeight = height; -} - -void GfxCoordAdjuster32::fromDisplayToScript(int16 &y, int16 &x) { - y = ((y * _scriptsRunningHeight) / g_sci->_gfxScreen->getHeight()); - x = ((x * _scriptsRunningWidth) / g_sci->_gfxScreen->getWidth()); -} - -void GfxCoordAdjuster32::fromScriptToDisplay(int16 &y, int16 &x) { - y = ((y * g_sci->_gfxScreen->getHeight()) / _scriptsRunningHeight); - x = ((x * g_sci->_gfxScreen->getWidth()) / _scriptsRunningWidth); -} - -void GfxCoordAdjuster32::pictureSetDisplayArea(Common::Rect displayArea) { - _pictureDisplayArea = displayArea; -} - -Common::Rect GfxCoordAdjuster32::pictureGetDisplayArea() { - return _pictureDisplayArea; -} -#endif - } // End of namespace Sci diff --git a/engines/sci/graphics/coordadjuster.h b/engines/sci/graphics/coordadjuster.h index cb0227fbe4..f7ebd3ec75 100644 --- a/engines/sci/graphics/coordadjuster.h +++ b/engines/sci/graphics/coordadjuster.h @@ -35,27 +35,7 @@ class GfxPorts; * most of the time sci32 doesn't do any coordinate adjustment at all * sci16 does a lot of port adjustment on given coordinates */ -class GfxCoordAdjuster { -public: - GfxCoordAdjuster(); - virtual ~GfxCoordAdjuster() { } - - virtual void kernelGlobalToLocal(int16 &x, int16 &y, reg_t planeObject = NULL_REG) { } - virtual void kernelLocalToGlobal(int16 &x, int16 &y, reg_t planeObject = NULL_REG) { } - - virtual Common::Rect onControl(Common::Rect rect) { return rect; } - virtual void setCursorPos(Common::Point &pos) { } - virtual void moveCursor(Common::Point &pos) { } - - virtual void setScriptsResolution(uint16 width, uint16 height) { } - virtual void fromScriptToDisplay(int16 &y, int16 &x) { } - virtual void fromDisplayToScript(int16 &y, int16 &x) { } - - virtual Common::Rect pictureGetDisplayArea() { return Common::Rect(0, 0); } -private: -}; - -class GfxCoordAdjuster16 : public GfxCoordAdjuster { +class GfxCoordAdjuster16 { public: GfxCoordAdjuster16(GfxPorts *ports); ~GfxCoordAdjuster16(); @@ -73,32 +53,6 @@ private: GfxPorts *_ports; }; -#ifdef ENABLE_SCI32 -class GfxCoordAdjuster32 : public GfxCoordAdjuster { -public: - GfxCoordAdjuster32(SegManager *segMan); - ~GfxCoordAdjuster32(); - - void kernelGlobalToLocal(int16 &x, int16 &y, reg_t planeObject = NULL_REG); - void kernelLocalToGlobal(int16 &x, int16 &y, reg_t planeObject = NULL_REG); - - void setScriptsResolution(uint16 width, uint16 height); - void fromScriptToDisplay(int16 &y, int16 &x); - void fromDisplayToScript(int16 &y, int16 &x); - - void pictureSetDisplayArea(Common::Rect displayArea); - Common::Rect pictureGetDisplayArea(); - -private: - SegManager *_segMan; - - Common::Rect _pictureDisplayArea; - - uint16 _scriptsRunningWidth; - uint16 _scriptsRunningHeight; -}; -#endif - } // End of namespace Sci #endif diff --git a/engines/sci/graphics/cursor.cpp b/engines/sci/graphics/cursor.cpp index f5dd473959..c3229121c8 100644 --- a/engines/sci/graphics/cursor.cpp +++ b/engines/sci/graphics/cursor.cpp @@ -80,7 +80,7 @@ GfxCursor::~GfxCursor() { kernelClearZoomZone(); } -void GfxCursor::init(GfxCoordAdjuster *coordAdjuster, EventManager *event) { +void GfxCursor::init(GfxCoordAdjuster16 *coordAdjuster, EventManager *event) { _coordAdjuster = coordAdjuster; _event = event; } diff --git a/engines/sci/graphics/cursor.h b/engines/sci/graphics/cursor.h index 5125469cfe..c57d9dab52 100644 --- a/engines/sci/graphics/cursor.h +++ b/engines/sci/graphics/cursor.h @@ -55,7 +55,7 @@ public: GfxCursor(ResourceManager *resMan, GfxPalette *palette, GfxScreen *screen); ~GfxCursor(); - void init(GfxCoordAdjuster *coordAdjuster, EventManager *event); + void init(GfxCoordAdjuster16 *coordAdjuster, EventManager *event); void kernelShow(); void kernelHide(); @@ -103,7 +103,7 @@ private: ResourceManager *_resMan; GfxScreen *_screen; GfxPalette *_palette; - GfxCoordAdjuster *_coordAdjuster; + GfxCoordAdjuster16 *_coordAdjuster; EventManager *_event; int _upscaledHires; diff --git a/engines/sci/graphics/cursor32.cpp b/engines/sci/graphics/cursor32.cpp new file mode 100644 index 0000000000..014b617c74 --- /dev/null +++ b/engines/sci/graphics/cursor32.cpp @@ -0,0 +1,396 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "common/rational.h" // for Rational, operator* +#include "common/system.h" // for OSystem, g_system +#include "graphics/cursorman.h" // for CursorMan +#include "sci/graphics/celobj32.h" // for CelObjView, CelInfo32, Ratio +#include "sci/graphics/cursor32.h" +#include "sci/graphics/frameout.h" // for GfxFrameout + +namespace Sci { + +GfxCursor32::GfxCursor32() : + _hideCount(0), + _position(0, 0), + _writeToVMAP(false) { + CursorMan.showMouse(false); +} + +void GfxCursor32::init(const Buffer &vmap) { + _vmap = vmap; + _vmapRegion.rect = Common::Rect(_vmap.screenWidth, _vmap.screenHeight); + _vmapRegion.data = (byte *)_vmap.getPixels(); + _restrictedArea = _vmapRegion.rect; +} + +GfxCursor32::~GfxCursor32() { + CursorMan.showMouse(true); + free(_cursor.data); + free(_cursorBack.data); + free(_drawBuff1.data); + free(_drawBuff2.data); + free(_savedVmapRegion.data); +} + +void GfxCursor32::hide() { + if (_hideCount++) { + return; + } + + if (!_cursorBack.rect.isEmpty()) { + drawToHardware(_cursorBack); + } +} + +void GfxCursor32::revealCursor() { + _cursorBack.rect = _cursor.rect; + _cursorBack.rect.clip(_vmapRegion.rect); + if (_cursorBack.rect.isEmpty()) { + return; + } + + readVideo(_cursorBack); + _drawBuff1.rect = _cursor.rect; + copy(_drawBuff1, _cursorBack); + paint(_drawBuff1, _cursor); + drawToHardware(_drawBuff1); +} + +void GfxCursor32::paint(DrawRegion &target, const DrawRegion &source) { + if (source.rect.isEmpty()) { + return; + } + + Common::Rect drawRect(source.rect); + drawRect.clip(target.rect); + if (drawRect.isEmpty()) { + return; + } + + const int16 sourceXOffset = drawRect.left - source.rect.left; + const int16 sourceYOffset = drawRect.top - source.rect.top; + const int16 drawRectWidth = drawRect.width(); + const int16 drawRectHeight = drawRect.height(); + + byte *targetPixel = target.data + ((drawRect.top - target.rect.top) * target.rect.width()) + (drawRect.left - target.rect.left); + const byte *sourcePixel = source.data + (sourceYOffset * source.rect.width()) + sourceXOffset; + const uint8 skipColor = source.skipColor; + + const int16 sourceStride = source.rect.width() - drawRectWidth; + const int16 targetStride = target.rect.width() - drawRectWidth; + + for (int16 y = 0; y < drawRectHeight; ++y) { + for (int16 x = 0; x < drawRectWidth; ++x) { + if (*sourcePixel != skipColor) { + *targetPixel = *sourcePixel; + } + ++targetPixel; + ++sourcePixel; + } + sourcePixel += sourceStride; + targetPixel += targetStride; + } +} + +void GfxCursor32::drawToHardware(const DrawRegion &source) { + Common::Rect drawRect(source.rect); + drawRect.clip(_vmapRegion.rect); + const int16 sourceXOffset = drawRect.left - source.rect.left; + const int16 sourceYOffset = drawRect.top - source.rect.top; + byte *sourcePixel = source.data + (sourceYOffset * source.rect.width()) + sourceXOffset; + + g_system->copyRectToScreen(sourcePixel, source.rect.width(), drawRect.left, drawRect.top, drawRect.width(), drawRect.height()); +} + +void GfxCursor32::unhide() { + if (_hideCount == 0 || --_hideCount) { + return; + } + + _cursor.rect.moveTo(_position.x - _hotSpot.x, _position.y - _hotSpot.y); + revealCursor(); +} + +void GfxCursor32::show() { + if (_hideCount) { + _hideCount = 0; + _cursor.rect.moveTo(_position.x - _hotSpot.x, _position.y - _hotSpot.y); + revealCursor(); + } +} + +void GfxCursor32::setRestrictedArea(const Common::Rect &rect) { + _restrictedArea = rect; + + const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; + const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; + const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + + mulru(_restrictedArea, Ratio(screenWidth, scriptWidth), Ratio(screenHeight, scriptHeight), 0); + + if (_position.x < rect.left) { + _position.x = rect.left; + } + if (_position.x >= rect.right) { + _position.x = rect.right - 1; + } + if (_position.y < rect.top) { + _position.y = rect.top; + } + if (_position.y >= rect.bottom) { + _position.y = rect.bottom - 1; + } + + g_system->warpMouse(_position.x, _position.y); +} + +void GfxCursor32::clearRestrictedArea() { + _restrictedArea = _vmapRegion.rect; +} + +void GfxCursor32::setView(const GuiResourceId viewId, const int16 loopNo, const int16 celNo) { + hide(); + + _cursorInfo.resourceId = viewId; + _cursorInfo.loopNo = loopNo; + _cursorInfo.celNo = celNo; + + if (viewId != -1) { + CelObjView view(viewId, loopNo, celNo); + + _hotSpot = view._displace; + _width = view._width; + _height = view._height; + + // SSCI never increased the size of cursors, but some of the cursors + // in early SCI32 games were designed for low-resolution display mode + // and so are kind of hard to pick out when running in high-resolution + // mode. + // To address this, we make some slight adjustments to cursor display + // in these early games: + // GK1: All the cursors are increased in size since they all appear to + // be designed for low-res display. + // PQ4: We only make the cursors bigger if they are above a set + // threshold size because inventory items usually have a + // high-resolution cursor representation. + bool pixelDouble = false; + if (g_sci->_gfxFrameout->_isHiRes && + (g_sci->getGameId() == GID_GK1 || + (g_sci->getGameId() == GID_PQ4 && _width <= 22 && _height <= 22))) { + + _width *= 2; + _height *= 2; + _hotSpot.x *= 2; + _hotSpot.y *= 2; + pixelDouble = true; + } + + _cursor.data = (byte *)realloc(_cursor.data, _width * _height); + _cursor.rect = Common::Rect(_width, _height); + memset(_cursor.data, 255, _width * _height); + _cursor.skipColor = 255; + + Buffer target(_width, _height, _cursor.data); + if (pixelDouble) { + view.draw(target, _cursor.rect, Common::Point(0, 0), false, 2, 2); + } else { + view.draw(target, _cursor.rect, Common::Point(0, 0), false); + } + } else { + _hotSpot = Common::Point(0, 0); + _width = _height = 1; + _cursor.data = (byte *)realloc(_cursor.data, _width * _height); + _cursor.rect = Common::Rect(_width, _height); + *_cursor.data = _cursor.skipColor; + _cursorBack.rect = _cursor.rect; + _cursorBack.rect.clip(_vmapRegion.rect); + if (!_cursorBack.rect.isEmpty()) { + readVideo(_cursorBack); + } + } + + _cursorBack.data = (byte *)realloc(_cursorBack.data, _width * _height); + _drawBuff1.data = (byte *)realloc(_drawBuff1.data, _width * _height); + _drawBuff2.data = (byte *)realloc(_drawBuff2.data, _width * _height * 4); + _savedVmapRegion.data = (byte *)realloc(_savedVmapRegion.data, _width * _height); + + unhide(); +} + +void GfxCursor32::readVideo(DrawRegion &target) { + if (g_sci->_gfxFrameout->_frameNowVisible) { + copy(target, _vmapRegion); + } else { + // NOTE: SSCI would read the background for the cursor directly out of + // video memory here, but as far as can be determined, this does not + // seem to actually be necessary for proper cursor rendering + } +} + +void GfxCursor32::copy(DrawRegion &target, const DrawRegion &source) { + if (source.rect.isEmpty()) { + return; + } + + Common::Rect drawRect(source.rect); + drawRect.clip(target.rect); + if (drawRect.isEmpty()) { + return; + } + + const int16 sourceXOffset = drawRect.left - source.rect.left; + const int16 sourceYOffset = drawRect.top - source.rect.top; + const int16 drawWidth = drawRect.width(); + const int16 drawHeight = drawRect.height(); + + byte *targetPixel = target.data + ((drawRect.top - target.rect.top) * target.rect.width()) + (drawRect.left - target.rect.left); + const byte *sourcePixel = source.data + (sourceYOffset * source.rect.width()) + sourceXOffset; + + const int16 sourceStride = source.rect.width(); + const int16 targetStride = target.rect.width(); + + for (int y = 0; y < drawHeight; ++y) { + memcpy(targetPixel, sourcePixel, drawWidth); + targetPixel += targetStride; + sourcePixel += sourceStride; + } +} + +void GfxCursor32::setPosition(const Common::Point &position) { + const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; + const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; + + _position.x = (position.x * Ratio(screenWidth, scriptWidth)).toInt(); + _position.y = (position.y * Ratio(screenHeight, scriptHeight)).toInt(); + + g_system->warpMouse(_position.x, _position.y); +} + +void GfxCursor32::gonnaPaint(Common::Rect paintRect) { + if (!_hideCount && !_writeToVMAP && !_cursorBack.rect.isEmpty()) { + paintRect.left &= ~3; + paintRect.right |= 3; + if (_cursorBack.rect.intersects(paintRect)) { + _writeToVMAP = true; + } + } +} + +void GfxCursor32::paintStarting() { + if (_writeToVMAP) { + _savedVmapRegion.rect = _cursor.rect; + copy(_savedVmapRegion, _vmapRegion); + paint(_vmapRegion, _cursor); + } +} + +void GfxCursor32::donePainting() { + if (_writeToVMAP) { + copy(_vmapRegion, _savedVmapRegion); + _savedVmapRegion.rect = Common::Rect(); + _writeToVMAP = false; + } + + if (!_hideCount && !_cursorBack.rect.isEmpty()) { + copy(_cursorBack, _vmapRegion); + } +} + +void GfxCursor32::deviceMoved(Common::Point &position) { + if (position.x < _restrictedArea.left) { + position.x = _restrictedArea.left; + } + if (position.x >= _restrictedArea.right) { + position.x = _restrictedArea.right - 1; + } + if (position.y < _restrictedArea.top) { + position.y = _restrictedArea.top; + } + if (position.y >= _restrictedArea.bottom) { + position.y = _restrictedArea.bottom - 1; + } + + _position = position; + + g_system->warpMouse(position.x, position.y); + move(); +} + +void GfxCursor32::move() { + if (_hideCount) { + return; + } + + // Cursor moved onto the screen after being offscreen + _cursor.rect.moveTo(_position.x - _hotSpot.x, _position.y - _hotSpot.y); + if (_cursorBack.rect.isEmpty()) { + revealCursor(); + return; + } + + // Cursor moved offscreen + if (!_cursor.rect.intersects(_vmapRegion.rect)) { + drawToHardware(_cursorBack); + return; + } + + if (!_cursor.rect.intersects(_cursorBack.rect)) { + // Cursor moved to a completely different part of the screen + _drawBuff1.rect = _cursor.rect; + _drawBuff1.rect.clip(_vmapRegion.rect); + readVideo(_drawBuff1); + + _drawBuff2.rect = _drawBuff1.rect; + copy(_drawBuff2, _drawBuff1); + + paint(_drawBuff1, _cursor); + drawToHardware(_drawBuff1); + + drawToHardware(_cursorBack); + + _cursorBack.rect = _cursor.rect; + _cursorBack.rect.clip(_vmapRegion.rect); + copy(_cursorBack, _drawBuff2); + } else { + // Cursor moved, but still overlaps the previous cursor location + Common::Rect mergedRect(_cursorBack.rect); + mergedRect.extend(_cursor.rect); + mergedRect.clip(_vmapRegion.rect); + + _drawBuff2.rect = mergedRect; + readVideo(_drawBuff2); + + copy(_drawBuff2, _cursorBack); + + _cursorBack.rect = _cursor.rect; + _cursorBack.rect.clip(_vmapRegion.rect); + copy(_cursorBack, _drawBuff2); + + paint(_drawBuff2, _cursor); + drawToHardware(_drawBuff2); + } +} +} // End of namespace Sci diff --git a/engines/sci/graphics/cursor32.h b/engines/sci/graphics/cursor32.h new file mode 100644 index 0000000000..d4745536b1 --- /dev/null +++ b/engines/sci/graphics/cursor32.h @@ -0,0 +1,250 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef SCI_GRAPHICS_CURSOR32_H +#define SCI_GRAPHICS_CURSOR32_H + +#include "common/rect.h" // for Point, Rect +#include "common/scummsys.h" // for int16, byte, uint8 +#include "common/serializer.h" // for Serializable, Serializer (ptr only) +#include "sci/graphics/celobj32.h" // for CelInfo32 +#include "sci/graphics/helpers.h" // for GuiResourceId + +namespace Sci { + +class GfxCursor32 : Common::Serializable { +public: + GfxCursor32(); + ~GfxCursor32(); + + /** + * Initialises the cursor system with the given + * buffer to use as the output buffer for + * rendering the cursor. + */ + void init(const Buffer &vmap); + + /** + * Called when the hardware mouse moves. + */ + void deviceMoved(Common::Point &position); + + /** + * Called by GfxFrameout once for each show + * rectangle that is going to be drawn to + * hardware. + */ + void gonnaPaint(Common::Rect paintRect); + + /** + * Called by GfxFrameout when the rendering to + * hardware begins. + */ + void paintStarting(); + + /** + * Called by GfxFrameout when the output buffer + * has finished rendering to hardware. + */ + void donePainting(); + + /** + * Hides the cursor. Each call to `hide` will + * increment a hide counter, which must be + * returned to 0 before the cursor will be + * shown again. + */ + void hide(); + + /** + * Shows the cursor, if the hide counter is + * returned to 0. + */ + void unhide(); + + /** + * Shows the cursor regardless of the state of + * the hide counter. + */ + void show(); + + /** + * Sets the view used to render the cursor. + */ + void setView(const GuiResourceId viewId, const int16 loopNo, const int16 celNo); + + /** + * Explicitly sets the position of the cursor, + * in game script coordinates. + */ + void setPosition(const Common::Point &position); + + /** + * Sets the region that the mouse is allowed + * to move within. + */ + void setRestrictedArea(const Common::Rect &rect); + + /** + * Removes restrictions on mouse movement. + */ + void clearRestrictedArea(); + + virtual void saveLoadWithSerializer(Common::Serializer &ser); + +private: + struct DrawRegion { + Common::Rect rect; + byte *data; + uint8 skipColor; + + DrawRegion() : rect(), data(nullptr) {} + }; + + /** + * Information about the current cursor. + * Used to restore cursor when loading a + * savegame. + */ + CelInfo32 _cursorInfo; + + /** + * Content behind the cursor? TODO + */ + DrawRegion _cursorBack; + + /** + * Scratch buffer. + */ + DrawRegion _drawBuff1; + + /** + * Scratch buffer 2. + */ + DrawRegion _drawBuff2; + + /** + * A draw region representing the current + * output buffer. + */ + DrawRegion _vmapRegion; + + /** + * The content behind the cursor in the + * output buffer. + */ + DrawRegion _savedVmapRegion; + + /** + * The cursor bitmap. + */ + DrawRegion _cursor; + + /** + * The width and height of the cursor, + * in screen coordinates. + */ + int16 _width, _height; + + /** + * The output buffer where the cursor is + * rendered. + */ + Buffer _vmap; + + /** + * The number of times the cursor has been + * hidden. + */ + int _hideCount; + + /** + * The rendered position of the cursor, in + * screen coordinates. + */ + Common::Point _position; + + /** + * The position of the cursor hot spot, relative + * to the cursor origin, in screen pixels. + */ + Common::Point _hotSpot; + + /** + * The area within which the cursor is allowed + * to move, in screen pixels. + */ + Common::Rect _restrictedArea; + + /** + * Indicates whether or not the cursor needs to + * be repainted on the output buffer due to a + * change of graphics in the area underneath the + * cursor. + */ + bool _writeToVMAP; + + /** + * Reads data from the output buffer or hardware + * to the given draw region. + */ + void readVideo(DrawRegion &target); + + /** + * Reads data from the output buffer to the + * given draw region. + */ + void readVideoFromVmap(DrawRegion &target); + + /** + * Copies pixel data from the given source to + * the given target. + */ + void copy(DrawRegion &target, const DrawRegion &source); + + /** + * Draws from the given source onto the given + * target, skipping pixels in the source that + * match the `skipColor` property. + */ + void paint(DrawRegion &target, const DrawRegion &source); + + /** + * Draws the cursor to the position it was + * drawn to prior to moving offscreen or being + * hidden by a call to `hide`. + */ + void revealCursor(); + + /** + * Draws the given source to the output buffer. + */ + void drawToHardware(const DrawRegion &source); + + /** + * Renders the cursor at its new location. + */ + void move(); +}; + +} // End of namespace Sci +#endif diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index 21ffb5f937..4e0aa22669 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -29,6 +29,7 @@ #include "common/system.h" #include "common/textconsole.h" #include "engines/engine.h" +#include "engines/util.h" #include "graphics/palette.h" #include "graphics/surface.h" @@ -39,47 +40,53 @@ #include "sci/engine/selector.h" #include "sci/engine/vm.h" #include "sci/graphics/cache.h" -#include "sci/graphics/coordadjuster.h" #include "sci/graphics/compare.h" +#include "sci/graphics/cursor32.h" #include "sci/graphics/font.h" -#include "sci/graphics/screen.h" +#include "sci/graphics/frameout.h" #include "sci/graphics/paint32.h" #include "sci/graphics/palette32.h" #include "sci/graphics/plane32.h" #include "sci/graphics/remap32.h" +#include "sci/graphics/screen.h" #include "sci/graphics/screen_item32.h" #include "sci/graphics/text32.h" #include "sci/graphics/frameout.h" -#include "sci/video/robot_decoder.h" #include "sci/graphics/transitions32.h" +#include "sci/graphics/video32.h" namespace Sci { -GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette32 *palette, GfxTransitions32 *transitions) : +GfxFrameout::GfxFrameout(SegManager *segMan, GfxPalette32 *palette, GfxTransitions32 *transitions, GfxCursor32 *cursor) : _isHiRes(ConfMan.getBool("enable_high_resolution_graphics")), _palette(palette), - _resMan(resMan), - _screen(screen), + _cursor(cursor), _segMan(segMan), _transitions(transitions), _benchmarkingFinished(false), _throttleFrameOut(true), _throttleState(0), - // TODO: Stop using _gfxScreen - _currentBuffer(screen->getDisplayWidth(), screen->getDisplayHeight(), nullptr), _remapOccurred(false), _frameNowVisible(false), - _screenRect(screen->getDisplayWidth(), screen->getDisplayHeight()), _overdrawThreshold(0), _palMorphIsOn(false) { - _currentBuffer.setPixels(calloc(1, screen->getDisplayWidth() * screen->getDisplayHeight())); - - // QFG4 is the only SCI32 game that doesn't have a high-resolution toggle + // QFG4 is the only SCI32 game that doesn't have a high-resolution version if (g_sci->getGameId() == GID_QFG4) { _isHiRes = false; } + if (g_sci->getGameId() == GID_PHANTASMAGORIA) { + _currentBuffer = Buffer(630, 450, nullptr); + } else if (_isHiRes) { + _currentBuffer = Buffer(640, 480, nullptr); + } else { + _currentBuffer = Buffer(320, 200, nullptr); + } + _currentBuffer.setPixels(calloc(1, _currentBuffer.screenWidth * _currentBuffer.screenHeight)); + _screenRect = Common::Rect(_currentBuffer.screenWidth, _currentBuffer.screenHeight); + initGraphics(_currentBuffer.screenWidth, _currentBuffer.screenHeight, _isHiRes); + switch (g_sci->getGameId()) { case GID_HOYLE5: case GID_GK2: @@ -96,20 +103,6 @@ GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAd // default script width for other games is 320x200 break; } - - // TODO: Nothing in the renderer really uses this. Currently, - // the cursor renderer does, and kLocalToGlobal/kGlobalToLocal - // do, but in the real engine (1) the cursor is handled in - // frameOut, and (2) functions do a very simple lookup of the - // plane and arithmetic with the plane's gameRect. In - // principle, CoordAdjuster could be reused for - // convertGameRectToPlaneRect, but it is not super clear yet - // what the benefit would be to do that. - _coordAdjuster = (GfxCoordAdjuster32 *)coordAdjuster; - - // TODO: Script resolution is hard-coded per game; - // also this must be set or else the engine will crash - _coordAdjuster->setScriptsResolution(_currentBuffer.scriptWidth, _currentBuffer.scriptHeight); } GfxFrameout::~GfxFrameout() { @@ -493,10 +486,12 @@ void GfxFrameout::kernelAddPicAt(const reg_t planeObject, const GuiResourceId pi #pragma mark Rendering void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &eraseRect) { -// TODO: Robot -// if (_robot != nullptr) { -// _robot.doRobot(); -// } + RobotDecoder &robotPlayer = g_sci->_video32->getRobotPlayer(); + const bool robotIsActive = robotPlayer.getStatus() != RobotDecoder::kRobotStatusUninitialized; + + if (robotIsActive) { + robotPlayer.doRobot(); + } // NOTE: The original engine allocated these as static arrays of 100 // pointers to ScreenItemList / RectList @@ -534,10 +529,9 @@ void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &eraseR drawScreenItemList(screenItemLists[i]); } -// TODO: Robot -// if (_robot != nullptr) { -// _robot->frameAlmostVisible(); -// } + if (robotIsActive) { + robotPlayer.frameAlmostVisible(); + } _palette->updateHardware(!shouldShowBits); @@ -547,10 +541,9 @@ void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &eraseR _frameNowVisible = true; -// TODO: Robot -// if (_robot != nullptr) { -// robot->frameNowVisible(); -// } + if (robotIsActive) { + robotPlayer.frameNowVisible(); + } } void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, PlaneShowStyle *showStyle) { @@ -559,7 +552,7 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, PlaneShowStyle *show int16 prevRoom = g_sci->getEngineState()->variables[VAR_GLOBAL][12].toSint16(); - Common::Rect rect(_screen->getDisplayWidth(), _screen->getDisplayHeight()); + Common::Rect rect(_currentBuffer.screenWidth, _currentBuffer.screenHeight); _showList.add(rect); showBits(); @@ -1114,6 +1107,10 @@ void GfxFrameout::mergeToShowList(const Common::Rect &drawRect, RectList &showLi } void GfxFrameout::showBits() { + if (!_showList.size()) { + return; + } + for (RectList::const_iterator rect = _showList.begin(); rect != _showList.end(); ++rect) { Common::Rect rounded(**rect); // NOTE: SCI engine used BR-inclusive rects so used slightly @@ -1121,13 +1118,10 @@ void GfxFrameout::showBits() { // was always even. rounded.left &= ~1; rounded.right = (rounded.right + 1) & ~1; - - // TODO: - // _cursor->GonnaPaint(rounded); + _cursor->gonnaPaint(rounded); } - // TODO: - // _cursor->PaintStarting(); + _cursor->paintStarting(); for (RectList::const_iterator rect = _showList.begin(); rect != _showList.end(); ++rect) { Common::Rect rounded(**rect); @@ -1149,8 +1143,7 @@ void GfxFrameout::showBits() { g_system->copyRectToScreen(sourceBuffer, _currentBuffer.screenWidth, rounded.left, rounded.top, rounded.width(), rounded.height()); } - // TODO: - // _cursor->DonePainting(); + _cursor->donePainting(); _showList.clear(); } @@ -1266,6 +1259,30 @@ void GfxFrameout::showRect(const Common::Rect &rect) { } } +void GfxFrameout::shakeScreen(int16 numShakes, const ShakeDirection direction) { + if (direction & kShakeHorizontal) { + // Used by QFG4 room 750 + warning("TODO: Horizontal shake not implemented"); + return; + } + + while (numShakes--) { + if (direction & kShakeVertical) { + g_system->setShakePos(_isHiRes ? 8 : 4); + } + + g_system->updateScreen(); + g_sci->getEngineState()->wait(3); + + if (direction & kShakeVertical) { + g_system->setShakePos(0); + } + + g_system->updateScreen(); + g_sci->getEngineState()->wait(3); + } +} + #pragma mark - #pragma mark Mouse cursor @@ -1324,7 +1341,7 @@ bool GfxFrameout::isOnMe(const ScreenItem &screenItem, const Plane &plane, const return true; } -void GfxFrameout::kernelSetNowSeen(const reg_t screenItemObject) const { +bool GfxFrameout::kernelSetNowSeen(const reg_t screenItemObject) const { const reg_t planeObject = readSelector(_segMan, screenItemObject, SELECTOR(plane)); Plane *plane = _planes.findByObject(planeObject); @@ -1334,7 +1351,7 @@ void GfxFrameout::kernelSetNowSeen(const reg_t screenItemObject) const { ScreenItem *screenItem = plane->_screenItemList.findByObject(screenItemObject); if (screenItem == nullptr) { - error("kSetNowSeen: Screen item %04x:%04x not found in plane %04x:%04x", PRINT_REG(screenItemObject), PRINT_REG(planeObject)); + return false; } Common::Rect result = screenItem->getNowSeenRect(*plane); @@ -1342,6 +1359,7 @@ void GfxFrameout::kernelSetNowSeen(const reg_t screenItemObject) const { writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsTop), result.top); writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsRight), result.right - 1); writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsBottom), result.bottom - 1); + return true; } void GfxFrameout::remapMarkRedraw() { diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h index 012ecf9e64..e4caffd9e5 100644 --- a/engines/sci/graphics/frameout.h +++ b/engines/sci/graphics/frameout.h @@ -30,8 +30,7 @@ namespace Sci { typedef Common::Array<DrawList> ScreenItemListList; typedef Common::Array<RectList> EraseListList; -class GfxCoordAdjuster32; -class GfxScreen; +class GfxCursor32; class GfxTransitions32; struct PlaneShowStyle; @@ -41,16 +40,16 @@ struct PlaneShowStyle; */ class GfxFrameout { private: - GfxCoordAdjuster32 *_coordAdjuster; + GfxCursor32 *_cursor; GfxPalette32 *_palette; - ResourceManager *_resMan; - GfxScreen *_screen; SegManager *_segMan; public: - GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette32 *palette, GfxTransitions32 *transitions); + GfxFrameout(SegManager *segMan, GfxPalette32 *palette, GfxTransitions32 *transitions, GfxCursor32 *cursor); ~GfxFrameout(); + bool _isHiRes; + void clear(); void syncWithScripts(bool addElements); // this is what Game::restore does, only needed when our ScummVM dialogs are patched in void run(); @@ -111,7 +110,7 @@ public: void kernelAddScreenItem(const reg_t object); void kernelUpdateScreenItem(const reg_t object); void kernelDeleteScreenItem(const reg_t object); - void kernelSetNowSeen(const reg_t screenItemObject) const; + bool kernelSetNowSeen(const reg_t screenItemObject) const; #pragma mark - #pragma mark Planes @@ -196,13 +195,6 @@ private: bool _remapOccurred; /** - * Whether or not the data in the current buffer is what - * is visible to the user. During rendering updates, - * this flag is set to false. - */ - bool _frameNowVisible; - - /** * TODO: Document * TODO: Depending upon if the engine ever modifies this * rect, it may be stupid to store it separately instead @@ -308,7 +300,12 @@ private: } public: - bool _isHiRes; + /** + * Whether or not the data in the current buffer is what + * is visible to the user. During rendering updates, + * this flag is set to false. + */ + bool _frameNowVisible; /** * Whether palMorphFrameOut should be used instead of @@ -366,6 +363,11 @@ public: */ void showRect(const Common::Rect &rect); + /** + * Shakes the screen. + */ + void shakeScreen(const int16 numShakes, const ShakeDirection direction); + #pragma mark - #pragma mark Mouse cursor private: diff --git a/engines/sci/graphics/helpers.h b/engines/sci/graphics/helpers.h index 3fcc83c5e2..1da3749c90 100644 --- a/engines/sci/graphics/helpers.h +++ b/engines/sci/graphics/helpers.h @@ -40,8 +40,10 @@ namespace Sci { #define MAX_CACHED_FONTS 20 #define MAX_CACHED_VIEWS 50 -#define SCI_SHAKE_DIRECTION_VERTICAL 1 -#define SCI_SHAKE_DIRECTION_HORIZONTAL 2 +enum ShakeDirection { + kShakeVertical = 1, + kShakeHorizontal = 2 +}; typedef int GuiResourceId; // is a resource-number and -1 means no parameter given diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp index 6004e9ce7a..91817d4060 100644 --- a/engines/sci/graphics/paint16.cpp +++ b/engines/sci/graphics/paint16.cpp @@ -41,7 +41,7 @@ namespace Sci { -GfxPaint16::GfxPaint16(ResourceManager *resMan, SegManager *segMan, GfxCache *cache, GfxPorts *ports, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette *palette, GfxTransitions *transitions, AudioPlayer *audio) +GfxPaint16::GfxPaint16(ResourceManager *resMan, SegManager *segMan, GfxCache *cache, GfxPorts *ports, GfxCoordAdjuster16 *coordAdjuster, GfxScreen *screen, GfxPalette *palette, GfxTransitions *transitions, AudioPlayer *audio) : _resMan(resMan), _segMan(segMan), _cache(cache), _ports(ports), _coordAdjuster(coordAdjuster), _screen(screen), _palette(palette), _transitions(transitions), _audio(audio), _EGAdrawingVisualize(false) { diff --git a/engines/sci/graphics/paint16.h b/engines/sci/graphics/paint16.h index 317388b2df..6fc9cbbdfc 100644 --- a/engines/sci/graphics/paint16.h +++ b/engines/sci/graphics/paint16.h @@ -36,7 +36,7 @@ class GfxView; */ class GfxPaint16 { public: - GfxPaint16(ResourceManager *resMan, SegManager *segMan, GfxCache *cache, GfxPorts *ports, GfxCoordAdjuster *coordAdjuster, GfxScreen *screen, GfxPalette *palette, GfxTransitions *transitions, AudioPlayer *audio); + GfxPaint16(ResourceManager *resMan, SegManager *segMan, GfxCache *cache, GfxPorts *ports, GfxCoordAdjuster16 *coordAdjuster, GfxScreen *screen, GfxPalette *palette, GfxTransitions *transitions, AudioPlayer *audio); ~GfxPaint16(); void init(GfxAnimate *animate, GfxText16 *text16); @@ -91,7 +91,7 @@ private: GfxAnimate *_animate; GfxCache *_cache; GfxPorts *_ports; - GfxCoordAdjuster *_coordAdjuster; + GfxCoordAdjuster16 *_coordAdjuster; GfxScreen *_screen; GfxPalette *_palette; GfxText16 *_text16; diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp index 2eab391afd..0025b24476 100644 --- a/engines/sci/graphics/picture.cpp +++ b/engines/sci/graphics/picture.cpp @@ -35,7 +35,7 @@ namespace Sci { //#define DEBUG_PICTURE_DRAW -GfxPicture::GfxPicture(ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxPorts *ports, GfxScreen *screen, GfxPalette *palette, GuiResourceId resourceId, bool EGAdrawingVisualize) +GfxPicture::GfxPicture(ResourceManager *resMan, GfxCoordAdjuster16 *coordAdjuster, GfxPorts *ports, GfxScreen *screen, GfxPalette *palette, GuiResourceId resourceId, bool EGAdrawingVisualize) : _resMan(resMan), _coordAdjuster(coordAdjuster), _ports(ports), _screen(screen), _palette(palette), _resourceId(resourceId), _EGAdrawingVisualize(EGAdrawingVisualize) { assert(resourceId != -1); initData(resourceId); diff --git a/engines/sci/graphics/picture.h b/engines/sci/graphics/picture.h index 942fa0f107..1be1ae3004 100644 --- a/engines/sci/graphics/picture.h +++ b/engines/sci/graphics/picture.h @@ -38,7 +38,7 @@ enum { class GfxPorts; class GfxScreen; class GfxPalette; -class GfxCoordAdjuster; +class GfxCoordAdjuster16; class ResourceManager; class Resource; @@ -48,7 +48,7 @@ class Resource; */ class GfxPicture { public: - GfxPicture(ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxPorts *ports, GfxScreen *screen, GfxPalette *palette, GuiResourceId resourceId, bool EGAdrawingVisualize = false); + GfxPicture(ResourceManager *resMan, GfxCoordAdjuster16 *coordAdjuster, GfxPorts *ports, GfxScreen *screen, GfxPalette *palette, GuiResourceId resourceId, bool EGAdrawingVisualize = false); ~GfxPicture(); GuiResourceId getResourceId(); @@ -84,7 +84,7 @@ private: void vectorPatternTexturedCircle(Common::Rect box, byte size, byte color, byte prio, byte control, byte texture); ResourceManager *_resMan; - GfxCoordAdjuster *_coordAdjuster; + GfxCoordAdjuster16 *_coordAdjuster; GfxPorts *_ports; GfxScreen *_screen; GfxPalette *_palette; diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp index c977a93817..601ab9f09f 100644 --- a/engines/sci/graphics/screen.cpp +++ b/engines/sci/graphics/screen.cpp @@ -53,12 +53,6 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { if ((g_sci->getPlatform() == Common::kPlatformWindows) || (g_sci->forceHiresGraphics())) { if (g_sci->getGameId() == GID_KQ6) _upscaledHires = GFX_SCREEN_UPSCALED_640x440; -#ifdef ENABLE_SCI32 - if (g_sci->getGameId() == GID_GK1) - _upscaledHires = GFX_SCREEN_UPSCALED_640x480; - if (g_sci->getGameId() == GID_PQ4) - _upscaledHires = GFX_SCREEN_UPSCALED_640x480; -#endif } // Japanese versions of games use hi-res font on upscaled version of the game. @@ -90,28 +84,11 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { } } -#ifdef ENABLE_SCI32 - // GK1 Mac uses a 640x480 resolution too - if (g_sci->getPlatform() == Common::kPlatformMacintosh) { - if (g_sci->getGameId() == GID_GK1) - _upscaledHires = GFX_SCREEN_UPSCALED_640x480; - } -#endif - if (_resMan->detectHires()) { _scriptWidth = 640; _scriptHeight = 480; } -#ifdef ENABLE_SCI32 - // Phantasmagoria 1 effectively outputs 630x450 - // Coordinate translation has to use this resolution as well - if (g_sci->getGameId() == GID_PHANTASMAGORIA) { - _width = 630; - _height = 450; - } -#endif - // if not yet set, set those to script-width/height if (!_width) _width = _scriptWidth; @@ -632,13 +609,13 @@ void GfxScreen::setVerticalShakePos(uint16 shakePos) { void GfxScreen::kernelShakeScreen(uint16 shakeCount, uint16 directions) { while (shakeCount--) { - if (directions & SCI_SHAKE_DIRECTION_VERTICAL) + if (directions & kShakeVertical) setVerticalShakePos(10); // TODO: horizontal shakes g_system->updateScreen(); g_sci->getEngineState()->wait(3); - if (directions & SCI_SHAKE_DIRECTION_VERTICAL) + if (directions & kShakeVertical) setVerticalShakePos(0); g_system->updateScreen(); diff --git a/engines/sci/graphics/screen_item32.h b/engines/sci/graphics/screen_item32.h index 3d9d5ef3d7..4221c0ea52 100644 --- a/engines/sci/graphics/screen_item32.h +++ b/engines/sci/graphics/screen_item32.h @@ -31,6 +31,7 @@ namespace Sci { enum ScaleSignals32 { kScaleSignalNone = 0, + // TODO: rename to 'manual' kScaleSignalDoScaling32 = 1, // enables scaling when drawing that cel (involves scaleX and scaleY) kScaleSignalUseVanishingPoint = 2, // TODO: Is this actually a thing? I have not seen it and diff --git a/engines/sci/graphics/text16.cpp b/engines/sci/graphics/text16.cpp index b0f2c52791..cb6e614657 100644 --- a/engines/sci/graphics/text16.cpp +++ b/engines/sci/graphics/text16.cpp @@ -633,7 +633,7 @@ reg_t GfxText16::allocAndFillReferenceRectArray() { if (rectCount) { reg_t rectArray; byte *rectArrayPtr = g_sci->getEngineState()->_segMan->allocDynmem(4 * 2 * (rectCount + 1), "text code reference rects", &rectArray); - GfxCoordAdjuster *coordAdjuster = g_sci->_gfxCoordAdjuster; + GfxCoordAdjuster16 *coordAdjuster = g_sci->_gfxCoordAdjuster; for (uint curRect = 0; curRect < rectCount; curRect++) { coordAdjuster->kernelLocalToGlobal(_codeRefRects[curRect].left, _codeRefRects[curRect].top); coordAdjuster->kernelLocalToGlobal(_codeRefRects[curRect].right, _codeRefRects[curRect].bottom); diff --git a/engines/sci/graphics/transitions32.cpp b/engines/sci/graphics/transitions32.cpp index bceb0fa84d..37f608da85 100644 --- a/engines/sci/graphics/transitions32.cpp +++ b/engines/sci/graphics/transitions32.cpp @@ -203,10 +203,6 @@ void GfxTransitions32::kernelSetShowStyle(const uint16 argc, const reg_t planeOb color = 0; } - if ((getSciVersion() < SCI_VERSION_2_1_MIDDLE && g_sci->getGameId() != GID_KQ7 && type == 15) || type > 15) { - error("Illegal show style %d for plane %04x:%04x", type, PRINT_REG(planeObj)); - } - Plane *plane = g_sci->_gfxFrameout->getPlanes().findByObject(planeObj); if (plane == nullptr) { error("Plane %04x:%04x is not present in active planes list", PRINT_REG(planeObj)); diff --git a/engines/sci/graphics/video32.cpp b/engines/sci/graphics/video32.cpp index 51be08dac9..8b1d4ef32b 100644 --- a/engines/sci/graphics/video32.cpp +++ b/engines/sci/graphics/video32.cpp @@ -20,6 +20,7 @@ * */ +#include "audio/mixer.h" // for Audio::Mixer::kSFXSoundType #include "common/config-manager.h" // for ConfMan #include "common/textconsole.h" // for warning, error #include "common/util.h" // for ARRAYSIZE @@ -31,7 +32,7 @@ #include "sci/engine/vm_types.h" // for reg_t #include "sci/event.h" // for SciEvent, EventManager, SCI_... #include "sci/graphics/celobj32.h" // for CelInfo32, ::kLowResX, ::kLo... -#include "sci/graphics/cursor.h" // for GfxCursor +#include "sci/graphics/cursor32.h" // for GfxCursor32 #include "sci/graphics/frameout.h" // for GfxFrameout #include "sci/graphics/helpers.h" // for Color, Palette #include "sci/graphics/palette32.h" // for GfxPalette32 @@ -269,6 +270,11 @@ void AVIPlayer::init() { g_sci->_gfxFrameout->addScreenItem(*_screenItem); g_sci->_gfxFrameout->frameOut(true); } else { + // Attempting to draw a palettized cursor into a 24bpp surface will + // cause memory corruption, so hide the cursor in this mode (SCI did not + // have a 24bpp mode but just directed VFW to display videos instead) + g_sci->_gfxCursor32->hide(); + const Buffer ¤tBuffer = g_sci->_gfxFrameout->getCurrentBuffer(); const Graphics::PixelFormat format = _decoder->getPixelFormat(); initGraphics(currentBuffer.screenWidth, currentBuffer.screenHeight, g_sci->_gfxFrameout->_isHiRes, &format); @@ -326,6 +332,7 @@ AVIPlayer::IOStatus AVIPlayer::close() { const Buffer ¤tBuffer = g_sci->_gfxFrameout->getCurrentBuffer(); const Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8(); initGraphics(currentBuffer.screenWidth, currentBuffer.screenHeight, isHiRes, &format); + g_sci->_gfxCursor32->unhide(); } _decoder->close(); @@ -597,7 +604,7 @@ VMDPlayer::IOStatus VMDPlayer::close() { } if (!_showCursor) { - g_sci->_gfxCursor->kernelShow(); + g_sci->_gfxCursor32->unhide(); } _lastYieldedFrameNo = 0; @@ -606,6 +613,22 @@ VMDPlayer::IOStatus VMDPlayer::close() { return kIOSuccess; } +VMDPlayer::VMDStatus VMDPlayer::getStatus() const { + if (!_isOpen) { + return kVMDNotOpen; + } + if (_decoder->isPaused()) { + return kVMDPaused; + } + if (_decoder->isPlaying()) { + return kVMDPlaying; + } + if (_decoder->endOfVideo()) { + return kVMDFinished; + } + return kVMDOpen; +} + VMDPlayer::EventFlags VMDPlayer::kernelPlayUntilEvent(const EventFlags flags, const int16 lastFrameNo, const int16 yieldInterval) { assert(lastFrameNo >= -1); @@ -658,7 +681,7 @@ VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags) { _isInitialized = true; if (!_showCursor) { - g_sci->_gfxCursor->kernelHide(); + g_sci->_gfxCursor32->hide(); } Common::Rect vmdRect(_x, diff --git a/engines/sci/graphics/video32.h b/engines/sci/graphics/video32.h index 0496f61d5d..75b8fb2d21 100644 --- a/engines/sci/graphics/video32.h +++ b/engines/sci/graphics/video32.h @@ -27,6 +27,7 @@ #include "common/scummsys.h" // for int16, uint8, uint16, int32 #include "common/str.h" // for String #include "sci/engine/vm_types.h" // for reg_t +#include "sci/video/robot_decoder.h" // for RobotDecoder namespace Video { class AdvancedVMDDecoder; @@ -267,6 +268,15 @@ public: kEventFlagReverse = 0x80 }; + enum VMDStatus { + kVMDNotOpen = 0, + kVMDOpen = 1, + kVMDPlaying = 2, + kVMDPaused = 3, + kVMDStopped = 4, + kVMDFinished = 5 + }; + VMDPlayer(SegManager *segMan, EventManager *eventMan); ~VMDPlayer(); @@ -294,6 +304,11 @@ public: */ IOStatus close(); + /** + * Gets the playback status of the VMD player. + */ + VMDStatus getStatus() const; + // NOTE: Was WaitForEvent in SSCI EventFlags kernelPlayUntilEvent(const EventFlags flags, const int16 lastFrameNo, const int16 yieldInterval); @@ -505,16 +520,19 @@ public: Video32(SegManager *segMan, EventManager *eventMan) : _SEQPlayer(segMan), _AVIPlayer(segMan, eventMan), - _VMDPlayer(segMan, eventMan) {} + _VMDPlayer(segMan, eventMan), + _robotPlayer(segMan) {} SEQPlayer &getSEQPlayer() { return _SEQPlayer; } AVIPlayer &getAVIPlayer() { return _AVIPlayer; } VMDPlayer &getVMDPlayer() { return _VMDPlayer; } + RobotDecoder &getRobotPlayer() { return _robotPlayer; } private: SEQPlayer _SEQPlayer; AVIPlayer _AVIPlayer; VMDPlayer _VMDPlayer; + RobotDecoder _robotPlayer; }; } // End of namespace Sci diff --git a/engines/sci/graphics/view.cpp b/engines/sci/graphics/view.cpp index 1939e66179..0c09fcbb30 100644 --- a/engines/sci/graphics/view.cpp +++ b/engines/sci/graphics/view.cpp @@ -351,18 +351,6 @@ void GfxView::initData(GuiResourceId resourceId) { celData += celSize; } } -#ifdef ENABLE_SCI32 - // adjust width/height returned to scripts - if (_sci2ScaleRes != SCI_VIEW_NATIVERES_NONE) { - for (loopNo = 0; loopNo < _loopCount; loopNo++) - for (celNo = 0; celNo < _loop[loopNo].celCount; celNo++) - _screen->adjustBackUpscaledCoordinates(_loop[loopNo].cel[celNo].scriptWidth, _loop[loopNo].cel[celNo].scriptHeight, _sci2ScaleRes); - } else if ((getSciVersion() >= SCI_VERSION_2_1_EARLY) && (getSciVersion() <= SCI_VERSION_2_1_LATE)) { - for (loopNo = 0; loopNo < _loopCount; loopNo++) - for (celNo = 0; celNo < _loop[loopNo].celCount; celNo++) - _coordAdjuster->fromDisplayToScript(_loop[loopNo].cel[celNo].scriptHeight, _loop[loopNo].cel[celNo].scriptWidth); - } -#endif break; default: diff --git a/engines/sci/graphics/view.h b/engines/sci/graphics/view.h index 96b48c0477..5e422468b5 100644 --- a/engines/sci/graphics/view.h +++ b/engines/sci/graphics/view.h @@ -92,7 +92,7 @@ private: void unditherBitmap(byte *bitmap, int16 width, int16 height, byte clearKey); ResourceManager *_resMan; - GfxCoordAdjuster *_coordAdjuster; + GfxCoordAdjuster16 *_coordAdjuster; GfxScreen *_screen; GfxPalette *_palette; diff --git a/engines/sci/module.mk b/engines/sci/module.mk index 18d97ea57e..eb2c6a148b 100644 --- a/engines/sci/module.mk +++ b/engines/sci/module.mk @@ -93,6 +93,7 @@ MODULE_OBJS += \ graphics/text32.o \ graphics/transitions32.o \ graphics/video32.o \ + graphics/cursor32.o \ sound/audio32.o \ sound/decoders/sol.o \ video/robot_decoder.o diff --git a/engines/sci/resource.cpp b/engines/sci/resource.cpp index 48278e35a7..2bd941b11a 100644 --- a/engines/sci/resource.cpp +++ b/engines/sci/resource.cpp @@ -573,6 +573,9 @@ Resource *ResourceManager::testResource(ResourceId id) { } int ResourceManager::addAppropriateSources() { +#ifdef ENABLE_SCI32 + _multiDiscAudio = false; +#endif if (Common::File::exists("resource.map")) { // SCI0-SCI2 file naming scheme ResourceSource *map = addExternalMap("resource.map"); @@ -615,6 +618,10 @@ int ResourceManager::addAppropriateSources() { if (mapFiles.empty() || files.empty() || mapFiles.size() != files.size()) return 0; + if (Common::File::exists("resaud.001")) { + _multiDiscAudio = true; + } + for (Common::ArchiveMemberList::const_iterator mapIterator = mapFiles.begin(); mapIterator != mapFiles.end(); ++mapIterator) { Common::String mapName = (*mapIterator)->getName(); int mapNumber = atoi(strrchr(mapName.c_str(), '.') + 1); @@ -859,6 +866,13 @@ void ResourceManager::addResourcesFromChunk(uint16 id) { scanNewSources(); } +void ResourceManager::findDisc(const int16 discNo) { + // Since all resources are expected to be copied from the original discs + // into a single game directory, this call just records the number of the CD + // that the game has requested + _currentDiscNo = discNo; +} + #endif void ResourceManager::freeResourceSources() { @@ -878,7 +892,9 @@ void ResourceManager::init() { _LRU.clear(); _resMap.clear(); _audioMapSCI1 = NULL; - +#ifdef ENABLE_SCI32 + _currentDiscNo = 1; +#endif // FIXME: put this in an Init() function, so that we can error out if detection fails completely _mapVersion = detectMapVersion(); @@ -1477,6 +1493,12 @@ void ResourceManager::readResourcePatchesBase36() { for (Common::ArchiveMemberList::const_iterator x = files.begin(); x != files.end(); ++x) { name = (*x)->getName(); + // The S/T prefixes often conflict with non-patch files and generate + // spurious warnings about invalid patches + if (name.hasSuffix(".DLL") || name.hasSuffix(".EXE") || name.hasSuffix(".TXT")) { + continue; + } + ResourceId resource36 = convertPatchNameBase36((ResourceType)i, name); /* @@ -1738,11 +1760,42 @@ int ResourceManager::readResourceMapSCI1(ResourceSource *map) { // if we use the first entries in the resource file, half of the // game will be English and umlauts will also be missing :P if (resource->_source->getSourceType() == kSourceVolume) { + // Maps are read during the scanning process (below), so + // need to be treated as unallocated in order for the new + // data from this volume to be picked up and used + if (resId.getType() == kResourceTypeMap) { + resource->_status = kResStatusNoMalloc; + } resource->_source = source; resource->_fileOffset = fileOffset; resource->size = 0; } } + +#ifdef ENABLE_SCI32 + // Different CDs may have different audio maps on each disc. The + // ResourceManager does not know how to deal with this; it expects + // each resource ID to be unique across an entire game. To work + // around this problem, all audio maps from this disc must be + // processed immediately, since they will be replaced by the audio + // map from the next disc on the next call to readResourceMapSCI1 + if (_multiDiscAudio && resId.getType() == kResourceTypeMap) { + IntMapResourceSource *audioMap = static_cast<IntMapResourceSource *>(addSource(new IntMapResourceSource("MAP", mapVolumeNr, resId.getNumber()))); + Common::String volumeName; + if (resId.getNumber() == 65535) { + volumeName = Common::String::format("RESSFX.%03d", mapVolumeNr); + } else { + volumeName = Common::String::format("RESAUD.%03d", mapVolumeNr); + } + + ResourceSource *audioVolume = addSource(new AudioVolumeResourceSource(this, volumeName, audioMap, mapVolumeNr)); + if (!audioMap->_scanned) { + audioVolume->_scanned = true; + audioMap->_scanned = true; + audioMap->scanSource(this); + } + } +#endif } } diff --git a/engines/sci/resource.h b/engines/sci/resource.h index f70bf48bd4..70db5909b7 100644 --- a/engines/sci/resource.h +++ b/engines/sci/resource.h @@ -296,6 +296,7 @@ protected: typedef Common::HashMap<ResourceId, Resource *, ResourceIdHash> ResourceMap; +class IntMapResourceSource; class ResourceManager { // FIXME: These 'friend' declarations are meant to be a temporary hack to // ease transition to the ResourceSource class system. @@ -397,6 +398,30 @@ public: * resource manager. */ void addResourcesFromChunk(uint16 id); + + /** + * Updates the currently active disc number. + */ + void findDisc(const int16 discNo); + + /** + * Gets the currently active disc number. + */ + int16 getCurrentDiscNo() const { return _currentDiscNo; } + +private: + /** + * The currently active disc number. + */ + int16 _currentDiscNo; + + /** + * If true, the game has multiple audio volumes that contain different + * audio files for each disc. + */ + bool _multiDiscAudio; + +public: #endif bool detectHires(); @@ -520,7 +545,7 @@ protected: * @param map The map * @return 0 on success, an SCI_ERROR_* code otherwise */ - int readAudioMapSCI11(ResourceSource *map); + int readAudioMapSCI11(IntMapResourceSource *map); /** * Reads SCI1 audio map files. diff --git a/engines/sci/resource_audio.cpp b/engines/sci/resource_audio.cpp index 5ab443a16d..cbc4a02739 100644 --- a/engines/sci/resource_audio.cpp +++ b/engines/sci/resource_audio.cpp @@ -277,7 +277,7 @@ void ResourceManager::removeAudioResource(ResourceId resId) { // w syncSize (iff seq has bit 7 set) // w syncAscSize (iff seq has bit 6 set) -int ResourceManager::readAudioMapSCI11(ResourceSource *map) { +int ResourceManager::readAudioMapSCI11(IntMapResourceSource *map) { #ifndef ENABLE_SCI32 // SCI32 support is not built in. Check if this is a SCI32 game // and if it is abort here. @@ -286,17 +286,19 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) { #endif uint32 offset = 0; - Resource *mapRes = findResource(ResourceId(kResourceTypeMap, map->_volumeNumber), false); + Resource *mapRes = findResource(ResourceId(kResourceTypeMap, map->_mapNumber), false); if (!mapRes) { - warning("Failed to open %i.MAP", map->_volumeNumber); + warning("Failed to open %i.MAP", map->_mapNumber); return SCI_ERROR_RESMAP_NOT_FOUND; } - ResourceSource *src = findVolume(map, 0); + ResourceSource *src = findVolume(map, map->_volumeNumber); - if (!src) + if (!src) { + warning("Failed to find volume for %i.MAP", map->_mapNumber); return SCI_ERROR_NO_RESOURCE_FILES_FOUND; + } byte *ptr = mapRes->data; @@ -309,7 +311,7 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) { break; } - if (map->_volumeNumber == 65535) { + if (map->_mapNumber == 65535) { while (ptr < mapRes->data + mapRes->size) { uint16 n = READ_LE_UINT16(ptr); ptr += 2; @@ -327,7 +329,7 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) { addResource(ResourceId(kResourceTypeAudio, n), src, offset); } - } else if (map->_volumeNumber == 0 && entrySize == 10 && ptr[3] == 0) { + } else if (map->_mapNumber == 0 && entrySize == 10 && ptr[3] == 0) { // QFG3 demo format // ptr[3] would be 'seq' in the normal format and cannot possibly be 0 while (ptr < mapRes->data + mapRes->size) { @@ -344,7 +346,7 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) { addResource(ResourceId(kResourceTypeAudio, n), src, offset, size); } - } else if (map->_volumeNumber == 0 && entrySize == 8 && READ_LE_UINT16(ptr + 2) == 0xffff) { + } else if (map->_mapNumber == 0 && entrySize == 8 && READ_LE_UINT16(ptr + 2) == 0xffff) { // LB2 Floppy/Mother Goose SCI1.1 format Common::SeekableReadStream *stream = getVolumeFile(src); @@ -400,7 +402,7 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) { // FIXME: The sync36 resource seems to be two bytes too big in KQ6CD // (bytes taken from the RAVE resource right after it) if (syncSize > 0) - addResource(ResourceId(kResourceTypeSync36, map->_volumeNumber, n & 0xffffff3f), src, offset, syncSize); + addResource(ResourceId(kResourceTypeSync36, map->_mapNumber, n & 0xffffff3f), src, offset, syncSize); } if (n & 0x40) { @@ -410,12 +412,12 @@ int ResourceManager::readAudioMapSCI11(ResourceSource *map) { ptr += 2; if (kq6HiresSyncSize > 0) { - addResource(ResourceId(kResourceTypeRave, map->_volumeNumber, n & 0xffffff3f), src, offset + syncSize, kq6HiresSyncSize); + addResource(ResourceId(kResourceTypeRave, map->_mapNumber, n & 0xffffff3f), src, offset + syncSize, kq6HiresSyncSize); syncSize += kq6HiresSyncSize; } } - addResource(ResourceId(kResourceTypeAudio36, map->_volumeNumber, n & 0xffffff3f), src, offset + syncSize); + addResource(ResourceId(kResourceTypeAudio36, map->_mapNumber, n & 0xffffff3f), src, offset + syncSize); } } @@ -937,13 +939,21 @@ void AudioVolumeResourceSource::loadResource(ResourceManager *resMan, Resource * } bool ResourceManager::addAudioSources() { +#ifdef ENABLE_SCI32 + // Multi-disc audio is added during addAppropriateSources for those titles + // that require it + if (_multiDiscAudio) { + return true; + } +#endif + Common::List<ResourceId> resources = listResources(kResourceTypeMap); Common::List<ResourceId>::iterator itr; for (itr = resources.begin(); itr != resources.end(); ++itr) { - ResourceSource *src = addSource(new IntMapResourceSource("MAP", itr->getNumber())); + ResourceSource *src = addSource(new IntMapResourceSource("MAP", 0, itr->getNumber())); - if ((itr->getNumber() == 65535) && Common::File::exists("RESOURCE.SFX")) + if (itr->getNumber() == 65535 && Common::File::exists("RESOURCE.SFX")) addSource(new AudioVolumeResourceSource(this, "RESOURCE.SFX", src, 0)); else if (Common::File::exists("RESOURCE.AUD")) addSource(new AudioVolumeResourceSource(this, "RESOURCE.AUD", src, 0)); @@ -991,7 +1001,7 @@ void ResourceManager::changeAudioDirectory(Common::String path) { if ((it->getNumber() == 65535)) continue; - ResourceSource *src = addSource(new IntMapResourceSource(mapName, it->getNumber())); + ResourceSource *src = addSource(new IntMapResourceSource(mapName, 0, it->getNumber())); addSource(new AudioVolumeResourceSource(this, audioResourceName, src, 0)); } diff --git a/engines/sci/resource_intern.h b/engines/sci/resource_intern.h index 461d684005..fe4b0a97f4 100644 --- a/engines/sci/resource_intern.h +++ b/engines/sci/resource_intern.h @@ -134,8 +134,9 @@ public: class IntMapResourceSource : public ResourceSource { public: - IntMapResourceSource(const Common::String &name, int volNum) - : ResourceSource(kSourceIntMap, name, volNum) { + uint16 _mapNumber; + IntMapResourceSource(const Common::String &name, int volNum, int mapNum) + : ResourceSource(kSourceIntMap, name, volNum), _mapNumber(mapNum) { } virtual void scanSource(ResourceManager *resMan); diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp index 4c178b6ed7..6c51060296 100644 --- a/engines/sci/sci.cpp +++ b/engines/sci/sci.cpp @@ -65,6 +65,7 @@ #ifdef ENABLE_SCI32 #include "sci/graphics/controls32.h" +#include "sci/graphics/cursor32.h" #include "sci/graphics/frameout.h" #include "sci/graphics/palette32.h" #include "sci/graphics/remap32.h" @@ -72,17 +73,12 @@ #include "sci/graphics/transitions32.h" #include "sci/graphics/video32.h" #include "sci/sound/audio32.h" -// TODO: Move this to video32 -#include "sci/video/robot_decoder.h" #endif namespace Sci { SciEngine *g_sci = 0; - -class GfxDriver; - SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc, SciGameId gameId) : Engine(syst), _gameDescription(desc), _gameId(gameId), _rng("sci") { @@ -96,6 +92,7 @@ SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc, SciGameId gam #ifdef ENABLE_SCI32 _audio32 = nullptr; _video32 = nullptr; + _gfxCursor32 = nullptr; #endif _features = 0; _resMan = 0; @@ -130,6 +127,7 @@ SciEngine::SciEngine(OSystem *syst, const ADGameDescription *desc, SciGameId gam DebugMan.addDebugChannel(kDebugLevelScripts, "Scripts", "Notifies when scripts are unloaded"); DebugMan.addDebugChannel(kDebugLevelScriptPatcher, "ScriptPatcher", "Notifies when scripts are patched"); DebugMan.addDebugChannel(kDebugLevelWorkarounds, "Workarounds", "Notifies when workarounds are triggered"); + DebugMan.addDebugChannel(kDebugLevelVideo, "Video", "Video (SEQ, VMD, RBT) debugging"); DebugMan.addDebugChannel(kDebugLevelGC, "GC", "Garbage Collector debugging"); DebugMan.addDebugChannel(kDebugLevelResMan, "ResMan", "Resource manager debugging"); DebugMan.addDebugChannel(kDebugLevelOnStartup, "OnStartup", "Enter debugger at start of game"); @@ -171,11 +169,11 @@ SciEngine::~SciEngine() { delete _gfxControls32; delete _gfxPaint32; delete _gfxText32; - delete _robotDecoder; // GfxFrameout and GfxPalette32 must be deleted after Video32 since // destruction of screen items in the Video32 destructor relies on these // components delete _video32; + delete _gfxCursor32; delete _gfxPalette32; delete _gfxTransitions32; delete _gfxFrameout; @@ -244,35 +242,31 @@ Common::Error SciEngine::run() { _scriptPatcher = new ScriptPatcher(); SegManager *segMan = new SegManager(_resMan, _scriptPatcher); - // Read user option for hires graphics + // Read user option for forcing hires graphics // Only show/selectable for: // - King's Quest 6 CD // - King's Quest 6 CD demo // - Gabriel Knight 1 CD // - Police Quest 4 CD // TODO: Check, if Gabriel Knight 1 floppy supports high resolution - // TODO: Check, if Gabriel Knight 1 on Mac supports high resolution - switch (getPlatform()) { - case Common::kPlatformDOS: - case Common::kPlatformWindows: - // Only DOS+Windows - switch (_gameId) { - case GID_KQ6: - case GID_GK1: - case GID_PQ4: - if (isCD()) - _forceHiresGraphics = ConfMan.getBool("enable_high_resolution_graphics"); - break; - default: - break; - } - default: - break; - }; + // + // Gabriel Knight 1 on Mac is hi-res only, so it should NOT get this option. + // Confirmed by [md5] and originally by clone2727. + if (Common::checkGameGUIOption(GAMEOPTION_HIGH_RESOLUTION_GRAPHICS, ConfMan.get("guioptions"))) { + // GAMEOPTION_HIGH_RESOLUTION_GRAPHICS is available for the currently detected game, + // so read the user option now. + // We need to do this, because the option's default is "true", but we don't want "true" + // for any game that does not have this option. + _forceHiresGraphics = ConfMan.getBool("enable_high_resolution_graphics"); + } - // Initialize the game screen - _gfxScreen = new GfxScreen(_resMan); - _gfxScreen->enableUndithering(ConfMan.getBool("disable_dithering")); + if (getSciVersion() < SCI_VERSION_2) { + // Initialize the game screen + _gfxScreen = new GfxScreen(_resMan); + _gfxScreen->enableUndithering(ConfMan.getBool("disable_dithering")); + } else { + _gfxScreen = nullptr; + } _kernel = new Kernel(_resMan, segMan); _kernel->init(); @@ -709,12 +703,12 @@ void SciEngine::initGraphics() { #ifdef ENABLE_SCI32 _gfxControls32 = 0; _gfxText32 = 0; - _robotDecoder = 0; _gfxFrameout = 0; _gfxPaint32 = 0; _gfxPalette32 = 0; _gfxRemap32 = 0; _gfxTransitions32 = 0; + _gfxCursor32 = 0; #endif if (hasMacIconBar()) @@ -734,24 +728,23 @@ void SciEngine::initGraphics() { #endif _gfxCache = new GfxCache(_resMan, _gfxScreen, _gfxPalette16); - _gfxCursor = new GfxCursor(_resMan, _gfxPalette16, _gfxScreen); #ifdef ENABLE_SCI32 if (getSciVersion() >= SCI_VERSION_2) { // SCI32 graphic objects creation - _gfxCoordAdjuster = new GfxCoordAdjuster32(_gamestate->_segMan); - _gfxCursor->init(_gfxCoordAdjuster, _eventMan); - _gfxCompare = new GfxCompare(_gamestate->_segMan, _gfxCache, _gfxScreen, _gfxCoordAdjuster); + _gfxCursor32 = new GfxCursor32(); + _gfxCompare = new GfxCompare(_gamestate->_segMan, _gfxCache, nullptr, _gfxCoordAdjuster); _gfxPaint32 = new GfxPaint32(_gamestate->_segMan); - _robotDecoder = new RobotDecoder(getPlatform() == Common::kPlatformMacintosh); _gfxTransitions32 = new GfxTransitions32(_gamestate->_segMan); - _gfxFrameout = new GfxFrameout(_gamestate->_segMan, _resMan, _gfxCoordAdjuster, _gfxScreen, _gfxPalette32, _gfxTransitions32); + _gfxFrameout = new GfxFrameout(_gamestate->_segMan, _gfxPalette32, _gfxTransitions32, _gfxCursor32); + _gfxCursor32->init(_gfxFrameout->getCurrentBuffer()); _gfxText32 = new GfxText32(_gamestate->_segMan, _gfxCache); _gfxControls32 = new GfxControls32(_gamestate->_segMan, _gfxCache, _gfxText32); _gfxFrameout->run(); } else { #endif // SCI0-SCI1.1 graphic objects creation + _gfxCursor = new GfxCursor(_resMan, _gfxPalette16, _gfxScreen); _gfxPorts = new GfxPorts(_gamestate->_segMan, _gfxScreen); _gfxCoordAdjuster = new GfxCoordAdjuster16(_gfxPorts); _gfxCursor->init(_gfxCoordAdjuster, _eventMan); diff --git a/engines/sci/sci.h b/engines/sci/sci.h index a42095259b..b336eb8cce 100644 --- a/engines/sci/sci.h +++ b/engines/sci/sci.h @@ -45,6 +45,18 @@ struct ADGameDescription; */ namespace Sci { +// GUI-options, primarily used by detection_tables.h +#define GAMEOPTION_PREFER_DIGITAL_SFX GUIO_GAMEOPTIONS1 +#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS2 +#define GAMEOPTION_FB01_MIDI GUIO_GAMEOPTIONS3 +#define GAMEOPTION_JONES_CDAUDIO GUIO_GAMEOPTIONS4 +#define GAMEOPTION_KQ6_WINDOWS_CURSORS GUIO_GAMEOPTIONS5 +#define GAMEOPTION_SQ4_SILVER_CURSORS GUIO_GAMEOPTIONS6 +#define GAMEOPTION_EGA_UNDITHER GUIO_GAMEOPTIONS7 +// HIGH_RESOLUTION_GRAPHICS availability is checked for in SciEngine::run() +#define GAMEOPTION_HIGH_RESOLUTION_GRAPHICS GUIO_GAMEOPTIONS8 +#define GAMEOPTION_ENABLE_BLACK_LINED_VIDEO GUIO_GAMEOPTIONS9 + struct EngineState; class Vocabulary; class ResourceManager; @@ -63,7 +75,7 @@ class GfxCache; class GfxCompare; class GfxControls16; class GfxControls32; -class GfxCoordAdjuster; +class GfxCoordAdjuster16; class GfxCursor; class GfxMacIconBar; class GfxMenu; @@ -80,12 +92,11 @@ class GfxText32; class GfxTransitions; #ifdef ENABLE_SCI32 -// TODO: Move RobotDecoder to Video32 -class RobotDecoder; class GfxFrameout; class Audio32; class Video32; class GfxTransitions32; +class GfxCursor32; #endif // our engine debug levels @@ -113,7 +124,8 @@ enum kDebugLevels { kDebugLevelOnStartup = 1 << 20, kDebugLevelDebugMode = 1 << 21, kDebugLevelScriptPatcher = 1 << 22, - kDebugLevelWorkarounds = 1 << 23 + kDebugLevelWorkarounds = 1 << 23, + kDebugLevelVideo = 1 << 24 }; enum SciGameId { @@ -357,7 +369,7 @@ public: GfxCompare *_gfxCompare; GfxControls16 *_gfxControls16; // Controls for 16-bit gfx GfxControls32 *_gfxControls32; // Controls for 32-bit gfx - GfxCoordAdjuster *_gfxCoordAdjuster; + GfxCoordAdjuster16 *_gfxCoordAdjuster; GfxCursor *_gfxCursor; GfxMenu *_gfxMenu; // Menu for 16-bit gfx GfxPalette *_gfxPalette16; @@ -376,9 +388,9 @@ public: #ifdef ENABLE_SCI32 Audio32 *_audio32; Video32 *_video32; - RobotDecoder *_robotDecoder; GfxFrameout *_gfxFrameout; // kFrameout and the like for 32-bit gfx GfxTransitions32 *_gfxTransitions32; + GfxCursor32 *_gfxCursor32; #endif AudioPlayer *_audio; diff --git a/engines/sci/sound/audio32.cpp b/engines/sci/sound/audio32.cpp index 288b7c00f5..4af474b918 100644 --- a/engines/sci/sound/audio32.cpp +++ b/engines/sci/sound/audio32.cpp @@ -164,7 +164,7 @@ Audio32::~Audio32() { #pragma mark - #pragma mark AudioStream implementation -int Audio32::writeAudioInternal(Audio::RewindableAudioStream *const sourceStream, Audio::RateConverter *const converter, Audio::st_sample_t *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume, const bool loop) { +int Audio32::writeAudioInternal(Audio::AudioStream *const sourceStream, Audio::RateConverter *const converter, Audio::st_sample_t *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume, const bool loop) { int samplesToRead = numSamples; // The parent rate converter will request N * 2 @@ -182,7 +182,8 @@ int Audio32::writeAudioInternal(Audio::RewindableAudioStream *const sourceStream do { if (loop && sourceStream->endOfStream()) { - sourceStream->rewind(); + Audio::RewindableAudioStream *rewindableStream = dynamic_cast<Audio::RewindableAudioStream *>(sourceStream); + rewindableStream->rewind(); } const int loopSamplesWritten = converter->flow(*sourceStream, targetBuffer, samplesToRead, leftVolume, rightVolume); @@ -305,7 +306,14 @@ int Audio32::readBuffer(Audio::st_sample_t *buffer, const int numSamples) { } if (channel.robot) { - // TODO: Robot audio into output buffer + if (channel.stream->endOfStream()) { + stop(channelIndex--); + } else { + const int channelSamplesWritten = writeAudioInternal(channel.stream, channel.converter, buffer, numSamples, kMaxVolume, kMaxVolume, channel.loop); + if (channelSamplesWritten > maxSamplesWritten) { + maxSamplesWritten = channelSamplesWritten; + } + } continue; } @@ -443,9 +451,9 @@ void Audio32::freeUnusedChannels() { Common::StackLock lock(_mutex); for (int channelIndex = 0; channelIndex < _numActiveChannels; ++channelIndex) { const AudioChannel &channel = getChannel(channelIndex); - if (channel.stream->endOfStream()) { + if (!channel.robot && channel.stream->endOfStream()) { if (channel.loop) { - channel.stream->rewind(); + dynamic_cast<Audio::SeekableAudioStream *>(channel.stream)->rewind(); } else { stop(channelIndex--); } @@ -466,21 +474,29 @@ void Audio32::freeChannel(const int16 channelIndex) { Common::StackLock lock(_mutex); AudioChannel &channel = getChannel(channelIndex); - // We cannot unlock resources from the audio thread - // because ResourceManager is not thread-safe; instead, - // we just record that the resource needs unlocking and - // unlock it whenever we are on the main thread again - if (_inAudioThread) { - _resourcesToUnlock.push_back(channel.resource); + // Robots have no corresponding resource to free + if (channel.robot) { + delete channel.stream; + channel.stream = nullptr; + channel.robot = false; } else { - _resMan->unlockResource(channel.resource); + // We cannot unlock resources from the audio thread + // because ResourceManager is not thread-safe; instead, + // we just record that the resource needs unlocking and + // unlock it whenever we are on the main thread again + if (_inAudioThread) { + _resourcesToUnlock.push_back(channel.resource); + } else { + _resMan->unlockResource(channel.resource); + } + + channel.resource = nullptr; + delete channel.stream; + channel.stream = nullptr; + delete channel.resourceStream; + channel.resourceStream = nullptr; } - channel.resource = nullptr; - delete channel.stream; - channel.stream = nullptr; - delete channel.resourceStream; - channel.resourceStream = nullptr; delete channel.converter; channel.converter = nullptr; @@ -527,6 +543,111 @@ void Audio32::setNumOutputChannels(int16 numChannels) { } #pragma mark - +#pragma mark Robot + +int16 Audio32::findRobotChannel() const { + Common::StackLock lock(_mutex); + for (int16 i = 0; i < _numActiveChannels; ++i) { + if (_channels[i].robot) { + return i; + } + } + + return kNoExistingChannel; +} + +bool Audio32::playRobotAudio(const RobotAudioStream::RobotAudioPacket &packet) { + // Stop immediately + if (packet.dataSize == 0) { + warning("Stopping robot stream by zero-length packet"); + return stopRobotAudio(); + } + + // Flush and then stop + if (packet.dataSize == -1) { + warning("Stopping robot stream by negative-length packet"); + return finishRobotAudio(); + } + + Common::StackLock lock(_mutex); + int16 channelIndex = findRobotChannel(); + + bool isNewChannel = false; + if (channelIndex == kNoExistingChannel) { + if (_numActiveChannels == _channels.size()) { + return false; + } + + channelIndex = _numActiveChannels++; + isNewChannel = true; + } + + AudioChannel &channel = getChannel(channelIndex); + + if (isNewChannel) { + channel.id = ResourceId(); + channel.resource = nullptr; + channel.loop = false; + channel.robot = true; + channel.fadeStartTick = 0; + channel.pausedAtTick = 0; + channel.soundNode = NULL_REG; + channel.volume = kMaxVolume; + // TODO: SCI3 introduces stereo audio + channel.pan = -1; + channel.converter = Audio::makeRateConverter(RobotAudioStream::kRobotSampleRate, getRate(), false); + // The RobotAudioStream buffer size is + // ((bytesPerSample * channels * sampleRate * 2000ms) / 1000ms) & ~3 + // where bytesPerSample = 2, channels = 1, and sampleRate = 22050 + channel.stream = new RobotAudioStream(88200); + _robotAudioPaused = false; + + if (_numActiveChannels == 1) { + _startedAtTick = g_sci->getTickCount(); + } + } + + return static_cast<RobotAudioStream *>(channel.stream)->addPacket(packet); +} + +bool Audio32::queryRobotAudio(RobotAudioStream::StreamState &status) const { + Common::StackLock lock(_mutex); + + const int16 channelIndex = findRobotChannel(); + if (channelIndex == kNoExistingChannel) { + status.bytesPlaying = 0; + return false; + } + + status = static_cast<RobotAudioStream *>(getChannel(channelIndex).stream)->getStatus(); + return true; +} + +bool Audio32::finishRobotAudio() { + Common::StackLock lock(_mutex); + + const int16 channelIndex = findRobotChannel(); + if (channelIndex == kNoExistingChannel) { + return false; + } + + static_cast<RobotAudioStream *>(getChannel(channelIndex).stream)->finish(); + return true; +} + +bool Audio32::stopRobotAudio() { + Common::StackLock lock(_mutex); + + const int16 channelIndex = findRobotChannel(); + if (channelIndex == kNoExistingChannel) { + return false; + } + + stop(channelIndex); + return true; +} + +#pragma mark - #pragma mark Playback uint16 Audio32::play(int16 channelIndex, const ResourceId resourceId, const bool autoPlay, const bool loop, const int16 volume, const reg_t soundNode, const bool monitor) { @@ -536,14 +657,15 @@ uint16 Audio32::play(int16 channelIndex, const ResourceId resourceId, const bool if (channelIndex != kNoExistingChannel) { AudioChannel &channel = getChannel(channelIndex); + Audio::SeekableAudioStream *stream = dynamic_cast<Audio::SeekableAudioStream *>(channel.stream); if (channel.pausedAtTick) { resume(channelIndex); - return MIN(65534, 1 + channel.stream->getLength().msecs() * 60 / 1000); + return MIN(65534, 1 + stream->getLength().msecs() * 60 / 1000); } warning("Tried to resume channel %s that was not paused", channel.id.toString().c_str()); - return MIN(65534, 1 + channel.stream->getLength().msecs() * 60 / 1000); + return MIN(65534, 1 + stream->getLength().msecs() * 60 / 1000); } if (_numActiveChannels == _channels.size()) { @@ -642,7 +764,7 @@ uint16 Audio32::play(int16 channelIndex, const ResourceId resourceId, const bool // use audio streams, and allocate and fill the monitoring buffer // when reading audio data from the stream. - channel.duration = /* round up */ 1 + (channel.stream->getLength().msecs() * 60 / 1000); + channel.duration = /* round up */ 1 + (dynamic_cast<Audio::SeekableAudioStream *>(channel.stream)->getLength().msecs() * 60 / 1000); const uint32 now = g_sci->getTickCount(); channel.pausedAtTick = autoPlay ? 0 : now; @@ -687,8 +809,6 @@ bool Audio32::resume(const int16 channelIndex) { if (channel.robot) { channel.startedAtTick += now - channel.pausedAtTick; channel.pausedAtTick = 0; - // TODO: Robot - // StartRobot(); return true; } } diff --git a/engines/sci/sound/audio32.h b/engines/sci/sound/audio32.h index ac3176cc5a..a9905ab6bf 100644 --- a/engines/sci/sound/audio32.h +++ b/engines/sci/sound/audio32.h @@ -30,8 +30,10 @@ #include "common/scummsys.h" // for int16, uint8, uint32, uint16 #include "engines/sci/resource.h" // for ResourceId #include "sci/engine/vm_types.h" // for reg_t, NULL_REG +#include "sci/video/robot_decoder.h" // for RobotAudioStream namespace Sci { +#pragma mark AudioChannel /** * An audio channel used by the software SCI mixer. @@ -53,14 +55,11 @@ struct AudioChannel { Common::SeekableReadStream *resourceStream; /** - * The audio stream loaded into this channel. - * `SeekableAudioStream` is used here instead of - * `RewindableAudioStream` because - * `RewindableAudioStream` does not include the - * `getLength` function, which is needed to tell the - * game engine the duration of audio streams. + * The audio stream loaded into this channel. Can cast + * to `SeekableAudioStream` for normal channels and + * `RobotAudioStream` for robot channels. */ - Audio::SeekableAudioStream *stream; + Audio::AudioStream *stream; /** * The converter used to transform and merge the input @@ -188,7 +187,7 @@ private: * Mixes audio from the given source stream into the * target buffer using the given rate converter. */ - int writeAudioInternal(Audio::RewindableAudioStream *const sourceStream, Audio::RateConverter *const converter, Audio::st_sample_t *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume, const bool loop); + int writeAudioInternal(Audio::AudioStream *const sourceStream, Audio::RateConverter *const converter, Audio::st_sample_t *targetBuffer, const int numSamples, const Audio::st_volume_t leftVolume, const Audio::st_volume_t rightVolume, const bool loop); #pragma mark - #pragma mark Channel management @@ -395,9 +394,18 @@ private: #pragma mark - #pragma mark Robot public: + bool playRobotAudio(const RobotAudioStream::RobotAudioPacket &packet); + bool queryRobotAudio(RobotAudioStream::StreamState &outStatus) const; + bool finishRobotAudio(); + bool stopRobotAudio(); private: /** + * Finds a channel that is configured for robot playback. + */ + int16 findRobotChannel() const; + + /** * When true, channels marked as robot audio will not be * played. */ diff --git a/engines/sci/sound/decoders/sol.cpp b/engines/sci/sound/decoders/sol.cpp index e445403120..ee1ba35406 100644 --- a/engines/sci/sound/decoders/sol.cpp +++ b/engines/sci/sound/decoders/sol.cpp @@ -21,6 +21,7 @@ */ #include "audio/audiostream.h" +#include "audio/rate.h" #include "audio/decoders/raw.h" #include "common/substream.h" #include "common/util.h" @@ -52,7 +53,7 @@ static const byte tableDPCM8[8] = { 0, 1, 2, 3, 6, 10, 15, 21 }; * Decompresses 16-bit DPCM compressed audio. Each byte read * outputs one sample into the decompression buffer. */ -static void deDPCM16(int16 *out, Common::ReadStream &audioStream, uint32 numBytes, int16 &sample) { +static void deDPCM16(int16 *out, Common::ReadStream &audioStream, const uint32 numBytes, int16 &sample) { for (uint32 i = 0; i < numBytes; ++i) { const uint8 delta = audioStream.readByte(); if (delta & 0x80) { @@ -65,6 +66,19 @@ static void deDPCM16(int16 *out, Common::ReadStream &audioStream, uint32 numByte } } +void deDPCM16(int16 *out, const byte *in, const uint32 numBytes, int16 &sample) { + for (uint32 i = 0; i < numBytes; ++i) { + const uint8 delta = *in++; + if (delta & 0x80) { + sample -= tableDPCM16[delta & 0x7f]; + } else { + sample += tableDPCM16[delta]; + } + sample = CLIP<int16>(sample, -32768, 32767); + *out++ = TO_LE_16(sample); + } +} + /** * Decompresses one half of an 8-bit DPCM compressed audio * byte. @@ -178,7 +192,7 @@ int SOLStream<STEREO, S16BIT>::getRate() const { template <bool STEREO, bool S16BIT> bool SOLStream<STEREO, S16BIT>::endOfData() const { - return _stream->eos() || _stream->pos() >= _dataOffset + _rawDataSize; + return _stream->eos() || _stream->pos() >= _rawDataSize; } template <bool STEREO, bool S16BIT> @@ -269,5 +283,4 @@ Audio::SeekableAudioStream *makeSOLStream(Common::SeekableReadStream *headerStre return Audio::makeRawStream(dataStream, sampleRate, rawFlags, disposeAfterUse); } - } diff --git a/engines/sci/video/robot_decoder.cpp b/engines/sci/video/robot_decoder.cpp index a2795d21f9..f3354f9e44 100644 --- a/engines/sci/video/robot_decoder.cpp +++ b/engines/sci/video/robot_decoder.cpp @@ -20,391 +20,1598 @@ * */ -#include "common/archive.h" -#include "common/stream.h" -#include "common/substream.h" -#include "common/system.h" -#include "common/textconsole.h" -#include "common/util.h" - -#include "graphics/surface.h" -#include "audio/audiostream.h" -#include "audio/decoders/raw.h" - -#include "sci/resource.h" -#include "sci/util.h" -#include "sci/sound/audio.h" #include "sci/video/robot_decoder.h" +#include "common/archive.h" // for SearchMan +#include "common/debug.h" // for debugC +#include "common/endian.h" // for MKTAG +#include "common/memstream.h" // for MemoryReadStream +#include "common/platform.h" // for Platform::kPlatformMacintosh +#include "common/rational.h" // for operator*, Rational +#include "common/str.h" // for String +#include "common/stream.h" // for SeekableReadStream +#include "common/substream.h" // for SeekableSubReadStreamEndian +#include "common/textconsole.h" // for error, warning +#include "common/types.h" // for Flag::NO, Flag::YES +#include "sci/engine/seg_manager.h" // for SegManager +#include "sci/graphics/celobj32.h" // for Ratio, ::kLowResX, ::kLowResY +#include "sci/graphics/text32.h" // for BitmapResource +#include "sci/sound/audio32.h" // for Audio32 +#include "sci/sci.h" // for kDebugLevels::kDebugLevelVideo +#include "sci/util.h" // for READ_SCI11ENDIAN_UINT16, READ_SC... namespace Sci { -// TODO: -// - Positioning -// - Proper handling of frame scaling - scaled frames look squashed -// (probably because both dimensions should be scaled) -// - Transparency support -// - Timing - the arbitrary 100ms delay between each frame is not quite right -// - Proper handling of sound chunks in some cases, so that the frame size -// table can be ignored (it's only used to determine the correct sound chunk -// size at the moment, cause it can be wrong in some cases) -// - Fix audio "hiccups" - probably data that shouldn't be in the audio frames - - -// Some non technical information on robot files, from an interview with -// Greg Tomko-Pavia of Sierra On-Line -// Taken from http://anthonylarme.tripod.com/phantas/phintgtp.html -// -// (...) What we needed was a way of playing video, but have it blend into -// normal room art instead of occupying its own rectangular area. Room art -// consists of a background pic overlaid with various animating cels -// (traditional lingo: sprites). The cels each have a priority that determines -// who is on top and who is behind in the drawing order. Cels are read from -// *.v56 files (another proprietary format). A Robot is video frames with -// transparent background including priority and x,y information. Thus, it is -// like a cel, except it comes from an RBT - not a v56. Because it blends into -// our graphics engine, it looks just like a part of the room. A RBT can move -// around the screen and go behind other objects. (...) - -enum RobotPalTypes { - kRobotPalVariable = 0, - kRobotPalConstant = 1 -}; - -RobotDecoder::RobotDecoder(bool isBigEndian) { - _fileStream = 0; - _pos = Common::Point(0, 0); - _isBigEndian = isBigEndian; - _frameTotalSize = 0; +#pragma mark RobotAudioStream + +extern void deDPCM16(int16 *out, const byte *in, const uint32 numBytes, int16 &sample); + +RobotAudioStream::RobotAudioStream(const int32 bufferSize) : + _loopBuffer((byte *)malloc(bufferSize)), + _loopBufferSize(bufferSize), + _decompressionBuffer(nullptr), + _decompressionBufferSize(0), + _decompressionBufferPosition(-1), + _waiting(true), + _finished(false), + _firstPacketPosition(-1) {} + +RobotAudioStream::~RobotAudioStream() { + free(_loopBuffer); + free(_decompressionBuffer); } -RobotDecoder::~RobotDecoder() { - close(); +static void interpolateChannel(int16 *buffer, int32 numSamples, const int8 bufferIndex) { + if (numSamples <= 0) { + return; + } + + if (bufferIndex) { + int16 lastSample = *buffer; + int sample = lastSample; + int16 *target = buffer + 1; + const int16 *source = buffer + 2; + --numSamples; + + while (numSamples--) { + sample = *source + lastSample; + lastSample = *source; + sample /= 2; + *target = sample; + source += 2; + target += 2; + } + + *target = sample; + } else { + int16 *target = buffer; + const int16 *source = buffer + 1; + int16 lastSample = *source; + + while (numSamples--) { + int sample = *source + lastSample; + lastSample = *source; + sample /= 2; + *target = sample; + source += 2; + target += 2; + } + } } -bool RobotDecoder::loadStream(Common::SeekableReadStream *stream) { - close(); +static void copyEveryOtherSample(int16 *out, const int16 *in, int numSamples) { + while (numSamples--) { + *out = *in++; + out += 2; + } +} + +bool RobotAudioStream::addPacket(const RobotAudioPacket &packet) { + Common::StackLock lock(_mutex); + + if (_finished) { + warning("Packet %d sent to finished robot audio stream", packet.position); + return false; + } + + // `packet.position` is the decompressed (doubled) position of the packet, + // so values of `position` will always be divisible either by 2 (even) or by + // 4 (odd). + const int8 bufferIndex = packet.position % 4 ? 1 : 0; + + // Packet 0 is the first primer, packet 2 is the second primer, + // packet 4+ are regular audio data + if (packet.position <= 2 && _firstPacketPosition == -1) { + _readHead = 0; + _readHeadAbs = 0; + _maxWriteAbs = _loopBufferSize; + _writeHeadAbs = 2; + _jointMin[0] = 0; + _jointMin[1] = 2; + _waiting = true; + _finished = false; + _firstPacketPosition = packet.position; + fillRobotBuffer(packet, bufferIndex); + return true; + } - _fileStream = new Common::SeekableSubReadStreamEndian(stream, 0, stream->size(), _isBigEndian, DisposeAfterUse::YES); + const int32 packetEndByte = packet.position + (packet.dataSize * sizeof(int16) * kEOSExpansion); - readHeaderChunk(); + // Already read all the way past this packet (or already wrote valid samples + // to this channel all the way past this packet), so discard it + if (packetEndByte <= MAX(_readHeadAbs, _jointMin[bufferIndex])) { + debugC(kDebugLevelVideo, "Rejecting packet %d, read past %d / %d", packet.position, _readHeadAbs, _jointMin[bufferIndex]); + return true; + } - // There are several versions of robot files, ranging from 3 to 6. - // v3: no known examples - // v4: PQ:SWAT demo - // v5: SCI2.1 and SCI3 games - // v6: SCI3 games - if (_header.version < 4 || _header.version > 6) - error("Unknown robot version: %d", _header.version); + // The loop buffer is full, so tell the caller to send the packet again + // later + if (_maxWriteAbs <= _jointMin[bufferIndex]) { + debugC(kDebugLevelVideo, "Rejecting packet %d, full buffer", packet.position); + return false; + } - RobotVideoTrack *videoTrack = new RobotVideoTrack(_header.frameCount); - addTrack(videoTrack); + fillRobotBuffer(packet, bufferIndex); - if (_header.hasSound) - addTrack(new RobotAudioTrack()); + // This packet is the second primer, so allow playback to begin + if (_firstPacketPosition != -1 && _firstPacketPosition != packet.position) { + debugC(kDebugLevelVideo, "Done waiting. Robot audio begins"); + _waiting = false; + _firstPacketPosition = -1; + } - videoTrack->readPaletteChunk(_fileStream, _header.paletteDataSize); - readFrameSizesChunk(); - videoTrack->calculateVideoDimensions(_fileStream, _frameTotalSize); + // Only part of the packet could be read into the loop buffer before it was + // full, so tell the caller to send the packet again later + if (packetEndByte > _maxWriteAbs) { + debugC(kDebugLevelVideo, "Partial read of packet %d (%d / %d)", packet.position, packetEndByte - _maxWriteAbs, packetEndByte - packet.position); + return false; + } + + // The entire packet was successfully read into the loop buffer return true; } -bool RobotDecoder::load(GuiResourceId id) { - // TODO: RAMA's robot 1003 cannot be played (shown at the menu screen) - - // its drawn at odd coordinates. SV can't play it either (along with some - // others), so it must be some new functionality added in RAMA's robot - // videos. Skip it for now. - if (g_sci->getGameId() == GID_RAMA && id == 1003) - return false; +void RobotAudioStream::fillRobotBuffer(const RobotAudioPacket &packet, const int8 bufferIndex) { + int32 sourceByte = 0; - // Robots for the options in the RAMA menu - if (g_sci->getGameId() == GID_RAMA && (id >= 1004 && id <= 1009)) - return false; + const int32 decompressedSize = packet.dataSize * sizeof(int16); + if (_decompressionBufferPosition != packet.position) { + if (decompressedSize != _decompressionBufferSize) { + _decompressionBuffer = (byte *)realloc(_decompressionBuffer, decompressedSize); + _decompressionBufferSize = decompressedSize; + } - // TODO: The robot video in the Lighthouse demo gets stuck - if (g_sci->getGameId() == GID_LIGHTHOUSE && id == 16) - return false; + int16 carry = 0; + deDPCM16((int16 *)_decompressionBuffer, packet.data, packet.dataSize, carry); + _decompressionBufferPosition = packet.position; + } + + int32 numBytes = decompressedSize; + int32 packetPosition = packet.position; + int32 endByte = packet.position + decompressedSize * kEOSExpansion; + int32 startByte = MAX(_readHeadAbs + bufferIndex * 2, _jointMin[bufferIndex]); + int32 maxWriteByte = _maxWriteAbs + bufferIndex * 2; + if (packetPosition < startByte) { + sourceByte = (startByte - packetPosition) / kEOSExpansion; + numBytes -= sourceByte; + packetPosition = startByte; + } + if (packetPosition > maxWriteByte) { + numBytes += (packetPosition - maxWriteByte) / kEOSExpansion; + packetPosition = maxWriteByte; + } + if (endByte > maxWriteByte) { + numBytes -= (endByte - maxWriteByte) / kEOSExpansion; + endByte = maxWriteByte; + } - Common::String fileName = Common::String::format("%d.rbt", id); + const int32 maxJointMin = MAX(_jointMin[0], _jointMin[1]); + if (endByte > maxJointMin) { + _writeHeadAbs += endByte - maxJointMin; + } + + if (packetPosition > _jointMin[bufferIndex]) { + int32 packetEndByte = packetPosition % _loopBufferSize; + int32 targetBytePosition; + int32 numBytesToEnd; + if ((packetPosition & ~3) > (_jointMin[1 - bufferIndex] & ~3)) { + targetBytePosition = _jointMin[1 - bufferIndex] % _loopBufferSize; + if (targetBytePosition >= packetEndByte) { + numBytesToEnd = _loopBufferSize - targetBytePosition; + memset(_loopBuffer + targetBytePosition, 0, numBytesToEnd); + targetBytePosition = (1 - bufferIndex) ? 2 : 0; + } + numBytesToEnd = packetEndByte - targetBytePosition; + if (numBytesToEnd > 0) { + memset(_loopBuffer + targetBytePosition, 0, numBytesToEnd); + } + } + targetBytePosition = _jointMin[bufferIndex] % _loopBufferSize; + if (targetBytePosition >= packetEndByte) { + numBytesToEnd = _loopBufferSize - targetBytePosition; + interpolateChannel((int16 *)(_loopBuffer + targetBytePosition), numBytesToEnd / sizeof(int16) / kEOSExpansion, 0); + targetBytePosition = bufferIndex ? 2 : 0; + } + numBytesToEnd = packetEndByte - targetBytePosition; + if (numBytesToEnd > 0) { + interpolateChannel((int16 *)(_loopBuffer + targetBytePosition), numBytesToEnd / sizeof(int16) / kEOSExpansion, 0); + } + } + + if (numBytes > 0) { + int32 targetBytePosition = packetPosition % _loopBufferSize; + int32 packetEndByte = endByte % _loopBufferSize; + int32 numBytesToEnd = 0; + if (targetBytePosition >= packetEndByte) { + numBytesToEnd = (_loopBufferSize - (targetBytePosition & ~3)) / kEOSExpansion; + copyEveryOtherSample((int16 *)(_loopBuffer + targetBytePosition), (int16 *)(_decompressionBuffer + sourceByte), numBytesToEnd / kEOSExpansion); + targetBytePosition = bufferIndex ? 2 : 0; + } + copyEveryOtherSample((int16 *)(_loopBuffer + targetBytePosition), (int16 *)(_decompressionBuffer + sourceByte + numBytesToEnd), (packetEndByte - targetBytePosition) / sizeof(int16) / kEOSExpansion); + } + _jointMin[bufferIndex] = endByte; +} + +void RobotAudioStream::interpolateMissingSamples(int32 numSamples) { + int32 numBytes = numSamples * sizeof(int16) * kEOSExpansion; + int32 targetPosition = _readHead; + + if (_readHeadAbs > _jointMin[1]) { + if (_readHeadAbs > _jointMin[0]) { + if (targetPosition + numBytes >= _loopBufferSize) { + const int32 numBytesToEdge = (_loopBufferSize - targetPosition); + memset(_loopBuffer + targetPosition, 0, numBytesToEdge); + numBytes -= numBytesToEdge; + targetPosition = 0; + } + memset(_loopBuffer + targetPosition, 0, numBytes); + _jointMin[0] += numBytes; + _jointMin[1] += numBytes; + } else { + if (targetPosition + numBytes >= _loopBufferSize) { + const int32 numSamplesToEdge = (_loopBufferSize - targetPosition) / sizeof(int16) / kEOSExpansion; + interpolateChannel((int16 *)(_loopBuffer + targetPosition), numSamplesToEdge, 1); + numSamples -= numSamplesToEdge; + targetPosition = 0; + } + interpolateChannel((int16 *)(_loopBuffer + targetPosition), numSamples, 1); + _jointMin[1] += numBytes; + } + } else if (_readHeadAbs > _jointMin[0]) { + if (targetPosition + numBytes >= _loopBufferSize) { + const int32 numSamplesToEdge = (_loopBufferSize - targetPosition) / sizeof(int16) / kEOSExpansion; + interpolateChannel((int16 *)(_loopBuffer + targetPosition), numSamplesToEdge, 0); + numSamples -= numSamplesToEdge; + targetPosition = 2; + } + interpolateChannel((int16 *)(_loopBuffer + targetPosition), numSamples, 0); + _jointMin[0] += numBytes; + } +} + +void RobotAudioStream::finish() { + Common::StackLock lock(_mutex); + _finished = true; +} + +RobotAudioStream::StreamState RobotAudioStream::getStatus() const { + Common::StackLock lock(_mutex); + StreamState status; + status.bytesPlaying = _readHeadAbs; + status.rate = getRate(); + status.bits = 8 * sizeof(int16); + return status; +} + +int RobotAudioStream::readBuffer(Audio::st_sample_t *outBuffer, int numSamples) { + Common::StackLock lock(_mutex); + + if (_waiting) { + return 0; + } + + assert(!((_writeHeadAbs - _readHeadAbs) & 1)); + const int maxNumSamples = (_writeHeadAbs - _readHeadAbs) / sizeof(Audio::st_sample_t); + numSamples = MIN(numSamples, maxNumSamples); + + if (!numSamples) { + return 0; + } + + interpolateMissingSamples(numSamples); + + Audio::st_sample_t *inBuffer = (Audio::st_sample_t *)(_loopBuffer + _readHead); + + assert(!((_loopBufferSize - _readHead) & 1)); + const int numSamplesToEnd = (_loopBufferSize - _readHead) / sizeof(Audio::st_sample_t); + + int numSamplesToRead = MIN(numSamples, numSamplesToEnd); + Common::copy(inBuffer, inBuffer + numSamplesToRead, outBuffer); + + if (numSamplesToRead < numSamples) { + inBuffer = (Audio::st_sample_t *)_loopBuffer; + outBuffer += numSamplesToRead; + numSamplesToRead = numSamples - numSamplesToRead; + Common::copy(inBuffer, inBuffer + numSamplesToRead, outBuffer); + } + + const int32 numBytes = numSamples * sizeof(Audio::st_sample_t); + + _readHead += numBytes; + if (_readHead > _loopBufferSize) { + _readHead -= _loopBufferSize; + } + _readHeadAbs += numBytes; + _maxWriteAbs += numBytes; + assert(!(_readHead & 1)); + assert(!(_readHeadAbs & 1)); + + return numSamples; +} + +#pragma mark - +#pragma mark RobotDecoder + +RobotDecoder::RobotDecoder(SegManager *segMan) : + _delayTime(this), + _segMan(segMan), + _status(kRobotStatusUninitialized), + _audioBuffer(nullptr), + _rawPalette((uint8 *)malloc(kRawPaletteSize)) {} + +RobotDecoder::~RobotDecoder() { + close(); + free(_rawPalette); + free(_audioBuffer); +} + +#pragma mark - +#pragma mark RobotDecoder - Initialization + +void RobotDecoder::initStream(const GuiResourceId robotId) { + const Common::String fileName = Common::String::format("%d.rbt", robotId); Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(fileName); + _fileOffset = 0; - if (!stream) { - warning("Unable to open robot file %s", fileName.c_str()); - return false; + if (stream == nullptr) { + error("Unable to open robot file %s", fileName.c_str()); + } + + const uint16 id = stream->readUint16LE(); + if (id != 0x16) { + error("Invalid robot file %s", fileName.c_str()); + } + + // TODO: Mac version not tested, so this could be totally wrong + _stream = new Common::SeekableSubReadStreamEndian(stream, 0, stream->size(), g_sci->getPlatform() == Common::kPlatformMacintosh, DisposeAfterUse::YES); + _stream->seek(2, SEEK_SET); + if (_stream->readUint32BE() != MKTAG('S', 'O', 'L', 0)) { + error("Resource %s is not Robot type!", fileName.c_str()); + } +} + +void RobotDecoder::initPlayback() { + _startFrameNo = 0; + _startTime = -1; + _startingFrameNo = -1; + _cueForceShowFrame = -1; + _previousFrameNo = -1; + _currentFrameNo = 0; + _status = kRobotStatusPaused; +} + +void RobotDecoder::initAudio() { + _syncFrame = true; + + _audioRecordInterval = RobotAudioStream::kRobotSampleRate / _frameRate; + + // TODO: Might actually be for all games newer than Lighthouse; check to + // see which games have this condition. + if (g_sci->getGameId() != GID_LIGHTHOUSE && !(_audioRecordInterval & 1)) { + ++_audioRecordInterval; + } + + _expectedAudioBlockSize = _audioBlockSize - kAudioBlockHeaderSize; + _audioBuffer = (byte *)realloc(_audioBuffer, kRobotZeroCompressSize + _expectedAudioBlockSize); + + if (_primerReservedSize != 0) { + const int32 primerHeaderPosition = _stream->pos(); + _totalPrimerSize = _stream->readSint32(); + const int16 compressionType = _stream->readSint16(); + _evenPrimerSize = _stream->readSint32(); + _oddPrimerSize = _stream->readSint32(); + _primerPosition = _stream->pos(); + + if (compressionType) { + error("Unknown audio header compression type %d", compressionType); + } + + if (_evenPrimerSize + _oddPrimerSize != _primerReservedSize) { + _stream->seek(primerHeaderPosition + _primerReservedSize, SEEK_SET); + } + } else if (_primerZeroCompressFlag) { + _evenPrimerSize = 19922; + _oddPrimerSize = 21024; + } + + _firstAudioRecordPosition = _evenPrimerSize * 2; + + const int usedEachFrame = (RobotAudioStream::kRobotSampleRate / 2) / _frameRate; + _maxSkippablePackets = MAX(0, _audioBlockSize / usedEachFrame - 1); +} + +void RobotDecoder::initVideo(const int16 x, const int16 y, const int16 scale, const reg_t plane, const bool hasPalette, const uint16 paletteSize) { + _position = Common::Point(x, y); + + if (scale != 128) { + _scaleInfo.x = scale; + _scaleInfo.y = scale; + _scaleInfo.signal = kScaleSignalDoScaling32; + } + + _plane = g_sci->_gfxFrameout->getPlanes().findByObject(plane); + if (_plane == nullptr) { + error("Invalid plane %04x:%04x passed to RobotDecoder::open", PRINT_REG(plane)); + } + + _minFrameRate = _frameRate - kMaxFrameRateDrift; + _maxFrameRate = _frameRate + kMaxFrameRateDrift; + + if (_xResolution == 0 || _yResolution == 0) { + // TODO: Default values were taken from RESOURCE.CFG hires property + // if it exists, so need to check games' configuration files for those + _xResolution = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; + _yResolution = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; } - return loadStream(stream); + if (hasPalette) { + _stream->read(_rawPalette, paletteSize); + } else { + _stream->seek(paletteSize, SEEK_CUR); + } + + _screenItemList.reserve(kScreenItemListSize); + _maxCelArea.reserve(kFixedCelListSize); + + // Fixed cel buffers are for version 5 and newer + _fixedCels.reserve(MIN(_maxCelsPerFrame, (int16)kFixedCelListSize)); + _celDecompressionBuffer.reserve(_maxCelArea[0] + SciBitmap::getBitmapHeaderSize() + kRawPaletteSize); + _celDecompressionArea = _maxCelArea[0]; +} + +void RobotDecoder::initRecordAndCuePositions() { + PositionList recordSizes; + _videoSizes.reserve(_numFramesTotal); + _recordPositions.reserve(_numFramesTotal); + recordSizes.reserve(_numFramesTotal); + + switch(_version) { + case 5: // 16-bit sizes and positions + for (int i = 0; i < _numFramesTotal; ++i) { + _videoSizes.push_back(_stream->readUint16()); + } + for (int i = 0; i < _numFramesTotal; ++i) { + recordSizes.push_back(_stream->readUint16()); + } + break; + case 6: // 32-bit sizes and positions + for (int i = 0; i < _numFramesTotal; ++i) { + _videoSizes.push_back(_stream->readSint32()); + } + for (int i = 0; i < _numFramesTotal; ++i) { + recordSizes.push_back(_stream->readSint32()); + } + break; + default: + error("Unknown Robot version %d", _version); + } + + for (int i = 0; i < kCueListSize; ++i) { + _cueTimes[i] = _stream->readSint32(); + } + + for (int i = 0; i < kCueListSize; ++i) { + _cueValues[i] = _stream->readUint16(); + } + + Common::copy(_cueTimes, _cueTimes + kCueListSize, _masterCueTimes); + + int bytesRemaining = (_stream->pos() - _fileOffset) % kRobotFrameSize; + if (bytesRemaining != 0) { + _stream->seek(kRobotFrameSize - bytesRemaining, SEEK_CUR); + } + + int position = _stream->pos(); + _recordPositions.push_back(position); + for (int i = 0; i < _numFramesTotal - 1; ++i) { + position += recordSizes[i]; + _recordPositions.push_back(position); + } +} + +#pragma mark - +#pragma mark RobotDecoder - Playback + +void RobotDecoder::open(const GuiResourceId robotId, const reg_t plane, const int16 priority, const int16 x, const int16 y, const int16 scale) { + if (_status != kRobotStatusUninitialized) { + warning("Last robot was not closed"); + close(); + } + + initStream(robotId); + + _version = _stream->readUint16(); + + // TODO: Version 4 for PQ:SWAT demo? + if (_version < 5 || _version > 6) { + error("Unsupported version %d of Robot resource", _version); + } + + debugC(kDebugLevelVideo, "Opening version %d robot %d", _version, robotId); + + initPlayback(); + + _audioBlockSize = _stream->readUint16(); + _primerZeroCompressFlag = _stream->readSint16(); + _stream->seek(2, SEEK_CUR); // unused + _numFramesTotal = _stream->readUint16(); + const uint16 paletteSize = _stream->readUint16(); + _primerReservedSize = _stream->readUint16(); + _xResolution = _stream->readSint16(); + _yResolution = _stream->readSint16(); + const bool hasPalette = (bool)_stream->readByte(); + _hasAudio = (bool)_stream->readByte(); + _stream->seek(2, SEEK_CUR); // unused + _frameRate = _normalFrameRate = _stream->readSint16(); + _isHiRes = (bool)_stream->readSint16(); + _maxSkippablePackets = _stream->readSint16(); + _maxCelsPerFrame = _stream->readSint16(); + + // used for memory preallocation of fixed cels + _maxCelArea.push_back(_stream->readSint32()); + _maxCelArea.push_back(_stream->readSint32()); + _maxCelArea.push_back(_stream->readSint32()); + _maxCelArea.push_back(_stream->readSint32()); + _stream->seek(8, SEEK_CUR); // reserved + + if (_hasAudio) { + initAudio(); + } else { + _stream->seek(_primerReservedSize, SEEK_CUR); + } + + _priority = priority; + initVideo(x, y, scale, plane, hasPalette, paletteSize); + initRecordAndCuePositions(); } void RobotDecoder::close() { - VideoDecoder::close(); + if (_status == kRobotStatusUninitialized) { + return; + } + + debugC(kDebugLevelVideo, "Closing robot"); - delete _fileStream; - _fileStream = 0; + _status = kRobotStatusUninitialized; + _videoSizes.clear(); + _recordPositions.clear(); + _celDecompressionBuffer.clear(); + _doVersion5Scratch.clear(); + delete _stream; + _stream = nullptr; + + for (CelHandleList::size_type i = 0; i < _celHandles.size(); ++i) { + if (_celHandles[i].status == CelHandleInfo::kFrameLifetime) { + _segMan->freeBitmap(_celHandles[i].bitmapId); + } + } + _celHandles.clear(); + + for (FixedCelsList::size_type i = 0; i < _fixedCels.size(); ++i) { + _segMan->freeBitmap(_fixedCels[i]); + } + _fixedCels.clear(); + + if (g_sci->_gfxFrameout->getPlanes().findByObject(_plane->_object) != nullptr) { + for (RobotScreenItemList::size_type i = 0; i < _screenItemList.size(); ++i) { + if (_screenItemList[i] != nullptr) { + g_sci->_gfxFrameout->deleteScreenItem(*_screenItemList[i]); + } + } + } + _screenItemList.clear(); - delete[] _frameTotalSize; - _frameTotalSize = 0; + if (_hasAudio) { + _audioList.reset(); + } } -void RobotDecoder::readNextPacket() { - // Get our track - RobotVideoTrack *videoTrack = (RobotVideoTrack *)getTrack(0); - videoTrack->increaseCurFrame(); - Graphics::Surface *surface = videoTrack->getSurface(); +void RobotDecoder::pause() { + if (_status != kRobotStatusPlaying) { + return; + } + + if (_hasAudio) { + _audioList.stopAudioNow(); + } + + _status = kRobotStatusPaused; + _frameRate = _normalFrameRate; +} - if (videoTrack->endOfTrack()) +void RobotDecoder::resume() { + if (_status != kRobotStatusPaused) { return; + } + + _startingFrameNo = _currentFrameNo; + _status = kRobotStatusPlaying; + if (_hasAudio) { + primeAudio(_currentFrameNo * 60 / _frameRate); + _syncFrame = true; + } + + setRobotTime(_currentFrameNo); + for (int i = 0; i < kCueListSize; ++i) { + if (_masterCueTimes[i] != -1 && _masterCueTimes[i] < _currentFrameNo) { + _cueTimes[i] = -1; + } else { + _cueTimes[i] = _masterCueTimes[i]; + } + } +} - // Read frame image header (24 bytes) - _fileStream->skip(3); - byte frameScale = _fileStream->readByte(); - uint16 frameWidth = _fileStream->readUint16(); - uint16 frameHeight = _fileStream->readUint16(); - _fileStream->skip(4); // unknown, almost always 0 - uint16 frameX = _fileStream->readUint16(); - uint16 frameY = _fileStream->readUint16(); - - // TODO: In v4 robot files, frameX and frameY have a different meaning. - // Set them both to 0 for v4 for now, so that robots in PQ:SWAT show up - // correctly. - if (_header.version == 4) - frameX = frameY = 0; - - uint16 compressedSize = _fileStream->readUint16(); - uint16 frameFragments = _fileStream->readUint16(); - _fileStream->skip(4); // unknown - uint32 decompressedSize = frameWidth * frameHeight * frameScale / 100; - - // FIXME: A frame's height + position can go off limits... why? With the - // following, we cut the contents to fit the frame - uint16 scaledHeight = CLIP<uint16>(decompressedSize / frameWidth, 0, surface->h - frameY); - - // FIXME: Same goes for the frame's width + position. In this case, we - // modify the position to fit the contents on screen. - if (frameWidth + frameX > surface->w) - frameX = surface->w - frameWidth; - - assert(frameWidth + frameX <= surface->w && scaledHeight + frameY <= surface->h); - - DecompressorLZS lzs; - byte *decompressedFrame = new byte[decompressedSize]; - byte *outPtr = decompressedFrame; - - if (_header.version == 4) { - // v4 has just the one fragment, it seems, and ignores the fragment count - Common::SeekableSubReadStream fragmentStream(_fileStream, _fileStream->pos(), _fileStream->pos() + compressedSize); - lzs.unpack(&fragmentStream, outPtr, compressedSize, decompressedSize); +void RobotDecoder::showFrame(const uint16 frameNo, const uint16 newX, const uint16 newY, const uint16 newPriority) { + debugC(kDebugLevelVideo, "Show frame %d (%d %d %d)", frameNo, newX, newY, newPriority); + + if (newX != kUnspecified) { + _position.x = newX; + } + + if (newY != kUnspecified) { + _position.y = newY; + } + + if (newPriority != kUnspecified) { + _priority = newPriority; + } + + _currentFrameNo = frameNo; + pause(); + + if (frameNo != _previousFrameNo) { + seekToFrame(frameNo); + doVersion5(false); } else { - for (uint16 i = 0; i < frameFragments; ++i) { - uint32 compressedFragmentSize = _fileStream->readUint32(); - uint32 decompressedFragmentSize = _fileStream->readUint32(); - uint16 compressionType = _fileStream->readUint16(); - - if (compressionType == 0) { - Common::SeekableSubReadStream fragmentStream(_fileStream, _fileStream->pos(), _fileStream->pos() + compressedFragmentSize); - lzs.unpack(&fragmentStream, outPtr, compressedFragmentSize, decompressedFragmentSize); - } else if (compressionType == 2) { // untested - _fileStream->read(outPtr, compressedFragmentSize); + for (RobotScreenItemList::size_type i = 0; i < _screenItemList.size(); ++i) { + if (_isHiRes) { + SciBitmap &bitmap = *_segMan->lookupBitmap(_celHandles[i].bitmapId); + + const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; + const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; + + if (scriptWidth == kLowResX && scriptHeight == kLowResY) { + const Ratio lowResToScreenX(screenWidth, kLowResX); + const Ratio lowResToScreenY(screenHeight, kLowResY); + const Ratio screenToLowResX(kLowResX, screenWidth); + const Ratio screenToLowResY(kLowResY, screenHeight); + + const int16 scaledX = _originalScreenItemX[i] + (_position.x * lowResToScreenX).toInt(); + const int16 scaledY1 = _originalScreenItemY[i] + (_position.y * lowResToScreenY).toInt(); + const int16 scaledY2 = scaledY1 + bitmap.getHeight() - 1; + + const int16 lowResX = (scaledX * screenToLowResX).toInt(); + const int16 lowResY = (scaledY2 * screenToLowResY).toInt(); + + bitmap.setDisplace(Common::Point( + (scaledX - (lowResX * lowResToScreenX).toInt()) * -1, + (lowResY * lowResToScreenY).toInt() - scaledY1 + )); + + _screenItemX[i] = lowResX; + _screenItemY[i] = lowResY; + } else { + const int16 scaledX = _originalScreenItemX[i] + _position.x; + const int16 scaledY = _originalScreenItemY[i] + _position.y + bitmap.getHeight() - 1; + bitmap.setDisplace(Common::Point(0, bitmap.getHeight() - 1)); + _screenItemX[i] = scaledX; + _screenItemY[i] = scaledY; + } + } else { + _screenItemX[i] = _originalScreenItemX[i] + _position.x; + _screenItemY[i] = _originalScreenItemY[i] + _position.y; + } + + if (_screenItemList[i] == nullptr) { + CelInfo32 celInfo; + celInfo.type = kCelTypeMem; + celInfo.bitmap = _celHandles[i].bitmapId; + ScreenItem *screenItem = new ScreenItem(_plane->_object, celInfo); + _screenItemList[i] = screenItem; + screenItem->_position = Common::Point(_screenItemX[i], _screenItemY[i]); + if (_priority == -1) { + screenItem->_fixedPriority = false; + } else { + screenItem->_priority = _priority; + screenItem->_fixedPriority = true; + } + g_sci->_gfxFrameout->addScreenItem(*screenItem); } else { - error("Unknown frame compression found: %d", compressionType); + ScreenItem *screenItem = _screenItemList[i]; + screenItem->_celInfo.bitmap = _celHandles[i].bitmapId; + screenItem->_position = Common::Point(_screenItemX[i], _screenItemY[i]); + if (_priority == -1) { + screenItem->_fixedPriority = false; + } else { + screenItem->_priority = _priority; + screenItem->_fixedPriority = true; + } + g_sci->_gfxFrameout->updateScreenItem(*screenItem); + } + } + } + + _previousFrameNo = frameNo; +} + +int16 RobotDecoder::getCue() const { + if (_status == kRobotStatusUninitialized || + _status == kRobotStatusPaused || + _syncFrame) { + return 0; + } + + if (_status == kRobotStatusEnd) { + return -1; + } + + const uint16 estimatedNextFrameNo = MIN(calculateNextFrameNo(_delayTime.predictedTicks()), _numFramesTotal); + + for (int i = 0; i < kCueListSize; ++i) { + if (_cueTimes[i] != -1 && _cueTimes[i] <= estimatedNextFrameNo) { + if (_cueTimes[i] >= _previousFrameNo) { + _cueForceShowFrame = _cueTimes[i] + 1; } - outPtr += decompressedFragmentSize; + _cueTimes[i] = -1; + return _cueValues[i]; } } - // Copy over the decompressed frame - byte *inFrame = decompressedFrame; - byte *outFrame = (byte *)surface->getPixels(); + return 0; +} - // Black out the surface - memset(outFrame, 0, surface->w * surface->h); +int16 RobotDecoder::getFrameNo() const { + if (_status == kRobotStatusUninitialized) { + return 0; + } - // Move to the correct y coordinate - outFrame += surface->w * frameY; + return _currentFrameNo; +} + +RobotDecoder::RobotStatus RobotDecoder::getStatus() const { + return _status; +} - for (uint16 y = 0; y < scaledHeight; y++) { - memcpy(outFrame + frameX, inFrame, frameWidth); - inFrame += frameWidth; - outFrame += surface->w; +bool RobotDecoder::seekToFrame(const int frameNo) { + return _stream->seek(_recordPositions[frameNo], SEEK_SET); +} + +void RobotDecoder::setRobotTime(const int frameNo) { + _startTime = getTickCount(); + _startFrameNo = frameNo; +} + +#pragma mark - +#pragma mark RobotDecoder - Timing + +RobotDecoder::DelayTime::DelayTime(RobotDecoder *decoder) : + _decoder(decoder) { + for (int i = 0; i < kDelayListSize; ++i) { + _timestamps[i] = i; + _delays[i] = 0; } - delete[] decompressedFrame; + _oldestTimestamp = 0; + _newestTimestamp = kDelayListSize - 1; + _startTime = 0; +} - uint32 audioChunkSize = _frameTotalSize[videoTrack->getCurFrame()] - (24 + compressedSize); +void RobotDecoder::DelayTime::startTiming() { + _startTime = _decoder->getTickCount(); +} -// TODO: The audio chunk size below is usually correct, but there are some -// exceptions (e.g. robot 4902 in Phantasmagoria, towards its end) -#if 0 - // Read frame audio header (14 bytes) - _fileStream->skip(2); // buffer position - _fileStream->skip(2); // unknown (usually 1) - _fileStream->skip(2); /*uint16 audioChunkSize = _fileStream->readUint16() + 8;*/ - _fileStream->skip(2); -#endif +void RobotDecoder::DelayTime::endTiming() { + const int timeDelta = _decoder->getTickCount() - _startTime; + for (uint i = 0; i < kDelayListSize; ++i) { + if (_timestamps[i] == _oldestTimestamp) { + _timestamps[i] = ++_newestTimestamp; + _delays[i] = timeDelta; + break; + } + } + ++_newestTimestamp; + _startTime = 0; + sortList(); +} - // Queue the next audio frame - // FIXME: For some reason, there are audio hiccups/gaps - if (_header.hasSound) { - RobotAudioTrack *audioTrack = (RobotAudioTrack *)getTrack(1); - _fileStream->skip(8); // header - audioChunkSize -= 8; - audioTrack->queueBuffer(g_sci->_audio->getDecodedRobotAudioFrame(_fileStream, audioChunkSize), audioChunkSize * 2); +bool RobotDecoder::DelayTime::timingInProgress() const { + return _startTime != 0; +} + +int RobotDecoder::DelayTime::predictedTicks() const { + return _delays[kDelayListSize / 2]; +} + +void RobotDecoder::DelayTime::sortList() { + for (uint i = 0; i < kDelayListSize - 1; ++i) { + int smallestDelay = _delays[i]; + uint smallestIndex = i; + + for (uint j = i + 1; j < kDelayListSize - 1; ++j) { + if (_delays[j] < smallestDelay) { + smallestDelay = _delays[j]; + smallestIndex = j; + } + } + + if (smallestIndex != i) { + SWAP(_delays[i], _delays[smallestIndex]); + SWAP(_timestamps[i], _timestamps[smallestIndex]); + } + } +} + +uint16 RobotDecoder::calculateNextFrameNo(const uint32 extraTicks) const { + return ticksToFrames(getTickCount() + extraTicks - _startTime) + _startFrameNo; +} + +uint32 RobotDecoder::ticksToFrames(const uint32 ticks) const { + return (ticks * _frameRate) / 60; +} + +uint32 RobotDecoder::getTickCount() const { + return g_sci->getTickCount(); +} + +#pragma mark - +#pragma mark RobotDecoder - Audio + +RobotDecoder::AudioList::AudioList() : + _blocks(), + _blocksSize(0), + _oldestBlockIndex(0), + _newestBlockIndex(0), + _startOffset(0), + _status(kRobotAudioReady) {} + +void RobotDecoder::AudioList::startAudioNow() { + submitDriverMax(); + g_sci->_audio32->resume(kRobotChannel); + _status = kRobotAudioPlaying; +} + +void RobotDecoder::AudioList::stopAudio() { + g_sci->_audio32->finishRobotAudio(); + freeAudioBlocks(); + _status = kRobotAudioStopping; +} + +void RobotDecoder::AudioList::stopAudioNow() { + if (_status == kRobotAudioPlaying || _status == kRobotAudioStopping || _status == kRobotAudioPaused) { + g_sci->_audio32->stopRobotAudio(); + _status = kRobotAudioStopped; + } + + freeAudioBlocks(); +} + +void RobotDecoder::AudioList::submitDriverMax() { + while (_blocksSize != 0) { + if (!_blocks[_oldestBlockIndex]->submit(_startOffset)) { + return; + } + + delete _blocks[_oldestBlockIndex]; + _blocks[_oldestBlockIndex] = nullptr; + ++_oldestBlockIndex; + if (_oldestBlockIndex == kAudioListSize) { + _oldestBlockIndex = 0; + } + + --_blocksSize; + } +} + +void RobotDecoder::AudioList::addBlock(const int position, const int size, const byte *data) { + assert(data != nullptr); + assert(size >= 0); + assert(position >= -1); + + if (_blocksSize == kAudioListSize) { + delete _blocks[_oldestBlockIndex]; + _blocks[_oldestBlockIndex] = nullptr; + ++_oldestBlockIndex; + if (_oldestBlockIndex == kAudioListSize) { + _oldestBlockIndex = 0; + } + --_blocksSize; + } + + if (_blocksSize == 0) { + _oldestBlockIndex = _newestBlockIndex = 0; } else { - _fileStream->skip(audioChunkSize); - } -} - -void RobotDecoder::readHeaderChunk() { - // Header (60 bytes) - _fileStream->skip(6); - _header.version = _fileStream->readUint16(); - _header.audioChunkSize = _fileStream->readUint16(); - _header.audioSilenceSize = _fileStream->readUint16(); - _fileStream->skip(2); - _header.frameCount = _fileStream->readUint16(); - _header.paletteDataSize = _fileStream->readUint16(); - _header.unkChunkDataSize = _fileStream->readUint16(); - _fileStream->skip(5); - _header.hasSound = _fileStream->readByte(); - _fileStream->skip(34); - - // Some videos (e.g. robot 1305 in Phantasmagoria and - // robot 184 in Lighthouse) have an unknown chunk before - // the palette chunk (probably used for sound preloading). - // Skip it here. - if (_header.unkChunkDataSize) - _fileStream->skip(_header.unkChunkDataSize); -} - -void RobotDecoder::readFrameSizesChunk() { - // The robot video file contains 2 tables, with one entry for each frame: - // - A table containing the size of the image in each video frame - // - A table containing the total size of each video frame. - // In v5 robots, the tables contain 16-bit integers, whereas in v6 robots, - // they contain 32-bit integers. - - _frameTotalSize = new uint32[_header.frameCount]; - - // TODO: The table reading code can probably be removed once the - // audio chunk size is figured out (check the TODO inside processNextFrame()) -#if 0 - // We don't need any of the two tables to play the video, so we ignore - // both of them. - uint16 wordSize = _header.version == 6 ? 4 : 2; - _fileStream->skip(_header.frameCount * wordSize * 2); -#else - switch (_header.version) { - case 4: - case 5: // sizes are 16-bit integers - // Skip table with frame image sizes, as we don't need it - _fileStream->skip(_header.frameCount * 2); - for (int i = 0; i < _header.frameCount; ++i) - _frameTotalSize[i] = _fileStream->readUint16(); - break; - case 6: // sizes are 32-bit integers - // Skip table with frame image sizes, as we don't need it - _fileStream->skip(_header.frameCount * 4); - for (int i = 0; i < _header.frameCount; ++i) - _frameTotalSize[i] = _fileStream->readUint32(); - break; - default: - error("Can't yet handle index table for robot version %d", _header.version); + ++_newestBlockIndex; + if (_newestBlockIndex == kAudioListSize) { + _newestBlockIndex = 0; + } } -#endif - // 2 more unknown tables - _fileStream->skip(1024 + 512); + _blocks[_newestBlockIndex] = new AudioBlock(position, size, data); + ++_blocksSize; +} - // Pad to nearest 2 kilobytes - uint32 curPos = _fileStream->pos(); - if (curPos & 0x7ff) - _fileStream->seek((curPos & ~0x7ff) + 2048); +void RobotDecoder::AudioList::reset() { + stopAudioNow(); + _startOffset = 0; + _status = kRobotAudioReady; } -RobotDecoder::RobotVideoTrack::RobotVideoTrack(int frameCount) : _frameCount(frameCount) { - _surface = new Graphics::Surface(); - _curFrame = -1; - _dirtyPalette = false; +void RobotDecoder::AudioList::prepareForPrimer() { + g_sci->_audio32->pause(kRobotChannel); + _status = kRobotAudioPaused; } -RobotDecoder::RobotVideoTrack::~RobotVideoTrack() { - _surface->free(); - delete _surface; +void RobotDecoder::AudioList::setAudioOffset(const int offset) { + _startOffset = offset; } -uint16 RobotDecoder::RobotVideoTrack::getWidth() const { - return _surface->w; +RobotDecoder::AudioList::AudioBlock::AudioBlock(const int position, const int size, const byte* const data) : + _position(position), + _size(size) { + _data = (byte *)malloc(size); + memcpy(_data, data, size); } -uint16 RobotDecoder::RobotVideoTrack::getHeight() const { - return _surface->h; +RobotDecoder::AudioList::AudioBlock::~AudioBlock() { + free(_data); } -Graphics::PixelFormat RobotDecoder::RobotVideoTrack::getPixelFormat() const { - return _surface->format; +bool RobotDecoder::AudioList::AudioBlock::submit(const int startOffset) { + assert(_data != nullptr); + RobotAudioStream::RobotAudioPacket packet(_data, _size, (_position - startOffset) * 2); + return g_sci->_audio32->playRobotAudio(packet); } -void RobotDecoder::RobotVideoTrack::readPaletteChunk(Common::SeekableSubReadStreamEndian *stream, uint16 chunkSize) { - byte *paletteData = new byte[chunkSize]; - stream->read(paletteData, chunkSize); +void RobotDecoder::AudioList::freeAudioBlocks() { + while (_blocksSize != 0) { + delete _blocks[_oldestBlockIndex]; + _blocks[_oldestBlockIndex] = nullptr; + ++_oldestBlockIndex; + if (_oldestBlockIndex == kAudioListSize) { + _oldestBlockIndex = 0; + } + + --_blocksSize; + } +} - // SCI1.1 palette - byte palFormat = paletteData[32]; - uint16 palColorStart = paletteData[25]; - uint16 palColorCount = READ_SCI11ENDIAN_UINT16(paletteData + 29); +bool RobotDecoder::primeAudio(const uint32 startTick) { + bool success = true; + _audioList.reset(); + + if (startTick == 0) { + _audioList.prepareForPrimer(); + byte *evenPrimerBuff = new byte[_evenPrimerSize]; + byte *oddPrimerBuff = new byte[_oddPrimerSize]; + + success = readPrimerData(evenPrimerBuff, oddPrimerBuff); + if (success) { + if (_evenPrimerSize != 0) { + _audioList.addBlock(0, _evenPrimerSize, evenPrimerBuff); + } + if (_oddPrimerSize != 0) { + _audioList.addBlock(1, _oddPrimerSize, oddPrimerBuff); + } + } + + delete[] evenPrimerBuff; + delete[] oddPrimerBuff; + } else { + assert(_evenPrimerSize * 2 >= _audioRecordInterval || _oddPrimerSize * 2 >= _audioRecordInterval); + + int audioStartFrame = 0; + int videoStartFrame = startTick * _frameRate / 60; + assert(videoStartFrame < _numFramesTotal); + + int audioStartPosition = (startTick * RobotAudioStream::kRobotSampleRate) / 60; + if (audioStartPosition & 1) { + audioStartPosition--; + } + _audioList.setAudioOffset(audioStartPosition); + _audioList.prepareForPrimer(); + + if (audioStartPosition < _evenPrimerSize * 2 || + audioStartPosition + 1 < _oddPrimerSize * 2) { + + byte *evenPrimerBuffer = new byte[_evenPrimerSize]; + byte *oddPrimerBuffer = new byte[_oddPrimerSize]; + success = readPrimerData(evenPrimerBuffer, oddPrimerBuffer); + if (success) { + int halfAudioStartPosition = audioStartPosition / 2; + if (audioStartPosition < _evenPrimerSize * 2) { + _audioList.addBlock(audioStartPosition, _evenPrimerSize - halfAudioStartPosition, &evenPrimerBuffer[halfAudioStartPosition]); + } + + if (audioStartPosition + 1 < _oddPrimerSize * 2) { + _audioList.addBlock(audioStartPosition + 1, _oddPrimerSize - halfAudioStartPosition, &oddPrimerBuffer[halfAudioStartPosition]); + } + } + + delete[] evenPrimerBuffer; + delete[] oddPrimerBuffer; + } - int palOffset = 37; - memset(_palette, 0, 256 * 3); + if (audioStartPosition >= _firstAudioRecordPosition) { + int audioRecordSize = _expectedAudioBlockSize; + assert(audioRecordSize > 0); + assert(_audioRecordInterval > 0); + assert(_firstAudioRecordPosition >= 0); - for (uint16 colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) { - if (palFormat == kRobotPalVariable) - palOffset++; - _palette[colorNo * 3 + 0] = paletteData[palOffset++]; - _palette[colorNo * 3 + 1] = paletteData[palOffset++]; - _palette[colorNo * 3 + 2] = paletteData[palOffset++]; + audioStartFrame = (audioStartPosition - _firstAudioRecordPosition) / _audioRecordInterval; + assert(audioStartFrame < videoStartFrame); + + if (audioStartFrame > 0) { + int lastAudioFrame = audioStartFrame - 1; + int oddRemainder = lastAudioFrame & 1; + int audioRecordStart = (lastAudioFrame * _audioRecordInterval) + oddRemainder + _firstAudioRecordPosition; + int audioRecordEnd = (audioRecordStart + ((audioRecordSize - 1) * 2)) + oddRemainder + _firstAudioRecordPosition; + + if (audioStartPosition >= audioRecordStart && audioStartPosition <= audioRecordEnd) { + --audioStartFrame; + } + } + + assert(!(audioStartPosition & 1)); + if (audioStartFrame & 1) { + ++audioStartPosition; + } + + if (!readPartialAudioRecordAndSubmit(audioStartFrame, audioStartPosition)) { + return false; + } + + ++audioStartFrame; + assert(audioStartFrame < videoStartFrame); + + int oddRemainder = audioStartFrame & 1; + int audioRecordStart = (audioStartFrame * _audioRecordInterval) + oddRemainder + _firstAudioRecordPosition; + int audioRecordEnd = (audioRecordStart + ((audioRecordSize - 1) * 2)) + oddRemainder + _firstAudioRecordPosition; + + if (audioStartPosition >= audioRecordStart && audioStartPosition <= audioRecordEnd) { + if (!readPartialAudioRecordAndSubmit(audioStartFrame, audioStartPosition + 1)) { + return false; + } + + ++audioStartFrame; + } + } + + int audioPosition, audioSize; + for (int i = audioStartFrame; i < videoStartFrame; i++) { + if (!readAudioDataFromRecord(i, _audioBuffer, audioPosition, audioSize)) { + break; + } + + _audioList.addBlock(audioPosition, audioSize, _audioBuffer); + } } - _dirtyPalette = true; - delete[] paletteData; + return success; } -void RobotDecoder::RobotVideoTrack::calculateVideoDimensions(Common::SeekableSubReadStreamEndian *stream, uint32 *frameSizes) { - // This is an O(n) operation, as each frame has a different size. - // We need to know the actual frame size to have a constant video size. - uint32 pos = stream->pos(); +bool RobotDecoder::readPrimerData(byte *outEvenBuffer, byte *outOddBuffer) { + if (_primerReservedSize != 0) { + if (_totalPrimerSize != 0) { + _stream->seek(_primerPosition, SEEK_SET); + if (_evenPrimerSize > 0) { + _stream->read(outEvenBuffer, _evenPrimerSize); + } + + if (_oddPrimerSize > 0) { + _stream->read(outOddBuffer, _oddPrimerSize); + } + } + } else if (_primerZeroCompressFlag) { + memset(outEvenBuffer, 0, _evenPrimerSize); + memset(outOddBuffer, 0, _oddPrimerSize); + } else { + error("ReadPrimerData - Flags corrupt"); + } + + return !_stream->err(); +} + +bool RobotDecoder::readAudioDataFromRecord(const int frameNo, byte *outBuffer, int &outAudioPosition, int &outAudioSize) { + _stream->seek(_recordPositions[frameNo] + _videoSizes[frameNo], SEEK_SET); + _audioList.submitDriverMax(); + + // Compressed absolute position of the audio block in the audio stream + const int position = _stream->readSint32(); - uint16 width = 0, height = 0; + // Size of the block of audio, excluding the audio block header + int size = _stream->readSint32(); - for (int curFrame = 0; curFrame < _frameCount; curFrame++) { - stream->skip(4); - uint16 frameWidth = stream->readUint16(); - uint16 frameHeight = stream->readUint16(); - if (frameWidth > width) - width = frameWidth; - if (frameHeight > height) - height = frameHeight; - stream->skip(frameSizes[curFrame] - 8); + assert(size <= _expectedAudioBlockSize); + + if (position == 0) { + return false; } - stream->seek(pos); + if (size != _expectedAudioBlockSize) { + memset(outBuffer, 0, kRobotZeroCompressSize); + _stream->read(outBuffer + kRobotZeroCompressSize, size); + size += kRobotZeroCompressSize; + } else { + _stream->read(outBuffer, size); + } - _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); + outAudioPosition = position; + outAudioSize = size; + return !_stream->err(); } -RobotDecoder::RobotAudioTrack::RobotAudioTrack() { - _audioStream = Audio::makeQueuingAudioStream(11025, false); +bool RobotDecoder::readPartialAudioRecordAndSubmit(const int startFrame, const int startPosition) { + int audioPosition, audioSize; + bool success = readAudioDataFromRecord(startFrame, _audioBuffer, audioPosition, audioSize); + if (success) { + const int relativeStartOffset = (startPosition - audioPosition) / 2; + _audioList.addBlock(startPosition, audioSize - relativeStartOffset, _audioBuffer + relativeStartOffset); + } + + return success; } -RobotDecoder::RobotAudioTrack::~RobotAudioTrack() { - delete _audioStream; +#pragma mark - +#pragma mark RobotDecoder - Rendering + +uint16 RobotDecoder::getFrameSize(Common::Rect &outRect) const { + outRect.clip(0, 0); + for (RobotScreenItemList::size_type i = 0; i < _screenItemList.size(); ++i) { + ScreenItem &screenItem = *_screenItemList[i]; + outRect.extend(screenItem.getNowSeenRect(*_plane)); + } + + return _numFramesTotal; } -void RobotDecoder::RobotAudioTrack::queueBuffer(byte *buffer, int size) { - _audioStream->queueBuffer(buffer, size, DisposeAfterUse::YES, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN); +void RobotDecoder::doRobot() { + if (_status != kRobotStatusPlaying) { + return; + } + + if (!_syncFrame) { + if (_cueForceShowFrame != -1) { + _currentFrameNo = _cueForceShowFrame; + _cueForceShowFrame = -1; + } else { + const int nextFrameNo = calculateNextFrameNo(_delayTime.predictedTicks()); + if (nextFrameNo < _currentFrameNo) { + return; + } + _currentFrameNo = nextFrameNo; + } + } + + if (_currentFrameNo >= _numFramesTotal) { + const int finalFrameNo = _numFramesTotal - 1; + if (_previousFrameNo == finalFrameNo) { + _status = kRobotStatusEnd; + if (_hasAudio) { + _audioList.stopAudio(); + _frameRate = _normalFrameRate; + _hasAudio = false; + } + return; + } else { + _currentFrameNo = finalFrameNo; + } + } + + if (_currentFrameNo == _previousFrameNo) { + _audioList.submitDriverMax(); + return; + } + + if (_hasAudio) { + for (int candidateFrameNo = _previousFrameNo + _maxSkippablePackets + 1; candidateFrameNo < _currentFrameNo; candidateFrameNo += _maxSkippablePackets + 1) { + + _audioList.submitDriverMax(); + + int audioPosition, audioSize; + if (readAudioDataFromRecord(candidateFrameNo, _audioBuffer, audioPosition, audioSize)) { + _audioList.addBlock(audioPosition, audioSize, _audioBuffer); + } + } + _audioList.submitDriverMax(); + } + + _delayTime.startTiming(); + seekToFrame(_currentFrameNo); + doVersion5(); + if (_hasAudio) { + _audioList.submitDriverMax(); + } +} + +void RobotDecoder::frameAlmostVisible() { + if (_status == kRobotStatusPlaying && !_syncFrame) { + if (_previousFrameNo != _currentFrameNo) { + while (calculateNextFrameNo() < _currentFrameNo) { + _audioList.submitDriverMax(); + } + } + } +} + +void RobotDecoder::frameNowVisible() { + if (_status != kRobotStatusPlaying) { + return; + } + + if (_syncFrame) { + _syncFrame = false; + if (_hasAudio) { + _audioList.startAudioNow(); + _checkAudioSyncTime = _startTime + kAudioSyncCheckInterval; + } + + setRobotTime(_currentFrameNo); + } + + if (_delayTime.timingInProgress()) { + _delayTime.endTiming(); + } + + if (_hasAudio) { + _audioList.submitDriverMax(); + } + + if (_previousFrameNo != _currentFrameNo) { + _previousFrameNo = _currentFrameNo; + } + + if (!_syncFrame && _hasAudio && getTickCount() >= _checkAudioSyncTime) { + RobotAudioStream::StreamState status; + const bool success = g_sci->_audio32->queryRobotAudio(status); + if (!success) { + return; + } + + const int bytesPerFrame = status.rate / _normalFrameRate * (status.bits == 16 ? 2 : 1); + // check again in 1/3rd second + _checkAudioSyncTime = getTickCount() + 60 / 3; + + const int currentVideoFrameNo = calculateNextFrameNo() - _startingFrameNo; + const int currentAudioFrameNo = status.bytesPlaying / bytesPerFrame; + debugC(kDebugLevelVideo, "Video frame %d %s audio frame %d", currentVideoFrameNo, currentVideoFrameNo == currentAudioFrameNo ? "=" : currentVideoFrameNo < currentAudioFrameNo ? "<" : ">", currentAudioFrameNo); + if (currentVideoFrameNo < _numFramesTotal && + currentAudioFrameNo < _numFramesTotal) { + + bool shouldResetRobotTime = false; + + if (currentAudioFrameNo < currentVideoFrameNo - 1 && _frameRate != _minFrameRate) { + debugC(kDebugLevelVideo, "[v] Reducing frame rate"); + _frameRate = _minFrameRate; + shouldResetRobotTime = true; + } else if (currentAudioFrameNo > currentVideoFrameNo + 1 && _frameRate != _maxFrameRate) { + debugC(kDebugLevelVideo, "[^] Increasing frame rate"); + _frameRate = _maxFrameRate; + shouldResetRobotTime = true; + } else if (_frameRate != _normalFrameRate) { + debugC(kDebugLevelVideo, "[=] Setting to normal frame rate"); + _frameRate = _normalFrameRate; + shouldResetRobotTime = true; + } + + if (shouldResetRobotTime) { + if (currentAudioFrameNo < _currentFrameNo) { + setRobotTime(_currentFrameNo); + } else { + setRobotTime(currentAudioFrameNo); + } + } + } + } +} + +void RobotDecoder::expandCel(byte* target, const byte* source, const int16 celWidth, const int16 celHeight) const { + assert(source != nullptr && target != nullptr); + + const int sourceHeight = (celHeight * _verticalScaleFactor) / 100; + assert(sourceHeight > 0); + + const int16 numerator = celHeight; + const int16 denominator = sourceHeight; + int remainder = 0; + for (int16 y = sourceHeight - 1; y >= 0; --y) { + remainder += numerator; + int16 linesToDraw = remainder / denominator; + remainder %= denominator; + + while (linesToDraw--) { + memcpy(target, source, celWidth); + target += celWidth; + } + + source += celWidth; + } +} + +void RobotDecoder::setPriority(const int16 newPriority) { + _priority = newPriority; +} + +void RobotDecoder::doVersion5(const bool shouldSubmitAudio) { + const RobotScreenItemList::size_type oldScreenItemCount = _screenItemList.size(); + const int videoSize = _videoSizes[_currentFrameNo]; + _doVersion5Scratch.resize(videoSize); + + byte *videoFrameData = _doVersion5Scratch.begin(); + + if (!_stream->read(videoFrameData, videoSize)) { + error("RobotDecoder::doVersion5: Read error"); + } + + const RobotScreenItemList::size_type screenItemCount = READ_SCI11ENDIAN_UINT16(videoFrameData); + + if (screenItemCount > kScreenItemListSize) { + return; + } + + if (_hasAudio && + (getSciVersion() < SCI_VERSION_3 || shouldSubmitAudio)) { + int audioPosition, audioSize; + if (readAudioDataFromRecord(_currentFrameNo, _audioBuffer, audioPosition, audioSize)) { + _audioList.addBlock(audioPosition, audioSize, _audioBuffer); + } + } + + if (screenItemCount > oldScreenItemCount) { + _screenItemList.resize(screenItemCount); + _screenItemX.resize(screenItemCount); + _screenItemY.resize(screenItemCount); + _originalScreenItemX.resize(screenItemCount); + _originalScreenItemY.resize(screenItemCount); + } + + createCels5(videoFrameData + 2, screenItemCount, true); + for (RobotScreenItemList::size_type i = 0; i < screenItemCount; ++i) { + Common::Point position(_screenItemX[i], _screenItemY[i]); + +// TODO: Version 6 robot? +// int scaleXRemainder; + if (_scaleInfo.signal == kScaleSignalDoScaling32) { + position.x = (position.x * _scaleInfo.x) / 128; +// TODO: Version 6 robot? +// scaleXRemainder = (position.x * _scaleInfo.x) % 128; + position.y = (position.y * _scaleInfo.y) / 128; + } + + if (_screenItemList[i] == nullptr) { + CelInfo32 celInfo; + celInfo.bitmap = _celHandles[i].bitmapId; + ScreenItem *screenItem = new ScreenItem(_plane->_object, celInfo, position, _scaleInfo); + _screenItemList[i] = screenItem; + // TODO: Version 6 robot? + // screenItem->_field_30 = scaleXRemainder; + + if (_priority == -1) { + screenItem->_fixedPriority = false; + } else { + screenItem->_fixedPriority = true; + screenItem->_priority = _priority; + } + g_sci->_gfxFrameout->addScreenItem(*screenItem); + } else { + ScreenItem *screenItem = _screenItemList[i]; + screenItem->_celInfo.bitmap = _celHandles[i].bitmapId; + screenItem->_position = position; + // TODO: Version 6 robot? + // screenItem->_field_30 = scaleXRemainder; + + if (_priority == -1) { + screenItem->_fixedPriority = false; + } else { + screenItem->_fixedPriority = true; + screenItem->_priority = _priority; + } + g_sci->_gfxFrameout->updateScreenItem(*screenItem); + } + } + + for (RobotScreenItemList::size_type i = screenItemCount; i < oldScreenItemCount; ++i) { + if (_screenItemList[i] != nullptr) { + g_sci->_gfxFrameout->deleteScreenItem(*_screenItemList[i]); + _screenItemList[i] = nullptr; + } + } +} + +void RobotDecoder::createCels5(const byte *rawVideoData, const int16 numCels, const bool usePalette) { + preallocateCelMemory(rawVideoData, numCels); + for (int16 i = 0; i < numCels; ++i) { + rawVideoData += createCel5(rawVideoData, i, usePalette); + } +} + +uint32 RobotDecoder::createCel5(const byte *rawVideoData, const int16 screenItemIndex, const bool usePalette) { + _verticalScaleFactor = rawVideoData[1]; + const int16 celWidth = (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 2); + const int16 celHeight = (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 4); + const Common::Point celPosition((int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 10), + (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 12)); + const uint16 dataSize = READ_SCI11ENDIAN_UINT16(rawVideoData + 14); + const int16 numDataChunks = (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 16); + + rawVideoData += kCelHeaderSize; + + const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; + const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; + + Common::Point displace; + if (scriptWidth == kLowResX && scriptHeight == kLowResY) { + const Ratio lowResToScreenX(screenWidth, kLowResX); + const Ratio lowResToScreenY(screenHeight, kLowResY); + const Ratio screenToLowResX(kLowResX, screenWidth); + const Ratio screenToLowResY(kLowResY, screenHeight); + + const int16 scaledX = celPosition.x + (_position.x * lowResToScreenX).toInt(); + const int16 scaledY1 = celPosition.y + (_position.y * lowResToScreenY).toInt(); + const int16 scaledY2 = scaledY1 + celHeight - 1; + + const int16 lowResX = (scaledX * screenToLowResX).toInt(); + const int16 lowResY = (scaledY2 * screenToLowResY).toInt(); + + displace.x = (scaledX - (lowResX * lowResToScreenX).toInt()) * -1; + displace.y = (lowResY * lowResToScreenY).toInt() - scaledY1; + _screenItemX[screenItemIndex] = lowResX; + _screenItemY[screenItemIndex] = lowResY; + + debugC(kDebugLevelVideo, "Low resolution position c: %d %d l: %d/%d %d/%d d: %d %d s: %d/%d %d/%d x: %d y: %d", celPosition.x, celPosition.y, lowResX, scriptWidth, lowResY, scriptHeight, displace.x, displace.y, scaledX, screenWidth, scaledY2, screenHeight, scaledX - displace.x, scaledY2 - displace.y); + } else { + const int16 highResX = celPosition.x + _position.x; + const int16 highResY = celPosition.y + _position.y + celHeight - 1; + + displace.x = 0; + displace.y = celHeight - 1; + _screenItemX[screenItemIndex] = highResX; + _screenItemY[screenItemIndex] = highResY; + + debugC(kDebugLevelVideo, "High resolution position c: %d %d s: %d %d d: %d %d", celPosition.x, celPosition.y, highResX, highResY, displace.x, displace.y); + } + + _originalScreenItemX[screenItemIndex] = celPosition.x; + _originalScreenItemY[screenItemIndex] = celPosition.y; + + assert(_celHandles[screenItemIndex].area >= celWidth * celHeight); + + SciBitmap &bitmap = *_segMan->lookupBitmap(_celHandles[screenItemIndex].bitmapId); + assert(bitmap.getWidth() == celWidth && bitmap.getHeight() == celHeight); + assert(bitmap.getScaledWidth() == _xResolution && bitmap.getScaledHeight() == _yResolution); + assert(bitmap.getHunkPaletteOffset() == (uint32)bitmap.getWidth() * bitmap.getHeight() + SciBitmap::getBitmapHeaderSize()); + bitmap.setDisplace(displace); + + byte *targetBuffer = nullptr; + if (_verticalScaleFactor == 100) { + // direct copy to bitmap + targetBuffer = bitmap.getPixels(); + } else { + // go through squashed cel decompressor + _celDecompressionBuffer.resize(_celDecompressionArea >= celWidth * (celHeight * _verticalScaleFactor / 100)); + targetBuffer = _celDecompressionBuffer.begin(); + } + + for (int i = 0; i < numDataChunks; ++i) { + uint compressedSize = READ_SCI11ENDIAN_UINT32(rawVideoData); + uint decompressedSize = READ_SCI11ENDIAN_UINT32(rawVideoData + 4); + uint16 compressionType = READ_SCI11ENDIAN_UINT16(rawVideoData + 8); + rawVideoData += 10; + + switch (compressionType) { + case kCompressionLZS: { + Common::MemoryReadStream videoDataStream(rawVideoData, compressedSize, DisposeAfterUse::NO); + _decompressor.unpack(&videoDataStream, targetBuffer, compressedSize, decompressedSize); + break; + } + case kCompressionNone: + Common::copy(rawVideoData, rawVideoData + decompressedSize, targetBuffer); + break; + default: + error("Unknown compression type %d!", compressionType); + } + + rawVideoData += compressedSize; + targetBuffer += decompressedSize; + } + + if (_verticalScaleFactor != 100) { + expandCel(bitmap.getPixels(), _celDecompressionBuffer.begin(), celWidth, celHeight); + } + + if (usePalette) { + Common::copy(_rawPalette, _rawPalette + kRawPaletteSize, bitmap.getHunkPalette()); + } + + return kCelHeaderSize + dataSize; } -Audio::AudioStream *RobotDecoder::RobotAudioTrack::getAudioStream() const { - return _audioStream; +void RobotDecoder::preallocateCelMemory(const byte *rawVideoData, const int16 numCels) { + for (CelHandleList::size_type i = 0; i < _celHandles.size(); ++i) { + CelHandleInfo &celHandle = _celHandles[i]; + + if (celHandle.status == CelHandleInfo::kFrameLifetime) { + _segMan->freeBitmap(celHandle.bitmapId); + celHandle.bitmapId = NULL_REG; + celHandle.status = CelHandleInfo::kNoCel; + celHandle.area = 0; + } + } + _celHandles.resize(numCels); + + const int numFixedCels = MIN(numCels, (int16)kFixedCelListSize); + for (int i = 0; i < numFixedCels; ++i) { + CelHandleInfo &celHandle = _celHandles[i]; + + // NOTE: There was a check to see if the cel handle was not allocated + // here, for some reason, which would mean that nothing was ever + // allocated from fixed cels, because the _celHandles array just got + // deleted and recreated... + if (celHandle.bitmapId == NULL_REG) { + break; + } + + celHandle.bitmapId = _fixedCels[i]; + celHandle.status = CelHandleInfo::kRobotLifetime; + celHandle.area = _maxCelArea[i]; + } + + uint maxFrameArea = 0; + for (int i = 0; i < numCels; ++i) { + const int16 celWidth = (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 2); + const int16 celHeight = (int16)READ_SCI11ENDIAN_UINT16(rawVideoData + 4); + const uint16 dataSize = READ_SCI11ENDIAN_UINT16(rawVideoData + 14); + const uint area = celWidth * celHeight; + + if (area > maxFrameArea) { + maxFrameArea = area; + } + + CelHandleInfo &celHandle = _celHandles[i]; + if (celHandle.status == CelHandleInfo::kRobotLifetime) { + if (_maxCelArea[i] < area) { + _segMan->freeBitmap(celHandle.bitmapId); + _segMan->allocateBitmap(&celHandle.bitmapId, celWidth, celHeight, 255, 0, 0, _xResolution, _yResolution, kRawPaletteSize, false, false); + celHandle.area = area; + celHandle.status = CelHandleInfo::kFrameLifetime; + } + } else if (celHandle.status == CelHandleInfo::kNoCel) { + _segMan->allocateBitmap(&celHandle.bitmapId, celWidth, celHeight, 255, 0, 0, _xResolution, _yResolution, kRawPaletteSize, false, false); + celHandle.area = area; + celHandle.status = CelHandleInfo::kFrameLifetime; + } else { + error("Cel Handle has bad status"); + } + + rawVideoData += kCelHeaderSize + dataSize; + } + + if (maxFrameArea > _celDecompressionBuffer.size()) { + _celDecompressionBuffer.resize(maxFrameArea); + } } } // End of namespace Sci diff --git a/engines/sci/video/robot_decoder.h b/engines/sci/video/robot_decoder.h index 4faea5008a..5fd6ad49c4 100644 --- a/engines/sci/video/robot_decoder.h +++ b/engines/sci/video/robot_decoder.h @@ -20,109 +20,1407 @@ * */ -#ifndef SCI_VIDEO_ROBOT_DECODER_H -#define SCI_VIDEO_ROBOT_DECODER_H +#ifndef SCI_SOUND_DECODERS_ROBOT_H +#define SCI_SOUND_DECODERS_ROBOT_H -#include "common/rational.h" -#include "common/rect.h" -#include "video/video_decoder.h" +#include "audio/audiostream.h" // for AudioStream +#include "audio/rate.h" // for st_sample_t +#include "common/array.h" // for Array +#include "common/mutex.h" // for StackLock, Mutex +#include "common/rect.h" // for Point, Rect (ptr only) +#include "common/scummsys.h" // for int16, int32, byte, uint16 +#include "sci/engine/vm_types.h" // for NULL_REG, reg_t +#include "sci/graphics/helpers.h" // for GuiResourceId +#include "sci/graphics/screen_item32.h" // for ScaleInfo, ScreenItem (ptr o... -namespace Audio { -class QueuingAudioStream; -} +namespace Common { class SeekableSubReadStreamEndian; } +namespace Sci { +class Plane; +class SegManager; -namespace Common { -class SeekableSubReadStreamEndian; -} +// Notes on Robot v5/v6 format: +// +// Robot is a packetized streaming AV format that encodes multiple bitmaps + +// positioning data, plus synchronised audio, for rendering in the SCI graphics +// system. +// +// Unlike traditional AV formats, Robot videos almost always require playback +// within the game engine because certain information (like the resolution of +// the Robot coordinates and the background for the video) is dependent on data +// that does not exist within the Robot file itself. +// +// The Robot container consists of a file header, an optional primer audio +// section, an optional colour palette, a frame seek index, a set of cuepoints, +// and variable-sized packets of compressed video+audio data. +// +// Integers in Robot files are coded using native endianness (LSB for x86 +// versions, MSB for 68k/PPC versions). +// +// Robot video coding is a relatively simple variable-length compression with no +// interframe compression. Each cel in a frame is constructed from multiple +// contiguous data blocks, each of which can be independently compressed with +// LZS or left uncompressed. An entire cel can also be line decimated, where +// lines are deleted from the source bitmap at compression time and are +// reconstructed by decompression using line doubling. Each cel also includes +// coordinates where it should be placed within the video frame, relative to the +// top-left corner of the frame. +// +// Audio coding is fixed-length, and all audio blocks except for the primer +// audio are the same size. Audio is encoded with Sierra SOL DPCM16 compression, +// and is split into two channels ('even' and 'odd'), each at a 11025Hz sample +// rate. The original signal is restored by interleaving samples from the two +// channels together. Channel packets are 'even' if they have an ''absolute +// position of audio'' that is evenly divisible by 2; otherwise, they are 'odd'. +// Because the channels use DPCM compression, there is an 8-byte runway at the +// start of every audio block that is never written to the output stream, which +// is used to move the signal to the correct location by the 9th sample. +// +// File header (v5/v6): +// +// byte | description +// 0 | signature 0x16 +// 1 | unused +// 2-5 | signature 'SOL\0' +// 6-7 | version (4, 5, and 6 are the only known versions) +// 8-9 | size of audio blocks +// 10-11 | primer is compressed flag +// 12-13 | unused +// 14-15 | total number of video frames +// 16-17 | embedded palette size, in bytes +// 18-19 | primer reserved size +// 20-21 | coordinate X-resolution (if 0, uses game coordinates) +// 22-23 | coordinate Y-resolution (if 0, uses game coordinates) +// 24 | if non-zero, Robot includes a palette +// 25 | if non-zero, Robot includes audio +// 26-27 | unused +// 28-29 | the frame rate, in frames per second +// 30-31 | coordinate conversion flag; if true, screen item coordinates +// | from the robot should be used as-is with NO conversion when +// | explicitly displaying a specific frame +// 32-33 | the maximum number of packets that can be skipped without causing +// | audio drop-out +// 34-35 | the maximum possible number of cels that will be displayed in any +// | frame of the robot +// 36-39 | the maximum possible size, in bytes, of the first fixed cel +// 40-43 | the maximum possible size, in bytes, of the second fixed cel +// 44-47 | the maximum possible size, in bytes, of the third fixed cel +// 48-51 | the maximum possible size, in bytes, of the fourth fixed cel +// 52-59 | unused +// +// If the ''file includes audio'' flag is false, seek ''primer reserved size'' +// bytes from the end of the file header to get past a padding zone. +// +// If the ''file includes audio'' flag is true, and the ''primer reserved size'' +// is not zero, the data immediately after the file header consists of an audio +// primer header plus compressed audio data: +// +// Audio primer header: +// +// byte | description +// 0-3 | the size, in bytes, of the entire primer audio section +// 4-5 | the compression format of the primer audio (must be zero) +// 6-9 | the size, in bytes, of the "even" primer +// 10-13 | the size, in bytes, of the "odd" primer +// +// If the combined sizes of the even and odd primers do not match the ''primer +// reserved size'', the next header block can be found ''primer reserved size'' +// bytes from the *start* of the audio primer header. +// +// Otherwise, if the Robot has audio, and the ''primer reserved size'' is zero, +// and the ''primer is compressed flag'' is set, the "even" primer size is +// 19922, the "odd" primer size is 21024, and the "even" and "odd" buffers +// should be zero-filled. +// +// Any other combination of these flags is an error. +// +// If the Robot has a palette, the next ''palette size'' bytes should be read +// as a SCI HunkPalette. Otherwise, seek ''palette size'' bytes from the current +// position to get to the frame index. +// +// The next section of the Robot is the video frame size index. In version 5 +// robots, read ''total number of frames'' 16-bit integers to get the size of +// the compressed video for each frame. For version 6 robots, use 32-bit +// integers. +// +// The next section of the Robot is the packet size index (combined compressed +// size of video + audio for each frame). In version 5 Robots, read ''total +// number of frames'' 16-bit integers. In version 6 robots, use 32-bit integers. +// +// The next section of the Robot is the cue times index. Read 256 32-bit +// integers, which represent the number of ticks from the start of playback that +// the given cue point falls on. +// +// The next section of the Robot is the cue values index. Read 256 16-bit +// integers, which represent the actual cue values that will be passed back to +// the game engine when a cue is requested. +// +// Finally, to get to the first frame packet, seek from the current position to +// the start of the next 2048-byte-aligned sector. +// +// Frame packet: +// +// byte | description +// 0..n | video data (size is in the ''video frame size index'') +// n+1.. | optional audio data (size is ''size of audio blocks'') +// +// Video data: +// +// byte | description +// 0-2 | number of cels in the frame (max 10) +// 3..n | cels +// +// Cel: +// +// 0-17 | cel header +// 18..n | data chunks +// +// Cel header: +// +// byte | description +// 0 | unused +// 1 | vertical scale factor, in percent decimation (100 = no decimation, +// | 50 = 50% of lines were removed) +// 2-3 | cel width +// 4-5 | cel height +// 6-9 | unused +// 10-11 | cel x-position, in Robot coordinates +// 12-13 | cel y-position, in Robot coordinates +// 14-15 | cel total data chunk size, in bytes +// 16-17 | number of data chunks +// +// Cel data chunk: +// +// 0-9 | cel data chunk header +// 10..n | cel data +// +// Cel data chunk header: +// +// byte | description +// 0-3 | compressed size +// 4-7 | decompressed size +// 8-9 | compression type (0 = LZS, 2 = uncompressed) +// +// Random frame seeking can be done by calculating the address of the frame +// packet by adding up the ''packet size index'' entries up to the current +// frame. This will normally disable audio playback, as audio data in a packet +// does not correspond to the video in the same packet. +// +// Audio data is placed immediately after the end of the video data in a packet, +// and consists of an audio header plus compressed audio data: +// +// Audio data: +// +// byte | description +// 0-7 | audio data header +// 8-15 | DPCM runway +// 16..n | compressed audio data +// +// Audio data header: +// +// byte | description +// 0-3 | absolute position of audio in the audio stream +// 4-7 | the size of the audio block, excluding the header +// +// When a block of audio is processed, first check to ensure that the +// decompressed audio block's `position * 2 + length * 4` runs past the end of +// the last packet of the same evenness/oddness. Discard the audio block +// entirely if data has already been written past the end of this block for this +// channel, or if the read head has already read past the end of this audio +// block. +// +// If the block is not discarded, apply DPCM decompression to the entire block, +// starting from beginning of the DPCM runway, using an initial sample value of +// 0. Then, copy every sample from the decompressed source outside of the DPCM +// runway into every *other* sample of the final audio buffer (1 -> 2, 2 -> 4, +// 3 -> 6, etc.). +// +// Finally, for any skipped samples where the opposing (even/odd) channel did +// not yet write, interpolate the skipped areas by adding together the +// neighbouring samples from this audio block and dividing by two. (This allows +// the audio quality to degrade to 11kHz in case it takes too long to decode all +// the frames in the stream). Interpolated samples must not be written on top of +// true data from the opposing channel. Audio from later packets must also not +// be written on top of data in the same channel that was already written by an +// earlier packet, in particular because the first 8 bytes of the next packet +// are garbage data used to move the waveform to the correct position (due to +// the use of DPCM compression). -namespace Sci { +#pragma mark - +#pragma mark RobotAudioStream + +/** + * A Robot audio stream is a simple loop buffer + * that accepts audio blocks from the Robot engine. + */ +class RobotAudioStream : public Audio::AudioStream { +public: + enum { + /** + * The sample rate used for all robot audio. + */ + kRobotSampleRate = 22050, + + /** + * Multiplier for the size of a packet that + * is being expanded by writing to every other + * byte of the target buffer. + */ + kEOSExpansion = 2 + }; + + /** + * Playback state information. Used for framerate + * calculation. + */ + struct StreamState { + /** + * The current position of the read head of + * the audio stream. + */ + int bytesPlaying; + + /** + * The sample rate of the audio stream. + * Always 22050. + */ + uint16 rate; + + /** + * The bit depth of the audio stream. + * Always 16. + */ + uint8 bits; + }; + + /** + * A single packet of compressed audio from a + * Robot data stream. + */ + struct RobotAudioPacket { + /** + * Raw DPCM-compressed audio data. + */ + byte *data; + + /** + * The size of the compressed audio data, + * in bytes. + */ + int dataSize; + + /** + * The uncompressed, file-relative position + * of this audio packet. + */ + int position; + + RobotAudioPacket(byte *data_, const int dataSize_, const int position_) : + data(data_), dataSize(dataSize_), position(position_) {} + }; + + RobotAudioStream(const int32 bufferSize); + virtual ~RobotAudioStream(); + + /** + * Adds a new audio packet to the stream. + * @returns `true` if the audio packet was fully + * consumed, otherwise `false`. + */ + bool addPacket(const RobotAudioPacket &packet); + + /** + * Prevents any additional audio packets from + * being added to the audio stream. + */ + void finish(); + + /** + * Returns the current status of the audio + * stream. + */ + StreamState getStatus() const; + +private: + Common::Mutex _mutex; + + /** + * Loop buffer for playback. Contains decompressed + * 16-bit PCM samples. + */ + byte *_loopBuffer; + + /** + * The size of the loop buffer, in bytes. + */ + int32 _loopBufferSize; + + /** + * The position of the read head within the loop + * buffer, in bytes. + */ + int32 _readHead; + + /** + * The lowest file position that can be buffered, + * in uncompressed bytes. + */ + int32 _readHeadAbs; + + /** + * The highest file position that can be buffered, + * in uncompressed bytes. + */ + int32 _maxWriteAbs; + + /** + * The highest file position, in uncompressed bytes, + * that has been written to the stream. + * Different from `_maxWriteAbs`, which is the highest + * uncompressed position which *can* be written right + * now. + */ + int32 _writeHeadAbs; + + /** + * The highest file position, in uncompressed bytes, + * that has been written to the even & odd sides of + * the stream. + * + * Index 0 corresponds to the 'even' side; index + * 1 correspond to the 'odd' side. + */ + int32 _jointMin[2]; + + /** + * When `true`, the stream is waiting for all primer + * blocks to be received before allowing playback to + * begin. + */ + bool _waiting; + + /** + * When `true`, the stream will accept no more audio + * blocks. + */ + bool _finished; + + /** + * The uncompressed position of the first packet of + * robot data. Used to decide whether all primer + * blocks have been received and the stream should + * be started. + */ + int32 _firstPacketPosition; + + /** + * Decompression buffer, used to temporarily store + * an uncompressed block of audio data. + */ + byte *_decompressionBuffer; + + /** + * The size of the decompression buffer, in bytes. + */ + int32 _decompressionBufferSize; + + /** + * The position of the packet currently in the + * decompression buffer. Used to avoid + * re-decompressing audio data that has already + * been decompressed during a partial packet read. + */ + int32 _decompressionBufferPosition; + + /** + * Calculates the absolute ranges for new fills + * into the loop buffer. + */ + void fillRobotBuffer(const RobotAudioPacket &packet, const int8 bufferIndex); + + /** + * Interpolates `numSamples` samples from the read + * head, if no true samples were written for one + * (or both) of the joint channels. + */ + void interpolateMissingSamples(const int32 numSamples); + +#pragma mark - +#pragma mark RobotAudioStream - AudioStream implementation +public: + int readBuffer(Audio::st_sample_t *outBuffer, int numSamples) override; + virtual bool isStereo() const override { return false; }; + virtual int getRate() const override { return 22050; }; + virtual bool endOfData() const override { + Common::StackLock lock(_mutex); + return _readHeadAbs >= _writeHeadAbs; + }; + virtual bool endOfStream() const override { + Common::StackLock lock(_mutex); + return _finished && endOfData(); + } +}; + +#pragma mark - +#pragma mark RobotDecoder + +/** + * RobotDecoder implements the logic required + * for Robot animations. + * + * @note A paused or finished RobotDecoder was + * classified as serializable in SCI3, but the + * save/load code would attempt to use uninitialised + * values, so it seems that robots were not ever + * actually able to be saved. + */ +class RobotDecoder { +public: + RobotDecoder(SegManager *segMan); + ~RobotDecoder(); + +private: + SegManager *_segMan; + +#pragma mark Constants +public: + /** + * The playback status of the robot. + */ + enum RobotStatus { + kRobotStatusUninitialized = 0, + kRobotStatusPlaying = 1, + kRobotStatusEnd = 2, + kRobotStatusPaused = 3 + }; + + enum { + // Special high value used to represent + // parameters that should be left unchanged + // when calling `showFrame` + kUnspecified = 50000 + }; + +private: + enum { + /** + * Maximum number of on-screen screen items. + */ + kScreenItemListSize = 10, + + /** + * Maximum number of queued audio blocks. + */ + kAudioListSize = 10, + + /** + * Maximum number of samples used for frame timing. + */ + kDelayListSize = 10, + + /** + * Maximum number of cues. + */ + kCueListSize = 256, + + /** + * Maximum number of 'fixed' cels that never + * change for the duration of a robot. + */ + kFixedCelListSize = 4, + + /** + * The size of a hunk palette in the Robot stream. + */ + kRawPaletteSize = 1200, + + /** + * The size of a frame of Robot data. This + * value was used to align the first block of + * data after the main Robot header to the next + * CD sector. + */ + kRobotFrameSize = 2048, + + /** + * The size of a block of zero-compressed + * audio. Used to fill audio when the size of + * an audio packet does not match the expected + * packet size. + */ + kRobotZeroCompressSize = 2048, -class RobotDecoder : public Video::VideoDecoder { + /** + * The size of the audio block header, in bytes. + * The audio block header consists of the + * compressed size of the audio in the record, + * plus the position of the audio in the + * compressed data stream. + */ + kAudioBlockHeaderSize = 8, + + /** + * The size of a Robot cel header, in bytes. + */ + kCelHeaderSize = 22, + + /** + * The maximum amount that the frame rate is + * allowed to drift from the nominal frame rate + * in order to correct for AV drift or slow + * playback. + */ + kMaxFrameRateDrift = 1 + }; + + /** + * The version number for the currently loaded + * robot. + * + * There are several known versions of robot: + * + * v2: before Nov 1994; no known examples + * v3: before Nov 1994; no known examples + * v4: Jan 1995; PQ:SWAT demo + * v5: Mar 1995; SCI2.1 and SCI3 games + * v6: SCI3 games + */ + uint16 _version; + +#pragma mark - +#pragma mark Initialisation +private: + /** + * Sets up the read stream for the robot. + */ + void initStream(const GuiResourceId robotId); + + /** + * Sets up the initial values for playback control. + */ + void initPlayback(); + + /** + * Sets up the initial values for audio decoding. + */ + void initAudio(); + + /** + * Sets up the initial values for video rendering. + */ + void initVideo(const int16 x, const int16 y, const int16 scale, const reg_t plane, const bool hasPalette, const uint16 paletteSize); + + /** + * Sets up the robot's data record and cue positions. + */ + void initRecordAndCuePositions(); + +#pragma mark - +#pragma mark Playback public: - RobotDecoder(bool isBigEndian); - virtual ~RobotDecoder(); + /** + * Opens a robot file for playback. + * Newly opened robots are paused by default. + */ + void open(const GuiResourceId robotId, const reg_t plane, const int16 priority, const int16 x, const int16 y, const int16 scale); - bool loadStream(Common::SeekableReadStream *stream); - bool load(GuiResourceId id); + /** + * Closes the currently open robot file. + */ void close(); - void setPos(uint16 x, uint16 y) { _pos = Common::Point(x, y); } - Common::Point getPos() const { return _pos; } + /** + * Pauses the robot. Once paused, the audio for a robot + * is disabled until the end of playback. + */ + void pause(); + + /** + * Resumes a paused robot. + */ + void resume(); + + /** + * Moves robot to the specified frame and pauses playback. + * + * @note Called DisplayFrame in SSCI. + */ + void showFrame(const uint16 frameNo, const uint16 newX, const uint16 newY, const uint16 newPriority); + + /** + * Retrieves the value associated with the + * current cue point. + */ + int16 getCue() const; + + /** + * Gets the currently displayed frame. + */ + int16 getFrameNo() const; + + /** + * Gets the playback status of the player. + */ + RobotStatus getStatus() const; + +private: + /** + * The read stream containing raw robot data. + */ + Common::SeekableSubReadStreamEndian *_stream; + + /** + * The current status of the player. + */ + RobotStatus _status; + + typedef Common::Array<int> PositionList; + + /** + * A map of frame numbers to byte offsets within `_stream`. + */ + PositionList _recordPositions; + + /** + * The offset of the Robot file within a + * resource bundle. + */ + int32 _fileOffset; + + /** + * A list of cue times that is updated to + * prevent earlier cue values from being + * given to the game more than once. + */ + mutable int32 _cueTimes[kCueListSize]; + + /** + * The original list of cue times from the + * raw Robot data. + */ + int32 _masterCueTimes[kCueListSize]; + + /** + * The list of values to provide to a game + * when a cue value is requested. + */ + int32 _cueValues[kCueListSize]; + + /** + * The current playback frame rate. + */ + int16 _frameRate; + + /** + * The nominal playback frame rate. + */ + int16 _normalFrameRate; + + /** + * The minimal playback frame rate. Used to + * correct for AV sync drift when the video + * is more than one frame ahead of the audio. + */ + int16 _minFrameRate; + + /** + * The maximum playback frame rate. Used to + * correct for AV sync drift when the video + * is more than one frame behind the audio. + */ + int16 _maxFrameRate; + + /** + * The maximum number of record blocks that + * can be skipped without causing audio to + * drop out. + */ + int16 _maxSkippablePackets; + + /** + * The currently displayed frame number. + */ + int _currentFrameNo; + + /** + * The last displayed frame number. + */ + int _previousFrameNo; + + /** + * The time, in ticks, when the robot was + * last started or resumed. + */ + int32 _startTime; + + /** + * The first frame displayed when the + * robot was resumed. + */ + int32 _startFrameNo; + + /** + * The last frame displayed when the robot + * was resumed. + */ + int32 _startingFrameNo; + + /** + * Seeks the raw data stream to the record for + * the given frame number. + */ + bool seekToFrame(const int frameNo); -protected: - void readNextPacket(); + /** + * Sets the start time and frame of the robot + * when the robot is started or resumed. + */ + void setRobotTime(const int frameNo); +#pragma mark - +#pragma mark Timing private: - class RobotVideoTrack : public FixedRateVideoTrack { + /** + * This class tracks the amount of time it takes for + * a frame of robot animation to be rendered. This + * information is used by the player to speculatively + * skip rendering of future frames to keep the + * animation in sync with the robot audio. + */ + class DelayTime { public: - RobotVideoTrack(int frameCount); - ~RobotVideoTrack(); - - uint16 getWidth() const; - uint16 getHeight() const; - Graphics::PixelFormat getPixelFormat() const; - int getCurFrame() const { return _curFrame; } - int getFrameCount() const { return _frameCount; } - const Graphics::Surface *decodeNextFrame() { return _surface; } - const byte *getPalette() const { _dirtyPalette = false; return _palette; } - bool hasDirtyPalette() const { return _dirtyPalette; } - - void readPaletteChunk(Common::SeekableSubReadStreamEndian *stream, uint16 chunkSize); - void calculateVideoDimensions(Common::SeekableSubReadStreamEndian *stream, uint32 *frameSizes); - Graphics::Surface *getSurface() { return _surface; } - void increaseCurFrame() { _curFrame++; } - - protected: - Common::Rational getFrameRate() const { return Common::Rational(60, 10); } + DelayTime(RobotDecoder *decoder); + + /** + * Starts performance timing. + */ + void startTiming(); + + /** + * Ends performance timing. + */ + void endTiming(); + + /** + * Returns whether or not timing is currently in + * progress. + */ + bool timingInProgress() const; + + /** + * Returns the median time, in ticks, of the + * currently stored timing samples. + */ + int predictedTicks() const; private: - int _frameCount; - int _curFrame; - byte _palette[256 * 3]; - mutable bool _dirtyPalette; - Graphics::Surface *_surface; + RobotDecoder *_decoder; + + /** + * The start time, in ticks, of the current timing + * loop. If no loop is in progress, the value is 0. + * + * @note This is slightly different than SSCI where + * the not-timing value was -1. + */ + uint32 _startTime; + + /** + * A sorted list containing the timing data for + * the last `kDelayListSize` frames, in ticks. + */ + int _delays[kDelayListSize]; + + /** + * A list of monotonically increasing identifiers + * used to identify and replace the oldest sample + * in the `_delays` array when finishing the + * next timing operation. + */ + uint _timestamps[kDelayListSize]; + + /** + * The identifier of the oldest timing. + */ + uint _oldestTimestamp; + + /** + * The identifier of the newest timing. + */ + uint _newestTimestamp; + + /** + * Sorts the list of timings. + */ + void sortList(); }; - class RobotAudioTrack : public AudioTrack { + /** + * Calculates the next frame number that needs + * to be rendered, using the timing data + * collected by DelayTime. + */ + uint16 calculateNextFrameNo(const uint32 extraTicks = 0) const; + + /** + * Calculates and returns the number of frames + * that should be rendered in `ticks` time, + * according to the current target frame rate + * of the robot. + */ + uint32 ticksToFrames(const uint32 ticks) const; + + /** + * Gets the current game time, in ticks. + */ + uint32 getTickCount() const; + + /** + * The performance timer for the robot. + */ + DelayTime _delayTime; + +#pragma mark - +#pragma mark Audio +private: + enum { + /** + * The number of ticks that should elapse + * between each AV sync check. + */ + kAudioSyncCheckInterval = 5 * 60 /* 5 seconds */ + }; + + /** + * The status of the audio track of a Robot + * animation. + */ + enum RobotAudioStatus { + kRobotAudioReady = 1, + kRobotAudioStopped = 2, + kRobotAudioPlaying = 3, + kRobotAudioPaused = 4, + kRobotAudioStopping = 5 + }; + +#pragma mark - +#pragma mark Audio - AudioList +private: + /** + * This class manages packetized audio playback + * for robots. + */ + class AudioList { public: - RobotAudioTrack(); - ~RobotAudioTrack(); + AudioList(); + + /** + * Starts playback of robot audio. + */ + void startAudioNow(); + + /** + * Stops playback of robot audio, allowing + * any queued audio to finish playing back. + */ + void stopAudio(); + + /** + * Stops playback of robot audio immediately. + */ + void stopAudioNow(); + + /** + * Submits as many blocks of audio as possible + * to the audio engine. + */ + void submitDriverMax(); + + /** + * Adds a new AudioBlock to the queue. + * + * @param position The absolute position of the + * audio for the block, in compressed bytes. + * @param size The size of the buffer. + * @param buffer A pointer to compressed audio + * data that will be copied into the new + * AudioBlock. + */ + void addBlock(const int position, const int size, const byte *buffer); - Audio::Mixer::SoundType getSoundType() const { return Audio::Mixer::kMusicSoundType; } + /** + * Immediately stops any active playback and + * purges all audio data in the audio list. + */ + void reset(); - void queueBuffer(byte *buffer, int size); + /** + * Pauses the robot audio channel in + * preparation for the first block of audio + * data to be read. + */ + void prepareForPrimer(); - protected: - Audio::AudioStream *getAudioStream() const; + /** + * Sets the audio offset which is used to + * offset the position of audio packets + * sent to the audio stream. + */ + void setAudioOffset(const int offset); + +#pragma mark - +#pragma mark Audio - AudioList - AudioBlock private: - Audio::QueuingAudioStream *_audioStream; + /** + * AudioBlock represents a block of audio + * from the Robot's audio track. + */ + class AudioBlock { + public: + AudioBlock(const int position, const int size, const byte *const data); + ~AudioBlock(); + + /** + * Submits the block of audio to the + * audio manager. + * @returns true if the block was fully + * read, or false if the block was not + * read or only partially read. + */ + bool submit(const int startOffset); + + private: + /** + * The absolute position, in compressed + * bytes, of this audio block's audio + * data in the audio stream. + */ + int _position; + + /** + * The compressed size, in bytes, of + * this audio block's audio data. + */ + int _size; + + /** + * A buffer containing raw + * SOL-compressed audio data. + */ + byte *_data; + }; + + /** + * The list of compressed audio blocks + * submitted for playback. + */ + AudioBlock *_blocks[kAudioListSize]; + + /** + * The number of blocks in `_blocks` that are + * ready to be submitted. + */ + uint8 _blocksSize; + + /** + * The index of the oldest submitted audio block. + */ + uint8 _oldestBlockIndex; + + /** + * The index of the newest submitted audio block. + */ + uint8 _newestBlockIndex; + + /** + * The offset used when sending packets to the + * audio stream. + */ + int _startOffset; + + /** + * The status of robot audio playback. + */ + RobotAudioStatus _status; + + /** + * Frees all audio blocks in the `_blocks` list. + */ + void freeAudioBlocks(); }; - struct RobotHeader { - // 6 bytes, identifier bytes - uint16 version; - uint16 audioChunkSize; - uint16 audioSilenceSize; - // 2 bytes, unknown - uint16 frameCount; - uint16 paletteDataSize; - uint16 unkChunkDataSize; - // 5 bytes, unknown - byte hasSound; - // 34 bytes, unknown - } _header; - - void readHeaderChunk(); - void readFrameSizesChunk(); - - Common::Point _pos; - bool _isBigEndian; - uint32 *_frameTotalSize; - - Common::SeekableSubReadStreamEndian *_fileStream; -}; + /** + * Whether or not this robot animation has + * an audio track. + */ + bool _hasAudio; + + /** + * The audio list for the current robot. + */ + AudioList _audioList; + + /** + * The size, in bytes, of a block of audio data, + * excluding the audio block header. + */ + uint16 _audioBlockSize; + + /** + * The expected size of a block of audio data, + * in bytes, excluding the audio block header. + */ + int16 _expectedAudioBlockSize; + + /** + * The number of compressed audio bytes that are + * needed per frame to fill the audio buffer + * without causing audio to drop out. + */ + int16 _audioRecordInterval; + + /** + * If true, primer audio buffers should be filled + * with silence instead of trying to read buffers + * from the Robot data. + */ + uint16 _primerZeroCompressFlag; + + /** + * The size, in bytes, of the primer audio in the + * Robot, including any extra alignment padding. + */ + uint16 _primerReservedSize; + + /** + * The combined size, in bytes, of the even and odd + * primer channels. + */ + int32 _totalPrimerSize; + + /** + * The absolute offset of the primer audio data in + * the robot data stream. + */ + int32 _primerPosition; + + /** + * The size, in bytes, of the even primer. + */ + int32 _evenPrimerSize; + + /** + * The size, in bytes, of the odd primer. + */ + int32 _oddPrimerSize; + + /** + * The absolute position in the audio stream of + * the first audio packet. + */ + int32 _firstAudioRecordPosition; -} // End of namespace Sci + /** + * A temporary buffer used to hold one frame of + * raw (DPCM-compressed) audio when reading audio + * records from the robot stream. + */ + byte *_audioBuffer; + /** + * The next tick count when AV sync should be + * checked and framerate adjustments made, if + * necessary. + */ + uint32 _checkAudioSyncTime; + + /** + * Primes the audio buffer with the first frame + * of audio data. + * + * @note `primeAudio` was `InitAudio` in SSCI + */ + bool primeAudio(const uint32 startTick); + + /** + * Reads primer data from the robot data stream + * and puts it into the given buffers. + */ + bool readPrimerData(byte *outEvenBuffer, byte *outOddBuffer); + + /** + * Reads audio data for the given frame number + * into the given buffer. + * + * @param outAudioPosition The position of the + * audio, in compressed bytes, in the data stream. + * @param outAudioSize The size of the audio data, + * in compressed bytes. + */ + bool readAudioDataFromRecord(const int frameNo, byte *outBuffer, int &outAudioPosition, int &outAudioSize); + + /** + * Submits part of the audio packet of the given + * frame to the audio list, starting `startPosition` + * bytes into the audio. + */ + bool readPartialAudioRecordAndSubmit(const int startFrame, const int startPosition); + +#pragma mark - +#pragma mark Rendering +public: + /** + * Puts the current dimensions of the robot, in game script + * coordinates, into the given rect, and returns the total + * number of frames in the robot animation. + */ + uint16 getFrameSize(Common::Rect &outRect) const; + + /** + * Pumps the robot player for the next frame of video. + * This is the main rendering function. + */ + void doRobot(); + + /** + * Submits any outstanding audio blocks that should + * be added to the queue before the robot frame + * becomes visible. + */ + void frameAlmostVisible(); + + /** + * Evaluates frame drift and makes modifications to + * the player in order to ensure that future frames + * will arrive on time. + */ + void frameNowVisible(); + + /** + * Scales a vertically compressed cel to its original + * uncompressed dimensions. + */ + void expandCel(byte *target, const byte* source, const int16 celWidth, const int16 celHeight) const; + + /** + * Sets the visual priority of the robot. + * @see Plane::_priority + */ + void setPriority(const int16 newPriority); + +private: + enum CompressionType { + kCompressionLZS = 0, + kCompressionNone = 2 + }; + + /** + * Describes the state of a Robot video cel. + */ + struct CelHandleInfo { + /** + * The persistence level of Robot cels. + */ + enum CelHandleLifetime { + kNoCel = 0, + kFrameLifetime = 1, + kRobotLifetime = 2 + }; + + /** + * A reg_t pointer to an in-memory + * bitmap containing the cel. + */ + reg_t bitmapId; + + /** + * The lifetime of the cel, either just + * for this frame or for the entire + * duration of the robot playback. + */ + CelHandleLifetime status; + + /** + * The size, in pixels, of the decompressed + * cel. + */ + int area; + + CelHandleInfo() : bitmapId(NULL_REG), status(kNoCel), area(0) {} + }; + + typedef Common::Array<ScreenItem *> RobotScreenItemList; + typedef Common::Array<CelHandleInfo> CelHandleList; + typedef Common::Array<int> VideoSizeList; + typedef Common::Array<uint> MaxCelAreaList; + typedef Common::Array<reg_t> FixedCelsList; + typedef Common::Array<Common::Point> CelPositionsList; + typedef Common::Array<byte> ScratchMemory; + + /** + * Renders a version 5/6 robot frame. + */ + void doVersion5(const bool shouldSubmitAudio = true); + + /** + * Creates screen items for a version 5/6 robot. + */ + void createCels5(const byte *rawVideoData, const int16 numCels, const bool usePalette); + + /** + * Creates a single screen item for a cel in a + * version 5/6 robot. + * + * Returns the size, in bytes, of the raw cel data. + */ + uint32 createCel5(const byte *rawVideoData, const int16 screenItemIndex, const bool usePalette); + + /** + * Preallocates memory for the next `numCels` cels + * in the robot data stream. + */ + void preallocateCelMemory(const byte *rawVideoData, const int16 numCels); + + /** + * The decompressor for LZS-compressed cels. + */ + DecompressorLZS _decompressor; + + /** + * The origin of the robot animation, in screen + * coordinates. + */ + Common::Point _position; + + /** + * Global scaling applied to the robot. + */ + ScaleInfo _scaleInfo; + + /** + * The native resolution of the robot. + */ + int16 _xResolution, _yResolution; + + /** + * Whether or not the coordinates read from robot + * data are high resolution. + */ + bool _isHiRes; + + /** + * The maximum number of cels that will be rendered + * on any given frame in this robot. Used for + * preallocation of cel memory. + */ + int16 _maxCelsPerFrame; + + /** + * The maximum areas, in pixels, for each of + * the fixed cels in the robot. Used for + * preallocation of cel memory. + */ + MaxCelAreaList _maxCelArea; + + /** + * The hunk palette to use when rendering the + * current frame, if the `usePalette` flag was set + * in the robot header. + */ + uint8 *_rawPalette; + + /** + * A list of the raw video data sizes, in bytes, + * for each frame of the robot. + */ + VideoSizeList _videoSizes; + + /** + * A list of cels that will be present for the + * entire duration of the robot animation. + */ + FixedCelsList _fixedCels; + + /** + * A list of handles for each cel in the current + * frame. + */ + CelHandleList _celHandles; + + /** + * Scratch memory used to temporarily store + * decompressed cel data for vertically squashed + * cels. + */ + ScratchMemory _celDecompressionBuffer; + + /** + * The size, in bytes, of the squashed cel + * decompression buffer. + */ + int _celDecompressionArea; + + /** + * If true, the robot just started playing and + * is awaiting output for the first frame. + */ + bool _syncFrame; + + /** + * Scratch memory used to store the compressed robot + * video data for the current frame. + */ + ScratchMemory _doVersion5Scratch; + + /** + * When set to a non-negative value, forces the next + * call to doRobot to render the given frame number + * instead of whatever frame would have normally been + * rendered. + */ + mutable int _cueForceShowFrame; + + /** + * The plane where the robot animation will be drawn. + */ + Plane *_plane; + + /** + * A list of pointers to ScreenItems used by the robot. + */ + RobotScreenItemList _screenItemList; + + /** + * The positions of the various screen items in this + * robot, in screen coordinates. + */ + Common::Array<int16> _screenItemX, _screenItemY; + + /** + * The raw position values from the cel header for + * each screen item currently on-screen. + */ + Common::Array<int16> _originalScreenItemX, _originalScreenItemY; + + /** + * The duration of the current robot, in frames. + */ + uint16 _numFramesTotal; + + /** + * The screen priority of the video. + * @see ScreenItem::_priority + */ + int16 _priority; + + /** + * The amount of visual vertical compression applied + * to the current cel. A value of 100 means no + * compression; a value above 100 indicates how much + * the cel needs to be scaled along the y-axis to + * return to its original dimensions. + */ + uint8 _verticalScaleFactor; +}; +} // end of namespace Sci #endif diff --git a/engines/titanic/carry/bowl_ear.cpp b/engines/titanic/carry/bowl_ear.cpp index bb5172e580..852a77899a 100644 --- a/engines/titanic/carry/bowl_ear.cpp +++ b/engines/titanic/carry/bowl_ear.cpp @@ -24,6 +24,13 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CBowlEar, CEar) + ON_MESSAGE(PETGainedObjectMsg) + ON_MESSAGE(ReplaceBowlAndNutsMsg) + ON_MESSAGE(NutPuzzleMsg) + ON_MESSAGE(MouseDragStartMsg) +END_MESSAGE_MAP() + void CBowlEar::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); CEar::save(file, indent); @@ -34,4 +41,28 @@ void CBowlEar::load(SimpleFile *file) { CEar::load(file); } +bool CBowlEar::PETGainedObjectMsg(CPETGainedObjectMsg *msg) { + CBowlStateChangeMsg changeMsg(3); + changeMsg.execute("ParrotNutBowlActor"); + + return CEar::PETGainedObjectMsg(msg); +} + +bool CBowlEar::ReplaceBowlAndNutsMsg(CReplaceBowlAndNutsMsg *msg) { + setVisible(false); + return true; +} + +bool CBowlEar::NutPuzzleMsg(CNutPuzzleMsg *msg) { + if (msg->_value == "BowlUnlocked") + _fieldE0 = 1; + + return true; +} + +bool CBowlEar::MouseDragStartMsg(CMouseDragStartMsg *msg) { + setVisible(true); + return CEar::MouseDragStartMsg(msg); +} + } // End of namespace Titanic diff --git a/engines/titanic/carry/bowl_ear.h b/engines/titanic/carry/bowl_ear.h index 4f2fbea478..d78092f6d7 100644 --- a/engines/titanic/carry/bowl_ear.h +++ b/engines/titanic/carry/bowl_ear.h @@ -28,6 +28,11 @@ namespace Titanic { class CBowlEar : public CEar { + DECLARE_MESSAGE_MAP; + bool PETGainedObjectMsg(CPETGainedObjectMsg *msg); + bool ReplaceBowlAndNutsMsg(CReplaceBowlAndNutsMsg *msg); + bool NutPuzzleMsg(CNutPuzzleMsg *msg); + bool MouseDragStartMsg(CMouseDragStartMsg *msg); public: CLASSDEF; diff --git a/engines/titanic/carry/carry_parrot.cpp b/engines/titanic/carry/carry_parrot.cpp index cf96204122..8a453e348c 100644 --- a/engines/titanic/carry/carry_parrot.cpp +++ b/engines/titanic/carry/carry_parrot.cpp @@ -111,7 +111,7 @@ bool CCarryParrot::MouseDragEndMsg(CMouseDragEndMsg *msg) { if (compareViewNameTo("ParrotLobby.Node 1.N")) { if (msg->_mousePos.x >= 75 && msg->_mousePos.x <= 565 && - !CParrot::_v2 && !CCage::_v2) { + !CParrot::_v2 && !CCage::_open) { setVisible(false); _fieldE0 = 0; CTreeItem *perchedParrot = findUnder(getRoot(), "PerchedParrot"); diff --git a/engines/titanic/carry/central_core.cpp b/engines/titanic/carry/central_core.cpp index a50c95abbc..e210b34cbe 100644 --- a/engines/titanic/carry/central_core.cpp +++ b/engines/titanic/carry/central_core.cpp @@ -21,9 +21,16 @@ */ #include "titanic/carry/central_core.h" +#include "titanic/npcs/parrot.h" namespace Titanic { +BEGIN_MESSAGE_MAP(CCentralCore, CBrain) + ON_MESSAGE(UseWithOtherMsg) + ON_MESSAGE(DropZoneLostObjectMsg) + ON_MESSAGE(DropZoneGotObjectMsg) +END_MESSAGE_MAP() + void CCentralCore::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); CBrain::save(file, indent); @@ -34,4 +41,53 @@ void CCentralCore::load(SimpleFile *file) { CBrain::load(file); } +bool CCentralCore::UseWithOtherMsg(CUseWithOtherMsg *msg) { + CString name = msg->_other->getName(); + if (name == "HammerDispensorButton") { + CPuzzleSolvedMsg solvedMsg; + solvedMsg.execute("BigHammer"); + } else if (name == "SpeechCentre") { + CShowTextMsg textMsg("This does not reach."); + textMsg.execute("PET"); + } + + return CBrain::UseWithOtherMsg(msg); +} + +bool CCentralCore::DropZoneLostObjectMsg(CDropZoneLostObjectMsg *msg) { + CString name = msg->_object->getName(); + if (name == "PerchCoreHolder") { + CParrot::_v2 = 1; + if (isEquals("CentralCore")) + CParrot::_v5 = 0; + + CActMsg actMsg("LosePerch"); + actMsg.execute("ParrotLobbyController"); + } else if (name == "PerchHolder") { + CActMsg actMsg("LoseStick"); + actMsg.execute("ParrotLobbyController"); + } + + return true; +} + +bool CCentralCore::DropZoneGotObjectMsg(CDropZoneGotObjectMsg *msg) { + CString name = msg->_object->getName(); + if (name == "PerchCoreHolder") { + if (isEquals("CentralCore")) { + CParrot::_v5 = 1; + CActMsg actMsg("CoreReplaced"); + actMsg.execute("ParrotCage"); + } + + CActMsg actMsg("GainPerch"); + actMsg.execute("ParrotLobbyController"); + } else if (name == "PerchHolder") { + CActMsg actMsg("GainStick"); + actMsg.execute("ParrotLobbyController"); + } + + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/carry/central_core.h b/engines/titanic/carry/central_core.h index 9d7bef2c13..cc5d9c2f95 100644 --- a/engines/titanic/carry/central_core.h +++ b/engines/titanic/carry/central_core.h @@ -28,6 +28,10 @@ namespace Titanic { class CCentralCore : public CBrain { + DECLARE_MESSAGE_MAP; + bool UseWithOtherMsg(CUseWithOtherMsg *msg); + bool DropZoneLostObjectMsg(CDropZoneLostObjectMsg *msg); + bool DropZoneGotObjectMsg(CDropZoneGotObjectMsg *msg); public: CLASSDEF; diff --git a/engines/titanic/carry/chicken.h b/engines/titanic/carry/chicken.h index 65fe30fd81..e64ae458a4 100644 --- a/engines/titanic/carry/chicken.h +++ b/engines/titanic/carry/chicken.h @@ -41,7 +41,7 @@ class CChicken : public CCarry { bool MouseDragEndMsg(CMouseDragEndMsg *msg); bool PETObjectStateMsg(CPETObjectStateMsg *msg); bool PETLostObjectMsg(CPETLostObjectMsg *msg); -private: +public: static int _v1; public: int _field12C; diff --git a/engines/titanic/carry/crushed_tv.cpp b/engines/titanic/carry/crushed_tv.cpp index a265b611a9..486537d28e 100644 --- a/engines/titanic/carry/crushed_tv.cpp +++ b/engines/titanic/carry/crushed_tv.cpp @@ -76,5 +76,4 @@ bool CCrushedTV::MouseDragStartMsg(CMouseDragStartMsg *msg) { return CCarry::MouseDragStartMsg(msg); } - } // End of namespace Titanic diff --git a/engines/titanic/carry/ear.cpp b/engines/titanic/carry/ear.cpp index 8d85e247f7..a2234bc6dc 100644 --- a/engines/titanic/carry/ear.cpp +++ b/engines/titanic/carry/ear.cpp @@ -21,9 +21,15 @@ */ #include "titanic/carry/ear.h" +#include "titanic/game/head_slot.h" namespace Titanic { +BEGIN_MESSAGE_MAP(CEar, CHeadPiece) + ON_MESSAGE(ActMsg) + ON_MESSAGE(UseWithOtherMsg) +END_MESSAGE_MAP() + CEar::CEar() : CHeadPiece() { } @@ -37,4 +43,25 @@ void CEar::load(SimpleFile *file) { CHeadPiece::load(file); } +bool CEar::ActMsg(CActMsg *msg) { + if (msg->_action == "MusicSolved") + _fieldE0 = true; + return true; +} + +bool CEar::UseWithOtherMsg(CUseWithOtherMsg *msg) { + CHeadSlot *slot = dynamic_cast<CHeadSlot *>(msg->_other); + if (slot) { + setVisible(false); + petMoveToHiddenRoom(); + setPosition(Point(0, 0)); + + CAddHeadPieceMsg addMsg(getName()); + if (addMsg._value != "NULL") + addMsg.execute(addMsg._value == "Ear1" ? "Ear1Slot" : "Ear2Slot"); + } + + return CCarry::UseWithOtherMsg(msg); +} + } // End of namespace Titanic diff --git a/engines/titanic/carry/ear.h b/engines/titanic/carry/ear.h index edef873d35..a357f46bbf 100644 --- a/engines/titanic/carry/ear.h +++ b/engines/titanic/carry/ear.h @@ -28,6 +28,9 @@ namespace Titanic { class CEar : public CHeadPiece { + DECLARE_MESSAGE_MAP; + bool ActMsg(CActMsg *msg); + bool UseWithOtherMsg(CUseWithOtherMsg *msg); public: CLASSDEF; CEar(); diff --git a/engines/titanic/carry/head_piece.cpp b/engines/titanic/carry/head_piece.cpp index ae709644a0..34850488a7 100644 --- a/engines/titanic/carry/head_piece.cpp +++ b/engines/titanic/carry/head_piece.cpp @@ -24,13 +24,19 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CHeadPiece, CCarry) + ON_MESSAGE(SenseWorkingMsg) + ON_MESSAGE(PETGainedObjectMsg) + ON_MESSAGE(MouseDragStartMsg) +END_MESSAGE_MAP() + CHeadPiece::CHeadPiece() : CCarry(), _string6("Not Working"), - _field12C(0), _field13C(0) { + _flag(0), _field13C(false) { } void CHeadPiece::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); - file->writeNumberLine(_field12C, indent); + file->writeNumberLine(_flag, indent); file->writeQuotedLine(_string6, indent); file->writeNumberLine(_field13C, indent); @@ -39,11 +45,49 @@ void CHeadPiece::save(SimpleFile *file, int indent) { void CHeadPiece::load(SimpleFile *file) { file->readNumber(); - _field12C = file->readNumber(); + _flag = file->readNumber(); _string6 = file->readString(); _field13C = file->readNumber(); CCarry::load(file); } +bool CHeadPiece::SenseWorkingMsg(CSenseWorkingMsg *msg) { + _string6 = msg->_value; + return true; +} + +bool CHeadPiece::PETGainedObjectMsg(CPETGainedObjectMsg *msg) { + _visibleFrame = 1; + if (!_field13C) { + stateInc38(); + _field13C = true; + } + + return true; +} + +bool CHeadPiece::MouseDragStartMsg(CMouseDragStartMsg *msg) { + if (!checkPoint(msg->_mousePos, false, true)) { + return false; + } else if (!_fieldE0) { + return true; + } + + if (_flag) { + setVisible(true); + moveToView(); + setPosition(Point(msg->_mousePos.x - _bounds.width() / 2, + msg->_mousePos.y - _bounds.height() / 2)); + + CTakeHeadPieceMsg takeMsg(getName()); + if (takeMsg._value != "NULL") + takeMsg.execute("TitaniaControl"); + + _flag = false; + } + + return CCarry::MouseDragStartMsg(msg); +} + } // End of namespace Titanic diff --git a/engines/titanic/carry/head_piece.h b/engines/titanic/carry/head_piece.h index 05ac772853..a76496072b 100644 --- a/engines/titanic/carry/head_piece.h +++ b/engines/titanic/carry/head_piece.h @@ -24,14 +24,19 @@ #define TITANIC_HEAD_PIECE_H #include "titanic/carry/carry.h" +#include "titanic/messages/pet_messages.h" namespace Titanic { class CHeadPiece : public CCarry { + DECLARE_MESSAGE_MAP; + bool SenseWorkingMsg(CSenseWorkingMsg *msg); + bool PETGainedObjectMsg(CPETGainedObjectMsg *msg); + bool MouseDragStartMsg(CMouseDragStartMsg *msg); private: - int _field12C; + bool _flag; CString _string6; - int _field13C; + bool _field13C; public: CLASSDEF; CHeadPiece(); diff --git a/engines/titanic/carry/hose.h b/engines/titanic/carry/hose.h index ebd45860e8..77ab437b8b 100644 --- a/engines/titanic/carry/hose.h +++ b/engines/titanic/carry/hose.h @@ -34,10 +34,10 @@ struct CHoseStatics { class CHose : public CCarry { protected: - static CHoseStatics *_statics; - CString _string6; public: + static CHoseStatics *_statics; +public: CLASSDEF; CHose(); static void init(); diff --git a/engines/titanic/carry/phonograph_ear.cpp b/engines/titanic/carry/phonograph_ear.cpp index ceb71babd2..9cd461d7e0 100644 --- a/engines/titanic/carry/phonograph_ear.cpp +++ b/engines/titanic/carry/phonograph_ear.cpp @@ -24,6 +24,12 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CPhonographEar, CEar) + ON_MESSAGE(CorrectMusicPlayedMsg) + ON_MESSAGE(PETGainedObjectMsg) + ON_MESSAGE(TimerMsg) +END_MESSAGE_MAP() + void CPhonographEar::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); file->writeNumberLine(_field140, indent); @@ -36,4 +42,24 @@ void CPhonographEar::load(SimpleFile *file) { CEar::load(file); } +bool CPhonographEar::CorrectMusicPlayedMsg(CCorrectMusicPlayedMsg *msg) { + _fieldE0 = true; + return true; +} + +bool CPhonographEar::PETGainedObjectMsg(CPETGainedObjectMsg *msg) { + if (_field140) { + _field140 = false; + addTimer(1000); + } + + return PETGainedObjectMsg(msg); +} + +bool CPhonographEar::TimerMsg(CTimerMsg *msg) { + CVisibleMsg visibleMsg; + visibleMsg.execute("Replacement Phonograph Ear"); + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/carry/phonograph_ear.h b/engines/titanic/carry/phonograph_ear.h index 582db9f7ef..b5db015f90 100644 --- a/engines/titanic/carry/phonograph_ear.h +++ b/engines/titanic/carry/phonograph_ear.h @@ -28,11 +28,15 @@ namespace Titanic { class CPhonographEar : public CEar { + DECLARE_MESSAGE_MAP; + bool CorrectMusicPlayedMsg(CCorrectMusicPlayedMsg *msg); + bool PETGainedObjectMsg(CPETGainedObjectMsg *msg); + bool TimerMsg(CTimerMsg *msg); private: - int _field140; + bool _field140; public: CLASSDEF; - CPhonographEar() : CEar(), _field140(1) {} + CPhonographEar() : CEar(), _field140(true) {} /** * Save the data for the class to file diff --git a/engines/titanic/core/click_responder.cpp b/engines/titanic/core/click_responder.cpp index f9694557df..9a0e0de7ab 100644 --- a/engines/titanic/core/click_responder.cpp +++ b/engines/titanic/core/click_responder.cpp @@ -24,20 +24,33 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CClickResponder, CGameObject) + ON_MESSAGE(MouseButtonDownMsg) +END_MESSAGE_MAP() + void CClickResponder::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); - file->writeQuotedLine(_string1, indent); - file->writeQuotedLine(_string2, indent); + file->writeQuotedLine(_message, indent); + file->writeQuotedLine(_soundName, indent); CGameObject::save(file, indent); } void CClickResponder::load(SimpleFile *file) { file->readNumber(); - _string1 = file->readString(); - _string2 = file->readString(); + _message = file->readString(); + _soundName = file->readString(); CGameObject::load(file); } +bool CClickResponder::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { + if (!_soundName.empty()) + playSound(_soundName); + if (!_message.empty()) + petDisplayMessage(_message); + + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/core/click_responder.h b/engines/titanic/core/click_responder.h index 78381b9948..40f22d7906 100644 --- a/engines/titanic/core/click_responder.h +++ b/engines/titanic/core/click_responder.h @@ -28,8 +28,10 @@ namespace Titanic { class CClickResponder : public CGameObject { + DECLARE_MESSAGE_MAP; + bool MouseButtonDownMsg(CMouseButtonDownMsg *msg); protected: - CString _string1, _string2; + CString _message, _soundName; public: CLASSDEF; diff --git a/engines/titanic/core/game_object.cpp b/engines/titanic/core/game_object.cpp index 12d4c5603a..a61dd17003 100644 --- a/engines/titanic/core/game_object.cpp +++ b/engines/titanic/core/game_object.cpp @@ -1442,7 +1442,7 @@ void CGameObject::resetMail() { mailMan->resetValue(); } -int CGameObject::getNewRandomNumber(int max, int *oldVal) { +int CGameObject::getRandomNumber(int max, int *oldVal) { if (oldVal) { int startingVal = *oldVal; while (*oldVal == startingVal && max > 0) @@ -1608,10 +1608,9 @@ void CGameObject::starFn1(int v) { starControl->fn1(v); } -void CGameObject::starFn2() { +bool CGameObject::starFn2() { CStarControl *starControl = getStarControl(); - if (starControl) - starControl->fn4(); + return starControl ? starControl->fn4() : false; } /*------------------------------------------------------------------------*/ diff --git a/engines/titanic/core/game_object.h b/engines/titanic/core/game_object.h index cdb5f6906a..0749bde5f2 100644 --- a/engines/titanic/core/game_object.h +++ b/engines/titanic/core/game_object.h @@ -531,7 +531,7 @@ protected: /** * Gets a new random number */ - int getNewRandomNumber(int max, int *oldVal = nullptr); + int getRandomNumber(int max, int *oldVal = nullptr); public: Rect _bounds; bool _isMail; @@ -884,7 +884,7 @@ public: CStarControl *getStarControl() const; void starFn1(int v); - void starFn2(); + bool starFn2(); /*--- CTrueTalkManager Methods ---*/ diff --git a/engines/titanic/core/named_item.cpp b/engines/titanic/core/named_item.cpp index 6eafbf8c8b..9c4c28d04d 100644 --- a/engines/titanic/core/named_item.cpp +++ b/engines/titanic/core/named_item.cpp @@ -51,11 +51,11 @@ void CNamedItem::load(SimpleFile *file) { CTreeItem::load(file); } -int CNamedItem::compareTo(const CString &name, int maxLen) const { +bool CNamedItem::isEquals(const CString &name, int maxLen) const { if (maxLen) { - return getName().left(maxLen).compareToIgnoreCase(name); + return getName().left(maxLen).compareToIgnoreCase(name) == 0; } else { - return getName().compareToIgnoreCase(name); + return getName().compareToIgnoreCase(name) == 0; } } diff --git a/engines/titanic/core/named_item.h b/engines/titanic/core/named_item.h index acd59f344e..9ee3d490ae 100644 --- a/engines/titanic/core/named_item.h +++ b/engines/titanic/core/named_item.h @@ -59,9 +59,9 @@ public: virtual const CString getName() const { return _name; } /** - * Compares the name of the item to a passed name + * Returns true if the item's name matches a passed name */ - virtual int compareTo(const CString &name, int maxLen = 0) const; + virtual bool isEquals(const CString &name, int maxLen = 0) const; /** * Find a parent node for the item diff --git a/engines/titanic/core/saveable_object.cpp b/engines/titanic/core/saveable_object.cpp index 62cee47045..5fc2d7e738 100644 --- a/engines/titanic/core/saveable_object.cpp +++ b/engines/titanic/core/saveable_object.cpp @@ -92,7 +92,7 @@ #include "titanic/game/bar_menu.h" #include "titanic/game/bar_menu_button.h" #include "titanic/game/belbot_get_light.h" -#include "titanic/game/bilge_succubus.h" +#include "titanic/npcs/bilge_succubus.h" #include "titanic/game/bomb.h" #include "titanic/game/bottom_of_well_monitor.h" #include "titanic/game/bowl_unlocker.h" @@ -103,7 +103,6 @@ #include "titanic/game/broken_pellerator.h" #include "titanic/game/broken_pellerator_froz.h" #include "titanic/game/cage.h" -#include "titanic/game/call_pellerator.h" #include "titanic/game/captains_wheel.h" #include "titanic/game/cdrom.h" #include "titanic/game/cdrom_computer.h" @@ -228,7 +227,7 @@ #include "titanic/game/parrot/parrot_nut_bowl_actor.h" #include "titanic/game/parrot/parrot_nut_eater.h" #include "titanic/game/parrot/parrot_perch_holder.h" -#include "titanic/game/parrot/parrot_succubus.h" +#include "titanic/npcs/parrot_succubus.h" #include "titanic/game/parrot/parrot_trigger.h" #include "titanic/game/parrot/player_meets_parrot.h" #include "titanic/game/pet/pet.h" @@ -333,6 +332,7 @@ #include "titanic/messages/pet_messages.h" #include "titanic/messages/service_elevator_door.h" +#include "titanic/moves/call_pellerator.h" #include "titanic/moves/enter_bomb_room.h" #include "titanic/moves/enter_bridge.h" #include "titanic/moves/enter_exit_first_class_state.h" @@ -757,7 +757,7 @@ DEFFN(CAutoSoundEvent); DEFFN(CBilgeAutoSoundEvent); DEFFN(CBilgeDispensorEvent); DEFFN(CBodyInBilgeRoomMsg); -DEFFN(CBowlStateChange); +DEFFN(CBowlStateChangeMsg); DEFFN(CCarryObjectArrivedMsg); DEFFN(CChangeMusicMsg); DEFFN(CChangeSeasonMsg); @@ -1344,7 +1344,7 @@ void CSaveableObject::initClassList() { ADDFN(CBilgeAutoSoundEvent, CAutoSoundEvent); ADDFN(CBilgeDispensorEvent, CAutoSoundEvent); ADDFN(CBodyInBilgeRoomMsg, CMessage); - ADDFN(CBowlStateChange, CMessage); + ADDFN(CBowlStateChangeMsg, CMessage); ADDFN(CCarryObjectArrivedMsg, CMessage); ADDFN(CChangeMusicMsg, CMessage); ADDFN(CChangeSeasonMsg, CMessage); diff --git a/engines/titanic/core/tree_item.h b/engines/titanic/core/tree_item.h index 45ce5164ac..b2d40daab9 100644 --- a/engines/titanic/core/tree_item.h +++ b/engines/titanic/core/tree_item.h @@ -125,6 +125,11 @@ public: virtual const CString getName() const { return CString(); } /** + * Returns true if the item's name matches a passed name + */ + virtual bool isEquals(const CString &name, int maxLen = 0) const { return false; } + + /** * Compares the name of the item to a passed name */ virtual int compareTo(const CString &name, int maxLen = 0) const { return false; } diff --git a/engines/titanic/game/belbot_get_light.cpp b/engines/titanic/game/belbot_get_light.cpp index 3e678a8a0c..2cc4c3ae19 100644 --- a/engines/titanic/game/belbot_get_light.cpp +++ b/engines/titanic/game/belbot_get_light.cpp @@ -24,6 +24,13 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CBelbotGetLight, CGameObject) + ON_MESSAGE(ActMsg) + ON_MESSAGE(MovieEndMsg) + ON_MESSAGE(MovieFrameMsg) + ON_MESSAGE(EnterViewMsg) +END_MESSAGE_MAP() + void CBelbotGetLight::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); file->writeQuotedLine(_value, indent); @@ -36,4 +43,36 @@ void CBelbotGetLight::load(SimpleFile *file) { CGameObject::load(file); } +bool CBelbotGetLight::ActMsg(CActMsg *msg) { + if (msg->_action == "BellbotGetLight") { + _value = getFullViewName(); + lockMouse(); + changeView("1stClassState.Node 11.N", ""); + } + + return true; +} + +bool CBelbotGetLight::MovieEndMsg(CMovieEndMsg *msg) { + sleep(1000); + changeView(_value, ""); + unlockMouse(); + return true; +} + +bool CBelbotGetLight::MovieFrameMsg(CMovieFrameMsg *msg) { + if (getMovieFrame() == 37) { + CActMsg actMsg("BellbotGetLight"); + actMsg.execute("Eye1"); + } + + return true; +} + +bool CBelbotGetLight::EnterViewMsg(CEnterViewMsg *msg) { + playMovie(MOVIE_NOTIFY_OBJECT); + movieEvent(37); + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/belbot_get_light.h b/engines/titanic/game/belbot_get_light.h index a3aa0f737e..1707ad4793 100644 --- a/engines/titanic/game/belbot_get_light.h +++ b/engines/titanic/game/belbot_get_light.h @@ -28,6 +28,11 @@ namespace Titanic { class CBelbotGetLight : public CGameObject { + DECLARE_MESSAGE_MAP; + bool ActMsg(CActMsg *msg); + bool MovieEndMsg(CMovieEndMsg *msg); + bool MovieFrameMsg(CMovieFrameMsg *msg); + bool EnterViewMsg(CEnterViewMsg *msg); private: CString _value; public: diff --git a/engines/titanic/game/bilge_succubus.cpp b/engines/titanic/game/bilge_succubus.cpp deleted file mode 100644 index ceee3f7740..0000000000 --- a/engines/titanic/game/bilge_succubus.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include "titanic/game/bilge_succubus.h" - -namespace Titanic { - -CBilgeSuccUBus::CBilgeSuccUBus() : CSuccUBus(), _field1DC(0), - _field1E0(0), _field1E4(0), _field1E8(0) { -} - -void CBilgeSuccUBus::save(SimpleFile *file, int indent) { - file->writeNumberLine(1, indent); - file->writeNumberLine(_field1DC, indent); - file->writeNumberLine(_field1E0, indent); - file->writeNumberLine(_field1E4, indent); - file->writeNumberLine(_field1E8, indent); - - CSuccUBus::save(file, indent); -} - -void CBilgeSuccUBus::load(SimpleFile *file) { - file->readNumber(); - _field1DC = file->readNumber(); - _field1E0 = file->readNumber(); - _field1E4 = file->readNumber(); - _field1E8 = file->readNumber(); - - CSuccUBus::load(file); -} - -} // End of namespace Titanic diff --git a/engines/titanic/game/bomb.cpp b/engines/titanic/game/bomb.cpp index 9a08f26ece..b1f3fdc5f7 100644 --- a/engines/titanic/game/bomb.cpp +++ b/engines/titanic/game/bomb.cpp @@ -25,17 +25,64 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CBomb, CBackground) + ON_MESSAGE(StatusChangeMsg) + ON_MESSAGE(EnterViewMsg) + ON_MESSAGE(MouseButtonDownMsg) + ON_MESSAGE(EnterRoomMsg) + ON_MESSAGE(ActMsg) + ON_MESSAGE(TurnOn) + ON_MESSAGE(TimerMsg) + ON_MESSAGE(TrueTalkGetStateValueMsg) + ON_MESSAGE(SetFrameMsg) +END_MESSAGE_MAP() + +static const char *const WAVE_NAMES1[] = { + "z#353.wav", "z#339.wav", "z#325.wav", "z#311.wav", "z#297.wav", + "z#283.wav", "z#269.wav", "z#255.wav", "z#241.wav" +}; + +static const char *const WAVE_NAMES2[] = { + "", "z#352.wav", "z#338.wav", "z#324.wav", "z#310.wav", "z#296.wav", + "z#281.wav", "z#268.wav", "z#254.wav", "z#240.wav", "", "z#351.wav", + "z#337.wav", "z#323.wav", "z#309.wav", "z#295.wav", "z#282.wav", + "z#267.wav", "z#253.wav", "z#239.wav" +}; + +static const char *const WAVE_NAMES3[100] = { + "bombcountdown_c0.wav", "z#355.wav", "z#341.wav", "z#327.wav", "z#313.wav", + "z#299.wav", "z#285.wav", "z#271.wav", "z#257.wav", "z#243.wav", + "z#354.wav", "z#350.wav", "z#349.wav", "z#348.wav", "z#347.wav", + "z#346.wav", "z#345.wav", "z#344.wav", "z#343.wav", "z#342.wav", + "z#340.wav", "z#336.wav", "z#335.wav", "z#334.wav", "z#333.wav", + "z#332.wav", "z#331.wav", "z#330.wav", "z#329.wav", "z#328.wav", + "z#326.wav", "z#322.wav", "z#321.wav", "z#320.wav", "z#319.wav", + "z#318.wav", "z#317.wav", "z#316.wav", "z#315.wav", "z#314.wav", + "z#312.wav", "z#308.wav", "z#307.wav", "z#306.wav", "z#305.wav", + "z#304.wav", "z#303.wav", "z#302.wav", "z#301.wav", "z#300.wav", + "z#298.wav", "z#294.wav", "z#293.wav", "z#292.wav", "z#291.wav", + "z#290.wav", "z#289.wav", "z#288.wav", "z#287.wav", "z#286.wav", + "z#284.wav", "z#280.wav", "z#279.wav", "z#278.wav", "z#277.wav", + "z#276.wav", "z#275.wav", "z#274.wav", "z#273.wav", "z#272.wav", + "z#270.wav", "z#266.wav", "z#265.wav", "z#264.wav", "z#263.wav", + "z#262.wav", "z#261.wav", "z#260.wav", "z#259.wav", "z#258.wav", + "z#256.wav", "z#252.wav", "z#251.wav", "z#250.wav", "z#249.wav", + "z#248.wav", "z#247.wav", "z#246.wav", "z#245.wav", "z#244.wav", + "z#242.wav", "z#238.wav", "z#237.wav", "z#236.wav", "z#235.wav", + "z#234.wav", "z#233.wav", "z#232.wav", "z#231.wav", "z#230.wav", +}; + CBomb::CBomb() : CBackground() { _fieldE0 = 0; _fieldE4 = 0; _fieldE8 = 17; _fieldEC = 9; _fieldF0 = 0; - _fieldF4 = 999; - _fieldF8 = 0; + _countdown = 999; + _soundHandle = 0; _fieldFC = 0; _startingTicks = 0; - _field104 = 60; + _volume = 60; } void CBomb::save(SimpleFile *file, int indent) { @@ -45,11 +92,11 @@ void CBomb::save(SimpleFile *file, int indent) { file->writeNumberLine(_fieldE8, indent); file->writeNumberLine(_fieldEC, indent); file->writeNumberLine(_fieldF0, indent); - file->writeNumberLine(_fieldF4, indent); - file->writeNumberLine(_fieldF8, indent); + file->writeNumberLine(_countdown, indent); + file->writeNumberLine(_soundHandle, indent); file->writeNumberLine(_fieldFC, indent); file->writeNumberLine(_startingTicks, indent); - file->writeNumberLine(_field104, indent); + file->writeNumberLine(_volume, indent); CBackground::save(file, indent); } @@ -61,21 +108,257 @@ void CBomb::load(SimpleFile *file) { _fieldE8 = file->readNumber(); _fieldEC = file->readNumber(); _fieldF0 = file->readNumber(); - _fieldF4 = file->readNumber(); - _fieldF8 = file->readNumber(); + _countdown = file->readNumber(); + _soundHandle = file->readNumber(); _fieldFC = file->readNumber(); _startingTicks = file->readNumber(); - _field104 = file->readNumber(); + _volume = file->readNumber(); CBackground::load(file); } +bool CBomb::StatusChangeMsg(CStatusChangeMsg *msg) { + _fieldE4 += msg->_newStatus; + + if (_fieldE4 == 23) { + startAnimTimer("Disarmed", 2000); + lockMouse(); + } + + _fieldF0 %= 1000; + if (!(_fieldF0 % 20) && _countdown < 995) { + int val = getRandomNumber(5) + 25; + if (_fieldF0 < 20 || _fieldF0 > 80) + val = 28; + + CString name; + switch (val - 25) { + case 0: + name = "z#372.wav"; + break; + case 1: + name = "z#371.wav"; + break; + case 2: + name = "z#370.wav"; + break; + case 3: + name = "z#369.wav"; + break; + case 4: + name = "z#368.wav"; + break; + default: + name = "z#366.wav"; + break; + } + + _soundHandle = queueSound(name, _soundHandle, _volume); + } + + return true; +} + +bool CBomb::EnterViewMsg(CEnterViewMsg *msg) { + _fieldE4 = 2; + return true; +} + +bool CBomb::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { + playSound("z#62.wav"); + + if (_fieldE0) { + stopSound(_soundHandle); + if (_fieldE4 < 23) { + _fieldE8 = MIN(_fieldE8 + 1, 23); + + CString name; + switch (_fieldE8) { + case 18: + name = "z#380.wav"; + break; + case 19: + name = "z#379.wav"; + break; + case 20: + name = "z#377.wav"; + break; + case 21: + name = "z#376.wav"; + break; + case 22: + name = "z#375.wav"; + break; + default: + name = "z#374.wav"; + break; + } + + _soundHandle = queueSound(name, _soundHandle, _volume); + _countdown = 999; + } + } else { + _soundHandle = playSound("z#389.wav", _volume); + _fieldE0 = true; + CActMsg actMsg("Arm Bomb"); + actMsg.execute("EndExplodeShip"); + } + + return true; +} + bool CBomb::EnterRoomMsg(CEnterRoomMsg *msg) { - _fieldE8 = 12; + _fieldE8 = 17; _fieldEC = 9; _fieldF0 = 0; _startingTicks = g_vm->_events->getTicksCount(); return true; } +bool CBomb::ActMsg(CActMsg *msg) { + if (msg->_action == "Hit") { + playSound("z#63.wav"); + stopSound(_soundHandle); + + if (_fieldEC < 17) + ++_fieldEC; + + CString name; + switch (_fieldEC) { + case 10: + name = "z#388.wav"; + break; + case 11: + name = "z#387.wav"; + break; + case 12: + name = "z#386.wav"; + break; + case 13: + name = "z#385.wav"; + break; + case 14: + name = "z#384.wav"; + break; + case 15: + name = "z#383.wav"; + break; + case 16: + name = "z#382.wav"; + break; + default: + name = "z#381.wav"; + break; + } + + _soundHandle = queueSound(name, _soundHandle, _volume); + _countdown = 999; + } + + return true; +} + +bool CBomb::TurnOn(CTurnOn *msg) { + if (!_fieldE0) { + _soundHandle = playSound("z#389.wav", _volume); + _fieldE0 = true; + + CActMsg actMsg("Arm Bomb"); + actMsg.execute("EndExplodeShip"); + addTimer(0); + } + + changeView("Titania.Node 8.W", ""); + CActMsg actMsg("Titania.Node 8.N"); + actMsg.execute("BombNav"); + actMsg.execute("EnterBombRoom"); + + return true; +} + +bool CBomb::TimerMsg(CTimerMsg *msg) { + if (msg->_action == "Disarmed") { + stopSound(_soundHandle); + playSound("z#364.wav", _volume); + + CActMsg actMsg1("Disarm Bomb"); + actMsg1.execute("EndExplodeShip"); + _fieldE0 = false; + CActMsg actMsg2("Titania.Node 5.N"); + actMsg2.execute("BombNav"); + actMsg2.execute("EnterBombNav"); + + changeView("Titania.Node 8.W", ""); + changeView("Titania.Node 13.N", ""); + unlockMouse(); + } + + if (compareRoomNameTo("Titania")) { + if (msg->_actionVal == 1 && getRandomNumber(9) == 0) { + if (!_fieldE0) + return true; + + CParrotSpeakMsg speakMsg("Bomb", "BombCountdown"); + speakMsg.execute("PerchedParrot"); + } + + if (_fieldE0) { + if (isSoundActive(_soundHandle)) { + if (msg->_actionVal == 0) { + addTimer(1, 1000, 0); + } else { + _soundHandle = 0; + int section = _countdown / 100; + int index = _countdown % 100; + + if (_countdown >= 100) { + CString name1 = index ? WAVE_NAMES2[section] : + WAVE_NAMES1[section]; + playSound(name1, _volume); + } + + CString name2 = WAVE_NAMES3[index]; + if (_countdown == 10) { + name2 = "z#229.wav"; + _countdown = 998; + } + + if (_soundHandle > 0) { + _soundHandle = queueSound(name2, _soundHandle, _volume); + } else { + _soundHandle = playSound(name2, _volume); + } + + --_countdown; + addTimer(0, 1000, 0); + } + } else { + addTimer(0, 100, 0); + } + } + } else { + if (_fieldE0) { + --_countdown; + addTimer(6000); + + if (_countdown < 11) + _countdown = getRandomNumber(900) + 50; + } + } + + return true; +} + +bool CBomb::TrueTalkGetStateValueMsg(CTrueTalkGetStateValueMsg *msg) { + if (msg->_stateNum == 10) + msg->_stateVal = _fieldE0; + + return true; +} + +bool CBomb::SetFrameMsg(CSetFrameMsg *msg) { + _volume = msg->_frameNumber; + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/bomb.h b/engines/titanic/game/bomb.h index ab4df16db0..f78c42cff0 100644 --- a/engines/titanic/game/bomb.h +++ b/engines/titanic/game/bomb.h @@ -29,18 +29,27 @@ namespace Titanic { class CBomb : public CBackground { + bool StatusChangeMsg(CStatusChangeMsg *msg); + bool EnterViewMsg(CEnterViewMsg *msg); + bool MouseButtonDownMsg(CMouseButtonDownMsg *msg); bool EnterRoomMsg(CEnterRoomMsg *msg); + bool ActMsg(CActMsg *msg); + bool TurnOn(CTurnOn *msg); + bool TimerMsg(CTimerMsg *msg); + bool TrueTalkGetStateValueMsg(CTrueTalkGetStateValueMsg *msg); + bool SetFrameMsg(CSetFrameMsg *msg); + DECLARE_MESSAGE_MAP; private: int _fieldE0; int _fieldE4; int _fieldE8; int _fieldEC; int _fieldF0; - int _fieldF4; - int _fieldF8; + int _countdown; + int _soundHandle; int _fieldFC; int _startingTicks; - int _field104; + int _volume; public: CLASSDEF; CBomb(); diff --git a/engines/titanic/game/bottom_of_well_monitor.cpp b/engines/titanic/game/bottom_of_well_monitor.cpp index beb2a80ce9..373fe4cbdc 100644 --- a/engines/titanic/game/bottom_of_well_monitor.cpp +++ b/engines/titanic/game/bottom_of_well_monitor.cpp @@ -24,6 +24,13 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CBottomOfWellMonitor, CGameObject) + ON_MESSAGE(ActMsg) + ON_MESSAGE(MouseButtonDownMsg) + ON_MESSAGE(EnterViewMsg) + ON_MESSAGE(LeaveViewMsg) +END_MESSAGE_MAP() + int CBottomOfWellMonitor::_v1; int CBottomOfWellMonitor::_v2; @@ -31,7 +38,7 @@ void CBottomOfWellMonitor::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); file->writeNumberLine(_v1, indent); file->writeNumberLine(_v2, indent); - file->writeNumberLine(_value, indent); + file->writeNumberLine(_flag, indent); CGameObject::save(file, indent); } @@ -39,8 +46,69 @@ void CBottomOfWellMonitor::load(SimpleFile *file) { file->readNumber(); _v1 = file->readNumber(); _v2 = file->readNumber(); - _value = file->readNumber(); + _flag = file->readNumber(); CGameObject::load(file); } +bool CBottomOfWellMonitor::ActMsg(CActMsg *msg) { + if (msg->_action == "TelevisionTaken") { + _v1 = 0; + _cursorId = CURSOR_ARROW; + CVisibleMsg visibleMsg; + visibleMsg.execute("CrushedTV2NE"); + visibleMsg.execute("CrushedTV4SW"); + _cursorId = CURSOR_ARROW; + } else if (msg->_action == "LiftbotHeadTaken") { + _v2 = 0; + _cursorId = CURSOR_ARROW; + CVisibleMsg visibleMsg; + visibleMsg.execute("LiftbotHead2NE"); + visibleMsg.execute("LiftbotHead4SW"); + _cursorId = CURSOR_ARROW; + } else if (msg->_action == "LiftbotHeadTaken") { + _v2 = 1; + CVisibleMsg visibleMsg; + visibleMsg.execute("CrushedTV2NE"); + visibleMsg.execute("CrushedTV4SW"); + _cursorId = CURSOR_MOVE_DOWN1; + } + + return true; +} + +bool CBottomOfWellMonitor::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { + if (!compareTo("BOWTelevisionMonitor")) { + if (_v2) + changeView("BottomOfWell.Node 8.N", ""); + } else { + if (_v1) + changeView("BottomOfWell.Node 7.N", ""); + } + + return true; +} + +bool CBottomOfWellMonitor::EnterViewMsg(CEnterViewMsg *msg) { + if (_flag) { + if (!compareTo("BOWTelevisionMonitor")) { + if (_v2) { + changeView("BottomOfWell.Node 8.N", ""); + _flag = false; + } + } else { + if (_v1) { + changeView("BottomOfWell.Node 7.N", ""); + _flag = false; + } + } + } + + return true; +} + +bool CBottomOfWellMonitor::LeaveViewMsg(CLeaveViewMsg *msg) { + _flag = true; + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/bottom_of_well_monitor.h b/engines/titanic/game/bottom_of_well_monitor.h index 65424aad70..be9ae2c093 100644 --- a/engines/titanic/game/bottom_of_well_monitor.h +++ b/engines/titanic/game/bottom_of_well_monitor.h @@ -28,12 +28,17 @@ namespace Titanic { class CBottomOfWellMonitor : public CGameObject { + DECLARE_MESSAGE_MAP; + bool ActMsg(CActMsg *msg); + bool MouseButtonDownMsg(CMouseButtonDownMsg *msg); + bool EnterViewMsg(CEnterViewMsg *msg); + bool LeaveViewMsg(CLeaveViewMsg *msg); public: static int _v1, _v2; - int _value; + bool _flag; public: CLASSDEF; - CBottomOfWellMonitor() : _value(1) {} + CBottomOfWellMonitor() : _flag(true) {} /** * Save the data for the class to file diff --git a/engines/titanic/game/bowl_unlocker.cpp b/engines/titanic/game/bowl_unlocker.cpp index c3c501dbd6..c4adac34f2 100644 --- a/engines/titanic/game/bowl_unlocker.cpp +++ b/engines/titanic/game/bowl_unlocker.cpp @@ -21,19 +21,58 @@ */ #include "titanic/game/bowl_unlocker.h" +#include "titanic/core/room_item.h" namespace Titanic { +BEGIN_MESSAGE_MAP(CBowlUnlocker, CGameObject) + ON_MESSAGE(NutPuzzleMsg) + ON_MESSAGE(MovieEndMsg) + ON_MESSAGE(MouseButtonDownMsg) + ON_MESSAGE(LeaveViewMsg) +END_MESSAGE_MAP() + void CBowlUnlocker::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); - file->writeNumberLine(_value, indent); + file->writeNumberLine(_bowlUnlocked, indent); CGameObject::save(file, indent); } void CBowlUnlocker::load(SimpleFile *file) { file->readNumber(); - _value = file->readNumber(); + _bowlUnlocked = file->readNumber(); CGameObject::load(file); } +bool CBowlUnlocker::NutPuzzleMsg(CNutPuzzleMsg *msg) { + if (msg->_value == "UnlockBowl") { + setVisible(true); + playMovie(MOVIE_NOTIFY_OBJECT); + } + + return true; +} + +bool CBowlUnlocker::MovieEndMsg(CMovieEndMsg *msg) { + setVisible(false); + _bowlUnlocked = true; + + CNutPuzzleMsg puzzleMsg("BowlUnlocked"); + puzzleMsg.execute(getRoom(), nullptr, MSGFLAG_SCAN); + + playSound("z#47.wav"); + return true; +} + +bool CBowlUnlocker::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { + if (_bowlUnlocked) + msg->execute("Ear1"); + return true; +} + +bool CBowlUnlocker::LeaveViewMsg(CLeaveViewMsg *msg) { + _bowlUnlocked = false; + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/bowl_unlocker.h b/engines/titanic/game/bowl_unlocker.h index 2559ac2c52..b940661904 100644 --- a/engines/titanic/game/bowl_unlocker.h +++ b/engines/titanic/game/bowl_unlocker.h @@ -28,11 +28,16 @@ namespace Titanic { class CBowlUnlocker : public CGameObject { + DECLARE_MESSAGE_MAP; + bool NutPuzzleMsg(CNutPuzzleMsg *msg); + bool MovieEndMsg(CMovieEndMsg *msg); + bool MouseButtonDownMsg(CMouseButtonDownMsg *msg); + bool LeaveViewMsg(CLeaveViewMsg *msg); public: - int _value; + bool _bowlUnlocked; public: CLASSDEF; - CBowlUnlocker() : CGameObject(), _value(0) {} + CBowlUnlocker() : CGameObject(), _bowlUnlocked(false) {} /** * Save the data for the class to file diff --git a/engines/titanic/game/brain_slot.cpp b/engines/titanic/game/brain_slot.cpp index f1963142ac..57521ead88 100644 --- a/engines/titanic/game/brain_slot.cpp +++ b/engines/titanic/game/brain_slot.cpp @@ -21,18 +21,27 @@ */ #include "titanic/game/brain_slot.h" +#include "titanic/core/project_item.h" namespace Titanic { -int CBrainSlot::_v1; -int CBrainSlot::_v2; +BEGIN_MESSAGE_MAP(CBrainSlot, CGameObject) + ON_MESSAGE(SetFrameMsg) + ON_MESSAGE(AddHeadPieceMsg) + ON_MESSAGE(EnterViewMsg) + ON_MESSAGE(ActMsg) + ON_MESSAGE(MouseDragStartMsg) +END_MESSAGE_MAP() + +bool CBrainSlot::_added; +bool CBrainSlot::_woken; void CBrainSlot::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); file->writeNumberLine(_value1, indent); - file->writeQuotedLine(_value2, indent); - file->writeNumberLine(_v1, indent); - file->writeNumberLine(_v2, indent); + file->writeQuotedLine(_target, indent); + file->writeNumberLine(_added, indent); + file->writeNumberLine(_woken, indent); CGameObject::save(file, indent); } @@ -40,11 +49,101 @@ void CBrainSlot::save(SimpleFile *file, int indent) { void CBrainSlot::load(SimpleFile *file) { file->readNumber(); _value1 = file->readNumber(); - _value2 = file->readString(); - _v1 = file->readNumber(); - _v2 = file->readNumber(); + _target = file->readString(); + _added = file->readNumber(); + _woken = file->readNumber(); CGameObject::load(file); } +bool CBrainSlot::SetFrameMsg(CSetFrameMsg *msg) { + loadFrame(msg->_frameNumber); + _value1 = 1; + return true; +} + +bool CBrainSlot::AddHeadPieceMsg(CAddHeadPieceMsg *msg) { + _added = true; + _cursorId = CURSOR_HAND; + CAddHeadPieceMsg addMsg("NULL"); + + if (isEquals("AuditoryCentreSlot")) { + if (msg->_value == "AuditoryCentre") + addMsg._value = "AuditoryCentre"; + } else if (isEquals("SpeechCentreSlot")) { + if (msg->_value == "SpeechCentre") + addMsg._value = "SpeechCentre"; + } else if (isEquals("OlfactoryCentreSlot")) { + if (msg->_value == "OlfactoryCentre") + addMsg._value = "OlfactoryCentre"; + } else if (isEquals("VisionCentreSlot")) { + if (msg->_value == "VisionCentre") + addMsg._value = "VisionCentre"; + } else if (isEquals("CentralCoreSlot")) { + if (msg->_value == "CentralCore") + addMsg._value = "CentralCore"; + } + + if (addMsg._value != "NULL") + addMsg.execute("TitaniaControl"); + + if (addMsg._value == "OlfactoryCentre") + loadFrame(2); + else if (addMsg._value == "AuditoryCentre") + loadFrame(1); + else if (addMsg._value == "SpeechCentre") + loadFrame(3); + else if (addMsg._value == "VisionCentre") + loadFrame(4); + else if (addMsg._value == "CentralCore") { + CActMsg actMsg("Insert Central Core"); + actMsg.execute("CentralCoreSlot"); + } + + _target = msg->_value; + _value1 = 1; + return true; +} + +bool CBrainSlot::EnterViewMsg(CEnterViewMsg *msg) { + if (getName() == "CentralCoreSlot") + loadFrame(21); + if (_woken) + _cursorId = CURSOR_ARROW; + + return true; +} + +bool CBrainSlot::ActMsg(CActMsg *msg) { + if (msg->_action == "Insert Central Core") + playMovie(0, 21, 0); + else if (msg->_action == "Woken") + _woken = true; + + return true; +} + +bool CBrainSlot::MouseDragStartMsg(CMouseDragStartMsg *msg) { + if (!_value1 || _woken || !checkPoint(msg->_mousePos, false, true)) + return false; + + _cursorId = CURSOR_ARROW; + CVisibleMsg visibleMsg(true); + visibleMsg.execute(_target); + CTakeHeadPieceMsg takeMsg(_target); + takeMsg.execute("TitaniaControl"); + + loadFrame(isEquals("CentralCoreSlot") ? 21 : 0); + _value1 = 0; + + CPassOnDragStartMsg passMsg; + passMsg._mousePos = msg->_mousePos; + passMsg.execute(_target); + + msg->_dragItem = getRoot()->findByName(_target); + _added = false; + + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/brain_slot.h b/engines/titanic/game/brain_slot.h index 94b6d7f227..fce9ab02c7 100644 --- a/engines/titanic/game/brain_slot.h +++ b/engines/titanic/game/brain_slot.h @@ -28,11 +28,17 @@ namespace Titanic { class CBrainSlot : public CGameObject { + DECLARE_MESSAGE_MAP; + bool SetFrameMsg(CSetFrameMsg *msg); + bool AddHeadPieceMsg(CAddHeadPieceMsg *msg); + bool EnterViewMsg(CEnterViewMsg *msg); + bool ActMsg(CActMsg *msg); + bool MouseDragStartMsg(CMouseDragStartMsg *msg); public: - static int _v1, _v2; + static bool _added, _woken; public: int _value1; - CString _value2; + CString _target; public: CLASSDEF; CBrainSlot() : CGameObject(), _value1(0) {} diff --git a/engines/titanic/game/bridge_door.cpp b/engines/titanic/game/bridge_door.cpp index 57cdbd23ad..bfa30fd650 100644 --- a/engines/titanic/game/bridge_door.cpp +++ b/engines/titanic/game/bridge_door.cpp @@ -24,6 +24,12 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CBridgeDoor, CGameObject) + ON_MESSAGE(MouseButtonDownMsg) + ON_MESSAGE(StatusChangeMsg) + ON_MESSAGE(MovieEndMsg) +END_MESSAGE_MAP() + void CBridgeDoor::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); CGameObject::save(file, indent); @@ -34,4 +40,23 @@ void CBridgeDoor::load(SimpleFile *file) { CGameObject::load(file); } +bool CBridgeDoor::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { + setVisible(true); + playMovie(0, 6, 0); + changeView("Titania.Node 12.N"); + + return true; +} + +bool CBridgeDoor::StatusChangeMsg(CStatusChangeMsg *msg) { + setVisible(true); + playMovie(7, 0, MOVIE_NOTIFY_OBJECT); + return true; +} + +bool CBridgeDoor::MovieEndMsg(CMovieEndMsg *msg) { + setVisible(false); + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/bridge_door.h b/engines/titanic/game/bridge_door.h index c1872a29be..010a8b8bc0 100644 --- a/engines/titanic/game/bridge_door.h +++ b/engines/titanic/game/bridge_door.h @@ -28,6 +28,10 @@ namespace Titanic { class CBridgeDoor : public CGameObject { + DECLARE_MESSAGE_MAP; + bool MouseButtonDownMsg(CMouseButtonDownMsg *msg); + bool StatusChangeMsg(CStatusChangeMsg *msg); + bool MovieEndMsg(CMovieEndMsg *msg); public: CLASSDEF; diff --git a/engines/titanic/game/bridge_view.cpp b/engines/titanic/game/bridge_view.cpp index 9854969494..466480a64c 100644 --- a/engines/titanic/game/bridge_view.cpp +++ b/engines/titanic/game/bridge_view.cpp @@ -24,16 +24,92 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CBridgeView, CBackground) + ON_MESSAGE(ActMsg) + ON_MESSAGE(MovieEndMsg) +END_MESSAGE_MAP() + void CBridgeView::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); - file->writeNumberLine(_fieldE0, indent); + file->writeNumberLine(_mode, indent); CBackground::save(file, indent); } void CBridgeView::load(SimpleFile *file) { file->readNumber(); - _fieldE0 = file->readNumber(); + _mode = file->readNumber(); CBackground::load(file); } +bool CBridgeView::ActMsg(CActMsg *msg) { + CTurnOn onMsg; + CSetVolumeMsg volumeMsg; + volumeMsg._secondsTransition = 1; + + if (msg->_action == "End") { + _mode = 4; + petLockInput(); + petHide(); + setVisible(true); + playMovie(MOVIE_NOTIFY_OBJECT); + } else if (msg->_action == "Go") { + _mode = 1; + setVisible(true); + volumeMsg._volume = 100; + volumeMsg.execute("EngineSounds"); + onMsg.execute("EngineSounds"); + playMovie(MOVIE_NOTIFY_OBJECT); + } else { + volumeMsg._volume = 50; + volumeMsg.execute("EngineSounds"); + onMsg.execute("EngineSounds"); + + if (msg->_action == "Cruise") { + _mode = 2; + setVisible(true); + playMovie(MOVIE_NOTIFY_OBJECT); + } else if (msg->_action == "GoENd") { + _mode = 3; + setVisible(true); + CChangeMusicMsg musicMsg; + musicMsg._flags = 1; + musicMsg.execute("BridgeAutoMusicPlayer"); + playSound("a#42.wav"); + playMovie(MOVIE_NOTIFY_OBJECT); + } + } + + return true; +} + +bool CBridgeView::MovieEndMsg(CMovieEndMsg *msg) { + CTurnOff offMsg; + offMsg.execute("EngineSounds"); + + switch (_mode) { + case 0: + case 1: + setVisible(false); + dec54(); + break; + + case 2: { + setVisible(false); + CActMsg actMsg("End"); + actMsg.execute("HomeSequence"); + break; + } + + case 3: + setVisible(false); + changeView("TheEnd.Node 3.N"); + break; + + default: + break; + } + + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/bridge_view.h b/engines/titanic/game/bridge_view.h index d7c7c35aa9..45cfa3f4c8 100644 --- a/engines/titanic/game/bridge_view.h +++ b/engines/titanic/game/bridge_view.h @@ -28,11 +28,14 @@ namespace Titanic { class CBridgeView : public CBackground { + DECLARE_MESSAGE_MAP; + bool ActMsg(CActMsg *msg); + bool MovieEndMsg(CMovieEndMsg *msg); public: - int _fieldE0; + int _mode; public: CLASSDEF; - CBridgeView() : CBackground(), _fieldE0(0) {} + CBridgeView() : CBackground(), _mode(0) {} /** * Save the data for the class to file diff --git a/engines/titanic/game/broken_pell_base.cpp b/engines/titanic/game/broken_pell_base.cpp index 59e2b9bca1..02c2d873ac 100644 --- a/engines/titanic/game/broken_pell_base.cpp +++ b/engines/titanic/game/broken_pell_base.cpp @@ -26,7 +26,7 @@ namespace Titanic { EMPTY_MESSAGE_MAP(CBrokenPellBase, CBackground); -int CBrokenPellBase::_v1; +bool CBrokenPellBase::_v1; int CBrokenPellBase::_v2; void CBrokenPellBase::save(SimpleFile *file, int indent) { diff --git a/engines/titanic/game/broken_pell_base.h b/engines/titanic/game/broken_pell_base.h index f63cd0112b..4ca7eddd20 100644 --- a/engines/titanic/game/broken_pell_base.h +++ b/engines/titanic/game/broken_pell_base.h @@ -29,8 +29,8 @@ namespace Titanic { class CBrokenPellBase : public CBackground { DECLARE_MESSAGE_MAP; -private: - static int _v1; +protected: + static bool _v1; static int _v2; int _fieldE0; diff --git a/engines/titanic/game/broken_pellerator.cpp b/engines/titanic/game/broken_pellerator.cpp index d3b204b1e5..8fb7244b7e 100644 --- a/engines/titanic/game/broken_pellerator.cpp +++ b/engines/titanic/game/broken_pellerator.cpp @@ -21,9 +21,17 @@ */ #include "titanic/game/broken_pellerator.h" +#include "titanic/core/view_item.h" namespace Titanic { +BEGIN_MESSAGE_MAP(CBrokenPellerator, CBrokenPellBase) + ON_MESSAGE(MouseButtonDownMsg) + ON_MESSAGE(LeaveViewMsg) + ON_MESSAGE(ActMsg) + ON_MESSAGE(MovieEndMsg) +END_MESSAGE_MAP() + void CBrokenPellerator::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); file->writeQuotedLine(_string2, indent); @@ -44,4 +52,103 @@ void CBrokenPellerator::load(SimpleFile *file) { CBrokenPellBase::load(file); } +bool CBrokenPellerator::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { + if (_v1) { + changeView(_v2 ? _string5 : _string4); + } else { + if (_v2) { + playMovie(28, 43, 0); + } else { + playMovie(0, 14, MOVIE_NOTIFY_OBJECT); + } + + _v1 = true; + } + + return true; +} + +bool CBrokenPellerator::LeaveViewMsg(CLeaveViewMsg *msg) { + CString name = msg->_newView->getNodeViewName(); + if (name == "Node 3.S" || name == "Node 3.N") { + _v1 = false; + loadFrame(0); + } + + return true; +} + +bool CBrokenPellerator::ActMsg(CActMsg *msg) { + if (msg->_action == "PlayerGetsHose") { + _v2 = 1; + loadFrame(43); + + CStatusChangeMsg statusMsg; + statusMsg.execute("PickupHose"); + } else { + _fieldE0 = 0; + bool closeFlag = msg->_action == "Close"; + if (msg->_action == "CloseLeft") { + closeFlag = true; + _fieldE0 = 1; + } + if (msg->_action == "CloseRight") { + closeFlag = true; + _fieldE0 = 2; + } + + if (closeFlag) { + if (_v1) { + _v1 = false; + if (_v2) + playMovie(43, 57, MOVIE_NOTIFY_OBJECT); + else + playMovie(14, 28, MOVIE_NOTIFY_OBJECT); + } else { + switch (_fieldE0) { + case 1: + changeView(_string2); + break; + case 2: + changeView(_string3); + break; + default: + break; + } + + _fieldE0 = 0; + } + } + } + + return true; +} + +bool CBrokenPellerator::MovieEndMsg(CMovieEndMsg *msg) { + if (msg->_endFrame == 14) { + CStatusChangeMsg statusMsg; + statusMsg._newStatus = 1; + statusMsg.execute("PickUpHose"); + } + + if (msg->_endFrame == 28) { + CStatusChangeMsg statusMsg; + statusMsg._newStatus = 0; + statusMsg.execute("PickUpHose"); + } + + switch (_fieldE0) { + case 1: + changeView(_string2); + break; + case 2: + changeView(_string3); + break; + default: + break; + } + + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/broken_pellerator.h b/engines/titanic/game/broken_pellerator.h index 6fbde91053..3b8c3ba587 100644 --- a/engines/titanic/game/broken_pellerator.h +++ b/engines/titanic/game/broken_pellerator.h @@ -28,6 +28,11 @@ namespace Titanic { class CBrokenPellerator : public CBrokenPellBase { + DECLARE_MESSAGE_MAP; + bool MouseButtonDownMsg(CMouseButtonDownMsg *msg); + bool LeaveViewMsg(CLeaveViewMsg *msg); + bool ActMsg(CActMsg *msg); + bool MovieEndMsg(CMovieEndMsg *msg); private: CString _string2; CString _string3; diff --git a/engines/titanic/game/broken_pellerator_froz.cpp b/engines/titanic/game/broken_pellerator_froz.cpp index 4b21ea93d0..690ab76820 100644 --- a/engines/titanic/game/broken_pellerator_froz.cpp +++ b/engines/titanic/game/broken_pellerator_froz.cpp @@ -21,9 +21,17 @@ */ #include "titanic/game/broken_pellerator_froz.h" +#include "titanic/core/view_item.h" namespace Titanic { +BEGIN_MESSAGE_MAP(CBrokenPelleratorFroz, CBrokenPellBase) + ON_MESSAGE(MouseButtonDownMsg) + ON_MESSAGE(LeaveViewMsg) + ON_MESSAGE(ActMsg) + ON_MESSAGE(MovieEndMsg) +END_MESSAGE_MAP() + void CBrokenPelleratorFroz::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); file->writeQuotedLine(_string2, indent); @@ -44,4 +52,99 @@ void CBrokenPelleratorFroz::load(SimpleFile *file) { CBrokenPellBase::load(file); } +bool CBrokenPelleratorFroz::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { + if (_v1) { + changeView(_v2 ? _string5 : _string4); + } else { + _v1 = true; + if (_v2) { + playMovie(0, 13, 0); + } else { + playMovie(43, 55, MOVIE_NOTIFY_OBJECT); + } + } + + return true; +} + +bool CBrokenPelleratorFroz::LeaveViewMsg(CLeaveViewMsg *msg) { + CString name = msg->_newView->getNodeViewName(); + + if (name == "Node 3.S" || name == "Node 3.E") { + _v1 = false; + loadFrame(0); + } + + return true; +} + +bool CBrokenPelleratorFroz::ActMsg(CActMsg *msg) { + if (msg->_action == "PlayerGetsHose") { + _v2 = 1; + CStatusChangeMsg statusMsg; + statusMsg._newStatus = 0; + statusMsg.execute("FPickUpHose"); + } else { + _fieldE0 = 0; + bool closeFlag = msg->_action == "Close"; + if (msg->_action == "CloseLeft") { + closeFlag = true; + _fieldE0 = 1; + } + if (msg->_action == "CloseRight") { + closeFlag = true; + _fieldE0 = 2; + } + + if (closeFlag) { + if (_v1) { + _v1 = false; + if (_v2) + playMovie(29, 42, MOVIE_NOTIFY_OBJECT); + else + playMovie(72, 84, MOVIE_NOTIFY_OBJECT); + } else { + switch (_fieldE0) { + case 1: + changeView(_string2); + break; + case 2: + changeView(_string3); + break; + default: + break; + } + + _fieldE0 = 0; + } + } + } + + return true; +} + +bool CBrokenPelleratorFroz::MovieEndMsg(CMovieEndMsg *msg) { + if (msg->_endFrame == 55) { + CStatusChangeMsg statusMsg; + statusMsg._newStatus = 1; + statusMsg.execute("FPickUpHose"); + } + + if (msg->_endFrame == 84) { + CStatusChangeMsg statusMsg; + statusMsg._newStatus = 0; + statusMsg.execute("FPickUpHose"); + } + + if (_fieldE0 == 1) { + changeView(_string2); + _fieldE0 = 0; + } else if (_fieldE0 == 2) { + changeView(_string3); + _fieldE0 = 0; + } + + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/broken_pellerator_froz.h b/engines/titanic/game/broken_pellerator_froz.h index 1df6d2d0b2..ccdae6ffa8 100644 --- a/engines/titanic/game/broken_pellerator_froz.h +++ b/engines/titanic/game/broken_pellerator_froz.h @@ -28,6 +28,11 @@ namespace Titanic { class CBrokenPelleratorFroz : public CBrokenPellBase { + DECLARE_MESSAGE_MAP; + bool MouseButtonDownMsg(CMouseButtonDownMsg *msg); + bool LeaveViewMsg(CLeaveViewMsg *msg); + bool ActMsg(CActMsg *msg); + bool MovieEndMsg(CMovieEndMsg *msg); private: CString _string2; CString _string3; diff --git a/engines/titanic/game/cage.cpp b/engines/titanic/game/cage.cpp index 7fbc052278..bbac384cea 100644 --- a/engines/titanic/game/cage.cpp +++ b/engines/titanic/game/cage.cpp @@ -21,16 +21,25 @@ */ #include "titanic/game/cage.h" +#include "titanic/npcs/parrot.h" namespace Titanic { +BEGIN_MESSAGE_MAP(CCage, CBackground) + ON_MESSAGE(MouseButtonDownMsg) + ON_MESSAGE(ActMsg) + ON_MESSAGE(MovieEndMsg) + ON_MESSAGE(PreEnterViewMsg) + ON_MESSAGE(MouseMoveMsg) +END_MESSAGE_MAP() + int CCage::_v1; -int CCage::_v2; +bool CCage::_open; void CCage::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); file->writeNumberLine(_v1, indent); - file->writeNumberLine(_v2, indent); + file->writeNumberLine(_open, indent); CBackground::save(file, indent); } @@ -38,9 +47,64 @@ void CCage::save(SimpleFile *file, int indent) { void CCage::load(SimpleFile *file) { file->readNumber(); _v1 = file->readNumber(); - _v2 = file->readNumber(); + _open = file->readNumber(); CBackground::load(file); } +bool CCage::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { + if (CParrot::_v4 && !CParrot::_v5) { + CActMsg actMsg(_open ? "Open" : "Shut"); + actMsg.execute(this); + } + + return true; +} + +bool CCage::ActMsg(CActMsg *msg) { + if (msg->_action == "Shut") { + if (!_open) { + playClip("Shut", MOVIE_STOP_PREVIOUS | MOVIE_NOTIFY_OBJECT); + disableMouse(); + } + } else if (msg->_action == "Open") { + if (_open) { + playClip("Open", MOVIE_STOP_PREVIOUS | MOVIE_NOTIFY_OBJECT); + disableMouse(); + } + } else if (msg->_action == "CoreReplaced") { + CActMsg actMsg("Shut"); + actMsg.execute(this); + } else if (msg->_action == "OpenNow") { + loadFrame(0); + _open = false; + } + + return true; +} + +bool CCage::MovieEndMsg(CMovieEndMsg *msg) { + enableMouse(); + _open = clipExistsByEnd("Shut", msg->_endFrame); + + CStatusChangeMsg statusMsg; + statusMsg._newStatus = _open ? 1 : (CParrot::_v4 == 0 ? 1 : 0); + statusMsg.execute("PerchCoreHolder"); + + return true; +} + +bool CCage::PreEnterViewMsg(CPreEnterViewMsg *msg) { + loadSurface(); + _open = CParrot::_v4 != 0; + loadFrame(_open ? 8 : 0); + + return true; +} + +bool CCage::MouseMoveMsg(CMouseMoveMsg *msg) { + _cursorId = CParrot::_v4 && !CParrot::_v5 ? CURSOR_ACTIVATE : CURSOR_ARROW; + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/cage.h b/engines/titanic/game/cage.h index bbce978489..48b1b46ab7 100644 --- a/engines/titanic/game/cage.h +++ b/engines/titanic/game/cage.h @@ -28,9 +28,15 @@ namespace Titanic { class CCage : public CBackground { + DECLARE_MESSAGE_MAP; + bool MouseButtonDownMsg(CMouseButtonDownMsg *msg); + bool ActMsg(CActMsg *msg); + bool MovieEndMsg(CMovieEndMsg *msg); + bool PreEnterViewMsg(CPreEnterViewMsg *msg); + bool MouseMoveMsg(CMouseMoveMsg *msg); public: static int _v1; - static int _v2; + static bool _open; public: CLASSDEF; diff --git a/engines/titanic/game/captains_wheel.cpp b/engines/titanic/game/captains_wheel.cpp index c84c9194ce..79908b561d 100644 --- a/engines/titanic/game/captains_wheel.cpp +++ b/engines/titanic/game/captains_wheel.cpp @@ -24,6 +24,15 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CCaptainsWheel, CBackground) + ON_MESSAGE(MouseButtonDownMsg) + ON_MESSAGE(LeaveViewMsg) + ON_MESSAGE(ActMsg) + ON_MESSAGE(TurnOff) + ON_MESSAGE(TurnOn) + ON_MESSAGE(MovieEndMsg) +END_MESSAGE_MAP() + CCaptainsWheel::CCaptainsWheel() : CBackground(), _fieldE0(0), _fieldE4(0), _fieldE8(0), _fieldEC(0), _fieldF0(0), _fieldF4(0) { @@ -53,4 +62,148 @@ void CCaptainsWheel::load(SimpleFile *file) { CBackground::load(file); } +bool CCaptainsWheel::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { + if (_fieldE0) { + _fieldE0 = false; + CTurnOff offMsg; + offMsg.execute(this); + playMovie(162, 168, 0); + } else { + playMovie(0, 8, MOVIE_NOTIFY_OBJECT); + } + + return true; +} + +bool CCaptainsWheel::LeaveViewMsg(CLeaveViewMsg *msg) { + if (_fieldE0) { + _fieldE0 = false; + CTurnOff offMsg; + offMsg.execute(this); + playMovie(162, 168, MOVIE_GAMESTATE); + } + + return true; +} + +bool CCaptainsWheel::ActMsg(CActMsg *msg) { + if (msg->_action == "Spin") { + if (_fieldE0) { + CTurnOn onMsg; + onMsg.execute("RatchetySound"); + playMovie(8, 142, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); + } + } else if (msg->_action == "Honk") { + if (_fieldE0) { + playMovie(150, 160, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); + } + } else if (msg->_action == "Go") { + if (!_fieldE0) { + inc54(); + _fieldE0 = false; + _fieldE4 = 1; + + CTurnOff offMsg; + offMsg.execute(this); + playMovie(162, 168, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); + } + } else if (msg->_action == "Cruise") { + if (_fieldE0) { + inc54(); + _fieldE0 = false; + _fieldE4 = 2; + + CTurnOff offMsg; + offMsg.execute(this); + playMovie(162, 168, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); + } + } else if (msg->_action == "SetDestin") { + playSound("a#44.wav"); + CSetVolumeMsg volumeMsg; + volumeMsg._volume = 25; + volumeMsg.execute("EngineSounds"); + CTurnOn onMsg; + onMsg.execute("EngineSounds"); + _fieldF0 = 1; + } else if (msg->_action == "ClearDestin") { + _fieldF0 = 0; + } + + return true; +} + +bool CCaptainsWheel::TurnOff(CTurnOff *msg) { + CSignalObject signalMsg; + signalMsg._numValue = 0; + + static const char *const NAMES[8] = { + "WheelSpin", "SeagullHorn", "WheelStopButt", "StopHotSpot", + "WheelCruiseButt", "CruiseHotSpot", "WheelGoButt","GoHotSpot" + }; + for (int idx = 0; idx < 8; ++idx) + signalMsg.execute(NAMES[idx]); + + return true; +} + +bool CCaptainsWheel::TurnOn(CTurnOn *msg) { + CSignalObject signalMsg; + signalMsg._numValue = 1; + signalMsg.execute("WheelSpin"); + signalMsg.execute("SeagullHorn"); + + if (_fieldE0) { + signalMsg.execute("WheelStopButt"); + signalMsg.execute("StopHotSpot"); + } + + if (_fieldEC) { + signalMsg.execute("WheelCruiseButt"); + signalMsg.execute("CruiseHotSpot"); + } + + if (_fieldF0) { + signalMsg.execute("WheelGoButt"); + signalMsg.execute("GoHotSpot"); + } + + return true; +} + +bool CCaptainsWheel::MovieEndMsg(CMovieEndMsg *msg) { + if (msg->_endFrame == 8) { + _fieldE0 = true; + CTurnOn onMsg; + onMsg.execute(this); + } + + if (msg->_endFrame == 142) { + CTurnOff offMsg; + offMsg.execute("RatchetySound"); + } + + if (msg->_endFrame == 168) { + switch (_fieldE4) { + case 1: { + CActMsg actMsg(starFn2() ? "GoEnd" : "Go"); + actMsg.execute("GoSequence"); + break; + } + + case 2: { + CActMsg actMsg("Cruise"); + actMsg.execute("CruiseSequence"); + break; + } + + default: + break; + } + + _fieldE4 = 0; + } + + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/captains_wheel.h b/engines/titanic/game/captains_wheel.h index 549dcbe685..3aca45c21f 100644 --- a/engines/titanic/game/captains_wheel.h +++ b/engines/titanic/game/captains_wheel.h @@ -28,6 +28,13 @@ namespace Titanic { class CCaptainsWheel : public CBackground { + DECLARE_MESSAGE_MAP; + bool MouseButtonDownMsg(CMouseButtonDownMsg *msg); + bool LeaveViewMsg(CLeaveViewMsg *msg); + bool ActMsg(CActMsg *msg); + bool TurnOff(CTurnOff *msg); + bool TurnOn(CTurnOn *msg); + bool MovieEndMsg(CMovieEndMsg *msg); public: int _fieldE0; int _fieldE4; diff --git a/engines/titanic/game/cell_point_button.cpp b/engines/titanic/game/cell_point_button.cpp index 18ece09cb0..207dd73543 100644 --- a/engines/titanic/game/cell_point_button.cpp +++ b/engines/titanic/game/cell_point_button.cpp @@ -24,12 +24,17 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CCellPointButton, CBackground) + ON_MESSAGE(MouseButtonDownMsg) + ON_MESSAGE(EnterViewMsg) +END_MESSAGE_MAP() + CCellPointButton::CCellPointButton() : CBackground() { _fieldE0 = 0; _fieldE4 = 0; _fieldE8 = 0; _fieldEC = 0; - _fieldF0 = 0; + _regionNum = 0; _fieldF4 = 0; _fieldF8 = 0; _fieldFC = 0; @@ -44,7 +49,7 @@ void CCellPointButton::save(SimpleFile *file, int indent) { file->writeNumberLine(_fieldE4, indent); file->writeNumberLine(_fieldE8, indent); file->writeNumberLine(_fieldEC, indent); - file->writeNumberLine(_fieldF0, indent); + file->writeNumberLine(_regionNum, indent); file->writeNumberLine(_fieldF4, indent); file->writeNumberLine(_fieldF8, indent); file->writeNumberLine(_fieldFC, indent); @@ -52,7 +57,7 @@ void CCellPointButton::save(SimpleFile *file, int indent) { file->writeNumberLine(_field104, indent); file->writeNumberLine(_field108, indent); file->writeQuotedLine(_string3, indent); - file->writeNumberLine(_field118, indent); + file->writeNumberLine(_dialNum, indent); CBackground::save(file, indent); } @@ -63,7 +68,7 @@ void CCellPointButton::load(SimpleFile *file) { _fieldE4 = file->readNumber(); _fieldE8 = file->readNumber(); _fieldEC = file->readNumber(); - _fieldF0 = file->readNumber(); + _regionNum = file->readNumber(); _fieldF4 = file->readNumber(); _fieldF8 = file->readNumber(); _fieldFC = file->readNumber(); @@ -71,9 +76,28 @@ void CCellPointButton::load(SimpleFile *file) { _field104 = file->readNumber(); _field108 = file->readNumber(); _string3 = file->readString(); - _field118 = file->readNumber(); + _dialNum = file->readNumber(); CBackground::load(file); } +bool CCellPointButton::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { + if (getRandomNumber(2) == 0) { + CParrotSpeakMsg speakMsg("Cellpoints", _string3); + speakMsg.execute("PerchedParrot"); + } + + playMovie(0); + _regionNum = _regionNum ? 0 : 1; + playSound("z#425.wav"); + talkSetDialRegion(_string3, _dialNum, _regionNum); + + return true; +} + +bool CCellPointButton::EnterViewMsg(CEnterViewMsg *msg) { + _regionNum = talkGetDialRegion(_string3, _dialNum); + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/cell_point_button.h b/engines/titanic/game/cell_point_button.h index 6f1fdc3809..33f58cbb83 100644 --- a/engines/titanic/game/cell_point_button.h +++ b/engines/titanic/game/cell_point_button.h @@ -28,12 +28,15 @@ namespace Titanic { class CCellPointButton : public CBackground { + DECLARE_MESSAGE_MAP; + bool MouseButtonDownMsg(CMouseButtonDownMsg *msg); + bool EnterViewMsg(CEnterViewMsg *msg); public: int _fieldE0; int _fieldE4; int _fieldE8; int _fieldEC; - int _fieldF0; + int _regionNum; int _fieldF4; int _fieldF8; int _fieldFC; @@ -41,7 +44,7 @@ public: int _field104; int _field108; CString _string3; - int _field118; + int _dialNum; public: CLASSDEF; CCellPointButton(); diff --git a/engines/titanic/game/chev_code.cpp b/engines/titanic/game/chev_code.cpp index ebc20578b7..07225f0cf8 100644 --- a/engines/titanic/game/chev_code.cpp +++ b/engines/titanic/game/chev_code.cpp @@ -24,16 +24,263 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CChevCode, CGameObject) + ON_MESSAGE(SetChevLiftBits) + ON_MESSAGE(SetChevClassBits) + ON_MESSAGE(SetChevFloorBits) + ON_MESSAGE(SetChevRoomBits) + ON_MESSAGE(GetChevLiftNum) + ON_MESSAGE(GetChevClassNum) + ON_MESSAGE(GetChevFloorNum) + ON_MESSAGE(GetChevRoomNum) + ON_MESSAGE(CheckChevCode) + ON_MESSAGE(GetChevCodeFromRoomNameMsg) +END_MESSAGE_MAP() + void CChevCode::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); - file->writeNumberLine(_value, indent); + file->writeNumberLine(_chevCode, indent); CGameObject::save(file, indent); } void CChevCode::load(SimpleFile *file) { file->readNumber(); - _value = file->readNumber(); + _chevCode = file->readNumber(); CGameObject::load(file); } +bool CChevCode::SetChevLiftBits(CSetChevLiftBits *msg) { + _chevCode &= ~0xC0000; + if (msg->_liftNum > 0 && msg->_liftNum < 5) + _chevCode = ((msg->_liftNum - 1) << 18) | _chevCode; + + return true; +} + +bool CChevCode::SetChevClassBits(CSetChevClassBits *msg) { + _chevCode &= ~0x30000; + if (msg->_classNum > 0 && msg->_classNum < 4) + _chevCode = (msg->_classNum << 16) | msg->_classNum; + + return true; +} + +bool CChevCode::SetChevFloorBits(CSetChevFloorBits *msg) { + int section = (msg->_floorNum + 4) / 10; + int index = (msg->_floorNum + 4) % 10; + _chevCode &= ~0xFF00; + + int val; + switch (section) { + case 0: + val = 144; + break; + case 1: + val = 208; + break; + case 2: + val = 224; + break; + case 3: + val = 240; + break; + default: + break; + } + + _chevCode |= ((index + val) << 8); + return true; +} + +bool CChevCode::SetChevRoomBits(CSetChevRoomBits *msg) { + _chevCode &= ~0xff; + if (msg->_roomNum > 0 && msg->_roomNum < 128) + _chevCode |= msg->_roomNum * 2; + + return true; +} + +bool CChevCode::GetChevLiftNum(CGetChevLiftNum *msg) { + msg->_liftNum = (_chevCode >> 18) & 3 + 1; + return true; +} + +bool CChevCode::GetChevClassNum(CGetChevClassNum *msg) { + msg->_classNum = (_chevCode >> 16) & 3; + return true; +} + +bool CChevCode::GetChevFloorNum(CGetChevFloorNum *msg) { + int val1 = (_chevCode >> 8) & 0xF; + int val2 = (_chevCode >> 12) & 0xF - 9; + + switch (val2) { + case 0: + val2 = 0; + break; + case 4: + val2 = 1; + break; + case 5: + val2 = 2; + break; + case 6: + val2 = 3; + break; + default: + val2 = 4; + break; + } + + msg->_floorNum = (val1 >= 10) ? 0 : val1 * 10; + return true; +} + +bool CChevCode::GetChevRoomNum(CGetChevRoomNum *msg) { + msg->_roomNum = (_chevCode >> 1) & 0x7F; + return true; +} + +bool CChevCode::CheckChevCode(CCheckChevCode *msg) { + CGetChevClassNum getClassMsg; + CGetChevLiftNum getLiftMsg; + CGetChevFloorNum getFloorMsg; + CGetChevRoomNum getRoomMsg; + CString roomName; + int classNum = 0; + uint bits; + + if (_chevCode & 1) { + switch (_chevCode) { + case 0x1D0D9: + roomName = "ParrLobby"; + classNum = 4; + break; + case 0x196D9: + roomName = "FCRestrnt"; + classNum = 4; + break; + case 0x39FCB: + roomName = "Bridge"; + classNum = 4; + break; + case 0x2F86D: + roomName = "CrtrsCham"; + classNum = 4; + break; + case 0x465FB: + roomName = "SculpCham"; + classNum = 4; + break; + case 0x3D94B: + roomName = "BilgeRoom"; + classNum = 4; + break; + case 0x59FAD: + roomName = "BoWell"; + classNum = 4; + break; + case 0x4D6AF: + roomName = "Arboretum"; + classNum = 4; + break; + case 0x8A397: + roomName = "TitRoom"; + classNum = 4; + break; + case 0x79C45: + roomName = "PromDeck"; + classNum = 4; + break; + case 0xB3D97: + roomName = "Bar"; + classNum = 4; + break; + case 0xCC971: + roomName = "EmbLobby"; + classNum = 4; + break; + case 0xF34DB: + roomName = "MusicRoom"; + classNum = 4; + break; + default: + roomName = "BadRoom"; + classNum = 5; + break; + } + + bits = classNum == 5 ? 0x3D94B : _chevCode; + } else { + getFloorMsg.execute(this); + getRoomMsg.execute(this); + getClassMsg.execute(this); + getLiftMsg.execute(this); + if (getFloorMsg._floorNum > 37 || getRoomMsg._roomNum > 18) + classNum = 5; + + if (classNum == 5) { + bits = 0x3D94B; + } else { + switch (getClassMsg._classNum) { + case 1: + if (getFloorMsg._floorNum >= 2 && getFloorMsg._floorNum <= 18 + && getRoomMsg._roomNum >= 1 && getRoomMsg._roomNum <= 3 + && getLiftMsg._liftNum >= 1 && getLiftMsg._liftNum <= 4) + classNum = 1; + else + classNum = 5; + break; + + case 2: + if (getFloorMsg._floorNum >= 19 && getFloorMsg._floorNum <= 26 + && getRoomMsg._roomNum >= 1 && getRoomMsg._roomNum <= 5 + && getLiftMsg._liftNum >= 1 && getLiftMsg._liftNum <= 4) + classNum = 2; + else + classNum = 5; + break; + + case 3: + if (getFloorMsg._floorNum >= 27 && getFloorMsg._floorNum <= 37 + && getRoomMsg._roomNum >= 1 && getRoomMsg._roomNum <= 18 + && (getLiftMsg._liftNum & 1) == 1 + && getLiftMsg._liftNum >= 1 && getLiftMsg._liftNum <= 4) + classNum = 3; + else + classNum = 5; + break; + } + } + // TODO + } + + msg->_classNum = classNum; + msg->_chevCode = bits; + + // WORKAROUND: Skipped code from original that was for debugging purposes only + return true; +} + +bool CChevCode::GetChevCodeFromRoomNameMsg(CGetChevCodeFromRoomNameMsg *msg) { + static const char *const ROOM_NAMES[13] = { + "ParrotLobby", "sculptureChamber", "Bar", "EmbLobby", "MusicRoom", + "Titania", "BottomOfWell", "Arboretum", "PromenadeDeck", + "FCRestrnt", "CrtrsCham", "BilgeRoom", "Bridge" + }; + static const uint CHEV_CODES[13] = { + 0x1D0D9, 0x465FB, 0xB3D97, 0xCC971, 0xF34DB, 0x8A397, 0x59FAD, + 0x4D6AF, 0x79C45, 0x196D9, 0x2F86D, 0x3D94B, 0x39FCB + }; + + for (int idx = 0; idx < 13; ++idx) { + if (msg->_roomName == ROOM_NAMES[idx]) { + msg->_chevCode = CHEV_CODES[idx]; + break; + } + } + + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/chev_code.h b/engines/titanic/game/chev_code.h index c4552d00a2..4a71b13f9e 100644 --- a/engines/titanic/game/chev_code.h +++ b/engines/titanic/game/chev_code.h @@ -28,11 +28,22 @@ namespace Titanic { class CChevCode : public CGameObject { + DECLARE_MESSAGE_MAP; + bool SetChevLiftBits(CSetChevLiftBits *msg); + bool SetChevClassBits(CSetChevClassBits *msg); + bool SetChevFloorBits(CSetChevFloorBits *msg); + bool SetChevRoomBits(CSetChevRoomBits *msg); + bool GetChevLiftNum(CGetChevLiftNum *msg); + bool GetChevClassNum(CGetChevClassNum *msg); + bool GetChevFloorNum(CGetChevFloorNum *msg); + bool GetChevRoomNum(CGetChevRoomNum *msg); + bool CheckChevCode(CCheckChevCode *msg); + bool GetChevCodeFromRoomNameMsg(CGetChevCodeFromRoomNameMsg *msg); public: - int _value; + int _chevCode; public: CLASSDEF; - CChevCode() : CGameObject(), _value(0) {} + CChevCode() : CGameObject(), _chevCode(0) {} /** * Save the data for the class to file diff --git a/engines/titanic/game/chev_panel.cpp b/engines/titanic/game/chev_panel.cpp index 245968e356..c644776bc9 100644 --- a/engines/titanic/game/chev_panel.cpp +++ b/engines/titanic/game/chev_panel.cpp @@ -21,25 +21,101 @@ */ #include "titanic/game/chev_panel.h" +#include "titanic/game/chev_code.h" namespace Titanic { +BEGIN_MESSAGE_MAP(CChevPanel, CGameObject) + ON_MESSAGE(MouseDragStartMsg) + ON_MESSAGE(MouseDragMoveMsg) + ON_MESSAGE(MouseButtonUpMsg) + ON_MESSAGE(SetChevPanelBitMsg) + ON_MESSAGE(MouseDragEndMsg) + ON_MESSAGE(ClearChevPanelBits) + ON_MESSAGE(MouseButtonDownMsg) + ON_MESSAGE(SetChevPanelButtonsMsg) +END_MESSAGE_MAP() + void CChevPanel::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); - file->writeNumberLine(_fieldBC, indent); - file->writeNumberLine(_fieldC0, indent); - file->writeNumberLine(_fieldC4, indent); + file->writeNumberLine(_startPos.x, indent); + file->writeNumberLine(_startPos.y, indent); + file->writeNumberLine(_chevCode, indent); CGameObject::save(file, indent); } void CChevPanel::load(SimpleFile *file) { file->readNumber(); - _fieldBC = file->readNumber(); - _fieldC0 = file->readNumber(); - _fieldC4 = file->readNumber(); + _startPos.x = file->readNumber(); + _startPos.y = file->readNumber(); + _chevCode = file->readNumber(); CGameObject::load(file); } +bool CChevPanel::MouseDragStartMsg(CMouseDragStartMsg *msg) { + if (checkStartDragging(msg)) { + _startPos = Point(msg->_mousePos.x - _bounds.left, + msg->_mousePos.y - _bounds.top); + CChildDragStartMsg dragMsg(_startPos); + dragMsg.execute(this, nullptr, MSGFLAG_SCAN); + } + + return true; +} + +bool CChevPanel::MouseDragMoveMsg(CMouseDragMoveMsg *msg) { + CChildDragMoveMsg dragMsg(_startPos); + dragMsg.execute(this, nullptr, MSGFLAG_SCAN); + + setPosition(msg->_mousePos - _startPos); + return true; +} + +bool CChevPanel::MouseButtonUpMsg(CMouseButtonUpMsg *msg) { + CChevCode chevCode; + chevCode._chevCode = _chevCode; + CCheckChevCode checkCode; + checkCode.execute(this); + CClearChevPanelBits panelBits; + panelBits.execute(this, nullptr, MSGFLAG_SCAN); + CSetChevPanelButtonsMsg setMsg; + setMsg._chevCode = checkCode._chevCode; + setMsg.execute(this); + + return true; +} + +bool CChevPanel::SetChevPanelBitMsg(CSetChevPanelBitMsg *msg) { + _chevCode = _chevCode & ~(1 << msg->_value1) | (msg->_value2 << msg->_value1); + return true; +} + +bool CChevPanel::MouseDragEndMsg(CMouseDragEndMsg *msg) { + setPosition(msg->_mousePos - _startPos); + return true; +} + +bool CChevPanel::ClearChevPanelBits(CClearChevPanelBits *msg) { + CSetChevPanelButtonsMsg setMsg; + setMsg._chevCode = 0; + setMsg.execute(this); + + return true; +} + +bool CChevPanel::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { + return true; +} + +bool CChevPanel::SetChevPanelButtonsMsg(CSetChevPanelButtonsMsg *msg) { + _chevCode = msg->_chevCode; + CSetChevButtonImageMsg setMsg; + setMsg._value2 = 1; + setMsg.execute(this, nullptr, MSGFLAG_SCAN); + + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/chev_panel.h b/engines/titanic/game/chev_panel.h index 99b5501ac2..bcfb920221 100644 --- a/engines/titanic/game/chev_panel.h +++ b/engines/titanic/game/chev_panel.h @@ -28,13 +28,21 @@ namespace Titanic { class CChevPanel : public CGameObject { + DECLARE_MESSAGE_MAP; + bool MouseDragStartMsg(CMouseDragStartMsg *msg); + bool MouseDragMoveMsg(CMouseDragMoveMsg *msg); + bool MouseButtonUpMsg(CMouseButtonUpMsg *msg); + bool SetChevPanelBitMsg(CSetChevPanelBitMsg *msg); + bool MouseDragEndMsg(CMouseDragEndMsg *msg); + bool ClearChevPanelBits(CClearChevPanelBits *msg); + bool MouseButtonDownMsg(CMouseButtonDownMsg *msg); + bool SetChevPanelButtonsMsg(CSetChevPanelButtonsMsg *msg); public: - int _fieldBC; - int _fieldC0; - int _fieldC4; + Point _startPos; + int _chevCode; public: CLASSDEF; - CChevPanel() : _fieldBC(0), _fieldC0(0), _fieldC4(0) {} + CChevPanel() : _chevCode(0) {} /** * Save the data for the class to file diff --git a/engines/titanic/game/chicken_cooler.cpp b/engines/titanic/game/chicken_cooler.cpp index 29232e10bf..d10405de38 100644 --- a/engines/titanic/game/chicken_cooler.cpp +++ b/engines/titanic/game/chicken_cooler.cpp @@ -21,9 +21,15 @@ */ #include "titanic/game/chicken_cooler.h" +#include "titanic/carry/chicken.h" namespace Titanic { +BEGIN_MESSAGE_MAP(CChickenCooler, CGameObject) + ON_MESSAGE(EnterRoomMsg) + ON_MESSAGE(EnterViewMsg) +END_MESSAGE_MAP() + void CChickenCooler::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); file->writeNumberLine(_fieldBC, indent); @@ -41,7 +47,32 @@ void CChickenCooler::load(SimpleFile *file) { } bool CChickenCooler::EnterRoomMsg(CEnterRoomMsg *msg) { - warning("CChickenCoolor::handlEvent"); + if (_fieldC0) { + CGameObject *obj = getMailManFirstObject(); + if (obj) { + // WORKAROUND: Redundant loop for chicken in originalhere + } else { + getNextMail(nullptr); + if (CChicken::_v1 > _fieldBC) + CChicken::_v1 = _fieldBC; + } + } + + return true; +} + +bool CChickenCooler::EnterViewMsg(CEnterViewMsg *msg) { + if (!_fieldC0) { + for (CGameObject *obj = getMailManFirstObject(); obj; + obj = getNextMail(obj)) { + if (obj->isEquals("Chicken")) + return true; + } + + if (CChicken::_v1 > _fieldBC) + CChicken::_v1 = _fieldBC; + } + return true; } diff --git a/engines/titanic/game/chicken_cooler.h b/engines/titanic/game/chicken_cooler.h index 724727b905..54dba90686 100644 --- a/engines/titanic/game/chicken_cooler.h +++ b/engines/titanic/game/chicken_cooler.h @@ -29,7 +29,9 @@ namespace Titanic { class CChickenCooler : public CGameObject { + DECLARE_MESSAGE_MAP; bool EnterRoomMsg(CEnterRoomMsg *msg); + bool EnterViewMsg(CEnterViewMsg *msg); private: int _fieldBC; int _fieldC0; diff --git a/engines/titanic/game/chicken_dispensor.cpp b/engines/titanic/game/chicken_dispensor.cpp index a9bf576765..7fb8fefcda 100644 --- a/engines/titanic/game/chicken_dispensor.cpp +++ b/engines/titanic/game/chicken_dispensor.cpp @@ -21,9 +21,21 @@ */ #include "titanic/game/chicken_dispensor.h" +#include "titanic/core/project_item.h" +#include "titanic/pet_control/pet_control.h" namespace Titanic { +BEGIN_MESSAGE_MAP(CChickenDispensor, CBackground) + ON_MESSAGE(StatusChangeMsg) + ON_MESSAGE(MovieEndMsg) + ON_MESSAGE(ActMsg) + ON_MESSAGE(LeaveViewMsg) + ON_MESSAGE(EnterViewMsg) + ON_MESSAGE(MouseDragStartMsg) + ON_MESSAGE(TurnOff) +END_MESSAGE_MAP() + CChickenDispensor::CChickenDispensor() : CBackground(), _fieldE0(0), _fieldE4(0), _fieldE8(0) { } @@ -45,4 +57,133 @@ void CChickenDispensor::load(SimpleFile *file) { CBackground::load(file); } +bool CChickenDispensor::StatusChangeMsg(CStatusChangeMsg *msg) { + msg->execute("SGTRestLeverAnimation"); + int v1 = _fieldE8 ? 0 : _fieldE4; + CPetControl *pet = getPetControl(); + CGameObject *obj; + + for (obj = pet->getFirstObject(); obj; obj = pet->getNextObject(obj)) { + if (obj->isEquals("Chicken")) { + petDisplayMessage(1, "Chickens are allocated on a one-per-customer basis."); + return true; + } + } + + for (obj = getMailManFirstObject(); obj; obj = getNextMail(obj)) { + if (obj->isEquals("Chicken")) { + petDisplayMessage(1, "Chickens are allocated on a one-per-customer basis."); + return true; + } + } + + if (v1 == 1 || v1 == 2) + _fieldE8 = 1; + + switch (v1) { + case 0: + petDisplayMessage(1, "Only one piece of chicken per passenger. Thank you."); + break; + case 1: + setVisible(true); + if (_fieldE0) { + playMovie(0, 12, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); + playSound("z#400.wav"); + _fieldE4 = 0; + } else { + playMovie(12, 16, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); + _fieldE8 = 1; + _fieldE4 = 0; + } + break; + + case 2: + setVisible(true); + if (_fieldE0) { + playMovie(0, 12, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); + playSound("z#400.wav"); + } else { + playMovie(12, 16, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); + _fieldE8 = 1; + } + break; + + default: + break; + } + + return true; +} + +bool CChickenDispensor::MovieEndMsg(CMovieEndMsg *msg) { + if (getMovieFrame() == 16) { + playSound("b#50.wav", 50); + CActMsg actMsg("Dispense Chicken"); + actMsg.execute("Chicken"); + } else if (_fieldE8) { + _cursorId = CURSOR_ARROW; + loadFrame(0); + setVisible(false); + if (_fieldE4 == 2) + _fieldE8 = 0; + } else { + loadFrame(0); + setVisible(false); + changeView("SgtLobby.Node 1.N"); + } + + return true; +} + +bool CChickenDispensor::ActMsg(CActMsg *msg) { + if (msg->_action == "EnableObject") + _fieldE0 = 0; + else if (msg->_action == "DisableObject") + _fieldE0 = 1; + else if (msg->_action == "IncreaseQuantity") + _fieldE4 = 2; + else if (msg->_action == "DecreaseQuantity") + _fieldE4 = 1; + + return true; +} + +bool CChickenDispensor::LeaveViewMsg(CLeaveViewMsg *msg) { + return true; +} + +bool CChickenDispensor::EnterViewMsg(CEnterViewMsg *msg) { + playSound("b#51.wav"); + _fieldE8 = 0; + _cursorId = CURSOR_ARROW; + return true; +} + +bool CChickenDispensor::MouseDragStartMsg(CMouseDragStartMsg *msg) { + if (getMovieFrame() == 16) { + setVisible(false); + loadFrame(0); + _cursorId = CURSOR_ARROW; + _fieldE8 = 1; + + CVisibleMsg visibleMsg; + visibleMsg.execute("Chicken"); + CPassOnDragStartMsg passMsg(msg->_mousePos, 1); + passMsg.execute("Chicken"); + + msg->_dragItem = getRoot()->findByName("Chicken"); + } + + return true; +} + +bool CChickenDispensor::TurnOff(CTurnOff *msg) { + if (getMovieFrame() == 16) + setVisible(false); + playMovie(16, 12, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); + _fieldE8 = 0; + + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/chicken_dispensor.h b/engines/titanic/game/chicken_dispensor.h index d86b850871..5e3ba47ee8 100644 --- a/engines/titanic/game/chicken_dispensor.h +++ b/engines/titanic/game/chicken_dispensor.h @@ -28,6 +28,14 @@ namespace Titanic { class CChickenDispensor : public CBackground { + DECLARE_MESSAGE_MAP; + bool StatusChangeMsg(CStatusChangeMsg *msg); + bool MovieEndMsg(CMovieEndMsg *msg); + bool ActMsg(CActMsg *msg); + bool LeaveViewMsg(CLeaveViewMsg *msg); + bool EnterViewMsg(CEnterViewMsg *msg); + bool MouseDragStartMsg(CMouseDragStartMsg *msg); + bool TurnOff(CTurnOff *msg); public: int _fieldE0; int _fieldE4; diff --git a/engines/titanic/game/close_broken_pel.cpp b/engines/titanic/game/close_broken_pel.cpp index d27441ac96..c234590849 100644 --- a/engines/titanic/game/close_broken_pel.cpp +++ b/engines/titanic/game/close_broken_pel.cpp @@ -24,16 +24,26 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CCloseBrokenPel, CBackground) + ON_MESSAGE(MouseButtonDownMsg) +END_MESSAGE_MAP() + void CCloseBrokenPel::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); - file->writeQuotedLine(_string3, indent); + file->writeQuotedLine(_target, indent); CBackground::save(file, indent); } void CCloseBrokenPel::load(SimpleFile *file) { file->readNumber(); - _string3 = file->readString(); + _target = file->readString(); CBackground::load(file); } +bool CCloseBrokenPel::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { + CActMsg actMsg("Close"); + actMsg.execute(_target); + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/close_broken_pel.h b/engines/titanic/game/close_broken_pel.h index aacda6c002..4bd66255df 100644 --- a/engines/titanic/game/close_broken_pel.h +++ b/engines/titanic/game/close_broken_pel.h @@ -28,8 +28,10 @@ namespace Titanic { class CCloseBrokenPel : public CBackground { + DECLARE_MESSAGE_MAP; + bool MouseButtonDownMsg(CMouseButtonDownMsg *msg); public: - CString _string3; + CString _target; public: CLASSDEF; diff --git a/engines/titanic/game/cookie.cpp b/engines/titanic/game/cookie.cpp index 915bb93b4a..96edca4058 100644 --- a/engines/titanic/game/cookie.cpp +++ b/engines/titanic/game/cookie.cpp @@ -24,6 +24,11 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CCookie, CGameObject) + ON_MESSAGE(LeaveNodeMsg) + ON_MESSAGE(FreshenCookieMsg) +END_MESSAGE_MAP() + void CCookie::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); file->writeNumberLine(_value1, indent); @@ -40,4 +45,16 @@ void CCookie::load(SimpleFile *file) { CGameObject::load(file); } +bool CCookie::LeaveNodeMsg(CLeaveNodeMsg *msg) { + if (_value2) + _value1 = 1; + return true; +} + +bool CCookie::FreshenCookieMsg(CFreshenCookieMsg *msg) { + _value1 = msg->_value2; + _value2 = msg->_value1; + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/cookie.h b/engines/titanic/game/cookie.h index 7ae04f1144..2018deeb3e 100644 --- a/engines/titanic/game/cookie.h +++ b/engines/titanic/game/cookie.h @@ -28,6 +28,9 @@ namespace Titanic { class CCookie : public CGameObject { + DECLARE_MESSAGE_MAP; + bool LeaveNodeMsg(CLeaveNodeMsg *msg); + bool FreshenCookieMsg(CFreshenCookieMsg *msg); public: int _value1; int _value2; diff --git a/engines/titanic/game/credits.cpp b/engines/titanic/game/credits.cpp index 7078d41a17..d9149f6dd2 100644 --- a/engines/titanic/game/credits.cpp +++ b/engines/titanic/game/credits.cpp @@ -24,6 +24,11 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CCredits, CGameObject) + ON_MESSAGE(SignalObject) + ON_MESSAGE(TimerMsg) +END_MESSAGE_MAP() + CCredits::CCredits() : CGameObject(), _fieldBC(-1), _fieldC0(1) { } @@ -43,4 +48,34 @@ void CCredits::load(SimpleFile *file) { CGameObject::load(file); } +bool CCredits::SignalObject(CSignalObject *msg) { + petHide(); + disableMouse(); + addTimer(50); + return true; +} + +bool CCredits::TimerMsg(CTimerMsg *msg) { + stopGlobalSound(true, -1); + setVisible(true); + loadSound("a#16.wav"); + loadSound("a#24.wav"); + + playCutscene(0, 18); + playGlobalSound("a#16.wav", -1, false, false, 0); + playCutscene(19, 642); + playSound("a#24.wav"); + playCutscene(643, 750); + + COpeningCreditsMsg creditsMsg; + creditsMsg.execute("Service Elevator Entity"); + changeView("EmbLobby.Node 6.S"); + + setVisible(false); + petShow(); + enableMouse(); + stopGlobalSound(true, -1); + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/credits.h b/engines/titanic/game/credits.h index fa9794b6de..23fd25584d 100644 --- a/engines/titanic/game/credits.h +++ b/engines/titanic/game/credits.h @@ -28,6 +28,9 @@ namespace Titanic { class CCredits : public CGameObject { + DECLARE_MESSAGE_MAP; + bool SignalObject(CSignalObject *msg); + bool TimerMsg(CTimerMsg *msg); public: int _fieldBC, _fieldC0; public: diff --git a/engines/titanic/game/credits_button.cpp b/engines/titanic/game/credits_button.cpp index 90bb1b5ebe..ee8f7bb329 100644 --- a/engines/titanic/game/credits_button.cpp +++ b/engines/titanic/game/credits_button.cpp @@ -24,6 +24,11 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CCreditsButton, CBackground) + ON_MESSAGE(MouseButtonUpMsg) + ON_MESSAGE(MouseButtonDownMsg) +END_MESSAGE_MAP() + CCreditsButton::CCreditsButton() : CBackground(), _fieldE0(1) { } @@ -39,4 +44,19 @@ void CCreditsButton::load(SimpleFile *file) { CBackground::load(file); } +bool CCreditsButton::MouseButtonUpMsg(CMouseButtonUpMsg *msg) { + return true; +} + +bool CCreditsButton::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { + if (_fieldE0) { + playSound("a#20.wav"); + CSignalObject signalMsg; + signalMsg._numValue = 1; + signalMsg.execute("CreditsPlayer"); + } + + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/credits_button.h b/engines/titanic/game/credits_button.h index 5e0bf96677..4a53083195 100644 --- a/engines/titanic/game/credits_button.h +++ b/engines/titanic/game/credits_button.h @@ -28,6 +28,9 @@ namespace Titanic { class CCreditsButton : public CBackground { + DECLARE_MESSAGE_MAP; + bool MouseButtonUpMsg(CMouseButtonUpMsg *msg); + bool MouseButtonDownMsg(CMouseButtonDownMsg *msg); public: int _fieldE0; public: diff --git a/engines/titanic/game/desk_click_responder.cpp b/engines/titanic/game/desk_click_responder.cpp index d9b2cb64b4..2cd5d1e83b 100644 --- a/engines/titanic/game/desk_click_responder.cpp +++ b/engines/titanic/game/desk_click_responder.cpp @@ -21,13 +21,19 @@ */ #include "titanic/game/desk_click_responder.h" +#include "titanic/titanic.h" namespace Titanic { +BEGIN_MESSAGE_MAP(CDeskClickResponder, CClickResponder) + ON_MESSAGE(MouseButtonDownMsg) + ON_MESSAGE(LoadSuccessMsg) +END_MESSAGE_MAP() + void CDeskClickResponder::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); file->writeNumberLine(_fieldD4, indent); - file->writeNumberLine(_fieldD8, indent); + file->writeNumberLine(_ticks, indent); CClickResponder::save(file, indent); } @@ -35,9 +41,28 @@ void CDeskClickResponder::save(SimpleFile *file, int indent) { void CDeskClickResponder::load(SimpleFile *file) { file->readNumber(); _fieldD4 = file->readNumber(); - _fieldD8 = file->readNumber(); + _ticks = file->readNumber(); CClickResponder::load(file); } +bool CDeskClickResponder::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { + _fieldD4 = (_fieldD4 + 1) % 3; + if (_fieldD4) + return CClickResponder::MouseButtonDownMsg(msg); + + uint ticks = g_vm->_events->getTicksCount(); + if (!_ticks || ticks > (_ticks + 4000)) { + playSound("a#22.wav"); + _ticks = ticks; + } + + return true; +} + +bool CDeskClickResponder::LoadSuccessMsg(CLoadSuccessMsg *msg) { + _ticks = 0; + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/desk_click_responder.h b/engines/titanic/game/desk_click_responder.h index 12825ba9de..13cf7f4b87 100644 --- a/engines/titanic/game/desk_click_responder.h +++ b/engines/titanic/game/desk_click_responder.h @@ -28,9 +28,12 @@ namespace Titanic { class CDeskClickResponder : public CClickResponder { + DECLARE_MESSAGE_MAP; + bool MouseButtonDownMsg(CMouseButtonDownMsg *msg); + bool LoadSuccessMsg(CLoadSuccessMsg *msg); protected: int _fieldD4; - int _fieldD8; + uint _ticks; public: CLASSDEF; diff --git a/engines/titanic/game/null_port_hole.cpp b/engines/titanic/game/null_port_hole.cpp index e651b1b59f..b1514c7cbf 100644 --- a/engines/titanic/game/null_port_hole.cpp +++ b/engines/titanic/game/null_port_hole.cpp @@ -27,22 +27,22 @@ namespace Titanic { EMPTY_MESSAGE_MAP(CNullPortHole, CClickResponder); CNullPortHole::CNullPortHole() : CClickResponder() { - _string1 = "For a better view, why not visit the Promenade Deck?"; - _string2 = "b#48.wav"; + _message = "For a better view, why not visit the Promenade Deck?"; + _soundName = "b#48.wav"; } void CNullPortHole::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); - file->writeQuotedLine(_string2, indent); - file->writeQuotedLine(_string1, indent); + file->writeQuotedLine(_soundName, indent); + file->writeQuotedLine(_message, indent); CClickResponder::save(file, indent); } void CNullPortHole::load(SimpleFile *file) { file->readNumber(); - _string2 = file->readString(); - _string1 = file->readString(); + _soundName = file->readString(); + _message = file->readString(); CClickResponder::load(file); } diff --git a/engines/titanic/game/parrot/parrot_succubus.cpp b/engines/titanic/game/parrot/parrot_succubus.cpp deleted file mode 100644 index 02a29b748e..0000000000 --- a/engines/titanic/game/parrot/parrot_succubus.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include "titanic/game/parrot/parrot_succubus.h" - -namespace Titanic { - -CParrotSuccUBus::CParrotSuccUBus() : CSuccUBus(), _field1DC(0), - _field1EC(0), _field1F0(376), _field1F4(393) { -} - -void CParrotSuccUBus::save(SimpleFile *file, int indent) { - file->writeNumberLine(1, indent); - file->writeNumberLine(_field1DC, indent); - file->writeQuotedLine(_string3, indent); - file->writeNumberLine(_field1EC, indent); - - CSuccUBus::save(file, indent); -} - -void CParrotSuccUBus::load(SimpleFile *file) { - file->readNumber(); - _field1DC = file->readNumber(); - _string3 = file->readString(); - _field1EC = file->readNumber(); - - CSuccUBus::load(file); -} - -} // End of namespace Titanic diff --git a/engines/titanic/game/placeholder/bar_shelf_vis_centre.cpp b/engines/titanic/game/placeholder/bar_shelf_vis_centre.cpp index a8a33fe1b1..fc5d680f0c 100644 --- a/engines/titanic/game/placeholder/bar_shelf_vis_centre.cpp +++ b/engines/titanic/game/placeholder/bar_shelf_vis_centre.cpp @@ -24,16 +24,44 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CBarShelfVisCentre, CPlaceHolderItem) + ON_MESSAGE(MouseButtonDownMsg) + ON_MESSAGE(TimerMsg) + ON_MESSAGE(EnterViewMsg) +END_MESSAGE_MAP() + void CBarShelfVisCentre::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); - file->writeNumberLine(_value, indent); + file->writeNumberLine(_flag, indent); CPlaceHolderItem::save(file, indent); } void CBarShelfVisCentre::load(SimpleFile *file) { file->readNumber(); - _value = file->readNumber(); + _flag = file->readNumber(); CPlaceHolderItem::load(file); } +bool CBarShelfVisCentre::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { + if (!_flag) { + CActMsg actMsg("ClickOnVision"); + actMsg.execute("Barbot"); + addTimer(3000); + _flag = true; + } + + return true; +} + +bool CBarShelfVisCentre::TimerMsg(CTimerMsg *msg) { + _flag = false; + return true; +} + +bool CBarShelfVisCentre::EnterViewMsg(CEnterViewMsg *msg) { + _flag = false; + return true; +} + + } // End of namespace Titanic diff --git a/engines/titanic/game/placeholder/bar_shelf_vis_centre.h b/engines/titanic/game/placeholder/bar_shelf_vis_centre.h index a53ef2633f..672655d368 100644 --- a/engines/titanic/game/placeholder/bar_shelf_vis_centre.h +++ b/engines/titanic/game/placeholder/bar_shelf_vis_centre.h @@ -28,11 +28,15 @@ namespace Titanic { class CBarShelfVisCentre : public CPlaceHolderItem { + DECLARE_MESSAGE_MAP; + bool MouseButtonDownMsg(CMouseButtonDownMsg *msg); + bool TimerMsg(CTimerMsg *msg); + bool EnterViewMsg(CEnterViewMsg *msg); private: - int _value; + bool _flag; public: CLASSDEF; - CBarShelfVisCentre() : CPlaceHolderItem(), _value(0) {} + CBarShelfVisCentre() : CPlaceHolderItem(), _flag(false) {} /** * Save the data for the class to file diff --git a/engines/titanic/game/sgt/basin.cpp b/engines/titanic/game/sgt/basin.cpp index 1eb1d161c9..775c67deca 100644 --- a/engines/titanic/game/sgt/basin.cpp +++ b/engines/titanic/game/sgt/basin.cpp @@ -24,6 +24,12 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CBasin, CSGTStateRoom) + ON_MESSAGE(TurnOn) + ON_MESSAGE(TurnOff) + ON_MESSAGE(MovieEndMsg) +END_MESSAGE_MAP() + void CBasin::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); CSGTStateRoom::save(file, indent); @@ -34,4 +40,39 @@ void CBasin::load(SimpleFile *file) { CSGTStateRoom::load(file); } +bool CBasin::TurnOn(CTurnOn *msg) { + if (_statics->_v10 == "Open" && _statics->_v11 == "Closed" + && _statics->_v2 == "Closed") { + setVisible(true); + _statics->_v11 = "Open"; + _fieldE0 = 0; + _startFrame = 0; + _endFrame = 6; + playMovie(0, 6, MOVIE_GAMESTATE); + playSound("b#13.wav"); + } + + return true; +} + +bool CBasin::TurnOff(CTurnOff *msg) { + if (_statics->_v11 == "Open") { + _statics->_v11 = "Closed"; + _fieldE0 = 1; + _startFrame = 8; + _endFrame = 14; + playMovie(8, 14, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); + playSound("b#13.wav"); + } + + return true; +} + +bool CBasin::MovieEndMsg(CMovieEndMsg *msg) { + if (_statics->_v11 == "Closed") + setVisible(false); + + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/sgt/basin.h b/engines/titanic/game/sgt/basin.h index e4a36eb841..1fcb469824 100644 --- a/engines/titanic/game/sgt/basin.h +++ b/engines/titanic/game/sgt/basin.h @@ -28,6 +28,10 @@ namespace Titanic { class CBasin : public CSGTStateRoom { + DECLARE_MESSAGE_MAP; + bool TurnOn(CTurnOn *msg); + bool TurnOff(CTurnOff *msg); + bool MovieEndMsg(CMovieEndMsg *msg); public: CLASSDEF; diff --git a/engines/titanic/game/sgt/bedfoot.cpp b/engines/titanic/game/sgt/bedfoot.cpp index 18ea07aca0..ff7d64569a 100644 --- a/engines/titanic/game/sgt/bedfoot.cpp +++ b/engines/titanic/game/sgt/bedfoot.cpp @@ -24,6 +24,11 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CBedfoot, CSGTStateRoom) + ON_MESSAGE(TurnOn) + ON_MESSAGE(TurnOff) +END_MESSAGE_MAP() + void CBedfoot::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); CSGTStateRoom::save(file, indent); @@ -34,4 +39,91 @@ void CBedfoot::load(SimpleFile *file) { CSGTStateRoom::load(file); } +bool CBedfoot::TurnOn(CTurnOn *msg) { + if (_statics->_v2 == "Closed" && _statics->_v11 == "Closed") { + _fieldE0 = 0; + _startFrame = 0; + if (_statics->_v10 == "Open") { + _endFrame = 13; + _statics->_v2 = "Open"; + playSound("b#7.wav"); + } else { + _endFrame = 17; + _statics->_v2 = "NotOnWashstand"; + playSound("b#4.wav"); + } + + playMovie(_startFrame, _endFrame, MOVIE_GAMESTATE); + } else if (_statics->_v2 == "RestingUnderTV") { + _fieldE0 = 0; + _startFrame = 8; + if (_statics->_v10 == "Open") { + _statics->_v2 = "Open"; + playSound("189_436_bed down 1.wav"); + } else { + _statics->_v2 = "NotOnWashstand"; + playSound("192_436_bed hits floor.wav"); + } + + playMovie(_startFrame, _endFrame, MOVIE_GAMESTATE); + } + + if (_statics->_v2 == "Open") + _statics->_v1 = "Closed"; + else if (_statics->_v2 == "NotOnWashstand") + _statics->_v1 = "ClosedWrong"; + + return true; +} + +bool CBedfoot::TurnOff(CTurnOff *msg) { + if (_statics->_v1 == "Closed" || _statics->_v1 == "ClosedWrong") { + setVisible(true); + CVisibleMsg visibleMsg(false); + visibleMsg.execute("Bedhead"); + } + + if (_statics->_v2 == "Open" && _statics->_v1 == "Closed") { + _fieldE0 = 0; + _startFrame = 20; + if (_statics->_v4 == "Closed") { + _statics->_v2 = "Closed"; + _endFrame = 30; + } else { + _statics->_v2 = "RestingUnderTV"; + _endFrame = 25; + } + + playMovie(_startFrame, _endFrame, MOVIE_GAMESTATE); + playSound("b#7.wav"); + + } else if (_statics->_v2 == "NotOnWashstand" && _statics->_v1 == "ClosedWrong") { + _fieldE0 = 0; + _startFrame = 17; + + if (_statics->_v4 == "Closed") { + _statics->_v2 = "Closed"; + _endFrame = 30; + } else { + _statics->_v2 = "RestingUnderTV"; + _endFrame = 25; + } + + playMovie(_startFrame, _endFrame, MOVIE_GAMESTATE); + playSound("b#7.wav"); + + } else if (_statics->_v2 == "RestingUTV" && _statics->_v4 == "Closed") { + _statics->_v2 = "Closed"; + _startFrame = 25; + _endFrame = 30; + playMovie(25, 30, MOVIE_GAMESTATE); + playSound("b#7.wav"); + } + + if (_statics->_v2 == "Closed") + _statics->_v1 = "Closed"; + + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/sgt/bedfoot.h b/engines/titanic/game/sgt/bedfoot.h index df3db42d6d..cc7b82b075 100644 --- a/engines/titanic/game/sgt/bedfoot.h +++ b/engines/titanic/game/sgt/bedfoot.h @@ -28,6 +28,9 @@ namespace Titanic { class CBedfoot : public CSGTStateRoom { + DECLARE_MESSAGE_MAP; + bool TurnOn(CTurnOn *msg); + bool TurnOff(CTurnOff *msg); public: CLASSDEF; diff --git a/engines/titanic/game/sgt/bedhead.cpp b/engines/titanic/game/sgt/bedhead.cpp index fad7272f3a..6f427ab625 100644 --- a/engines/titanic/game/sgt/bedhead.cpp +++ b/engines/titanic/game/sgt/bedhead.cpp @@ -24,6 +24,11 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CBedhead, CSGTStateRoom) + ON_MESSAGE(TurnOn) + ON_MESSAGE(TurnOff) +END_MESSAGE_MAP() + void CBedhead::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); CSGTStateRoom::save(file, indent); @@ -34,4 +39,14 @@ void CBedhead::load(SimpleFile *file) { CSGTStateRoom::load(file); } +bool CBedhead::TurnOn(CTurnOn *msg) { + // TODO + return true; +} + +bool CBedhead::TurnOff(CTurnOff *msg) { + // TODO + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/sgt/bedhead.h b/engines/titanic/game/sgt/bedhead.h index f1ba31786c..665dec021c 100644 --- a/engines/titanic/game/sgt/bedhead.h +++ b/engines/titanic/game/sgt/bedhead.h @@ -28,6 +28,9 @@ namespace Titanic { class CBedhead : public CSGTStateRoom { + DECLARE_MESSAGE_MAP; + bool TurnOn(CTurnOn *msg); + bool TurnOff(CTurnOff *msg); public: CLASSDEF; diff --git a/engines/titanic/game/sgt/chest_of_drawers.cpp b/engines/titanic/game/sgt/chest_of_drawers.cpp index be62e12c8e..d9c72d3021 100644 --- a/engines/titanic/game/sgt/chest_of_drawers.cpp +++ b/engines/titanic/game/sgt/chest_of_drawers.cpp @@ -24,6 +24,12 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CChestOfDrawers, CSGTStateRoom) + ON_MESSAGE(TurnOn) + ON_MESSAGE(TurnOff) + ON_MESSAGE(MovieEndMsg) +END_MESSAGE_MAP() + void CChestOfDrawers::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); CSGTStateRoom::save(file, indent); @@ -34,4 +40,41 @@ void CChestOfDrawers::load(SimpleFile *file) { CSGTStateRoom::load(file); } +bool CChestOfDrawers::TurnOn(CTurnOn *msg) { + if (_statics->_v6 == "Closed" && _statics->_v5 == "Open") { + _fieldE0 = false; + _statics->_v6 = "Open"; + _startFrame = 1; + _endFrame = 14; + playSound("b#11.wav"); + } + + return true; +} + +bool CChestOfDrawers::TurnOff(CTurnOff *msg) { + if (_statics->_v6 == "Open" && _statics->_v5 == "Closed") { + CVisibleMsg visibleMsg; + visibleMsg.execute("Drawer"); + _statics->_v6 = "Closed"; + _fieldE0 = true; + + _startFrame = 14; + _endFrame = 27; + playMovie(14, 27, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); + playSound("b#11.wav"); + } + + return true; +} + +bool CChestOfDrawers::MovieEndMsg(CMovieEndMsg *msg) { + if (_statics->_v6 == "Open") { + CVisibleMsg visibleMsg; + visibleMsg.execute("Drawer"); + } + + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/sgt/chest_of_drawers.h b/engines/titanic/game/sgt/chest_of_drawers.h index 16a1bf8fea..5bf852902b 100644 --- a/engines/titanic/game/sgt/chest_of_drawers.h +++ b/engines/titanic/game/sgt/chest_of_drawers.h @@ -28,6 +28,10 @@ namespace Titanic { class CChestOfDrawers : public CSGTStateRoom { + DECLARE_MESSAGE_MAP; + bool TurnOn(CTurnOn *msg); + bool TurnOff(CTurnOff *msg); + bool MovieEndMsg(CMovieEndMsg *msg); public: CLASSDEF; diff --git a/engines/titanic/game/sgt/desk.cpp b/engines/titanic/game/sgt/desk.cpp index 4dd0fdab92..51be14adb9 100644 --- a/engines/titanic/game/sgt/desk.cpp +++ b/engines/titanic/game/sgt/desk.cpp @@ -24,6 +24,12 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CDesk, CSGTStateRoom) + ON_MESSAGE(TurnOn) + ON_MESSAGE(TurnOff) + ON_MESSAGE(MovieEndMsg) +END_MESSAGE_MAP() + void CDesk::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); CSGTStateRoom::save(file, indent); @@ -34,4 +40,44 @@ void CDesk::load(SimpleFile *file) { CSGTStateRoom::load(file); } +bool CDesk::TurnOn(CTurnOn *msg) { + if (_statics->_v5 == "Closed" && _statics->_v1 == "RestingG" + && _statics->_v1 == "OpenWrong") { + _statics->_v5 = "Open"; + _fieldE0 = false; + _startFrame = 1; + _endFrame = 26; + playMovie(1, 26, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); + playSound("b#12.wav"); + } + + return true; +} + +bool CDesk::TurnOff(CTurnOff *msg) { + if (_statics->_v5 == "Open" && _statics->_v6 == "Closed" + && _statics->_v1 == "Open") { + CVisibleMsg visibleMsg(false); + visibleMsg.execute("ChestOfDrawers"); + + _statics->_v5 = "Closed"; + _fieldE0 = true; + _startFrame = 26; + _endFrame = 51; + playMovie(26, 51, MOVIE_GAMESTATE); + playSound("b#9.wav"); + } + + return true; +} + +bool CDesk::MovieEndMsg(CMovieEndMsg *msg) { + if (_statics->_v5 == "Open") { + CVisibleMsg visibleMsg(true); + visibleMsg.execute("ChestOfDrawers"); + } + + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/sgt/desk.h b/engines/titanic/game/sgt/desk.h index 77b5fa17af..8b9e1fe841 100644 --- a/engines/titanic/game/sgt/desk.h +++ b/engines/titanic/game/sgt/desk.h @@ -28,6 +28,10 @@ namespace Titanic { class CDesk : public CSGTStateRoom { + DECLARE_MESSAGE_MAP; + bool TurnOn(CTurnOn *msg); + bool TurnOff(CTurnOff *msg); + bool MovieEndMsg(CMovieEndMsg *msg); public: CLASSDEF; diff --git a/engines/titanic/game/sgt/deskchair.cpp b/engines/titanic/game/sgt/deskchair.cpp index a4a2badeb0..7f64c2ee34 100644 --- a/engines/titanic/game/sgt/deskchair.cpp +++ b/engines/titanic/game/sgt/deskchair.cpp @@ -24,6 +24,13 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CDeskchair, CSGTStateRoom) + ON_MESSAGE(TurnOn) + ON_MESSAGE(TurnOff) + ON_MESSAGE(ActMsg) + ON_MESSAGE(MovieEndMsg) +END_MESSAGE_MAP() + void CDeskchair::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); CSGTStateRoom::save(file, indent); @@ -34,4 +41,49 @@ void CDeskchair::load(SimpleFile *file) { CSGTStateRoom::load(file); } +bool CDeskchair::TurnOn(CTurnOn *msg) { + if (_statics->_v8 == "Closed" && _statics->_v9 == "Closed") { + setVisible(true); + _statics->_v9 = "Open"; + _fieldE0 = false; + _startFrame = 0; + _endFrame = 16; + playMovie(0, 16, MOVIE_GAMESTATE); + playSound("b#8.wav"); + } + + return true; +} + +bool CDeskchair::TurnOff(CTurnOff *msg) { + if (_statics->_v9 == "Open") { + _statics->_v9 = "Closed"; + _fieldE0 = true; + _startFrame = 16; + _endFrame = 32; + playMovie(16, 32, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); + playSound("b#2.wav"); + } + + return true; +} + +bool CDeskchair::ActMsg(CActMsg *msg) { + if (msg->_action == "Smash") { + setVisible(false); + _statics->_v9 = "Closed"; + _fieldE0 = true; + loadFrame(0); + return true; + } else { + return CSGTStateRoom::ActMsg(msg); + } +} + +bool CDeskchair::MovieEndMsg(CMovieEndMsg *msg) { + if (_statics->_v9 == "Closed") + setVisible(false); + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/sgt/deskchair.h b/engines/titanic/game/sgt/deskchair.h index 5181b650d2..6e7bbe4169 100644 --- a/engines/titanic/game/sgt/deskchair.h +++ b/engines/titanic/game/sgt/deskchair.h @@ -28,6 +28,11 @@ namespace Titanic { class CDeskchair : public CSGTStateRoom { + DECLARE_MESSAGE_MAP; + bool TurnOn(CTurnOn *msg); + bool TurnOff(CTurnOff *msg); + bool ActMsg(CActMsg *msg); + bool MovieEndMsg(CMovieEndMsg *msg); public: CLASSDEF; diff --git a/engines/titanic/game/sgt/sgt_state_room.cpp b/engines/titanic/game/sgt/sgt_state_room.cpp index 55f08de8b4..c089e401b8 100644 --- a/engines/titanic/game/sgt/sgt_state_room.cpp +++ b/engines/titanic/game/sgt/sgt_state_room.cpp @@ -21,17 +21,22 @@ */ #include "titanic/game/sgt/sgt_state_room.h" +#include "titanic/pet_control/pet_control.h" namespace Titanic { BEGIN_MESSAGE_MAP(CSGTStateRoom, CBackground) + ON_MESSAGE(ActMsg) + ON_MESSAGE(VisibleMsg) ON_MESSAGE(EnterRoomMsg) + ON_MESSAGE(LeaveRoomMsg) END_MESSAGE_MAP() CSGTStateRoomStatics *CSGTStateRoom::_statics; void CSGTStateRoom::init() { _statics = new CSGTStateRoomStatics(); + _statics->_v1 = "Closed"; } void CSGTStateRoom::deinit() { @@ -94,8 +99,80 @@ void CSGTStateRoom::load(SimpleFile *file) { CBackground::load(file); } +bool CSGTStateRoom::ActMsg(CActMsg *msg) { + CPetControl *pet = getPetControl(); + uint roomFlags = pet->getRoomFlags(); + uint assignedRoom = pet->getAssignedRoomFlags(); + + if (roomFlags != assignedRoom) { + petDisplayMessage("This is not your assigned room. Please do not enjoy."); + } else if (_fieldE0) { + CTurnOn onMsg; + onMsg.execute(this); + } else { + CTurnOff offMsg; + offMsg.execute(this); + } + + return true; +} + +bool CSGTStateRoom::VisibleMsg(CVisibleMsg *msg) { + setVisible(msg->_visible); + return true; +} + bool CSGTStateRoom::EnterRoomMsg(CEnterRoomMsg *msg) { - warning("CSGTStateRoom::handleEvent"); + CPetControl *pet = getPetControl(); + uint roomFlags = pet->getRoomFlags(); + uint assignedRoom = pet->getAssignedRoomFlags(); + + if (roomFlags == assignedRoom) { + loadFrame(_fieldE8); + _fieldE0 = _fieldEC; + setVisible(_fieldF0); + + if (isEquals("Desk") && _statics->_v5 == "Closed") + loadFrame(1); + } + + if (isEquals("Drawer")) { + petSetArea(PET_REMOTE); + if (roomFlags == assignedRoom && getPassengerClass() == 3 + && _statics->_v13) { + playSound("b#21.wav"); + _statics->_v13 = 0; + } + + _statics->_v7 = "Closed"; + setVisible(false); + _fieldE0 = true; + } else if (roomFlags != assignedRoom) { + loadFrame(0); + if (_fieldE4) { + setVisible(true); + if (isEquals("Desk")) + loadFrame(1); + } else { + setVisible(false); + } + } + + return true; +} + +bool CSGTStateRoom::LeaveRoomMsg(CLeaveRoomMsg *msg) { + CPetControl *pet = getPetControl(); + uint roomFlags = pet->getRoomFlags(); + uint assignedRoom = pet->getAssignedRoomFlags(); + + if (roomFlags == assignedRoom) { + _fieldE8 = getMovieFrame(); + _fieldEC = _fieldE0; + _fieldF0 = _visible; + } + + _statics->_v14 = roomFlags; return true; } diff --git a/engines/titanic/game/sgt/sgt_state_room.h b/engines/titanic/game/sgt/sgt_state_room.h index d9ffdb8e9e..3975f7b59b 100644 --- a/engines/titanic/game/sgt/sgt_state_room.h +++ b/engines/titanic/game/sgt/sgt_state_room.h @@ -47,15 +47,18 @@ struct CSGTStateRoomStatics { class CSGTStateRoom : public CBackground { DECLARE_MESSAGE_MAP; + bool ActMsg(CActMsg *msg); + bool VisibleMsg(CVisibleMsg *msg); bool EnterRoomMsg(CEnterRoomMsg *msg); + bool LeaveRoomMsg(CLeaveRoomMsg *msg); protected: static CSGTStateRoomStatics *_statics; protected: - int _fieldE0; + bool _fieldE0; int _fieldE4; int _fieldE8; - int _fieldEC; - int _fieldF0; + bool _fieldEC; + bool _fieldF0; public: CLASSDEF; CSGTStateRoom(); diff --git a/engines/titanic/game/sgt/sgt_upper_doors_sound.cpp b/engines/titanic/game/sgt/sgt_upper_doors_sound.cpp index ed37b0a5c7..72cd7f9037 100644 --- a/engines/titanic/game/sgt/sgt_upper_doors_sound.cpp +++ b/engines/titanic/game/sgt/sgt_upper_doors_sound.cpp @@ -25,19 +25,19 @@ namespace Titanic { CSGTUpperDoorsSound::CSGTUpperDoorsSound() { - _string2 = "b#53.wav"; + _soundName = "b#53.wav"; } void CSGTUpperDoorsSound::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); - file->writeQuotedLine(_string2, indent); + file->writeQuotedLine(_soundName, indent); CClickResponder::save(file, indent); } void CSGTUpperDoorsSound::load(SimpleFile *file) { file->readNumber(); - _string2 = file->readString(); + _soundName = file->readString(); CClickResponder::load(file); } diff --git a/engines/titanic/game/transport/pellerator.cpp b/engines/titanic/game/transport/pellerator.cpp index e789c20a3d..5bc2423478 100644 --- a/engines/titanic/game/transport/pellerator.cpp +++ b/engines/titanic/game/transport/pellerator.cpp @@ -21,34 +21,344 @@ */ #include "titanic/game/transport/pellerator.h" +#include "titanic/core/room_item.h" namespace Titanic { +static const char *const WAVE_NAMES[10] = { + "z#465.wav", "z#456.wav", "z#455.wav", "z#453.wav", + "z#452.wav", "NoStandingInFunnyWays", "z#450.wav", + "z#449.wav", "z#435.wav", "z#434.wav" +}; + BEGIN_MESSAGE_MAP(CPellerator, CTransport) + ON_MESSAGE(StatusChangeMsg) ON_MESSAGE(EnterRoomMsg) + ON_MESSAGE(MovieEndMsg) + ON_MESSAGE(TimerMsg) END_MESSAGE_MAP() -int CPellerator::_v1; -int CPellerator::_v2; +int CPellerator::_soundHandle; +int CPellerator::_destination; + +CPellerator::CPellerator() : CTransport() { +} void CPellerator::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); - file->writeNumberLine(_v1, indent); - file->writeNumberLine(_v2, indent); + file->writeNumberLine(_soundHandle, indent); + file->writeNumberLine(_destination, indent); CTransport::save(file, indent); } void CPellerator::load(SimpleFile *file) { file->readNumber(); - _v1 = file->readNumber(); - _v2 = file->readNumber(); + _soundHandle = file->readNumber(); + _destination = file->readNumber(); CTransport::load(file); } +bool CPellerator::StatusChangeMsg(CStatusChangeMsg *msg) { + setVisible(true); + playGlobalSound("z#74.wav", -2, true, true, 0); + int classNum = getPassengerClass(); + int newDest = msg->_newStatus; + + if (msg->_newStatus == _destination) { + petDisplayMessage(1, "You are already at your chosen destination."); + } else if (classNum == 3 || (msg->_newStatus > 4 && classNum != 1)) { + petDisplayMessage(1, "Passengers of your class are not permitted to enter this area."); + } else if (newDest > _destination) { + CString name = getName(); + changeView(name == "PelleratorObject2" ? + "Pellerator.Node 1.N" : "Pellerator.Node 1.S"); + + if (name == "PelleratorObject") { + for (; _destination < newDest; ++_destination) { + switch (_destination) { + case 0: + case 1: + playMovie(315, 323, 0); + for (int idx = 0; idx < 3; ++idx) + playMovie(299, 304, 0); + playMovie(305, 313, MOVIE_GAMESTATE); + break; + + case 2: + playMovie(315, 323, 0); + for (int idx = 0; idx < 3; ++idx) + playMovie(299, 304, 0); + for (int idx = 0; idx < 5; ++idx) + playMovie(253, 263, 0); + playMovie(153, 197, 0); + for (int idx = 0; idx < 5; ++idx) + playMovie(253, 263, 0); + playMovie(290, 293, MOVIE_GAMESTATE); + break; + + case 4: + playMovie(267, 270, 0); + for (int idx = 0; idx < 5; ++idx) + playMovie(253, 263, 0); + playMovie(3, 71, 0); + for (int idx = 0; idx < 3; ++idx) + playMovie(253, 263, 0); + for (int idx = 0; idx < 7; ++idx) + playMovie(336, 341, 0); + playMovie(342, 348, MOVIE_GAMESTATE); + break; + + case 5: + playMovie(315, 323, 0); + for (int idx = 0; idx < 7; ++idx) + playMovie(299, 304, 0); + for (int idx = 0; idx < 3; ++idx) + playMovie(253, 263, 0); + playMovie(3, 71, 0); + for (int idx = 0; idx < 3; ++idx) + playMovie(299, 304, 0); + + } + } + } else { + for (; _destination < newDest; ++_destination) { + switch (_destination) { + case 0: + case 1: + playMovie(315, 323, 0); + for (int idx = 0; idx < 3; ++idx) + playMovie(299, 304, 0); + playMovie(305, 313, MOVIE_GAMESTATE); + break; + + case 2: + playMovie(315, 323, 0); + for (int idx = 0; idx < 4; ++idx) + playMovie(299, 304, 0); + for (int idx = 0; idx < 15; ++idx) + playMovie(245, 255, 0); + playMovie(264, 267, MOVIE_GAMESTATE); + ++_destination; + break; + + case 4: + playMovie(241, 244, 0); + for (int idx = 0; idx < 15; ++idx) + playMovie(245, 255, 0); + for (int idx = 0; idx < 7; ++idx) + playMovie(336, 341, 0); + playMovie(342, 348, MOVIE_GAMESTATE); + break; + + case 5: + playMovie(315, 323, 0); + for (int idx = 0; idx < 7; ++idx) + playMovie(229, 304, 0); + for (int idx = 0; idx < 12; ++idx) + playMovie(245, 255, 0); + for (int idx = 0; idx < 3; ++idx) + playMovie(299, 304, 0); + playMovie(305, 313, MOVIE_GAMESTATE); + break; + + default: + break; + } + } + } + + playMovie(264, 264, MOVIE_NOTIFY_OBJECT); + _destination = newDest; + } else if (newDest < _destination) { + CString name = getName(); + changeView(name == "PelleratorObject2" ? + "Pellerator.Node 1.N" : "Pellerator.Node 1.S"); + + if (name == "PelleratorObject") { + for (; _destination > newDest; --_destination) { + switch (_destination) { + case 0: + case 1: + playMovie(351, 359, 0); + for (int idx = 0; idx < 3; ++idx) + playMovie(336, 341, 0); + playMovie(342, 348, MOVIE_GAMESTATE); + break; + + case 3: + playMovie(241, 244, 0); + for (int idx = 0; idx < 5; ++idx) + playMovie(245, 255, 0); + playMovie(197, 239, 0); + for (int idx = 0; idx < 5; ++idx) + playMovie(245, 255, 0); + for (int idx = 0; idx < 3; ++idx) + playMovie(336, 341, 0); + playMovie(342, 348, MOVIE_GAMESTATE); + --_destination; + break; + + case 4: + playMovie(315, 323, 0); + for (int idx = 0; idx < 7; ++idx) + playMovie(299, 304, 0); + for (int idx = 0; idx < 3; ++idx) + playMovie(245, 255, 0); + playMovie(78, 149, 0); + for (int idx = 0; idx < 5; ++idx) + playMovie(245, 255, 0); + playMovie(264, 267, MOVIE_GAMESTATE); + break; + + case 5: + playMovie(351, 359, 0); + for (int idx = 0; idx < 7; ++idx) + playMovie(336, 341, 0); + for (int idx = 0; idx < 3; ++idx) + playMovie(245, 255, 0); + playMovie(78, 149, 0); + for (int idx = 0; idx < 3; ++idx) + playMovie(336, 341, 0); + playMovie(342, 348, MOVIE_GAMESTATE); + break; + + default: + break; + } + } + } else { + for (; _destination > newDest; --_destination) { + switch (_destination) { + case 0: + case 1: + playMovie(351, 359, 0); + for (int idx = 0; idx < 3; ++idx) + playMovie(336, 341, 0); + playMovie(342, 348, MOVIE_GAMESTATE); + break; + + case 3: + playMovie(267, 270, 0); + for (int idx = 0; idx < 15; ++idx) + playMovie(253, 263, 0); + for (int idx = 0; idx < 3; ++idx) + playMovie(336, 341, 0); + playMovie(342, 348, MOVIE_GAMESTATE); + --_destination; + break; + + case 4: + playMovie(315, 323, 0); + for (int idx = 0; idx < 7; ++idx) + playMovie(299, 304, 0); + for (int idx = 0; idx < 15; ++idx) + playMovie(253, 263, 0); + playMovie(290, 293, MOVIE_GAMESTATE); + break; + + case 5: + playMovie(351, 359, 0); + for (int idx = 0; idx < 7; ++idx) + playMovie(336, 341, 0); + for (int idx = 0; idx < 13; ++idx) + playMovie(253, 263, 0); + for (int idx = 0; idx < 3; ++idx) + playMovie(336, 341, 0); + playMovie(342, 348, MOVIE_GAMESTATE); + break; + + default: + break; + } + } + } + + playMovie(264, 264, MOVIE_NOTIFY_OBJECT); + _destination = newDest; + } + + CStatusChangeMsg statusMsg; + statusMsg._newStatus = _destination; + statusMsg.execute("ExitPellerator"); + + return true; +} + bool CPellerator::EnterRoomMsg(CEnterRoomMsg *msg) { - warning("CPellerator::handleEvent"); + if (isEquals("PelleratorObject")) { + for (int idx = 0; idx < 10; ++idx) + loadSound(WAVE_NAMES[idx]); + addTimer(10000); + } + + CString name = msg->_oldRoom ? msg->_oldRoom->getName() : ""; + int oldVal = _destination; + + if (name.empty()) { + _destination = 4; + oldVal = 4; + } else if (name == "PromenadeDeck") { + _destination = 0; + } else if (name == "MusicRoomLobby") { + _destination = 1; + } else if (name == "Bar") { + _destination = 2; + } else if (name == "TopOfWell") { + _destination = 4; + } else if (name == "1stClassRestaurant") { + _destination = 5; + } else if (name == "Arboretum" || name == "FrozenArboretum") { + _destination = 6; + } + + if (_destination != oldVal) { + CStatusChangeMsg statusMsg; + statusMsg._newStatus = _destination; + statusMsg.execute("ExitPellerator"); + } + + loadFrame(264); + return true; +} + +bool CPellerator::MovieEndMsg(CMovieEndMsg *msg) { + setVisible(false); + stopGlobalSound(true, -1); + + switch (_destination) { + case 0: + _soundHandle = queueSound("z#429.wav", _soundHandle); + break; + case 1: + _soundHandle = queueSound("z#430.wav", _soundHandle); + break; + case 2: + _soundHandle = queueSound("z#431.wav", _soundHandle); + break; + case 4: + _soundHandle = queueSound("z#428.wav", _soundHandle); + break; + case 5: + _soundHandle = queueSound("z#433.wav", _soundHandle); + break; + case 6: + _soundHandle = queueSound("z#432.wav", _soundHandle); + break; + default: + break; + } + + return true; +} + +bool CPellerator::TimerMsg(CTimerMsg *msg) { + if (compareRoomNameTo("Pellerator")) { + _soundHandle = queueSound(WAVE_NAMES[getRandomNumber(9)], _soundHandle); + addTimer(20000 + getRandomNumber(10000)); + } + return true; } diff --git a/engines/titanic/game/transport/pellerator.h b/engines/titanic/game/transport/pellerator.h index fa400a49cd..c634f435cc 100644 --- a/engines/titanic/game/transport/pellerator.h +++ b/engines/titanic/game/transport/pellerator.h @@ -30,12 +30,16 @@ namespace Titanic { class CPellerator : public CTransport { DECLARE_MESSAGE_MAP; + bool StatusChangeMsg(CStatusChangeMsg *msg); bool EnterRoomMsg(CEnterRoomMsg *msg); -private: - static int _v1; - static int _v2; + bool MovieEndMsg(CMovieEndMsg *msg); + bool TimerMsg(CTimerMsg *msg); +public: + static int _soundHandle; + static int _destination; public: CLASSDEF; + CPellerator(); /** * Save the data for the class to file diff --git a/engines/titanic/gfx/changes_season_button.cpp b/engines/titanic/gfx/changes_season_button.cpp index d5242ad890..584a9542f3 100644 --- a/engines/titanic/gfx/changes_season_button.cpp +++ b/engines/titanic/gfx/changes_season_button.cpp @@ -21,9 +21,14 @@ */ #include "titanic/gfx/changes_season_button.h" +#include "titanic/core/project_item.h" namespace Titanic { +BEGIN_MESSAGE_MAP(CChangesSeasonButton, CSTButton) + ON_MESSAGE(MouseButtonDownMsg) +END_MESSAGE_MAP() + CChangesSeasonButton::CChangesSeasonButton() : CSTButton() { } @@ -37,4 +42,10 @@ void CChangesSeasonButton::load(SimpleFile *file) { CSTButton::load(file); } +bool CChangesSeasonButton::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { + CChangeSeasonMsg changeMsg(_actionName); + changeMsg.execute(getRoot(), nullptr, MSGFLAG_SCAN); + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/gfx/changes_season_button.h b/engines/titanic/gfx/changes_season_button.h index 2b58a3199b..4f588187eb 100644 --- a/engines/titanic/gfx/changes_season_button.h +++ b/engines/titanic/gfx/changes_season_button.h @@ -28,6 +28,8 @@ namespace Titanic { class CChangesSeasonButton : public CSTButton { + DECLARE_MESSAGE_MAP; + bool MouseButtonDownMsg(CMouseButtonDownMsg *msg); public: CLASSDEF; CChangesSeasonButton(); diff --git a/engines/titanic/gfx/chev_switch.cpp b/engines/titanic/gfx/chev_switch.cpp index a6ce93098c..177f0ada76 100644 --- a/engines/titanic/gfx/chev_switch.cpp +++ b/engines/titanic/gfx/chev_switch.cpp @@ -24,7 +24,13 @@ namespace Titanic { -CChevSwitch::CChevSwitch() : CToggleSwitch() { +BEGIN_MESSAGE_MAP(CChevSwitch, CToggleSwitch) + ON_MESSAGE(MouseButtonUpMsg) + ON_MESSAGE(SetChevButtonImageMsg) + ON_MESSAGE(MouseButtonDownMsg) +END_MESSAGE_MAP() + +CChevSwitch::CChevSwitch() : CToggleSwitch(), _value(0) { } void CChevSwitch::save(SimpleFile *file, int indent) { @@ -37,4 +43,36 @@ void CChevSwitch::load(SimpleFile *file) { CToggleSwitch::load(file); } +bool CChevSwitch::MouseButtonUpMsg(CMouseButtonUpMsg *msg) { + return true; +} + +bool CChevSwitch::SetChevButtonImageMsg(CSetChevButtonImageMsg *msg) { + if (msg->_value2 && getParent()) { + error("TODO: Don't know parent type"); + } + + _fieldBC = msg->_value1; + if (_fieldBC) { + loadImage((_value & 1) ? "on_odd.tga" : "on_even.tga"); + } else { + loadImage((_value & 1) ? "off_odd.tga" : "off_even.tga"); + } + + return true; +} + +bool CChevSwitch::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { + _fieldBC ^= 1; + if (getParent()) { + CSetChevPanelBitMsg bitMsg(_value, _fieldBC); + bitMsg.execute(getParent()); + } + + CSetChevButtonImageMsg chevMsg(_fieldBC, 0); + chevMsg.execute(this); + + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/gfx/chev_switch.h b/engines/titanic/gfx/chev_switch.h index 0305a6ca83..01da53c854 100644 --- a/engines/titanic/gfx/chev_switch.h +++ b/engines/titanic/gfx/chev_switch.h @@ -28,6 +28,12 @@ namespace Titanic { class CChevSwitch : public CToggleSwitch { + DECLARE_MESSAGE_MAP; + bool MouseButtonUpMsg(CMouseButtonUpMsg *msg); + bool SetChevButtonImageMsg(CSetChevButtonImageMsg *msg); + bool MouseButtonDownMsg(CMouseButtonDownMsg *msg); +public: + int _value; public: CLASSDEF; CChevSwitch(); diff --git a/engines/titanic/gfx/toggle_switch.h b/engines/titanic/gfx/toggle_switch.h index ae96c75ebd..8e7d057d8c 100644 --- a/engines/titanic/gfx/toggle_switch.h +++ b/engines/titanic/gfx/toggle_switch.h @@ -28,7 +28,7 @@ namespace Titanic { class CToggleSwitch : public CGameObject { -private: +protected: int _fieldBC; Point _pos1; public: diff --git a/engines/titanic/messages/bilge_dispensor_event.cpp b/engines/titanic/messages/bilge_dispensor_event.cpp index 043ffe75d3..584da00a6f 100644 --- a/engines/titanic/messages/bilge_dispensor_event.cpp +++ b/engines/titanic/messages/bilge_dispensor_event.cpp @@ -24,6 +24,13 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CBilgeDispensorEvent, CAutoSoundEvent) + ON_MESSAGE(EnterRoomMsg) + ON_MESSAGE(LeaveRoomMsg) + ON_MESSAGE(FrameMsg) + ON_MESSAGE(StatusChangeMsg) +END_MESSAGE_MAP() + void CBilgeDispensorEvent::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); CAutoSoundEvent::save(file, indent); @@ -39,4 +46,32 @@ bool CBilgeDispensorEvent::EnterRoomMsg(CEnterRoomMsg *msg) { return true; } +bool CBilgeDispensorEvent::LeaveRoomMsg(CLeaveRoomMsg *msg) { + _value1 = -1; + return true; +} + +bool CBilgeDispensorEvent::FrameMsg(CFrameMsg *msg) { + if (_value1 >= 0 && (_value1 & 0xffff) == 0x4000) { + int volume = 20 + getRandomNumber(30); + int val3 = getRandomNumber(20) - 10; + + if (getRandomNumber(2) == 0) { + playSound("b#18.wav", volume, val3); + } + } + + CAutoSoundEvent::FrameMsg(msg); + return true; +} + +bool CBilgeDispensorEvent::StatusChangeMsg(CStatusChangeMsg *msg) { + if (msg->_newStatus == 1) + _value1 = -1; + else if (msg->_newStatus == 2) + _value1 = 0; + + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/messages/bilge_dispensor_event.h b/engines/titanic/messages/bilge_dispensor_event.h index 96ef92a54e..61d3116db4 100644 --- a/engines/titanic/messages/bilge_dispensor_event.h +++ b/engines/titanic/messages/bilge_dispensor_event.h @@ -29,9 +29,14 @@ namespace Titanic { class CBilgeDispensorEvent : public CAutoSoundEvent { + DECLARE_MESSAGE_MAP; bool EnterRoomMsg(CEnterRoomMsg *msg); + bool LeaveRoomMsg(CLeaveRoomMsg *msg); + bool FrameMsg(CFrameMsg *msg); + bool StatusChangeMsg(CStatusChangeMsg *msg); public: CLASSDEF; + CBilgeDispensorEvent() : CAutoSoundEvent() {} /** * Save the data for the class to file diff --git a/engines/titanic/messages/door_auto_sound_event.cpp b/engines/titanic/messages/door_auto_sound_event.cpp index b9cedae6de..7618577e50 100644 --- a/engines/titanic/messages/door_auto_sound_event.cpp +++ b/engines/titanic/messages/door_auto_sound_event.cpp @@ -24,6 +24,12 @@ namespace Titanic { +BEGIN_MESSAGE_MAP(CDoorAutoSoundEvent, CAutoSoundEvent) + ON_MESSAGE(PreEnterNodeMsg) + ON_MESSAGE(LeaveNodeMsg) + ON_MESSAGE(TimerMsg) +END_MESSAGE_MAP() + void CDoorAutoSoundEvent::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); file->writeQuotedLine(_string1, indent); @@ -44,4 +50,16 @@ void CDoorAutoSoundEvent::load(SimpleFile *file) { CAutoSoundEvent::load(file); } +bool CDoorAutoSoundEvent::PreEnterNodeMsg(CPreEnterNodeMsg *msg) { + return true; +} + +bool CDoorAutoSoundEvent::LeaveNodeMsg(CLeaveNodeMsg *msg) { + return true; +} + +bool CDoorAutoSoundEvent::TimerMsg(CTimerMsg *msg) { + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/messages/door_auto_sound_event.h b/engines/titanic/messages/door_auto_sound_event.h index e6ea1b0f98..8b064a7221 100644 --- a/engines/titanic/messages/door_auto_sound_event.h +++ b/engines/titanic/messages/door_auto_sound_event.h @@ -28,6 +28,10 @@ namespace Titanic { class CDoorAutoSoundEvent : public CAutoSoundEvent { + DECLARE_MESSAGE_MAP; + bool PreEnterNodeMsg(CPreEnterNodeMsg *msg); + bool LeaveNodeMsg(CLeaveNodeMsg *msg); + bool TimerMsg(CTimerMsg *msg); public: CString _string1; CString _string2; diff --git a/engines/titanic/messages/messages.h b/engines/titanic/messages/messages.h index 71807f4e99..e6d494ebc8 100644 --- a/engines/titanic/messages/messages.h +++ b/engines/titanic/messages/messages.h @@ -206,15 +206,13 @@ MESSAGE1(CAnimateMaitreDMsg, int, value, 0); MESSAGE1(CArboretumGateMsg, int, value, 0); MESSAGE0(CArmPickedUpFromTableMsg); MESSAGE0(CBodyInBilgeRoomMsg); -MESSAGE1(CBowlStateChange, int, value, 0); +MESSAGE1(CBowlStateChangeMsg, int, value, 0); MESSAGE2(CCarryObjectArrivedMsg, CString, strValue, "", int, numValue, 0); MESSAGE2(CChangeMusicMsg, CString, filename, "", int, flags, 0); MESSAGE1(CChangeSeasonMsg, CString, season, "Summer"); MESSAGE0(CCheckAllPossibleCodes); -MESSAGE2(CCheckChevCode, int, value1, 0, int, value2, 0); +MESSAGE2(CCheckChevCode, int, classNum, 0, uint, chevCode, 0); MESSAGE1(CChildDragEndMsg, int, value, 0); -MESSAGE2(CChildDragMoveMsg, int, value1, 0, int, value2, 0); -MESSAGE2(CChildDragStartMsg, int, value1, 0, int, value2, 0); MESSAGE0(CClearChevPanelBits); MESSAGE0(CCorrectMusicPlayedMsg); MESSAGE0(CCreateMusicPlayerMsg); @@ -228,8 +226,8 @@ MESSAGE0(CDonNavHelmet); MESSAGE1(CDoorbotNeededInElevatorMsg, int, value, 0); MESSAGE0(CDoorbotNeededInHomeMsg); MESSAGE1(CDropObjectMsg, CCarry *, item, nullptr); -MESSAGE1(CDropZoneGotObjectMsg, int, value, 0); -MESSAGE1(CDropZoneLostObjectMsg, int, value, 0); +MESSAGE1(CDropZoneGotObjectMsg, CGameObject *, object, nullptr); +MESSAGE1(CDropZoneLostObjectMsg, CGameObject *, object, nullptr); MESSAGE1(CEjectCylinderMsg, int, value, 0); MESSAGE2(CPreEnterNodeMsg, CNodeItem *, oldNode, nullptr, CNodeItem *, newNode, nullptr); MESSAGE2(CPreEnterRoomMsg, CRoomItem *, oldRoom, nullptr, CRoomItem *, newRoom, nullptr); @@ -240,16 +238,16 @@ MESSAGE2(CEnterViewMsg, CViewItem *, oldView, nullptr, CViewItem *, newView, nul MESSAGE0(CErasePhonographCylinderMsg); MESSAGE1(CFrameMsg, uint, ticks, 0); MESSAGE2(CFreshenCookieMsg, int, value1, 0, int, value2, 0); -MESSAGE1(CGetChevClassBits, int, value, 0); -MESSAGE1(CGetChevClassNum, int, value, 0); -MESSAGE2(CGetChevCodeFromRoomNameMsg, CString, strValue, "", int, numValue, 0); -MESSAGE1(CGetChevFloorBits, int, value, 0); -MESSAGE1(CGetChevFloorNum, int, value, 0); -MESSAGE1(CGetChevLiftBits, int, value, 0); -MESSAGE1(CGetChevLiftNum, int, value, 0); -MESSAGE1(CGetChevRoomBits, int, value, 0); -MESSAGE1(CGetChevRoomNum, int, value, 0); -MESSAGE2(CHoseConnectedMsg, int, value1, 1, int, value2, 0); +MESSAGE1(CGetChevClassBits, int, classBits, 0); +MESSAGE1(CGetChevClassNum, int, classNum, 0); +MESSAGE2(CGetChevCodeFromRoomNameMsg, CString, roomName, "", uint, chevCode, 0); +MESSAGE1(CGetChevFloorBits, int, floorBits, 0); +MESSAGE1(CGetChevFloorNum, int, floorNum, 0); +MESSAGE1(CGetChevLiftBits, int, liftBits, 0); +MESSAGE1(CGetChevLiftNum, int, liftNum, 0); +MESSAGE1(CGetChevRoomBits, int, roomNum, 0); +MESSAGE1(CGetChevRoomNum, int, roomNum, 0); +MESSAGE2(CHoseConnectedMsg, int, value, 1, CGameObject *, object, nullptr); MESSAGE0(CInitializeAnimMsg); MESSAGE1(CIsEarBowlPuzzleDone, int, value, 0); MESSAGE3(CIsHookedOnMsg, Rect, rect, Rect(), bool, result, false, CString, string1, ""); @@ -288,7 +286,7 @@ MESSAGE2(CPlayRangeMsg, int, value1, 0, int, value2, 0); MESSAGE2(CPlayerTriesRestaurantTableMsg, int, value1, 0, int, value2, 0); MESSAGE1(CPreSaveMsg, int, value, 0); MESSAGE1(CProdMaitreDMsg, int, value, 0); -MESSAGE2(CPumpingMsg, int, value1, 0, int, value2, 0); +MESSAGE2(CPumpingMsg, int, value, 0, CGameObject *, object, nullptr); MESSAGE1(CPutBotBackInHisBoxMsg, int, value, 0); MESSAGE1(CPutParrotBackMsg, int, value, 0); MESSAGE0(CPuzzleSolvedMsg); @@ -308,12 +306,12 @@ MESSAGE2(CServiceElevatorFloorChangeMsg, int, value1, 0, int, value2, 0); MESSAGE0(CServiceElevatorFloorRequestMsg); MESSAGE1(CServiceElevatorMsg, int, value, 4); MESSAGE2(CSetChevButtonImageMsg, int, value1, 0, int, value2, 0); -MESSAGE1(CSetChevClassBits, int, value, 0); -MESSAGE1(CSetChevFloorBits, int, value, 0); -MESSAGE1(CSetChevLiftBits, int, value, 0); +MESSAGE1(CSetChevClassBits, int, classNum, 0); +MESSAGE1(CSetChevFloorBits, int, floorNum, 0); +MESSAGE1(CSetChevLiftBits, int, liftNum, 0); MESSAGE2(CSetChevPanelBitMsg, int, value1, 0, int, value2, 0); -MESSAGE1(CSetChevPanelButtonsMsg, int, value, 0); -MESSAGE1(CSetChevRoomBits, int, value, 0); +MESSAGE1(CSetChevPanelButtonsMsg, int, chevCode, 0); +MESSAGE1(CSetChevRoomBits, int, roomNum, 0); MESSAGE1(CSetFrameMsg, int, frameNumber, 0); MESSAGE0(CSetMusicControlsMsg); MESSAGE2(CSetVarMsg, CString, varName, "", int, value, 0); diff --git a/engines/titanic/messages/mouse_messages.h b/engines/titanic/messages/mouse_messages.h index d17bd51c78..e7c419bbdc 100644 --- a/engines/titanic/messages/mouse_messages.h +++ b/engines/titanic/messages/mouse_messages.h @@ -179,6 +179,32 @@ public: } }; +class CChildDragMoveMsg : public CMessage { +public: + Point _mousePos; +public: + CLASSDEF; + CChildDragMoveMsg() : CMessage() {} + CChildDragMoveMsg(const Point &pt) : CMessage(), _mousePos(pt) {} + + static bool isSupportedBy(const CTreeItem *item) { + return supports(item, _type); + } +}; + +class CChildDragStartMsg : public CMessage { +public: + Point _mousePos; +public: + CLASSDEF; + CChildDragStartMsg() : CMessage() {} + CChildDragStartMsg(const Point &pt) : CMessage(), _mousePos(pt) {} + + static bool isSupportedBy(const CTreeItem *item) { + return supports(item, _type); + } +}; + } // End of namespace Titanic #endif /* TITANIC_MOUSE_MESSAGES_H */ diff --git a/engines/titanic/module.mk b/engines/titanic/module.mk index 5c041174a2..a3afc7e4a4 100644 --- a/engines/titanic/module.mk +++ b/engines/titanic/module.mk @@ -81,7 +81,6 @@ MODULE_OBJS := \ game/arb_background.o \ game/arboretum_gate.o \ game/auto_animate.o \ - game/bilge_succubus.o \ game/bar_menu.o \ game/bar_menu_button.o \ game/bar_bell.o \ @@ -95,7 +94,6 @@ MODULE_OBJS := \ game/broken_pell_base.o \ game/broken_pellerator.o \ game/broken_pellerator_froz.o \ - game/call_pellerator.o \ game/cage.o \ game/captains_wheel.o \ game/cdrom.o \ @@ -221,7 +219,6 @@ MODULE_OBJS := \ game/parrot/parrot_nut_bowl_actor.o \ game/parrot/parrot_nut_eater.o \ game/parrot/parrot_perch_holder.o \ - game/parrot/parrot_succubus.o \ game/parrot/parrot_trigger.o \ game/parrot/player_meets_parrot.o \ game/pet/pet.o \ @@ -319,6 +316,7 @@ MODULE_OBJS := \ messages/messages.o \ messages/mouse_messages.o \ messages/service_elevator_door.o \ + moves/call_pellerator.o \ moves/enter_bomb_room.o \ moves/enter_bridge.o \ moves/enter_exit_first_class_state.o \ @@ -343,6 +341,7 @@ MODULE_OBJS := \ moves/trip_down_canal.o \ npcs/barbot.o \ npcs/bellbot.o \ + npcs/bilge_succubus.o \ npcs/callbot.o \ npcs/character.o \ npcs/deskbot.o \ @@ -351,6 +350,7 @@ MODULE_OBJS := \ npcs/maitre_d.o \ npcs/mobile.o \ npcs/parrot.o \ + npcs/parrot_succubus.o \ npcs/robot_controller.o \ npcs/starlings.o \ npcs/succubus.o \ diff --git a/engines/titanic/game/call_pellerator.cpp b/engines/titanic/moves/call_pellerator.cpp index 0ea48131b1..0dd8195277 100644 --- a/engines/titanic/game/call_pellerator.cpp +++ b/engines/titanic/moves/call_pellerator.cpp @@ -20,10 +20,17 @@ * */ -#include "titanic/game/call_pellerator.h" +#include "titanic/moves/call_pellerator.h" namespace Titanic { +BEGIN_MESSAGE_MAP(CCallPellerator, CGameObject) + ON_MESSAGE(EnterViewMsg) + ON_MESSAGE(LeaveViewMsg) + ON_MESSAGE(PETActivateMsg) + ON_MESSAGE(MouseButtonDownMsg) +END_MESSAGE_MAP() + void CCallPellerator::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); CGameObject::save(file, indent); @@ -34,4 +41,42 @@ void CCallPellerator::load(SimpleFile *file) { CGameObject::load(file); } +bool CCallPellerator::EnterViewMsg(CEnterViewMsg *msg) { + petSetArea(PET_REMOTE); + petHighlightGlyph(1); + CString name = getFullViewName(); + + if (name == "TopOfWell.Node 6.S") { + petDisplayMessage(2, "You are standing outside the Pellerator."); + } + + petSetRemoteTarget(); + return true; +} + +bool CCallPellerator::LeaveViewMsg(CLeaveViewMsg *msg) { + petClear(); + return true; +} + +bool CCallPellerator::PETActivateMsg(CPETActivateMsg *msg) { + CString name = getFullViewName(); + + if (msg->_name == "Pellerator") { + if (petDoorOrBellbotPresent()) { + petDisplayMessage("I'm sorry, you cannot enter this pellerator at present as a bot is in the way."); + } else if (name == "Bar.Node 1.S") { + changeView("Pellerator.Node 1.S"); + } else { + changeView("Pellerator.Node 1.N"); + } + } + + return true; +} + +bool CCallPellerator::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/game/call_pellerator.h b/engines/titanic/moves/call_pellerator.h index 7da4b40794..3a1ef3823a 100644 --- a/engines/titanic/game/call_pellerator.h +++ b/engines/titanic/moves/call_pellerator.h @@ -24,10 +24,16 @@ #define TITANIC_CALL_PELLERATOR_H #include "titanic/core/game_object.h" +#include "titanic/messages/pet_messages.h" namespace Titanic { class CCallPellerator : public CGameObject { + DECLARE_MESSAGE_MAP; + bool EnterViewMsg(CEnterViewMsg *msg); + bool LeaveViewMsg(CLeaveViewMsg *msg); + bool PETActivateMsg(CPETActivateMsg *msg); + bool MouseButtonDownMsg(CMouseButtonDownMsg *msg); public: CLASSDEF; diff --git a/engines/titanic/moves/exit_pellerator.cpp b/engines/titanic/moves/exit_pellerator.cpp index 68a2a8da91..12ca2c1e3c 100644 --- a/engines/titanic/moves/exit_pellerator.cpp +++ b/engines/titanic/moves/exit_pellerator.cpp @@ -21,9 +21,16 @@ */ #include "titanic/moves/exit_pellerator.h" +#include "titanic/game/transport/pellerator.h" namespace Titanic { +BEGIN_MESSAGE_MAP(CExitPellerator, CGameObject) + ON_MESSAGE(MouseButtonDownMsg) + ON_MESSAGE(StatusChangeMsg) + ON_MESSAGE(ChangeSeasonMsg) +END_MESSAGE_MAP() + CExitPelleratorStatics *CExitPellerator::_statics; void CExitPellerator::init() { @@ -38,7 +45,7 @@ void CExitPellerator::save(SimpleFile *file, int indent) { file->writeNumberLine(1, indent); file->writeQuotedLine(_statics->_v1, indent); file->writeNumberLine(_statics->_v2, indent); - file->writeNumberLine(_statics->_v3, indent); + file->writeNumberLine(_statics->_isWinter, indent); CGameObject::save(file, indent); } @@ -47,9 +54,84 @@ void CExitPellerator::load(SimpleFile *file) { file->readNumber(); _statics->_v1 = file->readString(); _statics->_v2 = file->readNumber(); - _statics->_v3 = file->readNumber(); + _statics->_isWinter = file->readNumber(); CGameObject::load(file); } +bool CExitPellerator::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { + CString name = getName(); + + if (name == "ExitPellerator") { + if (_statics->_v2 != 2) { + switch (getRandomNumber(2)) { + case 0: + CPellerator::_soundHandle = queueSound("z#457.wav", CPellerator::_soundHandle); + break; + case 1: + CPellerator::_soundHandle = queueSound("z#458.wav", CPellerator::_soundHandle); + break; + default: + CPellerator::_soundHandle = queueSound("z#464.wav", CPellerator::_soundHandle); + break; + } + } + + switch (_statics->_v2) { + case 0: + changeView("PromenadeDeck.Node 1.W"); + break; + case 1: + changeView("MusicRoomLobby.Node 1.S"); + break; + case 4: + changeView("TopOfWell.Node 6.N"); + break; + case 5: + changeView("1stClassRestaurant.Lobby Node.E"); + break; + case 6: + changeView(_statics->_isWinter ? "FrozenArboretum.Node 4.S" : "Arboretum.Node 4.W"); + break; + default: + petDisplayMessage(2, "Please exit from the other side."); + CPellerator::_soundHandle = queueSound("z#438.wav", CPellerator::_soundHandle); + + } + } else if (name == "ExitPellerator2") { + if (_statics->_v2 == 2) { + switch (getRandomNumber(2)) { + case 0: + CPellerator::_soundHandle = queueSound("z#457.wav", CPellerator::_soundHandle); + break; + case 1: + CPellerator::_soundHandle = queueSound("z#458.wav", CPellerator::_soundHandle); + break; + default: + CPellerator::_soundHandle = queueSound("z#464.wav", CPellerator::_soundHandle); + break; + } + } + + if (_statics->_v2 == 2) { + changeView("Bar.Node 1.N"); + } else { + petDisplayMessage(2, "Please exit from the other side."); + CPellerator::_soundHandle = queueSound("z#438.wav", CPellerator::_soundHandle); + } + } + + return true; +} + +bool CExitPellerator::StatusChangeMsg(CStatusChangeMsg *msg) { + _statics->_v2 = msg->_newStatus; + return true; +} + +bool CExitPellerator::ChangeSeasonMsg(CChangeSeasonMsg *msg) { + _statics->_isWinter = msg->_season == "Winter"; + return true; +} + } // End of namespace Titanic diff --git a/engines/titanic/moves/exit_pellerator.h b/engines/titanic/moves/exit_pellerator.h index 280d1e9a6c..8819d64355 100644 --- a/engines/titanic/moves/exit_pellerator.h +++ b/engines/titanic/moves/exit_pellerator.h @@ -30,10 +30,14 @@ namespace Titanic { struct CExitPelleratorStatics { CString _v1; int _v2; - int _v3; + bool _isWinter; }; class CExitPellerator : public CGameObject { + DECLARE_MESSAGE_MAP; + bool MouseButtonDownMsg(CMouseButtonDownMsg *msg); + bool StatusChangeMsg(CStatusChangeMsg *msg); + bool ChangeSeasonMsg(CChangeSeasonMsg *msg); private: static CExitPelleratorStatics *_statics; public: diff --git a/engines/titanic/npcs/bilge_succubus.cpp b/engines/titanic/npcs/bilge_succubus.cpp new file mode 100644 index 0000000000..16064bf212 --- /dev/null +++ b/engines/titanic/npcs/bilge_succubus.cpp @@ -0,0 +1,467 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "titanic/npcs/bilge_succubus.h" +#include "titanic/carry/chicken.h" +#include "titanic/core/view_item.h" +#include "titanic/pet_control/pet_control.h" + +namespace Titanic { + +BEGIN_MESSAGE_MAP(CBilgeSuccUBus, CSuccUBus) + ON_MESSAGE(FrameMsg) + ON_MESSAGE(PETReceiveMsg) + ON_MESSAGE(PETDeliverMsg) + ON_MESSAGE(MovieEndMsg) + ON_MESSAGE(MouseButtonDownMsg) + ON_MESSAGE(SubAcceptCCarryMsg) + ON_MESSAGE(EnterViewMsg) + ON_MESSAGE(LeaveViewMsg) + ON_MESSAGE(TrueTalkGetStateValueMsg) + ON_MESSAGE(TurnOn) + ON_MESSAGE(TurnOff) +END_MESSAGE_MAP() + +CBilgeSuccUBus::CBilgeSuccUBus() : CSuccUBus(), + _bilgeStartFrame1(-1), _bilgeEndFrame1(-1), + _bilgeStartFrame2(-1), _bilgeEndFrame2(-1) { +} + +void CBilgeSuccUBus::save(SimpleFile *file, int indent) { + file->writeNumberLine(1, indent); + file->writeNumberLine(_bilgeStartFrame1, indent); + file->writeNumberLine(_bilgeEndFrame1, indent); + file->writeNumberLine(_bilgeStartFrame2, indent); + file->writeNumberLine(_bilgeEndFrame2, indent); + + CSuccUBus::save(file, indent); +} + +void CBilgeSuccUBus::load(SimpleFile *file) { + file->readNumber(); + _bilgeStartFrame1 = file->readNumber(); + _bilgeEndFrame1 = file->readNumber(); + _bilgeStartFrame2 = file->readNumber(); + _bilgeEndFrame2 = file->readNumber(); + + CSuccUBus::load(file); +} + +bool CBilgeSuccUBus::FrameMsg(CFrameMsg *msg) { + return true; +} + +bool CBilgeSuccUBus::PETReceiveMsg(CPETReceiveMsg *msg) { + CPetControl *pet = getPetControl(); + + if (_v2) { + if (_startFrame4 >= 0) + playMovie(_startFrame4, _endFrame4, MOVIE_GAMESTATE); + if (_startFrame5 >= 0) + playMovie(_startFrame5, _endFrame5, MOVIE_GAMESTATE); + + playSound("z#28.wav", 70); + } else if (!_enabled) { + petDisplayMessage(2, "The Succ-U-Bus is in Standby, or \"Off\" mode at present."); + return false; + } else if (!pet) { + return false; + } else { + uint roomFlags = pet->getRoomFlags(); + CGameObject *mailObject = findMailByFlags( + _v3 && compareRoomNameTo("Titania") ? 3 : _field140, + roomFlags); + + if (mailObject) { + _mailP = mailObject; + if (_startFrame4 >= 0) + playMovie(_startFrame4, _endFrame4, MOVIE_GAMESTATE); + } else { + petDisplayMessage(2, "There is currently nothing to deliver."); + } + } + + return true; +} + +bool CBilgeSuccUBus::PETDeliverMsg(CPETDeliverMsg *msg) { + CPetControl *pet = getPetControl(); + if (!_enabled || !pet) + return true; + + uint petRoomFlags = pet->getRoomFlags(); + CGameObject *mailObject = findMail(petRoomFlags); + + if (!mailObject) { + petDisplayMessage(2, "There is currently nothing in the tray to send."); + return true; + } + + _field19C = 0; + _mailP = mailObject; + + uint roomFlags = _roomFlags; + if (!pet->testRooms5(roomFlags) || + getPassengerClass() > pet->getMailDest(roomFlags)) { + roomFlags = pet->getSpecialRoomFlags("BilgeRoom"); + _field19C = 1; + } + + _isChicken = mailObject->getName() == "Chicken"; + _isFeathers = mailObject->getName() == "Feathers"; + _field158 = 0; + + if (_v2) { + if (_isFeathers) { + startTalking(this, 230022); + _field158 = 1; + + if (_startFrame3 >= 0) + playMovie(_startFrame3, _endFrame3, MOVIE_NOTIFY_OBJECT); + + if (_bilgeStartFrame1 >= 0) { + playMovie(_startFrame12, _endFrame12, MOVIE_GAMESTATE); + playMovie(_bilgeStartFrame2, _bilgeEndFrame2, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); + playMovie(_bilgeStartFrame1, _bilgeEndFrame1, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); + inc54(); + } + } else { + startTalking(this, 230012); + _field158 = 2; + if (_startFrame3 >= 0) + playMovie(_startFrame3, _endFrame3, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); + if (_startFrame4 >= 0) + playMovie(_startFrame4, _endFrame4, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); + if (_startFrame5 >= 0) + playMovie(_startFrame5, _endFrame5, MOVIE_GAMESTATE); + } + } else { + if (_isFeathers) { + startTalking(this, 230022); + _field158 = 3; + + if (_startFrame3 >= 0) + playMovie(_startFrame3, _endFrame3, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); + if (_startFrame4 >= 0) + playMovie(_startFrame4, _endFrame4, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); + if (_startFrame5 >= 0) + playMovie(_startFrame5, _endFrame5, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); + } else { + removeMail(petRoomFlags, roomFlags); + startTalking(this, 230012); + if (_startFrame3 >= 0) { + _field158 = 4; + playMovie(_startFrame3, _endFrame3, MOVIE_NOTIFY_OBJECT | MOVIE_GAMESTATE); + } + } + } + + return true; +} + +bool CBilgeSuccUBus::MovieEndMsg(CMovieEndMsg *msg) { + CPetControl *pet = getPetControl(); + + if (msg->_endFrame == _endFrame12) { + if (_startFrame10 >= 0) + playSound("z#27.wav"); + } else if (msg->_endFrame == _endFrame10) { + if (_startFrame11 >= 0) + playSound("z#30.wav"); + } else { + if (_endFrame9 == _endFrame10 && pet) { + if (_v2) { + startTalking(this, getRandomNumber(1) ? 230062 : 230063); + } else if (!findMail(pet->getRoomFlags())) { + switch (getRandomNumber(4)) { + case 0: + startTalking(this, 230001); + break; + case 1: + startTalking(this, 230002); + break; + case 2: + startTalking(this, 230003); + break; + default: + break; + } + } + } + + if (msg->_endFrame == _endFrame3) { + switch (_field158) { + case 1: + stopSound(_soundHandle); + _soundHandle = playSound("z#3.wav"); + break; + case 2: + stopSound(_soundHandle); + _soundHandle = playSound("z#12.wav"); + break; + case 3: + if (_isChicken) { + startTalking(this, 230018); + _isChicken = false; + } else { + startTalking(this, 230013); + } + break; + case 4: + startTalking(this, 230017); + break; + default: + break; + } + + CSUBTransition transMsg; + transMsg.execute(this); + + } else if (msg->_endFrame == _bilgeEndFrame2) { + playSound("z#25.wav", 70); + playSound("z#24.wav", 70); + + } else if (msg->_endFrame == _endFrame4) { + if (_mailP) { + _mailP->petAddToInventory(); + CVisibleMsg visibleMsg(true); + visibleMsg.execute(_mailP); + + _mailP = nullptr; + petSetArea(PET_INVENTORY); + + CSUBTransition transMsg; + transMsg.execute(this); + } + + } else if (msg->_endFrame == _bilgeEndFrame1) { + changeView("BilgeRoomWith.Node 1.N", ""); + _v2 = 0; + resetMail(); + + if (_mailP) { + _mailP->petAddToInventory(); + CVisibleMsg visibleMsg(true); + visibleMsg.execute(_mailP); + + _mailP = nullptr; + petSetArea(PET_INVENTORY); + } + + startTalking(this, 150); + CBodyInBilgeRoomMsg bodyMsg; + bodyMsg.execute("Service Elevator Entity"); + dec54(); + _field158 = 0; + + } else { + _field158 = 0; + } + } + + return true; +} + +bool CBilgeSuccUBus::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { + if (_enabled) { + switch (getRandomNumber(4)) { + case 0: + case 4: { + _enabled = false; + CTurnOff offMsg; + offMsg.execute(this); + break; + } + + case 1: + startTalking(this, 230055); + break; + + case 2: + startTalking(this, 230067); + break; + + case 3: + startTalking(this, 230045); + break; + + default: + break; + } + } else { + CTurnOn onMsg; + onMsg.execute(this); + _enabled = true; + } + + return true; +} + +bool CBilgeSuccUBus::SubAcceptCCarryMsg(CSubAcceptCCarryMsg *msg) { + CPetControl *pet = getPetControl(); + if (!msg->_item) + return false; + + CCarry *item = dynamic_cast<CCarry *>(msg->_item); + if (!_enabled || !pet || !item) { + item->petAddToInventory(); + return true; + } + + uint petRoomFlags = pet->getRoomFlags(); + if (mailExists(petRoomFlags)) { + petDisplayMessage(2, "The Succ-U-Bus is a Single Entity Delivery Device."); + item->petAddToInventory(); + return true; + } + + petContainerRemove(item); + pet->phonographAction(""); + playSound("z#23.wav"); + + CChicken *chicken = dynamic_cast<CChicken *>(item); + bool chickenFlag = chicken ? chicken->_string6 == "None" : false; + + if (chickenFlag) { + if (_startFrame2 >= 0) { + startTalking(this, 70219); + playMovie(_startFrame2, _endFrame2, 0); + } + + if (_startFrame3 >= 0) { + _field158 = 5; + playMovie(_startFrame3, _endFrame3, MOVIE_NOTIFY_OBJECT); + } + + CViewItem *view = parseView(item->_fullViewName); + if (view) { + item->setVisible(false); + setPosition(item->_origPos); + item->moveUnder(view); + + CSUBTransition transMsg; + transMsg.execute(this); + } else { + return false; + } + } else { + item->addMail(petRoomFlags); + if (_startFrame2 >= 0) + playMovie(_startFrame2, _endFrame2, 0); + + petSetArea(PET_REMOTE); + CSUBTransition transMsg; + transMsg.execute(this); + } + + return true; +} + +bool CBilgeSuccUBus::EnterViewMsg(CEnterViewMsg *msg) { + petSetRemoteTarget(); + _mailP = nullptr; + + if (_startFrame8 >= 0) + loadFrame(_startFrame8); + + return true; +} + +bool CBilgeSuccUBus::LeaveViewMsg(CLeaveViewMsg *msg) { + petDisplayMessage(2, ""); + petClear(); + + if (_soundHandle != -1) { + stopSound(_soundHandle); + _soundHandle = -1; + } + + if (_enabled) { + _enabled = false; + if (_startFrame10 >= 0) + playSound("z#27.wav"); + } + + performAction(true); + CSUBTransition transMsg; + transMsg.execute(this); + + return true; +} + +bool CBilgeSuccUBus::TrueTalkGetStateValueMsg(CTrueTalkGetStateValueMsg *msg) { + if (msg->_stateNum == 1) + msg->_stateVal = _enabled; + + return true; +} + +bool CBilgeSuccUBus::TurnOn(CTurnOn *msg) { + CPetControl *pet = getPetControl(); + + if (pet) { + if (_startFrame9 >= 0) { + playMovie(_startFrame9, _endFrame9, MOVIE_NOTIFY_OBJECT); + playSound("z#26.wav"); + } + + if (mailExists(pet->getRoomFlags()) && _startFrame2 >= 0) + playMovie(_startFrame2, _endFrame2, 0); + + _enabled = true; + CSUBTransition transMsg; + transMsg.execute(this); + + endTalking(this, true); + petSetArea(PET_REMOTE); + petHighlightGlyph(16); + } + + return true; +} + +bool CBilgeSuccUBus::TurnOff(CTurnOff *msg) { + CPetControl *pet = getPetControl(); + + if (pet && mailExists(pet->getRoomFlags()) && _startFrame12 >= 0) + playMovie(_startFrame12, _endFrame12, MOVIE_NOTIFY_OBJECT); + else if (_endFrame12 >= 0) + playMovie(_endFrame12, _endFrame12, MOVIE_NOTIFY_OBJECT); + + if (_soundHandle != -1) { + stopSound(_soundHandle); + _soundHandle = -1; + } + + if (_startFrame10 >= 0) + playMovie(_startFrame10, _endFrame10, MOVIE_NOTIFY_OBJECT); + + _enabled = false; + performAction(true); + + CSUBTransition transMsg; + transMsg.execute(this); + + return true; +} + +} // End of namespace Titanic diff --git a/engines/titanic/game/bilge_succubus.h b/engines/titanic/npcs/bilge_succubus.h index 4b2a626dc2..754949a306 100644 --- a/engines/titanic/game/bilge_succubus.h +++ b/engines/titanic/npcs/bilge_succubus.h @@ -28,11 +28,23 @@ namespace Titanic { class CBilgeSuccUBus : public CSuccUBus { + DECLARE_MESSAGE_MAP; + bool FrameMsg(CFrameMsg *msg); + bool PETReceiveMsg(CPETReceiveMsg *msg); + bool PETDeliverMsg(CPETDeliverMsg *msg); + bool MovieEndMsg(CMovieEndMsg *msg); + bool MouseButtonDownMsg(CMouseButtonDownMsg *msg); + bool SubAcceptCCarryMsg(CSubAcceptCCarryMsg *msg); + bool EnterViewMsg(CEnterViewMsg *msg); + bool LeaveViewMsg(CLeaveViewMsg *msg); + bool TrueTalkGetStateValueMsg(CTrueTalkGetStateValueMsg *msg); + bool TurnOn(CTurnOn *msg); + bool TurnOff(CTurnOff *msg); public: - int _field1DC; - int _field1E0; - int _field1E4; - int _field1E8; + int _bilgeStartFrame1; + int _bilgeEndFrame1; + int _bilgeStartFrame2; + int _bilgeEndFrame2; public: CLASSDEF; CBilgeSuccUBus(); diff --git a/engines/titanic/npcs/liftbot.cpp b/engines/titanic/npcs/liftbot.cpp index fbaa60d800..3d6321bfb5 100644 --- a/engines/titanic/npcs/liftbot.cpp +++ b/engines/titanic/npcs/liftbot.cpp @@ -124,8 +124,7 @@ bool CLiftBot::TurnOn(CTurnOn *msg) { _enabled = true; if (!_flag) { if (compareTo("LiftBotTalking", 0)) { - CViewItem *view = findView(); - endTalking(this, MOVIE_REPEAT); + endTalking(this, MOVIE_REPEAT, findView()); petSetArea(PET_CONVERSATION); _flag = true; } diff --git a/engines/titanic/npcs/parrot_succubus.cpp b/engines/titanic/npcs/parrot_succubus.cpp new file mode 100644 index 0000000000..5f67b8f44c --- /dev/null +++ b/engines/titanic/npcs/parrot_succubus.cpp @@ -0,0 +1,152 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "titanic/npcs/parrot_succubus.h" +#include "titanic/pet_control/pet_control.h" +#include "titanic/carry/hose.h" + +namespace Titanic { + +BEGIN_MESSAGE_MAP(CParrotSuccUBus, CSuccUBus) + ON_MESSAGE(HoseConnectedMsg) + ON_MESSAGE(EnterViewMsg) + ON_MESSAGE(MovieEndMsg) + ON_MESSAGE(MouseButtonDownMsg) + ON_MESSAGE(LeaveNodeMsg) +END_MESSAGE_MAP() + +CParrotSuccUBus::CParrotSuccUBus() : CSuccUBus(), _field1DC(0), + _field1EC(0), _field1F0(376), _field1F4(393) { +} + +void CParrotSuccUBus::save(SimpleFile *file, int indent) { + file->writeNumberLine(1, indent); + file->writeNumberLine(_field1DC, indent); + file->writeQuotedLine(_string3, indent); + file->writeNumberLine(_field1EC, indent); + + CSuccUBus::save(file, indent); +} + +void CParrotSuccUBus::load(SimpleFile *file) { + file->readNumber(); + _field1DC = file->readNumber(); + _string3 = file->readString(); + _field1EC = file->readNumber(); + + CSuccUBus::load(file); +} + +bool CParrotSuccUBus::HoseConnectedMsg(CHoseConnectedMsg *msg) { + CPetControl *pet = getPetControl(); + if (msg->_value == _field1DC) + return true; + if (mailExists(pet->getRoomFlags())) + return false; + + _field1DC = msg->_value; + if (_field1DC) { + CGameObject *item = msg->_object; + _string3 = item->getName(); + CHoseConnectedMsg hoseMsg(1, this); + hoseMsg.execute(msg->_object); + item->petMoveToHiddenRoom(); + + CPumpingMsg pumpingMsg(1, this); + pumpingMsg.execute(this); + _field1DC = 1; + + if (_enabled) { + _enabled = false; + } else { + playMovie(_startFrame9, _endFrame9, 0); + playSound("z#26.wav"); + } + + playMovie(_field1C4, _field1C8, MOVIE_NOTIFY_OBJECT); + } else { + stopMovie(); + stopSound(_field1EC); + playMovie(_field1F0, _field1F4, MOVIE_NOTIFY_OBJECT); + + CPumpingMsg pumpingMsg(0, this); + pumpingMsg.execute(_string3); + + CGameObject *obj = getHiddenObject(_string3); + if (obj) { + obj->petAddToInventory(); + obj->setVisible(true); + } + + _enabled = true; + CTurnOff offMsg; + offMsg.execute(this); + } + + return true; +} + +bool CParrotSuccUBus::EnterViewMsg(CEnterViewMsg *msg) { + if (_field1DC) { + playMovie(_field1CC, _field1D0, MOVIE_REPEAT); + return true; + } else { + return CSuccUBus::EnterViewMsg(msg); + } +} + +bool CParrotSuccUBus::MovieEndMsg(CMovieEndMsg *msg) { + if (msg->_endFrame == _field1C8) { + playMovie(_field1CC, _field1D0, MOVIE_REPEAT); + _field1EC = playSound("z#472.wav"); + return true; + } else { + return CSuccUBus::MovieEndMsg(msg); + } +} + +bool CParrotSuccUBus::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { + if (_field1DC) { + CHoseConnectedMsg hoseMsg; + hoseMsg._value = 0; + hoseMsg.execute(this); + return true; + } else { + return CSuccUBus::MouseButtonDownMsg(msg); + } +} + +bool CParrotSuccUBus::LeaveNodeMsg(CLeaveNodeMsg *msg) { + if (_field1DC) { + getHiddenObject(_string3); + if (CHose::_statics->_v2.empty()) { + playSound("z#51.wav"); + CHoseConnectedMsg hoseMsg; + hoseMsg._value = 0; + hoseMsg.execute(this); + } + } + + return true; +} + +} // End of namespace Titanic diff --git a/engines/titanic/game/parrot/parrot_succubus.h b/engines/titanic/npcs/parrot_succubus.h index 6f5d9e602a..74a4a032eb 100644 --- a/engines/titanic/game/parrot/parrot_succubus.h +++ b/engines/titanic/npcs/parrot_succubus.h @@ -28,6 +28,12 @@ namespace Titanic { class CParrotSuccUBus : public CSuccUBus { + DECLARE_MESSAGE_MAP; + bool HoseConnectedMsg(CHoseConnectedMsg *msg); + bool EnterViewMsg(CEnterViewMsg *msg); + bool MovieEndMsg(CMovieEndMsg *msg); + bool MouseButtonDownMsg(CMouseButtonDownMsg *msg); + bool LeaveNodeMsg(CLeaveNodeMsg *msg); public: int _field1DC; CString _string3; diff --git a/engines/titanic/npcs/succubus.cpp b/engines/titanic/npcs/succubus.cpp index 75afb1b273..872b4f33ba 100644 --- a/engines/titanic/npcs/succubus.cpp +++ b/engines/titanic/npcs/succubus.cpp @@ -69,11 +69,11 @@ CSuccUBus::CSuccUBus() : CTrueTalkNPC() { _startFrame2 = 40; _endFrame2 = 68; _field140 = 1; - _field144 = 0; + _mailP = nullptr; _startFrame5 = 0; _endFrame5 = 0; - _field150 = 0xE0; - _field154 = 0; + _startFrame12 = 224; + _endFrame12 = 248; _field158 = 0; _field15C = 0; _string2 = "NULL"; @@ -124,8 +124,8 @@ void CSuccUBus::save(SimpleFile *file, int indent) { file->writeNumberLine(_v2, indent); file->writeNumberLine(_startFrame5, indent); file->writeNumberLine(_endFrame5, indent); - file->writeNumberLine(_field150, indent); - file->writeNumberLine(_field154, indent); + file->writeNumberLine(_startFrame12, indent); + file->writeNumberLine(_endFrame12, indent); file->writeNumberLine(_field158, indent); file->writeNumberLine(_field15C, indent); @@ -188,8 +188,8 @@ void CSuccUBus::load(SimpleFile *file) { _v2 = file->readNumber(); _startFrame5 = file->readNumber(); _endFrame5 = file->readNumber(); - _field150 = file->readNumber(); - _field154 = file->readNumber(); + _startFrame12 = file->readNumber(); + _endFrame12 = file->readNumber(); _field158 = file->readNumber(); _field15C = file->readNumber(); @@ -238,12 +238,12 @@ bool CSuccUBus::MouseButtonDownMsg(CMouseButtonDownMsg *msg) { CTurnOn onMsg; onMsg.execute(this); _enabled = true; - } else if (getNewRandomNumber(256) < 130) { + } else if (getRandomNumber(256) < 130) { _enabled = false; CTurnOff offMsg; offMsg.execute(this); } else { - switch (getNewRandomNumber(2)) { + switch (getRandomNumber(2)) { case 0: startTalking(this, 230055, findView()); break; @@ -330,7 +330,7 @@ bool CSuccUBus::EnterViewMsg(CEnterViewMsg *msg) { } petSetRemoteTarget(); - _field144 = nullptr; + _mailP = nullptr; if (_startFrame8 >= 0) loadFrame(_startFrame8); @@ -429,7 +429,7 @@ bool CSuccUBus::PETDeliverMsg(CPETDeliverMsg *msg) { playMovie(_startFrame3, _endFrame3, 0); if (_startFrame4 >= 0) { - _field144 = mailObject; + _mailP = mailObject; playMovie(_startFrame4, _endFrame4, MOVIE_NOTIFY_OBJECT); } @@ -559,7 +559,7 @@ bool CSuccUBus::MovieEndMsg(CMovieEndMsg *msg) { stopSound(_soundHandle); _soundHandle = -1; - switch (getNewRandomNumber(_v2 ? 7 : 5, &_field1B0)) { + switch (getRandomNumber(_v2 ? 7 : 5, &_field1B0)) { case 2: startTalking(this, 230001, findView()); break; @@ -609,12 +609,12 @@ bool CSuccUBus::MovieEndMsg(CMovieEndMsg *msg) { } if (msg->_endFrame == _endFrame4) { - if (pet && _field144) { - _field144->setMailId(petRoomFlags); + if (pet && _mailP) { + _mailP->setMailId(petRoomFlags); } _field188 = 1; - _field144 = 0; + _mailP = 0; if (_field1D8) { _field1D8 = 0; dec54(); @@ -727,7 +727,7 @@ bool CSuccUBus::SUBTransition(CSUBTransition *msg) { bool CSuccUBus::SetChevRoomBits(CSetChevRoomBits *msg) { if (_enabled) { - _roomFlags = msg->_value; + _roomFlags = msg->_roomNum; playSound("z#98.wav", 100); } diff --git a/engines/titanic/npcs/succubus.h b/engines/titanic/npcs/succubus.h index b8e4c316a9..7ca8037a0a 100644 --- a/engines/titanic/npcs/succubus.h +++ b/engines/titanic/npcs/succubus.h @@ -45,13 +45,13 @@ class CSuccUBus : public CTrueTalkNPC { bool SetChevRoomBits(CSetChevRoomBits *msg); bool ActMsg(CActMsg *msg); bool MouseDragStartMsg(CMouseDragStartMsg *msg); -private: +protected: static bool _enabled; static int _v1; static int _v2; static int _v3; static int _v4; -private: +protected: int _startFrame8; int _endFrame8; int _startFrame11; @@ -67,11 +67,11 @@ private: int _startFrame2; int _endFrame2; int _field140; - CGameObject *_field144; + CGameObject *_mailP; int _startFrame5; int _endFrame5; - int _field150; - int _field154; + int _startFrame12; + int _endFrame12; int _field158; bool _field15C; CString _string2; diff --git a/engines/titanic/support/string.cpp b/engines/titanic/support/string.cpp index cd39c03861..1400c25733 100644 --- a/engines/titanic/support/string.cpp +++ b/engines/titanic/support/string.cpp @@ -122,4 +122,20 @@ CString CString::format(const char *fmt, ...) { return output; } +bool CString::operator==(const CString &x) const { + return compareToIgnoreCase(x) == 0; +} + +bool CString::operator==(const char *x) const { + return compareToIgnoreCase(x) == 0; +} + +bool CString::operator!=(const CString &x) const { + return compareToIgnoreCase(x) != 0; +} + +bool CString::operator!=(const char *x) const { + return compareToIgnoreCase(x) != 0; +} + } // End of namespace Titanic diff --git a/engines/titanic/support/string.h b/engines/titanic/support/string.h index 9550ce88a7..487c138358 100644 --- a/engines/titanic/support/string.h +++ b/engines/titanic/support/string.h @@ -49,6 +49,11 @@ public: explicit CString(char c) : Common::String(c) {} explicit CString(int val); + bool operator==(const CString &x) const; + bool operator==(const char *x) const; + bool operator!=(const CString &x) const; + bool operator!=(const char *x) const; + /** * Returns the left n characters of the string */ |