added nice smoke puffs to interactible objects
This commit is contained in:
@@ -37,6 +37,13 @@ texture = ExtResource("1_hpvv5")
|
||||
shape = SubResource("RectangleShape2D_uvdjg")
|
||||
metadata/_edit_lock_ = true
|
||||
|
||||
[node name="KeyInteractionArea" type="Area2D" parent="." unique_id=982067740]
|
||||
metadata/_edit_lock_ = true
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="KeyInteractionArea" unique_id=1640987231]
|
||||
shape = SubResource("RectangleShape2D_la1wf")
|
||||
debug_color = Color(0.70196074, 0.67558956, 0.17869899, 0.41960785)
|
||||
|
||||
[node name="SfxOpenKeyDoor" type="AudioStreamPlayer2D" parent="." unique_id=47303726]
|
||||
stream = ExtResource("3_la1wf")
|
||||
max_distance = 1485.0
|
||||
@@ -45,30 +52,29 @@ panning_strength = 1.09
|
||||
|
||||
[node name="SfxOpenStoneDoor" type="AudioStreamPlayer2D" parent="." unique_id=885417421]
|
||||
stream = ExtResource("4_18pbm")
|
||||
volume_db = -3.382
|
||||
max_distance = 1204.0
|
||||
attenuation = 6.498014
|
||||
panning_strength = 1.25
|
||||
|
||||
[node name="SfxOpenGateDoor" type="AudioStreamPlayer2D" parent="." unique_id=442358170]
|
||||
stream = ExtResource("6_ju5n0")
|
||||
volume_db = -4.65
|
||||
volume_db = -5.073
|
||||
pitch_scale = 1.1
|
||||
max_distance = 1246.0
|
||||
attenuation = 7.999997
|
||||
panning_strength = 1.3
|
||||
|
||||
[node name="KeyInteractionArea" type="Area2D" parent="." unique_id=982067740]
|
||||
metadata/_edit_lock_ = true
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="KeyInteractionArea" unique_id=1640987231]
|
||||
shape = SubResource("RectangleShape2D_la1wf")
|
||||
debug_color = Color(0.70196074, 0.67558956, 0.17869899, 0.41960785)
|
||||
|
||||
[node name="SfxDoorCloses" type="AudioStreamPlayer2D" parent="." unique_id=1074871158]
|
||||
stream = SubResource("AudioStreamRandomizer_ey00f")
|
||||
volume_db = -1.268
|
||||
volume_db = -6.925
|
||||
max_distance = 1289.0
|
||||
attenuation = 3.7321312
|
||||
attenuation = 7.209997
|
||||
panning_strength = 1.14
|
||||
|
||||
[node name="SfxCloseGateDoor" type="AudioStreamPlayer2D" parent="." unique_id=1825261269]
|
||||
stream = ExtResource("8_pg2b6")
|
||||
volume_db = -4.227
|
||||
max_distance = 1246.0
|
||||
attenuation = 8.282116
|
||||
panning_strength = 1.02
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
[ext_resource type="PackedScene" uid="uid://cxfvw8y7jqn2p" path="res://scenes/player.tscn" id="2"]
|
||||
[ext_resource type="Script" uid="uid://db58xcyo4cjk" path="res://scripts/game_world.gd" id="4"]
|
||||
[ext_resource type="Script" uid="uid://wff5063ctp7g" path="res://scripts/debug_overlay.gd" id="5"]
|
||||
[ext_resource type="AudioStream" uid="uid://dthr2w8x0cj6v" path="res://assets/audio/sfx/ambience/wind-castle-loop.wav.mp3" id="6_6c6v5"]
|
||||
[ext_resource type="TileSet" uid="uid://dqem5tbvooxrg" path="res://assets/gfx/RPG DUNGEON VOL 3.tres" id="9"]
|
||||
|
||||
[node name="GameWorld" type="Node2D" unique_id=430665106]
|
||||
@@ -37,3 +38,8 @@ script = ExtResource("5")
|
||||
light_mask = 1048575
|
||||
visibility_layer = 1048575
|
||||
color = Color(0.671875, 0.671875, 0.671875, 1)
|
||||
|
||||
[node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="." unique_id=1141138343]
|
||||
stream = ExtResource("6_6c6v5")
|
||||
autoplay = true
|
||||
stream_paused = true
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
[ext_resource type="AudioStream" uid="uid://dsokwxmutlwk5" path="res://assets/audio/sfx/environment/move_rock/rock_push_loop_02.mp3" id="25_1u1k0"]
|
||||
[ext_resource type="AudioStream" uid="uid://4ilddgc4lgyq" path="res://assets/audio/sfx/environment/crate/crash_table-04.wav" id="26_vfomk"]
|
||||
[ext_resource type="AudioStream" uid="uid://c7kc0aw0wevah" path="res://assets/audio/sfx/environment/crate/wood_impact_break.mp3" id="27_2p257"]
|
||||
[ext_resource type="Texture2D" uid="uid://bknascfv4twmi" path="res://assets/gfx/smoke_puffs.png" id="28_2p257"]
|
||||
|
||||
[sub_resource type="CapsuleShape2D" id="CapsuleShape2D_nyc8x"]
|
||||
radius = 4.0
|
||||
@@ -80,6 +81,71 @@ streams_count = 2
|
||||
stream_0/stream = ExtResource("26_vfomk")
|
||||
stream_1/stream = ExtResource("27_2p257")
|
||||
|
||||
[sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_ik3co"]
|
||||
particles_animation = true
|
||||
particles_anim_h_frames = 4
|
||||
particles_anim_v_frames = 2
|
||||
particles_anim_loop = false
|
||||
|
||||
[sub_resource type="Curve" id="Curve_dh4ly"]
|
||||
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(0.780549, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
|
||||
point_count = 3
|
||||
|
||||
[sub_resource type="CurveTexture" id="CurveTexture_m11t2"]
|
||||
curve = SubResource("Curve_dh4ly")
|
||||
|
||||
[sub_resource type="Curve" id="Curve_c8svp"]
|
||||
_limits = [0.0, 100.0, 0.0, 1.0]
|
||||
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(0.733167, 4.55855), 0.0, 0.0, 0, 0, Vector2(0.815461, 91.8906), 0.0, 0.0, 0, 0, Vector2(0.892768, 100), 0.0, 0.0, 0, 0]
|
||||
point_count = 4
|
||||
|
||||
[sub_resource type="CurveTexture" id="CurveTexture_ui3li"]
|
||||
curve = SubResource("Curve_c8svp")
|
||||
|
||||
[sub_resource type="Curve" id="Curve_oexrv"]
|
||||
_limits = [0.0, 1.0, -1.0, 1.0]
|
||||
_data = [Vector2(-1, 0), 0.0, 0.0, 0, 0, Vector2(0.0124688, 1), 0.0, 0.0, 0, 0, Vector2(0.516209, 1), 0.0, 0.0, 0, 0, Vector2(0.947631, 0), 0.0, 0.0, 0, 0]
|
||||
point_count = 4
|
||||
|
||||
[sub_resource type="Curve" id="Curve_27s1c"]
|
||||
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 1), 0.0, 0.0, 0, 0]
|
||||
point_count = 2
|
||||
|
||||
[sub_resource type="Curve" id="Curve_igjib"]
|
||||
_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 1), 0.0, 0.0, 0, 0]
|
||||
point_count = 2
|
||||
|
||||
[sub_resource type="CurveXYZTexture" id="CurveXYZTexture_tjjlx"]
|
||||
curve_x = SubResource("Curve_oexrv")
|
||||
curve_y = SubResource("Curve_27s1c")
|
||||
curve_z = SubResource("Curve_igjib")
|
||||
|
||||
[sub_resource type="Curve" id="Curve_by4wh"]
|
||||
_data = [Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(0.0224439, 1), 0.0, 0.0, 0, 0, Vector2(0.880299, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0]
|
||||
point_count = 4
|
||||
|
||||
[sub_resource type="CurveTexture" id="CurveTexture_sp8mg"]
|
||||
curve = SubResource("Curve_by4wh")
|
||||
|
||||
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_ejwle"]
|
||||
particle_flag_disable_z = true
|
||||
direction = Vector3(1, 0.2, 0)
|
||||
spread = 62.79
|
||||
initial_velocity_min = -30.0
|
||||
initial_velocity_max = 30.0
|
||||
directional_velocity_min = -25.0
|
||||
directional_velocity_max = 25.0
|
||||
directional_velocity_curve = SubResource("CurveXYZTexture_tjjlx")
|
||||
gravity = Vector3(0, 0, 0)
|
||||
damping_max = 100.0
|
||||
damping_curve = SubResource("CurveTexture_ui3li")
|
||||
scale_min = 0.8
|
||||
scale_max = 1.2
|
||||
scale_curve = SubResource("CurveTexture_sp8mg")
|
||||
color = Color(1, 1, 1, 0.709804)
|
||||
alpha_curve = SubResource("CurveTexture_m11t2")
|
||||
anim_offset_max = 1.0
|
||||
|
||||
[node name="InteractableObject" type="CharacterBody2D" unique_id=1472163831]
|
||||
collision_layer = 2
|
||||
collision_mask = 71
|
||||
@@ -147,3 +213,24 @@ volume_db = -2.611
|
||||
[node name="SfxBreakCrate" type="AudioStreamPlayer2D" parent="." unique_id=1799447869]
|
||||
stream = SubResource("AudioStreamRandomizer_ik3co")
|
||||
volume_db = -6.092
|
||||
|
||||
[node name="DragParticles" type="GPUParticles2D" parent="." unique_id=2123830325]
|
||||
z_index = -1
|
||||
y_sort_enabled = true
|
||||
material = SubResource("CanvasItemMaterial_ik3co")
|
||||
emitting = false
|
||||
amount = 16
|
||||
texture = ExtResource("28_2p257")
|
||||
lifetime = 0.66
|
||||
interp_to_end = 0.026
|
||||
preprocess = 0.16
|
||||
explosiveness = 0.15
|
||||
randomness = 0.63
|
||||
use_fixed_seed = true
|
||||
seed = 1565624367
|
||||
process_material = SubResource("ParticleProcessMaterial_ejwle")
|
||||
|
||||
[node name="TimerSmokeParticles" type="Timer" parent="DragParticles" unique_id=2105569542]
|
||||
wait_time = 0.07
|
||||
|
||||
[connection signal="timeout" from="DragParticles/TimerSmokeParticles" to="." method="_on_timer_smoke_particles_timeout"]
|
||||
|
||||
@@ -58,7 +58,7 @@ radius = 8.0
|
||||
|
||||
[sub_resource type="AudioStreamRandomizer" id="AudioStreamRandomizer_l71n6"]
|
||||
playback_mode = 1
|
||||
random_pitch = 1.0059091
|
||||
random_pitch = 1.0118532
|
||||
streams_count = 6
|
||||
stream_0/stream = ExtResource("13_fulsm")
|
||||
stream_1/stream = ExtResource("14_4r5pv")
|
||||
@@ -69,7 +69,7 @@ stream_5/stream = ExtResource("18_4ni07")
|
||||
|
||||
[sub_resource type="AudioStreamRandomizer" id="AudioStreamRandomizer_487ah"]
|
||||
playback_mode = 1
|
||||
random_pitch = 1.0059091
|
||||
random_pitch = 1.0118532
|
||||
streams_count = 7
|
||||
stream_0/stream = ExtResource("20_ujl30")
|
||||
stream_1/stream = ExtResource("21_31cv2")
|
||||
@@ -182,8 +182,10 @@ horizontal_alignment = 1
|
||||
|
||||
[node name="SfxWalk" type="AudioStreamPlayer2D" parent="." unique_id=1693322702]
|
||||
stream = SubResource("AudioStreamRandomizer_l71n6")
|
||||
volume_db = -12.0
|
||||
volume_db = -18.527
|
||||
max_distance = 1412.0
|
||||
attenuation = 8.282109
|
||||
panning_strength = 1.11
|
||||
|
||||
[node name="TimerWalk" type="Timer" parent="SfxWalk" unique_id=590325386]
|
||||
wait_time = 0.3
|
||||
@@ -191,12 +193,15 @@ one_shot = true
|
||||
|
||||
[node name="SfxDie" type="AudioStreamPlayer2D" parent="." unique_id=1173215688]
|
||||
stream = ExtResource("19_4r5pv")
|
||||
volume_db = -2.537
|
||||
attenuation = 8.876548
|
||||
bus = &"Sfx"
|
||||
|
||||
[node name="SfxTakeDamage" type="AudioStreamPlayer2D" parent="." unique_id=322150091]
|
||||
stream = SubResource("AudioStreamRandomizer_487ah")
|
||||
volume_db = -6.092
|
||||
attenuation = 7.7274756
|
||||
panning_strength = 1.1
|
||||
bus = &"Sfx"
|
||||
|
||||
[node name="SfxThrow" type="AudioStreamPlayer2D" parent="." unique_id=961008127]
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
[ext_resource type="Texture2D" uid="uid://bknascfv4twmi" path="res://assets/gfx/smoke_puffs.png" id="2_smoke"]
|
||||
|
||||
[node name="SmokePuff" type="Node2D" unique_id=243995580]
|
||||
y_sort_enabled = true
|
||||
script = ExtResource("1_puff")
|
||||
|
||||
[node name="Sprite2D" type="Sprite2D" parent="." unique_id=1282738570]
|
||||
|
||||
@@ -211,7 +211,12 @@ func _process(delta: float) -> void:
|
||||
global_position = open_position # Also set global position
|
||||
# When moved from closed position (open), collision should be DISABLED
|
||||
set_collision_layer_value(7, false)
|
||||
print("Door: Opening animation complete - moved to open position: ", open_position, " (closed: ", closed_position, ", offset: ", open_offset, ") - collision DISABLED")
|
||||
print("Door: Opening animation complete - moved to open position: ", open_position, " (closed: ", closed_position, ", offset: ", open_offset, ") - collision DISABLED", " (key_used=", key_used, ")" if type == "KeyDoor" else "")
|
||||
|
||||
# CRITICAL: For KeyDoors, ensure key_used is true after animation completes
|
||||
# This prevents the door from being reset to closed in _process()
|
||||
if type == "KeyDoor":
|
||||
key_used = true
|
||||
|
||||
# Spawn smoke puffs when StoneDoor finishes opening (1-2 puffs)
|
||||
if type == "StoneDoor":
|
||||
@@ -1231,6 +1236,10 @@ func _sync_door_open():
|
||||
is_closed = true
|
||||
set_collision_layer_value(7, true)
|
||||
|
||||
# CRITICAL: For KeyDoors, set key_used to true so it doesn't get reset to closed
|
||||
if type == "KeyDoor":
|
||||
key_used = true
|
||||
|
||||
animation_start_position = position
|
||||
is_opening = true
|
||||
is_closing = false
|
||||
@@ -1244,7 +1253,7 @@ func _sync_door_open():
|
||||
else:
|
||||
$SfxOpenStoneDoor.play()
|
||||
|
||||
print("Door: Client received door open RPC for ", name, " - starting open animation")
|
||||
print("Door: Client received door open RPC for ", name, " - starting open animation", " (key_used=", key_used, ")" if type == "KeyDoor" else "")
|
||||
|
||||
@rpc("authority", "reliable")
|
||||
func _sync_puzzle_solved(is_solved: bool):
|
||||
|
||||
@@ -431,6 +431,7 @@ func _die():
|
||||
if killer_peer_id != 0 and killer_peer_id != multiplayer.get_unique_id() and killer_player.has_method("_sync_stats_update"):
|
||||
# Server is updating a client's player stats - sync to the client
|
||||
var coins = killer_player.character_stats.coin if "coin" in killer_player.character_stats else 0
|
||||
print(name, " syncing kill stats to client peer_id=", killer_peer_id, " kills=", killer_player.character_stats.kills, " coins=", coins)
|
||||
killer_player._sync_stats_update.rpc_id(killer_peer_id, killer_player.character_stats.kills, coins)
|
||||
|
||||
# Spawn loot immediately (before death animation)
|
||||
|
||||
@@ -35,6 +35,7 @@ var is_charging_attack: bool = false
|
||||
var attack_charge_time: float = 0.0
|
||||
var base_attack_charge_time: float = 0.4 # Base charge time before attack
|
||||
var dex: int = 10 # Dexterity stat (affects attack speed)
|
||||
var blood_scene = preload("res://scenes/blood_clot.tscn")
|
||||
|
||||
# AI state
|
||||
enum AIState {IDLE, WANDERING, NOTICED, CHASING, ATTACKING, GROUPING}
|
||||
@@ -69,14 +70,50 @@ const ANIMATIONS = {
|
||||
"nextAnimation": null
|
||||
},
|
||||
"RUN": {
|
||||
"frames": [2, 3, 4, 5],
|
||||
"frameDurations": [150, 150, 150, 150],
|
||||
"frames": [3, 2, 3, 4],
|
||||
"frameDurations": [140, 140, 140, 140],
|
||||
"loop": true,
|
||||
"nextAnimation": null
|
||||
},
|
||||
"SWORD": {
|
||||
"frames": [6, 7, 8],
|
||||
"frameDurations": [100, 100, 200],
|
||||
"frames": [5, 6, 7, 8],
|
||||
"frameDurations": [40, 60, 90, 80],
|
||||
"loop": false,
|
||||
"nextAnimation": "IDLE"
|
||||
},
|
||||
"AXE": {
|
||||
"frames": [5, 6, 7, 8],
|
||||
"frameDurations": [50, 70, 100, 90],
|
||||
"loop": false,
|
||||
"nextAnimation": "IDLE"
|
||||
},
|
||||
"PUNCH": {
|
||||
"frames": [16, 17, 18],
|
||||
"frameDurations": [50, 70, 100],
|
||||
"loop": false,
|
||||
"nextAnimation": "IDLE"
|
||||
},
|
||||
"BOW": {
|
||||
"frames": [9, 10, 11, 12],
|
||||
"frameDurations": [80, 110, 110, 80],
|
||||
"loop": false,
|
||||
"nextAnimation": "IDLE"
|
||||
},
|
||||
"STAFF": {
|
||||
"frames": [13, 14, 15],
|
||||
"frameDurations": [200, 200, 400],
|
||||
"loop": false,
|
||||
"nextAnimation": "IDLE"
|
||||
},
|
||||
"THROW": {
|
||||
"frames": [16, 17, 18],
|
||||
"frameDurations": [80, 80, 300],
|
||||
"loop": false,
|
||||
"nextAnimation": "IDLE"
|
||||
},
|
||||
"CONJURE": {
|
||||
"frames": [19],
|
||||
"frameDurations": [400],
|
||||
"loop": false,
|
||||
"nextAnimation": "IDLE"
|
||||
},
|
||||
@@ -91,6 +128,42 @@ const ANIMATIONS = {
|
||||
"frameDurations": [200, 200, 200, 800],
|
||||
"loop": false,
|
||||
"nextAnimation": null
|
||||
},
|
||||
"IDLE_HOLD": {
|
||||
"frames": [25],
|
||||
"frameDurations": [500],
|
||||
"loop": true,
|
||||
"nextAnimation": null
|
||||
},
|
||||
"RUN_HOLD": {
|
||||
"frames": [25, 26, 25, 27],
|
||||
"frameDurations": [150, 150, 150, 150],
|
||||
"loop": true,
|
||||
"nextAnimation": null
|
||||
},
|
||||
"JUMP": {
|
||||
"frames": [25, 26, 27, 28],
|
||||
"frameDurations": [80, 80, 80, 800],
|
||||
"loop": false,
|
||||
"nextAnimation": "IDLE"
|
||||
},
|
||||
"LIFT": {
|
||||
"frames": [19, 30],
|
||||
"frameDurations": [70, 70],
|
||||
"loop": false,
|
||||
"nextAnimation": "IDLE_HOLD"
|
||||
},
|
||||
"IDLE_PUSH": {
|
||||
"frames": [30],
|
||||
"frameDurations": [10],
|
||||
"loop": true,
|
||||
"nextAnimation": null
|
||||
},
|
||||
"RUN_PUSH": {
|
||||
"frames": [30, 29, 30, 31],
|
||||
"frameDurations": [260, 260, 260, 260],
|
||||
"loop": true,
|
||||
"nextAnimation": null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1328,6 +1401,19 @@ func _play_death_animation():
|
||||
|
||||
# Play death sound effect
|
||||
if sfx_die:
|
||||
for i in 12:
|
||||
var angle = randf_range(0, TAU)
|
||||
var speed = randf_range(50, 100)
|
||||
var initial_velocityZ = randf_range(50, 90)
|
||||
var b = blood_scene.instantiate() as CharacterBody2D
|
||||
b.scale = Vector2(randf_range(0.3, 2), randf_range(0.3, 2))
|
||||
b.global_position = global_position
|
||||
|
||||
# Set initial velocities from the synchronized data
|
||||
var direction = Vector2.from_angle(angle)
|
||||
b.velocity = direction * speed
|
||||
b.velocityZ = initial_velocityZ
|
||||
get_parent().call_deferred("add_child", b)
|
||||
sfx_die.play()
|
||||
|
||||
# Wait for death animation to finish (same duration as player: 200+200+200+800 = 1400ms = 1.4s)
|
||||
|
||||
@@ -24,6 +24,9 @@ var level_coins_collected: int = 0
|
||||
# Client ready tracking (server only)
|
||||
var clients_ready: Dictionary = {} # peer_id -> bool
|
||||
|
||||
# Level complete tracking
|
||||
var level_complete_triggered: bool = false # Prevent multiple level complete triggers
|
||||
|
||||
func _ready():
|
||||
# Add to group for easy access
|
||||
add_to_group("game_world")
|
||||
@@ -376,6 +379,31 @@ func _request_loot_pickup(loot_id: int, loot_position: Vector2, player_peer_id:
|
||||
else:
|
||||
print("GameWorld: Could not find loot for pickup request: id=", loot_id, " pos=", loot_position)
|
||||
|
||||
@rpc("authority", "reliable")
|
||||
func _sync_player_exit_stairs(player_peer_id: int):
|
||||
# Client receives notification that a player reached exit stairs
|
||||
if multiplayer.is_server():
|
||||
return # Server ignores this (it's the sender)
|
||||
|
||||
# Find the player by peer ID
|
||||
var players = get_tree().get_nodes_in_group("player")
|
||||
var target_player = null
|
||||
for p in players:
|
||||
if p.has_method("get_multiplayer_authority") and p.get_multiplayer_authority() == player_peer_id:
|
||||
target_player = p
|
||||
break
|
||||
|
||||
# Only disable controls/collision if this is our local player
|
||||
if target_player:
|
||||
var my_peer_id = multiplayer.get_unique_id()
|
||||
if player_peer_id == my_peer_id:
|
||||
# This is our local player - disable controls and collision
|
||||
target_player.controls_disabled = true
|
||||
target_player.set_collision_layer_value(1, false)
|
||||
print("GameWorld: Client disabled controls and collision for local player ", target_player.name)
|
||||
# Show black fade overlay for this player
|
||||
_show_black_fade_overlay()
|
||||
|
||||
@rpc("authority", "reliable")
|
||||
func _sync_show_level_complete(level_time: float):
|
||||
# Clients receive level complete UI sync from server
|
||||
@@ -400,6 +428,30 @@ func _sync_hide_level_complete():
|
||||
if level_complete_ui:
|
||||
level_complete_ui.visible = false
|
||||
|
||||
@rpc("authority", "reliable")
|
||||
func _sync_restore_player_controls():
|
||||
# Clients receive restore controls/collision sync from server
|
||||
if multiplayer.is_server():
|
||||
return # Server ignores this (it's the sender)
|
||||
|
||||
# Restore controls and collision for local player
|
||||
var my_peer_id = multiplayer.get_unique_id()
|
||||
var players = get_tree().get_nodes_in_group("player")
|
||||
for player in players:
|
||||
if player.has_method("get_multiplayer_authority") and player.get_multiplayer_authority() == my_peer_id:
|
||||
player.controls_disabled = false
|
||||
player.set_collision_layer_value(1, true)
|
||||
print("GameWorld: Client restored controls and collision for local player ", player.name)
|
||||
break
|
||||
|
||||
@rpc("authority", "reliable")
|
||||
func _sync_remove_black_fade():
|
||||
# Clients receive remove black fade sync from server
|
||||
if multiplayer.is_server():
|
||||
return # Server ignores this (it's the sender)
|
||||
|
||||
_remove_black_fade_overlay()
|
||||
|
||||
@rpc("authority", "reliable")
|
||||
func _sync_show_level_number(level: int):
|
||||
# Clients receive level number UI sync from server
|
||||
@@ -478,6 +530,9 @@ func _generate_dungeon():
|
||||
print("GameWorld: Not server, skipping dungeon generation")
|
||||
return
|
||||
|
||||
# Reset level complete flag for new level
|
||||
level_complete_triggered = false
|
||||
|
||||
print("GameWorld: Generating dungeon level ", current_level)
|
||||
|
||||
# Generate seed (deterministic for level 1, can be random for future levels)
|
||||
@@ -1555,8 +1610,32 @@ func _on_player_reached_stairs(player: Node):
|
||||
if not multiplayer.is_server() and multiplayer.has_multiplayer_peer():
|
||||
return # Only server handles this
|
||||
|
||||
# Prevent multiple triggers - if already triggered, ignore
|
||||
if level_complete_triggered:
|
||||
print("GameWorld: Level complete already triggered, ignoring duplicate trigger")
|
||||
return
|
||||
|
||||
print("GameWorld: Player ", player.name, " reached stairs!")
|
||||
|
||||
# Mark as triggered to prevent re-triggering
|
||||
level_complete_triggered = true
|
||||
|
||||
# Disable controls and collision for the player who reached stairs
|
||||
var player_peer_id = player.get_multiplayer_authority() if player.has_method("get_multiplayer_authority") else 0
|
||||
player.controls_disabled = true
|
||||
# Remove collision layer (layer 1 = players)
|
||||
player.set_collision_layer_value(1, false)
|
||||
print("GameWorld: Disabled controls and collision for player ", player.name)
|
||||
|
||||
# Show black fade overlay for server's local player if this is the server's player
|
||||
var my_peer_id = multiplayer.get_unique_id() if multiplayer.has_multiplayer_peer() else 1
|
||||
if player_peer_id == my_peer_id:
|
||||
_show_black_fade_overlay()
|
||||
|
||||
# Sync controls disabled and collision removal to clients
|
||||
if multiplayer.has_multiplayer_peer() and player_peer_id > 0:
|
||||
_sync_player_exit_stairs.rpc(player_peer_id)
|
||||
|
||||
# Drop any held objects for all players before level completion
|
||||
var entities_node = get_node_or_null("Entities")
|
||||
if entities_node:
|
||||
@@ -1629,6 +1708,18 @@ func _on_player_reached_stairs(player: Node):
|
||||
if hud and hud.has_method("start_timer"):
|
||||
hud.start_timer()
|
||||
|
||||
# Restore controls and collision for all players (server side)
|
||||
_restore_player_controls_and_collision()
|
||||
|
||||
# Sync restore to all clients
|
||||
if multiplayer.has_multiplayer_peer():
|
||||
_sync_restore_player_controls.rpc()
|
||||
|
||||
# Remove black fade overlay (server and clients)
|
||||
_remove_black_fade_overlay()
|
||||
if multiplayer.has_multiplayer_peer():
|
||||
_sync_remove_black_fade.rpc()
|
||||
|
||||
# Move all players to start room (server side)
|
||||
_move_all_players_to_start_room()
|
||||
|
||||
@@ -1665,9 +1756,10 @@ func _get_local_player_stats() -> Dictionary:
|
||||
var local_player = null
|
||||
|
||||
if multiplayer.has_multiplayer_peer():
|
||||
# Multiplayer: find player with matching authority
|
||||
# Multiplayer: find player with matching authority (client's own player)
|
||||
var my_peer_id = multiplayer.get_unique_id()
|
||||
for player in players:
|
||||
if player.has_method("is_multiplayer_authority") and player.is_multiplayer_authority():
|
||||
if player.has_method("get_multiplayer_authority") and player.get_multiplayer_authority() == my_peer_id:
|
||||
local_player = player
|
||||
break
|
||||
else:
|
||||
@@ -1759,6 +1851,54 @@ func _fade_in_player(player: Node):
|
||||
sprite_layer.modulate.a = 0.0 # Start invisible
|
||||
fade_tween.tween_property(sprite_layer, "modulate:a", 1.0, 1.0)
|
||||
|
||||
func _show_black_fade_overlay():
|
||||
# Create black fade overlay for player who reached exit
|
||||
# Remove existing fade if any
|
||||
var existing_fade = get_node_or_null("BlackFadeOverlay")
|
||||
if existing_fade:
|
||||
existing_fade.queue_free()
|
||||
|
||||
# Create CanvasLayer with z_index 999 (below level complete UI which is 1000)
|
||||
var fade_layer = CanvasLayer.new()
|
||||
fade_layer.name = "BlackFadeOverlay"
|
||||
fade_layer.layer = 999 # Below level complete UI (1000) but above gameplay
|
||||
add_child(fade_layer)
|
||||
|
||||
# Create ColorRect that fills the screen
|
||||
var fade_rect = ColorRect.new()
|
||||
fade_rect.name = "FadeRect"
|
||||
fade_rect.color = Color(0, 0, 0, 1) # Black, fully opaque
|
||||
fade_rect.set_anchors_preset(Control.PRESET_FULL_RECT) # Fill entire screen
|
||||
fade_layer.add_child(fade_rect)
|
||||
|
||||
# Fade in from transparent to black
|
||||
fade_rect.modulate.a = 0.0 # Start transparent
|
||||
var fade_tween = create_tween()
|
||||
fade_tween.tween_property(fade_rect, "modulate:a", 1.0, 0.5) # Fade in over 0.5 seconds
|
||||
print("GameWorld: Created black fade overlay for player who reached exit")
|
||||
|
||||
func _remove_black_fade_overlay():
|
||||
# Remove black fade overlay when new level starts
|
||||
var existing_fade = get_node_or_null("BlackFadeOverlay")
|
||||
if existing_fade:
|
||||
# Fade out quickly before removing
|
||||
var fade_rect = existing_fade.get_node_or_null("FadeRect")
|
||||
if fade_rect:
|
||||
var fade_tween = create_tween()
|
||||
fade_tween.tween_property(fade_rect, "modulate:a", 0.0, 0.2) # Fade out over 0.2 seconds
|
||||
await fade_tween.finished
|
||||
existing_fade.queue_free()
|
||||
print("GameWorld: Removed black fade overlay")
|
||||
|
||||
func _restore_player_controls_and_collision():
|
||||
# Restore controls and collision for all players when new level starts
|
||||
var players = get_tree().get_nodes_in_group("player")
|
||||
for player in players:
|
||||
player.controls_disabled = false
|
||||
# Restore collision layer (layer 1 = players)
|
||||
player.set_collision_layer_value(1, true)
|
||||
print("GameWorld: Restored controls and collision for player ", player.name)
|
||||
|
||||
func _show_level_complete_ui(level_time: float = 0.0):
|
||||
# Create or show level complete UI
|
||||
var level_complete_ui = get_node_or_null("LevelCompleteUI")
|
||||
@@ -1894,6 +2034,7 @@ func _create_level_complete_ui_programmatically() -> Node:
|
||||
# Create level complete UI programmatically
|
||||
var canvas_layer = CanvasLayer.new()
|
||||
canvas_layer.name = "LevelCompleteUI"
|
||||
canvas_layer.layer = 1000 # Very high z_index so it appears above black fade
|
||||
add_child(canvas_layer)
|
||||
|
||||
# Load standard font (as FontFile)
|
||||
|
||||
152
src/scripts/inspiration_scripts/pot.tscn30323093389.tmp
Normal file
152
src/scripts/inspiration_scripts/pot.tscn30323093389.tmp
Normal file
@@ -0,0 +1,152 @@
|
||||
[gd_scene load_steps=21 format=3 uid="uid://bdlg5orah64m5"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bj0ueurl3vovc" path="res://scripts/entities/world/pot.gd" id="1_hsjxb"]
|
||||
[ext_resource type="Texture2D" uid="uid://bu4dq78f8lgj5" path="res://assets/gfx/sheet_18.png" id="1_rxnv2"]
|
||||
[ext_resource type="AudioStream" uid="uid://fl0rfi4in3n4" path="res://assets/audio/sfx/environment/pot/Drunk lad destroys plant pot.mp3" id="3_vktry"]
|
||||
[ext_resource type="AudioStream" uid="uid://dejjc0uqthi1b" path="res://assets/audio/sfx/environment/pot/pot_destroy_sound1.mp3" id="4_nb533"]
|
||||
[ext_resource type="AudioStream" uid="uid://iuxunaogc8xr" path="res://assets/audio/sfx/environment/pot/pot_destroy_sound2.mp3" id="5_cmff4"]
|
||||
[ext_resource type="AudioStream" uid="uid://bfqusej0pbxem" path="res://assets/audio/sfx/environment/pot/pot_destroy_sound3.mp3" id="6_lq20m"]
|
||||
[ext_resource type="AudioStream" uid="uid://dq461vpiih3lc" path="res://assets/audio/sfx/environment/pot/pot_destroy_sound4.mp3" id="7_76fyq"]
|
||||
[ext_resource type="AudioStream" uid="uid://cg1ndvx4t7xtd" path="res://assets/audio/sfx/environment/pot/pot_destroy_sound5.mp3" id="8_m11t2"]
|
||||
[ext_resource type="AudioStream" uid="uid://bt5npaenq15h2" path="res://assets/audio/sfx/environment/pot/smaller_pot_crash.mp3" id="9_sb38x"]
|
||||
[ext_resource type="Texture2D" uid="uid://b1twy68vd7f20" path="res://assets/gfx/pickups/indicator.png" id="10_nb533"]
|
||||
|
||||
[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_hsjxb"]
|
||||
properties/0/path = NodePath(".:position")
|
||||
properties/0/spawn = true
|
||||
properties/0/replication_mode = 2
|
||||
|
||||
[sub_resource type="Gradient" id="Gradient_nb533"]
|
||||
offsets = PackedFloat32Array(0.847255, 0.861575)
|
||||
colors = PackedColorArray(0, 0, 0, 0.764706, 0, 0, 0, 0)
|
||||
|
||||
[sub_resource type="GradientTexture2D" id="GradientTexture2D_87nuj"]
|
||||
gradient = SubResource("Gradient_nb533")
|
||||
width = 16
|
||||
height = 6
|
||||
fill = 1
|
||||
fill_from = Vector2(0.504274, 0.478632)
|
||||
fill_to = Vector2(0.897436, 0.0769231)
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_hsjxb"]
|
||||
size = Vector2(12, 8)
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_87nuj"]
|
||||
size = Vector2(18, 15)
|
||||
|
||||
[sub_resource type="AudioStreamRandomizer" id="AudioStreamRandomizer_ui3li"]
|
||||
streams_count = 7
|
||||
stream_0/stream = ExtResource("3_vktry")
|
||||
stream_1/stream = ExtResource("4_nb533")
|
||||
stream_2/stream = ExtResource("5_cmff4")
|
||||
stream_3/stream = ExtResource("6_lq20m")
|
||||
stream_4/stream = ExtResource("7_76fyq")
|
||||
stream_5/stream = ExtResource("8_m11t2")
|
||||
stream_6/stream = ExtResource("9_sb38x")
|
||||
|
||||
[sub_resource type="Animation" id="Animation_cmff4"]
|
||||
resource_name = "indicate"
|
||||
length = 0.8
|
||||
loop_mode = 1
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:offset")
|
||||
tracks/0/interp = 2
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0, 0.4, 0.8),
|
||||
"transitions": PackedFloat32Array(1, 1, 1),
|
||||
"update": 0,
|
||||
"values": [Vector2(0, 0), Vector2(0, -1), Vector2(0, 0)]
|
||||
}
|
||||
|
||||
[sub_resource type="Animation" id="Animation_lq20m"]
|
||||
length = 0.001
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:offset")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0),
|
||||
"transitions": PackedFloat32Array(1),
|
||||
"update": 0,
|
||||
"values": [Vector2(0, 0)]
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_76fyq"]
|
||||
_data = {
|
||||
&"RESET": SubResource("Animation_lq20m"),
|
||||
&"indicate": SubResource("Animation_cmff4")
|
||||
}
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_nb533"]
|
||||
size = Vector2(14, 9)
|
||||
|
||||
[node name="Pot" type="CharacterBody2D"]
|
||||
collision_layer = 128
|
||||
collision_mask = 64
|
||||
script = ExtResource("1_hsjxb")
|
||||
|
||||
[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."]
|
||||
replication_config = SubResource("SceneReplicationConfig_hsjxb")
|
||||
|
||||
[node name="Sprite2DShadow" type="Sprite2D" parent="."]
|
||||
position = Vector2(0, 3)
|
||||
texture = SubResource("GradientTexture2D_87nuj")
|
||||
|
||||
[node name="Sprite2D" type="Sprite2D" parent="."]
|
||||
position = Vector2(0, -4)
|
||||
texture = ExtResource("1_rxnv2")
|
||||
hframes = 19
|
||||
vframes = 19
|
||||
frame = 14
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
position = Vector2(0, -1)
|
||||
shape = SubResource("RectangleShape2D_hsjxb")
|
||||
|
||||
[node name="Area2DPickup" type="Area2D" parent="."]
|
||||
collision_layer = 1024
|
||||
collision_mask = 512
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2DPickup"]
|
||||
position = Vector2(0, -1)
|
||||
shape = SubResource("RectangleShape2D_87nuj")
|
||||
debug_color = Color(0.688142, 0.7, 0.0440007, 0.42)
|
||||
|
||||
[node name="SfxShatter" type="AudioStreamPlayer2D" parent="."]
|
||||
stream = SubResource("AudioStreamRandomizer_ui3li")
|
||||
attenuation = 9.84915
|
||||
panning_strength = 1.46
|
||||
bus = &"Sfx"
|
||||
|
||||
[node name="SfxThrow" type="AudioStreamPlayer2D" parent="."]
|
||||
|
||||
[node name="SfxDrop" type="AudioStreamPlayer2D" parent="."]
|
||||
|
||||
[node name="Indicator" type="Sprite2D" parent="."]
|
||||
position = Vector2(0, -11)
|
||||
texture = ExtResource("10_nb533")
|
||||
|
||||
[node name="AnimationPlayer" type="AnimationPlayer" parent="Indicator"]
|
||||
libraries = {
|
||||
&"": SubResource("AnimationLibrary_76fyq")
|
||||
}
|
||||
autoplay = "indicate"
|
||||
|
||||
[node name="Area2DCollision" type="Area2D" parent="."]
|
||||
collision_layer = 1024
|
||||
collision_mask = 704
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2DCollision"]
|
||||
position = Vector2(0, -1)
|
||||
shape = SubResource("RectangleShape2D_nb533")
|
||||
debug_color = Color(0.7, 0.132592, 0.232379, 0.42)
|
||||
|
||||
[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"]
|
||||
[connection signal="body_entered" from="Area2DCollision" to="." method="_on_area_2d_collision_body_entered"]
|
||||
[connection signal="body_exited" from="Area2DCollision" to="." method="_on_area_2d_collision_body_exited"]
|
||||
@@ -140,6 +140,9 @@ func _land():
|
||||
tween.tween_property(sprite, "scale", Vector2(1.0, 1.0), 0.1)
|
||||
|
||||
print(name, " landed!")
|
||||
$SfxLand.play()
|
||||
$DragParticles.emitting = true
|
||||
$DragParticles/TimerSmokeParticles.start()
|
||||
|
||||
func _handle_air_collision():
|
||||
# Handle collision while airborne
|
||||
@@ -273,11 +276,13 @@ func _break_into_pieces():
|
||||
|
||||
play_destroy_sound()
|
||||
self.set_deferred("collision_layer", 0)
|
||||
self.visible = false
|
||||
$Shadow.visible = false
|
||||
$Sprite2DAbove.visible = false
|
||||
$Sprite2D.visible = false
|
||||
if ($SfxShatter.playing):
|
||||
await $SfxShatter.finished
|
||||
if ($SfxBreakCrate.playing):
|
||||
await $SfxShatter.finished
|
||||
await $SfxBreakCrate.finished
|
||||
# Remove self
|
||||
queue_free()
|
||||
|
||||
@@ -652,6 +657,8 @@ func play_destroy_sound():
|
||||
$SfxShatter.play()
|
||||
else:
|
||||
$SfxBreakCrate.play()
|
||||
$DragParticles.emitting = true
|
||||
$DragParticles/TimerSmokeParticles.start()
|
||||
pass
|
||||
|
||||
func play_drag_sound():
|
||||
@@ -661,9 +668,16 @@ func play_drag_sound():
|
||||
else:
|
||||
if !$SfxDragRock.playing:
|
||||
$SfxDragRock.play()
|
||||
$DragParticles.emitting = true
|
||||
pass
|
||||
|
||||
func stop_drag_sound():
|
||||
$SfxDrag.stop()
|
||||
$SfxDragRock.stop()
|
||||
$DragParticles.emitting = false
|
||||
pass
|
||||
|
||||
|
||||
func _on_timer_smoke_particles_timeout() -> void:
|
||||
$DragParticles.emitting = false
|
||||
pass # Replace with function body.
|
||||
|
||||
@@ -552,7 +552,7 @@ func _sync_remove():
|
||||
queue_free()
|
||||
|
||||
@rpc("any_peer", "reliable")
|
||||
func _sync_show_floating_text(loot_type_value: int, text: String, color_value: Color, value: int, sprite_frame_value: int, player_peer_id: int):
|
||||
func _sync_show_floating_text(loot_type_value: int, text: String, color_value: Color, _value: int, sprite_frame_value: int, player_peer_id: int):
|
||||
# Client receives floating text sync from server
|
||||
if multiplayer.is_server():
|
||||
return # Server ignores this (it's the sender)
|
||||
|
||||
@@ -40,6 +40,9 @@ var initial_player_position = Vector2.ZERO # Position of player when first grabb
|
||||
var object_blocked_by_wall = false # True if pushed object is blocked by a wall
|
||||
var was_dragging_last_frame = false # Track if we were dragging last frame to detect start/stop
|
||||
|
||||
# Level complete state
|
||||
var controls_disabled: bool = false # True when player has reached exit and controls should be disabled
|
||||
|
||||
# Being held state
|
||||
var being_held_by: Node = null
|
||||
var struggle_time: float = 0.0
|
||||
@@ -57,6 +60,7 @@ var attack_cooldown: float = 0.0 # No cooldown - instant attacks!
|
||||
var is_attacking: bool = false
|
||||
var sword_slash_scene = preload("res://scenes/sword_slash.tscn") # Old rotating version (kept for reference)
|
||||
var sword_projectile_scene = preload("res://scenes/sword_projectile.tscn") # New projectile version
|
||||
var blood_scene = preload("res://scenes/blood_clot.tscn")
|
||||
|
||||
# Simulated Z-axis for height (when thrown)
|
||||
var position_z: float = 0.0
|
||||
@@ -737,6 +741,11 @@ func _physics_process(delta):
|
||||
if is_dead:
|
||||
return
|
||||
|
||||
# Skip input if controls are disabled (e.g., when player reached exit)
|
||||
if controls_disabled:
|
||||
velocity = velocity.lerp(Vector2.ZERO, delta * 8.0) # Slow down movement
|
||||
return
|
||||
|
||||
# Handle knockback timer
|
||||
if is_knocked_back:
|
||||
knockback_time += delta
|
||||
@@ -2186,6 +2195,19 @@ func _die():
|
||||
|
||||
# Play death sound effect
|
||||
if sfx_die:
|
||||
for i in 12:
|
||||
var angle = randf_range(0, TAU)
|
||||
var speed = randf_range(50, 100)
|
||||
var initial_velocityZ = randf_range(50, 90)
|
||||
var b = blood_scene.instantiate() as CharacterBody2D
|
||||
b.scale = Vector2(randf_range(0.3, 2), randf_range(0.3, 2))
|
||||
b.global_position = global_position
|
||||
|
||||
# Set initial velocities from the synchronized data
|
||||
var direction = Vector2.from_angle(angle)
|
||||
b.velocity = direction * speed
|
||||
b.velocityZ = initial_velocityZ
|
||||
get_parent().call_deferred("add_child", b)
|
||||
sfx_die.play()
|
||||
|
||||
# Play DIE animation
|
||||
@@ -2419,20 +2441,26 @@ func add_coins(amount: int):
|
||||
|
||||
# Sync coins to client if this is server-side coin collection
|
||||
# (e.g., when loot is collected on server, sync to client)
|
||||
if multiplayer.has_multiplayer_peer() and multiplayer.is_server() and not is_multiplayer_authority() and can_send_rpcs and is_inside_tree():
|
||||
# Server is adding coins to a client's player - sync to the client
|
||||
var peer_id = get_multiplayer_authority()
|
||||
if peer_id != 0:
|
||||
_sync_stats_update.rpc_id(peer_id, character_stats.kills, character_stats.coin)
|
||||
if multiplayer.has_multiplayer_peer() and multiplayer.is_server() and can_send_rpcs and is_inside_tree():
|
||||
# Server is adding coins to a player - sync to the client if it's a client player
|
||||
var the_peer_id = get_multiplayer_authority()
|
||||
# Only sync if this is a client player (not server's own player)
|
||||
if the_peer_id != 0 and the_peer_id != multiplayer.get_unique_id():
|
||||
print(name, " syncing stats to client peer_id=", the_peer_id, " kills=", character_stats.kills, " coins=", character_stats.coin)
|
||||
_sync_stats_update.rpc_id(the_peer_id, character_stats.kills, character_stats.coin)
|
||||
else:
|
||||
coins += amount
|
||||
print(name, " picked up ", amount, " coin(s)! Total coins: ", coins)
|
||||
|
||||
|
||||
@rpc("authority", "reliable")
|
||||
@rpc("any_peer", "reliable")
|
||||
func _sync_stats_update(kills_count: int, coins_count: int):
|
||||
# Client receives stats update from server (for kills and coins)
|
||||
# Update local stats to match server
|
||||
# Only process on client (not on server where the update originated)
|
||||
if multiplayer.is_server():
|
||||
return # Server ignores this (it's the sender)
|
||||
|
||||
if character_stats:
|
||||
character_stats.kills = kills_count
|
||||
character_stats.coin = coins_count
|
||||
|
||||
Reference in New Issue
Block a user