replace with multiplayer-coop files
This commit is contained in:
201
src/scripts/enemy_spawner.gd
Normal file
201
src/scripts/enemy_spawner.gd
Normal file
@@ -0,0 +1,201 @@
|
||||
extends Node2D
|
||||
|
||||
# Enemy Spawner - Spawns enemies at this position
|
||||
|
||||
@export var enemy_scenes: Array[PackedScene] = [] # List of enemy scenes to randomly choose from
|
||||
@export var spawn_on_ready: bool = true
|
||||
@export var respawn_time: float = 10.0 # Time to respawn after enemy dies
|
||||
@export var max_enemies: int = 1 # Maximum number of enemies this spawner can have alive
|
||||
|
||||
var spawned_enemies: Array = []
|
||||
var respawn_timer: float = 0.0
|
||||
var smoke_puff_scene = preload("res://scenes/smoke_puff.tscn")
|
||||
|
||||
func _ready():
|
||||
print("========== EnemySpawner READY ==========")
|
||||
print(" Position: ", global_position)
|
||||
print(" Is server: ", multiplayer.is_server())
|
||||
print(" Has multiplayer peer: ", multiplayer.has_multiplayer_peer())
|
||||
print(" spawn_on_ready: ", spawn_on_ready)
|
||||
print(" max_enemies: ", max_enemies)
|
||||
print(" Parent: ", get_parent())
|
||||
|
||||
# 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)
|
||||
|
||||
if should_spawn:
|
||||
print(" Calling spawn_enemy()...")
|
||||
call_deferred("spawn_enemy") # Use call_deferred to ensure scene is ready
|
||||
else:
|
||||
print(" NOT spawning - conditions not met")
|
||||
print("========================================")
|
||||
|
||||
func _process(delta):
|
||||
# Only server spawns, or single player
|
||||
if multiplayer.has_multiplayer_peer() and not multiplayer.is_server():
|
||||
return
|
||||
|
||||
# Clean up dead enemies from list
|
||||
spawned_enemies = spawned_enemies.filter(func(e): return is_instance_valid(e) and not e.is_dead)
|
||||
|
||||
# Check if we need to respawn
|
||||
if spawned_enemies.size() < max_enemies:
|
||||
respawn_timer += delta
|
||||
if respawn_timer >= respawn_time:
|
||||
spawn_enemy()
|
||||
respawn_timer = 0.0
|
||||
|
||||
func spawn_enemy():
|
||||
print(">>> spawn_enemy() CALLED <<<")
|
||||
|
||||
# Choose enemy scene to spawn
|
||||
var scene_to_spawn: PackedScene = null
|
||||
if enemy_scenes.size() > 0:
|
||||
# Use random scene from list
|
||||
scene_to_spawn = enemy_scenes[randi() % enemy_scenes.size()]
|
||||
print(" Selected enemy scene from list: ", scene_to_spawn)
|
||||
|
||||
if not scene_to_spawn:
|
||||
push_error("ERROR: No enemy scene set for spawner! Add scenes to enemy_scenes array.")
|
||||
return
|
||||
|
||||
print(" Spawning enemy at ", global_position)
|
||||
|
||||
# Spawn smoke puff effect
|
||||
_spawn_smoke_puff()
|
||||
|
||||
print(" Instantiating enemy scene...")
|
||||
var enemy = scene_to_spawn.instantiate()
|
||||
|
||||
if not enemy:
|
||||
push_error("ERROR: Failed to instantiate enemy!")
|
||||
return
|
||||
|
||||
print(" Enemy instantiated: ", enemy)
|
||||
enemy.global_position = global_position
|
||||
# Set spawn position for deterministic appearance seed (before adding to scene)
|
||||
if "spawn_position" in enemy:
|
||||
enemy.spawn_position = global_position
|
||||
print(" Set enemy position to: ", global_position)
|
||||
|
||||
# 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()
|
||||
print(" Parent node: ", parent)
|
||||
|
||||
if not parent:
|
||||
push_error("ERROR: No parent node!")
|
||||
return
|
||||
|
||||
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)
|
||||
|
||||
# Determine which scene index was used (for syncing to clients)
|
||||
var scene_index = -1
|
||||
if enemy_scenes.size() > 0:
|
||||
# Find which scene was used
|
||||
for i in range(enemy_scenes.size()):
|
||||
if enemy_scenes[i] == scene_to_spawn:
|
||||
scene_index = i
|
||||
break
|
||||
|
||||
# Store scene_index as metadata on the enemy (for syncing existing enemies to new clients)
|
||||
enemy.set_meta("spawn_scene_index", scene_index)
|
||||
|
||||
spawned_enemies.append(enemy)
|
||||
|
||||
print(" ✓ Successfully spawned enemy: ", enemy.name, " at ", global_position, " scene_index: ", scene_index)
|
||||
print(" Total spawned enemies: ", spawned_enemies.size())
|
||||
|
||||
# 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)
|
||||
if game_world and game_world.has_method("_sync_enemy_spawn"):
|
||||
# Use spawner name (relative to GameWorld) since it's a direct child
|
||||
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")
|
||||
|
||||
func spawn_enemy_at_position(spawn_pos: Vector2, scene_index: int = -1):
|
||||
# This method is called by GameWorld RPC to spawn enemies on clients
|
||||
# scene_index tells us which scene from enemy_scenes array was used on the server
|
||||
var scene_to_spawn: PackedScene = null
|
||||
if scene_index >= 0 and scene_index < enemy_scenes.size():
|
||||
# Use the scene index that was synced from server
|
||||
scene_to_spawn = enemy_scenes[scene_index]
|
||||
print("Client: Using enemy scene at index ", scene_index, ": ", scene_to_spawn)
|
||||
elif enemy_scenes.size() > 0:
|
||||
# Fallback: use first scene if index is invalid
|
||||
scene_to_spawn = enemy_scenes[0]
|
||||
print("Client: Invalid scene_index, using first enemy scene: ", scene_to_spawn)
|
||||
|
||||
if not scene_to_spawn:
|
||||
push_error("ERROR: Spawner has no enemy scenes set! Add scenes to enemy_scenes array.")
|
||||
return
|
||||
|
||||
print("Client: spawn_enemy_at_position called at ", spawn_pos)
|
||||
|
||||
# Spawn smoke puff effect
|
||||
_spawn_smoke_puff()
|
||||
|
||||
# Instantiate and add enemy
|
||||
var enemy = scene_to_spawn.instantiate()
|
||||
if not enemy:
|
||||
push_error("ERROR: Failed to instantiate enemy on client!")
|
||||
return
|
||||
|
||||
enemy.global_position = spawn_pos
|
||||
# Set spawn position for deterministic appearance seed (before adding to scene)
|
||||
if "spawn_position" in enemy:
|
||||
enemy.spawn_position = spawn_pos
|
||||
|
||||
# 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()
|
||||
if not parent:
|
||||
push_error("ERROR: No parent node on client!")
|
||||
return
|
||||
|
||||
parent.add_child(enemy)
|
||||
|
||||
# Set multiplayer authority to server (peer 1)
|
||||
if multiplayer.has_multiplayer_peer():
|
||||
enemy.set_multiplayer_authority(1)
|
||||
|
||||
print(" ✓ Client spawned enemy: ", enemy.name, " at ", spawn_pos)
|
||||
|
||||
func get_spawned_enemy_positions() -> Array:
|
||||
# Return array of dictionaries with position and scene_index for all currently spawned enemies
|
||||
var enemy_data = []
|
||||
for enemy in spawned_enemies:
|
||||
if is_instance_valid(enemy) and not enemy.is_dead:
|
||||
var scene_index = -1
|
||||
if enemy.has_meta("spawn_scene_index"):
|
||||
scene_index = enemy.get_meta("spawn_scene_index")
|
||||
enemy_data.append({"position": enemy.global_position, "scene_index": scene_index})
|
||||
return enemy_data
|
||||
|
||||
func _spawn_smoke_puff():
|
||||
print(" _spawn_smoke_puff() called")
|
||||
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)
|
||||
else:
|
||||
print(" ERROR: Failed to instantiate smoke puff")
|
||||
else:
|
||||
print(" WARNING: No smoke puff scene loaded")
|
||||
Reference in New Issue
Block a user