Updated lots of things

This commit is contained in:
2025-10-22 16:39:19 +03:00
parent 9c4bea5983
commit 3f0484e87c
56 changed files with 4497 additions and 160 deletions

View File

@@ -43,12 +43,32 @@ BoatModule::BoatModule(flecs::world &ecs)
b2e.entities[body.body] = e;
} else if (type.resourceName.find(".scene") !=
std::string::npos) {
int i;
std::vector<Ogre::SceneNode *> colliderTarget;
boat.mNode =
ECS::get<EngineData>()
.mScnMgr->getRootSceneNode()
->createChildSceneNode(
type.position,
type.orientation);
auto pnodes = boat.mNode->getChildren();
for (i = 0; i < pnodes.size(); i++) {
Ogre::SceneNode *pnode =
static_cast<Ogre::SceneNode *>(
pnodes[i]);
Ogre::Any any =
pnode->getUserObjectBindings()
.getUserAny("type");
if (!any.has_value())
continue;
Ogre::String obj_type =
Ogre::any_cast<Ogre::String>(
any);
if (obj_type == "hull-collider") {
/* FIXME */
}
}
Ogre::SceneNode *attachment =
eng.mScnMgr->getRootSceneNode()
->createChildSceneNode();
@@ -63,14 +83,15 @@ BoatModule::BoatModule(flecs::world &ecs)
// attachment->loadChildren(type.resourceName);
std::vector<Ogre::Node *> v =
attachment->getChildren();
int i;
OgreAssert(v.size() == 1, "Bad nodes count");
OgreAssert(v.size() == 1,
"Bad root nodes count in " +
type.resourceName);
Ogre::Any any =
static_cast<Ogre::SceneNode *>(v[0])
->getUserObjectBindings()
.getUserAny("type");
OgreAssert(any.has_value(),
"bas node type costom prop");
"no \"type\" costom prop");
Ogre::String obj_type =
Ogre::any_cast<Ogre::String>(any);
std::cout << "type: " << obj_type << std::endl;
@@ -89,7 +110,7 @@ BoatModule::BoatModule(flecs::world &ecs)
ECS::get<EngineData>()
.mWorld->addRigidBody(
0, boat.mEnt,
Ogre::Bullet::CT_HULL,
Ogre::Bullet::CT_TRIMESH,
nullptr, 2, 0x7fffffff);
b2e.entities[body.body] = e;
std::vector<Ogre::Node *> slots =
@@ -149,4 +170,4 @@ BoatModule::BoatModule(flecs::world &ecs)
e.modified<BoatBody>();
});
}
}
}

View File

@@ -1,6 +1,9 @@
project(gamedata)
find_package(OGRE REQUIRED COMPONENTS Bites Bullet Paging Terrain Overlay CONFIG)
find_package(Bullet REQUIRED)
add_library(GameData STATIC GameData.cpp CharacterModule.cpp WaterModule.cpp SunModule.cpp TerrainModule.cpp GUIModule.cpp LuaData.cpp WorldMapModule.cpp
BoatModule.cpp EventTriggerModule.cpp CharacterAnimationModule.cpp)
target_link_libraries(GameData PUBLIC OgreMain OgreBites OgreBullet OgrePaging OgreTerrain OgreOverlay flecs::flecs_static lua PRIVATE sceneloader)
target_include_directories(GameData PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
BoatModule.cpp EventTriggerModule.cpp CharacterAnimationModule.cpp SmartObject.cpp goap.cpp)
target_link_libraries(GameData PUBLIC OgreMain OgreBites OgreBullet
OgrePaging OgreTerrain OgreOverlay flecs::flecs_static
lua ${BULLET_LIBRARIES} PRIVATE sceneloader world-build)
target_include_directories(GameData PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${BULLET_INCLUDE_DIR})

View File

@@ -3,6 +3,7 @@
#include "EventTriggerModule.h"
#include "CharacterModule.h"
#include "CharacterAnimationModule.h"
#include "world-build.h"
namespace ECS
{
CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
@@ -29,7 +30,9 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
"swimming-edge-climb",
"character-talk",
"pass-character",
"idle-act"
"idle-act",
"sitting-chair",
"sitting-ground"
};
int state_count = sizeof(animNames) /
sizeof(animNames[0]);
@@ -107,6 +110,12 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
->state("character-talk")
->animation("character-talk")
->end()
->state("sitting")
->animation("sitting-chair")
->end()
->state("sitting-ground")
->animation("sitting-ground")
->end()
->transition_end("swimming-edge-climb", "idle")
->transition_end("hanging-climb", "idle")
->transition_end("pass-character", "idle")
@@ -646,5 +655,47 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
result.m_collisionObject)));
}
});
struct AnimationSetCommand : public GameWorld::Command {
int operator()(const std::vector<GameWorld::Parameter *> &args)
override
{
GameWorld::ValueParameter<flecs::entity> *param_e =
static_cast<GameWorld::ValueParameter<
flecs::entity> *>(args[0]);
OgreAssert(param_e->get().is_valid(), "bad entity");
GameWorld::ValueParameter<std::string> *param_node =
static_cast<GameWorld::ValueParameter<
std::string> *>(args[1]);
GameWorld::ValueParameter<std::string> *param_state =
static_cast<GameWorld::ValueParameter<
std::string> *>(args[2]);
if (param_e->get().has<AnimationControl>() &&
param_e->get().get<AnimationControl>().configured ==
true) {
const AnimationControl &control =
param_e->get().get<AnimationControl>();
AnimationNodeStateMachine *sm =
control.mAnimationSystem
->get<AnimationNodeStateMachine>(
param_node->get());
bool reset = false;
if (args.size() == 4) {
GameWorld::ValueParameter<bool>
*param_reset = static_cast<
GameWorld::ValueParameter<
bool> *>(
args[3]);
reset = param_reset->get();
}
sm->setAnimation(param_state->get(), reset);
std::cout << "animation switch: "
<< param_node->get() << " "
<< param_state->get() << std::endl;
}
return 0;
}
};
ECS::get_mut<GameWorld>().add_command<AnimationSetCommand>(
"set_animation_state");
}
}

View File

@@ -893,6 +893,9 @@ struct AnimationControl {
bool configured;
AnimationSystem *mAnimationSystem;
};
struct DefaultAnimation {
std::vector<std::pair<std::string, std::string> > animations;
};
struct CharacterAnimationModule {
CharacterAnimationModule(flecs::world &ecs);
};

View File

@@ -7,6 +7,7 @@
#include "Components.h"
#include "CharacterAnimationModule.h"
#include "CharacterModule.h"
#include "goap.h"
namespace ECS
{
CharacterModule::CharacterModule(flecs::world &ecs)
@@ -21,6 +22,13 @@ CharacterModule::CharacterModule(flecs::world &ecs)
ecs.component<CharacterDisablePhysics>();
ecs.component<CharacterUpdatePhysicsState>();
ecs.component<CharacterInActuator>();
ecs.component<Blackboard>();
ecs.component<ActionTarget>();
ecs.component<Plan>();
ecs.component<Male>();
ecs.component<Female>();
ecs.component<Planner>().add(flecs::Singleton);
ecs.system<EngineData, CharacterBase>("UpdateTimer")
.kind(flecs::OnUpdate)
.each([this](EngineData &eng, CharacterBase &ch) {
@@ -540,4 +548,182 @@ void CharacterModule::updateCameraGoal(Camera &camera, Ogre::Real deltaYaw,
camera.mCameraGoal->translate(0, 0, distChange,
Ogre::Node::TS_LOCAL);
}
CharacterAIModule::CharacterAIModule(flecs::world &ecs)
{
ecs.module<CharacterAIModule>();
ecs.system<Blackboard>("UpdateCharacters")
.kind(flecs::OnUpdate)
.each([&](flecs::entity e, Blackboard &bb) {
bb.flags &=
~(Blackboard::LOW_HEALTH |
Blackboard::FULL_HEALTH |
Blackboard::LOW_STAMINA |
Blackboard::FULL_STAMINA |
Blackboard::LOW_LUST | Blackboard::HIGH_LUST |
Blackboard::FULL_LUST);
if (bb.health < 5)
bb.flags |= Blackboard::LOW_HEALTH;
else if (bb.health >= 100)
bb.flags |= Blackboard::FULL_HEALTH;
if (bb.stamina < 5)
bb.flags |= Blackboard::LOW_STAMINA;
if (bb.stamina >= 100)
bb.flags |= Blackboard::FULL_STAMINA;
if (bb.lust >= 100)
bb.flags |= Blackboard::FULL_LUST;
if (bb.lust > 10)
bb.flags |= Blackboard::HIGH_LUST;
if (bb.lust < 5)
bb.flags |= Blackboard::LOW_LUST;
if (bb.stamina < 0)
bb.stamina = 0;
else if (bb.stamina > 100)
bb.stamina = 100;
if (bb.lust < 0)
bb.lust = 0;
else if (bb.lust > 100)
bb.lust = 100;
});
ecs.system<Blackboard, Plan>("UpdateCharactersPlan2")
.kind(flecs::OnUpdate)
.each([&](flecs::entity e, Blackboard &bb, Plan &p) {
int i;
bool ok_plan = true;
for (i = p.position; i < p.length; i++) {
if (!p.actions[i]->can_run(bb, true)) {
ok_plan = false;
break;
}
int ret = p.actions[i]->execute(bb);
p.position = i;
if (ret == BaseAction::BUSY)
break;
else if (ret == BaseAction::ABORT) {
ok_plan = false;
break;
}
if (ret == BaseAction::OK && i == p.length - 1)
ok_plan = false;
// stop_this = true;
}
if (!ok_plan) {
std::cout << e.name() << ": invalidated plan"
<< " step: " << i << std::endl;
for (i = 0; i < p.length; i++)
p.actions[i]->stop(bb);
e.remove<Plan>();
}
});
ecs.system<Blackboard, Planner>("UpdateCharacters2")
.kind(flecs::OnUpdate)
.without<Plan>()
.each([&](flecs::entity e, Blackboard &bb, Planner &planner) {
int i;
std::vector<BaseAction *> actions;
actions.insert(actions.end(), planner.actions.begin(),
planner.actions.end());
e.world()
.query_builder<const ActionTarget>()
.build()
.each([&](flecs::entity me,
const ActionTarget &t) {
actions.insert(actions.end(),
t.actions.begin(),
t.actions.end());
#if 0
auto it = t.actions.begin();
while (it != t.actions.end()) {
if (me != bb.me)
actions.push_back(*it);
// std::cout << (*it)->get_name()
// << std::endl;
it++;
}
#endif
});
#if 0
for (i = 0; i < actions.size(); i++)
std::cout << "action: " << i << " "
<< actions[i]->get_name()
<< std::endl;
#endif
if (actions.size() == 0)
return;
int len = -1;
OgreAssert(
bb.stamina < 100 ||
bb.stamina >= 100 &&
!bb.get_flag(
Blackboard::LOW_STAMINA),
"bad thing");
for (i = 0; i < planner.goals.size(); i++) {
if (planner.goals[i]->distance_to(bb) > 1000000)
continue;
len = planner.planner.plan(
bb, *planner.goals[i], actions.data(),
actions.size(), planner.path.data(),
100);
std::cout << bb.me.name() << ": goal: " << len
<< " " << planner.goals[i]->get_name()
<< std::endl;
if (len > 0)
break;
}
// std::cout << "plan length: " << len << std::endl;
#if 0
if (len > 0)
stop_this = true;
if (len < 0)
stop_this = true;
#endif
if (len > 0) {
Plan &p = e.ensure<Plan>();
p.actions = planner.path;
p.position = 0;
p.length = len;
for (i = 0; i < len; i++)
std::cout
<< i << " "
<< planner.path[i]->get_name()
<< " "
<< planner.path[i]->get_cost(bb)
<< std::endl;
bool ok_plan = true;
for (i = 0; i < len; i++) {
if (!planner.path[i]->can_run(bb,
true)) {
ok_plan = false;
break;
}
int ret = planner.path[i]->execute(bb);
p.position = i;
std::cout << "exec: " << i << " "
<< planner.path[i]->get_name()
<< std::endl;
if (ret == BaseAction::BUSY)
break;
else if (ret == BaseAction::ABORT) {
ok_plan = false;
} else if (ret == BaseAction::OK)
std::cout
<< "exec: complete "
<< i << " "
<< planner.path[i]
->get_name()
<< std::endl;
}
e.modified<Plan>();
if (!ok_plan) {
std::cout << e.name()
<< ": invalidate plan"
<< " step: " << i
<< std::endl;
for (i = 0; i < len; i++)
planner.path[i]->stop(bb);
e.remove<Plan>();
}
}
});
}
}

View File

@@ -1,6 +1,7 @@
#ifndef CHARACTER_MODULE_H_
#define CHARACTER_MODULE_H_
#include <flecs.h>
#include "goap.h"
namespace ECS
{
struct Camera;
@@ -11,6 +12,9 @@ struct CharacterGravity {};
struct CharacterBuoyancy {};
struct CharacterDisablePhysics {};
struct CharacterUpdatePhysicsState {};
struct Male {};
struct Female {};
struct CharacterBase {
Ogre::String type;
float mTimer;
@@ -45,10 +49,28 @@ struct CharacterInActuator {
Ogre::String animationState;
Vector3 prevMotion;
};
struct ActionTarget {
std::vector<BaseAction *> actions;
};
struct Plan {
std::vector<BaseAction *> actions;
int position;
int length;
};
struct Planner {
BasePlanner<ECS::Blackboard, BaseAction> planner;
std::vector<BaseAction *> path;
std::vector<BasePlanner<ECS::Blackboard, BaseAction>::BaseGoal *> goals;
std::vector<BaseAction *> actions;
};
struct CharacterModule {
CharacterModule(flecs::world &ecs);
void updateCameraGoal(Camera &camera, Ogre::Real deltaYaw,
Ogre::Real deltaPitch, Ogre::Real deltaZoom);
};
struct CharacterAIModule {
CharacterAIModule(flecs::world &ecs);
};
}
#endif

View File

@@ -0,0 +1,11 @@
#ifndef EVENT_SYSTEM_H_
#define EVENT_SYSTEM_H_
struct EventSystem {
struct CommonEvent {
int event_type;
};
void send_event(const EventSystem &event)
{
}
};
#endif

View File

@@ -127,11 +127,13 @@ ECS::EventTriggerModule::EventTriggerModule(flecs::world &ecs)
body.mSceneNode->getUserObjectBindings().setUserAny(
"BtCollisionObject", objWrapper);
});
ecs.component<EventTrigger>().on_set(
[](flecs::entity e, EventTrigger &ev) {
e.add<TriggerBody>();
e.set<EventTriggerData>({});
});
ecs.component<EventTrigger>().on_set([](flecs::entity e,
EventTrigger &ev) {
e.add<TriggerBody>();
e.set<EventTriggerData>({});
ECS::get<LuaBase>().mLua->call_handler("actuator_created", e,
e);
});
ecs.system<const EngineData, const EventTrigger, TriggerBody,
EventTriggerData>("CheckCollisions")
.kind(flecs::OnUpdate)

View File

@@ -12,20 +12,19 @@
#include "BoatModule.h"
#include "EventTriggerModule.h"
#include "CharacterAnimationModule.h"
#include "world-build.h"
namespace ECS
{
static flecs::world ecs;
flecs::entity player;
void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world,
Ogre::SceneNode *cameraNode, Ogre::Camera *camera,
Ogre::RenderWindow *window)
void setup_minimal()
{
std::cout << "Setup GameData\n";
ecs.component<EngineData>().add(flecs::Singleton);
ecs.component<GameData>().add(flecs::Singleton);
ecs.component<Input>().add(flecs::Singleton);
ecs.component<Camera>().add(flecs::Singleton);
ecs.import <GameWorldModule>();
ecs.component<InWater>();
ecs.component<WaterReady>().add(flecs::Singleton);
ecs.component<GroundCheckReady>().add(flecs::Singleton);
@@ -41,6 +40,13 @@ void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world,
ecs.component<ParentSlot>();
ecs.component<ObjectSlots>();
ecs.component<Body2Entity>().add(flecs::Singleton);
}
void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world,
Ogre::SceneNode *cameraNode, Ogre::Camera *camera,
Ogre::RenderWindow *window)
{
std::cout << "Setup GameData\n";
setup_minimal();
ecs.import <WaterModule>();
ecs.import <CharacterModule>();
ecs.import <TerrainModule>();

View File

@@ -5,6 +5,7 @@
namespace ECS
{
extern flecs::entity player;
void setup_minimal();
void setup(Ogre::SceneManager *scnMgr, Ogre::Bullet::DynamicsWorld *world,
Ogre::SceneNode *cameraNode, Ogre::Camera *camera,
Ogre::RenderWindow *window);

View File

@@ -3,8 +3,10 @@
#include "Components.h"
#include "GUIModule.h"
#include "CharacterModule.h"
#include "CharacterAnimationModule.h"
#include "BoatModule.h"
#include "EventTriggerModule.h"
#include "world-build.h"
#include "LuaData.h"
extern "C" {
@@ -258,35 +260,6 @@ LuaData::LuaData()
return 0;
});
lua_setglobal(L, "main_menu");
lua_pushcfunction(L, [](lua_State *L) -> int {
OgreAssert(lua_gettop(L) == 3, "Invalid parameters");
luaL_checktype(L, 1, LUA_TNUMBER);
luaL_checktype(L, 2, LUA_TSTRING);
luaL_checktype(L, 3, LUA_TBOOLEAN);
bool enable = lua_toboolean(L, 3);
flecs::entity e = idmap.get_entity(lua_tointeger(L, 1));
Ogre::String what = lua_tostring(L, 2);
OgreAssert(e.is_valid(), "Invalid character");
OgreAssert(e.has<Character>(), "Not a character");
if (what == "gravity") {
/* clear momentum */
e.get_mut<CharacterVelocity>().gvelocity.y = 0.0f;
e.get_mut<CharacterVelocity>().velocity.y = 0.0f;
e.modified<CharacterVelocity>();
if (enable)
e.add<CharacterGravity>();
else
e.remove<CharacterGravity>();
} else if (what == "buoyancy") {
if (enable)
e.add<CharacterBuoyancy>();
else
e.remove<CharacterBuoyancy>();
} else
OgreAssert(false, "Bad parameter " + what);
return 0;
});
lua_setglobal(L, "ecs_character_params_set");
lua_pushcfunction(L, [](lua_State *L) -> int {
luaL_checktype(L, 1, LUA_TSTRING);
luaL_checktype(L, 2, LUA_TNUMBER);
@@ -476,44 +449,114 @@ LuaData::LuaData()
});
lua_setglobal(L, "ecs_set_debug_drawing");
lua_pushcfunction(L, [](lua_State *L) -> int {
OgreAssert(lua_gettop(L) == 2, "Bad parameters");
luaL_checktype(L, 1, LUA_TNUMBER); // object
luaL_checktype(L, 2, LUA_TBOOLEAN);
int object = lua_tointeger(L, 1);
flecs::entity object_e = idmap.get_entity(object);
bool enable = lua_toboolean(L, 2);
if (enable)
object_e.remove<CharacterDisablePhysics>();
else
object_e.add<CharacterDisablePhysics>();
object_e.add<CharacterUpdatePhysicsState>();
return 0;
});
lua_setglobal(L, "ecs_character_physics_control");
lua_pushcfunction(L, [](lua_State *L) -> int {
luaL_checktype(L, 1, LUA_TNUMBER); // object
int object = lua_tointeger(L, 1);
flecs::entity object_e = idmap.get_entity(object);
lua_pushboolean(L, object_e.has<Player>());
return 1;
});
lua_setglobal(L, "ecs_character_is_player");
lua_pushcfunction(L, [](lua_State *L) -> int {
luaL_checktype(L, 1, LUA_TNUMBER); // object
luaL_checktype(L, 2, LUA_TSTRING); // animation
Ogre::String animation = lua_tostring(L, 2);
OgreAssert(lua_gettop(L) >= 1, "Bad parameters");
luaL_checktype(L, 1, LUA_TSTRING);
Ogre::String command = lua_tostring(L, 1);
if (command == "physics-control") {
OgreAssert(lua_gettop(L) == 3, "Bad parameters");
luaL_checktype(L, 2, LUA_TNUMBER); // object
luaL_checktype(L, 3, LUA_TBOOLEAN);
int object = lua_tointeger(L, 2);
flecs::entity object_e = idmap.get_entity(object);
bool enable = lua_toboolean(L, 3);
if (enable)
object_e.remove<CharacterDisablePhysics>();
else
object_e.add<CharacterDisablePhysics>();
object_e.add<CharacterUpdatePhysicsState>();
return 0;
} else if (command == "is-player") {
OgreAssert(lua_gettop(L) == 2, "Bad parameters");
luaL_checktype(L, 2, LUA_TNUMBER); // object
int object = lua_tointeger(L, 2);
flecs::entity object_e = idmap.get_entity(object);
lua_pushboolean(L, object_e.has<Player>());
return 1;
} else if (command == "set-actuator") {
OgreAssert(lua_gettop(L) == 3, "Bad parameters");
luaL_checktype(L, 2, LUA_TNUMBER); // object
luaL_checktype(L, 3, LUA_TSTRING); // animation
Ogre::String animation = lua_tostring(L, 3);
int object = lua_tointeger(L, 1);
flecs::entity object_e = idmap.get_entity(object);
object_e.set<CharacterVelocity>({ { 0, 0, 0 }, { 0, 0, 0 } });
if (animation.length() > 0)
object_e.set<CharacterInActuator>(
{ animation, { 0, 0, 0 } });
else
object_e.remove<CharacterInActuator>();
return 0;
int object = lua_tointeger(L, 2);
flecs::entity object_e = idmap.get_entity(object);
object_e.set<CharacterVelocity>(
{ { 0, 0, 0 }, { 0, 0, 0 } });
if (animation.length() > 0)
object_e.set<CharacterInActuator>(
{ animation, { 0, 0, 0 } });
else
object_e.remove<CharacterInActuator>();
return 0;
} else if (command == "animation-state") {
OgreAssert(lua_gettop(L) >= 4, "Bad parameters");
luaL_checktype(L, 2, LUA_TNUMBER); // object
luaL_checktype(L, 3, LUA_TSTRING); // node
luaL_checktype(L, 4, LUA_TSTRING); // state
if (lua_gettop(L) == 5)
luaL_checktype(L, 5, LUA_TBOOLEAN); // reset
int object = lua_tointeger(L, 2);
flecs::entity object_e = idmap.get_entity(object);
Ogre::String nodeName = lua_tostring(L, 3);
Ogre::String stateName = lua_tostring(L, 4);
bool reset = false;
if (lua_gettop(L) == 5)
reset = lua_toboolean(L, 5);
GameWorld::ValueParameter<flecs::entity> *obj =
object_e.world()
.get_mut<GameWorld>()
.allocate_entity(object_e);
GameWorld::ValueParameter<std::string> *obj_node =
object_e.world()
.get_mut<GameWorld>()
.allocate_string(nodeName);
GameWorld::ValueParameter<std::string> *obj_state =
object_e.world()
.get_mut<GameWorld>()
.allocate_string(stateName);
ECS::get_mut<GameWorld>().command("set_animation_state",
{ obj, obj_node,
obj_state });
ECS::modified<GameWorld>();
return 0;
} else if (command == "params-set") {
OgreAssert(lua_gettop(L) == 4, "Invalid parameters");
luaL_checktype(L, 2, LUA_TNUMBER);
luaL_checktype(L, 3, LUA_TSTRING);
luaL_checktype(L, 4, LUA_TBOOLEAN);
bool enable = lua_toboolean(L, 4);
flecs::entity e = idmap.get_entity(lua_tointeger(L, 2));
Ogre::String what = lua_tostring(L, 3);
OgreAssert(e.is_valid(), "Invalid character");
OgreAssert(e.has<Character>(), "Not a character");
if (what == "gravity") {
/* clear momentum */
if (e.has<CharacterVelocity>()) {
e.get_mut<CharacterVelocity>()
.gvelocity.y = 0.0f;
e.get_mut<CharacterVelocity>()
.velocity.y = 0.0f;
e.modified<CharacterVelocity>();
}
if (enable)
e.add<CharacterGravity>();
else
e.remove<CharacterGravity>();
} else if (what == "buoyancy") {
if (enable)
e.add<CharacterBuoyancy>();
else
e.remove<CharacterBuoyancy>();
} else
OgreAssert(false, "Bad parameter " + what);
return 0;
} else {
OgreAssert(false, "bad argument " + command);
return 0;
}
});
lua_setglobal(L, "ecs_character_set_actuator");
lua_setglobal(L, "ecs_character");
lua_pushcfunction(L, [](lua_State *L) -> int {
OgreAssert(lua_gettop(L) == 3, "Bad parameters");
luaL_checktype(L, 1, LUA_TNUMBER); // parent
@@ -693,6 +736,7 @@ void LuaData::lateSetup()
LuaModule::LuaModule(flecs::world &ecs)
{
ecs.module<LuaModule>();
ecs.component<LuaChildEventTrigger>();
ecs.component<LuaBase>()
.on_add([](LuaBase &lua) {

View File

@@ -0,0 +1,10 @@
#include "SmartObject.h"
namespace ECS
{
SmartObjectModule::SmartObjectModule(flecs::world &ecs)
{
ecs.module<SmartObjectModule>();
ecs.component<SmartObjectManager>().add(flecs::Singleton);
ecs.component<SmartObject>();
}
}

View File

@@ -0,0 +1,19 @@
#ifndef SMART_OBJECT_H_
#define SMART_OBJECT_H_
#include <Ogre.h>
#include <flecs.h>
namespace ECS
{
struct SmartObject {
Ogre::String enterNodeName;
Ogre::String exitNodeName;
Ogre::String scenario;
};
struct SmartObjectManager {
std::vector<flecs::entity> targets;
};
struct SmartObjectModule {
SmartObjectModule(flecs::world &ecs);
};
}
#endif

38
src/gamedata/goap.cpp Normal file
View File

@@ -0,0 +1,38 @@
#include <set>
#include "goap.h"
bool BaseAction::can_run(const ECS::Blackboard &state, bool debug)
{
return m_exec->_can_run(state, debug);
}
void BaseAction::_plan_effects(ECS::Blackboard &state)
{
state.clear_flag(m_exec->m_clear_bits);
state.set_flag(m_exec->m_set_bits);
}
bool BaseAction::is_active(const ECS::Blackboard &state)
{
return m_exec->is_active(state);
}
bool BaseAction::stop(ECS::Blackboard &state)
{
if (!is_active(state))
return false;
m_exec->_exit(state);
state._active.erase(m_exec.get());
return true;
}
int BaseAction::execute(ECS::Blackboard &state)
{
if (!is_active(state)) {
state._active.insert(m_exec.get());
m_exec->_enter(state);
}
int ret = m_exec->_execute(state);
if (ret == OK)
stop(state);
return ret;
}
int BaseAction::get_cost(const ECS::Blackboard &state) const
{
return m_exec->_get_cost(state);
}

349
src/gamedata/goap.h Normal file
View File

@@ -0,0 +1,349 @@
#ifndef H_GOAP_H_
#define H_GOAP_H_
#include <string>
#include <memory>
#include <flecs.h>
namespace ECS
{
struct Blackboard;
}
struct BaseAction;
struct BaseActionExec;
namespace ECS
{
struct Blackboard {
enum {
HIGH_LUST = (1 << 0),
LOW_LUST = (1 << 1),
FULL_LUST = (1 << 2),
LOW_HEALTH = (1 << 3),
FULL_HEALTH = (1 << 4),
HAS_TARGET = (1 << 5),
LOW_STAMINA = (1 << 6),
FULL_STAMINA = (1 << 7),
REACHED_TARGET = (1 << 8),
};
flecs::entity me;
int health;
int stamina;
int lust;
uint32_t flags;
std::set<BaseActionExec *> _active;
bool operator==(const Blackboard &other)
{
return flags == other.flags;
}
void set_flag(int flag)
{
flags |= flag;
}
void clear_flag(int flag)
{
flags &= ~flag;
}
bool get_flag(int flag) const
{
return flags & flag;
}
bool check_flag(int flag) const
{
return (flags & flag) == flag;
}
};
}
struct BaseAction {
std::string m_name;
public:
enum { OK = 0, BUSY, ABORT };
private:
std::unique_ptr<BaseActionExec> m_exec;
public:
BaseAction(const std::string &name, BaseActionExec *exec)
: m_name(name)
, m_exec(exec)
{
}
const std::string &get_name() const
{
return m_name;
}
void plan_effects(ECS::Blackboard &state)
{
// std::cout << m_name << " pre: " << &state << " " << state.flags
// << std::endl;
_plan_effects(state);
// std::cout << m_name << " post: " << &state << " " << state.flags
// << std::endl;
}
virtual bool can_run(const ECS::Blackboard &state, bool debug = false);
virtual void _plan_effects(ECS::Blackboard &state);
bool is_active(const ECS::Blackboard &state);
bool stop(ECS::Blackboard &state);
int execute(ECS::Blackboard &state);
virtual int get_cost(const ECS::Blackboard &state) const;
};
struct BaseActionExec {
enum {
OK = BaseAction::OK,
BUSY = BaseAction::BUSY,
ABORT = BaseAction::ABORT
};
BaseActionExec(int set_bits, int clear_bits)
: m_set_bits(set_bits)
, m_clear_bits(clear_bits)
{
}
bool is_active(const ECS::Blackboard &state)
{
return state._active.find(this) != state._active.end();
}
virtual int _execute(ECS::Blackboard &state) = 0;
virtual void _enter(ECS::Blackboard &state) = 0;
virtual void _exit(ECS::Blackboard &state) = 0;
virtual int _get_cost(const ECS::Blackboard &state) const
{
return 1;
}
virtual bool _can_run(const ECS::Blackboard &state, bool debug = false)
{
return true;
}
int m_set_bits, m_clear_bits;
};
template <typename State, typename Act, int N = 100> class BasePlanner {
struct VisitedState {
int priority;
int cost;
State state;
// Used to reconstruct the path
VisitedState *parent;
Act *action;
// Only used for linked list management
VisitedState *next;
};
VisitedState nodes[N];
void visited_states_array_to_list(VisitedState *nodes, int len)
{
for (auto i = 0; i < len - 1; i++) {
nodes[i].next = &nodes[i + 1];
}
nodes[len - 1].next = nullptr;
}
VisitedState *priority_list_pop(VisitedState *&list)
{
VisitedState *min_elem = nullptr;
auto min_prio = std::numeric_limits<int>::max();
// Find the element with the lowest priority
for (auto p = list; p != nullptr; p = p->next) {
if (p->priority < min_prio) {
min_elem = p;
min_prio = p->priority;
}
}
// Remove it from the list
if (min_elem == list) {
list = list->next;
} else {
for (auto p = list; p != nullptr; p = p->next) {
if (p->next == min_elem) {
p->next = p->next->next;
min_elem->next = nullptr;
}
}
}
return min_elem;
}
VisitedState *list_pop_head(VisitedState *&head)
{
auto ret = head;
head = head->next;
ret->next = nullptr;
return ret;
}
void list_push_head(VisitedState *&head, VisitedState *elem)
{
elem->next = head;
head = elem;
}
void update_queued_state(VisitedState *previous,
const VisitedState *current)
{
if (previous->cost > current->cost) {
previous->priority = current->cost;
previous->priority = current->priority;
previous->parent = current->parent;
previous->action = current->action;
}
}
public:
struct BaseGoal {
std::string m_name;
public:
BaseGoal(const std::string &name)
: m_name(name)
{
}
/** Checks if the goal is reached for the given state. */
virtual bool is_reached(const State &state) const
{
return distance_to(state) == 0;
}
/** Computes the distance from state to goal. */
virtual int distance_to(const State &state) const = 0;
virtual ~BaseGoal() = default;
const std::string &get_name() const
{
return m_name;
}
};
/** Finds a plan from state to goal and returns its length.
*
* If path is given, then the found path is stored there.
*/
int plan(const State &state, BaseGoal &goal, Act *actions[],
unsigned action_count, Act **path = nullptr, int path_len = 10)
{
visited_states_array_to_list(nodes, N);
auto free_nodes = &nodes[0];
VisitedState *open = list_pop_head(free_nodes);
VisitedState *close = nullptr;
open->state = state;
open->cost = 0;
open->priority = 0;
open->parent = nullptr;
open->action = nullptr;
while (open) {
VisitedState *current = priority_list_pop(open);
list_push_head(close, current);
if (goal.is_reached(current->state)) {
auto len = 0;
for (auto p = current->parent; p;
p = p->parent) {
len++;
}
if (len > path_len) {
return -1;
}
if (path) {
auto i = len - 1;
for (auto p = current; p->parent;
p = p->parent, i--) {
path[i] = p->action;
}
}
return len;
}
for (auto i = 0u; i < action_count; i++) {
auto action = actions[i];
if (action->can_run(current->state)) {
// Cannot allocate a new node, abort
if (free_nodes == nullptr) {
// Garbage collect the node that is most unlikely to be
// visited (i.e. lowest priority)
VisitedState *gc,
*gc_prev = nullptr;
for (gc = open; gc && gc->next;
gc = gc->next) {
gc_prev = gc;
}
if (!gc) {
return -2 /* OOM */;
}
if (gc_prev) {
gc_prev->next = nullptr;
}
free_nodes = gc;
}
auto neighbor =
list_pop_head(free_nodes);
neighbor->state = current->state;
action->plan_effects(neighbor->state);
neighbor->cost =
current->cost + 1 +
action->get_cost(
neighbor->state);
neighbor->priority =
current->priority + 1 +
goal.distance_to(
neighbor->state);
neighbor->parent = current;
neighbor->action = action;
bool should_insert = true;
// Check if the node is already in the list of nodes
// scheduled to be visited
for (auto p = open; p; p = p->next) {
if (p->state ==
neighbor->state) {
should_insert = false;
update_queued_state(
p, neighbor);
}
}
// Check if the state is in the list of already visited
// state
for (auto p = close; p; p = p->next) {
if (p->state ==
neighbor->state) {
should_insert = false;
update_queued_state(
p, neighbor);
}
}
if (should_insert) {
list_push_head(open, neighbor);
} else {
list_push_head(free_nodes,
neighbor);
}
}
}
}
// Reaching here means we did not find a path
return -1 /* No path */;
}
};
template <class RunnerType> struct DeclareAction : public BaseAction {
RunnerType runner;
template <class... Args>
DeclareAction(const std::string &name, Args... args)
: runner(args...)
, BaseAction(name, &runner)
{
}
};
#endif