aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Gilbert2016-01-11 06:20:46 +1100
committerPaul Gilbert2016-01-11 06:20:46 +1100
commit8c906fbb3ee56cf3869a66ad44277a7969e3964c (patch)
treec9b8473d78532d16293c3a232c0bb2f1096dbaa5
parent16a43c9ac8f0ee53fcff2eeca7d7118683bb5716 (diff)
downloadscummvm-rg350-8c906fbb3ee56cf3869a66ad44277a7969e3964c.tar.gz
scummvm-rg350-8c906fbb3ee56cf3869a66ad44277a7969e3964c.tar.bz2
scummvm-rg350-8c906fbb3ee56cf3869a66ad44277a7969e3964c.zip
MADS: Implement conversation dialog scripts loading
-rw-r--r--engines/mads/conversations.cpp288
-rw-r--r--engines/mads/conversations.h98
2 files changed, 288 insertions, 98 deletions
diff --git a/engines/mads/conversations.cpp b/engines/mads/conversations.cpp
index 61391c1b12..85093d1885 100644
--- a/engines/mads/conversations.cpp
+++ b/engines/mads/conversations.cpp
@@ -45,6 +45,7 @@ GameConversations::GameConversations(MADSEngine *vm) : _vm(vm) {
_interlocutorTrigger = 0;
_interlocutorTriggerMode = SEQUENCE_TRIGGER_PARSER;
_currentNode = 0;
+ _dialogNodeOffset = _dialogNodeSize = 0;
// Mark all conversation slots as empty
for (int idx = 0; idx < MAX_CONVERSATIONS; ++idx)
@@ -147,7 +148,9 @@ void GameConversations::start() {
_vars = &_runningConv->_cnd._vars[0];
_nextStartNode = &_runningConv->_cnd._vars[1];
- warning("TODO: GameConversations::start");
+ _runningConv->_cnd._currentNode = -1;
+ _runningConv->_cnd._numImports = 0;
+ _runningConv->_cnd._vars[0].setValue(_nextStartNode->_val);
}
void GameConversations::setVariable(uint idx, int val) {
@@ -233,6 +236,7 @@ void GameConversations::setInterlocutorTrigger(int val) {
}
int *GameConversations::getVariable(int idx) {
+ assert(idx >= 0); // TODO: Some negative values are allowed? Investigate
return _vars[idx].getValue();
}
@@ -251,21 +255,21 @@ void GameConversations::release() {
}
}
-void GameConversations::flagEntry(ConvFlagMode mode, int entryIndex) {
+void GameConversations::flagEntry(DialogCommand mode, int entryIndex) {
assert(_runningConv);
uint &flags = _runningConv->_cnd._entryFlags[entryIndex];
switch (mode) {
- case FLAGMODE_1:
+ case CMD_1:
flags |= ENTRYFLAG_4000;
flags &= ~ENTRYFLAG_8000;
break;
- case FLAGMODE_2:
+ case CMD_HIDE:
flags &= ~ENTRYFLAG_8000;
break;
- case FLAGMODE_3:
+ case CMD_UNHIDE:
if (!(flags & ENTRYFLAG_4000))
flags |= ENTRYFLAG_8000;
break;
@@ -328,7 +332,7 @@ void GameConversations::update(bool flag) {
_verbId = _vm->_game->_scene._action._activeAction._verbId;
if (!(_runningConv->_cnd._entryFlags[_verbId] & ENTRYFLAG_2))
- flagEntry(FLAGMODE_2, _verbId);
+ flagEntry(CMD_HIDE, _verbId);
removeActiveWindow();
_vm->_game->_scene._userInterface.emptyConversationList();
@@ -424,8 +428,27 @@ bool GameConversations::nextNode() {
return _runningConv->_data._nodes[var0._val]._active;
}
-void GameConversations::executeEntry(int index) {
+int GameConversations::executeEntry(int index) {
+ ConvDialog &dlg = _runningConv->_data._dialogs[index];
+ ConversationVar &var0 = _runningConv->_cnd._vars[0];
+
+ _runningConv->_cnd._fieldC = 0;
+ _runningConv->_cnd._fieldE = 0;
+ _runningConv->_cnd._field10 = 0;
+ _runningConv->_cnd._field12 = 0;
+ _nextStartNode->_val = var0._val;
+ bool flag = true;
+ for (uint scriptIdx = 0; scriptIdx < dlg._script.size(); ++scriptIdx) {
+ DialogCommand cmd = dlg._script[scriptIdx]._command;
+ // TODO
+ }
+
+ if (flag) {
+ var0._val = -1;
+ }
+
+ return var0._val;
}
/*------------------------------------------------------------------------*/
@@ -493,8 +516,8 @@ void ConversationData::load(const Common::String &filename) {
for (uint idx = 0; idx < _dialogCount; ++idx) {
_dialogs[idx]._textLineIndex = convFile->readSint16LE();
_dialogs[idx]._speechIndex = convFile->readSint16LE();
- _dialogs[idx]._nodeOffset = convFile->readUint16LE();
- _dialogs[idx]._nodeSize = convFile->readUint16LE();
+ _dialogs[idx]._scriptOffset = convFile->readUint16LE();
+ _dialogs[idx]._scriptSize = convFile->readUint16LE();
}
delete convFile;
@@ -536,81 +559,23 @@ void ConversationData::load(const Common::String &filename) {
delete[] textLineOffsets;
delete convFile;
- // **** Section 6: Node entry commands ************************************
+ // **** Section 6: Scripts ************************************************
convFile = convFileUnpacked.getItemStream(6);
assert(convFile->size() == _commandsSize);
- for (uint16 i = 0; i < _nodeCount; i++) {
- uint16 dialogCount = _nodes[i]._dialogCount;
-
- for (uint16 j = 0; j < dialogCount; j++) {
- //ConvDialog dialog = _convNodes[i].dialogs[j];
- byte command;
- uint16 chk;
-
- do {
- command = convFile->readByte();
- chk = convFile->readUint16BE();
- if (chk != 0xFF00 && chk != 0x0000) {
- warning("Error while reading conversation node entries - bailing out");
- break;
- }
+ for (uint idx = 0; idx < _dialogs.size(); ++idx) {
+ // Move to the correct position for the dialog's script, and create
+ // a memory stream to represent the data for just that script
+ convFile->seek(_dialogs[idx]._scriptOffset);
+ Common::SeekableReadStream *scriptStream = convFile->readStream(_dialogs[idx]._scriptSize);
- switch (command) {
- case cmdNodeEnd:
- //debug("Node end");
- break;
- case cmdDialogEnd:
- //debug("Dialog end");
- break;
- case cmdHide: {
- byte count = convFile->readByte();
- for (byte k = 0; k < count; k++) {
- /*uint16 nodeRef = */convFile->readUint16LE();
- //debug("Hide node %d", nodeRef);
- }
-
- }
- break;
- case cmdUnhide: {
- byte count = convFile->readByte();
- for (byte k = 0; k < count; k++) {
- /*uint16 nodeRef = */convFile->readUint16LE();
- //debug("Unhide node %d", nodeRef);
- }
-
- }
- break;
- case cmdMessage:
- //debug("Message");
- convFile->skip(7); // TODO
- break;
- case cmdGoto: {
- convFile->skip(3); // unused?
- /*byte nodeRef = */convFile->readByte();
- //debug("Goto %d", nodeRef);
- }
- break;
- case cmdAssign: {
- convFile->skip(3); // unused?
- /*uint16 value = */convFile->readUint16LE();
- /*uint16 variable = */convFile->readUint16LE();
- //debug("Variable %d = %d", variable, value);
- }
- break;
- default:
- error("Unknown conversation command %d", command);
- break;
- }
- } while (command != cmdNodeEnd && command != cmdDialogEnd);
- }
+ // Pass it to the dialog's script set class to parse into commands
+ _dialogs[idx]._script.load(*scriptStream, _dialogs[idx]._scriptOffset);
+ delete scriptStream;
}
delete convFile;
inFile.close();
-
- // TODO: Still stuff to do
- warning("TODO GameConversations::get");
}
/*------------------------------------------------------------------------*/
@@ -700,4 +665,175 @@ void ConversationVar::setValue(int *val) {
_val = 0;
}
+/*------------------------------------------------------------------------*/
+
+void DialogScript::load(Common::SeekableReadStream &s, uint startingOffset) {
+ clear();
+ Common::HashMap<uint, uint> instructionOffsets;
+
+ // Iterate getting each instruction in turn
+ while (s.pos() < s.size()) {
+ // Create a new entry for the next script command
+ instructionOffsets[startingOffset + s.pos()] = s.size();
+ push_back(ScriptEntry());
+ ScriptEntry &se = (*this)[size() - 1];
+
+ // Load the instruction
+ se.load(s);
+ }
+
+ // Do a final iteration over the loaded instructions to convert
+ // any GOTO instructions from original offsets to instruction indexes
+ for (uint idx = 0; idx < size(); ++idx) {
+ ScriptEntry &se = (*this)[idx];
+
+ if (se._command == CMD_GOTO)
+ se._params[0] = instructionOffsets[se._params[0]];
+ }
+}
+
+/*------------------------------------------------------------------------*/
+
+void ScriptEntry::load(Common::SeekableReadStream &s) {
+ // Get the command byte
+ _command = (DialogCommand)s.readByte();
+
+ if (!(_command == CMD_DIALOG_END || (_command >= CMD_NODE_END && _command <= CMD_ASSIGN))) {
+ warning("unknown opcode - %d", _command);
+ s.seek(0, SEEK_END);
+ return;
+ }
+
+ // Get in the conditional values
+ int numConditionals = 1;
+ if (_command == CMD_7)
+ numConditionals = 3;
+ else if (_command == CMD_ERROR)
+ numConditionals = 0;
+
+ for (int idx = 0; idx < numConditionals; ++idx)
+ _conditionals[idx].load(s);
+
+ // Get further parameters
+ switch (_command) {
+ case CMD_1:
+ case CMD_HIDE:
+ case CMD_UNHIDE: {
+ // Read in the list of entries whose flags are to be updated
+ int count = s.readByte();
+ for (int idx = 0; idx < count; ++idx)
+ _params.push_back(s.readSint16LE());
+ break;
+ }
+
+ case CMD_MESSAGE:
+ case CMD_5: {
+ int count1 = s.readByte();
+ int count2 = s.readByte();
+ _params.push_back(count1);
+ _params.push_back(count2);
+
+ for (int idx = 0; idx < count1; ++idx)
+ _params.push_back(s.readByte());
+ for (int idx = 0; idx < count1; ++idx)
+ _params.push_back(s.readUint16LE());
+ for (int idx = 0; idx < count2; ++idx)
+ _params.push_back(s.readUint16LE());
+ break;
+ }
+
+ case CMD_ERROR:
+ case CMD_7:
+ // These opcodes have no extra parameters
+ break;
+
+ case CMD_GOTO:
+ case CMD_ASSIGN:
+ // Goto has a single extra parameter for the destination
+ // Assign has a single extra parameter for the variable index
+ // that the value resulting from the condition will be set to
+ _params.push_back(s.readUint16LE());
+ break;
+
+
+
+ default:
+ break;
+ }
+}
+
+void ScriptEntry::Conditional::load(Common::SeekableReadStream &s) {
+ _paramsFlag = s.readUint16LE();
+
+ if (_paramsFlag == 0xff) {
+ _param1._isVariable = false;
+ _param1._val = 0;
+ _param2._isVariable = false;
+ _param2._val = 0;
+ } else {
+ _param1._isVariable = s.readByte() != 0;
+ _param1._val = 0;
+ _param2._isVariable = s.readByte() != 0;
+ _param2._val = 0;
+ }
+}
+
+/*
+do {
+command = convFile->readByte();
+chk = convFile->readUint16BE();
+if (chk != 0xFF00 && chk != 0x0000) {
+warning("Error while reading conversation node entries - bailing out");
+break;
+}
+
+switch (command) {
+case cmdNodeEnd:
+//debug("Node end");
+break;
+case cmdDialogEnd:
+//debug("Dialog end");
+break;
+case cmdHide: {
+byte count = convFile->readByte();
+for (byte k = 0; k < count; k++) {
+//uint16 nodeRef = convFile->readUint16LE();
+//debug("Hide node %d", nodeRef);
+}
+
+}
+break;
+case cmdUnhide: {
+byte count = convFile->readByte();
+for (byte k = 0; k < count; k++) {
+//uint16 nodeRef = convFile->readUint16LE();
+//debug("Unhide node %d", nodeRef);
+}
+
+}
+break;
+case cmdMessage:
+//debug("Message");
+convFile->skip(7); // TODO
+break;
+case cmdGoto: {
+convFile->skip(3); // unused?
+//byte nodeRef = convFile->readByte();
+//debug("Goto %d", nodeRef);
+}
+break;
+case cmdAssign: {
+convFile->skip(3); // unused?
+//uint16 value = convFile->readUint16LE();
+//uint16 variable = convFile->readUint16LE();
+//debug("Variable %d = %d", variable, value);
+}
+break;
+default:
+error("Unknown conversation command %d", command);
+break;
+}
+} while (command != cmdNodeEnd && command != cmdDialogEnd);
+*/
+
} // End of namespace MADS
diff --git a/engines/mads/conversations.h b/engines/mads/conversations.h
index dbf000f5a5..812896566c 100644
--- a/engines/mads/conversations.h
+++ b/engines/mads/conversations.h
@@ -48,24 +48,18 @@ enum ConversationMode {
CONVMODE_STOP = 10
};
-enum DialogCommands {
- cmdNodeEnd = 0,
- //
- cmdHide = 2,
- cmdUnhide = 3,
- cmdMessage = 4,
- //
- //
- cmdGoto = 7,
- //
- cmdAssign = 9,
- cmdDialogEnd = 255
-};
-
-enum ConvFlagMode {
- FLAGMODE_1 = 1,
- FLAGMODE_2 = 2,
- FLAGMODE_3 = 3
+enum DialogCommand {
+ CMD_NODE_END = 0,
+ CMD_1 = 1,
+ CMD_HIDE = 2,
+ CMD_UNHIDE = 3,
+ CMD_MESSAGE = 4,
+ CMD_5 = 5,
+ CMD_ERROR = 6,
+ CMD_7 = 7,
+ CMD_GOTO = 8,
+ CMD_ASSIGN = 9,
+ CMD_DIALOG_END = 255
};
enum ConvEntryFlag {
@@ -74,14 +68,73 @@ enum ConvEntryFlag {
ENTRYFLAG_8000 = 0x8000
};
+struct ScriptEntry {
+ struct Conditional {
+ struct CondtionalParamEntry {
+ bool _isVariable;
+ int _val;
+
+ /**
+ * Constructor
+ */
+ CondtionalParamEntry() : _isVariable(false), _val(0) {}
+ };
+
+ uint _paramsFlag;
+ CondtionalParamEntry _param1;
+ CondtionalParamEntry _param2;
+
+ /**
+ * Constructor
+ */
+ Conditional() : _paramsFlag(false) {}
+
+ /**
+ * Loads data from a passed stream into the parameters structure
+ */
+ void load(Common::SeekableReadStream &s);
+ };
+
+ DialogCommand _command;
+ Conditional _conditionals[3];
+ Common::Array<int> _params;
+
+ /**
+ * Constructor
+ */
+ ScriptEntry() : _command(CMD_NODE_END) {}
+
+ /**
+ * Loads data from a passed stream into the parameters structure
+ */
+ void load(Common::SeekableReadStream &s);
+};
+
+/**
+ * Representation of scripts associated with a dialog
+ */
+class DialogScript : public Common::Array<ScriptEntry> {
+public:
+ /**
+ * Loads a script from the passed stream
+ */
+ void load(Common::SeekableReadStream &s, uint startingOffset);
+};
+
/**
* Reperesents the data for a dialog to be displayed in a conversation
*/
struct ConvDialog {
+ struct ScriptEntry {
+ DialogCommand _command;
+ };
+
int16 _textLineIndex; // 0-based
int16 _speechIndex; // 1-based
- uint16 _nodeOffset; // offset in section 6
- uint16 _nodeSize; // size in section 6
+ uint16 _scriptOffset; // offset of script entry
+ uint16 _scriptSize; // size of script entry
+
+ DialogScript _script;
};
/**
@@ -232,6 +285,7 @@ private:
ConversationVar *_vars;
ConversationVar *_nextStartNode;
int _currentNode;
+ int _dialogNodeOffset, _dialogNodeSize;
/**
* Returns the record for the specified conversation, if it's loaded
@@ -251,7 +305,7 @@ private:
/**
* Flags a conversation option/entry
*/
- void flagEntry(ConvFlagMode mode, int entryIndex);
+ void flagEntry(DialogCommand mode, int entryIndex);
/**
* Generate a menu
@@ -276,7 +330,7 @@ private:
/**
* Executes a conversation entry
*/
- void executeEntry(int index);
+ int executeEntry(int index);
public:
/**
* Constructor