Twirpytherobot 1 month ago
parent 7629342540
commit 7fa2efabaf
  1. 1
      level/resources/weapon_lobsteraxe.tres
  2. 8
      level/scenes/level.tscn
  3. 126
      level/scripts/armed_enemy.gd
  4. 20
      level/scripts/basic_enemy.gd
  5. 4
      level/scripts/hit_box.gd
  6. 29
      level/scripts/level.gd
  7. 20
      level/scripts/practice_dummy.gd

@ -7,6 +7,7 @@
script = ExtResource("1")
weapon_name = "Lobster Axe"
description = "A heavy-hitting axe shaped like a lobster claw. Surprisingly quick for its size."
hand_type = 2
damage = 18.0
attack_cooldown = 0.7
attack_animation = "Attack_TwoHandSwing"

@ -3,9 +3,9 @@
[ext_resource type="Script" uid="uid://d0dgljwwl463n" path="res://level/scripts/level.gd" id="1_e1sh7"]
[ext_resource type="PackedScene" uid="uid://db06e8q8f8bdq" path="res://level/scenes/Player_Lilguy.tscn" id="1_uvcbi"]
[ext_resource type="PackedScene" uid="uid://dif4t1y3c07ax" path="res://level/scenes/enemies/practice_dummy.tscn" id="3_i7s07"]
[ext_resource type="PackedScene" path="res://level/scenes/enemies/armed_enemy.tscn" id="4_armed"]
[ext_resource type="FontFile" uid="uid://wipqjhfqeuwd" path="res://assets/fonts/Kurland.ttf" id="3_icc4p"]
[ext_resource type="PackedScene" uid="uid://blm8lav3xh2yw" path="res://level/scenes/enemy_spawner.tscn" id="3_spawner"]
[ext_resource type="PackedScene" path="res://level/scenes/enemies/armed_enemy.tscn" id="4_armed"]
[ext_resource type="PackedScene" uid="uid://chkrcwlprbn88" path="res://assets/Objects/Colosseum_10.fbx" id="4_u750a"]
[ext_resource type="PackedScene" uid="uid://hd6pq287rgye" path="res://level/scenes/weapons/world_weapon_testsword.tscn" id="5_cwx4m"]
[ext_resource type="PackedScene" uid="uid://8c4l6s6x67vh" path="res://level/scenes/weapons/world_weapon_applecorer.tscn" id="6_xerh7"]
@ -300,6 +300,12 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 3, 0.5, 5)
[node name="EnemySpawner" parent="." instance=ExtResource("3_spawner")]
spawn_radius = 100.0
[node name="EnemySpawnPoint1" type="Node3D" parent="EnemySpawner"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 25.138117)
[node name="EnemySpawnPoint2" type="Node3D" parent="EnemySpawner"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -23.835087)
[node name="PlayerSpawnPoints" type="Node3D" parent="."]
[node name="PlayerSpawn1" type="Node3D" parent="PlayerSpawnPoints"]

@ -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

@ -263,6 +263,16 @@ func _on_enemy_died(killer_id: int):
if _mesh:
_mesh.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.monitorable = false
# Deactivate hitbox
if _hitbox:
_hitbox.deactivate()
@ -278,6 +288,16 @@ func _on_enemy_respawned():
_mesh.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.monitorable = true
# Reset state
_attack_timer = 0.0
_is_attacking = false

@ -110,6 +110,10 @@ func _process_hit(hurtbox: HurtBox):
if hurtbox.owner_entity in _hits_this_attack:
return
# Enemies don't damage other enemies (only players)
if owner_entity is BaseEnemy and hurtbox.owner_entity is BaseEnemy:
return
# Register this hit
var target = hurtbox.owner_entity
if target:

@ -717,11 +717,26 @@ func _spawn_armed_enemies():
print("[Server] Spawning armed enemies")
# Spawn armed enemies at different positions with weapons
var enemy_configs = [
{"pos": Vector3(15, 0, 5), "weapon": "res://level/resources/weapon_sword.tres", "offhand": ""},
{"pos": Vector3(-15, 0, 5), "weapon": "res://level/resources/weapon_sword.tres", "offhand": "res://level/resources/weapon_shield.tres"},
]
# Find enemy spawn points from EnemySpawner
var enemy_spawner = get_node_or_null("EnemySpawner")
if not enemy_spawner:
push_warning("[Level] EnemySpawner not found - skipping armed enemy spawn")
return
# Get all spawn points (children of EnemySpawner that start with "EnemySpawnPoint")
var spawn_points: Array[Node3D] = []
for child in enemy_spawner.get_children():
if child is Node3D and child.name.begins_with("EnemySpawnPoint"):
spawn_points.append(child)
if spawn_points.is_empty():
push_warning("[Level] No enemy spawn points found in EnemySpawner")
return
print("[Server] Found ", spawn_points.size(), " enemy spawn points")
for config in enemy_configs:
spawn_armed_enemy(config["pos"], config["weapon"], config["offhand"])
# Spawn an unarmed enemy at each spawn point
for spawn_point in spawn_points:
var spawn_pos = spawn_point.global_position
print("[Server] Spawning armed enemy at ", spawn_pos)
spawn_armed_enemy(spawn_pos, "", "")

@ -97,6 +97,16 @@ func _on_enemy_died(killer_id: int):
if _mesh:
_mesh.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.monitorable = false
print("[PracticeDummy] Killed by player ", killer_id, ". Respawning in ", respawn_delay, " seconds...")
## Override respawn to show mesh again
@ -108,5 +118,15 @@ func _on_enemy_respawned():
_mesh.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.monitorable = true
_update_health_display()
print("[PracticeDummy] Respawned!")

Loading…
Cancel
Save