]> git.tdb.fi Git - libs/game.git/commitdiff
Make asteroids take damage when shot and eventually be destroyed
authorMikko Rasa <tdb@tdb.fi>
Sun, 27 Nov 2022 21:49:23 +0000 (23:49 +0200)
committerMikko Rasa <tdb@tdb.fi>
Sun, 27 Nov 2022 21:49:23 +0000 (23:49 +0200)
17 files changed:
examples/bassteroids/source/asteroid.cpp
examples/bassteroids/source/asteroid.h
examples/bassteroids/source/bassteroids.cpp
examples/bassteroids/source/bullet.cpp
examples/bassteroids/source/bullet.h
examples/bassteroids/source/damagedealer.cpp [new file with mode: 0644]
examples/bassteroids/source/damagedealer.h [new file with mode: 0644]
examples/bassteroids/source/damagesource.cpp [new file with mode: 0644]
examples/bassteroids/source/damagesource.h [new file with mode: 0644]
examples/bassteroids/source/gamecontroller.cpp
examples/bassteroids/source/gamecontroller.h
examples/bassteroids/source/hitpoints.cpp [new file with mode: 0644]
examples/bassteroids/source/hitpoints.h [new file with mode: 0644]
examples/bassteroids/source/hittable.cpp [new file with mode: 0644]
examples/bassteroids/source/hittable.h [new file with mode: 0644]
examples/bassteroids/source/playercontroller.cpp
examples/bassteroids/source/playercontroller.h

index 7e5bcd5aee5df35377a865f5add3c908d69628a2..cb7f95490df0addd479d2ae2fa38fb3ecd0e87b2 100644 (file)
@@ -3,7 +3,7 @@
 using namespace Msp;
 
 Asteroid::Asteroid(Game::Handle<Entity> p, const AsteroidSetup &s):
-       PhysicalEntity(p, s.physical),
+       Hittable(p, s.hittable, s.physical),
        setup(s),
        mesh(*this, setup.mesh)
 { }
index dcb64c37aa6d18d104b85f5a5d69082f8121888e..b80cc65a3437dbe2d532417ef6b432f0902f86b0 100644 (file)
@@ -3,15 +3,16 @@
 
 #include <msp/game/entity.h>
 #include <msp/game/meshsource.h>
-#include "physicalentity.h"
+#include "hittable.h"
 
 struct AsteroidSetup
 {
        PhysicalSetup physical;
+       HittableSetup hittable;
        Msp::Game::MeshSourceSetup mesh;
 };
 
-class Asteroid: public PhysicalEntity
+class Asteroid: public Hittable
 {
 public:
        using Setup = AsteroidSetup;
index 317b6944f4ce7600b75684008862c1d85d4cb696..d6f255609c9f06e188886a4bba6ac2b03aaf3a03 100644 (file)
@@ -4,6 +4,7 @@
 #include <msp/game/transform.h>
 #include <msp/game/transformpropagator.h>
 #include "controls.h"
+#include "damagedealer.h"
 #include "gamecontroller.h"
 #include "physics.h"
 #include "playercontroller.h"
@@ -21,6 +22,7 @@ Bassteroids::Bassteroids(int, char **):
 {
        game_stage.add_system<GameController>();
        game_stage.add_system<Physics>();
+       game_stage.add_system<DamageDealer>();
        game_stage.add_system<Game::TransformPropagator>();
        player_controller = &game_stage.add_system<PlayerController>();
 
index 957a0394757c1b25bdda9c588d5d844429f370f3..e26b4089b05763443a92b694e1a74eb5cb48418a 100644 (file)
@@ -3,6 +3,6 @@
 using namespace Msp;
 
 Bullet::Bullet(Game::Handle<Game::Entity> p, const Setup &s, const Game::TransformValues &tv):
-       PhysicalEntity(p, s.physical, tv),
+       Hittable(p, s.hittable, s.physical, tv),
        mesh(*this, s.mesh)
 { }
index 44c9d8053e366931a3536a1803d14dc716976555..de0804a83974826a8681f511c29d44fe48c96bff 100644 (file)
@@ -2,15 +2,16 @@
 #define BULLET_H_
 
 #include <msp/game/meshsource.h>
-#include "physicalentity.h"
+#include "hittable.h"
 
 struct BulletSetup
 {
        PhysicalSetup physical;
+       HittableSetup hittable;
        Msp::Game::MeshSourceSetup mesh;
 };
 
-class Bullet: public PhysicalEntity
+class Bullet: public Hittable
 {
 private:
        Msp::Game::Owned<Msp::Game::MeshSource> mesh;
diff --git a/examples/bassteroids/source/damagedealer.cpp b/examples/bassteroids/source/damagedealer.cpp
new file mode 100644 (file)
index 0000000..37be786
--- /dev/null
@@ -0,0 +1,37 @@
+#include "damagedealer.h"
+#include <msp/game/stage.h>
+#include "collider.h"
+#include "hittable.h"
+
+using namespace Msp;
+
+DamageDealer::DamageDealer(Game::Stage &s):
+       System(s),
+       observer(stage.get_event_bus())
+{
+       observer.observe<Events::Collision>([this](auto &e){ collision(e); });
+}
+
+void DamageDealer::tick(Time::TimeDelta)
+{
+       for(DamagingCollision &c: collisions)
+       {
+               Game::Handle<HitPoints> hits1 = c.entity1->get_hitpoints();
+               Game::Handle<DamageSource> damage1 = c.entity1->get_damage();
+               Game::Handle<HitPoints> hits2 = c.entity2->get_hitpoints();
+               Game::Handle<DamageSource> damage2 = c.entity2->get_damage();
+               if(damage1 && hits2)
+                       hits2->take_damage(damage1->get_amount(), damage1->get_type());
+               if(damage2 && hits1)
+                       hits1->take_damage(damage2->get_amount(), damage2->get_type());
+       }
+       collisions.clear();
+}
+
+void DamageDealer::collision(const Events::Collision &e)
+{
+       Game::Handle<Hittable> hittable1 = dynamic_handle_cast<Hittable>(e.collider1->get_entity());
+       Game::Handle<Hittable> hittable2 = dynamic_handle_cast<Hittable>(e.collider2->get_entity());
+       if(hittable1 && hittable2)
+               collisions.emplace_back(hittable1, hittable2);
+}
diff --git a/examples/bassteroids/source/damagedealer.h b/examples/bassteroids/source/damagedealer.h
new file mode 100644 (file)
index 0000000..fe171de
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef DAMAGEDEALER_H_
+#define DAMAGEDEALER_H_
+
+#include <msp/game/eventobserver.h>
+#include <msp/game/handle.h>
+#include <msp/game/system.h>
+#include "events.h"
+
+class Collider;
+class Hittable;
+
+class DamageDealer: public Msp::Game::System
+{
+private:
+       struct DamagingCollision
+       {
+               Msp::Game::Handle<Hittable> entity1;
+               Msp::Game::Handle<Hittable> entity2;
+       };
+
+       Msp::Game::EventObserver observer;
+       std::vector<DamagingCollision> collisions;
+
+public:
+       DamageDealer(Msp::Game::Stage &);
+
+       void tick(Msp::Time::TimeDelta) override;
+
+private:
+       void collision(const Events::Collision &);
+};
+
+#endif
diff --git a/examples/bassteroids/source/damagesource.cpp b/examples/bassteroids/source/damagesource.cpp
new file mode 100644 (file)
index 0000000..a513879
--- /dev/null
@@ -0,0 +1,8 @@
+#include "damagesource.h"
+
+using namespace Msp;
+
+DamageSource::DamageSource(Game::Handle<Game::Entity> p, const Setup &s):
+       Component(p),
+       setup(s)
+{ }
diff --git a/examples/bassteroids/source/damagesource.h b/examples/bassteroids/source/damagesource.h
new file mode 100644 (file)
index 0000000..41a2755
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef DAMAGESOURCE_H_
+#define DAMAGESOURCE_H_
+
+#include <msp/game/component.h>
+
+struct DamageSourceSetup
+{
+       unsigned amount = 1;
+       unsigned type = 0;
+};
+
+class DamageSource: public Msp::Game::Component
+{
+public:
+       using Setup = DamageSourceSetup;
+
+private:
+       const Setup &setup;
+
+public:
+       DamageSource(Msp::Game::Handle<Msp::Game::Entity>, const Setup &);
+
+       unsigned get_amount() const { return setup.amount; }
+       unsigned get_type() const { return setup.type; }
+};
+
+#endif
index b5640330310ad111f01f70d4b7aafba260a305fe..97701e379fa603d9bc6ad265351bc0ffa48f845f 100644 (file)
@@ -9,6 +9,7 @@ using namespace Msp;
 GameController::GameController(Game::Stage &s):
        System(s),
        asteroid_setup{ .physical={ .body={ .mass=200, .moment_of_inertia=2160 }, .collider={ .type=ColliderType::CIRCLE, .radius=3.0f } },
+               .hittable={ .damaging=true, .hits={ .max_hits=3, .vulnerable_to=1 }, .damage={ .amount=10, .type=1 }},
                .mesh={ .object_name="Asteroid 1.object" }}
 { }
 
@@ -37,3 +38,9 @@ void GameController::tick(Time::TimeDelta)
                throw logic_error("Unimplemented state");
        }
 }
+
+void GameController::deferred_tick()
+{
+       System::deferred_tick();
+       erase_if(asteroids, [](Game::Handle<Asteroid> a){ return !a->get_hitpoints()->is_alive(); });
+}
index 1f0d3cfdea9ec607a84aea3b8979f59f48bf6ae8..121544429b08114115aea9c91418e8ec2914ae1a 100644 (file)
@@ -24,6 +24,7 @@ public:
        GameController(Msp::Game::Stage &);
 
        void tick(Msp::Time::TimeDelta) override;
+       void deferred_tick() override;
 };
 
 #endif
diff --git a/examples/bassteroids/source/hitpoints.cpp b/examples/bassteroids/source/hitpoints.cpp
new file mode 100644 (file)
index 0000000..ccd0441
--- /dev/null
@@ -0,0 +1,17 @@
+#include "hitpoints.h"
+
+using namespace Msp;
+
+HitPoints::HitPoints(Game::Handle<Game::Entity> e, const Setup &s):
+       Component(e),
+       setup(s),
+       remaining_hits(setup.max_hits)
+{ }
+
+void HitPoints::take_damage(unsigned amount, unsigned type)
+{
+       if(!(setup.vulnerable_to&(1<<type)))
+               return;
+
+       remaining_hits = (amount<remaining_hits ? remaining_hits-amount : 0);
+}
diff --git a/examples/bassteroids/source/hitpoints.h b/examples/bassteroids/source/hitpoints.h
new file mode 100644 (file)
index 0000000..a11d961
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef HITPOINTS_H_
+#define HITPOINTS_H_
+
+#include <msp/game/component.h>
+
+struct HitPointsSetup
+{
+       /*class Loader: public Msp::DataFile::ObjectLoader<HitPointsSetup>
+       {
+       };*/
+
+       unsigned max_hits = 1;
+       unsigned vulnerable_to = ~0U;
+};
+
+class HitPoints: public Msp::Game::Component
+{
+public:
+       using Setup = HitPointsSetup;
+
+private:
+       const Setup &setup;
+       unsigned remaining_hits;
+
+public:
+       HitPoints(Msp::Game::Handle<Msp::Game::Entity>, const Setup &);
+
+       void take_damage(unsigned, unsigned);
+
+       bool is_alive() const { return remaining_hits; }
+};
+
+#endif
diff --git a/examples/bassteroids/source/hittable.cpp b/examples/bassteroids/source/hittable.cpp
new file mode 100644 (file)
index 0000000..08355f1
--- /dev/null
@@ -0,0 +1,12 @@
+#include "hittable.h"
+
+using namespace Msp;
+
+Hittable::Hittable(Game::Handle<Game::Entity> p, const Setup &setup, const PhysicalSetup &ps, const Game::TransformValues &tv):
+       PhysicalEntity(p, ps, tv)
+{
+       if(!setup.immortal)
+               hits = Game::Owned<HitPoints>(*this, setup.hits);
+       if(setup.damaging)
+               damage = Game::Owned<DamageSource>(*this, setup.damage);
+}
diff --git a/examples/bassteroids/source/hittable.h b/examples/bassteroids/source/hittable.h
new file mode 100644 (file)
index 0000000..b548a38
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef HITTABLE_H_
+#define HITTABLE_H_
+
+#include <msp/game/entity.h>
+#include "damagesource.h"
+#include "hitpoints.h"
+#include "physicalentity.h"
+
+struct HittableSetup
+{
+       bool immortal = false;
+       bool damaging = false;
+       HitPointsSetup hits;
+       DamageSourceSetup damage;
+};
+
+class Hittable: public PhysicalEntity
+{
+public:
+       using Setup = HittableSetup;
+
+private:
+       Msp::Game::Owned<HitPoints> hits;
+       Msp::Game::Owned<DamageSource> damage;
+
+public:
+       Hittable(Msp::Game::Handle<Msp::Game::Entity>, const Setup &, const PhysicalSetup &,
+               const Msp::Game::TransformValues & = Msp::Game::TransformValues());
+
+       Msp::Game::Handle<HitPoints> get_hitpoints() { return hits; }
+       Msp::Game::Handle<DamageSource> get_damage() { return damage; }
+};
+
+#endif
index 066b5f24d002b01f5e68824d620753651ba7bf6a..46f9c1f85e30dfe710c96411a041c73455b22029 100644 (file)
@@ -12,6 +12,7 @@ PlayerController::PlayerController(Game::Stage &s):
                .mesh={ .object_name="Bass guitar.object" },
                .speed=12.0f, .turn_rate=4.71f },
        bullet_setup{ .physical={ .body={ .mass=0.05f, .moment_of_inertia=0.04f }, .collider={ .type=ColliderType::CIRCLE, .radius=0.2f }},
+               .hittable={ .damaging=true, .hits={ .max_hits=1 }, .damage={ .amount=1, .type=0 }},
                .mesh={ .object_name="Quaver.object" }}
 { }
 
@@ -55,6 +56,12 @@ void PlayerController::tick(Time::TimeDelta dt)
        controls->reset_edges();
 }
 
+void PlayerController::deferred_tick()
+{
+       System::deferred_tick();
+       erase_if(bullets, [](Game::Handle<Bullet> b){ return !b->get_hitpoints()->is_alive(); });
+}
+
 void PlayerController::fire()
 {
        Game::Handle<Game::Transform> player_tf = player_ship->get_transform();
index d29b2d6218a5fbc9f0a924f3fd9ca06e6167d39c..a4e35d4cc4adbe6a4c7dc384f6da61bb9c0aeba9 100644 (file)
@@ -24,6 +24,7 @@ public:
        void set_controls(Controls *);
 
        void tick(Msp::Time::TimeDelta) override;
+       void deferred_tick() override;
 private:
        void fire();
 };