From 5908494977e0e3776e0ccc92b918496326420836 Mon Sep 17 00:00:00 2001 From: Minimata Date: Sat, 17 Jan 2026 10:10:14 +0100 Subject: [PATCH] two enemy types, ready to refactor --- Movement tests.csproj | 1 + interfaces/IDamageable.cs | 6 ++ interfaces/IDamageable.cs.uid | 1 + interfaces/IKillable.cs | 6 ++ interfaces/IKillable.cs.uid | 1 + interfaces/IKnockbackable.cs | 6 ++ interfaces/IKnockbackable.cs.uid | 1 + interfaces/{spawnable => }/ISpawnable.cs | 1 - interfaces/{spawnable => }/ISpawnable.cs.uid | 0 maps/GYMs/enemies.tscn | 35 +++++--- player_controller/PlayerController.tscn | 3 +- player_controller/Scripts/PlayerController.cs | 21 +++-- scenes/enemies/FirstEnemy.cs | 21 +++-- scenes/enemies/FlyingEnemy.cs | 85 +++++++++++++++++++ scenes/enemies/FlyingEnemy.cs.uid | 1 + scenes/enemies/FlyingEnemyInputs.cs | 23 +++++ scenes/enemies/FlyingEnemyInputs.cs.uid | 1 + scenes/enemies/first_enemy.tscn | 15 +++- scenes/enemies/flying_enemy.tscn | 60 +++++++++++++ 19 files changed, 256 insertions(+), 32 deletions(-) create mode 100644 interfaces/IDamageable.cs create mode 100644 interfaces/IDamageable.cs.uid create mode 100644 interfaces/IKillable.cs create mode 100644 interfaces/IKillable.cs.uid create mode 100644 interfaces/IKnockbackable.cs create mode 100644 interfaces/IKnockbackable.cs.uid rename interfaces/{spawnable => }/ISpawnable.cs (62%) rename interfaces/{spawnable => }/ISpawnable.cs.uid (100%) create mode 100644 scenes/enemies/FlyingEnemy.cs create mode 100644 scenes/enemies/FlyingEnemy.cs.uid create mode 100644 scenes/enemies/FlyingEnemyInputs.cs create mode 100644 scenes/enemies/FlyingEnemyInputs.cs.uid create mode 100644 scenes/enemies/flying_enemy.tscn diff --git a/Movement tests.csproj b/Movement tests.csproj index 691e7dcb..0608ca76 100644 --- a/Movement tests.csproj +++ b/Movement tests.csproj @@ -123,6 +123,7 @@ + diff --git a/interfaces/IDamageable.cs b/interfaces/IDamageable.cs new file mode 100644 index 00000000..d8f6d349 --- /dev/null +++ b/interfaces/IDamageable.cs @@ -0,0 +1,6 @@ +namespace Movementtests.interfaces; + +public interface IDamageable +{ + void TakeDamage(); +} \ No newline at end of file diff --git a/interfaces/IDamageable.cs.uid b/interfaces/IDamageable.cs.uid new file mode 100644 index 00000000..198507cc --- /dev/null +++ b/interfaces/IDamageable.cs.uid @@ -0,0 +1 @@ +uid://cf56b2ep3bu3j diff --git a/interfaces/IKillable.cs b/interfaces/IKillable.cs new file mode 100644 index 00000000..04ea6835 --- /dev/null +++ b/interfaces/IKillable.cs @@ -0,0 +1,6 @@ +namespace Movementtests.interfaces; + +public interface IKillable +{ + +} \ No newline at end of file diff --git a/interfaces/IKillable.cs.uid b/interfaces/IKillable.cs.uid new file mode 100644 index 00000000..d7fe0fd6 --- /dev/null +++ b/interfaces/IKillable.cs.uid @@ -0,0 +1 @@ +uid://bea2kvnu3kuhu diff --git a/interfaces/IKnockbackable.cs b/interfaces/IKnockbackable.cs new file mode 100644 index 00000000..9b93519e --- /dev/null +++ b/interfaces/IKnockbackable.cs @@ -0,0 +1,6 @@ +namespace Movementtests.interfaces; + +public interface IKnockbackable +{ + +} \ No newline at end of file diff --git a/interfaces/IKnockbackable.cs.uid b/interfaces/IKnockbackable.cs.uid new file mode 100644 index 00000000..a12aac9a --- /dev/null +++ b/interfaces/IKnockbackable.cs.uid @@ -0,0 +1 @@ +uid://bh06s1yefucf7 diff --git a/interfaces/spawnable/ISpawnable.cs b/interfaces/ISpawnable.cs similarity index 62% rename from interfaces/spawnable/ISpawnable.cs rename to interfaces/ISpawnable.cs index 4b6b1671..e5edbf18 100644 --- a/interfaces/spawnable/ISpawnable.cs +++ b/interfaces/ISpawnable.cs @@ -3,6 +3,5 @@ using Godot; interface ISpawnable { - Resource GetSpawnInitResource(); void TestMethod(); } \ No newline at end of file diff --git a/interfaces/spawnable/ISpawnable.cs.uid b/interfaces/ISpawnable.cs.uid similarity index 100% rename from interfaces/spawnable/ISpawnable.cs.uid rename to interfaces/ISpawnable.cs.uid diff --git a/maps/GYMs/enemies.tscn b/maps/GYMs/enemies.tscn index b0716bcf..cc8cdcd7 100644 --- a/maps/GYMs/enemies.tscn +++ b/maps/GYMs/enemies.tscn @@ -1,10 +1,11 @@ -[gd_scene load_steps=13 format=3 uid="uid://q7uc1h2jpbd2"] +[gd_scene load_steps=15 format=3 uid="uid://q7uc1h2jpbd2"] [ext_resource type="PackedScene" uid="uid://bei4nhkf8lwdo" path="res://player_controller/PlayerController.tscn" id="1_62kkh"] [ext_resource type="Material" uid="uid://31aulub2nqov" path="res://assets/greybox/m_greybox.tres" id="2_3uydm"] [ext_resource type="PackedScene" uid="uid://dxt0e2ugmttqq" path="res://scenes/enemies/first_enemy.tscn" id="3_3uydm"] [ext_resource type="Script" uid="uid://b2vdwkiqauhk3" path="res://scenes/enemies/EnemyInitInputs.cs" id="4_nd7vd"] -[ext_resource type="PackedScene" uid="uid://c305mfrtumcyq" path="res://scenes/spawners/spawner.tscn" id="5_8fd2t"] +[ext_resource type="PackedScene" uid="uid://cmlud1hwkd6sv" path="res://scenes/enemies/flying_enemy.tscn" id="5_8fd2t"] +[ext_resource type="Script" uid="uid://do0dic4r3ri0s" path="res://scenes/enemies/FlyingEnemyInputs.cs" id="6_7m3bq"] [sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_0xm2m"] sky_horizon_color = Color(0.662243, 0.671743, 0.686743, 1) @@ -30,23 +31,30 @@ glow_enabled = true [sub_resource type="Resource" id="Resource_8fd2t"] script = ExtResource("4_nd7vd") -Speed = 3.2 +Speed = 1.2 metadata/_custom_type_script = "uid://b2vdwkiqauhk3" [sub_resource type="Resource" id="Resource_7m3bq"] script = ExtResource("4_nd7vd") -Speed = 2.8 +Speed = 2.7 metadata/_custom_type_script = "uid://b2vdwkiqauhk3" [sub_resource type="Resource" id="Resource_sysok"] script = ExtResource("4_nd7vd") -Speed = 6.4 +Speed = 4.0 metadata/_custom_type_script = "uid://b2vdwkiqauhk3" [sub_resource type="Resource" id="Resource_caohq"] -script = ExtResource("4_nd7vd") -Speed = 3.1 -metadata/_custom_type_script = "uid://b2vdwkiqauhk3" +script = ExtResource("6_7m3bq") +Speed = 4.0 +TargetHeight = 10.0 +metadata/_custom_type_script = "uid://do0dic4r3ri0s" + +[sub_resource type="Resource" id="Resource_dmw1t"] +script = ExtResource("6_7m3bq") +Speed = 6.0 +TargetHeight = 15.0 +metadata/_custom_type_script = "uid://do0dic4r3ri0s" [node name="Main" type="Node3D"] @@ -129,7 +137,12 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.5, 0, -16.83681) Target = NodePath("../Player") Inputs = SubResource("Resource_sysok") -[node name="Spawner" parent="." node_paths=PackedStringArray("Target") instance=ExtResource("5_8fd2t")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 10, 3.5, -16.5) -Inputs = SubResource("Resource_caohq") +[node name="FlyingEnemy" parent="." node_paths=PackedStringArray("Target") instance=ExtResource("5_8fd2t")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8, 7, -16) Target = NodePath("../Player") +Inputs = SubResource("Resource_caohq") + +[node name="FlyingEnemy2" parent="." node_paths=PackedStringArray("Target") instance=ExtResource("5_8fd2t")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 10, 7, -16) +Target = NodePath("../Player") +Inputs = SubResource("Resource_dmw1t") diff --git a/player_controller/PlayerController.tscn b/player_controller/PlayerController.tscn index 082fa339..f54b1929 100644 --- a/player_controller/PlayerController.tscn +++ b/player_controller/PlayerController.tscn @@ -50,6 +50,7 @@ height = 1.7 radius = 0.45 [sub_resource type="SphereShape3D" id="SphereShape3D_q14ux"] +radius = 1.0 [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_nodcl"] transparency = 1 @@ -174,7 +175,7 @@ collision_mask = 16 monitorable = false [node name="CollisionShape3D" type="CollisionShape3D" parent="HeadSystem/WeaponHitbox"] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, -1) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, -1.5) shape = SubResource("SphereShape3D_q14ux") [node name="StairsSystem" type="Node3D" parent="."] diff --git a/player_controller/Scripts/PlayerController.cs b/player_controller/Scripts/PlayerController.cs index f0942b5a..2fb56abd 100644 --- a/player_controller/Scripts/PlayerController.cs +++ b/player_controller/Scripts/PlayerController.cs @@ -2,11 +2,12 @@ using System; using Godot; using GodotStateCharts; using Movementtests.addons.godot_state_charts.csharp; +using Movementtests.interfaces; using Movementtests.systems; using Movementtests.player_controller.Scripts; using RustyOptions; -public partial class PlayerController : CharacterBody3D +public partial class PlayerController : CharacterBody3D, IDamageable, IKnockbackable { // Enums public enum AllowedInputs @@ -1580,14 +1581,13 @@ public partial class PlayerController : CharacterBody3D ThrowWeapon(); } - if (WeaponSystem.InHandState.Active) + if (!WeaponSystem.InHandState.Active) return; + + var bodies = WeaponHitbox.GetOverlappingBodies(); + foreach (var body in bodies) { - var bodies = WeaponHitbox.GetOverlappingBodies(); - foreach (var body in bodies) - { - if(body is ISpawnable spawnable) - spawnable.TestMethod(); - } + if(body is IDamageable spawnable) + spawnable.TakeDamage(); } } @@ -1741,4 +1741,9 @@ public partial class PlayerController : CharacterBody3D DashIndicatorNode.LookAt(WeaponSystem.GlobalPosition); } } + + public void TakeDamage() + { + GD.Print("Ouch!"); + } } diff --git a/scenes/enemies/FirstEnemy.cs b/scenes/enemies/FirstEnemy.cs index 655db471..60c18448 100644 --- a/scenes/enemies/FirstEnemy.cs +++ b/scenes/enemies/FirstEnemy.cs @@ -1,10 +1,10 @@ using Godot; using System; -using Movementtests.player_controller.Scripts; +using Movementtests.interfaces; [GlobalClass] -public partial class FirstEnemy : CharacterBody3D, ISpawnable +public partial class FirstEnemy : CharacterBody3D, IDamageable, IKillable, IKnockbackable { [Export] public Node3D Target { get; set; } @@ -13,20 +13,18 @@ public partial class FirstEnemy : CharacterBody3D, ISpawnable public EnemyInitInputs Inputs; private RayCast3D _wallInFrontRayCast; + private Area3D _damageBox; public override void _Ready() { _wallInFrontRayCast = GetNode("WallInFrontRayCast"); + _damageBox = GetNode("DamageBox"); + _damageBox.BodyEntered += OnDamageBoxTriggered; } - public Resource GetSpawnInitResource() + public void OnDamageBoxTriggered(Node3D body) { - return Inputs; - } - - public void TestMethod() - { - GD.Print("I'm an enemy"); + if(body is IDamageable damageable) damageable.TakeDamage(); } public override void _PhysicsProcess(double delta) @@ -49,4 +47,9 @@ public partial class FirstEnemy : CharacterBody3D, ISpawnable Velocity = velocity; MoveAndSlide(); } + + public void TakeDamage() + { + GD.Print("Emotional daaamaaage!"); + } } diff --git a/scenes/enemies/FlyingEnemy.cs b/scenes/enemies/FlyingEnemy.cs new file mode 100644 index 00000000..3e6fffb0 --- /dev/null +++ b/scenes/enemies/FlyingEnemy.cs @@ -0,0 +1,85 @@ +using Godot; +using System; +using Movementtests.interfaces; + +[GlobalClass] +public partial class FlyingEnemy : CharacterBody3D, IDamageable, IKillable, IKnockbackable +{ + [Export] + public Node3D Target { get; set; } + + [Export] + public FlyingEnemyInputs Inputs; + + private RayCast3D _groundDistanceRaycast; + private Area3D _damageBox; + + private bool _movingToDesiredHeight = true; + private Vector3 _randomDirection; + + public override void _Ready() + { + _groundDistanceRaycast = GetNode("GroundDistance"); + _groundDistanceRaycast.TargetPosition = new Vector3(0, -Inputs.TargetHeight, 0); + + _damageBox = GetNode("DamageBox"); + _damageBox.BodyEntered += OnDamageBoxTriggered; + + _randomDirection = new Vector3(GD.RandRange(-1, 1), 1, GD.RandRange(-1, 1)).Normalized(); + } + + public void OnDamageBoxTriggered(Node3D body) + { + if(body is IDamageable damageable) damageable.TakeDamage(); + } + + public override void _PhysicsProcess(double delta) + { + var target = Target.GlobalPosition; + var direction = (target - GlobalPosition).Normalized(); + Vector3 velocity = Velocity; + + var targetPlane = new Vector3(target.X, GlobalPosition.Y, target.Z); + LookAt(targetPlane); + + // Check if we have a direct line of sight to the player + if (!_movingToDesiredHeight) + { + velocity = direction * Inputs.Speed; + + var spaceState = GetWorld3D().DirectSpaceState; + var query = PhysicsRayQueryParameters3D.Create(GlobalPosition, target, _groundDistanceRaycast.CollisionMask); + var result = spaceState.IntersectRay(query); + if (result.Count > 0) + { + _movingToDesiredHeight = true; + _randomDirection = new Vector3(GD.RandRange(-1, 1), 1, GD.RandRange(-1, 1)).Normalized(); + } + } + else + { + velocity = _randomDirection * Inputs.Speed; + + if (!_groundDistanceRaycast.IsColliding()) + { + velocity.Y = 0; + + var spaceState = GetWorld3D().DirectSpaceState; + var query = PhysicsRayQueryParameters3D.Create(GlobalPosition, target, _groundDistanceRaycast.CollisionMask); + var result = spaceState.IntersectRay(query); + if (result.Count == 0) + { + _movingToDesiredHeight = false; + } + } + } + + Velocity = velocity; + MoveAndSlide(); + } + + public void TakeDamage() + { + GD.Print("Oh no I'm falling"); + } +} diff --git a/scenes/enemies/FlyingEnemy.cs.uid b/scenes/enemies/FlyingEnemy.cs.uid new file mode 100644 index 00000000..f5204e53 --- /dev/null +++ b/scenes/enemies/FlyingEnemy.cs.uid @@ -0,0 +1 @@ +uid://cmvep0qi7qlvf diff --git a/scenes/enemies/FlyingEnemyInputs.cs b/scenes/enemies/FlyingEnemyInputs.cs new file mode 100644 index 00000000..20ceedb7 --- /dev/null +++ b/scenes/enemies/FlyingEnemyInputs.cs @@ -0,0 +1,23 @@ +using Godot; +using System; + +[GlobalClass] +public partial class FlyingEnemyInputs : Resource +{ + [Export(PropertyHint.Range, "0,10,0.1,or_greater")] + public float Speed = 5.0f; + + [Export(PropertyHint.Range, "0,100,1,or_greater")] + public float TargetHeight = 50.0f; + + public FlyingEnemyInputs() + { + Speed = 5.0f; + TargetHeight = 50.0f; + } + public FlyingEnemyInputs(float speed, float targetHeight) + { + Speed = speed; + TargetHeight = targetHeight; + } +} \ No newline at end of file diff --git a/scenes/enemies/FlyingEnemyInputs.cs.uid b/scenes/enemies/FlyingEnemyInputs.cs.uid new file mode 100644 index 00000000..0734f3ad --- /dev/null +++ b/scenes/enemies/FlyingEnemyInputs.cs.uid @@ -0,0 +1 @@ +uid://do0dic4r3ri0s diff --git a/scenes/enemies/first_enemy.tscn b/scenes/enemies/first_enemy.tscn index 7fabf55e..0911bf17 100644 --- a/scenes/enemies/first_enemy.tscn +++ b/scenes/enemies/first_enemy.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=6 format=3 uid="uid://dxt0e2ugmttqq"] +[gd_scene load_steps=7 format=3 uid="uid://dxt0e2ugmttqq"] [ext_resource type="Script" uid="uid://bn7sc6id7n166" path="res://scenes/enemies/FirstEnemy.cs" id="1_4yfjf"] @@ -15,7 +15,10 @@ rings = 4 [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_4yfjf"] albedo_color = Color(0.06469653, 0.06469653, 0.06469653, 1) -[node name="CharacterBody3D" type="CharacterBody3D"] +[sub_resource type="BoxShape3D" id="BoxShape3D_4yfjf"] +size = Vector3(1, 2, 2) + +[node name="FirstEnemy" type="CharacterBody3D"] collision_layer = 16 collision_mask = 273 script = ExtResource("1_4yfjf") @@ -49,3 +52,11 @@ surface_material_override/0 = SubResource("StandardMaterial3D_4yfjf") transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.1, 0) target_position = Vector3(0, 0, -1.5) collision_mask = 272 + +[node name="DamageBox" type="Area3D" parent="."] +collision_layer = 0 +monitorable = false + +[node name="CollisionShape3D" type="CollisionShape3D" parent="DamageBox"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, -0.5) +shape = SubResource("BoxShape3D_4yfjf") diff --git a/scenes/enemies/flying_enemy.tscn b/scenes/enemies/flying_enemy.tscn new file mode 100644 index 00000000..862f61ae --- /dev/null +++ b/scenes/enemies/flying_enemy.tscn @@ -0,0 +1,60 @@ +[gd_scene load_steps=7 format=3 uid="uid://cmlud1hwkd6sv"] + +[ext_resource type="Script" uid="uid://cmvep0qi7qlvf" path="res://scenes/enemies/FlyingEnemy.cs" id="1_b46rq"] + +[sub_resource type="SphereShape3D" id="SphereShape3D_b46rq"] + +[sub_resource type="SphereMesh" id="SphereMesh_1bsgx"] + +[sub_resource type="SphereMesh" id="SphereMesh_4yfjf"] +radius = 0.05 +height = 0.1 +radial_segments = 4 +rings = 4 + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_4yfjf"] +albedo_color = Color(0.06469653, 0.06469653, 0.06469653, 1) + +[sub_resource type="BoxShape3D" id="BoxShape3D_4yfjf"] +size = Vector3(1, 1, 1.5) + +[node name="FlyingEnemy" type="CharacterBody3D"] +collision_layer = 16 +collision_mask = 273 +motion_mode = 1 +script = ExtResource("1_b46rq") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +shape = SubResource("SphereShape3D_b46rq") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +layers = 33 +mesh = SubResource("SphereMesh_1bsgx") + +[node name="MeshInstance3D2" type="MeshInstance3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.2, 0, -0.5) +layers = 33 +cast_shadow = 0 +ignore_occlusion_culling = true +mesh = SubResource("SphereMesh_4yfjf") +surface_material_override/0 = SubResource("StandardMaterial3D_4yfjf") + +[node name="MeshInstance3D3" type="MeshInstance3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.2, 0, -0.5) +layers = 33 +cast_shadow = 0 +ignore_occlusion_culling = true +mesh = SubResource("SphereMesh_4yfjf") +surface_material_override/0 = SubResource("StandardMaterial3D_4yfjf") + +[node name="GroundDistance" type="RayCast3D" parent="."] +target_position = Vector3(0, 0, 0) +collision_mask = 272 + +[node name="DamageBox" type="Area3D" parent="."] +collision_layer = 0 +monitorable = false + +[node name="CollisionShape3D" type="CollisionShape3D" parent="DamageBox"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.75) +shape = SubResource("BoxShape3D_4yfjf")