From 6b39998232a99e67287d6ef35fb182ead35e5297 Mon Sep 17 00:00:00 2001 From: Marto Date: Mon, 21 Jul 2025 23:56:21 +0200 Subject: [PATCH] improved road implementation --- src/main.zig | 2 +- src/road/node.zig | 6 +-- src/road/road-manager.zig | 78 ++++++++++++++++++++++++++++++++++----- src/road/road.zig | 17 +++++++-- src/structures.zig | 8 ++++ 5 files changed, 92 insertions(+), 19 deletions(-) diff --git a/src/main.zig b/src/main.zig index 49bfb45..cadac49 100644 --- a/src/main.zig +++ b/src/main.zig @@ -40,8 +40,8 @@ pub fn main() !void { rl.clearBackground(.light_gray); - area_manager.draw(); try road_manager.inputHandler(); + area_manager.draw(); road_manager.draw(); } } diff --git a/src/road/node.zig b/src/road/node.zig index bb7b17d..ec8be9a 100644 --- a/src/road/node.zig +++ b/src/road/node.zig @@ -7,15 +7,13 @@ const globals = @import("../globals.zig"); pub const Node = struct { // road_sections: std.ArrayList(road_str.Road), location: rl.Vector2, - // todo validation - pub fn init(location: rl.Vector2) Node { return Node{ .location = location, }; } - pub fn draw(self: *const Node) void { - rl.drawCircleV(self.location, globals.getScale() * road_data.road_thickness / @sqrt(2), .green); + pub fn draw(self: *const Node, valid: bool) void { + rl.drawCircleV(self.location, globals.getScale() * road_data.road_thickness / @sqrt(2.0), if (valid) .green else .red); } }; diff --git a/src/road/road-manager.zig b/src/road/road-manager.zig index b004c73..d5f9cc8 100644 --- a/src/road/road-manager.zig +++ b/src/road/road-manager.zig @@ -13,6 +13,7 @@ pub const RoadManager = struct { nodes: std.ArrayList(node_str.Node), mode: str.InputMode, selected_road: ?usize, + min_distance: f32, pub fn init(allocator: std.mem.Allocator) RoadManager { return RoadManager{ @@ -22,6 +23,7 @@ pub const RoadManager = struct { .nodes = std.ArrayList(node_str.Node).init(allocator), .mode = str.InputMode.normal, .selected_road = null, + .min_distance = globals.getScreenWidthF32() / 25.0, }; } @@ -36,18 +38,33 @@ pub const RoadManager = struct { // if last road exists and is not fully built if (last_id != null and self.roads.items[last_id.?].end_point == null) { + // only confirm if distance is minimal or greater + if (rl.Vector2.distance(self.roads.items[last_id.?].start_point, pos) < self.min_distance) return; + // if too close to existing nodes, return + if (!self.canCreateNode(pos)) return; + self.roads.items[last_id.?].confirmRoad(pos); + try self.nodes.append(node_str.Node.init(pos)); } try self.roads.append(road_str.Road.init(pos)); } + fn canCreateNode(self: *const RoadManager, pos: rl.Vector2) bool { + for (self.nodes.items) |node| { + if (rl.Vector2.distance(node.location, pos) < self.min_distance) return false; + } + + return true; + } + fn removeLastRoad(self: *RoadManager) void { _ = self.roads.pop(); } fn clearRoads(self: *RoadManager) void { self.roads.clearAndFree(); + self.nodes.clearAndFree(); } fn trackRoad(self: *RoadManager, pos: rl.Vector2) void { @@ -58,11 +75,9 @@ pub const RoadManager = struct { for (0..self.roads.items.len) |i| { // we skip because that road is not complete if (self.roads.items[i].end_point == null) continue; - std.debug.print("tracking pt1\n", .{}); if (self.cursorOnRoad(self.roads.items[i].start_point, pos, self.roads.items[i].end_point.?)) { - std.debug.print("tracking pt2", .{}); - self.roads.items[i].setColor(true); + self.roads.items[i].setColor(str.RoadState.selected); self.selected_road = i; break; } @@ -109,6 +124,8 @@ pub const RoadManager = struct { self.clearRoads(); } else if (rl.isKeyReleased(.d)) { self.toggleDeleteMode(); + } else if (rl.isKeyReleased(.n)) { + self.toggleNodeMode(); } } @@ -135,21 +152,62 @@ pub const RoadManager = struct { } fn toggleDeleteMode(self: *RoadManager) void { - if (self.mode == str.InputMode.delete) { - self.resetTrackedRoad(); - } + self.togglePrepare(); - self.cancelRoadbuilding(); self.mode = switch (self.mode) { .normal => str.InputMode.delete, .delete => str.InputMode.normal, - .node => self.mode, + .node => str.InputMode.delete, }; } + fn toggleNodeMode(self: *RoadManager) void { + self.togglePrepare(); + + self.mode = switch (self.mode) { + .normal => str.InputMode.node, + .delete => str.InputMode.node, + .node => str.InputMode.normal, + }; + } + + fn togglePrepare(self: *RoadManager) void { + if (self.mode == str.InputMode.delete) { + self.resetTrackedRoad(); + } + self.cancelRoadbuilding(); + } + pub fn draw(self: *const RoadManager) void { - for (self.roads.items) |road| { + // if last road is being drawn display node marker + const last = self.roads.getLastOrNull(); + const pos = rl.getMousePosition(); + const marker_node = node_str.Node.init(pos); + var draw_marker = false; + + if (last != null and last.?.end_point == null) { + draw_marker = true; + + // might have to put it in one function because we check this two times + // todo canCreateNode and canCreateRoad should be part of node/road struct + if (rl.Vector2.distance(last.?.start_point, pos) < self.min_distance) { + draw_marker = false; + self.roads.items[self.getLastIndex().?].setColor(str.RoadState.illegal); + } + } + + for (self.roads.items) |*road| { road.draw(); + if (!road.isValid()) + road.*.setColor(str.RoadState.valid); + } + + if (draw_marker) + marker_node.draw(self.canCreateNode(pos)); + + if (self.mode != str.InputMode.node) return; + for (self.nodes.items) |node| { + node.draw(true); } } @@ -160,7 +218,7 @@ pub const RoadManager = struct { fn setDefaultColour(self: *RoadManager) void { for (self.roads.items) |*road| { - road.setColor(false); + road.setColor(str.RoadState.valid); } } diff --git a/src/road/road.zig b/src/road/road.zig index 2784b38..ae28cd9 100644 --- a/src/road/road.zig +++ b/src/road/road.zig @@ -1,17 +1,18 @@ const rl = @import("raylib"); const globals = @import("../globals.zig"); const road_data = @import("road-data.zig"); +const str = @import("../structures.zig"); pub const Road = struct { - color: rl.Color, start_point: rl.Vector2, end_point: ?rl.Vector2, + color: rl.Color, pub fn init(pos: rl.Vector2) Road { return Road{ - .color = .black, .start_point = pos, .end_point = null, + .color = .black, }; } @@ -19,8 +20,16 @@ pub const Road = struct { self.end_point = pos; } - pub fn setColor(self: *Road, is_selected: bool) void { - self.color = if (is_selected) .green else .black; + pub fn setColor(self: *Road, road_state: str.RoadState) void { + self.color = switch (road_state) { + .valid => .black, + .illegal => .red, + .selected => .green, + }; + } + + pub fn isValid(self: *const Road) bool { + return rl.colorIsEqual(self.color, rl.Color.black); } pub fn draw(self: *const Road) void { diff --git a/src/structures.zig b/src/structures.zig index 8c6a988..bc2bb62 100644 --- a/src/structures.zig +++ b/src/structures.zig @@ -1,3 +1,4 @@ +// valid area states pub const AreaLocation = enum { top_left, top_right, @@ -5,8 +6,15 @@ pub const AreaLocation = enum { bottom_right, }; +// road modes pub const InputMode = enum { normal, delete, node, }; + +pub const RoadState = enum { + valid, + illegal, + selected, +};