Base Weapons
Place in world, pick up and drop working on multiplayer
This commit is contained in:
+192
-1
@@ -13,6 +13,7 @@ enum SkinColor { BLUE, YELLOW, GREEN, RED }
|
||||
@export_category("Objects")
|
||||
@export var _body: Node3D = null
|
||||
@export var _spring_arm_offset: Node3D = null
|
||||
@export var _weapon_attachment: BoneAttachment3D = null # WeaponPoint bone attachment
|
||||
|
||||
@export_category("Skin Colors")
|
||||
@export var blue_texture : CompressedTexture2D
|
||||
@@ -28,6 +29,10 @@ enum SkinColor { BLUE, YELLOW, GREEN, RED }
|
||||
var _current_speed: float
|
||||
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
|
||||
|
||||
# Weapon system
|
||||
var equipped_weapon: BaseWeapon = null
|
||||
var _nearby_weapons: Array[WorldWeapon] = []
|
||||
|
||||
# Attack system
|
||||
@export var attack_damage: float = 10.0
|
||||
@export var attack_range: float = 3.0
|
||||
@@ -67,6 +72,18 @@ func _ready():
|
||||
if is_multiplayer_authority():
|
||||
_create_health_ui()
|
||||
|
||||
# Setup weapon pickup detection area
|
||||
_setup_weapon_pickup_area()
|
||||
|
||||
# Auto-find weapon attachment if not set
|
||||
if _weapon_attachment == null:
|
||||
var bone_attach = get_node_or_null("3DGodotRobot/RobotArmature/Skeleton3D/BoneAttachment3D")
|
||||
if bone_attach:
|
||||
_weapon_attachment = bone_attach
|
||||
print("Auto-found weapon attachment point")
|
||||
else:
|
||||
push_warning("Could not find BoneAttachment3D for weapons!")
|
||||
|
||||
func _physics_process(delta):
|
||||
# Check if multiplayer is ready
|
||||
if multiplayer.multiplayer_peer == null:
|
||||
@@ -122,9 +139,23 @@ func _process(delta):
|
||||
_perform_dash()
|
||||
|
||||
# Handle attack input
|
||||
if Input.is_action_just_pressed("attack") and _attack_timer <= 0 and not is_dead and not _is_dashing:
|
||||
if Input.is_action_just_pressed("attack") and not is_dead and not _is_dashing:
|
||||
_perform_attack()
|
||||
|
||||
# Handle weapon pickup/drop
|
||||
if Input.is_action_just_pressed("pickup") and not is_dead:
|
||||
print("Pickup pressed! Equipped: ", equipped_weapon != null, " Nearby: ", _nearby_weapons.size())
|
||||
if equipped_weapon:
|
||||
# Tell server to drop (server will handle spawning)
|
||||
if multiplayer.is_server():
|
||||
drop_weapon()
|
||||
else:
|
||||
rpc_id(1, "drop_weapon") # Only send to server
|
||||
elif _nearby_weapons.size() > 0:
|
||||
_pickup_nearest_weapon()
|
||||
else:
|
||||
print("No weapons nearby to pick up")
|
||||
|
||||
func freeze():
|
||||
velocity.x = 0
|
||||
velocity.z = 0
|
||||
@@ -208,10 +239,19 @@ func _perform_attack():
|
||||
if not is_multiplayer_authority() or is_dead:
|
||||
return
|
||||
|
||||
# Use equipped weapon if available
|
||||
if equipped_weapon and equipped_weapon.can_attack():
|
||||
equipped_weapon.perform_attack()
|
||||
return
|
||||
|
||||
# Fallback to default unarmed attack
|
||||
# Don't attack if already attacking
|
||||
if _body and _body.animation_player and _body.animation_player.current_animation == "Attack1":
|
||||
return
|
||||
|
||||
if _attack_timer > 0:
|
||||
return
|
||||
|
||||
_attack_timer = attack_cooldown
|
||||
|
||||
# Play attack animation once
|
||||
@@ -399,3 +439,154 @@ func _perform_dash():
|
||||
func _reset_dash_rotation(rotation_value: float):
|
||||
if _body:
|
||||
_body.rotation.x = rotation_value
|
||||
|
||||
## Weapon System
|
||||
func _setup_weapon_pickup_area():
|
||||
# Create an Area3D to detect nearby weapons
|
||||
var pickup_area = Area3D.new()
|
||||
pickup_area.name = "WeaponPickupArea"
|
||||
pickup_area.collision_layer = 0
|
||||
pickup_area.collision_mask = 4 # Will detect WorldWeapons (we'll use layer 3)
|
||||
add_child(pickup_area)
|
||||
|
||||
# Create collision shape for pickup range
|
||||
var pickup_collision = CollisionShape3D.new()
|
||||
var sphere = SphereShape3D.new()
|
||||
sphere.radius = 2.0 # Pickup range
|
||||
pickup_collision.shape = sphere
|
||||
pickup_area.add_child(pickup_collision)
|
||||
|
||||
# Connect signals to track nearby weapons
|
||||
pickup_area.area_entered.connect(_on_weapon_area_entered)
|
||||
pickup_area.area_exited.connect(_on_weapon_area_exited)
|
||||
|
||||
func _on_weapon_area_entered(area: Area3D):
|
||||
# Check if the area belongs to a WorldWeapon
|
||||
var weapon = area.get_parent()
|
||||
if weapon is WorldWeapon and weapon not in _nearby_weapons:
|
||||
_nearby_weapons.append(weapon)
|
||||
if is_multiplayer_authority():
|
||||
print("Weapon nearby: ", weapon.weapon_data.weapon_name if weapon.weapon_data else "Unknown")
|
||||
|
||||
func _on_weapon_area_exited(area: Area3D):
|
||||
# Remove weapon from nearby list
|
||||
var weapon = area.get_parent()
|
||||
if weapon is WorldWeapon and weapon in _nearby_weapons:
|
||||
_nearby_weapons.erase(weapon)
|
||||
if is_multiplayer_authority():
|
||||
print("Weapon left range: ", weapon.weapon_data.weapon_name if weapon.weapon_data else "Unknown")
|
||||
|
||||
## Equip a weapon from WorldWeapon data (receives resource path)
|
||||
@rpc("any_peer", "call_local", "reliable")
|
||||
func equip_weapon_from_world(weapon_data_path: String):
|
||||
var data = load(weapon_data_path) as WeaponData
|
||||
if data:
|
||||
equip_weapon(data)
|
||||
else:
|
||||
push_error("Failed to load weapon data from: " + weapon_data_path)
|
||||
|
||||
## Equip a weapon with given data
|
||||
func equip_weapon(data: WeaponData):
|
||||
# Unequip current weapon first
|
||||
if equipped_weapon:
|
||||
unequip_weapon()
|
||||
|
||||
if not _weapon_attachment:
|
||||
push_error("No weapon attachment point found!")
|
||||
return
|
||||
|
||||
# Create new weapon instance
|
||||
var weapon = BaseWeapon.new()
|
||||
weapon.weapon_data = data
|
||||
weapon.name = "EquippedWeapon"
|
||||
weapon.set_owner_character(self)
|
||||
|
||||
# Attach to bone
|
||||
_weapon_attachment.add_child(weapon)
|
||||
equipped_weapon = weapon
|
||||
|
||||
if is_multiplayer_authority():
|
||||
print("Equipped: ", data.weapon_name)
|
||||
|
||||
## Unequip current weapon (local only)
|
||||
func unequip_weapon():
|
||||
if equipped_weapon:
|
||||
equipped_weapon.queue_free()
|
||||
equipped_weapon = null
|
||||
|
||||
## Sync unequip across all clients
|
||||
@rpc("any_peer", "call_local", "reliable")
|
||||
func _unequip_weapon_sync():
|
||||
unequip_weapon()
|
||||
|
||||
## Drop currently equipped weapon
|
||||
@rpc("any_peer", "reliable")
|
||||
func drop_weapon():
|
||||
print("drop_weapon called on peer ", multiplayer.get_unique_id(), " is_server: ", multiplayer.is_server(), " has weapon: ", equipped_weapon != null)
|
||||
|
||||
if not equipped_weapon:
|
||||
print("No weapon equipped, cannot drop")
|
||||
return
|
||||
|
||||
# Only server spawns the world weapon
|
||||
if multiplayer.is_server():
|
||||
print("Server spawning dropped weapon")
|
||||
_spawn_world_weapon(equipped_weapon.weapon_data)
|
||||
# Tell all clients to unequip
|
||||
rpc("_unequip_weapon_sync")
|
||||
|
||||
# Unequip locally
|
||||
unequip_weapon()
|
||||
|
||||
if is_multiplayer_authority():
|
||||
print("Dropped weapon")
|
||||
|
||||
## Spawn a weapon in the world (server only)
|
||||
func _spawn_world_weapon(data: WeaponData):
|
||||
if not multiplayer.is_server():
|
||||
return
|
||||
|
||||
# Get the resource path
|
||||
var resource_path = data.resource_path
|
||||
if resource_path == "":
|
||||
push_error("WeaponData has no resource path! Make sure to save it as a .tres file")
|
||||
return
|
||||
|
||||
# Position in front of player
|
||||
var spawn_pos = global_position + (-transform.basis.z * 2.0)
|
||||
spawn_pos.y += 1.0 # Spawn at chest height
|
||||
|
||||
# Calculate forward velocity
|
||||
var velocity = -transform.basis.z * 3.0
|
||||
|
||||
# Tell level to spawn weapon on all clients
|
||||
var level = get_tree().get_current_scene()
|
||||
if level and level.has_method("spawn_world_weapon"):
|
||||
# Increment the level's weapon counter
|
||||
level._weapon_spawn_counter += 1
|
||||
level.rpc("spawn_world_weapon", resource_path, spawn_pos, velocity, level._weapon_spawn_counter)
|
||||
|
||||
## Pick up nearest weapon
|
||||
func _pickup_nearest_weapon():
|
||||
if _nearby_weapons.size() == 0:
|
||||
return
|
||||
|
||||
# Find closest weapon
|
||||
var closest_weapon: WorldWeapon = null
|
||||
var closest_distance: float = INF
|
||||
|
||||
for weapon in _nearby_weapons:
|
||||
if not is_instance_valid(weapon):
|
||||
continue
|
||||
|
||||
var distance = global_position.distance_to(weapon.global_position)
|
||||
if distance < closest_distance:
|
||||
closest_distance = distance
|
||||
closest_weapon = weapon
|
||||
|
||||
if closest_weapon:
|
||||
# Request server to pickup (server validates)
|
||||
if multiplayer.is_server():
|
||||
closest_weapon.try_pickup(multiplayer.get_unique_id())
|
||||
else:
|
||||
closest_weapon.rpc_id(1, "try_pickup", multiplayer.get_unique_id())
|
||||
|
||||
Reference in New Issue
Block a user