diff options
author | Paul Gilbert | 2015-04-01 23:12:49 -0400 |
---|---|---|
committer | Paul Gilbert | 2015-04-01 23:12:49 -0400 |
commit | c13c02b079d0881b4d1cd205364d043920bab9a5 (patch) | |
tree | 7fe40c874bae7f3080be8631dd9c8d8b32762dde /engines/sherlock | |
parent | 283c6074ad77c0c18c9d1550d7ac7c2443072aec (diff) | |
download | scummvm-rg350-c13c02b079d0881b4d1cd205364d043920bab9a5.tar.gz scummvm-rg350-c13c02b079d0881b4d1cd205364d043920bab9a5.tar.bz2 scummvm-rg350-c13c02b079d0881b4d1cd205364d043920bab9a5.zip |
SHERLOCK: Implemented loadJournalFile
Diffstat (limited to 'engines/sherlock')
-rw-r--r-- | engines/sherlock/journal.cpp | 379 | ||||
-rw-r--r-- | engines/sherlock/journal.h | 30 | ||||
-rw-r--r-- | engines/sherlock/people.h | 1 | ||||
-rw-r--r-- | engines/sherlock/sherlock.cpp | 2 | ||||
-rw-r--r-- | engines/sherlock/talk.h | 8 |
5 files changed, 412 insertions, 8 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 diff --git a/engines/sherlock/journal.h b/engines/sherlock/journal.h index 87e5a4f8f2..af8d683619 100644 --- a/engines/sherlock/journal.h +++ b/engines/sherlock/journal.h @@ -25,20 +25,44 @@ #include "common/scummsys.h" #include "common/array.h" +#include "common/str-array.h" +#include "common/stream.h" namespace Sherlock { +struct JournalEntry { + int _converseNum; + bool _replyOnly; + int _statementNum; + + JournalEntry() : _converseNum(0), _replyOnly(false), _statementNum(0) {} + JournalEntry(int converseNum, int statementNum, bool replyOnly = false) : + _converseNum(converseNum), _statementNum(statementNum), _replyOnly(replyOnly) {} +}; + +class SherlockEngine; + class Journal { -public: - Common::Array<int> _data; +private: + SherlockEngine *_vm; + Common::Array<JournalEntry> _data; + Common::StringArray _directory; + Common::StringArray _locations; + Common::StringArray _entries; int _count; int _maxPage; int _index; int _sub; int _up, _down; int _page; + int _converseNum; + + void loadJournalLocations(); + + bool loadJournalFile(bool alreadyLoaded); +public: public: - Journal(); + Journal(SherlockEngine *vm); void record(int converseNum, int statementNum); }; diff --git a/engines/sherlock/people.h b/engines/sherlock/people.h index aa54c67567..a1fad019c8 100644 --- a/engines/sherlock/people.h +++ b/engines/sherlock/people.h @@ -82,6 +82,7 @@ public: ~People(); Person &operator[](PeopleId id) { return _data[id]; } + Person &operator[](int idx) { return _data[idx]; } bool isHolmesActive() const { return _walkLoaded && _holmesOn; } diff --git a/engines/sherlock/sherlock.cpp b/engines/sherlock/sherlock.cpp index 04a9ed54d5..20a805594e 100644 --- a/engines/sherlock/sherlock.cpp +++ b/engines/sherlock/sherlock.cpp @@ -79,7 +79,7 @@ void SherlockEngine::initialize() { _debugger = new Debugger(this); _events = new Events(this); _inventory = new Inventory(this); - _journal = new Journal(); + _journal = new Journal(this); _people = new People(this); _scene = new Scene(this); _screen = new Screen(this); diff --git a/engines/sherlock/talk.h b/engines/sherlock/talk.h index 0cd0a8c638..48cdd2b5b2 100644 --- a/engines/sherlock/talk.h +++ b/engines/sherlock/talk.h @@ -90,15 +90,12 @@ private: TalkHistoryEntry _talkHistory[500]; int _speaker; int _talkIndex; - int _talkTo; int _scriptSelect; int _converseNum; int _talkStealth; int _talkToFlag; bool _moreTalkUp, _moreTalkDown; - void loadTalkFile(const Common::String &filename); - void stripVoiceCommands(); void setTalkMap(); @@ -108,11 +105,14 @@ private: public: bool _talkToAbort; int _talkCounter; + int _talkTo; public: Talk(SherlockEngine *vm); void setSequences(const byte *talkSequences, const byte *stillSequences, int maxPeople); + Statement &operator[](int idx) { return _statements[idx]; } + void talkTo(const Common::String &filename); void talk(int objNum); @@ -121,6 +121,8 @@ public: void drawInterface(); + void loadTalkFile(const Common::String &filename); + void setStillSeq(int speaker); void clearSequences(); void pullSequence(); |