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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
extends PanelContainer
class_name TrackEditor

@export var group: ButtonGroup
@onready var brush := %brush

enum Mode { Select, Move, Rotate, Scale }
var mode: Mode
var selected: Array[TrackObject]:
	set(s):
		if s != selected:
			for b in selected: # easier than finding the items that are not in selected but are in s
				b.live_node.un_highlight()
			selected = s
			for b in selected:
				b.live_node.highlight()
			make_gizmo.emit(mode)
			selection_changed.emit(selected)
var snapping := true
var objects: Array[TrackObject] = []
var history := UndoRedo.new()
var track: TrackResource
var sun: DirectionalLight3D
signal make_gizmo(mode: Mode)
signal selection_changed(objects: Array[TrackObject])

const loader := preload("res://scenes/track.tscn")

func _ready() -> void:
	track = Globals.editing if Globals.editing else TrackResource.new([])
	var l: TrackLoader = loader.instantiate()
	l.editor = true
	l.track = track
	add_child(l)
	# move over the loaders children
	for c in l.get_children():
		l.remove_child(c)
		%port.add_child(c)
		if c is DirectionalLight3D:
			sun = c
	# the loader has loaded, get rid of it
	l.queue_free()

	objects = track.blocks.duplicate() # duplicate: if not saved, will be lost
	%propertys.name_.text = track.name
	%propertys.laps_.value = track.laps
	%propertys.time_.time = { hour = rot2hour(track.sun_x), minute = 00 }
	%propertys.time_.time_changed.connect(func(t: Dictionary): sun.global_rotation.x = hour2rot(t.hour))
	%cam.global_transform = IntroCam.get_origin(track) # put the camera up high, looking straight down

	if not FileAccess.file_exists(Globals.TRACKS % track.name) and not track.builtin:
		%save.unsaved = true

	group.pressed.connect(pressed)
	tree_exiting.connect(
		func():
			for obj in objects:
				obj.delete_live()
	)

func rot2hour(rot: int) -> int:
	return round(24.0 * float(posmod(90 - rot, 360)) / 360.0)

func hour2rot(hour: int) -> float:
	return (6.0 - hour) / 24.0 * TAU

func pressed(b: Button) -> void:
	mode = Mode[b.name.to_pascal_case()]
	make_gizmo.emit(mode)

func reset_selected() -> void:
	var new: Array[TrackObject] = []
	selected = new

func _on_mousecast_hit(colls: Array[Block]) -> void:
	var new_selected: Array[TrackObject] = []
	new_selected.resize(colls.size())
	var painting: bool = brush.button_pressed and not colls.is_empty()
	if painting:
		history.create_action("paint")
	for i in len(colls):
		new_selected[i] = tobj_from_node(colls[i])
		if painting and colls[i].materials_allowed() & brush.mat:
			history.add_do_method(colls[i].set_mat.bind(brush.mat))
			history.add_undo_method(colls[i].set_mat.bind(colls[i].mat))
		assert(new_selected[i]!=null, "%s was not found" % [colls[i]])
	if not brush.button_pressed:
		selected = new_selected
	if painting:
		history.commit_action()

func _on_snapping_toggled(button_pressed: bool) -> void:
	snapping = button_pressed

func get_trackdata() -> TrackResource:
	objects = objects.filter(func(o: TrackObject): return is_instance_valid(o.live_node))
	track.blocks = objects.duplicate()
	track.name = %propertys.name_.text
	track.laps = %propertys.laps_.value
	track.sun_x = roundi(sun.global_rotation_degrees.x)
	return track

func _on_item_created(object: TrackObject) -> void:
	objects.append(object)
	if object.live_node is Booster:
		for block in objects:
			if block.live_node is Booster:
				block.live_node.sync()
	%thonk.pitch_scale = randf_range(.9, 1.2)
	%thonk.play()

func tobj_from_node(node: Block) -> TrackObject:
	for o in objects:
		if o.live_node == node:
			return o
	return null

func _on_delete_pressed() -> void:
	selected = []

func _on_remove_tobj(tobj: TrackObject) -> void:
	objects.erase(tobj)

func _on_brush_toggled(on: bool) -> void:
	if on:
		var painting: bool = brush.button_pressed and not selected.is_empty()
		if painting:
			history.create_action("paint")
		for o in selected:
			if painting and o.live_node.materials_allowed() & brush.mat:
				history.add_do_method(o.live_node.set_mat.bind(brush.mat))
				history.add_undo_method(o.live_node.set_mat.bind(o.live_node.mat))
		if painting:
			history.commit_action()
		reset_selected()