perfected collision with pots n stuff

This commit is contained in:
2025-12-26 05:46:19 +01:00
parent b85b4fd447
commit 37b96d0518
6 changed files with 224 additions and 77 deletions

View File

@@ -1,4 +1,4 @@
[gd_resource type="AudioBusLayout" load_steps=2 format=3 uid="uid://bdtqippaaj7i7"]
[gd_resource type="AudioBusLayout" format=3 uid="uid://bdtqippaaj7i7"]
[sub_resource type="AudioEffectReverb" id="AudioEffectReverb_j3pel"]
resource_name = "Reverb"

File diff suppressed because one or more lines are too long

View File

@@ -8,11 +8,15 @@
config_version=5
[animation]
compatibility/default_parent_skeleton_in_mesh_instance_3d=true
[application]
config/name="ruinborn_new"
run/main_scene="uid://c6s2i06bbd6u6"
config/features=PackedStringArray("4.5", "Forward Plus")
config/features=PackedStringArray("4.6", "Forward Plus")
run/max_fps=60
boot_splash/show_image=false
config/icon="res://icon.svg"

View File

@@ -23,6 +23,16 @@ const JUMP_VELOCITY = -400.0
var held_entity = null
var grabbed_entity = null
var current_height = 0
var gravity = 800
var holder = null
var flipFromWall = false
# Add Z-axis variables similar to loot.gd
@export var positionZ = 0.0
var velocityZ = 0.0
var accelerationZ = -330.0 # Gravity
@export var held_entity_path: String = "":
set(value):
if value != "":
@@ -401,8 +411,11 @@ func _apply_movement_from_input(_delta: float) -> void:
use_button_up = false
if is_releasing:
var is_throwing = false
if held_entity != null:
if velocity.x != 0 or velocity.y != 0:
# THROWING: We have held_entity, so just throw it - DON'T call release() on grabbed_entity
is_throwing = true
if multiplayer.is_server():
held_entity.throw(last_direction)
held_entity = null
@@ -411,14 +424,20 @@ func _apply_movement_from_input(_delta: float) -> void:
else:
MultiplayerManager.request_throw_pot.rpc_id(1, held_entity.get_path(), multiplayer.get_unique_id(), last_direction)
else:
# PUTTING DOWN: Don't call release(), put_down handles it
if multiplayer.is_server():
if held_entity.put_down():
held_entity = null
held_entity_path = ""
else:
MultiplayerManager.request_put_down_pot.rpc_id(1, held_entity.get_path(), multiplayer.get_unique_id())
if grabbed_entity != null and "release" in grabbed_entity:
# Only release grabbed_entity if we're NOT throwing AND we have a grabbed_entity (not held_entity)
# When throwing, we have held_entity, so release() should NOT be called
# When releasing (not throwing), we have grabbed_entity, so release() SHOULD be called
if not is_throwing and grabbed_entity != null and "release" in grabbed_entity:
grabbed_entity.release()
# Clear grabbed_entity_path first so the setter clears the entity properly
grabbed_entity_path = ""
grabbed_entity = null
@@ -829,7 +848,7 @@ func sync_player_deaths(iDeaths: int):
MultiplayerManager.updateScore()
pass
@rpc("call_local", "reliable")
@rpc("any_peer", "reliable")
func sync_animation(animation_name: String):
#print("Client ", multiplayer.get_unique_id(), " received sync_animation: ", animation_name)
current_animation = animation_name

View File

@@ -43,8 +43,8 @@ var lift_speed = 10.0 # Speed of smooth movement
var put_down_start_pos = Vector2.ZERO
var put_down_target_pos = Vector2.ZERO
@export var lift_progress = 0.0
var re_enable_collision_after_time = 0.0
var re_enable_time = 0.17
@export var re_enable_collision_after_time = 0.0
var re_enable_time = 0.08
var previousFrameVel = Vector2.ZERO
var hasShownSmokePuffs = false
var grab_follow_speed = 15.0 # Speed at which pot follows holder when grabbed
@@ -52,6 +52,7 @@ var previous_holder_position = Vector2.ZERO # Track previous holder position to
var previous_client_position = Vector2.ZERO # Track previous position on client for movement detection
var push_lead_factor = 1.1 # When pushing, pot moves slightly ahead (110% of player movement)
var min_push_distance = 12.0 # Minimum distance pot should maintain from player when pushing to avoid blocking
var _throw_collision_initialized = false # Track if collision state has been initialized for throw on client
@export var holder_peer_id: int = 0:
set(value):
@@ -90,13 +91,14 @@ func _physics_process(delta: float) -> void:
holder = player
elif holder_peer_id == 0:
# No holder - clear everything
# BUT: Don't clear velocity if pot is being thrown (it needs velocity to fly)
if holder != null:
holder = null
if is_being_grabbed:
if is_being_grabbed and not is_being_thrown:
is_being_grabbed = false
if is_being_lifted:
if is_being_lifted and not is_being_thrown:
is_being_lifted = false
if velocity != Vector2.ZERO:
if velocity != Vector2.ZERO and not is_being_thrown:
velocity = Vector2.ZERO
# Handle lifted pot position on ALL clients for smooth following
@@ -107,7 +109,8 @@ func _physics_process(delta: float) -> void:
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)
# Use the same calculation on both server and client to ensure consistent Y position
var target_pos = holder.global_position + Vector2(0, 0)
if lift_progress < 1.0:
global_position = global_position.lerp(target_pos, lift_progress)
positionZ = lift_height * lift_progress
@@ -115,6 +118,7 @@ func _physics_process(delta: float) -> void:
# When fully lifted, maintain exact position above holder
global_position = target_pos
positionZ = lift_height
update_sprite_scale() # Ensure sprite scale/offset is updated consistently
else:
# Debug: Check why pot is not following
if is_being_lifted and !holder:
@@ -129,7 +133,8 @@ func _physics_process(delta: float) -> void:
if multiplayer.is_server():
# CRITICAL: If holder_peer_id is 0, ALWAYS clear grab state immediately
# This must happen before ANY logic runs to prevent movement after release
if holder_peer_id == 0:
# BUT: Don't clear velocity if pot is being thrown (it needs velocity to fly)
if holder_peer_id == 0 and not is_being_thrown:
is_being_grabbed = false
if holder != null:
holder = null
@@ -141,14 +146,14 @@ func _physics_process(delta: float) -> void:
$SfxDrag2.stop()
# Skip all grab logic if holder_peer_id is 0
pass
elif is_being_thrown:
if is_being_thrown:
re_enable_collision_after_time -= delta
if re_enable_collision_after_time <= 0.0:
# enable collisions again
# enable collisions with players again after the delay
self.set_collision_layer_value(8, true)
self.set_collision_mask_value(9, true)
self.set_collision_mask_value(10, true)
self.set_collision_mask_value(8, true)
# Collision layer is already enabled, just re-enable collision mask with players
self.set_collision_mask_value(9, true) # Re-enable collision with players
self.set_collision_mask_value(10, true) # Re-enable collision with players (if using both)
re_enable_collision_after_time = 0
# Apply gravity to vertical movement
@@ -172,10 +177,13 @@ func _physics_process(delta: float) -> void:
thrown_by = null
# Move horizontally
var collision = move_and_collide(velocity * delta)
if collision:
if positionZ == 0:
is_being_thrown = false
if self.get_collision_layer_value(8) == false:
move_and_slide()
else:
var collision = move_and_collide(velocity * delta)
if collision:
if positionZ == 0:
is_being_thrown = false
update_sprite_scale()
elif is_being_put_down:
lift_progress -= delta * lift_speed
@@ -204,7 +212,8 @@ func _physics_process(delta: float) -> void:
# Check holder_peer_id FIRST before is_being_grabbed to ensure we never follow when released
# ONLY run this on server (pot has authority 1 = server)
# DOUBLE CHECK: holder_peer_id must be non-zero AND holder must exist AND match
if holder_peer_id != 0 and is_being_grabbed and holder != null:
# BUT: Don't run grab logic if pot is being thrown (throw logic handles movement)
if holder_peer_id != 0 and is_being_grabbed and holder != null and not is_being_thrown:
# Only follow if holder's authority matches holder_peer_id
if holder.get_multiplayer_authority() == holder_peer_id:
# Calculate how much the player has moved since last frame
@@ -276,7 +285,8 @@ func _physics_process(delta: float) -> void:
# CRITICAL FINAL CHECK: If holder_peer_id is 0, STOP ALL MOVEMENT immediately
# This must run AFTER all grab logic to catch any cases where holder_peer_id was set to 0
# but the pot is still trying to move
if holder_peer_id == 0:
# BUT: Don't clear velocity if pot is being thrown (it needs velocity to fly)
if holder_peer_id == 0 and not is_being_thrown:
if is_being_grabbed:
is_being_grabbed = false
if holder != null:
@@ -289,7 +299,8 @@ func _physics_process(delta: float) -> void:
$SfxDrag2.stop()
# Only handle free-falling if we're not being held (holder_peer_id == 0 is the source of truth)
if holder_peer_id == 0 and !is_being_lifted: # it just spawned or is free-falling:
# BUT: Don't run free-falling logic if pot is being thrown (throw logic handles movement)
if holder_peer_id == 0 and !is_being_lifted and !is_being_thrown: # it just spawned or is free-falling:
# Apply gravity to vertical movement
velocityZ += accelerationZ * delta
positionZ += velocityZ * delta
@@ -333,12 +344,66 @@ func _physics_process(delta: float) -> void:
if is_being_thrown:
if $SfxDrag2.playing:
$SfxDrag2.stop()
# Timer is synced from server via sync_throw_timer RPC
# If not initialized yet, wait for RPC (fallback initialization)
if not _throw_collision_initialized:
# Fallback: initialize if RPC hasn't arrived yet
self.set_collision_layer_value(8, false)
self.set_collision_mask_value(7, true)
self.set_collision_mask_value(9, false)
self.set_collision_mask_value(10, false)
# CRITICAL: Set timer to a positive value to prevent immediate expiration
re_enable_collision_after_time = re_enable_time
_throw_collision_initialized = true
# Apply the same throw movement logic on client for smooth movement
# Handle collision re-enable timer (same as server)
# CRITICAL: Only decrement and check timer if it's been initialized AND is positive
if _throw_collision_initialized and re_enable_collision_after_time > 0.0:
re_enable_collision_after_time -= delta
if re_enable_collision_after_time <= 0.0:
# enable collisions with players again after the delay
self.set_collision_layer_value(8, true)
# Collision layer is already enabled, just re-enable collision mask with players
self.set_collision_mask_value(9, true) # Re-enable collision with players
self.set_collision_mask_value(10, true) # Re-enable collision with players (if using both)
re_enable_collision_after_time = 0
# Apply gravity to vertical movement
velocityZ += accelerationZ * delta
positionZ += velocityZ * delta
if positionZ <= 0:
# Pot has hit the ground
positionZ = 0
if !$SfxLand.playing:
$SfxLand.play()
$GPUParticles2D.emitting = true
$GPUParticles2D/TimerSmokeParticles.start()
elif is_being_put_down:
if abs(velocityZ) > minBounceVelocity:
velocityZ = - velocityZ * bounceRestitution
else:
velocityZ = 0
is_being_thrown = false
velocity = velocity.lerp(Vector2.ZERO, 0.5)
if velocity.x == 0 and velocity.y == 0:
thrown_by = null
# Move horizontally using the same logic as server
if self.get_collision_layer_value(8) == false:
move_and_slide()
else:
var collision = move_and_collide(velocity * delta)
if collision:
if positionZ == 0:
is_being_thrown = false
update_sprite_scale()
else:
# Pot is no longer being thrown - reset initialization flag
if _throw_collision_initialized:
_throw_collision_initialized = false
if is_being_put_down:
if $SfxDrag2.playing:
$SfxDrag2.stop()
if !$SfxLand.playing:
@@ -351,7 +416,7 @@ func _physics_process(delta: float) -> void:
$GPUParticles2D.emitting = false
# Update position on client to follow holder
if holder:
target_position = holder.global_position + Vector2(0, -8)
target_position = holder.global_position + Vector2(0, 0)
if lift_progress < 1.0:
lift_progress += delta * lift_speed
lift_progress = min(lift_progress, 1.0)
@@ -419,6 +484,14 @@ func _physics_process(delta: float) -> void:
destroy_initiated = true
show_destroy_effect()
# Update debug label
if has_node("LabelPotStateNDirectionNSpeed"):
var collision_layer_8 = self.get_collision_layer_value(8)
var collision_str = "Col 8: %s" % ("TRUE" if collision_layer_8 else "FALSE")
collision_str += "\nthrown? %s" % ("TRUE" if is_being_thrown else "FALSE")
$LabelPotStateNDirectionNSpeed.text = collision_str
pass
pass
func update_sprite_scale() -> void:
@@ -443,13 +516,20 @@ func update_sprite_scale() -> void:
#$Sprite2DShadow.modulate.
func throw(direction: Vector2, initial_velocity: float = 200):
self.set_collision_mask_value(7, true)
# When thrown, enable collision layer so pot can use move_and_collide
# But disable collision mask with players temporarily (re-enabled after re_enable_collision_after_time)
# Enable collision with walls so pot can bounce off walls
self.set_collision_layer_value(8, false) # Enable pot's collision layer (needed for move_and_collide to work)
self.set_collision_mask_value(7, true) # Enable collision with walls
self.set_collision_mask_value(9, false) # Disable collision with players initially
self.set_collision_mask_value(10, false) # Disable collision with players initially (if using both)
$Area2DCollision.set_deferred("monitoring", true)
$SfxThrow.play()
is_being_lifted = false
is_being_thrown = true
is_being_put_down = false
is_being_grabbed = false # Clear grab state
thrown_by = holder
holder = null
holder_peer_id = 0 # Clear the network holder reference
@@ -458,6 +538,22 @@ func throw(direction: Vector2, initial_velocity: float = 200):
positionZ = lift_height
current_height = 0
re_enable_collision_after_time = re_enable_time
# Sync timer to clients so they know when to re-enable collisions
sync_throw_timer.rpc(re_enable_time)
@rpc("any_peer", "reliable")
func sync_throw_timer(timer_value: float):
# Client receives timer value from server
if not multiplayer.is_server():
# CRITICAL: Always set collision layer to false FIRST when receiving timer sync
# This ensures it's false even if something else tried to set it to true
self.set_collision_layer_value(8, false)
self.set_collision_mask_value(7, true) # Enable collision with walls
self.set_collision_mask_value(9, false) # Disable collision with players initially
self.set_collision_mask_value(10, false) # Disable collision with players initially
# Set timer value from server (ensures it's positive)
re_enable_collision_after_time = timer_value
_throw_collision_initialized = true
@rpc("any_peer", "reliable")
func throw_rpc(direction: Vector2, initial_velocity: float = 200):
@@ -485,6 +581,19 @@ func grab(new_holder: CharacterBody2D) -> bool:
func release():
# Clear all grab-related state
# CRITICAL: Don't re-enable collision layer if pot is being thrown (throw logic handles it)
if is_being_thrown:
# Pot is being thrown - don't change collision layer, throw logic will handle it
holder = null
holder_peer_id = 0
is_being_grabbed = false
hasShownSmokePuffs = false
#velocity = Vector2.ZERO
indicate(true)
if $SfxDrag2.playing:
$SfxDrag2.stop()
return
# CRITICAL: If we're not the server, we need to notify the server to release
# The pot has authority 1 (server), so the server must be the one to clear holder_peer_id
if not multiplayer.is_server():
@@ -496,9 +605,9 @@ func release():
holder_peer_id = 0
is_being_grabbed = false
hasShownSmokePuffs = false
velocity = Vector2.ZERO
#velocity = Vector2.ZERO
# Re-enable pot's collision layer when released
self.set_collision_layer_value(8, true) # Re-enable pot's collision layer
#self.set_collision_layer_value(8, true) # Re-enable pot's collision layer
indicate(true)
if $SfxDrag2.playing:
$SfxDrag2.stop()
@@ -508,9 +617,9 @@ func release():
holder_peer_id = 0
is_being_grabbed = false
hasShownSmokePuffs = false
velocity = Vector2.ZERO
#velocity = Vector2.ZERO
# Re-enable pot's collision layer when released
self.set_collision_layer_value(8, true) # Re-enable pot's collision layer
#self.set_collision_layer_value(8, true) # Re-enable pot's collision layer
indicate(true)
if $SfxDrag2.playing:
$SfxDrag2.stop()
@@ -544,12 +653,12 @@ func lift(new_holder: CharacterBody2D):
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)
self.set_collision_mask_value(8, false)
self.set_collision_mask_value(9, false)
self.set_collision_mask_value(10, false)
# disable collisions with walls and players when lifted
self.set_collision_layer_value(8, false) # Disable pot's collision layer (so players can't collide with it)
self.set_collision_mask_value(7, false) # Disable collision with walls
self.set_collision_mask_value(8, false) # Disable collision with other pots
self.set_collision_mask_value(9, false) # Disable collision with players
self.set_collision_mask_value(10, false) # Disable collision with players (if using both)
is_being_lifted = true
is_being_grabbed = false
is_being_thrown = false

View File

@@ -24,6 +24,7 @@
[ext_resource type="AudioStream" uid="uid://bnuh7ima5cq0n" path="res://assets/audio/sfx/environment/pot/pot_drag2.mp3" id="20_wv4em"]
[ext_resource type="AudioStream" uid="uid://co7i1f4t8qtqp" path="res://assets/audio/sfx/environment/pot/pot_place_06.mp3" id="21_0qg0s"]
[ext_resource type="AudioStream" uid="uid://ohm0t5c7hw0w" path="res://assets/audio/sfx/player/throw/throw_01.wav.mp3" id="21_hd4fl"]
[ext_resource type="FontFile" uid="uid://bajcvmidrnc33" path="res://assets/fonts/standard_font.png" id="25_p028i"]
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_hsjxb"]
properties/0/path = NodePath(".:position")
@@ -327,6 +328,20 @@ position = Vector2(0, -1)
shape = SubResource("RectangleShape2D_nb533")
debug_color = Color(0.7, 0.132592, 0.232379, 0.42)
[node name="LabelPotStateNDirectionNSpeed" type="Label" parent="." unique_id=1790795234]
z_index = 18
z_as_relative = false
offset_left = -29.82
offset_top = -40.0
offset_right = 30.18
offset_bottom = -34.0
size_flags_horizontal = 3
size_flags_vertical = 6
theme_override_constants/outline_size = 6
theme_override_fonts/font = ExtResource("25_p028i")
theme_override_font_sizes/font_size = 6
horizontal_alignment = 1
[connection signal="timeout" from="GPUParticles2D/TimerSmokeParticles" to="." method="_on_timer_smoke_particles_timeout"]
[connection signal="body_entered" from="Area2DPickup" to="." method="_on_area_2d_pickup_body_entered"]
[connection signal="body_exited" from="Area2DPickup" to="." method="_on_area_2d_pickup_body_exited"]