feat: Implement dialog loading
This commit is contained in:
parent
7718bf149d
commit
c4fb806af3
20 changed files with 356 additions and 59 deletions
|
@ -25,6 +25,8 @@
|
|||
|
||||
#include "../core/log.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace audio {
|
||||
|
@ -43,29 +45,31 @@ void AudioPlayer::init(const AudioOptions &opts) {
|
|||
|
||||
_device = alcOpenDevice(nullptr);
|
||||
if (!_device) {
|
||||
throw std::runtime_error("Failed to open audio device");
|
||||
throw runtime_error("Failed to open audio device");
|
||||
}
|
||||
|
||||
_context = alcCreateContext(_device, nullptr);
|
||||
if (!_context) {
|
||||
throw std::runtime_error("Failed to create audio context");
|
||||
throw runtime_error("Failed to create audio context");
|
||||
}
|
||||
alcMakeContextCurrent(_context);
|
||||
alListenerf(AL_GAIN, _opts.volume / 100.0f);
|
||||
|
||||
_thread = std::thread(std::bind(threadStart, this));
|
||||
_thread = thread(bind(&AudioPlayer::threadStart, this));
|
||||
}
|
||||
|
||||
void AudioPlayer::threadStart(AudioPlayer *player) {
|
||||
while (player->_run) {
|
||||
std::lock_guard<std::recursive_mutex> lock(player->_soundsMutex);
|
||||
std::remove_if(
|
||||
player->_sounds.begin(),
|
||||
player->_sounds.end(),
|
||||
[](const SoundInstance &sound) { return sound.stopped(); });
|
||||
void AudioPlayer::threadStart() {
|
||||
while (_run) {
|
||||
lock_guard<recursive_mutex> lock(_soundsMutex);
|
||||
auto it = remove_if(
|
||||
_sounds.begin(),
|
||||
_sounds.end(),
|
||||
[](const shared_ptr<SoundInstance> &sound) { return sound->stopped(); });
|
||||
|
||||
for (auto &sound : player->_sounds) {
|
||||
sound.update();
|
||||
_sounds.erase(it, _sounds.end());
|
||||
|
||||
for (auto &sound : _sounds) {
|
||||
sound->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -93,18 +97,21 @@ void AudioPlayer::deinit() {
|
|||
}
|
||||
}
|
||||
|
||||
void AudioPlayer::play(const std::shared_ptr<AudioStream> &stream, bool loop) {
|
||||
if (!stream) {
|
||||
throw std::invalid_argument("Audio stream is empty");
|
||||
}
|
||||
SoundInstance sound(stream, loop);
|
||||
std::lock_guard<std::recursive_mutex> lock(_soundsMutex);
|
||||
_sounds.push_back(std::move(sound));
|
||||
void AudioPlayer::reset() {
|
||||
lock_guard<recursive_mutex> lock(_soundsMutex);
|
||||
_sounds.clear();
|
||||
}
|
||||
|
||||
void AudioPlayer::reset() {
|
||||
std::lock_guard<std::recursive_mutex> lock(_soundsMutex);
|
||||
_sounds.clear();
|
||||
shared_ptr<SoundInstance> AudioPlayer::play(const shared_ptr<AudioStream> &stream, bool loop) {
|
||||
if (!stream) {
|
||||
throw invalid_argument("Audio stream is empty");
|
||||
}
|
||||
shared_ptr<SoundInstance> sound(new SoundInstance(stream, loop));
|
||||
|
||||
lock_guard<recursive_mutex> lock(_soundsMutex);
|
||||
_sounds.push_back(sound);
|
||||
|
||||
return move(sound);
|
||||
}
|
||||
|
||||
} // namespace audio
|
||||
|
|
|
@ -36,8 +36,9 @@ public:
|
|||
|
||||
void init(const AudioOptions &opts);
|
||||
void deinit();
|
||||
void play(const std::shared_ptr<AudioStream> &stream, bool loop = false);
|
||||
|
||||
void reset();
|
||||
std::shared_ptr<SoundInstance> play(const std::shared_ptr<AudioStream> &stream, bool loop = false);
|
||||
|
||||
private:
|
||||
AudioOptions _opts;
|
||||
|
@ -45,7 +46,7 @@ private:
|
|||
ALCcontext *_context { nullptr };
|
||||
std::thread _thread;
|
||||
std::atomic_bool _run { true };
|
||||
std::list<SoundInstance> _sounds;
|
||||
std::list<std::shared_ptr<SoundInstance>> _sounds;
|
||||
std::recursive_mutex _soundsMutex;
|
||||
|
||||
AudioPlayer() = default;
|
||||
|
@ -54,7 +55,7 @@ private:
|
|||
|
||||
AudioPlayer &operator=(const AudioPlayer &) = delete;
|
||||
|
||||
static void threadStart(AudioPlayer *);
|
||||
void threadStart();
|
||||
};
|
||||
|
||||
#define TheAudioPlayer audio::AudioPlayer::instance()
|
||||
|
|
|
@ -67,7 +67,14 @@ void SoundInstance::update() {
|
|||
if (_state == State::NotInited) {
|
||||
init();
|
||||
}
|
||||
if (!_multiframe) return;
|
||||
if (!_multiframe) {
|
||||
ALint state = 0;
|
||||
alGetSourcei(_source, AL_SOURCE_STATE, &state);
|
||||
if (state == AL_STOPPED) {
|
||||
_state = State::Stopped;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ALint processed = 0;
|
||||
alGetSourcei(_source, AL_BUFFERS_PROCESSED, &processed);
|
||||
|
@ -88,6 +95,10 @@ void SoundInstance::update() {
|
|||
}
|
||||
}
|
||||
|
||||
void SoundInstance::stop() {
|
||||
alSourceStop(_source);
|
||||
}
|
||||
|
||||
bool SoundInstance::stopped() const {
|
||||
return _state == State::Stopped;
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@ public:
|
|||
SoundInstance &operator=(SoundInstance &&) = default;
|
||||
|
||||
void update();
|
||||
void stop();
|
||||
|
||||
bool stopped() const;
|
||||
|
||||
private:
|
||||
|
@ -49,8 +51,8 @@ private:
|
|||
State _state { State::NotInited };
|
||||
int _nextFrame { 0 };
|
||||
int _nextBuffer { 0 };
|
||||
unsigned int _source { 0 };
|
||||
std::vector<unsigned int> _buffers;
|
||||
uint32_t _source { 0 };
|
||||
std::vector<uint32_t> _buffers;
|
||||
|
||||
SoundInstance(SoundInstance &) = delete;
|
||||
SoundInstance &operator=(SoundInstance &) = delete;
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
#include "dialog.h"
|
||||
|
||||
#include "../resources/manager.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
using namespace reone::resources;
|
||||
|
@ -25,7 +27,72 @@ namespace reone {
|
|||
|
||||
namespace game {
|
||||
|
||||
void Dialog::reset() {
|
||||
_entries.clear();
|
||||
_replies.clear();
|
||||
_startEntries.clear();
|
||||
}
|
||||
|
||||
void Dialog::load(const string &resRef, const GffStruct &dlg) {
|
||||
for (auto &entry : dlg.getList("EntryList")) {
|
||||
_entries.push_back(getEntryReply(entry));
|
||||
}
|
||||
for (auto &reply : dlg.getList("ReplyList")) {
|
||||
_replies.push_back(getEntryReply(reply));
|
||||
}
|
||||
for (auto &entry : dlg.getList("StartingList")) {
|
||||
_startEntries.push_back(getEntryReplyLink(entry));
|
||||
}
|
||||
}
|
||||
|
||||
Dialog::EntryReplyLink Dialog::getEntryReplyLink(const GffStruct &gffs) const {
|
||||
EntryReplyLink link;
|
||||
link.index = gffs.getInt("Index");
|
||||
link.active = gffs.getString("Active");
|
||||
|
||||
return move(link);
|
||||
}
|
||||
|
||||
Dialog::EntryReply Dialog::getEntryReply(const GffStruct &gffs) const {
|
||||
int strRef = gffs.getInt("Text");
|
||||
|
||||
EntryReply entry;
|
||||
entry.speaker = gffs.getString("Speaker");
|
||||
entry.text = strRef == -1 ? "" : ResMan.getString(strRef).text;
|
||||
entry.voResRef = gffs.getString("VO_ResRef");
|
||||
entry.script = gffs.getString("Script");
|
||||
entry.sound = gffs.getString("Sound");
|
||||
entry.listener = gffs.getString("Listener");
|
||||
entry.cameraAngle = gffs.getInt("CameraAngle");
|
||||
|
||||
const GffField *repliesList = gffs.find("RepliesList");
|
||||
if (repliesList) {
|
||||
for (auto &link : repliesList->children()) {
|
||||
entry.replies.push_back(getEntryReplyLink(link));
|
||||
}
|
||||
}
|
||||
const GffField *entriesList = gffs.find("EntriesList");
|
||||
if (entriesList) {
|
||||
for (auto &link : entriesList->children()) {
|
||||
entry.entries.push_back(getEntryReplyLink(link));
|
||||
}
|
||||
}
|
||||
|
||||
return move(entry);
|
||||
}
|
||||
|
||||
const vector<Dialog::EntryReplyLink> &Dialog::startEntries() const {
|
||||
return _startEntries;
|
||||
}
|
||||
|
||||
const Dialog::EntryReply &Dialog::getEntry(int index) const {
|
||||
assert(index >= 0 && index < _entries.size());
|
||||
return _entries[index];
|
||||
}
|
||||
|
||||
const Dialog::EntryReply &Dialog::getReply(int index) const {
|
||||
assert(index >= 0 && index < _replies.size());
|
||||
return _replies[index];
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../resources/gfffile.h"
|
||||
|
||||
|
@ -27,13 +28,43 @@ namespace game {
|
|||
|
||||
class Dialog {
|
||||
public:
|
||||
struct EntryReplyLink {
|
||||
int index { 0 };
|
||||
std::string active;
|
||||
};
|
||||
|
||||
struct EntryReply {
|
||||
std::string speaker;
|
||||
std::string text;
|
||||
std::string voResRef;
|
||||
std::string script;
|
||||
std::string sound;
|
||||
std::string listener;
|
||||
int cameraAngle { 0 };
|
||||
std::vector<EntryReplyLink> replies;
|
||||
std::vector<EntryReplyLink> entries;
|
||||
};
|
||||
|
||||
Dialog() = default;
|
||||
|
||||
void reset();
|
||||
void load(const std::string &resRef, const resources::GffStruct &dlg);
|
||||
|
||||
const std::vector<EntryReplyLink> &startEntries() const;
|
||||
const EntryReply &getEntry(int index) const;
|
||||
const EntryReply &getReply(int index) const;
|
||||
|
||||
private:
|
||||
std::vector<EntryReplyLink> _startEntries;
|
||||
std::vector<EntryReply> _entries;
|
||||
std::vector<EntryReply> _replies;
|
||||
int _entryIndex { -1 };
|
||||
|
||||
Dialog(const Dialog &) = delete;
|
||||
Dialog &operator=(const Dialog &) = delete;
|
||||
|
||||
EntryReplyLink getEntryReplyLink(const resources::GffStruct &gffs) const;
|
||||
EntryReply getEntryReply(const resources::GffStruct &gffs) const;
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
|
|
|
@ -143,6 +143,9 @@ void Game::loadModule(const string &name, string entry) {
|
|||
unique_ptr<DialogGui> dialog(new DialogGui(_opts.graphics));
|
||||
dialog->load(_version);
|
||||
dialog->initGL();
|
||||
dialog->setOnDialogFinished([this]() {
|
||||
_screen = Screen::InGame;
|
||||
});
|
||||
_dialog = move(dialog);
|
||||
}
|
||||
|
||||
|
@ -164,6 +167,7 @@ void Game::configureModule() {
|
|||
});
|
||||
_module->setStartConversation([this](const string &name) {
|
||||
_screen = Screen::Dialog;
|
||||
_dialog->startDialog(name);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -17,15 +17,22 @@
|
|||
|
||||
#include "dialog.h"
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#include "../../audio/player.h"
|
||||
#include "../../gui/control/listbox.h"
|
||||
#include "../../gui/control/panel.h"
|
||||
#include "../../resources/manager.h"
|
||||
#include "../../script/execution.h"
|
||||
#include "../../script/manager.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
using namespace reone::audio;
|
||||
using namespace reone::gui;
|
||||
using namespace reone::render;
|
||||
using namespace reone::resources;
|
||||
using namespace reone::script;
|
||||
|
||||
namespace reone {
|
||||
|
||||
|
@ -94,7 +101,6 @@ void DialogGui::configureMessage() {
|
|||
extent.top = -_rootControl->extent().top;
|
||||
|
||||
Control::Text text(message.text());
|
||||
text.text = "Hello, world!";
|
||||
text.color = _version == GameVersion::KotOR ? g_kotorBaseColor : g_tslBaseColor;
|
||||
|
||||
message.setExtent(move(extent));
|
||||
|
@ -114,16 +120,110 @@ void DialogGui::configureReplies() {
|
|||
protoItem.setHilight(move(hilight));
|
||||
protoItem.setText(move(text));
|
||||
|
||||
replies.add(ListBox::Item { "", "1. Reply 1" });
|
||||
replies.add(ListBox::Item { "", "2. Reply 2" });
|
||||
replies.add(ListBox::Item { "", "3. Reply 3" });
|
||||
replies.add(ListBox::Item { "", "4. Reply 4" });
|
||||
replies.add(ListBox::Item { "", "5. Reply 5" });
|
||||
replies.add(ListBox::Item { "", "6. Reply 6" });
|
||||
replies.add(ListBox::Item { "", "7. Reply 7" });
|
||||
replies.add(ListBox::Item { "", "8. Reply 8" });
|
||||
replies.add(ListBox::Item { "", "9. Reply 9" });
|
||||
replies.add(ListBox::Item { "", "10. Reply 10" });
|
||||
replies.setOnItemClicked([this](const string &ctrl, const string &item) {
|
||||
int replyIdx = stoi(item);
|
||||
onReplyClicked(replyIdx);
|
||||
});
|
||||
}
|
||||
|
||||
void DialogGui::onReplyClicked(int index) {
|
||||
const Dialog::EntryReply &reply = _dialog->getReply(index);
|
||||
if (reply.entries.empty()) {
|
||||
if (_onDialogFinished) _onDialogFinished();
|
||||
return;
|
||||
}
|
||||
int entryIdx = -1;
|
||||
|
||||
for (auto &link : reply.entries) {
|
||||
if (link.active.empty()) {
|
||||
entryIdx = link.index;
|
||||
continue;
|
||||
}
|
||||
if (checkCondition(link.active)) {
|
||||
entryIdx = link.index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (entryIdx != -1) {
|
||||
_currentEntry.reset(new Dialog::EntryReply(_dialog->getEntry(entryIdx)));
|
||||
loadCurrentEntry();
|
||||
}
|
||||
}
|
||||
|
||||
void DialogGui::startDialog(const string &resRef) {
|
||||
shared_ptr<GffStruct> dlg(ResMan.findGFF(resRef, ResourceType::Conversation));
|
||||
if (!dlg) {
|
||||
if (_onDialogFinished) _onDialogFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
_dialog.reset(new Dialog());
|
||||
_dialog->load(resRef, *dlg);
|
||||
|
||||
loadStartEntry();
|
||||
}
|
||||
|
||||
void DialogGui::loadStartEntry() {
|
||||
int entryIdx = -1;
|
||||
for (auto &link : _dialog->startEntries()) {
|
||||
if (link.active.empty()) {
|
||||
entryIdx = link.index;
|
||||
continue;
|
||||
}
|
||||
if (checkCondition(link.active)) {
|
||||
entryIdx = link.index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (entryIdx != -1) {
|
||||
_currentEntry.reset(new Dialog::EntryReply(_dialog->getEntry(entryIdx)));
|
||||
loadCurrentEntry();
|
||||
}
|
||||
}
|
||||
|
||||
bool DialogGui::checkCondition(const string &script) {
|
||||
shared_ptr<ScriptProgram> program(ScriptMan.find(script));
|
||||
return ScriptExecution(program, ExecutionContext()).run() != 0;
|
||||
}
|
||||
|
||||
void DialogGui::loadCurrentEntry() {
|
||||
if (_currentVoice) _currentVoice->stop();
|
||||
|
||||
assert(_currentEntry);
|
||||
if (!_currentEntry->voResRef.empty()) {
|
||||
shared_ptr<AudioStream> voice(ResMan.findAudio(_currentEntry->voResRef));
|
||||
if (voice) {
|
||||
_currentVoice = TheAudioPlayer.play(voice);
|
||||
}
|
||||
}
|
||||
|
||||
Control &message = getControl("LBL_MESSAGE");
|
||||
message.setTextMessage(_currentEntry->text);
|
||||
|
||||
ListBox &replies = static_cast<ListBox &>(getControl("LB_REPLIES"));
|
||||
replies.clearItems();
|
||||
|
||||
int replyCount = 0;
|
||||
for (auto &link : _currentEntry->replies) {
|
||||
if (!link.active.empty() && !checkCondition(link.active)) continue;
|
||||
|
||||
string text(_dialog->getReply(link.index).text);
|
||||
if (text.empty()) text = "[continue]";
|
||||
|
||||
replies.add({ to_string(link.index), str(boost::format("%d. %s") % ++replyCount % text) });
|
||||
}
|
||||
|
||||
if (!_currentEntry->script.empty()) {
|
||||
ScriptExecution(ScriptMan.find(_currentEntry->script), ExecutionContext()).run();
|
||||
}
|
||||
if (replyCount == 0 && _onDialogFinished) {
|
||||
_onDialogFinished();
|
||||
}
|
||||
}
|
||||
|
||||
void DialogGui::setOnDialogFinished(const std::function<void()> &fn) {
|
||||
_onDialogFinished = fn;
|
||||
}
|
||||
|
||||
} // namespace game
|
||||
|
|
|
@ -17,9 +17,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "../../audio/soundinstance.h"
|
||||
#include "../../gui/gui.h"
|
||||
#include "../../resources/types.h"
|
||||
|
||||
#include "../dialog.h"
|
||||
|
||||
namespace reone {
|
||||
|
||||
namespace game {
|
||||
|
@ -29,15 +32,26 @@ public:
|
|||
DialogGui(const render::GraphicsOptions &opts);
|
||||
|
||||
void load(resources::GameVersion version);
|
||||
void startDialog(const std::string &resRef);
|
||||
|
||||
void setOnDialogFinished(const std::function<void()> &fn);
|
||||
|
||||
private:
|
||||
resources::GameVersion _version { resources::GameVersion::KotOR };
|
||||
std::shared_ptr<Dialog> _dialog;
|
||||
std::shared_ptr<Dialog::EntryReply> _currentEntry;
|
||||
std::shared_ptr<audio::SoundInstance> _currentVoice;
|
||||
std::function<void()> _onDialogFinished;
|
||||
|
||||
void addTopFrame();
|
||||
void addBottomFrame();
|
||||
void addFrame(int top, int height);
|
||||
void configureMessage();
|
||||
void configureReplies();
|
||||
void onReplyClicked(int index);
|
||||
void loadStartEntry();
|
||||
bool checkCondition(const std::string &script);
|
||||
void loadCurrentEntry();
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
|
|
|
@ -394,6 +394,10 @@ void Control::setText(const Text &text) {
|
|||
_text = text;
|
||||
}
|
||||
|
||||
void Control::setTextMessage(const string &text) {
|
||||
_text.text = text;
|
||||
}
|
||||
|
||||
const string &Control::tag() const {
|
||||
return _tag;
|
||||
}
|
||||
|
|
|
@ -85,6 +85,7 @@ public:
|
|||
void setBorder(const Border &border);
|
||||
void setHilight(const Border &hilight);
|
||||
void setText(const Text &text);
|
||||
void setTextMessage(const std::string &text);
|
||||
|
||||
const std::string &tag() const;
|
||||
const Extent &extent() const;
|
||||
|
|
|
@ -73,6 +73,12 @@ void ListBox::updateItems() {
|
|||
}
|
||||
}
|
||||
|
||||
void ListBox::clearItems() {
|
||||
_items.clear();
|
||||
_itemOffset = 0;
|
||||
updateItems();
|
||||
}
|
||||
|
||||
void ListBox::add(const Item &item) {
|
||||
_items.push_back(item);
|
||||
updateItems();
|
||||
|
@ -106,7 +112,7 @@ int ListBox::getItemIndex(int y) const {
|
|||
|
||||
bool ListBox::handleMouseWheel(int x, int y) {
|
||||
if (y < 0) {
|
||||
if (_itemOffset < _items.size() - _slotCount) _itemOffset++;
|
||||
if (_items.size() - _itemOffset > _slotCount) _itemOffset++;
|
||||
return true;
|
||||
} else if (y > 0) {
|
||||
if (_itemOffset > 0) _itemOffset--;
|
||||
|
@ -145,8 +151,10 @@ void ListBox::render(const glm::mat4 &transform, const std::string &textOverride
|
|||
const Control::Extent &protoExtent = _protoItem->extent();
|
||||
glm::mat4 itemTransform(glm::translate(transform, glm::vec3(_extent.left, _extent.top - protoExtent.top, 0.0f)));
|
||||
|
||||
for (int i = 0; i < _items.size() && i < _slotCount; ++i) {
|
||||
for (int i = 0; i < _slotCount; ++i) {
|
||||
int itemIdx = i + _itemOffset;
|
||||
if (itemIdx >= _items.size()) break;
|
||||
|
||||
_protoItem->setFocus(_hilightedIndex == itemIdx);
|
||||
_protoItem->render(itemTransform, _items[itemIdx].text);
|
||||
itemTransform = glm::translate(itemTransform, glm::vec3(0.0f, protoExtent.height + _padding, 0.0f));
|
||||
|
@ -155,7 +163,7 @@ void ListBox::render(const glm::mat4 &transform, const std::string &textOverride
|
|||
if (_scrollBar) {
|
||||
ScrollBar &scrollBar = static_cast<ScrollBar &>(*_scrollBar);
|
||||
scrollBar.setCanScrollUp(_itemOffset > 0);
|
||||
scrollBar.setCanScrollDown(_itemOffset + _slotCount < _items.size());
|
||||
scrollBar.setCanScrollDown(_items.size() - _itemOffset > _slotCount);
|
||||
scrollBar.render(transform, "");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ public:
|
|||
ListBox(const std::string &tag);
|
||||
|
||||
void loadCustom();
|
||||
void clearItems();
|
||||
void add(const Item &item);
|
||||
|
||||
void load(const resources::GffStruct &gffs) override;
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
|
||||
#include "util.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
namespace reone {
|
||||
|
@ -29,24 +31,30 @@ namespace resources {
|
|||
|
||||
void Folder::load(const fs::path &path) {
|
||||
if (!fs::is_directory(path)) {
|
||||
throw std::runtime_error("Folder not found: " + path.string());
|
||||
throw runtime_error("Folder not found: " + path.string());
|
||||
}
|
||||
loadDirectory(path);
|
||||
_path = path;
|
||||
}
|
||||
|
||||
void Folder::loadDirectory(const fs::path &path) {
|
||||
for (auto &entry : fs::directory_iterator(path)) {
|
||||
const fs::path &path2 = entry.path();
|
||||
if (fs::is_directory(path2)) continue;
|
||||
const fs::path &childPath = entry.path();
|
||||
if (fs::is_directory(childPath)) {
|
||||
loadDirectory(childPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string resRef(path2.filename().replace_extension("").string());
|
||||
string resRef(childPath.filename().replace_extension("").string());
|
||||
boost::to_lower(resRef);
|
||||
|
||||
std::string ext(path2.extension().string().substr(1));
|
||||
string ext(childPath.extension().string().substr(1));
|
||||
|
||||
Resource res;
|
||||
res.path = path2;
|
||||
res.path = childPath;
|
||||
res.type = getResTypeByExt(ext);
|
||||
|
||||
_resources.insert(std::make_pair(resRef, res));
|
||||
_resources.insert(make_pair(resRef, res));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,7 +62,7 @@ bool Folder::supports(ResourceType type) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<ByteArray> Folder::find(const std::string &resRef, ResourceType type) {
|
||||
shared_ptr<ByteArray> Folder::find(const string &resRef, ResourceType type) {
|
||||
fs::path path;
|
||||
for (auto &res : _resources) {
|
||||
if (res.first == resRef && res.second.type == type) {
|
||||
|
@ -63,18 +71,18 @@ std::shared_ptr<ByteArray> Folder::find(const std::string &resRef, ResourceType
|
|||
}
|
||||
}
|
||||
if (path.empty()) {
|
||||
return std::shared_ptr<ByteArray>();
|
||||
return shared_ptr<ByteArray>();
|
||||
}
|
||||
fs::ifstream in(path, std::ios::binary);
|
||||
fs::ifstream in(path, ios::binary);
|
||||
|
||||
in.seekg(0, std::ios::end);
|
||||
in.seekg(0, ios::end);
|
||||
size_t size = in.tellg();
|
||||
|
||||
in.seekg(std::ios::beg);
|
||||
in.seekg(ios::beg);
|
||||
ByteArray data(size);
|
||||
in.read(&data[0], size);
|
||||
|
||||
return std::make_shared<ByteArray>(std::move(data));
|
||||
return make_shared<ByteArray>(move(data));
|
||||
}
|
||||
|
||||
} // namespace resources
|
||||
|
|
|
@ -48,6 +48,8 @@ private:
|
|||
|
||||
Folder(const Folder &) = delete;
|
||||
Folder &operator=(const Folder &) = delete;
|
||||
|
||||
void loadDirectory(const boost::filesystem::path &path);
|
||||
};
|
||||
|
||||
} // namespace resources
|
||||
|
|
|
@ -55,6 +55,9 @@ static const char kTexturePackDirectoryName[] = "texturepacks";
|
|||
static const char kTexturePackFilename[] = "swpc_tex_tpa.erf";
|
||||
static const char kGUITexturePackFilename[] = "swpc_tex_gui.erf";
|
||||
static const char kMusicDirectoryName[] = "streammusic";
|
||||
static const char kSoundsDirectoryName[] = "streamsounds";
|
||||
static const char kWavesDirectoryName[] = "streamwaves";
|
||||
static const char kVoiceDirectoryName[] = "streamvoice";
|
||||
|
||||
static map<string, shared_ptr<ByteArray>> g_resCache;
|
||||
static map<string, shared_ptr<TwoDaTable>> g_2daCache;
|
||||
|
@ -94,10 +97,25 @@ void ResourceManager::init(GameVersion version, const boost::filesystem::path &g
|
|||
fs::path texPackPath(getPathIgnoreCase(texPacksPath, kTexturePackFilename));
|
||||
fs::path guiTexPackPath(getPathIgnoreCase(texPacksPath, kGUITexturePackFilename));
|
||||
fs::path musicPath(getPathIgnoreCase(gamePath, kMusicDirectoryName));
|
||||
fs::path soundsPath(getPathIgnoreCase(gamePath, kSoundsDirectoryName));
|
||||
|
||||
addErfProvider(texPackPath);
|
||||
addErfProvider(guiTexPackPath);
|
||||
addFolderProvider(musicPath);
|
||||
addFolderProvider(soundsPath);
|
||||
|
||||
switch (version) {
|
||||
case GameVersion::KotOR: {
|
||||
fs::path wavesPath(getPathIgnoreCase(gamePath, kWavesDirectoryName));
|
||||
addFolderProvider(wavesPath);
|
||||
break;
|
||||
}
|
||||
case GameVersion::TheSithLords: {
|
||||
fs::path voicePath(getPathIgnoreCase(gamePath, kVoiceDirectoryName));
|
||||
addFolderProvider(voicePath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_version = version;
|
||||
_gamePath = gamePath;
|
||||
|
@ -143,6 +161,11 @@ void ResourceManager::loadModule(const string &name) {
|
|||
|
||||
addTransientRimProvider(rimPath);
|
||||
addTransientRimProvider(rimsPath);
|
||||
|
||||
if (_version == GameVersion::TheSithLords) {
|
||||
fs::path dlgPath(getPathIgnoreCase(modulesPath, name + "_dlg.erf"));
|
||||
addTransientErfProvider(dlgPath);
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceManager::addTransientRimProvider(const fs::path &path) {
|
||||
|
@ -151,6 +174,12 @@ void ResourceManager::addTransientRimProvider(const fs::path &path) {
|
|||
_transientProviders.push_back(move(rim));
|
||||
}
|
||||
|
||||
void ResourceManager::addTransientErfProvider(const fs::path &path) {
|
||||
unique_ptr<ErfFile> erf(new ErfFile());
|
||||
erf->load(path);
|
||||
_transientProviders.push_back(move(erf));
|
||||
}
|
||||
|
||||
void ResourceManager::addFolderProvider(const fs::path &path) {
|
||||
unique_ptr<Folder> folder(new Folder());
|
||||
folder->load(path);
|
||||
|
|
|
@ -78,6 +78,7 @@ private:
|
|||
|
||||
void addErfProvider(const boost::filesystem::path &path);
|
||||
void addTransientRimProvider(const boost::filesystem::path &path);
|
||||
void addTransientErfProvider(const boost::filesystem::path &path);
|
||||
void addFolderProvider(const boost::filesystem::path &path);
|
||||
void initModuleNames();
|
||||
inline std::string getCacheKey(const std::string &resRef, ResourceType type) const;
|
||||
|
|
|
@ -85,11 +85,12 @@ ScriptExecution::ScriptExecution(const std::shared_ptr<ScriptProgram> &program,
|
|||
|
||||
int ScriptExecution::run() {
|
||||
uint32_t insOff = kStartInstructionOffset;
|
||||
_stack.push_back(Variable(0));
|
||||
|
||||
if (_context.savedState) {
|
||||
vector<Variable> globals(_context.savedState->globals);
|
||||
copy(globals.begin(), globals.end(), back_inserter(_stack));
|
||||
_globalCount = globals.size();
|
||||
_globalCount = _stack.size();
|
||||
|
||||
vector<Variable> locals(_context.savedState->locals);
|
||||
copy(locals.begin(), locals.end(), back_inserter(_stack));
|
||||
|
@ -112,7 +113,9 @@ int ScriptExecution::run() {
|
|||
insOff = _nextInstruction;
|
||||
}
|
||||
|
||||
return 0;
|
||||
assert(_stack.front().type == VariableType::Int);
|
||||
|
||||
return _stack.front().intValue;
|
||||
}
|
||||
|
||||
void ScriptExecution::executeCopyDownSP(const Instruction &ins) {
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace script {
|
|||
#define Action VariableType::Action
|
||||
|
||||
void RoutineManager::addKotorRoutines() {
|
||||
_routines.emplace_back("Random", Int, vector<VariableType>());
|
||||
_routines.emplace_back("Random", Int, vector<VariableType> { Int });
|
||||
_routines.emplace_back("PrintString", Void, vector<VariableType> { String });
|
||||
_routines.emplace_back("PrintFloat", Void, vector<VariableType> { Float, Int, Int });
|
||||
_routines.emplace_back("FloatToString", String, vector<VariableType> { Float, Int, Int });
|
||||
|
|
|
@ -43,8 +43,11 @@ Variable Variable::operator+(const Variable &other) const {
|
|||
if (type == VariableType::Float && other.type == VariableType::Float) {
|
||||
return floatValue + other.floatValue;
|
||||
}
|
||||
if (type == VariableType::String && other.type == VariableType::String) {
|
||||
return strValue + other.strValue;
|
||||
}
|
||||
|
||||
throw logic_error(str(boost::format("Unsupported variable types: %02x %02x") % static_cast<int>(type) % static_cast<int>(other.type)));
|
||||
throw logic_error(str(boost::format("Unsupported variable types: %02x %02x") % static_cast<int>(type) % static_cast<int>(other.type)));
|
||||
}
|
||||
|
||||
Variable Variable::operator-(const Variable &other) const {
|
||||
|
|
Loading…
Reference in a new issue