* Added console

* Set the game to automatically start a server and client.
* Made it so second player can pick up pot correctly and move it around.
* Still buggy when putting it down.
* Disabled "Countdown" as it became annoying when testing.
This commit is contained in:
2025-10-06 23:49:07 +02:00
parent 4ec52c34d4
commit ff083769d8
15 changed files with 1007 additions and 126 deletions

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"godotTools.editorPath.godot4": "c:\\Program Files\\godot\\4\\Godot_v4.4-stable_win64.exe"
}

View File

@@ -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():
@@ -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
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
@@ -129,7 +137,6 @@ func _delPlayer(id:int):
func _on_timer_timeout() -> void:
if has_started:
var countPots = 0
for child in $SpawnRoot.get_children():
@@ -141,6 +148,9 @@ func _on_timer_timeout() -> void:
pot.is_spawning = true
pot.positionZ = 90
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...
@@ -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")

View File

@@ -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]

View File

@@ -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

View File

@@ -0,0 +1 @@
uid://c8t6r0pvo3ko8

View File

@@ -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

View File

@@ -242,6 +242,80 @@ 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

View File

@@ -23,6 +23,38 @@ 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)
@@ -150,6 +182,7 @@ var time_since_last_frame = 0.0
@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
@@ -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,7 +335,8 @@ 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:
@@ -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:
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:
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
@@ -380,11 +435,20 @@ func _apply_movement_from_input(_delta: float) -> void:
# 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:
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 = a.get_parent()
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
@@ -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:
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:
@@ -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:
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()
@@ -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
@@ -775,6 +879,15 @@ func not_use():
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

View File

@@ -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

View File

@@ -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"]

View File

@@ -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
@@ -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
@@ -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
@@ -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...
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.

View File

@@ -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)

View File

@@ -104,8 +104,6 @@ func update_character_display() -> void:
pass
func checkSaves():
pass
func _input(event: InputEvent):
@@ -125,7 +123,7 @@ func _on_line_edit_text_changed(new_text: String) -> 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)
@@ -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.

View File

@@ -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

View File

@@ -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.