Weapon and shield slots

This commit is contained in:
Twirpytherobot
2025-11-15 16:44:58 +00:00
parent 86db6865b7
commit 6da70f9bfe
7 changed files with 199 additions and 82 deletions
+114 -33
View File
@@ -13,7 +13,10 @@ 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 var _weapon_attachment: BoneAttachment3D = null # WeaponPoint bone attachment (main hand)
@export var _weapon_container: Node3D = null # Container node for main hand weapons
@export var _offhand_attachment: BoneAttachment3D = null # OffHandPoint bone attachment
@export var _offhand_container: Node3D = null # Container node for off-hand weapons
@export_category("Skin Colors")
@export var blue_texture : CompressedTexture2D
@@ -30,7 +33,8 @@ var _current_speed: float
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")
# Weapon system
var equipped_weapon: BaseWeapon = null
var equipped_weapon: BaseWeapon = null # Main hand weapon
var equipped_offhand: BaseWeapon = null # Off-hand weapon
var _nearby_weapons: Array[WorldWeapon] = []
# Attack system
@@ -84,6 +88,29 @@ func _ready():
else:
push_warning("Could not find BoneAttachment3D for weapons!")
# Auto-find weapon container if not set
if _weapon_container == null and _weapon_attachment:
var container = _weapon_attachment.get_node_or_null("WeaponContainer")
if container:
_weapon_container = container
print("Auto-found main hand weapon container")
else:
push_warning("Could not find WeaponContainer! Weapons will attach directly to BoneAttachment3D.")
# Auto-find off-hand attachment if not set
if _offhand_attachment == null:
var offhand_attach = get_node_or_null("3DGodotRobot/RobotArmature/Skeleton3D/OffHandPoint")
if offhand_attach:
_offhand_attachment = offhand_attach
print("Auto-found off-hand attachment point")
# Auto-find off-hand container if not set
if _offhand_container == null and _offhand_attachment:
var container = _offhand_attachment.get_node_or_null("OffHandContainer")
if container:
_offhand_container = container
print("Auto-found off-hand weapon container")
func _physics_process(delta):
# Check if multiplayer is ready
if multiplayer.multiplayer_peer == null:
@@ -144,15 +171,24 @@ func _process(delta):
# 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:
print("Pickup pressed! Main: ", equipped_weapon != null, " OffHand: ", equipped_offhand != null, " Nearby: ", _nearby_weapons.size())
# Try to pickup nearby weapon
if _nearby_weapons.size() > 0:
_pickup_nearest_weapon()
# If no nearby weapons, drop what we're holding (main hand first, then off-hand)
elif equipped_weapon:
# Tell server to drop (server will handle spawning)
if multiplayer.is_server():
drop_weapon()
drop_weapon(false) # main hand
else:
rpc_id(1, "drop_weapon") # Only send to server
elif _nearby_weapons.size() > 0:
_pickup_nearest_weapon()
rpc_id(1, "drop_weapon", false) # Only send to server
elif equipped_offhand:
# Drop off-hand
if multiplayer.is_server():
drop_weapon(true) # off-hand
else:
rpc_id(1, "drop_weapon", true)
else:
print("No weapons nearby to pick up")
@@ -239,11 +275,16 @@ func _perform_attack():
if not is_multiplayer_authority() or is_dead:
return
# Use equipped weapon if available
# Use main hand weapon if available
if equipped_weapon and equipped_weapon.can_attack():
equipped_weapon.perform_attack()
return
# Or use off-hand weapon if available
if equipped_offhand and equipped_offhand.can_attack():
equipped_offhand.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":
@@ -487,59 +528,84 @@ func equip_weapon_from_world(weapon_data_path: String):
## Equip a weapon with given data
func equip_weapon(data: WeaponData):
# Unequip current weapon first
if equipped_weapon:
unequip_weapon()
# Determine which hand based on weapon type
var is_offhand = (data.hand_type == WeaponData.Hand.OFF_HAND)
if not _weapon_attachment:
push_error("No weapon attachment point found!")
# Unequip current weapon in that hand first
if is_offhand:
if equipped_offhand:
unequip_weapon(true)
else:
if equipped_weapon:
unequip_weapon(false)
# Determine where to attach the weapon
var attach_point: Node3D
if is_offhand:
attach_point = _offhand_container if _offhand_container else _offhand_attachment
else:
attach_point = _weapon_container if _weapon_container else _weapon_attachment
if not attach_point:
push_error("No weapon attachment point found for ", "off-hand" if is_offhand else "main hand")
return
# Create new weapon instance
var weapon = BaseWeapon.new()
weapon.weapon_data = data
weapon.name = "EquippedWeapon"
weapon.name = "EquippedOffHand" if is_offhand else "EquippedWeapon"
weapon.set_owner_character(self)
# Attach to bone
_weapon_attachment.add_child(weapon)
equipped_weapon = weapon
# Attach to weapon container (or bone attachment if no container)
attach_point.add_child(weapon)
# Store reference
if is_offhand:
equipped_offhand = weapon
else:
equipped_weapon = weapon
if is_multiplayer_authority():
print("Equipped: ", data.weapon_name)
print("Equipped: ", data.weapon_name, " to ", attach_point.name)
## Unequip current weapon (local only)
func unequip_weapon():
if equipped_weapon:
equipped_weapon.queue_free()
equipped_weapon = null
func unequip_weapon(is_offhand: bool = false):
if is_offhand:
if equipped_offhand:
equipped_offhand.queue_free()
equipped_offhand = null
else:
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()
func _unequip_weapon_sync(is_offhand: bool = false):
unequip_weapon(is_offhand)
## 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)
func drop_weapon(is_offhand: bool = false):
var has_weapon = equipped_offhand if is_offhand else equipped_weapon
print("drop_weapon called on peer ", multiplayer.get_unique_id(), " is_server: ", multiplayer.is_server(), " has weapon: ", has_weapon != null, " offhand: ", is_offhand)
if not equipped_weapon:
print("No weapon equipped, cannot drop")
if not has_weapon:
print("No weapon equipped in ", "off-hand" if is_offhand else "main hand", ", 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)
_spawn_world_weapon(has_weapon.weapon_data)
# Tell all clients to unequip
rpc("_unequip_weapon_sync")
rpc("_unequip_weapon_sync", is_offhand)
# Unequip locally
unequip_weapon()
unequip_weapon(is_offhand)
if is_multiplayer_authority():
print("Dropped weapon")
print("Dropped weapon from ", "off-hand" if is_offhand else "main hand")
## Spawn a weapon in the world (server only)
func _spawn_world_weapon(data: WeaponData):
@@ -585,6 +651,21 @@ func _pickup_nearest_weapon():
closest_weapon = weapon
if closest_weapon:
# Check what hand this weapon goes in
var weapon_hand_type = closest_weapon.weapon_data.hand_type if closest_weapon.weapon_data else WeaponData.Hand.MAIN_HAND
var is_offhand = (weapon_hand_type == WeaponData.Hand.OFF_HAND)
var slot_occupied = equipped_offhand if is_offhand else equipped_weapon
# If the slot is occupied, drop it first
if slot_occupied:
print("Slot occupied, dropping current weapon before picking up")
if multiplayer.is_server():
drop_weapon(is_offhand)
else:
rpc_id(1, "drop_weapon", is_offhand)
# Wait a tiny bit for the drop to complete
await get_tree().create_timer(0.1).timeout
# Request server to pickup (server validates)
if multiplayer.is_server():
closest_weapon.try_pickup(multiplayer.get_unique_id())