improved road implementation

This commit is contained in:
Martin Vrhovšek 2025-07-21 23:56:21 +02:00
parent 2edda20571
commit 6b39998232
5 changed files with 92 additions and 19 deletions

View File

@ -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();
}
}

View File

@ -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);
}
};

View File

@ -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);
}
}

View File

@ -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 {

View File

@ -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,
};