diff --git a/Movement tests.sln.DotSettings.user b/Movement tests.sln.DotSettings.user index 87f55fae..5d33025d 100644 --- a/Movement tests.sln.DotSettings.user +++ b/Movement tests.sln.DotSettings.user @@ -1,5 +1,6 @@  ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded diff --git a/components/damage/CDamageable.cs b/components/damage/CDamageable.cs index b1065df2..4b6cabfb 100644 --- a/components/damage/CDamageable.cs +++ b/components/damage/CDamageable.cs @@ -20,4 +20,12 @@ public partial class CDamageable : Node, IDamageable DamageTaken?.Invoke(this, finalDamageRecord); return finalDamageRecord; } + + public DamageRecord ComputeDamage(DamageRecord damageRecord) + { + var finalDamage = 0f; + foreach (var damageable in DamageModifiers.ToIDamageables()) + finalDamage += damageable.ComputeDamage(damageRecord).Damage.DamageDealt; + return damageRecord with { Damage = new RDamage(finalDamage, damageRecord.Damage.DamageType) }; + } } diff --git a/components/damage/RDamageModifier.cs b/components/damage/RDamageModifier.cs index e5aaaf76..fc2d1676 100644 --- a/components/damage/RDamageModifier.cs +++ b/components/damage/RDamageModifier.cs @@ -30,4 +30,13 @@ public partial class RDamageModifier : Resource, IDamageable DamageTaken?.Invoke(this, finalDamageRecord); return finalDamageRecord; } + + public DamageRecord ComputeDamage(DamageRecord damageRecord) + { + if (damageRecord.Damage.DamageType != DamageType) + return damageRecord with { Damage = new RDamage(0, damageRecord.Damage.DamageType) }; + + var finalDamage = damageRecord.Damage.DamageDealt * Modifier; + return damageRecord with { Damage = new RDamage(finalDamage, damageRecord.Damage.DamageType) }; + } } diff --git a/components/knockback/CKnockback.cs b/components/knockback/CKnockback.cs index 71576010..72b62436 100644 --- a/components/knockback/CKnockback.cs +++ b/components/knockback/CKnockback.cs @@ -20,6 +20,7 @@ public partial class CKnockback : Node3D, IKnockbackable var knockbackDirection = GlobalPosition - _damageRecord.Source.GlobalPosition; _damageRecord = null; - return knockbackDirection.Normalized() * RKnockback.Modifier; + var finalKnockback = knockbackDirection.Normalized() * RKnockback.Modifier; + return finalKnockback; } } diff --git a/interfaces/IDamageable.cs b/interfaces/IDamageable.cs index c96e1e60..8fb7e335 100644 --- a/interfaces/IDamageable.cs +++ b/interfaces/IDamageable.cs @@ -9,4 +9,5 @@ public interface IDamageable { event Action DamageTaken; DamageRecord TakeDamage(DamageRecord damageRecord); + DamageRecord ComputeDamage(DamageRecord damageRecord); } \ No newline at end of file diff --git a/interfaces/IStunnable.cs b/interfaces/IStunnable.cs new file mode 100644 index 00000000..7041ebde --- /dev/null +++ b/interfaces/IStunnable.cs @@ -0,0 +1,12 @@ +using Godot; + +namespace Movementtests.interfaces; + +public interface IStunnable +{ + bool IsStunned { get; set; } + [Export] float StunDuration { get; set; } + + void Stun(); + void Unstun(); +} \ No newline at end of file diff --git a/interfaces/IStunnable.cs.uid b/interfaces/IStunnable.cs.uid new file mode 100644 index 00000000..cf2f933d --- /dev/null +++ b/interfaces/IStunnable.cs.uid @@ -0,0 +1 @@ +uid://cr8ify1hgetw6 diff --git a/maps/GYMs/enemies.tscn b/maps/GYMs/enemies.tscn index 521bb0ab..ba8a3f72 100644 --- a/maps/GYMs/enemies.tscn +++ b/maps/GYMs/enemies.tscn @@ -1,11 +1,11 @@ -[gd_scene load_steps=24 format=3 uid="uid://q7uc1h2jpbd2"] +[gd_scene load_steps=25 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="Script" uid="uid://jitubgv6judn" path="res://components/damage/RDamage.cs" id="2_sysok"] [ext_resource type="PackedScene" uid="uid://dxt0e2ugmttqq" path="res://scenes/enemies/grounded_enemy/grounded_enemy.tscn" id="3_3uydm"] [ext_resource type="Script" uid="uid://b4cwruitopcee" path="res://components/health/RDeathEffect.cs" id="5_7m3bq"] [ext_resource type="PackedScene" uid="uid://cmlud1hwkd6sv" path="res://scenes/enemies/flying_enemy/flying_enemy.tscn" id="5_8fd2t"] +[ext_resource type="Script" uid="uid://b44cse62qru7j" path="res://components/knockback/RKnockback.cs" id="6_1hrkh"] [ext_resource type="PackedScene" uid="uid://c305mfrtumcyq" path="res://scenes/spawners/spawner.tscn" id="6_7m3bq"] [ext_resource type="Script" uid="uid://dtpxijlnb2c5" path="res://components/movement/RMovement.cs" id="6_ybosk"] [ext_resource type="Resource" uid="uid://bqq6uukbdfysr" path="res://scenes/enemies/grounded_enemy/grounded_enemy_movement.tres" id="7_caohq"] @@ -14,10 +14,7 @@ [ext_resource type="Resource" uid="uid://dgo65k2ceqfvy" path="res://scenes/enemies/flying_enemy/flying_enemy_damage.tres" id="9_gp7s3"] [ext_resource type="Resource" uid="uid://bwqjaom4k7rc3" path="res://scenes/enemies/flying_enemy/flying_enemy_movement.tres" id="10_spw1u"] [ext_resource type="Resource" uid="uid://dg1xbjhyhgnnk" path="res://scenes/enemies/flying_enemy/flying_enemy_health.tres" id="11_2e4ci"] - -[sub_resource type="Resource" id="Resource_2e4ci"] -script = ExtResource("2_sysok") -metadata/_custom_type_script = "uid://jitubgv6judn" +[ext_resource type="PackedScene" uid="uid://qup00a7x2sji" path="res://scenes/FixedDashTarget/fixed_dashthrough_target.tscn" id="15_5fa36"] [sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_0xm2m"] sky_horizon_color = Color(0.662243, 0.671743, 0.686743, 1) @@ -45,6 +42,10 @@ glow_enabled = true script = ExtResource("5_7m3bq") metadata/_custom_type_script = "uid://b4cwruitopcee" +[sub_resource type="Resource" id="Resource_q21h6"] +script = ExtResource("6_1hrkh") +metadata/_custom_type_script = "uid://b44cse62qru7j" + [sub_resource type="Resource" id="Resource_5fa36"] script = ExtResource("6_ybosk") GravityModifier = 1.0 @@ -70,7 +71,6 @@ metadata/_custom_type_script = "uid://baiapod3csndf" [node name="Player" parent="." instance=ExtResource("1_62kkh")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 7.5) TutorialDone = true -RDamage = SubResource("Resource_2e4ci") [node name="WorldEnvironment" type="WorldEnvironment" parent="."] environment = SubResource("Environment_1bvp3") @@ -120,6 +120,12 @@ use_collision = true size = Vector3(6.5, 11, 5.5) material = ExtResource("2_3uydm") +[node name="CSGBox3D9" type="CSGBox3D" parent="Greybox"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 50.154, 0, -20.4585) +use_collision = true +size = Vector3(6.5, 2, 116) +material = ExtResource("2_3uydm") + [node name="CSGBox3D4" type="CSGBox3D" parent="Greybox"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 18.154, 4.5, -14.2085) use_collision = true @@ -136,16 +142,19 @@ material = ExtResource("2_3uydm") transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -16.83681) Target = NodePath("../Player") DeathEffects = Array[Object]([SubResource("Resource_sysok")]) +RKnockback = SubResource("Resource_q21h6") RMovement = SubResource("Resource_5fa36") [node name="Enemy2" parent="." node_paths=PackedStringArray("Target") instance=ExtResource("3_3uydm")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3, 0, -16.83681) Target = NodePath("../Player") +RKnockback = SubResource("Resource_q21h6") RMovement = SubResource("Resource_blhrq") [node name="Enemy3" parent="." node_paths=PackedStringArray("Target") instance=ExtResource("3_3uydm")] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.5, 0, -16.83681) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 7, 0, 0.16319084) Target = NodePath("../Player") +RKnockback = SubResource("Resource_q21h6") RMovement = SubResource("Resource_5fa36") [node name="FlyingEnemy" parent="." node_paths=PackedStringArray("Target") instance=ExtResource("5_8fd2t")] @@ -175,3 +184,6 @@ HealthInputs = ExtResource("11_2e4ci") DamageInputs = ExtResource("9_gp7s3") Target = NodePath("../Player") IsActiveOnStart = false + +[node name="FixedDashthroughTarget" parent="." instance=ExtResource("15_5fa36")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 3.5, 0) diff --git a/player_controller/PlayerController.tscn b/player_controller/PlayerController.tscn index 7c00a0ba..2351f368 100644 --- a/player_controller/PlayerController.tscn +++ b/player_controller/PlayerController.tscn @@ -1,9 +1,12 @@ -[gd_scene load_steps=58 format=3 uid="uid://bei4nhkf8lwdo"] +[gd_scene load_steps=64 format=3 uid="uid://bei4nhkf8lwdo"] [ext_resource type="Script" uid="uid://bbbrf5ckydfna" path="res://player_controller/Scripts/PlayerController.cs" id="1_poq2x"] [ext_resource type="PackedScene" uid="uid://cf3rrgr1imvv4" path="res://scenes/path/path.tscn" id="2_6lejt"] +[ext_resource type="Script" uid="uid://jitubgv6judn" path="res://components/damage/RDamage.cs" id="2_x835q"] +[ext_resource type="Script" uid="uid://b44cse62qru7j" path="res://components/knockback/RKnockback.cs" id="3_cb2lu"] [ext_resource type="Resource" uid="uid://bl5crtu1gkrtr" path="res://systems/inputs/base_mode/base_mode.tres" id="3_cresl"] [ext_resource type="PackedScene" uid="uid://c4ikbhojckpnc" path="res://components/health/CHealth.tscn" id="3_q7bng"] +[ext_resource type="Script" uid="uid://baiapod3csndf" path="res://components/health/RHealth.cs" id="4_abfq8"] [ext_resource type="Resource" uid="uid://bjyd801wvverk" path="res://player_controller/resources/player_health.tres" id="4_m8gvy"] [ext_resource type="Resource" uid="uid://cpdaw41ah5gic" path="res://systems/inputs/base_mode/rotate_y.tres" id="4_rxwoh"] [ext_resource type="Resource" uid="uid://ccrb5xsnphc8" path="res://systems/inputs/base_mode/rotate_floorplane.tres" id="5_4u7i3"] @@ -49,6 +52,21 @@ [ext_resource type="Script" uid="uid://b4dwolbvt8our" path="res://addons/godot_state_charts/history_state.gd" id="41_ruloh"] [ext_resource type="Texture2D" uid="uid://buu21kg4kkhiw" path="res://guide_examples/shared/fireball/fireball.svg" id="42_cmijs"] +[sub_resource type="Resource" id="Resource_cb2lu"] +script = ExtResource("2_x835q") +DamageDealt = 3.0 +metadata/_custom_type_script = "uid://jitubgv6judn" + +[sub_resource type="Resource" id="Resource_abfq8"] +script = ExtResource("3_cb2lu") +Modifier = 10.0 +metadata/_custom_type_script = "uid://b44cse62qru7j" + +[sub_resource type="Resource" id="Resource_ue7xq"] +script = ExtResource("4_abfq8") +StartingHealth = 10.0 +metadata/_custom_type_script = "uid://baiapod3csndf" + [sub_resource type="CapsuleMesh" id="CapsuleMesh_xc2g5"] height = 1.7 @@ -58,9 +76,6 @@ radius = 0.45 [sub_resource type="SphereShape3D" id="SphereShape3D_q14ux"] radius = 1.0 -[sub_resource type="SphereShape3D" id="SphereShape3D_cmijs"] -radius = 1.0 - [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_nodcl"] transparency = 1 albedo_color = Color(0, 0.627451, 0.6313726, 0.49019608) @@ -71,6 +86,9 @@ top_radius = 0.2 bottom_radius = 0.2 height = 1.0 +[sub_resource type="SphereShape3D" id="SphereShape3D_cmijs"] +radius = 1.0 + [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_6lejt"] radius = 1.0 height = 3.5 @@ -84,6 +102,9 @@ blend_mode = 1 [node name="Player" type="CharacterBody3D"] collision_mask = 272 script = ExtResource("1_poq2x") +RDamage = SubResource("Resource_cb2lu") +RKnockback = SubResource("Resource_abfq8") +RHealth = SubResource("Resource_ue7xq") WalkSpeed = 7.5 AccelerationFloor = 4.0 DecelerationFloor = 3.0 @@ -206,13 +227,6 @@ monitorable = false transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, -1.5) shape = SubResource("SphereShape3D_q14ux") -[node name="CloseEnemyDetector" type="ShapeCast3D" parent="."] -unique_name_in_owner = true -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.6, 0) -shape = SubResource("SphereShape3D_cmijs") -target_position = Vector3(0, 0, -5) -collision_mask = 16 - [node name="StairsSystem" type="Node3D" parent="."] script = ExtResource("7_bmt5a") @@ -307,6 +321,13 @@ visible = false transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0, 0, -1) mesh = SubResource("CylinderMesh_nodcl") +[node name="CloseEnemyDetector" type="ShapeCast3D" parent="."] +unique_name_in_owner = true +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.6, 0) +shape = SubResource("SphereShape3D_cmijs") +target_position = Vector3(0, 0, -5) +collision_mask = 48 + [node name="GroundDetector" type="ShapeCast3D" parent="."] shape = SubResource("CapsuleShape3D_6lejt") collision_mask = 256 @@ -446,7 +467,7 @@ layout_mode = 2 [node name="EnemyTarget" type="TextureRect" parent="UI"] unique_name_in_owner = true -modulate = Color(0.9882353, 0, 0.10980392, 1) +modulate = Color(0, 0.61278194, 0.56044877, 1) layout_mode = 1 anchors_preset = 8 anchor_left = 0.5 @@ -537,6 +558,49 @@ to = NodePath("../../AtLeastOneCharge") event = &"power_used" delay_in_seconds = "0.0" +[node name="Attack" type="Node" parent="StateChart/Root"] +script = ExtResource("26_infe6") +initial_state = NodePath("Ready") + +[node name="Ready" type="Node" parent="StateChart/Root/Attack"] +script = ExtResource("27_34snm") + +[node name="ToStandardAttack" type="Node" parent="StateChart/Root/Attack/Ready"] +script = ExtResource("28_n7qhm") +to = NodePath("../../StandardAttack") +event = &"standard_attack" +delay_in_seconds = "0.0" + +[node name="ToDashAttack" type="Node" parent="StateChart/Root/Attack/Ready"] +script = ExtResource("28_n7qhm") +to = NodePath("../../DashAttack") +event = &"dash_attack" +delay_in_seconds = "0.0" + +[node name="StandardAttack" type="Node" parent="StateChart/Root/Attack"] +script = ExtResource("27_34snm") + +[node name="ToReady" type="Node" parent="StateChart/Root/Attack/StandardAttack"] +script = ExtResource("28_n7qhm") +to = NodePath("../../Ready") +event = &"attack_finished" +delay_in_seconds = "0.0" + +[node name="ToDashAttack" type="Node" parent="StateChart/Root/Attack/StandardAttack"] +script = ExtResource("28_n7qhm") +to = NodePath("../../DashAttack") +event = &"dash_attack" +delay_in_seconds = "0.0" + +[node name="DashAttack" type="Node" parent="StateChart/Root/Attack"] +script = ExtResource("27_34snm") + +[node name="ToReady" type="Node" parent="StateChart/Root/Attack/DashAttack"] +script = ExtResource("28_n7qhm") +to = NodePath("../../Ready") +event = &"attack_finished" +delay_in_seconds = "0.0" + [node name="Movement" type="Node" parent="StateChart/Root"] script = ExtResource("26_infe6") initial_state = NodePath("Grounded") diff --git a/player_controller/PlayerUi.cs b/player_controller/PlayerUi.cs index 26b7691e..e5bf287f 100644 --- a/player_controller/PlayerUi.cs +++ b/player_controller/PlayerUi.cs @@ -11,10 +11,18 @@ public partial class PlayerUi : Control NoTarget, TargetTooFar, TargetInRange, + TargetDashThrough } public record TargetProperties(TargetState State, Vector2 Position); + [Export] + public Color DashThroughColor { get; set; } = new Color("009c8f"); + [Export] + public Color DashBlockedColor { get; set; } = new Color("fc001c"); + [Export] + public Color DashOutOfRangeColor { get; set; } = new Color("ffffff"); + public override void _Ready() { _dashIcons[0] = GetNode("%Dash1"); @@ -29,7 +37,14 @@ public partial class PlayerUi : Control var (state, position) = targetProperties; var visible = state != TargetState.NoTarget; - var modulation = state == TargetState.TargetInRange ? new Color("ffffff") : new Color("fc001c"); + + var modulation = state switch + { + TargetState.TargetTooFar => DashOutOfRangeColor, + TargetState.TargetInRange => DashBlockedColor, + TargetState.TargetDashThrough => DashThroughColor, + _ => DashOutOfRangeColor + }; _enemyTarget.SetVisible(visible); _enemyTarget.SetPosition(position - _enemyTarget.Size / 2); _enemyTarget.SetModulate(modulation); diff --git a/player_controller/Scripts/PlayerController.cs b/player_controller/Scripts/PlayerController.cs index f03d2cc0..d47e8efb 100644 --- a/player_controller/Scripts/PlayerController.cs +++ b/player_controller/Scripts/PlayerController.cs @@ -7,6 +7,7 @@ using Movementtests.addons.godot_state_charts.csharp; using Movementtests.interfaces; using Movementtests.systems; using Movementtests.player_controller.Scripts; +using Movementtests.systems.damage; using RustyOptions; public partial class PlayerController : CharacterBody3D, @@ -323,6 +324,11 @@ public partial class PlayerController : CharacterBody3D, private StateChartState _onWallHanging; private StateChartState _onWallRunning; + private StateChartState _attack; + private StateChartState _attackReady; + private StateChartState _attackStandard; + private StateChartState _attackDash; + private Transition _onDashEnded; private Transition _onJumpFromWall; @@ -340,7 +346,6 @@ public partial class PlayerController : CharacterBody3D, public float CurrentHealth { get; set; } private bool _isInvincible; - private bool _canAttack = true; private readonly List _hitEnemies = new List(); private ShapeCast3D _closeEnemyDetector; @@ -464,6 +469,12 @@ public partial class PlayerController : CharacterBody3D, _onLeaveWallFromRun = Transition.Of(GetNode("StateChart/Root/Movement/OnWall/Running/OnLeaveWall")); _onAirborneToGrounded = Transition.Of(GetNode("StateChart/Root/Movement/Airborne/OnGrounded")); + // Attack states + _attack = StateChartState.Of(GetNode("StateChart/Root/Attack")); + _attackReady = StateChartState.Of(GetNode("StateChart/Root/Attack/Ready")); + _attackStandard = StateChartState.Of(GetNode("StateChart/Root/Attack/StandardAttack")); + _attackDash = StateChartState.Of(GetNode("StateChart/Root/Attack/DashAttack")); + // State timers _powerCooldownTimer = GetNode("PowerCooldown"); _timeScaleAimInAirTimer = GetNode("TimeScaleAimInAir"); @@ -570,7 +581,11 @@ public partial class PlayerController : CharacterBody3D, _onJumpFromWall.Taken += OnJumpFromWall; _onJumpFromWallFalling.Taken += OnJumpFromWall; _onLeaveWallFromRun.Taken += OnLeaveWallFromRun; - _onAirborneToGrounded.Taken += OnAirborneToGrounded; + _onAirborneToGrounded.Taken += OnAirborneToGrounded; + + // Attack states + _attackStandard.StateEntered += OnStandardAttackStarted; + _attackDash.StateEntered += OnDashAttackStarted; } /////////////////////////// @@ -1705,9 +1720,13 @@ public partial class PlayerController : CharacterBody3D, } public void OnAimedDashFinished() { - var postDashVelocity = _preDashVelocity.Length() > PostDashSpeed ? PostDashSpeed : _preDashVelocity.Length(); + if (_customMantle) + { + _playerState.SendEvent("mantle"); + return; + } + var postDashVelocity = _preDashVelocity.Length() > PostDashSpeed ? _preDashVelocity.Length() : PostDashSpeed; Velocity = _dashDirection * postDashVelocity; - if (_customMantle) _playerState.SendEvent("mantle"); } // Weapon dashing @@ -1823,8 +1842,18 @@ public partial class PlayerController : CharacterBody3D, DashIndicatorNode.LookAt(WeaponSystem.GlobalPosition); } } + + /////////////////////////// + // Hit Management /////// + /////////////////////////// + + private bool _isEnemyInDashAttackRange; + private Vector3 _targetHitLocation; + private Vector3 _targetLocation; + private Object _targetObject; public void HandleEnemyTargeting() { + _isEnemyInDashAttackRange = false; _closeEnemyDetector.SetRotation(HeadSystem.GetGlobalLookRotation()); var enemyTargetState = PlayerUi.TargetState.NoTarget; @@ -1834,24 +1863,35 @@ public partial class PlayerController : CharacterBody3D, PlayerUi.SetEnemyTargetProperties(new PlayerUi.TargetProperties(enemyTargetState, positionOnScreen)); return; } - - var collidedObject = _closeEnemyDetector.GetCollider(0); - if (collidedObject is not ITargetable target) + + _targetHitLocation = _closeEnemyDetector.GetCollisionPoint(0); + _targetObject = _closeEnemyDetector.GetCollider(0); + if (_targetObject is not ITargetable target) { PlayerUi.SetEnemyTargetProperties(new PlayerUi.TargetProperties(enemyTargetState, positionOnScreen)); return; } - var targetPos = target.GetTargetGlobalPosition(); - var targetDistance = targetPos.DistanceTo(GlobalPosition); - enemyTargetState = targetDistance > TargetInRangeDistance ? PlayerUi.TargetState.TargetInRange : PlayerUi.TargetState.TargetTooFar; - positionOnScreen = _camera.UnprojectPosition(targetPos); + _targetLocation = target.GetTargetGlobalPosition(); + var targetDistance = _targetLocation.DistanceTo(GlobalPosition); + positionOnScreen = _camera.UnprojectPosition(_targetLocation); + + _isEnemyInDashAttackRange = targetDistance < TargetInRangeDistance; + if (_isEnemyInDashAttackRange) + { + enemyTargetState = PlayerUi.TargetState.TargetDashThrough; + if (_targetObject is IDamageable damageable && _targetObject is IHealthable healthable) + { + var wouldBeDamage = damageable.ComputeDamage(new DamageRecord(this, RDamage)); + if (wouldBeDamage.Damage.DamageDealt < healthable.CurrentHealth) + enemyTargetState = PlayerUi.TargetState.TargetInRange; + } + } + else enemyTargetState = PlayerUi.TargetState.TargetTooFar; + PlayerUi.SetEnemyTargetProperties(new PlayerUi.TargetProperties(enemyTargetState, positionOnScreen)); } - /////////////////////////// - // Hit Management /////// - /////////////////////////// public DamageRecord TakeDamage(DamageRecord damageRecord) { if (_isInvincible) @@ -1868,11 +1908,71 @@ public partial class PlayerController : CharacterBody3D, return finalDamage; } + public DamageRecord ComputeDamage(DamageRecord damageRecord) + { + return CDamageable.ComputeDamage(damageRecord); + } + public void OnHitInvincibility() { _isInvincible = true; _invincibilityTimer.Start(); } + + public void OnStandardAttackStarted() + { + _attackCooldown.Start(); + HeadSystem.OnHit(); + _audioStream!.SwitchToClipByName("attacks"); + } + + public void OnDashAttackStarted() + { + _audioStream!.SwitchToClipByName("attacks"); + + _isInvincible = true; + + var actualDashLocation = _targetLocation + Vector3.Down*HeadSystem.Position.Y; + var travel = actualDashLocation - GlobalPosition; + _preDashVelocity = Velocity; + _dashDirection = travel.Normalized(); + var dashTween = CreatePositionTween(actualDashLocation, AimedDashTime); + dashTween.Finished += OnDashAttackEnded; + } + + public void OnDashAttackEnded() + { + if (_targetObject is IDamageable damageable) + { + _hitEnemies.Add(damageable); + TriggerDamage(); + } + + if (_targetObject is IStunnable stunnable) + { + stunnable.Stun(); + } + + var shouldKnockback = false; + if (_targetObject is IHealthable healthable) + { + if (healthable.CurrentHealth > 0) shouldKnockback = true; + } + + if (shouldKnockback) + { + Velocity = -_dashDirection*RKnockback.Modifier; + } + else + { + var locationOtherSide = _targetLocation + (_targetLocation - _targetHitLocation); + GlobalPosition = locationOtherSide; + var postDashVelocity = _preDashVelocity.Length() > PostDashSpeed ? _preDashVelocity.Length() : PostDashSpeed; + Velocity = _dashDirection * postDashVelocity; + } + _isInvincible = false; + _playerState.SendEvent("attack_finished"); + } public void OnInputHitPressed() { @@ -1881,24 +1981,15 @@ public partial class PlayerController : CharacterBody3D, ThrowWeapon(); } - if (!WeaponSystem.InHandState.Active) return; - if (!_canAttack) return; - - _canAttack = false; - _attackCooldown.Start(); - PerformHit(); + var attackToDo = _isEnemyInDashAttackRange ? "dash_attack" : "standard_attack"; + _playerState.SendEvent(attackToDo); } public void ResetAttackCooldown() { - _canAttack = true; + _playerState.SendEvent("attack_finished"); } - public void PerformHit() - { - HeadSystem.OnHit(); - _audioStream!.SwitchToClipByName("attacks"); - } public void OnHitboxActivated() { @@ -1942,6 +2033,7 @@ public partial class PlayerController : CharacterBody3D, } public void ReduceHealth(IDamageable source, DamageRecord damageRecord) { + GD.Print("That's NOT fine"); CHealth.ReduceHealth(source, damageRecord); HealthChanged?.Invoke(this, CHealth.CurrentHealth); } @@ -1952,7 +2044,8 @@ public partial class PlayerController : CharacterBody3D, public Vector3 ComputeKnockback() { - return CKnockback.ComputeKnockback(); + var kb = CKnockback.ComputeKnockback(); + return kb; } public void Kill(IHealthable source) diff --git a/project.godot b/project.godot index f6026e96..6020d088 100644 --- a/project.godot +++ b/project.godot @@ -159,6 +159,7 @@ locale/translations=PackedStringArray("res://addons/maaacks_game_template/base/t 3d_physics/layer_3="3" 3d_physics/layer_4="4" 3d_physics/layer_5="enemies" +3d_physics/layer_6="InteractiveGeo" 3d_physics/layer_9="terrain" 3d_physics/layer_17="weapon" diff --git a/scenes/FixedDashTarget/FixedDashthroughTarget.cs b/scenes/FixedDashTarget/FixedDashthroughTarget.cs new file mode 100644 index 00000000..51da84be --- /dev/null +++ b/scenes/FixedDashTarget/FixedDashthroughTarget.cs @@ -0,0 +1,31 @@ +using Godot; +using System; +using Movementtests.interfaces; + +[GlobalClass] +public partial class FixedDashthroughTarget : AnimatableBody3D, ITargetable, IStunnable +{ + public Vector3 GetTargetGlobalPosition() + { + return GlobalPosition; + } + + private uint _defaultCollisionMask; + public override void _Ready() + { + _defaultCollisionMask = CollisionMask; + } + + public bool IsStunned { get; set; } + public float StunDuration { get; set; } = 0.1f; + public void Stun() + { + _defaultCollisionMask = 0; + GetTree().CreateTimer(StunDuration).Timeout += Unstun; + } + + public void Unstun() + { + _defaultCollisionMask = CollisionMask; + } +} diff --git a/scenes/FixedDashTarget/FixedDashthroughTarget.cs.uid b/scenes/FixedDashTarget/FixedDashthroughTarget.cs.uid new file mode 100644 index 00000000..786e3db6 --- /dev/null +++ b/scenes/FixedDashTarget/FixedDashthroughTarget.cs.uid @@ -0,0 +1 @@ +uid://c10qfkvmrm6uq diff --git a/scenes/FixedDashTarget/fixed_dashthrough_target.tscn b/scenes/FixedDashTarget/fixed_dashthrough_target.tscn new file mode 100644 index 00000000..f77325cf --- /dev/null +++ b/scenes/FixedDashTarget/fixed_dashthrough_target.tscn @@ -0,0 +1,30 @@ +[gd_scene load_steps=5 format=3 uid="uid://qup00a7x2sji"] + +[ext_resource type="Script" uid="uid://c10qfkvmrm6uq" path="res://scenes/FixedDashTarget/FixedDashthroughTarget.cs" id="1_r0j7a"] + +[sub_resource type="SphereShape3D" id="SphereShape3D_nkm8n"] +radius = 1.0 + +[sub_resource type="SphereMesh" id="SphereMesh_r0j7a"] +radius = 1.0 +height = 2.0 + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_kgb3i"] +albedo_color = Color(5.1018596e-06, 0.6818665, 0.7627612, 1) +metallic = 0.8 +metallic_specular = 0.6 +roughness = 0.1 +emission_enabled = true +emission = Color(0, 0.68968636, 0.7473501, 1) + +[node name="FixedDashthroughTarget" type="AnimatableBody3D"] +collision_layer = 288 +collision_mask = 0 +script = ExtResource("1_r0j7a") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +shape = SubResource("SphereShape3D_nkm8n") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +mesh = SubResource("SphereMesh_r0j7a") +surface_material_override/0 = SubResource("StandardMaterial3D_kgb3i") diff --git a/scenes/enemies/Enemy.cs b/scenes/enemies/Enemy.cs index b7d80fbd..868b40a7 100644 --- a/scenes/enemies/Enemy.cs +++ b/scenes/enemies/Enemy.cs @@ -11,7 +11,8 @@ public partial class Enemy : CharacterBody3D, IMoveable, ISpawnable, IKnockbackable, - ITargetable + ITargetable, + IStunnable { // Signals and events public event Action DamageTaken; @@ -44,10 +45,15 @@ public partial class Enemy : CharacterBody3D, public IMoveable CMovement { get; set; } // Public stuff - public float CurrentHealth { get; set; } + public float CurrentHealth + { + get => CHealth.CurrentHealth; + set => CHealth.CurrentHealth = value; + } // Private stuff private Area3D _damageBox; + private Node3D _target; public override void _Ready() { @@ -58,6 +64,7 @@ public partial class Enemy : CharacterBody3D, public void Initialize() { _damageBox = GetNode("DamageBox"); + _target = GetNode("CTarget"); CDamageable = GetNode("CDamageable") as IDamageable; CMovement = GetNode("CMovement") as IMoveable; @@ -106,6 +113,8 @@ public partial class Enemy : CharacterBody3D, public void ProcessGameplay(double delta) { + if (IsStunned) return; + var bodies = _damageBox.GetOverlappingBodies(); foreach (var body in bodies) { @@ -130,6 +139,14 @@ public partial class Enemy : CharacterBody3D, return finalDamage; } + public DamageRecord ComputeDamage(DamageRecord damageRecord) + { + if (CDamageable is null) + return damageRecord with { Damage = new RDamage(0, damageRecord.Damage.DamageType) }; + + return CDamageable.ComputeDamage(damageRecord); + } + public void ReduceHealth(IDamageable source, DamageRecord damageRecord) { if (CHealth is null) return; @@ -160,8 +177,22 @@ public partial class Enemy : CharacterBody3D, public Vector3 GetTargetGlobalPosition() { - var target = GetNode("CTarget"); - if (target is null) return GlobalPosition; - return target.GlobalPosition; + if (_target is null) return GlobalPosition; + return _target.GlobalPosition; + } + + // Stun management + public bool IsStunned { get; set; } = false; + + [Export(PropertyHint.Range, "0.1, 2, 0.1, or_greater")] + public float StunDuration { get; set; } = 1f; + public void Stun() + { + IsStunned = true; + GetTree().CreateTimer(StunDuration).Timeout += Unstun; + } + public void Unstun() + { + IsStunned = false; } } diff --git a/scenes/enemies/flying_enemy/flying_enemy.tscn b/scenes/enemies/flying_enemy/flying_enemy.tscn index 9c39874c..9f726774 100644 --- a/scenes/enemies/flying_enemy/flying_enemy.tscn +++ b/scenes/enemies/flying_enemy/flying_enemy.tscn @@ -42,19 +42,15 @@ albedo_color = Color(0.06469653, 0.06469653, 0.06469653, 1) [sub_resource type="BoxShape3D" id="BoxShape3D_4yfjf"] -[node name="FlyingEnemy" type="CharacterBody3D" node_paths=PackedStringArray("CHealth", "CDamage", "CKnockback", "CMovement")] +[node name="FlyingEnemy" type="CharacterBody3D"] collision_layer = 16 collision_mask = 273 motion_mode = 1 script = ExtResource("1_q8l7o") -CHealth = NodePath("CHealth") RHealth = ExtResource("2_ma2bq") DeathEffects = Array[Object]([]) -CDamage = NodePath("CDamageable") RDamage = ExtResource("2_on7rt") -CKnockback = NodePath("CKnockback") RKnockback = ExtResource("11_mpa2u") -CMovement = NodePath("CMovement") RMovement = ExtResource("4_dejyg") [node name="CHealth" type="Node" parent="."] @@ -74,6 +70,8 @@ TerrainCollision = 256 [node name="CKnockback" parent="." instance=ExtResource("10_dejyg")] RKnockback = ExtResource("11_mpa2u") +[node name="CTarget" type="Marker3D" parent="."] + [node name="CollisionShape3D" type="CollisionShape3D" parent="."] shape = SubResource("SphereShape3D_b46rq") diff --git a/scenes/enemies/grounded_enemy/grounded_enemy.tscn b/scenes/enemies/grounded_enemy/grounded_enemy.tscn index 6bc10910..9bad7043 100644 --- a/scenes/enemies/grounded_enemy/grounded_enemy.tscn +++ b/scenes/enemies/grounded_enemy/grounded_enemy.tscn @@ -14,7 +14,7 @@ [sub_resource type="Resource" id="Resource_qj0ob"] script = ExtResource("2_r3cnf") -Modifier = 3.0 +Modifier = 1.0 metadata/_custom_type_script = "uid://b6y3ugfydvch0" [sub_resource type="Resource" id="Resource_6d4gl"] diff --git a/scenes/enemies/grounded_enemy/grounded_enemy_health.tres b/scenes/enemies/grounded_enemy/grounded_enemy_health.tres index 076bfa41..c39cffba 100644 --- a/scenes/enemies/grounded_enemy/grounded_enemy_health.tres +++ b/scenes/enemies/grounded_enemy/grounded_enemy_health.tres @@ -4,4 +4,5 @@ [resource] script = ExtResource("1_h6jgd") +StartingHealth = 10.0 metadata/_custom_type_script = "uid://baiapod3csndf"