Updated lots of things
This commit is contained in:
@@ -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>();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
11
src/gamedata/EventSystem.h
Normal file
11
src/gamedata/EventSystem.h
Normal 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
|
||||
@@ -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)
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
10
src/gamedata/SmartObject.cpp
Normal file
10
src/gamedata/SmartObject.cpp
Normal 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>();
|
||||
}
|
||||
}
|
||||
19
src/gamedata/SmartObject.h
Normal file
19
src/gamedata/SmartObject.h
Normal 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
38
src/gamedata/goap.cpp
Normal 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
349
src/gamedata/goap.h
Normal 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
|
||||
Reference in New Issue
Block a user