converted entity code to new component system
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 26 Feb 2022 01:16:18 +0000 (19:16 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sat, 26 Feb 2022 01:16:18 +0000 (19:16 -0600)
src/entity/editor.onyx
src/entity/items.onyx
src/entity/manager.onyx
src/entity/player.onyx
src/game.onyx
src/utils/any_utils.onyx

index 88226b46c2b45fdb598fd8af5dafbc57c40e35c2..24473bada592284d85a397cf15482caf7958736b 100644 (file)
@@ -55,13 +55,12 @@ editor_update :: (dt: f32) {
     mouse_pos := mouse_get_position_vector();
 
     if is_button_just_down(GLFW_MOUSE_BUTTON_LEFT) && mouse_pos.x < ~~window_width - sidebar_width && mouse_pos.y > menubar_height {
-        /*
-        entity_type := ^scene.entity_types.entries[active_index].value;
-
-        entity := entity_type.create_default(^scene);
+        entity_create := scene.schematics.entries[active_index].value;
+        entity := entity_create(^scene);
         scene->add(entity);
+
         entity.pos = mouse_pos;
-        */
+        entity.size = .{editor_grid_size, editor_grid_size};
     }
 }
 
@@ -224,20 +223,17 @@ editor_draw :: () {
 
 #local render_create_sidebar :: (x, y, w, h: f32) {
     i := 0;
-    /*
-    for scene.entity_types.entries {
+    for^ scene.schematics.entries {
         defer i += 1;
 
-        type := it.key;
-        info := cast(^type_info.Type_Info_Struct) type_info.get_type_info(type);
+        name := it.key;
 
         y += 40.0f;
         theme := Button_Theme.{active = active_index == i};
-        if draw_button(.{x, y - 18, w, 36.0f}, info.name, ^theme, increment=i) {
+        if draw_button(.{x, y - 18, w, 36.0f}, name, ^theme, increment=i) {
             active_index = i;
         }
     }
-    */
 
     #persist test : [..] u8;
     if test.data == null {
index c2a648f3b9f6bfed386a8131338200a609dd6851..c53bb904636c3d0a75b19eb32548a25c270cb3bb 100644 (file)
@@ -87,27 +87,22 @@ item_store_get_item :: (use this: ^Item_Store, id: str) -> ^Item {
     return items[id];
 }
 
-Item_Entity :: struct {
-    use entity: Entity;
-
-    [Editor_Custom_Field.{render_item_picker}]
-    item: str;
-
-    init_data :: struct {
-        pos   := Vector2.{0, 0};
-    }
-
-    init :: (use this: ^Item_Entity, data: init_data) {
-        this.pos   = data.pos;
-        this.size  = .{16, 16};
+#local Item_Entity :: struct [Entity_Schematic.{create}] {
+    create :: (scene) => {
+        this := scene->make();
+        this.pos = .{0, 0};
+        this.size = .{0, 0};
         this.flags |= .Carryable;
-    }
 
-    get_rect :: Entity.get_rect
+        scene->create_and_add(this, RenderComponent) { comp.func = render; }
+        scene->create_and_add(this, ItemComponent)   {}
+        return this;
+    }
 
-    draw :: (use this: ^Item_Entity) {
+    render :: (use this: ^Entity) {
         r := this->get_rect();
-        item_data := item_store->get_item(item);
+        item_comp := this->get(ItemComponent);
+        item_data := item_store->get_item(item_comp.item);
         if item_data == null {
             immediate_set_color(.{1,0,0});
             immediate_rectangle(r.x, r.y, r.w, r.h);
@@ -125,6 +120,13 @@ Item_Entity :: struct {
     }
 }
 
+ItemComponent :: struct {
+    use base: Component;
+
+    [Editor_Custom_Field.{render_item_picker}]
+    item: str;
+}
+
 
 #local {
     render_item_picker :: () {
index e54cfda9d014bac74f652425792e4556a1674904..cad011ee9efd9757f524cd72cee441741be89579 100644 (file)
@@ -33,6 +33,16 @@ RenderComponent :: struct {
     func : (e: ^Entity) -> void;
 }
 
+SizeComponent :: struct {
+    use base: Component;
+    func : (e: ^Entity) -> Rect;
+}
+
+InteractableComponent :: struct {
+    use base: Component;
+    interact: (e: ^Entity, interactor: ^Entity) -> void;
+}
+
 Entity :: struct {
     id: Entity_ID;
     flags: Entity_Flags;
@@ -41,7 +51,11 @@ Entity :: struct {
 
     // Does every entity need to have a size?
     size: Vector2;
-    get_rect :: (use e: ^Entity) => Rect.{ pos.x - size.x / 2, pos.y - size.y / 2, size.x, size.y };
+    get_rect :: (use e: ^Entity) => {
+        if e->has(SizeComponent) do return (e->get(SizeComponent)).func(e);
+
+        return Rect.{ pos.x - size.x / 2, pos.y - size.y / 2, size.x, size.y };
+    }
 
     components: Map(type_expr, ^Component);
     has :: (use this: ^Entity,  component_type: type_expr) => components->has(component_type);
@@ -59,6 +73,10 @@ Entity_Flags :: enum #flags {
     Carryable    :: 0x04;
 }
 
+Entity_Schematic :: struct {
+    create: (^Entity_Manager) -> ^Entity;
+}
+
 Entity_Manager :: struct {
     entity_allocator: Allocator;
 
@@ -66,7 +84,7 @@ Entity_Manager :: struct {
     entities: [..] ^Entity;
     entity_map: Map(Entity_ID, ^Entity);
 
-    // entity_types: Map(type_expr, Entity_Info);
+    schematics: Map(str, (^Entity_Manager) -> ^Entity);
 
     next_entity_id: Entity_ID;
 
@@ -96,9 +114,29 @@ entity_manager_create :: () -> Entity_Manager {
     // Entity ID 0 is reserved as a "empty / null" entity
     em.next_entity_id = 1;
 
+    entity_manager_load_schematics(^em);
     return em;
 }
 
+#local entity_manager_load_schematics :: (use this: ^Entity_Manager) {
+    use type_info;
+
+    for type_table {
+        if it.kind != .Struct do continue;
+
+        s_info := cast(^Type_Info_Struct) it;
+        for^ s_info.tags {
+            if it.type == Entity_Schematic {
+                schematic := cast(^Entity_Schematic) it.data;
+                // This is safe to use as the key, as a struct's name exists in static memory.
+                schematics[s_info.name] = schematic.create;
+
+                debug_log(.Debug, "Discovered schematic: '{}'", s_info.name);
+            }
+        }
+    }
+}
+
 entity_manager_add :: (use this: ^Entity_Manager, entity: ^Entity) -> Entity_ID {
     // Automatically assign an id if the entity does not have one.
     // This will not be used when loading the entity from a file.
index 19e71b2e198d9fd46c9e848819a9df9d89a37ae3..308f7618e1cddf77ff6b69e04536adf9a145d492 100644 (file)
@@ -45,7 +45,11 @@ facing_to_direction_vector :: macro (f: Facing) -> Vector2 {
     }
 }
 
-player_create :: (scene: ^Entity_Manager, pos := Vector2.{400, 200}, controls := player_1_controls) -> ^Entity {
+#local Player :: struct [Entity_Schematic.{create}] {
+    create :: (scene: ^Entity_Manager) => player_create(scene, .{0,0});
+}
+
+player_create :: (scene: ^Entity_Manager, pos: Vector2, controls := player_1_controls) -> ^Entity {
     this := scene->make();
     this.pos = pos;
     this.size = .{32, 32};
@@ -76,29 +80,6 @@ PlayerComponent :: struct {
     controls : Player_Controls;
     facing   := Facing.Up;
 
-    create :: (scene: ^Entity_Manager, pos := Vector2.{400, 200}, controls := player_1_controls) -> ^Entity {
-        this := scene->make();
-        this.pos = pos;
-        this.size = .{32, 32};
-        this.flags |= .Solid;
-
-        player_comp := scene->create_component(PlayerComponent);
-        player_comp.holding = Entity_Nothing;
-        player_comp.controls = controls;
-        player_comp.color = .{1,1,1};
-        this->add(player_comp);
-
-        update_comp := scene->create_component(UpdateComponent);
-        update_comp.func = update;
-        this->add(update_comp);
-
-        render_comp := scene->create_component(RenderComponent);
-        render_comp.func = render;
-        this->add(render_comp);
-
-        return this;
-    }
-
     update :: (use this: ^Entity, dt: f32) {
         speed :: 128.0f;
 
@@ -116,96 +97,35 @@ PlayerComponent :: struct {
 
         try_move(this, .{delta.x, 0}, walls);
         try_move(this, .{0, delta.y}, walls);
-    }
 
-    try_move :: (use this: ^Entity, delta: Vector2, obsticles: [] ^Entity) {
-        pos += delta;
-        ent_rect := Entity.get_rect(this);
-
-        holding: Entity_ID;
-        if player := this->get(PlayerComponent); player != null {
-            holding = player.holding;
-        }
+        // Highlight the object that you are going to interact with
 
-        for obsticles {
-            // Don't check collision on the object that you are carrying or yourself because it probably won't make sense.
-            if it == this do continue;
-            if it.id == holding do continue;
+        //
+        // Try to interact with nearby objects
+        //
+        if is_key_just_up(player.controls.interact) {
+            area    := Rect.{pos.x - size.x, pos.y - size.y, size.x * 2, size.y * 2};
+            objects := scene->query_by_component(area, InteractableComponent);
+            defer memory.free_slice(^objects);
 
-            if Rect.intersects(ent_rect, Entity.get_rect(it)) {
-                pos -= delta;
-                break;
+            for objects {
+                interact_comp := it->get(InteractableComponent);
+                interact_comp.interact(it, this);
             }
         }
-    }
-
-    render :: (use this: ^Entity) {
-        player := this->get(PlayerComponent);
-        immediate_set_color(player.color);
-
-        rect := Entity.get_rect(this);
-        immediate_image(^player_assets.texture, rect.x, rect.y, rect.w, rect.h);
-    }
-}
-
-wall_create :: (scene: ^Entity_Manager, pos, size: Vector2) -> ^Entity {
-    this := scene->make();
-    this.pos = pos;
-    this.size = size;
-    this.flags |= .Solid;
-
-    scene->create_and_add(this, RenderComponent) {
-        comp.func = wall_draw;
-    }
-
-    return this;
-}
-
-wall_draw :: (use this: ^Entity) {
-    immediate_set_color(.{1,1,1});
-
-    r := Entity.get_rect(this);
-    immediate_rectangle(r.x, r.y, r.w, r.h);
-}
-
-/*
-    get_rect :: Entity.get_rect
-
-    update :: (use this: ^Player, dt: f32) {
-        speed :: 128.0f;
-
-        delta: Vector2;
-        if is_key_down(controls.left)  { delta.x -= speed * dt; facing = .Left;  }
-        if is_key_down(controls.right) { delta.x += speed * dt; facing = .Right; }
-        if is_key_down(controls.up)    { delta.y -= speed * dt; facing = .Up;    }
-        if is_key_down(controls.down)  { delta.y += speed * dt; facing = .Down;  }
-
-        dist := math.max(size.x, size.y) * 2;
-        walls := scene->query_by_flags(.{pos.x - dist, pos.y - dist, dist * 2, dist * 2}, .Solid);
-        defer memory.free_slice(^walls);
-
-        try_move(this, .{delta.x, 0}, walls);
-        try_move(this, .{0, delta.y}, walls);
-
-        // Highlight the object that you are going to interact with
 
         //
         // Try to pick up nearby objects
         //
-        if is_key_just_up(controls.pick_up) {
-            if holding != Entity_Nothing {
-                holding_object := scene->get(holding);
+        if is_key_just_up(player.controls.pick_up) {
+            if player.holding != Entity_Nothing {
+                holding_object := scene->get(player.holding);
                 holding_object.flags |= .Carryable;
-                /* :ENT_INFO
-                obj_get_rect := ENT_INFO(holding_object).get_rect;
-                if obj_get_rect != null_proc {
-                    r := get_rect(holding_object);
+                r := Entity.get_rect(holding_object);
 
-                    d := Vector2.{(size.x + r.w) / 2 + 4, (size.y + r.h) / 2 + 4};
-                    holding_object.pos = pos + facing_to_direction_vector(facing) * d;
-                }
-                */
-                holding = Entity_Nothing;
+                d := Vector2.{(size.x + r.w) / 2 + 4, (size.y + r.h) / 2 + 4};
+                holding_object.pos = pos + facing_to_direction_vector(player.facing) * d;
+                player.holding = Entity_Nothing;
 
             } else {
                 area    := Rect.{pos.x - size.x, pos.y - size.y, size.x * 2, size.y * 2};
@@ -219,7 +139,7 @@ wall_draw :: (use this: ^Entity) {
 
                     target_object := objects[i];
                     target_object.flags ^= .Carryable; // This only works because we assume that it is Carryable, otherwise this would make it carryable.
-                    this.holding = target_object.id;
+                    player.holding = target_object.id;
                     break;
                 }
 
@@ -227,111 +147,134 @@ wall_draw :: (use this: ^Entity) {
             }
         }
 
-        //
-        // Try to interact with nearby objects
-        //
-        if is_key_just_up(controls.interact) {
-            area    := Rect.{pos.x - size.x, pos.y - size.y, size.x * 2, size.y * 2};
-            objects := scene->query_by_flags(area, .Interactable);
-            defer memory.free_slice(^objects);
-
-            for objects {
-                /* :ENT_INFO
-                if interact := ENT_INFO(it).interact; interact != null_proc {
-                    interact(it, this);
-                }
-                */
-            }
-        }
-
         //
         // Adjust the position of the entity you are carrying
         // to be above the players head.
         //
-        if holding != Entity_Nothing {
-            holding_object := scene->get(holding);
-                /* :ENT_INFO
-            obj_get_rect := ENT_INFO(holding_object).get_rect;
-            if obj_get_rect != null_proc {
-                r := obj_get_rect(holding_object);
-                holding_object.pos = pos - .{0, (size.y + r.h) / 2};
-            }
-            */
+        if player.holding != Entity_Nothing {
+            holding_object := scene->get(player.holding);
+            r := Entity.get_rect(holding_object);
+            holding_object.pos = pos - .{0, (size.y + r.h) / 2};
         }
     }
 
-    // This should be made generic and work for all kinds of entities
-    try_move :: (use this: ^Player, delta: Vector2, obsticles: [] ^Entity) {
+    try_move :: (use this: ^Entity, delta: Vector2, obsticles: [] ^Entity) {
         pos += delta;
-        ent_rect := get_rect(this);
+        ent_rect := Entity.get_rect(this);
+
+        holding: Entity_ID;
+        if player := this->get(PlayerComponent); player != null {
+            holding = player.holding;
+        }
 
         for obsticles {
             // Don't check collision on the object that you are carrying or yourself because it probably won't make sense.
             if it == this do continue;
-            if it.id == this.holding do continue;
+            if it.id == holding do continue;
 
-                /* :ENT_INFO
-            vtable := ENT_INFO(it);
-            if Rect.intersects(ent_rect, vtable.get_rect(it)) {
+            if Rect.intersects(ent_rect, Entity.get_rect(it)) {
                 pos -= delta;
                 break;
             }
-            */
         }
     }
 
+    render :: (use this: ^Entity) {
+        player := this->get(PlayerComponent);
+        immediate_set_color(player.color);
+
+        rect := Entity.get_rect(this);
+        immediate_image(^player_assets.texture, rect.x, rect.y, rect.w, rect.h);
+    }
+}
+
+#local Wall :: struct [Entity_Schematic.{create}] {
+    create :: (scene) => wall_create(scene, .{0,0}, .{0,0});
+
+    draw :: (use this: ^Entity) {
+        immediate_set_color(.{1,1,1});
+
+        r := Entity.get_rect(this);
+        immediate_rectangle(r.x, r.y, r.w, r.h);
+    }
+}
+
+wall_create :: (scene: ^Entity_Manager, pos, size: Vector2) -> ^Entity {
+    this := scene->make();
+    this.pos = pos;
+    this.size = size;
+    this.flags |= .Solid;
+
+    scene->create_and_add(this, RenderComponent) {
+        comp.func = Wall.draw;
+    }
+
+    return this;
 }
-*/
 
 player_assets: struct {
     ["assets/images/player.png"] texture: Texture;
 }
 
-Door :: struct {
-    use entity: Entity;
-
-    [Entity_Store.Skip] openness := 0.0f;
-    target_openness := 0.0f;
+#local Door :: struct [Entity_Schematic.{create}] {
+    create :: (scene) => door_create(scene, .{0, 0}, .{0, 0});
 
-    init_data :: struct {
-        pos := Vector2.{0, 0};
-        size := Vector2.{10, 10};
+    update :: (use this: ^Entity, dt: f32) {
+        door := this->get(DoorComponent);
+        if door.openness != door.target_openness {
+            move_towards(^door.openness, door.target_openness, dt * 2);
+        }
     }
 
-    init :: (use this: ^Door, data: init_data) {
-        this.pos  = data.pos;
-        this.size = data.size;
+    render :: (use this: ^Entity) {
+        immediate_set_color(.{0.7, 0.7, 0.1});
 
-        this.flags |= .Interactable;
-        this.flags |= .Solid;
+        r := Entity.get_rect(this);
+        immediate_rectangle(r.x, r.y, r.w, r.h);
     }
 
-    get_rect :: (use this: ^Door) => {
-        size_x := size.x * (1 - openness);
+    get_rect :: (use this: ^Entity) => {
+        door := this->get(DoorComponent);
+        size_x := size.x * (1 - door.openness);
         return Rect.{ pos.x - size_x / 2, pos.y - size.y / 2, size_x, size.y };
     }
 
-    update :: (use this: ^Door, dt: f32) {
-        if openness != target_openness {
-            move_towards(^openness, target_openness, dt * 2);
+    interact :: (use this: ^Entity, interactor: ^Entity) {
+        // Doors only open if interacted with by a player
+        if interactor->has(PlayerComponent) {
+            door := this->get(DoorComponent);
+            door->toggle_open();
         }
     }
+}
 
-    interact :: (use this: ^Door, interactor: ^Entity) {
-        // Doors only open if interacted with by a player
-        //if_entity_is(interactor, Player) {
-            target_openness = 0.8f - target_openness;
-        //}
-    }
+DoorComponent :: struct {
+    use base: Component;
 
-    draw :: (use this: ^Door) {
-        immediate_set_color(.{0.7, 0.7, 0.1});
+    target_openness := 0.0f;
+    openness        := 0.0f;
 
-        r := this->get_rect();
-        immediate_rectangle(r.x, r.y, r.w, r.h);
+    toggle_open :: (use this: ^DoorComponent) {
+        target_openness = 0.8f - target_openness;
     }
 }
 
+door_create :: (scene: ^Entity_Manager, pos, size: Vector2) -> ^Entity {
+    this := scene->make();
+    this.pos  = pos;
+    this.size = size;
+
+    this.flags |= .Interactable;
+    this.flags |= .Solid;
+
+    scene->create_and_add(this, DoorComponent)         {}
+    scene->create_and_add(this, UpdateComponent)       { comp.func = Door.update; }
+    scene->create_and_add(this, RenderComponent)       { comp.func = Door.render; }
+    scene->create_and_add(this, SizeComponent)         { comp.func = Door.get_rect; }
+    scene->create_and_add(this, InteractableComponent) { comp.interact = Door.interact; }
+
+    return this;
+}
 
 
 
index de36b3436abb48894bbf0e5d3989ee795c651503..83c1d9516a275649420d5a1782f337ee141167b9 100644 (file)
@@ -18,12 +18,31 @@ game_init :: () {
 
     scene = entity_manager_create();
     // scene->load_from_file("scenes/quick_save.scene");
-    player := player_create(^scene);
+    // player := player_create(^scene);
+
+    player := scene.schematics["Player"](^scene);
     scene->add(player);
 
-    wall := wall_create(^scene, .{0, 100}, .{300, 40});
+    // wall := wall_create(^scene, .{0, 100}, .{300, 40});
+    wall := scene.schematics["Wall"](^scene);
+    wall.pos = .{0, 100};
+    wall.size = .{300, 40};
     scene->add(wall);
 
+    door := scene.schematics["Door"](^scene);
+    door.pos = .{500, 300};
+    door.size = .{100, 20};
+    scene->add(door);
+
+    item := scene.schematics["Item_Entity"](^scene);
+    item.pos = .{100, 200};
+    item.size = .{32, 32};
+    with (item->get(ItemComponent)) {
+        it.item = "beer";
+    }
+
+    scene->add(item);
+
     item_store = item_store_make();
     item_store->load_items_from_file("scenes/default.items");
 }
index 46d1b17bebd2dd8d023ade8e754f2638534ed5b6..ab06c1d8131eaee63d4aeb0d4a535c7ce8636dd3 100644 (file)
@@ -40,3 +40,8 @@ get_any_for_member :: (base: any, member_name: str) -> any {
 
     return .{null, void};
 }
+
+with :: macro (v: $T, body: Code) {
+    it := v;
+    #insert body;
+}