aboutsummaryrefslogtreecommitdiff
path: root/src/apps/maze_app.jai
diff options
context:
space:
mode:
Diffstat (limited to 'src/apps/maze_app.jai')
-rw-r--r--src/apps/maze_app.jai257
1 files changed, 257 insertions, 0 deletions
diff --git a/src/apps/maze_app.jai b/src/apps/maze_app.jai
new file mode 100644
index 0000000..b3b588c
--- /dev/null
+++ b/src/apps/maze_app.jai
@@ -0,0 +1,257 @@
+#import "Basic";
+#import "Math";
+#import "String";
+
+Maze_Data :: struct {
+ rows: int;
+ cols: int;
+ grid: [..] bool; // false = empty, true = wall
+
+ // Camera
+ camera_x: float;
+ camera_y: float;
+ zoom: float;
+
+ // Panning State
+ is_panning: bool;
+ last_mouse_x: float;
+ last_mouse_y: float;
+
+ // UI State
+ width_input: [..] u8;
+ height_input: [..] u8;
+
+ font: *Simp.Dynamic_Font;
+ panel: UI_Panel;
+}
+
+init_maze_app :: (widget: *Widget_State) {
+ data := New(Maze_Data);
+ data.rows = 10;
+ data.cols = 10;
+ array_resize(*data.grid, data.rows * data.cols);
+ for 0..data.grid.count-1 { data.grid[it] = false; }
+
+ data.camera_x = cast(float) window_width / 2.0 - (5.0 * 50.0);
+ data.camera_y = cast(float) window_height / 2.0 - (5.0 * 50.0);
+ data.zoom = 50.0; // 50 pixels per cell
+ data.is_panning = false;
+
+ // prefill text inputs
+ // TODO reconsider if the UI element should just manage this directly and parse the given value into backing string buffer
+ str_w := sprint("%", data.cols);
+ defer free(str_w);
+ for 0..str_w.count-1 array_add(*data.width_input, str_w[it]);
+
+ str_h := sprint("%", data.rows);
+ defer free(str_h);
+ for 0..str_h.count-1 array_add(*data.height_input, str_h[it]);
+
+ // Setup UI Panel
+ data.panel.x = 20.0;
+ data.panel.y = cast(float)window_height - 300.0;
+ data.panel.w = 200.0;
+ data.panel.h = 240.0;
+ data.panel.userdata = data; // Give callback access to our data!
+ data.panel.app_owner = widget; // Bind panel visibility to this app!
+
+ array_add(*data.panel.elements, .{type=.HEADER, id=5, x0=0.0, y0=data.panel.h - 40.0, x1=data.panel.w, y1=data.panel.h, label="Controls"});
+
+ // Inputs (from top to bottom, note Simp Y=0 is bottom, so highest Y is top)
+ array_add(*data.panel.elements, .{type=.TEXT_INPUT, id=1, x0=10.0, y0=data.panel.h - 100.0, x1=190.0, y1=data.panel.h - 60.0, label="Width:", input_buf=*data.width_input});
+ array_add(*data.panel.elements, .{type=.TEXT_INPUT, id=2, x0=10.0, y0=data.panel.h - 160.0, x1=190.0, y1=data.panel.h - 120.0, label="Height:", input_buf=*data.height_input});
+
+ // Back button at bottom
+ array_add(*data.panel.elements, .{type=.BUTTON, id=4, x0=10.0, y0=10.0, x1=190.0, y1=50.0, label="Back", action=(userdata: *void) { switch_app(*menu_state); }});
+
+ widget.data = data;
+
+ widget.init = (widget: *Widget_State) {
+ data := cast(*Maze_Data) widget.data;
+ data.panel.font = Simp.get_font_at_size("data", "Anonymous_Pro.ttf", 32);
+ data.font = data.panel.font;
+ ui_register_panel(*data.panel);
+ };
+
+ widget.update = (widget: *Widget_State, dt: float) {
+ data := cast(*Maze_Data) widget.data;
+
+ // maze gen / solving step happens in here maybe?
+ };
+
+ widget.shutdown = (widget: *Widget_State) {
+ data := cast(*Maze_Data) widget.data;
+ ui_unregister_panel(*data.panel);
+ };
+
+ widget.update = (widget: *Widget_State, dt: float) {};
+ widget.handle_input = handle_input;
+ widget.render = render;
+
+ widget.shutdown = (widget: *Widget_State) {
+ data := cast(*Maze_Data) widget.data;
+ array_free(data.grid);
+ array_free(data.width_input);
+ array_free(data.height_input);
+ free(data);
+ };
+}
+
+#scope_file
+
+apply_resize :: (data: *Maze_Data) {
+ w_str: string;
+ w_str.data = data.width_input.data;
+ w_str.count = data.width_input.count;
+
+ h_str: string;
+ h_str.data = data.height_input.data;
+ h_str.count = data.height_input.count;
+
+ new_cols, ok_w := string_to_int(w_str);
+ new_rows, ok_h := string_to_int(h_str);
+
+ if ok_w && ok_h && new_cols > 0 && new_rows > 0 {
+ new_grid: [..] bool;
+ array_resize(*new_grid, new_rows * new_cols);
+ for r: 0..new_rows-1 {
+ for c: 0..new_cols-1 {
+ val := false;
+ if r < data.rows && c < data.cols {
+ val = data.grid[r * data.cols + c];
+ }
+ new_grid[r * new_cols + c] = val;
+ }
+ }
+ array_free(data.grid);
+ data.grid = new_grid;
+ data.rows = new_rows;
+ data.cols = new_cols;
+ }
+}
+
+handle_input :: (widget: *Widget_State, occluded: bool) -> (occludes: bool) {
+ data := cast(*Maze_Data) widget.data;
+
+ mouse_x, mouse_y, success := get_mouse_pointer_position(window, true);
+ if !success return occluded;
+ mouse_x_f := cast(float) mouse_x;
+ mouse_y_f := cast(float) mouse_y;
+
+ space_state := Input.input_button_states[Input.Key_Code.SPACEBAR];
+ space_down := (space_state & Input.Key_Current_State.DOWN) != 0;
+
+ for event: Input.events_this_frame {
+ if event.type == .MOUSE_WHEEL {
+ zoom_factor := 1.0;
+ if event.wheel_delta < 0 {
+ zoom_factor = 0.9;
+ } else if event.wheel_delta > 0 {
+ zoom_factor = 1.1;
+ }
+ if zoom_factor != 1.0 {
+ world_x := (mouse_x_f - data.camera_x) / data.zoom;
+ world_y := (mouse_y_f - data.camera_y) / data.zoom;
+
+ data.zoom *= zoom_factor;
+ if data.zoom < 5.0 data.zoom = 5.0;
+ if data.zoom > 300.0 data.zoom = 300.0;
+
+ data.camera_x = mouse_x_f - world_x * data.zoom;
+ data.camera_y = mouse_y_f - world_y * data.zoom;
+ }
+ }
+
+ // if event.type == .TEXT_INPUT && data.panel.focus != 0 {
+ // print("we are text inputting into panel %", data.panel.focus);
+ // value := event.utf32;
+ // if value >= #char "0" && value <= #char "9" {
+ // if data.panel.focus == 1 array_add(*data.width_input, cast(u8)value);
+ // if data.panel.focus == 2 array_add(*data.height_input, cast(u8)value);
+ // }
+ // }
+
+ if event.type == .KEYBOARD && event.key_pressed {
+ if event.key_code == .ENTER {
+ if data.panel.focus != 0 {
+ apply_resize(data);
+ data.panel.focus = 0;
+ }
+ }
+
+ // Mouse clicks
+ if event.key_code == .MOUSE_BUTTON_LEFT {
+ if !occluded {
+ // data.panel.focus = 0;
+
+ // Clicked on grid (only if not panning with spacebar)
+ if !space_down {
+ world_x := (mouse_x_f - data.camera_x) / data.zoom;
+ world_y := (mouse_y_f - data.camera_y) / data.zoom;
+ col := cast(int) world_x;
+ row := cast(int) world_y;
+
+ // Check if within bounds
+ if col >= 0 && col < data.cols && row >= 0 && row < data.rows {
+ data.grid[row * data.cols + col] = !data.grid[row * data.cols + col];
+ }
+ }
+ }
+ }
+ }
+ }
+
+ mouse_down := (Input.input_button_states[Input.Key_Code.MOUSE_BUTTON_LEFT] & Input.Key_Current_State.DOWN) != 0;
+
+ // Panning logic
+ if space_down && mouse_down {
+ if !data.is_panning {
+ data.is_panning = true;
+ data.last_mouse_x = mouse_x_f;
+ data.last_mouse_y = mouse_y_f;
+ } else {
+ dx := mouse_x_f - data.last_mouse_x;
+ dy := mouse_y_f - data.last_mouse_y;
+ data.camera_x += dx;
+ data.camera_y += dy;
+ data.last_mouse_x = mouse_x_f;
+ data.last_mouse_y = mouse_y_f;
+ }
+ } else {
+ data.is_panning = false;
+ }
+
+ return false;
+}
+
+render :: (widget: *Widget_State) {
+ data := cast(*Maze_Data) widget.data;
+
+ Simp.clear_render_target(0.1, 0.1, 0.1, 1.0);
+ Simp.set_shader_for_color(true);
+ Simp.immediate_begin();
+ Simp.immediate_set_2d_projection(window_width, window_height);
+
+
+ // Draw cells
+ for r: 0..data.rows-1 {
+ for c: 0..data.cols-1 {
+ x0 := data.camera_x + cast(float) c * data.zoom;
+ y0 := data.camera_y + cast(float) r * data.zoom;
+
+ cx0 := x0 + 1.0;
+ cy0 := y0 + 1.0;
+ cx1 := x0 + data.zoom - 1.0;
+ cy1 := y0 + data.zoom - 1.0;
+
+ // Culling optimization
+ if cx1 < 0 || cx0 > cast(float)window_width || cy1 < 0 || cy0 > cast(float)window_height continue;
+
+ is_wall := data.grid[r * data.cols + c];
+ color := ifx is_wall then Vector4.{0.8, 0.2, 0.2, 1.0} else Vector4.{0.6, 0.6, 0.6, 1.0};
+ Simp.immediate_quad(cx0, cy0, cx1, cy1, color);
+ }
+ }
+
+ // UI_Panel drawing is entirely handled by the global Window Manager in main.jai!
+}