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.
.dependencies = .{
.raylib_zig = .{
.url = "git+https://github.com/Not-Nik/raylib-zig?ref=devel#5013830647196ba938a3a25a36b8245606e9a9cd",
.hash = "raylib_zig-5.6.0-dev-KE8REM0tBQAHVn9Xjqlgu9l1qgfTmP8aJa1kLhD584bV",
.url = "git+https://github.com/Not-Nik/raylib-zig?ref=devel#e8167c2e560402510220fdf419afbc7c2fc51e5d",
.hash = "raylib_zig-5.6.0-dev-KE8REIouBQDbt-DSHmhUc9YFjJ_pFUS5tvRDPnBzsMdU",
},
},
.paths = .{

View File

@ -7,15 +7,19 @@ const car = @import("../car/car.zig");
pub const SpawnArea = struct {
area: structures.AreaLocation,
location: rl.Vector2,
calculated: rl.Vector2, // x/y + width/height * scale
width: i32,
height: i32,
red_line_th: f32,
pub fn init(area_loc: structures.AreaLocation) SpawnArea {
var new_spawn = SpawnArea{
.area = area_loc,
.location = undefined,
.calculated = undefined,
.width = 100,
.height = 50,
.red_line_th = 5.0,
};
// 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_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 {
@ -49,53 +56,9 @@ pub const SpawnArea = struct {
};
rl.drawRectangleRec(rect, .dark_gray);
// red line for hover
if (self.checkHover()) {
var start_pos: rl.Vector2 = undefined;
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);
}
// TODO implement a functionality which will prevent user from drawing near corner
// when the corner is touched road will not follow through it will be forbiden
// and the corner rectangle will start shining
}
fn checkHover(self: *const SpawnArea) bool {

View File

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

View File

@ -19,16 +19,14 @@ pub fn main() !void {
rl.setExitKey(.null);
rl.maximizeWindow();
rl.setTargetFPS(60);
rl.setTargetFPS(144);
var area_manager = try areas.Areas.init(allocator, 4);
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();
try road_manager.addAreaNodes(area_manager);
while (!rl.windowShouldClose()) {
rl.beginDrawing();
defer rl.endDrawing();
@ -40,8 +38,7 @@ pub fn main() !void {
globals.setWindowSize(new_width, new_height);
area_manager.recalculate();
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);

View File

@ -16,9 +16,14 @@ pub const RoadManager = struct {
mode: str.InputMode,
selected_road: ?usize,
min_distance: f32,
area_num: usize,
temp_nodes: bool,
pub fn init(allocator: std.mem.Allocator) RoadManager {
return RoadManager{
pub fn init(allocator: std.mem.Allocator, areas: area_str.Areas) !RoadManager {
const nodes = try areas.getNodes();
defer allocator.free(nodes);
var road_man = RoadManager{
.buffer = road_data.road_thickness * globals.getScale() / 2.0,
.allocator = allocator,
.roads = std.ArrayList(road_str.Road).init(allocator),
@ -26,7 +31,12 @@ pub const RoadManager = struct {
.mode = str.InputMode.normal,
.selected_road = null,
.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 {
@ -40,13 +50,16 @@ 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) {
// TODO RETURN IF MOUSE IS IN AREA!!! (we can only draw roads from there)
// 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;
// only if close enough to node AND TODO THEN FORCE THE FUCKER TO SNAP ON IT
self.roads.items[last_id.?].confirmRoad(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));
@ -66,9 +79,7 @@ pub const RoadManager = struct {
fn clearRoads(self: *RoadManager) void {
self.roads.clearAndFree();
// todo this will delete spawn nodes which MUST NOT be deleted
// fix at earliest convinience
self.nodes.clearAndFree();
self.nodes.shrinkAndFree(self.area_num);
}
fn trackRoad(self: *RoadManager, pos: rl.Vector2) void {
@ -124,13 +135,19 @@ pub const RoadManager = struct {
fn handleKeyboardInput(self: *RoadManager) void {
// keyboard inputs
if (rl.isKeyReleased(.c)) {
if (rl.isKeyReleased(.b)) {
self.toggleMode(str.InputMode.build);
} else if (rl.isKeyReleased(.c)) {
self.clearRoads();
} else if (rl.isKeyReleased(.d)) {
self.toggleDeleteMode();
self.toggleMode(str.InputMode.delete);
} 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 {
@ -144,7 +161,7 @@ pub const RoadManager = struct {
fn handleLeftClick(self: *RoadManager) !void {
switch (self.mode) {
.normal => try self.addRoad(rl.getMousePosition()),
.normal => {},
.delete => {
if (self.selected_road == null) return;
@ -152,27 +169,15 @@ pub const RoadManager = struct {
self.selected_road = null;
},
.node => {},
.build => try self.addRoad(rl.getMousePosition()),
}
}
fn toggleDeleteMode(self: *RoadManager) void {
fn toggleMode(self: *RoadManager, mode: str.InputMode) void {
self.togglePrepare();
self.mode = switch (self.mode) {
.normal => str.InputMode.delete,
.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,
};
if (self.mode != mode)
self.mode = mode;
}
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| {
node.draw(true);
}
}
pub fn drawMode(self: *const RoadManager) !void {
const prefix = "Mode: ";
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);
// draw funct
rl.drawText(full_text, @divTrunc(globals.getScreenWidth(), 3), 50, 100, .dark_blue);
}
@ -239,13 +242,6 @@ pub const RoadManager = struct {
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 {
const nodes = try areas.getNodes();
defer self.allocator.free(nodes);

View File

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