commit 91e205869c1baf84bf10a841e1828acb4b804647 Author: Marto Date: Fri Apr 24 14:22:25 2026 +0200 Initial commit diff --git a/common/constants.odin b/common/constants.odin new file mode 100644 index 0000000..b3f439c --- /dev/null +++ b/common/constants.odin @@ -0,0 +1,19 @@ +package common + +import rl "vendor:raylib" + +WIDTH :: 1920 +HEIGHT :: 1080 +MONITOR :: 0 + +ROAD_SIZE :: 20 +NODE_RADIUS :: 20 +NODE_SNAP_RADIUS :: 3 +TEXT_SIZE :: 50 + +ROAD_COLOUR :: rl.BLACK +NODE_DONE_COLOUR :: rl.BROWN +NODE_BUILD_COLOUR :: rl.GOLD +NODE_CURSOR_COLOUR :: rl.BLUE + +BACKGROUND_COLOUR :: rl.LIGHTGRAY \ No newline at end of file diff --git a/infrastructure/node.odin b/infrastructure/node.odin new file mode 100644 index 0000000..9ea3a50 --- /dev/null +++ b/infrastructure/node.odin @@ -0,0 +1,19 @@ +package infrastructure + +import rl "vendor:raylib" + +Node :: struct { + id: u32, + enabled: bool, + pos: rl.Vector2, + roads: [dynamic]u32, +} + +node_init :: proc(new_id: u32, new_pos: rl.Vector2) -> Node { + return { + id = new_id, + enabled = true, + pos = new_pos, + roads = nil, + } +} \ No newline at end of file diff --git a/infrastructure/road.odin b/infrastructure/road.odin new file mode 100644 index 0000000..842e118 --- /dev/null +++ b/infrastructure/road.odin @@ -0,0 +1,13 @@ +package infrastructure + +Road :: struct { + id: u32, + nodes: [2]u32, +} + +road_init :: proc(new_id: u32, start: u32, end: u32) -> Road { + return { + id = new_id, + nodes = {start, end} + } +} \ No newline at end of file diff --git a/main.odin b/main.odin new file mode 100644 index 0000000..15becb3 --- /dev/null +++ b/main.odin @@ -0,0 +1,27 @@ +package main + +import rl "vendor:raylib" + +import "common" + +main :: proc() { + rl.SetConfigFlags({.MSAA_4X_HINT, .WINDOW_HIGHDPI}) + rl.InitWindow(common.WIDTH, common.HEIGHT, "Base Road Network") + defer rl.CloseWindow() + + rl.SetWindowMonitor(common.MONITOR) + rl.SetTargetFPS(rl.GetMonitorRefreshRate(common.MONITOR)) + + sim := init() + defer deinit(&sim) + + for !rl.WindowShouldClose() { + rl.BeginDrawing() + defer rl.EndDrawing() + + pos := rl.GetMousePosition() + handle_input(&sim, pos) + + draw(&sim, pos) + } +} \ No newline at end of file diff --git a/simulator.odin b/simulator.odin new file mode 100644 index 0000000..307c2a4 --- /dev/null +++ b/simulator.odin @@ -0,0 +1,105 @@ +package main + +import "base:intrinsics" +import rl "vendor:raylib" + +import "common" +import inf "infrastructure" + +Simulator :: struct { + nodes: [dynamic]inf.Node, + roads: [dynamic]inf.Road, + temp_node_index: Maybe(u32), +} + +init :: proc() -> Simulator { + return { + nodes = nil, + roads = nil, + temp_node_index = nil, + } +} + +deinit :: proc(self: ^Simulator) { + self.temp_node_index = nil + delete(self.roads) + delete(self.nodes) +} + +handle_input :: proc(self: ^Simulator, pos: rl.Vector2) { + handle_keyboard_input(self) + handle_mouse_input(self, pos) +} + +@(private="file") +handle_keyboard_input :: proc(self: ^Simulator) { + if (rl.IsKeyReleased(.C)) { + self.temp_node_index = nil + clear(&self.roads) + clear(&self.nodes) + } +} + +@(private="file") +handle_mouse_input :: proc(self: ^Simulator, pos: rl.Vector2) { + if (rl.IsMouseButtonReleased(.LEFT)) do left_click_event(self, pos) +} + +@(private="file") +left_click_event :: proc(self: ^Simulator, pos: rl.Vector2) { + cur_node_index := get_selected_node(self, pos) + + if val, ok := self.temp_node_index.?; ok { + road := inf.road_init(get_new_id(self, self.roads[:]), val, cur_node_index) + append(&self.roads, road) + + self.temp_node_index = nil + return + } + + self.temp_node_index = cur_node_index +} + +draw :: proc(self: ^Simulator, pos: rl.Vector2) { + rl.ClearBackground(common.BACKGROUND_COLOUR) + + for &road in self.roads { + start := road.nodes[0] + end := road.nodes[1] + + rl.DrawLineEx(self.nodes[start].pos, self.nodes[end].pos, common.ROAD_SIZE, common.ROAD_COLOUR) + } + + for &node in self.nodes { + rl.DrawCircleV(node.pos, common.NODE_RADIUS, common.NODE_DONE_COLOUR) + } + + // draw temp road if exists + if val, ok := self.temp_node_index.?; ok { + rl.DrawLineEx(self.nodes[val].pos, pos, common.ROAD_SIZE, common.ROAD_COLOUR) + } +} + +// Given position, the function will attempt the return the pointer to the node in near vicinity, +// or if unsuccesful manually creating the node based on the position in the list and then returning the pointer to it +@(private="file") +get_selected_node :: proc(self: ^Simulator, pos: rl.Vector2) -> u32 { + for node, index in self.nodes { + if (!rl.CheckCollisionPointCircle(pos, node.pos, common.NODE_SNAP_RADIUS * common.NODE_RADIUS)) do continue + + return u32(index) + } + + node := inf.node_init(get_new_id(self, self.nodes[:]), pos) + append(&self.nodes, node) + + return u32(len(self.nodes) - 1) +} + +@(private="file") +get_new_id :: proc(self: ^Simulator, man: []$T) -> u32 +where intrinsics.type_field_type(T, "id") == u32 { + mlen := len(man) + + return mlen == 0 ? 0 : man[mlen - 1].id + 1 +} \ No newline at end of file