Compare commits
7 Commits
c854605a96
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 0405214388 | |||
| 4fb7e94fed | |||
| 4d47125ea9 | |||
| 1d09728bc0 | |||
| d2d7d36655 | |||
| 4da4b0d529 | |||
| 8de2de63ad |
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1,4 +1,5 @@
|
||||
*.blend filter=lfs diff=lfs merge=lfs -text
|
||||
*.zip filter=lfs diff=lfs merge=lfs -text
|
||||
*.kra filter=lfs diff=lfs merge=lfs -text
|
||||
*.vrm filter=lfs diff=lfs merge=lfs -text
|
||||
*.vroid filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
@@ -258,7 +258,7 @@ add_custom_target(stage_files ALL DEPENDS ${CMAKE_BINARY_DIR}/resources.cfg ${MA
|
||||
|
||||
add_custom_target(remove_scenes COMMAND rm -f ${VRM_SOURCE} ${VRM_IMPORTED_BLENDS} ${CHARACTER_GLBS})
|
||||
|
||||
target_compile_definitions(Game PRIVATE FLECS_CPP_NO_AUTO_REGISTRATION)
|
||||
target_compile_definitions(Game PRIVATE FLECS_CPP_NO_AUTO_REGISTRATION JPH_PROFILE_ENABLED)
|
||||
|
||||
install(TARGETS Game DESTINATION bin)
|
||||
install(TARGETS Editor DESTINATION bin)
|
||||
|
||||
155
Game.cpp
155
Game.cpp
@@ -17,6 +17,7 @@
|
||||
#include "TerrainModule.h"
|
||||
#include "GUIModuleCommon.h"
|
||||
#include "AppModule.h"
|
||||
#include "GUIModule.h"
|
||||
#include "sound.h"
|
||||
class App;
|
||||
class SkyRenderer : public Ogre::SceneManager::Listener {
|
||||
@@ -470,16 +471,10 @@ public:
|
||||
// TODO: implement rough water level calculation
|
||||
float getWaterLevel(const Ogre::Vector3 &position)
|
||||
{
|
||||
Ogre::Vector3::UNIT_Y;
|
||||
float etime =
|
||||
Ogre::ControllerManager::getSingleton().getElapsedTime();
|
||||
return 0.0f;
|
||||
}
|
||||
void updateWorld(float delta)
|
||||
{
|
||||
#if 0
|
||||
mDynWorld->getBtWorld()->stepSimulation(delta, 3);
|
||||
#endif
|
||||
if (!ECS::get().has<ECS::GUI>())
|
||||
goto end;
|
||||
{
|
||||
@@ -495,13 +490,9 @@ public:
|
||||
}
|
||||
end:
|
||||
ECS::update(delta);
|
||||
|
||||
#if 0
|
||||
if (ECS::get<ECS::EngineData>().enableDbgDraw)
|
||||
mDbgDraw->update();
|
||||
#endif
|
||||
}
|
||||
class InputListenerChainFlexible : public OgreBites::InputListener {
|
||||
#if 0
|
||||
class InputListenerChainFlexible : public OgreBites::InputListener {
|
||||
protected:
|
||||
std::vector<OgreBites::InputListener *> mListenerChain;
|
||||
|
||||
@@ -604,6 +595,7 @@ end:
|
||||
bool
|
||||
mousePressed(const OgreBites::MouseButtonEvent &evt) override
|
||||
{
|
||||
std::cout << mListenerChain.size() << std::endl;
|
||||
for (auto listner : mListenerChain) {
|
||||
if (listner->mousePressed(evt))
|
||||
return true;
|
||||
@@ -628,6 +620,7 @@ end:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
flecs::entity input_update;
|
||||
flecs::entity find_wait_gui;
|
||||
void setupInput()
|
||||
@@ -652,108 +645,44 @@ end:
|
||||
"Skybox/Dynamic", "General");
|
||||
OgreAssert(m, "Sky box material not found.");
|
||||
m->load();
|
||||
ECS::get().component<ECS::RenderWindow>().add(flecs::Singleton);
|
||||
ECS::get().set<ECS::RenderWindow>(
|
||||
{ getRenderWindow(), getDisplayDPI() });
|
||||
ECS::setupExteriorScene(mScnMgr,
|
||||
/*mDynWorld.get(), */ mCameraNode,
|
||||
mCamera, getRenderWindow());
|
||||
ECS::get().set<ECS::RenderWindow>(
|
||||
{ getRenderWindow(), getDisplayDPI() });
|
||||
ECS::get()
|
||||
.observer<ECS::GUI>("UpdateGrab")
|
||||
.event(flecs::OnSet)
|
||||
.each([this](ECS::GUI &gui) {
|
||||
if (gui.grabChanged)
|
||||
setWindowGrab(gui.grab);
|
||||
std::cout << "grab: " << gui.grab << "\n";
|
||||
std::cout << "GUI enabled: " << gui.enabled
|
||||
<< "\n";
|
||||
});
|
||||
ECS::get()
|
||||
.observer<ECS::App>("UpdateInputListener")
|
||||
.event(flecs::OnSet)
|
||||
.each([this](ECS::App &app) {
|
||||
if (app.mInput)
|
||||
removeInputListener(app.mInput);
|
||||
delete app.mInput;
|
||||
app.mInput =
|
||||
OGRE_NEW OgreBites::InputListenerChain(
|
||||
app.listeners);
|
||||
addInputListener(app.mInput);
|
||||
});
|
||||
#if 0
|
||||
ECS::get()
|
||||
.observer<ECS::GUI, ECS::App>("SetInputListener2")
|
||||
.event(flecs::OnSet)
|
||||
.each([this](ECS::GUI &gui, ECS::App &app) {
|
||||
if (gui.mGuiInpitListener &&
|
||||
app.listeners.size() == 1) {
|
||||
app.listeners.clear();
|
||||
app.listeners.push_back(
|
||||
gui.mGuiInpitListener);
|
||||
app.listeners.push_back(&mKbd);
|
||||
ECS::modified<ECS::App>();
|
||||
}
|
||||
});
|
||||
#endif
|
||||
#if 0
|
||||
input_update =
|
||||
ECS::get()
|
||||
.system<ECS::GUI, ECS::App>("SetInputListener")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](ECS::GUI &gui, ECS::App &app) {
|
||||
if (app.listeners.size() < 2 &&
|
||||
gui.mGuiInpitListener) {
|
||||
OgreBites::InputListener *guiListener =
|
||||
gui.mGuiInpitListener;
|
||||
if (guiListener) {
|
||||
app.listeners.clear();
|
||||
app.listeners.push_back(
|
||||
guiListener);
|
||||
app.listeners.push_back(
|
||||
&mKbd);
|
||||
std::cout
|
||||
<< "input update complete\n";
|
||||
gui.mGuiInpitListener =
|
||||
guiListener;
|
||||
if (app.mInput)
|
||||
removeInputListener(
|
||||
app.mInput);
|
||||
delete app.mInput;
|
||||
app.mInput = new OgreBites::
|
||||
InputListenerChain(
|
||||
app.listeners);
|
||||
addInputListener(
|
||||
app.mInput);
|
||||
std::cout
|
||||
<< "update listeners: "
|
||||
<< app.listeners
|
||||
.size()
|
||||
<< "\n";
|
||||
if (app.listeners
|
||||
.size() ==
|
||||
2)
|
||||
OgreAssert(
|
||||
app.listeners.size() ==
|
||||
2,
|
||||
"");
|
||||
input_update.disable();
|
||||
OgreAssert(false, "");
|
||||
} else {
|
||||
app.listeners.clear();
|
||||
app.listeners.push_back(
|
||||
&mKbd);
|
||||
}
|
||||
} else
|
||||
input_update.disable();
|
||||
std::cout << "input update "
|
||||
<< app.listeners.size()
|
||||
<< "\n";
|
||||
});
|
||||
#endif
|
||||
ECS::get().set<ECS::App>(
|
||||
{ initialiseImGui(),
|
||||
nullptr,
|
||||
{ getImGuiInputListener(), &mKbd } });
|
||||
Sound::setup();
|
||||
ECS::get()
|
||||
.observer<ECS::App>("UpdateInputListener")
|
||||
.event(flecs::OnSet)
|
||||
.each([this](ECS::App &app) {
|
||||
if (app.mInput)
|
||||
removeInputListener(app.mInput);
|
||||
delete app.mInput;
|
||||
app.mInput =
|
||||
OGRE_NEW OgreBites::InputListenerChain(
|
||||
app.listeners);
|
||||
addInputListener(app.mInput);
|
||||
});
|
||||
ECS::get().set<ECS::App>(
|
||||
{ initialiseImGui(),
|
||||
nullptr,
|
||||
{ getImGuiInputListener(), &mKbd } });
|
||||
/* FIXME: this is bad */
|
||||
ECS::GUIModule::configure();
|
||||
ECS::get()
|
||||
.observer<ECS::GUI>("UpdateGrab")
|
||||
.event(flecs::OnSet)
|
||||
.each([this](ECS::GUI &gui) {
|
||||
if (gui.grabChanged)
|
||||
setWindowGrab(gui.grab);
|
||||
std::cout << "grab: " << gui.grab << "\n";
|
||||
std::cout << "GUI enabled: " << gui.enabled
|
||||
<< "\n";
|
||||
});
|
||||
ECS::get_mut<ECS::GUI>().grab = false;
|
||||
ECS::get_mut<ECS::GUI>().grabChanged = true;
|
||||
ECS::modified<ECS::GUI>();
|
||||
Sound::setup();
|
||||
Sound::ding();
|
||||
}
|
||||
void create_entity_node(const Ogre::String &name, int key)
|
||||
@@ -786,10 +715,6 @@ end:
|
||||
{
|
||||
return mCamera;
|
||||
}
|
||||
flecs::entity getPlayer() const
|
||||
{
|
||||
return ECS::player;
|
||||
}
|
||||
void enableDbgDraw(bool enable)
|
||||
{
|
||||
ECS::get_mut<ECS::EngineData>().enableDbgDraw = enable;
|
||||
|
||||
@@ -20,7 +20,7 @@ add_custom_command(
|
||||
${CMAKE_BINARY_DIR}/assets/blender/vrm-vroid-normal-${EDITED_BLEND}.blend ${EDITED_BLEND}
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
list(APPEND EDITED_BLEND_TARGETS ${CMAKE_BINARY_DIR}/assets/blender/edited-normal-${EDITED_BLEND}.blend)
|
||||
list(APPEND EDITED_BLEND_TARGETS ${CMAKE_BINARY_DIR}/assets/blender/characters/edited-normal-${EDITED_BLEND}.blend)
|
||||
list(APPEND CHARACTER_GLBS ${CMAKE_BINARY_DIR}/characters/${EDITED_BLEND}/normal-${EDITED_BLEND}.glb)
|
||||
endforeach()
|
||||
|
||||
@@ -40,14 +40,8 @@ set(VRM_IMPORTED_BLENDS
|
||||
|
||||
set(VRM_SOURCE)
|
||||
|
||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/characters/shapes/male/chibi/vroid-normal-male-chibi.glb
|
||||
DEPENDS ${CMAKE_BINARY_DIR}/assets/blender/characters/shapes/male/chibi/vroid-normal-male-chibi.glb
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/assets/blender/characters/shapes/male/chibi/vroid-normal-male-chibi.glb
|
||||
${CMAKE_BINARY_DIR}/characters/shapes/male/chibi/vroid-normal-male-chibi.glb)
|
||||
add_custom_target(morph ALL DEPENDS MorphTargetsResearch ${CMAKE_BINARY_DIR}/characters/shapes/male/chibi/vroid-normal-male-chibi.glb)
|
||||
|
||||
set(VRM_FILES buch1-chibi.vrm buch1.vrm jane2-dress.vrm jane2.vrm)
|
||||
file(GLOB MIXAMO_FILES RELATIVE ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/assets/blender/mixamo/**/*.fbx)
|
||||
file(GLOB MIXAMO_FILES RELATIVE ${CMAKE_SOURCE_DIR}/assets/blender/mixamo ${CMAKE_SOURCE_DIR}/assets/blender/mixamo/**/*.fbx)
|
||||
foreach(VRM_FILE ${VRM_FILES})
|
||||
set(OUTPUT_FILE "${CMAKE_BINARY_DIR}/assets/vroid/${VRM_FILE}")
|
||||
set(INPUT_FILE "${CMAKE_SOURCE_DIR}/assets/vroid/${VRM_FILE}")
|
||||
@@ -58,13 +52,15 @@ foreach(VRM_FILE ${VRM_FILES})
|
||||
endforeach()
|
||||
foreach(MIXAMO_FILE ${MIXAMO_FILES})
|
||||
set(OUTPUT_FILE "${CMAKE_BINARY_DIR}/assets/blender/mixamo/${MIXAMO_FILE}")
|
||||
set(INPUT_FILE "${CMAKE_SOURCE_DIR}/assets/vroid/mixamo/${MIXAMO_FILE}")
|
||||
set(INPUT_FILE "${CMAKE_SOURCE_DIR}/assets/blender/mixamo/${MIXAMO_FILE}")
|
||||
add_custom_command(OUTPUT "${OUTPUT_FILE}"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy "${INPUT_FILE}" "${OUTPUT_FILE}"
|
||||
DEPENDS "${INPUT_FILE}" VERBATIM)
|
||||
list(APPEND VRM_SOURCE "${OUTPUT_FILE}")
|
||||
endforeach()
|
||||
add_custom_command(OUTPUT ${VRM_IMPORTED_BLENDS}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/assets/blender/scripts/addons ${CMAKE_BINARY_DIR}/assets/blender/scripts/addons
|
||||
COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/install_addons.py
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CREATE_DIRECTORIES}
|
||||
COMMAND ${BLENDER} -b -Y -P ${CMAKE_SOURCE_DIR}/assets/blender/scripts/import_vrm.py
|
||||
COMMAND ${CMAKE_COMMAND} -E touch_nocreate ${VRM_IMPORTED_BLENDS}
|
||||
@@ -72,8 +68,8 @@ add_custom_command(OUTPUT ${VRM_IMPORTED_BLENDS}
|
||||
${VRM_SOURCE}
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
|
||||
set(COPY_BLENDS edited-shape-test-male.blend edited-normal-male-base.blend)
|
||||
|
||||
foreach (COPY_BLEND_FILE ${COPY_BLENDS})
|
||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/assets/blender/${COPY_BLEND_FILE}
|
||||
DEPENDS ${CMAKE_SOURCE_DIR}/assets/blender/${COPY_BLEND_FILE}
|
||||
@@ -84,6 +80,14 @@ foreach (COPY_BLEND_FILE ${COPY_BLENDS})
|
||||
list(APPEND EDITED_BLEND_TARGETS ${CMAKE_BINARY_DIR}/assets/blender/${COPY_BLEND_FILE})
|
||||
endforeach()
|
||||
|
||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/characters/shapes/male/chibi/vroid-normal-male-chibi.glb
|
||||
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/shapes/male/chibi/vroid-normal-male-chibi.glb
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/shapes/male/chibi/vroid-normal-male-chibi.glb
|
||||
${CMAKE_BINARY_DIR}/characters/shapes/male/chibi/vroid-normal-male-chibi.glb)
|
||||
add_custom_target(morph ALL DEPENDS MorphTargetsResearch ${CHARACTER_GLBS})
|
||||
|
||||
|
||||
add_custom_target(edited-blends ALL DEPENDS ${EDITED_BLEND_TARGETS})
|
||||
|
||||
add_custom_target(import_vrm DEPENDS ${CHARACTER_GLBS})
|
||||
|
||||
|
||||
BIN
assets/blender/scripts/addons/3.6/VRM_Addon_for_Blender-release.zip
LFS
Normal file
BIN
assets/blender/scripts/addons/3.6/VRM_Addon_for_Blender-release.zip
LFS
Normal file
Binary file not shown.
BIN
assets/blender/scripts/addons/3.6/io_ogre.zip
LFS
Normal file
BIN
assets/blender/scripts/addons/3.6/io_ogre.zip
LFS
Normal file
Binary file not shown.
@@ -6,7 +6,7 @@ script_list = [item for item in file_list if item.endswith('.zip')]
|
||||
for file in file_list:
|
||||
path_to_file = os.path.join(path_to_script_dir, file)
|
||||
bpy.ops.preferences.addon_install(overwrite=True, target='DEFAULT', filepath=path_to_file, filter_folder=True, filter_python=False, filter_glob="*.py;*.zip")
|
||||
enableTheseAddons = ["VRM_Addon_for_Blender-release"]
|
||||
enableTheseAddons = ["VRM_Addon_for_Blender-release", "io_ogre"]
|
||||
for string in enableTheseAddons:
|
||||
name = enableTheseAddons
|
||||
bpy.ops.preferences.addon_enable(module = string)
|
||||
|
||||
@@ -219,30 +219,99 @@ function Quest(name, book)
|
||||
return quest
|
||||
end
|
||||
function StartGameQuest()
|
||||
local quest = {}
|
||||
-- Parse a book from the Ink file.
|
||||
local book = narrator.parse_file('stories.initiation')
|
||||
local quest = Quest('start game', book)
|
||||
quest.base = {}
|
||||
quest.base.activate = quest.activate
|
||||
quest.base.complete = quest.complete
|
||||
quest.boat = false
|
||||
quest.activate = function(this)
|
||||
print('activate...')
|
||||
local mc_is_free = function()
|
||||
this.boat = true
|
||||
local ent = ecs_get_player_entity()
|
||||
ecs_character("params-set", ent, "gravity", true)
|
||||
ecs_character("params-set", ent, "buoyancy", true)
|
||||
end
|
||||
this.story:bind('mc_is_free', mc_is_free)
|
||||
this.base.activate(this)
|
||||
local book = narrator.parse_file('stories.initiation')
|
||||
local story = narrator.init_story(book)
|
||||
this._add_narration({
|
||||
book = book,
|
||||
story = story,
|
||||
activate = function(this)
|
||||
local mc_is_free = function()
|
||||
this.boat = true
|
||||
local ent = ecs_get_player_entity()
|
||||
ecs_character("params-set", ent, "gravity", true)
|
||||
ecs_character("params-set", ent, "buoyancy", true)
|
||||
end
|
||||
this.story:bind('mc_is_free', mc_is_free)
|
||||
this.story:begin()
|
||||
this:narration_update()
|
||||
end,
|
||||
event = function(this, event)
|
||||
if event == "narration_progress" then
|
||||
this:narration_update()
|
||||
end
|
||||
if event == "narration_answered" then
|
||||
local answer = this._get_narration_answer()
|
||||
this.story:choose(answer)
|
||||
this:narration_update()
|
||||
end
|
||||
end,
|
||||
finish = function(this)
|
||||
end,
|
||||
narration_update = function(this)
|
||||
local ret = ""
|
||||
local choices = {}
|
||||
local have_choice = false
|
||||
local have_paragraph = false
|
||||
print("narration_update")
|
||||
if this.story:can_continue() then
|
||||
print("CAN continue")
|
||||
have_paragraph = true
|
||||
local paragraph = this.story:continue(1)
|
||||
print(dump(paragraph))
|
||||
local text = paragraph.text
|
||||
if paragraph.tags then
|
||||
-- text = text .. ' #' .. table.concat(paragraph.tags, ' #')
|
||||
for i, tag in ipairs(paragraph.tags) do
|
||||
if tag == 'discard' then
|
||||
text = ''
|
||||
elseif tag == 'crash' then
|
||||
print(text)
|
||||
crash()
|
||||
end
|
||||
this:handle_tag(tag)
|
||||
end
|
||||
end
|
||||
ret = text
|
||||
if this.story:can_choose() then
|
||||
have_choice = true
|
||||
local ch = this.story:get_choices()
|
||||
for i, choice in ipairs(ch) do
|
||||
table.insert(choices, choice.text)
|
||||
print(i, dump(choice))
|
||||
end
|
||||
if #choices == 1 and choices[1] == "Ascend" then
|
||||
this.story:choose(1)
|
||||
choices = {}
|
||||
end
|
||||
if #choices == 1 and choices[1] == "Continue" then
|
||||
this.story:choose(1)
|
||||
choices = {}
|
||||
end
|
||||
end
|
||||
else
|
||||
print("can NOT continue")
|
||||
end
|
||||
print(ret)
|
||||
if (#choices > 0) then
|
||||
print("choices!!!")
|
||||
this._narration(ret, choices)
|
||||
else
|
||||
this._narration(ret, {})
|
||||
end
|
||||
if not have_choice and not have_paragraph then
|
||||
this._finish()
|
||||
end
|
||||
end,
|
||||
handle_tag = function(this, tag)
|
||||
print("tag: " .. tag)
|
||||
end,
|
||||
})
|
||||
end
|
||||
quest.complete = function(this)
|
||||
this.base.complete(this)
|
||||
this.active = false
|
||||
if not this.boat then
|
||||
ecs_save_object_debug(boat, 'boat.scene')
|
||||
end
|
||||
quest.finish = function(this)
|
||||
end
|
||||
return quest
|
||||
end
|
||||
@@ -616,25 +685,27 @@ setup_handler(function(event, trigger_entity, what_entity)
|
||||
return
|
||||
end
|
||||
if event == "startup" then
|
||||
main_menu()
|
||||
--
|
||||
elseif event == "narration_progress" then
|
||||
print("narration progress!")
|
||||
elseif event == "narration_answered" then
|
||||
local answer = narration_get_answer()
|
||||
print("answered:", answer)
|
||||
elseif event == "spawn_player" then
|
||||
elseif event == "new_game" then
|
||||
local ent = ecs_get_player_entity()
|
||||
ecs_character("params-set", ent, "gravity", true)
|
||||
ecs_character("params-set", ent, "buoyancy", false)
|
||||
local quest = StartGameQuest()
|
||||
quests[quest.name] = quest
|
||||
for k, v in pairs(quests) do
|
||||
print(k, v.active)
|
||||
end
|
||||
quest:activate()
|
||||
local start_boat = create_boat()
|
||||
table.insert(vehicles, start_boat)
|
||||
table.insert(player_vehicles, start_boat)
|
||||
local quest = StartGameQuest()
|
||||
add_quest("main", quest)
|
||||
-- quests[quest.name] = quest
|
||||
-- for k, v in pairs(quests) do
|
||||
-- print(k, v.active)
|
||||
-- end
|
||||
-- quest:activate()
|
||||
elseif event == "actuator_created" then
|
||||
print(trigger_entity)
|
||||
local act = create_actuator2(trigger_entity)
|
||||
@@ -829,3 +900,5 @@ setup_action_handler("talk", function(town, index, word)
|
||||
end)
|
||||
]]--
|
||||
|
||||
main_menu()
|
||||
|
||||
|
||||
@@ -661,10 +661,6 @@ public:
|
||||
ECS::get().get_mut<ECS::GUI>().enabled = active;
|
||||
ECS::get().modified<ECS::GUI>();
|
||||
}
|
||||
flecs::entity getPlayer() const
|
||||
{
|
||||
return ECS::player;
|
||||
}
|
||||
void enableDbgDraw(bool enable)
|
||||
{
|
||||
ECS::get_mut<ECS::EngineData>().enableDbgDraw = enable;
|
||||
|
||||
@@ -8,7 +8,7 @@ add_subdirectory(items)
|
||||
add_library(GameData STATIC GameData.cpp CharacterModule.cpp WaterModule.cpp SunModule.cpp TerrainModule.cpp
|
||||
GUIModule.cpp EditorGUIModule.cpp LuaData.cpp WorldMapModule.cpp BoatModule.cpp EventTriggerModule.cpp
|
||||
CharacterAnimationModule.cpp PhysicsModule.cpp EventModule.cpp CharacterManagerModule.cpp
|
||||
VehicleManagerModule.cpp AppModule.cpp StaticGeometryModule.cpp SmartObject.cpp SlotsModule.cpp
|
||||
VehicleManagerModule.cpp AppModule.cpp StaticGeometryModule.cpp SmartObject.cpp SlotsModule.cpp QuestModule.cpp
|
||||
PlayerActionModule.cpp CharacterAIModule.cpp goap.cpp)
|
||||
target_link_libraries(GameData PUBLIC
|
||||
lua
|
||||
|
||||
@@ -33,7 +33,6 @@ class ActionNodeActions {
|
||||
const ActionNodeList &alist =
|
||||
ECS::get<ActionNodeList>();
|
||||
state.apply(effects);
|
||||
// node position can change in case of character
|
||||
const Ogre::Vector3 &nodePosition =
|
||||
alist.dynamicNodes[node].position;
|
||||
state.setPosition(nodePosition);
|
||||
@@ -49,13 +48,8 @@ class ActionNodeActions {
|
||||
{
|
||||
const ActionNodeList &alist =
|
||||
ECS::get<ActionNodeList>();
|
||||
// const TownNPCs &npcs = bb.town.get<TownNPCs>();
|
||||
// const TownAI &ai = bb.town.get<TownAI>();
|
||||
const Ogre::Vector3 &nodePosition =
|
||||
alist.dynamicNodes[node].position;
|
||||
// flecs::entity e = npcs.npcs.at(bb.index).e;
|
||||
// bool validActive = e.is_valid() &&
|
||||
// e.has<CharacterBase>();
|
||||
const Ogre::Vector3 &npcPosition =
|
||||
bb.getPosition();
|
||||
float dist = npcPosition.squaredDistance(
|
||||
@@ -164,16 +158,16 @@ public:
|
||||
}
|
||||
Blackboard actionPrereq({ jactionPrereq });
|
||||
Blackboard actionEffect({ jactionEffect });
|
||||
if (!prereq.stats.is_null())
|
||||
if (!prereq.is_valid())
|
||||
actionPrereq.apply(prereq);
|
||||
m_actions.push_back(OGRE_NEW RunActionNode(
|
||||
node, action, actionPrereq, actionEffect, cost));
|
||||
if (effectName == "") {
|
||||
std::cout << props.dump(4) << std::endl;
|
||||
std::cout << "Prereq" << std::endl;
|
||||
std::cout << actionPrereq.stats.dump(4) << std::endl;
|
||||
actionPrereq.dump_bits();
|
||||
std::cout << "Effect" << std::endl;
|
||||
std::cout << actionEffect.stats.dump(4) << std::endl;
|
||||
actionEffect.dump_bits();
|
||||
OgreAssert(false, "action");
|
||||
}
|
||||
}
|
||||
@@ -241,7 +235,7 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs)
|
||||
2000 },
|
||||
#endif
|
||||
{ "EatMedicine",
|
||||
{ { { "have_medicine", 1 }, { "healty", 0 } } },
|
||||
{ { { "have_medicine", 1 }, { "healthy", 0 } } },
|
||||
{ { { "healthy", 1 } } },
|
||||
100 },
|
||||
{ "UseToilet",
|
||||
@@ -359,9 +353,6 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs)
|
||||
std::lock_guard<std::mutex> lock(
|
||||
ecs_mutex);
|
||||
|
||||
alist.build();
|
||||
updateBlackboardsBits(
|
||||
town, alist, npcs, ai);
|
||||
updateBlackboards(town, alist,
|
||||
npcs, ai);
|
||||
}
|
||||
@@ -370,12 +361,12 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs)
|
||||
->addMainThreadTask([this, town,
|
||||
&alist]() {
|
||||
town.modified<TownAI>();
|
||||
town.modified<TownNPCs>();
|
||||
ECS::modified<
|
||||
ActionNodeList>();
|
||||
});
|
||||
});
|
||||
});
|
||||
#if 1
|
||||
ecs.system<TownAI, TownNPCs>("PlanAI")
|
||||
.kind(flecs::OnUpdate)
|
||||
.interval(0.5f)
|
||||
@@ -394,7 +385,6 @@ CharacterAIModule::CharacterAIModule(flecs::world &ecs)
|
||||
});
|
||||
});
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
void CharacterAIModule::createAI(flecs::entity town)
|
||||
@@ -402,97 +392,89 @@ void CharacterAIModule::createAI(flecs::entity town)
|
||||
town.add<TownAI>();
|
||||
}
|
||||
|
||||
struct PlanTask {
|
||||
Blackboard blackboard;
|
||||
TownAI::goal_t goal;
|
||||
TownAI::Plan plan;
|
||||
TownAI::planner_t *planner;
|
||||
bool operator()()
|
||||
{
|
||||
auto buildPlan = [this](Blackboard &blackboard,
|
||||
const TownAI::goal_t &goal,
|
||||
TownAI::Plan &plan) -> bool {
|
||||
if (goal.is_reached(blackboard))
|
||||
return false;
|
||||
plan.goal = &goal;
|
||||
std::vector<goap::BaseAction<Blackboard> *> path;
|
||||
int actionCount = blackboard.getActionsCount();
|
||||
auto actionsData = blackboard.getActionsData();
|
||||
path.resize(actionCount * actionCount);
|
||||
int path_length = planner->plan(
|
||||
blackboard, goal, actionsData, actionCount,
|
||||
path.data(), path.size());
|
||||
if (path_length > 0) {
|
||||
plan.goal = &goal;
|
||||
plan.plan.insert(plan.plan.end(), path.begin(),
|
||||
path.begin() + path_length);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
return buildPlan(blackboard, goal, plan);
|
||||
}
|
||||
PlanTask(Blackboard &blackboard, const TownAI::goal_t &goal,
|
||||
TownAI::planner_t *planner)
|
||||
: blackboard(blackboard)
|
||||
, goal(goal)
|
||||
, planner(planner)
|
||||
{
|
||||
}
|
||||
};
|
||||
static std::deque<PlanTask> plan_tasks;
|
||||
|
||||
void CharacterAIModule::buildPlans(flecs::entity town, const TownNPCs &npcs,
|
||||
TownAI &ai)
|
||||
{
|
||||
OgreAssert(town.is_valid(), "Bad town entity");
|
||||
std::lock_guard<std::mutex> lock(*ai.mutex);
|
||||
for (auto it = npcs.npcs.begin(); it != npcs.npcs.end(); it++) {
|
||||
if (ai.blackboards.find(it->first) == ai.blackboards.end())
|
||||
continue;
|
||||
auto &bb = ai.blackboards.at(it->first);
|
||||
/* if there are plans, skip until these get discarded */
|
||||
if (ai.plans.find(it->first) != ai.plans.end() &&
|
||||
ai.plans.at(it->first).size() > 0)
|
||||
continue;
|
||||
const auto &npc = npcs.npcs.at(it->first);
|
||||
int index = it->first;
|
||||
ai.plans[index] = {};
|
||||
for (const auto &goal : ai.goals) {
|
||||
if (goal.is_reached(bb))
|
||||
continue;
|
||||
struct TownAI::Plan plan;
|
||||
plan.goal = &goal;
|
||||
#if 0
|
||||
std::cout << "blackboard: "
|
||||
<< bb.stats.dump(4)
|
||||
<< std::endl;
|
||||
std::cout << "goal: "
|
||||
<< goal.goal.stats.dump(4)
|
||||
<< std::endl;
|
||||
#endif
|
||||
std::vector<goap::BaseAction<Blackboard> *> path;
|
||||
int actionCount =
|
||||
ai.blackboards.at(it->first).getActionsCount();
|
||||
auto actionsData =
|
||||
ai.blackboards.at(it->first).getActionsData();
|
||||
path.resize(actionCount * actionCount);
|
||||
int path_length = ai.planner->plan(
|
||||
bb, goal, actionsData, actionCount, path.data(),
|
||||
path.size());
|
||||
#if 0
|
||||
std::cout << "Actions: " << std::endl;
|
||||
for (auto &action : actions) {
|
||||
std::cout << "name: "
|
||||
<< action->get_name()
|
||||
<< std::endl;
|
||||
std::cout
|
||||
<< "\tprereq:\n"
|
||||
<< action->prereq.stats
|
||||
.dump(4)
|
||||
<< std::endl;
|
||||
std::cout
|
||||
<< "\teffects:\n"
|
||||
<< action->effects.stats
|
||||
.dump(4)
|
||||
<< std::endl;
|
||||
}
|
||||
#endif
|
||||
#if 1
|
||||
std::cout << bb.index << " ";
|
||||
std::cout << "Goal: " << goal.get_name();
|
||||
auto planner = ai.planner;
|
||||
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: ";
|
||||
int count = 0;
|
||||
if (path_length < 0) {
|
||||
std::cout << "Bad plan " << path_length
|
||||
<< std::endl;
|
||||
}
|
||||
for (auto &action : path) {
|
||||
if (count >= path_length)
|
||||
break;
|
||||
for (auto &action : plan_tasks.front().plan.plan) {
|
||||
OgreAssert(action, "No action");
|
||||
std::cout << action->get_name();
|
||||
if (count < path_length - 1)
|
||||
std::cout << ", ";
|
||||
count++;
|
||||
std::cout << action->get_name() + " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
std::cout << path_length << std::endl;
|
||||
// OgreAssert(path_length == 0,
|
||||
// "planning");
|
||||
#endif
|
||||
if (path_length > 0) {
|
||||
plan.goal = &goal;
|
||||
plan.plan.insert(plan.plan.end(), path.begin(),
|
||||
path.begin() + path_length);
|
||||
ai.plans[it->first].push_back(plan);
|
||||
break;
|
||||
}
|
||||
if (path_length > 0)
|
||||
OgreAssert(false, "plan");
|
||||
std::cout << " size: "
|
||||
<< plan_tasks.front().plan.plan.size()
|
||||
<< std::endl;
|
||||
ai.plans[plan_tasks.front().blackboard.index].push_back(
|
||||
plan_tasks.front().plan);
|
||||
}
|
||||
plan_tasks.pop_front();
|
||||
} else
|
||||
for (auto it = npcs.npcs.begin(); it != npcs.npcs.end(); it++) {
|
||||
if (ai.blackboards.find(it->first) ==
|
||||
ai.blackboards.end())
|
||||
continue;
|
||||
auto &bb = ai.blackboards.at(it->first);
|
||||
/* if there are plans, skip until these get discarded */
|
||||
if (ai.plans.find(it->first) != ai.plans.end() &&
|
||||
ai.plans.at(it->first).size() > 0)
|
||||
continue;
|
||||
const auto &npc = npcs.npcs.at(it->first);
|
||||
int index = it->first;
|
||||
ai.plans[index] = {};
|
||||
for (const auto &goal : ai.goals)
|
||||
plan_tasks.emplace_back(bb, goal,
|
||||
ai.planner.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterAIModule::createBlackboards(flecs::entity town,
|
||||
@@ -511,20 +493,24 @@ void CharacterAIModule::createBlackboards(flecs::entity town,
|
||||
strength += 10;
|
||||
dexterity += 10;
|
||||
}
|
||||
// FIXME: use separate "memory" for stats
|
||||
// Do not keep actual stats in blackboard
|
||||
nlohmann::json memory;
|
||||
memory["strength"] = strength;
|
||||
memory["dexterity"] = dexterity;
|
||||
memory["health"] = health;
|
||||
memory["stamina"] = stamina;
|
||||
|
||||
memory["needs_hunger"] = 0;
|
||||
memory["needs_thirst"] = 0;
|
||||
memory["needs_toilet"] = 0;
|
||||
|
||||
nlohmann::json bb;
|
||||
bb["strength"] = strength;
|
||||
bb["dexterity"] = dexterity;
|
||||
bb["health"] = health;
|
||||
bb["stamina"] = stamina;
|
||||
|
||||
bb["needs_hunger"] = 0;
|
||||
bb["needs_thirst"] = 0;
|
||||
bb["needs_toilet"] = 0;
|
||||
|
||||
bb["have_water"] = 0;
|
||||
bb["have_food"] = 0;
|
||||
bb["have_medicine"] = 0;
|
||||
bb["at_object"] = 0;
|
||||
ai.memory[it->first] = memory;
|
||||
ai.blackboards[it->first] = Blackboard(bb);
|
||||
ai.blackboards[it->first].index = it->first;
|
||||
ai.blackboards.at(it->first).town = town;
|
||||
@@ -532,103 +518,102 @@ void CharacterAIModule::createBlackboards(flecs::entity town,
|
||||
ai.blackboards.at(it->first).actionRefResize(0);
|
||||
ai.blackboards.at(it->first).actionRefAddActions(
|
||||
ai.actions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterAIModule::updateBlackboardsBits(flecs::entity town,
|
||||
ActionNodeList &alist,
|
||||
const TownNPCs &npcs, TownAI &ai)
|
||||
{
|
||||
OgreAssert(town.is_valid(), "Bad town entity");
|
||||
std::lock_guard<std::mutex> lock(*ai.mutex);
|
||||
struct UpdateBit {
|
||||
Ogre::String checkValue;
|
||||
int recover;
|
||||
int minValue;
|
||||
int maxValue;
|
||||
int threshold;
|
||||
Ogre::String writeValue;
|
||||
};
|
||||
struct UpdateBit updateBits[] = {
|
||||
{ "health", 0, 0, 100, 20, "healthy" },
|
||||
{ "needs_hunger", 1, 0, 10000, 2000, "hungry" },
|
||||
{ "needs_thirst", 1, 0, 10000, 1000, "thirsty" },
|
||||
{ "needs_toilet", 1, 0, 10000, 1500, "toilet" }
|
||||
};
|
||||
for (auto it = npcs.npcs.begin(); it != npcs.npcs.end(); it++) {
|
||||
if (ai.blackboards.find(it->first) == ai.blackboards.end())
|
||||
continue;
|
||||
auto &stats = ai.blackboards.at(it->first).stats;
|
||||
auto &object = ai.blackboards.at(it->first).object;
|
||||
ai.blackboards.at(it->first).index = it->first;
|
||||
ai.blackboards.at(it->first).town = town;
|
||||
for (const auto &bits : updateBits) {
|
||||
int value = stats[bits.checkValue].get<int>();
|
||||
int maxValue = bits.maxValue;
|
||||
int minValue = bits.minValue;
|
||||
int threshold = bits.threshold;
|
||||
if (it->second.props.find(bits.checkValue + "_max") !=
|
||||
it->second.props.end())
|
||||
maxValue =
|
||||
it->second
|
||||
.props[bits.checkValue + "_max"]
|
||||
.get<int>();
|
||||
if (it->second.props.find(bits.checkValue +
|
||||
"_threshold") !=
|
||||
it->second.props.end())
|
||||
threshold = it->second
|
||||
.props[bits.checkValue +
|
||||
"_threshold"]
|
||||
.get<int>();
|
||||
value += bits.recover;
|
||||
if (value > maxValue)
|
||||
value = maxValue;
|
||||
if (value >= threshold)
|
||||
stats[bits.writeValue] = 1;
|
||||
else
|
||||
stats[bits.writeValue] = 0;
|
||||
if (value < bits.minValue)
|
||||
value = bits.minValue;
|
||||
stats[bits.checkValue] = value;
|
||||
ai.conditions =
|
||||
TownAI::BitEvolutionBuilder()
|
||||
.addRule(
|
||||
"healthy",
|
||||
[](const nlohmann::json &data)
|
||||
-> bool {
|
||||
return data["health"]
|
||||
.get<int>() >
|
||||
20;
|
||||
})
|
||||
->addRule(
|
||||
"hungry",
|
||||
[](const nlohmann::json &data)
|
||||
-> bool {
|
||||
return data["needs_hunger"]
|
||||
.get<int>() >
|
||||
2000;
|
||||
})
|
||||
->addRule(
|
||||
"thirsty",
|
||||
[](const nlohmann::json &data)
|
||||
-> bool {
|
||||
return data["needs_thirst"]
|
||||
.get<int>() >
|
||||
1000;
|
||||
})
|
||||
->addRule(
|
||||
"toilet",
|
||||
[](const nlohmann::json &data)
|
||||
-> bool {
|
||||
return data["needs_toilet"]
|
||||
.get<int>() >
|
||||
1500;
|
||||
})
|
||||
->build();
|
||||
ai.memoryUpdates =
|
||||
TownAI::MemoryUpdateBuilder()
|
||||
.addUpdate(
|
||||
"health",
|
||||
[](int index,
|
||||
const std::string &name,
|
||||
int value) -> int {
|
||||
if (value < 50)
|
||||
return value +
|
||||
1;
|
||||
else
|
||||
return value;
|
||||
})
|
||||
.addUpdate("needs_hunger",
|
||||
[](int index,
|
||||
const std::string &name,
|
||||
int value) -> int {
|
||||
return value + 1;
|
||||
})
|
||||
.addUpdate("needs_thirst",
|
||||
[](int index,
|
||||
const std::string &name,
|
||||
int value) -> int {
|
||||
return value + 1;
|
||||
})
|
||||
.addUpdate("needs_toilet",
|
||||
[](int index,
|
||||
const std::string &name,
|
||||
int value) -> int {
|
||||
return value + 1;
|
||||
})
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CharacterAIModule::updateBlackboards(flecs::entity town,
|
||||
const ActionNodeList &alist,
|
||||
ActionNodeList &alist,
|
||||
const TownNPCs &npcs, TownAI &ai)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*ai.mutex);
|
||||
OgreAssert(town.is_valid(), "Bad town entity");
|
||||
alist.build();
|
||||
for (auto it = npcs.npcs.begin(); it != npcs.npcs.end(); it++) {
|
||||
if (ai.blackboards.find(it->first) == ai.blackboards.end())
|
||||
continue;
|
||||
auto &stats = ai.blackboards.at(it->first).stats;
|
||||
auto &object = ai.blackboards.at(it->first).object;
|
||||
ai.blackboards.at(it->first).index = it->first;
|
||||
ai.blackboards.at(it->first).town = town;
|
||||
for (auto e : ai.memoryUpdates) {
|
||||
std::string key = e.name;
|
||||
auto &memory = ai.memory.at(it->first);
|
||||
if (memory.find(key) == memory.end())
|
||||
memory[key] = 0;
|
||||
int value = memory[key].get<int>();
|
||||
int new_value = e.update(it->first, key, value);
|
||||
if (value != new_value)
|
||||
memory[key] = new_value;
|
||||
}
|
||||
auto &bb = ai.blackboards.at(it->first);
|
||||
bb.updateBits(ai, ai.memory.at(it->first), it->second.props);
|
||||
bb.query_ai();
|
||||
|
||||
#if 0
|
||||
OgreAssert(nodeActionCount > 0 ||
|
||||
points.size() == 0,
|
||||
"no node actions and no points");
|
||||
if (nodeActionCount == 0) {
|
||||
std::cout << "nodes:"
|
||||
<< alist.nodes.size() << " "
|
||||
<< alist.dynamicNodes.size()
|
||||
<< std::endl;
|
||||
std::cout << "points: " << points.size()
|
||||
<< std::endl;
|
||||
std::cout << "position: " << position
|
||||
<< std::endl;
|
||||
}
|
||||
OgreAssert(nodeActionCount > 0,
|
||||
"no node actions");
|
||||
#endif
|
||||
bb.fixupBooleanKeys();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -646,23 +631,56 @@ void Blackboard::_actionRefResize(int count)
|
||||
}
|
||||
|
||||
Blackboard::Blackboard()
|
||||
: stats(nlohmann::json::object())
|
||||
, object(-1)
|
||||
: object(-1)
|
||||
, index(-1)
|
||||
, actionRefCount(0)
|
||||
, mutex(std::make_shared<std::mutex>())
|
||||
, bits(0)
|
||||
, mask(0)
|
||||
{
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, size_t> Blackboard::mapping;
|
||||
Blackboard::Blackboard(const nlohmann::json &stats)
|
||||
: Blackboard()
|
||||
{
|
||||
this->stats = stats;
|
||||
populate(stats, mapping);
|
||||
}
|
||||
|
||||
void Blackboard::populate(const nlohmann::json &stats,
|
||||
std::unordered_map<std::string, size_t> &mapping)
|
||||
{
|
||||
if (stats.empty())
|
||||
return;
|
||||
|
||||
for (auto &[key, value] : stats.items()) {
|
||||
if (value.is_number_integer()) {
|
||||
int val = value.get<int>();
|
||||
if (val == 0 || val == 1) {
|
||||
// Update mapping if key is new
|
||||
if (mapping.find(key) == mapping.end()) {
|
||||
size_t next_bit = mapping.size();
|
||||
if (next_bit < 64) {
|
||||
mapping[key] = next_bit;
|
||||
} else {
|
||||
OgreAssert(false,
|
||||
"Out of bits");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
size_t bit_idx = mapping[key];
|
||||
bits.set(bit_idx, val == 1);
|
||||
mask.set(bit_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Blackboard::operator==(const Blackboard &other) const
|
||||
{
|
||||
return is_satisfied_by(this->stats, other.stats);
|
||||
OgreAssert(mask != 0 && other.mask != 0, "blackboard not prepared");
|
||||
return (bits & other.mask) == (other.bits & other.mask);
|
||||
}
|
||||
|
||||
bool Blackboard::operator!=(const Blackboard &other) const
|
||||
@@ -673,7 +691,13 @@ bool Blackboard::operator!=(const Blackboard &other) const
|
||||
void Blackboard::apply(const Blackboard &other)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*mutex);
|
||||
stats.update(other.stats);
|
||||
// stats.update(other.stats);
|
||||
// 1. Clear bits in 'values' that are about to be overwritten (where effect mask is 1)
|
||||
// 2. OR the result with the effect values (filtered by the effect mask)
|
||||
bits = (bits & ~other.mask) | (other.bits & other.mask);
|
||||
|
||||
// 3. Update our mask to include any new state definitions introduced by the effect
|
||||
mask |= other.mask;
|
||||
}
|
||||
|
||||
Ogre::String Blackboard::dumpActions()
|
||||
@@ -685,8 +709,10 @@ Ogre::String Blackboard::dumpActions()
|
||||
for (count = 0; count < actionRefCount; count++) {
|
||||
auto &action = actionRef[count];
|
||||
ret += "name: " + action->get_name() + "\n";
|
||||
ret += "\tprereq:\n" + action->prereq.stats.dump(4) + "\n";
|
||||
ret += "\teffects:\n" + action->effects.stats.dump(4) + "\n";
|
||||
ret += "\tprereq:\n";
|
||||
action->prereq.dump_bits();
|
||||
ret += "\teffects:\n";
|
||||
action->effects.dump_bits();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -696,23 +722,6 @@ void Blackboard::printActions()
|
||||
std::cout << dumpActions() << std::endl;
|
||||
}
|
||||
|
||||
void Blackboard::fixupBooleanKeys()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*mutex);
|
||||
int count;
|
||||
for (count = 0; count < actionRefCount; count++) {
|
||||
auto &action = actionRef[count];
|
||||
const nlohmann::json &prereq = action->prereq.stats;
|
||||
const nlohmann::json &effects = action->effects.stats;
|
||||
for (auto it = prereq.begin(); it != prereq.end(); it++)
|
||||
if (stats.find(it.key()) == stats.end())
|
||||
stats[it.key()] = 0;
|
||||
for (auto it = effects.begin(); it != effects.end(); it++)
|
||||
if (stats.find(it.key()) == stats.end())
|
||||
stats[it.key()] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Blackboard::actionRefResize(int count)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(*mutex);
|
||||
@@ -765,6 +774,17 @@ void Blackboard::actionRefAddActions(goap::BaseAction<Blackboard> **actions,
|
||||
}
|
||||
}
|
||||
|
||||
void Blackboard::updateBits(const TownAI &ai, const nlohmann::json &memory,
|
||||
const nlohmann::json &props)
|
||||
{
|
||||
for (const auto &mcond : ai.conditions) {
|
||||
if (mapping.find(mcond.key) == mapping.end())
|
||||
populate(nlohmann::json::object({ { mcond.key, 0 } }),
|
||||
mapping);
|
||||
bits.set(mapping[mcond.key], mcond.condition(memory));
|
||||
}
|
||||
}
|
||||
|
||||
struct ComparePair {
|
||||
const nlohmann::json ¤t;
|
||||
const nlohmann::json ⌖
|
||||
@@ -808,33 +828,8 @@ bool Blackboard::is_satisfied_by(const nlohmann::json ¤t,
|
||||
int Blackboard::distance_to(const Blackboard &goal) const
|
||||
{
|
||||
int distance = 0;
|
||||
|
||||
OgreAssert(goal.stats.is_object(),
|
||||
"Not an object:\n" + goal.stats.dump(4));
|
||||
for (auto it = goal.stats.begin(); it != goal.stats.end(); ++it) {
|
||||
const std::string &key = it.key();
|
||||
const auto &goalVal = it.value();
|
||||
|
||||
// If current state doesn't have the key, treat it as a maximum difference
|
||||
if (stats.find(key) == stats.end()) {
|
||||
distance += 100; // Example: High cost for missing state
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto ¤tVal = stats[key];
|
||||
|
||||
if (goalVal.is_number() && currentVal.is_number()) {
|
||||
// Add numerical difference
|
||||
distance += std::abs(goalVal.get<float>() -
|
||||
currentVal.get<float>());
|
||||
} else {
|
||||
// Check non-numeric equality
|
||||
if (goalVal != currentVal) {
|
||||
distance += 1; // Penalty for mismatch
|
||||
}
|
||||
}
|
||||
}
|
||||
return distance;
|
||||
OgreAssert(mask != 0 && goal.mask != 0, "blackboard not prepared");
|
||||
return ((bits ^ goal.bits) & goal.mask).count();
|
||||
}
|
||||
|
||||
void Blackboard::setPosition(const Ogre::Vector3 &position)
|
||||
@@ -860,7 +855,6 @@ void Blackboard::query_ai()
|
||||
ActionNodeList &alist = ECS::get_mut<ActionNodeList>();
|
||||
alist.query_ai(position, distance, points, distances);
|
||||
_actionRefResize(ai.actions.size());
|
||||
int actionRefIndex = ai.actions.size();
|
||||
int nodeActionCount = 0;
|
||||
for (size_t point : points) {
|
||||
Ogre::Vector3 &p = alist.dynamicNodes[point].position;
|
||||
@@ -869,10 +863,10 @@ void Blackboard::query_ai()
|
||||
if (object >= 0 && (size_t)object == point &&
|
||||
distance > radius * radius) {
|
||||
object = -1;
|
||||
stats["at_object"] = 0;
|
||||
bits.set(mapping["at_object"], false);
|
||||
} else if (object >= 0 && (size_t)object == point &&
|
||||
distance <= radius * radius)
|
||||
stats["at_object"] = 1;
|
||||
bits.set(mapping["at_object"], true);
|
||||
/* some nodes do not have usable actions */
|
||||
if (ai.nodeActions[point].size() > 0) {
|
||||
OgreAssert(ai.nodeActions[point].size() > 0,
|
||||
@@ -889,7 +883,6 @@ void Blackboard::query_ai()
|
||||
j["global_position_y"] = p.y;
|
||||
j["global_position_z"] = p.z;
|
||||
nodes.push_back(j);
|
||||
stats["nodes"] = nodes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,14 +8,26 @@
|
||||
namespace ECS
|
||||
{
|
||||
|
||||
struct TownAI;
|
||||
|
||||
struct Blackboard {
|
||||
nlohmann::json stats;
|
||||
struct UpdateBit {
|
||||
Ogre::String checkValue;
|
||||
int recover;
|
||||
int minValue;
|
||||
int maxValue;
|
||||
int threshold;
|
||||
Ogre::String writeValue;
|
||||
};
|
||||
int object;
|
||||
int index;
|
||||
flecs::entity town;
|
||||
std::shared_ptr<std::mutex> mutex;
|
||||
static std::unordered_map<std::string, size_t> mapping;
|
||||
|
||||
private:
|
||||
// nlohmann::json stats;
|
||||
std::bitset<64> bits, mask;
|
||||
std::vector<goap::BaseAction<Blackboard> *> actionRef;
|
||||
int actionRefCount;
|
||||
int actionRefPtr;
|
||||
@@ -25,6 +37,7 @@ private:
|
||||
void _actionRefResize(int count);
|
||||
void _actionRefAddActions(
|
||||
const std::vector<goap::BaseAction<Blackboard> *> &actions);
|
||||
void populate(const nlohmann::json &stats, std::unordered_map<std::string, size_t>& mapping);
|
||||
|
||||
public:
|
||||
Blackboard();
|
||||
@@ -34,21 +47,40 @@ public:
|
||||
void apply(const Blackboard &other);
|
||||
Ogre::String dumpActions();
|
||||
void printActions();
|
||||
void fixupBooleanKeys();
|
||||
void actionRefResize(int count);
|
||||
void actionRefAddAction(goap::BaseAction<Blackboard> *action);
|
||||
void actionRefAddActions(
|
||||
const std::vector<goap::BaseAction<Blackboard> *> &actions);
|
||||
void actionRefAddActions(goap::BaseAction<Blackboard> **actions,
|
||||
int count);
|
||||
const goap::BaseAction<Blackboard> *const *getActionsData() const
|
||||
{
|
||||
return actionRef.data();
|
||||
}
|
||||
goap::BaseAction<Blackboard> **getActionsData()
|
||||
{
|
||||
return actionRef.data();
|
||||
}
|
||||
int getActionsCount()
|
||||
int getActionsCount() const
|
||||
{
|
||||
return actionRefCount;
|
||||
}
|
||||
#if 0
|
||||
void commit()
|
||||
{
|
||||
populate(stats, mapping);
|
||||
}
|
||||
#endif
|
||||
void dump_bits() const
|
||||
{
|
||||
std::cout << "bits: " << bits << std::endl;
|
||||
std::cout << "mask: " << mask << std::endl;
|
||||
}
|
||||
bool is_valid() const
|
||||
{
|
||||
return /* !stats.is_null() && */ mask != 0;
|
||||
}
|
||||
void updateBits(const TownAI &ai, const nlohmann::json &memory, const nlohmann::json &props);
|
||||
|
||||
private:
|
||||
static bool is_satisfied_by(const nlohmann::json ¤t,
|
||||
@@ -78,13 +110,62 @@ struct TownAI {
|
||||
goap::BasePlanner<Blackboard, goap::BaseAction<Blackboard> > >
|
||||
planner;
|
||||
std::unordered_map<int, Blackboard> blackboards;
|
||||
typedef goap::BasePlanner<
|
||||
Blackboard, goap::BaseAction<Blackboard> >::BaseGoal goal_t;
|
||||
typedef goap::BasePlanner<Blackboard, goap::BaseAction<Blackboard> >
|
||||
planner_t;
|
||||
struct Plan {
|
||||
const goap::BasePlanner<Blackboard,
|
||||
goap::BaseAction<Blackboard> >::BaseGoal *goal;
|
||||
const goal_t *goal;
|
||||
std::vector<goap::BaseAction<Blackboard> *> plan;
|
||||
};
|
||||
std::unordered_map<int, std::vector<struct Plan> > plans;
|
||||
std::unordered_map<int, std::vector<goap::BaseAction<Blackboard> *> > nodeActions;
|
||||
std::unordered_map<int, std::vector<goap::BaseAction<Blackboard> *> >
|
||||
nodeActions;
|
||||
struct Condition {
|
||||
std::string key;
|
||||
std::function<bool(const nlohmann::json &data)> condition;
|
||||
};
|
||||
struct MemoryUpdate {
|
||||
std::string name;
|
||||
std::function<int(int index, const std::string &name, int value)>
|
||||
update;
|
||||
};
|
||||
|
||||
struct BitEvolutionBuilder {
|
||||
std::vector<Condition> conditions;
|
||||
BitEvolutionBuilder *
|
||||
addRule(const std::string &key,
|
||||
std::function<bool(const nlohmann::json &data)>
|
||||
condition)
|
||||
{
|
||||
conditions.push_back({ key, condition });
|
||||
return this;
|
||||
}
|
||||
std::vector<Condition> build()
|
||||
{
|
||||
return conditions;
|
||||
}
|
||||
};
|
||||
struct MemoryUpdateBuilder {
|
||||
std::vector<MemoryUpdate> memoryUpdates;
|
||||
MemoryUpdateBuilder &
|
||||
addUpdate(const std::string &name,
|
||||
std::function<int(int index, const std::string &name,
|
||||
int value)>
|
||||
update)
|
||||
{
|
||||
memoryUpdates.push_back({ name, update });
|
||||
return *this;
|
||||
}
|
||||
std::vector<MemoryUpdate> build()
|
||||
{
|
||||
return memoryUpdates;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<Condition> conditions;
|
||||
std::unordered_map<int, nlohmann::json> memory;
|
||||
std::vector<MemoryUpdate> memoryUpdates;
|
||||
};
|
||||
|
||||
struct CharacterAIModule {
|
||||
@@ -92,8 +173,7 @@ struct CharacterAIModule {
|
||||
void createAI(flecs::entity town);
|
||||
void buildPlans(flecs::entity town, const TownNPCs &npcs, TownAI &ai);
|
||||
void createBlackboards(flecs::entity town, const TownNPCs &npcs, TownAI &ai);
|
||||
void updateBlackboardsBits(flecs::entity town, ActionNodeList &alist, const TownNPCs &npcs, TownAI &ai);
|
||||
void updateBlackboards(flecs::entity town, const ActionNodeList &alist, const TownNPCs &npcs, TownAI &ai);
|
||||
void updateBlackboards(flecs::entity town, ActionNodeList &alist, const TownNPCs &npcs, TownAI &ai);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -259,15 +259,6 @@ CharacterAnimationModule::CharacterAnimationModule(flecs::world &ecs)
|
||||
}
|
||||
#endif
|
||||
v.velocity = rot * boneMotion / safeDelta;
|
||||
#if 0
|
||||
if (!e.has<CharacterDisablePhysics>() &&
|
||||
!e.has<CharacterInActuator>()) {
|
||||
if (eng.startupDelay <= 0.0f)
|
||||
v.velocity += v.gvelocity;
|
||||
v.velocity.y = Ogre::Math::Clamp(v.velocity.y,
|
||||
-10.5f, 10.0f);
|
||||
}
|
||||
#endif
|
||||
// if (v.velocity.squaredLength() > 1.4f * 1.4f)
|
||||
// v.velocity = v.velocity.normalisedCopy() * 1.4f;
|
||||
// ch.mBoneMotion = Ogre::Vector3::ZERO;
|
||||
|
||||
@@ -307,30 +307,6 @@ CharacterModule::CharacterModule(flecs::world &ecs)
|
||||
// "need contact response");
|
||||
});
|
||||
#endif
|
||||
#if 0
|
||||
ecs.system<const EngineData, CharacterBase, CharacterBody>(
|
||||
"UpdateCharacterPhysics")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<Character>()
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.each([](const EngineData &eng, CharacterBase &ch,
|
||||
CharacterBody &body) {
|
||||
#if 0
|
||||
if (ch.mBodyNode && !body.mController &&
|
||||
eng.startupDelay < 0.0f) {
|
||||
body.mController =
|
||||
new Ogre::Bullet::KinematicMotionSimple(
|
||||
body.mGhostObject,
|
||||
ch.mBodyNode);
|
||||
body.mController->enableManualNarrowPhase(true);
|
||||
eng.mWorld->getBtWorld()->addAction(
|
||||
body.mController);
|
||||
OgreAssert(body.mController, "Need controller");
|
||||
}
|
||||
#endif
|
||||
});
|
||||
#endif
|
||||
#define CAM_HEIGHT 1.6f // height of camera above character's center of mass
|
||||
ecs.system<const EngineData, Camera, const CharacterBase>(
|
||||
"UpdateCamera")
|
||||
|
||||
@@ -78,5 +78,8 @@ struct Body2Entity {
|
||||
struct EditorSceneSwitch {
|
||||
int scene;
|
||||
};
|
||||
struct GameState {
|
||||
bool running;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "CharacterManagerModule.h"
|
||||
#include "items.h"
|
||||
#include "physics.h"
|
||||
#include "QuestModule.h"
|
||||
#include "GUIModule.h"
|
||||
#include "GUIModuleCommon.h"
|
||||
namespace ECS
|
||||
@@ -60,13 +61,6 @@ struct GUIListener : public Ogre::RenderTargetListener {
|
||||
bigFont = ECS::get<GUIData>().mGuiOverlay->addFont("bigFont",
|
||||
"General");
|
||||
OgreAssert(bigFont, "Could not load font");
|
||||
#if 0
|
||||
Ogre::FontPtr _midFont = createFont("midFont", "General",
|
||||
"Kenney Bold.ttf", 28.0f);
|
||||
#endif
|
||||
#if 0
|
||||
ImGui::GetIO().Fonts->Build();
|
||||
#endif
|
||||
}
|
||||
Ogre::FontPtr createFont(const Ogre::String &name,
|
||||
const Ogre::String &group,
|
||||
@@ -100,8 +94,6 @@ struct GUIListener : public Ogre::RenderTargetListener {
|
||||
ImGui::SetNextWindowSize(ImVec2(window_width, window_height),
|
||||
ImGuiCond_Always);
|
||||
ImGui::Begin("Control");
|
||||
// if (ECS::get().get<GUI>().enabled)
|
||||
// ECS::get().get<App>().app->setWindowGrab(true);
|
||||
if (ImGui::Button("Quit"))
|
||||
Ogre::Root::getSingleton().queueEndRendering();
|
||||
if (ImGui::Button("Return"))
|
||||
@@ -251,11 +243,15 @@ struct GUIListener : public Ogre::RenderTargetListener {
|
||||
{
|
||||
int i;
|
||||
Ogre::ImGuiOverlay::NewFrame();
|
||||
if (ECS::get().get<EngineData>().startupDelay > 0.0f) {
|
||||
|
||||
if (ECS::get().get<EngineData>().startupDelay > 0.0f &&
|
||||
ECS::get().has<GameState>()) {
|
||||
ImVec2 size = ImGui::GetMainViewport()->Size;
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(size.x, size.y),
|
||||
ImGuiCond_Always);
|
||||
ImVec4 solidColor = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, solidColor);
|
||||
ImGui::Begin(
|
||||
"StartupScreen", nullptr,
|
||||
ImGuiWindowFlags_NoTitleBar |
|
||||
@@ -274,6 +270,7 @@ struct GUIListener : public Ogre::RenderTargetListener {
|
||||
"This game does not autosave. Please use save function to keep your state");
|
||||
ImGui::PopFont();
|
||||
ImGui::End();
|
||||
ImGui::PopStyleColor();
|
||||
} else if (ECS::get<GUI>().narrationHandlers.size() > 0) {
|
||||
ECS::get_mut<GUI>().grab = false;
|
||||
ECS::get_mut<GUI>().grabChanged = true;
|
||||
@@ -376,9 +373,14 @@ struct GUIListener : public Ogre::RenderTargetListener {
|
||||
ImVec2 size = ImGui::GetMainViewport()->Size;
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0),
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(size.x, size.y),
|
||||
ImGui::SetNextWindowSize(ImVec2(size.x + 4,
|
||||
size.y + 1),
|
||||
ImGuiCond_Always);
|
||||
ImGui::Begin(
|
||||
ImVec4 solidColor =
|
||||
ImVec4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg,
|
||||
solidColor);
|
||||
ImGui::Begin(
|
||||
"MainMenu", nullptr,
|
||||
ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_NoDecoration |
|
||||
@@ -388,8 +390,6 @@ struct GUIListener : public Ogre::RenderTargetListener {
|
||||
ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoFocusOnAppearing |
|
||||
0);
|
||||
// if (ECS::get().get<GUI>().enabled)
|
||||
// ECS::get().get<App>().app->setWindowGrab(true);
|
||||
ImGui::PushFont(bigFont);
|
||||
ImGui::TextWrapped("%s", "Booo!!!!");
|
||||
bool pressed = false;
|
||||
@@ -412,16 +412,22 @@ struct GUIListener : public Ogre::RenderTargetListener {
|
||||
ImGui::PopFont();
|
||||
ImGui::Spacing();
|
||||
ImGui::End();
|
||||
if (quit)
|
||||
ImGui::PopStyleColor();
|
||||
if (quit)
|
||||
Ogre::Root::getSingleton()
|
||||
.queueEndRendering();
|
||||
if (pressed)
|
||||
ECS::get().get<GUI>().finish();
|
||||
if (new_game) {
|
||||
ECS::get<LuaBase>().mLua->call_handler(
|
||||
"new_game");
|
||||
}
|
||||
} else {
|
||||
ECS::get().set<GameState>({ true });
|
||||
ECS::get<LuaBase>().mLua->call_handler(
|
||||
"new_game_selected");
|
||||
} else if (load_game) {
|
||||
ECS::get().set<GameState>({ true });
|
||||
ECS::get<LuaBase>().mLua->call_handler(
|
||||
"load_game_selected");
|
||||
}
|
||||
} else {
|
||||
buttons_panel();
|
||||
if (enableEditor)
|
||||
buildings_editor();
|
||||
@@ -439,7 +445,6 @@ struct GUIListener : public Ogre::RenderTargetListener {
|
||||
ImGui::SetNextWindowSize(ImVec2(window_width,
|
||||
window_height),
|
||||
ImGuiCond_Always);
|
||||
// ImGui::Begin("Dumb and Stupid", &mKbd.gui_active);
|
||||
ImGui::Begin("Panel...");
|
||||
std::deque<Ogre::SceneNode *> tree_input_queue,
|
||||
tree_output_queue;
|
||||
@@ -645,41 +650,6 @@ struct GUIListener : public Ogre::RenderTargetListener {
|
||||
.action_text
|
||||
.c_str());
|
||||
}
|
||||
#if 0
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(screenPos.x,
|
||||
screenPos.y),
|
||||
ImGuiCond_Always,
|
||||
ImVec2(0.5f, 0.5f));
|
||||
ImGui::Begin(
|
||||
("SensorLabel##" +
|
||||
Ogre::StringConverter::toString(
|
||||
p))
|
||||
.c_str(),
|
||||
nullptr,
|
||||
ImGuiWindowFlags_NoBackground |
|
||||
ImGuiWindowFlags_NoTitleBar |
|
||||
ImGuiWindowFlags_AlwaysAutoResize |
|
||||
ImGuiWindowFlags_NoInputs);
|
||||
ImDrawList *drawList =
|
||||
ImGui::GetWindowDrawList();
|
||||
ImVec2 cursor =
|
||||
ImGui::GetCursorScreenPos();
|
||||
drawList->AddCircleFilled(
|
||||
ImVec2(cursor.x, cursor.y),
|
||||
16.0f,
|
||||
IM_COL32(0, 0, 255, 255));
|
||||
drawList->AddCircle(
|
||||
ImVec2(cursor.x, cursor.y),
|
||||
16.0f,
|
||||
IM_COL32(32, 32, 255, 255));
|
||||
|
||||
ImGui::TextColored(
|
||||
ImVec4(1, 0, 0, 1), "%s",
|
||||
list.nodes[p]
|
||||
.action_text.c_str());
|
||||
ImGui::End();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -690,13 +660,14 @@ GUIModule::GUIModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<GUIModule>();
|
||||
ecs.import <AppModule>();
|
||||
ecs.import <QuestModule>();
|
||||
ecs.component<GUI>()
|
||||
.on_add([](GUI &gui) {
|
||||
gui.enabled = false;
|
||||
gui.grab = false;
|
||||
gui.grabChanged = false;
|
||||
gui.enableActions = true;
|
||||
})
|
||||
})
|
||||
.add(flecs::Singleton);
|
||||
ecs.component<GUIData>()
|
||||
.on_add([](GUIData &priv) {
|
||||
@@ -705,51 +676,39 @@ GUIModule::GUIModule(flecs::world &ecs)
|
||||
priv.mGuiOverlay = nullptr;
|
||||
})
|
||||
.add(flecs::Singleton);
|
||||
ecs.set<GUI>({ false, true, false, false, false, true, "", {}, -1 });
|
||||
ecs.set<GUIData>({ nullptr, {}, nullptr });
|
||||
ui_wait =
|
||||
ecs.system<const RenderWindow, App, GUIData>("SetupGUI")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([this](const RenderWindow &window, App &app,
|
||||
GUIData &gui) {
|
||||
if (!gui.mGuiOverlay) {
|
||||
float vpScale =
|
||||
window.dpi / 96 *
|
||||
window.window->getWidth() /
|
||||
1600.0f;
|
||||
Ogre::OverlayManager::getSingleton()
|
||||
.setPixelRatio(vpScale);
|
||||
std::cout << "GUI configure\n";
|
||||
OgreAssert(app.mGuiOverlay,
|
||||
"No ImGUI overlay");
|
||||
gui.mGuiOverlay = app.mGuiOverlay;
|
||||
gui.mGUIListener = new GUIListener();
|
||||
gui.mGuiOverlay->setZOrder(300);
|
||||
gui.mGuiOverlay->show();
|
||||
gui.mGUIListener->panel_width = 300.0f;
|
||||
gui.mGUIListener->enableEditor = false;
|
||||
window.window->addListener(
|
||||
gui.mGUIListener);
|
||||
int i;
|
||||
}
|
||||
|
||||
const std::vector<Ogre::String> &groups =
|
||||
Ogre::ResourceGroupManager::
|
||||
getSingleton()
|
||||
.getResourceGroups();
|
||||
for (i = 0; i < groups.size(); i++) {
|
||||
std::vector<Ogre::String> names =
|
||||
*Ogre::ResourceGroupManager::getSingleton()
|
||||
.findResourceNames(
|
||||
groups[i],
|
||||
"*.glb");
|
||||
gui.glb_names.insert(
|
||||
gui.glb_names.end(),
|
||||
names.begin(),
|
||||
names.end());
|
||||
}
|
||||
ECS::modified<ECS::GUI>();
|
||||
std::cout << "GUI configure finished\n";
|
||||
}
|
||||
});
|
||||
void GUIModule::configure()
|
||||
{
|
||||
ECS::get().set<GUIData>({ nullptr, {}, nullptr });
|
||||
const RenderWindow &window = ECS::get<RenderWindow>();
|
||||
GUIData &gui = ECS::get_mut<GUIData>();
|
||||
const App &app = ECS::get<App>();
|
||||
if (gui.mGuiOverlay)
|
||||
return;
|
||||
float vpScale = window.dpi / 96 * window.window->getWidth() / 1600.0f;
|
||||
Ogre::OverlayManager::getSingleton().setPixelRatio(vpScale);
|
||||
std::cout << "GUI configure\n";
|
||||
OgreAssert(app.mGuiOverlay, "No ImGUI overlay");
|
||||
gui.mGuiOverlay = app.mGuiOverlay;
|
||||
gui.mGUIListener = new GUIListener();
|
||||
gui.mGuiOverlay->setZOrder(300);
|
||||
gui.mGuiOverlay->show();
|
||||
gui.mGUIListener->panel_width = 300.0f;
|
||||
gui.mGUIListener->enableEditor = false;
|
||||
window.window->addListener(gui.mGUIListener);
|
||||
int i;
|
||||
|
||||
const std::vector<Ogre::String> &groups =
|
||||
Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
|
||||
for (i = 0; i < groups.size(); i++) {
|
||||
std::vector<Ogre::String> names =
|
||||
*Ogre::ResourceGroupManager::getSingleton()
|
||||
.findResourceNames(groups[i], "*.glb");
|
||||
gui.glb_names.insert(gui.glb_names.end(), names.begin(),
|
||||
names.end());
|
||||
}
|
||||
ECS::modified<GUIData>();
|
||||
std::cout << "GUI configure finished\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace ECS
|
||||
struct GUIModule {
|
||||
flecs::entity ui_wait;
|
||||
GUIModule(flecs::world &ecs);
|
||||
static void configure();
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "PlayerActionModule.h"
|
||||
#include "AppModule.h"
|
||||
#include "CharacterAIModule.h"
|
||||
#include "QuestModule.h"
|
||||
#include "world-build.h"
|
||||
|
||||
namespace ECS
|
||||
@@ -49,9 +50,9 @@ void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
Ogre::Camera *camera, Ogre::RenderWindow *window)
|
||||
{
|
||||
std::cout << "Setup GameData\n";
|
||||
setup_minimal();
|
||||
ecs.component<RenderWindow>().add(flecs::Singleton);
|
||||
ecs.import <CharacterModule>();
|
||||
setup_minimal();
|
||||
ecs.component<GameState>().add(flecs::Singleton);
|
||||
ecs.import <CharacterModule>();
|
||||
ecs.import <BoatModule>();
|
||||
ecs.import <PhysicsModule>();
|
||||
ecs.import <WaterModule>();
|
||||
@@ -64,8 +65,11 @@ void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
ecs.import <CharacterAnimationModule>();
|
||||
ecs.import <PlayerActionModule>();
|
||||
ecs.import <CharacterAIModule>();
|
||||
ecs.import <QuestModule>();
|
||||
ecs.add<ActionNodeList>();
|
||||
|
||||
ecs.set<GUI>({ false, false, true, false, false, true, "", {}, -1 });
|
||||
|
||||
ecs.system<EngineData>("UpdateDelta")
|
||||
.kind(flecs::OnUpdate)
|
||||
.each([](EngineData &eng) {
|
||||
@@ -73,6 +77,7 @@ void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
});
|
||||
ecs.system<EngineData>("UpdateDelay")
|
||||
.kind(flecs::OnUpdate)
|
||||
.with<GameState>()
|
||||
.with<TerrainReady>()
|
||||
.with<WaterReady>()
|
||||
.with<GroundCheckReady>()
|
||||
@@ -119,49 +124,69 @@ void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
|
||||
// ecs.set<Body2Entity>({});
|
||||
std::cout << "Setup GameData done\n";
|
||||
ecs.system("SpawnPlayer").kind(flecs::OnUpdate).interval(0.5f).run([&](flecs::iter &it) {
|
||||
flecs::entity player =
|
||||
ECS::get<CharacterManagerModule>().getPlayer();
|
||||
if (!player.is_valid()) {
|
||||
/* Create player */
|
||||
Ogre::Vector3 position;
|
||||
JPH::BodyID id;
|
||||
long x, y;
|
||||
Ogre::TerrainGroup *tg =
|
||||
ECS::get<ECS::Terrain>().mTerrainGroup;
|
||||
if (tg->isDerivedDataUpdateInProgress())
|
||||
return;
|
||||
tg->convertWorldPositionToTerrainSlot(
|
||||
Ogre::Vector3(0, 0, 4), &x, &y);
|
||||
Ogre::Terrain *terrain = tg->getTerrain(x, y);
|
||||
if (terrain && terrain->isLoaded()) {
|
||||
if (PhysicsModule::raycastQuery(
|
||||
Ogre::Vector3(0, 500, 4),
|
||||
Ogre::Vector3(0, -500, 4), position,
|
||||
id)) {
|
||||
if (position.y < -10.0f &&
|
||||
position.y > -50.0f) {
|
||||
player =
|
||||
ecs.get_mut<
|
||||
CharacterManagerModule>()
|
||||
.createPlayer(
|
||||
{ position.x,
|
||||
position.y,
|
||||
position.z },
|
||||
Ogre::Quaternion(
|
||||
Ogre::Radian(
|
||||
Ogre::Math::
|
||||
PI),
|
||||
Ogre::Vector3::
|
||||
UNIT_Y));
|
||||
std::cout << position
|
||||
<< std::endl;
|
||||
// OgreAssert(false, "spawn");
|
||||
ecs.system<Terrain, GameState>("SpawnPlayer")
|
||||
.kind(flecs::OnUpdate)
|
||||
.interval(0.5f)
|
||||
.each([&](Terrain &mterrain, GameState &game) {
|
||||
flecs::entity player =
|
||||
ECS::get<CharacterManagerModule>().getPlayer();
|
||||
if (!player.is_valid()) {
|
||||
/* Create player */
|
||||
Ogre::Vector3 position;
|
||||
JPH::BodyID id;
|
||||
long x, y;
|
||||
Ogre::TerrainGroup *tg = mterrain.mTerrainGroup;
|
||||
if (tg->isDerivedDataUpdateInProgress())
|
||||
return;
|
||||
tg->convertWorldPositionToTerrainSlot(
|
||||
Ogre::Vector3(0, 0, 4), &x, &y);
|
||||
Ogre::Terrain *terrain = tg->getTerrain(x, y);
|
||||
if (terrain && terrain->isLoaded()) {
|
||||
if (PhysicsModule::raycastQuery(
|
||||
Ogre::Vector3(0, 500, 4),
|
||||
Ogre::Vector3(0, -500, 4),
|
||||
position, id)) {
|
||||
if (position.y < -10.0f &&
|
||||
position.y > -50.0f) {
|
||||
player =
|
||||
ecs.get_mut<
|
||||
CharacterManagerModule>()
|
||||
.createPlayer(
|
||||
{ position.x,
|
||||
position.y,
|
||||
position.z },
|
||||
Ogre::Quaternion(
|
||||
Ogre::Radian(
|
||||
Ogre::Math::
|
||||
PI),
|
||||
Ogre::Vector3::
|
||||
UNIT_Y));
|
||||
std::cout << position
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// FIXME: convert this later to scene setup event
|
||||
static flecs::entity new_game_run =
|
||||
ecs.system<GameState>("NewGame")
|
||||
.kind(flecs::OnUpdate)
|
||||
.interval(0.5f)
|
||||
.each([&](GameState &game) {
|
||||
flecs::entity player =
|
||||
ECS::get<CharacterManagerModule>()
|
||||
.getPlayer();
|
||||
if (player.is_valid() &&
|
||||
player.has<CharacterBase>() &&
|
||||
player.has<Character>() &&
|
||||
player.has<Player>()) {
|
||||
ECS::get<LuaBase>().mLua->call_handler(
|
||||
"new_game");
|
||||
new_game_run.destruct();
|
||||
}
|
||||
});
|
||||
std::cout << "scene setup done" << std::endl;
|
||||
}
|
||||
void setupInteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
Ogre::Camera *camera, Ogre::RenderWindow *window)
|
||||
@@ -250,8 +275,6 @@ void setupEditor(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
ecs.modified<GUI>();
|
||||
ecs.get_mut<GUI>().setWindowGrab(true);
|
||||
ecs.modified<GUI>();
|
||||
ecs.get_mut<GUI>().enabled = true;
|
||||
ecs.modified<GUI>();
|
||||
}
|
||||
|
||||
void update(float delta)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#ifndef GAMEDATA_H
|
||||
#define GAMEDATA_H
|
||||
#include <Ogre.h>
|
||||
#include <flecs.h>
|
||||
namespace ECS
|
||||
{
|
||||
extern flecs::entity player;
|
||||
void setup_minimal();
|
||||
void setupExteriorScene(Ogre::SceneManager *scnMgr, Ogre::SceneNode *cameraNode,
|
||||
Ogre::Camera *camera, Ogre::RenderWindow *window);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "SlotsModule.h"
|
||||
#include "world-build.h"
|
||||
#include "PlayerActionModule.h"
|
||||
#include "QuestModule.h"
|
||||
#include "LuaData.h"
|
||||
#include "lua.hpp"
|
||||
extern "C" {
|
||||
@@ -123,7 +124,7 @@ int LuaData::call_handler(const Ogre::String &event)
|
||||
lua_pushstring(L, event.c_str());
|
||||
lua_pushinteger(L, -1);
|
||||
lua_pushinteger(L, -1);
|
||||
if (lua_pcall(L, 3, 0, 0)) {
|
||||
if (lua_pcall(L, 3, 0, 0) != LUA_OK) {
|
||||
Ogre::LogManager::getSingleton().stream()
|
||||
<< lua_tostring(L, -1);
|
||||
OgreAssert(false, "Lua error");
|
||||
@@ -141,7 +142,7 @@ int LuaData::call_handler(const Ogre::String &event, flecs::entity e,
|
||||
lua_pushstring(L, event.c_str());
|
||||
lua_pushinteger(L, idmap.add_entity(e));
|
||||
lua_pushinteger(L, idmap.add_entity(o));
|
||||
if (lua_pcall(L, 3, 0, 0)) {
|
||||
if (lua_pcall(L, 3, 0, 0) != LUA_OK) {
|
||||
Ogre::LogManager::getSingleton().stream()
|
||||
<< lua_tostring(L, -1);
|
||||
OgreAssert(false, "Lua error");
|
||||
@@ -315,6 +316,14 @@ LuaData::LuaData()
|
||||
return 0;
|
||||
});
|
||||
lua_setglobal(L, "setup_action_handler");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
luaL_checktype(L, 1, LUA_TSTRING);
|
||||
luaL_checktype(L, 2, LUA_TTABLE);
|
||||
ECS::get_mut<QuestModule>().addLuaQuest(L);
|
||||
ECS::modified<QuestModule>();
|
||||
return 0;
|
||||
});
|
||||
lua_setglobal(L, "add_quest");
|
||||
lua_pushcfunction(L, [](lua_State *L) -> int {
|
||||
// FIXME
|
||||
return 0;
|
||||
|
||||
326
src/gamedata/QuestModule.cpp
Normal file
326
src/gamedata/QuestModule.cpp
Normal file
@@ -0,0 +1,326 @@
|
||||
#include <iostream>
|
||||
#include "Components.h"
|
||||
#include "GameData.h"
|
||||
#include "GUIModuleCommon.h"
|
||||
#include "QuestModule.h"
|
||||
|
||||
namespace ECS
|
||||
{
|
||||
|
||||
QuestModule::QuestModule(flecs::world &ecs)
|
||||
{
|
||||
ecs.module<QuestModule>();
|
||||
ecs.observer<GameState>("EnableQuests")
|
||||
.event(flecs::OnAdd)
|
||||
.each([this](GameState &game) {
|
||||
quest_update =
|
||||
ECS::get()
|
||||
.system<const EngineData>(
|
||||
"UpdateQuests")
|
||||
.each([this](const EngineData &eng) {
|
||||
for (auto &quest : quests) {
|
||||
if (!quest->_is_complete())
|
||||
quest->_update(
|
||||
eng.delta);
|
||||
}
|
||||
});
|
||||
});
|
||||
ecs.observer<GameState>("DisableQuests")
|
||||
.event(flecs::OnRemove)
|
||||
.each([this](GameState &game) {
|
||||
if (quest_update.is_valid())
|
||||
quest_update.destruct();
|
||||
});
|
||||
}
|
||||
|
||||
void QuestModule::addQuest(Quest *quest)
|
||||
{
|
||||
quests.insert(quest);
|
||||
}
|
||||
|
||||
void QuestModule::removeQuest(Quest *quest)
|
||||
{
|
||||
quests.erase(quests.find(quest));
|
||||
}
|
||||
struct LuaNarrationHandler : GUI::NarrationHandler {
|
||||
int ref;
|
||||
lua_State *L;
|
||||
LuaNarrationHandler(lua_State *L, int ref)
|
||||
: ref(ref)
|
||||
, L(L)
|
||||
{
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
|
||||
lua_pushlightuserdata(L, this);
|
||||
lua_pushcclosure(
|
||||
L,
|
||||
[](lua_State *L) {
|
||||
luaL_checktype(L, 1, LUA_TSTRING);
|
||||
luaL_checktype(L, 2, LUA_TTABLE);
|
||||
LuaNarrationHandler *handler =
|
||||
static_cast<LuaNarrationHandler *>(
|
||||
lua_touserdata(
|
||||
L,
|
||||
lua_upvalueindex(1)));
|
||||
Ogre::String event = lua_tostring(L, 1);
|
||||
std::vector<Ogre::String> choices;
|
||||
int choicesLen = (int)lua_rawlen(L, 2);
|
||||
choices.reserve(choicesLen);
|
||||
for (int i = 1; i <= choicesLen; ++i) {
|
||||
lua_rawgeti(L, 2, i);
|
||||
if (lua_isstring(L, -1))
|
||||
choices.push_back(
|
||||
lua_tostring(L, -1));
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
handler->_narration(event, choices);
|
||||
return 0;
|
||||
},
|
||||
1);
|
||||
lua_setfield(L, -2, "_narration");
|
||||
lua_pushlightuserdata(L, this);
|
||||
lua_pushcclosure(
|
||||
L,
|
||||
[](lua_State *L) {
|
||||
LuaNarrationHandler *handler =
|
||||
static_cast<LuaNarrationHandler *>(
|
||||
lua_touserdata(
|
||||
L,
|
||||
lua_upvalueindex(1)));
|
||||
handler->_finish();
|
||||
return 0;
|
||||
},
|
||||
1);
|
||||
lua_setfield(L, -2, "_finish");
|
||||
lua_pushlightuserdata(L, this);
|
||||
lua_pushcclosure(
|
||||
L,
|
||||
[](lua_State *L) {
|
||||
LuaNarrationHandler *handler =
|
||||
static_cast<LuaNarrationHandler *>(
|
||||
lua_touserdata(
|
||||
L,
|
||||
lua_upvalueindex(1)));
|
||||
int answer = handler->getNarrationAnswer();
|
||||
lua_pushinteger(L, answer);
|
||||
return 1;
|
||||
},
|
||||
1);
|
||||
lua_setfield(L, -2, "_get_narration_answer");
|
||||
lua_pushlightuserdata(L, this);
|
||||
lua_pushcclosure(
|
||||
L,
|
||||
[](lua_State *L) {
|
||||
LuaNarrationHandler *handler =
|
||||
static_cast<LuaNarrationHandler *>(
|
||||
lua_touserdata(
|
||||
L,
|
||||
lua_upvalueindex(1)));
|
||||
lua_pushstring(
|
||||
L, handler->getProperties().c_str());
|
||||
return 1;
|
||||
},
|
||||
1);
|
||||
lua_setfield(L, -2, "_get_properties");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
void finish() override
|
||||
{
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
|
||||
int type = lua_getfield(L, -1, "finish");
|
||||
OgreAssert(type == LUA_TFUNCTION, "bad finish()");
|
||||
lua_insert(L, -2);
|
||||
if (lua_pcall(L, 1, 0, 0) != 0) {
|
||||
std::cerr << lua_tostring(L, -1) << std::endl;
|
||||
OgreAssert(false, "lua error");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
_clear_narration();
|
||||
}
|
||||
void activate() override
|
||||
{
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
|
||||
int type = lua_getfield(L, -1, "activate");
|
||||
OgreAssert(type == LUA_TFUNCTION, "bad activate()");
|
||||
lua_insert(L, -2);
|
||||
if (lua_pcall(L, 1, 0, 0) != 0) {
|
||||
std::cerr << lua_tostring(L, -1) << std::endl;
|
||||
OgreAssert(false, "lua error");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
void event(const Ogre::String &evt) override
|
||||
{
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
|
||||
int type = lua_getfield(L, -1, "event");
|
||||
OgreAssert(type == LUA_TFUNCTION, "bad event()");
|
||||
lua_insert(L, -2);
|
||||
lua_pushstring(L, evt.c_str());
|
||||
if (lua_pcall(L, 2, 0, 0) != 0) {
|
||||
std::cerr << lua_tostring(L, -1) << std::endl;
|
||||
OgreAssert(false, "lua error");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct LuaQuest : QuestModule::Quest {
|
||||
lua_State *L;
|
||||
int ref;
|
||||
LuaQuest(const std::string &name, lua_State *L, int ref)
|
||||
: Quest(name)
|
||||
, L(L)
|
||||
, ref(ref)
|
||||
{
|
||||
OgreAssert(L, "bad Lua state");
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
|
||||
lua_pushlightuserdata(L, this);
|
||||
lua_pushcclosure(
|
||||
L,
|
||||
[](lua_State *L) {
|
||||
LuaQuest *_this = static_cast<LuaQuest *>(
|
||||
lua_touserdata(L, lua_upvalueindex(1)));
|
||||
_this->_finish(LuaQuest::OK);
|
||||
return 0;
|
||||
},
|
||||
1);
|
||||
lua_setfield(L, -2, "_finish");
|
||||
lua_pushlightuserdata(L, this);
|
||||
lua_pushcclosure(
|
||||
L,
|
||||
[](lua_State *L) {
|
||||
LuaQuest *_this = static_cast<LuaQuest *>(
|
||||
lua_touserdata(L, lua_upvalueindex(1)));
|
||||
luaL_checktype(L, 1, LUA_TTABLE);
|
||||
lua_pushvalue(L, 1);
|
||||
int nref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
LuaNarrationHandler *handle =
|
||||
OGRE_NEW LuaNarrationHandler(L, nref);
|
||||
ECS::get_mut<GUI>().addNarrationHandler(handle);
|
||||
ECS::modified<GUI>();
|
||||
|
||||
return 0;
|
||||
},
|
||||
1);
|
||||
lua_setfield(L, -2, "_add_narration");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
void finish(int rc) override
|
||||
{
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
|
||||
int type = lua_getfield(L, -1, "finish");
|
||||
OgreAssert(type == LUA_TFUNCTION, "bad finish()");
|
||||
lua_insert(L, -2);
|
||||
lua_pushinteger(L, rc);
|
||||
if (lua_pcall(L, 2, 0, 0) != 0) {
|
||||
std::cerr << lua_tostring(L, -1) << std::endl;
|
||||
OgreAssert(false, "lua error");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
void activate() override
|
||||
{
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
|
||||
int type = lua_getfield(L, -1, "activate");
|
||||
OgreAssert(type == LUA_TFUNCTION, "bad activate()");
|
||||
lua_insert(L, -2);
|
||||
if (lua_pcall(L, 1, 0, 0) != 0) {
|
||||
std::cerr << lua_tostring(L, -1) << std::endl;
|
||||
OgreAssert(false, "lua error");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
void event(const Ogre::String &evt) override
|
||||
{
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
|
||||
int type = lua_getfield(L, -1, "event");
|
||||
OgreAssert(type == LUA_TFUNCTION, "bad event()");
|
||||
lua_insert(L, -2);
|
||||
lua_pushstring(L, evt.c_str());
|
||||
if (lua_pcall(L, 2, 0, 0) != 0) {
|
||||
std::cerr << lua_tostring(L, -1) << std::endl;
|
||||
OgreAssert(false, "lua error");
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
int update(float delta) override
|
||||
{
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
|
||||
int type = lua_getfield(L, -1, "finish");
|
||||
OgreAssert(type == LUA_TFUNCTION, "bad finish()");
|
||||
lua_insert(L, -2);
|
||||
if (lua_pcall(L, 1, 1, 0) != LUA_OK) {
|
||||
std::cerr << lua_tostring(L, -1) << std::endl;
|
||||
OgreAssert(false, "lua error");
|
||||
lua_pop(L, 1);
|
||||
} else {
|
||||
int ret = lua_tointeger(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return ret;
|
||||
}
|
||||
return ERROR;
|
||||
}
|
||||
};
|
||||
|
||||
void QuestModule::addLuaQuest(lua_State *L)
|
||||
{
|
||||
luaL_checktype(L, 1, LUA_TSTRING);
|
||||
luaL_checktype(L, 2, LUA_TTABLE);
|
||||
Ogre::String name = lua_tostring(L, 1);
|
||||
lua_pushvalue(L, 2);
|
||||
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
struct LuaQuest *quest = OGRE_NEW LuaQuest(name, L, ref);
|
||||
addQuest(quest);
|
||||
}
|
||||
|
||||
void QuestModule::Quest::_activate()
|
||||
{
|
||||
activate();
|
||||
active = true;
|
||||
}
|
||||
|
||||
void QuestModule::Quest::_finish(int rc)
|
||||
{
|
||||
finish(rc);
|
||||
active = false;
|
||||
if (rc == OK)
|
||||
complete = true;
|
||||
}
|
||||
|
||||
QuestModule::Quest::Quest(const std::string &name)
|
||||
: name(name)
|
||||
, active(false)
|
||||
, complete(false)
|
||||
, updatePeriod(1)
|
||||
, timeAcc(0)
|
||||
{
|
||||
}
|
||||
|
||||
QuestModule::Quest::~Quest()
|
||||
{
|
||||
}
|
||||
|
||||
int QuestModule::Quest::_update(float delta)
|
||||
{
|
||||
if (!active && !_can_activate())
|
||||
return ERROR;
|
||||
if (!active)
|
||||
_activate();
|
||||
timeAcc += delta;
|
||||
|
||||
if (timeAcc > updatePeriod)
|
||||
timeAcc = 0;
|
||||
else
|
||||
return BUSY;
|
||||
int ret = update(delta);
|
||||
if (ret == BUSY)
|
||||
return ret;
|
||||
_finish(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void QuestModule::Quest::_event(const std::string &evt)
|
||||
{
|
||||
event(evt);
|
||||
}
|
||||
|
||||
} // namespace ECS
|
||||
47
src/gamedata/QuestModule.h
Normal file
47
src/gamedata/QuestModule.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef ECS_QUESTMODULE_H
|
||||
#define ECS_QUESTMODULE_H
|
||||
|
||||
#include <flecs.h>
|
||||
#include <Ogre.h>
|
||||
#include <lua.hpp>
|
||||
|
||||
namespace ECS {
|
||||
|
||||
struct QuestModule
|
||||
{
|
||||
struct Quest {
|
||||
enum {OK = 0, BUSY, ERROR};
|
||||
private:
|
||||
virtual void activate() = 0;
|
||||
virtual int update(float delta) = 0;
|
||||
virtual void finish(int ret) = 0;
|
||||
virtual void event(const std::string &evt) = 0;
|
||||
virtual bool _can_activate() {return true;};
|
||||
protected:
|
||||
std::string name;
|
||||
bool active;
|
||||
bool complete;
|
||||
float updatePeriod;
|
||||
float timeAcc;
|
||||
void _activate();
|
||||
void _finish(int ret);
|
||||
public:
|
||||
Quest(const std::string &name);
|
||||
virtual ~Quest();
|
||||
int _update(float delta);
|
||||
void _event(const std::string &evt);
|
||||
bool _is_active() {return active;}
|
||||
bool _is_complete() {return complete;}
|
||||
};
|
||||
std::set<Quest *> quests;
|
||||
flecs::entity quest_update;
|
||||
public:
|
||||
QuestModule(flecs::world &ecs);
|
||||
void addQuest(Quest *quest);
|
||||
void removeQuest(Quest *quest);
|
||||
void addLuaQuest(lua_State *L);
|
||||
};
|
||||
|
||||
} // namespace ECS
|
||||
|
||||
#endif // ECS_QUESTMODULE_H
|
||||
@@ -16,3 +16,5 @@ find_package(flecs REQUIRED CONFIG)
|
||||
add_library(physics STATIC physics.cpp)
|
||||
target_link_libraries(physics PUBLIC OgreMain Jolt::Jolt)
|
||||
target_include_directories(physics PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_compile_definitions(physics PRIVATE JPH_PROFILE_ENABLED)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user