mirror of
https://github.com/OpenMW/openmw.git
synced 2025-06-20 07:11:36 +00:00
Merge branch 'mwdialogue-bindings' into 'master'
Add Lua bindings for ESM::Dialogue record stores to openmw.core Closes #7964 See merge request OpenMW/openmw!4034
This commit is contained in:
commit
0e1678b3b8
8 changed files with 629 additions and 8 deletions
|
@ -229,6 +229,7 @@ Programmers
|
||||||
thegriglat
|
thegriglat
|
||||||
Thomas Luppi (Digmaster)
|
Thomas Luppi (Digmaster)
|
||||||
tlmullis
|
tlmullis
|
||||||
|
trav
|
||||||
tri4ng1e
|
tri4ng1e
|
||||||
Thoronador
|
Thoronador
|
||||||
Tobias Tribble (zackhasacat)
|
Tobias Tribble (zackhasacat)
|
||||||
|
|
|
@ -232,6 +232,7 @@
|
||||||
Feature #7932: Support two-channel normal maps
|
Feature #7932: Support two-channel normal maps
|
||||||
Feature #7936: Scalable icons in Qt applications
|
Feature #7936: Scalable icons in Qt applications
|
||||||
Feature #7953: Allow to change SVG icons colors depending on color scheme
|
Feature #7953: Allow to change SVG icons colors depending on color scheme
|
||||||
|
Feature #7964: Add Lua read access to MW Dialogue records
|
||||||
Task #5896: Do not use deprecated MyGUI properties
|
Task #5896: Do not use deprecated MyGUI properties
|
||||||
Task #6085: Replace boost::filesystem with std::filesystem
|
Task #6085: Replace boost::filesystem with std::filesystem
|
||||||
Task #6149: Dehardcode Lua API_REVISION
|
Task #6149: Dehardcode Lua API_REVISION
|
||||||
|
|
|
@ -81,7 +81,7 @@ message(STATUS "Configuring OpenMW...")
|
||||||
set(OPENMW_VERSION_MAJOR 0)
|
set(OPENMW_VERSION_MAJOR 0)
|
||||||
set(OPENMW_VERSION_MINOR 49)
|
set(OPENMW_VERSION_MINOR 49)
|
||||||
set(OPENMW_VERSION_RELEASE 0)
|
set(OPENMW_VERSION_RELEASE 0)
|
||||||
set(OPENMW_LUA_API_REVISION 61)
|
set(OPENMW_LUA_API_REVISION 62)
|
||||||
set(OPENMW_POSTPROCESSING_API_REVISION 1)
|
set(OPENMW_POSTPROCESSING_API_REVISION 1)
|
||||||
|
|
||||||
set(OPENMW_VERSION_COMMITHASH "")
|
set(OPENMW_VERSION_COMMITHASH "")
|
||||||
|
|
|
@ -62,7 +62,7 @@ add_openmw_dir (mwscript
|
||||||
add_openmw_dir (mwlua
|
add_openmw_dir (mwlua
|
||||||
luamanagerimp object objectlists userdataserializer luaevents engineevents objectvariant
|
luamanagerimp object objectlists userdataserializer luaevents engineevents objectvariant
|
||||||
context menuscripts globalscripts localscripts playerscripts luabindings objectbindings cellbindings
|
context menuscripts globalscripts localscripts playerscripts luabindings objectbindings cellbindings
|
||||||
mwscriptbindings camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings
|
mwscriptbindings camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings dialoguebindings
|
||||||
postprocessingbindings stats recordstore debugbindings corebindings worldbindings worker magicbindings factionbindings
|
postprocessingbindings stats recordstore debugbindings corebindings worldbindings worker magicbindings factionbindings
|
||||||
classbindings itemdata inputprocessor animationbindings birthsignbindings racebindings markupbindings
|
classbindings itemdata inputprocessor animationbindings birthsignbindings racebindings markupbindings
|
||||||
types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc
|
types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "../mwworld/esmstore.hpp"
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
|
||||||
#include "animationbindings.hpp"
|
#include "animationbindings.hpp"
|
||||||
|
#include "dialoguebindings.hpp"
|
||||||
#include "factionbindings.hpp"
|
#include "factionbindings.hpp"
|
||||||
#include "luaevents.hpp"
|
#include "luaevents.hpp"
|
||||||
#include "magicbindings.hpp"
|
#include "magicbindings.hpp"
|
||||||
|
@ -98,7 +99,7 @@ namespace MWLua
|
||||||
api["stats"] = initCoreStatsBindings(context);
|
api["stats"] = initCoreStatsBindings(context);
|
||||||
|
|
||||||
api["factions"] = initCoreFactionBindings(context);
|
api["factions"] = initCoreFactionBindings(context);
|
||||||
|
api["dialogue"] = initCoreDialogueBindings(context);
|
||||||
api["l10n"] = LuaUtil::initL10nLoader(lua->sol(), MWBase::Environment::get().getL10nManager());
|
api["l10n"] = LuaUtil::initL10nLoader(lua->sol(), MWBase::Environment::get().getL10nManager());
|
||||||
const MWWorld::Store<ESM::GameSetting>* gmstStore
|
const MWWorld::Store<ESM::GameSetting>* gmstStore
|
||||||
= &MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
|
= &MWBase::Environment::get().getESMStore()->get<ESM::GameSetting>();
|
||||||
|
|
419
apps/openmw/mwlua/dialoguebindings.cpp
Normal file
419
apps/openmw/mwlua/dialoguebindings.cpp
Normal file
|
@ -0,0 +1,419 @@
|
||||||
|
#include "dialoguebindings.hpp"
|
||||||
|
#include "apps/openmw/mwbase/environment.hpp"
|
||||||
|
#include "apps/openmw/mwworld/esmstore.hpp"
|
||||||
|
#include "apps/openmw/mwworld/store.hpp"
|
||||||
|
#include "context.hpp"
|
||||||
|
#include "object.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <components/esm3/loaddial.hpp>
|
||||||
|
#include <components/lua/luastate.hpp>
|
||||||
|
#include <components/misc/resourcehelpers.hpp>
|
||||||
|
#include <components/vfs/pathutil.hpp>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
template <ESM::Dialogue::Type filter>
|
||||||
|
class FilteredDialogueStore
|
||||||
|
{
|
||||||
|
const MWWorld::Store<ESM::Dialogue>& mDialogueStore;
|
||||||
|
|
||||||
|
const ESM::Dialogue* foundDialogueFilteredOut(const ESM::Dialogue* possibleResult) const
|
||||||
|
{
|
||||||
|
if (possibleResult && possibleResult->mType == filter)
|
||||||
|
{
|
||||||
|
return possibleResult;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
FilteredDialogueStore()
|
||||||
|
: mDialogueStore{ MWBase::Environment::get().getESMStore()->get<ESM::Dialogue>() }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
class FilteredDialogueIterator
|
||||||
|
{
|
||||||
|
using DecoratedIterator = MWWorld::Store<ESM::Dialogue>::iterator;
|
||||||
|
DecoratedIterator mIter;
|
||||||
|
DecoratedIterator mEndIter;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using iterator_category = DecoratedIterator::iterator_category;
|
||||||
|
using value_type = DecoratedIterator::value_type;
|
||||||
|
using difference_type = DecoratedIterator::difference_type;
|
||||||
|
using pointer = DecoratedIterator::pointer;
|
||||||
|
using reference = DecoratedIterator::reference;
|
||||||
|
|
||||||
|
FilteredDialogueIterator(const DecoratedIterator& pointingIterator, const DecoratedIterator& end)
|
||||||
|
: mIter{ pointingIterator }
|
||||||
|
, mEndIter{ end }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FilteredDialogueIterator& operator++()
|
||||||
|
{
|
||||||
|
if (mIter == mEndIter)
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
++mIter;
|
||||||
|
} while (mIter != mEndIter && mIter->mType != filter);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
FilteredDialogueIterator operator++(int)
|
||||||
|
{
|
||||||
|
FilteredDialogueIterator iter = *this;
|
||||||
|
++(*this);
|
||||||
|
return iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
FilteredDialogueIterator& operator+=(difference_type advance)
|
||||||
|
{
|
||||||
|
while (advance > 0 && mIter != mEndIter)
|
||||||
|
{
|
||||||
|
++(*this);
|
||||||
|
--advance;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const FilteredDialogueIterator& x) const { return mIter == x.mIter; }
|
||||||
|
|
||||||
|
bool operator!=(const FilteredDialogueIterator& x) const { return !(*this == x); }
|
||||||
|
|
||||||
|
const value_type& operator*() const { return *mIter; }
|
||||||
|
|
||||||
|
const value_type* operator->() const { return &(*mIter); }
|
||||||
|
};
|
||||||
|
|
||||||
|
using iterator = FilteredDialogueIterator;
|
||||||
|
|
||||||
|
const ESM::Dialogue* search(const ESM::RefId& id) const
|
||||||
|
{
|
||||||
|
return foundDialogueFilteredOut(mDialogueStore.search(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
const ESM::Dialogue* at(size_t index) const
|
||||||
|
{
|
||||||
|
auto result = begin();
|
||||||
|
result += index;
|
||||||
|
|
||||||
|
if (result == end())
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &(*result);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t getSize() const
|
||||||
|
{
|
||||||
|
return std::count_if(
|
||||||
|
mDialogueStore.begin(), mDialogueStore.end(), [](const auto& d) { return d.mType == filter; });
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator begin() const
|
||||||
|
{
|
||||||
|
iterator result{ mDialogueStore.begin(), mDialogueStore.end() };
|
||||||
|
while (result != end() && result->mType != filter)
|
||||||
|
{
|
||||||
|
++result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator end() const { return iterator{ mDialogueStore.end(), mDialogueStore.end() }; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <ESM::Dialogue::Type filter>
|
||||||
|
void prepareBindingsForDialogueRecordStores(sol::table& table, const MWLua::Context& context)
|
||||||
|
{
|
||||||
|
using StoreT = FilteredDialogueStore<filter>;
|
||||||
|
|
||||||
|
sol::state_view& lua = context.mLua->sol();
|
||||||
|
sol::usertype<StoreT> storeBindingsClass
|
||||||
|
= lua.new_usertype<StoreT>("ESM3_Dialogue_Type" + std::to_string(filter) + " Store");
|
||||||
|
storeBindingsClass[sol::meta_function::to_string] = [](const StoreT& store) {
|
||||||
|
return "{" + std::to_string(store.getSize()) + " ESM3_Dialogue_Type" + std::to_string(filter) + " records}";
|
||||||
|
};
|
||||||
|
storeBindingsClass[sol::meta_function::length] = [](const StoreT& store) { return store.getSize(); };
|
||||||
|
storeBindingsClass[sol::meta_function::index] = sol::overload(
|
||||||
|
[](const StoreT& store, size_t index) -> const ESM::Dialogue* {
|
||||||
|
if (index == 0)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return store.at(index - 1);
|
||||||
|
},
|
||||||
|
[](const StoreT& store, std::string_view id) -> const ESM::Dialogue* {
|
||||||
|
return store.search(ESM::RefId::deserializeText(id));
|
||||||
|
});
|
||||||
|
storeBindingsClass[sol::meta_function::ipairs] = lua["ipairsForArray"].template get<sol::function>();
|
||||||
|
storeBindingsClass[sol::meta_function::pairs] = lua["ipairsForArray"].template get<sol::function>();
|
||||||
|
|
||||||
|
table["records"] = StoreT{};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DialogueInfos
|
||||||
|
{
|
||||||
|
const ESM::Dialogue& parentDialogueRecord;
|
||||||
|
};
|
||||||
|
|
||||||
|
void prepareBindingsForDialogueRecord(sol::state_view& lua)
|
||||||
|
{
|
||||||
|
auto recordBindingsClass = lua.new_usertype<ESM::Dialogue>("ESM3_Dialogue");
|
||||||
|
recordBindingsClass[sol::meta_function::to_string]
|
||||||
|
= [](const ESM::Dialogue& rec) { return "ESM3_Dialogue[" + rec.mId.toDebugString() + "]"; };
|
||||||
|
recordBindingsClass["id"]
|
||||||
|
= sol::readonly_property([](const ESM::Dialogue& rec) { return rec.mId.serializeText(); });
|
||||||
|
recordBindingsClass["name"]
|
||||||
|
= sol::readonly_property([](const ESM::Dialogue& rec) -> std::string_view { return rec.mStringId; });
|
||||||
|
recordBindingsClass["questName"]
|
||||||
|
= sol::readonly_property([](const ESM::Dialogue& rec) -> sol::optional<std::string_view> {
|
||||||
|
if (rec.mType != ESM::Dialogue::Type::Journal)
|
||||||
|
{
|
||||||
|
return sol::nullopt;
|
||||||
|
}
|
||||||
|
for (const auto& mwDialogueInfo : rec.mInfo)
|
||||||
|
{
|
||||||
|
if (mwDialogueInfo.mQuestStatus == ESM::DialInfo::QuestStatus::QS_Name)
|
||||||
|
{
|
||||||
|
return sol::optional<std::string_view>(mwDialogueInfo.mResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sol::nullopt;
|
||||||
|
});
|
||||||
|
recordBindingsClass["infos"]
|
||||||
|
= sol::readonly_property([](const ESM::Dialogue& rec) { return DialogueInfos{ rec }; });
|
||||||
|
}
|
||||||
|
|
||||||
|
void prepareBindingsForDialogueRecordInfoList(sol::state_view& lua)
|
||||||
|
{
|
||||||
|
auto recordInfosBindingsClass = lua.new_usertype<DialogueInfos>("ESM3_Dialogue_Infos");
|
||||||
|
recordInfosBindingsClass[sol::meta_function::to_string] = [](const DialogueInfos& store) {
|
||||||
|
const ESM::Dialogue& dialogueRecord = store.parentDialogueRecord;
|
||||||
|
return "{" + std::to_string(dialogueRecord.mInfo.size()) + " ESM3_Dialogue["
|
||||||
|
+ dialogueRecord.mId.toDebugString() + "] info elements}";
|
||||||
|
};
|
||||||
|
recordInfosBindingsClass[sol::meta_function::length]
|
||||||
|
= [](const DialogueInfos& store) { return store.parentDialogueRecord.mInfo.size(); };
|
||||||
|
recordInfosBindingsClass[sol::meta_function::index]
|
||||||
|
= [](const DialogueInfos& store, size_t index) -> const ESM::DialInfo* {
|
||||||
|
const ESM::Dialogue& dialogueRecord = store.parentDialogueRecord;
|
||||||
|
if (index == 0 || index > dialogueRecord.mInfo.size())
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
ESM::Dialogue::InfoContainer::const_iterator iter{ dialogueRecord.mInfo.cbegin() };
|
||||||
|
std::advance(iter, index - 1);
|
||||||
|
return &(*iter);
|
||||||
|
};
|
||||||
|
recordInfosBindingsClass[sol::meta_function::ipairs] = lua["ipairsForArray"].template get<sol::function>();
|
||||||
|
recordInfosBindingsClass[sol::meta_function::pairs] = lua["ipairsForArray"].template get<sol::function>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void prepareBindingsForDialogueRecordInfoListElement(sol::state_view& lua)
|
||||||
|
{
|
||||||
|
auto recordInfoBindingsClass = lua.new_usertype<ESM::DialInfo>("ESM3_Dialogue_Info");
|
||||||
|
|
||||||
|
recordInfoBindingsClass[sol::meta_function::to_string]
|
||||||
|
= [](const ESM::DialInfo& rec) { return "ESM3_Dialogue_Info[" + rec.mId.toDebugString() + "]"; };
|
||||||
|
recordInfoBindingsClass["id"]
|
||||||
|
= sol::readonly_property([](const ESM::DialInfo& rec) { return rec.mId.serializeText(); });
|
||||||
|
recordInfoBindingsClass["text"]
|
||||||
|
= sol::readonly_property([](const ESM::DialInfo& rec) -> std::string_view { return rec.mResponse; });
|
||||||
|
recordInfoBindingsClass["questStage"]
|
||||||
|
= sol::readonly_property([](const ESM::DialInfo& rec) -> sol::optional<int> {
|
||||||
|
if (rec.mData.mType != ESM::Dialogue::Type::Journal)
|
||||||
|
{
|
||||||
|
return sol::nullopt;
|
||||||
|
}
|
||||||
|
return rec.mData.mJournalIndex;
|
||||||
|
});
|
||||||
|
recordInfoBindingsClass["isQuestFinished"]
|
||||||
|
= sol::readonly_property([](const ESM::DialInfo& rec) -> sol::optional<bool> {
|
||||||
|
if (rec.mData.mType != ESM::Dialogue::Type::Journal)
|
||||||
|
{
|
||||||
|
return sol::nullopt;
|
||||||
|
}
|
||||||
|
return (rec.mQuestStatus == ESM::DialInfo::QuestStatus::QS_Finished);
|
||||||
|
});
|
||||||
|
recordInfoBindingsClass["isQuestRestart"]
|
||||||
|
= sol::readonly_property([](const ESM::DialInfo& rec) -> sol::optional<bool> {
|
||||||
|
if (rec.mData.mType != ESM::Dialogue::Type::Journal)
|
||||||
|
{
|
||||||
|
return sol::nullopt;
|
||||||
|
}
|
||||||
|
return (rec.mQuestStatus == ESM::DialInfo::QuestStatus::QS_Restart);
|
||||||
|
});
|
||||||
|
recordInfoBindingsClass["isQuestName"]
|
||||||
|
= sol::readonly_property([](const ESM::DialInfo& rec) -> sol::optional<bool> {
|
||||||
|
if (rec.mData.mType != ESM::Dialogue::Type::Journal)
|
||||||
|
{
|
||||||
|
return sol::nullopt;
|
||||||
|
}
|
||||||
|
return (rec.mQuestStatus == ESM::DialInfo::QuestStatus::QS_Name);
|
||||||
|
});
|
||||||
|
recordInfoBindingsClass["filterActorId"]
|
||||||
|
= sol::readonly_property([](const ESM::DialInfo& rec) -> sol::optional<std::string> {
|
||||||
|
if (rec.mData.mType == ESM::Dialogue::Type::Journal || rec.mActor.empty())
|
||||||
|
{
|
||||||
|
return sol::nullopt;
|
||||||
|
}
|
||||||
|
return rec.mActor.serializeText();
|
||||||
|
});
|
||||||
|
recordInfoBindingsClass["filterActorRace"]
|
||||||
|
= sol::readonly_property([](const ESM::DialInfo& rec) -> sol::optional<std::string> {
|
||||||
|
if (rec.mData.mType == ESM::Dialogue::Type::Journal || rec.mRace.empty())
|
||||||
|
{
|
||||||
|
return sol::nullopt;
|
||||||
|
}
|
||||||
|
return rec.mRace.serializeText();
|
||||||
|
});
|
||||||
|
recordInfoBindingsClass["filterActorClass"]
|
||||||
|
= sol::readonly_property([](const ESM::DialInfo& rec) -> sol::optional<std::string> {
|
||||||
|
if (rec.mData.mType == ESM::Dialogue::Type::Journal || rec.mClass.empty())
|
||||||
|
{
|
||||||
|
return sol::nullopt;
|
||||||
|
}
|
||||||
|
return rec.mClass.serializeText();
|
||||||
|
});
|
||||||
|
recordInfoBindingsClass["filterActorFaction"]
|
||||||
|
= sol::readonly_property([](const ESM::DialInfo& rec) -> sol::optional<std::string> {
|
||||||
|
if (rec.mData.mType == ESM::Dialogue::Type::Journal || rec.mFaction.empty())
|
||||||
|
{
|
||||||
|
return sol::nullopt;
|
||||||
|
}
|
||||||
|
if (rec.mFactionLess)
|
||||||
|
{
|
||||||
|
return sol::optional<std::string>("");
|
||||||
|
}
|
||||||
|
return rec.mFaction.serializeText();
|
||||||
|
});
|
||||||
|
recordInfoBindingsClass["filterActorFactionRank"]
|
||||||
|
= sol::readonly_property([](const ESM::DialInfo& rec) -> sol::optional<int> {
|
||||||
|
if (rec.mData.mType == ESM::Dialogue::Type::Journal || rec.mData.mRank == -1)
|
||||||
|
{
|
||||||
|
return sol::nullopt;
|
||||||
|
}
|
||||||
|
return rec.mData.mRank + 1;
|
||||||
|
});
|
||||||
|
recordInfoBindingsClass["filterPlayerCell"]
|
||||||
|
= sol::readonly_property([](const ESM::DialInfo& rec) -> sol::optional<std::string> {
|
||||||
|
if (rec.mData.mType == ESM::Dialogue::Type::Journal || rec.mCell.empty())
|
||||||
|
{
|
||||||
|
return sol::nullopt;
|
||||||
|
}
|
||||||
|
return rec.mCell.serializeText();
|
||||||
|
});
|
||||||
|
recordInfoBindingsClass["filterActorDisposition"]
|
||||||
|
= sol::readonly_property([](const ESM::DialInfo& rec) -> sol::optional<int> {
|
||||||
|
if (rec.mData.mType == ESM::Dialogue::Type::Journal)
|
||||||
|
{
|
||||||
|
return sol::nullopt;
|
||||||
|
}
|
||||||
|
return rec.mData.mDisposition;
|
||||||
|
});
|
||||||
|
recordInfoBindingsClass["filterActorGender"]
|
||||||
|
= sol::readonly_property([](const ESM::DialInfo& rec) -> sol::optional<std::string_view> {
|
||||||
|
if (rec.mData.mType == ESM::Dialogue::Type::Journal || rec.mData.mGender == -1)
|
||||||
|
{
|
||||||
|
return sol::nullopt;
|
||||||
|
}
|
||||||
|
return sol::optional<std::string_view>(rec.mData.mGender == 0 ? "male" : "female");
|
||||||
|
});
|
||||||
|
recordInfoBindingsClass["filterPlayerFaction"]
|
||||||
|
= sol::readonly_property([](const ESM::DialInfo& rec) -> sol::optional<std::string> {
|
||||||
|
if (rec.mData.mType == ESM::Dialogue::Type::Journal || rec.mPcFaction.empty())
|
||||||
|
{
|
||||||
|
return sol::nullopt;
|
||||||
|
}
|
||||||
|
return rec.mPcFaction.serializeText();
|
||||||
|
});
|
||||||
|
recordInfoBindingsClass["filterPlayerFactionRank"]
|
||||||
|
= sol::readonly_property([](const ESM::DialInfo& rec) -> sol::optional<int> {
|
||||||
|
if (rec.mData.mType == ESM::Dialogue::Type::Journal || rec.mData.mPCrank == -1)
|
||||||
|
{
|
||||||
|
return sol::nullopt;
|
||||||
|
}
|
||||||
|
return rec.mData.mPCrank + 1;
|
||||||
|
});
|
||||||
|
recordInfoBindingsClass["sound"]
|
||||||
|
= sol::readonly_property([](const ESM::DialInfo& rec) -> sol::optional<std::string> {
|
||||||
|
if (rec.mData.mType == ESM::Dialogue::Type::Journal || rec.mSound.empty())
|
||||||
|
{
|
||||||
|
return sol::nullopt;
|
||||||
|
}
|
||||||
|
return Misc::ResourceHelpers::correctSoundPath(VFS::Path::Normalized(rec.mSound)).value();
|
||||||
|
});
|
||||||
|
recordInfoBindingsClass["resultScript"]
|
||||||
|
= sol::readonly_property([](const ESM::DialInfo& rec) -> sol::optional<std::string_view> {
|
||||||
|
if (rec.mResultScript.empty())
|
||||||
|
{
|
||||||
|
return sol::nullopt;
|
||||||
|
}
|
||||||
|
return sol::optional<std::string_view>(rec.mResultScript);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void prepareBindingsForDialogueRecords(sol::state_view& lua)
|
||||||
|
{
|
||||||
|
prepareBindingsForDialogueRecord(lua);
|
||||||
|
prepareBindingsForDialogueRecordInfoList(lua);
|
||||||
|
prepareBindingsForDialogueRecordInfoListElement(lua);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace sol
|
||||||
|
{
|
||||||
|
template <ESM::Dialogue::Type filter>
|
||||||
|
struct is_automagical<FilteredDialogueStore<filter>> : std::false_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct is_automagical<ESM::Dialogue> : std::false_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct is_automagical<DialogueInfos> : std::false_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct is_automagical<ESM::DialInfo> : std::false_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
sol::table initCoreDialogueBindings(const Context& context)
|
||||||
|
{
|
||||||
|
sol::state_view& lua = context.mLua->sol();
|
||||||
|
sol::table api(lua, sol::create);
|
||||||
|
|
||||||
|
sol::table journalTable(lua, sol::create);
|
||||||
|
sol::table topicTable(lua, sol::create);
|
||||||
|
sol::table greetingTable(lua, sol::create);
|
||||||
|
sol::table persuasionTable(lua, sol::create);
|
||||||
|
sol::table voiceTable(lua, sol::create);
|
||||||
|
prepareBindingsForDialogueRecordStores<ESM::Dialogue::Type::Journal>(journalTable, context);
|
||||||
|
prepareBindingsForDialogueRecordStores<ESM::Dialogue::Type::Topic>(topicTable, context);
|
||||||
|
prepareBindingsForDialogueRecordStores<ESM::Dialogue::Type::Greeting>(greetingTable, context);
|
||||||
|
prepareBindingsForDialogueRecordStores<ESM::Dialogue::Type::Persuasion>(persuasionTable, context);
|
||||||
|
prepareBindingsForDialogueRecordStores<ESM::Dialogue::Type::Voice>(voiceTable, context);
|
||||||
|
api["journal"] = LuaUtil::makeStrictReadOnly(journalTable);
|
||||||
|
api["topic"] = LuaUtil::makeStrictReadOnly(topicTable);
|
||||||
|
api["greeting"] = LuaUtil::makeStrictReadOnly(greetingTable);
|
||||||
|
api["persuasion"] = LuaUtil::makeStrictReadOnly(persuasionTable);
|
||||||
|
api["voice"] = LuaUtil::makeStrictReadOnly(voiceTable);
|
||||||
|
|
||||||
|
prepareBindingsForDialogueRecords(lua);
|
||||||
|
|
||||||
|
return LuaUtil::makeReadOnly(api);
|
||||||
|
}
|
||||||
|
}
|
12
apps/openmw/mwlua/dialoguebindings.hpp
Normal file
12
apps/openmw/mwlua/dialoguebindings.hpp
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef MWLUA_DIALOGUEBINDINGS_H
|
||||||
|
#define MWLUA_DIALOGUEBINDINGS_H
|
||||||
|
|
||||||
|
#include <sol/forward.hpp>
|
||||||
|
|
||||||
|
namespace MWLua
|
||||||
|
{
|
||||||
|
struct Context;
|
||||||
|
sol::table initCoreDialogueBindings(const Context&);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MWLUA_DIALOGUEBINDINGS_H
|
|
@ -925,6 +925,193 @@
|
||||||
-- @field #string failureSound VFS path to the failure sound
|
-- @field #string failureSound VFS path to the failure sound
|
||||||
-- @field #string hitSound VFS path to the hit sound
|
-- @field #string hitSound VFS path to the hit sound
|
||||||
|
|
||||||
|
--- @{#Dialogue}: Dialogue
|
||||||
|
-- @field [parent=#core] #Dialogue dialogue
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @{#DialogueRecords} functions for journal (quest) read-only records.
|
||||||
|
-- @field [parent=#Dialogue] journal
|
||||||
|
-- @usage --print the name of the record, which is a capitalized version of its id
|
||||||
|
-- print(core.dialogue.journal.records["ms_fargothring"].name) -- MS_FargothRing
|
||||||
|
-- @usage --print ids of all journal records
|
||||||
|
-- for _, journalRecord in pairs(core.dialogue.journal.records) do
|
||||||
|
-- print(journalRecord.id)
|
||||||
|
-- end
|
||||||
|
-- @usage --print quest names for all quests the player has inside a player script
|
||||||
|
-- for _, quest in pairs(types.Player.quests(self)) do
|
||||||
|
-- print(quest.id, core.dialogue.journal.records[quest.id].questName)
|
||||||
|
-- end
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @{#DialogueRecords} functions for topic read-only records.
|
||||||
|
-- @field [parent=#Dialogue] topic
|
||||||
|
-- @usage --print ids of all topic records
|
||||||
|
-- for _, topicRecord in pairs(core.dialogue.topic.records) do
|
||||||
|
-- print(topicRecord.id)
|
||||||
|
-- end
|
||||||
|
-- @usage --print all NPC lines for "vivec"
|
||||||
|
-- for idx, topicInfo in pairs(core.dialogue.topic.records["vivec"].infos) do
|
||||||
|
-- print(idx, topicInfo.text)
|
||||||
|
-- end
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @{#DialogueRecords} functions for voice read-only records.
|
||||||
|
-- @field [parent=#Dialogue] voice
|
||||||
|
-- @usage --print ids of all voice records
|
||||||
|
-- for _, voiceRecord in pairs(core.dialogue.voice.records) do
|
||||||
|
-- print(voiceRecord.id)
|
||||||
|
-- end
|
||||||
|
-- @usage --print all NPC lines & sounds for "flee"
|
||||||
|
-- for idx, voiceInfo in pairs(core.dialogue.voice.records["flee"].infos) do
|
||||||
|
-- print(idx, voiceInfo.text, voiceInfo.sound)
|
||||||
|
-- end
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @{#DialogueRecords} functions for greeting read-only records.
|
||||||
|
-- @field [parent=#Dialogue] greeting
|
||||||
|
-- @usage --print ids of all greeting records
|
||||||
|
-- for _, greetingRecord in pairs(core.dialogue.greeting.records) do
|
||||||
|
-- print(greetingRecord.id)
|
||||||
|
-- end
|
||||||
|
-- @usage --print all NPC lines for "greeting 0"
|
||||||
|
-- for idx, greetingInfo in pairs(core.dialogue.greeting.records["greeting 0"].infos) do
|
||||||
|
-- print(idx, greetingInfo.text)
|
||||||
|
-- end
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @{#DialogueRecords} functions for persuasion read-only records.
|
||||||
|
-- @field [parent=#Dialogue] persuasion
|
||||||
|
-- @usage --print ids of all persuasion records
|
||||||
|
-- for _, persuasionRecord in pairs(core.dialogue.persuasion.records) do
|
||||||
|
-- print(persuasionRecord.id)
|
||||||
|
-- end
|
||||||
|
-- @usage --print all NPC lines for "admire success"
|
||||||
|
-- for idx, persuasionInfo in pairs(core.dialogue.persuasion.records["admire success"].infos) do
|
||||||
|
-- print(idx, persuasionInfo.text)
|
||||||
|
-- end
|
||||||
|
|
||||||
|
---
|
||||||
|
-- A read-only list of all @{#DialogueRecord}s in the world database, may be indexed by recordId, which doesn't have to be lowercase.
|
||||||
|
-- Implements [iterables#List](iterables.html#list-iterable) of #DialogueRecord.
|
||||||
|
-- @field [parent=#DialogueRecords] #list<#DialogueRecord> records
|
||||||
|
-- @usage local record = core.dialogue.journal.records['ms_fargothring']
|
||||||
|
-- @usage local record = core.dialogue.journal.records['MS_FargothRing']
|
||||||
|
-- @usage local record = core.dialogue.journal.records[1]
|
||||||
|
-- @usage local record = core.dialogue.topic.records[1]
|
||||||
|
-- @usage local record = core.dialogue.topic.records['background']
|
||||||
|
-- @usage local record = core.dialogue.greeting.records[1]
|
||||||
|
-- @usage local record = core.dialogue.greeting.records['greeting 0']
|
||||||
|
-- @usage local record = core.dialogue.persuasion.records[1]
|
||||||
|
-- @usage local record = core.dialogue.persuasion.records['admire success']
|
||||||
|
-- @usage local record = core.dialogue.voice.records[1]
|
||||||
|
-- @usage local record = core.dialogue.voice.records["flee"]
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Depending on which store this read-only dialogue record is from, it may either be a journal, topic, greeting, persuasion or voice.
|
||||||
|
-- @type DialogueRecord
|
||||||
|
-- @field #string id Record identifier
|
||||||
|
-- @field #string name Same as id, but with upper cases preserved.
|
||||||
|
-- @field #string questName Non-nil only for journal records with available value. Holds the quest name for this journal entry. Same info may be available under `infos[1].text` as well, but this variable is made for convenience.
|
||||||
|
-- @field #list<#DialogueRecordInfo> infos A read-only list containing all @{#DialogueRecordInfo}s for this record, in order.
|
||||||
|
-- @usage local journalId = core.dialogue.journal.records['A2_4_MiloGone'].id -- "a2_4_milogone"
|
||||||
|
-- @usage local journalName = core.dialogue.journal.records['A2_4_MiloGone'].name -- "A2_4_MiloGone"
|
||||||
|
-- @usage local questName = core.dialogue.journal.records['A2_4_MiloGone'].questName -- "Mehra Milo and the Lost Prophecies"
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Holds the read-only data for one of many info entries inside a dialogue record. Depending on the type of the dialogue record (journal, topic, greeting, persuasion or voice), it could be, for example, a single journal entry or a NPC dialogue line.
|
||||||
|
-- @type DialogueRecordInfo
|
||||||
|
-- @field #string id Identifier for this info entry. Is unique only within the @{#DialogueRecord} it belongs to.
|
||||||
|
-- @field #string text Text associated with this info entry.
|
||||||
|
-- @usage --Variable `aa` below is "Congratulations, %PCName. You are now %PCName the %NextPCRank." in vanilla MW:
|
||||||
|
-- local aa = core.dialogue.topic.records['advancement'].infos[100].text
|
||||||
|
-- @usage --Variable `bb` below is "sound/vo/a/f/fle_af003.mp3" in vanilla MW:
|
||||||
|
-- local bb = core.dialogue.voice.records['flee'].infos[149].sound
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Quest stage (same as in @{openmw_types#PlayerQuest.stage}) this info entry is associated with.
|
||||||
|
-- Non-nil only for journal records.
|
||||||
|
-- @field [parent=#DialogueRecordInfo] #number questStage
|
||||||
|
|
||||||
|
---
|
||||||
|
-- True if this info entry has the "Finished" flag checked.
|
||||||
|
-- Non-nil only for journal records.
|
||||||
|
-- @field [parent=#DialogueRecordInfo] #boolean isQuestFinished
|
||||||
|
|
||||||
|
---
|
||||||
|
-- True if this info entry has the "Restart" flag checked.
|
||||||
|
-- Non-nil only for journal records.
|
||||||
|
-- @field [parent=#DialogueRecordInfo] #boolean isQuestRestart
|
||||||
|
|
||||||
|
---
|
||||||
|
-- True if this info entry has the "Quest Name" flag checked.
|
||||||
|
-- Non-nil only for journal records.
|
||||||
|
-- If true, then the @{#DialogueRecord}, to which this info entry belongs, should have this info entry's @{#DialogueRecordInfo.text} value available in its @{#DialogueRecord.questName}.
|
||||||
|
-- @field [parent=#DialogueRecordInfo] #boolean isQuestName
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Faction of which the speaker must be a member for this info entry to appear.
|
||||||
|
-- Always nil for journal records. Otherwise the nil value represents no conditions, i.e. no filtering applied using these criteria.
|
||||||
|
-- Can return an empty string - this means that the actor must not be a member of any faction for this filtering to apply.
|
||||||
|
-- @field [parent=#DialogueRecordInfo] #string filterActorFaction
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Speaker ID allowing for this info entry to appear.
|
||||||
|
-- Always nil for journal records. Otherwise the nil value represents no conditions, i.e. no filtering applied using these criteria.
|
||||||
|
-- @field [parent=#DialogueRecordInfo] #string filterActorId
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Speaker race allowing for this info entry to appear.
|
||||||
|
-- Always nil for journal records. Otherwise the nil value represents no conditions, i.e. no filtering applied using these criteria.
|
||||||
|
-- @field [parent=#DialogueRecordInfo] #string filterActorRace
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Speaker class allowing for this info entry to appear.
|
||||||
|
-- Always nil for journal records. Otherwise the nil value represents no conditions, i.e. no filtering applied using these criteria.
|
||||||
|
-- @field [parent=#DialogueRecordInfo] #string filterActorClass
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Minimum speaker's rank in their faction allowing for this info entry to appear.
|
||||||
|
-- Always nil for journal records. Otherwise the nil value represents no conditions, i.e. no filtering applied using these criteria.
|
||||||
|
-- Rank index starts from 1, matching the value in @{openmw_types#NPC.getFactionRank}
|
||||||
|
-- @field [parent=#DialogueRecordInfo] #number filterActorFactionRank
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Cell name prefix of location where the player must be for this info entry to appear.
|
||||||
|
-- Always nil for journal records. Otherwise the nil value represents no conditions, i.e. no filtering applied using these criteria.
|
||||||
|
-- "Prefix" means that the cell's name starting with this value shall pass the filtering. For example: `filterPlayerCell` being "Seyda Neen" does apply to the cell "Seyda Neen, Fargoth's House".
|
||||||
|
-- @field [parent=#DialogueRecordInfo] #string filterPlayerCell
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Minimum speaker disposition allowing for this info entry to appear.
|
||||||
|
-- Always nil for journal records. Otherwise is a nonnegative number, with the zero value representing no conditions, i.e. no filtering applied using these criteria.
|
||||||
|
-- @field [parent=#DialogueRecordInfo] #number filterActorDisposition
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Speaker gender allowing for this info entry to appear: "male" or "female".
|
||||||
|
-- Always nil for journal records. Otherwise the nil value represents no conditions, i.e. no filtering applied using these criteria.
|
||||||
|
-- @field [parent=#DialogueRecordInfo] #string filterActorGender
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Faction of which the player must be a member for this info entry to appear.
|
||||||
|
-- Always nil for journal records. Otherwise the nil value represents no conditions, i.e. no filtering applied using these criteria.
|
||||||
|
-- @field [parent=#DialogueRecordInfo] #string filterPlayerFaction
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Minimum player's rank in their faction allowing for this info entry to appear.
|
||||||
|
-- Always nil for journal records. Otherwise the nil value represents no conditions, i.e. no filtering applied using these criteria.
|
||||||
|
-- Rank index starts from 1, matching the value in @{openmw_types#NPC.getFactionRank}
|
||||||
|
-- @field [parent=#DialogueRecordInfo] #number filterPlayerFactionRank
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Sound file path for this info entry.
|
||||||
|
-- Always nil for journal records or if there is no sound set.
|
||||||
|
-- @field [parent=#DialogueRecordInfo] #string sound
|
||||||
|
|
||||||
|
---
|
||||||
|
-- MWScript (full script text) executed when this info is chosen.
|
||||||
|
-- Always nil for journal records or if there is no value set.
|
||||||
|
-- @field [parent=#DialogueRecordInfo] #string resultScript
|
||||||
|
|
||||||
--- @{#Factions}: Factions
|
--- @{#Factions}: Factions
|
||||||
-- @field [parent=#core] #Factions factions
|
-- @field [parent=#core] #Factions factions
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue