aboutsummaryrefslogtreecommitdiff
path: root/engines/sherlock/journal.cpp
diff options
context:
space:
mode:
authorPaul Gilbert2015-04-01 23:12:49 -0400
committerPaul Gilbert2015-04-01 23:12:49 -0400
commitc13c02b079d0881b4d1cd205364d043920bab9a5 (patch)
tree7fe40c874bae7f3080be8631dd9c8d8b32762dde /engines/sherlock/journal.cpp
parent283c6074ad77c0c18c9d1550d7ac7c2443072aec (diff)
downloadscummvm-rg350-c13c02b079d0881b4d1cd205364d043920bab9a5.tar.gz
scummvm-rg350-c13c02b079d0881b4d1cd205364d043920bab9a5.tar.bz2
scummvm-rg350-c13c02b079d0881b4d1cd205364d043920bab9a5.zip
SHERLOCK: Implemented loadJournalFile
Diffstat (limited to 'engines/sherlock/journal.cpp')
-rw-r--r--engines/sherlock/journal.cpp379
1 files changed, 378 insertions, 1 deletions
diff --git a/engines/sherlock/journal.cpp b/engines/sherlock/journal.cpp
index 34423d2480..a9eb86bf07 100644
--- a/engines/sherlock/journal.cpp
+++ b/engines/sherlock/journal.cpp
@@ -21,10 +21,11 @@
*/
#include "sherlock/journal.h"
+#include "sherlock/sherlock.h"
namespace Sherlock {
-Journal::Journal() {
+Journal::Journal(SherlockEngine *vm): _vm(vm) {
// Allow up to 1000 statements
_data.resize(1000);
@@ -35,10 +36,386 @@ Journal::Journal() {
_sub = 0;
_up = _down = 0;
_page = 0;
+ _converseNum = -1;
+
+ // Load the journal directory and location names
+ loadJournalLocations();
}
+/**
+ * Records statements that are said, in the order which they are said. The player
+ * can then read the journal to review them
+ */
void Journal::record(int converseNum, int statementNum) {
+ int saveIndex = _index;
+ int saveSub = _sub;
+
+ // Record the entry into the list
+ _data.push_back(JournalEntry(converseNum, statementNum));
+
+ bool newLines = loadJournalFile(true);
+
// TODO
}
+void Journal::loadJournalLocations() {
+ Resources &res = *_vm->_res;
+ char c;
+
+ _directory.clear();
+
+ Common::SeekableReadStream *dir = res.load("talk.lib");
+ dir->skip(4); // Skip header
+
+ // Get the numer of entries
+ _directory.resize(dir->readUint16LE());
+
+ // Read in each entry
+ for (uint idx = 0; idx < _directory.size(); ++idx) {
+ Common::String line;
+ while ((c = dir->readByte()) != 0)
+ line += c;
+
+ _directory.push_back(line);
+ }
+
+ delete dir;
+
+ // Load in the locations stored in journal.txt
+ Common::SeekableReadStream *loc = res.load("journal.txt");
+
+ _locations.clear();
+ while (loc->pos() < loc->size()) {
+ Common::String line;
+ while ((c = loc->readByte()) != '\0')
+ line += c;
+
+ _locations.push_back(line);
+ }
+
+ delete loc;
+}
+
+/**
+ * Loads the description for the current display index in the journal, and then
+ * word wraps the result to prepare it for being displayed
+ */
+bool Journal::loadJournalFile(bool alreadyLoaded) {
+ Inventory &inv = *_vm->_inventory;
+ People &people = *_vm->_people;
+ Screen &screen = *_vm->_screen;
+ Talk &talk = *_vm->_talk;
+ JournalEntry &journalEntry = _data[_index];
+ Statement &statement = talk[journalEntry._statementNum];
+
+ Common::String dirFilename = _directory[journalEntry._converseNum];
+ bool replyOnly = journalEntry._replyOnly;
+ Common::String locStr(dirFilename.c_str(), dirFilename.c_str() + 4);
+ int newLocation = atoi(locStr.c_str());
+
+ // If not flagged as alrady loaded, load the conversation into script variables
+ if (!alreadyLoaded) {
+ // See if the file to be used is already loaded
+ if (journalEntry._converseNum != _converseNum) {
+ // Nope. Free any previously loaded talk
+ talk.freeTalkVars();
+
+ // Find the person being talked to
+ talk._talkTo = -1;
+ for (int idx = 0; idx < MAX_PEOPLE; ++idx) {
+ Common::String portrait = people[idx]._portrait;
+ Common::String numStr(portrait.c_str(), portrait.c_str() + 4);
+
+ if (locStr == numStr) {
+ talk._talkTo = idx;
+ break;
+ }
+ }
+
+ // Load the talk file
+ talk.loadTalkFile(dirFilename);
+ }
+ }
+
+ if (talk[0]._statement.hasPrefix("*") || talk[0]._statement.hasPrefix("^"))
+ replyOnly = true;
+
+ // If this isn't the first journal entry, see if the previous journal entry
+ // was in the same scene to see if we need to include the scene header
+ int oldLocation = -1;
+ if (_index != 0) {
+ // Get the scene number of the prior journal entry
+ Common::String priorEntry = _directory[_data[_index - 1]._converseNum];
+ oldLocation = atoi(Common::String(priorEntry.c_str() + 4, priorEntry.c_str() + 6).c_str());
+ }
+
+ // Start building journal string
+ Common::String journalString;
+
+ if (newLocation != oldLocation) {
+ // Add in scene title
+ journalString = "@" + _locations[newLocation - 1] + ":";
+
+ // See if title can fit into a single line, or requires splitting on 2 lines
+ int width = screen.stringWidth(journalString.c_str() + 1);
+ if (width > 230) {
+ // Scan backwards from end of title to find a space between a word
+ // where the width is less than the maximum allowed for the line
+ const char *lineP = journalString.c_str() + journalString.size() - 1;
+ while (width > 230 || *lineP != ' ')
+ width -= screen.charWidth(*lineP--);
+
+ // Split the header into two lines, and add a '@' prefix
+ // to the second line as well
+ journalString = Common::String(journalString.c_str(), lineP) + "\n@" +
+ Common::String(lineP + 1);
+ }
+
+ // Add a newline at the end of the title
+ journalString += '\n';
+ }
+
+ // If Holmes has something to say first, then take care of it
+ if (!replyOnly) {
+ // Handle the grammar
+ journalString += "Holmes ";
+ if (talk[journalEntry._statementNum]._statement.hasSuffix("?"))
+ journalString += "asked ";
+ else
+ journalString += "said to ";
+
+ switch (talk._talkTo) {
+ case 1:
+ journalString += "me";
+ break;
+ case 2:
+ journalString += "the Inspector";
+ break;
+ default:
+ journalString += inv._names[talk._talkTo];
+ break;
+ }
+ journalString += ", \"";
+
+ // Add the statement
+ journalString += statement._statement;
+ }
+
+ // Handle including the reply
+ bool startOfReply = true;
+ bool ctrlSpace = false;
+ bool commentFlag = false;
+ bool commentJustPrinted = false;
+ const char *replyP = statement._reply.c_str();
+
+ while (*replyP) {
+ char c = *replyP;
+
+ // Is it a control character?
+ if (c < 128) {
+ // Nope. Set flag for allowing control coes to insert spaces
+ ctrlSpace = true;
+
+ // Check for embedded comments
+ if (c == '{' || c == '}') {
+ // Comment characters. If we're starting a comment and there's
+ // already text displayed, add a closing quote
+ if (c == '{' && !startOfReply && !commentJustPrinted)
+ journalString += '"';
+
+ // If a reply isn't just being started, and we didn't just end
+ // a comment (which would have added a line), add a carriage return
+ if (!startOfReply && ((!commentJustPrinted && c == '{') || c == '}'))
+ journalString += '"';
+
+ // Handle setting or clearing comment state
+ if (c == '{') {
+ commentFlag = true;
+ commentJustPrinted = false;
+ } else {
+ commentFlag = false;
+ commentJustPrinted = true;
+ }
+ } else {
+ if (startOfReply) {
+ if (!replyOnly) {
+ journalString += "\"\n";
+
+ if (talk._talkTo == 1)
+ journalString += "I replied, \"";
+ else
+ journalString += "The reply was, \"";
+ } else {
+ if (talk._talkTo == 1)
+ journalString += "I";
+ else if (talk._talkTo == 2)
+ journalString += "The Inspector";
+ else
+ journalString += inv._names[talk._talkTo];
+
+ const char *strP = replyP + 1;
+ char v;
+ do {
+ v = *strP++;
+ } while (v && v < 128 && v != '.' && v != '!' && v != '?');
+
+ if (v == '?')
+ journalString += " asked, \"";
+ else
+ journalString += " said, \"";
+ }
+
+ startOfReply = false;
+ }
+
+ // Copy text from the place until either the reply ends, a comment
+ // {} block is started, or a control character is encountered
+ do {
+ journalString += *replyP++;
+ } while (*replyP && *replyP < 128 && *replyP != '{' && *replyP != '}');
+
+ // Move pointer back, since the outer for loop will increment it again
+ --replyP;
+ commentJustPrinted = false;
+ }
+ } else if (c == 128) {
+ if (!startOfReply) {
+ if (!commentFlag && !commentJustPrinted)
+ journalString += "\"\n";
+
+ journalString += "Then ";
+ commentFlag = false;
+ } else if (!replyOnly) {
+ journalString += "\"\n";
+ }
+
+ startOfReply = false;
+ c = *++replyP;
+
+ if ((c - 1) == 0)
+ journalString += "Holmes";
+ else if ((c - 1) == 1)
+ journalString += "I";
+ else if ((c - 1) == 2)
+ journalString += "the Inspector";
+ else
+ journalString += inv._names[c - 1];
+
+ const char *strP = replyP + 1;
+ char v;
+ do {
+ v = *strP++;
+ } while (v && v < 128 && v != '.' && v != '!' && v != '?');
+
+ if (v == '?')
+ journalString += " asked, \"";
+ else
+ journalString += " said, \"";
+ } else {
+ // Control code, so move past it and any parameters
+ ++replyP;
+ switch (c) {
+ case 129: // Run canim
+ case 130: // Assign side
+ case 131: // Pause with control
+ case 136: // Pause without control
+ case 157: // Walk to canimation
+ // These commands don't have any param
+ break;
+
+ case 134: // Change sequence
+ replyP += (replyP[0] & 127) + replyP[2] + 1;
+ break;
+
+ case 135: // Walk to co-ords
+ case 154: // Move mouse
+ replyP += 3;
+ break;
+
+ case 139: // Set flag
+ case 143: // If statement
+ ++replyP;
+ break;
+
+ case 140: // Play voice file
+ case 150: // Play prologue
+ case 153: // Call talk file
+ replyP += 7;
+ break;
+
+ case 141: // Toggle object
+ case 151: // Put item in inventory
+ case 152: // Set object
+ case 155: // Info line
+ case 158: // Delete item from inventory
+ replyP += *replyP & 127;
+ break;
+
+ case 149: // Goto scene
+ replyP += 4;
+ break;
+
+ case 161: // End of line
+ journalString += "\n";
+ break;
+
+ default:
+ break;
+ }
+
+ // Put a space in the output for a control character, unless it's
+ // immediately coming after another control character
+ if (ctrlSpace && c != 130 && c != 161 && !commentJustPrinted) {
+ journalString += " ";
+ ctrlSpace = false;
+ }
+ }
+ }
+
+ if (!startOfReply && !commentJustPrinted)
+ journalString += '"';
+
+ // Finally finished building the journal text. Need to process the text to
+ // word wrap it to fit on-screen. The resulting lines are stored in the
+ // _entries array
+ _entries.clear();
+
+ while (!journalString.empty()) {
+ const char *startP = journalString.c_str();
+
+ // If the first character is a '@' flagging a title line, then move
+ // past it, so the @ won't be included in the line width calculation
+ if (*startP == '@')
+ ++startP;
+
+ // Build up chacters until a full line is found
+ int width = 0;
+ const char *endP = startP;
+ while (width < 230 && *endP && *endP != '\n' && (endP - startP) < 79)
+ width += screen.charWidth(*endP++);
+
+ // If word wrapping, move back to end of prior word
+ if (width >= 230 || (endP - startP) >= 79) {
+ while (*--endP != ' ')
+ ;
+ }
+
+ // Add in the line
+ _entries.push_back(Common::String(startP, endP));
+
+ // Strip line off from string being processed
+ journalString = *endP ? Common::String(endP + 1) : "";
+ }
+
+ // Add a blank line at the end of the text as long as text was present
+ if (!startOfReply) {
+ _entries.push_back("");
+ } else {
+ _entries.clear();
+ }
+
+ return _entries.size();
+}
+
+
} // End of namespace Sherlock