diff --git a/Autoloads/DamageNumberManager.cs b/Autoloads/DamageNumberManager.cs new file mode 100644 index 0000000..39df530 --- /dev/null +++ b/Autoloads/DamageNumberManager.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using Godot; +using Mr.BrickAdventures.scripts.components; +using Mr.BrickAdventures.scripts.UI; + +namespace Mr.BrickAdventures.Autoloads; + +public partial class DamageNumberManager : Node +{ + [Export] public PackedScene DamageNumberScene { get; set; } + + private readonly List _managedNodes = []; + + public void Register(Node node) + { + if (_managedNodes.Contains(node)) return; + + var healthComponent = node.GetNodeOrNull("HealthComponent"); + if (healthComponent == null) + { + GD.PrintErr($"Node '{node.Name}' tried to register with DamageNumberManager but has no HealthComponent."); + return; + } + + healthComponent.HealthChanged += (delta, total) => OnHealthChanged(healthComponent, delta); + node.TreeExiting += () => Unregister(node); + _managedNodes.Add(node); + } + + public void Unregister(Node node) + { + var healthComponent = node.GetNodeOrNull("HealthComponent"); + if (healthComponent != null) + { + healthComponent.HealthChanged -= (delta, _) => OnHealthChanged(healthComponent, delta); + } + _managedNodes.Remove(node); + } + + private void OnHealthChanged(HealthComponent healthComponent, float delta) + { + if (delta >= 0) return; + + if (DamageNumberScene == null) + { + GD.PrintErr("DamageNumberManager: DamageNumberScene is not set!"); + return; + } + + var damageNumber = DamageNumberScene.Instantiate(); + GetTree().CurrentScene.AddChild(damageNumber); + + var position = healthComponent.GlobalPosition; + damageNumber.ShowDamage(Mathf.Abs(delta), position); + } +} \ No newline at end of file diff --git a/Autoloads/DamageNumberManager.cs.uid b/Autoloads/DamageNumberManager.cs.uid new file mode 100644 index 0000000..c344901 --- /dev/null +++ b/Autoloads/DamageNumberManager.cs.uid @@ -0,0 +1 @@ +uid://i06td076ej68 diff --git a/objects/damage_number_manager.tscn b/objects/damage_number_manager.tscn new file mode 100644 index 0000000..7b086f0 --- /dev/null +++ b/objects/damage_number_manager.tscn @@ -0,0 +1,8 @@ +[gd_scene load_steps=3 format=3 uid="uid://dpibw6s3dcggr"] + +[ext_resource type="Script" uid="uid://i06td076ej68" path="res://Autoloads/DamageNumberManager.cs" id="1_me007"] +[ext_resource type="PackedScene" uid="uid://c7mp0o2goauyy" path="res://objects/ui/damage_number.tscn" id="2_ghslv"] + +[node name="DamageNumberManager" type="Node"] +script = ExtResource("1_me007") +DamageNumberScene = ExtResource("2_ghslv") diff --git a/objects/entities/brick_player.tscn b/objects/entities/brick_player.tscn index 5776ba7..d34955e 100644 --- a/objects/entities/brick_player.tscn +++ b/objects/entities/brick_player.tscn @@ -216,10 +216,11 @@ Sprite = NodePath("../Graphics/Root/Right Eye") FlashDuration = 1.0 HealthComponent = NodePath("../HealthComponent") -[node name="StompDamageComponent" type="Node" parent="." node_paths=PackedStringArray("Area")] +[node name="StompDamageComponent" type="Node" parent="." node_paths=PackedStringArray("Area", "Root")] script = ExtResource("17_bl1gx") Damage = 4.0 Area = NodePath("../StompDamageArea") +Root = NodePath("..") [node name="SkillManager" type="Node" parent="."] script = ExtResource("18_6lsog") diff --git a/objects/ui/damage_number.tscn b/objects/ui/damage_number.tscn new file mode 100644 index 0000000..1275a90 --- /dev/null +++ b/objects/ui/damage_number.tscn @@ -0,0 +1,15 @@ +[gd_scene load_steps=2 format=3 uid="uid://c7mp0o2goauyy"] + +[ext_resource type="Script" uid="uid://bbupymh6krrgx" path="res://scripts/UI/DamageNumber.cs" id="1_yv42p"] + +[node name="DamageNumber" type="Label"] +offset_right = 40.0 +offset_bottom = 8.0 +theme_override_constants/outline_size = 1 +theme_override_constants/shadow_outline_size = 3 +theme_override_font_sizes/font_size = 8 +text = "23" +horizontal_alignment = 1 +vertical_alignment = 1 +script = ExtResource("1_yv42p") +metadata/_custom_type_script = "uid://bbupymh6krrgx" diff --git a/project.godot b/project.godot index e7dceb7..2043fbf 100644 --- a/project.godot +++ b/project.godot @@ -40,6 +40,7 @@ LimboConsole="*res://addons/limbo_console/limbo_console.gd" DialogueManager="*res://addons/dialogue_manager/dialogue_manager.gd" SteamManager="*res://Autoloads/SteamManager.cs" AchievementManager="*res://objects/achievement_manager.tscn" +DamageNumberManager="*res://objects/damage_number_manager.tscn" [debug] diff --git a/scenes/level_village_4.tscn b/scenes/level_village_4.tscn index d57dbb1..13a9489 100644 --- a/scenes/level_village_4.tscn +++ b/scenes/level_village_4.tscn @@ -197,10 +197,6 @@ process_mode = 4 [node name="HitParticles" parent="Brick Player" index="26"] process_material = SubResource("ParticleProcessMaterial_lgb3u") -[node name="ProgressiveDamageComponent" parent="Brick Player" index="31"] -MinJumpHeight = null -JumpReductionPercentage = null - [node name="Enemies" type="Node2D" parent="."] [node name="Flying Enemy" parent="Enemies" instance=ExtResource("18_162yw")] diff --git a/scripts/UI/DamageNumber.cs b/scripts/UI/DamageNumber.cs new file mode 100644 index 0000000..5c6b4e9 --- /dev/null +++ b/scripts/UI/DamageNumber.cs @@ -0,0 +1,41 @@ +using System.Globalization; +using Godot; + +namespace Mr.BrickAdventures.scripts.UI; + +[GlobalClass] +public partial class DamageNumber : Label +{ + [Export] public float Duration { get; set; } = 0.8f; + [Export] public float FallDistance { get; set; } = 40f; + [Export] public float HorizontalDrift { get; set; } = 15f; + + public void ShowDamage(float damageAmount, Vector2 position) + { + Text = Mathf.Round(damageAmount * 100f).ToString(CultureInfo.InvariantCulture); + GlobalPosition = position; + + var rng = new RandomNumberGenerator(); + var horizontalOffset = rng.RandfRange(-HorizontalDrift, HorizontalDrift); + + var startPosition = GlobalPosition; + var endPosition = GlobalPosition + new Vector2(horizontalOffset, FallDistance); + + var startColor = Colors.White; + startColor.A = 1f; + Modulate = startColor; + + var tween = CreateTween(); + tween.SetParallel(); + + tween.TweenProperty(this, "global_position", endPosition, Duration) + .SetTrans(Tween.TransitionType.Quad) + .SetEase(Tween.EaseType.In); + + tween.Chain().TweenProperty(this, "modulate:a", 0f, Duration * 0.5f) + .SetTrans(Tween.TransitionType.Sine) + .SetEase(Tween.EaseType.Out); + + tween.TweenCallback(Callable.From(QueueFree)); + } +} \ No newline at end of file diff --git a/scripts/UI/DamageNumber.cs.uid b/scripts/UI/DamageNumber.cs.uid new file mode 100644 index 0000000..ae931ba --- /dev/null +++ b/scripts/UI/DamageNumber.cs.uid @@ -0,0 +1 @@ +uid://bbupymh6krrgx diff --git a/scripts/components/HealthComponent.cs b/scripts/components/HealthComponent.cs index 34fddb3..49f9f7c 100644 --- a/scripts/components/HealthComponent.cs +++ b/scripts/components/HealthComponent.cs @@ -1,5 +1,6 @@ using System.Threading.Tasks; using Godot; +using Mr.BrickAdventures.Autoloads; namespace Mr.BrickAdventures.scripts.components; @@ -12,7 +13,15 @@ public partial class HealthComponent : Node2D [Signal] public delegate void HealthChangedEventHandler(float delta, float totalHealth); [Signal] public delegate void DeathEventHandler(); + + private DamageNumberManager _damageNumberManager; + public override void _Ready() + { + _damageNumberManager = GetNode("/root/DamageNumberManager"); + _damageNumberManager?.Register(Owner); + } + public void SetHealth(float newValue) { _ = ApplyHealthChange(newValue);