added blocking doors to paths.
This commit is contained in:
@@ -16,10 +16,16 @@ func _ready():
|
||||
print(" Position: ", global_position)
|
||||
print(" Is server: ", multiplayer.is_server())
|
||||
print(" Has multiplayer peer: ", multiplayer.has_multiplayer_peer())
|
||||
print(" Is authority: ", is_multiplayer_authority() if multiplayer.has_multiplayer_peer() else "N/A")
|
||||
print(" spawn_on_ready: ", spawn_on_ready)
|
||||
print(" max_enemies: ", max_enemies)
|
||||
print(" enemy_scenes.size(): ", enemy_scenes.size())
|
||||
print(" Parent: ", get_parent())
|
||||
|
||||
# Verify enemy_scenes is set
|
||||
if enemy_scenes.size() == 0:
|
||||
push_error("EnemySpawner: ERROR - enemy_scenes array is EMPTY! Spawner will not be able to spawn enemies!")
|
||||
|
||||
# Spawn on server, or in single player (no multiplayer peer)
|
||||
var should_spawn = spawn_on_ready and (multiplayer.is_server() or not multiplayer.has_multiplayer_peer())
|
||||
print(" Should spawn? ", should_spawn)
|
||||
@@ -28,7 +34,7 @@ func _ready():
|
||||
print(" Calling spawn_enemy()...")
|
||||
call_deferred("spawn_enemy") # Use call_deferred to ensure scene is ready
|
||||
else:
|
||||
print(" NOT spawning - conditions not met")
|
||||
print(" NOT spawning - conditions not met (spawn_on_ready=", spawn_on_ready, ", will spawn when player enters room)")
|
||||
print("========================================")
|
||||
|
||||
func _process(delta):
|
||||
@@ -48,6 +54,23 @@ func _process(delta):
|
||||
|
||||
func spawn_enemy():
|
||||
print(">>> spawn_enemy() CALLED <<<")
|
||||
print(" Spawner: ", name, " at ", global_position)
|
||||
print(" enemy_scenes.size(): ", enemy_scenes.size())
|
||||
print(" spawned_enemies.size(): ", spawned_enemies.size(), " / max_enemies: ", max_enemies)
|
||||
print(" spawn_on_ready: ", spawn_on_ready)
|
||||
|
||||
# CRITICAL: Check if we can spawn (don't spawn if already at max)
|
||||
# Clean up dead enemies first
|
||||
spawned_enemies = spawned_enemies.filter(func(e): return is_instance_valid(e) and not e.is_dead)
|
||||
|
||||
if spawned_enemies.size() >= max_enemies:
|
||||
print(" ERROR: Cannot spawn - already at max enemies (", spawned_enemies.size(), " >= ", max_enemies, ")")
|
||||
return
|
||||
|
||||
# Only spawn on server (authority)
|
||||
if multiplayer.has_multiplayer_peer() and not is_multiplayer_authority():
|
||||
print(" ERROR: Cannot spawn - not multiplayer authority!")
|
||||
return
|
||||
|
||||
# Choose enemy scene to spawn
|
||||
var scene_to_spawn: PackedScene = null
|
||||
@@ -55,15 +78,39 @@ func spawn_enemy():
|
||||
# Use random scene from list
|
||||
scene_to_spawn = enemy_scenes[randi() % enemy_scenes.size()]
|
||||
print(" Selected enemy scene from list: ", scene_to_spawn)
|
||||
else:
|
||||
push_error("ERROR: enemy_scenes array is EMPTY! Spawner has no enemy scenes to spawn!")
|
||||
return
|
||||
|
||||
if not scene_to_spawn:
|
||||
push_error("ERROR: No enemy scene set for spawner! Add scenes to enemy_scenes array.")
|
||||
push_error("ERROR: Failed to select enemy scene!")
|
||||
return
|
||||
|
||||
print(" Spawning enemy at ", global_position)
|
||||
|
||||
# Spawn smoke puff effect
|
||||
_spawn_smoke_puff()
|
||||
# CRITICAL: Spawn 3-4 smoke puffs first, wait for them to finish, THEN spawn enemy
|
||||
var num_puffs = randi_range(3, 4) # 3 or 4 smoke puffs
|
||||
print(" Spawning ", num_puffs, " smoke puffs before enemy...")
|
||||
|
||||
# Spawn multiple smoke puffs at slightly different positions
|
||||
var smoke_puffs = []
|
||||
var puff_spawn_radius = 8.0 # Pixels - spawn puffs in a small area around spawner
|
||||
|
||||
for i in range(num_puffs):
|
||||
var puff_offset = Vector2(
|
||||
randf_range(-puff_spawn_radius, puff_spawn_radius),
|
||||
randf_range(-puff_spawn_radius, puff_spawn_radius)
|
||||
)
|
||||
var puff = _spawn_smoke_puff_at_position(global_position + puff_offset)
|
||||
if puff:
|
||||
smoke_puffs.append(puff)
|
||||
|
||||
# Wait for smoke puffs to finish animating before spawning enemy
|
||||
# Smoke puff animation: 4 frames * (1/10.0) seconds per frame = 0.4s, plus move_duration 1.5s, plus fade_duration 0.5s = ~2.4s total
|
||||
var smoke_animation_duration = (4.0 / 10.0) + 1.5 + 0.5 # Total animation time
|
||||
await get_tree().create_timer(smoke_animation_duration).timeout
|
||||
|
||||
print(" Smoke puffs finished - now spawning enemy...")
|
||||
|
||||
print(" Instantiating enemy scene...")
|
||||
var enemy = scene_to_spawn.instantiate()
|
||||
@@ -79,6 +126,18 @@ func spawn_enemy():
|
||||
enemy.spawn_position = global_position
|
||||
print(" Set enemy position to: ", global_position)
|
||||
|
||||
# CRITICAL: Mark this enemy as spawned from a spawner (for door puzzle tracking)
|
||||
enemy.set_meta("spawned_from_spawner", true)
|
||||
enemy.set_meta("spawner_name", name)
|
||||
|
||||
# CRITICAL: Set collision mask BEFORE adding to scene to ensure enemies collide with walls (layer 7 = bit 6 = 64)
|
||||
# This overrides any collision_mask set in the scene file
|
||||
enemy.collision_mask = 1 | 2 | 64 # Collide with players (layer 1), objects (layer 2), and walls (layer 7 = bit 6 = 64)
|
||||
|
||||
# Set multiplayer authority BEFORE adding to scene tree (CRITICAL for RPC to work!)
|
||||
if multiplayer.has_multiplayer_peer():
|
||||
enemy.set_multiplayer_authority(1)
|
||||
|
||||
# Add to YSort node for automatic Y-sorting
|
||||
var ysort = get_parent().get_node_or_null("Entities")
|
||||
var parent = ysort if ysort else get_parent()
|
||||
@@ -91,9 +150,9 @@ func spawn_enemy():
|
||||
print(" Adding enemy as child...")
|
||||
parent.add_child(enemy)
|
||||
|
||||
# Set multiplayer authority to server (peer 1)
|
||||
if multiplayer.has_multiplayer_peer():
|
||||
enemy.set_multiplayer_authority(1)
|
||||
# CRITICAL: Re-verify collision_mask AFTER adding to scene (in case _ready() or scene file overrides it)
|
||||
# Use call_deferred to ensure _ready() has completed first, then set the entire mask
|
||||
call_deferred("_verify_enemy_collision_mask", enemy)
|
||||
|
||||
# Determine which scene index was used (for syncing to clients)
|
||||
var scene_index = -1
|
||||
@@ -112,18 +171,32 @@ func spawn_enemy():
|
||||
print(" ✓ Successfully spawned enemy: ", enemy.name, " at ", global_position, " scene_index: ", scene_index)
|
||||
print(" Total spawned enemies: ", spawned_enemies.size())
|
||||
|
||||
# If this spawner is marked for one-time spawn, destroy it after spawning
|
||||
if has_meta("spawn_once") and get_meta("spawn_once"):
|
||||
print(" Spawner marked for one-time spawn - destroying after spawn")
|
||||
call_deferred("queue_free") # Destroy spawner after spawning once
|
||||
|
||||
# Sync spawn to all clients via GameWorld
|
||||
if multiplayer.has_multiplayer_peer() and multiplayer.is_server():
|
||||
# Get GameWorld directly since spawner is a child of GameWorld
|
||||
var game_world = get_parent()
|
||||
print(" DEBUG: game_world=", game_world, " spawner name=", name)
|
||||
# Get GameWorld by traversing up the tree (spawner is child of Entities, which is child of GameWorld)
|
||||
var game_world = get_tree().get_first_node_in_group("game_world")
|
||||
if not game_world:
|
||||
# Fallback: traverse up the tree to find GameWorld
|
||||
var node = get_parent()
|
||||
while node:
|
||||
if node.has_method("_sync_enemy_spawn"):
|
||||
game_world = node
|
||||
break
|
||||
node = node.get_parent()
|
||||
|
||||
if game_world and game_world.has_method("_sync_enemy_spawn"):
|
||||
# Use spawner name (relative to GameWorld) since it's a direct child
|
||||
# Use spawner name for identification
|
||||
print(" DEBUG: Calling _sync_enemy_spawn.rpc with name=", name, " pos=", global_position, " scene_index=", scene_index)
|
||||
game_world._sync_enemy_spawn.rpc(name, global_position, scene_index)
|
||||
print(" Sent RPC to sync enemy spawn to clients: spawner=", name, " pos=", global_position, " scene_index=", scene_index)
|
||||
else:
|
||||
push_error("ERROR: Could not find GameWorld or _sync_enemy_spawn method! game_world=", game_world, " has_method=", game_world.has_method("_sync_enemy_spawn") if game_world else "N/A")
|
||||
var has_method_str = str(game_world.has_method("_sync_enemy_spawn")) if game_world else "N/A"
|
||||
push_error("ERROR: Could not find GameWorld or _sync_enemy_spawn method! game_world=", game_world, " has_method=", has_method_str)
|
||||
|
||||
func spawn_enemy_at_position(spawn_pos: Vector2, scene_index: int = -1):
|
||||
# This method is called by GameWorld RPC to spawn enemies on clients
|
||||
@@ -158,6 +231,14 @@ func spawn_enemy_at_position(spawn_pos: Vector2, scene_index: int = -1):
|
||||
if "spawn_position" in enemy:
|
||||
enemy.spawn_position = spawn_pos
|
||||
|
||||
# CRITICAL: Set collision mask BEFORE adding to scene to ensure enemies collide with walls (layer 7 = bit 6 = 64)
|
||||
# This overrides any collision_mask set in the scene file
|
||||
enemy.collision_mask = 1 | 2 | 64 # Collide with players (layer 1), objects (layer 2), and walls (layer 7 = bit 6 = 64)
|
||||
|
||||
# Set multiplayer authority BEFORE adding to scene tree (CRITICAL!)
|
||||
if multiplayer.has_multiplayer_peer():
|
||||
enemy.set_multiplayer_authority(1)
|
||||
|
||||
# Add to YSort node for automatic Y-sorting
|
||||
var ysort = get_parent().get_node_or_null("Entities")
|
||||
var parent = ysort if ysort else get_parent()
|
||||
@@ -167,9 +248,9 @@ func spawn_enemy_at_position(spawn_pos: Vector2, scene_index: int = -1):
|
||||
|
||||
parent.add_child(enemy)
|
||||
|
||||
# Set multiplayer authority to server (peer 1)
|
||||
if multiplayer.has_multiplayer_peer():
|
||||
enemy.set_multiplayer_authority(1)
|
||||
# CRITICAL: Re-verify collision_mask AFTER adding to scene (in case _ready() or scene file overrides it)
|
||||
# Use call_deferred to ensure _ready() has completed first, then set the entire mask
|
||||
call_deferred("_verify_enemy_collision_mask", enemy)
|
||||
|
||||
print(" ✓ Client spawned enemy: ", enemy.name, " at ", spawn_pos)
|
||||
|
||||
@@ -184,18 +265,50 @@ func get_spawned_enemy_positions() -> Array:
|
||||
enemy_data.append({"position": enemy.global_position, "scene_index": scene_index})
|
||||
return enemy_data
|
||||
|
||||
func _verify_enemy_collision_mask(enemy: Node):
|
||||
# Verify and correct enemy collision_mask after _ready() has completed
|
||||
# This ensures enemies always collide with walls (layer 7 = bit 6 = 64), not layer 3 or 4
|
||||
if not is_instance_valid(enemy):
|
||||
return
|
||||
|
||||
var expected_mask = 1 | 2 | 64 # Collide with players (layer 1), objects (layer 2), and walls (layer 7 = bit 6 = 64)
|
||||
if enemy.collision_mask != expected_mask:
|
||||
print("EnemySpawner: WARNING - Enemy ", enemy.name, " collision_mask was ", enemy.collision_mask, ", expected ", expected_mask, "! Correcting...")
|
||||
enemy.collision_mask = expected_mask
|
||||
|
||||
# Double-check by setting individual layers to be absolutely sure
|
||||
enemy.set_collision_mask_value(1, true) # Players
|
||||
enemy.set_collision_mask_value(2, true) # Objects
|
||||
enemy.set_collision_mask_value(7, true) # Walls (layer 7)
|
||||
enemy.set_collision_mask_value(3, false) # Ensure layer 3 is NOT set
|
||||
enemy.set_collision_mask_value(4, false) # Ensure layer 4 is NOT set
|
||||
print("EnemySpawner: Corrected enemy ", enemy.name, " collision_mask to ", enemy.collision_mask)
|
||||
|
||||
func _spawn_smoke_puff():
|
||||
print(" _spawn_smoke_puff() called")
|
||||
# Legacy function - use _spawn_smoke_puff_at_position instead
|
||||
_spawn_smoke_puff_at_position(global_position)
|
||||
|
||||
func _spawn_smoke_puff_at_position(puff_position: Vector2) -> Node:
|
||||
print(" _spawn_smoke_puff_at_position() called at ", puff_position)
|
||||
print(" smoke_puff_scene: ", smoke_puff_scene)
|
||||
|
||||
if smoke_puff_scene:
|
||||
print(" Instantiating smoke puff...")
|
||||
var puff = smoke_puff_scene.instantiate()
|
||||
if puff:
|
||||
puff.global_position = global_position
|
||||
get_parent().add_child(puff)
|
||||
print(" ✓ Smoke puff spawned at ", global_position)
|
||||
puff.global_position = puff_position
|
||||
var parent = get_parent()
|
||||
if parent:
|
||||
parent.add_child(puff)
|
||||
print(" ✓ Smoke puff spawned at ", puff_position)
|
||||
return puff
|
||||
else:
|
||||
print(" ERROR: No parent node for smoke puff!")
|
||||
puff.queue_free()
|
||||
return null
|
||||
else:
|
||||
print(" ERROR: Failed to instantiate smoke puff")
|
||||
return null
|
||||
else:
|
||||
print(" WARNING: No smoke puff scene loaded")
|
||||
return null
|
||||
|
||||
Reference in New Issue
Block a user