diff --git a/Autoloads/EventBus.cs b/Autoloads/EventBus.cs index 5647cec..de97ee8 100644 --- a/Autoloads/EventBus.cs +++ b/Autoloads/EventBus.cs @@ -82,6 +82,7 @@ public partial class EventBus : Node [Signal] public delegate void CoinCollectedEventHandler(int amount, Vector2 position); [Signal] public delegate void ItemCollectedEventHandler(CollectableType itemType, float amount, Vector2 position); [Signal] public delegate void ChildRescuedEventHandler(Vector2 position); + [Signal] public delegate void SkillCollectedEventHandler(SkillData skill, Vector2 position); public static void EmitCoinCollected(int amount, Vector2 position) => Instance?.EmitSignal(SignalName.CoinCollected, amount, position); @@ -92,6 +93,9 @@ public partial class EventBus : Node public static void EmitChildRescued(Vector2 position) => Instance?.EmitSignal(SignalName.ChildRescued, position); + public static void EmitSkillCollected(SkillData skill, Vector2 position) + => Instance?.EmitSignal(SignalName.SkillCollected, skill, position); + #endregion #region Skill Events diff --git a/objects/entities/double_jump_skill_pickup.tscn b/objects/entities/double_jump_skill_pickup.tscn new file mode 100644 index 0000000..4ad4764 --- /dev/null +++ b/objects/entities/double_jump_skill_pickup.tscn @@ -0,0 +1,34 @@ +[gd_scene load_steps=6 format=3 uid="uid://dk2cu8qs7odib"] + +[ext_resource type="Texture2D" uid="uid://djifxc5x0dyrw" path="res://sprites/ppc_tileset.png" id="1_214vd"] +[ext_resource type="Script" uid="uid://r4jybneigfcn" path="res://scripts/components/CollectableComponent.cs" id="2_h7fi3"] +[ext_resource type="Script" uid="uid://bjln6jb1sigx2" path="res://scripts/components/FadeAwayComponent.cs" id="3_b687r"] +[ext_resource type="Resource" path="res://resources/collectables/double_jump_pickup.tres" id="3_h7fi3"] + +[sub_resource type="CircleShape2D" id="CircleShape2D_pickup"] +radius = 12.0 + +[node name="SkillPickup" type="Area2D" groups=["Collectables"]] +collision_layer = 2 +collision_mask = 4 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +shape = SubResource("CircleShape2D_pickup") + +[node name="Sprite2D" type="Sprite2D" parent="."] +texture = ExtResource("1_214vd") +hframes = 12 +vframes = 12 +frame = 24 + +[node name="CollectableComponent" type="Node" parent="." node_paths=PackedStringArray("Area2D", "CollisionShape")] +script = ExtResource("2_h7fi3") +Area2D = NodePath("..") +CollisionShape = NodePath("../CollisionShape2D") +Data = ExtResource("3_h7fi3") + +[node name="FadeAwayComponent" type="Node" parent="." node_paths=PackedStringArray("Sprite", "Area")] +script = ExtResource("3_b687r") +Sprite = NodePath("../Sprite2D") +FadeDuration = 0.5 +Area = NodePath("..") diff --git a/objects/entities/skill_pickup.tscn b/objects/entities/skill_pickup.tscn new file mode 100644 index 0000000..724bbcb --- /dev/null +++ b/objects/entities/skill_pickup.tscn @@ -0,0 +1,32 @@ +[gd_scene load_steps=5 format=3 uid="uid://0idmnkwids1r"] + +[ext_resource type="Texture2D" uid="uid://djifxc5x0dyrw" path="res://sprites/ppc_tileset.png" id="1_sprite"] +[ext_resource type="Script" uid="uid://r4jybneigfcn" path="res://scripts/components/CollectableComponent.cs" id="2_collectable"] +[ext_resource type="Script" uid="uid://bjln6jb1sigx2" path="res://scripts/components/FadeAwayComponent.cs" id="3_fadeaway"] + +[sub_resource type="CircleShape2D" id="CircleShape2D_pickup"] +radius = 12.0 + +[node name="SkillPickup" type="Area2D" groups=["Collectables"]] +collision_layer = 2 +collision_mask = 4 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +shape = SubResource("CircleShape2D_pickup") + +[node name="Sprite2D" type="Sprite2D" parent="."] +texture = ExtResource("1_sprite") +hframes = 12 +vframes = 12 +frame = 24 + +[node name="CollectableComponent" type="Node" parent="." node_paths=PackedStringArray("Area2D", "CollisionShape")] +script = ExtResource("2_collectable") +Area2D = NodePath("..") +CollisionShape = NodePath("../CollisionShape2D") + +[node name="FadeAwayComponent" type="Node" parent="." node_paths=PackedStringArray("Sprite", "Area")] +script = ExtResource("3_fadeaway") +Sprite = NodePath("../Sprite2D") +FadeDuration = 0.5 +Area = NodePath("..") diff --git a/project.godot b/project.godot index 61af395..7c0a8ca 100644 --- a/project.godot +++ b/project.godot @@ -49,6 +49,7 @@ StatisticsEventHandler="*res://scripts/Events/StatisticsEventHandler.cs" CoinStateHandler="*res://scripts/Events/CoinStateHandler.cs" LevelStateHandler="*res://scripts/Events/LevelStateHandler.cs" LivesStateHandler="*res://scripts/Events/LivesStateHandler.cs" +SkillCollectHandler="*res://scripts/Events/SkillCollectHandler.cs" GameStateStore="*res://Autoloads/GameStateStore.cs" GameManager="*res://objects/game_manager.tscn" diff --git a/resources/collectables/double_jump_pickup.tres b/resources/collectables/double_jump_pickup.tres new file mode 100644 index 0000000..b2a9b65 --- /dev/null +++ b/resources/collectables/double_jump_pickup.tres @@ -0,0 +1,10 @@ +[gd_resource type="Resource" script_class="CollectableResource" load_steps=3 format=3] + +[ext_resource type="Script" path="res://scripts/Resources/CollectableResource.cs" id="1_script"] +[ext_resource type="Resource" uid="uid://bxsgq8703qx4u" path="res://resources/skills/double_jump.tres" id="2_skill"] + +[resource] +script = ExtResource("1_script") +Amount = 0.0 +Type = 3 +Skill = ExtResource("2_skill") diff --git a/scenes/level_village_3.tscn b/scenes/level_village_3.tscn index da67bb4..323c5ad 100644 --- a/scenes/level_village_3.tscn +++ b/scenes/level_village_3.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=33 format=4 uid="uid://h60obxmju6mo"] +[gd_scene load_steps=34 format=4 uid="uid://h60obxmju6mo"] [ext_resource type="PackedScene" uid="uid://dyp4i4ru2j2jh" path="res://objects/fxs/explosion_fx.tscn" id="1_p30ax"] [ext_resource type="PackedScene" uid="uid://dx80ivlvuuew4" path="res://objects/fxs/fire_fx.tscn" id="2_a7yjf"] @@ -23,6 +23,7 @@ [ext_resource type="PackedScene" uid="uid://bqom4cm7r18db" path="res://objects/entities/killzone.tscn" id="21_p30ax"] [ext_resource type="PackedScene" uid="uid://12jnkdygpxwc" path="res://objects/entities/exit_level.tscn" id="22_a7yjf"] [ext_resource type="PackedScene" uid="uid://t6h2ra7kjyq" path="res://objects/entities/small_heal_potion.tscn" id="23_m6h4x"] +[ext_resource type="PackedScene" uid="uid://dk2cu8qs7odib" path="res://objects/entities/double_jump_skill_pickup.tscn" id="24_6fdf4"] [sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_j7bvy"] texture = ExtResource("7_uvxky") @@ -300,7 +301,7 @@ z_index = 5 position = Vector2(903, -118) metadata/_edit_group_ = true -[node name="HitParticles" parent="Brick Player" index="24"] +[node name="HitParticles" parent="Brick Player" index="23"] process_material = SubResource("ParticleProcessMaterial_lgb3u") [node name="Camera2D" parent="." instance=ExtResource("12_qhkyq")] @@ -364,6 +365,9 @@ position = Vector2(1359, -42) [node name="Killzone" parent="." instance=ExtResource("21_p30ax")] position = Vector2(2456, 815) +[node name="SkillPickup" parent="." instance=ExtResource("24_6fdf4")] +position = Vector2(1136, -109) + [connection signal="Death" from="Brick Player/HealthComponent" to="UI Layer/DeathScreen" method="OnPlayerDeath"] [connection signal="Death" from="Brick Player/HealthComponent" to="UI Layer/GameOverScreen" method="OnPlayerDeath"] diff --git a/scripts/Events/SkillCollectHandler.cs b/scripts/Events/SkillCollectHandler.cs new file mode 100644 index 0000000..e2dd3b3 --- /dev/null +++ b/scripts/Events/SkillCollectHandler.cs @@ -0,0 +1,44 @@ +using Godot; +using Mr.BrickAdventures.Autoloads; +using Mr.BrickAdventures.scripts.Resources; + +namespace Mr.BrickAdventures.scripts.Events; + +/// +/// Handles skill collection events and unlocks skills via GameStateStore. +/// Skills are immediately activated but only persisted on level complete. +/// +public partial class SkillCollectHandler : Node +{ + private SkillManager _skillManager; + + public override void _Ready() + { + _skillManager = GetNode(Constants.SkillManagerPath); + EventBus.Instance.SkillCollected += OnSkillCollected; + } + + public override void _ExitTree() + { + if (EventBus.Instance != null) + { + EventBus.Instance.SkillCollected -= OnSkillCollected; + } + } + + private void OnSkillCollected(SkillData skill, Vector2 position) + { + if (skill == null) return; + + // Unlock in session (will be committed on level complete, lost on death) + GameStateStore.Instance?.UnlockSkillInSession(skill); + + // Immediately activate the skill for the player + skill.IsActive = true; + skill.Level = 1; + _skillManager?.AddSkill(skill); + + // Emit skill unlocked event for UI/achievements + EventBus.EmitSkillUnlocked(skill.Name, skill.Level); + } +} diff --git a/scripts/Events/SkillCollectHandler.cs.uid b/scripts/Events/SkillCollectHandler.cs.uid new file mode 100644 index 0000000..a9cecdb --- /dev/null +++ b/scripts/Events/SkillCollectHandler.cs.uid @@ -0,0 +1 @@ +uid://c1po4hjvqbslm diff --git a/scripts/Resources/CollectableResource.cs b/scripts/Resources/CollectableResource.cs index 96cb03a..6f5bad7 100644 --- a/scripts/Resources/CollectableResource.cs +++ b/scripts/Resources/CollectableResource.cs @@ -6,4 +6,9 @@ public partial class CollectableResource : Resource { [Export] public float Amount { get; set; } = 0.0f; [Export] public CollectableType Type { get; set; } + + /// + /// The skill to unlock when collected. Only used when Type is Skill. + /// + [Export] public SkillData Skill { get; set; } } \ No newline at end of file diff --git a/scripts/Resources/CollectableType.cs b/scripts/Resources/CollectableType.cs index 840b8b9..133195e 100644 --- a/scripts/Resources/CollectableType.cs +++ b/scripts/Resources/CollectableType.cs @@ -5,4 +5,5 @@ public enum CollectableType Coin, Kid, Health, + Skill, } \ No newline at end of file diff --git a/scripts/components/CollectableComponent.cs b/scripts/components/CollectableComponent.cs index 418b33d..e4d9535 100644 --- a/scripts/components/CollectableComponent.cs +++ b/scripts/components/CollectableComponent.cs @@ -64,6 +64,13 @@ public partial class CollectableComponent : Node _floatingTextManager?.ShowMessage("Rescued!", ownerNode.GlobalPosition); EventBus.EmitChildRescued(ownerNode.GlobalPosition); break; + case CollectableType.Skill: + if (Data.Skill != null) + { + _floatingTextManager?.ShowMessage($"{Data.Skill.Name} Unlocked!", ownerNode.GlobalPosition); + EventBus.EmitSkillCollected(Data.Skill, ownerNode.GlobalPosition); + } + break; default: EventBus.EmitItemCollected(Data.Type, Data.Amount, ownerNode.GlobalPosition); break;