--- /dev/null
+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);
+ }
+ }
+ }
+}
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;
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) => {
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;
}
}
+#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 {
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);
}
}
}
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;
+ }
+}