Much better narration/dialogue/action handling (and for lua too)

This commit is contained in:
2026-01-23 02:42:29 +03:00
parent cd91174f5d
commit 1d358d206e
7 changed files with 220 additions and 28 deletions

View File

@@ -656,6 +656,29 @@ setup_handler(function(event, trigger_entity, what_entity)
end
end)
setup_action_handler("talk", {
count = 0,
activate = function(this)
this._narration("Booo!! Lua...", {"One", "Two", "Three"})
end,
event = function(this, event)
this.count = this.count + 1
if event == "narration_progress" then
this.count = this.count + 1
this._narration("Booo!!!", {})
end
if event == "narration_answered" then
this.count = this.count + 1
this._narration("Booo!!!_", {})
end
if this.count > 10 then
this._finish()
end
end,
finish = function(this)
this.count = 0
end,
})
--[[
active_dialogues = {}
setup_action_handler("talk", function(town, index, word)

View File

@@ -12,21 +12,11 @@
namespace ECS
{
struct TownNPCs {
struct NPCData {
flecs::entity e;
nlohmann::json props;
Ogre::Vector3 position;
Ogre::Quaternion orientation;
Ogre::String model;
};
std::map<int, NPCData> npcs;
};
struct LivesIn {};
void createNPCActionNodes(flecs::entity town, flecs::entity e, int index)
{
NPCActionNodes &anodes = e.get_mut<NPCActionNodes>();
const TownNPCs &npcs = town.get<TownNPCs>();
nlohmann::json npcprops = npcs.npcs.at(index).props;
const CharacterBase &ch = e.get<CharacterBase>();
Ogre::Vector3 characterPos = ch.mBodyNode->_getDerivedPosition();
Ogre::Quaternion characterRot = ch.mBodyNode->_getDerivedOrientation();
@@ -66,6 +56,7 @@ void createNPCActionNodes(flecs::entity town, flecs::entity e, int index)
anode.props["height"] = anode.height;
anode.props["town"] = town.id();
anode.props["index"] = index;
anode.props["npc"] = npcprops;
anodes.anodes.push_back(anode);
}
{
@@ -86,6 +77,7 @@ void createNPCActionNodes(flecs::entity town, flecs::entity e, int index)
anode.props["height"] = anode.height;
anode.props["town"] = town.id();
anode.props["index"] = index;
anode.props["npc"] = npcprops;
anodes.anodes.push_back(anode);
}
e.modified<NPCActionNodes>();

View File

@@ -1,9 +1,22 @@
#ifndef _CHARACTER_MANAGER_MODULE_
#define _CHARACTER_MANAGER_MODULE_
#include <flecs.h>
#include <nlohmann/json.hpp>
namespace ECS
{
struct TownCharacterHolder{int index;};
struct TownNPCs {
struct NPCData {
flecs::entity e;
nlohmann::json props;
Ogre::Vector3 position;
Ogre::Quaternion orientation;
Ogre::String model;
};
std::map<int, NPCData> npcs;
};
struct LivesIn {};
struct CharacterManagerModule {
std::set<flecs::entity> characters;
flecs::entity player;

View File

@@ -2,6 +2,7 @@
#define __GUIMODULECOMMON_H__
#include <iostream>
#include <Ogre.h>
#include <nlohmann/json.hpp>
#include "Components.h"
#include "GameData.h"
namespace ECS
@@ -21,6 +22,7 @@ struct GUI {
Ogre::String mnarrationText;
std::vector<Ogre::String> mchoices;
int narration_answer;
nlohmann::json props;
private:
bool complete;
@@ -91,6 +93,18 @@ struct GUI {
event(ev);
}
virtual ~NarrationHandler() {}
void setProperties(const Ogre::String &properties)
{
props = nlohmann::json::parse(properties);
}
Ogre::String getProperties() const
{
return props.dump();
}
const nlohmann::json &getPropsJSON() const
{
return props;
}
};
static void setWindowGrab(bool g = true)

View File

@@ -302,7 +302,10 @@ LuaData::LuaData()
lua_setglobal(L, "setup_handler");
lua_pushcfunction(L, [](lua_State *L) -> int {
luaL_checktype(L, 1, LUA_TSTRING);
luaL_checktype(L, 2, LUA_TFUNCTION);
if (lua_type(L, 2) == LUA_TFUNCTION)
luaL_checktype(L, 2, LUA_TFUNCTION);
else
luaL_checktype(L, 2, LUA_TTABLE);
ECS::get_mut<PlayerActionModule>().setupLuaActionHandler(L);
ECS::modified<PlayerActionModule>();
return 0;

View File

@@ -72,7 +72,8 @@ struct TestNarrativeHandler : GUI::NarrationHandler {
}
void activate() override
{
_narration("Dialogue...", {});
_narration("Greetings...", {});
std::cout << getPropsJSON().dump(4) << std::endl;
count = 0;
}
void event(const Ogre::String &evt) override
@@ -101,11 +102,119 @@ struct TestNarrativeHandler : GUI::NarrationHandler {
<< std::endl;
}
};
struct LuaNarrationHandler : GUI::NarrationHandler {
int ref;
lua_State *L;
LuaNarrationHandler(lua_State *L, int ref)
: ref(ref)
, L(L)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
lua_pushlightuserdata(L, this);
lua_pushcclosure(
L,
[](lua_State *L) {
luaL_checktype(L, 1, LUA_TSTRING);
luaL_checktype(L, 2, LUA_TTABLE);
LuaNarrationHandler *handler =
static_cast<LuaNarrationHandler *>(
lua_touserdata(
L,
lua_upvalueindex(1)));
Ogre::String event = lua_tostring(L, 1);
std::vector<Ogre::String> choices;
int choicesLen = (int)lua_rawlen(L, 2);
choices.reserve(choicesLen);
for (int i = 1; i <= choicesLen; ++i) {
lua_rawgeti(L, 2, i);
if (lua_isstring(L, -1))
choices.push_back(
lua_tostring(L, -1));
lua_pop(L, 1);
}
handler->_narration(event, choices);
return 0;
},
1);
lua_setfield(L, -2, "_narration");
lua_pushlightuserdata(L, this);
lua_pushcclosure(
L,
[](lua_State *L) {
LuaNarrationHandler *handler =
static_cast<LuaNarrationHandler *>(
lua_touserdata(
L,
lua_upvalueindex(1)));
handler->_finish();
return 0;
},
1);
lua_setfield(L, -2, "_finish");
lua_pop(L, 1);
}
void finish() override
{
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
int type = lua_getfield(L, -1, "finish");
OgreAssert(type == LUA_TFUNCTION, "bad finish()");
lua_insert(L, -2);
if (lua_pcall(L, 1, 0, 0) != 0) {
std::cerr << lua_tostring(L, -1) << std::endl;
OgreAssert(false, "lua error");
lua_pop(L, 1);
}
_clear_narration();
}
void activate() override
{
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
int type = lua_getfield(L, -1, "activate");
OgreAssert(type == LUA_TFUNCTION, "bad activate()");
lua_insert(L, -2);
if (lua_pcall(L, 1, 0, 0) != 0) {
std::cerr << lua_tostring(L, -1) << std::endl;
OgreAssert(false, "lua error");
lua_pop(L, 1);
}
// _narration("Greetings...", {});
// std::cout << getPropsJSON().dump(4) << std::endl;
}
void event(const Ogre::String &evt) override
{
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
int type = lua_getfield(L, -1, "event");
OgreAssert(type == LUA_TFUNCTION, "bad event()");
lua_insert(L, -2);
lua_pushstring(L, evt.c_str());
if (lua_pcall(L, 2, 0, 0) != 0) {
std::cerr << lua_tostring(L, -1) << std::endl;
OgreAssert(false, "lua error");
lua_pop(L, 1);
}
}
};
struct SimpleWordHandler : PlayerActionModule::ActionWordHandler {
void operator()(flecs::entity town, int index,
const Ogre::String &word) override
{
TestNarrativeHandler *handle = OGRE_NEW TestNarrativeHandler();
const TownNPCs::NPCData &npc =
town.get<TownNPCs>().npcs.at(index);
flecs::entity e = npc.e;
for (const auto &anode : e.get<NPCActionNodes>().anodes) {
if (anode.action == word) {
nlohmann::json props = anode.props;
props["initiator"] =
ECS::get<CharacterManagerModule>()
.getPlayer()
.id();
props["recipient"] = e.id();
handle->setProperties(props.dump());
break;
}
}
ECS::get_mut<GUI>().addNarrationHandler(handle);
ECS::modified<GUI>();
}
@@ -205,8 +314,8 @@ PlayerActionModule::PlayerActionModule(flecs::world &ecs)
if (!ECS::get<GUI>().enabled)
list.busy = false;
});
SimpleWordHandler *handler = OGRE_NEW SimpleWordHandler;
addWordHandler("talk", handler);
// SimpleWordHandler *handler = OGRE_NEW SimpleWordHandler;
// addWordHandler("talk", handler);
}
void PlayerActionModule::addWordHandler(const Ogre::String &word,
@@ -233,13 +342,39 @@ struct LuaWordHandler : PlayerActionModule::ActionWordHandler {
const Ogre::String &word) override
{
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
lua_pushinteger(L, town.id());
lua_pushinteger(L, index);
lua_pushstring(L, word.c_str());
if (lua_pcall(L, 3, 0, 0)) {
Ogre::LogManager::getSingleton().stream()
<< lua_tostring(L, -1);
OgreAssert(false, "Lua error");
if (lua_type(L, -1) == LUA_TFUNCTION) {
luaL_checktype(L, -1, LUA_TFUNCTION);
lua_pushinteger(L, town.id());
lua_pushinteger(L, index);
lua_pushstring(L, word.c_str());
if (lua_pcall(L, 3, 0, 0)) {
Ogre::LogManager::getSingleton().stream()
<< lua_tostring(L, -1);
OgreAssert(false, "Lua error");
}
} else if (lua_type(L, -1) == LUA_TTABLE) {
luaL_checktype(L, -1, LUA_TTABLE);
lua_pop(L, 1);
LuaNarrationHandler *handle =
OGRE_NEW LuaNarrationHandler(L, ref);
const TownNPCs::NPCData &npc =
town.get<TownNPCs>().npcs.at(index);
flecs::entity e = npc.e;
for (const auto &anode :
e.get<NPCActionNodes>().anodes) {
if (anode.action == word) {
nlohmann::json props = anode.props;
props["initiator"] =
ECS::get<CharacterManagerModule>()
.getPlayer()
.id();
props["recipient"] = e.id();
handle->setProperties(props.dump());
break;
}
}
ECS::get_mut<GUI>().addNarrationHandler(handle);
ECS::modified<GUI>();
}
}
};
@@ -268,11 +403,19 @@ void PlayerActionModule::removeLuaWordHandler(const Ogre::String &word,
int PlayerActionModule::setupLuaActionHandler(lua_State *L)
{
luaL_checktype(L, 1, LUA_TSTRING);
luaL_checktype(L, 2, LUA_TFUNCTION);
Ogre::String word = lua_tostring(L, 1);
lua_pushvalue(L, 2);
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
addLuaWordHandler(word, L, ref);
if (lua_type(L, 2) == LUA_TFUNCTION) {
luaL_checktype(L, 2, LUA_TFUNCTION);
Ogre::String word = lua_tostring(L, 1);
lua_pushvalue(L, 2);
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
addLuaWordHandler(word, L, ref);
} else if (lua_type(L, 2) == LUA_TTABLE) {
luaL_checktype(L, 2, LUA_TTABLE);
Ogre::String word = lua_tostring(L, 1);
lua_pushvalue(L, 2);
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
addLuaWordHandler(word, L, ref);
}
return 0;
}

View File

@@ -2134,6 +2134,8 @@ bool editNPCs(nlohmann::json &npcs)
if (ImGui::SmallButton("Add NPC")) {
nlohmann::json npc;
npc["lastName"] = Ogre::String(lastName);
npc["firstName"] = Ogre::String(firstName);
npc["tags"] = Ogre::String(tags);
Ogre::Vector3 npcPosition =
ECS::get<EditorGizmo>().sceneNode->_getDerivedPosition();
Ogre::Quaternion npcOrientation =
@@ -2142,6 +2144,8 @@ bool editNPCs(nlohmann::json &npcs)
to_json(npc["position"], npcPosition);
to_json(npc["orientation"], npcOrientation);
npc["sex"] = selection;
npc["health"] = 100;
npc["stamina"] = 100;
npcs.push_back(npc);
changed = true;
}