feat(game): Add support for multiple speakers in dialogs
This commit is contained in:
parent
ac29db6335
commit
d88a01dc6e
8 changed files with 95 additions and 35 deletions
|
@ -17,6 +17,8 @@
|
|||
|
||||
#include "dialog.h"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include "../resources/manager.h"
|
||||
|
||||
using namespace std;
|
||||
|
@ -65,6 +67,9 @@ Dialog::EntryReply Dialog::getEntryReply(const GffStruct &gffs) const {
|
|||
entry.listener = gffs.getString("Listener");
|
||||
entry.cameraAngle = gffs.getInt("CameraAngle");
|
||||
|
||||
boost::to_lower(entry.speaker);
|
||||
boost::to_lower(entry.listener);
|
||||
|
||||
const GffField *repliesList = gffs.find("RepliesList");
|
||||
if (repliesList) {
|
||||
for (auto &link : repliesList->children()) {
|
||||
|
|
|
@ -143,6 +143,21 @@ void Game::loadModule(const string &name, string entry) {
|
|||
unique_ptr<DialogGui> dialog(new DialogGui(_opts.graphics));
|
||||
dialog->load(_version);
|
||||
dialog->initGL();
|
||||
dialog->setOnSpeakerChanged([this](const string &from, const string &to) {
|
||||
Object *player = _module->area().player().get();
|
||||
Creature *prevSpeaker = !from.empty() ? static_cast<Creature *>(_module->area().find(from).get()) : nullptr;
|
||||
Creature *speaker = !to.empty() ? static_cast<Creature *>(_module->area().find(to).get()) : nullptr;
|
||||
|
||||
if (prevSpeaker) {
|
||||
prevSpeaker->playDefaultAnimation();
|
||||
}
|
||||
if (player && speaker) {
|
||||
player->face(*speaker);
|
||||
speaker->face(*player);
|
||||
speaker->playTalkAnimation();
|
||||
_module->update3rdPersonCameraHeading();
|
||||
}
|
||||
});
|
||||
dialog->setOnDialogFinished([this]() {
|
||||
_screen = Screen::InGame;
|
||||
});
|
||||
|
@ -165,9 +180,9 @@ void Game::configureModule() {
|
|||
_nextModule = name;
|
||||
_nextEntry = entry;
|
||||
});
|
||||
_module->setStartConversation([this](const string &name) {
|
||||
_module->setStartDialog([this](const string &resRef, const string &owner) {
|
||||
_screen = Screen::Dialog;
|
||||
_dialog->startDialog(name);
|
||||
_dialog->startDialog(resRef, owner);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -129,7 +129,12 @@ void DialogGui::configureReplies() {
|
|||
void DialogGui::onReplyClicked(int index) {
|
||||
const Dialog::EntryReply &reply = _dialog->getReply(index);
|
||||
if (reply.entries.empty()) {
|
||||
if (_onDialogFinished) _onDialogFinished();
|
||||
if (_onDialogFinished) {
|
||||
if (_onSpeakerChanged) {
|
||||
_onSpeakerChanged(_currentSpeaker, "");
|
||||
}
|
||||
_onDialogFinished();
|
||||
}
|
||||
return;
|
||||
}
|
||||
int entryIdx = -1;
|
||||
|
@ -151,16 +156,20 @@ void DialogGui::onReplyClicked(int index) {
|
|||
}
|
||||
}
|
||||
|
||||
void DialogGui::startDialog(const string &resRef) {
|
||||
void DialogGui::startDialog(const string &resRef, const string &owner) {
|
||||
shared_ptr<GffStruct> dlg(ResMan.findGFF(resRef, ResourceType::Conversation));
|
||||
if (!dlg) {
|
||||
if (_onDialogFinished) _onDialogFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
_dialog.reset(new Dialog());
|
||||
_dialog->load(resRef, *dlg);
|
||||
|
||||
_currentSpeaker = owner;
|
||||
if (_onSpeakerChanged) {
|
||||
_onSpeakerChanged("", _currentSpeaker);
|
||||
}
|
||||
|
||||
loadStartEntry();
|
||||
}
|
||||
|
||||
|
@ -191,6 +200,14 @@ void DialogGui::loadCurrentEntry() {
|
|||
if (_currentVoice) _currentVoice->stop();
|
||||
|
||||
assert(_currentEntry);
|
||||
|
||||
if (!_currentEntry->speaker.empty() && _currentSpeaker != _currentEntry->speaker) {
|
||||
string prevSpeaker(_currentSpeaker);
|
||||
_currentSpeaker = _currentEntry->speaker;
|
||||
if (_onSpeakerChanged) {
|
||||
_onSpeakerChanged(prevSpeaker, _currentSpeaker);
|
||||
}
|
||||
}
|
||||
if (!_currentEntry->voResRef.empty()) {
|
||||
shared_ptr<AudioStream> voice(ResMan.findAudio(_currentEntry->voResRef));
|
||||
if (voice) {
|
||||
|
@ -218,11 +235,18 @@ void DialogGui::loadCurrentEntry() {
|
|||
ScriptExecution(ScriptMan.find(_currentEntry->script), ExecutionContext()).run();
|
||||
}
|
||||
if (replyCount == 0 && _onDialogFinished) {
|
||||
if (_onSpeakerChanged) {
|
||||
_onSpeakerChanged(_currentSpeaker, "");
|
||||
}
|
||||
_onDialogFinished();
|
||||
}
|
||||
}
|
||||
|
||||
void DialogGui::setOnDialogFinished(const std::function<void()> &fn) {
|
||||
void DialogGui::setOnSpeakerChanged(const std::function<void(const string&, const string &)> &fn) {
|
||||
_onSpeakerChanged = fn;
|
||||
}
|
||||
|
||||
void DialogGui::setOnDialogFinished(const function<void()> &fn) {
|
||||
_onDialogFinished = fn;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,8 +32,9 @@ public:
|
|||
DialogGui(const render::GraphicsOptions &opts);
|
||||
|
||||
void load(resources::GameVersion version);
|
||||
void startDialog(const std::string &resRef);
|
||||
void startDialog(const std::string &resRef, const std::string &owner);
|
||||
|
||||
void setOnSpeakerChanged(const std::function<void(const std::string &, const std::string &)> &fn);
|
||||
void setOnDialogFinished(const std::function<void()> &fn);
|
||||
|
||||
private:
|
||||
|
@ -41,6 +42,8 @@ private:
|
|||
std::shared_ptr<Dialog> _dialog;
|
||||
std::shared_ptr<Dialog::EntryReply> _currentEntry;
|
||||
std::shared_ptr<audio::SoundInstance> _currentVoice;
|
||||
std::string _currentSpeaker;
|
||||
std::function<void(const std::string &, const std::string &)> _onSpeakerChanged;
|
||||
std::function<void()> _onDialogFinished;
|
||||
|
||||
void addTopFrame();
|
||||
|
|
|
@ -82,13 +82,8 @@ void Module::loadArea(const GffStruct &ifo) {
|
|||
}
|
||||
});
|
||||
area->setOnPlayerChanged([this]() {
|
||||
syncThirdPersonCamera();
|
||||
if (_cameraType != CameraType::ThirdPerson) {
|
||||
_cameraType = CameraType::ThirdPerson;
|
||||
if (_onCameraChanged) {
|
||||
_onCameraChanged(CameraType::ThirdPerson);
|
||||
}
|
||||
}
|
||||
update3rdPersonCameraTarget();
|
||||
switchTo3rdPersonCamera();
|
||||
});
|
||||
area->load(*are, *git);
|
||||
_area = move(area);
|
||||
|
@ -151,8 +146,24 @@ void Module::loadParty(const string &entry) {
|
|||
|
||||
_area->loadParty(position, heading);
|
||||
|
||||
syncThirdPersonCamera();
|
||||
_thirdPersonCamera->setHeading(heading);
|
||||
update3rdPersonCameraTarget();
|
||||
update3rdPersonCameraHeading();
|
||||
switchTo3rdPersonCamera();
|
||||
}
|
||||
|
||||
void Module::update3rdPersonCameraTarget() {
|
||||
Object &player = *_area->player();
|
||||
_thirdPersonCamera->setTargetPosition(player.position() + player.model()->getNodeAbsolutePosition("camerahook"));
|
||||
}
|
||||
|
||||
void Module::update3rdPersonCameraHeading() {
|
||||
Object &player = *_area->player();
|
||||
_thirdPersonCamera->setHeading(player.heading());
|
||||
}
|
||||
|
||||
void Module::switchTo3rdPersonCamera() {
|
||||
if (_cameraType == CameraType::ThirdPerson) return;
|
||||
|
||||
_cameraType = CameraType::ThirdPerson;
|
||||
|
||||
if (_onCameraChanged) {
|
||||
|
@ -160,11 +171,6 @@ void Module::loadParty(const string &entry) {
|
|||
}
|
||||
}
|
||||
|
||||
void Module::syncThirdPersonCamera() {
|
||||
Creature &player = static_cast<Creature &>(*_area->player());
|
||||
_thirdPersonCamera->setTargetPosition(player.position() + player.model()->getNodeAbsolutePosition("camerahook"));
|
||||
}
|
||||
|
||||
bool Module::handle(const SDL_Event &event) {
|
||||
if (!_loaded) return false;
|
||||
if (getCamera()->handle(event)) return true;
|
||||
|
@ -213,16 +219,10 @@ bool Module::handleMouseButtonUp(const SDL_MouseButtonEvent &event) {
|
|||
|
||||
Creature *creature = dynamic_cast<Creature *>(obstacle);
|
||||
if (creature) {
|
||||
if (!creature->conversation().empty() && _startConversation) {
|
||||
if (!creature->conversation().empty() && _startDialog) {
|
||||
resetInput();
|
||||
getCamera()->resetInput();
|
||||
|
||||
shared_ptr<Object> player(_area->player());
|
||||
player->face(*creature);
|
||||
creature->face(*player);
|
||||
_thirdPersonCamera->setHeading(player->heading());
|
||||
|
||||
_startConversation(creature->conversation());
|
||||
_startDialog(creature->conversation(), creature->tag());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -368,7 +368,7 @@ void Module::updatePlayer(float dt) {
|
|||
|
||||
if (_area->moveCreatureTowards(player, target, dt)) {
|
||||
player.setMovementType(MovementType::Run);
|
||||
syncThirdPersonCamera();
|
||||
update3rdPersonCameraTarget();
|
||||
}
|
||||
} else {
|
||||
player.setMovementType(MovementType::None);
|
||||
|
@ -399,8 +399,8 @@ void Module::setOnModuleTransition(const function<void(const string &, const str
|
|||
_onModuleTransition = fn;
|
||||
}
|
||||
|
||||
void Module::setStartConversation(const function<void(const string &)> &fn) {
|
||||
_startConversation = fn;
|
||||
void Module::setStartDialog(const function<void(const string &, const string &)> &fn) {
|
||||
_startDialog = fn;
|
||||
}
|
||||
|
||||
const string &Module::name() const {
|
||||
|
|
|
@ -55,6 +55,8 @@ public:
|
|||
void initGL() override;
|
||||
void render() const override;
|
||||
|
||||
void update3rdPersonCameraHeading();
|
||||
|
||||
// Setters
|
||||
void setLoadParty(bool load);
|
||||
void setTransitionEnabled(bool enabled);
|
||||
|
@ -70,7 +72,7 @@ public:
|
|||
// Callbacks
|
||||
void setOnCameraChanged(const std::function<void(render::CameraType)> &fn);
|
||||
void setOnModuleTransition(const std::function<void(const std::string &, const std::string &)> &fn);
|
||||
void setStartConversation(const std::function<void(const std::string &)> &fn);
|
||||
void setStartDialog(const std::function<void(const std::string &, const std::string &)> &fn);
|
||||
|
||||
protected:
|
||||
resources::GameVersion _version { resources::GameVersion::KotOR };
|
||||
|
@ -94,7 +96,7 @@ private:
|
|||
// Callbacks
|
||||
std::function<void(render::CameraType)> _onCameraChanged;
|
||||
std::function<void(const std::string &, const std::string &)> _onModuleTransition;
|
||||
std::function<void(const std::string &)> _startConversation;
|
||||
std::function<void(const std::string &, const std::string &)> _startDialog;
|
||||
|
||||
void loadInfo(const resources::GffStruct &ifo);
|
||||
void loadArea(const resources::GffStruct &ifo);
|
||||
|
@ -108,7 +110,8 @@ private:
|
|||
void cycleDebugMode(bool forward);
|
||||
void updatePlayer(float dt);
|
||||
bool findObstacle(const glm::vec3 &from, const glm::vec3 &to, glm::vec3 &intersection) const;
|
||||
void syncThirdPersonCamera();
|
||||
void update3rdPersonCameraTarget();
|
||||
void switchTo3rdPersonCamera();
|
||||
void resetInput();
|
||||
};
|
||||
|
||||
|
|
|
@ -221,12 +221,20 @@ void Creature::initGL() {
|
|||
if (_portrait) _portrait->initGL();
|
||||
}
|
||||
|
||||
void Creature::playDefaultAnimation() {
|
||||
if (_model) _model->playDefaultAnimation();
|
||||
}
|
||||
|
||||
void Creature::playGreetingAnimation() {
|
||||
if (_movementType != MovementType::None) return;
|
||||
|
||||
animate("greeting");
|
||||
}
|
||||
|
||||
void Creature::playTalkAnimation() {
|
||||
animate("tlknorm", kAnimationLoop);
|
||||
}
|
||||
|
||||
void Creature::clearActions() {
|
||||
while (!_actions.empty() && _actions.back().type != ActionType::QueueEmpty) {
|
||||
_actions.pop_back();
|
||||
|
|
|
@ -59,7 +59,9 @@ public:
|
|||
void load(const resources::GffStruct &gffs);
|
||||
void load(int appearance, const glm::vec3 &position, float heading);
|
||||
void initGL() override;
|
||||
void playDefaultAnimation();
|
||||
void playGreetingAnimation();
|
||||
void playTalkAnimation();
|
||||
void clearActions();
|
||||
void enqueue(const Action &action);
|
||||
void equip(const std::string &resRef);
|
||||
|
|
Loading…
Reference in a new issue