Action nodes work better now

This commit is contained in:
2026-02-01 00:13:33 +03:00
parent 201aa92fe7
commit e6efd89bb0
14 changed files with 548 additions and 126 deletions

View File

@@ -12,6 +12,7 @@
#include "GUIModule.h"
#include "GUIModuleCommon.h"
#include "LuaData.h"
#include "PhysicsModule.h"
#include "PlayerActionModule.h"
namespace ECS
@@ -252,27 +253,28 @@ struct LuaNarrationHandler : GUI::NarrationHandler {
};
struct SimpleWordHandler : PlayerActionModule::ActionWordHandler {
void operator()(flecs::entity town, int index,
const Ogre::String &word) override
void operator()(int actor, flecs::entity town, int index,
const Ogre::String &word, int actionNode) 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 : npc.actionNodes) {
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;
if (index >= 0) {
TestNarrativeHandler *handle =
OGRE_NEW TestNarrativeHandler();
/* this is for NPCs only, right? */
const TownNPCs::NPCData &npc =
town.get<TownNPCs>().npcs.at(index);
flecs::entity e = npc.e;
for (const auto &anode : npc.actionNodes) {
if (anode.action == word) {
nlohmann::json props = anode.props;
props["initiator"] = actor;
props["recipient"] = index;
handle->setProperties(props.dump());
break;
}
}
ECS::get_mut<GUI>().addNarrationHandler(handle);
ECS::modified<GUI>();
}
ECS::get_mut<GUI>().addNarrationHandler(handle);
ECS::modified<GUI>();
}
};
@@ -346,11 +348,13 @@ PlayerActionModule::PlayerActionModule(flecs::world &ecs)
.selected]
.action) {
(*it->second)(
town, index,
-1, town, index,
list.dynamicNodes
[list.getUIData()
.selected]
.action);
.action,
list.getUIData()
.selected);
list.setBusy();
}
}
@@ -358,6 +362,19 @@ PlayerActionModule::PlayerActionModule(flecs::world &ecs)
if (!ECS::get<GUI>().enabled)
list.setReady();
});
ecs.system<const EngineData>("UpdateActivatedWords")
.kind(flecs::OnUpdate)
.each([this](const EngineData &eng) {
for (auto it = activatedWords.begin();
it != activatedWords.end();) {
int ret = it->second->_update(eng.delta);
if (ret != ActivatedWordHandler::BUSY) {
delete it->second;
it = activatedWords.erase(it);
} else
it++;
}
});
}
void PlayerActionModule::addWordHandler(const Ogre::String &word,
@@ -377,11 +394,197 @@ void PlayerActionModule::removeWordHandler(const Ogre::String &word,
}
}
static std::set<int> activeActors;
struct TestActivatedWordHandler : PlayerActionModule::ActivatedWordHandler {
int actor;
flecs::entity town;
int index;
Ogre::String word;
int actionNode;
ActionNodeList::ActionNode anode;
flecs::entity ch;
int state;
float delay;
std::unordered_map<Ogre::String, Ogre::Vector3> placeLocalOffset;
std::unordered_map<Ogre::String, Ogre::Quaternion> placeLocalRotation;
TestActivatedWordHandler(int actor, flecs::entity town, int index,
const Ogre::String &word, int actionNode)
: PlayerActionModule::ActivatedWordHandler()
, actor(actor)
, town(town)
, index(index)
, word(word)
, actionNode(actionNode)
, state(0)
, delay(0)
{
activeActors.insert(actor);
// dynamic nodes can disappear on us so to avoid that use a copy
anode = ECS::get<ActionNodeList>().dynamicNodes[actionNode];
if (actor == -1)
ch = ECS::get<CharacterManagerModule>().getPlayer();
else if (actor >= 0) {
const TownNPCs &npcs = town.get<TownNPCs>();
ch = npcs.npcs.at(actor).e;
}
if (anode.props.find("positions") == anode.props.end())
goto out;
for (const auto &position : anode.props["positions"]) {
if (position.find("name") == position.end())
continue;
Ogre::Vector3 localPosition;
Ogre::Quaternion localRotation;
localPosition.x = position["position_x"].get<float>();
localPosition.y = position["position_y"].get<float>();
localPosition.z = position["position_z"].get<float>();
localRotation.w = position["rotation_w"].get<float>();
localRotation.x = position["rotation_x"].get<float>();
localRotation.y = position["rotation_y"].get<float>();
localRotation.z = position["rotation_z"].get<float>();
placeLocalOffset[position["name"].get<Ogre::String>()] =
localPosition;
placeLocalRotation[position["name"].get<Ogre::String>()] =
localRotation;
}
out:;
}
void teleport(const Ogre::String &place)
{
if (placeLocalOffset.find(place) == placeLocalOffset.end())
return;
Ogre::Quaternion newRotation =
anode.rotation * placeLocalRotation[place];
Ogre::Vector3 newPosition =
anode.position + newRotation * placeLocalOffset[place];
if (ch.is_valid() && ch.has<CharacterBase>()) {
ch.get<CharacterBase>()
.mBodyNode->_setDerivedOrientation(newRotation);
ch.get<CharacterBase>().mBodyNode->_setDerivedPosition(
newPosition);
}
if (actor >= 0) {
town.get_mut<TownNPCs>().npcs[actor].position =
newPosition;
town.get_mut<TownNPCs>().npcs[actor].orientation =
newRotation;
town.modified<TownNPCs>();
}
}
int update(float delta)
{
switch (state) {
case 0:
if (ECS::get<Input>().act)
delay += delta;
// activate anly after delay
if (ECS::get<Input>().act == 0 && delay > 0.2f) {
// Yay!!!
std::cout << "Node data: " << std::endl;
std::cout << anode.props.dump(4) << std::endl;
if (ch.is_valid()) {
PhysicsModule::controlPhysics(ch,
false);
// no control by player or ai
ch.add<CharacterControlDisable>();
if (word == "sit")
ch.set<CharacterInActuator>(
{ "sitting-chair",
{ 0, 0, 0 } });
// else
// ch.set<CharacterInActuator>(
// { "idle", { 0, 0, 0 } });
}
teleport("enter");
delay = 0.0f;
state = 5;
} else if (ECS::get<Input>().act == 0 &&
delay <= 0.2f) {
delay = 0.0f;
state = 10;
}
break;
case 5:
// teleport again to handle possible problems caused by root motion
delay = 0.0f;
teleport("enter");
state++;
break;
case 6:
// do not move anywhere until we depress key and wait a bit
if (ECS::get<Input>().act == 0)
delay += delta;
if (delay > 1.0f) {
delay = 0.0f;
state++;
}
break;
case 7:
// if the key is pressed for a second move to next state
if (ECS::get<Input>().act)
delay += delta;
if (delay > 1.0f) {
delay = 0.0f;
state++;
}
break;
case 8:
delay = 0.0;
state = 10;
break;
case 10:
delay = 0.0;
state++;
break;
case 11:
// wait until key is depressed for a second
if (ECS::get<Input>().act == 0)
delay += delta;
if (delay > 1.0f) {
delay = 0.0;
state++;
}
break;
case 12:
delay = 0.0f;
state = 0;
return OK;
break;
}
std::cout << this << " delay: " << delay << " state: " << state
<< std::endl;
return BUSY;
}
void enter()
{
delay = 0.0f;
state = 0;
ECS::get_mut<GUI>().enableActions = false;
ECS::modified<GUI>();
std::cout << "enter" << std::endl;
}
void exit(int result)
{
ch.remove<CharacterInActuator>();
ch.remove<CharacterControlDisable>();
PhysicsModule::controlPhysics(ch, true);
// OgreAssert(false, "exit");
delay = 0.0f;
state = 0;
ECS::get_mut<GUI>().enableActions = true;
ECS::modified<GUI>();
std::cout << "exit" << std::endl;
}
virtual ~TestActivatedWordHandler()
{
activeActors.erase(actor);
}
};
struct LuaWordHandler : PlayerActionModule::ActionWordHandler {
lua_State *L;
int ref;
void operator()(flecs::entity town, int index,
const Ogre::String &word) override
void operator()(int actor, flecs::entity town, int index,
const Ogre::String &word, int actionNode) override
{
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
if (lua_type(L, -1) == LUA_TFUNCTION) {
@@ -397,25 +600,39 @@ struct LuaWordHandler : PlayerActionModule::ActionWordHandler {
} 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 : npc.actionNodes) {
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;
/* trying to talk to NPC activates narration mode */
if (index >= 0 && word == "talk") {
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 : npc.actionNodes) {
if (anode.action == word) {
nlohmann::json props =
anode.props;
props["initiator"] = actor;
props["recipient"] = index;
handle->setProperties(
props.dump());
break;
}
}
ECS::get_mut<GUI>().addNarrationHandler(handle);
ECS::modified<GUI>();
} else {
if (activeActors.find(actor) ==
activeActors.end()) {
TestActivatedWordHandler *handler =
OGRE_NEW TestActivatedWordHandler(
actor, town, index,
word, actionNode);
ECS::get_mut<PlayerActionModule>()
.addActivatedWordHandler(
word, handler);
ECS::modified<PlayerActionModule>();
}
}
ECS::get_mut<GUI>().addNarrationHandler(handle);
ECS::modified<GUI>();
}
}
};
@@ -460,6 +677,26 @@ int PlayerActionModule::setupLuaActionHandler(lua_State *L)
return 0;
}
void PlayerActionModule::addActivatedWordHandler(const Ogre::String &word,
ActivatedWordHandler *handler)
{
for (auto it = activatedWords.begin(); it != activatedWords.end();
it++) {
}
activatedWords.insert({ word, handler });
}
void PlayerActionModule::removeActivatedWordHandler(
const Ogre::String &word, ActivatedWordHandler *handler)
{
for (auto it = activatedWords.begin(); it != activatedWords.end();) {
if (it->first == word && it->second == handler)
it = activatedWords.erase(it);
else
it++;
}
}
void ActionNodeList::updateDynamicNodes()
{
std::lock_guard<std::mutex> lock(*nodeMutex);