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.
140 lines
3.7 KiB
140 lines
3.7 KiB
|
3 weeks ago
|
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 _mesh_instance: Node3D = null
|
||
|
|
var _collision_shape: CollisionShape3D = null
|
||
|
|
var _pickup_area: Area3D = null
|
||
|
|
var _is_being_picked_up: bool = false
|
||
|
|
|
||
|
|
func _ready():
|
||
|
|
# 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
|
||
|
|
|
||
|
|
# Spawn mesh
|
||
|
|
if weapon_data and weapon_data.mesh_scene:
|
||
|
|
_spawn_mesh()
|
||
|
|
|
||
|
|
# 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)
|
||
|
|
|
||
|
|
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
|
||
|
|
|
||
|
|
# Tell the player to equip this weapon (on all clients)
|
||
|
|
player.rpc("equip_weapon_from_world", resource_path)
|
||
|
|
|
||
|
|
# Remove this world weapon from all clients
|
||
|
|
rpc("_remove_from_all_clients")
|
||
|
|
|
||
|
|
## Remove weapon from all clients
|
||
|
|
@rpc("any_peer", "call_local", "reliable")
|
||
|
|
func _remove_from_all_clients():
|
||
|
|
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
|