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)
{ }
#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;
#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"
{
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>();
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)
{ }
#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;
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#include "damagesource.h"
+
+using namespace Msp;
+
+DamageSource::DamageSource(Game::Handle<Game::Entity> p, const Setup &s):
+ Component(p),
+ setup(s)
+{ }
--- /dev/null
+#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
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" }}
{ }
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(); });
+}
GameController(Msp::Game::Stage &);
void tick(Msp::Time::TimeDelta) override;
+ void deferred_tick() override;
};
#endif
--- /dev/null
+#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);
+}
--- /dev/null
+#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
--- /dev/null
+#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);
+}
--- /dev/null
+#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
.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" }}
{ }
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();
void set_controls(Controls *);
void tick(Msp::Time::TimeDelta) override;
+ void deferred_tick() override;
private:
void fire();
};