From a1fcf14af4ebf53c3e87235ad66eb00124e677f0 Mon Sep 17 00:00:00 2001 From: Brendan Hansen Date: Thu, 25 Nov 2021 22:08:48 -0600 Subject: [PATCH] refactored snake example --- misc/onyx/heartbreak.onyx | 1 + misc/onyx/heartbreak_graphics.onyx | 1 + misc/onyx/heartbreak_utils.onyx | 61 ++++++++++ src/heartbreak_graphics.c | 11 ++ tests/snake.onyx | 175 +++++++++++++++-------------- 5 files changed, 165 insertions(+), 84 deletions(-) create mode 100644 misc/onyx/heartbreak_utils.onyx diff --git a/misc/onyx/heartbreak.onyx b/misc/onyx/heartbreak.onyx index d512831..ad2cfb2 100644 --- a/misc/onyx/heartbreak.onyx +++ b/misc/onyx/heartbreak.onyx @@ -6,6 +6,7 @@ package heartbreak #load "./heartbreak_thread_runtime" #load "./heartbreak_timer" #load "./heartbreak_window" +#load "./heartbreak_utils" use package core {println} diff --git a/misc/onyx/heartbreak_graphics.onyx b/misc/onyx/heartbreak_graphics.onyx index 77cac2a..f652e61 100644 --- a/misc/onyx/heartbreak_graphics.onyx +++ b/misc/onyx/heartbreak_graphics.onyx @@ -4,6 +4,7 @@ package heartbreak.graphics setClearColor :: (r, g, b: f32, a: f32 = 1) -> void #foreign "heartbreak" "graphics_set_clear_color" --- setColor :: (r, g, b: f32, a: f32 = 1) -> void #foreign "heartbreak" "graphics_set_color" --- +getColor :: (r, g, b, a: ^f32) -> void #foreign "heartbreak" "graphics_get_color" --- setLineWidth :: (width: i32) -> void #foreign "heartbreak" "graphics_set_line_width" --- // diff --git a/misc/onyx/heartbreak_utils.onyx b/misc/onyx/heartbreak_utils.onyx new file mode 100644 index 0000000..5b2c2dd --- /dev/null +++ b/misc/onyx/heartbreak_utils.onyx @@ -0,0 +1,61 @@ +package heartbreak.utils + +use package core +#local hb :: package heartbreak + +load_assets :: (assets: any) { + ptr_asset_info := cast(^type_info.Type_Info_Pointer) type_info.get_type_info(assets.type); + assert(ptr_asset_info.kind == .Pointer, "Loading assets only works for pointers to structures."); + asset_info := cast(^type_info.Type_Info_Struct) type_info.get_type_info(ptr_asset_info.to); + assert(asset_info.kind == .Struct, "Loading assets only works for pointers to structures."); + + set_member :: macro (a, T, m, v) => { + *cast(^T) ((cast(^u8) *cast(^rawptr) a.data) + m.offset) = v; + } + + for ^asset: asset_info.members { + switch asset.type { + case hb.graphics.Image { + path := *(cast(^str) array.first(asset.tags, (t) => t.type == str).data); + img := hb.graphics.newImage(path); + + if img == 0 { + println("Failed to load image"); + continue; + } + + @TODO // Add settings like scaling, filters, etc. + filter_ptr := array.first(asset.tags, (t) => t.type == hb.graphics.ImageFilter); + if filter_ptr != null { + filter := *cast(^hb.graphics.ImageFilter) filter_ptr.data; + hb.graphics.setImageFilters(img, filter, filter); + } + + wrapping_ptr := array.first(asset.tags, (t) => t.type == hb.graphics.ImageWrapping); + if wrapping_ptr != null { + wrapping := *cast(^hb.graphics.ImageWrapping) wrapping_ptr.data; + hb.graphics.setImageWrapping(img, wrapping, wrapping); + } + + set_member(assets, hb.graphics.Image, asset, img); + } + + case hb.graphics.Font { + path := *(cast(^str) array.first(asset.tags, (t) => t.type == str).data); + size := *(cast(^i32) array.first(asset.tags, (t) => t.type == i32).data); + font := hb.graphics.newFont(path, ~~size); + + if font == 0 { + println("Failed to load font"); + continue; + } + + set_member(assets, hb.graphics.Font, asset, font); + } + + case #default { + printf("Cannot load something of type '{}'.\n", asset.type); + } + } + } +} diff --git a/src/heartbreak_graphics.c b/src/heartbreak_graphics.c index 101dd91..a131a6f 100644 --- a/src/heartbreak_graphics.c +++ b/src/heartbreak_graphics.c @@ -31,6 +31,16 @@ HEARTBREAK_DEF(set_color, (WASM_F32,WASM_F32,WASM_F32,WASM_F32), ()) { return NULL; } +HEARTBREAK_DEF(get_color, (WASM_I32,WASM_I32,WASM_I32,WASM_I32), ()) { + char* mem = wasm_memory_data(wasm_memory); + + *(f32 *) (mem + params->data[0].of.i32) = renderer.current_color.r; + *(f32 *) (mem + params->data[1].of.i32) = renderer.current_color.g; + *(f32 *) (mem + params->data[2].of.i32) = renderer.current_color.b; + *(f32 *) (mem + params->data[3].of.i32) = renderer.current_color.a; + return NULL; +} + HEARTBREAK_DEF(set_line_width, (WASM_I32), ()) { glLineWidth(params->data[0].of.i32); return NULL; @@ -480,6 +490,7 @@ HEARTBREAK_DEF(get_text_height, (WASM_I32, WASM_I32, WASM_F32), (WASM_F32)) { HEARTBREAK_MODULE { HEARTBREAK_FUNC(set_clear_color) HEARTBREAK_FUNC(set_color) + HEARTBREAK_FUNC(get_color) HEARTBREAK_FUNC(set_line_width) HEARTBREAK_FUNC(clear) diff --git a/tests/snake.onyx b/tests/snake.onyx index 1ca8c7b..7bea9a6 100644 --- a/tests/snake.onyx +++ b/tests/snake.onyx @@ -2,72 +2,6 @@ use package core -assets : struct { - ["./tests/player.png"] - [hb.graphics.ImageFilter.Nearest] - player: hb.graphics.Image; - - ["./assets/fonts/mononoki-Regular Nerd Font Complete Mono.ttf", 72] - title_font: hb.graphics.Font; -} - -load_assets :: (assets: any) { - ptr_asset_info := cast(^type_info.Type_Info_Pointer) type_info.get_type_info(assets.type); - assert(ptr_asset_info.kind == .Pointer, "Loading assets only works for pointers to strucutres."); - asset_info := cast(^type_info.Type_Info_Struct) type_info.get_type_info(ptr_asset_info.to); - assert(asset_info.kind == .Struct, "Loading assets only works for pointers to strucutres."); - - set_member :: macro (a, T, m, v) => { - *cast(^T) ((cast(^u8) *cast(^rawptr) a.data) + m.offset) = v; - } - - for ^asset: asset_info.members { - switch asset.type { - case hb.graphics.Image { - path := *(cast(^str) array.first(asset.tags, (t) => t.type == str).data); - img := hb.graphics.newImage(path); - - if img == 0 { - println("Failed to load image"); - continue; - } - - @TODO // Add settings like scaling, filters, etc. - filter_ptr := array.first(asset.tags, (t) => t.type == hb.graphics.ImageFilter); - if filter_ptr != null { - filter := *cast(^hb.graphics.ImageFilter) filter_ptr.data; - hb.graphics.setImageFilters(img, filter, filter); - } - - wrapping_ptr := array.first(asset.tags, (t) => t.type == hb.graphics.ImageWrapping); - if wrapping_ptr != null { - wrapping := *cast(^hb.graphics.ImageWrapping) wrapping_ptr.data; - hb.graphics.setImageWrapping(img, wrapping, wrapping); - } - - set_member(assets, hb.graphics.Image, asset, img); - } - - case hb.graphics.Font { - path := *(cast(^str) array.first(asset.tags, (t) => t.type == str).data); - size := *(cast(^i32) array.first(asset.tags, (t) => t.type == i32).data); - font := hb.graphics.newFont(path, ~~size); - - if font == 0 { - println("Failed to load font"); - continue; - } - - set_member(assets, hb.graphics.Font, asset, font); - } - - case #default { - printf("Cannot load something of type '{}'.\n", asset.type); - } - } - } -} - Vec2 :: struct (T: type_expr) { x, y: T; @@ -145,13 +79,43 @@ state := GameState.MainMenu; the_snake: Snake; the_food: Vec2i; +assets : struct { + ["./tests/player.png", hb.graphics.ImageFilter.Nearest] + player: hb.graphics.Image; + + ["./assets/fonts/mononoki-Regular Nerd Font Complete Mono.ttf", 72] + title_font: hb.graphics.Font; +} + +Menu :: struct { + options: type_expr; + selected_option := 0; +} + +Main_Menu_Options :: struct { + ["Play game"] + option_1 := () { + active_menu = .{void}; + state = .Playing; + }; + + ["Quit"] + option_2 := () { + hb.window.setShouldClose(true); + }; +} + +active_menu: Menu; + load :: () { hb.window.setTitle("Snake 🐍"); - load_assets(^assets); + hb.utils.load_assets(^assets); the_snake = snake_make(.{ 0, 0 }); the_food = .{ 10, 10 }; + + active_menu = .{ Main_Menu_Options }; } cell_contains_snake_body :: (p: Vec2i) => { @@ -166,14 +130,36 @@ update :: (dt: f32) { hb.window.setShouldClose(true); } - if state == .MainMenu { + if state != .Playing { + if active_menu.options == void do return; + + #persist last_up_down := false; + #persist last_down_down := false; + up_down := hb.input.keyIsDown(.Up); + down_down := hb.input.keyIsDown(.Down); + defer { + last_up_down = up_down; + last_down_down = down_down; + } + + if !up_down && last_up_down { active_menu.selected_option -= 1; } + if !down_down && last_down_down { active_menu.selected_option += 1; } + + option_count := (cast(^type_info.Type_Info_Struct) type_info.get_type_info(active_menu.options)).members.count; + + if active_menu.selected_option < 0 do active_menu.selected_option = 0; + if active_menu.selected_option >= option_count do active_menu.selected_option = option_count - 1; + if hb.input.keyIsDown(.Space) { - state = .Playing; - return; + option := (cast(^type_info.Type_Info_Struct) type_info.get_type_info(active_menu.options)).members[active_menu.selected_option]; + if option.type == #type () -> void { + func := *(cast(^() -> void) option.default); + func(); + } } - } - if state != .Playing do return; + return; + } #persist last_left_down := false; #persist last_right_down := false; @@ -211,26 +197,24 @@ update :: (dt: f32) { } } +#local centered_text :: macro (text: str, y: f32) { + width := hb.graphics.getTextWidth(text); + hb.graphics.print(text, (~~ww - width) / 2, y); +} + draw :: () { hb.graphics.setClearColor(0.9, 0.9, 0.9); hb.graphics.clear(); ww, wh := hb.window.getDimensions(); - centered_text :: macro (text: str, y: f32) { - width := hb.graphics.getTextWidth(text); - hb.graphics.print(text, (~~ww - width) / 2, y); - } - switch state { case .MainMenu { hb.graphics.setFont(assets.title_font); hb.graphics.setColor(0.2, 0.2, 0.2); centered_text("Snake!", 100); - hb.graphics.setFont(); - centered_text("Option 1", 200); - centered_text("Option 2", 240); + draw_menu(200); } case .Playing { @@ -255,10 +239,9 @@ draw :: () { hb.graphics.setColor(0, 0, 0, 0.4); hb.graphics.rectangle(.Fill, 0, 0, ~~ww, ~~wh); - message := "You Lost!"; - message_width := hb.graphics.getTextWidth(message); + hb.graphics.setFont(assets.title_font); hb.graphics.setColor(1, 1, 1); - hb.graphics.print(message, (~~ww - message_width) / 2, 100); + centered_text("You Lost!", 100); } } } @@ -273,4 +256,28 @@ draw_game :: () { hb.graphics.setLineWidth(2); hb.graphics.setColor(0.2, 0.25, 0.03); hb.graphics.circle(.Line, cell_size * ~~the_food.x + cell_size / 2, cell_size * ~~the_food.y + cell_size / 2, cell_size / 2); -} \ No newline at end of file +} + +draw_menu :: (y: f32) { + ww, wh := hb.window.getDimensions(); + hb.graphics.setFont(); + + menu_info := cast(^type_info.Type_Info_Struct) type_info.get_type_info(active_menu.options); + if menu_info.kind != .Struct do return; + + for it: iter.enumerate(menu_info.members) { + text := *(cast(^str) array.first(it.value.tags, (t) => t.type == str).data); + height := hb.graphics.getTextHeight(text); + + if it.index == active_menu.selected_option { + r, g, b, a: f32; + hb.graphics.getColor(^r, ^g, ^b, ^a); + hb.graphics.setColor(0.2, 0.2, 0.2, 0.2); + hb.graphics.rectangle(.Fill, 0, y - height - 8, ~~ww, height + 16); + hb.graphics.setColor(r, g, b, a); + } + + centered_text(text, ~~y); + y += 40; + } +} -- 2.25.1