feat(game): Add support for multiple speakers in dialogs

This commit is contained in:
Vsevolod Kremianskii 2020-08-07 17:56:32 +07:00
parent ac29db6335
commit d88a01dc6e
8 changed files with 95 additions and 35 deletions

View file

@ -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()) {

View file

@ -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);
});
}

View file

@ -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;
}

View file

@ -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();

View file

@ -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 {

View file

@ -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();
};

View file

@ -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();

View file

@ -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);