aboutsummaryrefslogtreecommitdiff
path: root/engines/mads/conversations.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/mads/conversations.cpp')
-rw-r--r--engines/mads/conversations.cpp393
1 files changed, 182 insertions, 211 deletions
diff --git a/engines/mads/conversations.cpp b/engines/mads/conversations.cpp
index 53b8e7900d..f51759dadf 100644
--- a/engines/mads/conversations.cpp
+++ b/engines/mads/conversations.cpp
@@ -28,187 +28,226 @@
namespace MADS {
-#define MAX_SPEAKERS 5
-
-enum DialogCommands {
- cmdNodeEnd = 0,
- //
- cmdHide = 2,
- cmdUnhide = 3,
- cmdMessage = 4,
- //
- //
- cmdGoto = 7,
- //
- cmdAssign = 9,
- cmdDialogEnd = 255
-};
-
-struct ConvDialog {
- int16 textLineIndex; // 0-based
- int16 speechIndex; // 1-based
- uint16 nodeOffset; // offset in section 6
- uint16 nodeSize; // size in section 6
-};
-
-struct ConvNode {
- uint16 index;
- uint16 dialogCount;
- int16 unk1;
- int16 unk2;
- int16 unk3;
-
- Common::Array<ConvDialog> dialogs;
-};
-
-struct ConvData {
- uint16 nodeCount; // conversation nodes, each one containing several dialog options and messages
- uint16 dialogCount; // messages (non-selectable) + texts (selectable)
- uint16 messageCount; // messages (non-selectable)
- uint16 textLineCount;
- uint16 unk2;
- uint16 importCount;
- uint16 speakerCount;
- Common::String portraits[MAX_SPEAKERS];
- bool speakerExists[MAX_SPEAKERS];
- Common::String speechFile;
- Common::Array<Common::String> textLines;
- Common::Array<ConvNode> convNodes;
-};
-
-GameConversation::GameConversation(MADSEngine *vm)
- : _vm(vm) {
- _running = _restoreRunning = 0;
+GameConversations::GameConversations(MADSEngine *vm) : _vm(vm) {
+ _runningConv = nullptr;
+ _restoreRunning = 0;
_nextStartNode = nullptr;
+ _playerEnabled = false;
+ _startFrameNumber = 0;
+
+ // Mark all conversation slots as empty
+ for (int idx = 0; idx < MAX_CONVERSATIONS; ++idx)
+ _conversations[idx]._convId = -1;
}
-GameConversation::~GameConversation() {
+GameConversations::~GameConversations() {
}
-void GameConversation::get(int id) {
- Common::File inFile;
- Common::String fileName = Common::String::format("CONV%03d.CNV", id);
+void GameConversations::get(int id) {
+ // Scan through the conversation list for a free slot
+ int slotIndex = -1;
+ for (int idx = 0; idx < MAX_CONVERSATIONS && slotIndex == -1; ++idx) {
+ if (_conversations[idx]._convId == -1)
+ slotIndex = idx;
+ }
+ if (slotIndex == -1)
+ error("Too many conversations loaded");
+
+ // Set the conversation the slot will contain
+ _conversations[slotIndex]._convId = id;
+
+ // Load the conversation data
+ Common::String cnvFilename = Common::String::format("CONV%03d.CNV", id);
+ _conversations[slotIndex]._data.load(cnvFilename);
+
// TODO: Also handle the .CND file
+}
- inFile.open(fileName);
- MadsPack convFileUnpacked(&inFile);
- Common::SeekableReadStream *convFile = convFileUnpacked.getItemStream(0);
+ConversationEntry *GameConversations::getConv(int convId) {
+ for (uint idx = 0; idx < MAX_CONVERSATIONS; ++idx) {
+ if (_conversations[idx]._convId == convId)
+ return &_conversations[idx];
+ }
+
+ return nullptr;
+}
+
+
+void GameConversations::run(int id) {
+ // If another conversation is running, then stop it first
+ if (_runningConv)
+ stop();
+
+ // Get the next conversation to run
+ _runningConv = getConv(id);
+ if (!_runningConv)
+ error("Specified conversation %d not loaded", id);
+
+ // Initialize needed fields
+ _startFrameNumber = _vm->_events->getFrameCounter();
+ _playerEnabled = _vm->_game->_player._stepEnabled;
+ //TODO
+
+ // Start the conversation
+ start();
+
+ warning("TODO GameConversations::run");
+}
+
+void GameConversations::start() {
+
+ warning("TODO: GameConversations::start");
+}
+
+void GameConversations::stop() {
+ warning("TODO GameConversations::stop");
+}
+
+void GameConversations::exportPointer(int *val) {
+ warning("TODO GameConversations::exportPointer");
+}
+
+void GameConversations::exportValue(int val) {
+ warning("TODO GameConversations::exportValue");
+}
+
+void GameConversations::setHeroTrigger(int val) {
+ _vm->_game->_trigger = val; // HACK
+ //_running = -1; // HACK
+ warning("TODO: GameConversations::setHeroTrigger");
+}
+
+void GameConversations::setInterlocutorTrigger(int val) {
+ warning("TODO: GameConversations::setInterlocutorTrigger");
+}
+
+int* GameConversations::getVariable(int idx) {
+ warning("TODO: GameConversations::getVariable");
+ return nullptr;
+}
+
+void GameConversations::hold() {
+ warning("TODO: GameConversations::hold");
+}
+
+void GameConversations::release() {
+ warning("TODO: GameConversations::release");
+}
+
+void GameConversations::reset(int id) {
+ warning("TODO: GameConversations::reset");
+}
+
+void GameConversations::abortConv() {
+ warning("TODO: GameConversations::abort");
+}
+/*------------------------------------------------------------------------*/
+
+void ConversationData::load(const Common::String &filename) {
+ Common::File inFile;
char buffer[16];
- ConvData conv;
+ inFile.open(filename);
+ MadsPack convFileUnpacked(&inFile);
+ Common::SeekableReadStream *convFile = convFileUnpacked.getItemStream(0);
// **** Section 0: Header *************************************************
- conv.nodeCount = convFile->readUint16LE();
- conv.dialogCount = convFile->readUint16LE();
- conv.messageCount = convFile->readUint16LE();
- conv.textLineCount = convFile->readUint16LE();
- conv.unk2 = convFile->readUint16LE();
- conv.importCount = convFile->readUint16LE();
- conv.speakerCount = convFile->readUint16LE();
-
- //debug("Conv %d has %d nodes, %d dialogs, %d messages, %d text lines, %d unk2, %d imports and %d speakers",
- // id, conv.nodeCount, conv.dialogCount, conv.messageCount, conv.textLineCount, conv.unk2, conv.importCount, conv.speakerCount);
-
- for (uint16 i = 0; i < MAX_SPEAKERS; i++) {
+ _nodeCount = convFile->readUint16LE();
+ _dialogCount = convFile->readUint16LE();
+ _messageCount = convFile->readUint16LE();
+ _textLineCount = convFile->readUint16LE();
+ _unk2 = convFile->readUint16LE();
+ _importCount = convFile->readUint16LE();
+ _speakerCount = convFile->readUint16LE();
+
+ for (uint idx = 0; idx < MAX_SPEAKERS; ++idx) {
convFile->read(buffer, 16);
- conv.portraits[i] = buffer;
- //debug("Speaker %d, portrait %s", i, conv.portraits[i].c_str());
+ _portraits[idx] = buffer;
}
- for (uint16 i = 0; i < MAX_SPEAKERS; i++) {
- conv.speakerExists[i] = convFile->readUint16LE();
- //debug("Speaker %d exists: %d", i, conv.speakerExists[i]);
+ for (uint idx = 0; idx < MAX_SPEAKERS; ++idx) {
+ _speakerExists[idx] = convFile->readUint16LE();
}
convFile->read(buffer, 14);
- conv.speechFile = Common::String(buffer);
- //debug("Speech file %s", conv.speechFile.c_str());
-
- uint16 textLength = convFile->readUint16LE(); // Total text length in section 5
- convFile->skip(2); // TODO: unknown
- uint16 commandLength = convFile->readUint16LE(); // Total length of commands in section 6
- //debug("Node entry commands length: %d", commandLength);
-
-#if 0
- debug("Section 0 unknown bytes");
- byte *tmp0 = new byte[26];
- convFile->read(tmp0, 26);
- Common::hexdump(tmp0, 26);
- delete[] tmp0;
+ _speechFile = Common::String(buffer);
+
+ // Total text length in section 5
+ _textSize = convFile->readUint32LE();
+ _commandsSize = convFile->readUint32LE();
+
+ // The rest of the section 0 is padding to allow room for a set of pointers
+ // to the contents of the remaining sections loaded into memory as a
+ // continuous data block containing both the header and the sections
delete convFile;
-#else
- warning("Section 0 unknown bytes");
-#endif
+
// **** Section 1: Nodes **************************************************
convFile = convFileUnpacked.getItemStream(1);
- for (uint16 i = 0; i < conv.nodeCount; i++) {
+ _convNodes.clear();
+ for (uint i = 0; i < _nodeCount; i++) {
ConvNode node;
- node.index = convFile->readUint16LE();
- node.dialogCount = convFile->readUint16LE();
- node.unk1 = convFile->readSint16LE(); // TODO
- node.unk2 = convFile->readSint16LE(); // TODO
- node.unk3 = convFile->readSint16LE(); // TODO
- conv.convNodes.push_back(node);
+ node._index = convFile->readUint16LE();
+ node._dialogCount = convFile->readUint16LE();
+ node._unk1 = convFile->readSint16LE(); // TODO
+ node._unk2 = convFile->readSint16LE(); // TODO
+ node._unk3 = convFile->readSint16LE(); // TODO
+ _convNodes.push_back(node);
//debug("Node %d, index %d, entries %d - %d, %d, %d", i, node.index, node.dialogCount, node.unk1, node.unk2, node.unk3);
}
delete convFile;
// **** Section 2: Dialogs ************************************************
convFile = convFileUnpacked.getItemStream(2);
- assert(convFile->size() == conv.dialogCount * 8);
+ assert(convFile->size() == _dialogCount * 8);
- for (uint16 i = 0; i < conv.nodeCount; i++) {
- uint16 dialogCount = conv.convNodes[i].dialogCount;
+ for (uint idx = 0; idx < _nodeCount; ++idx) {
+ uint dialogCount = _convNodes[idx]._dialogCount;
- for (uint16 j = 0; j < dialogCount; j++) {
+ for (uint j = 0; j < dialogCount; ++j) {
ConvDialog dialog;
- dialog.textLineIndex = convFile->readSint16LE();
- dialog.speechIndex = convFile->readSint16LE();
- dialog.nodeOffset = convFile->readUint16LE();
- dialog.nodeSize = convFile->readUint16LE();
- conv.convNodes[i].dialogs.push_back(dialog);
- //debug("Node %d, dialog %d: text line %d, speech index %d, node offset %d, node size %d", j, i, dialog.textLineIndex, dialog.speechIndex, dialog.nodeOffset, dialog.nodeSize);
+ dialog._textLineIndex = convFile->readSint16LE();
+ dialog._speechIndex = convFile->readSint16LE();
+ dialog._nodeOffset = convFile->readUint16LE();
+ dialog._nodeSize = convFile->readUint16LE();
+ _convNodes[idx]._dialogs.push_back(dialog);
}
}
delete convFile;
- // **** Section 3: ???? ***************************************************
-#if 0
- debug("Section 3");
+ // **** Section 3: Messages ***********************************************
convFile = convFileUnpacked.getItemStream(3);
- byte *tmp1 = new byte[convFile->size()];
- convFile->read(tmp1, convFile->size());
- Common::hexdump(tmp1, convFile->size());
- delete[] tmp1;
+ assert(convFile->size() == _messageCount * 8);
+
+ _messages.resize(_messageCount);
+ for (uint idx = 0; idx < _messageCount; ++idx)
+ _messages[idx] = convFile->readUint32LE();
+
delete convFile;
-#else
- warning("Section 3 - TODO");
-#endif
+
// **** Section 4: Text line offsets **************************************
convFile = convFileUnpacked.getItemStream(4);
- assert(convFile->size() == conv.textLineCount * 2);
+ assert(convFile->size() == _textLineCount * 2);
- uint16 *textLineOffsets = new uint16[conv.textLineCount]; // deleted below in section 5
- for (uint16 i = 0; i < conv.textLineCount; i++)
+ uint16 *textLineOffsets = new uint16[_textLineCount]; // deleted below in section 5
+ for (uint16 i = 0; i < _textLineCount; i++)
textLineOffsets[i] = convFile->readUint16LE();
delete convFile;
// **** Section 5: Text lines *********************************************
convFile = convFileUnpacked.getItemStream(5);
- assert(convFile->size() == textLength);
+ assert(convFile->size() == _textSize);
Common::String textLine;
- conv.textLines.resize(conv.textLineCount);
+ _textLines.resize(_textLineCount);
char textLineBuffer[256];
uint16 nextOffset;
- for (uint16 i = 0; i < conv.textLineCount; i++) {
- nextOffset = (i != conv.textLineCount - 1) ? textLineOffsets[i + 1] : convFile->size();
+ for (uint16 i = 0; i < _textLineCount; i++) {
+ nextOffset = (i != _textLineCount - 1) ? textLineOffsets[i + 1] : convFile->size();
convFile->read(textLineBuffer, nextOffset - textLineOffsets[i]);
- conv.textLines[i] = Common::String(textLineBuffer);
- //debug("Text line %d: %s", i, conv.textLines[i].c_str());
+ _textLines[i] = Common::String(textLineBuffer);
}
delete[] textLineOffsets;
@@ -216,13 +255,13 @@ void GameConversation::get(int id) {
// **** Section 6: Node entry commands ************************************
convFile = convFileUnpacked.getItemStream(6);
- assert(convFile->size() == commandLength);
+ assert(convFile->size() == _commandsSize);
- for (uint16 i = 0; i < conv.nodeCount; i++) {
- uint16 dialogCount = conv.convNodes[i].dialogCount;
+ for (uint16 i = 0; i < _nodeCount; i++) {
+ uint16 dialogCount = _convNodes[i]._dialogCount;
for (uint16 j = 0; j < dialogCount; j++) {
- //ConvDialog dialog = conv.convNodes[i].dialogs[j];
+ //ConvDialog dialog = _convNodes[i].dialogs[j];
byte command;
uint16 chk;
@@ -248,8 +287,8 @@ void GameConversation::get(int id) {
//debug("Hide node %d", nodeRef);
}
- }
- break;
+ }
+ break;
case cmdUnhide: {
byte count = convFile->readByte();
for (byte k = 0; k < count; k++) {
@@ -257,8 +296,8 @@ void GameConversation::get(int id) {
//debug("Unhide node %d", nodeRef);
}
- }
- break;
+ }
+ break;
case cmdMessage:
//debug("Message");
convFile->skip(7); // TODO
@@ -267,15 +306,15 @@ void GameConversation::get(int id) {
convFile->skip(3); // unused?
/*byte nodeRef = */convFile->readByte();
//debug("Goto %d", nodeRef);
- }
- break;
+ }
+ break;
case cmdAssign: {
convFile->skip(3); // unused?
/*uint16 value = */convFile->readUint16LE();
/*uint16 variable = */convFile->readUint16LE();
//debug("Variable %d = %d", variable, value);
- }
- break;
+ }
+ break;
default:
error("Unknown conversation command %d", command);
break;
@@ -287,76 +326,8 @@ void GameConversation::get(int id) {
delete convFile;
inFile.close();
- /*
- // DEBUG: Show the very first message, and play the very first speech
- _vm->_audio->setSoundGroup(conv.speechFile);
- uint16 firstText = 0, firstSpeech = 1;
-
- for (uint16 i = 0; i < conv.convNodes.size(); i++) {
- for (uint16 k = 0; k < conv.convNodes[i].dialogs.size(); k++) {
- if (conv.convNodes[i].dialogs[k].textLineIndex >= 0) {
- firstText = conv.convNodes[i].dialogs[k].textLineIndex;
- firstSpeech = conv.convNodes[i].dialogs[k].speechIndex;
- break;
- }
- }
- }
-
- _vm->_audio->playSound(firstSpeech - 1);
-
- TextDialog *dialog = new TextDialog(_vm, FONT_INTERFACE, Common::Point(0, 100), 30);
- dialog->addLine(conv.textLines[firstText]);
- dialog->show();
- delete dialog;
- */
-
- warning("TODO GameConversation::get");
-}
-
-void GameConversation::run(int id) {
- warning("TODO GameConversation::run");
-}
-
-void GameConversation::stop() {
- warning("TODO GameConversation::stop");
-}
-
-void GameConversation::exportPointer(int *val) {
- warning("TODO GameConversation::exportPointer");
-}
-
-void GameConversation::exportValue(int val) {
- warning("TODO GameConversation::exportValue");
-}
-
-void GameConversation::setHeroTrigger(int val) {
- _vm->_game->_trigger = val; // HACK
- _running = -1; // HACK
- warning("TODO: GameConversation::setHeroTrigger");
+ // TODO: Still stuff to do
+ warning("TODO GameConversations::get");
}
-void GameConversation::setInterlocutorTrigger(int val) {
- warning("TODO: GameConversation::setInterlocutorTrigger");
-}
-
-int* GameConversation::getVariable(int idx) {
- warning("TODO: GameConversation::getVariable");
- return nullptr;
-}
-
-void GameConversation::hold() {
- warning("TODO: GameConversation::hold");
-}
-
-void GameConversation::release() {
- warning("TODO: GameConversation::release");
-}
-
-void GameConversation::reset(int id) {
- warning("TODO: GameConversation::reset");
-}
-
-void GameConversation::abortConv() {
- warning("TODO: GameConversation::abort");
-}
} // End of namespace MADS