diff --git a/assets/player_icon.png b/assets/player_icon.png new file mode 100644 index 0000000..c679b88 Binary files /dev/null and b/assets/player_icon.png differ diff --git a/assets/player_icon.png.import b/assets/player_icon.png.import new file mode 100644 index 0000000..e4a095d --- /dev/null +++ b/assets/player_icon.png.import @@ -0,0 +1,35 @@ +[remap] + +importer="texture" +type="StreamTexture" +path="res://.import/player_icon.png-97b071f9d92f0abba7d34c4aae551713.stex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/player_icon.png" +dest_files=[ "res://.import/player_icon.png-97b071f9d92f0abba7d34c4aae551713.stex" ] + +[params] + +compress/mode=0 +compress/lossy_quality=0.7 +compress/hdr_mode=0 +compress/bptc_ldr=0 +compress/normal_map=0 +flags/repeat=0 +flags/filter=true +flags/mipmaps=false +flags/anisotropic=false +flags/srgb=2 +process/fix_alpha_border=true +process/premult_alpha=false +process/HDR_as_SRGB=false +process/invert_color=false +process/normal_map_invert_y=false +stream=false +size_limit=0 +detect_3d=true +svg/scale=1.0 diff --git a/network/multiplayer_controller.gd b/network/multiplayer_controller.gd index 0d69f03..2b0a210 100644 --- a/network/multiplayer_controller.gd +++ b/network/multiplayer_controller.gd @@ -1,5 +1,7 @@ extends Node +signal peers_updated + const GUI_SCENE = "res://player/gui.tscn" var enet_peer = NetworkedMultiplayerENet.new() @@ -9,31 +11,42 @@ var current_track: Node = null var current_track_path: String var current_vehicle: String +var colors = [ + Color.blue, Color.yellow, Color.green, Color.violet, Color.brown, Color.cyan, Color.orange +] + class PeerInfo: var id: int var vehicle: String var name: String var spawned: bool + var color: Color static func new_peer( - new_id: int, new_vehicle: String = "", new_name: String = "", new_spawned: bool = false + new_id: int, + new_vehicle: String = "", + new_name: String = "", + new_spawned: bool = false, + new_color: Color = Color(1, 1, 1) ) -> PeerInfo: var new_instance = PeerInfo.new() new_instance.id = new_id new_instance.vehicle = new_vehicle new_instance.name = new_name new_instance.spawned = new_spawned + new_instance.color = new_color return new_instance func to_array() -> Array: - return [id, vehicle, name, spawned] + return [id, vehicle, name, spawned, color] func from_array(data: Array) -> void: self.id = data[0] self.vehicle = data[1] self.name = data[2] self.spawned = data[3] + self.color = data[4] func _ready(): @@ -57,9 +70,12 @@ func create_server(port, track, vehicle): current_vehicle = vehicle enet_peer.create_server(port, 2) get_tree().network_peer = enet_peer - peers[1] = PeerInfo.new_peer(1, vehicle, GlobalSettings.multiplayer_name, true) + peers[1] = PeerInfo.new_peer( + 1, vehicle, GlobalSettings.multiplayer_name, true, get_next_color(1) + ) create_player(1, vehicle) get_tree().root.call_deferred("add_child", current_track) + emit_signal("peers_updated") func create_client(address, port, vehicle): @@ -67,18 +83,24 @@ func create_client(address, port, vehicle): enet_peer.create_client(address, port) get_tree().network_peer = enet_peer peers[get_tree().get_network_unique_id()] = PeerInfo.new_peer( - get_tree().get_network_unique_id(), vehicle, GlobalSettings.multiplayer_name, true + get_tree().get_network_unique_id(), + vehicle, + GlobalSettings.multiplayer_name, + true, + get_next_color(1) ) + emit_signal("peers_updated") func _peer_connected(peer_id): - peers[peer_id] = PeerInfo.new_peer(peer_id, "", "", false) + peers[peer_id] = PeerInfo.new_peer(peer_id, "", "", false, get_next_color(peer_id)) rpc_id( peer_id, "add_player", get_tree().get_network_unique_id(), peers[get_tree().get_network_unique_id()].to_array() ) + emit_signal("peers_updated") if get_tree().get_network_unique_id() == 1: rpc_id(peer_id, "select_track", current_track_path) @@ -135,12 +157,12 @@ func quit(): remote func add_player(peer_id, peer_info: Array): if peers[peer_id] == null: - peers[peer_id] = PeerInfo.new_peer(peer_id, "", "", false) + peers[peer_id] = PeerInfo.new_peer(peer_id, "", "", false, get_next_color(peer_id)) if peers[peer_id].spawned == false: peers[peer_id].from_array(peer_info) - # Check for duplicate names on server side + # Check for duplicate names on server side and update colors if get_tree().get_network_unique_id() == 1: var names = [] for peer in peers: @@ -151,11 +173,15 @@ remote func add_player(peer_id, peer_info: Array): peers[peer_id].name = peers[peer_id].name + ("(%s)" % peer_id) rpc("update_name", peer_id, peers[peer_id].name) + peers[peer_id].color = get_next_color(peer_id) + rpc("update_color", peer_id, peers[peer_id].color) + if current_track != null: peers[peer_id].spawned = true create_player(peer_id, peers[peer_id].vehicle) else: peers[peer_id].spawned = false + emit_signal("peers_updated") remote func select_track(track_path): @@ -166,7 +192,21 @@ remote func select_track(track_path): if peers[peer].spawned == false: peers[peer].spawned = true create_player(peer, peers[peer].vehicle) + emit_signal("peers_updated") remote func update_name(peer_id, name: String): peers[peer_id].name = name + emit_signal("peers_updated") + + +remote func update_color(peer_id, color: Color): + peers[peer_id].color = color + emit_signal("peers_updated") + + +func get_next_color(peer_id: int) -> Color: + if peers.size() >= colors.size(): + return Color(peer_id) + else: + return colors[peers.size()] diff --git a/scenes/base_track_level.gd b/scenes/base_track_level.gd index ed546c3..9d6420f 100644 --- a/scenes/base_track_level.gd +++ b/scenes/base_track_level.gd @@ -27,6 +27,7 @@ func spawn_player(player_node: BuggedVehicle, gui: Node) -> void: func spawn_vehicle(vehicle: BuggedVehicle) -> void: + vehicle.connect("position_updated", track, "_on_player_position_updated") reset_player_to(track.get_furthest_checkpoint(), vehicle) add_child(vehicle) @@ -47,6 +48,7 @@ func reset_player_to(node_to_reset_to: Node, player_node: BuggedVehicle) -> void func _spawn_in_player(): reset_player_to(track.get_furthest_checkpoint(), player_node) + player_node.connect("position_updated", track, "_on_player_position_updated") add_child(player_node) add_child(gui) player_controller = PLAYER_CONTROLLER.new() diff --git a/scenes/components/curved_line_2d.gd b/scenes/components/curved_line_2d.gd new file mode 100644 index 0000000..da07f82 --- /dev/null +++ b/scenes/components/curved_line_2d.gd @@ -0,0 +1,12 @@ +extends Node2D + +export(Curve2D) var curve +export(Color) var color = Color.red +export(float) var width = 2.0 + + +func _draw() -> void: + if curve == null: + return + + draw_polyline(curve.get_baked_points(), color, width) diff --git a/scenes/components/curved_line_2d.tscn b/scenes/components/curved_line_2d.tscn new file mode 100644 index 0000000..91c4788 --- /dev/null +++ b/scenes/components/curved_line_2d.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://scenes/components/curved_line_2d.gd" type="Script" id=1] + +[node name="CurvedLine2d" type="Node2D"] +script = ExtResource( 1 ) diff --git a/scenes/track_minimap.gd b/scenes/track_minimap.gd new file mode 100644 index 0000000..bf5c94f --- /dev/null +++ b/scenes/track_minimap.gd @@ -0,0 +1,90 @@ +extends Node2D + +export(Vector2) var size = Vector2(200, 200) +export(Texture) var player_icon + +onready var scaling_helper = $ScalingHelper +onready var track_path = $ScalingHelper/TrackPath +onready var track_line = $ScalingHelper/TrackLine +onready var start_line = $ScalingHelper/TrackPath/StartLine +onready var players_container = $ScalingHelper/Players + + +func _ready() -> void: + scaling_helper.position.x = size.x / 2 + scaling_helper.position.y = size.y / 2 + MultiplayerController.connect("peers_updated", self, "_on_peers_updated") + + +func set_curve(curve: Curve3D) -> void: + var curve2d = Curve2D.new() + var point_count = curve.get_point_count() + for point in range(point_count): + var point3d = curve.get_point_position(point) + var point_in = curve.get_point_in(point) + var point_out = curve.get_point_out(point) + curve2d.add_point( + Vector2(point3d.x, point3d.z), + Vector2(point_in.x, point_in.z), + Vector2(point_out.x, point_out.z) + ) + + var max_x = curve2d.get_baked_points()[0].x + var max_y = curve2d.get_baked_points()[0].y + var min_x = curve2d.get_baked_points()[0].x + var min_y = curve2d.get_baked_points()[0].y + for point in curve2d.get_baked_points(): + if point.x > max_x: + max_x = point.x + if point.x < min_x: + min_x = point.x + if point.y > max_y: + max_y = point.y + if point.y < min_y: + min_y = point.y + + var center_x = (max_x + min_x) / 2 + var center_y = (max_y + min_y) / 2 + var width = max_x - min_x + var height = max_y - min_y + var max_dim = max(width, height) + var scale_factor = size.x / (max_dim + track_line.width) + var new_center_x = scaling_helper.position.x - (center_x * scale_factor) + var new_center_y = scaling_helper.position.y - (center_y * scale_factor) + scaling_helper.scale.x = scale_factor + scaling_helper.scale.y = scale_factor + scaling_helper.position.x = new_center_x + scaling_helper.position.y = new_center_y + track_path.curve = curve2d + track_line.curve = curve2d + start_line.offset = 0.01 + + +func on_player_position_updated(player_id: int, position: Transform) -> void: + var existing_node: Sprite = players_container.get_node_or_null(String(player_id)) + if existing_node == null: + var player_color + if MultiplayerController.is_online(): + player_color = MultiplayerController.peers[player_id].color + else: + player_color = Color(1, 1, 1) + existing_node = Sprite.new() + existing_node.name = String(player_id) + existing_node.texture = player_icon + existing_node.modulate = player_color + players_container.add_child(existing_node) + existing_node.position.x = position.origin.x + existing_node.position.y = position.origin.z + existing_node.rotation = position.basis.x.signed_angle_to(Vector3.LEFT, Vector3.UP) + + +func _on_peers_updated() -> void: + for peer in MultiplayerController.peers: + var existing_node: Sprite = players_container.get_node_or_null(String(peer)) + if existing_node != null: + var player_color = MultiplayerController.peers[peer].color + existing_node.modulate = player_color + + for child in players_container.get_children(): + if not MultiplayerController.peers.has(int(float(child.name))): + child.queue_free() diff --git a/scenes/track_minimap.tscn b/scenes/track_minimap.tscn new file mode 100644 index 0000000..473c6ef --- /dev/null +++ b/scenes/track_minimap.tscn @@ -0,0 +1,37 @@ +[gd_scene load_steps=6 format=2] + +[ext_resource path="res://assets/player_icon.png" type="Texture" id=1] +[ext_resource path="res://scenes/track_minimap.gd" type="Script" id=2] +[ext_resource path="res://scenes/components/curved_line_2d.tscn" type="PackedScene" id=3] + +[sub_resource type="Gradient" id=1] +offsets = PoolRealArray( 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1 ) +colors = PoolColorArray( 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1 ) + +[sub_resource type="GradientTexture" id=2] +gradient = SubResource( 1 ) +width = 50 + +[node name="TrackMinimap" type="Node2D"] +script = ExtResource( 2 ) +player_icon = ExtResource( 1 ) + +[node name="ScalingHelper" type="Node2D" parent="."] + +[node name="TrackPath" type="Path2D" parent="ScalingHelper"] +curve = null + +[node name="StartLine" type="PathFollow2D" parent="ScalingHelper/TrackPath"] +position = Vector2( -99.1171, -154.856 ) +rotation = -0.742965 + +[node name="Sprite" type="Sprite" parent="ScalingHelper/TrackPath/StartLine"] +rotation = 1.5708 +scale = Vector2( 1, 5 ) +texture = SubResource( 2 ) + +[node name="TrackLine" parent="ScalingHelper" instance=ExtResource( 3 )] +color = Color( 0.482353, 0, 0, 1 ) +width = 10.0 + +[node name="Players" type="Node2D" parent="ScalingHelper"] diff --git a/scenes/trackgui.gd b/scenes/trackgui.gd index 85ac67e..27427ef 100644 --- a/scenes/trackgui.gd +++ b/scenes/trackgui.gd @@ -8,6 +8,7 @@ onready var best_time_value = $VBoxContainer/HBoxContainer/BestTimeValue onready var wrong_way_label = $CenterContainer/WrongWayLabel onready var leaderboards = $VBoxContainer/LeaderboardsLine onready var leaderboards_list = $VBoxContainer/LeaderboardsLine/VBoxContainer/Leaderboards +onready var track_minimap = $VBoxContainer2/HBoxContainer/ViewportContainer/TrackRadar/TrackMinimap func _ready() -> void: @@ -15,6 +16,7 @@ func _ready() -> void: best_time_value.text = "NaN" if MultiplayerController.is_online(): leaderboards.visible = true + MultiplayerController.connect("peers_updated", self, "_on_peers_updated") else: leaderboards.visible = false @@ -47,7 +49,10 @@ func _format_time(time: float) -> String: remote func update_leaderboard_time(peer_id: String, lap_time: float): leaderboards_data[peer_id] = lap_time + _refresh_leaderboard() + +func _refresh_leaderboard(): var leaderboards_sorted = [] for peer in leaderboards_data: leaderboards_sorted.append([peer, leaderboards_data[peer]]) @@ -67,3 +72,19 @@ remote func update_leaderboard_time(peer_id: String, lap_time: float): func _leaderbords_comparison(left: Array, right: Array) -> bool: return left[1] < right[1] + + +func set_curve(curve: Curve3D) -> void: + track_minimap.set_curve(curve) + + +func on_player_position_updated(player_id: int, position: Transform) -> void: + track_minimap.on_player_position_updated(player_id, position) + + +func _on_peers_updated() -> void: + for peer in leaderboards_data.keys(): + if not MultiplayerController.peers.has(peer): + leaderboards_data.erase(peer) + + _refresh_leaderboard() diff --git a/scenes/trackgui.tscn b/scenes/trackgui.tscn index 18f7c5e..818607b 100644 --- a/scenes/trackgui.tscn +++ b/scenes/trackgui.tscn @@ -1,7 +1,8 @@ -[gd_scene load_steps=5 format=2] +[gd_scene load_steps=6 format=2] [ext_resource path="res://assets/fonts/kenney-future-narrow.ttf" type="DynamicFontData" id=1] [ext_resource path="res://scenes/trackgui.gd" type="Script" id=2] +[ext_resource path="res://scenes/track_minimap.tscn" type="PackedScene" id=3] [sub_resource type="DynamicFont" id=1] size = 36 @@ -125,3 +126,30 @@ align = 1 __meta__ = { "_edit_use_anchors_": false } + +[node name="VBoxContainer2" type="VBoxContainer" parent="."] +margin_right = 984.0 +margin_bottom = 560.0 +alignment = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer2"] +margin_top = 360.0 +margin_right = 984.0 +margin_bottom = 560.0 +alignment = 2 + +[node name="ViewportContainer" type="ViewportContainer" parent="VBoxContainer2/HBoxContainer"] +margin_left = 784.0 +margin_right = 984.0 +margin_bottom = 200.0 + +[node name="TrackRadar" type="Viewport" parent="VBoxContainer2/HBoxContainer/ViewportContainer"] +size = Vector2( 200, 200 ) +transparent_bg = true +handle_input_locally = false +disable_3d = true +usage = 0 +render_target_update_mode = 3 +gui_disable_input = true + +[node name="TrackMinimap" parent="VBoxContainer2/HBoxContainer/ViewportContainer/TrackRadar" instance=ExtResource( 3 )] diff --git a/scenes/tracks/track.gd b/scenes/tracks/track.gd index df0319d..33627d5 100644 --- a/scenes/tracks/track.gd +++ b/scenes/tracks/track.gd @@ -24,6 +24,7 @@ var current_time = 0 var lap_done = false onready var checkpoints = $Checkpoints +onready var track_gui = $TrackGUI onready var path: Path = get_node(track_path) @@ -51,6 +52,7 @@ func _ready() -> void: gate.scale = gate_size checkpoints.get_child(0).add_child(gate) checkpoints.global_transform.origin = path.global_transform.origin + track_gui.set_curve(path.curve) func _process(_delta: float) -> void: @@ -96,3 +98,7 @@ func _build_checkpoint_collision(): func _update_time(): current_time = OS.get_ticks_msec() - start_time emit_signal("time_updated", current_time) + + +func _on_player_position_updated(player_id: int, position: Transform) -> void: + track_gui.on_player_position_updated(player_id, position) diff --git a/vehicles/vehicle.gd b/vehicles/vehicle.gd index f53a533..b6463d3 100644 --- a/vehicles/vehicle.gd +++ b/vehicles/vehicle.gd @@ -8,6 +8,7 @@ signal brake_updated(brake_percent) signal clutch_updated(clutch_percent) signal gear_updated(gear) signal steering_updated(steering_angle, steering_percent) +signal position_updated(player_id, position) enum GearRequest { NONE, UP, DOWN } @@ -256,6 +257,8 @@ func _physics_process(delta: float): steering = steering_input * lerp(max_steer_angle_rad, speed_steer_angle_rad, steer_speed_factor) emit_signal("steering_updated", steering, steering / max_steer_angle_rad) + emit_signal("position_updated", get_network_master(), global_transform) + if MultiplayerController.is_online(): if get_network_master() == get_tree().get_network_unique_id(): _synchronize() @@ -288,6 +291,7 @@ func _synchronize(): remote func sync_position(position: Transform): reset_transform = position + emit_signal("position_updated", get_network_master(), global_transform) remote func sync_inputs(remote_inputs):