leveling up
							parent
							
								
									a07642c3af
								
							
						
					
					
						commit
						385ed4ffdb
					
				|  | @ -11,6 +11,7 @@ public partial class Hurtbox : BoundingBox, IFaction | |||
|         float damage, | ||||
|         Character inflictor, | ||||
|         float knockback, | ||||
|         Items.Weapon weapon = null, | ||||
|         Vector2 knockbackDir = default); | ||||
| 
 | ||||
|     /// <summary> | ||||
|  | @ -39,6 +40,7 @@ public partial class Hurtbox : BoundingBox, IFaction | |||
|         float damage, | ||||
|         Character inflictor, | ||||
|         float knockback, | ||||
|         Items.Weapon weapon = default, | ||||
|         Vector2 knockbackOrigin = default, | ||||
|         Vector2 knockbackVector = default) | ||||
|     { | ||||
|  | @ -83,6 +85,7 @@ public partial class Hurtbox : BoundingBox, IFaction | |||
|             damage, | ||||
|             inflictor, | ||||
|             knockback, | ||||
|             weapon, | ||||
|             knockbackDir); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -308,6 +308,7 @@ public partial class Character : CharacterBody2D, IFaction | |||
|         float damage, | ||||
|         Character inflictor, | ||||
|         float knockback, | ||||
|         Weapon weapon = null, | ||||
|         Vector2 knockbackDir = default) | ||||
|     { | ||||
|         if (Health <= 0) | ||||
|  | @ -352,11 +353,17 @@ public partial class Character : CharacterBody2D, IFaction | |||
|             Attacker = inflictor, | ||||
|             OldHealth = oldHealth, | ||||
|             NewHealth = Health, | ||||
|             Weapon = weapon, | ||||
|             Damage = damage, | ||||
|         }; | ||||
| 
 | ||||
|         EmitSignal(SignalName.Hurt, args); | ||||
| 
 | ||||
|         if (inflictor is Player) | ||||
|         { | ||||
|             EmitPlayerHitSignal(args); | ||||
|         } | ||||
| 
 | ||||
|         if (Health <= 0) | ||||
|         { | ||||
|             EmitSignal(SignalName.Death, args); | ||||
|  | @ -366,6 +373,25 @@ public partial class Character : CharacterBody2D, IFaction | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// <summary> | ||||
|     /// Converts a HurtArgs to HitArgs if the attacker was a player and emits | ||||
|     /// the <c>EventBus.PlayerHit</c> signal. | ||||
|     /// </summary> | ||||
|     private void EmitPlayerHitSignal(Events.HurtArgs args) | ||||
|     { | ||||
|         var newArgs = new Events.HitArgs | ||||
|         { | ||||
|             OldHealth = args.OldHealth, | ||||
|             NewHealth = args.NewHealth, | ||||
|             Damage = args.Damage, | ||||
|             Weapon = args.Weapon, | ||||
|             Victim = this, | ||||
|         }; | ||||
| 
 | ||||
|         var bus = Events.EventBus.Instance; | ||||
|         bus.EmitSignal(Events.EventBus.SignalName.PlayerHit, newArgs); | ||||
|     } | ||||
| 
 | ||||
| #if DEBUG | ||||
|     /// <summary> | ||||
|     /// For debugging purposes | ||||
|  |  | |||
|  | @ -81,6 +81,7 @@ public sealed partial class Player : Character | |||
|         float damage, | ||||
|         Character inflictor, | ||||
|         float knockback, | ||||
|         Items.Weapon weapon = null, | ||||
|         Vector2 knockbackDir = default) | ||||
|     { | ||||
|         if (damage >= 10 && IsAlive) | ||||
|  | @ -91,7 +92,11 @@ public sealed partial class Player : Character | |||
|         GetNode<GpuParticles2D>("Effects/HurtParticles") | ||||
|             .SetDirection(knockbackDir); | ||||
| 
 | ||||
|         base.OnReceivedDamage(damage, inflictor, knockback, knockbackDir); | ||||
|         base.OnReceivedDamage(damage, | ||||
|             inflictor, | ||||
|             knockback, | ||||
|             weapon, | ||||
|             knockbackDir); | ||||
|     } | ||||
| 
 | ||||
|     public override void Die() | ||||
|  |  | |||
|  | @ -1,12 +1,15 @@ | |||
| [gd_scene load_steps=63 format=3 uid="uid://b2254pup8k161"] | ||||
| [gd_scene load_steps=66 format=3 uid="uid://b2254pup8k161"] | ||||
| 
 | ||||
| [ext_resource type="Script" path="res://Characters/Player.cs" id="1_flygr"] | ||||
| [ext_resource type="Shader" path="res://Shaders/Flash.gdshader" id="2_ngsgt"] | ||||
| [ext_resource type="Texture2D" uid="uid://dpepm54hjuyga" path="res://Assets/Sprites/Characters/forsen-hand.png" id="3_3dqh7"] | ||||
| [ext_resource type="Texture2D" uid="uid://bej8thq7ruyty" path="res://Assets/Sprites/Characters/forsen2.png" id="4_5vird"] | ||||
| [ext_resource type="Script" path="res://Utils/PlayerStats.cs" id="4_06oya"] | ||||
| [ext_resource type="PackedScene" uid="uid://cl56eadpklnbo" path="res://Utils/PlayerCamera.tscn" id="4_ym125"] | ||||
| [ext_resource type="Script" path="res://State/Character/CharacterStateMachine.cs" id="5_rgckv"] | ||||
| [ext_resource type="Script" path="res://Utils/Values/DoubleValue.cs" id="5_txl0r"] | ||||
| [ext_resource type="Script" path="res://State/Character/CharacterDashState.cs" id="6_rft7p"] | ||||
| [ext_resource type="Script" path="res://Utils/Values/IntValue.cs" id="6_sunc5"] | ||||
| [ext_resource type="Script" path="res://State/Character/PlayerIdleState.cs" id="6_wkfdm"] | ||||
| [ext_resource type="PackedScene" uid="uid://dvqap2uhcah63" path="res://Items/Weapons/Sword.tscn" id="7_4rxuv"] | ||||
| [ext_resource type="Script" path="res://State/Character/PlayerMoveState.cs" id="7_dfqd8"] | ||||
|  | @ -331,6 +334,15 @@ StateMachine = NodePath("StateMachine") | |||
| Hurtbox = NodePath("Hurtbox") | ||||
| Faction = 1 | ||||
| 
 | ||||
| [node name="Stats" type="Node" parent="."] | ||||
| script = ExtResource("4_06oya") | ||||
| 
 | ||||
| [node name="XP" type="Node" parent="Stats"] | ||||
| script = ExtResource("5_txl0r") | ||||
| 
 | ||||
| [node name="Level" type="Node" parent="Stats"] | ||||
| script = ExtResource("6_sunc5") | ||||
| 
 | ||||
| [node name="StateMachine" type="Node" parent="." node_paths=PackedStringArray("InitialState", "Character")] | ||||
| script = ExtResource("5_rgckv") | ||||
| InitialState = NodePath("Idle") | ||||
|  |  | |||
|  | @ -43,8 +43,11 @@ public partial class Projectile : RigidBody2D | |||
|     [Export] | ||||
|     public double Delay { get; set; } = 0; | ||||
| 
 | ||||
|     [System.Obsolete] | ||||
|     public Character Character { get; set; } | ||||
| 
 | ||||
|     public Items.Weapon Weapon { get; set; } | ||||
| 
 | ||||
|     public bool IsDead { get; set; } | ||||
| 
 | ||||
|     public override void _Ready() | ||||
|  | @ -87,8 +90,9 @@ public partial class Projectile : RigidBody2D | |||
|         { | ||||
|             hurtbox.InflictDamage( | ||||
|                 Hitbox.Damage, | ||||
|                 Character, | ||||
|                 Hitbox.Inflictor, | ||||
|                 Hitbox.Knockback, | ||||
|                 weapon: Weapon, | ||||
|                 knockbackVector: Direction | ||||
|             ); | ||||
|             EmitSignal(SignalName.Hit, box); | ||||
|  |  | |||
|  | @ -63,6 +63,7 @@ public partial class ShungiteSpike : Projectile | |||
|         float damage, | ||||
|         Characters.Character inflictor, | ||||
|         float knockback, | ||||
|         Items.Weapon weapon, | ||||
|         Vector2 knockbackDir) | ||||
|     { | ||||
|         // if we were hit by the player before the spike freezes, | ||||
|  |  | |||
|  | @ -4,6 +4,8 @@ namespace SupaLidlGame.Events; | |||
| 
 | ||||
| public partial class EventBus : Node | ||||
| { | ||||
|     public static EventBus Instance { get; private set; } | ||||
| 
 | ||||
|     [Signal] | ||||
|     public delegate void RequestMoveToAreaEventHandler(RequestAreaArgs args); | ||||
| 
 | ||||
|  | @ -16,6 +18,15 @@ public partial class EventBus : Node | |||
|     [Signal] | ||||
|     public delegate void PlayerHurtEventHandler(HurtArgs args); | ||||
| 
 | ||||
|     [Signal] | ||||
|     public delegate void PlayerHitEventHandler(HitArgs args); | ||||
| 
 | ||||
|     [Signal] | ||||
|     public delegate void PlayerXPChangedEventHandler(double xp); | ||||
| 
 | ||||
|     [Signal] | ||||
|     public delegate void PlayerLevelChangedEventHandler(int level); | ||||
| 
 | ||||
|     [Signal] | ||||
|     public delegate void PlayerHealthChangedEventHandler(HealthChangedArgs args); | ||||
| 
 | ||||
|  | @ -37,5 +48,10 @@ public partial class EventBus : Node | |||
|     public override void _Ready() | ||||
|     { | ||||
|         ProcessMode = ProcessModeEnum.Always; | ||||
|         if (Instance is not null) | ||||
|         { | ||||
|             throw new MultipleSingletonsException(); | ||||
|         } | ||||
|         Instance = this; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -0,0 +1,6 @@ | |||
| namespace SupaLidlGame.Events; | ||||
| 
 | ||||
| public partial class HitArgs : HurtArgs | ||||
| { | ||||
|     public Characters.Character Victim { get; set; } | ||||
| } | ||||
|  | @ -0,0 +1,6 @@ | |||
| namespace SupaLidlGame; | ||||
| 
 | ||||
| public class MultipleSingletonsException : System.Exception | ||||
| { | ||||
| 
 | ||||
| } | ||||
|  | @ -60,6 +60,9 @@ public abstract partial class Weapon : Item | |||
|     [Export] | ||||
|     public float MaxDistanceHint { get; set; } | ||||
| 
 | ||||
|     [Export] | ||||
|     public float PlayerLevelGain { get; set; } | ||||
| 
 | ||||
|     [Export] | ||||
|     public Sprite2D HandAnchor { get; set; } | ||||
| 
 | ||||
|  |  | |||
|  | @ -49,6 +49,9 @@ public partial class ProjectileSpawner : Ranged | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         projectile.Hitbox.Inflictor = Character; | ||||
|         projectile.Weapon = this; | ||||
| 
 | ||||
|         if (projectile is Utils.ITarget target) | ||||
|         { | ||||
|             if (Character is Characters.NPC npc) | ||||
|  |  | |||
|  | @ -370,35 +370,53 @@ shader_parameter/color = Quaternion(1, 1, 1, 1) | |||
| shader_parameter/intensity = 0.0 | ||||
| shader_parameter/alpha_modulate = 1.0 | ||||
| 
 | ||||
| [node name="Pugio" type="Node2D"] | ||||
| [node name="Pugio" type="Node2D" node_paths=PackedStringArray("Hitbox", "AnimationPlayer", "ParryParticles", "StateMachine", "Anchor", "HandAnchor")] | ||||
| y_sort_enabled = true | ||||
| texture_filter = 3 | ||||
| script = ExtResource("1_mai31") | ||||
| Hitbox = NodePath("Hitbox") | ||||
| AnimationPlayer = NodePath("AnimationPlayer") | ||||
| AttackTime = 0.2 | ||||
| AttackAltTime = 0.75 | ||||
| AttackAnimationDuration = 0.5 | ||||
| ParryParticles = NodePath("Anchor/Node2D/Sprite2D/ParryParticles") | ||||
| NPCAnticipateTime = 0.3 | ||||
| StateMachine = NodePath("State") | ||||
| Anchor = NodePath("Anchor") | ||||
| Damage = 20.0 | ||||
| UseTime = 0.55 | ||||
| UseAltTime = 1.5 | ||||
| Knockback = 64.0 | ||||
| PlayerLevelGain = 1.0 | ||||
| HandAnchor = NodePath("Anchor/Node2D/Sprite2D/Hand") | ||||
| 
 | ||||
| [node name="State" type="Node" parent="."] | ||||
| [node name="State" type="Node" parent="." node_paths=PackedStringArray("InitialState")] | ||||
| script = ExtResource("2_5ramr") | ||||
| InitialState = NodePath("Idle") | ||||
| UsedItemStates = Array[NodePath]([NodePath("Attack"), NodePath("Block")]) | ||||
| DeusedItemStates = Array[NodePath]([NodePath("Idle")]) | ||||
| 
 | ||||
| [node name="Idle" type="Node" parent="State"] | ||||
| [node name="Idle" type="Node" parent="State" node_paths=PackedStringArray("UseState", "UseAltState", "Sword")] | ||||
| script = ExtResource("3_fwkit") | ||||
| UseState = NodePath("../Anticipate") | ||||
| UseAltState = NodePath("../Block") | ||||
| Sword = NodePath("../..") | ||||
| 
 | ||||
| [node name="Anticipate" type="Node" parent="State"] | ||||
| [node name="Anticipate" type="Node" parent="State" node_paths=PackedStringArray("Sword", "AttackState")] | ||||
| script = ExtResource("4_nsn1q") | ||||
| Sword = NodePath("../..") | ||||
| AttackState = NodePath("../Attack") | ||||
| 
 | ||||
| [node name="Attack" type="Node" parent="State"] | ||||
| [node name="Attack" type="Node" parent="State" node_paths=PackedStringArray("Sword", "IdleState")] | ||||
| script = ExtResource("5_g1en5") | ||||
| Sword = NodePath("../..") | ||||
| IdleState = NodePath("../Idle") | ||||
| 
 | ||||
| [node name="Block" type="Node" parent="State"] | ||||
| [node name="Block" type="Node" parent="State" node_paths=PackedStringArray("Sword", "IdleState", "UseState")] | ||||
| script = ExtResource("6_yvm8x") | ||||
| Sword = NodePath("../..") | ||||
| IdleState = NodePath("../Idle") | ||||
| UseState = NodePath("../Anticipate") | ||||
| BlockAnimKey = "block" | ||||
| 
 | ||||
| [node name="WorldEnvironment" type="WorldEnvironment" parent="."] | ||||
|  |  | |||
|  | @ -258,6 +258,7 @@ StateMachine = NodePath("State") | |||
| Damage = 12.0 | ||||
| UseTime = 1.5 | ||||
| InitialVelocity = 220.0 | ||||
| PlayerLevelGain = 0.5 | ||||
| 
 | ||||
| [node name="State" type="Node" parent="." node_paths=PackedStringArray("InitialState")] | ||||
| script = ExtResource("2_ag6rd") | ||||
|  |  | |||
|  | @ -191,7 +191,11 @@ public partial class Sword : Weapon, IParryable | |||
|         { | ||||
|             if (box is Hurtbox hurtbox) | ||||
|             { | ||||
|                 hurtbox.InflictDamage(Damage, Character, Knockback); | ||||
|                 hurtbox.InflictDamage( | ||||
|                     Damage, | ||||
|                     Character, | ||||
|                     Knockback, | ||||
|                     this); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -354,31 +354,45 @@ graph_offset = Vector2(0, -104.073) | |||
| 
 | ||||
| [sub_resource type="AnimationNodeStateMachinePlayback" id="AnimationNodeStateMachinePlayback_37556"] | ||||
| 
 | ||||
| [node name="Sword" type="Node2D"] | ||||
| [node name="Sword" type="Node2D" node_paths=PackedStringArray("Hitbox", "AnimationPlayer", "ParryParticles", "StateMachine", "Anchor", "HandAnchor")] | ||||
| y_sort_enabled = true | ||||
| texture_filter = 3 | ||||
| script = ExtResource("1_mlo73") | ||||
| Hitbox = NodePath("Hitbox") | ||||
| AnimationPlayer = NodePath("AnimationPlayer") | ||||
| AttackTime = 0.2 | ||||
| AttackAnimationDuration = 0.75 | ||||
| ParryParticles = NodePath("Anchor/Node2D/Sprite2D/ParryParticles") | ||||
| NPCAnticipateTime = 0.3 | ||||
| StateMachine = NodePath("State") | ||||
| Anchor = NodePath("Anchor") | ||||
| Damage = 20.0 | ||||
| UseTime = 0.55 | ||||
| Knockback = 64.0 | ||||
| ShouldHideIdle = true | ||||
| PlayerLevelGain = 1.0 | ||||
| HandAnchor = NodePath("Anchor/Node2D/Sprite2D/Hand") | ||||
| 
 | ||||
| [node name="State" type="Node" parent="."] | ||||
| [node name="State" type="Node" parent="." node_paths=PackedStringArray("InitialState")] | ||||
| script = ExtResource("2_vwirq") | ||||
| InitialState = NodePath("Idle") | ||||
| UsedItemStates = Array[NodePath]([NodePath("Attack")]) | ||||
| DeusedItemStates = Array[NodePath]([NodePath("Idle")]) | ||||
| 
 | ||||
| [node name="Idle" type="Node" parent="State"] | ||||
| [node name="Idle" type="Node" parent="State" node_paths=PackedStringArray("UseState", "Sword")] | ||||
| script = ExtResource("3_nw6r0") | ||||
| UseState = NodePath("../Anticipate") | ||||
| Sword = NodePath("../..") | ||||
| 
 | ||||
| [node name="Anticipate" type="Node" parent="State"] | ||||
| [node name="Anticipate" type="Node" parent="State" node_paths=PackedStringArray("Sword", "AttackState")] | ||||
| script = ExtResource("4_j3cud") | ||||
| Sword = NodePath("../..") | ||||
| AttackState = NodePath("../Attack") | ||||
| 
 | ||||
| [node name="Attack" type="Node" parent="State"] | ||||
| [node name="Attack" type="Node" parent="State" node_paths=PackedStringArray("Sword", "IdleState")] | ||||
| script = ExtResource("5_hmisb") | ||||
| Sword = NodePath("../..") | ||||
| IdleState = NodePath("../Idle") | ||||
| 
 | ||||
| [node name="WorldEnvironment" type="WorldEnvironment" parent="."] | ||||
| environment = SubResource("Environment_72txp") | ||||
|  |  | |||
								
									
									
										
											21
										
									
									UI/Base.tscn
									
									
									
									
								
								
							
							
										
											21
										
									
									UI/Base.tscn
									
									
									
									
								|  | @ -1,9 +1,10 @@ | |||
| [gd_scene load_steps=7 format=3 uid="uid://c271rdjhd1gfo"] | ||||
| [gd_scene load_steps=8 format=3 uid="uid://c271rdjhd1gfo"] | ||||
| 
 | ||||
| [ext_resource type="PackedScene" uid="uid://73jm5qjy52vq" path="res://Dialogue/balloon.tscn" id="1_atjb1"] | ||||
| [ext_resource type="Script" path="res://UI/UIController.cs" id="2_b4b6l"] | ||||
| [ext_resource type="PackedScene" uid="uid://bxo553hblp6nf" path="res://UI/HealthBar.tscn" id="3_j1j6h"] | ||||
| [ext_resource type="PackedScene" uid="uid://01d24ij5av1y" path="res://UI/BossBar.tscn" id="4_igi28"] | ||||
| [ext_resource type="PackedScene" uid="uid://cr7tkxctmyags" path="res://UI/LevelBar.tscn" id="4_rcekd"] | ||||
| [ext_resource type="PackedScene" uid="uid://c77754nvmckn" path="res://UI/LocationDisplay.tscn" id="5_cr6vo"] | ||||
| [ext_resource type="PackedScene" uid="uid://d3q1yu3n7cqfj" path="res://UI/SceneTransition.tscn" id="6_j0nhv"] | ||||
| 
 | ||||
|  | @ -47,7 +48,7 @@ BossBar = NodePath("Bottom/BossBar") | |||
| layout_mode = 1 | ||||
| anchors_preset = 10 | ||||
| anchor_right = 1.0 | ||||
| offset_bottom = 40.0 | ||||
| offset_bottom = 64.0 | ||||
| grow_horizontal = 2 | ||||
| 
 | ||||
| [node name="Margin" type="MarginContainer" parent="SubViewportContainer/UIViewport/MainUILayer/Main/Top"] | ||||
|  | @ -55,9 +56,21 @@ layout_mode = 2 | |||
| theme_override_constants/margin_left = 16 | ||||
| theme_override_constants/margin_top = 16 | ||||
| 
 | ||||
| [node name="HealthBar" parent="SubViewportContainer/UIViewport/MainUILayer/Main/Top/Margin" instance=ExtResource("3_j1j6h")] | ||||
| [node name="VBoxContainer" type="VBoxContainer" parent="SubViewportContainer/UIViewport/MainUILayer/Main/Top/Margin"] | ||||
| layout_mode = 2 | ||||
| size_flags_horizontal = 3 | ||||
| theme_override_constants/separation = 12 | ||||
| 
 | ||||
| [node name="HealthBar" parent="SubViewportContainer/UIViewport/MainUILayer/Main/Top/Margin/VBoxContainer" instance=ExtResource("3_j1j6h")] | ||||
| layout_mode = 2 | ||||
| 
 | ||||
| [node name="LevelBar" parent="SubViewportContainer/UIViewport/MainUILayer/Main/Top/Margin/VBoxContainer" instance=ExtResource("4_rcekd")] | ||||
| layout_mode = 2 | ||||
| 
 | ||||
| [node name="Margin2" type="MarginContainer" parent="SubViewportContainer/UIViewport/MainUILayer/Main/Top/Margin/VBoxContainer"] | ||||
| visible = false | ||||
| layout_mode = 2 | ||||
| theme_override_constants/margin_left = 16 | ||||
| theme_override_constants/margin_top = 16 | ||||
| 
 | ||||
| [node name="Bottom" type="HBoxContainer" parent="SubViewportContainer/UIViewport/MainUILayer/Main"] | ||||
| layout_mode = 1 | ||||
|  |  | |||
|  | @ -0,0 +1,36 @@ | |||
| using Godot; | ||||
| using SupaLidlGame.Events; | ||||
| 
 | ||||
| namespace SupaLidlGame.UI; | ||||
| 
 | ||||
| public partial class LevelBar : Control | ||||
| { | ||||
|     public TextureProgressBar XPBar { get; set; } | ||||
| 
 | ||||
|     private TextureProgressBar[] _levelBars = new TextureProgressBar[4]; | ||||
| 
 | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         XPBar = GetNode<TextureProgressBar>("%XPBar"); | ||||
| 
 | ||||
|         for (int i = 0; i < 4; i++) | ||||
|         { | ||||
|             _levelBars[i] = GetNode<TextureProgressBar>($"%Level{i + 1}Bar"); | ||||
|         } | ||||
| 
 | ||||
|         EventBus.Instance.PlayerXPChanged += (xp) => | ||||
|         { | ||||
|             XPBar.Value = xp; | ||||
|         }; | ||||
| 
 | ||||
|         EventBus.Instance.PlayerLevelChanged += (level) => | ||||
|         { | ||||
|             for (int i = 0; i < _levelBars.Length; i++) | ||||
|             { | ||||
|                 // level 0: 0 is not less than 0 | ||||
|                 // level 1: 0 is less than 1 | ||||
|                 _levelBars[i].Value = i < level ? 1 : 0; | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,94 @@ | |||
| [gd_scene load_steps=4 format=3 uid="uid://cr7tkxctmyags"] | ||||
| 
 | ||||
| [ext_resource type="Script" path="res://UI/LevelBar.cs" id="1_eqetx"] | ||||
| [ext_resource type="Texture2D" uid="uid://b75oak1nd2q6x" path="res://Assets/Sprites/UI/over-under-bar.png" id="2_f332l"] | ||||
| [ext_resource type="Texture2D" uid="uid://co7xm7i5f6n51" path="res://Assets/Sprites/UI/progress-bar.png" id="3_arvub"] | ||||
| 
 | ||||
| [node name="LevelBar" type="Control"] | ||||
| layout_mode = 3 | ||||
| anchors_preset = 0 | ||||
| offset_right = 128.0 | ||||
| offset_bottom = 8.0 | ||||
| script = ExtResource("1_eqetx") | ||||
| 
 | ||||
| [node name="HBoxContainer" type="HBoxContainer" parent="."] | ||||
| layout_mode = 2 | ||||
| offset_right = 128.0 | ||||
| offset_bottom = 8.0 | ||||
| 
 | ||||
| [node name="XPBar" type="TextureProgressBar" parent="HBoxContainer"] | ||||
| unique_name_in_owner = true | ||||
| texture_filter = 1 | ||||
| layout_mode = 2 | ||||
| size_flags_horizontal = 3 | ||||
| max_value = 4.0 | ||||
| step = 0.01 | ||||
| nine_patch_stretch = true | ||||
| stretch_margin_left = 3 | ||||
| stretch_margin_top = 3 | ||||
| stretch_margin_right = 3 | ||||
| stretch_margin_bottom = 3 | ||||
| texture_under = ExtResource("2_f332l") | ||||
| texture_progress = ExtResource("3_arvub") | ||||
| 
 | ||||
| [node name="Level1Bar" type="TextureProgressBar" parent="HBoxContainer"] | ||||
| unique_name_in_owner = true | ||||
| texture_filter = 1 | ||||
| layout_mode = 2 | ||||
| max_value = 1.0 | ||||
| nine_patch_stretch = true | ||||
| stretch_margin_left = 3 | ||||
| stretch_margin_top = 3 | ||||
| stretch_margin_right = 3 | ||||
| stretch_margin_bottom = 3 | ||||
| texture_under = ExtResource("2_f332l") | ||||
| texture_progress = ExtResource("3_arvub") | ||||
| 
 | ||||
| [node name="Level2Bar" type="TextureProgressBar" parent="HBoxContainer"] | ||||
| unique_name_in_owner = true | ||||
| texture_filter = 1 | ||||
| layout_mode = 2 | ||||
| max_value = 1.0 | ||||
| nine_patch_stretch = true | ||||
| stretch_margin_left = 3 | ||||
| stretch_margin_top = 3 | ||||
| stretch_margin_right = 3 | ||||
| stretch_margin_bottom = 3 | ||||
| texture_under = ExtResource("2_f332l") | ||||
| texture_progress = ExtResource("3_arvub") | ||||
| 
 | ||||
| [node name="Level3Bar" type="TextureProgressBar" parent="HBoxContainer"] | ||||
| unique_name_in_owner = true | ||||
| texture_filter = 1 | ||||
| layout_mode = 2 | ||||
| max_value = 1.0 | ||||
| nine_patch_stretch = true | ||||
| stretch_margin_left = 3 | ||||
| stretch_margin_top = 3 | ||||
| stretch_margin_right = 3 | ||||
| stretch_margin_bottom = 3 | ||||
| texture_under = ExtResource("2_f332l") | ||||
| texture_progress = ExtResource("3_arvub") | ||||
| 
 | ||||
| [node name="Level4Bar" type="TextureProgressBar" parent="HBoxContainer"] | ||||
| unique_name_in_owner = true | ||||
| texture_filter = 1 | ||||
| layout_mode = 2 | ||||
| max_value = 1.0 | ||||
| nine_patch_stretch = true | ||||
| stretch_margin_left = 3 | ||||
| stretch_margin_top = 3 | ||||
| stretch_margin_right = 3 | ||||
| stretch_margin_bottom = 3 | ||||
| texture_under = ExtResource("2_f332l") | ||||
| texture_progress = ExtResource("3_arvub") | ||||
| 
 | ||||
| [node name="VBoxContainer" type="VBoxContainer" parent="."] | ||||
| visible = false | ||||
| layout_mode = 1 | ||||
| anchors_preset = 15 | ||||
| anchor_right = 1.0 | ||||
| anchor_bottom = 1.0 | ||||
| grow_horizontal = 2 | ||||
| grow_vertical = 2 | ||||
| theme_override_constants/separation = 4 | ||||
|  | @ -0,0 +1,75 @@ | |||
| using Godot; | ||||
| using SupaLidlGame.Events; | ||||
| 
 | ||||
| namespace SupaLidlGame.Utils; | ||||
| 
 | ||||
| public partial class PlayerStats : Node | ||||
| { | ||||
|     public const int MAX_XP_PER_LEVEL = 4; | ||||
| 
 | ||||
|     public const int MAX_LEVELS = 4; | ||||
| 
 | ||||
|     public DoubleValue XP { get; set; } | ||||
| 
 | ||||
|     public IntValue Level { get; set; } | ||||
| 
 | ||||
|     public double XPDecayVelocity { get; set; } = 1; | ||||
| 
 | ||||
|     protected bool _shouldDecayXP = true; | ||||
| 
 | ||||
|     protected Timer _xpDecayTimer; | ||||
| 
 | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         XP = GetNode<DoubleValue>("XP"); | ||||
|         Level = GetNode<IntValue>("Level"); | ||||
| 
 | ||||
|         _xpDecayTimer = new Timer(); | ||||
|         _xpDecayTimer.Timeout += () => _shouldDecayXP = true; | ||||
|         _xpDecayTimer.Stop(); | ||||
|         AddChild(_xpDecayTimer); | ||||
| 
 | ||||
|         var bus = EventBus.Instance; | ||||
|         XP.Changed += (oldValue, newValue) => | ||||
|         { | ||||
|             bus.EmitSignal(EventBus.SignalName.PlayerXPChanged, newValue); | ||||
|         }; | ||||
| 
 | ||||
|         Level.Changed += (oldValue, newValue) => | ||||
|         { | ||||
|             bus.EmitSignal(EventBus.SignalName.PlayerLevelChanged, newValue); | ||||
|         }; | ||||
| 
 | ||||
|         bus.PlayerHit += (args) => | ||||
|         { | ||||
|             double xp = XP.Value; | ||||
|             xp += args.Weapon?.PlayerLevelGain ?? 0; | ||||
| 
 | ||||
|             if (xp >= MAX_XP_PER_LEVEL) | ||||
|             { | ||||
|                 int deltaLevel = (int)(xp / MAX_XP_PER_LEVEL); | ||||
|                 xp %= MAX_XP_PER_LEVEL; | ||||
|                 Level.Value = Mathf.Min(Level.Value + deltaLevel, MAX_LEVELS); | ||||
|             } | ||||
| 
 | ||||
|             if (Level.Value == MAX_LEVELS) | ||||
|             { | ||||
|                 // if max level, can only go up to 1 xp | ||||
|                 xp = Mathf.Min(xp, 1); | ||||
|             } | ||||
| 
 | ||||
|             XP.Value = xp; | ||||
| 
 | ||||
|             _shouldDecayXP = false; | ||||
|             _xpDecayTimer.Start(1); | ||||
|         }; | ||||
|     } | ||||
| 
 | ||||
|     public override void _Process(double delta) | ||||
|     { | ||||
|         if (_shouldDecayXP) | ||||
|         { | ||||
|             XP.Value = Mathf.MoveToward(XP.Value, 1, XPDecayVelocity * delta); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,22 @@ | |||
| using Godot; | ||||
| 
 | ||||
| namespace SupaLidlGame; | ||||
| 
 | ||||
| public partial class DoubleValue : Node, IValue<double> | ||||
| { | ||||
|     [Signal] | ||||
|     public delegate void ChangedEventHandler(double oldValue, double newValue); | ||||
| 
 | ||||
|     protected double _value = default; | ||||
| 
 | ||||
|     [Export] | ||||
|     public double Value | ||||
|     { | ||||
|         get => _value; | ||||
|         set | ||||
|         { | ||||
|             EmitSignal(SignalName.Changed, _value, value); | ||||
|             _value = value; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,22 @@ | |||
| using Godot; | ||||
| 
 | ||||
| namespace SupaLidlGame; | ||||
| 
 | ||||
| public partial class FloatValue : Node, IValue<float> | ||||
| { | ||||
|     [Signal] | ||||
|     public delegate void ChangedEventHandler(float oldValue, float newValue); | ||||
| 
 | ||||
|     protected float _value = default; | ||||
| 
 | ||||
|     [Export] | ||||
|     public float Value | ||||
|     { | ||||
|         get => _value; | ||||
|         set | ||||
|         { | ||||
|             EmitSignal(SignalName.Changed, _value, value); | ||||
|             _value = value; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,8 @@ | |||
| namespace SupaLidlGame; | ||||
| 
 | ||||
| public interface IValue<T> | ||||
| { | ||||
|     public delegate void ChangedEventHandler(T oldValue, T newValue); | ||||
| 
 | ||||
|     public T Value { get; set; } | ||||
| } | ||||
|  | @ -0,0 +1,22 @@ | |||
| using Godot; | ||||
| 
 | ||||
| namespace SupaLidlGame; | ||||
| 
 | ||||
| public partial class IntValue : Node, IValue<int> | ||||
| { | ||||
|     [Signal] | ||||
|     public delegate void ChangedEventHandler(int oldValue, int newValue); | ||||
| 
 | ||||
|     protected int _value = default; | ||||
| 
 | ||||
|     [Export] | ||||
|     public int Value | ||||
|     { | ||||
|         get => _value; | ||||
|         set | ||||
|         { | ||||
|             EmitSignal(SignalName.Changed, _value, value); | ||||
|             _value = value; | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Loading…
	
		Reference in New Issue