aboutsummaryrefslogtreecommitdiff
path: root/engines/sherlock/talk.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sherlock/talk.cpp')
-rw-r--r--engines/sherlock/talk.cpp326
1 files changed, 121 insertions, 205 deletions
diff --git a/engines/sherlock/talk.cpp b/engines/sherlock/talk.cpp
index d259182a70..3c6bf44837 100644
--- a/engines/sherlock/talk.cpp
+++ b/engines/sherlock/talk.cpp
@@ -28,16 +28,21 @@
#include "sherlock/scalpel/scalpel_talk.h"
#include "sherlock/scalpel/scalpel_user_interface.h"
#include "sherlock/tattoo/tattoo.h"
+#include "sherlock/tattoo/tattoo_fixed_text.h"
#include "sherlock/tattoo/tattoo_people.h"
#include "sherlock/tattoo/tattoo_scene.h"
#include "sherlock/tattoo/tattoo_talk.h"
+#include "sherlock/tattoo/tattoo_user_interface.h"
namespace Sherlock {
SequenceEntry::SequenceEntry() {
_objNum = 0;
- _frameNumber = 0;
+ _obj = nullptr;
+ _seqStack = 0;
_seqTo = 0;
+ _sequenceNumber = _frameNumber = 0;
+ _seqCounter = _seqCounter2 = 0;
}
/*----------------------------------------------------------------*/
@@ -87,17 +92,6 @@ TalkHistoryEntry::TalkHistoryEntry() {
/*----------------------------------------------------------------*/
-TalkSequence::TalkSequence() {
- _obj = nullptr;
- _frameNumber = 0;
- _sequenceNumber = 0;
- _seqStack = 0;
- _seqTo = 0;
- _seqCounter = _seqCounter2 = 0;
-}
-
-/*----------------------------------------------------------------*/
-
Talk *Talk::init(SherlockEngine *vm) {
if (vm->getGameID() == GType_SerratedScalpel)
return new Scalpel::ScalpelTalk(vm);
@@ -121,6 +115,7 @@ Talk::Talk(SherlockEngine *vm) : _vm(vm) {
_scriptSaveIndex = -1;
_opcodes = nullptr;
_opcodeTable = nullptr;
+ _3doSpeechIndex = -1;
_charCount = 0;
_line = 0;
@@ -134,13 +129,14 @@ Talk::Talk(SherlockEngine *vm) : _vm(vm) {
_talkHistory.resize(IS_ROSE_TATTOO ? 1500 : 500);
}
-void Talk::talkTo(const Common::String &filename) {
+void Talk::talkTo(const Common::String filename) {
Events &events = *_vm->_events;
Inventory &inv = *_vm->_inventory;
Journal &journal = *_vm->_journal;
People &people = *_vm->_people;
Scene &scene = *_vm->_scene;
Screen &screen = *_vm->_screen;
+ Sound &sound = *_vm->_sound;
UserInterface &ui = *_vm->_ui;
Common::Rect savedBounds = screen.getDisplayBounds();
bool abortFlag = false;
@@ -176,7 +172,7 @@ void Talk::talkTo(const Common::String &filename) {
// Turn on the Exit option
ui._endKeyActive = true;
- if (people[HOLMES]._walkCount || (people[HOLMES]._walkTo.size() > 0 &&
+ if (people[HOLMES]._walkCount || (!people[HOLMES]._walkTo.empty() &&
(IS_SERRATED_SCALPEL || people._allowWalkAbort))) {
// Only interrupt if trying to do an action, and not just if player is walking around the scene
if (people._allowWalkAbort)
@@ -202,8 +198,12 @@ void Talk::talkTo(const Common::String &filename) {
}
}
- while (!_sequenceStack.empty())
+ if (IS_ROSE_TATTOO) {
pullSequence();
+ } else {
+ while (!isSequencesEmpty())
+ pullSequence();
+ }
if (IS_SERRATED_SCALPEL) {
// Restore any pressed button
@@ -308,8 +308,14 @@ void Talk::talkTo(const Common::String &filename) {
if (_scriptMoreFlag && _scriptSelect != 100)
select = _scriptSelect;
- if (select == -1)
+ if (select == -1) {
+ if (IS_ROSE_TATTOO) {
+ static_cast<Tattoo::TattooUserInterface *>(&ui)->putMessage(
+ "%s", _vm->_fixedText->getText(Tattoo::kFixedText_NoEffect));
+ return;
+ }
error("Couldn't find statement to display");
+ }
// Add the statement into the journal and talk history
if (_talkTo != -1 && !_talkHistory[_converseNum][select])
@@ -339,9 +345,19 @@ void Talk::talkTo(const Common::String &filename) {
_scriptSelect = select;
_speaker = _talkTo;
- Statement &statement = _statements[select];
+ // Set up the talk file extension
+ if (IS_ROSE_TATTOO && sound._speechOn && _scriptMoreFlag != 1)
+ sound._talkSoundFile += Common::String::format("%02dB", select + 1);
+
+ // Make a copy of the statement (in case the script frees the statement list), and then execute it
+ Statement statement = _statements[select];
doScript(_statements[select]._reply);
+ if (IS_ROSE_TATTOO) {
+ for (int idx = 0; idx < MAX_CHARACTERS; ++idx)
+ people[idx]._misc = 0;
+ }
+
if (_talkToAbort)
return;
@@ -391,30 +407,26 @@ void Talk::talkTo(const Common::String &filename) {
if (_talkTo != -1 && !_talkHistory[_converseNum][select])
journal.record(_converseNum, select, true);
_talkHistory[_converseNum][select] = true;
-
}
- ui._key = ui._oldKey = Scalpel::COMMANDS[TALK_MODE - 1];
+ ui._key = ui._oldKey = 'T'; // FIXME: I'm not sure what to do here, I need ScalpelUI->_hotkeyTalk
ui._temp = ui._oldTemp = 0;
ui._menuMode = TALK_MODE;
_talkToFlag = 2;
} else {
freeTalkVars();
- if (!ui._lookScriptFlag) {
- ui.drawInterface(2);
-
- if (IS_SERRATED_SCALPEL) {
+ if (IS_SERRATED_SCALPEL) {
+ if (!ui._lookScriptFlag) {
+ ui.drawInterface(2);
ui._menuMode = STD_MODE;
ui._windowBounds.top = CONTROLS_Y1;
- } else {
- ui._menuMode = static_cast<Tattoo::TattooScene *>(_vm->_scene)->_labTableScene ?
- LAB_MODE : STD_MODE;
}
-
- ui.banishWindow();
+ } else {
+ ui._menuMode = static_cast<Tattoo::TattooScene *>(_vm->_scene)->_labTableScene ? LAB_MODE : STD_MODE;
}
+ ui.banishWindow();
break;
}
}
@@ -435,16 +447,10 @@ void Talk::talkTo(const Common::String &filename) {
// previous script can continue
popStack();
- if (IS_SERRATED_SCALPEL && filename == "Tube59c") {
- // WORKAROUND: Original game bug causes the results of testing the powdery substance
- // to disappear too quickly. Introduce a delay to allow it to be properly displayed
- ui._menuCounter = 30;
- }
-
events.setCursor(ARROW);
}
-void Talk::talk(int objNum) {
+void Talk::initTalk(int objNum) {
Events &events = *_vm->_events;
People &people = *_vm->_people;
Scene &scene = *_vm->_scene;
@@ -465,8 +471,15 @@ void Talk::talk(int objNum) {
break;
}
}
- if (select == -1)
- error("No entry matched all required flags");
+
+ if (select == -1) {
+ freeTalkVars();
+ if (!scumm_strnicmp(talkFilename.c_str(), "PATH", 4))
+ error("No entries found to execute in path file");
+
+ nothingToSay();
+ return;
+ }
// See if the statement is a stealth mode reply
Statement &statement = _statements[select];
@@ -485,7 +498,7 @@ void Talk::talk(int objNum) {
Object &obj = scene._bgShapes[objNum];
clearSequences();
pushSequence(_talkTo);
- setStillSeq(_talkTo);
+ people.setListenSequence(_talkTo, 129);
events.setCursor(WAIT);
if (obj._lookPosition.y != 0)
@@ -506,7 +519,7 @@ void Talk::talk(int objNum) {
Object &obj = scene._bgShapes[objNum];
clearSequences();
pushSequence(_talkTo);
- setStillSeq(_talkTo);
+ people.setListenSequence(_talkTo, 129);
events.setCursor(WAIT);
if (obj._lookPosition.y != 0)
@@ -561,11 +574,16 @@ void Talk::loadTalkFile(const Common::String &filename) {
Common::String talkFile = chP ? Common::String(filename.c_str(), chP) + ".tlk" :
Common::String(filename.c_str(), filename.c_str() + 7) + ".tlk";
+ // Create the base of the sound filename used for talking in Rose Tattoo
+ if (IS_ROSE_TATTOO && _scriptMoreFlag != 1)
+ sound._talkSoundFile = Common::String(filename.c_str(), filename.c_str() + 7) + ".";
+
// Open the talk file for reading
Common::SeekableReadStream *talkStream = res.load(talkFile);
_converseNum = res.resourceIndex();
talkStream->skip(2); // Skip talk file version num
+ _statements.clear();
_statements.resize(talkStream->readByte());
for (uint idx = 0; idx < _statements.size(); ++idx)
_statements[idx].load(*talkStream, IS_ROSE_TATTOO);
@@ -588,7 +606,7 @@ void Talk::stripVoiceCommands() {
// rest of the name following it
statement._reply = Common::String(statement._reply.c_str(),
statement._reply.c_str() + idx) + " " +
- Common::String(statement._reply.c_str() + 9);
+ Common::String(statement._reply.c_str() + idx + 9);
}
}
@@ -617,113 +635,15 @@ void Talk::setTalkMap() {
}
}
-void Talk::clearSequences() {
- _sequenceStack.clear();
-}
-
-void Talk::pullSequence() {
- Scene &scene = *_vm->_scene;
-
- if (_sequenceStack.empty() || IS_ROSE_TATTOO)
- return;
-
- SequenceEntry seq = _sequenceStack.pop();
- if (seq._objNum != -1) {
- Object &obj = scene._bgShapes[seq._objNum];
-
- if (obj._seqSize < MAX_TALK_SEQUENCES) {
- warning("Tried to restore too few frames");
- } else {
- for (int idx = 0; idx < MAX_TALK_SEQUENCES; ++idx)
- obj._sequences[idx] = seq._sequences[idx];
-
- obj._frameNumber = seq._frameNumber;
- obj._seqTo = seq._seqTo;
- }
- }
-}
-
void Talk::pushSequence(int speaker) {
People &people = *_vm->_people;
Scene &scene = *_vm->_scene;
// Only proceed if a speaker is specified
- if (speaker == -1 || IS_ROSE_TATTOO)
- return;
-
- SequenceEntry seqEntry;
- if (!speaker) {
- seqEntry._objNum = -1;
- } else {
- seqEntry._objNum = people.findSpeaker(speaker);
-
- if (seqEntry._objNum != -1) {
- Object &obj = scene._bgShapes[seqEntry._objNum];
- for (uint idx = 0; idx < MAX_TALK_SEQUENCES; ++idx)
- seqEntry._sequences.push_back(obj._sequences[idx]);
-
- seqEntry._frameNumber = obj._frameNumber;
- seqEntry._seqTo = obj._seqTo;
- }
- }
-
- _sequenceStack.push(seqEntry);
- if (_scriptStack.size() >= 5)
- error("script stack overflow");
-}
-
-void Talk::pushTalkSequence(Object *obj) {
- // Check if the shape is already on the stack
- for (uint idx = 0; idx < TALK_SEQUENCE_STACK_SIZE; ++idx) {
- if (_talkSequenceStack[idx]._obj == obj)
- return;
- }
-
- // Find a free slot and save the details in it
- for (uint idx = 0; idx < TALK_SEQUENCE_STACK_SIZE; ++idx) {
- TalkSequence &ts = _talkSequenceStack[idx];
- if (ts._obj == nullptr) {
- ts._obj = obj;
- ts._frameNumber = obj->_frameNumber;
- ts._sequenceNumber = obj->_sequenceNumber;
- ts._seqStack = obj->_seqStack;
- ts._seqTo = obj->_seqTo;
- ts._seqCounter = obj->_seqCounter;
- ts._seqCounter2 = obj->_seqCounter2;
- return;
- }
- }
-
- error("Ran out of talk sequence stack space");
-}
-
-void Talk::setStillSeq(int speaker) {
- People &people = *_vm->_people;
- Scene &scene = *_vm->_scene;
-
- // Don't bother doing anything if no specific speaker is specified
- if (speaker == -1)
- return;
-
- if (speaker) {
+ if (speaker != -1) {
int objNum = people.findSpeaker(speaker);
- if (objNum != -1) {
- Object &obj = scene._bgShapes[objNum];
-
- if (obj._seqSize < MAX_TALK_SEQUENCES) {
- warning("Tried to copy too few still frames");
- } else {
- for (uint idx = 0; idx < MAX_TALK_SEQUENCES; ++idx) {
- obj._sequences[idx] = people._characters[speaker]._stillSequences[idx];
- if (idx > 0 && !people._characters[speaker]._talkSequences[idx] &&
- !people._characters[speaker]._talkSequences[idx - 1])
- break;
- }
-
- obj._frameNumber = 0;
- obj._seqTo = 0;
- }
- }
+ if (objNum != -1)
+ pushSequenceEntry(&scene._bgShapes[objNum]);
}
}
@@ -757,6 +677,7 @@ void Talk::doScript(const Common::String &script) {
Tattoo::TattooPerson &p = (*(Tattoo::TattooPeople *)_vm->_people)[idx];
p._savedNpcSequence = p._sequenceNumber;
p._savedNpcFrame = p._frameNumber;
+ p._resetNPCPath = true;
}
}
@@ -770,17 +691,23 @@ void Talk::doScript(const Common::String &script) {
_talkStealth = 2;
_speaker |= SPEAKER_REMOVE;
} else {
- pushSequence(_speaker);
+ if (IS_SERRATED_SCALPEL)
+ pushSequence(_speaker);
if (IS_SERRATED_SCALPEL || ui._windowOpen)
ui.clearWindow();
// Need to switch speakers?
if (str[0] == _opcodes[OP_SWITCH_SPEAKER]) {
_speaker = str[1] - 1;
- str += IS_SERRATED_SCALPEL ? 2 : 3;
- pullSequence();
- pushSequence(_speaker);
+ if (IS_SERRATED_SCALPEL) {
+ str += 2;
+ pullSequence();
+ pushSequence(_speaker);
+ } else {
+ str += 3;
+ }
+
people.setTalkSequence(_speaker);
} else {
people.setTalkSequence(_speaker);
@@ -821,9 +748,6 @@ void Talk::doScript(const Common::String &script) {
}
}
- bool trigger3DOMovie = true;
- uint16 subIndex = 1;
-
do {
Common::String tempString;
_wait = 0;
@@ -836,15 +760,28 @@ void Talk::doScript(const Common::String &script) {
while (*str++ != '}')
;
} else if (isOpcode(c)) {
+ // the original interpreter checked for c being >= 0x80
+ // and if that is the case, it tried to process it as opcode, BUT ALSO ALWAYS skipped over it
+ // This was done inside the Spanish + German interpreters of Serrated Scalpel, not the original
+ // English interpreter (reverse engineered from the binaries).
+ //
+ // This resulted in special characters not getting shown in case they occurred at the start
+ // of sentences like for example the inverted exclamation mark and the inverted question mark.
+ // For further study see fonts.cpp
+ //
+ // We create an inverted exclamation mark for the Spanish version and we show it.
+ //
+ // Us not skipping over those characters may result in an assert() happening inside fonts.cpp
+ // in case more invalid characters exist.
+ // More information see bug #6931
+ //
+
// Handle control code
switch ((this->*_opcodeTable[c - _opcodes[0]])(str)) {
case RET_EXIT:
return;
case RET_CONTINUE:
continue;
- case OP_SWITCH_SPEAKER:
- trigger3DOMovie = true;
- break;
default:
break;
}
@@ -867,14 +804,6 @@ void Talk::doScript(const Common::String &script) {
_openTalkWindow = false;
}
- if ((_wait) && (trigger3DOMovie)) {
- // Trigger to play 3DO movie
- talk3DOMovieTrigger(subIndex);
-
- trigger3DOMovie = false; // wait for next switch speaker opcode
- subIndex++;
- }
-
if (_wait)
// Handling pausing
talkWait(str);
@@ -910,14 +839,23 @@ int Talk::waitForMore(int delay) {
UserInterface &ui = *_vm->_ui;
CursorId oldCursor = events.getCursor();
int key2 = 254;
+ bool playingSpeech = false;
// Unless we're in stealth mode, show the appropriate cursor
if (!_talkStealth) {
events.setCursor(ui._lookScriptFlag ? MAGNIFY : ARROW);
}
+ // Handle playing any speech associated with the text being displayed
+ switchSpeaker();
+ if (sound._speechOn && IS_ROSE_TATTOO) {
+ sound.playSpeech(sound._talkSoundFile);
+ sound._talkSoundFile.setChar(sound._talkSoundFile.lastChar() + 1, sound._talkSoundFile.size() - 1);
+ }
+ playingSpeech = sound.isSpeechPlaying();
+
do {
- if (sound._speechOn && !*sound._soundIsOn)
+ if (IS_SERRATED_SCALPEL && playingSpeech && !sound.isSpeechPlaying())
people._portrait._frameNumber = -1;
scene.doBgAnim();
@@ -928,6 +866,7 @@ int Talk::waitForMore(int delay) {
events._released = true;
} else {
// See if there's been a button press
+ events.pollEventsAndWait();
events.setButtonState();
if (events.kbHit()) {
@@ -955,18 +894,17 @@ int Talk::waitForMore(int delay) {
if ((delay > 0 && !ui._invLookFlag && !ui._lookScriptFlag) || _talkStealth)
--delay;
- // If there are voices playing, reset delay so that they keep playing
- if (sound._voices == 2 && *sound._soundIsOn)
+ if (playingSpeech && !sound.isSpeechPlaying())
delay = 0;
- } while (!_vm->shouldQuit() && key2 == 254 && (delay || (sound._voices == 2 && *sound._soundIsOn))
+ } while (!_vm->shouldQuit() && key2 == 254 && (delay || (playingSpeech && sound.isSpeechPlaying()))
&& !events._released && !events._rightReleased);
- // If voices was set 2 to indicate a voice file was place, then reset it back to 1
+ // If voices was set 2 to indicate a Scalpel voice file was playing, then reset it back to 1
if (sound._voices == 2)
sound._voices = 1;
- if (delay > 0 && sound._diskSoundPlaying)
- sound.stopSndFuncPtr(0, 0);
+ if (delay > 0 && sound.isSpeechPlaying())
+ sound.stopSpeech();
// Adjust _talkStealth mode:
// mode 1 - It was by a pause without stealth being on before the pause, so reset back to 0
@@ -983,7 +921,8 @@ int Talk::waitForMore(int delay) {
break;
}
- sound._speechOn = false;
+
+ sound.stopSpeech();
events.setCursor(_talkToAbort ? ARROW : oldCursor);
events._pressed = events._released = false;
@@ -1063,6 +1002,10 @@ OpcodeReturn Talk::cmdAdjustObjectSequence(const byte *&str) {
_seqCount = str[1];
str += (str[0] & 127) + 2;
+ // WORKAROUND: Original German Scalpel crash when moving box at Tobacconists
+ if (_vm->getLanguage() == Common::DE_DEU && _scriptName == "Alfr30Z")
+ _seqCount = 16;
+
// Copy in the new sequence
for (int idx = 0; idx < _seqCount; ++idx, ++str)
scene._bgShapes[objId]._sequences[idx] = str[0] - 1;
@@ -1092,42 +1035,6 @@ OpcodeReturn Talk::cmdBanishWindow(const byte *&str) {
return RET_SUCCESS;
}
-OpcodeReturn Talk::cmdCallTalkFile(const byte *&str) {
- Common::String tempString;
-
- ++str;
- for (int idx = 0; idx < 8 && str[idx] != '~'; ++idx)
- tempString += str[idx];
- str += 8;
-
- int scriptCurrentIndex = str - _scriptStart;
-
- // Save the current script position and new talk file
- if (_scriptStack.size() < 9) {
- ScriptStackEntry rec1;
- rec1._name = _scriptName;
- rec1._currentIndex = scriptCurrentIndex;
- rec1._select = _scriptSelect;
- _scriptStack.push(rec1);
-
- // Push the new talk file onto the stack
- ScriptStackEntry rec2;
- rec2._name = tempString;
- rec2._currentIndex = 0;
- rec2._select = 100;
- _scriptStack.push(rec2);
- }
- else {
- error("Script stack overflow");
- }
-
- _scriptMoreFlag = 1;
- _endStr = true;
- _wait = 0;
-
- return RET_SUCCESS;
-}
-
OpcodeReturn Talk::cmdDisableEndKey(const byte *&str) {
_vm->_ui->_endKeyActive = false;
return RET_SUCCESS;
@@ -1145,6 +1052,7 @@ OpcodeReturn Talk::cmdEndTextWindow(const byte *&str) {
OpcodeReturn Talk::cmdHolmesOff(const byte *&str) {
People &people = *_vm->_people;
people[HOLMES]._type = REMOVE;
+ people._holmesOn = false;
return RET_SUCCESS;
}
@@ -1152,6 +1060,7 @@ OpcodeReturn Talk::cmdHolmesOff(const byte *&str) {
OpcodeReturn Talk::cmdHolmesOn(const byte *&str) {
People &people = *_vm->_people;
people[HOLMES]._type = CHARACTER;
+ people._holmesOn = true;
return RET_SUCCESS;
}
@@ -1168,6 +1077,8 @@ OpcodeReturn Talk::cmdPauseWithoutControl(const byte *&str) {
Scene &scene = *_vm->_scene;
++str;
+ events.incWaitCounter();
+
for (int idx = 0; idx < (str[0] - 1); ++idx) {
scene.doBgAnim();
if (_talkToAbort)
@@ -1178,6 +1089,9 @@ OpcodeReturn Talk::cmdPauseWithoutControl(const byte *&str) {
events.setButtonState();
}
+ events.decWaitCounter();
+
+ _endStr = false;
return RET_SUCCESS;
}
@@ -1204,7 +1118,9 @@ OpcodeReturn Talk::cmdRunCAnimation(const byte *&str) {
return RET_EXIT;
// Check if next character is changing side or changing portrait
- if (_charCount && (str[1] == _opcodes[OP_SWITCH_SPEAKER] || str[1] == _opcodes[OP_ASSIGN_PORTRAIT_LOCATION]))
+ _wait = 0;
+ if (_charCount && (str[1] == _opcodes[OP_SWITCH_SPEAKER] ||
+ (IS_SERRATED_SCALPEL && str[1] == _opcodes[OP_ASSIGN_PORTRAIT_LOCATION])))
_wait = 1;
return RET_SUCCESS;
@@ -1292,7 +1208,7 @@ void Talk::talkWait(const byte *&str) {
_endStr = true;
// If a key was pressed to finish the window, see if further voice files should be skipped
- if (_wait >= 0 && _wait < 254) {
+ if (IS_SERRATED_SCALPEL && _wait >= 0 && _wait < 254) {
if (str[0] == _opcodes[OP_SFX_COMMAND])
str += 9;
}