Compare commits

...

1 Commits

Author SHA1 Message Date
d139e77969 Continued working on GOAP action executor 2026-02-07 17:03:12 +03:00
5 changed files with 269 additions and 106 deletions

View File

@@ -18,6 +18,8 @@
#include "GUIModuleCommon.h"
#include "AppModule.h"
#include "GUIModule.h"
#include "PhysicsModule.h"
#include "physics.h"
#include "sound.h"
class App;
class SkyRenderer : public Ogre::SceneManager::Listener {
@@ -626,10 +628,13 @@ end:
void setupInput()
{
}
JoltPhysicsWrapper *mJolt;
void createContent()
{
int i;
sky = new SkyBoxRenderer(getSceneManager());
mJolt = new JoltPhysicsWrapper(mScnMgr, mCameraNode);
sky = new SkyBoxRenderer(getSceneManager());
bool drawFirst = true;
uint8_t renderQueue = drawFirst ?
Ogre::RENDER_QUEUE_SKIES_EARLY :
@@ -651,6 +656,12 @@ end:
ECS::setupExteriorScene(mScnMgr,
/*mDynWorld.get(), */ mCameraNode,
mCamera, getRenderWindow());
ECS::get().import <ECS::PhysicsModule>();
ECS::Physics &ph = ECS::get().ensure<ECS::Physics>();
ph.physics = mJolt;
ECS::modified<ECS::Physics>();
ECS::get()
.observer<ECS::App>("UpdateInputListener")
.event(flecs::OnSet)

View File

@@ -9,7 +9,89 @@
#include "CharacterAIModule.h"
namespace ECS
{
class ActionNodeActions {
struct ActionExec {
struct PlanExecData {
TownNPCs::NPCData &npc;
Blackboard &bb;
nlohmann::json &memory;
};
enum { OK = 0, BUSY, ERROR };
TownNPCs::NPCData &npc;
Blackboard &bb;
nlohmann::json &memory;
bool complete;
bool active;
const goap::BaseAction<Blackboard> *action;
ActionExec(PlanExecData &data,
const goap::BaseAction<Blackboard> *action)
: npc(data.npc)
, bb(data.bb)
, memory(data.memory)
, complete(false)
, active(false)
, action(action)
{
}
ActionExec(const ActionExec &other)
: npc(other.npc)
, bb(other.bb)
, memory(other.memory)
, complete(other.complete)
, active(other.active)
, action(other.action)
{
}
ActionExec &operator=(const ActionExec &other)
{
npc = other.npc;
bb = other.bb;
memory = other.memory;
complete = other.complete;
active = other.active;
action = other.action;
return *this;
}
private:
virtual int update(float delta) = 0;
virtual void finish(int result) = 0;
virtual void activate() = 0;
public:
int operator()(float delta)
{
if (!active)
activate();
int ret = update(delta);
if (ret != BUSY)
finish(ret);
return ret;
}
};
struct PlanExec {
enum { OK = 0, BUSY, ERROR };
std::vector<struct ActionExec *> action_exec;
int index;
PlanExec()
: index(-1)
{
}
int operator()(float delta)
{
if (index == -1)
index = 0;
int rc = (*action_exec[index])(delta);
if (rc == ActionExec::ERROR)
return ERROR;
if (action_exec[index]->complete)
index++;
if (index >= action_exec.size())
return OK;
return BUSY;
}
};
struct ActionNodeActions {
struct WalkToAction : public goap::BaseAction<Blackboard> {
int node;
WalkToAction(int node, int cost)
@@ -98,12 +180,103 @@ out:
std::vector<goap::BaseAction<Blackboard> *> m_actions;
public:
struct ActionExecWalk : ActionExec {
private:
Ogre::Vector3 targetPosition;
float radius;
int update(float delta) override
{
if (npc.e.is_valid()) {
Ogre::Vector3 position =
npc.e.get<CharacterBase>()
.mBodyNode
->_getDerivedPosition();
if (position.squaredDistance(targetPosition) >=
radius * radius) {
if (npc.e.is_valid())
npc.e.get<CharacterBase>()
.mBodyNode
->_setDerivedPosition(
targetPosition);
npc.position = targetPosition;
return BUSY;
}
} else {
if (npc.position.squaredDistance(
targetPosition) >=
radius * radius) {
npc.position = targetPosition;
return BUSY;
}
}
return OK;
}
void finish(int rc) override
{
if (rc == OK)
bb.apply(action->effects);
}
void activate() override
{
const ActionNodeActions::WalkToAction *wtaction =
static_cast<
const ActionNodeActions::WalkToAction *>(
action);
radius = ECS::get<ActionNodeList>()
.dynamicNodes[wtaction->node]
.radius;
targetPosition = ECS::get<ActionNodeList>()
.dynamicNodes[wtaction->node]
.position;
Ogre::Vector3 direction =
(targetPosition - npc.position).normalisedCopy();
targetPosition -= direction * radius;
std::cout << action->get_name();
}
public:
ActionExecWalk(ActionExec::PlanExecData &data,
goap::BaseAction<Blackboard> *action)
: ActionExec(data, action)
{
}
};
struct ActionExecUse : ActionExec {
private:
int update(float delta) override
{
return OK;
}
void finish(int rc) override
{
if (rc == OK)
bb.apply(action->effects);
}
void activate() override
{
std::cout << action->get_name();
const ActionNodeActions::RunActionNode *runaction =
static_cast<const ActionNodeActions::RunActionNode
*>(action);
if (runaction)
std::cout << "Is Run" << std::endl;
OgreAssert(false, "activate");
}
public:
ActionExecUse(ActionExec::PlanExecData &data,
goap::BaseAction<Blackboard> *action)
: ActionExec(data, action)
{
}
};
ActionNodeActions(int node, const Blackboard &prereq, int cost)
{
OgreAssert(
node < ECS::get<ActionNodeList>().dynamicNodes.size(),
"bad node " + Ogre::StringConverter::toString(node));
m_actions.push_back(OGRE_NEW WalkToAction(node, 10000));
auto paction = OGRE_NEW WalkToAction(node, 10000);
m_actions.push_back(paction);
nlohmann::json jactionPrereq = nlohmann::json::object();
nlohmann::json jactionEffect = nlohmann::json::object();
jactionPrereq["at_object"] = 1;
@@ -176,6 +349,32 @@ public:
return m_actions;
}
};
struct ActionExecCommon : ActionExec {
private:
int update(float delta) override
{
std::cout << "running: " << action->get_name() << std::endl;
return OK;
}
void finish(int rc) override
{
if (rc == OK)
bb.apply(action->effects);
std::cout << "finish: " << action->get_name() << std::endl;
}
void activate() override
{
std::cout << action->get_name();
std::cout << "!";
}
public:
ActionExecCommon(ActionExec::PlanExecData &data,
goap::BaseAction<Blackboard> *action)
: ActionExec(data, action)
{
}
};
CharacterAIModule::CharacterAIModule(flecs::world &ecs)
{
static std::mutex ecs_mutex;
@@ -376,92 +575,6 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs)
});
});
});
struct ActionExec {
enum { OK = 0, BUSY, ERROR };
TownNPCs::NPCData &npc;
Blackboard &bb;
nlohmann::json &memory;
bool complete;
bool active;
const goap::BaseAction<Blackboard> *action;
ActionExec(TownNPCs::NPCData &npc, Blackboard &bb,
nlohmann::json &memory,
const goap::BaseAction<Blackboard> *action)
: npc(npc)
, bb(bb)
, memory(memory)
, complete(false)
, active(false)
, action(action)
{
}
ActionExec(const ActionExec &other)
: npc(other.npc)
, bb(other.bb)
, memory(other.memory)
, complete(other.complete)
, active(other.active)
, action(other.action)
{
}
ActionExec &operator=(const ActionExec &other)
{
npc = other.npc;
bb = other.bb;
memory = other.memory;
complete = other.complete;
active = other.active;
action = other.action;
return *this;
}
private:
int update(float delta)
{
return OK;
}
void finish(int result)
{
}
void activate()
{
std::cout << action->get_name();
OgreAssert(false, "activate");
}
public:
int operator()(float delta)
{
if (!active)
activate();
int ret = update(delta);
if (ret != BUSY)
finish(ret);
return ret;
}
};
struct PlanExec {
enum { OK = 0, BUSY, ERROR };
std::vector<struct ActionExec> action_exec;
int index;
PlanExec()
: index(-1)
{
}
int operator()(float delta)
{
if (index == -1)
index = 0;
int rc = action_exec[index](delta);
if (rc == ActionExec::ERROR)
return ERROR;
if (action_exec[index].complete)
index++;
if (index >= action_exec.size())
return OK;
return BUSY;
}
};
static std::unordered_map<int, struct PlanExec> plan_exec;
ecs.system<const EngineData, TownNPCs, TownAI>("RunPLAN")
.kind(flecs::OnUpdate)
@@ -470,7 +583,14 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs)
for (const auto &plans : ai.plans) {
if (plan_exec.find(plans.first) !=
plan_exec.end()) {
plan_exec[plans.first](eng.delta);
int rc = plan_exec[plans.first](
eng.delta);
if (rc != PlanExec::BUSY) {
plan_exec.erase(plans.first);
ai.plans[plans.first].erase(
ai.plans[plans.first]
.begin());
}
continue;
}
std::cout << "NPC: " << plans.first;
@@ -482,14 +602,45 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs)
std::cout << " Goal: ";
plan.goal->goal.dump_bits();
for (const auto &action : plan.plan) {
pexec.action_exec.emplace_back(
ActionExec::PlanExecData data({
npcs.npcs.at(
plans.first),
ai.blackboards.at(
plans.first),
ai.memory.at(
plans.first),
action);
});
// TODO: executor factory is needed
if (action->get_name().substr(
0, 4) == "Walk") {
ActionExec *e = OGRE_NEW
ActionNodeActions::ActionExecWalk(
data,
action);
pexec.action_exec
.push_back(e);
} else if (action->get_name()
.substr(0,
4) ==
"Use(") {
ActionExec *e = OGRE_NEW
ActionNodeActions::ActionExecUse(
data,
action);
pexec.action_exec
.push_back(e);
} else {
std::cout
<< action->get_name()
<< " ";
ActionExec *e = OGRE_NEW
ActionExecCommon(
data,
action);
pexec.action_exec
.push_back(e);
}
std::cout << action->get_name()
<< " ";
}

View File

@@ -27,6 +27,7 @@
#include "CharacterAIModule.h"
#include "QuestModule.h"
#include "world-build.h"
#include "physics.h"
namespace ECS
{
@@ -50,9 +51,11 @@ void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
Ogre::Camera *camera, Ogre::RenderWindow *window)
{
std::cout << "Setup GameData\n";
setup_minimal();
setup_minimal();
std::cout << "Setup Editor\n";
ecs.component<GameState>().add(flecs::Singleton);
ecs.import <CharacterModule>();
ecs.import <CharacterModule>();
ecs.import <BoatModule>();
ecs.import <PhysicsModule>();
ecs.import <WaterModule>();
@@ -208,9 +211,11 @@ void setupInventoryScene(Ogre::SceneManager *scnMgr,
void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
Ogre::Camera *camera, Ogre::RenderWindow *window)
{
std::cout << "Setup Editor\n";
setup_minimal();
ecs.component<RenderWindow>().add(flecs::Singleton);
setup_minimal();
Physics &ph = ECS::get().ensure<Physics>();
ph.physics = new JoltPhysicsWrapper(scnMgr, cameraNode);
ECS::modified<Physics>();
ecs.component<RenderWindow>().add(flecs::Singleton);
ecs.component<EditorSceneSwitch>().add(flecs::Singleton);
ecs.import <CharacterModule>();
ecs.import <BoatModule>();

View File

@@ -627,11 +627,6 @@ void PhysicsModule::setDebugDraw(bool enable)
void PhysicsModule::configurePhysics()
{
Physics &ph = ECS::get().ensure<Physics>();
const EngineData &e = ECS::get<EngineData>();
const Camera &c = ECS::get<Camera>();
ph.physics = new JoltPhysicsWrapper(e.mScnMgr, c.mCameraNode);
ECS::modified<Physics>();
}
bool WaterBody::isInWater(const JPH::BodyID &id) const
{

View File

@@ -579,9 +579,6 @@ public:
static int instanceCount = 0;
OgreAssert(instanceCount == 0, "Bad initialisation");
instanceCount++;
// Create a factory, this class is responsible for creating instances of classes based on their name or hash and is mainly used for deserialization of saved data.
// It is not directly used in this example but still required.
JPH::Factory::sInstance = new JPH::Factory();
// Register all physics types with the factory and install their collision handlers with the CollisionDispatch class.
// If you have your own custom shape types you probably need to register their handlers with the CollisionDispatch before calling this function.
@@ -1530,7 +1527,11 @@ JoltPhysicsWrapper::JoltPhysicsWrapper(Ogre::SceneManager *scnMgr,
// This needs to be done before any other Jolt function is called.
JPH::RegisterDefaultAllocator();
// Install trace and assert callbacks
// Create a factory, this class is responsible for creating instances of classes based on their name or hash and is mainly used for deserialization of saved data.
// It is not directly used in this example but still required.
JPH::Factory::sInstance = new JPH::Factory();
// Install trace and assert callbacks
JPH::Trace = TraceImpl;
JPH_IF_ENABLE_ASSERTS(JPH::AssertFailed = AssertFailedImpl;)