From 7772f69cd3512cbdd173e6df0fbc280c0b8fd212 Mon Sep 17 00:00:00 2001 From: MaddoScientisto Date: Sat, 21 Jun 2025 18:54:14 +0200 Subject: [PATCH] Enemy Damage --- 3D/MapScenes/TestLevel.tscn | 6 +- IsoTest/IsoMapTest2.tscn | 3 +- .../Emitter_Sprial_Test_Bullets.tres | 2 +- Scenes/Actors/Generic_Enemy_FSM_3D.tscn | 39 ++++++- Scripts/Actors/BulletSpawner3D.cs | 2 +- Scripts/Actors/PreviewMarker3D.cs | 2 +- .../Actors/ActorResourceProvider.cs | 2 +- Scripts/Components/Actors/DamageReceiver3D.cs | 104 ++++++++++++++++++ .../Components/Actors/DamageReceiver3D.cs.uid | 1 + Scripts/Components/FSM/Enemy/3D/Dead.cs | 2 +- .../FSM/Enemy/3D/EnemyDamageModule3D.cs | 55 +++++++++ .../FSM/Enemy/3D/EnemyDamageModule3D.cs.uid | 1 + 12 files changed, 207 insertions(+), 12 deletions(-) create mode 100644 Scripts/Components/Actors/DamageReceiver3D.cs create mode 100644 Scripts/Components/Actors/DamageReceiver3D.cs.uid create mode 100644 Scripts/Components/FSM/Enemy/3D/EnemyDamageModule3D.cs create mode 100644 Scripts/Components/FSM/Enemy/3D/EnemyDamageModule3D.cs.uid diff --git a/3D/MapScenes/TestLevel.tscn b/3D/MapScenes/TestLevel.tscn index 934b4401..8763de33 100644 --- a/3D/MapScenes/TestLevel.tscn +++ b/3D/MapScenes/TestLevel.tscn @@ -2429,7 +2429,7 @@ metadata/_edit_group_ = true transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 7.38277, 2, 25.0228) [node name="FloorEmitter" parent="Props" instance=ExtResource("63_r8ono")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 24.9526, 1.32944, 25.5859) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 17.9898, 1.32944, 24.6781) Script = ExtResource("64_fi82p") EmitOnStart = false @@ -2449,6 +2449,10 @@ AutoSpawn = true Billboard = true PixelSize = 0.05 +[node name="ControlPad" parent="Props" node_paths=PackedStringArray("Target") instance=ExtResource("35_c4mw0")] +transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 22.344, 2.41968, 23.1991) +Target = NodePath("../FloorEmitter") + [node name="Light" type="Node3D" parent="."] [node name="OmniLight3D4" type="OmniLight3D" parent="Light"] diff --git a/IsoTest/IsoMapTest2.tscn b/IsoTest/IsoMapTest2.tscn index e144566b..f6c7f4a0 100644 --- a/IsoTest/IsoMapTest2.tscn +++ b/IsoTest/IsoMapTest2.tscn @@ -444,8 +444,9 @@ materials = Array[Material]([ExtResource("7_01bfr")]) [node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] transform = Transform3D(0.442606, -0.744379, 0.5, 0.287606, 0.645974, 0.707107, -0.849343, -0.169166, 0.5, 30.2584, 5.82742, 20.7297) -light_energy = 2.434 +light_energy = 1.375 light_bake_mode = 1 +shadow_enabled = true [node name="DirectionalLight3D2" type="DirectionalLight3D" parent="."] transform = Transform3D(0.768277, -0.517955, -0.376129, -0.0314369, -0.617415, 0.786009, -0.639345, -0.592048, -0.490629, 25.8082, 5.82742, 12.8407) diff --git a/Resources/BulletScripts/Emitter_Sprial_Test_Bullets.tres b/Resources/BulletScripts/Emitter_Sprial_Test_Bullets.tres index 7e730bcf..14e81d11 100644 --- a/Resources/BulletScripts/Emitter_Sprial_Test_Bullets.tres +++ b/Resources/BulletScripts/Emitter_Sprial_Test_Bullets.tres @@ -9,7 +9,7 @@ script = ExtResource("3_2rxa2") BulletResource = ExtResource("1_bjips") EmitterOffset = Vector3(0, 0, 0) -bulletCount = 16 +bulletCount = 6 rotationSpeed = 60.0 _rotationOffset = 0.0 duration = 10.0 diff --git a/Scenes/Actors/Generic_Enemy_FSM_3D.tscn b/Scenes/Actors/Generic_Enemy_FSM_3D.tscn index 5b742504..02d8da48 100644 --- a/Scenes/Actors/Generic_Enemy_FSM_3D.tscn +++ b/Scenes/Actors/Generic_Enemy_FSM_3D.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=18 format=3 uid="uid://bh3vxmqflijgj"] +[gd_scene load_steps=22 format=3 uid="uid://bh3vxmqflijgj"] [ext_resource type="Script" uid="uid://dwregubt4iila" path="res://Scripts/Components/FSM/Enemy/3D/EnemyProxy3D.cs" id="1_a3crc"] [ext_resource type="Resource" uid="uid://ccym6mcq4fbul" path="res://Resources/Enemies/Fairy_Guard_3D.tres" id="2_jgarc"] @@ -15,6 +15,9 @@ [ext_resource type="Script" uid="uid://extjdng8nk6r" path="res://Scripts/Components/FSM/Enemy/3D/PlayerDetection3D.cs" id="13_rg1hb"] [ext_resource type="Script" uid="uid://k5k8wf821ytg" path="res://Scripts/Components/FSM/Enemy/3D/NavigationProvider3D.cs" id="14_dm2sd"] [ext_resource type="PackedScene" uid="uid://cfgc6ik8vb08c" path="res://Scenes/Weapons/BaseWeapon_3D.tscn" id="15_27vgy"] +[ext_resource type="Script" uid="uid://bvcfa6wivpgy1" path="res://Scripts/Components/FSM/Enemy/3D/EnemyDamageModule3D.cs" id="16_27vgy"] +[ext_resource type="Script" uid="uid://dmawekjfas6k8" path="res://Scripts/Components/Actors/DamageReceiver3D.cs" id="17_tabhk"] +[ext_resource type="Script" uid="uid://cqwvssstkrdmw" path="res://Scripts/Components/Actors/ActorResourceProvider.cs" id="18_4fnoq"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_jgarc"] radius = 0.264547 @@ -24,6 +27,10 @@ height = 0.935884 height = 1.91858 radius = 3.04834 +[sub_resource type="CylinderShape3D" id="CylinderShape3D_uv2lf"] +height = 0.853027 +radius = 0.426758 + [node name="Enemy" type="CharacterBody3D" node_paths=PackedStringArray("EnemyFSM")] collision_layer = 64 collision_mask = 17 @@ -47,7 +54,7 @@ script = ExtResource("5_rg1hb") Storage = NodePath("../../Storage") PlayerDetection = NodePath("../../PlayerDetectionProvider") DebugEnabled = true -_moduleNodes = [NodePath(""), NodePath(""), NodePath("")] +_moduleNodes = [NodePath("../../DamageModule")] [node name="Alert" type="Node" parent="StateMachine" node_paths=PackedStringArray("Storage", "PlayerDetection", "NavigationModule", "_moduleNodes")] script = ExtResource("6_jgarc") @@ -55,7 +62,7 @@ Storage = NodePath("../../Storage") PlayerDetection = NodePath("../../PlayerDetectionProvider") NavigationModule = NodePath("../../NavigationProvider") DebugEnabled = true -_moduleNodes = [NodePath(""), NodePath("")] +_moduleNodes = [NodePath("../../DamageModule")] [node name="Shooting" type="Node" parent="StateMachine" node_paths=PackedStringArray("Storage", "PlayerDetection", "EquippedWeapon", "NavigationModule", "_moduleNodes")] script = ExtResource("7_rg1hb") @@ -63,7 +70,7 @@ Storage = NodePath("../../Storage") PlayerDetection = NodePath("../../PlayerDetectionProvider") EquippedWeapon = NodePath("../../Weapon") NavigationModule = NodePath("../../NavigationProvider") -_moduleNodes = [NodePath(""), NodePath("")] +_moduleNodes = [NodePath("../../DamageModule")] [node name="Dead" type="Node" parent="StateMachine" node_paths=PackedStringArray("Storage")] script = ExtResource("8_5j04l") @@ -72,7 +79,7 @@ Storage = NodePath("../../Storage") [node name="Controlled" type="Node" parent="StateMachine" node_paths=PackedStringArray("Storage", "_moduleNodes")] script = ExtResource("9_dm2sd") Storage = NodePath("../../Storage") -_moduleNodes = [NodePath("")] +_moduleNodes = [NodePath("../../DamageModule")] [node name="AnimatedSprite3D" type="AnimatedSprite3D" parent="." node_paths=PackedStringArray("EnemyProxy")] pixel_size = 0.05 @@ -108,6 +115,28 @@ debug_enabled = true [node name="Weapon" parent="." instance=ExtResource("15_27vgy")] +[node name="DamageModule" type="Node" parent="." node_paths=PackedStringArray("DamageReceiver", "StorageModule")] +script = ExtResource("16_27vgy") +DamageReceiver = NodePath("../DamageReceiver") +StorageModule = NodePath("../Storage") + +[node name="DamageReceiver" type="Area3D" parent="." node_paths=PackedStringArray("HealthProvider")] +collision_layer = 64 +collision_mask = 8 +script = ExtResource("17_tabhk") +HealthProvider = NodePath("HealthProvider") +BulletGroup = 2 +DeleteParentOnDeath = false + +[node name="CollisionShape3D" type="CollisionShape3D" parent="DamageReceiver"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.00463867, 0) +shape = SubResource("CylinderShape3D_uv2lf") + +[node name="HealthProvider" type="Node" parent="DamageReceiver"] +script = ExtResource("18_4fnoq") +ResourceName = "Health" + [connection signal="body_entered" from="PlayerDetectionProvider" to="PlayerDetectionProvider" method="_on_body_entered"] [connection signal="body_exited" from="PlayerDetectionProvider" to="PlayerDetectionProvider" method="_on_body_exited"] [connection signal="velocity_computed" from="NavigationAgent3D" to="NavigationProvider" method="_on_navigation_agent_3d_velocity_computed"] +[connection signal="area_entered" from="DamageReceiver" to="DamageReceiver" method="_on_damage_hitbox_area_entered"] diff --git a/Scripts/Actors/BulletSpawner3D.cs b/Scripts/Actors/BulletSpawner3D.cs index a35c9933..a4bb7f88 100644 --- a/Scripts/Actors/BulletSpawner3D.cs +++ b/Scripts/Actors/BulletSpawner3D.cs @@ -62,7 +62,7 @@ public partial class BulletSpawner3D : Node3D // Optional: Debug offset to visualize all bullets // bullet.GlobalPosition += new Vector3(i * 0.1f, 0, 0); - GD.Print($"Bullet {i}: Angle={Mathf.RadToDeg(angle)}, Direction={bulletDirection}"); + //GD.Print($"Bullet {i}: Angle={Mathf.RadToDeg(angle)}, Direction={bulletDirection}"); } } diff --git a/Scripts/Actors/PreviewMarker3D.cs b/Scripts/Actors/PreviewMarker3D.cs index ecd0e302..c283c6e2 100644 --- a/Scripts/Actors/PreviewMarker3D.cs +++ b/Scripts/Actors/PreviewMarker3D.cs @@ -106,7 +106,7 @@ public partial class PreviewMarker3D : Marker3D if (_sprite is null) { - GD.Print("Remaking sprite"); + //GD.Print("Remaking sprite"); _sprite = new EditorSprite3D(); this.AddChild(_sprite); //_sprite.Owner = GetTree().EditedSceneRoot; diff --git a/Scripts/Components/Actors/ActorResourceProvider.cs b/Scripts/Components/Actors/ActorResourceProvider.cs index 9f64913f..72205669 100644 --- a/Scripts/Components/Actors/ActorResourceProvider.cs +++ b/Scripts/Components/Actors/ActorResourceProvider.cs @@ -2,7 +2,7 @@ namespace Cirno.Scripts.Components.Actors; -public partial class ActorResourceProvider : Node2D +public partial class ActorResourceProvider : Node { [Export] public string ResourceName { get; private set; } diff --git a/Scripts/Components/Actors/DamageReceiver3D.cs b/Scripts/Components/Actors/DamageReceiver3D.cs new file mode 100644 index 00000000..6a83f352 --- /dev/null +++ b/Scripts/Components/Actors/DamageReceiver3D.cs @@ -0,0 +1,104 @@ +using System.Linq; +using Cirno.Scripts.Resources; +using Cirno.Scripts.Utils; +using Cirno.Scripts.Weapons; +using Godot; +using Godot.Collections; + +namespace Cirno.Scripts.Components.Actors; + +public partial class DamageReceiver3D : Area3D, IHittable +{ + [Export] public ActorResourceProvider HealthProvider { get; private set; } + + [Export] public bool Invulnerable { get; private set; } = false; + + [Export] public BulletOwner BulletGroup { get; set; } = BulletOwner.None; + + [Export] public PackedScene Debris { get; set; } + + [Export] public Array DamageResistances { get; set; } = []; + + [Export] public bool DeleteParentOnDeath { get; private set; } = true; + + [Signal] + public delegate void ShieldHitEventHandler(); + + [Signal] public delegate void BulletHitEventHandler(Bullet3D bullet, Vector3 position, Vector3 direction); + + //[Signal] public delegate void DeathEventHandler(); + + private Node3D _parent; + + public bool Enabled { get; private set; } = true; + + public override void _Ready() + { + _parent = GetParent(); + HealthProvider.FillResource(); + HealthProvider.ResourceDepleted += OnDeath; + } + + public void ChangeState(bool enabled) + { + Enabled = enabled; + } + + private void _on_damage_hitbox_area_entered(Area3D area) + { + if (!Enabled) return; + if (area is not Bullet3D bullet) return; + if (Invulnerable) + { + EmitSignalShieldHit(); + return; + }; + + if (BulletGroup is BulletOwner.None) + { + this.Hit(bullet.Damage, bullet.DamageType); + + EmitSignalBulletHit(bullet, area.GlobalPosition, (this.GlobalPosition - area.GlobalPosition).Normalized()); + + bullet.RequestCollisionDestruction(); + return; + } + + if (bullet.BulletInfo.Owner == BulletGroup) return; + + this.Hit(bullet.Damage, bullet.DamageType); + + EmitSignalBulletHit(bullet, area.GlobalPosition, (this.GlobalPosition - area.GlobalPosition).Normalized()); + + bullet.RequestCollisionDestruction(); + } + + public void Hit(float damage, DamageType damageType = DamageType.Neutral) + { + if (!Enabled) return; + if (Invulnerable) return; + + // Change value based on difficulty + float difficultyReducedDmg = damage * GlobalState.Instance.SessionSettings.DifficultyDamageMultiplier; + + var dmg = DamageResistances.Aggregate(difficultyReducedDmg, (current, resistance) => current * resistance.CalculateDamage(current, damageType)); + + HealthProvider.CurrentResource -= dmg; + } + + private void OnDeath() + { + if (Debris is not null) + { + _parent.CreateSibling(Debris); + } + + // Not needed because the health provider is accessible + //EmitSignal(SignalName.Death); + + if (DeleteParentOnDeath) + { + _parent.QueueFree(); + } + } +} \ No newline at end of file diff --git a/Scripts/Components/Actors/DamageReceiver3D.cs.uid b/Scripts/Components/Actors/DamageReceiver3D.cs.uid new file mode 100644 index 00000000..03141925 --- /dev/null +++ b/Scripts/Components/Actors/DamageReceiver3D.cs.uid @@ -0,0 +1 @@ +uid://dmawekjfas6k8 diff --git a/Scripts/Components/FSM/Enemy/3D/Dead.cs b/Scripts/Components/FSM/Enemy/3D/Dead.cs index d01b5a52..217e91ad 100644 --- a/Scripts/Components/FSM/Enemy/3D/Dead.cs +++ b/Scripts/Components/FSM/Enemy/3D/Dead.cs @@ -14,6 +14,6 @@ public partial class Dead : EnemyStateBase3D base.EnterState(); // player detection // damage receiver will be a module - + MainObject.Hide(); } } \ No newline at end of file diff --git a/Scripts/Components/FSM/Enemy/3D/EnemyDamageModule3D.cs b/Scripts/Components/FSM/Enemy/3D/EnemyDamageModule3D.cs new file mode 100644 index 00000000..ae6879bf --- /dev/null +++ b/Scripts/Components/FSM/Enemy/3D/EnemyDamageModule3D.cs @@ -0,0 +1,55 @@ +using Cirno.Scripts.Components.Actors; +using Cirno.Scripts.Enums; +using Godot; + +namespace Cirno.Scripts.Components.FSM.Enemy._3D; + +public partial class EnemyDamageModule3D : ModuleBase +{ + [Export] public DamageReceiver3D DamageReceiver { get; private set; } + [Export] public EnemyStorage3D StorageModule { get; private set; } + + private IStateMachine _machine; + public override void EnterState(EnemyState state) + { + DamageReceiver.ChangeState(true); + DamageReceiver.HealthProvider.ResourceDepleted += HealthProviderOnResourceDepleted; + + // DamageReceiver.HealthProvider.ResourceDecreased += HealthProviderOnResourceDecreased; + } + + public override void ExitState(EnemyState state) + { + DamageReceiver.HealthProvider.ResourceDepleted -= HealthProviderOnResourceDepleted; + + // DamageReceiver.HealthProvider.ResourceDecreased -= HealthProviderOnResourceDecreased; + DamageReceiver.ChangeState(false); + } + + public override void Init(IStateMachine machine) + { + _machine = machine; + } + + public override void Process(double delta) + { + + } + + public override void PhysicsProcess(double delta) + { + + } + + private void HealthProviderOnResourceDecreased(float oldvalue, float newvalue, float maxvalue) + { + StorageModule.AiState = AiState.Enabled; + _machine.SetState(EnemyState.Alert); + } + + private void HealthProviderOnResourceDepleted() + { + _machine.SetState(EnemyState.Dead); + } + +} \ No newline at end of file diff --git a/Scripts/Components/FSM/Enemy/3D/EnemyDamageModule3D.cs.uid b/Scripts/Components/FSM/Enemy/3D/EnemyDamageModule3D.cs.uid new file mode 100644 index 00000000..02b82431 --- /dev/null +++ b/Scripts/Components/FSM/Enemy/3D/EnemyDamageModule3D.cs.uid @@ -0,0 +1 @@ +uid://bvcfa6wivpgy1