MultiplayerFighter
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

162 lines
4.9 KiB

extends RigidBody3D
class_name WorldWeapon
## Weapon as a physics object in the world
## Can be picked up by players
## Spawned when a weapon is dropped or placed in the level
@export var weapon_data: WeaponData
var weapon_id: int = -1 # Set by Level when spawned
var _mesh_instance: Node3D = null
var _collision_shape: CollisionShape3D = null
var _pickup_area: Area3D = null
var _is_being_picked_up: bool = false
func _ready():
print("[WorldWeapon ", name, "] _ready() called. weapon_id=", weapon_id, ", weapon_data=", weapon_data)
# Set collision layer to "weapon" (layer 3 = bit 4)
collision_layer = 4
collision_mask = 2 # Collide with world
# Set up physics
if weapon_data:
mass = weapon_data.weight
print("[WorldWeapon ", name, "] Set mass to ", mass)
# Spawn mesh
if weapon_data and weapon_data.mesh_scene:
print("[WorldWeapon ", name, "] Spawning mesh from ", weapon_data.mesh_scene)
_spawn_mesh()
else:
print("[WorldWeapon ", name, "] ERROR: Cannot spawn mesh. weapon_data=", weapon_data, ", mesh_scene=", weapon_data.mesh_scene if weapon_data else "null")
# Create collision shape if not exists
_setup_collision()
# Create pickup area
_setup_pickup_area()
# Only server manages pickup logic
if multiplayer.is_server():
_pickup_area.body_entered.connect(_on_body_entered_pickup_area)
func _spawn_mesh():
# Remove old mesh if exists
if _mesh_instance:
_mesh_instance.queue_free()
# Instantiate mesh
_mesh_instance = weapon_data.mesh_scene.instantiate()
add_child(_mesh_instance)
print("[WorldWeapon ", name, "] Mesh spawned successfully: ", _mesh_instance)
func _setup_collision():
# Check if we already have a collision shape
for child in get_children():
if child is CollisionShape3D:
_collision_shape = child
return
# Create a basic box collision if none exists
_collision_shape = CollisionShape3D.new()
var box_shape = BoxShape3D.new()
box_shape.size = Vector3(0.5, 0.5, 1.5) # Approximate weapon size
_collision_shape.shape = box_shape
add_child(_collision_shape)
func _setup_pickup_area():
# Create Area3D for pickup detection
_pickup_area = Area3D.new()
_pickup_area.name = "PickupArea"
_pickup_area.collision_layer = 4 # Layer 3 (weapon layer) so player can detect it
_pickup_area.collision_mask = 1 # Detect players
add_child(_pickup_area)
# Create sphere collision for pickup range
var pickup_collision = CollisionShape3D.new()
var sphere = SphereShape3D.new()
sphere.radius = weapon_data.pickup_radius if weapon_data else 1.5
pickup_collision.shape = sphere
_pickup_area.add_child(pickup_collision)
func _on_body_entered_pickup_area(body: Node3D):
if _is_being_picked_up:
return
# Check if it's a player trying to pick up
if body is Character and body.is_multiplayer_authority():
# Let the player handle the pickup
# The player will call try_pickup() via RPC
pass
## Called by player to attempt pickup
@rpc("any_peer", "reliable")
func try_pickup(player_id: int):
# Only server validates pickup
if not multiplayer.is_server():
return
if _is_being_picked_up:
return
# Find the player
var level = get_tree().get_current_scene()
if not level or not level.has_node("PlayersContainer"):
return
var players_container = level.get_node("PlayersContainer")
if not players_container.has_node(str(player_id)):
return
var player = players_container.get_node(str(player_id))
if not player is Character:
return
# Check distance
var distance = global_position.distance_to(player.global_position)
if distance > (weapon_data.pickup_radius if weapon_data else 1.5):
return
_is_being_picked_up = true
# Get the resource path
var resource_path = weapon_data.resource_path
if resource_path == "":
push_error("WeaponData has no resource path!")
return
# Check weapon_id is valid
if weapon_id == -1:
push_error("WorldWeapon.try_pickup: weapon_id is -1! This weapon was not spawned correctly.")
print(" Weapon name: ", name)
print(" Weapon data: ", weapon_data.resource_path if weapon_data else "null")
return
# Tell the player to equip this weapon (on all clients)
print("[Server] Telling player ", player_id, " to equip weapon via RPC")
player.rpc("equip_weapon_from_world", resource_path)
# Remove this world weapon from all clients using level's centralized system
print("[Server] Removing world weapon ", name, " (ID: ", weapon_id, ") via level.remove_world_weapon")
if level.has_method("remove_world_weapon"):
level.remove_world_weapon(weapon_id)
else:
push_error("Level doesn't have remove_world_weapon method!")
## Remove weapon from all clients
@rpc("any_peer", "call_local", "reliable")
func _remove_from_all_clients():
print("[Client ", multiplayer.get_unique_id(), "] Removing weapon ", name)
# Delay removal to ensure RPC is fully processed
await get_tree().process_frame
queue_free()
## Set weapon data and refresh
func set_weapon_data(data: WeaponData):
weapon_data = data
if is_inside_tree():
_spawn_mesh()
if weapon_data:
mass = weapon_data.weight