This commit is contained in:
Twirpytherobot
2026-02-17 22:51:38 +00:00
parent 7629342540
commit 7fa2efabaf
7 changed files with 193 additions and 15 deletions
+119 -7
View File
@@ -17,6 +17,12 @@ class_name ArmedEnemy
@export var unarmed_startup: float = 0.15
@export var unarmed_active: float = 0.2
## Weapon seeking (when unarmed)
@export_category("Weapon Seeking")
@export var seek_weapons_when_unarmed: bool = true ## If true, unarmed enemies will seek nearby weapons
@export var weapon_seek_range: float = 30.0 ## How far to look for weapons
@export var weapon_pickup_range: float = 1.5 ## How close to get before picking up
## Weapon system
@export_category("Weapons")
@export var starting_weapon: WeaponData = null ## Weapon to equip on spawn
@@ -197,13 +203,19 @@ func _physics_process(delta):
if not is_on_floor():
velocity.y -= ProjectSettings.get_setting("physics/3d/default_gravity") * delta
# AI behavior
if current_target and is_instance_valid(current_target):
_ai_combat(delta)
else:
# Stop moving if no target
velocity.x = 0
velocity.z = 0
# AI behavior - prioritize weapon seeking when unarmed
var seeking_weapon = false
if seek_weapons_when_unarmed and equipped_weapon == null:
seeking_weapon = _ai_seek_weapon(delta)
# If not seeking a weapon (or armed), do normal combat
if not seeking_weapon:
if current_target and is_instance_valid(current_target):
_ai_combat(delta)
else:
# Stop moving if no target
velocity.x = 0
velocity.z = 0
move_and_slide()
@@ -265,6 +277,28 @@ func get_nearest_player() -> Node:
return nearest_player
## Find the nearest WorldWeapon within range
func get_nearest_world_weapon() -> WorldWeapon:
var level = get_tree().get_current_scene()
if not level:
return null
var weapons_container = level.get_node_or_null("WeaponsContainer")
if not weapons_container:
return null
var nearest_weapon: WorldWeapon = null
var nearest_distance = weapon_seek_range
for child in weapons_container.get_children():
if child is WorldWeapon:
var distance = global_position.distance_to(child.global_position)
if distance < nearest_distance:
nearest_distance = distance
nearest_weapon = child
return nearest_weapon
## Update target
func _update_target():
if not is_aggressive:
@@ -311,6 +345,62 @@ func _ai_combat(delta):
velocity.x = direction.x * move_speed
velocity.z = direction.z * move_speed
## Weapon seeking AI - returns true if actively seeking a weapon
func _ai_seek_weapon(delta) -> bool:
var nearest_weapon = get_nearest_world_weapon()
if not nearest_weapon or not is_instance_valid(nearest_weapon):
return false
var weapon_pos = nearest_weapon.global_position
var direction = (weapon_pos - global_position).normalized()
var distance = global_position.distance_to(weapon_pos)
# If close enough, pick up the weapon
if distance <= weapon_pickup_range:
velocity.x = 0
velocity.z = 0
_pickup_world_weapon(nearest_weapon)
return true
# Move towards the weapon
velocity.x = direction.x * move_speed
velocity.z = direction.z * move_speed
return true
## Pick up a world weapon (server only)
func _pickup_world_weapon(world_weapon: WorldWeapon):
if not multiplayer.is_server():
return
if not world_weapon or not is_instance_valid(world_weapon):
return
var weapon_data = world_weapon.weapon_data
if not weapon_data:
return
var resource_path = weapon_data.resource_path
if resource_path == "":
push_error("[ArmedEnemy] WorldWeapon has no resource path!")
return
var weapon_id = world_weapon.weapon_id
if weapon_id == -1:
push_error("[ArmedEnemy] WorldWeapon has invalid weapon_id!")
return
print("[ArmedEnemy ", name, "] Picking up weapon: ", weapon_data.weapon_name)
# Equip the weapon on all clients
rpc("_equip_weapon_sync", resource_path, false)
# Remove the world weapon from all clients using level's system
var level = get_tree().get_current_scene()
if level and level.has_method("remove_world_weapon"):
level.remove_world_weapon(weapon_id)
else:
push_error("[ArmedEnemy] Level doesn't have remove_world_weapon method!")
## Perform attack
func _perform_attack():
if _is_attacking or not multiplayer.is_server():
@@ -526,6 +616,17 @@ func _on_enemy_died(killer_id: int):
if _body:
_body.visible = false
# Disable collision so players can walk through
var collision_shape = get_node_or_null("CollisionShape3D")
if collision_shape:
collision_shape.disabled = true
# Disable hurtbox
var hurtbox = get_node_or_null("HurtBox")
if hurtbox:
hurtbox.monitoring = false
hurtbox.monitorable = false
# Deactivate hitboxes
if _unarmed_hitbox:
_unarmed_hitbox.deactivate()
@@ -601,6 +702,17 @@ func _on_enemy_respawned():
_body.visible = true
_reset_material()
# Re-enable collision
var collision_shape = get_node_or_null("CollisionShape3D")
if collision_shape:
collision_shape.disabled = false
# Re-enable hurtbox
var hurtbox = get_node_or_null("HurtBox")
if hurtbox:
hurtbox.monitoring = false # Hurtbox doesn't monitor, it's monitored
hurtbox.monitorable = true
# Reset state
_attack_timer = 0.0
_is_attacking = false