From 7fa2efabaf6ab58414c2a2d774c0a94d83f47fd5 Mon Sep 17 00:00:00 2001 From: Twirpytherobot Date: Tue, 17 Feb 2026 22:51:38 +0000 Subject: [PATCH] heheh --- level/resources/weapon_lobsteraxe.tres | 1 + level/scenes/level.tscn | 8 +- level/scripts/armed_enemy.gd | 126 +++++++++++++++++++++++-- level/scripts/basic_enemy.gd | 20 ++++ level/scripts/hit_box.gd | 4 + level/scripts/level.gd | 29 ++++-- level/scripts/practice_dummy.gd | 20 ++++ 7 files changed, 193 insertions(+), 15 deletions(-) diff --git a/level/resources/weapon_lobsteraxe.tres b/level/resources/weapon_lobsteraxe.tres index e3c23f1..13c40e4 100644 --- a/level/resources/weapon_lobsteraxe.tres +++ b/level/resources/weapon_lobsteraxe.tres @@ -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" diff --git a/level/scenes/level.tscn b/level/scenes/level.tscn index b609441..fa7db25 100644 --- a/level/scenes/level.tscn +++ b/level/scenes/level.tscn @@ -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"] diff --git a/level/scripts/armed_enemy.gd b/level/scripts/armed_enemy.gd index 83a503f..c5adfb5 100644 --- a/level/scripts/armed_enemy.gd +++ b/level/scripts/armed_enemy.gd @@ -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 diff --git a/level/scripts/basic_enemy.gd b/level/scripts/basic_enemy.gd index 935cca5..f1b2e71 100644 --- a/level/scripts/basic_enemy.gd +++ b/level/scripts/basic_enemy.gd @@ -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 diff --git a/level/scripts/hit_box.gd b/level/scripts/hit_box.gd index 5b54482..aba54cd 100644 --- a/level/scripts/hit_box.gd +++ b/level/scripts/hit_box.gd @@ -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: diff --git a/level/scripts/level.gd b/level/scripts/level.gd index 514a053..6bc5e4e 100644 --- a/level/scripts/level.gd +++ b/level/scripts/level.gd @@ -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, "", "") diff --git a/level/scripts/practice_dummy.gd b/level/scripts/practice_dummy.gd index 27bc8fe..d5cf3f2 100644 --- a/level/scripts/practice_dummy.gd +++ b/level/scripts/practice_dummy.gd @@ -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!")