diff --git a/interfaces/IDamageMaker.cs b/interfaces/IDamageDealer.cs similarity index 76% rename from interfaces/IDamageMaker.cs rename to interfaces/IDamageDealer.cs index 2ff503e1..24532650 100644 --- a/interfaces/IDamageMaker.cs +++ b/interfaces/IDamageDealer.cs @@ -2,7 +2,7 @@ using Godot; namespace Movementtests.interfaces; -public interface IDamageMaker +public interface IDamageDealer { [Export] RDamage RDamage { get; set; } diff --git a/interfaces/IDamageMaker.cs.uid b/interfaces/IDamageDealer.cs.uid similarity index 100% rename from interfaces/IDamageMaker.cs.uid rename to interfaces/IDamageDealer.cs.uid diff --git a/interfaces/IDamageable.cs b/interfaces/IDamageable.cs index 5c54b43d..c96e1e60 100644 --- a/interfaces/IDamageable.cs +++ b/interfaces/IDamageable.cs @@ -1,9 +1,12 @@ using System; +using Godot; namespace Movementtests.interfaces; +public record DamageRecord(Node3D Source, RDamage Damage); + public interface IDamageable { - event Action DamageTaken; - float TakeDamage(RDamage damage); + event Action DamageTaken; + DamageRecord TakeDamage(DamageRecord damageRecord); } \ No newline at end of file diff --git a/interfaces/IHealthable.cs b/interfaces/IHealthable.cs index 8ce69037..22db4842 100644 --- a/interfaces/IHealthable.cs +++ b/interfaces/IHealthable.cs @@ -12,5 +12,5 @@ public interface IHealthable float CurrentHealth { get; set; } - void ReduceHealth(IDamageable source, float amount); + void ReduceHealth(IDamageable source, DamageRecord damageRecord); } \ No newline at end of file diff --git a/interfaces/IKnockbackable.cs b/interfaces/IKnockbackable.cs index 9b93519e..420c4c4f 100644 --- a/interfaces/IKnockbackable.cs +++ b/interfaces/IKnockbackable.cs @@ -1,6 +1,11 @@ +using Godot; + namespace Movementtests.interfaces; public interface IKnockbackable { + [Export] RKnockback RKnockback { get; set;} + public void RegisterKnockback(IDamageable source, DamageRecord damageRecord); + public Vector3 ComputeKnockback(); } \ No newline at end of file diff --git a/maps/GYMs/enemies.tscn b/maps/GYMs/enemies.tscn index 626d7e07..e476f34d 100644 --- a/maps/GYMs/enemies.tscn +++ b/maps/GYMs/enemies.tscn @@ -16,7 +16,6 @@ [sub_resource type="Resource" id="Resource_2e4ci"] script = ExtResource("2_sysok") -DamageDealt = 10.0 metadata/_custom_type_script = "uid://jitubgv6judn" [sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_0xm2m"] @@ -145,6 +144,7 @@ MovementInputs = ExtResource("7_caohq") HealthInputs = SubResource("Resource_ybosk") DamageInputs = ExtResource("9_dmw1t") Target = NodePath("../Player") +IsActiveOnStart = false [node name="FlyingSpawner" parent="." node_paths=PackedStringArray("Target") instance=ExtResource("6_7m3bq")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 18, 11, -14) @@ -153,3 +153,4 @@ MovementInputs = ExtResource("10_spw1u") HealthInputs = ExtResource("11_2e4ci") DamageInputs = ExtResource("9_gp7s3") Target = NodePath("../Player") +IsActiveOnStart = false diff --git a/player_controller/Scripts/PlayerController.cs b/player_controller/Scripts/PlayerController.cs index 483e834a..bf070fb1 100644 --- a/player_controller/Scripts/PlayerController.cs +++ b/player_controller/Scripts/PlayerController.cs @@ -7,7 +7,9 @@ using Movementtests.systems; using Movementtests.player_controller.Scripts; using RustyOptions; -public partial class PlayerController : CharacterBody3D, IDamageable, IKnockbackable, IDamageMaker +public partial class PlayerController : CharacterBody3D, + IDamageable, + IDamageDealer { // Enums public enum AllowedInputs @@ -33,7 +35,7 @@ public partial class PlayerController : CharacterBody3D, IDamageable, IKnockback // Signals and events // /////////////////////////// - public event Action DamageTaken; + public event Action DamageTaken; /////////////////////////// // Public stuff // @@ -1735,9 +1737,9 @@ public partial class PlayerController : CharacterBody3D, IDamageable, IKnockback } } - public float TakeDamage(RDamage damage) + public DamageRecord TakeDamage(DamageRecord damageRecord) { - var finalDamage = CDamage.TakeDamage(damage); + var finalDamage = CDamage.TakeDamage(damageRecord); DamageTaken?.Invoke(this, finalDamage); return finalDamage; } @@ -1757,7 +1759,7 @@ public partial class PlayerController : CharacterBody3D, IDamageable, IKnockback foreach (var body in bodies) { if(body is IDamageable spawnable) - spawnable.TakeDamage(RDamage); + spawnable.TakeDamage(new DamageRecord(this, RDamage)); } } } diff --git a/resource_definitions/RDamageModifier.cs b/resource_definitions/RDamageModifier.cs index 29920497..e51a6b0a 100644 --- a/resource_definitions/RDamageModifier.cs +++ b/resource_definitions/RDamageModifier.cs @@ -6,7 +6,7 @@ using Movementtests.systems.damage; [GlobalClass] public partial class RDamageModifier : Resource, IDamageable { - public event Action DamageTaken; + public event Action DamageTaken; [Export] public EDamageTypes DamageType = EDamageTypes.Normal; @@ -24,11 +24,14 @@ public partial class RDamageModifier : Resource, IDamageable DamageType = damageType; } - public float TakeDamage(RDamage damage) + public DamageRecord TakeDamage(DamageRecord damageRecord) { - if (damage.DamageType != DamageType) return 0; - var finalDamage = damage.DamageDealt * Modifier; - DamageTaken?.Invoke(this, finalDamage); - return finalDamage; + if (damageRecord.Damage.DamageType != DamageType) + return damageRecord with { Damage = new RDamage(0, damageRecord.Damage.DamageType) }; + + var finalDamage = damageRecord.Damage.DamageDealt * Modifier; + var finalDamageRecord = damageRecord with { Damage = new RDamage(finalDamage, damageRecord.Damage.DamageType) }; + DamageTaken?.Invoke(this, finalDamageRecord); + return finalDamageRecord; } } diff --git a/resource_definitions/RKnockback.cs b/resource_definitions/RKnockback.cs new file mode 100644 index 00000000..cfcb1108 --- /dev/null +++ b/resource_definitions/RKnockback.cs @@ -0,0 +1,19 @@ +using Godot; +using System; +using Movementtests.interfaces; + +[GlobalClass] +public partial class RKnockback : Resource +{ + [Export] + public float Modifier = 1.0f; + + public RKnockback() + { + Modifier = 1.0f; + } + public RKnockback(float modifier) + { + Modifier = modifier; + } +} diff --git a/resource_definitions/RKnockback.cs.uid b/resource_definitions/RKnockback.cs.uid new file mode 100644 index 00000000..29250bb8 --- /dev/null +++ b/resource_definitions/RKnockback.cs.uid @@ -0,0 +1 @@ +uid://b44cse62qru7j diff --git a/resource_definitions/RMovement.cs b/resource_definitions/RMovement.cs index cafa0ac3..b44c082a 100644 --- a/resource_definitions/RMovement.cs +++ b/resource_definitions/RMovement.cs @@ -6,6 +6,8 @@ public partial class RMovement : Resource { [Export(PropertyHint.Range, "0,10,0.1,or_greater")] public float Speed; + [Export(PropertyHint.Range, "0,10,0.1,or_greater")] + public float Acceleration; [Export(PropertyHint.Range, "0,20,1,or_greater")] public float TargetHeight; @@ -13,11 +15,13 @@ public partial class RMovement : Resource public RMovement() { Speed = 3.0f; + Acceleration = 1.0f; TargetHeight = 10.0f; } - public RMovement(float speed, float targetHeight) + public RMovement(float speed, float acceleration, float targetHeight) { Speed = speed; + Acceleration = acceleration; TargetHeight = targetHeight; } } diff --git a/scenes/damage/CDamageable.cs b/scenes/damage/CDamageable.cs index 53158071..b1065df2 100644 --- a/scenes/damage/CDamageable.cs +++ b/scenes/damage/CDamageable.cs @@ -5,18 +5,19 @@ using Movementtests.interfaces; [GlobalClass] public partial class CDamageable : Node, IDamageable { - public event Action DamageTaken; + public event Action DamageTaken; [Export] public RDamageModifier[] DamageModifiers { get; set; } - public float TakeDamage(RDamage damage) + public DamageRecord TakeDamage(DamageRecord damageRecord) { var finalDamage = 0f; foreach (var damageable in DamageModifiers.ToIDamageables()) - finalDamage += damageable.TakeDamage(damage); - DamageTaken?.Invoke(this, finalDamage); - return finalDamage; + finalDamage += damageable.TakeDamage(damageRecord).Damage.DamageDealt; + var finalDamageRecord = damageRecord with { Damage = new RDamage(finalDamage, damageRecord.Damage.DamageType) }; + DamageTaken?.Invoke(this, finalDamageRecord); + return finalDamageRecord; } } diff --git a/scenes/enemies/Enemy.cs b/scenes/enemies/Enemy.cs index 4dedde43..6a315145 100644 --- a/scenes/enemies/Enemy.cs +++ b/scenes/enemies/Enemy.cs @@ -3,37 +3,53 @@ using Godot; using Movementtests.interfaces; [GlobalClass] -public partial class Enemy : CharacterBody3D, IDamageable, IDamageMaker, IHealthable, IKillable, IMoveable, ISpawnable +public partial class Enemy : CharacterBody3D, + IDamageable, + IDamageDealer, + IHealthable, + IKillable, + IMoveable, + ISpawnable, + IKnockbackable { - public event Action DamageTaken; + // Signals and events + public event Action DamageTaken; public event Action HealthChanged; public event Action HealthDepleted; + // Public export components [Export] public Node3D Target { get; set; } + [ExportGroup("Health")] [Export] public Node CHealth { get; set; } [Export] public RHealth RHealth { get; set; } - - public float CurrentHealth { get; set; } + [Export] + public RDeathEffect[] DeathEffects { get; set; } + + [ExportGroup("Damage")] [Export] public Node CDamage { get; set; } - [Export] public RDamage RDamage { get; set; } + [Export] + public Node CKnockback { get; set; } + [Export] + public RKnockback RKnockback { get; set; } + [ExportGroup("Movement")] [Export] public Node CMovement { get; set; } [Export] public RMovement RMovement { get; set; } - - - [Export] - public RDeathEffect[] DeathEffects { get; set; } + // Public stuff + public float CurrentHealth { get; set; } + + // Private stuff private Area3D _damageBox; public override void _Ready() @@ -52,12 +68,17 @@ public partial class Enemy : CharacterBody3D, IDamageable, IDamageMaker, IHealth healthable.RHealth = RHealth; healthable.CurrentHealth = RHealth.StartingHealth; } + if (CKnockback is IKnockbackable knockbackable && RKnockback != null) knockbackable.RKnockback = RKnockback; } public void SetupSignals() { _damageBox.BodyEntered += OnDamageBoxTriggered; - if (CDamage is IDamageable damageable) damageable.DamageTaken += ReduceHealth; + if (CDamage is IDamageable damageable) + { + damageable.DamageTaken += ReduceHealth; + damageable.DamageTaken += RegisterKnockback; + } if (CHealth is IHealthable healthable) healthable.HealthDepleted += Kill; } @@ -74,6 +95,7 @@ public partial class Enemy : CharacterBody3D, IDamageable, IDamageMaker, IHealth delta: delta ); Velocity = ComputeVelocity(inputs); + Velocity += ComputeKnockback(); MoveAndSlide(); } @@ -85,25 +107,28 @@ public partial class Enemy : CharacterBody3D, IDamageable, IDamageMaker, IHealth public void OnDamageBoxTriggered(Node3D body) { - GD.Print("Take this!"); if (body is not IDamageable damageable) return; - damageable.TakeDamage(RDamage); + + var damageRecord = new DamageRecord(this, RDamage); + damageable.TakeDamage(damageRecord); } - public float TakeDamage(RDamage damage) + public DamageRecord TakeDamage(DamageRecord damageRecord) { - GD.Print("Ouch!"); - if (CDamage is not IDamageable damageable) return 0; + if (CDamage is not IDamageable damageable) + return damageRecord with { Damage = new RDamage(0, damageRecord.Damage.DamageType) }; - var finalDamage = damageable.TakeDamage(damage); - DamageTaken?.Invoke(this, damage.DamageDealt); + var finalDamage = damageable.TakeDamage(damageRecord); + DamageTaken?.Invoke(this, finalDamage); + + GD.Print($"Received damage: {finalDamage.Damage.DamageDealt}"); return finalDamage; } - public void ReduceHealth(IDamageable source, float amount) + public void ReduceHealth(IDamageable source, DamageRecord damageRecord) { if (CHealth is not IHealthable healthable) return; - healthable.ReduceHealth(source, amount); + healthable.ReduceHealth(source, damageRecord); } public void Kill(IHealthable source) @@ -114,4 +139,16 @@ public partial class Enemy : CharacterBody3D, IDamageable, IDamageMaker, IHealth } QueueFree(); } + + public void RegisterKnockback(IDamageable source, DamageRecord damageRecord) + { + if (CKnockback is not IKnockbackable knockbackable) return; + knockbackable.RegisterKnockback(source, damageRecord); + } + + public Vector3 ComputeKnockback() + { + if (CKnockback is not IKnockbackable knockbackable) return Vector3.Zero; + return knockbackable.ComputeKnockback(); + } } diff --git a/scenes/enemies/grounded_enemy/grounded_enemy.tscn b/scenes/enemies/grounded_enemy/grounded_enemy.tscn index 1544064a..dddc570d 100644 --- a/scenes/enemies/grounded_enemy/grounded_enemy.tscn +++ b/scenes/enemies/grounded_enemy/grounded_enemy.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=17 format=3 uid="uid://dxt0e2ugmttqq"] +[gd_scene load_steps=19 format=3 uid="uid://dxt0e2ugmttqq"] [ext_resource type="Script" uid="uid://bn7sc6id7n166" path="res://scenes/enemies/Enemy.cs" id="1_r6506"] [ext_resource type="Resource" uid="uid://otfc2snh8umc" path="res://scenes/enemies/grounded_enemy/grounded_enemy_damage.tres" id="2_bn56u"] @@ -9,9 +9,12 @@ [ext_resource type="Script" uid="uid://b0u23nkpaimyc" path="res://scenes/damage/CDamageable.cs" id="7_1tw73"] [ext_resource type="PackedScene" uid="uid://dbr7ioio158ew" path="res://scenes/movement/CGroundedMovement.tscn" id="7_qyswd"] [ext_resource type="Script" uid="uid://dtpxijlnb2c5" path="res://resource_definitions/RMovement.cs" id="8_6d4gl"] +[ext_resource type="PackedScene" uid="uid://bctpe34ddamg5" path="res://scenes/knockback/CKnockback.tscn" id="10_jqqi6"] +[ext_resource type="Resource" uid="uid://cektf6waf4s04" path="res://scenes/enemies/grounded_enemy/grounded_enemy_knockback.tres" id="11_8k3xb"] [sub_resource type="Resource" id="Resource_qj0ob"] script = ExtResource("2_r3cnf") +Modifier = 3.0 metadata/_custom_type_script = "uid://b6y3ugfydvch0" [sub_resource type="Resource" id="Resource_6d4gl"] @@ -35,17 +38,19 @@ albedo_color = Color(0.06469653, 0.06469653, 0.06469653, 1) [sub_resource type="BoxShape3D" id="BoxShape3D_4yfjf"] size = Vector3(1, 2, 2) -[node name="GroundedEnemy" type="CharacterBody3D" node_paths=PackedStringArray("CHealth", "CDamage", "CMovement")] +[node name="GroundedEnemy" type="CharacterBody3D" node_paths=PackedStringArray("CHealth", "CDamage", "CKnockback", "CMovement")] collision_layer = 16 collision_mask = 273 script = ExtResource("1_r6506") CHealth = NodePath("CHealth") RHealth = ExtResource("2_w4lm8") +DeathEffects = Array[Object]([]) CDamage = NodePath("CDamageable") RDamage = ExtResource("2_bn56u") +CKnockback = NodePath("CKnockback") +RKnockback = ExtResource("11_8k3xb") CMovement = NodePath("CGroundedMovement") RMovement = ExtResource("4_na24f") -DeathEffects = Array[Object]([]) [node name="CHealth" type="Node" parent="."] script = ExtResource("2_gsmti") @@ -61,6 +66,8 @@ metadata/_custom_type_script = "uid://b0u23nkpaimyc" RMovement = SubResource("Resource_6d4gl") WallInFrontRayCast = NodePath("../WallInFrontRayCast") +[node name="CKnockback" parent="." instance=ExtResource("10_jqqi6")] + [node name="CollisionShape3D" type="CollisionShape3D" parent="."] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) shape = SubResource("CapsuleShape3D_62kkh") diff --git a/scenes/enemies/grounded_enemy/grounded_enemy_knockback.tres b/scenes/enemies/grounded_enemy/grounded_enemy_knockback.tres new file mode 100644 index 00000000..729c8f94 --- /dev/null +++ b/scenes/enemies/grounded_enemy/grounded_enemy_knockback.tres @@ -0,0 +1,8 @@ +[gd_resource type="Resource" script_class="RKnockback" load_steps=2 format=3 uid="uid://cektf6waf4s04"] + +[ext_resource type="Script" uid="uid://b44cse62qru7j" path="res://resource_definitions/RKnockback.cs" id="1_vdia8"] + +[resource] +script = ExtResource("1_vdia8") +Modifier = 100.0 +metadata/_custom_type_script = "uid://b44cse62qru7j" diff --git a/scenes/enemies/grounded_enemy/grounded_enemy_movement.tres b/scenes/enemies/grounded_enemy/grounded_enemy_movement.tres index 5e9d721e..fd7a1d24 100644 --- a/scenes/enemies/grounded_enemy/grounded_enemy_movement.tres +++ b/scenes/enemies/grounded_enemy/grounded_enemy_movement.tres @@ -5,4 +5,5 @@ [resource] script = ExtResource("1_hsy8g") Speed = 5.0 +Acceleration = 10.0 metadata/_custom_type_script = "uid://dtpxijlnb2c5" diff --git a/scenes/health/CHealth.cs b/scenes/health/CHealth.cs index 70445167..0a09a15e 100644 --- a/scenes/health/CHealth.cs +++ b/scenes/health/CHealth.cs @@ -18,10 +18,10 @@ public partial class CHealth : Node, IHealthable CurrentHealth = RHealth.StartingHealth; } - public void ReduceHealth(IDamageable source, float amount) + public void ReduceHealth(IDamageable source, DamageRecord damageRecord) { GD.Print(CurrentHealth); - CurrentHealth -= amount; + CurrentHealth -= damageRecord.Damage.DamageDealt; HealthChanged?.Invoke(this, CurrentHealth); if (CurrentHealth <= 0) diff --git a/scenes/knockback/CKnockback.cs b/scenes/knockback/CKnockback.cs new file mode 100644 index 00000000..71576010 --- /dev/null +++ b/scenes/knockback/CKnockback.cs @@ -0,0 +1,25 @@ +using Godot; +using System; +using Movementtests.interfaces; + +[GlobalClass] +public partial class CKnockback : Node3D, IKnockbackable +{ + [Export] public RKnockback RKnockback { get; set;} + + private DamageRecord _damageRecord = null; + + public void RegisterKnockback(IDamageable source, DamageRecord damageRecord) + { + _damageRecord = damageRecord; + } + + public Vector3 ComputeKnockback() + { + if (_damageRecord == null) return Vector3.Zero; + + var knockbackDirection = GlobalPosition - _damageRecord.Source.GlobalPosition; + _damageRecord = null; + return knockbackDirection.Normalized() * RKnockback.Modifier; + } +} diff --git a/scenes/knockback/CKnockback.cs.uid b/scenes/knockback/CKnockback.cs.uid new file mode 100644 index 00000000..6d4a814e --- /dev/null +++ b/scenes/knockback/CKnockback.cs.uid @@ -0,0 +1 @@ +uid://b8dprpcjeac7e diff --git a/scenes/knockback/CKnockback.tscn b/scenes/knockback/CKnockback.tscn new file mode 100644 index 00000000..d42c33dd --- /dev/null +++ b/scenes/knockback/CKnockback.tscn @@ -0,0 +1,12 @@ +[gd_scene load_steps=4 format=3 uid="uid://bctpe34ddamg5"] + +[ext_resource type="Script" uid="uid://b8dprpcjeac7e" path="res://scenes/knockback/CKnockback.cs" id="1_ix2yg"] +[ext_resource type="Script" uid="uid://b44cse62qru7j" path="res://resource_definitions/RKnockback.cs" id="2_uqiml"] + +[sub_resource type="Resource" id="Resource_gbu2d"] +script = ExtResource("2_uqiml") +metadata/_custom_type_script = "uid://b44cse62qru7j" + +[node name="CKnockback" type="Node3D"] +script = ExtResource("1_ix2yg") +RKnockback = SubResource("Resource_gbu2d") diff --git a/scenes/movement/CGroundedMovement.cs b/scenes/movement/CGroundedMovement.cs index 63712b2c..0c9523ef 100644 --- a/scenes/movement/CGroundedMovement.cs +++ b/scenes/movement/CGroundedMovement.cs @@ -19,9 +19,10 @@ public partial class CGroundedMovement : Node3D, IMoveable var targetPlane = new Vector3(target.X, GlobalPosition.Y, target.Z); LookAt(targetPlane); - - velocity.X = direction.X * RMovement.Speed; - velocity.Z = direction.Z * RMovement.Speed; + float xAcc = (float) Mathf.Lerp(velocity.X, direction.X * RMovement.Speed, inputs.delta * RMovement.Acceleration); + float zAcc = (float) Mathf.Lerp(velocity.Z, direction.Z * RMovement.Speed, inputs.delta * RMovement.Acceleration); + velocity.X = xAcc; + velocity.Z = zAcc; if (WallInFrontRayCast.IsColliding()) velocity.Y = RMovement.Speed;