testing what it feels like use the entity system
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 29 Jan 2022 19:48:03 +0000 (13:48 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 29 Jan 2022 19:48:03 +0000 (13:48 -0600)
.gitignore [new file with mode: 0644]
src/entity/manager.onyx
src/entity/player.onyx
src/main.onyx

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..1bb4614
--- /dev/null
@@ -0,0 +1 @@
+*.code-workspace
\ No newline at end of file
index 8d43f9c3f8c299b67ccee572aad2552071249c9e..29da0c066c6296f7a10b7b60a329663ac068e221 100644 (file)
@@ -2,8 +2,11 @@
 use package core
 use package glfw3
 
+Entity_ID :: u32
+Entity_Nothing :: cast(Entity_ID) 0
+
 Entity :: struct {
-    id: u32;
+    id: Entity_ID;
     type: type_expr;
     flags: Entity_Flags;
 
@@ -13,6 +16,7 @@ Entity :: struct {
 Entity_Flags :: enum #flags {
     Interactable;
     Solid;
+    Carryable;
 }
 
 IsEntity :: interface (e: $E) {
@@ -30,16 +34,21 @@ Entity_Handles :: struct {
 
 Entity_Manager :: struct {
     entity_allocator: Allocator;
+
+    // Hmmm... Do they need to be stored in both ways?
     entities: [..] ^Entity;
+    entity_map: Map(Entity_ID, ^Entity);
+
     entity_types: Map(type_expr, Entity_Handles);
 
-    next_entity_id: u32;
+    next_entity_id: Entity_ID;
 
     register       :: entity_manager_register;
     add            :: entity_manager_add;
     make           :: entity_manager_make;
     update         :: entity_manager_update;
     draw           :: entity_manager_draw;
+    get            :: entity_manager_get;
     query          :: entity_manager_query;
     query_by_type  :: entity_manager_query_by_type;
     query_by_flags :: entity_manager_query_by_flags;
@@ -82,6 +91,7 @@ entity_manager_add :: (use this: ^Entity_Manager, entity: ^$T) -> u32 where IsEn
     next_entity_id += 1;
 
     entities << entity;
+    entity_map[entity.id] = entity;
     return entity.id;
 }
 
@@ -111,6 +121,8 @@ entity_manager_draw :: (use this: ^Entity_Manager) {
     }
 }
 
+entity_manager_get :: (use this: ^Entity_Manager, id: Entity_ID) => entity_map[id];
+
 entity_manager_query :: (use this: ^Entity_Manager, area: Rect) -> [] ^Entity {
     ents: [..] ^Entity;
     for entities {
@@ -154,7 +166,7 @@ entity_manager_query_by_flags :: (use this: ^Entity_Manager, area: Rect, flags:
     expected_flags := cast(u32) flags;
 
     for entities {
-        if (~~ it.flags & expected_flags) != expected_flags do continue;
+        if ~~ it.flags & expected_flags != expected_flags do continue;
 
         get_rect := entity_types[it.type].get_rect;
         if get_rect == null_proc do continue;
@@ -167,9 +179,20 @@ entity_manager_query_by_flags :: (use this: ^Entity_Manager, area: Rect, flags:
     return ents;
 }
 
-entity_is :: macro (entity: ^Entity, type: type_expr, body: Code) {
+if_entity_is :: macro (entity: ^Entity, type: type_expr, body: Code) {
     if entity.type == type {
         it := cast(^type) entity;
         #insert body;
     }
 }
+
+switch_entity :: macro (entity: ^Entity, body: Code) {
+    switch entity.type do #insert body;
+}
+
+entity_case :: macro (type: type_expr, body: Code) {
+    case type {
+        object := cast(^type) it;
+        #insert body;
+    }
+}
index e3c119a0b1edecbdeb042d20175c9f4783c40839..b42f0c59845d07a34819d35827325534560d986f 100644 (file)
@@ -2,19 +2,34 @@
 use package core
 use package glfw3
 
+Player_Controls :: struct {
+    up       := GLFW_KEY_W;
+    down     := GLFW_KEY_S;
+    left     := GLFW_KEY_A;
+    right    := GLFW_KEY_D;
+    interact := GLFW_KEY_F;
+    pick_up  := GLFW_KEY_G;
+}
+
 Player :: struct {
     use entity: Entity;
 
-    health := 69.0f;
+    holding: Entity_ID;
+
+    controls: Player_Controls;
 
     Size :: 16.0f
 
     init_data :: struct {
         pos := Vector2.{400, 200};
+        controls := Player_Controls.{};
     }
 
     init :: (use this: ^Player, data: init_data) {
         this.pos = data.pos;
+        this.holding = Entity_Nothing;
+
+        this.controls = data.controls;
     }
 
     get_rect :: (use this: ^Player) => Rect.{ pos.x - Size, pos.y - Size, Size * 2, Size * 2 };
@@ -23,10 +38,10 @@ Player :: struct {
         speed :: Size * 8;
 
         delta: Vector2;
-        if is_key_down(GLFW_KEY_W) do delta.y -= speed * dt; 
-        if is_key_down(GLFW_KEY_S) do delta.y += speed * dt; 
-        if is_key_down(GLFW_KEY_A) do delta.x -= speed * dt; 
-        if is_key_down(GLFW_KEY_D) do delta.x += speed * dt; 
+        if is_key_down(controls.up)    do delta.y -= speed * dt; 
+        if is_key_down(controls.down)  do delta.y += speed * dt; 
+        if is_key_down(controls.left)  do delta.x -= speed * dt; 
+        if is_key_down(controls.right) do delta.x += speed * dt; 
 
         walls := entity_manager->query_by_flags(.{pos.x - 100, pos.y - 100, 200, 200}, .Solid);
         defer memory.free_slice(^walls);
@@ -34,9 +49,34 @@ Player :: struct {
         try_move(this, .{delta.x, 0}, walls);
         try_move(this, .{0, delta.y}, walls);
 
-        if is_key_just_up(GLFW_KEY_SPACE) {
+        //
+        // Try to pick up nearby objects
+        //
+        if is_key_just_up(controls.pick_up) {
+            if holding != Entity_Nothing {
+                holding_object := entity_manager->get(holding);
+                holding_object.pos = pos + .{20, 0};
+                holding = Entity_Nothing;
+
+            } else {
+                area    := Rect.{pos.x - Size * 2, pos.y - Size * 2, Size * 4, Size * 4};
+                objects := entity_manager->query_by_flags(area, .Carryable);
+                defer memory.free_slice(^objects);
+
+                // This should first sort by the distance to the object
+                target_object := objects[0];
+                this.holding = target_object.id;
+
+                // Should this fire an event on the target object?
+            }
+        }
+
+        //
+        // Try to interact with nearby objects
+        //
+        if is_key_just_up(controls.interact) {
             area    := Rect.{pos.x - Size * 2, pos.y - Size * 2, Size * 4, Size * 4};
-            objects := entity_manager->query_by_flags(area, Entity_Flags.Solid | .Interactable);
+            objects := entity_manager->query_by_flags(area, .Interactable);
             defer memory.free_slice(^objects);
 
             for objects {
@@ -45,6 +85,19 @@ Player :: struct {
                 vtable.interact(it, this);
             }
         }
+
+        //
+        // Adjust the position of the entity you are carrying
+        // to be above the players head.
+        //
+        if holding != Entity_Nothing {
+            holding_object := entity_manager->get(holding);
+            vtable := ^entity_manager.entity_types[holding_object.type];
+            if vtable.get_rect != null_proc {
+                r := vtable.get_rect(holding_object);
+                holding_object.pos = pos - .{0, Size + r.h};
+            }
+        }
     }
 
     try_move :: (use this: ^Player, delta: Vector2, obsticles: [] ^Entity) {
@@ -52,6 +105,9 @@ Player :: struct {
         ent_rect := get_rect(this);
 
         for obsticles {
+            // Don't check collision on the object that you are carrying because it probably won't make sense.
+            if it.id == this.holding do continue;
+
             vtable := ^entity_manager.entity_types[it.type];
             if Rect.intersects(ent_rect, vtable.get_rect(~~ it)) {
                 pos -= delta;
@@ -114,6 +170,7 @@ Door :: struct {
 
         this.flags |= .Interactable;
         this.flags |= .Solid;
+        this.flags |= .Carryable;
     }
 
     get_rect :: (use this: ^Door) => Rect.{ pos.x, pos.y, size.x * (1 - openness), size.y };
@@ -128,12 +185,8 @@ Door :: struct {
         printf("The door at {} was interacted with by a {}!\n", pos, interactor.type);
 
         // Doors only open if interacted with by a player
-        entity_is(interactor, Player) {
-            if target_openness > 0 do target_openness = 0;
-            else                   do target_openness = 0.8f;
-
-            dist := Vector2.mag(this.pos - interactor.pos);
-            printf("The player is {} units away and has {.2} health.\n", dist, it.health);
+        if_entity_is(interactor, Player) {
+            target_openness = 0.8f - target_openness;
         }
     }
 
@@ -145,6 +198,37 @@ Door :: struct {
     }
 }
 
+
+Item :: struct {
+    use entity: Entity;
+
+    Size :: 8.0f;
+
+    color: Color;
+
+    init_data :: struct {
+        pos   := Vector2.{0, 0};
+        color := Color.{1, 0, 1, 1};
+    }
+
+    init :: (use this: ^Item, data: init_data) {
+        this.pos   = data.pos;
+        this.color = data.color;
+        this.flags |= .Carryable;
+    }
+
+    get_rect :: (use this: ^Item) => Rect.{ pos.x - Size, pos.y - Size, Size * 2, Size * 2 };
+
+    draw :: (use this: ^Item) {
+        immediate_set_color(this.color);
+
+        r := this->get_rect();
+        immediate_rectangle(r.x, r.y, r.w, r.h);
+    }
+}
+
+
+
 @Relocate
 move_towards :: (v: ^$T, target: T, diff: T) {
     if math.abs(target - *v) <= diff {
index a96a1e53ccc6509566994a1e56cd1efba45e39cb..37efd8ed8c5f5a557fec2172e47317d39ed958cb 100644 (file)
@@ -54,6 +54,8 @@ init :: () {
     entity_manager->make(Door, .{ .{400, 150}, .{50, 50} });
     entity_manager->make(Door, .{ .{400, 400}, .{50, 50} });
     entity_manager->make(Door, .{ .{150, 400}, .{50, 50} });
+
+    entity_manager->make(Item, .{ pos=.{ 250, 250 } });
 }
 
 update :: (dt: f32) {