Setting up groundwork for vehicle (car) and pathfinding implementation
This commit is contained in:
@@ -31,4 +31,15 @@ NODE_CURSOR_COLOUR :: rl.BLUE
|
||||
// The colour of the overlay displaying the snap radius
|
||||
NODE_SNAP_COLOUR :: rl.PINK
|
||||
// The default colour of the text displayed
|
||||
TEXT_COLOUR :: rl.BLACK
|
||||
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
|
||||
@@ -14,4 +14,5 @@ Intersection_Data :: struct {
|
||||
Entity :: enum {
|
||||
Node,
|
||||
Road,
|
||||
Car,
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@(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
|
||||
|
||||
draw_ui(self)
|
||||
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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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,28 +41,28 @@ 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)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
create_road(self, pos)
|
||||
}
|
||||
|
||||
// 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.?
|
||||
|
||||
|
||||
temp_node := &self.nodes[index]
|
||||
self.temp_node = nil
|
||||
if len(temp_node.roads) > 0 do return
|
||||
|
||||
if len(temp_node.roads) > 0 do return
|
||||
|
||||
inf.node_deinit(temp_node)
|
||||
// We can safely call the remove here as the only way it will get deleted is if it's the only node aka it was created during our creation process
|
||||
// Consequently this means that it will always be on last place and never swapped with anything
|
||||
unordered_remove(&self.nodes, index)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
44
src/vehicles/car.odin
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user