const std = @import("std"); const rl = @import("raylib"); const e = @import("../errors.zig"); const st = @import("../common/structures.zig"); const ut = @import("../common/utils.zig"); const Road = @import("road.zig").Road; const Node = @import("node.zig").Node; pub const RoadManager = struct { next_id: usize, roads: std.ArrayList(*Road), pub fn init() RoadManager { return .{ .next_id = 0, .roads = .empty, }; } /// Deinitialises every road (pointer) and then the list itself pub fn deinit(self: *RoadManager, allocator: std.mem.Allocator) void { for (self.roads.items) |road| { road.deinit(allocator); } self.roads.deinit(allocator); } /// Draws all the roads in the list, sends the information ahead whether the road drawn should be highlighted pub fn draw(self: *const RoadManager, highlighted_road: ?*Road, display_info: bool) void { for (self.roads.items) |road| { const is_highlighted = if (highlighted_road) |h_road| road == h_road else false; road.draw(is_highlighted, display_info); } } /// Function which creates the road object, its pointer, adds it to the list /// and then also references that same road to the bounding nodes pub fn addRoad(self: *RoadManager, allocator: std.mem.Allocator, start: *Node, end: *Node) !void { const road_ptr = try allocator.create(Road); road_ptr.* = Road.init(self.getNextID(), start, end); try self.roads.append(allocator, road_ptr); const ref = self.roads.items[self.roads.items.len - 1]; try start.referenceRoad(allocator, ref); try end.referenceRoad(allocator, ref); } /// Returns the id, and increases it by one; used for generating ID's for new entities fn getNextID(self: *RoadManager) usize { const id = self.next_id; self.next_id += 1; return id; } /// Deinits all the roads, clears them but not deiniting the list itself; also resets the next ID var pub fn clear(self: *RoadManager, allocator: std.mem.Allocator) void { for (self.roads.items) |road| { road.deinit(allocator); } self.roads.clearRetainingCapacity(); self.next_id = 0; } /// Removes the references of the road, from the nodes that bound that road /// /// Then it deinitialises the road and removes it from the list /// /// Will return an error if the road itself is not present in the list pub fn deleteRoad(self: *RoadManager, allocator: std.mem.Allocator, road_to_delete: *Road) !void { // unreference the road from its bounding functions road_to_delete.unreferenceNodes() catch |err| { std.debug.panic("Failed to unreference the road from its nodes: {}\n", .{err}); }; for (0..self.roads.items.len) |i| { if (self.roads.items[i] != road_to_delete) continue; road_to_delete.deinit(allocator); _ = self.roads.swapRemove(i); return; } return e.Entity.NotFound; } /// Returns if pos is pointing at a road, or null if it isn't at any pub fn getHighlightedRoad(self: *const RoadManager, pos: Vector2) ?*Road { for (self.roads.items) |road| { if (road.collides(pos)) return road; } return null; } }; const Vector2 = @import("raylib").Vector2; const expect = std.testing.expect; test "id tracking" { var gpa: std.heap.DebugAllocator(.{}) = .init; defer _ = gpa.deinit(); const allocator = gpa.allocator(); var road_man: RoadManager = .init(); defer road_man.deinit(allocator); const n = 5; const start: Node = .init(0, .{.x = 0, .y = 0}); const start_ptr = try allocator.create(Node); start_ptr.* = start; const end: Node = .init(1, .{.x = 100, .y = 100}); const end_ptr = try allocator.create(Node); end_ptr.* = end; defer { start_ptr.deinit(allocator); end_ptr.deinit(allocator); allocator.destroy(start_ptr); allocator.destroy(end_ptr); } for (0..n) |_| { try road_man.addRoad(allocator, start_ptr, end_ptr); } try expect(road_man.next_id == n); try expect(road_man.roads.items.len == n); } // TODO tests // force resize pointer test // add, remove road // destroy road and then verify the nodes do not have a pointer to it