diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5b6c137 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "godotTools.editorPath.godot4": "c:\\Program Files\\godot\\4\\Godot_v4.4-stable_win64.exe" +} \ No newline at end of file diff --git a/src/main.gd b/src/main.gd index 2ea7375..a961c4c 100644 --- a/src/main.gd +++ b/src/main.gd @@ -1,5 +1,7 @@ extends Node2D +@export var DEBUG_MULTIPLAYER: bool = true + var player_scene = preload("res://scripts/entities/player/player.tscn") var pot_scene = preload("res://scripts/entities/world/pot.tscn") @@ -21,6 +23,12 @@ func _ready() -> void: #pot.position = Vector2(90,80) #$SpawnRoot.add_child(pot) + if DEBUG_MULTIPLAYER: + # Add a random delay to ensure instances start at different times + var random_delay = randf_range(0.1, 0.5) + await get_tree().create_timer(random_delay).timeout + call_deferred("_setup_debug_multiplayer") + pass func _finishedCountdown(): @@ -32,10 +40,10 @@ func _finishedCountdown(): _syncTimerToPlayer.rpc($TimerRound.time_left) pass -func time_to_minutes_secs(time : float): +func time_to_minutes_secs(time: float): var mins = int(floor(int(time) / 60.0)) time -= mins * 60 - var secs = int(time) + var secs = int(time) #var mili = int((time - int(time)) * 100) var extraSecZero = "0" if secs < 10 else "" var extraMinZero = "0" if mins < 10 else "" @@ -44,12 +52,12 @@ func time_to_minutes_secs(time : float): func _process(_delta: float) -> void: if round_finished == false and round_started == false and start_round == false and multiplayer != null and multiplayer.is_server(): # make sure atleast 2 players are connected - var players = 0 - for pl:CharacterBody2D in $SpawnRoot.get_children(): + var _players = 0 + for pl: CharacterBody2D in $SpawnRoot.get_children(): if "is_player" in pl: - players += 1 - if players == 2: - start_round_func.rpc() + _players += 1 + #if _players == 2: + #start_round_func.rpc() if round_started: $HUD/MarginContainerUpperRight/HBoxContainer/VBoxContainer/LabelTimeValue.text = time_to_minutes_secs($TimerRound.time_left) pass @@ -67,14 +75,14 @@ func _finishedHosting(): has_started = true pass -func _addPlayer(id:int): +func _addPlayer(id: int): print("add player:", id) #if id == 1: #var pot:CharacterBody2D = pot_scene.instantiate() #pot.position = Vector2(90,80) #$SpawnRoot.add_child(pot, true) - var player:CharacterBody2D = player_scene.instantiate() + var player: CharacterBody2D = player_scene.instantiate() player.name = str(id) #find empty 16x16 tile to spawn player ' @@ -89,17 +97,17 @@ func _addPlayer(id:int): terrainMultiplier = 0.5 pass pass' - var best_spawn = Vector2(0,0) + var best_spawn = Vector2(0, 0) var best_distance = -1 - for spawnP:Node2D in $PlayerSpawnPoints.get_children(): + for spawnP: Node2D in $PlayerSpawnPoints.get_children(): var pos = spawnP.position var min_distance = INF # find spawn position which is furthest from all players... - for pl:CharacterBody2D in $SpawnRoot.get_children(): + for pl: CharacterBody2D in $SpawnRoot.get_children(): if "is_player" in pl: var dist = pl.position.distance_to(pos) - min_distance = min(min_distance, dist) # Keep the smallest distance + min_distance = min(min_distance, dist) # Keep the smallest distance pass # Choose the spawn with the largest minimum distance if min_distance > best_distance: @@ -116,12 +124,12 @@ func _addPlayer(id:int): pass @rpc("reliable") -func _syncTimerToPlayer(iTimeLeft:float): +func _syncTimerToPlayer(iTimeLeft: float): round_started = true $TimerRound.start(iTimeLeft) pass -func _delPlayer(id:int): +func _delPlayer(id: int): if !$SpawnRoot.has_node(str(id)): return $SpawnRoot.get_node(str(id)).queue_free() @@ -129,18 +137,20 @@ func _delPlayer(id:int): func _on_timer_timeout() -> void: - if has_started: var countPots = 0 for child in $SpawnRoot.get_children(): if "object_name" in child: - countPots+=1 + countPots += 1 pass if countPots < 8: var pot = pot_scene.instantiate() pot.is_spawning = true pot.positionZ = 90 - pot.position = Vector2(64+16*randi_range(0,5),64+16*randi_range(0,5)) + pot.position = Vector2(64 + 16 * randi_range(0, 5), 64 + 16 * randi_range(0, 5)) + # Set server as authority for pot synchronization + pot.set_multiplayer_authority(1) + Console.print("Pot spawned with authority: ", pot.get_multiplayer_authority()) $SpawnRoot.add_child(pot, true) $TimerSpawnPots.wait_time = randf_range(0.2, 1.4) $TimerSpawnPots.start() # restart timer... @@ -158,21 +168,21 @@ func _on_timer_round_timeout() -> void: func _on_timer_until_next_round_timeout() -> void: if multiplayer.is_server(): - for pl2:CharacterBody2D in $SpawnRoot.get_children(): + for pl2: CharacterBody2D in $SpawnRoot.get_children(): if "is_player" in pl2: - var best_spawn = Vector2(0,0) + var best_spawn = Vector2(0, 0) var best_distance = -1 - for spawnP:Node2D in $PlayerSpawnPoints.get_children(): + for spawnP: Node2D in $PlayerSpawnPoints.get_children(): var pos = spawnP.position var min_distance = INF # find spawn position which is furthest from all players... - for pl:CharacterBody2D in $SpawnRoot.get_children(): + for pl: CharacterBody2D in $SpawnRoot.get_children(): if "is_player" in pl and pl != pl2: var dist = pl.position.distance_to(pos) - min_distance = min(min_distance, dist) # Keep the smallest distance + min_distance = min(min_distance, dist) # Keep the smallest distance pass # Choose the spawn with the largest minimum distance if min_distance > best_distance: @@ -184,3 +194,52 @@ func _on_timer_until_next_round_timeout() -> void: if multiplayer.is_server(): start_round_func.rpc() pass # Replace with function body. + +func _setup_debug_multiplayer(): + # Get the character select scene to access the selected character + var character_select = get_tree().get_first_node_in_group("character_select") + if not character_select: + # Try to find it in the scene tree + character_select = get_tree().current_scene.get_node_or_null("CanvasLayer/CharacterSelect") + + if character_select and character_select.has_method("get_current_character_stats"): + # Set the character data from the character select + MultiplayerManager.character_data = character_select.get_current_character_stats() + else: + # Fallback: create a default character + MultiplayerManager.character_data = CharacterStats.new() + MultiplayerManager.character_data.character_name = "DebugPlayer" + $MainMenu._showHostButton() + + # Determine if this instance should host or join + # Check for command line arguments first + var should_host = false + var args = OS.get_cmdline_args() + + # Check if --host or --join argument was passed + if "--host" in args: + should_host = true + print("DEBUG: Host mode specified via command line argument") + elif "--join" in args: + should_host = false + print("DEBUG: Join mode specified via command line argument") + else: + # Fallback: use process ID for deterministic behavior + var process_id = OS.get_process_id() + should_host = process_id % 2 == 1 + print("DEBUG: No command line args, using process ID: ", process_id, " Should host: ", should_host) + + if should_host: + print("DEBUG: Starting as HOST") + $MainMenu._on_button_host_pressed() + else: + print("DEBUG: Starting as CLIENT") + $MainMenu._on_button_join_pressed() + +func _exit_tree(): + # Clean up the debug lock file when exiting + if DEBUG_MULTIPLAYER: + var lock_file_path = "user://debug_host.lock" + if FileAccess.file_exists(lock_file_path): + DirAccess.remove_absolute(lock_file_path) + print("DEBUG: Cleaned up host lock file") diff --git a/src/project.godot b/src/project.godot index 4c7c455..e0ce5b8 100644 --- a/src/project.godot +++ b/src/project.godot @@ -12,7 +12,7 @@ config_version=5 config/name="ruinborn_new" run/main_scene="uid://c6s2i06bbd6u6" -config/features=PackedStringArray("4.4", "Forward Plus") +config/features=PackedStringArray("4.5", "Forward Plus") run/max_fps=60 boot_splash/show_image=false config/icon="res://icon.svg" @@ -20,6 +20,7 @@ config/icon="res://icon.svg" [autoload] MultiplayerManager="*res://scripts/Autoloads/multiplayer_manager.tscn" +Console="*res://scripts/Autoloads/console.tscn" [display] @@ -68,8 +69,7 @@ ui_down={ } Attack={ "deadzone": 0.2, -"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194326,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) -, Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":81,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null) ] } Use={ @@ -78,6 +78,11 @@ Use={ , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":70,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) ] } +Console={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":96,"key_label":0,"unicode":167,"location":0,"echo":false,"script":null) +] +} [layer_names] diff --git a/src/scripts/Autoloads/console.gd b/src/scripts/Autoloads/console.gd new file mode 100644 index 0000000..9954abe --- /dev/null +++ b/src/scripts/Autoloads/console.gd @@ -0,0 +1,29 @@ +extends Node2D + + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + pass # Replace with function body. + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + if (Input.is_action_just_pressed("Console")): + var tween = create_tween() + var target_y = 0 + if ($CanvasLayer/ColorRectBG.position.y == 0): + #tween console to -y 240 + target_y = -$CanvasLayer/ColorRectBG.size.y + tween.tween_property($CanvasLayer/ColorRectBG, "position:y", target_y, 0.13).set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) + pass + +func isOpen() -> bool: + return $CanvasLayer/ColorRectBG.position.y == 0 + +func print(...args) -> void: + var line := "" + for arg in args: + line += str(arg) + + print(line) + # append to your console label + $CanvasLayer/ColorRectBG/VBoxContainer/ScrollContainer/LabelText.text += "\n" + line diff --git a/src/scripts/Autoloads/console.gd.uid b/src/scripts/Autoloads/console.gd.uid new file mode 100644 index 0000000..991809c --- /dev/null +++ b/src/scripts/Autoloads/console.gd.uid @@ -0,0 +1 @@ +uid://c8t6r0pvo3ko8 diff --git a/src/scripts/Autoloads/console.tscn b/src/scripts/Autoloads/console.tscn new file mode 100644 index 0000000..9932c7d --- /dev/null +++ b/src/scripts/Autoloads/console.tscn @@ -0,0 +1,52 @@ +[gd_scene load_steps=3 format=3 uid="uid://8bd1fipm7lw5"] + +[ext_resource type="Script" uid="uid://c8t6r0pvo3ko8" path="res://scripts/Autoloads/console.gd" id="1_c563n"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_oawmp"] + +[node name="Console" type="Node2D"] +script = ExtResource("1_c563n") + +[node name="CanvasLayer" type="CanvasLayer" parent="."] +layer = 22 + +[node name="ColorRectBG" type="ColorRect" parent="CanvasLayer"] +material = SubResource("ShaderMaterial_oawmp") +anchors_preset = 10 +anchor_right = 1.0 +offset_bottom = 320.0 +grow_horizontal = 2 +color = Color(0, 0, 0, 0.74509805) + +[node name="VBoxContainer" type="VBoxContainer" parent="CanvasLayer/ColorRectBG"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="ScrollContainer" type="ScrollContainer" parent="CanvasLayer/ColorRectBG/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="LabelText" type="RichTextLabel" parent="CanvasLayer/ColorRectBG/VBoxContainer/ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +focus_mode = 2 +vertical_alignment = 2 +selection_enabled = true + +[node name="HBoxContainer" type="HBoxContainer" parent="CanvasLayer/ColorRectBG/VBoxContainer"] +layout_mode = 2 + +[node name="LabelGT" type="Label" parent="CanvasLayer/ColorRectBG/VBoxContainer/HBoxContainer"] +layout_mode = 2 +text = ">" + +[node name="LineEditInput" type="LineEdit" parent="CanvasLayer/ColorRectBG/VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +caret_blink = true +caret_force_displayed = true diff --git a/src/scripts/Autoloads/multiplayer_manager.gd b/src/scripts/Autoloads/multiplayer_manager.gd index 1afb86d..a63c12d 100644 --- a/src/scripts/Autoloads/multiplayer_manager.gd +++ b/src/scripts/Autoloads/multiplayer_manager.gd @@ -22,7 +22,7 @@ const MAX_CHAT_HISTORY = 10 var connection_label_timer: Timer var connection_label_tween: Tween -var character_data:CharacterStats = null +var character_data: CharacterStats = null var SERVER_PORT = 21212 var SERVER_HOST = "ruinborn.thefirstboss.com" @@ -39,8 +39,8 @@ var peers_ready_for_sync: Dictionary = {} # Add at the top with other variables var peers_character_data: Dictionary = {} -signal addPlayerSignal(id:int) -signal delPlayerSignal(id:int) +signal addPlayerSignal(id: int) +signal delPlayerSignal(id: int) signal finished_hosting() signal client_ready_for_players(peer_id: int) signal connectionFailed @@ -242,16 +242,90 @@ func broadcast_chat_message(message: String): # Add local copy of the message add_chat_message("You: " + message, "chat") +@rpc("any_peer", "reliable") +func request_lift_pot(pot_path: NodePath, peer_id: int): + Console.print("=== RPC RECEIVED ===") + Console.print("MultiplayerManager received request_lift_pot from peer ", peer_id, " for pot at ", pot_path) + Console.print("Current peer ID: ", multiplayer.get_unique_id()) + Console.print("Is server: ", multiplayer.is_server()) + Console.print("===================") + if multiplayer.is_server(): + var pot = get_node_or_null(pot_path) + var player = get_tree().get_current_scene().get_node("SpawnRoot").get_node_or_null(str(peer_id)) + Console.print("MultiplayerManager found pot: ", pot, " player: ", player) + if pot and "lift" in pot and pot.liftable and player: + Console.print("MultiplayerManager authorizing lift for peer ", peer_id) + # Clear grabbed entity if it's not the pot we're lifting + if player.grabbed_entity != null and player.grabbed_entity != pot and "release" in player.grabbed_entity: + player.grabbed_entity.release() + if player.grabbed_entity != pot: + player.grabbed_entity = null + # Don't clear grabbed_entity_path if we're lifting the same pot + # This prevents the pot from being released when we're trying to lift it + player.is_grabbing = false + player.is_lifting = true + # Set held_entity_path directly on server, use RPC for clients + player.held_entity_path = str(pot.get_path()) + # Send RPC to all other players (excluding server) + var all_players = get_tree().get_current_scene().get_node("SpawnRoot").get_children() + for p in all_players: + if p != player and p.has_method("set_held_entity_path_rpc"): + p.set_held_entity_path_rpc.rpc(str(pot.get_path())) + pot.lift(player) + # Set animation on the player who is lifting (the joiner) + player.current_animation = "LIFT" + # Sync animation to all clients + player.sync_animation.rpc("LIFT") + # Pot state is auto-synced via @export variables, no need for manual sync + + # Sync animation and entity to all clients + #print("MultiplayerManager syncing LIFT animation and held_entity to all clients") + # Send RPC to all players except the server (since server already has correct state) + #var all_players = get_tree().get_current_scene().get_node("SpawnRoot").get_children() + #for p in all_players: + #if p.has_method("sync_animation") and p.get_multiplayer_authority() != 1: + #print("MultiplayerManager syncing to player: ", p.name) + #p.sync_animation.rpc("LIFT") + #p.sync_held_entity.rpc(str(pot.get_path())) + #p.sync_grabbed_entity.rpc("") + else: + Console.print("MultiplayerManager denied lift request - pot: ", pot, " liftable: ", pot.liftable if pot else "null", " player: ", player) + +@rpc("any_peer", "reliable") +func request_throw_pot(pot_path: NodePath, peer_id: int, direction: Vector2): + Console.print("MultiplayerManager received request_throw_pot from peer ", peer_id, " for pot at ", pot_path) + if multiplayer.is_server(): + var pot = get_node_or_null(pot_path) + var player = get_tree().get_current_scene().get_node("SpawnRoot").get_node_or_null(str(peer_id)) + Console.print("MultiplayerManager found pot: ", pot, " player: ", player) + if pot and player: + # Check if the pot is being held by this player + if pot.holder_peer_id == peer_id or (pot.holder != null and pot.holder.get_multiplayer_authority() == peer_id): + Console.print("MultiplayerManager authorizing throw for peer ", peer_id) + pot.throw(direction) + # Use RPC to clear held_entity_path on all players (including server) + player.set_held_entity_path_rpc.rpc("") + player.current_animation = "THROW" + # Pot state is auto-synced via @export variables, no need for manual sync + + # Sync animation to all clients (entity sync is handled automatically by PlayerSynchronizer) + var all_players = get_tree().get_current_scene().get_node("SpawnRoot").get_children() + for p in all_players: + if p.has_method("sync_animation") and p.get_multiplayer_authority() != 1: + p.sync_animation.rpc("THROW") + else: + Console.print("MultiplayerManager denied throw - holder_peer_id mismatch or holder not found") + func sortScoreArr(a, b): if a.kills > b.kills: - return true # More kills should come first + return true # More kills should come first elif a.kills == b.kills: - return a.deaths < b.deaths # Fewer deaths should come first - return false # Otherwise, b comes first + return a.deaths < b.deaths # Fewer deaths should come first + return false # Otherwise, b comes first var previousScores = [] -func updateScore(playerJoined:bool = false): +func updateScore(playerJoined: bool = false): %LabelPlayerNr.text = "#" %LabelPlayerNames.text = "Player" %LabelPlayerKills.text = "Kills" @@ -260,7 +334,7 @@ func updateScore(playerJoined:bool = false): var scores = [] for pl in playersNode.get_children(): if "is_player" in pl: - scores.push_back({ "name": pl.stats.character_name, "kills": pl.stats.kills, "deaths": pl.stats.deaths}) + scores.push_back({"name": pl.stats.character_name, "kills": pl.stats.kills, "deaths": pl.stats.deaths}) pass pass scores.sort_custom(sortScoreArr) @@ -298,7 +372,7 @@ func updateScore(playerJoined:bool = false): for sc in scores: if cnt == 0: %LabelRoundWinner.text = sc.name + " WINS THE ROUND!" - if cnt != 0 and (scores[cnt].kills < scores[cnt-1].kills or scores[cnt].deaths > scores[cnt-1].deaths): + if cnt != 0 and (scores[cnt].kills < scores[cnt - 1].kills or scores[cnt].deaths > scores[cnt - 1].deaths): nr += 1 %LabelPlayerNr.text += "\r\n" + str(nr) %LabelPlayerNames.text += "\r\n" + sc.name diff --git a/src/scripts/entities/player/player.gd b/src/scripts/entities/player/player.gd index 1c3fa87..76cb7af 100644 --- a/src/scripts/entities/player/player.gd +++ b/src/scripts/entities/player/player.gd @@ -23,8 +23,40 @@ const JUMP_VELOCITY = -400.0 var held_entity = null var grabbed_entity = null +@export var held_entity_path: String = "": + set(value): + Console.print("Running player set held_entity_path") + if value != "": + self.held_entity = get_node_or_null(value) + if self.held_entity != null: + Console.print("Client ", multiplayer.get_unique_id(), " synced held_entity to: ", held_entity.name) + else: + Console.print("Client ", multiplayer.get_unique_id(), " failed to find held_entity at path: ", value) + else: + if self.held_entity != null: + Console.print("Client ", multiplayer.get_unique_id(), " about to release held_entity: ", self.held_entity.name) + self.held_entity.release() + self.held_entity = null + Console.print("Client ", multiplayer.get_unique_id(), " cleared held_entity via sync") + held_entity_path = value + +@export var grabbed_entity_path: String = "": + set(value): + Console.print("Running player set grabbed_entity_path") + if value != "": + self.grabbed_entity = get_node_or_null(value) + if self.grabbed_entity != null: + self.grabbed_entity.grab(self) + Console.print("Client ", multiplayer.get_unique_id(), " synced grabbed_entity to: ", self.grabbed_entity.name) + else: + Console.print("Client ", multiplayer.get_unique_id(), " failed to find grabbed_entity at path: ", value) + else: + if self.grabbed_entity != null: + self.grabbed_entity.release() + self.grabbed_entity = null + var is_player = true -@export var direction_vector = Vector2(0,0) +@export var direction_vector = Vector2(0, 0) const ANIMATIONS = { "IDLE": { @@ -112,8 +144,8 @@ const ANIMATIONS = { "nextAnimation": "IDLE" }, "LIFT": { - "frames": [19,30], - "frameDurations": [70,70], + "frames": [19, 30], + "frameDurations": [70, 70], "loop": false, "nextAnimation": "IDLE_HOLD" }, @@ -124,8 +156,8 @@ const ANIMATIONS = { "nextAnimation": null }, "RUN_PUSH": { - "frames": [30,29,30,31], - "frameDurations": [260,260,260,260], + "frames": [30, 29, 30, 31], + "frameDurations": [260, 260, 260, 260], "loop": true, "nextAnimation": null } @@ -142,14 +174,15 @@ enum Direction { UP_LEFT = 5 } -@export var direction = Vector2(0,0) # default down -@export var last_direction = Vector2(0,1) +@export var direction = Vector2(0, 0) # default down +@export var last_direction = Vector2(0, 1) @export var current_direction = Direction.DOWN var current_frame = 0 var time_since_last_frame = 0.0 -@export var current_animation:String = "IDLE": +@export var current_animation: String = "IDLE": set(iAnimation): if current_animation != iAnimation: + Console.print("Animation changed from ", current_animation, " to: ", iAnimation) current_frame = 0 time_since_last_frame = 0 current_animation = iAnimation @@ -158,8 +191,8 @@ var time_since_last_frame = 0.0 var liftable = true var is_demo_character = false -var invul_timer:float = 0.0 -var invul_duration:float = 2.0 +var invul_timer: float = 0.0 +var invul_duration: float = 2.0 var knockback_strength: float = 160.0 var knockback_duration: float = 0.4 var knockback_timer: float = 0.0 @@ -177,7 +210,7 @@ var knockback_direction: Vector2 = Vector2.ZERO @export var is_moving = false @export var isDemoCharacter = false -@export var stats:CharacterStats = CharacterStats.new() +@export var stats: CharacterStats = CharacterStats.new() signal player_died @@ -235,6 +268,7 @@ func _physics_process(delta: float) -> void: if isDemoCharacter: pass else: + # Entity synchronization is now handled by setter functions when paths change if get_multiplayer_authority() == multiplayer.get_unique_id(): _handleInput() if stats.hp > 0: # only allow to move if we are alive... @@ -301,11 +335,12 @@ func _apply_movement_from_input(_delta: float) -> void: pass pass movespeed *= terrainMultiplier - if grabbed_entity != null: + # Only restrict movement when grabbing/pushing entities, not when lifting/holding + if grabbed_entity != null and held_entity == null: grabMultiplier = 0.3 # set direction to only be last_direction or inverse last_direction if direction != Vector2.ZERO: - var inverseLastDirection = last_direction*-1 + var inverseLastDirection = last_direction * -1 if abs(direction.angle_to(last_direction)) > abs(direction.angle_to(inverseLastDirection)): direction = inverseLastDirection else: @@ -332,15 +367,29 @@ func _apply_movement_from_input(_delta: float) -> void: pass if is_attacking: + print("Player ", multiplayer.get_unique_id(), " is attacking, held_entity: ", held_entity) if grabbed_entity == null and held_entity == null and current_animation != "THROW" and current_animation != "DAMAGE" and current_animation != "DIE": current_frame = 0 time_since_last_frame = 0 current_animation = "SWORD" if held_entity != null: + print("Player ", multiplayer.get_unique_id(), " throwing held_entity, is_server: ", multiplayer.is_server()) + Console.print("Player ", multiplayer.get_unique_id(), " throwing held_entity, is_server: ", multiplayer.is_server()) # throw it: - held_entity.throw(last_direction) - current_animation = "THROW" - held_entity = null + if multiplayer.is_server(): + # Server can throw directly + held_entity.throw(last_direction) + current_animation = "THROW" + held_entity = null + held_entity_path = "" + else: + # Client requests throw from server + print("Player ", multiplayer.get_unique_id(), " requesting throw via RPC") + Console.print("Player ", multiplayer.get_unique_id(), " requesting throw via RPC") + MultiplayerManager.request_throw_pot.rpc_id(1, held_entity.get_path(), multiplayer.get_unique_id(), last_direction) + else: + print("Player ", multiplayer.get_unique_id(), " tried to throw but held_entity is null!") + Console.print("Player ", multiplayer.get_unique_id(), " tried to throw but held_entity is null!") is_releasing = false is_grabbing = false is_lifting = false @@ -352,6 +401,7 @@ func _apply_movement_from_input(_delta: float) -> void: if grabbed_entity != null and "release" in grabbed_entity: grabbed_entity.release() grabbed_entity = null + grabbed_entity_path = "" is_releasing = false is_grabbing = false use_button_up = false @@ -359,12 +409,17 @@ func _apply_movement_from_input(_delta: float) -> void: if is_releasing: if held_entity != null: if velocity.x != 0 or velocity.y != 0: - held_entity.throw(last_direction) - held_entity = null - current_animation = "THROW" + if multiplayer.is_server(): + held_entity.throw(last_direction) + held_entity = null + held_entity_path = "" + current_animation = "THROW" + else: + MultiplayerManager.request_throw_pot.rpc_id(1, held_entity.get_path(), multiplayer.get_unique_id(), last_direction) else: if held_entity.put_down(): held_entity = null + held_entity_path = "" if grabbed_entity != null and "release" in grabbed_entity: grabbed_entity.release() grabbed_entity = null @@ -374,17 +429,26 @@ func _apply_movement_from_input(_delta: float) -> void: is_lifting = false if held_entity == null and grabbed_entity == null and is_grabbing: - var areas:Array[Area2D] = $Area2DPickup.get_overlapping_areas() + var areas: Array[Area2D] = $Area2DPickup.get_overlapping_areas() if areas.size() != 0: - for a:Area2D in areas: + for a: Area2D in areas: # make sure we are looking in direction of the entity var player_to_pot = (a.get_parent().global_position - self.global_position).normalized() if player_to_pot.dot(self.last_direction) > 0.78: - if "grab" in a.get_parent() and a.get_parent().grab(self) == true: - if held_entity == null: - is_lifting = false - grabbed_entity = a.get_parent() - current_animation = "IDLE_PUSH" + var pot = a.get_parent() + if "grab" in pot: + if multiplayer.is_server(): + # Server can interact directly + if pot.grab(self) == true: + if held_entity == null: + is_lifting = false + grabbed_entity = pot + grabbed_entity_path = str(pot.get_path()) + current_animation = "IDLE_PUSH" + else: + # Client uses RPC to request from server + if "request_grab_pot" in pot: + pot.request_grab_pot.rpc(pot.get_path(), multiplayer.get_unique_id()) break pass @@ -393,9 +457,9 @@ func _apply_movement_from_input(_delta: float) -> void: pass if held_entity == null and is_lifting: - var areas:Array[Area2D] = $Area2DPickup.get_overlapping_areas() + var areas: Array[Area2D] = $Area2DPickup.get_overlapping_areas() if areas.size() != 0: - for a:Area2D in areas: + for a: Area2D in areas: if "lift" in a.get_parent() and a.get_parent() != self and "liftable" in a.get_parent(): # make sure we are looking in direction of the entity var player_to_pot = (a.get_parent().global_position - self.global_position).normalized() @@ -404,9 +468,28 @@ func _apply_movement_from_input(_delta: float) -> void: grabbed_entity.release() grabbed_entity = null is_grabbing = false - held_entity = a.get_parent() - held_entity.lift(self) + + var pot = a.get_parent() + held_entity = pot + held_entity_path = str(pot.get_path()) + pot.lift(self) current_animation = "LIFT" + + if multiplayer.is_server(): + # Server can interact directly + held_entity = pot + #held_entity_path = str(pot.get_path()) + #pot.lift(self) + #current_animation = "LIFT" + else: + # Client uses RPC to request from server + Console.print("Client ", multiplayer.get_unique_id(), " calling request_lift_pot RPC for pot at: ", pot.get_path()) + Console.print("Client ", multiplayer.get_unique_id(), " pot object: ", pot) + Console.print("Client ", multiplayer.get_unique_id(), " is_server: ", multiplayer.is_server()) + Console.print("Client ", multiplayer.get_unique_id(), " about to call RPC to server (peer 1)") + + MultiplayerManager.request_lift_pot.rpc_id(1, pot.get_path(), multiplayer.get_unique_id()) + Console.print("Client ", multiplayer.get_unique_id(), " RPC call completed") break # only allow 1 at a time :) pass pass @@ -419,12 +502,17 @@ func _apply_movement_from_input(_delta: float) -> void: is_lifting = false elif held_entity != null and is_lifting == false: if velocity.x != 0 or velocity.y != 0: - held_entity.throw(last_direction) - held_entity = null - current_animation = "THROW" + if multiplayer.is_server(): + held_entity.throw(last_direction) + held_entity = null + held_entity_path = "" + current_animation = "THROW" + else: + MultiplayerManager.request_throw_pot.rpc_id(1, held_entity.get_path(), multiplayer.get_unique_id(), last_direction) else: if held_entity.put_down(): held_entity = null + held_entity_path = "" else: is_lifting = true if grabbed_entity != null and "release" in grabbed_entity: @@ -478,7 +566,7 @@ func _apply_animations(delta: float): if ANIMATIONS[current_animation]["nextAnimation"] != null: current_frame = 0 current_animation = ANIMATIONS[current_animation]["nextAnimation"] - time_since_last_frame = 0 + time_since_last_frame = 0 pass @@ -635,6 +723,7 @@ func sync_player_stats(stats_dict: Dictionary): # Update visuals _stats_changed(stats) + @rpc("any_peer", "reliable") func take_dmg_sound(): $SfxTakeDamage.play() @@ -656,6 +745,7 @@ func take_damage(iBody: Node2D, _iByWhoOrWhat: Node2D) -> void: if current_animation != "DAMAGE": time_since_last_frame = 0 current_frame = 0 + current_animation = "DAMAGE" # Calculate knockback direction from the damaging body @@ -663,7 +753,12 @@ func take_damage(iBody: Node2D, _iByWhoOrWhat: Node2D) -> void: velocity = knockback_direction * knockback_strength _updateHp() if held_entity != null: - held_entity.throw(knockback_direction) + if multiplayer.is_server(): + held_entity.throw(knockback_direction) + else: + MultiplayerManager.request_throw_pot.rpc_id(1, held_entity.get_path(), multiplayer.get_unique_id(), knockback_direction) + held_entity = null + held_entity_path = "" is_lifting = false if grabbed_entity != null and "release" in grabbed_entity: @@ -721,6 +816,14 @@ func sync_player_deaths(iDeaths: int): MultiplayerManager.updateScore() pass +@rpc("call_local", "reliable") +func sync_animation(animation_name: String): + #print("Client ", multiplayer.get_unique_id(), " received sync_animation: ", animation_name) + current_animation = animation_name + pass + +# RPC functions removed - entity synchronization now handled by setter functions + @rpc("reliable") func _updateScore(): MultiplayerManager.updateScore() @@ -735,13 +838,13 @@ func _on_died(): # find spawn point: var spointPouints = get_parent().get_parent().get_node("PlayerSpawnPoints") var targetPos = null - for spawnP:Node2D in spointPouints.get_children(): + for spawnP: Node2D in spointPouints.get_children(): var pos = spawnP.position if targetPos == null: targetPos = pos else: # find spawn position which is furthest from all players... - for pl:CharacterBody2D in get_parent().get_children(): + for pl: CharacterBody2D in get_parent().get_children(): if "is_player" in pl: # compare if pl.position.distance_to(pos) > pl.position.distance_to(targetPos): @@ -753,7 +856,8 @@ func _on_died(): stats.hp = stats.maxhp stats.is_invulnerable = false stats.forceUpdate() - + if current_animation != "IDLE": + Console.print("Animation changed from ", current_animation, " to: ", "IDLE") current_animation = "IDLE" self.set_collision_layer_value(10, true) pass @@ -774,11 +878,20 @@ func not_use(): @rpc("call_local") func grab(): is_grabbing = true - + +@rpc("any_peer", "reliable") +func set_held_entity_path_rpc(entity_path: String): + held_entity_path = entity_path + +@rpc("any_peer", "reliable") +func set_grabbed_entity_path_rpc(entity_path: String): + grabbed_entity_path = entity_path + + @rpc("call_local") func lift(): is_lifting = !is_lifting -@rpc("call_local") +@rpc("call_local") func release(): is_releasing = true diff --git a/src/scripts/entities/player/player.tscn b/src/scripts/entities/player/player.tscn index f849931..0066941 100644 --- a/src/scripts/entities/player/player.tscn +++ b/src/scripts/entities/player/player.tscn @@ -76,6 +76,12 @@ properties/10/replication_mode = 2 properties/11/path = NodePath(".:direction_vector") properties/11/spawn = true properties/11/replication_mode = 2 +properties/12/path = NodePath(".:held_entity_path") +properties/12/spawn = true +properties/12/replication_mode = 2 +properties/13/path = NodePath(".:grabbed_entity_path") +properties/13/spawn = true +properties/13/replication_mode = 2 [sub_resource type="Gradient" id="Gradient_hsjxb"] offsets = PackedFloat32Array(0.847255, 0.861575) @@ -300,6 +306,21 @@ theme_override_font_sizes/font_size = 6 text = "Playername" horizontal_alignment = 1 +[node name="LabelCurrentAnimation" type="Label" parent="."] +z_index = 18 +z_as_relative = false +offset_left = -29.82 +offset_top = -33.945 +offset_right = 30.18 +offset_bottom = -27.945 +size_flags_horizontal = 3 +size_flags_vertical = 6 +theme_override_constants/outline_size = 6 +theme_override_fonts/font = ExtResource("21_pyh4g") +theme_override_font_sizes/font_size = 6 +text = "CurAnim" +horizontal_alignment = 1 + [node name="CanvasLayer" type="CanvasLayer" parent="."] follow_viewport_enabled = true diff --git a/src/scripts/entities/player/player.tscn205502527540.tmp b/src/scripts/entities/player/player.tscn205502527540.tmp new file mode 100644 index 0000000..1815d2c --- /dev/null +++ b/src/scripts/entities/player/player.tscn205502527540.tmp @@ -0,0 +1,356 @@ +[gd_scene load_steps=44 format=3 uid="uid://dgtfy455abe1t"] + +[ext_resource type="Script" uid="uid://cvvy2s6620mcw" path="res://scripts/entities/player/player.gd" id="1_sgemx"] +[ext_resource type="Texture2D" uid="uid://bkninujaqqvb1" path="res://assets/gfx/Puny-Characters/Layer 0 - Skins/Human1_1.png" id="3_0818e"] +[ext_resource type="Shader" uid="uid://cfd38qf1ojmft" path="res://assets/shaders/cloth.gdshader" id="4_6nxnb"] +[ext_resource type="Script" uid="uid://yid4hjp68enj" path="res://scripts/entities/player/camera_2d.gd" id="4_n1hb6"] +[ext_resource type="Texture2D" uid="uid://dx1fovugabbwc" path="res://assets/gfx/Puny-Characters/Layer 1 - Shoes/IronBoots.png" id="5_2bw0v"] +[ext_resource type="Texture2D" uid="uid://bbqk2lcs772q3" path="res://assets/gfx/Puny-Characters/Layer 2 - Clothes/Armour Body/BronzeArmour.png" id="5_7drg4"] +[ext_resource type="Texture2D" uid="uid://bkiexfnpcaxwa" path="res://assets/gfx/Puny-Characters/Layer 4 - Hairstyle/Facial Hairstyles/Mustache1White.png" id="7_2bw0v"] +[ext_resource type="Texture2D" uid="uid://0lmhxwt7k3e4" path="res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eye Color/EyecolorLightLime.png" id="8_68eso"] +[ext_resource type="Texture2D" uid="uid://ccu5cpyo7jpdr" path="res://assets/gfx/Puny-Characters/Layer 4 - Hairstyle/Hairstyles/MHairstyle8White.png" id="8_pyh4g"] +[ext_resource type="Texture2D" uid="uid://b4vh2v0x58v2f" path="res://assets/gfx/Puny-Characters/Layer 5 - Eyes/Eyelashes/MEyelash1.png" id="9_cvm1n"] +[ext_resource type="Texture2D" uid="uid://jxo0e2x145rs" path="res://assets/gfx/Puny-Characters/Layer 7 - Add-ons/Elf Add-ons/ElfEars3.png" id="10_o8aek"] +[ext_resource type="Texture2D" uid="uid://cu5fkio3ajr5i" path="res://assets/gfx/Puny-Characters/Layer 6 - Headgears/French/MusketeerHatPurple.png" id="11_idlgo"] +[ext_resource type="Texture2D" uid="uid://bloqx3mibftjn" path="res://assets/gfx/Puny-Characters/WeaponOverlayer.png" id="12_0818e"] +[ext_resource type="AudioStream" uid="uid://cbio6f0ssxvd6" path="res://assets/audio/sfx/walk/stone/walk_stone_1.wav.mp3" id="14_0818e"] +[ext_resource type="AudioStream" uid="uid://dq1va2882v23v" path="res://assets/audio/sfx/walk/stone/walk_stone_2.wav.mp3" id="15_2bw0v"] +[ext_resource type="AudioStream" uid="uid://dsuf4oa710gi8" path="res://assets/audio/sfx/walk/stone/walk_stone_3.wav.mp3" id="16_pyh4g"] +[ext_resource type="AudioStream" uid="uid://fvhvmxtcq018" path="res://assets/audio/sfx/walk/stone/walk_stone_4.wav.mp3" id="17_jfw4q"] +[ext_resource type="AudioStream" uid="uid://cw74evef8fm0t" path="res://assets/audio/sfx/walk/stone/walk_stone_5.wav.mp3" id="18_fj670"] +[ext_resource type="AudioStream" uid="uid://c43fyqtos11fd" path="res://assets/audio/sfx/walk/stone/walk_stone_6.wav.mp3" id="19_0j5vc"] +[ext_resource type="FontFile" uid="uid://bajcvmidrnc33" path="res://assets/fonts/standard_font.png" id="21_pyh4g"] +[ext_resource type="AudioStream" uid="uid://b4ng0o2en2hkm" path="res://assets/audio/sfx/player/fall_out/player_fall_infinitely-02.wav.mp3" id="22_jfw4q"] +[ext_resource type="AudioStream" uid="uid://bi546r2d771yg" path="res://assets/audio/sfx/player/take_damage/player_damaged_01.wav.mp3" id="23_7puce"] +[ext_resource type="AudioStream" uid="uid://b8trgc0pbomud" path="res://assets/audio/sfx/player/take_damage/player_damaged_02.wav.mp3" id="24_3n1we"] +[ext_resource type="AudioStream" uid="uid://dsnvagvhs152x" path="res://assets/audio/sfx/player/take_damage/player_damaged_03.wav.mp3" id="25_h8vet"] +[ext_resource type="AudioStream" uid="uid://ce51n4tvvflro" path="res://assets/audio/sfx/player/take_damage/player_damaged_04.wav.mp3" id="26_1rlbx"] +[ext_resource type="AudioStream" uid="uid://caclaiagfnr2o" path="res://assets/audio/sfx/player/take_damage/player_damaged_05.wav.mp3" id="27_1sdav"] +[ext_resource type="AudioStream" uid="uid://dighi525ty7sl" path="res://assets/audio/sfx/player/take_damage/player_damaged_06.wav.mp3" id="28_x7koh"] +[ext_resource type="AudioStream" uid="uid://bdhmel5vyixng" path="res://assets/audio/sfx/player/take_damage/player_damaged_07.wav.mp3" id="29_jl8uc"] + +[sub_resource type="Gradient" id="Gradient_n1hb6"] +offsets = PackedFloat32Array(0.742243, 0.75179) +colors = PackedColorArray(1, 1, 1, 1, 1, 1, 1, 0) + +[sub_resource type="GradientTexture2D" id="GradientTexture2D_n1hb6"] +gradient = SubResource("Gradient_n1hb6") +fill = 1 +fill_from = Vector2(0.508547, 0.487179) +fill_to = Vector2(0.961538, 0.034188) + +[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_fgrik"] +properties/0/path = NodePath(".:position") +properties/0/spawn = true +properties/0/replication_mode = 1 +properties/1/path = NodePath(".:is_attacking") +properties/1/spawn = true +properties/1/replication_mode = 2 +properties/2/path = NodePath(".:is_using") +properties/2/spawn = true +properties/2/replication_mode = 2 +properties/3/path = NodePath(".:current_animation") +properties/3/spawn = true +properties/3/replication_mode = 2 +properties/4/path = NodePath(".:last_direction") +properties/4/spawn = true +properties/4/replication_mode = 2 +properties/5/path = NodePath(".:is_grabbing") +properties/5/spawn = true +properties/5/replication_mode = 2 +properties/6/path = NodePath(".:is_lifting") +properties/6/spawn = true +properties/6/replication_mode = 2 +properties/7/path = NodePath(".:use_button_up") +properties/7/spawn = true +properties/7/replication_mode = 2 +properties/8/path = NodePath(".:use_button_down") +properties/8/spawn = true +properties/8/replication_mode = 2 +properties/9/path = NodePath(".:is_moving") +properties/9/spawn = true +properties/9/replication_mode = 2 +properties/10/path = NodePath(".:collision_layer") +properties/10/spawn = true +properties/10/replication_mode = 2 +properties/11/path = NodePath(".:direction_vector") +properties/11/spawn = true +properties/11/replication_mode = 2 +properties/12/path = NodePath(".:held_entity_path") +properties/12/spawn = true +properties/12/replication_mode = 2 +properties/13/path = NodePath(".:grabbed_entity_path") +properties/13/spawn = true +properties/13/replication_mode = 2 + +[sub_resource type="Gradient" id="Gradient_hsjxb"] +offsets = PackedFloat32Array(0.847255, 0.861575) +colors = PackedColorArray(0, 0, 0, 0.611765, 0, 0, 0, 0) + +[sub_resource type="GradientTexture2D" id="GradientTexture2D_0818e"] +gradient = SubResource("Gradient_hsjxb") +width = 14 +height = 6 +fill = 1 +fill_from = Vector2(0.504274, 0.478632) +fill_to = Vector2(0.897436, 0.0769231) + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_2bw0v"] +shader = ExtResource("4_6nxnb") +shader_parameter/original_0 = Color(0, 0, 0, 1) +shader_parameter/original_1 = Color(0, 0, 0, 1) +shader_parameter/original_2 = Color(0, 0, 0, 1) +shader_parameter/original_3 = Color(0, 0, 0, 1) +shader_parameter/original_4 = Color(0, 0, 0, 1) +shader_parameter/original_5 = Color(0, 0, 0, 1) +shader_parameter/original_6 = Color(0, 0, 0, 1) +shader_parameter/replace_0 = Color(0, 0, 0, 1) +shader_parameter/replace_1 = Color(0, 0, 0, 1) +shader_parameter/replace_2 = Color(0, 0, 0, 1) +shader_parameter/replace_3 = Color(0, 0, 0, 1) +shader_parameter/replace_4 = Color(0, 0, 0, 1) +shader_parameter/replace_5 = Color(0, 0, 0, 1) +shader_parameter/replace_6 = Color(0, 0, 0, 1) +shader_parameter/tint = Color(1, 1, 1, 1) + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_8ugno"] +shader = ExtResource("4_6nxnb") +shader_parameter/original_0 = Color(0, 0, 0, 1) +shader_parameter/original_1 = Color(0, 0, 0, 1) +shader_parameter/original_2 = Color(0, 0, 0, 1) +shader_parameter/original_3 = Color(0, 0, 0, 1) +shader_parameter/original_4 = Color(0, 0, 0, 1) +shader_parameter/original_5 = Color(0, 0, 0, 1) +shader_parameter/original_6 = Color(0, 0, 0, 1) +shader_parameter/replace_0 = Color(0, 0, 0, 1) +shader_parameter/replace_1 = Color(0, 0, 0, 1) +shader_parameter/replace_2 = Color(0, 0, 0, 1) +shader_parameter/replace_3 = Color(0, 0, 0, 1) +shader_parameter/replace_4 = Color(0, 0, 0, 1) +shader_parameter/replace_5 = Color(0, 0, 0, 1) +shader_parameter/replace_6 = Color(0, 0, 0, 1) +shader_parameter/tint = Color(1, 1, 1, 1) + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_sgemx"] +size = Vector2(8, 6) + +[sub_resource type="AudioStreamRandomizer" id="AudioStreamRandomizer_40ewq"] +streams_count = 6 +stream_0/stream = ExtResource("14_0818e") +stream_1/stream = ExtResource("15_2bw0v") +stream_2/stream = ExtResource("16_pyh4g") +stream_3/stream = ExtResource("17_jfw4q") +stream_4/stream = ExtResource("18_fj670") +stream_5/stream = ExtResource("19_0j5vc") + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_0818e"] +size = Vector2(10, 8) + +[sub_resource type="Gradient" id="Gradient_2bw0v"] +offsets = PackedFloat32Array(0) +colors = PackedColorArray(0, 0, 0, 1) + +[sub_resource type="GradientTexture1D" id="GradientTexture1D_pyh4g"] +gradient = SubResource("Gradient_2bw0v") +width = 16 + +[sub_resource type="Gradient" id="Gradient_jfw4q"] +offsets = PackedFloat32Array(1) +colors = PackedColorArray(1, 0.231947, 0.351847, 1) + +[sub_resource type="GradientTexture1D" id="GradientTexture1D_fj670"] +gradient = SubResource("Gradient_jfw4q") +width = 16 + +[sub_resource type="AudioStreamRandomizer" id="AudioStreamRandomizer_hnhes"] +streams_count = 7 +stream_0/stream = ExtResource("23_7puce") +stream_1/stream = ExtResource("24_3n1we") +stream_2/stream = ExtResource("25_h8vet") +stream_3/stream = ExtResource("26_1rlbx") +stream_4/stream = ExtResource("27_1sdav") +stream_5/stream = ExtResource("28_x7koh") +stream_6/stream = ExtResource("29_jl8uc") + +[node name="Player" type="CharacterBody2D"] +collision_layer = 512 +collision_mask = 704 +script = ExtResource("1_sgemx") +held_entity_path = null +grabbed_entity_path = null +direction_vector = null +direction = null +last_direction = null +current_direction = null +current_animation = null +is_attacking = null +is_using = null +is_grabbing = null +is_lifting = null +is_releasing = null +use_button_down = null +use_button_up = null +attack_button_down = null +attack_button_up = null +is_moving = null +isDemoCharacter = null + +[node name="PlayerLight" type="PointLight2D" parent="."] +z_index = 10 +position = Vector2(-1, -6) +blend_mode = 2 +range_layer_max = 2 +texture = SubResource("GradientTexture2D_n1hb6") + +[node name="PlayerSynchronizer" type="MultiplayerSynchronizer" parent="."] +replication_config = SubResource("SceneReplicationConfig_fgrik") + +[node name="Sprite2DShadow" type="Sprite2D" parent="."] +position = Vector2(0, 2) +texture = SubResource("GradientTexture2D_0818e") + +[node name="Sprite2DBody" type="Sprite2D" parent="."] +material = SubResource("ShaderMaterial_2bw0v") +position = Vector2(0, -5) +texture = ExtResource("3_0818e") +hframes = 35 +vframes = 8 + +[node name="Sprite2DBoots" type="Sprite2D" parent="."] +material = SubResource("ShaderMaterial_8ugno") +position = Vector2(0, -5) +texture = ExtResource("5_2bw0v") +hframes = 35 +vframes = 8 + +[node name="Sprite2DArmour" type="Sprite2D" parent="."] +material = SubResource("ShaderMaterial_8ugno") +position = Vector2(0, -5) +texture = ExtResource("5_7drg4") +hframes = 35 +vframes = 8 + +[node name="Sprite2DFacialHair" type="Sprite2D" parent="."] +material = SubResource("ShaderMaterial_8ugno") +position = Vector2(0, -5) +texture = ExtResource("7_2bw0v") +hframes = 35 +vframes = 8 + +[node name="Sprite2DHair" type="Sprite2D" parent="."] +material = SubResource("ShaderMaterial_8ugno") +position = Vector2(0, -5) +texture = ExtResource("8_pyh4g") +hframes = 35 +vframes = 8 + +[node name="Sprite2DEyes" type="Sprite2D" parent="."] +material = SubResource("ShaderMaterial_8ugno") +position = Vector2(0, -5) +texture = ExtResource("8_68eso") +hframes = 35 +vframes = 8 + +[node name="Sprite2DEyeLashes" type="Sprite2D" parent="."] +material = SubResource("ShaderMaterial_8ugno") +position = Vector2(0, -5) +texture = ExtResource("9_cvm1n") +hframes = 35 +vframes = 8 + +[node name="Sprite2DAddons" type="Sprite2D" parent="."] +material = SubResource("ShaderMaterial_8ugno") +position = Vector2(0, -5) +texture = ExtResource("10_o8aek") +hframes = 35 +vframes = 8 + +[node name="Sprite2DHeadgear" type="Sprite2D" parent="."] +material = SubResource("ShaderMaterial_8ugno") +position = Vector2(0, -5) +texture = ExtResource("11_idlgo") +hframes = 35 +vframes = 8 + +[node name="Sprite2DWeapon" type="Sprite2D" parent="."] +material = SubResource("ShaderMaterial_8ugno") +position = Vector2(0, -5) +texture = ExtResource("12_0818e") +hframes = 35 +vframes = 8 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +position = Vector2(0, -1) +shape = SubResource("RectangleShape2D_sgemx") + +[node name="Camera2D" type="Camera2D" parent="."] +zoom = Vector2(3, 3) +position_smoothing_enabled = true +script = ExtResource("4_n1hb6") + +[node name="Timer" type="Timer" parent="Camera2D"] + +[node name="SfxWalk" type="AudioStreamPlayer2D" parent="."] +stream = SubResource("AudioStreamRandomizer_40ewq") +volume_db = -12.0 +attenuation = 8.28211 +bus = &"Sfx" + +[node name="TimerWalk" type="Timer" parent="SfxWalk"] +wait_time = 0.3 +one_shot = true + +[node name="Area2DPickup" type="Area2D" parent="."] +collision_layer = 0 +collision_mask = 1536 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2DPickup"] +position = Vector2(0, -1) +shape = SubResource("RectangleShape2D_0818e") +debug_color = Color(0.7, 0.495943, 0.135446, 0.42) + +[node name="LabelPlayerName" type="Label" parent="."] +z_index = 18 +z_as_relative = false +offset_left = -29.82 +offset_top = -26.39 +offset_right = 30.18 +offset_bottom = -20.39 +size_flags_horizontal = 3 +size_flags_vertical = 6 +theme_override_constants/outline_size = 6 +theme_override_fonts/font = ExtResource("21_pyh4g") +theme_override_font_sizes/font_size = 6 +text = "Playername" +horizontal_alignment = 1 + +[node name="CanvasLayer" type="CanvasLayer" parent="."] +follow_viewport_enabled = true + +[node name="TextureProgressBarHealth" type="TextureProgressBar" parent="."] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -8.0 +offset_top = -16.0 +offset_right = 8.0 +offset_bottom = -15.0 +grow_horizontal = 2 +grow_vertical = 2 +value = 100.0 +texture_under = SubResource("GradientTexture1D_pyh4g") +texture_progress = SubResource("GradientTexture1D_fj670") + +[node name="SfxDie" type="AudioStreamPlayer2D" parent="."] +stream = ExtResource("22_jfw4q") +bus = &"Sfx" + +[node name="SfxTakeDamage" type="AudioStreamPlayer2D" parent="."] +stream = SubResource("AudioStreamRandomizer_hnhes") +bus = &"Sfx" + +[node name="TimerGrab" type="Timer" parent="."] +wait_time = 0.2 +one_shot = true + +[connection signal="timeout" from="Camera2D/Timer" to="Camera2D" method="_on_timer_timeout"] diff --git a/src/scripts/entities/world/pot.gd b/src/scripts/entities/world/pot.gd index b57c7d0..c80696b 100644 --- a/src/scripts/entities/world/pot.gd +++ b/src/scripts/entities/world/pot.gd @@ -8,8 +8,14 @@ var entity_id = "" var object_name = "bomb" @export var is_being_thrown = false -@export var is_being_lifted = false -@export var is_being_put_down = false +@export var is_being_lifted = false: + set(value): + is_being_lifted = value + Console.print("Pot is_being_lifted changed to: ", value, " on peer: ", multiplayer.get_unique_id()) +@export var is_being_put_down = false: + set(value): + is_being_put_down = value + Console.print("Pot is_being_put_down changed to: ", value, " on peer: ", multiplayer.get_unique_id()) @export var is_being_grabbed = false @export var is_moving = false @export var is_spawning = false @@ -44,6 +50,24 @@ var re_enable_time = 0.17 var previousFrameVel = Vector2.ZERO var hasShownSmokePuffs = false +@export var holder_peer_id: int = 0: + set(value): + holder_peer_id = value + Console.print("Pot holder_peer_id changed to: ", value, " on peer: ", multiplayer.get_unique_id()) + # Clear lifting state when holder is cleared, but only if we're not in the middle of lifting + if value == 0 and !is_being_lifted: + is_being_lifted = false + holder = null + elif value != 0: + # Find the holder by peer ID + var spawn_root = get_tree().get_current_scene().get_node("SpawnRoot") + if spawn_root: + holder = spawn_root.get_node_or_null(str(value)) + if holder: + Console.print("Pot found holder: ", holder.name, " for peer ID: ", value) + else: + Console.print("Pot failed to find holder for peer ID: ", value) + func _ready() -> void: if is_spawning: liftable = false @@ -58,6 +82,51 @@ func _ready() -> void: pass func _physics_process(delta: float) -> void: + # Update holder based on holder_peer_id for network sync + if holder_peer_id != 0: + var player = get_tree().get_current_scene().get_node("SpawnRoot").get_node_or_null(str(holder_peer_id)) + if player and holder != player: + Console.print("Pot updating holder from ", holder, " to ", player.name) + holder = player + # Ensure lifting state is synced when holder changes + if !is_being_lifted and !is_being_grabbed: + is_being_lifted = true + lift_progress = 0.0 + elif !player: + Console.print("Pot could not find player with peer_id: ", holder_peer_id) + elif holder_peer_id == 0 and holder != null: + Console.print("Pot clearing holder (holder_peer_id is 0)") + holder = null + is_being_lifted = false + + # Handle lifted pot position on ALL clients for smooth following + if is_being_lifted and holder: + $GPUParticles2D.emitting = false + if lift_progress < 1.0: + lift_progress += delta * lift_speed + lift_progress = min(lift_progress, 1.0) + + # Smoothly interpolate from current position to above holder during lifting + var target_pos = holder.global_position + Vector2(0, -8) + if lift_progress < 1.0: + global_position = global_position.lerp(target_pos, lift_progress) + positionZ = lift_height * lift_progress + else: + # When fully lifted, maintain exact position above holder + global_position = target_pos + positionZ = lift_height + else: + # Debug: Check why pot is not following + if is_being_lifted and !holder: + Console.print("Pot is_being_lifted but holder is null! holder_peer_id: ", holder_peer_id) + # Fix inconsistent state + if holder_peer_id == 0: + is_being_lifted = false + elif !is_being_lifted and holder: + # Pot has holder but is_being_lifted is false - this is normal during transitions + pass + update_sprite_scale() + if multiplayer.is_server(): if is_being_thrown: re_enable_collision_after_time -= delta @@ -81,7 +150,7 @@ func _physics_process(delta: float) -> void: $GPUParticles2D.emitting = true $GPUParticles2D/TimerSmokeParticles.start() if abs(velocityZ) > minBounceVelocity: - velocityZ = -velocityZ * bounceRestitution + velocityZ = - velocityZ * bounceRestitution else: velocityZ = 0 is_being_thrown = false @@ -117,23 +186,7 @@ func _physics_process(delta: float) -> void: global_position = put_down_start_pos.lerp(put_down_target_pos, 1.0 - lift_progress) positionZ = lift_height * lift_progress update_sprite_scale() - elif is_being_lifted: - # Smooth lifting animation - if holder: - $GPUParticles2D.emitting = false - target_position = holder.global_position - #target_position.y -= 2 - if lift_progress < 1.0: - lift_progress += delta * lift_speed - lift_progress = min(lift_progress, 1.0) - global_position = global_position.lerp(target_position, lift_progress) - positionZ = lift_height * lift_progress - else: - lift_progress = 1.0 - global_position = target_position - positionZ = lift_height - update_sprite_scale() - pass + elif is_being_grabbed: #if velocity != Vector2.ZERO and velocity != previousFrameVel and hasShownSmokePuffs == false: if velocity != Vector2.ZERO: @@ -150,7 +203,7 @@ func _physics_process(delta: float) -> void: move_and_collide(velocity * delta) previousFrameVel = velocity pass - else: # it just spawned: + elif !is_being_lifted: # it just spawned or is free-falling: # Apply gravity to vertical movement velocityZ += accelerationZ * delta positionZ += velocityZ * delta @@ -171,7 +224,7 @@ func _physics_process(delta: float) -> void: $GPUParticles2D.emitting = true $GPUParticles2D/TimerSmokeParticles.start() if abs(velocityZ) > minBounceVelocity: - velocityZ = -velocityZ * bounceRestitution + velocityZ = - velocityZ * bounceRestitution else: velocityZ = 0 velocity = velocity.lerp(Vector2.ZERO, 0.5) @@ -203,6 +256,19 @@ func _physics_process(delta: float) -> void: if $SfxDrag2.playing: $SfxDrag2.stop() $GPUParticles2D.emitting = false + # Update position on client to follow holder + if holder: + target_position = holder.global_position + Vector2(0, -8) + if lift_progress < 1.0: + lift_progress += delta * lift_speed + lift_progress = min(lift_progress, 1.0) + global_position = global_position.lerp(target_position, lift_progress) + positionZ = lift_height * lift_progress + else: + # When fully lifted, maintain exact position above holder + global_position = target_position + positionZ = lift_height + update_sprite_scale() elif is_being_grabbed: if is_moving: $GPUParticles2D.emitting = true @@ -224,6 +290,10 @@ func _physics_process(delta: float) -> void: if $SfxDrag2.playing: $SfxDrag2.stop() pass + # Fix stuck put_down state + if is_being_put_down and lift_progress <= 0: + Console.print("Pot is_being_put_down but lift_progress is 0, clearing state") + is_being_put_down = false update_sprite_scale() if is_destroyed and !destroy_initiated: destroy_initiated = true @@ -238,7 +308,7 @@ func update_sprite_scale() -> void: var posY = positionZ # Direct mapping of Z to Y offset var sc = 1.0 + (0.1 * height_factor) # Slightly less scale change than loot (0.3 instead of 0.8) $Sprite2D.scale = Vector2(sc, sc) - $Sprite2D.offset.y = -posY + $Sprite2D.offset.y = - posY # Also update shadow position and scale if is_being_lifted: $Sprite2D.z_as_relative = false @@ -249,7 +319,7 @@ func update_sprite_scale() -> void: $Sprite2D.z_index = 0 $Sprite2DShadow.offset.y = 0 #$Sprite2DShadow.scale = Vector2(1.125 * sc, 0.5 * sc) # Scale shadow with height - $Sprite2DShadow.scale = Vector2(1,1) + $Sprite2DShadow.scale = Vector2(1, 1) #$Sprite2DShadow.modulate. func throw(direction: Vector2, initial_velocity: float = 200): @@ -262,23 +332,34 @@ func throw(direction: Vector2, initial_velocity: float = 200): is_being_put_down = false thrown_by = holder holder = null + holder_peer_id = 0 # Clear the network holder reference velocity = direction * initial_velocity velocityZ = throw_height positionZ = lift_height current_height = 0 re_enable_collision_after_time = re_enable_time + +@rpc("any_peer", "reliable") +func throw_rpc(direction: Vector2, initial_velocity: float = 200): + Console.print("Pot throw_rpc called on peer: ", multiplayer.get_unique_id()) + # Only execute on server to avoid conflicts + if multiplayer.is_server(): + throw(direction, initial_velocity) func grab(new_holder: CharacterBody2D) -> bool: if positionZ <= 0 and holder == null: # only allow grab if no previous owner and position is 0 - $GPUParticles2D/TimerSmokeParticles.stop() #reset... + $GPUParticles2D/TimerSmokeParticles.stop() # reset... holder = new_holder + holder_peer_id = new_holder.get_multiplayer_authority() is_being_grabbed = true indicate(false) return true return false func release(): + Console.print("Pot release() called on peer: ", multiplayer.get_unique_id()) holder = null + holder_peer_id = 0 is_being_grabbed = false hasShownSmokePuffs = false indicate(true) @@ -287,6 +368,8 @@ func release(): pass func lift(new_holder: CharacterBody2D): + Console.print("Pot lift() called with holder: ", new_holder.name if new_holder else "null") + Console.print("Pot current authority: ", get_multiplayer_authority()) if (new_holder != holder and holder != null) and "lose_held_entity" in holder: # steal from holder holder.lose_held_entity(self) @@ -294,6 +377,7 @@ func lift(new_holder: CharacterBody2D): $Area2DCollision.set_deferred("monitoring", false) thrown_by = null holder = new_holder + holder_peer_id = new_holder.get_multiplayer_authority() # disable collisions self.set_collision_layer_value(8, false) self.set_collision_mask_value(7, false) @@ -306,7 +390,23 @@ func lift(new_holder: CharacterBody2D): is_being_put_down = false lift_progress = 0.0 velocityZ = 0 + # Store initial position for smooth lifting - don't change current position yet + # The pot will smoothly glide from its current position to above the holder $SfxLand.play() + Console.print("Pot lift() completed, is_being_lifted: ", is_being_lifted) + +@rpc("any_peer", "reliable") +func lift_rpc(holder_path: NodePath): + Console.print("Pot lift_rpc called with holder_path: ", holder_path, " on peer: ", multiplayer.get_unique_id()) + # Only execute on server to avoid conflicts + if multiplayer.is_server(): + # Find the holder by path + var holder_node = get_node_or_null(holder_path) + if holder_node and holder_node is CharacterBody2D: + Console.print("Pot found holder, calling lift()") + lift(holder_node) + else: + Console.print("Pot failed to find holder at path: ", holder_path) func put_down() -> bool: if not is_being_lifted or is_being_put_down: @@ -365,13 +465,13 @@ func put_down() -> bool: put_down_start_pos = global_position thrown_by = null holder = null + holder_peer_id = 0 indicate(true) return true func remove(): - var fade_tween = create_tween() fade_tween.set_trans(Tween.TRANS_CUBIC) fade_tween.set_ease(Tween.EASE_OUT) @@ -442,7 +542,6 @@ func _on_area_2d_collision_body_entered(body: Node2D) -> void: if is_being_thrown == false or body == self or body == thrown_by: return if multiplayer.is_server(): - var collision_shape = $Area2DCollision.get_overlapping_bodies() if collision_shape.size() > 0: @@ -477,6 +576,55 @@ func show_destroy_effect(): self.call_deferred("remove") pass +@rpc("any_peer", "reliable") +func request_grab_pot(pot_path: NodePath, peer_id: int): + if multiplayer.is_server(): + var pot = get_node_or_null(pot_path) + var player = get_tree().get_current_scene().get_node("SpawnRoot").get_node_or_null(str(peer_id)) + if pot and "grab" in pot and player: + if pot.grab(player): + player.grabbed_entity = pot + player.grabbed_entity_path = str(pot.get_path()) + player.current_animation = "IDLE_PUSH" + # Sync animation and entity to all clients + #var all_players = get_tree().get_current_scene().get_node("SpawnRoot").get_children() + #for p in all_players: + #if p.has_method("sync_animation"): + #p.sync_animation.rpc("IDLE_PUSH") + #p.sync_grabbed_entity.rpc(str(pot.get_path())) + +@rpc("any_peer", "reliable") +func request_lift_pot(pot_path: NodePath, peer_id: int): + # This function is now handled by MultiplayerManager + # Keeping it for backward compatibility but it should not be called + print("Pot received request_lift_pot RPC - this should be handled by MultiplayerManager") + pass + +@rpc("any_peer", "reliable") +func request_throw_pot(pot_path: NodePath, peer_id: int, direction: Vector2): + if multiplayer.is_server(): + var pot = get_node_or_null(pot_path) + var player = get_tree().get_current_scene().get_node("SpawnRoot").get_node_or_null(str(peer_id)) + print("Throw request: pot=", pot, " player=", player, " pot.holder_peer_id=", pot.holder_peer_id if pot else "null", " peer_id=", peer_id) + if pot and player: + # Check if the pot is being held by this player (either by holder_peer_id or by checking the holder directly) + if pot.holder_peer_id == peer_id or (pot.holder != null and pot.holder.get_multiplayer_authority() == peer_id): + print("Throw authorized for peer ", peer_id) + pot.throw(direction) + player.held_entity = null + player.held_entity_path = "" + player.current_animation = "THROW" + # Sync pot state to all clients first + pot.sync_pot_state.rpc(false, 0) # Not lifted, no holder + # Sync animation and clear held entity to all clients + var all_players = get_tree().get_current_scene().get_node("SpawnRoot").get_children() + for p in all_players: + if p.has_method("sync_animation"): + p.sync_animation.rpc("THROW") + p.sync_held_entity.rpc("") # Clear held entity + else: + print("Throw denied: holder_peer_id mismatch or holder not found") + @rpc("call_local") func take_damage(_iBody: Node2D, _iByWhoOrWhat: Node2D) -> void: is_destroyed = true # will trigger show_destroy_effect for clients... @@ -490,6 +638,21 @@ func take_damage(_iBody: Node2D, _iByWhoOrWhat: Node2D) -> void: self.set_collision_mask_value(10, false) pass +@rpc("call_local", "reliable") +func sync_pot_state(lifted: bool, holder_id: int): + is_being_lifted = lifted + holder_peer_id = holder_id + if holder_peer_id != 0: + var player = get_tree().get_current_scene().get_node("SpawnRoot").get_node_or_null(str(holder_peer_id)) + if player: + holder = player + else: + holder = null + else: + holder = null + print("Pot state synced: lifted=", lifted, " holder_id=", holder_id) + pass + func _on_area_2d_collision_body_exited(_body: Node2D) -> void: pass # Replace with function body. diff --git a/src/scripts/entities/world/pot.tscn b/src/scripts/entities/world/pot.tscn index 609d383..9f4a838 100644 --- a/src/scripts/entities/world/pot.tscn +++ b/src/scripts/entities/world/pot.tscn @@ -31,7 +31,7 @@ properties/0/spawn = true properties/0/replication_mode = 2 properties/1/path = NodePath(".:positionZ") properties/1/spawn = true -properties/1/replication_mode = 2 +properties/1/replication_mode = 1 properties/2/path = NodePath(".:is_being_thrown") properties/2/spawn = true properties/2/replication_mode = 2 @@ -62,6 +62,9 @@ properties/10/replication_mode = 2 properties/11/path = NodePath(".:is_spawning") properties/11/spawn = true properties/11/replication_mode = 2 +properties/12/path = NodePath(".:holder_peer_id") +properties/12/spawn = true +properties/12/replication_mode = 2 [sub_resource type="Gradient" id="Gradient_nb533"] offsets = PackedFloat32Array(0.847255, 0.861575) diff --git a/src/scripts/ui/character_select.gd b/src/scripts/ui/character_select.gd index ccf14b5..3838ac8 100644 --- a/src/scripts/ui/character_select.gd +++ b/src/scripts/ui/character_select.gd @@ -17,23 +17,23 @@ var current_character_stats = CharacterStats.new() func _ready() -> void: RenderingServer.set_default_clear_color(Color(0, 0, 0, 1.0)) - var pPanel:PopupPanel = $ControlCharacterInfo/MarginContainer/MarginContainer/VBoxContainer/TabContainer/Hair/HBoxContainerFacial/PickerButtonColorFacial.get_popup() - pPanel.max_size = Vector2i(80,120) - var cPicker:ColorPicker = $ControlCharacterInfo/MarginContainer/MarginContainer/VBoxContainer/TabContainer/Hair/HBoxContainerFacial/PickerButtonColorFacial.get_picker() + var pPanel: PopupPanel = $ControlCharacterInfo/MarginContainer/MarginContainer/VBoxContainer/TabContainer/Hair/HBoxContainerFacial/PickerButtonColorFacial.get_popup() + pPanel.max_size = Vector2i(80, 120) + var cPicker: ColorPicker = $ControlCharacterInfo/MarginContainer/MarginContainer/VBoxContainer/TabContainer/Hair/HBoxContainerFacial/PickerButtonColorFacial.get_picker() cPicker.hex_visible = true cPicker.sliders_visible = true cPicker.can_add_swatches = false cPicker.presets_visible = false - cPicker.scale = Vector2(0.24,0.24) + cPicker.scale = Vector2(0.24, 0.24) - var pPanel2:PopupPanel = $ControlCharacterInfo/MarginContainer/MarginContainer/VBoxContainer/TabContainer/Hair/HBoxContainerHair/PickerButtonColorHair.get_popup() - pPanel2.max_size = Vector2i(80,120) - var cPicker2:ColorPicker = $ControlCharacterInfo/MarginContainer/MarginContainer/VBoxContainer/TabContainer/Hair/HBoxContainerHair/PickerButtonColorHair.get_picker() + var pPanel2: PopupPanel = $ControlCharacterInfo/MarginContainer/MarginContainer/VBoxContainer/TabContainer/Hair/HBoxContainerHair/PickerButtonColorHair.get_popup() + pPanel2.max_size = Vector2i(80, 120) + var cPicker2: ColorPicker = $ControlCharacterInfo/MarginContainer/MarginContainer/VBoxContainer/TabContainer/Hair/HBoxContainerHair/PickerButtonColorHair.get_picker() cPicker2.hex_visible = true cPicker2.sliders_visible = true cPicker2.can_add_swatches = false cPicker2.presets_visible = false - cPicker2.scale = Vector2(0.24,0.24) + cPicker2.scale = Vector2(0.24, 0.24) $ControlSelectCharacter/VBoxContainer/ButtonPlay.visible = false @@ -75,7 +75,7 @@ func loadCharDatas(): characters.append(char_data) pass -func getSaveData(iSaveFile:String) -> Dictionary: +func getSaveData(iSaveFile: String) -> Dictionary: var savePath = "user://" + save_path + iSaveFile if not FileAccess.file_exists(savePath): return {} @@ -97,15 +97,13 @@ func update_character_display() -> void: $ControlYourCharacters/MarginContainer2/MarginContainer/VBoxContainerCharacters/ScrollContainer/VBoxContainer.add_child(charBut) charBut.set_data(chara) charBut.connect("pressed", _pressCharBut.bind(charBut, curIndex)) - curIndex+=1 + curIndex += 1 pass $ControlYourCharacters/MarginContainer2/MarginContainer/VBoxContainerCharacters/ScrollContainer.queue_redraw() $ControlYourCharacters/MarginContainer2/MarginContainer/VBoxContainerCharacters/ScrollContainer/VBoxContainer.queue_redraw() pass func checkSaves(): - - pass func _input(event: InputEvent): @@ -113,7 +111,7 @@ func _input(event: InputEvent): var isPressed = event.is_pressed() if isMouseButton and isPressed and event.button_index == 1: var evLocal = make_input_local(event) - if !Rect2(Vector2(0,0), size).has_point(evLocal.position): + if !Rect2(Vector2(0, 0), size).has_point(evLocal.position): release_focus() @@ -122,10 +120,10 @@ func _on_line_edit_text_changed(new_text: String) -> void: demoCharacter.initStats(demoCharacter.stats) pass # Replace with function body. -func _pressCharBut(iCharBut:Button, iIndex:int) -> void: +func _pressCharBut(iCharBut: Button, iIndex: int) -> void: $ControlSelectCharacter/VBoxContainer/ButtonPlay.visible = true $ControlCharacterInfo.visible = false - var polayer = iCharBut.find_child("Player") + var _polayer = iCharBut.find_child("Player") current_character_stats = iCharBut.find_child("Player").stats current_slot = iIndex demoCharacter.initStats(current_character_stats) @@ -187,7 +185,7 @@ func _on_button_save_pressed() -> void: if child.current_character_stats.character_name == current_character_stats.character_name: current_slot = cnt break - cnt+=1 + cnt += 1 demoCharacter.initStats(current_character_stats) demoCharacter._stats_changed(current_character_stats) @@ -253,7 +251,6 @@ func _on_h_slider_ears_value_changed(value: float) -> void: func _on_color_picker_button_picker_created() -> void: - pass # Replace with function body. @@ -264,4 +261,7 @@ func _on_picker_button_color_facial_color_changed(color: Color) -> void: func _on_picker_button_color_hair_color_changed(color: Color) -> void: current_character_stats.setHairColor(color) - pass # Replace with function body. + pass + +func get_current_character_stats() -> CharacterStats: + return current_character_stats # Replace with function body. diff --git a/src/scripts/ui/character_select.tscn b/src/scripts/ui/character_select.tscn index f23a13e..fa894da 100644 --- a/src/scripts/ui/character_select.tscn +++ b/src/scripts/ui/character_select.tscn @@ -4,7 +4,7 @@ [ext_resource type="PackedScene" uid="uid://dgtfy455abe1t" path="res://scripts/entities/player/player.tscn" id="4_5axoa"] [ext_resource type="PackedScene" uid="uid://dgniqbtakal50" path="res://scripts/ui/button_select_char.tscn" id="7_bft24"] -[node name="CharacterSelect" type="Control"] +[node name="CharacterSelect" type="Control" groups=["character_select"]] z_index = 26 layout_mode = 3 anchors_preset = 15 diff --git a/src/scripts/ui/main_menu.gd b/src/scripts/ui/main_menu.gd index af54d25..d9682a4 100644 --- a/src/scripts/ui/main_menu.gd +++ b/src/scripts/ui/main_menu.gd @@ -25,15 +25,17 @@ func _connectSuccess(): pass func _on_button_host_pressed() -> void: - self.visible = false - $CanvasLayer.visible = false + Console.print("I am host") MultiplayerManager.host() + $CanvasLayer.visible = false + self.visible = false #$CanvasLayer/CharacterSelect.queue_free() pass # Replace with function body. func _on_button_join_pressed() -> void: - self.visible = false - $CanvasLayer.visible = false + Console.print("I am joiner") MultiplayerManager.join() + $CanvasLayer.visible = false + self.visible = false pass # Replace with function body.