flushing out component system
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 28 Feb 2022 17:50:56 +0000 (11:50 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 28 Feb 2022 17:50:56 +0000 (11:50 -0600)
15 files changed:
run_tree/lib/onyx_openal.so [new file with mode: 0755]
run_tree/scenes/default.items
run_tree/scenes/quick_save_new.scene
src/build.onyx
src/entity/components/dispenser.onyx [new file with mode: 0644]
src/entity/items.onyx
src/entity/manager.onyx
src/entity/player.onyx
src/entity/schematics/tap.onyx [new file with mode: 0644]
src/game.onyx
src/gfx/texture.onyx
src/main.onyx
src/utils/any_utils.onyx
src/utils/asset_loader.onyx
src/utils/vecmath.onyx

diff --git a/run_tree/lib/onyx_openal.so b/run_tree/lib/onyx_openal.so
new file mode 100755 (executable)
index 0000000..77bd206
Binary files /dev/null and b/run_tree/lib/onyx_openal.so differ
index 5b797fead8b788afd26c6f09e7d61f002fd185fa..d0ee5eeefbfe830f5e0a95acc3ab4a3a9210dd07 100644 (file)
@@ -1,5 +1,7 @@
 :beer
 name="Beer"
 texture_path="./assets/images/beer-1.png"
+texture_pos = 0 0
+texture_size = 16 16
 
 
index 8d39eec9d63becbc97cec583304cd658cdc7edd5..19205dd67065aa2e2d50abf46fcdd7389fab6c99 100644 (file)
@@ -13,7 +13,7 @@ color.a = 1.0000
 
 [Wall]
 id = 11
-flags = 2
+flags = 3
 pos.x = 440.0000
 pos.y = 160.0000
 size.x = 16.0000
@@ -27,8 +27,8 @@ color.a = 1.0000
 [Player]
 id = 12
 flags = 2
-pos.x = 452.6227
-pos.y = 356.0881
+pos.x = 544.3174
+pos.y = 236.6171
 size.x = 32.0000
 size.y = 32.0000
 :MovementComponent
@@ -38,7 +38,7 @@ controls.left = 65
 controls.right = 68
 controls.interact = 70
 controls.pick_up = 71
-facing = 1
+facing = 4
 :PlayerComponent
 holding = 0
 :RenderComponent
@@ -79,3 +79,34 @@ color.a = 1.0000
 :ItemComponent
 item = "beer"
 
+[Item_Entity]
+id = 15
+flags = 4
+pos.x = 555.2645
+pos.y = 298.4719
+size.x = 16.0000
+size.y = 16.0000
+:RenderComponent
+color.r = 1.0000
+color.g = 1.0000
+color.b = 1.0000
+color.a = 1.0000
+:ItemComponent
+item = "beer"
+
+[Tap]
+id = 16
+flags = 3
+pos.x = 576.0000
+pos.y = 160.0000
+size.x = 32.0000
+size.y = 32.0000
+:RenderComponent
+color.r = 0.1000
+color.g = 0.1000
+color.b = 1.0000
+color.a = 1.0000
+:DispenserComponent
+item = "beer"
+max_timeout = 5.0000
+
index b949ecfb05f8c4cd1f11555422c20ee74e09e182..4adc9a10dec68f0fd0b9e9a4c0bf861276be4985 100644 (file)
@@ -13,29 +13,13 @@ DEBUG :: false
 
 #load "core/std"
 
-#load "config"
-#load "game"
-#load "main"
-
-#load "entity/editor"
-#load "entity/items"
-#load "entity/manager"
-#load "entity/player"
-#load "entity/store"
-
-#load "gfx/canvas"
-#load "gfx/font"
-#load "gfx/immediate"
-#load "gfx/mesh"
-#load "gfx/shader"
-#load "gfx/texture"
-#load "gfx/ui"
-
-#load "utils/any_utils"
-#load "utils/asset_loader"
-#load "utils/input"
-#load "utils/logger"
-#load "utils/vecmath"
+#load_all "./."
+
+#load_all "./entity"
+#load_all "./entity/components"
+#load_all "./entity/schematics"
+#load_all "./gfx"
+#load_all "./utils"
 
 #load "stb_image"
 #load "stb_truetype"
diff --git a/src/entity/components/dispenser.onyx b/src/entity/components/dispenser.onyx
new file mode 100644 (file)
index 0000000..748f6d8
--- /dev/null
@@ -0,0 +1,70 @@
+
+use package core
+
+//
+// Currently, DispenserComponents only dispense Item_Entity's, and no
+// other entity type. I can't think of a reason at the moment that
+// you would need to dispense something else, but that is a limitation.
+
+DispenserComponent :: struct {
+    use base: Component;
+
+    item: str;
+    max_timeout := 2.0f;
+
+    #tag Entity_Store.Skip
+    timeout := 0.0f;
+
+    added :: (use this: ^DispenserComponent, entity: ^Entity) {
+        if entity->has(InteractableComponent) {
+            debug_log(.Error, "DispenserComponent expected entity to not have an InteractableComponent");
+        }
+
+        scene->create_and_add(entity, InteractableComponent) {
+            comp.interact = interact;
+        }
+    }
+
+    update :: (use this: ^DispenserComponent, entity: ^Entity, dt: f32) {
+        timeout -= dt;
+        if timeout < 0 do timeout = 0;
+    }
+
+    interact :: (this: ^Entity, entity: ^Entity) {
+        if entity->has(PlayerComponent) {
+            player_comp := entity->get(PlayerComponent);
+            if player_comp.holding != Entity_Nothing do return;
+
+            dispenser_comp := this->get(DispenserComponent);
+            if dispenser_comp.timeout != 0 do return;
+
+            item := scene->create_from_schematic("Item_Entity");
+            if item == null {
+                debug_log(.Error, "Bad schematic type.");
+                return;
+            }
+
+            // This should dynamic...
+            item.size = .{16, 16};
+            (item->get(ItemComponent)).item = dispenser_comp.item;
+
+            scene->add(item);
+            player_comp.holding = item.id;
+            dispenser_comp.timeout = dispenser_comp.max_timeout;
+        }
+    }
+
+    post_render :: (use this: ^DispenserComponent, entity: ^Entity) {
+        if timeout != 0 {
+            rect := entity->get_rect();
+
+            percent := (max_timeout - timeout) / max_timeout;
+            rect.y += (1 - percent) * rect.h;
+            rect.h *= percent;
+
+            immediate_set_color(.{0.4, 1, 0.4, 0.8});
+            immediate_rectangle(rect.x, rect.y, rect.w, rect.h);
+        }
+    }
+}
+
index 5229ccac6ca4971f9b731fabe92f5804d64b2b62..a96a1daa5ba95d56f5a675f68af7267eb2088792 100644 (file)
@@ -7,6 +7,8 @@ Item :: struct {
 
     color        := Color.{ 1, 1, 1 };
     texture_path : str;
+    texture_pos  := Vector2.{0, 0};
+    texture_size := Vector2.{1, 1};
 }
 
 Item_Store :: struct {
@@ -116,7 +118,11 @@ item_store_get_item :: (use this: ^Item_Store, id: str) -> ^Item {
         if !loaded {
             immediate_rectangle(r.x, r.y, r.w, r.h);
         } else {
-            immediate_image(^texture, r.x, r.y, r.w, r.h);            
+            tp := item_data.texture_pos;
+            ts := item_data.texture_size;
+            // immediate_image(^texture, r.x, r.y, r.w, r.h);            
+            immediate_subimage(^texture, r.x, r.y, r.w, r.h,
+                                         tp.x, tp.y, ts.x, ts.y);
         }
     }
 }
index 5b18c1f1c8013b9a08f24b08dbf29ec655723315..bf4d5fcb8eae16e90a7e3a971bd3ef6261e011be 100644 (file)
@@ -16,8 +16,30 @@ Entity_ID :: #distinct u32
 }
 
 #local Component_Vtable :: struct {
-    init  : (rawptr) -> void               = null_proc;
-    update: (rawptr, ^Entity, f32) -> void = null_proc;
+    //
+    // 'init' is called when the component is first created, before it
+    // has been added to any entity.
+    init       : (rawptr) -> void               = null_proc;
+
+    // 
+    // 'added' is called when the component is added to an entity.
+    // In theory, it could be called multiple times, if the component
+    // is shared between multiple entities.
+    added      : (rawptr, ^Entity) -> void      = null_proc;
+
+    //
+    // 'update' is called every update cycle. Currently, there is no
+    // specified as to the order in which components get updated.
+    // I think by circumstance, it is the order that you add them
+    // to the entity, but could break in the future.
+    update     : (rawptr, ^Entity, f32) -> void = null_proc;
+
+    //
+    // 'post_render' is called after the entity has been rendered
+    // normally. Entities must have a 'RenderComponent' in order
+    // to have 'post_render' called on their components. This
+    // can be used to add overlays or popups to an entity.
+    post_render: (rawptr, ^Entity) -> void      = null_proc;
 }
 
 Component :: struct {
@@ -38,7 +60,8 @@ RenderComponent :: struct {
     #tag Editor_Hidden, Entity_Store.Skip
     func : (e: ^Entity) -> void;
 
-    color := Color.{1,1,1};
+    layer := 0;
+    color := Color.{1, 1, 1};
 }
 
 SizeComponent :: struct {
@@ -80,6 +103,9 @@ Entity :: struct {
     add :: (use this: ^Entity,  component: ^Component) => {
         if components->has(component.type) do return;
         components[component.type] = component;
+        if component.added != null_proc {
+            component->added(this);
+        }
     }
 }
 
@@ -253,11 +279,20 @@ entity_manager_update :: (use this: ^Entity_Manager, dt: f32) {
 
 entity_manager_draw :: (use this: ^Entity_Manager) {
     // Entities should be sorted by z-order.
+    
+
     for entities {
         render_comp := it->get(RenderComponent);
         if render_comp != null {
             immediate_set_color(render_comp.color);
             render_comp.func(it);
+
+            for^ entry: it.components.entries {
+                comp := entry.value;
+                if comp.post_render != null_proc {
+                    comp->post_render(it);
+                }
+            }
         }
     }
 }
index cf207d8cd5bfc66fab3a140fd288ae136685a49a..fdb21855cc217172338789d53240a0ede1cfa97a 100644 (file)
@@ -216,6 +216,7 @@ wall_create :: (scene: ^Entity_Manager, pos, size: Vector2) -> ^Entity {
 
 player_assets: struct {
     #tag "assets/images/player.png"
+    #tag Texture_Wrap.Clamp
     texture: Texture;
 }
 
diff --git a/src/entity/schematics/tap.onyx b/src/entity/schematics/tap.onyx
new file mode 100644 (file)
index 0000000..98a9907
--- /dev/null
@@ -0,0 +1,35 @@
+
+Tap :: struct {
+    #struct_tag Entity_Schematic.{
+        (scene) => Tap.create(scene, .{0,0}, .{0,0})
+    }
+
+    create :: (scene: ^Entity_Manager, pos: Vector2, size: Vector2) -> ^Entity {
+        this := scene->make();
+        this.pos = pos;
+        this.size = size;
+        this.flags |= .Solid;
+        this.flags |= .Interactable;
+
+        scene->create_and_add(this, RenderComponent) {
+            comp.func = render;
+        }
+
+        scene->create_and_add(this, DispenserComponent) {
+            comp.item = "beer";
+        }
+
+        return this;
+    }
+
+    render :: (use this: ^Entity) {
+        rect := this->get_rect();
+
+        immediate_rectangle(rect.x, rect.y, rect.w, rect.h);
+    }
+}
+
+tap_assets: struct {
+    #tag "./assets/"
+    texture: Texture;
+}
\ No newline at end of file
index 8a218bdac91586ceeb81d6f6ff01d7440c8cd9a8..5f551497947c9b3f035658eb55a82f5ca800fe53 100644 (file)
@@ -58,6 +58,7 @@ game_draw :: () {
     canvas_use(null);
     
     immediate_clear(.{1, 1, 1});
+    immediate_set_color(.{1, 1, 1});
 
     texture := canvas_to_texture(^scene_canvas);
     view_rect: Rect;
index 2ceb9801ff913943772242c44d77ca46ae2eb3c7..0df03b8a17d59231de75580b318d24b779dc278b 100644 (file)
@@ -46,7 +46,7 @@ texture_lookup :: #match {}
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
     glBindTexture(GL_TEXTURE_2D, 0);
 
@@ -65,3 +65,16 @@ texture_use :: (use tex: ^Texture, texture_binding := 0) {
     glActiveTexture(GL_TEXTURE0 + texture_binding);
     glBindTexture(GL_TEXTURE_2D, texture);
 }
+
+
+Texture_Wrap :: enum {
+    Clamp :: GL_CLAMP_TO_EDGE | 0;
+    Repeat :: GL_REPEAT | 0;
+    Mirrored_Repeat :: GL_MIRRORED_REPEAT | 0;
+}
+
+texture_wrap :: (use tex: ^Texture, wrap: Texture_Wrap) {
+    glBindTexture(GL_TEXTURE_2D, texture);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, ~~ wrap);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, ~~ wrap);
+}
\ No newline at end of file
index 42138c5c08fa9af236aac81ac699e21056be8b80..fa1abb93d1f3114dc50ad20c4cb5a819ae69f8d9 100644 (file)
@@ -141,7 +141,7 @@ create_window :: () => {
 }
 
 main :: (args) => {
-    debug_set_level(.Info);
+    debug_set_level(.Debug);
     if !glfwInit() {
         debug_log(.Critical, "Failed to initialize GLFW!");
         os.exit(1);
index ab06c1d8131eaee63d4aeb0d4a535c7ce8636dd3..803615d9586f282d631ec58e0ca21ea9b91d7852 100644 (file)
@@ -30,7 +30,7 @@ get_any_for_member :: (base: any, member_name: str) -> any {
     for info.members {
         if it.name == part_name {
             member_info := get_type_info(it.type);
-            if member_info.kind == .Struct {
+            if member_info.kind == .Struct && name != "" {
                 return get_any_for_member(any.{cast(^u8) base.data + it.offset, it.type}, name);
             } else {
                 return any.{cast(^u8) base.data + it.offset, it.type};
index c4ecea664060e6ec890b03b9b96af1670d017382..ccf5f733f5276c07ce3f7a80521d44b3ce3f99f0 100644 (file)
@@ -44,6 +44,11 @@ load_assets :: () {
                 if path_tag != null {
                     path := *cast(^str) path_tag.data;
                     if texture, success := texture_lookup(path); success {
+                        if wrap_tag := array.first(tags, (x) => x.type == Texture_Wrap); wrap_tag != null {
+                            wrap := *cast(^Texture_Wrap) wrap_tag.data;
+                            texture_wrap(^texture, wrap);
+                        }
+
                         *out_texture = texture;
                     } else {
                         debug_log(.Error, "Failed to load texture '{}' for asset queued here: {}:{},{}.\n", path, location.file, location.line, location.column);
index 9dbf7c17f0e67cbbdbca781a9355ee4fea06a2d4..3bda373ca7ad5541190e1528c84bc41586a248ad 100644 (file)
@@ -5,6 +5,7 @@ Vector2 :: struct {
     Zero :: Vector2.{0, 0}
 
     #struct_tag conv.Custom_Format.{format_vector2}
+    #struct_tag conv.Custom_Parse.{parse_vector2}
 }
 
 Vector3i :: struct {
@@ -79,6 +80,21 @@ Vector3 :: struct {
     format_vector3i :: (output: ^conv.Format_Output, format: ^conv.Format, v: ^Vector3i) {
         conv.format(output, "({}, {}, {})", v.x, v.y, v.z);
     }
+
+    parse_vector2 :: (output: ^Vector2, line_: str, string_allocator: Allocator) -> bool {
+        string :: package core.string
+
+        line := line_;
+        xs := string.read_until(^line, #char " ");
+        string.advance(^line, 1);
+        ys := string.read_until(^line, #char " ");
+
+        if xs == "" || ys == "" do return false;
+
+        output.x = ~~ conv.str_to_f64(xs);
+        output.y = ~~ conv.str_to_f64(ys);
+        return true;
+    }
 }