mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-04 08:17:40 +00:00
NANCY: Implement even terser Conversation records
Implemented the ConversationSoundTerse and ConversationCelTerse action records, which are even shorter variants of the corresponding Conversation types. Made changes to the base ConversationSound to reduce code duplication.
This commit is contained in:
parent
a04bb51bf5
commit
5683e5f6e6
@ -174,7 +174,13 @@ ActionRecord *ActionManager::createActionRecord(uint16 type, Common::SeekableRea
|
||||
return new Autotext();
|
||||
}
|
||||
case 62:
|
||||
return new MapCallHotMultiframe();
|
||||
if (g_nancy->getGameType() <= kGameTypeNancy7) {
|
||||
return new MapCallHotMultiframe(); // TVD/nancy1 only
|
||||
} else {
|
||||
return new ConversationCelTerse(); // nancy8 and up
|
||||
}
|
||||
case 63:
|
||||
return new ConversationSoundTerse();
|
||||
case 65:
|
||||
return new TableIndexOverlay();
|
||||
case 66:
|
||||
|
@ -39,8 +39,13 @@ namespace Nancy {
|
||||
namespace Action {
|
||||
|
||||
ConversationSound::ConversationSound() :
|
||||
RenderActionRecord(8),
|
||||
_noResponse(g_nancy->getGameType() <= kGameTypeNancy2 ? 10 : 20) {}
|
||||
RenderActionRecord(8),
|
||||
_noResponse(g_nancy->getGameType() <= kGameTypeNancy2 ? 10 : 20),
|
||||
_hasDrawnTextbox(false),
|
||||
_pickedResponse(-1) {
|
||||
_conditionalResponseCharacterID = _noResponse;
|
||||
_goodbyeResponseCharacterID = _noResponse;
|
||||
}
|
||||
|
||||
ConversationSound::~ConversationSound() {
|
||||
if (NancySceneState.getActiveConversation() == this) {
|
||||
@ -113,6 +118,83 @@ void ConversationSound::readData(Common::SeekableReadStream &stream) {
|
||||
}
|
||||
}
|
||||
|
||||
void ConversationSound::readTerseData(Common::SeekableReadStream &stream) {
|
||||
readFilename(stream, _sound.name);
|
||||
_sound.volume = stream.readUint16LE();
|
||||
_sound.channelID = 12; // hardcoded
|
||||
_sound.numLoops = 1;
|
||||
|
||||
_responseGenericSound.volume = _sound.volume;
|
||||
_responseGenericSound.numLoops = 1;
|
||||
_responseGenericSound.channelID = 13; // hardcoded
|
||||
|
||||
readTerseCaptionText(stream);
|
||||
|
||||
_conditionalResponseCharacterID = stream.readByte();
|
||||
_goodbyeResponseCharacterID = stream.readByte();
|
||||
|
||||
_defaultNextScene = stream.readByte();
|
||||
|
||||
_sceneChange.sceneID = stream.readUint16LE();
|
||||
_sceneChange.continueSceneSound = kContinueSceneSound;
|
||||
|
||||
uint16 numResponses = stream.readUint16LE();
|
||||
_responses.resize(numResponses);
|
||||
for (uint i = 0; i < numResponses; ++i) {
|
||||
ResponseStruct &response = _responses[i];
|
||||
response.conditionFlags.read(stream);
|
||||
readTerseResponseText(stream, response);
|
||||
readFilename(stream, response.soundName);
|
||||
response.sceneChange.sceneID = stream.readUint16LE();
|
||||
response.sceneChange.continueSceneSound = kContinueSceneSound;
|
||||
}
|
||||
|
||||
// No scene branches
|
||||
uint16 numFlagsStructs = stream.readUint16LE();
|
||||
_flagsStructs.resize(numFlagsStructs);
|
||||
for (uint i = 0; i < numFlagsStructs; ++i) {
|
||||
FlagsStruct &flagsStruct = _flagsStructs[i];
|
||||
flagsStruct.conditions.read(stream);
|
||||
flagsStruct.flagToSet.type = stream.readByte();
|
||||
flagsStruct.flagToSet.flag.label = stream.readSint16LE();
|
||||
flagsStruct.flagToSet.flag.flag = stream.readByte();
|
||||
}
|
||||
}
|
||||
|
||||
void ConversationSound::readCaptionText(Common::SeekableReadStream &stream) {
|
||||
char *rawText = new char[1500];
|
||||
stream.read(rawText, 1500);
|
||||
assembleTextLine(rawText, _text, 1500);
|
||||
delete[] rawText;
|
||||
}
|
||||
|
||||
void ConversationSound::readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) {
|
||||
char *rawText = new char[400];
|
||||
stream.read(rawText, 400);
|
||||
assembleTextLine(rawText, response.text, 400);
|
||||
delete[] rawText;
|
||||
}
|
||||
|
||||
void ConversationSound::readTerseCaptionText(Common::SeekableReadStream &stream) {
|
||||
Common::String key;
|
||||
readFilename(stream, key);
|
||||
|
||||
const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
|
||||
assert(convo);
|
||||
|
||||
_text = convo->texts[key];
|
||||
}
|
||||
|
||||
void ConversationSound::readTerseResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) {
|
||||
Common::String key;
|
||||
readFilename(stream, key);
|
||||
|
||||
const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
|
||||
assert(convo);
|
||||
|
||||
response.text = convo->texts[key];
|
||||
}
|
||||
|
||||
void ConversationSound::execute() {
|
||||
ConversationSound *activeConversation = NancySceneState.getActiveConversation();
|
||||
if (activeConversation != this && activeConversation != nullptr) {
|
||||
@ -289,20 +371,6 @@ void ConversationSound::execute() {
|
||||
}
|
||||
}
|
||||
|
||||
void ConversationSound::readCaptionText(Common::SeekableReadStream &stream) {
|
||||
char *rawText = new char[1500];
|
||||
stream.read(rawText, 1500);
|
||||
assembleTextLine(rawText, _text, 1500);
|
||||
delete[] rawText;
|
||||
}
|
||||
|
||||
void ConversationSound::readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) {
|
||||
char *rawText = new char[400];
|
||||
stream.read(rawText, 400);
|
||||
assembleTextLine(rawText, response.text, 400);
|
||||
delete[] rawText;
|
||||
}
|
||||
|
||||
void ConversationSound::addConditionalDialogue() {
|
||||
for (const auto &res : g_nancy->getStaticData().conditionalDialogue[_conditionalResponseCharacterID]) {
|
||||
bool isSatisfied = true;
|
||||
@ -678,33 +746,7 @@ void ConversationCel::readData(Common::SeekableReadStream &stream) {
|
||||
readFilename(stream, xsheetName);
|
||||
|
||||
readFilenameArray(stream, _treeNames, 4);
|
||||
|
||||
Common::SeekableReadStream *xsheet = SearchMan.createReadStreamForMember(Common::Path(xsheetName));
|
||||
|
||||
// Read the xsheet and load all images into the arrays
|
||||
// Completely unoptimized, the original engine uses a buffer
|
||||
xsheet->seek(0);
|
||||
Common::String signature = xsheet->readString('\0', 18);
|
||||
if (signature != "XSHEET WayneSikes") {
|
||||
warning("XSHEET signature doesn't match!");
|
||||
return;
|
||||
}
|
||||
|
||||
xsheet->seek(0x22);
|
||||
uint numFrames = xsheet->readUint16LE();
|
||||
xsheet->skip(2);
|
||||
_frameTime = xsheet->readUint16LE();
|
||||
xsheet->skip(2);
|
||||
|
||||
_celNames.resize(4, Common::Array<Common::Path>(numFrames));
|
||||
for (uint i = 0; i < numFrames; ++i) {
|
||||
for (uint j = 0; j < _celNames.size(); ++j) {
|
||||
readFilename(*xsheet, _celNames[j][i]);
|
||||
}
|
||||
|
||||
// 4 unknown values
|
||||
xsheet->skip(8);
|
||||
}
|
||||
readXSheet(stream, xsheetName);
|
||||
|
||||
// Continue reading the AR stream
|
||||
|
||||
@ -732,6 +774,35 @@ void ConversationCel::readData(Common::SeekableReadStream &stream) {
|
||||
ConversationSound::readData(stream);
|
||||
}
|
||||
|
||||
void ConversationCel::readXSheet(Common::SeekableReadStream &stream, const Common::String &xsheetName) {
|
||||
Common::SeekableReadStream *xsheet = SearchMan.createReadStreamForMember(Common::Path(xsheetName));
|
||||
|
||||
// Read the xsheet and load all images into the arrays
|
||||
// Completely unoptimized, the original engine uses a buffer
|
||||
xsheet->seek(0);
|
||||
Common::String signature = xsheet->readString('\0', 18);
|
||||
if (signature != "XSHEET WayneSikes") {
|
||||
warning("XSHEET signature doesn't match!");
|
||||
return;
|
||||
}
|
||||
|
||||
xsheet->seek(0x22);
|
||||
uint numFrames = xsheet->readUint16LE();
|
||||
xsheet->skip(2);
|
||||
_frameTime = xsheet->readUint16LE();
|
||||
xsheet->skip(2);
|
||||
|
||||
_celNames.resize(4, Common::Array<Common::Path>(numFrames));
|
||||
for (uint i = 0; i < numFrames; ++i) {
|
||||
for (uint j = 0; j < _celNames.size(); ++j) {
|
||||
readFilename(*xsheet, _celNames[j][i]);
|
||||
}
|
||||
|
||||
// 4 unknown values
|
||||
xsheet->skip(8);
|
||||
}
|
||||
}
|
||||
|
||||
bool ConversationCel::isVideoDonePlaying() {
|
||||
return _curFrame >= MIN<uint>(_lastFrame, _celNames[0].size()) && _nextFrameTime <= g_nancy->getTotalPlayTime();
|
||||
}
|
||||
@ -747,44 +818,22 @@ ConversationCel::Cel &ConversationCel::loadCel(const Common::Path &name, const C
|
||||
return _celCache[name];
|
||||
}
|
||||
|
||||
void ConversationSoundT::readCaptionText(Common::SeekableReadStream &stream) {
|
||||
Common::String key;
|
||||
readFilename(stream, key);
|
||||
|
||||
const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
|
||||
assert(convo);
|
||||
|
||||
_text = convo->texts[key];
|
||||
void ConversationSoundTerse::readData(Common::SeekableReadStream &stream) {
|
||||
readTerseData(stream);
|
||||
}
|
||||
|
||||
void ConversationSoundT::readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) {
|
||||
Common::String key;
|
||||
readFilename(stream, key);
|
||||
void ConversationCelTerse::readData(Common::SeekableReadStream &stream) {
|
||||
Common::String xsheetName;
|
||||
readFilename(stream, xsheetName);
|
||||
|
||||
const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
|
||||
assert(convo);
|
||||
readFilenameArray(stream, _treeNames, 2); // Only 2
|
||||
readXSheet(stream, xsheetName);
|
||||
|
||||
response.text = convo->texts[key];
|
||||
}
|
||||
_lastFrame = stream.readUint16LE();
|
||||
_drawingOrder = { 1, 0, 2, 3 };
|
||||
_overrideTreeRects.resize(4, kCelOverrideTreeRectsOff);
|
||||
|
||||
void ConversationCelT::readCaptionText(Common::SeekableReadStream &stream) {
|
||||
Common::String key;
|
||||
readFilename(stream, key);
|
||||
|
||||
const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
|
||||
assert(convo);
|
||||
|
||||
_text = convo->texts[key];
|
||||
}
|
||||
|
||||
void ConversationCelT::readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) {
|
||||
Common::String key;
|
||||
readFilename(stream, key);
|
||||
|
||||
const CVTX *convo = (const CVTX *)g_nancy->getEngineData("CONVO");
|
||||
assert(convo);
|
||||
|
||||
response.text = convo->texts[key];
|
||||
readTerseData(stream);
|
||||
}
|
||||
|
||||
} // End of namespace Action
|
||||
|
@ -100,6 +100,11 @@ protected:
|
||||
// Functions for reading captions are virtual to allow easier support for the terse Conversation variants
|
||||
virtual void readCaptionText(Common::SeekableReadStream &stream);
|
||||
virtual void readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response);
|
||||
|
||||
// Used in subclasses
|
||||
void readTerseData(Common::SeekableReadStream &stream);
|
||||
void readTerseCaptionText(Common::SeekableReadStream &stream);
|
||||
void readTerseResponseText(Common::SeekableReadStream &stream, ResponseStruct &response);
|
||||
|
||||
// Functions for handling the built-in dialogue responses found in the executable
|
||||
void addConditionalDialogue();
|
||||
@ -110,8 +115,8 @@ protected:
|
||||
SoundDescription _sound;
|
||||
SoundDescription _responseGenericSound;
|
||||
|
||||
byte _conditionalResponseCharacterID = 0;
|
||||
byte _goodbyeResponseCharacterID = 0;
|
||||
byte _conditionalResponseCharacterID;
|
||||
byte _goodbyeResponseCharacterID;
|
||||
byte _defaultNextScene = kDefaultNextSceneEnabled;
|
||||
byte _popNextScene = kNoPopNextScene;
|
||||
SceneChangeDescription _sceneChange;
|
||||
@ -120,8 +125,8 @@ protected:
|
||||
Common::Array<FlagsStruct> _flagsStructs;
|
||||
Common::Array<SceneBranchStruct> _sceneBranchStructs;
|
||||
|
||||
bool _hasDrawnTextbox = false;
|
||||
int16 _pickedResponse = -1;
|
||||
bool _hasDrawnTextbox;
|
||||
int16 _pickedResponse;
|
||||
|
||||
const byte _noResponse;
|
||||
};
|
||||
@ -186,6 +191,8 @@ protected:
|
||||
bool isVideoDonePlaying() override;
|
||||
Cel &loadCel(const Common::Path &name, const Common::String &treeName);
|
||||
|
||||
void readXSheet(Common::SeekableReadStream &stream, const Common::String &xsheetName);
|
||||
|
||||
Common::Array<Common::Array<Common::Path>> _celNames;
|
||||
Common::Array<Common::String> _treeNames;
|
||||
|
||||
@ -209,20 +216,40 @@ protected:
|
||||
Common::SharedPtr<ConversationCelLoader> _loaderPtr;
|
||||
};
|
||||
|
||||
// A ConversationSound without embedded text; uses the CONVO chunk instead
|
||||
class ConversationSoundT : public ConversationSound {
|
||||
protected:
|
||||
Common::String getRecordTypeName() const override { return "ConversationSoundT"; }
|
||||
|
||||
void readCaptionText(Common::SeekableReadStream &stream) override;
|
||||
void readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) override;
|
||||
void readCaptionText(Common::SeekableReadStream &stream) override { readTerseCaptionText(stream); }
|
||||
void readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) override { readTerseResponseText(stream, response); }
|
||||
};
|
||||
|
||||
// A ConversationCel without embedded text; uses the CONVO chunk instead
|
||||
class ConversationCelT : public ConversationCel {
|
||||
protected:
|
||||
Common::String getRecordTypeName() const override { return "ConversationCelT"; }
|
||||
|
||||
void readCaptionText(Common::SeekableReadStream &stream) override;
|
||||
void readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) override;
|
||||
void readCaptionText(Common::SeekableReadStream &stream) override { readTerseCaptionText(stream); }
|
||||
void readResponseText(Common::SeekableReadStream &stream, ResponseStruct &response) override { readTerseResponseText(stream, response); }
|
||||
};
|
||||
|
||||
// A ConversationSound with a much smaller data footprint
|
||||
class ConversationSoundTerse : public ConversationSound {
|
||||
public:
|
||||
void readData(Common::SeekableReadStream &stream) override;
|
||||
|
||||
protected:
|
||||
Common::String getRecordTypeName() const override { return "ConversationSoundTerse"; }
|
||||
};
|
||||
|
||||
// A ConversationCel with a much smaller data footprint
|
||||
class ConversationCelTerse : public ConversationCel {
|
||||
public:
|
||||
void readData(Common::SeekableReadStream &stream) override;
|
||||
|
||||
protected:
|
||||
Common::String getRecordTypeName() const override { return "ConversationCelTerse"; }
|
||||
};
|
||||
|
||||
} // End of namespace Action
|
||||
|
Loading…
Reference in New Issue
Block a user