improved roadbuilding

This commit is contained in:
Martin Vrhovšek 2025-07-23 21:38:55 +02:00
parent 147202d053
commit 2e8ebaa737
6 changed files with 51 additions and 94 deletions

View File

@ -37,8 +37,8 @@
// internet connectivity. // internet connectivity.
.dependencies = .{ .dependencies = .{
.raylib_zig = .{ .raylib_zig = .{
.url = "git+https://github.com/Not-Nik/raylib-zig?ref=devel#5013830647196ba938a3a25a36b8245606e9a9cd", .url = "git+https://github.com/Not-Nik/raylib-zig?ref=devel#e8167c2e560402510220fdf419afbc7c2fc51e5d",
.hash = "raylib_zig-5.6.0-dev-KE8REM0tBQAHVn9Xjqlgu9l1qgfTmP8aJa1kLhD584bV", .hash = "raylib_zig-5.6.0-dev-KE8REIouBQDbt-DSHmhUc9YFjJ_pFUS5tvRDPnBzsMdU",
}, },
}, },
.paths = .{ .paths = .{

View File

@ -7,15 +7,19 @@ const car = @import("../car/car.zig");
pub const SpawnArea = struct { pub const SpawnArea = struct {
area: structures.AreaLocation, area: structures.AreaLocation,
location: rl.Vector2, location: rl.Vector2,
calculated: rl.Vector2, // x/y + width/height * scale
width: i32, width: i32,
height: i32, height: i32,
red_line_th: f32,
pub fn init(area_loc: structures.AreaLocation) SpawnArea { pub fn init(area_loc: structures.AreaLocation) SpawnArea {
var new_spawn = SpawnArea{ var new_spawn = SpawnArea{
.area = area_loc, .area = area_loc,
.location = undefined, .location = undefined,
.calculated = undefined,
.width = 100, .width = 100,
.height = 50, .height = 50,
.red_line_th = 5.0,
}; };
// need to do this outside the "constructor", as the function needs reference to self // need to do this outside the "constructor", as the function needs reference to self
@ -31,6 +35,9 @@ pub const SpawnArea = struct {
.bottom_left => rl.Vector2{ .x = 0, .y = globals.getScreenHeightF32() - @as(f32, @floatFromInt(self.height)) * globals.getScale() }, .bottom_left => rl.Vector2{ .x = 0, .y = globals.getScreenHeightF32() - @as(f32, @floatFromInt(self.height)) * globals.getScale() },
.bottom_right => rl.Vector2{ .x = globals.getScreenWidthF32() - @as(f32, @floatFromInt(self.width)) * globals.getScale(), .y = globals.getScreenHeightF32() - @as(f32, @floatFromInt(self.height)) * globals.getScale() }, .bottom_right => rl.Vector2{ .x = globals.getScreenWidthF32() - @as(f32, @floatFromInt(self.width)) * globals.getScale(), .y = globals.getScreenHeightF32() - @as(f32, @floatFromInt(self.height)) * globals.getScale() },
}; };
self.calculated.x = self.location.x + intToFloat(self.width) * globals.getScale();
self.calculated.y = self.location.y + intToFloat(self.height) * globals.getScale();
} }
pub fn getNodeLocation(self: *const SpawnArea) rl.Vector2 { pub fn getNodeLocation(self: *const SpawnArea) rl.Vector2 {
@ -49,53 +56,9 @@ pub const SpawnArea = struct {
}; };
rl.drawRectangleRec(rect, .dark_gray); rl.drawRectangleRec(rect, .dark_gray);
// red line for hover // TODO implement a functionality which will prevent user from drawing near corner
if (self.checkHover()) { // when the corner is touched road will not follow through it will be forbiden
var start_pos: rl.Vector2 = undefined; // and the corner rectangle will start shining
var end_pos: rl.Vector2 = undefined;
// this should be precalculated shouldn't need do calculate this each time just for the sake of drawing it
switch (self.area) {
.top_left => {
start_pos = rl.Vector2 {
.x = self.location.x + intToFloat(self.width) * globals.getScale(),
.y = self.location.y,
};
end_pos = rl.Vector2 {
.x = start_pos.x,
.y = self.location.y + intToFloat(self.height) * globals.getScale(),
};
},
.top_right => {
start_pos = self.location;
end_pos = rl.Vector2 {
.x = start_pos.x,
.y = self.location.y + intToFloat(self.height) * globals.getScale(),
};
},
.bottom_left => {
start_pos = rl.Vector2 {
.x = self.location.x + intToFloat(self.width) * globals.getScale(),
.y = self.location.y,
};
end_pos = rl.Vector2 {
.x = start_pos.x,
.y = self.location.y + intToFloat(self.height) * globals.getScale(),
};
},
.bottom_right => {
start_pos = self.location;
end_pos = rl.Vector2 {
.x = start_pos.x,
.y = self.location.y + intToFloat(self.height) * globals.getScale(),
};
}
}
rl.drawLineEx(start_pos, end_pos, 5, .red);
}
} }
fn checkHover(self: *const SpawnArea) bool { fn checkHover(self: *const SpawnArea) bool {

View File

@ -16,7 +16,7 @@ pub const Car = struct {
.parent_location = parent_location, .parent_location = parent_location,
.cell = cells, .cell = cells,
.slot = slots, .slot = slots,
// destination (will be described as an area the car will try to reach)
.location = undefined, .location = undefined,
.color = .dark_blue, .color = .dark_blue,
.fuel = 100.0, .fuel = 100.0,

View File

@ -19,16 +19,14 @@ pub fn main() !void {
rl.setExitKey(.null); rl.setExitKey(.null);
rl.maximizeWindow(); rl.maximizeWindow();
rl.setTargetFPS(60); rl.setTargetFPS(144);
var area_manager = try areas.Areas.init(allocator, 4); var area_manager = try areas.Areas.init(allocator, 4);
defer area_manager.deinit(); defer area_manager.deinit();
var road_manager = roadman_str.RoadManager.init(allocator); var road_manager = try roadman_str.RoadManager.init(allocator, area_manager);
defer road_manager.deinit(); defer road_manager.deinit();
try road_manager.addAreaNodes(area_manager);
while (!rl.windowShouldClose()) { while (!rl.windowShouldClose()) {
rl.beginDrawing(); rl.beginDrawing();
defer rl.endDrawing(); defer rl.endDrawing();
@ -40,8 +38,7 @@ pub fn main() !void {
globals.setWindowSize(new_width, new_height); globals.setWindowSize(new_width, new_height);
area_manager.recalculate(); area_manager.recalculate();
try road_manager.updateAreaNodes(area_manager); try road_manager.updateAreaNodes(area_manager);
// todo this will bring some trouble because what if other nodes/roads, when resized, will be placed upon existing spawn area // this will bring some trouble because what if other nodes/roads, when resized, will be placed upon existing spawn area
} }
rl.clearBackground(.light_gray); rl.clearBackground(.light_gray);

View File

@ -16,9 +16,14 @@ pub const RoadManager = struct {
mode: str.InputMode, mode: str.InputMode,
selected_road: ?usize, selected_road: ?usize,
min_distance: f32, min_distance: f32,
area_num: usize,
temp_nodes: bool,
pub fn init(allocator: std.mem.Allocator) RoadManager { pub fn init(allocator: std.mem.Allocator, areas: area_str.Areas) !RoadManager {
return RoadManager{ const nodes = try areas.getNodes();
defer allocator.free(nodes);
var road_man = RoadManager{
.buffer = road_data.road_thickness * globals.getScale() / 2.0, .buffer = road_data.road_thickness * globals.getScale() / 2.0,
.allocator = allocator, .allocator = allocator,
.roads = std.ArrayList(road_str.Road).init(allocator), .roads = std.ArrayList(road_str.Road).init(allocator),
@ -26,7 +31,12 @@ pub const RoadManager = struct {
.mode = str.InputMode.normal, .mode = str.InputMode.normal,
.selected_road = null, .selected_road = null,
.min_distance = globals.getScreenWidthF32() / 25.0, .min_distance = globals.getScreenWidthF32() / 25.0,
.area_num = nodes.len,
.temp_nodes = false,
}; };
try road_man.nodes.appendSlice(nodes);
return road_man;
} }
pub fn deinit(self: *RoadManager) void { pub fn deinit(self: *RoadManager) void {
@ -40,13 +50,16 @@ pub const RoadManager = struct {
// if last road exists and is not fully built // if last road exists and is not fully built
if (last_id != null and self.roads.items[last_id.?].end_point == null) { if (last_id != null and self.roads.items[last_id.?].end_point == null) {
// TODO RETURN IF MOUSE IS IN AREA!!! (we can only draw roads from there)
// only confirm if distance is minimal or greater // 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 (rl.Vector2.distance(self.roads.items[last_id.?].start_point, pos) < self.min_distance) return;
// if too close to existing nodes, return // only if close enough to node AND TODO THEN FORCE THE FUCKER TO SNAP ON IT
if (!self.canCreateNode(pos)) return;
self.roads.items[last_id.?].confirmRoad(pos); self.roads.items[last_id.?].confirmRoad(pos);
try self.nodes.append(node_str.Node.init(pos)); try self.nodes.append(node_str.Node.init(pos));
} else if (self.canCreateNode(pos)) {
return;
} }
try self.roads.append(road_str.Road.init(pos)); try self.roads.append(road_str.Road.init(pos));
@ -66,9 +79,7 @@ pub const RoadManager = struct {
fn clearRoads(self: *RoadManager) void { fn clearRoads(self: *RoadManager) void {
self.roads.clearAndFree(); self.roads.clearAndFree();
// todo this will delete spawn nodes which MUST NOT be deleted self.nodes.shrinkAndFree(self.area_num);
// fix at earliest convinience
self.nodes.clearAndFree();
} }
fn trackRoad(self: *RoadManager, pos: rl.Vector2) void { fn trackRoad(self: *RoadManager, pos: rl.Vector2) void {
@ -124,13 +135,19 @@ pub const RoadManager = struct {
fn handleKeyboardInput(self: *RoadManager) void { fn handleKeyboardInput(self: *RoadManager) void {
// keyboard inputs // keyboard inputs
if (rl.isKeyReleased(.c)) { if (rl.isKeyReleased(.b)) {
self.toggleMode(str.InputMode.build);
} else if (rl.isKeyReleased(.c)) {
self.clearRoads(); self.clearRoads();
} else if (rl.isKeyReleased(.d)) { } else if (rl.isKeyReleased(.d)) {
self.toggleDeleteMode(); self.toggleMode(str.InputMode.delete);
} else if (rl.isKeyReleased(.n)) { } else if (rl.isKeyReleased(.n)) {
self.toggleNodeMode(); self.toggleMode(str.InputMode.node);
} else if (rl.isKeyReleased(.escape)) {
self.toggleMode(str.InputMode.normal);
} }
self.temp_nodes = rl.isKeyDown(.left_control);
} }
fn handleMouseInput(self: *RoadManager) !void { fn handleMouseInput(self: *RoadManager) !void {
@ -144,7 +161,7 @@ pub const RoadManager = struct {
fn handleLeftClick(self: *RoadManager) !void { fn handleLeftClick(self: *RoadManager) !void {
switch (self.mode) { switch (self.mode) {
.normal => try self.addRoad(rl.getMousePosition()), .normal => {},
.delete => { .delete => {
if (self.selected_road == null) return; if (self.selected_road == null) return;
@ -152,27 +169,15 @@ pub const RoadManager = struct {
self.selected_road = null; self.selected_road = null;
}, },
.node => {}, .node => {},
.build => try self.addRoad(rl.getMousePosition()),
} }
} }
fn toggleDeleteMode(self: *RoadManager) void { fn toggleMode(self: *RoadManager, mode: str.InputMode) void {
self.togglePrepare(); self.togglePrepare();
self.mode = switch (self.mode) { if (self.mode != mode)
.normal => str.InputMode.delete, self.mode = mode;
.delete => str.InputMode.normal,
.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 { fn togglePrepare(self: *RoadManager) void {
@ -207,19 +212,17 @@ pub const RoadManager = struct {
} }
} }
if (self.mode != str.InputMode.node) return; if (self.mode != str.InputMode.node and !self.temp_nodes) return;
for (self.nodes.items) |node| { for (self.nodes.items) |node| {
node.draw(true); node.draw(true);
} }
} }
pub fn drawMode(self: *const RoadManager) !void { pub fn drawMode(self: *const RoadManager) !void {
const prefix = "Mode: ";
const text = @tagName(self.mode); // this turns an enum into string like "normal" const text = @tagName(self.mode); // this turns an enum into string like "normal"
const full_text = try std.fmt.allocPrintZ(self.allocator, "{s}{s}", .{ prefix, text }); const full_text = try std.fmt.allocPrintZ(self.allocator, "Mode: {s}", .{text});
defer self.allocator.free(full_text); defer self.allocator.free(full_text);
// draw funct
rl.drawText(full_text, @divTrunc(globals.getScreenWidth(), 3), 50, 100, .dark_blue); rl.drawText(full_text, @divTrunc(globals.getScreenWidth(), 3), 50, 100, .dark_blue);
} }
@ -239,13 +242,6 @@ pub const RoadManager = struct {
self.selected_road = null; self.selected_road = null;
} }
pub fn addAreaNodes(self: *RoadManager, areas: area_str.Areas) !void {
const nodes = try areas.getNodes();
defer self.allocator.free(nodes);
try self.nodes.appendSlice(nodes);
}
pub fn updateAreaNodes(self: *RoadManager, areas: area_str.Areas) !void { pub fn updateAreaNodes(self: *RoadManager, areas: area_str.Areas) !void {
const nodes = try areas.getNodes(); const nodes = try areas.getNodes();
defer self.allocator.free(nodes); defer self.allocator.free(nodes);

View File

@ -11,6 +11,7 @@ pub const InputMode = enum {
normal, normal,
delete, delete,
node, node,
build,
}; };
pub const RoadState = enum { pub const RoadState = enum {