delete files in nickes
This commit is contained in:
@@ -9,7 +9,7 @@ extends StaticBody2D
|
||||
@export var is_closed: bool = true
|
||||
var is_closing:bool = false
|
||||
var is_opening:bool = false
|
||||
var time_to_move:float = 0.5
|
||||
var time_to_move:float = 0.2
|
||||
var move_timer:float = 0.0
|
||||
var animation_start_position: Vector2 = Vector2.ZERO # Position when animation started
|
||||
|
||||
@@ -35,6 +35,9 @@ var connected_switches: Array = [] # Array of floor switch nodes
|
||||
var requires_enemies: bool = false # True if door requires defeating enemies to open
|
||||
var requires_switch: bool = false # True if door requires activating switches to open
|
||||
|
||||
# Smoke puff scene for StoneDoor effects
|
||||
var smoke_puff_scene = preload("res://scenes/smoke_puff.tscn")
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
# Set texture based on door type
|
||||
@@ -209,6 +212,11 @@ func _process(delta: float) -> void:
|
||||
# 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")
|
||||
|
||||
# Spawn smoke puffs when StoneDoor finishes opening (1-2 puffs)
|
||||
if type == "StoneDoor":
|
||||
_spawn_smoke_puffs_on_open()
|
||||
|
||||
# Animation finished, reset flags
|
||||
is_opening = false
|
||||
is_closing = false
|
||||
@@ -222,6 +230,11 @@ func _process(delta: float) -> void:
|
||||
# When at closed position, collision should be ENABLED
|
||||
set_collision_layer_value(7, true)
|
||||
print("Door: Closing animation complete - moved to closed position: ", closed_position, " - collision ENABLED")
|
||||
|
||||
# Spawn smoke puffs when StoneDoor finishes closing (1-3 puffs)
|
||||
if type == "StoneDoor":
|
||||
_spawn_smoke_puffs_on_close()
|
||||
|
||||
# Animation finished, reset flags
|
||||
is_opening = false
|
||||
is_closing = false
|
||||
@@ -295,6 +308,10 @@ func _update_collision_based_on_position():
|
||||
set_collision_layer_value(7, false)
|
||||
|
||||
func _open():
|
||||
# Only open on server/authority in multiplayer, then sync to clients
|
||||
if multiplayer.has_multiplayer_peer() and not multiplayer.is_server():
|
||||
return # Clients wait for RPC
|
||||
|
||||
$TeleporterIntoClosedRoom.is_enabled = false
|
||||
# CRITICAL: For KeyDoors, ensure they start from closed position before opening
|
||||
# KeyDoors should ALWAYS start from closed position when opening (never from open position)
|
||||
@@ -338,8 +355,10 @@ func _open():
|
||||
else:
|
||||
push_error("Door: StoneDoor/GateDoor _open() called but closed_position is zero!")
|
||||
return
|
||||
|
||||
$SfxOpenStoneDoor.play()
|
||||
if type == "GateDoor":
|
||||
$SfxOpenGateDoor.play()
|
||||
else:
|
||||
$SfxOpenStoneDoor.play()
|
||||
|
||||
# CRITICAL: Store starting position for animation (should be closed_position)
|
||||
animation_start_position = position
|
||||
@@ -348,7 +367,17 @@ func _open():
|
||||
is_closing = false
|
||||
move_timer = 0.0
|
||||
|
||||
# Sync door opening to clients in multiplayer
|
||||
if multiplayer.has_multiplayer_peer() and multiplayer.is_server() and is_inside_tree():
|
||||
_sync_door_open.rpc()
|
||||
# Also sync puzzle_solved state
|
||||
_sync_puzzle_solved.rpc(puzzle_solved)
|
||||
|
||||
func _close():
|
||||
# Only close on server/authority in multiplayer, then sync to clients
|
||||
if multiplayer.has_multiplayer_peer() and not multiplayer.is_server():
|
||||
return # Clients wait for RPC
|
||||
|
||||
# CRITICAL: KeyDoors should NEVER be closed (they only open with a key and stay open)
|
||||
if type == "KeyDoor":
|
||||
print("Door: ERROR - _close() called on KeyDoor! KeyDoors should never be closed!")
|
||||
@@ -401,11 +430,18 @@ func _close():
|
||||
animation_start_position = position
|
||||
|
||||
print("Door: Starting close animation from ", animation_start_position, " to ", closed_position, " (offset: ", open_offset, ")")
|
||||
$SfxDoorCloses.play()
|
||||
if type == "GateDoor":
|
||||
$SfxCloseGateDoor.play()
|
||||
else:
|
||||
$SfxDoorCloses.play()
|
||||
is_opening = false
|
||||
is_closing = true
|
||||
move_timer = 0.0
|
||||
$TeleporterIntoClosedRoom.is_enabled = true
|
||||
|
||||
# Sync door closing to clients in multiplayer
|
||||
if multiplayer.has_multiplayer_peer() and multiplayer.is_server() and is_inside_tree():
|
||||
_sync_door_close.rpc()
|
||||
|
||||
func _ready_after_setup():
|
||||
# Called after door is fully set up with room references and positioned
|
||||
@@ -646,27 +682,77 @@ func _on_room_exited(body):
|
||||
# Doors stay in their current state
|
||||
|
||||
func _check_puzzle_state():
|
||||
# Check if room puzzle is solved
|
||||
# IMPORTANT: Only check puzzle state if we're in the blocking room
|
||||
if puzzle_solved:
|
||||
return # Already solved
|
||||
# Only check puzzle state on server/authority in multiplayer
|
||||
if multiplayer.has_multiplayer_peer() and not multiplayer.is_server():
|
||||
return # Clients wait for server to check and sync via RPC
|
||||
|
||||
# CRITICAL: Don't check puzzle state while door is animating (closing or opening)
|
||||
# This prevents race conditions where switch triggers before door finishes closing
|
||||
if is_closing or is_opening:
|
||||
print("Door: Skipping puzzle check - door is animating (is_closing: ", is_closing, ", is_opening: ", is_opening, ")")
|
||||
return
|
||||
|
||||
# Check door's actual state (position-based check is more reliable than flags)
|
||||
var distance_to_closed = position.distance_to(closed_position) if closed_position != Vector2.ZERO else 999.0
|
||||
var is_actually_closed = distance_to_closed < 5.0 # Within 5 pixels of closed position
|
||||
var is_actually_open = distance_to_closed > 5.0 # More than 5 pixels away from closed position
|
||||
var collision_enabled = get_collision_layer_value(7) # Check if collision layer 7 is enabled
|
||||
|
||||
# CRITICAL: If puzzle_solved is true but door is not actually open (not in open position or collision still enabled),
|
||||
# allow switch to trigger again to open the door
|
||||
# This handles race conditions where switch triggers while door is still closing
|
||||
if puzzle_solved and (not is_actually_open or collision_enabled):
|
||||
# Door should be open but isn't (position or collision) - reset puzzle_solved to allow switch to trigger again
|
||||
print("Door: puzzle_solved is true but door is not actually open (position: ", is_actually_open, ", collision: ", collision_enabled, ") - resetting to allow switch to trigger again")
|
||||
puzzle_solved = false
|
||||
switches_activated = false
|
||||
|
||||
# Check if all enemies are defeated (enemies in blocking room)
|
||||
if requires_enemies and _are_all_enemies_defeated():
|
||||
print("Door: All enemies defeated! Opening door ", name, " (type: ", type, ", room: ", blocking_room.get("x", "?") if blocking_room and not blocking_room.is_empty() else "?", ",", blocking_room.get("y", "?") if blocking_room and not blocking_room.is_empty() else "?", ")")
|
||||
enemies_defeated = true
|
||||
puzzle_solved = true
|
||||
if is_closed:
|
||||
if is_actually_closed:
|
||||
_open()
|
||||
return
|
||||
|
||||
# Check if all required switches are activated (switches in switch_room, before the door)
|
||||
if _are_all_switches_activated():
|
||||
var all_switches_active = _are_all_switches_activated()
|
||||
|
||||
# Check if any connected switches are pillar switches (for special handling)
|
||||
var has_pillar_switch = false
|
||||
for switch in connected_switches:
|
||||
if is_instance_valid(switch) and "switch_type" in switch and switch.switch_type == "pillar":
|
||||
has_pillar_switch = true
|
||||
break
|
||||
|
||||
if all_switches_active:
|
||||
# All switches are active - solve puzzle and open door if closed
|
||||
switches_activated = true
|
||||
puzzle_solved = true
|
||||
if is_closed:
|
||||
# Only open if door is actually closed (not just the flag, but actual position)
|
||||
# This prevents race condition where switch triggers while door is still closing
|
||||
if is_actually_closed:
|
||||
_open()
|
||||
return
|
||||
else:
|
||||
# Not all switches are active
|
||||
if puzzle_solved and has_pillar_switch:
|
||||
# Pillar switch became inactive and door was open - close it and reset puzzle
|
||||
print("Door: Pillar switch deactivated - closing door ", name)
|
||||
switches_activated = false
|
||||
puzzle_solved = false
|
||||
if not is_actually_closed:
|
||||
_close()
|
||||
return
|
||||
elif puzzle_solved and not has_pillar_switch:
|
||||
# Walk switch puzzle - once solved, stays solved (door stays open)
|
||||
# Don't reset puzzle_solved for walk switches
|
||||
return
|
||||
else:
|
||||
# Puzzle not solved yet - just reset flags
|
||||
switches_activated = false
|
||||
puzzle_solved = false
|
||||
|
||||
func _are_all_enemies_defeated() -> bool:
|
||||
# Check if all enemies spawned from spawners in the puzzle room are defeated
|
||||
@@ -693,16 +779,16 @@ func _are_all_enemies_defeated() -> bool:
|
||||
|
||||
# Check if enemy is in this room (use position-based check, more reliable)
|
||||
var enemy_in_room = false
|
||||
var tile_size = 16
|
||||
var enemy_tile_x = int(child.global_position.x / tile_size)
|
||||
var enemy_tile_y = int(child.global_position.y / tile_size)
|
||||
var room_min_x = target_room.x + 2
|
||||
var room_max_x = target_room.x + target_room.w - 2
|
||||
var room_min_y = target_room.y + 2
|
||||
var room_max_y = target_room.y + target_room.h - 2
|
||||
# Use tile_size and room bounds from parent scope (declared below)
|
||||
var enemy_tile_x = int(child.global_position.x / 16)
|
||||
var enemy_tile_y = int(child.global_position.y / 16)
|
||||
var enemy_room_min_x = target_room.x + 2
|
||||
var enemy_room_max_x = target_room.x + target_room.w - 2
|
||||
var enemy_room_min_y = target_room.y + 2
|
||||
var enemy_room_max_y = target_room.y + target_room.h - 2
|
||||
|
||||
if enemy_tile_x >= room_min_x and enemy_tile_x < room_max_x and \
|
||||
enemy_tile_y >= room_min_y and enemy_tile_y < room_max_y:
|
||||
if enemy_tile_x >= enemy_room_min_x and enemy_tile_x < enemy_room_max_x and \
|
||||
enemy_tile_y >= enemy_room_min_y and enemy_tile_y < enemy_room_max_y:
|
||||
enemy_in_room = true
|
||||
# Also check spawner metadata - if enemy has spawner_name matching this room's spawners
|
||||
if child.has_meta("spawner_name"):
|
||||
@@ -718,10 +804,247 @@ func _are_all_enemies_defeated() -> bool:
|
||||
# Check if all spawned enemies are dead
|
||||
print("Door: _are_all_enemies_defeated() - Found ", room_spawned_enemies.size(), " spawned enemies in room (", target_room.get("x", "?") if target_room and not target_room.is_empty() else "?", ",", target_room.get("y", "?") if target_room and not target_room.is_empty() else "?", ")")
|
||||
|
||||
# First, check if any enemies in room_spawned_enemies are still alive
|
||||
# If any are alive, puzzle is not solved
|
||||
for enemy in room_spawned_enemies:
|
||||
if is_instance_valid(enemy):
|
||||
var enemy_is_dead = false
|
||||
if "is_dead" in enemy:
|
||||
enemy_is_dead = enemy.is_dead
|
||||
else:
|
||||
enemy_is_dead = enemy.is_queued_for_deletion() or not enemy.is_inside_tree()
|
||||
|
||||
if not enemy_is_dead:
|
||||
print("Door: Enemy ", enemy.name, " is still alive - puzzle not solved yet")
|
||||
return false # Enemy is still alive, puzzle not solved
|
||||
|
||||
# If we have enemies and all are dead, puzzle is solved
|
||||
if room_spawned_enemies.size() > 0:
|
||||
print("Door: All ", room_spawned_enemies.size(), " spawned enemies are dead! Puzzle solved!")
|
||||
return true # All enemies found are dead
|
||||
|
||||
# No spawned enemies found - check if spawners have actually spawned enemies before
|
||||
# CRITICAL: Only consider puzzle solved if spawners have spawned enemies and they're all dead
|
||||
# Don't solve if spawners haven't spawned yet (e.g., spawn_on_ready=false and player hasn't entered room)
|
||||
|
||||
# IMPORTANT: Before checking spawners, verify there are NO ALIVE enemies in the room
|
||||
# This catches cases where enemies weren't added to room_spawned_enemies due to position check issues
|
||||
var entities_child = entities_node.get_node_or_null("Entities") if entities_node else null
|
||||
if not entities_child and entities_node:
|
||||
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||
if game_world:
|
||||
entities_child = game_world.get_node_or_null("Entities")
|
||||
|
||||
var tile_size = 16
|
||||
var room_min_x = target_room.x + 2
|
||||
var room_max_x = target_room.x + target_room.w - 2
|
||||
var room_min_y = target_room.y + 2
|
||||
var room_max_y = target_room.y + target_room.h - 2
|
||||
|
||||
if entities_child:
|
||||
for child in entities_child.get_children():
|
||||
if child.is_in_group("enemy"):
|
||||
# Only check enemies that were spawned from spawners
|
||||
if not child.has_meta("spawned_from_spawner") or not child.get_meta("spawned_from_spawner"):
|
||||
continue
|
||||
|
||||
# Check if enemy is in this room by position
|
||||
var enemy_tile_x = int(child.global_position.x / tile_size)
|
||||
var enemy_tile_y = int(child.global_position.y / tile_size)
|
||||
var enemy_in_room = (enemy_tile_x >= room_min_x and enemy_tile_x < room_max_x and \
|
||||
enemy_tile_y >= room_min_y and enemy_tile_y < room_max_y)
|
||||
|
||||
if not enemy_in_room:
|
||||
continue # Skip enemies not in this room
|
||||
|
||||
# Check if enemy is alive
|
||||
var enemy_is_alive = false
|
||||
if "is_dead" in child:
|
||||
enemy_is_alive = not child.is_dead
|
||||
else:
|
||||
enemy_is_alive = not child.is_queued_for_deletion() and child.is_inside_tree()
|
||||
|
||||
if enemy_is_alive:
|
||||
# Found an ALIVE enemy in this room - puzzle not solved!
|
||||
print("Door: Found ALIVE enemy ", child.name, " in room - puzzle not solved yet (enemy still alive)")
|
||||
return false
|
||||
|
||||
# No alive enemies found in room - now check if spawners have spawned
|
||||
if room_spawned_enemies.size() == 0:
|
||||
# No spawned enemies found - if door requires enemies, puzzle is not solved
|
||||
# But if there were never any enemies, this might mean they haven't spawned yet or all are already dead/removed
|
||||
print("Door: No spawned enemies found in room - puzzle not solved yet (enemies may not have spawned or already removed)")
|
||||
# No spawned enemies found - check if spawners have actually spawned enemies before
|
||||
# CRITICAL: Only consider puzzle solved if spawners have spawned enemies and they're all dead
|
||||
# Don't solve if spawners haven't spawned yet (e.g., spawn_on_ready=false and player hasn't entered room)
|
||||
|
||||
var spawners_in_room = []
|
||||
var spawners_that_have_spawned = []
|
||||
|
||||
# First, check ALL spawners in the room (including destroyed ones by checking for enemies with their names)
|
||||
for spawner in get_tree().get_nodes_in_group("enemy_spawner"):
|
||||
if is_instance_valid(spawner):
|
||||
var spawner_tile_x = int(spawner.global_position.x / 16)
|
||||
var spawner_tile_y = int(spawner.global_position.y / 16)
|
||||
if spawner_tile_x >= target_room.x + 2 and spawner_tile_x < target_room.x + target_room.w - 2 and \
|
||||
spawner_tile_y >= target_room.y + 2 and spawner_tile_y < target_room.y + target_room.h - 2:
|
||||
spawners_in_room.append(spawner)
|
||||
|
||||
# Check if this spawner has spawned by multiple methods:
|
||||
# 1. Check if spawner has has_ever_spawned flag (most reliable)
|
||||
# 2. Check if any enemies in scene were spawned by this spawner
|
||||
# 3. Check if spawner has spawned_enemies list with valid enemies
|
||||
var has_spawned = false
|
||||
|
||||
# First, check if spawner has has_ever_spawned flag
|
||||
if "has_ever_spawned" in spawner and spawner.has_ever_spawned:
|
||||
has_spawned = true
|
||||
|
||||
# Also check if any enemies in scene were spawned by this spawner (ONLY if they're dead/removed)
|
||||
# If we find ALIVE enemies, don't mark spawner as spawned - wait until they're dead
|
||||
if not has_spawned:
|
||||
var entities_child_for_spawner = entities_node.get_node_or_null("Entities") if entities_node else null
|
||||
if not entities_child_for_spawner and entities_node:
|
||||
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||
if game_world:
|
||||
entities_child_for_spawner = game_world.get_node_or_null("Entities")
|
||||
|
||||
if entities_child_for_spawner:
|
||||
for child in entities_child_for_spawner.get_children():
|
||||
if child.is_in_group("enemy"):
|
||||
if child.has_meta("spawner_name") and child.get_meta("spawner_name") == spawner.name:
|
||||
# Found an enemy spawned by this spawner
|
||||
# Check if it's alive - if alive, don't mark spawner as spawned yet
|
||||
var enemy_is_alive = false
|
||||
if "is_dead" in child:
|
||||
enemy_is_alive = not child.is_dead
|
||||
else:
|
||||
enemy_is_alive = not child.is_queued_for_deletion() and child.is_inside_tree()
|
||||
|
||||
if not enemy_is_alive:
|
||||
# Enemy is dead - spawner has spawned
|
||||
has_spawned = true
|
||||
break
|
||||
# If enemy is alive, don't mark spawner as spawned - wait until it's dead
|
||||
|
||||
# Also check if spawner currently has enemies in its spawned_enemies list
|
||||
# Only count if all enemies in the list are dead (puzzle can only be solved when all are dead)
|
||||
if not has_spawned and "spawned_enemies" in spawner:
|
||||
var spawned_enemies_list = spawner.spawned_enemies
|
||||
if spawned_enemies_list and spawned_enemies_list.size() > 0:
|
||||
# Check if all enemies in list are dead
|
||||
var all_enemies_dead = true
|
||||
for enemy in spawned_enemies_list:
|
||||
if is_instance_valid(enemy):
|
||||
var enemy_is_dead = false
|
||||
if "is_dead" in enemy:
|
||||
enemy_is_dead = enemy.is_dead
|
||||
else:
|
||||
enemy_is_dead = enemy.is_queued_for_deletion() or not enemy.is_inside_tree()
|
||||
|
||||
if not enemy_is_dead:
|
||||
all_enemies_dead = false
|
||||
break
|
||||
|
||||
# Only mark spawner as spawned if all enemies are dead
|
||||
if all_enemies_dead:
|
||||
has_spawned = true
|
||||
|
||||
if has_spawned:
|
||||
spawners_that_have_spawned.append(spawner)
|
||||
|
||||
# Also check for spawners that may have been destroyed but had enemies spawned
|
||||
# Look for any dead enemies with spawner name matching this room AND position in this room
|
||||
var spawner_name_pattern = "EnemySpawner_" + str(target_room.x) + "_" + str(target_room.y)
|
||||
var found_dead_enemies_with_matching_spawner = []
|
||||
if entities_child:
|
||||
for child in entities_child.get_children():
|
||||
if child.is_in_group("enemy"):
|
||||
# Only check enemies that were spawned from spawners
|
||||
if not child.has_meta("spawned_from_spawner") or not child.get_meta("spawned_from_spawner"):
|
||||
continue
|
||||
|
||||
# Check if enemy is in this room by position
|
||||
var enemy_tile_x = int(child.global_position.x / tile_size)
|
||||
var enemy_tile_y = int(child.global_position.y / tile_size)
|
||||
var enemy_in_room = (enemy_tile_x >= room_min_x and enemy_tile_x < room_max_x and \
|
||||
enemy_tile_y >= room_min_y and enemy_tile_y < room_max_y)
|
||||
|
||||
if not enemy_in_room:
|
||||
continue # Skip enemies not in this room
|
||||
|
||||
# Check if enemy is dead
|
||||
var enemy_is_dead = false
|
||||
if "is_dead" in child:
|
||||
enemy_is_dead = child.is_dead
|
||||
else:
|
||||
enemy_is_dead = child.is_queued_for_deletion() or not child.is_inside_tree()
|
||||
|
||||
# Only check dead/removed enemies as evidence that spawners spawned
|
||||
if enemy_is_dead and child.has_meta("spawner_name"):
|
||||
var enemy_spawner_name = child.get_meta("spawner_name")
|
||||
if spawner_name_pattern in enemy_spawner_name or enemy_spawner_name == spawner_name_pattern:
|
||||
found_dead_enemies_with_matching_spawner.append(child)
|
||||
|
||||
# Track unique spawner names that have spawned (only based on dead enemies)
|
||||
var unique_spawner_names_that_spawned = {}
|
||||
for enemy in found_dead_enemies_with_matching_spawner:
|
||||
if enemy.has_meta("spawner_name"):
|
||||
var spawner_name = enemy.get_meta("spawner_name")
|
||||
unique_spawner_names_that_spawned[spawner_name] = true
|
||||
|
||||
# Only mark spawners as spawned if we found DEAD enemies (ensures puzzle only solves when all are dead)
|
||||
if unique_spawner_names_that_spawned.size() > 0:
|
||||
# Found dead enemies with matching spawner names - spawners definitely spawned and enemies are dead
|
||||
if spawners_in_room.size() == 0:
|
||||
for spawner_name in unique_spawner_names_that_spawned.keys():
|
||||
spawners_in_room.append(null) # Placeholder for destroyed spawner
|
||||
spawners_that_have_spawned.append(null) # Count as spawned
|
||||
print("Door: Spawner ", spawner_name, " was destroyed but spawned enemies that are now all dead - counting as spawned")
|
||||
else:
|
||||
# Spawners exist - check if any weren't counted as spawned yet
|
||||
for spawner_name in unique_spawner_names_that_spawned.keys():
|
||||
var spawner_already_counted = false
|
||||
for spawned_spawner in spawners_that_have_spawned:
|
||||
if is_instance_valid(spawned_spawner) and spawned_spawner.name == spawner_name:
|
||||
spawner_already_counted = true
|
||||
break
|
||||
|
||||
if not spawner_already_counted:
|
||||
for i in range(spawners_in_room.size()):
|
||||
var spawner = spawners_in_room[i]
|
||||
if is_instance_valid(spawner) and spawner.name == spawner_name:
|
||||
spawners_that_have_spawned.append(spawner)
|
||||
print("Door: Found dead enemy from spawner ", spawner_name, " - marking as spawned")
|
||||
break
|
||||
|
||||
|
||||
# Only solve if:
|
||||
# 1. There are spawners in the room (or were spawners that spawned)
|
||||
# 2. All spawners have spawned enemies
|
||||
# 3. All those enemies are now dead (since no enemies found in room)
|
||||
|
||||
# Count valid spawners (including null placeholders for destroyed spawners)
|
||||
var valid_spawners_count = spawners_in_room.size()
|
||||
var valid_spawned_count = spawners_that_have_spawned.size()
|
||||
|
||||
# If we have spawners (including destroyed ones) and they've all spawned, puzzle is solved
|
||||
if valid_spawners_count > 0 and valid_spawned_count >= valid_spawners_count:
|
||||
# All spawners in room have spawned at least once, and no enemies found in room
|
||||
# This means all spawned enemies are dead - puzzle solved!
|
||||
print("Door: No spawned enemies found, but all ", valid_spawners_count, " spawners in room have spawned enemies that are all dead - puzzle solved!")
|
||||
return true
|
||||
|
||||
# Also check: if no spawners found (they were destroyed), but this is a puzzle room (has blocking doors),
|
||||
# and we previously found enemies with matching spawner names that are now gone,
|
||||
# consider the puzzle solved
|
||||
if valid_spawners_count == 0 and valid_spawned_count > 0:
|
||||
# Spawners were destroyed, but we found evidence they spawned
|
||||
# Since no enemies found, they must all be dead - puzzle solved!
|
||||
print("Door: No spawners or enemies found, but found evidence of spawned enemies that are now all dead - puzzle solved!")
|
||||
return true
|
||||
|
||||
if valid_spawners_count > 0:
|
||||
print("Door: Spawners in room (", valid_spawners_count, ") but only ", valid_spawned_count, " have spawned - puzzle not solved yet")
|
||||
else:
|
||||
print("Door: No spawned enemies found in room - puzzle not solved yet (enemies may not have spawned or already removed)")
|
||||
return false
|
||||
|
||||
for enemy in room_spawned_enemies:
|
||||
@@ -743,6 +1066,64 @@ func _are_all_enemies_defeated() -> bool:
|
||||
print("Door: All ", room_spawned_enemies.size(), " spawned enemies are dead! Puzzle solved!")
|
||||
return true # All enemies are dead
|
||||
|
||||
func _spawn_smoke_puffs_on_close():
|
||||
# Spawn 1-3 smoke puffs when StoneDoor finishes closing
|
||||
if not smoke_puff_scene:
|
||||
return
|
||||
|
||||
var puff_count = randi_range(1, 3) # Random between 1-3 puffs
|
||||
|
||||
for i in range(puff_count):
|
||||
var puff = smoke_puff_scene.instantiate()
|
||||
if puff:
|
||||
# Spawn at door position with small random offset
|
||||
var offset_x = randf_range(-8, 8)
|
||||
var offset_y = randf_range(-8, 8)
|
||||
puff.global_position = global_position + Vector2(offset_x, offset_y)
|
||||
puff.z_index = 10 # High z-index to ensure visibility
|
||||
|
||||
# Add to Entities node for proper layering
|
||||
var entities_node = get_tree().get_first_node_in_group("game_world")
|
||||
if entities_node:
|
||||
entities_node = entities_node.get_node_or_null("Entities")
|
||||
if entities_node:
|
||||
entities_node.add_child(puff)
|
||||
else:
|
||||
# Fallback: add to scene root
|
||||
get_tree().current_scene.add_child(puff)
|
||||
else:
|
||||
# Fallback: add to scene root
|
||||
get_tree().current_scene.add_child(puff)
|
||||
|
||||
func _spawn_smoke_puffs_on_open():
|
||||
# Spawn 1-2 smoke puffs when StoneDoor starts opening
|
||||
if not smoke_puff_scene:
|
||||
return
|
||||
|
||||
var puff_count = randi_range(1, 2) # Random between 1-2 puffs
|
||||
|
||||
for i in range(puff_count):
|
||||
var puff = smoke_puff_scene.instantiate()
|
||||
if puff:
|
||||
# Spawn at door position with small random offset
|
||||
var offset_x = randf_range(-8, 8)
|
||||
var offset_y = randf_range(-8, 8)
|
||||
puff.global_position = global_position + Vector2(offset_x, offset_y)
|
||||
puff.z_index = 10 # High z-index to ensure visibility
|
||||
|
||||
# Add to Entities node for proper layering
|
||||
var entities_node = get_tree().get_first_node_in_group("game_world")
|
||||
if entities_node:
|
||||
entities_node = entities_node.get_node_or_null("Entities")
|
||||
if entities_node:
|
||||
entities_node.add_child(puff)
|
||||
else:
|
||||
# Fallback: add to scene root
|
||||
get_tree().current_scene.add_child(puff)
|
||||
else:
|
||||
# Fallback: add to scene root
|
||||
get_tree().current_scene.add_child(puff)
|
||||
|
||||
func _are_all_switches_activated() -> bool:
|
||||
# Check if all required switches are activated
|
||||
# CRITICAL: ONLY check connected_switches - switches are explicitly connected when spawned
|
||||
@@ -795,6 +1176,11 @@ func _show_key_indicator():
|
||||
key_indicator.visible = true
|
||||
|
||||
func teleportPlayer(body: Node2D):
|
||||
# Only teleport on server in multiplayer (server is authority for all players)
|
||||
if multiplayer.has_multiplayer_peer() and not multiplayer.is_server():
|
||||
# Client should wait for server to sync position
|
||||
return
|
||||
|
||||
var keydoor_open_offset = Vector2.ZERO
|
||||
match direction:
|
||||
"Up":
|
||||
@@ -805,5 +1191,93 @@ func teleportPlayer(body: Node2D):
|
||||
keydoor_open_offset = Vector2(16, 0) # Open is 16px LEFT from closed
|
||||
"Right":
|
||||
keydoor_open_offset = Vector2(-16, 0) # Open is 16px RIGHT from closed
|
||||
body.position = self.global_position + keydoor_open_offset
|
||||
|
||||
var new_position = self.global_position + keydoor_open_offset
|
||||
|
||||
if body.is_in_group("player"):
|
||||
# Player teleportation - set position on server and sync to all clients
|
||||
if multiplayer.has_multiplayer_peer() and multiplayer.is_server():
|
||||
# Server: set position directly and sync to all clients
|
||||
body.global_position = new_position
|
||||
# Also reset velocity to prevent player from moving back
|
||||
if "velocity" in body:
|
||||
body.velocity = Vector2.ZERO
|
||||
# Sync position to all clients (including the teleported player's client)
|
||||
if body.has_method("_sync_teleport_position") and body.can_send_rpcs and body.is_inside_tree():
|
||||
body._sync_teleport_position.rpc(new_position)
|
||||
else:
|
||||
# Single-player: just set position
|
||||
body.global_position = new_position
|
||||
if "velocity" in body:
|
||||
body.velocity = Vector2.ZERO
|
||||
else:
|
||||
# Non-player teleportation (shouldn't happen, but handle it)
|
||||
body.global_position = new_position
|
||||
pass
|
||||
|
||||
@rpc("authority", "reliable")
|
||||
func _sync_door_open():
|
||||
# Client receives door open RPC - open the door locally
|
||||
if multiplayer.has_multiplayer_peer() and not multiplayer.is_server():
|
||||
# Only open if door is closed (avoid reopening already open doors)
|
||||
var distance_to_closed = position.distance_to(closed_position) if closed_position != Vector2.ZERO else 999.0
|
||||
var is_actually_open = distance_to_closed > 5.0
|
||||
|
||||
if not is_actually_open and not is_opening:
|
||||
# Door is closed - open it
|
||||
if closed_position != Vector2.ZERO:
|
||||
position = closed_position
|
||||
global_position = closed_position
|
||||
is_closed = true
|
||||
set_collision_layer_value(7, true)
|
||||
|
||||
animation_start_position = position
|
||||
is_opening = true
|
||||
is_closing = false
|
||||
move_timer = 0.0
|
||||
|
||||
# Play sound effect on client
|
||||
if type == "KeyDoor":
|
||||
$SfxOpenKeyDoor.play()
|
||||
elif type == "GateDoor":
|
||||
$SfxOpenGateDoor.play()
|
||||
else:
|
||||
$SfxOpenStoneDoor.play()
|
||||
|
||||
print("Door: Client received door open RPC for ", name, " - starting open animation")
|
||||
|
||||
@rpc("authority", "reliable")
|
||||
func _sync_puzzle_solved(is_solved: bool):
|
||||
# Client receives puzzle solved state sync
|
||||
if multiplayer.has_multiplayer_peer() and not multiplayer.is_server():
|
||||
puzzle_solved = is_solved
|
||||
if is_solved:
|
||||
enemies_defeated = true
|
||||
switches_activated = true
|
||||
print("Door: Client received puzzle_solved sync for ", name, " - puzzle_solved: ", is_solved)
|
||||
|
||||
@rpc("authority", "reliable")
|
||||
func _sync_door_close():
|
||||
# Client receives door close RPC - close the door locally
|
||||
if multiplayer.has_multiplayer_peer() and not multiplayer.is_server():
|
||||
# Only close if door is open (avoid reclosing already closed doors)
|
||||
var distance_to_closed = position.distance_to(closed_position) if closed_position != Vector2.ZERO else 999.0
|
||||
var is_actually_at_closed = distance_to_closed < 5.0
|
||||
|
||||
if not is_actually_at_closed and not is_closing:
|
||||
# Door is open - close it
|
||||
var expected_open_pos = closed_position + open_offset
|
||||
var distance_to_open = position.distance_to(expected_open_pos)
|
||||
|
||||
if distance_to_open > 10.0:
|
||||
animation_start_position = expected_open_pos
|
||||
position = expected_open_pos
|
||||
global_position = expected_open_pos
|
||||
else:
|
||||
animation_start_position = position
|
||||
|
||||
is_opening = false
|
||||
is_closing = true
|
||||
move_timer = 0.0
|
||||
|
||||
print("Door: Client received door close RPC for ", name, " - starting close animation")
|
||||
|
||||
Reference in New Issue
Block a user