]> git.tdb.fi Git - libs/game.git/commitdiff
Support loading stage contents from files
authorMikko Rasa <tdb@tdb.fi>
Sat, 15 Mar 2025 17:39:22 +0000 (19:39 +0200)
committerMikko Rasa <tdb@tdb.fi>
Sat, 15 Mar 2025 22:29:37 +0000 (00:29 +0200)
source/game/resources.cpp
source/game/stage.cpp
source/game/stage.h
source/game/stageplan.cpp [new file with mode: 0644]
source/game/stageplan.h [new file with mode: 0644]

index 0eb5a85d83ed23be6d7ea89b4af7c0161f7dd6c4..e1f17c73f0f802473ae66b6bc2adaa60db5c585c 100644 (file)
@@ -3,6 +3,7 @@
 #include <msp/fs/dir.h>
 #include <msp/fs/stat.h>
 #include <msp/game/setups.h>
+#include "stageplan.h"
 
 using namespace std;
 
@@ -13,6 +14,7 @@ Resources::Resources()
        add_type<CameraSetup>().suffix(".camera.setup");
        add_type<HeightmapTerrainSetup>().suffix(".hmap.setup");
        add_type<LightSetup>().suffix(".light.setup");
+       add_type<StagePlan>().suffix(".stage");
 }
 
 
index 530811bd47ad936464d3445255b659ec5b30b578..34940b5e8d5e872b46e6df9ae8f3d17671b19a66 100644 (file)
@@ -4,12 +4,19 @@
 #include "accessguard.h"
 #include "camera.h"
 #include "root.h"
+#include "stageplan.h"
 #include "system.h"
 
 using namespace std;
 
 namespace Msp::Game {
 
+struct Stage::Content
+{
+       vector<Owned<Entity>> entities;
+};
+
+
 Stage::Stage(const string &n, ThreadPool &t, Reflection::Reflector &f, DataFile::Collection &r):
        name(n),
        threads(t),
@@ -32,6 +39,19 @@ Stage::Stage(const string &n, ThreadPool &t, Reflection::Reflector &f, DataFile:
 Stage::~Stage()
 { }
 
+void Stage::apply_plan(const StagePlan &plan)
+{
+       vector<Owned<Entity>> new_entities = plan.create_entities(*this);
+       if(new_entities.empty())
+               return;
+
+       if(!content)
+               content = make_unique<Content>();
+       content->entities.reserve(content->entities.size()+new_entities.size());
+       for(Owned<Entity> &e: new_entities)
+               content->entities.push_back(move(e));
+}
+
 void Stage::remove_system(System &s)
 {
        scheduler.remove_system(s);
index b6a8c08a6ec47447e497ae2f15abe6d06f6dfb87..b8020c913f87a049bc1f273a1e335548339ce6cc 100644 (file)
@@ -18,6 +18,7 @@ namespace Msp::Game {
 
 class Camera;
 class Root;
+class StagePlan;
 class System;
 
 class MSPGAME_API Stage
@@ -28,6 +29,8 @@ public:
                Events::RemotePlayerEntered, Events::RemotePlayerExited>;
 
 private:
+       struct Content;
+
        std::string name;
        ThreadPool &threads;
        Reflection::Reflector &reflector;
@@ -39,6 +42,7 @@ private:
        /* Use unique_ptr because there's only one root per stage so it's pointless
        to put it in a pool. */
        std::unique_ptr<Root> root;
+       std::unique_ptr<Content> content;
        std::vector<std::unique_ptr<System>> systems;
        SystemScheduler scheduler;
        Handle<Camera> active_camera;
@@ -60,6 +64,8 @@ public:
        template<typename T, typename F>
        void iterate_objects(const F &);
 
+       void apply_plan(const StagePlan &);
+
        template<typename T, typename... Args>
        T &add_system(Args &&...);
 
diff --git a/source/game/stageplan.cpp b/source/game/stageplan.cpp
new file mode 100644 (file)
index 0000000..a1b279d
--- /dev/null
@@ -0,0 +1,91 @@
+#include "stageplan.h"
+#include "entity.h"
+#include "root.h"
+
+using namespace std;
+
+namespace Msp::Game {
+
+vector<Owned<Entity>> StagePlan::create_entities(Stage &stage) const
+{
+       vector<Owned<Entity>> result;
+       result.reserve(entities.size());
+       for(const unique_ptr<EntityBase> &e: entities)
+       {
+               Handle<Entity> parent = (e->parent_index>=0 ? static_cast<Handle<Entity>>(result[e->parent_index]) : stage.get_root());
+               result.push_back(e->create(parent));
+       }
+       return result;
+}
+
+auto StagePlan::get_entity_registry() -> EntityRegistry &
+{
+       static EntityRegistry registry;
+       return registry;
+}
+
+
+StagePlan::Loader::Loader(StagePlan &p, DataFile::Collection &c):
+       CollectionObjectLoader(p, &c)
+{
+       static ActionMap shared_actions;
+       set_actions(shared_actions);
+}
+
+void StagePlan::Loader::init_actions()
+{
+       get_entity_registry().invoke_all(*this);
+}
+
+
+StagePlan::EntityBase::Loader::Loader(EntityBase &e):
+       ObjectLoader(e)
+{
+       static ActionMap shared_actions;
+       set_actions(shared_actions);
+}
+
+void StagePlan::EntityBase::Loader::init_actions()
+{
+       add("transform", &Loader::transform);
+}
+
+void StagePlan::EntityBase::Loader::transform()
+{
+       TransformLoader ldr(obj);
+       load_sub_with(ldr);
+}
+
+
+StagePlan::EntityBase::TransformLoader::TransformLoader(EntityBase &e):
+       ObjectLoader(e)
+{
+       static ActionMap shared_actions;
+       set_actions(shared_actions);
+}
+
+void StagePlan::EntityBase::TransformLoader::init_actions()
+{
+       add("euler", &TransformLoader::euler);
+       add("position", &TransformLoader::position);
+       add("rotation", &TransformLoader::rotation);
+}
+
+void StagePlan::EntityBase::TransformLoader::euler(float x, float y, float z)
+{
+       obj.transform.rotation = Geometry::make_quat(z*Geometry::degrees, { 0.0f, 0.0f, 1.0f })*
+               Geometry::make_quat(y*Geometry::degrees, { 0.0f, 1.0f, 0.0f })*
+               Geometry::make_quat(x*Geometry::degrees, { 1.0f, 0.0f, 0.0f });
+}
+
+void StagePlan::EntityBase::TransformLoader::position(float x, float y, float z)
+{
+       obj.transform.position = { x, y, z };
+}
+
+void StagePlan::EntityBase::TransformLoader::rotation(float a, float x, float y, float z)
+{
+       obj.transform.rotation = Geometry::make_quat(a*Geometry::degrees, { x, y, z });
+}
+
+} // namespace Msp::Game
diff --git a/source/game/stageplan.h b/source/game/stageplan.h
new file mode 100644 (file)
index 0000000..ed68e9c
--- /dev/null
@@ -0,0 +1,126 @@
+#ifndef STAGEPLAN_H_
+#define STAGEPLAN_H_
+
+#include <msp/core/typeregistry.h>
+#include <msp/datafile/objectloader.h>
+#include "mspgame_api.h"
+#include "owned.h"
+#include "transform.h"
+
+namespace Msp::Game {
+
+class Entity;
+
+class MSPGAME_API StagePlan
+{
+public:
+       class MSPGAME_API Loader: public DataFile::CollectionObjectLoader<StagePlan>
+       {
+               friend class StagePlan;
+
+       private:
+               template<typename T>
+               struct AddEntityType
+               {
+                       void operator()(const std::string &kw, Loader &l) const { l.add(kw, &Loader::entity<T>); }
+               };
+
+       public:
+               Loader(StagePlan &, DataFile::Collection &);
+
+       private:
+               void init_actions() override;
+
+               template<typename T>
+               void entity(const std::string &);
+       };
+
+private:
+       struct EntityBase
+       {
+               class MSPGAME_API Loader: public DataFile::ObjectLoader<EntityBase>
+               {
+               public:
+                       Loader(EntityBase &);
+
+               private:
+                       void init_actions() override;
+
+               public:
+                       void transform();
+               };
+
+               class TransformLoader: public DataFile::ObjectLoader<EntityBase>
+               {
+               public:
+                       TransformLoader(EntityBase &);
+
+               private:
+                       void init_actions() override;
+
+                       void euler(float, float, float);
+                       void position(float, float, float);
+                       void rotation(float, float, float, float);
+               };
+
+               int parent_index = -1;
+               TransformValues transform;
+
+               virtual ~EntityBase() = default;
+
+               virtual Owned<Entity> create(Handle<Entity>) const = 0;
+       };
+
+       template<typename T>
+       struct PlannedEntity: EntityBase
+       {
+               const T::Setup &setup = nullptr;
+
+               PlannedEntity(const T::Setup &s): setup(s) { }
+
+               Owned<Entity> create(Handle<Entity>) const override;
+       };
+
+       using EntityRegistry = TypeRegistry<Loader::AddEntityType, Loader &>;
+
+       std::vector<std::unique_ptr<EntityBase>> entities;
+
+public:
+       StagePlan() = default;
+       StagePlan(StagePlan &&) = default;
+       StagePlan &operator=(StagePlan &&) = default;
+
+       std::vector<Owned<Entity>> create_entities(Stage &) const;
+
+       template<typename T>
+       static void register_entity_type(const std::string &);
+
+private:
+       static EntityRegistry &get_entity_registry();
+};
+
+
+template<typename T>
+void StagePlan::register_entity_type(const std::string &n)
+{
+       get_entity_registry().register_type<T>(n);
+}
+
+
+template<typename T>
+Owned<Entity> StagePlan::PlannedEntity<T>::create(Handle<Entity> p) const
+{
+       return Owned<T>(p, setup, transform);
+}
+
+
+template<typename T>
+void StagePlan::Loader::entity(const std::string &sn)
+{
+       const typename T::Setup &setup = get_collection().get<typename T::Setup>(sn);
+       obj.entities.push_back(make_sub<PlannedEntity<T>>(setup).load());
+}
+
+} // namespace Msp::Game
+
+#endif