Fixed AIs and crashes

This commit is contained in:
2026-02-21 00:55:39 +03:00
parent eab5ed0794
commit ccf451336d
17 changed files with 627 additions and 129 deletions

View File

@@ -387,14 +387,22 @@ public:
}
void locateResources() override
{
Ogre::ResourceGroupManager::getSingleton().createResourceGroup(
Ogre::ResourceGroupManager::getSingleton().createResourceGroup(
"Characters", true);
Ogre::ResourceGroupManager::getSingleton().createResourceGroup(
"Water", true);
Ogre::ResourceGroupManager::getSingleton().createResourceGroup(
"LuaScripts", false);
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
"./lua-scripts", "FileSystem", "LuaScripts", true,
true);
OgreBites::ApplicationContext::locateResources();
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
"./characters/male", "FileSystem", "Characters", false,
true);
Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
"./characters/female", "FileSystem", "Characters",
false, true);
OgreBites::ApplicationContext::locateResources();
}
void loadResources() override
{

View File

@@ -271,6 +271,7 @@ public:
};
ActionNodeActions(int node, const Blackboard &prereq, int cost)
{
ZoneScoped;
OgreAssert(
node < ECS::get<ActionNodeList>().dynamicNodes.size(),
"bad node " + Ogre::StringConverter::toString(node));
@@ -347,6 +348,7 @@ public:
have_bits = true;
}
if (!have_bits) {
ZoneScopedN("Use");
std::cout << "use: " << props.dump(4)
<< std::endl;
// OgreAssert(false, "props: " + props.dump(4));
@@ -396,8 +398,8 @@ private:
}
void activate() override
{
std::cout << action->get_name();
std::cout << "!";
ZoneScoped;
ZoneTextF("%s", action->get_name().c_str());
delay = 1.0f;
}
@@ -414,6 +416,7 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs)
static std::mutex ecs_mutex;
ecs.module<CharacterAIModule>();
ecs.import <CharacterManagerModule>();
ecs.import <PlayerActionModule>();
ecs.component<Blackboard>();
ecs.component<TownAI>().on_add([](flecs::entity e, TownAI &ai) {
std::lock_guard<std::mutex> lock(ecs_mutex);
@@ -512,6 +515,7 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs)
const TownNPCs &npcs) {
ZoneScopedN("CreateBlackboards");
std::lock_guard<std::mutex> lock(ecs_mutex);
OgreAssert(npcs.npcs.size() > 0, "npcs not crated");
createBlackboards(town, npcs, ai);
});
ecs.system<ActionNodeList, TownAI, TownNPCs>("UpdateDynamicActions")
@@ -520,11 +524,15 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs)
TownNPCs &npcs) {
ZoneScopedN("UpdateDynamicActions");
std::lock_guard<std::mutex> lock(ecs_mutex);
OgreAssert(npcs.npcs.size() > 0, "npcs not crated");
if (ai.nodeActions.size() > 0)
return;
if (alist.dynamicNodes.size() == 0)
ECS::get_mut<ActionNodeList>()
.updateDynamicNodes();
OgreAssert(alist.nodes.size() > 0, "bad nodes");
if (alist.dynamicNodes.size() == 0)
return;
OgreAssert(alist.dynamicNodes.size() > 0,
"bad dynamic nodes");
int nodeIndex;
@@ -550,6 +558,7 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs)
.each([this](flecs::entity town, ActionNodeList &alist,
TownAI &ai, TownNPCs &npcs) {
ZoneScopedN("UpdateDynamicNodes");
OgreAssert(npcs.npcs.size() > 0, "npcs not crated");
std::lock_guard<std::mutex> lock(ecs_mutex);
ECS::get_mut<ActionNodeList>().updateDynamicNodes();
});
@@ -574,6 +583,7 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs)
.kind(flecs::OnUpdate)
.each([](flecs::entity e, TownNPCs &npcs) {
ZoneScopedN("UpdateNPCPositions");
OgreAssert(npcs.npcs.size() > 0, "npcs not crated");
for (auto it = npcs.npcs.begin(); it != npcs.npcs.end();
it++) {
auto &npc = npcs.npcs.at(it->first);
@@ -591,6 +601,7 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs)
.each([this](flecs::entity town, ActionNodeList &alist,
TownAI &ai, const TownNPCs &npcs) {
ZoneScopedN("UpdateBlackboards");
OgreAssert(npcs.npcs.size() > 0, "npcs not crated");
Ogre::Root::getSingleton().getWorkQueue()->addTask([this,
town,
@@ -624,6 +635,10 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs)
.each([&](flecs::entity town, TownAI &ai,
const TownNPCs &npcs) {
ZoneScopedN("PlanAI");
OgreAssert(npcs.npcs.size() > 0, "npcs not crated");
OgreAssert(ai.blackboards.size() > 0,
"blackboards not crated");
OgreAssert(ai.memory.size() > 0, "memory not crated");
Ogre::Root::getSingleton().getWorkQueue()->addTask([this,
town,
npcs,
@@ -646,6 +661,10 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs)
.each([&](flecs::entity town, const EngineData &eng,
TownNPCs &npcs, TownAI &ai) {
ZoneScopedN("RunPLAN");
OgreAssert(npcs.npcs.size() > 0, "npcs not crated");
OgreAssert(ai.blackboards.size() > 0,
"blackboards not crated");
OgreAssert(ai.memory.size() > 0, "memory not crated");
for (const auto &plans : ai.plans) {
if (plan_exec.find(plans.first) !=
plan_exec.end()) {
@@ -668,13 +687,19 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs)
// std::cout << " Goal: ";
plan.goal->goal.dump_bits();
for (const auto &action : plan.plan) {
ActionExec::PlanExecData data({
TownNPCs::NPCData &npc =
npcs.npcs.at(
plans.first),
plans.first);
Blackboard &bb =
ai.blackboards.at(
plans.first),
plans.first);
nlohmann::json &mem =
ai.memory.at(
plans.first),
plans.first);
ActionExec::PlanExecData data({
npc,
bb,
mem,
});
// TODO: executor factory is needed
@@ -773,19 +798,26 @@ void CharacterAIModule::buildPlans(flecs::entity town, const TownNPCs &npcs,
if (plan_tasks.size() > 0) {
bool created = (plan_tasks.front())();
if (created) {
std::cout << plan_tasks.front().blackboard.index << " ";
std::cout << "Goal: "
<< plan_tasks.front().goal.get_name();
plan_tasks.front().goal.goal.dump_bits();
std::cout << std::endl;
std::cout << "Path: ";
for (auto &action : plan_tasks.front().plan.plan) {
OgreAssert(action, "No action");
std::cout << action->get_name() + " ";
ZoneTextF("%d: Goal: %s",
plan_tasks.front().blackboard.index,
plan_tasks.front().goal.get_name().c_str());
{
std::cout << plan_tasks.front().blackboard.index
<< " ";
std::cout << "Goal: "
<< plan_tasks.front().goal.get_name();
plan_tasks.front().goal.goal.dump_bits();
std::cout << std::endl;
std::cout << "Path: ";
for (auto &action :
plan_tasks.front().plan.plan) {
OgreAssert(action, "No action");
std::cout << action->get_name() + " ";
}
std::cout << " size: "
<< plan_tasks.front().plan.plan.size()
<< std::endl;
}
std::cout << " size: "
<< plan_tasks.front().plan.plan.size()
<< std::endl;
ai.plans[plan_tasks.front().blackboard.index].push_back(
plan_tasks.front().plan);
}

View File

@@ -88,12 +88,12 @@ void createNPCActionNodes(flecs::entity town, int index)
CharacterManagerModule::CharacterManagerModule(flecs::world &ecs)
{
ecs.module<CharacterManagerModule>();
ecs.import <CharacterModule>();
ecs.component<TownNPCs>();
ecs.import <CharacterModule>();
ecs.import <CharacterAnimationModule>();
ecs.import <PhysicsModule>();
ecs.import <PlayerActionModule>();
ecs.component<TownCharacterHolder>();
ecs.component<TownNPCs>();
ecs.component<LivesIn>();
ecs.system<TerrainItem, TownNPCs>("UpdateCharacters")
.kind(flecs::OnUpdate)
@@ -178,15 +178,17 @@ CharacterManagerModule::createPlayer(const Ogre::Vector3 &position,
OgreAssert(!player.is_valid(), "Player already created");
player = ECS::get().entity("player");
OgreAssert(player.is_valid(), "Can't create player");
std::cout << "Begin player create" << std::endl;
player.add<Player>();
ECS::get_mut<CharacterModule>().createCharacter(
player, position, rotation, "male_Face.mesh",
"male_Hair001.mesh", "male_BodyTop.mesh",
"male_BodyBottom.mesh", "male_BodyFeet.mesh");
ECS::modified<CharacterModule>();
std::cout << "End player create" << std::endl;
count++;
{
ZoneScopedN("PlayerCreate");
player.add<Player>();
ECS::get_mut<CharacterModule>().createCharacter(
player, position, rotation, "male_Face.mesh",
"male_Hair001.mesh", "male_BodyTop.mesh",
"male_BodyBottom.mesh", "male_BodyFeet.mesh");
ECS::modified<CharacterModule>();
count++;
}
return player;
}
@@ -221,41 +223,33 @@ void CharacterManagerModule::registerTownCharacters(flecs::entity town)
nlohmann::json npcs = nlohmann::json::array();
if (town.has<TownNPCs>())
return;
if (j.find("npcs") != j.end())
npcs = j["npcs"];
if (j.find("npcs") == j.end())
return;
npcs = j["npcs"];
std::cout << npcs.dump(4) << std::endl;
if (npcs.size() == 0)
return;
int index = 0;
std::map<int, TownNPCs::NPCData> npcMap;
for (auto &npc : npcs) {
struct desc {
const char *face, *hair, *top, *bottom, *feet;
};
struct desc models[] = {
{ "male_Face.mesh", "male_Hair001.mesh",
"male_BodyTop.mesh", "male_BodyBottom.mesh",
"male_BodyFeet.mesh" },
{ "female_Face.mesh", "female_Hair001.mesh",
"female_BodyTop.mesh", "female_BodyBottom.mesh",
"female_BodyFeet.mesh" }
};
int sex = npc["sex"].get<int>();
Ogre::Vector3 npcPosition;
Ogre::Quaternion npcOrientation;
from_json(npc["position"], npcPosition);
from_json(npc["orientation"], npcOrientation);
TownNPCs::NPCData npcData;
npcData.e = flecs::entity();
npcData.modelFace = models[sex].face;
npcData.modelHair = models[sex].hair;
npcData.modelTop = models[sex].top;
npcData.modelBottom = models[sex].bottom;
npcData.modelFeet = models[sex].feet;
npcData.modelFace = npc["slot_face"].get<Ogre::String>();
npcData.modelHair = npc["slot_hair"].get<Ogre::String>();
npcData.modelTop = npc["slot_top"].get<Ogre::String>();
npcData.modelBottom = npc["slot_bottom"].get<Ogre::String>();
npcData.modelFeet = npc["slot_feet"].get<Ogre::String>();
npcData.orientation = npcOrientation;
npcData.position = npcPosition;
npcData.props = npc;
npcMap[index] = npcData;
index++;
}
OgreAssert(npcMap.size() > 0, "no npcs registered");
town.set<TownNPCs>({ npcMap });
}

View File

@@ -16,7 +16,53 @@ CharacterModule::CharacterModule(flecs::world &ecs)
{
ZoneScoped;
struct TriggerPhysicsChange {};
ecs.module<CharacterModule>();
static std::vector<Ogre::String> part_names;
const std::vector<Ogre::String> &groups =
Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
if (part_names.size() == 0) {
int i;
for (i = 0; i < groups.size(); i++) {
std::vector<Ogre::String> names =
*Ogre::ResourceGroupManager::getSingleton()
.findResourceNames(groups[i],
"body_part_*.json");
part_names.insert(part_names.end(), names.begin(),
names.end());
}
}
body_parts = nlohmann::json::object();
for (auto &g : part_names) {
Ogre::String group = Ogre::ResourceGroupManager::getSingleton()
.findGroupContainingResource(g);
Ogre::DataStreamPtr stream =
Ogre::ResourceGroupManager::getSingleton().openResource(
g, group);
Ogre::String json = stream->getAsString();
nlohmann::json jdata = nlohmann::json::parse(json);
if (jdata.find("age") == jdata.end())
continue;
if (jdata.find("sex") == jdata.end())
continue;
if (jdata.find("slot") == jdata.end())
continue;
if (jdata.find("mesh") == jdata.end())
continue;
Ogre::String age = jdata["age"].get<Ogre::String>();
Ogre::String sex = jdata["sex"].get<Ogre::String>();
Ogre::String slot = jdata["slot"].get<Ogre::String>();
Ogre::String mesh = jdata["mesh"].get<Ogre::String>();
if (body_parts.find(age) == body_parts.end())
body_parts[age] = nlohmann::json::object();
if (body_parts[age].find(sex) == body_parts[age].end())
body_parts[age][sex] = nlohmann::json::object();
if (body_parts[age][sex].find(slot) ==
body_parts[age][sex].end())
body_parts[age][sex][slot] = nlohmann::json::array();
body_parts[age][sex][slot].push_back(mesh);
Ogre::MeshManager::getSingleton().load(mesh, "Characters");
}
std::cout << body_parts.dump(4) << std::endl;
ecs.module<CharacterModule>();
ecs.component<Character>();
ecs.component<Player>();
ecs.component<CharacterBase>()
@@ -701,4 +747,19 @@ void CharacterModule::remapMeshToMasterSkeleton(Ogre::MeshPtr clothMesh,
clothMesh->setSkeletonName(masterSkel->getName());
clothMesh->_compileBoneAssignments();
}
void CharacterModule::getSlotMeshes(const Ogre::String &age,
const Ogre::String &sex,
const Ogre::String &slotName,
std::vector<Ogre::String> &meshes)
{
OgreAssert(body_parts.find(age) != body_parts.end(), "bad age: " + age);
OgreAssert(body_parts[age].find(sex) != body_parts[age].end(),
"bad sex: " + sex);
OgreAssert(body_parts[age][sex].find(slotName) !=
body_parts[age][sex].end(),
"bad slot: " + slotName);
for (auto &slots : body_parts[age][sex][slotName])
meshes.push_back(slots.get<Ogre::String>());
}
}

View File

@@ -1,6 +1,7 @@
#ifndef CHARACTER_MODULE_H_
#define CHARACTER_MODULE_H_
#include <flecs.h>
#include <nlohmann/json.hpp>
#include <Ogre.h>
#include "Components.h"
namespace ECS
@@ -39,23 +40,36 @@ struct CharacterModule {
void updateCameraGoal(Camera &camera, Ogre::Real deltaYaw,
Ogre::Real deltaPitch, Ogre::Real deltaZoom);
void createCharacter(flecs::entity e, const Ogre::Vector3 &position,
const Ogre::Quaternion &rotation, const Ogre::String &faceModel, const Ogre::String &hairModel,
const Ogre::String &topModel, const Ogre::String &bottomModel, const Ogre::String &feetModel);
const Ogre::Quaternion &rotation,
const Ogre::String &faceModel,
const Ogre::String &hairModel,
const Ogre::String &topModel,
const Ogre::String &bottomModel,
const Ogre::String &feetModel);
std::unordered_map<flecs::entity_t, Ogre::SceneNode *> characterNodes;
std::unordered_map<flecs::entity_t, Ogre::Entity *> characterEntitiesFace;
std::unordered_map<flecs::entity_t, Ogre::Entity *> characterEntitiesHair;
std::unordered_map<flecs::entity_t, Ogre::Entity *>
characterEntitiesFace;
std::unordered_map<flecs::entity_t, Ogre::Entity *>
characterEntitiesHair;
std::unordered_map<flecs::entity_t, Ogre::Entity *> characterEntitiesTop;
std::unordered_map<flecs::entity_t, Ogre::Entity *> characterEntitiesBottom;
std::unordered_map<flecs::entity_t, Ogre::Entity *> characterEntitiesFeet;
std::unordered_map<flecs::entity_t, Ogre::Entity *>
characterEntitiesBottom;
std::unordered_map<flecs::entity_t, Ogre::Entity *>
characterEntitiesFeet;
std::unordered_map<flecs::entity_t, Ogre::String> characterModelsFace;
std::unordered_map<flecs::entity_t, Ogre::String> characterModelsHair;
std::unordered_map<flecs::entity_t, Ogre::String> characterModelsTop;
std::unordered_map<flecs::entity_t, Ogre::String> characterModelsBottom;
std::unordered_map<flecs::entity_t, Ogre::String> characterModelsFeet;
std::unordered_map<flecs::entity_t, Ogre::Vector3> characterPositions;
std::unordered_map<flecs::entity_t, Ogre::Quaternion> characterOrientations;
std::unordered_map<flecs::entity_t, Ogre::Quaternion>
characterOrientations;
nlohmann::json body_parts;
void remapMeshToMasterSkeleton(Ogre::MeshPtr clothMesh,
Ogre::MeshPtr masterMesh);
Ogre::MeshPtr masterMesh);
void getSlotMeshes(const Ogre::String &age, const Ogre::String &sex,
const Ogre::String &slotName,
std::vector<Ogre::String> &meshes);
};
}
#endif

View File

@@ -58,8 +58,8 @@ PhysicsModule::PhysicsModule(flecs::world &ecs)
ecs.component<CharacterBody>().on_remove([](flecs::entity e,
CharacterBody &body) {
JPH::Character *ch =
static_cast<JPH::Character *>(body.ch.get());
std::shared_ptr<JPH::Character> ch =
std::static_pointer_cast<JPH::Character>(body.ch);
if (ch) {
if (e.has<JPH::BodyID>())
e.remove<JPH::BodyID>();

View File

@@ -13,8 +13,10 @@
#include "TerrainModule.h"
#include "physics.h"
#include "PhysicsModule.h"
#include "CharacterManagerModule.h"
#include "items.h"
#include "StaticGeometryModule.h"
#include "CharacterAIModule.h"
#include <tracy/Tracy.hpp>
namespace ECS
@@ -28,6 +30,8 @@ StaticGeometryModule::StaticGeometryModule(flecs::world &ecs)
{
ZoneScoped;
ecs.module<StaticGeometryModule>();
ecs.import <CharacterManagerModule>();
ecs.import <CharacterAIModule>();
ecs.component<TerrainSlotParent>();
ecs.component<TerrainItem>();
ecs.component<FurnitureItem>();
@@ -89,6 +93,19 @@ StaticGeometryModule::StaticGeometryModule(flecs::world &ecs)
});
if (!Ogre::MeshLodGenerator::getSingletonPtr())
new Ogre::MeshLodGenerator();
ecs.system<TerrainItem>("SetupTowns")
.kind(flecs::OnUpdate)
.without<TownNPCs>()
.without<TownAI>()
.each([&](flecs::entity e, TerrainItem &item) {
Ogre::String props = item.properties;
nlohmann::json jp = nlohmann::json::parse(props);
if (jp.find("type") == jp.end())
return;
Ogre::String itemType = jp["type"].get<Ogre::String>();
if (itemType == "town")
Geometry::registerTownItem(e);
});
ecs.system("AddGeometryQueue").kind(flecs::OnUpdate).run([&](flecs::iter &it) {
ZoneScopedN("AddGeometryQueue");
std::list<flecs::entity> items;
@@ -134,9 +151,8 @@ StaticGeometryModule::StaticGeometryModule(flecs::world &ecs)
const TerrainItem &item) {
items.push_back(e);
});
for (auto e : items) {
for (auto e : items)
createItemGeometry(e);
}
addQueue.pop_front();
} else {
output.push_back(item);

View File

@@ -313,7 +313,7 @@ void createItemGeometry(flecs::entity e)
e.set<TerrainItemNode>({ itemNode, geo });
} else if (itemType == "town") {
OgreAssert(geo, "Can't create static geometry");
createTown(e, itemNode, geo);
createTown(e, itemNode, geo);
e.set<TerrainItemNode>({ itemNode, geo });
std::cout << " town created: " << e.id() << std::endl;
} else {
@@ -381,5 +381,10 @@ flecs::entity createMeshGeometry(const Ogre::String &meshName,
return e;
}
void registerTownItem(flecs::entity e)
{
registerTown(e);
}
}
}

View File

@@ -57,6 +57,7 @@ struct harbourMaker {
void createItemGeometry(flecs::entity e);
void destroyItemGeometry(flecs::entity e);
void updateItemGeometry(flecs::entity e);
void registerTownItem(flecs::entity e);
flecs::entity createMeshGeometry(const Ogre::String &meshName,
flecs::entity parente,
Ogre::SceneNode *sceneNode,

View File

@@ -18,6 +18,7 @@
#include "PhysicsModule.h"
#include "LuaData.h"
#include "PlayerActionModule.h"
#include "CharacterModule.h"
#include "CharacterManagerModule.h"
#include "CharacterAIModule.h"
#include "items.h"
@@ -2142,13 +2143,95 @@ void runAllScriptsForTown(flecs::entity e)
j["districts"] = districts;
StaticGeometryModule::setItemProperties(e, j.dump());
}
struct Selector {
int selection;
Ogre::String result;
Ogre::String label;
std::vector<Ogre::String> options;
Selector(const Ogre::String &label,
const std::vector<Ogre::String> &options)
: label(label)
, options(options)
, selection(-1)
{
}
bool select()
{
bool changed = false;
if (selection < 0)
selection = 0;
if (selection >= options.size())
selection = (options.size() > 0) ? options.size() - 1 :
0;
if (options.size() == 0) {
ImGui::Text("None: %s", label.c_str());
return false;
}
if (ImGui::BeginCombo(label.c_str(),
options[selection].c_str())) {
int i;
for (i = 0; i < options.size(); i++) {
bool selected = selection == i;
if (ImGui::Selectable(options[i].c_str(),
selected)) {
selection = i;
changed = true;
}
}
ImGui::EndCombo();
}
if (changed || result.empty())
result = options[selection];
return changed;
}
void set_default(const Ogre::String &def)
{
int index = -1;
auto pos = std::find(options.begin(), options.end(), def);
if (pos == options.end())
index = -1;
else {
index = std::distance(options.begin(), pos);
selection = index;
result = options[index];
}
}
};
bool editNPCs(nlohmann::json &npcs)
{
struct slotEdit {
const Ogre::String &label;
const Ogre::String &slotBase;
std::vector<Ogre::String> *options_m;
std::vector<Ogre::String> *options_f;
const Ogre::String &slot;
};
static std::vector<Ogre::String> faces_a_m, hairs_a_m, tops_a_m,
bottoms_a_m, feet_a_m;
static std::vector<Ogre::String> faces_a_f, hairs_a_f, tops_a_f,
bottoms_a_f, feet_a_f;
ZoneScoped;
bool changed = false;
ImGui::Text("NPC");
int id = 0;
struct slotEdit pslots_a[] = {
{ "Face", "face", &faces_a_m, &faces_a_f, "slot_face" },
{ "Hair", "hair", &hairs_a_m, &hairs_a_f, "slot_hair" },
{ "Top", "top", &tops_a_m, &tops_a_f, "slot_top" },
{ "Bottom", "bottom", &bottoms_a_m, &bottoms_a_f,
"slot_bottom" },
{ "Feet", "feet", &feet_a_m, &feet_a_f, "slot_feet" },
};
for (auto g : pslots_a) {
g.options_m->clear();
g.options_f->clear();
ECS::get_mut<CharacterModule>().getSlotMeshes(
"adult", "male", g.slotBase, *g.options_m);
ECS::get_mut<CharacterModule>().getSlotMeshes(
"adult", "female", g.slotBase, *g.options_f);
}
for (auto &npc : npcs) {
Ogre::String meid = Ogre::StringConverter::toString(id);
static char firstName[64] = { 0 };
static char lastName[64] = { 0 };
static char nickName[64] = { 0 };
@@ -2163,6 +2246,21 @@ bool editNPCs(nlohmann::json &npcs)
npc["tags"] = "";
if (npc.find("sex") == npc.end())
npc["sex"] = 1;
if (npc["sex"].get<int>() == 0) {
for (const auto &m : pslots_a) {
if (npc.find(m.slot) == npc.end()) {
npc[m.slot] = m.options_m->at(0);
changed = true;
}
}
} else if (npc["sex"].get<int>() == 1) {
for (const auto &m : pslots_a) {
if (npc.find(m.slot) == npc.end()) {
npc[m.slot] = m.options_m->at(0);
changed = true;
}
}
}
strncpy(firstName, npc["firstName"].get<Ogre::String>().c_str(),
sizeof(firstName));
@@ -2173,61 +2271,77 @@ bool editNPCs(nlohmann::json &npcs)
strncpy(tags, npc["tags"].get<Ogre::String>().c_str(),
sizeof(tags));
ImGui::InputText(
("Last name##" + Ogre::StringConverter::toString(id))
.c_str(),
lastName, sizeof(lastName));
ImGui::InputText(("Last name##" + meid).c_str(), lastName,
sizeof(lastName));
if (ImGui::IsItemDeactivatedAfterEdit()) {
npc["lastName"] = Ogre::String(lastName);
changed = true;
}
ImGui::InputText(
("First name##" + Ogre::StringConverter::toString(id))
.c_str(),
firstName, sizeof(firstName));
ImGui::InputText(("First name##" + meid).c_str(), firstName,
sizeof(firstName));
if (ImGui::IsItemDeactivatedAfterEdit()) {
npc["firstName"] = Ogre::String(firstName);
changed = true;
}
ImGui::InputText(
("Nickname##" + Ogre::StringConverter::toString(id))
.c_str(),
nickName, sizeof(nickName));
ImGui::InputText(("Nickname##" + meid).c_str(), nickName,
sizeof(nickName));
if (ImGui::IsItemDeactivatedAfterEdit()) {
npc["nickName"] = Ogre::String(nickName);
changed = true;
}
Selector race(("Race##" + meid).c_str(), { "human" });
Selector age(("Age##" + meid).c_str(), { "adult" });
Selector sex(("Sex##" + meid).c_str(), { "male", "female" });
if (npc.find("race") != npc.end())
race.set_default(npc["race"].get<Ogre::String>());
if (npc.find("age") != npc.end())
age.set_default(npc["age"].get<Ogre::String>());
if (npc.find("sex") != npc.end())
sex.selection = npc["sex"].get<int>();
if (race.select()) {
npc["race"] = race.result;
changed = true;
}
if (age.select()) {
npc["age"] = age.result;
changed = true;
}
/* sex is integer */
if (sex.select()) {
npc["sex"] = sex.selection;
changed = true;
}
if (sex.selection == 0) {
for (const auto &es : pslots_a) {
Selector sel((es.label + "##" + meid).c_str(),
*es.options_m);
sel.set_default(
npc[es.slot].get<Ogre::String>());
if (sel.select()) {
npc[es.slot] = sel.result;
changed = true;
}
}
} else if (sex.selection == 1) {
for (const auto &es : pslots_a) {
Selector sel((es.label + "##" + meid).c_str(),
*es.options_f);
sel.set_default(
npc[es.slot].get<Ogre::String>());
if (sel.select()) {
npc[es.slot] = sel.result;
changed = true;
}
}
}
ImGui::InputText(
("Tags##" + Ogre::StringConverter::toString(id)).c_str(),
tags, sizeof(tags));
ImGui::InputText(("Tags##" + meid).c_str(), tags, sizeof(tags));
if (ImGui::IsItemDeactivatedAfterEdit()) {
npc["tags"] = Ogre::String(tags);
changed = true;
}
int selection = npc["sex"].get<int>();
const char *items[] = { "Male", "Female" };
if (ImGui::Combo(("Sex##" + Ogre::StringConverter::toString(id))
.c_str(),
&selection, items, 2))
npc["sex"] = selection;
if (ImGui::SmallButton(
("Spawn##" + Ogre::StringConverter::toString(id))
.c_str())) {
int sex = npc["sex"].get<int>();
struct desc {
const char *face, *hair, *top, *bottom, *feet;
};
struct desc models[] = {
{ "male_Face.mesh", "male_Hair001.mesh",
"male_BodyTop.mesh", "male_BodyBottom.mesh",
"male_BodyFeet.mesh" },
{ "female_Face.mesh", "female_Hair001.mesh",
"female_BodyTop.mesh",
"female_BodyBottom.mesh",
"female_BodyFeet.mesh" }
};
if (ImGui::SmallButton(("Spawn##" + meid).c_str())) {
Ogre::Vector3 npcPosition;
Ogre::Quaternion npcOrientation;
from_json(npc["position"], npcPosition);
@@ -2236,10 +2350,12 @@ bool editNPCs(nlohmann::json &npcs)
// FIXME: create TownCharacterManager and register NPCs through there
ECS::get_mut<CharacterManagerModule>()
.createCharacterData(
models[sex].face, models[sex].hair,
models[sex].top, models[sex].bottom,
models[sex].feet, npcPosition,
npcOrientation);
npc["slot_face"].get<Ogre::String>(),
npc["slot_hair"].get<Ogre::String>(),
npc["slot_top"].get<Ogre::String>(),
npc["slot_bottom"].get<Ogre::String>(),
npc["slot_feet"].get<Ogre::String>(),
npcPosition, npcOrientation);
}
if (ImGui::SmallButton(
("Delete##" + Ogre::StringConverter::toString(id))
@@ -2258,9 +2374,39 @@ bool editNPCs(nlohmann::json &npcs)
ImGui::InputText("First name", firstName, sizeof(firstName));
static char tags[256] = { 0 };
ImGui::InputText("Tags", tags, sizeof(tags));
static int selection = 0;
const char *items[] = { "Male", "Female" };
ImGui::Combo("Sex", &selection, items, 2);
static Selector race("Race##new_npc", { "human" });
static Selector age("Age##new_npc", { "adult" });
static Selector sex("Sex##new_npc", { "male", "female" });
changed = changed || race.select();
changed = changed || age.select();
changed = changed || sex.select();
static Selector sel_face("Face##new_npc", {});
static Selector sel_hair("Hair##new_npc", {});
static Selector sel_top("Top##new_npc", {});
static Selector sel_bottom("Bottom##new_npc", {});
static Selector sel_feet("Feet##new_npc", {});
if (changed || sel_face.options.size() == 0) {
if (sex.selection == 0) {
sel_face = Selector("Face##new_npc", faces_a_m);
sel_hair = Selector("Hair##new_npc", hairs_a_m);
sel_top = Selector("Top##new_npc", tops_a_m);
sel_bottom = Selector("Bottom##new_npc",
bottoms_a_m);
sel_feet = Selector("Feet##new_npc", feet_a_m);
} else if (sex.selection == 1) {
sel_face = Selector("Face##new_npc", faces_a_f);
sel_hair = Selector("Hair##new_npc", hairs_a_f);
sel_top = Selector("Top##new_npc", tops_a_f);
sel_bottom = Selector("Bottom##new_npc",
bottoms_a_f);
sel_feet = Selector("Feet##new_npc", feet_a_f);
}
}
changed = changed || sel_face.select();
changed = changed || sel_hair.select();
changed = changed || sel_top.select();
changed = changed || sel_bottom.select();
changed = changed || sel_feet.select();
if (ImGui::SmallButton("Add NPC")) {
nlohmann::json npc;
npc["lastName"] = Ogre::String(lastName);
@@ -2274,7 +2420,12 @@ bool editNPCs(nlohmann::json &npcs)
.sceneNode->_getDerivedOrientation();
to_json(npc["position"], npcPosition);
to_json(npc["orientation"], npcOrientation);
npc["sex"] = selection;
npc["sex"] = sex.selection;
npc["slot_hair"] = sel_hair.result;
npc["slot_face"] = sel_face.result;
npc["slot_top"] = sel_top.result;
npc["slot_bottom"] = sel_bottom.result;
npc["slot_feet"] = sel_feet.result;
npc["health"] = 100;
npc["stamina"] = 100;
npcs.push_back(npc);
@@ -5977,6 +6128,7 @@ struct TownDecorateFurniture : TownTask {
float radius =
action["radius"]
.get<float>();
#if 0
if (ECS::get()
.has<ActionNodeList>()) {
ActionNodeList::ActionNode
@@ -6019,6 +6171,7 @@ struct TownDecorateFurniture : TownTask {
ECS::modified<
ActionNodeList>();
}
#endif
}
}
}
@@ -6123,12 +6276,209 @@ void createTown(flecs::entity e, Ogre::SceneNode *sceneNode,
geo->build();
});
});
}
void registerTown(flecs::entity e)
{
ZoneScoped;
registerTownNPCs(e);
if (ECS::get().entity<CharacterAIModule>().is_valid())
if (ECS::get().has<CharacterAIModule>()) {
ECS::get_mut<CharacterAIModule>().createAI(e);
ECS::modified<CharacterAIModule>();
}
}
createTownActionNodes(e);
}
void createTownActionNodes(flecs::entity e)
{
const TerrainItem &item = e.get<TerrainItem>();
Ogre::String props = item.properties;
nlohmann::json jprops = nlohmann::json::parse(props);
#if 0
Ogre::MaterialPtr townMaterial = createTownMaterial(e);
#endif
for (const auto &jdistrict : jprops["districts"]) {
const nlohmann::json &jp = jdistrict;
nlohmann::json jlots = nlohmann::json::array();
float baseHeight = 4.0f;
Ogre::Vector3 localPosition(0, 0, 0);
Ogre::Quaternion localRotation = Ogre::Quaternion::IDENTITY;
Ogre::Vector3 centerPosition = item.position;
Ogre::Quaternion centerOrientation = item.orientation;
float delevation = 0.0f;
float radius = 50.0f;
if (jp.find("elevation") != jp.end())
delevation = jp["elevation"].get<float>();
if (jp.find("radius") != jp.end())
radius = jp["radius"].get<float>();
from_json(jp["position"], localPosition);
from_json(jp["rotation"], localRotation);
centerPosition = centerPosition + localPosition +
Ogre::Vector3(0, delevation, 0);
centerOrientation = centerOrientation * localRotation;
if (jdistrict.find("lots") != jdistrict.end())
jlots = jdistrict["lots"];
for (const auto &jb : jlots) {
float angle = 0.0f;
int depth = 10;
int width = 10;
float distance = radius;
float elevation = 0.0f;
std::cout << jb.dump() << std::endl;
if (jb.find("angle") != jb.end())
angle = jb["angle"].get<float>();
if (jb.find("depth") != jb.end())
depth = jb["depth"].get<int>();
if (jb.find("width") != jb.end())
width = jb["width"].get<int>();
if (jb.find("elevation") != jb.end())
elevation = jb["elevation"].get<float>();
OgreAssert(width > 1 && depth > 1 && baseHeight > 1,
"Bad stuff happen");
Ogre::Quaternion rotation = Ogre::Quaternion(
Ogre::Degree(angle), Ogre::Vector3::UNIT_Y);
Ogre::Vector3 offset =
centerOrientation * rotation *
(Ogre::Vector3::UNIT_Z * distance);
Ogre::Vector3 worldCenterPosition =
centerPosition + offset +
Ogre::Vector3(0, elevation, 0);
Ogre::Quaternion worldCenterOrientation =
centerOrientation * rotation;
float outOffset = 1.05f;
if (jb.find("furniture_cells") != jb.end()) {
for (auto &jfcell : jb["furniture_cells"]) {
int x = jfcell["x"].get<int>();
int y = jfcell["y"].get<int>();
int z = jfcell["z"].get<int>();
nlohmann::json furniture =
jfcell["furniture"];
Ogre::Vector3 cellOffset(
x * 2.0f, y * 4.0f, z * 2.0f);
Ogre::Vector3 offsetX =
worldCenterOrientation *
Ogre::Vector3::UNIT_X *
(float)x * 2.0f;
Ogre::Vector3 offsetZ =
worldCenterOrientation *
Ogre::Vector3::UNIT_Z *
(float)z * 2.0f;
Ogre::Vector3 offsetY(0, y * 4.0f, 0);
static Ogre::String materialName1 = "";
static Ogre::String materialName2 = "";
int rotation = 2;
if (jfcell.find("rotation") !=
jfcell.end())
rotation = jfcell["rotation"]
.get<int>();
Ogre::Vector3 offset =
worldCenterOrientation *
Ogre::Vector3::UNIT_Z * 0.0f;
#if 0
Ogre::Vector3 furniturePosition =
worldCenterPosition + offsetX +
offsetZ + offsetY + offset;
Ogre::Quaternion furnitureOrientation =
worldCenterOrientation *
Ogre::Quaternion(
Ogre::Degree(
90.0f *
(float)rotation),
Ogre::Vector3::UNIT_Y);
#endif
if (furniture.find("actions") !=
furniture.end()) {
for (const auto &action :
furniture["actions"]) {
std::cout
<< "SENSOR: "
<< action.dump()
<< std::endl;
std::cout
<< furniture
.dump()
<< std::endl;
Ogre::Vector3
actionPosition;
actionPosition.x =
action["position_x"]
.get<float>();
actionPosition.y =
action["position_y"]
.get<float>();
actionPosition.z =
action["position_z"]
.get<float>();
Ogre::Quaternion worldSensorRotation =
worldCenterOrientation *
Ogre::Quaternion(
Ogre::Degree(
90.0f *
(float)rotation),
Ogre::Vector3::
UNIT_Y);
Ogre::Vector3 worldSensorPosition =
worldCenterPosition +
offsetX +
offsetZ +
offsetY +
offset +
worldSensorRotation *
actionPosition;
if (ECS::get()
.has<ActionNodeList>()) {
ActionNodeList::ActionNode
anode;
anode.action =
action["action"]
.get<Ogre::String>();
anode.action_text =
action["action_text"]
.get<Ogre::String>();
anode.radius =
action["radius"]
.get<float>();
anode.height =
action["height"]
.get<float>();
anode.props =
action;
anode.props
["town"] =
e.id();
anode.props
["index"] =
-1;
anode.position =
worldSensorPosition;
anode.rotation =
worldSensorRotation;
anode.dynamic =
false;
ECS::get_mut<
ActionNodeList>()
.addNode(
anode);
std::cout
<< "action: "
<< action.dump(
4)
<< std::endl;
ECS::modified<
ActionNodeList>();
}
}
}
}
}
}
}
}
}
}

View File

@@ -2,7 +2,8 @@
#define __TOWN_H__
#include <OgreMeshLodGenerator.h>
#include <flecs.h>
namespace Procedural {
namespace Procedural
{
class TriangleBuffer;
}
namespace ECS
@@ -21,6 +22,8 @@ void clampUV(flecs::entity e, Procedural::TriangleBuffer &tb,
Ogre::MaterialPtr createTownMaterial(flecs::entity e, bool force = false);
void createTown(flecs::entity e, Ogre::SceneNode *sceneNode,
Ogre::StaticGeometry *geo);
void registerTown(flecs::entity e);
void createTownActionNodes(flecs::entity e);
}
}
#endif

View File

@@ -573,14 +573,13 @@ public:
std::thread::hardware_concurrency() - 1)
, mDebugRenderer(new DebugRenderer(scnMgr, cameraNode))
, object_vs_broadphase_layer_filter{}
, object_vs_object_layer_filter{}
, object_vs_object_layer_filter{}
, debugDraw(false)
{
static int instanceCount = 0;
OgreAssert(instanceCount == 0, "Bad initialisation");
instanceCount++;
// This is the max amount of rigid bodies that you can add to the physics system. If you try to add more you'll get an error.
// Note: This value is low because this is a simple test. For a real project use something in the order of 65536.
const uint cMaxBodies = 65536;
@@ -1510,14 +1509,14 @@ public:
{
return characterBodies.find(id) != characterBodies.end();
}
void destroyCharacter(JPH::Character *ch)
void destroyCharacter(std::shared_ptr<JPH::Character> ch)
{
characterBodies.erase(characterBodies.find(ch->GetBodyID()));
characters.erase(ch);
characters.erase(ch.get());
Ogre::SceneNode *node = id2node[ch->GetBodyID()];
id2node.erase(ch->GetBodyID());
node2id.erase(node);
OGRE_DELETE ch;
ch = nullptr;
}
};
@@ -1539,17 +1538,17 @@ JoltPhysicsWrapper::JoltPhysicsWrapper(Ogre::SceneManager *scnMgr,
// Install trace and assert callbacks
JPH::Trace = TraceImpl;
JPH_IF_ENABLE_ASSERTS(JPH::AssertFailed = AssertFailedImpl;)
// 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.
// If you implement your own default material (PhysicsMaterial::sDefault) make sure to initialize it before this function or else this function will create one for you.
// 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.
// If you implement your own default material (PhysicsMaterial::sDefault) make sure to initialize it before this function or else this function will create one for you.
JPH::RegisterTypes();
phys = std::make_unique<Physics>(scnMgr, cameraNode, nullptr, &contacts);
phys = std::make_unique<Physics>(scnMgr, cameraNode, nullptr,
&contacts);
}
JoltPhysicsWrapper::~JoltPhysicsWrapper()
@@ -1824,7 +1823,7 @@ bool JoltPhysicsWrapper::bodyIsCharacter(JPH::BodyID id) const
return phys->bodyIsCharacter(id);
}
void JoltPhysicsWrapper::destroyCharacter(JPH::Character *ch)
void JoltPhysicsWrapper::destroyCharacter(std::shared_ptr<JPH::Character> ch)
{
phys->destroyCharacter(ch);
}

View File

@@ -219,6 +219,6 @@ public:
bool raycastQuery(Ogre::Vector3 startPoint, Ogre::Vector3 endPoint,
Ogre::Vector3 &position, JPH::BodyID &id);
bool bodyIsCharacter(JPH::BodyID id) const;
void destroyCharacter(JPH::Character *ch);
void destroyCharacter(std::shared_ptr<JPH::Character> ch);
};
#endif