Setting up groundwork for vehicle (car) and pathfinding implementation

This commit is contained in:
2026-04-26 16:53:46 +02:00
parent ae5e68e2a5
commit 533b6b1c00
8 changed files with 131 additions and 24 deletions

View File

@@ -32,3 +32,14 @@ NODE_CURSOR_COLOUR :: rl.BLUE
NODE_SNAP_COLOUR :: rl.PINK
// The default colour of the text displayed
TEXT_COLOUR :: rl.BLACK
// Default car colour
CAR_COLOUR :: rl.BLUE
// Speed limit for majority of roads
DEFAULT_SPEED_LIMIT :: 60
// Maximum fuel level
FUEL_MAX :: 100
// Maximum car speed
CAR_MAX_SPEED :: 240
CAR_HEIGHT :: 20
CAR_WIDTH :: 2 * CAR_HEIGHT

View File

@@ -14,4 +14,5 @@ Intersection_Data :: struct {
Entity :: enum {
Node,
Road,
Car,
}

View File

@@ -9,7 +9,16 @@ import "common"
draw :: proc(self: ^Simulator, pos: rl.Vector2) {
rl.ClearBackground(rl.LIGHTGRAY)
// draw roads
draw_roads(self)
draw_nodes(self)
draw_cars(self)
draw_temp_road(self, pos)
draw_ui(self)
}
@(private="file")
draw_roads :: proc(self: ^Simulator) {
for &road, index in self.roads {
start := road.nodes[0]
end := road.nodes[1]
@@ -21,24 +30,44 @@ draw :: proc(self: ^Simulator, pos: rl.Vector2) {
rl.DrawLineEx(self.nodes[start].pos, self.nodes[end].pos, common.ROAD_SIZE, road_colour)
}
}
// draw nodes
@(private="file")
draw_nodes :: proc(self: ^Simulator) {
for &node in self.nodes {
// draws the snapping radius if key is held down
if self.show_details do rl.DrawCircleV(node.pos, common.NODE_SNAP_RADIUS * common.NODE_RADIUS, common.NODE_SNAP_COLOUR)
// draws the node
rl.DrawCircleV(node.pos, common.NODE_RADIUS, common.NODE_DONE_COLOUR)
}
}
// draw temp road if exists
if val, ok := self.temp_node.?; ok {
rl.DrawLineEx(self.nodes[val].pos, pos, common.ROAD_SIZE, common.ROAD_COLOUR)
@(private="file")
draw_cars :: proc(self: ^Simulator) {
for &car in self.cars {
pos := self.nodes[car.origin].pos
rl.DrawCircleV(self.nodes[val].pos, common.NODE_RADIUS, common.NODE_BUILD_COLOUR)
rl.DrawCircleV(pos, common.NODE_RADIUS, common.NODE_CURSOR_COLOUR)
rect := rl.Rectangle {
x = pos.x,
y = pos.y,
width = common.CAR_WIDTH,
height = common.CAR_HEIGHT
}
rl.DrawRectangleRec(rect, common.CAR_COLOUR)
}
}
draw_ui(self)
@(private="file")
draw_temp_road :: proc(self: ^Simulator, pos: rl.Vector2) {
// draw temp road if exists
val, ok := self.temp_node.?
if !ok do return
rl.DrawLineEx(self.nodes[val].pos, pos, common.ROAD_SIZE, common.ROAD_COLOUR)
rl.DrawCircleV(self.nodes[val].pos, common.NODE_RADIUS, common.NODE_BUILD_COLOUR)
rl.DrawCircleV(pos, common.NODE_RADIUS, common.NODE_CURSOR_COLOUR)
}
// Drawing UI text, mostly for debugging purposes

View File

@@ -1,14 +1,18 @@
package infrastructure
import "../common"
Road :: struct {
// Index to nodes that limit the road
nodes: [2]u32,
speed_limit: u8,
}
// Road Initialisation
road_init :: proc(start: u32, end: u32) -> Road {
return {
nodes = {start, end}
nodes = {start, end},
speed_limit = common.DEFAULT_SPEED_LIMIT,
}
}

View File

@@ -4,6 +4,7 @@ import rl "vendor:raylib"
import "common"
import inf "infrastructure"
import v "vehicles"
// This function only returns the index to the node or if it doesn't exist bool in the tuple is false
@private
@@ -62,6 +63,8 @@ delete_entity :: proc(self: ^Simulator, entity_index: u32, type: common.Entity)
mlen = u32(len(self.nodes))
case .Road:
mlen = u32(len(self.roads))
case .Car:
mlen = u32(len(self.cars))
}
last := mlen - 1
@@ -76,14 +79,19 @@ delete_entity :: proc(self: ^Simulator, entity_index: u32, type: common.Entity)
if !swap_made do return {}, false
for &road in self.roads do inf.road_update_node_reference(&road, index_change[0], index_change[1])
for &car in self.cars do v.car_update_node_reference(&car, index_change[0], index_change[1])
return index_change, true
case .Road:
unordered_remove(&self.roads, entity_index)
if !swap_made do return {}, false
for &node in self.nodes do inf.node_update_road_reference(&node, index_change[0], index_change[1])
return index_change, true
case .Car:
unordered_remove(&self.cars, entity_index)
if !swap_made do return {}, false
// So far NOT needed as we don't reference the cars anywhere YET
// In the future this might be the cause for failure!!!
}
return {}, false

View File

@@ -3,6 +3,7 @@ package main
import rl "vendor:raylib"
import inf "infrastructure"
import v "vehicles"
// Public input function that gets called in graphics library loop
handle_input :: proc(self: ^Simulator, pos: rl.Vector2) {
@@ -11,21 +12,26 @@ handle_input :: proc(self: ^Simulator, pos: rl.Vector2) {
}
// General keyboard input event handler
@(private="file")
@(private = "file")
handle_keyboard_input :: proc(self: ^Simulator) {
self.show_details = rl.IsKeyDown(.LEFT_ALT)
self.auto_continue = rl.IsKeyDown(.LEFT_CONTROL)
self.delete_mode = rl.IsKeyDown(.LEFT_SHIFT)
if (rl.IsKeyReleased(.C)) {
if rl.IsKeyReleased(.C) {
self.temp_node = nil
clear(&self.roads)
clear(&self.nodes)
}
if !rl.IsKeyReleased(.N) || len(self.nodes) == 0 do return
car := v.car_init(u32(len(self.nodes)))
append(&self.cars, car)
}
// Generally mouse event handler
@(private="file")
@(private = "file")
handle_mouse_input :: proc(self: ^Simulator, pos: rl.Vector2) {
if (rl.IsMouseButtonReleased(.LEFT)) {
left_click_event(self, pos)
@@ -35,7 +41,7 @@ handle_mouse_input :: proc(self: ^Simulator, pos: rl.Vector2) {
}
// Handles left click functionality
@(private="file")
@(private = "file")
left_click_event :: proc(self: ^Simulator, pos: rl.Vector2) {
if self.delete_mode {
if road, ok := self.highlighted_road.?; ok do delete_road(self, road)
@@ -46,7 +52,7 @@ left_click_event :: proc(self: ^Simulator, pos: rl.Vector2) {
}
// Handles right click functionality
@(private="file")
@(private = "file")
right_click_event :: proc(self: ^Simulator) {
if self.temp_node == nil do return
index := self.temp_node.?

View File

@@ -5,12 +5,15 @@ import rl "vendor:raylib"
import "common"
import "core:fmt"
import inf "infrastructure"
import v "vehicles"
Simulator :: struct {
// Stores all nodes
nodes: [dynamic]inf.Node,
// Stores all roads
roads: [dynamic]inf.Road,
// Stores all cars
cars: [dynamic]v.Car,
// Tracks the temporary node location
temp_node: Maybe(u32),
// Tracks the selected road
@@ -34,6 +37,7 @@ deinit :: proc(self: ^Simulator) {
inf.node_deinit(&node)
}
delete(self.nodes)
delete(self.cars)
}
// Functionality that runs every tick regardless of input

44
src/vehicles/car.odin Normal file
View File

@@ -0,0 +1,44 @@
package vehicles
import "core:math/rand"
import "../common"
Car :: struct {
// Fuel level 0-100%
fuel_level: u8,
// Vehicle's maximum speed
max_speed: u8,
// Pathfinding
// Car's origin node
origin: u32,
// Car's destination node
destination: Maybe(u32),
}
// Constructor
car_init :: proc(nodes_len: u32) -> Car {
return {
fuel_level = common.FUEL_MAX,
max_speed = common.CAR_MAX_SPEED,
origin = rand.uint32_max(nodes_len)
}
}
// Sets a (valid) route for the car
//
// Does NOT guarantee the route is reachable (TODO?)
car_set_route :: proc(self: ^Car, nodes_len: u32) {
for self.origin == self.destination {
self.destination = rand.uint32_max(nodes_len)
}
}
// Updates (origin and destination) node reference
car_update_node_reference :: proc(self: ^Car, old_ref: u32, new_ref: u32) {
if self.origin == old_ref do self.origin = new_ref
if self.destination == old_ref do self.destination = new_ref
}