diff options
-rw-r--r-- | queen/cutaway.cpp | 50 | ||||
-rw-r--r-- | queen/cutaway.h | 6 | ||||
-rw-r--r-- | queen/module.mk | 1 | ||||
-rw-r--r-- | queen/talk.cpp | 428 | ||||
-rw-r--r-- | queen/talk.h | 148 |
5 files changed, 594 insertions, 39 deletions
diff --git a/queen/cutaway.cpp b/queen/cutaway.cpp index 72d0847403..275d18ba46 100644 --- a/queen/cutaway.cpp +++ b/queen/cutaway.cpp @@ -22,6 +22,7 @@ #include "stdafx.h" #include "cutaway.h" #include "graphics.h" +#include "talk.h" namespace Queen { @@ -46,7 +47,6 @@ namespace Queen { Data needed: CURRSONG - GAMESTATE JOEF (Joe's face direction) JX,JY (Joe's coordintes) PERSON_FACE @@ -68,11 +68,11 @@ Cutaway::Cutaway( const char *filename, Logic *logic, Resource *resource) -: _logic(logic), _quit(false), _lastSong(0), _songBeforeComic(0) { +: _logic(logic), _resource(resource), _quit(false), _lastSong(0), _songBeforeComic(0) { // XXX should not create this object ourselves _graphics = new Graphics(resource); memset(&_bankNames, 0, sizeof(_bankNames)); - load(filename, resource); + load(filename); } Cutaway::~Cutaway() { @@ -81,14 +81,12 @@ Cutaway::~Cutaway() { delete[] _fileData; } -void Cutaway::load(const char *filename, Resource *resource) { +void Cutaway::load(const char *filename) { byte *ptr; - ptr = _fileData = resource->loadFile(filename, 20); + ptr = _fileData = _resource->loadFile(filename, 20); if (!_fileData) { error("Failed to load resource data file '%s'", filename); - _quit = true; - return; } if (0 == scumm_stricmp(filename, "comic.cut")) @@ -149,7 +147,7 @@ void Cutaway::load(const char *filename, Resource *resource) { } char entryString[MAX_STRING_SIZE]; - _nextSentence = getString(_nextSentence, entryString, MAX_STRING_LENGTH); + _nextSentence = Talk::getString(_nextSentence, entryString, MAX_STRING_LENGTH); debug(0, "Entry string = '%s'", entryString); if (entryString[0] == '*' && @@ -173,27 +171,6 @@ void Cutaway::load(const char *filename, Resource *resource) { } -byte *Cutaway::getString(byte *ptr, char *str, int maxLength) { - int length = *ptr; - ptr++; - - if (length > maxLength) { - error("String too long. Length = %i, maxLength = %i, str = '%*s'", - length, maxLength, length, (const char*)ptr); - } - else if (length) { - memcpy(str, (const char*)ptr, length); - ptr += length; - - while ((int)ptr % 2) - ptr++; - } - - str[length] = '\0'; - - return ptr; -} - void Cutaway::loadStrings(byte *ptr) { int i,j; @@ -208,7 +185,7 @@ void Cutaway::loadStrings(byte *ptr) { */ for (i = 0, j = 0; i < bankNameCount; i++) { - ptr = getString(ptr, _bankNames[j], MAX_FILENAME_LENGTH); + ptr = Talk::getString(ptr, _bankNames[j], MAX_FILENAME_LENGTH); if (_bankNames[j][0]) { debug(0, "Bank name %i = '%s'", _bankNames[j]); @@ -217,7 +194,7 @@ void Cutaway::loadStrings(byte *ptr) { } debug(0, "Getting talk file"); - ptr = getString(ptr, _talkFile, MAX_FILENAME_LENGTH); + ptr = Talk::getString(ptr, _talkFile, MAX_FILENAME_LENGTH); debug(0, "Talk file = '%s'", _talkFile); int TALKTO = READ_BE_UINT16(ptr); @@ -268,6 +245,7 @@ void Cutaway::dumpCutawayObject(int index, CutawayObject &object) switch (object.objectNumber) { case -1: objectNumberStr = "MESSAGE"; break; case 0: objectNumberStr = "Joe"; break; + case 196: objectNumberStr = "Chef"; break; case 548: objectNumberStr = "Anderson"; break; default: objectNumberStr = "unknown"; break; } @@ -731,7 +709,7 @@ void Cutaway::run(char *nextFilename) { limitBob(object); char sentence[MAX_STRING_SIZE]; - _nextSentence = getString(_nextSentence, sentence, MAX_STRING_LENGTH); + _nextSentence = Talk::getString(_nextSentence, sentence, MAX_STRING_LENGTH); debug(0, "Sentence = '%s'", sentence); if (OBJECT_ROOMFADE == object.objectNumber) { @@ -1006,11 +984,11 @@ void Cutaway::updateGameState() { // Turn area on or off if (areaSubIndex > 0) { - int16* area = _logic->area(areaIndex, areaSubIndex); + int16 *area = _logic->area(areaIndex, areaSubIndex); area[0] = abs(area[0]); } else { - int16* area = _logic->area(areaIndex, abs(areaSubIndex)); + int16 *area = _logic->area(areaIndex, abs(areaSubIndex)); area[0] = -abs(area[0]); } } @@ -1033,8 +1011,10 @@ void Cutaway::talk(char *nextFilename) { // Lines 2119-2131 in cutaway.c if (0 == scumm_stricmp(right(_talkFile, 4), ".dog")) { - warning("Cutaway::talk() needed but not yet implemented"); + warning("Cutaway::talk() used but not fully implemented"); nextFilename[0] = '\0'; + + Talk::run(_talkFile, nextFilename, _logic, _resource); } } diff --git a/queen/cutaway.h b/queen/cutaway.h index 94e3dd4c5e..2718cd773a 100644 --- a/queen/cutaway.h +++ b/queen/cutaway.h @@ -121,6 +121,7 @@ class Cutaway { }; Logic *_logic; + Resource *_resource; Graphics *_graphics; //! Raw .cut file data (without 20 byte header) @@ -188,7 +189,7 @@ class Cutaway { void run(char *nextFilename); //! Load cutaway data from file - void load(const char *filename, Resource *resource); + void load(const char *filename); //! Used by load to read string data void loadStrings(byte *ptr); @@ -235,9 +236,6 @@ class Cutaway { //! Get CutawayAnim data from ptr and return new ptr byte *getCutawayAnim(byte *ptr, int header, CutawayAnim &anim); - //! Read a string from ptr and return new ptr - static byte *getString(byte *ptr, char *str, int maxLength); - //! Read a CutawayObject from ptr and return new ptr static byte *getCutawayObject(byte *ptr, CutawayObject &object); diff --git a/queen/module.mk b/queen/module.mk index 8290094d50..9835ec84eb 100644 --- a/queen/module.mk +++ b/queen/module.mk @@ -7,6 +7,7 @@ MODULE_OBJS = \ queen/queen.o \ queen/resource.o \ queen/restables.o \ + queen/talk.o # This module can be built as a plugin ifdef BUILD_PLUGINS diff --git a/queen/talk.cpp b/queen/talk.cpp new file mode 100644 index 0000000000..95ebbc3ccd --- /dev/null +++ b/queen/talk.cpp @@ -0,0 +1,428 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#include "stdafx.h" +#include "talk.h" +#include "graphics.h" + +namespace Queen { + +/* + Functions needed: + + Data needed: + + */ + +void Talk::run( + const char *filename, + char *cutawayFilename, + Logic *logic, + Resource *resource) { + Talk *talk = new Talk(logic, resource); + talk->talk(filename, cutawayFilename); + delete talk; +} + +Talk::Talk( + Logic *logic, + Resource *resource) +: _logic(logic), _resource(resource), _fileData(NULL), _quit(false) { + + //! TODO Move this to the Logic class later! + memset(_talkSelected, 0, sizeof(_talkSelected)); +} + +Talk::~Talk() { + delete[] _fileData; +} + + + +void Talk::talk(const char *filename, char *cutawayFilename) { + _oldSelectedSentenceIndex = 0; + _oldSelectedSentenceValue = 0; + + debug(0, "----- talk(\"%s\") -----", filename); + + cutawayFilename[0] = '\0'; + load(filename); + + char personName[MAX_STRING_SIZE]; + // XXX SET_PERSON_DATA(N,NAMEstr,0); + int bobNum = 0; // XXX P_BNUM; + // XXX strcpy(PERstr,P_NAMEstr); + personName[0] = '\0'; + + int16 oldLevel = 0; + bool personWalking = false; // OWALK in talk.c + + // Lines 828-846 in talk.c + for (int i = 1; i <= 4; i++) { + if (talkSelected()->values[i-1] > 0) { + // This option has been redefined so display new dialogue option + _dialogueTree[1][i].head = talkSelected()->values[i-1]; + } + else if (talkSelected()->values[i-1] == -1) { + + // Already selected so don't redisplay + if (_dialogueTree[1][i].gameStateIndex >= 0) { + _dialogueTree[1][i].head = -1; + _dialogueTree[1][i].dialogueNodeValue1 = -1; + _dialogueTree[1][i].gameStateIndex = -1; + _dialogueTree[1][i].gameStateValue = -1; + } + } + } + + initialTalk(); + + // Lines 906-? in talk.c + // XXX drawmouseflag=1; + int16 level=1, retval=0, type=1; + int16 head = _dialogueTree[level][0].head; + + while(retval != -1) { + debug(0, "retval = %i", retval); + + char otherVoiceFilePrefix [MAX_STRING_SIZE]; + char joeVoiceFilePrefix [5][MAX_STRING_SIZE]; + char talkString [5][MAX_STRING_SIZE]; + + talkString[0][0] = '\0'; + + if(talkSelected()->hasTalkedTo == 1 && head == 1) + strcpy(talkString[0], _person2String); + else + findDialogueString(_person1Ptr, head, talkString[0]); + + if(talkSelected()->hasTalkedTo == 1 && head == 1) + sprintf(otherVoiceFilePrefix, "%2dXXXXP", _talkKey); + else + sprintf(otherVoiceFilePrefix, "%2d%4xP", _talkKey, head); + + if (talkString[0][0] == '\0' && retval > 1) { + findDialogueString(_person1Ptr, retval, talkString[0]); + sprintf(otherVoiceFilePrefix,"%2d%4xP", _talkKey, retval); + } + + // Joe dialogue + + for (int i = 1; i <= 4; i++) { + findDialogueString(_joePtr, _dialogueTree[level][i].head, talkString[i]); + + int16 index = _dialogueTree[level][i].gameStateIndex; + + if (index < 0 && _logic->gameState(abs(index)) != _dialogueTree[level][i].gameStateValue) + talkString[i][0] = '\0'; + + sprintf(joeVoiceFilePrefix[i], "%2d%4xJ", _talkKey, _dialogueTree[level][i].head); + } + + // Check to see if(all the dialogue options have been selected. + // if this is the case, and the last one left is the exit option, + // then automatically set S to that and exit. + + int choicesLeft = 0; + int selectedSentence = 0; + + for (int i = 1; i <= 4; i++) { + if (talkString[i][0] != '\0') { + choicesLeft++; + selectedSentence = i; + } + } + + debug(0, "choicesLeft = %i", choicesLeft); + + if (1 == choicesLeft) { + // Automatically run the final dialogue option + if (speak(talkString[0], personName, otherVoiceFilePrefix)) + personWalking = true; + + if (_quit) + break; + + speak(talkString[selectedSentence], personName, joeVoiceFilePrefix[selectedSentence]); + + } + else { + // XXX if (bobNum > 0) + // XXX selectedSentence = bobTalk(personName); + for (int i = 1; i <= 4; i++) { + if (talkString[i][0] != '\0') { + selectedSentence = i; + break; + } + } + } + + if (_quit) + break; + + retval = _dialogueTree[level][selectedSentence].dialogueNodeValue1; + head = _dialogueTree[level][selectedSentence].head; + oldLevel = level; + level = 0; + + // Set LEVEL to the selected child in dialogue tree + + for (int i = 1; i <= _levelMax; i++) + if (_dialogueTree[i][0].head == head) + level = i; + + if (0 == level) { + // No new level has been selected, so lets set LEVEL to the + // tree path pointed to by the RETVAL + + for (int i = 1; i <= _levelMax; i++) + for (int j = 0; j <= 5; j++) + if (_dialogueTree[i][j].head == retval) + level = i; + + disableSentence(oldLevel, selectedSentence); + } + else { // 0 != level + // Check to see if Person Return value is positive, if it is, then + // change the selected dialogue option to the Return value + + if (_dialogueTree[level][0].dialogueNodeValue1 > 0) { + if (1 == oldLevel) { + _oldSelectedSentenceIndex = selectedSentence; + _oldSelectedSentenceValue = talkSelected()->values[selectedSentence-1]; + talkSelected()->values[selectedSentence-1] = _dialogueTree[level][0].dialogueNodeValue1; + } + + _dialogueTree[oldLevel][selectedSentence].head = _dialogueTree[level][0].dialogueNodeValue1; + _dialogueTree[level][0].dialogueNodeValue1 = -1; + } + else { + disableSentence(oldLevel, selectedSentence); + } + } + + // Check selected person to see if any Gamestates need setting + + int16 index = _dialogueTree[level][0].gameStateIndex; + + if (index > 0) + _logic->gameState(index, _dialogueTree[level][0].gameStateValue); + + // if the selected dialogue line has a POSITIVE game state value + // then set gamestate to Value = TALK(OLDLEVEL,S,3) + + index = _dialogueTree[oldLevel][selectedSentence].gameStateIndex; + if (index > 0) + _logic->gameState(index, _dialogueTree[oldLevel][selectedSentence].gameStateValue); + + + // if(RETVAL = -1, then before we exit, check to see if(person + // has something final to say! + + + // XXX debug + //break; + } +} + +void Talk::disableSentence(int oldLevel, int selectedSentence) { + // Mark off selected option + + if (1 == oldLevel) { + if (_dialogueTree[oldLevel][selectedSentence].dialogueNodeValue1 != -1) { + // Make sure choice is not exit option + _oldSelectedSentenceIndex = selectedSentence; + _oldSelectedSentenceValue = talkSelected()->values[selectedSentence-1]; + talkSelected()->values[selectedSentence-1] = -1; + } + } + + // Cancel selected dialogue line, so that its no longer displayed + + _dialogueTree[oldLevel][selectedSentence].head = -1; + _dialogueTree[oldLevel][selectedSentence].dialogueNodeValue1 = -1; +} + +void Talk::findDialogueString(byte *ptr, int16 id, char *str) { + str[0] = '\0'; + + for (int i = 1; i <= _pMax; i++) { + ptr += 2; + int16 currentId = (int16)READ_BE_UINT16(ptr); ptr += 2; + if (id == currentId) { + ptr = getString(ptr, str, MAX_STRING_LENGTH, 4); + debug(0, "Found string with ID %i: '%s'", id, str); + break; + } + else + ptr = getString(ptr, NULL, MAX_STRING_LENGTH, 4); + } + + if (str[0] == '\0') + warning("Failed to find string with ID %i", id); +} + +void Talk::load(const char *filename) { + byte *ptr = _fileData = _resource->loadFile(filename, 20); + if (!_fileData) { + error("Failed to load resource data file '%s'", filename); + } + + bool canQuit; + + // + // Load talk header + // + + _levelMax = (int16)READ_BE_UINT16(ptr); ptr += 2; + + debug(0, "levelMax = %i", _levelMax); + + if (_levelMax < 0) { + _levelMax = -_levelMax; + canQuit = false; + } + else + canQuit = true; + + _uniqueKey = (int16)READ_BE_UINT16(ptr); ptr += 2; + _talkKey = (int16)READ_BE_UINT16(ptr); ptr += 2; + int16 jMax = (int16)READ_BE_UINT16(ptr); ptr += 2; + _pMax = (int16)READ_BE_UINT16(ptr); ptr += 2; + int16 gameState1 = (int16)READ_BE_UINT16(ptr); ptr += 2; + int16 testValue1 = (int16)READ_BE_UINT16(ptr); ptr += 2; + int16 itemToInsert1 = (int16)READ_BE_UINT16(ptr); ptr += 2; + int16 gameState2 = (int16)READ_BE_UINT16(ptr); ptr += 2; + int16 testValue2 = (int16)READ_BE_UINT16(ptr); ptr += 2; + int16 itemToInsert2 = (int16)READ_BE_UINT16(ptr); ptr += 2; + + debug(0, "uniqueKey = %i", _uniqueKey); + debug(0, "talkKey = %i", _talkKey); + + _person1Ptr = _fileData + READ_BE_UINT16(ptr); ptr += 2; + byte *cutawayPtr = _fileData + READ_BE_UINT16(ptr); ptr += 2; + _person2Ptr = _fileData + READ_BE_UINT16(ptr); ptr += 2; + + if (ptr != (_fileData + 28)) + error("ptr != (_fileData + 28))"); + + byte *dataPtr = _fileData + 32; + _joePtr = dataPtr + _levelMax * 96; + + // + // Load dialogue tree + // + + ptr = dataPtr; + + for (int i = 1; i <= _levelMax; i++) + for (int j = 0; j <= 5; j++) { + ptr += 2; + _dialogueTree[i][j].head = (int16)READ_BE_UINT16(ptr); ptr += 2; + ptr += 2; + _dialogueTree[i][j].dialogueNodeValue1 = (int16)READ_BE_UINT16(ptr); ptr += 2; + ptr += 2; + _dialogueTree[i][j].gameStateIndex = (int16)READ_BE_UINT16(ptr); ptr += 2; + ptr += 2; + _dialogueTree[i][j].gameStateValue = (int16)READ_BE_UINT16(ptr); ptr += 2; + } +} + +void Talk::initialTalk() { + // Lines 848-903 in talk.c + + byte *ptr = _joePtr + 2; + + uint16 hasString = READ_BE_UINT16(ptr); ptr += 2; + + char joeString[MAX_STRING_SIZE]; + if (hasString) { + ptr = getString(ptr, joeString, MAX_STRING_LENGTH); + debug(0, "joeString = '%s'", joeString); + } + else + joeString[0] = '\0'; + + ptr = _person2Ptr; + ptr = getString(ptr, _person2String, MAX_STRING_LENGTH); + debug(0, "person2String = '%s'", _person2String); + + char joe2String[MAX_STRING_SIZE]; + ptr = getString(ptr, joe2String, MAX_STRING_LENGTH); + debug(0, "joe2String = '%s'", joe2String); + + if (talkSelected()->hasTalkedTo == 0) { + + // Not yet talked to this person + + if (joeString[0] != '0') { + char voiceFilePrefix[MAX_STRING_SIZE]; + sprintf(voiceFilePrefix, "%2dSSSSJ", _talkKey); + speak(joeString, "JOE", voiceFilePrefix); + } + } + else { + // Already spoken to them, choose second response + + if (joe2String[0] != '0') { + char voiceFilePrefix[MAX_STRING_SIZE]; + sprintf(voiceFilePrefix, "%2dSSSSJ", _talkKey); + speak(joe2String, "JOE", voiceFilePrefix); + } + + } + +} + +bool Talk::speak(const char *sentence, const char *person, const char *voiceFilePrefix) { + debug(0, "Sentence '%s' is said by person '%s' and voice files with prefix '%s' played", + sentence, person, voiceFilePrefix); + return false; // XXX +} + +byte *Talk::getString(byte *ptr, char *str, int maxLength, int align) { + int length = *ptr; + ptr++; + + if (length > maxLength) { + error("String too long. Length = %i, maxLength = %i, str = '%*s'", + length, maxLength, length, (const char*)ptr); + } + else if (length) { + if (str) + memcpy(str, (const char*)ptr, length); + ptr += length; + + while ((int)ptr % align) + ptr++; + } + + if (str) + str[length] = '\0'; + + return ptr; +} + +Talk::TalkSelected *Talk::talkSelected() { + return _talkSelected + _uniqueKey; +} + +} // End of namespace Queen diff --git a/queen/talk.h b/queen/talk.h new file mode 100644 index 0000000000..42fdeebf73 --- /dev/null +++ b/queen/talk.h @@ -0,0 +1,148 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#ifndef QUEEN_TALK_H +#define QUEEN_TALK_H + +#include "queen/queen.h" + +namespace Queen { + +class Resource; +class Logic; + +class Talk { + public: + + //! Public interface to run a talk from a file + static void run( + const char *filename, + char *cutawayFilename, + Logic *logic, + Resource *resource); + + //! Public interface to speak a sentence +#if 0 + static void run( + const char *sentence, + const char *person, + int noun, + Logic *logic, + Resource *resource); +#endif + + //! Read a string from ptr and return new ptr + static byte *getString(byte *ptr, char *str, int maxLength, int align = 2); + + private: + //! Collection of constants used by Talk + enum { + MAX_STRING_LENGTH = 255, + MAX_STRING_SIZE = (MAX_STRING_LENGTH + 1), + TALK_SELECTED_COUNT = 86 + }; + + //! TODO Move this to struct.h later! + struct TalkSelected { + int16 hasTalkedTo; + int16 values[4]; + }; + + struct DialogueNode { + int16 head; + int16 dialogueNodeValue1; + int16 gameStateIndex; + int16 gameStateValue; + }; + + Logic *_logic; + Resource *_resource; + + //! Raw .dog file data (without 20 byte header) + byte *_fileData; + + //! Number of dialogue levels + int16 _levelMax; + + //! Unique key for this dialogue + int16 _uniqueKey; + + //! Used to select voice files + int16 _talkKey; + + //! Used by findDialogueString + int16 _pMax; + + //! String data + byte *_person1Ptr; + + //! Data used if we have talked to the person before + byte *_person2Ptr; + + //! Data used if we haven't talked to the person before + byte *_joePtr; + + //! Set to true to quit talking + bool _quit; + + //! IDs for sentences + DialogueNode _dialogueTree[18][6]; + + //! TODO Move this to the Logic class later! + TalkSelected _talkSelected[TALK_SELECTED_COUNT]; + + //! Greeting from person Joe has talked to before + char _person2String[MAX_STRING_SIZE]; + + int _oldSelectedSentenceIndex; + int _oldSelectedSentenceValue; + + + Talk(Logic *logic, Resource *resource); + ~Talk(); + + //! Perform talk in file and return a cutaway filename + void talk(const char *filename, char *cutawayFilename); + + //! Load talk data from .dog file + void load(const char *filename); + + //! First things spoken + void initialTalk(); + + //! Find a string in the dialogue tree + void findDialogueString(byte *ptr, int16 id, char *str); + + //! Speak sentence + bool speak(const char *sentence, const char *person, const char *voiceFilePrefix); + + //! Get TalkSelected struct for this talk + TalkSelected *talkSelected(); + + //! The sentence will not be displayed again + void disableSentence(int oldLevel, int selectedSentence); + + +}; + +} // End of namespace Queen + +#endif |