From bf23ff877ab2ee95bc9d2e19590d044c64255d9c Mon Sep 17 00:00:00 2001
From: Sara <sara@saragerretsen.nl>
Date: Thu, 11 Jan 2024 23:27:45 +0100
Subject: [PATCH] feat: replaced messagereceiver with mirror

---
 core/src/message_receiver.c |  13 -----
 core/src/message_receiver.h |  36 -------------
 core/src/mirror.c           |  10 ++++
 core/src/mirror.h           | 101 ++++++++++++++++++++++++++++++++++++
 core/src/physics_entity.h   |   9 ++--
 core/src/strutil.c          |  15 ++++++
 core/src/strutil.h          |  10 ++++
 game/src/Damagable.h        |  31 +++++++++++
 game/src/DamageEvent.h      |  11 ----
 game/src/Enemy.c            |  40 +++++++-------
 game/src/Enemy.h            |  16 ++++--
 game/src/Player.c           |  15 ++++--
 game/src/Player.h           |   5 +-
 game/src/PlayerStates.c     |  18 ++++---
 game/src/Prop.c             |  16 +++---
 game/src/Prop.h             |   6 +--
 16 files changed, 236 insertions(+), 116 deletions(-)
 delete mode 100644 core/src/message_receiver.c
 delete mode 100644 core/src/message_receiver.h
 create mode 100644 core/src/mirror.c
 create mode 100644 core/src/mirror.h
 create mode 100644 core/src/strutil.c
 create mode 100644 core/src/strutil.h
 create mode 100644 game/src/Damagable.h
 delete mode 100644 game/src/DamageEvent.h

diff --git a/core/src/message_receiver.c b/core/src/message_receiver.c
deleted file mode 100644
index b0dec28..0000000
--- a/core/src/message_receiver.c
+++ /dev/null
@@ -1,13 +0,0 @@
-#include "message_receiver.h"
-#include "stddef.h"
-
-void* message_receiver_refuse(void* self, MessageID id, uintptr_t data) {
-    return NULL;
-}
-
-MessageReceiver message_receiver_no_implementation(void* data) {
-    static IMessageReceiver const tc = {
-        .handle_message = message_receiver_refuse
-    };
-    return (MessageReceiver){.data = data, .tc = &tc};
-}
diff --git a/core/src/message_receiver.h b/core/src/message_receiver.h
deleted file mode 100644
index f4a3043..0000000
--- a/core/src/message_receiver.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef _fencer_message_receiver_h
-#define _fencer_message_receiver_h
-
-#include "stdint.h"
-#include "typeclass_helpers.h"
-
-typedef uint32_t MessageID;
-
-typedef struct IMessageReceiver {
-    void* (*const handle_message)(void*, MessageID, uintptr_t);
-} IMessageReceiver;
-
-typedef struct MessageReceiver {
-    void* data;
-    IMessageReceiver const* tc;
-} MessageReceiver;
-
-extern void* message_receiver_refuse(void* self, MessageID id, uintptr_t);
-
-extern MessageReceiver message_receiver_no_implementation(void* x);
-
-#define impl_no_MessageReceiver_for(T)\
-static inline MessageReceiver T##_as_MessageReceiver(T* x) {\
-    return message_receiver_no_implementation(x);\
-}
-
-#define impl_MessageReceiver_for(T, handle_message_f)\
-static inline MessageReceiver T##_as_MessageReceiver(T* x) {\
-    TC_FN_TYPECHECK(void*, handle_message_f, T*, MessageID, uintptr_t);\
-    static IMessageReceiver const tc = {\
-        .handle_message = (void*(*const)(void*,MessageID,uintptr_t)) handle_message_f,\
-    };\
-    return (MessageReceiver){.data = x, .tc = &tc};\
-}
-
-#endif // !_fencer_message_receiver_h
diff --git a/core/src/mirror.c b/core/src/mirror.c
new file mode 100644
index 0000000..f8e5f54
--- /dev/null
+++ b/core/src/mirror.c
@@ -0,0 +1,10 @@
+#include "mirror.h"
+
+void* mirror_get_converter(void* self, IMirror const* tc, const char* typeclass) {
+    uintptr_t target_hash = strhash(typeclass);
+    list_foreach(MirroredTypeclass*, class, tc->get_typeclasses(self)) {
+        if(target_hash == class->name_hash && strcmp(typeclass, class->typeclass_name) == 0)
+            return class->wrap;
+    }
+    return NULL;
+}
diff --git a/core/src/mirror.h b/core/src/mirror.h
new file mode 100644
index 0000000..22475e5
--- /dev/null
+++ b/core/src/mirror.h
@@ -0,0 +1,101 @@
+#ifndef _fencer_mirror_h
+#define _fencer_mirror_h
+
+#include "typeclass_helpers.h"
+#include "stdint.h"
+#include "string.h"
+#include "list.h"
+#include "strutil.h"
+
+typedef uintptr_t typeid;
+
+typedef struct {
+    const char* (*const get_typestring)(void* self);
+    typeid (*const get_typeid)(void* self);
+    List* (*const get_typeclasses)(void* self);
+} IMirror;
+
+typedef struct {
+    void* data;
+    IMirror const* tc;
+} Mirror;
+
+typedef struct {
+    const char* typeclass_name;
+    uintptr_t name_hash;
+    void* (*const wrap)(void*);
+} MirroredTypeclass;
+
+static inline int mirror_is_typeid(const Mirror* mirror, typeid id) {
+    return mirror->tc->get_typeid(mirror->data) == id;
+}
+static inline int mirror_is_typestring(const Mirror* mirror, const char* id) {
+    return strcmp(id, mirror->tc->get_typestring(mirror->data)) == 0;
+}
+static inline int mirror_eq(const Mirror* lhs, const Mirror* rhs) {
+    return lhs->tc->get_typeid(lhs->data) == rhs->tc->get_typeid(rhs->data);
+}
+
+extern void* mirror_get_converter(void* data, IMirror const* tc, const char* typeclass);
+
+#define MIRROR_TRY_WRAP(Into_, Mirror_, Typeclass_){\
+    MirroredTypeclassWrapFunc fn_ = mirror_get_typeclass(Mirror_, #Typeclass_);\
+    if(fn_ != NULL) {\
+        Into_ = (TypeClass_)fn(Mirror_->data);\
+    }\
+}
+
+#define impl_Mirror_for(T, get_typestring_f, get_typeid_f, get_typeclasses_f)\
+static inline Mirror T##_as_Mirror(T* x) {\
+    TC_FN_TYPECHECK(const char*, get_typestring_f, T*);\
+    TC_FN_TYPECHECK(typeid, get_typeid_f, T*);\
+    TC_FN_TYPECHECK(List*, get_typeclasses_f, T*);\
+    static IMirror const tc = {\
+        .get_typestring = (const char*(*const)(void*)) get_typestring_f,\
+        .get_typeid = (typeid (*const)(void*)) get_typeid_f,\
+        .get_typeclasses = (List* (*const)(void*)) get_typeclasses_f,\
+    };\
+    return (Mirror){.tc = &tc, .data = x};\
+}
+
+#define DECL_REFLECT(T)\
+extern const char* T##_get_typestring(T* self);\
+extern typeid T##_get_typeid(T* self);\
+extern List* T##_get_typeclasses(T* self);\
+impl_Mirror_for(T, T##_get_typestring, T##_get_typeid, T##_get_typeclasses)
+
+
+#define START_REFLECT(T)\
+const char* T##_get_typestring(T* self) {\
+    static const char* const typestring = #T;\
+    return typestring;\
+}\
+typeid T##_get_typeid(T* self) {\
+    static char init_flag = 0;\
+    static typeid id = 0;\
+    if(!init_flag) {\
+        init_flag = 1;\
+        id = strhash(#T);\
+    }\
+    return id;\
+}\
+List* T##_get_typeclasses(T* self) {\
+    static char init_flag = 0;\
+    static List typeclasses;\
+    if(!init_flag) {\
+        init_flag = 1,\
+        typeclasses = list_init(sizeof(MirroredTypeclass));\
+
+#define REFLECT_TYPECLASS(T, TypeClass_)\
+        list_add(&typeclasses, &(MirroredTypeclass){\
+            .name_hash = strhash(#TypeClass_),\
+            .typeclass_name = #TypeClass_,\
+            .wrap = (void*(*const)(void*)) T##_as_##TypeClass_\
+        });
+
+#define END_REFLECT(T)\
+    }\
+    return &typeclasses;\
+}\
+
+#endif // !_fencer_mirror_h
diff --git a/core/src/physics_entity.h b/core/src/physics_entity.h
index 212a606..acfef5d 100644
--- a/core/src/physics_entity.h
+++ b/core/src/physics_entity.h
@@ -4,12 +4,11 @@
 #include "typeclass_helpers.h"
 #include "list.h"
 #include "transformable.h"
-#include "message_receiver.h"
+#include "mirror.h"
 
 typedef struct Collider Collider;
 typedef struct Collision Collision;
 typedef struct RigidBody RigidBody;
-
 typedef struct PhysicsEntity PhysicsEntity;
 
 typedef struct IPhysicsEntity {
@@ -22,7 +21,7 @@ typedef struct PhysicsEntity {
     void* data;
     IPhysicsEntity const* tc;
     ITransformable const* transformable;
-    IMessageReceiver const* message_receiver;
+    IMirror const* mirror;
 } PhysicsEntity;
 
 extern void physics_entity_debug_draw(PhysicsEntity self);
@@ -40,8 +39,8 @@ static inline PhysicsEntity T##_as_PhysicsEntity(T* x) {\
         .on_overlap =               (void(*const)(void*,Collider*))     on_overlap_f,\
     };\
     Transformable transformable = T##_as_Transformable(x);\
-    MessageReceiver receiver = T##_as_MessageReceiver(x);\
-    return (PhysicsEntity){.data = x, .tc = &tc, .transformable = transformable.tc, .message_receiver = receiver.tc};\
+    Mirror mirror = T##_as_Mirror(x);\
+    return (PhysicsEntity){.data = x, .tc = &tc, .transformable = transformable.tc, .mirror = mirror.tc};\
 }
 
 #endif // !_fencer_collidable_h
diff --git a/core/src/strutil.c b/core/src/strutil.c
new file mode 100644
index 0000000..d133af3
--- /dev/null
+++ b/core/src/strutil.c
@@ -0,0 +1,15 @@
+#include "strutil.h"
+#include "string.h"
+
+uintptr_t strnhash(const char* s, size_t n) {
+    static const size_t shift = sizeof(uintptr_t) * 8 - 4;
+    uintptr_t hash = 0;
+    for(size_t i = 0; i < n; ++i) {
+        hash = ((hash << 4) | s[i]) ^ (((uintptr_t)0xF << shift) & hash);
+    }
+    return hash;
+}
+
+uintptr_t strhash(const char* s) {
+    return strnhash(s, strlen(s));
+}
diff --git a/core/src/strutil.h b/core/src/strutil.h
new file mode 100644
index 0000000..55e36af
--- /dev/null
+++ b/core/src/strutil.h
@@ -0,0 +1,10 @@
+#ifndef _fencer_strutil_h
+#define _fencer_strutil_h
+
+#include "stdlib.h"
+#include "stdint.h"
+
+extern uintptr_t strnhash(const char* s, size_t n);
+extern uintptr_t strhash(const char* s);
+
+#endif // !_fencer_strutil_h
diff --git a/game/src/Damagable.h b/game/src/Damagable.h
new file mode 100644
index 0000000..453f938
--- /dev/null
+++ b/game/src/Damagable.h
@@ -0,0 +1,31 @@
+#ifndef FIGHT_DAMAGABLE_H
+#define FIGHT_DAMAGABLE_H
+
+#include "typeclass_helpers.h"
+
+#include "vmath.h"
+
+typedef struct DamageEventData {
+    int damageAmount;
+    Vector origin;
+} DamageEventData;
+
+typedef struct {
+    int (*const damage)(void*, DamageEventData*);
+} IDamagable;
+
+typedef struct {
+    void* data;
+    IDamagable const* tc;
+} Damagable;
+
+#define impl_Damagable_for(T, damage_f)\
+static inline Damagable T##_as_Damagable(T* x) {\
+    TC_FN_TYPECHECK(int, damage_f, T*, DamageEventData*);\
+    static const IDamagable tc = {\
+        .damage = (int(*const)(void*, DamageEventData*)) damage_f,\
+    };\
+    return (Damagable){.data = x, .tc = &tc};\
+}
+
+#endif // !FIGHT_DAMAGABLE_H
diff --git a/game/src/DamageEvent.h b/game/src/DamageEvent.h
deleted file mode 100644
index 081e9c0..0000000
--- a/game/src/DamageEvent.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef FIGHT_DAMAGE_EVENT_H
-#define FIGHT_DAMAGE_EVENT_H
-
-#include "vmath.h"
-
-typedef struct DamageEventData {
-    int damageAmount;
-    Vector origin;
-} DamageEventData;
-
-#endif // !FIGHT_DAMAGE_EVENT_H
diff --git a/game/src/Enemy.c b/game/src/Enemy.c
index b58ed91..4889107 100644
--- a/game/src/Enemy.c
+++ b/game/src/Enemy.c
@@ -1,12 +1,18 @@
 #include "Enemy.h"
 #include "debug.h"
-#include "Messages.h"
-#include "DamageEvent.h"
 #include "game_world.h"
 #include "physics.h"
 #include "physics_world.h"
 #include "sprite.h"
 
+START_REFLECT(Enemy)
+    REFLECT_TYPECLASS(Enemy, Transformable)
+    REFLECT_TYPECLASS(Enemy, Drop)
+    REFLECT_TYPECLASS(Enemy, PhysicsEntity)
+    REFLECT_TYPECLASS(Enemy, BehaviourEntity)
+    REFLECT_TYPECLASS(Enemy, Damagable)
+END_REFLECT(Enemy)
+
 Enemy* MakeEnemy() {
     Enemy* self = malloc(sizeof(Enemy));
     ASSERT_RETURN(self != NULL, NULL, "Failed to allocate Enemy");
@@ -70,23 +76,6 @@ void EnemyDestroy(Enemy* self) {
 void EnemyOnCollision(Enemy* self, Collision collision) {}
 void EnemyOnOverlap(Enemy* self, Collider* other) {}
 
-void* EnemyHandleMessage(Enemy* self, MessageID id, uintptr_t data) {
-    DamageEventData* damage = (DamageEventData*)data;
-    switch(id) {
-    case MESSAGE_DEAL_DAMAGE:
-        self->health -= damage->damageAmount;
-        self->hurt = 1;
-        self->facing = ((damage->origin.x - self->transform.position.x) > 0) * 2 - 1;
-        if(self->health <= 0)
-            game_world_destroy_entity(Enemy_as_BehaviourEntity(self));
-        break;
-    default:
-        break;
-    }
-
-    return NULL;
-}
-
 Transform* EnemyGetTransform(Enemy* self) {
     return &self->transform;
 }
@@ -94,3 +83,16 @@ Transform* EnemyGetTransform(Enemy* self) {
 RigidBody* EnemyGetRigidBody(Enemy* self) {
     return self->rigidbody;
 }
+
+int EnemyDamage(Enemy* self, DamageEventData* data) {
+    self->health -= data->damageAmount;
+    LOG_INFO("Damage received");
+    if(self->health <= 0) {
+        game_world_destroy_entity(Enemy_as_BehaviourEntity(self));
+        return 1;
+    } else {
+        self->hurt = 1;
+        self->facing = ((data->origin.x - self->transform.position.x) > 0) * 2 - 1;
+        return 0;
+    }
+}
diff --git a/game/src/Enemy.h b/game/src/Enemy.h
index 98adbd8..1e54323 100644
--- a/game/src/Enemy.h
+++ b/game/src/Enemy.h
@@ -1,6 +1,7 @@
 #ifndef FIGHT_ENEMY_H
 #define FIGHT_ENEMY_H
 
+#include "mirror.h"
 #include "transform.h"
 #include "state_machine.h"
 #include "rigidbody.h"
@@ -9,6 +10,8 @@
 #include "collider.h"
 #include "sprite.h"
 #include "animation_sprite.h"
+
+#include "Damagable.h"
 #include "EnemyStates.h"
 
 typedef struct Enemy {
@@ -44,20 +47,19 @@ extern void EnemyDraw(Enemy* self);
 
 extern void EnemyOnCollision(Enemy* self, Collision collision);
 extern void EnemyOnOverlap(Enemy* self, Collider* other);
-extern void* EnemyHandleMessage(Enemy* self, MessageID id, uintptr_t data);
 
 extern Transform* EnemyGetTransform(Enemy* self);
 extern RigidBody* EnemyGetRigidBody(Enemy* self);
 static long EnemyGetDepth(Enemy* self) { return (long)(-self->transform.position.y * 1000); }
 
+extern int EnemyDamage(Enemy* self, DamageEventData* data);
+
+DECL_REFLECT(Enemy)
+
 impl_Transformable_for(Enemy,
     EnemyGetTransform
 )
 
-impl_MessageReceiver_for(Enemy,
-    EnemyHandleMessage
-)
-
 impl_Drop_for(Enemy,
     EnemyDestroy
 )
@@ -75,4 +77,8 @@ impl_PhysicsEntity_for(Enemy,
     EnemyOnOverlap
 )
 
+impl_Damagable_for(Enemy,
+    EnemyDamage
+)
+
 #endif // !FIGHT_ENEMY_H
diff --git a/game/src/Player.c b/game/src/Player.c
index 64eb9e4..3976e6d 100644
--- a/game/src/Player.c
+++ b/game/src/Player.c
@@ -1,24 +1,31 @@
 #include "Player.h"
-#include "PlayerStates.h"
-#include "Layers.h"
 #include "debug.h"
 #include "game_world.h"
 #include "input_axis.h"
 #include "physics_world.h"
 
+#include "PlayerStates.h"
+#include "Layers.h"
+
 const Vector PLAYER_SPEED = { 1.0f, 0.70f };
 
+START_REFLECT(Player)
+    REFLECT_TYPECLASS(Player, Drop)
+    REFLECT_TYPECLASS(Player, PhysicsEntity)
+    REFLECT_TYPECLASS(Player, BehaviourEntity)
+END_REFLECT(Player)
+
 static inline
 void Internal_PlayerInitInput(Player* self) {
     playerinput_add(self->playerInput, CompositeAxis1D_as_InputAxis(compositeaxis1d_new(
         KeyBind_as_InputAxis(keybind_new(SDL_SCANCODE_A)),
         KeyBind_as_InputAxis(keybind_new(SDL_SCANCODE_D)),
-        InputEvent_Float)), (InputDelegateFn)PlayerHorizontalInput);
+    InputEvent_Float)), (InputDelegateFn)PlayerHorizontalInput);
 
     playerinput_add(self->playerInput, CompositeAxis1D_as_InputAxis(compositeaxis1d_new(
         KeyBind_as_InputAxis(keybind_new(SDL_SCANCODE_S)),
         KeyBind_as_InputAxis(keybind_new(SDL_SCANCODE_W)),
-        InputEvent_Float)), (InputDelegateFn)PlayerVerticalInput);
+    InputEvent_Float)), (InputDelegateFn)PlayerVerticalInput);
 
     playerinput_add(self->playerInput, KeyBind_as_InputAxis(keybind_new(SDL_SCANCODE_J)), (InputDelegateFn)PlayerAttackInput);
 }
diff --git a/game/src/Player.h b/game/src/Player.h
index 4b2cfad..146a6d7 100644
--- a/game/src/Player.h
+++ b/game/src/Player.h
@@ -2,10 +2,9 @@
 #define FIGHT_PLAYER_H
 
 #include "state_machine.h"
-#include "state.h"
+#include "mirror.h"
 #include "behaviour_entity.h"
 #include "animation_sprite.h"
-#include "input.h"
 #include "vmath.h"
 #include "transform.h"
 #include "player_input.h"
@@ -60,7 +59,7 @@ void PlayerOnOverlap(Player* self, Collider* other);
 
 static long PlayerGetDepth(Player* self) { return (int)(-10-self->transform.position.y * 1000); }
 
-impl_no_MessageReceiver_for(Player)
+DECL_REFLECT(Player);
 
 impl_Drop_for(Player,
     DestroyPlayer
diff --git a/game/src/PlayerStates.c b/game/src/PlayerStates.c
index 99fd083..ccd226d 100644
--- a/game/src/PlayerStates.c
+++ b/game/src/PlayerStates.c
@@ -1,8 +1,10 @@
 #include "PlayerStates.h"
+#include "physics_world.h"
+
 #include "DamageEvent.h"
+#include "Damagable.h"
 #include "Player.h"
 #include "Layers.h"
-#include "physics_world.h"
 
 static inline
 void InternalSpriteFlipWithMovement(Player* self) {
@@ -58,11 +60,15 @@ void PlayerAttackTrigger(Player* self) {
         MakeVector(0.1f, 0.06f), PHYSICS_LAYER_COMBAT, self->rigidbody);
     if(found != NULL) {
         PhysicsEntity entity = collider_get_owner(found);
-        DamageEventData data = {
-            .damageAmount = 1,
-            .origin = self->transform.position
-        };
-        entity.message_receiver->handle_message(entity.data, 1, (uintptr_t)&data);
+        Damagable(*const as_damagable)(void*) = mirror_get_converter(entity.data, entity.mirror, "Damagable");
+        if(as_damagable) {
+            Damagable damagable = as_damagable(entity.data);
+            DamageEventData data = {
+                .damageAmount = 1,
+                .origin = self->transform.position
+            };
+            damagable.tc->damage(entity.data, &data);
+        }
     }
     ++self->animationTriggers;
 }
diff --git a/game/src/Prop.c b/game/src/Prop.c
index ea5f7ab..caa0b53 100644
--- a/game/src/Prop.c
+++ b/game/src/Prop.c
@@ -1,10 +1,16 @@
 #include "Prop.h"
 #include "DamageEvent.h"
-#include "Messages.h"
 #include "debug.h"
 #include "game_world.h"
+#include "mirror.h"
 #include "physics_world.h"
 
+START_REFLECT(Prop)
+    REFLECT_TYPECLASS(Prop, Transformable)
+    REFLECT_TYPECLASS(Prop, Drop)
+    REFLECT_TYPECLASS(Prop, BehaviourEntity)
+END_REFLECT(Prop)
+
 Prop* MakeProp(Sprite* sprite, Shape* shape) {
     Prop* self = malloc(sizeof(Prop));
     ASSERT_RETURN(self != NULL, NULL, "Failed to allocate space for Prop instance");
@@ -61,11 +67,3 @@ Transform* PropGetTransform(Prop* self) {
 RigidBody* PropGetRigidBody(Prop* self) {
     return self->rigidbody;
 }
-
-void* PropReceiveMessage(Prop* self, MessageID message, uintptr_t data) {
-    DamageEventData* damage = (DamageEventData*)data;
-    if(message == MESSAGE_DEAL_DAMAGE) {
-        LOG_INFO("Punching bag took %d damage", damage->damageAmount);
-    }
-    return 0;
-}
diff --git a/game/src/Prop.h b/game/src/Prop.h
index 73bcddc..5fe516f 100644
--- a/game/src/Prop.h
+++ b/game/src/Prop.h
@@ -31,13 +31,9 @@ void PropOnOverlap(Prop* self, Collider* other);
 Transform* PropGetTransform(Prop* self);
 RigidBody* PropGetRigidBody(Prop* self);
 
-void* PropReceiveMessage(Prop* self, MessageID message, uintptr_t data);
-
 static long PropGetDepth(Prop* self) { return -(int)(self->transform.position.y * 1000); }
 
-impl_MessageReceiver_for(Prop,
-    PropReceiveMessage
-)
+DECL_REFLECT(Prop)
 
 impl_Transformable_for(Prop,
     PropGetTransform