Initial commit
This commit is contained in:
303
engines/ultima/ultima4/conversation/conversation.cpp
Normal file
303
engines/ultima/ultima4/conversation/conversation.cpp
Normal file
@@ -0,0 +1,303 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/conversation/conversation.h"
|
||||
#include "ultima/ultima4/game/person.h"
|
||||
#include "ultima/ultima4/game/script.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
const uint Conversation::BUFFERLEN = 16;
|
||||
ResponseParts *g_responseParts;
|
||||
|
||||
ResponseParts::ResponseParts() :
|
||||
NONE("<NONE>", "", true),
|
||||
ASK("<ASK>", "", true),
|
||||
END("<END>", "", true),
|
||||
ATTACK("<ATTACK>", "", true),
|
||||
BRAGGED("<BRAGGED>", "", true),
|
||||
HUMBLE("<HUMBLE>", "", true),
|
||||
ADVANCELEVELS("<ADVANCELEVELS>", "", true),
|
||||
HEALCONFIRM("<HEALCONFIRM>", "", true),
|
||||
STARTMUSIC_LB("<STARTMUSIC_LB>", "", true),
|
||||
STARTMUSIC_HW("<STARTMUSIC_HW>", "", true),
|
||||
STOPMUSIC("<STOPMUSIC>", "", true),
|
||||
HAWKWIND("<HAWKWIND>", "", true) {
|
||||
g_responseParts = this;
|
||||
}
|
||||
|
||||
ResponseParts::~ResponseParts() {
|
||||
g_responseParts = nullptr;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------------------*/
|
||||
|
||||
Response::Response(const Common::String &response) : _references(0) {
|
||||
add(response);
|
||||
}
|
||||
|
||||
void Response::add(const ResponsePart &part) {
|
||||
_parts.push_back(part);
|
||||
}
|
||||
|
||||
const Std::vector<ResponsePart> &Response::getParts() const {
|
||||
return _parts;
|
||||
}
|
||||
|
||||
Response::operator Common::String() const {
|
||||
Common::String result;
|
||||
for (const auto &i : _parts) {
|
||||
result += i;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Response *Response::addref() {
|
||||
_references++;
|
||||
return this;
|
||||
}
|
||||
|
||||
void Response::release() {
|
||||
_references--;
|
||||
if (_references <= 0)
|
||||
delete this;
|
||||
}
|
||||
|
||||
ResponsePart::ResponsePart(const Common::String &value, const Common::String &arg, bool command) {
|
||||
_value = value;
|
||||
_arg = arg;
|
||||
_command = command;
|
||||
}
|
||||
|
||||
ResponsePart::operator Common::String() const {
|
||||
return _value;
|
||||
}
|
||||
|
||||
bool ResponsePart::operator==(const ResponsePart &rhs) const {
|
||||
return _value == rhs._value;
|
||||
}
|
||||
|
||||
bool ResponsePart::isCommand() const {
|
||||
return _command;
|
||||
}
|
||||
|
||||
DynamicResponse::DynamicResponse(Response * (*generator)(const DynamicResponse *), const Common::String ¶m) :
|
||||
Response(""), _param(param) {
|
||||
_generator = generator;
|
||||
_currentResponse = nullptr;
|
||||
}
|
||||
|
||||
DynamicResponse::~DynamicResponse() {
|
||||
if (_currentResponse)
|
||||
delete _currentResponse;
|
||||
}
|
||||
|
||||
const Std::vector<ResponsePart> &DynamicResponse::getParts() const {
|
||||
// blah, must cast away constness
|
||||
const_cast<DynamicResponse *>(this)->_currentResponse = (*_generator)(this);
|
||||
return _currentResponse->getParts();
|
||||
}
|
||||
|
||||
/*
|
||||
* Dialogue::Question class
|
||||
*/
|
||||
Dialogue::Question::Question(const Common::String &txt, Response *yes, Response *no) :
|
||||
_text(txt), _yesResp(yes->addref()), _noResp(no->addref()) {}
|
||||
|
||||
Common::String Dialogue::Question::getText() {
|
||||
return _text;
|
||||
}
|
||||
|
||||
Response *Dialogue::Question::getResponse(bool yes) {
|
||||
if (yes)
|
||||
return _yesResp;
|
||||
return _noResp;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Dialogue::Keyword class
|
||||
*/
|
||||
Dialogue::Keyword::Keyword(const Common::String &kw, Response *resp) :
|
||||
_keyword(kw), _response(resp->addref()) {
|
||||
trim(_keyword);
|
||||
lowercase(_keyword);
|
||||
}
|
||||
|
||||
Dialogue::Keyword::Keyword(const Common::String &kw, const Common::String &resp) :
|
||||
_keyword(kw), _response((new Response(resp))->addref()) {
|
||||
trim(_keyword);
|
||||
lowercase(_keyword);
|
||||
}
|
||||
|
||||
Dialogue::Keyword::~Keyword() {
|
||||
_response->release();
|
||||
}
|
||||
|
||||
bool Dialogue::Keyword::operator==(const Common::String &kw) const {
|
||||
// minimum 4-character "guessing"
|
||||
int testLen = (_keyword.size() < 4) ? _keyword.size() : 4;
|
||||
|
||||
// exception: empty keyword only matches empty Common::String (alias for 'bye')
|
||||
if (testLen == 0 && kw.size() > 0)
|
||||
return false;
|
||||
|
||||
if (scumm_strnicmp(kw.c_str(), _keyword.c_str(), testLen) == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dialogue class
|
||||
*/
|
||||
|
||||
Dialogue::Dialogue()
|
||||
: _intro(nullptr)
|
||||
, _longIntro(nullptr)
|
||||
, _defaultAnswer(nullptr)
|
||||
, _question(nullptr) {
|
||||
}
|
||||
|
||||
Dialogue::~Dialogue() {
|
||||
for (auto &i : _keywords) {
|
||||
delete i._value;
|
||||
}
|
||||
}
|
||||
|
||||
void Dialogue::addKeyword(const Common::String &kw, Response *response) {
|
||||
if (_keywords.find(kw) != _keywords.end())
|
||||
delete _keywords[kw];
|
||||
|
||||
_keywords[kw] = new Keyword(kw, response);
|
||||
}
|
||||
|
||||
Dialogue::Keyword *Dialogue::operator[](const Common::String &kw) {
|
||||
KeywordMap::iterator i = _keywords.find(kw);
|
||||
|
||||
// If they entered the keyword verbatim, return it!
|
||||
if (i != _keywords.end())
|
||||
return i->_value;
|
||||
// Otherwise, go find one that fits the description.
|
||||
else {
|
||||
for (i = _keywords.begin(); i != _keywords.end(); i++) {
|
||||
if ((*i->_value) == kw)
|
||||
return i->_value;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const ResponsePart &Dialogue::getAction() const {
|
||||
int prob = xu4_random(0x100);
|
||||
|
||||
/* Does the person turn away from/attack you? */
|
||||
if (prob >= _turnAwayProb)
|
||||
return g_responseParts->NONE;
|
||||
else {
|
||||
if (_attackProb - prob < 0x40)
|
||||
return g_responseParts->END;
|
||||
else
|
||||
return g_responseParts->ATTACK;
|
||||
}
|
||||
}
|
||||
|
||||
Common::String Dialogue::dump(const Common::String &arg) {
|
||||
Common::String result;
|
||||
if (arg == "") {
|
||||
result = "keywords:\n";
|
||||
for (const auto &i : _keywords) {
|
||||
result += i._key + "\n";
|
||||
}
|
||||
} else {
|
||||
if (_keywords.find(arg) != _keywords.end())
|
||||
result = static_cast<Common::String>(*_keywords[arg]->getResponse());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Conversation class
|
||||
*/
|
||||
|
||||
Conversation::Conversation() : _state(INTRO), _script(new Script()),
|
||||
_question(nullptr), _quant(0), _player(0), _price(0) {
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::incrementConversationCount();
|
||||
#endif
|
||||
}
|
||||
|
||||
Conversation::~Conversation() {
|
||||
#ifdef IOS_ULTIMA4
|
||||
U4IOS::decrementConversationCount();
|
||||
#endif
|
||||
delete _script;
|
||||
}
|
||||
|
||||
Conversation::InputType Conversation::getInputRequired(int *bufferlen) {
|
||||
switch (_state) {
|
||||
case BUY_QUANTITY:
|
||||
case SELL_QUANTITY: {
|
||||
*bufferlen = 2;
|
||||
return INPUT_STRING;
|
||||
}
|
||||
|
||||
case TALK:
|
||||
case BUY_PRICE:
|
||||
case TOPIC: {
|
||||
*bufferlen = BUFFERLEN;
|
||||
return INPUT_STRING;
|
||||
}
|
||||
|
||||
case GIVEBEGGAR: {
|
||||
*bufferlen = 2;
|
||||
return INPUT_STRING;
|
||||
}
|
||||
|
||||
case ASK:
|
||||
case ASKYESNO: {
|
||||
*bufferlen = 3;
|
||||
return INPUT_STRING;
|
||||
}
|
||||
|
||||
case VENDORQUESTION:
|
||||
case BUY_ITEM:
|
||||
case SELL_ITEM:
|
||||
case CONFIRMATION:
|
||||
case CONTINUEQUESTION:
|
||||
case PLAYER:
|
||||
return INPUT_CHARACTER;
|
||||
|
||||
case ATTACK:
|
||||
case DONE:
|
||||
case INTRO:
|
||||
case FULLHEAL:
|
||||
case ADVANCELEVELS:
|
||||
return INPUT_NONE;
|
||||
}
|
||||
|
||||
error("invalid state: %d", _state);
|
||||
return INPUT_NONE;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
321
engines/ultima/ultima4/conversation/conversation.h
Normal file
321
engines/ultima/ultima4/conversation/conversation.h
Normal file
@@ -0,0 +1,321 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_CONVERSATION_CONVERSATION_H
|
||||
#define ULTIMA4_CONVERSATION_CONVERSATION_H
|
||||
|
||||
#include "ultima/ultima4/core/utils.h"
|
||||
#include "ultima/shared/std/containers.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class Debug;
|
||||
class Person;
|
||||
class Script;
|
||||
|
||||
/**
|
||||
* A response part can be text or a "command" that triggers an
|
||||
* action.
|
||||
*/
|
||||
class ResponsePart {
|
||||
public:
|
||||
ResponsePart(const Common::String &value, const Common::String &arg = "", bool command = false);
|
||||
|
||||
operator Common::String() const;
|
||||
bool operator==(const ResponsePart &rhs) const;
|
||||
bool isCommand() const;
|
||||
|
||||
private:
|
||||
Common::String _value, _arg;
|
||||
bool _command;
|
||||
};
|
||||
|
||||
/**
|
||||
* A list of valid command response parts
|
||||
*/
|
||||
class ResponseParts {
|
||||
public:
|
||||
const ResponsePart NONE;
|
||||
const ResponsePart ASK;
|
||||
const ResponsePart END;
|
||||
const ResponsePart ATTACK;
|
||||
const ResponsePart BRAGGED;
|
||||
const ResponsePart HUMBLE;
|
||||
const ResponsePart ADVANCELEVELS;
|
||||
const ResponsePart HEALCONFIRM;
|
||||
const ResponsePart STARTMUSIC_LB;
|
||||
const ResponsePart STARTMUSIC_HW;
|
||||
const ResponsePart STOPMUSIC;
|
||||
const ResponsePart HAWKWIND;
|
||||
|
||||
ResponseParts();
|
||||
~ResponseParts();
|
||||
};
|
||||
|
||||
extern ResponseParts *g_responseParts;
|
||||
|
||||
/**
|
||||
* A static response. Each response can be made up of any number of
|
||||
* ResponseParts, which are either text fragments or commands.
|
||||
*/
|
||||
class Response {
|
||||
public:
|
||||
Response(const Common::String &response);
|
||||
virtual ~Response() {}
|
||||
|
||||
void add(const ResponsePart &part);
|
||||
|
||||
virtual const Std::vector<ResponsePart> &getParts() const;
|
||||
|
||||
operator Common::String() const;
|
||||
|
||||
Response *addref();
|
||||
void release();
|
||||
|
||||
private:
|
||||
int _references;
|
||||
Std::vector<ResponsePart> _parts;
|
||||
};
|
||||
|
||||
/**
|
||||
* A dynamically generated response. This class allows the response
|
||||
* to be generated dynamically at the time of the conversation instead
|
||||
* of when the conversation data is loaded.
|
||||
*/
|
||||
class DynamicResponse : public Response {
|
||||
public:
|
||||
DynamicResponse(Response * (*generator)(const DynamicResponse *), const Common::String ¶m = "");
|
||||
virtual ~DynamicResponse();
|
||||
|
||||
const Std::vector<ResponsePart> &getParts() const override;
|
||||
|
||||
const Common::String &getParam() const {
|
||||
return _param;
|
||||
}
|
||||
|
||||
private:
|
||||
Response *(*_generator)(const DynamicResponse *);
|
||||
Response *_currentResponse;
|
||||
Common::String _param;
|
||||
};
|
||||
|
||||
/**
|
||||
* The dialogue class, which holds conversation information for
|
||||
* townspeople and others who may talk to you. It includes information
|
||||
* like pronouns, keywords, actual conversation text (of course),
|
||||
* questions, and what happens when you answer these questions.
|
||||
*/
|
||||
class Dialogue {
|
||||
public:
|
||||
/**
|
||||
* A question-response to a keyword.
|
||||
*/
|
||||
class Question {
|
||||
public:
|
||||
Question(const Common::String &txt, Response *yes, Response *no);
|
||||
|
||||
Common::String getText();
|
||||
Response *getResponse(bool yes);
|
||||
|
||||
private:
|
||||
Common::String _text;
|
||||
Response *_yesResp, *_noResp;
|
||||
};
|
||||
|
||||
/**
|
||||
* A dialogue keyword.
|
||||
* It contains all the keywords that the talker will respond to, as
|
||||
* well as the responses to those keywords.
|
||||
*/
|
||||
class Keyword {
|
||||
public:
|
||||
Keyword(const Common::String &kw, Response *resp);
|
||||
Keyword(const Common::String &kw, const Common::String &resp);
|
||||
~Keyword();
|
||||
|
||||
bool operator==(const Common::String &kw) const;
|
||||
|
||||
/*
|
||||
* Accessor methods
|
||||
*/
|
||||
const Common::String &getKeyword() {
|
||||
return _keyword;
|
||||
}
|
||||
Response *getResponse() {
|
||||
return _response;
|
||||
}
|
||||
|
||||
private:
|
||||
Common::String _keyword;
|
||||
Response *_response;
|
||||
};
|
||||
|
||||
/**
|
||||
* A mapping of keywords to the Keyword object that represents them
|
||||
*/
|
||||
typedef Common::HashMap<Common::String, Keyword *> KeywordMap;
|
||||
|
||||
/*
|
||||
* Constructors/Destructors
|
||||
*/
|
||||
Dialogue();
|
||||
virtual ~Dialogue();
|
||||
|
||||
/*
|
||||
* Accessor methods
|
||||
*/
|
||||
const Common::String &getName() const {
|
||||
return _name;
|
||||
}
|
||||
const Common::String &getPronoun() const {
|
||||
return _pronoun;
|
||||
}
|
||||
const Common::String &getPrompt() const {
|
||||
return _prompt;
|
||||
}
|
||||
Response *getIntro(bool familiar = false) {
|
||||
return _intro;
|
||||
}
|
||||
Response *getLongIntro(bool familiar = false) {
|
||||
return _longIntro;
|
||||
}
|
||||
Response *getDefaultAnswer() {
|
||||
return _defaultAnswer;
|
||||
}
|
||||
Dialogue::Question *getQuestion() {
|
||||
return _question;
|
||||
}
|
||||
|
||||
/*
|
||||
* Getters
|
||||
*/
|
||||
void setName(const Common::String &n) {
|
||||
_name = n;
|
||||
}
|
||||
void setPronoun(const Common::String &pn) {
|
||||
_pronoun = pn;
|
||||
}
|
||||
void setPrompt(const Common::String &prompt) {
|
||||
this->_prompt = prompt;
|
||||
}
|
||||
void setIntro(Response *i) {
|
||||
_intro = i;
|
||||
}
|
||||
void setLongIntro(Response *i) {
|
||||
_longIntro = i;
|
||||
}
|
||||
void setDefaultAnswer(Response *a) {
|
||||
_defaultAnswer = a;
|
||||
}
|
||||
void setTurnAwayProb(int prob) {
|
||||
_turnAwayProb = prob;
|
||||
}
|
||||
void setQuestion(Question *q) {
|
||||
_question = q;
|
||||
}
|
||||
void addKeyword(const Common::String &kw, Response *response);
|
||||
|
||||
const ResponsePart &getAction() const;
|
||||
Common::String dump(const Common::String &arg);
|
||||
|
||||
/*
|
||||
* Operators
|
||||
*/
|
||||
Keyword *operator[](const Common::String &kw);
|
||||
|
||||
private:
|
||||
Common::String _name;
|
||||
Common::String _pronoun;
|
||||
Common::String _prompt;
|
||||
Response *_intro;
|
||||
Response *_longIntro;
|
||||
Response *_defaultAnswer;
|
||||
KeywordMap _keywords;
|
||||
union {
|
||||
int _turnAwayProb;
|
||||
int _attackProb;
|
||||
};
|
||||
Question *_question;
|
||||
};
|
||||
|
||||
/**
|
||||
* The conversation class, which handles the flow of text from the
|
||||
* player to the talker and vice-versa. It is responsible for beginning
|
||||
* and termination conversations and handing state changes during.
|
||||
*/
|
||||
class Conversation {
|
||||
public:
|
||||
/** Different states the conversation may be in */
|
||||
enum State {
|
||||
INTRO, /**< The initial state of the conversation, before anything is said */
|
||||
TALK, /**< The "default" state of the conversation */
|
||||
ASK, /**< The talker is asking the player a question */
|
||||
ASKYESNO, /**< The talker is asking the player a yes/no question */
|
||||
VENDORQUESTION, /**< A vendor is asking the player a question */
|
||||
BUY_ITEM, /**< Asked which item to buy */
|
||||
SELL_ITEM, /**< Asked which item to sell */
|
||||
BUY_QUANTITY, /**< Asked how many items to buy */
|
||||
SELL_QUANTITY, /**< Asked how many items to sell */
|
||||
BUY_PRICE, /**< Asked how much money to give someone */
|
||||
CONFIRMATION, /**< Asked by a vendor to confirm something */
|
||||
CONTINUEQUESTION, /**< Asked whether or not to continue */
|
||||
TOPIC, /**< Asked a topic to speak about */
|
||||
PLAYER, /**< Input for which player is required */
|
||||
FULLHEAL, /**< Heal the entire party before continuing conversation */
|
||||
ADVANCELEVELS, /**< Check and advance the party's levels before continuing */
|
||||
GIVEBEGGAR, /**< Asked how much to give a beggar */
|
||||
ATTACK, /**< The conversation ends with the talker attacking you */
|
||||
DONE /**< The conversation is over */
|
||||
};
|
||||
|
||||
/** Different types of conversation input required */
|
||||
enum InputType {
|
||||
INPUT_STRING,
|
||||
INPUT_CHARACTER,
|
||||
INPUT_NONE
|
||||
};
|
||||
|
||||
/* Constructor/Destructors */
|
||||
Conversation();
|
||||
~Conversation();
|
||||
|
||||
/* Member functions */
|
||||
InputType getInputRequired(int *bufferLen);
|
||||
|
||||
/* Static variables */
|
||||
static const uint BUFFERLEN; /**< The default maxixum length of input */
|
||||
|
||||
public:
|
||||
State _state; /**< The state of the conversation */
|
||||
Common::String _playerInput; /**< A Common::String holding the text the player inputs */
|
||||
Common::List<Common::String> _reply; /**< What the talker says */
|
||||
class Script *_script; /**< A script that this person follows during the conversation (may be nullptr) */
|
||||
Dialogue::Question *_question; /**< The current question the player is being asked */
|
||||
int _quant; /**< For vendor transactions */
|
||||
int _player; /**< For vendor transactions */
|
||||
int _price; /**< For vendor transactions */
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
51
engines/ultima/ultima4/conversation/dialogueloader.cpp
Normal file
51
engines/ultima/ultima4/conversation/dialogueloader.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/conversation/conversation.h"
|
||||
#include "ultima/ultima4/conversation/dialogueloader.h"
|
||||
#include "ultima/ultima4/conversation/dialogueloader_hw.h"
|
||||
#include "ultima/ultima4/conversation/dialogueloader_lb.h"
|
||||
#include "ultima/ultima4/conversation/dialogueloader_tlk.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
static DialogueLoaders *g_loaders;
|
||||
|
||||
DialogueLoaders::DialogueLoaders() {
|
||||
g_loaders = this;
|
||||
registerLoader(new U4HWDialogueLoader, "application/x-u4hwtlk");
|
||||
registerLoader(new U4LBDialogueLoader, "application/x-u4lbtlk");
|
||||
registerLoader(new U4TlkDialogueLoader(), "application/x-u4tlk");
|
||||
}
|
||||
|
||||
DialogueLoaders::~DialogueLoaders() {
|
||||
for (auto &l : _loaders)
|
||||
delete l._value;
|
||||
g_loaders = nullptr;
|
||||
}
|
||||
|
||||
DialogueLoader *DialogueLoaders::getLoader(const Common::String &mimeType) {
|
||||
return (*g_loaders)[mimeType];
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
71
engines/ultima/ultima4/conversation/dialogueloader.h
Normal file
71
engines/ultima/ultima4/conversation/dialogueloader.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_CONVERSATION_DIALOGUELOADER_H
|
||||
#define ULTIMA4_CONVERSATION_DIALOGUELOADER_H
|
||||
|
||||
#include "common/hashmap.h"
|
||||
#include "common/str.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
class Dialogue;
|
||||
|
||||
/**
|
||||
* The generic dialogue loader interface. Different dialogue
|
||||
* loaders should override the load method to load dialogues from
|
||||
* different sources (.tlk files, xml config elements, etc.). They
|
||||
* must also register themselves with registerLoader for one or more
|
||||
* source types. By convention, the source type of load() and
|
||||
* registerLoader() is an xu4-specific mime type.
|
||||
* The two main types used are application/x-u4tlk and text/x-u4cfg.
|
||||
*/
|
||||
class DialogueLoader {
|
||||
public:
|
||||
virtual ~DialogueLoader() {}
|
||||
|
||||
virtual Dialogue *load(Common::SeekableReadStream *source) = 0;
|
||||
};
|
||||
|
||||
class DialogueLoaders {
|
||||
private:
|
||||
Common::HashMap<Common::String, DialogueLoader *> _loaders;
|
||||
public:
|
||||
static DialogueLoader *getLoader(const Common::String &mimeType);
|
||||
public:
|
||||
DialogueLoaders();
|
||||
~DialogueLoaders();
|
||||
|
||||
void registerLoader(DialogueLoader *loader, const Common::String &mimeType) {
|
||||
_loaders[mimeType] = loader;
|
||||
}
|
||||
|
||||
DialogueLoader *operator[](const Common::String &mimeType) {
|
||||
return _loaders[mimeType];
|
||||
}
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
143
engines/ultima/ultima4/conversation/dialogueloader_hw.cpp
Normal file
143
engines/ultima/ultima4/conversation/dialogueloader_hw.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/game/context.h"
|
||||
#include "ultima/ultima4/conversation/conversation.h"
|
||||
#include "ultima/ultima4/conversation/dialogueloader_hw.h"
|
||||
#include "ultima/ultima4/game/player.h"
|
||||
#include "ultima/ultima4/filesys/savegame.h"
|
||||
#include "ultima/ultima4/filesys/u4file.h"
|
||||
#include "ultima/ultima4/core/utils.h"
|
||||
#include "ultima/ultima4/ultima4.h"
|
||||
#include "ultima/shared/std/containers.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
Response *hawkwindGetAdvice(const DynamicResponse *kw);
|
||||
Response *hawkwindGetIntro(const DynamicResponse *dynResp);
|
||||
|
||||
/* Hawkwind text indexes */
|
||||
#define HW_SPEAKONLYWITH 40
|
||||
#define HW_RETURNWHEN 41
|
||||
#define HW_ISREVIVED 42
|
||||
#define HW_WELCOME 43
|
||||
#define HW_GREETING1 44
|
||||
#define HW_GREETING2 45
|
||||
#define HW_PROMPT 46
|
||||
#define HW_DEFAULT 49
|
||||
#define HW_ALREADYAVATAR 50
|
||||
#define HW_GOTOSHRINE 51
|
||||
#define HW_BYE 52
|
||||
|
||||
/**
|
||||
* A special case dialogue loader for Hawkwind.
|
||||
*/
|
||||
Dialogue *U4HWDialogueLoader::load(Common::SeekableReadStream *source) {
|
||||
Std::vector<Common::String> &hawkwindText = g_ultima->_hawkwindText;
|
||||
hawkwindText = u4read_stringtable("hawkwind");
|
||||
|
||||
Dialogue *dlg = new Dialogue();
|
||||
dlg->setTurnAwayProb(0);
|
||||
|
||||
dlg->setName("Hawkwind");
|
||||
dlg->setPronoun("He");
|
||||
dlg->setPrompt(hawkwindText[HW_PROMPT]);
|
||||
Response *intro = new DynamicResponse(&hawkwindGetIntro);
|
||||
dlg->setIntro(intro);
|
||||
dlg->setLongIntro(intro);
|
||||
dlg->setDefaultAnswer(new Response(Common::String("\n" + hawkwindText[HW_DEFAULT])));
|
||||
|
||||
for (int v = 0; v < VIRT_MAX; v++) {
|
||||
Common::String virtue(getVirtueName((Virtue) v));
|
||||
lowercase(virtue);
|
||||
virtue = virtue.substr(0, 4);
|
||||
dlg->addKeyword(virtue, new DynamicResponse(&hawkwindGetAdvice, virtue));
|
||||
}
|
||||
|
||||
Response *bye = new Response(hawkwindText[HW_BYE]);
|
||||
bye->add(g_responseParts->STOPMUSIC);
|
||||
bye->add(g_responseParts->END);
|
||||
dlg->addKeyword("bye", bye);
|
||||
dlg->addKeyword("", bye);
|
||||
|
||||
return dlg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the appropriate response when the player asks Lord British
|
||||
* for help. The help text depends on the current party status; when
|
||||
* one quest item is complete, Lord British provides some direction to
|
||||
* the next one.
|
||||
*/
|
||||
Response *hawkwindGetAdvice(const DynamicResponse *dynResp) {
|
||||
Common::String text;
|
||||
int virtue = -1, virtueLevel = -1;
|
||||
Std::vector<Common::String> &hawkwindText = g_ultima->_hawkwindText;
|
||||
|
||||
/* check if asking about a virtue */
|
||||
for (int v = 0; v < VIRT_MAX; v++) {
|
||||
if (scumm_strnicmp(dynResp->getParam().c_str(), getVirtueName((Virtue) v), 4) == 0) {
|
||||
virtue = v;
|
||||
virtueLevel = g_ultima->_saveGame->_karma[v];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (virtue != -1) {
|
||||
text = "\n\n";
|
||||
if (virtueLevel == 0)
|
||||
text += hawkwindText[HW_ALREADYAVATAR] + "\n";
|
||||
else if (virtueLevel < 80)
|
||||
text += hawkwindText[(virtueLevel / 20) * 8 + virtue];
|
||||
else if (virtueLevel < 99)
|
||||
text += hawkwindText[3 * 8 + virtue];
|
||||
else /* virtueLevel >= 99 */
|
||||
text = hawkwindText[4 * 8 + virtue] + hawkwindText[HW_GOTOSHRINE];
|
||||
} else {
|
||||
text = Common::String("\n") + hawkwindText[HW_DEFAULT];
|
||||
}
|
||||
|
||||
return new Response(text);
|
||||
}
|
||||
|
||||
Response *hawkwindGetIntro(const DynamicResponse *dynResp) {
|
||||
Response *intro = new Response("");
|
||||
Std::vector<Common::String> &hawkwindText = g_ultima->_hawkwindText;
|
||||
|
||||
if (g_context->_party->member(0)->getStatus() == STAT_SLEEPING ||
|
||||
g_context->_party->member(0)->getStatus() == STAT_DEAD) {
|
||||
intro->add(hawkwindText[HW_SPEAKONLYWITH] + g_context->_party->member(0)->getName() +
|
||||
hawkwindText[HW_RETURNWHEN] + g_context->_party->member(0)->getName() +
|
||||
hawkwindText[HW_ISREVIVED]);
|
||||
intro->add(g_responseParts->END);
|
||||
} else {
|
||||
intro->add(g_responseParts->STARTMUSIC_HW);
|
||||
intro->add(g_responseParts->HAWKWIND);
|
||||
|
||||
intro->add(hawkwindText[HW_WELCOME] + g_context->_party->member(0)->getName() +
|
||||
hawkwindText[HW_GREETING1] + hawkwindText[HW_GREETING2]);
|
||||
}
|
||||
|
||||
return intro;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
41
engines/ultima/ultima4/conversation/dialogueloader_hw.h
Normal file
41
engines/ultima/ultima4/conversation/dialogueloader_hw.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_CONVERSATION_DIALOGUELOADER_HW_H
|
||||
#define ULTIMA4_CONVERSATION_DIALOGUELOADER_HW_H
|
||||
|
||||
#include "ultima/ultima4/conversation/dialogueloader.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* The dialogue loader for Hawkwind.
|
||||
*/
|
||||
class U4HWDialogueLoader : public DialogueLoader {
|
||||
public:
|
||||
Dialogue *load(Common::SeekableReadStream *source) override;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
196
engines/ultima/ultima4/conversation/dialogueloader_lb.cpp
Normal file
196
engines/ultima/ultima4/conversation/dialogueloader_lb.cpp
Normal file
@@ -0,0 +1,196 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/game/context.h"
|
||||
#include "ultima/ultima4/conversation/conversation.h"
|
||||
#include "ultima/ultima4/conversation/dialogueloader_lb.h"
|
||||
#include "ultima/ultima4/game/player.h"
|
||||
#include "ultima/ultima4/filesys/savegame.h"
|
||||
#include "ultima/ultima4/filesys/u4file.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
Response *lordBritishGetHelp(const DynamicResponse *resp);
|
||||
Response *lordBritishGetIntro(const DynamicResponse *resp);
|
||||
|
||||
/**
|
||||
* A special case dialogue loader for Lord British. Loads most of the
|
||||
* keyword/responses from a string table originally extracted from the
|
||||
* game executable. The "help" response is a special case that changes
|
||||
* based on the current party status.
|
||||
*/
|
||||
Dialogue *U4LBDialogueLoader::load(Common::SeekableReadStream *source) {
|
||||
Std::vector<Common::String> lbKeywords = u4read_stringtable("lb_keywords");
|
||||
Std::vector<Common::String> lbText = u4read_stringtable("lb_text");
|
||||
|
||||
Dialogue *dlg = new Dialogue();
|
||||
dlg->setTurnAwayProb(0);
|
||||
|
||||
dlg->setName("Lord British");
|
||||
dlg->setPronoun("He");
|
||||
dlg->setPrompt("What else?\n");
|
||||
Response *intro = new DynamicResponse(&lordBritishGetIntro);
|
||||
dlg->setIntro(intro);
|
||||
dlg->setLongIntro(intro);
|
||||
dlg->setDefaultAnswer(new Response("\nHe says: I\ncannot help thee\nwith that.\n"));
|
||||
|
||||
for (unsigned i = 0; i < lbKeywords.size(); i++) {
|
||||
dlg->addKeyword(lbKeywords[i], new Response(lbText[i]));
|
||||
}
|
||||
|
||||
Response *heal = new Response("\n\n\n\n\n\nHe says: I am\nwell, thank ye.");
|
||||
heal->add(g_responseParts->HEALCONFIRM);
|
||||
dlg->addKeyword("heal", heal);
|
||||
|
||||
Response *bye;
|
||||
if (g_context->_party->size() > 1)
|
||||
bye = new Response("Lord British says: Fare thee well my friends!");
|
||||
else
|
||||
bye = new Response("Lord British says: Fare thee well my friend!");
|
||||
bye->add(g_responseParts->STOPMUSIC);
|
||||
bye->add(g_responseParts->END);
|
||||
dlg->addKeyword("bye", bye);
|
||||
dlg->addKeyword("", bye);
|
||||
|
||||
dlg->addKeyword("help", new DynamicResponse(&lordBritishGetHelp));
|
||||
|
||||
return dlg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the appropriate response when the player asks Lord British
|
||||
* for help. The help text depends on the current party status; when
|
||||
* one quest item is complete, Lord British provides some direction to
|
||||
* the next one.
|
||||
*/
|
||||
Response *lordBritishGetHelp(const DynamicResponse *resp) {
|
||||
int v;
|
||||
bool fullAvatar, partialAvatar;
|
||||
Common::String text;
|
||||
|
||||
/*
|
||||
* check whether player is full avatar (in all virtues) or partial
|
||||
* avatar (in at least one virtue)
|
||||
*/
|
||||
fullAvatar = true;
|
||||
partialAvatar = false;
|
||||
for (v = 0; v < VIRT_MAX; v++) {
|
||||
fullAvatar &= (g_ultima->_saveGame->_karma[v] == 0);
|
||||
partialAvatar |= (g_ultima->_saveGame->_karma[v] == 0);
|
||||
}
|
||||
|
||||
if (g_ultima->_saveGame->_moves <= 1000) {
|
||||
text = "To survive in this hostile land thou must first know thyself! Seek ye to master thy weapons and thy magical ability!\n"
|
||||
"\nTake great care in these thy first travels in Britannia.\n"
|
||||
"\nUntil thou dost well know thyself, travel not far from the safety of the townes!\n";
|
||||
}
|
||||
|
||||
else if (g_ultima->_saveGame->_members == 1) {
|
||||
text = "Travel not the open lands alone. There are many worthy people in the diverse townes whom it would be wise to ask to Join thee!\n"
|
||||
"\nBuild thy party unto eight travellers, for only a true leader can win the Quest!\n";
|
||||
}
|
||||
|
||||
else if (g_ultima->_saveGame->_runes == 0) {
|
||||
text = "Learn ye the paths of virtue. Seek to gain entry unto the eight shrines!\n"
|
||||
"\nFind ye the Runes, needed for entry into each shrine, and learn each chant or \"Mantra\" used to focus thy meditations.\n"
|
||||
"\nWithin the Shrines thou shalt learn of the deeds which show thy inner virtue or vice!\n"
|
||||
"\nChoose thy path wisely for all thy deeds of good and evil are remembered and can return to hinder thee!\n";
|
||||
}
|
||||
|
||||
else if (!partialAvatar) {
|
||||
text = "Visit the Seer Hawkwind often and use his wisdom to help thee prove thy virtue.\n"
|
||||
"\nWhen thou art ready, Hawkwind will advise thee to seek the Elevation unto partial Avatarhood in a virtue.\n"
|
||||
"\nSeek ye to become a partial Avatar in all eight virtues, for only then shalt thou be ready to seek the codex!\n";
|
||||
}
|
||||
|
||||
else if (g_ultima->_saveGame->_stones == 0) {
|
||||
text = "Go ye now into the depths of the dungeons. Therein recover the 8 colored stones from the altar pedestals in the halls of the dungeons.\n"
|
||||
"\nFind the uses of these stones for they can help thee in the Abyss!\n";
|
||||
}
|
||||
|
||||
else if (!fullAvatar) {
|
||||
text = "Thou art doing very well indeed on the path to Avatarhood! Strive ye to achieve the Elevation in all eight virtues!\n";
|
||||
}
|
||||
|
||||
else if ((g_ultima->_saveGame->_items & ITEM_BELL) == 0 ||
|
||||
(g_ultima->_saveGame->_items & ITEM_BOOK) == 0 ||
|
||||
(g_ultima->_saveGame->_items & ITEM_CANDLE) == 0) {
|
||||
text = "Find ye the Bell, Book and Candle! With these three things, one may enter the Great Stygian Abyss!\n";
|
||||
}
|
||||
|
||||
else if ((g_ultima->_saveGame->_items & ITEM_KEY_C) == 0 ||
|
||||
(g_ultima->_saveGame->_items & ITEM_KEY_L) == 0 ||
|
||||
(g_ultima->_saveGame->_items & ITEM_KEY_T) == 0) {
|
||||
text = "Before thou dost enter the Abyss thou shalt need the Key of Three Parts, and the Word of Passage.\n"
|
||||
"\nThen might thou enter the Chamber of the Codex of Ultimate Wisdom!\n";
|
||||
}
|
||||
|
||||
else {
|
||||
text = "Thou dost now seem ready to make the final journey into the dark Abyss! Go only with a party of eight!\n"
|
||||
"\nGood Luck, and may the powers of good watch over thee on this thy most perilous endeavor!\n"
|
||||
"\nThe hearts and souls of all Britannia go with thee now. Take care, my friend.\n";
|
||||
}
|
||||
|
||||
return new Response(Common::String("He says: ") + text);
|
||||
}
|
||||
|
||||
Response *lordBritishGetIntro(const DynamicResponse *resp) {
|
||||
Response *intro = new Response("");
|
||||
intro->add(g_responseParts->STARTMUSIC_LB);
|
||||
|
||||
if (g_ultima->_saveGame->_lbIntro) {
|
||||
if (g_ultima->_saveGame->_members == 1) {
|
||||
intro->add(Common::String("\n\n\nLord British\nsays: Welcome\n") +
|
||||
g_context->_party->member(0)->getName() + "!\n\n");
|
||||
} else if (g_ultima->_saveGame->_members == 2) {
|
||||
intro->add(Common::String("\n\nLord British\nsays: Welcome\n") +
|
||||
g_context->_party->member(0)->getName() +
|
||||
" and thee also " +
|
||||
g_context->_party->member(1)->getName() +
|
||||
"!\n\n");
|
||||
} else {
|
||||
intro->add(Common::String("\n\n\nLord British\nsays: Welcome\n") +
|
||||
g_context->_party->member(0)->getName() +
|
||||
" and thy\nworthy\nAdventurers!\n\n");
|
||||
}
|
||||
|
||||
// Lord British automatically adds "What would thou ask of me?"
|
||||
|
||||
// Check levels here, just like the original!
|
||||
intro->add(g_responseParts->ADVANCELEVELS);
|
||||
}
|
||||
|
||||
else {
|
||||
intro->add(Common::String("\n\n\nLord British rises and says: At long last!\n") +
|
||||
g_context->_party->member(0)->getName() +
|
||||
" thou hast come! We have waited such a long, long time...\n"
|
||||
"\n\nLord British sits and says: A new age is upon Britannia. The great evil Lords are gone but our people lack direction and purpose in their lives...\n\n\n"
|
||||
"A champion of virtue is called for. Thou may be this champion, but only time shall tell. I will aid thee any way that I can!\n\n"
|
||||
"How may I help thee?\n");
|
||||
g_ultima->_saveGame->_lbIntro = 1;
|
||||
}
|
||||
|
||||
return intro;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
41
engines/ultima/ultima4/conversation/dialogueloader_lb.h
Normal file
41
engines/ultima/ultima4/conversation/dialogueloader_lb.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_CONVERSATION_DIALOGUELOADER_LB_H
|
||||
#define ULTIMA4_CONVERSATION_DIALOGUELOADER_LB_H
|
||||
|
||||
#include "ultima/ultima4/conversation/dialogueloader.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* The dialogue loader for Lord British
|
||||
*/
|
||||
class U4LBDialogueLoader : public DialogueLoader {
|
||||
public:
|
||||
Dialogue *load(Common::SeekableReadStream *source) override;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
162
engines/ultima/ultima4/conversation/dialogueloader_tlk.cpp
Normal file
162
engines/ultima/ultima4/conversation/dialogueloader_tlk.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ultima/ultima4/conversation/conversation.h"
|
||||
#include "ultima/ultima4/conversation/dialogueloader_tlk.h"
|
||||
#include "ultima/shared/std/containers.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* A dialogue loader for standard u4dos .tlk files.
|
||||
*/
|
||||
Dialogue *U4TlkDialogueLoader::load(Common::SeekableReadStream *source) {
|
||||
enum QTrigger {
|
||||
NONE = 0,
|
||||
JOB = 3,
|
||||
HEALTH = 4,
|
||||
KEYWORD1 = 5,
|
||||
KEYWORD2 = 6
|
||||
};
|
||||
|
||||
/* there's no dialogues left in the file */
|
||||
char tlk_buffer[288];
|
||||
if (source->read(tlk_buffer, sizeof(tlk_buffer)) != sizeof(tlk_buffer))
|
||||
return nullptr;
|
||||
|
||||
char *ptr = &tlk_buffer[3];
|
||||
Std::vector<Common::String> strings;
|
||||
for (int i = 0; i < 12; i++) {
|
||||
strings.push_back(ptr);
|
||||
ptr += strlen(ptr) + 1;
|
||||
}
|
||||
|
||||
Dialogue *dlg = new Dialogue();
|
||||
byte prob = tlk_buffer[2];
|
||||
QTrigger qtrigger = QTrigger(tlk_buffer[0]);
|
||||
bool humilityTestQuestion = tlk_buffer[1] == 1;
|
||||
|
||||
dlg->setTurnAwayProb(prob);
|
||||
|
||||
dlg->setName(strings[0]);
|
||||
dlg->setPronoun(strings[1]);
|
||||
dlg->setPrompt("\nYour Interest:\n");
|
||||
|
||||
// Fix the actor description Common::String, converting the first character
|
||||
// to lower-case.
|
||||
strings[2].setChar(tolower(strings[2][0]), 0);
|
||||
|
||||
// ... then replace any newlines in the Common::String with spaces
|
||||
size_t index = strings[2].find("\n");
|
||||
while (index != Common::String::npos) {
|
||||
strings[2].setChar(' ', index);
|
||||
index = strings[2].find("\n");
|
||||
}
|
||||
|
||||
// ... then append a period to the end of the Common::String if one does
|
||||
// not already exist
|
||||
if (!Common::isPunct(strings[2][strings[2].size() - 1]))
|
||||
strings[2] = strings[2] + Common::String(".");
|
||||
|
||||
// ... and finally, a few characters in the game have descriptions
|
||||
// that do not begin with a definite (the) or indefinite (a/an)
|
||||
// article. On those characters, insert the appropriate article.
|
||||
if ((strings[0] == "Iolo")
|
||||
|| (strings[0] == "Tracie")
|
||||
|| (strings[0] == "Dupre")
|
||||
|| (strings[0] == "Traveling Dan"))
|
||||
strings[2] = Common::String("a ") + strings[2];
|
||||
|
||||
Common::String introBase = Common::String("\nYou meet ") + strings[2] + "\n";
|
||||
|
||||
dlg->setIntro(new Response(introBase + dlg->getPrompt()));
|
||||
dlg->setLongIntro(new Response(introBase +
|
||||
"\n" + dlg->getPronoun() + " says: I am " + dlg->getName() + "\n"
|
||||
+ dlg->getPrompt()));
|
||||
dlg->setDefaultAnswer(new Response("That I cannot\nhelp thee with."));
|
||||
|
||||
Response *yes = new Response(strings[8]);
|
||||
Response *no = new Response(strings[9]);
|
||||
if (humilityTestQuestion) {
|
||||
yes->add(g_responseParts->BRAGGED);
|
||||
no->add(g_responseParts->HUMBLE);
|
||||
}
|
||||
dlg->setQuestion(new Dialogue::Question(strings[7], yes, no));
|
||||
|
||||
// one of the following four keywords triggers the speaker's question
|
||||
Response *job = new Response(Common::String("\n") + strings[3]);
|
||||
Response *health = new Response(Common::String("\n") + strings[4]);
|
||||
Response *kw1 = new Response(Common::String("\n") + strings[5]);
|
||||
Response *kw2 = new Response(Common::String("\n") + strings[6]);
|
||||
|
||||
switch (qtrigger) {
|
||||
case JOB:
|
||||
job->add(g_responseParts->ASK);
|
||||
break;
|
||||
case HEALTH:
|
||||
health->add(g_responseParts->ASK);
|
||||
break;
|
||||
case KEYWORD1:
|
||||
kw1->add(g_responseParts->ASK);
|
||||
break;
|
||||
case KEYWORD2:
|
||||
kw2->add(g_responseParts->ASK);
|
||||
break;
|
||||
case NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dlg->addKeyword("job", job);
|
||||
dlg->addKeyword("heal", health);
|
||||
dlg->addKeyword(strings[10], kw1);
|
||||
dlg->addKeyword(strings[11], kw2);
|
||||
|
||||
// NOTE: We let the talker's custom keywords override the standard
|
||||
// keywords like HEAL and LOOK. This behavior differs from u4dos,
|
||||
// but fixes a couple conversation files which have keywords that
|
||||
// conflict with the standard ones (e.g. Calabrini in Moonglow has
|
||||
// HEAL for healer, which is unreachable in u4dos, but clearly
|
||||
// more useful than "Fine." for health).
|
||||
Common::String look = Common::String("\nYou see ") + strings[2];
|
||||
dlg->addKeyword("look", new Response(look));
|
||||
dlg->addKeyword("name", new Response(Common::String("\n") + dlg->getPronoun() + " says: I am " + dlg->getName()));
|
||||
dlg->addKeyword("give", new Response(Common::String("\n") + dlg->getPronoun() + " says: I do not need thy gold. Keep it!"));
|
||||
dlg->addKeyword("join", new Response(Common::String("\n") + dlg->getPronoun() + " says: I cannot join thee."));
|
||||
|
||||
Response *bye = new Response("\nBye.");
|
||||
bye->add(g_responseParts->END);
|
||||
dlg->addKeyword("bye", bye);
|
||||
dlg->addKeyword("", bye);
|
||||
|
||||
/*
|
||||
* This little easter egg appeared in the Amiga version of Ultima IV.
|
||||
* I've never figured out what the number means.
|
||||
* "Banjo" Bob Hardy was the programmer for the Amiga version.
|
||||
*/
|
||||
dlg->addKeyword("ojna", new Response("\nHi Banjo Bob!\nYour secret\nnumber is\n4F4A4E0A"));
|
||||
|
||||
return dlg;
|
||||
}
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
41
engines/ultima/ultima4/conversation/dialogueloader_tlk.h
Normal file
41
engines/ultima/ultima4/conversation/dialogueloader_tlk.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ULTIMA4_CONVERSATION_DIALOGUELOADER_TLK_H
|
||||
#define ULTIMA4_CONVERSATION_DIALOGUELOADER_TLK_H
|
||||
|
||||
#include "ultima/ultima4/conversation/dialogueloader.h"
|
||||
|
||||
namespace Ultima {
|
||||
namespace Ultima4 {
|
||||
|
||||
/**
|
||||
* The dialogue loader for u4dos .tlk files
|
||||
*/
|
||||
class U4TlkDialogueLoader : public DialogueLoader {
|
||||
public:
|
||||
Dialogue *load(Common::SeekableReadStream *source) override;
|
||||
};
|
||||
|
||||
} // End of namespace Ultima4
|
||||
} // End of namespace Ultima
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user