small racing game im working on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
extends Node3D
class_name Race

@export var car_scene: PackedScene
@export var ghost_scene: PackedScene
@export var track_loader_scene: PackedScene

# in order of initialization
var track_res: TrackResource
var track: TrackLoader
var data: TrackSaveableData
var best_time_data: TrackSaveableData
var car: Car
var ghost: GhostCar
var start_frame: int

var current_lap := 0
var playing := false
var timer := GameTimer.new()

const SaveLoad := preload("res://addons/@bendn/remap/private/SaveLoadUtils.gd")
const saves := "user://%s.trackdata"

signal next_lap
signal created_car(car: Car)
signal created_ghost(ghost: GhostCar)
signal finished
signal split(time: float, prev_time: float)

func initialize(t: TrackResource) -> void:
	track_res = t

func mkghost() -> void:
	var g: Node3D = ghost_scene.instantiate()
	g.set_script(load("res://classes/ghost.gd"))
	ghost = g
	add_child(ghost)
	ghost.global_position = best_time_data.loadshot(0)[0]
	ghost.global_rotation = best_time_data.loadshot(0)[1]
	ghost.hide()
	created_ghost.emit(ghost)

func mkcar() -> void:
	car = HumanCar.attach(car_scene)
	car.visible = false
	add_child(car)
	car.rotation = track.start_rot + Vector3(0, PI, -PI/2)
	# car.set_deferred(&"rotation", track.start_rot + Vector3(0, PI, -PI/2))
	car.global_position = track.start_pos + Vector3(0, 2, 0) - (car.ball.global_transform.basis.z * 2) # bump forward a teensy bit
	car.visible = true
	created_car.emit(car)
	print("car created")

func _ready() -> void:
	set_physics_process(false)
	track = track_loader_scene.instantiate()
	track.track = track_res
	add_child(track)
	data = TrackSaveableData.new(track_res.checkpoints.size(), track_res.laps)
	best_time_data = TrackSaveableData._load(saves % track_res.name)
	mkcar()
	if best_time_data:
		mkghost()
	connect_checkpoints()
	add_child(timer)

func connect_checkpoints() -> void:
	for i in len(track.checkpoints):
		track.checkpoints[i].collected.connect(passed_cp.bind(i))
	track.finish.collected.connect(passed_finish)

func passed_cp(cp: int) -> void: if playing and data.checkpoints[current_lap][cp] < 0: collect(cp)

func passed_finish() -> void:
	if !playing: return
	for i in len(data.checkpoints[current_lap]) - 1:
		if data.checkpoints[current_lap][i] < 0:
			return
	collect(-1)
	if track_res.laps - 1 == current_lap:
		finished.emit()
		playing = false
		print("finished")
		if not best_time_data or data.time < best_time_data.time:
			print("new pb!")
			SaveLoad.save(saves % track_res.name, data.data())
		data = TrackSaveableData.new(track.checkpoints.size())
	else:
		current_lap += 1
		next_lap.emit()

func _physics_process(_delta: float) -> void:
	data.snapshot(car)
	if ghost:
		if best_time_data.snaps() - 1 < Engine.get_physics_frames() - start_frame:
			print("ran out of snaps, removing ghost")
			ghost.queue_free()
			ghost = null
			return
		var shot := best_time_data.loadshot(Engine.get_physics_frames() - start_frame)
		ghost.update(shot[0], shot[1], shot[2])
		ghost.visible = (ghost.global_position.distance_squared_to(car.car_mesh.global_position) > 10)


func collect(cp: int) -> void:
	var time := best_time_data.get_time(current_lap, cp) if best_time_data else -1.0
	time = best_time_data.time if (not track_res.laps or track_res.laps == current_lap + 1) and cp == -1 and time != -1.0 else time
	split.emit(timer.now(), time)
	data.collect(current_lap, cp, timer.now())


func start() -> void:
	timer.start()
	start_frame = Engine.get_physics_frames()
	playing = true
	car.start()
	set_physics_process(true)