diff --git a/Assets/Dialogue/books.dialogue.import b/Assets/Dialogue/books.dialogue.import
index a2928b8..fd22018 100644
--- a/Assets/Dialogue/books.dialogue.import
+++ b/Assets/Dialogue/books.dialogue.import
@@ -1,6 +1,6 @@
[remap]
-importer="dialogue_manager_compiler_11"
+importer="dialogue_manager_compiler_12"
type="Resource"
uid="uid://dilmuoilweoeh"
path="res://.godot/imported/books.dialogue-cc272ebae322ae3ca46820dca11a3437.tres"
diff --git a/Assets/Dialogue/clone-machine.dialogue.import b/Assets/Dialogue/clone-machine.dialogue.import
index 70159e2..629926e 100644
--- a/Assets/Dialogue/clone-machine.dialogue.import
+++ b/Assets/Dialogue/clone-machine.dialogue.import
@@ -1,6 +1,6 @@
[remap]
-importer="dialogue_manager_compiler_11"
+importer="dialogue_manager_compiler_12"
type="Resource"
uid="uid://c2om4y0fm81yr"
path="res://.godot/imported/clone-machine.dialogue-8810934a67eacdad52469e9ef5f970fb.tres"
diff --git a/Assets/Dialogue/doc.dialogue b/Assets/Dialogue/doc.dialogue
index 2452e7d..5417f16 100644
--- a/Assets/Dialogue/doc.dialogue
+++ b/Assets/Dialogue/doc.dialogue
@@ -18,11 +18,10 @@ Doc, The Two Time: Nothing but success.
Doc, The Two Time: That's what my life's about. Period.
Doc, The Two Time: And the arena today, ladies and gentlemen...
Doc, The Two Time: is wide open, and the crowds are flooding in. VIP seating. Skybox section. Reserved for the Slick Daddy Club.
-Doc, The Two Time: The Slick Daddy Club lofoking so damn good today. I'm feeling so damn good. It's obvious.
-Doc, The Two Time: The V of success.
+Doc, The Two Time: The Slick Daddy Club looking so damn good today. I'm feeling so damn good. It's obvious.
Challenge [b]Doc, The Two Time[/b] to a duel?
- Yes
do emit("SummonBoss", "Doc")
-- No => END
+- No, I'm a little chubby cheek wannabe punk kid. => END
=> END
diff --git a/Assets/Dialogue/doc.dialogue.import b/Assets/Dialogue/doc.dialogue.import
index 68f302f..491bd4e 100644
--- a/Assets/Dialogue/doc.dialogue.import
+++ b/Assets/Dialogue/doc.dialogue.import
@@ -1,6 +1,6 @@
[remap]
-importer="dialogue_manager_compiler_11"
+importer="dialogue_manager_compiler_12"
type="Resource"
uid="uid://dntkvjjr8mrgf"
path="res://.godot/imported/doc.dialogue-8f95f6a09d3ac685b71d7e07c49df1c6.tres"
diff --git a/Assets/Sprites/UI/hotbar-active.ase b/Assets/Sprites/UI/hotbar-active.ase
new file mode 100644
index 0000000..76abcee
Binary files /dev/null and b/Assets/Sprites/UI/hotbar-active.ase differ
diff --git a/Assets/Sprites/UI/hotbar-active.png b/Assets/Sprites/UI/hotbar-active.png
new file mode 100644
index 0000000..678ef9f
Binary files /dev/null and b/Assets/Sprites/UI/hotbar-active.png differ
diff --git a/Assets/Sprites/UI/hotbar-active.png.import b/Assets/Sprites/UI/hotbar-active.png.import
new file mode 100644
index 0000000..150ed23
--- /dev/null
+++ b/Assets/Sprites/UI/hotbar-active.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dc1gcsbhkchvg"
+path="res://.godot/imported/hotbar-active.png-7657e1b4001be05323aa0d0697509f58.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Assets/Sprites/UI/hotbar-active.png"
+dest_files=["res://.godot/imported/hotbar-active.png-7657e1b4001be05323aa0d0697509f58.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/Assets/Sprites/UI/hotbar-inactive.ase b/Assets/Sprites/UI/hotbar-inactive.ase
index cc58d37..f7ff4d9 100644
Binary files a/Assets/Sprites/UI/hotbar-inactive.ase and b/Assets/Sprites/UI/hotbar-inactive.ase differ
diff --git a/Assets/Sprites/UI/hotbar-inactive.png b/Assets/Sprites/UI/hotbar-inactive.png
index 584cce5..1df834c 100644
Binary files a/Assets/Sprites/UI/hotbar-inactive.png and b/Assets/Sprites/UI/hotbar-inactive.png differ
diff --git a/Assets/Sprites/UI/menu-rect-no-bg-white.ase b/Assets/Sprites/UI/menu-rect-no-bg-white.ase
new file mode 100644
index 0000000..8062cf4
Binary files /dev/null and b/Assets/Sprites/UI/menu-rect-no-bg-white.ase differ
diff --git a/Assets/Sprites/UI/menu-rect-no-bg-white.png b/Assets/Sprites/UI/menu-rect-no-bg-white.png
new file mode 100644
index 0000000..d13149c
Binary files /dev/null and b/Assets/Sprites/UI/menu-rect-no-bg-white.png differ
diff --git a/Assets/Sprites/UI/menu-rect-no-bg-white.png.import b/Assets/Sprites/UI/menu-rect-no-bg-white.png.import
new file mode 100644
index 0000000..cdcd8be
--- /dev/null
+++ b/Assets/Sprites/UI/menu-rect-no-bg-white.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://uhmowtsi3wfh"
+path="res://.godot/imported/menu-rect-no-bg-white.png-5ea2d275879af97991070fb370031211.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Assets/Sprites/UI/menu-rect-no-bg-white.png"
+dest_files=["res://.godot/imported/menu-rect-no-bg-white.png-5ea2d275879af97991070fb370031211.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/Assets/Sprites/forsenLevel.ase b/Assets/Sprites/forsenLevel.ase
new file mode 100644
index 0000000..e424a53
Binary files /dev/null and b/Assets/Sprites/forsenLevel.ase differ
diff --git a/Assets/Sprites/forsenLevel.png b/Assets/Sprites/forsenLevel.png
new file mode 100644
index 0000000..baebf74
Binary files /dev/null and b/Assets/Sprites/forsenLevel.png differ
diff --git a/Assets/Sprites/forsenLevel.png.import b/Assets/Sprites/forsenLevel.png.import
new file mode 100644
index 0000000..9c8119e
--- /dev/null
+++ b/Assets/Sprites/forsenLevel.png.import
@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://vjdyrv8wp7gl"
+path="res://.godot/imported/forsenLevel.png-ffd03264deccb9275d087fadbb57e56f.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Assets/Sprites/forsenLevel.png"
+dest_files=["res://.godot/imported/forsenLevel.png-ffd03264deccb9275d087fadbb57e56f.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
diff --git a/BoundingBoxes/BoundingBox.cs b/BoundingBoxes/BoundingBox.cs
index f1972ef..5c6d377 100644
--- a/BoundingBoxes/BoundingBox.cs
+++ b/BoundingBoxes/BoundingBox.cs
@@ -5,6 +5,6 @@ namespace SupaLidlGame.BoundingBoxes;
public abstract partial class BoundingBox : Area2D, IFaction
{
- [Export]
- public ushort Faction { get; set; }
+ [Export(PropertyHint.Flags)]
+ public FactionName Faction { get; set; }
}
diff --git a/Characters/Character.cs b/Characters/Character.cs
index 7a067ea..afd9ff8 100644
--- a/Characters/Character.cs
+++ b/Characters/Character.cs
@@ -93,8 +93,8 @@ public partial class Character : CharacterBody2D, IFaction
[Export]
public BoundingBoxes.Hurtbox Hurtbox { get; set; }
- [Export]
- public ushort Faction { get; set; }
+ [Export(PropertyHint.Flags)]
+ public FactionName Faction { get; set; }
public AnimationPlayer MovementAnimation { get; set; }
diff --git a/Characters/Doc.tscn b/Characters/Doc.tscn
index 3643012..6e90a99 100644
--- a/Characters/Doc.tscn
+++ b/Characters/Doc.tscn
@@ -573,6 +573,7 @@ Sprite = NodePath("Sprite")
Inventory = NodePath("Inventory")
StateMachine = NodePath("StateMachine")
Hurtbox = NodePath("Hurtbox")
+Faction = 2
[node name="Stats" type="Node" parent="."]
script = ExtResource("5_a7fiw")
diff --git a/Characters/NPC.cs b/Characters/NPC.cs
index 2b438e7..a5806a3 100644
--- a/Characters/NPC.cs
+++ b/Characters/NPC.cs
@@ -3,6 +3,7 @@
using Godot;
using SupaLidlGame.Extensions;
using SupaLidlGame.Items;
+using SupaLidlGame.Utils;
using System;
namespace SupaLidlGame.Characters;
@@ -127,6 +128,37 @@ public partial class NPC : Character
return bestChar;
}
+ ///
+ /// Finds the best character whose faction aligns with this character's.
+ ///
+ public virtual Character FindBestNeutral()
+ {
+ float bestScore = float.MaxValue;
+ Character bestChar = null;
+ // NOTE: this relies on all Characters being under the Entities node
+ foreach (Node node in GetParent().GetChildren())
+ {
+ if (node is Character character)
+ {
+ bool isFriendly = ((IFaction)this).AlignsWith(character);
+ if (isFriendly || character.Health <= 0)
+ {
+ continue;
+ }
+
+ float score = 0;
+ score -= Position.DistanceTo(character.Position);
+
+ if (score < bestScore)
+ {
+ bestScore = score;
+ bestChar = character;
+ }
+ }
+ }
+ return bestChar;
+ }
+
public override void _Process(double delta)
{
ThinkerStateMachine.Process(delta);
diff --git a/Characters/SnusDealer.tscn b/Characters/SnusDealer.tscn
new file mode 100644
index 0000000..569eb06
--- /dev/null
+++ b/Characters/SnusDealer.tscn
@@ -0,0 +1,74 @@
+[gd_scene load_steps=11 format=3 uid="uid://rd08pot25h00"]
+
+[ext_resource type="Script" path="res://Characters/NPC.cs" id="1_04gcf"]
+[ext_resource type="Script" path="res://State/Character/CharacterStateMachine.cs" id="2_kynkg"]
+[ext_resource type="Texture2D" uid="uid://bej8thq7ruyty" path="res://Assets/Sprites/Characters/forsen2.png" id="2_s5nik"]
+[ext_resource type="Script" path="res://State/Character/NPCIdleState.cs" id="3_pcrll"]
+[ext_resource type="Script" path="res://State/Thinker/ThinkerStateMachine.cs" id="4_mo4wj"]
+[ext_resource type="Script" path="res://State/Thinker/VendorIdle.cs" id="5_oau5d"]
+[ext_resource type="PackedScene" uid="uid://dldnp8eunxj3q" path="res://BoundingBoxes/InteractionTrigger.tscn" id="5_sjs24"]
+[ext_resource type="Script" path="res://Utils/InteractionTriggerDialogue.cs" id="5_yknpw"]
+[ext_resource type="Resource" uid="uid://c4n7vhoxybu70" path="res://Dialogue/snus-dealer.dialogue" id="6_isvnq"]
+[ext_resource type="Script" path="res://Items/Inventory.cs" id="7_vip6b"]
+
+[node name="SnusDealer" type="CharacterBody2D" node_paths=PackedStringArray("ThinkerStateMachine", "Sprite", "Inventory", "StateMachine")]
+script = ExtResource("1_04gcf")
+ThinkerStateMachine = NodePath("Thinker")
+Sprite = NodePath("Sprites/Sprite")
+Inventory = NodePath("Inventory")
+StateMachine = NodePath("StateMachine")
+
+[node name="StateMachine" type="Node" parent="." node_paths=PackedStringArray("InitialState", "Character")]
+script = ExtResource("2_kynkg")
+InitialState = NodePath("Idle")
+Character = NodePath("..")
+
+[node name="Idle" type="Node" parent="StateMachine" node_paths=PackedStringArray("Character")]
+script = ExtResource("3_pcrll")
+Character = NodePath("../..")
+
+[node name="Thinker" type="Node" parent="." node_paths=PackedStringArray("InitialState")]
+script = ExtResource("4_mo4wj")
+InitialState = NodePath("Idle")
+
+[node name="Idle" type="Node" parent="Thinker" node_paths=PackedStringArray("NPC")]
+script = ExtResource("5_oau5d")
+NPC = NodePath("../..")
+
+[node name="Animations" type="Node" parent="."]
+
+[node name="Movement" type="AnimationPlayer" parent="Animations"]
+
+[node name="Hurt" type="AnimationPlayer" parent="Animations"]
+
+[node name="Stun" type="AnimationPlayer" parent="Animations"]
+
+[node name="Attack" type="AnimationPlayer" parent="Animations"]
+
+[node name="Stats" type="Node" parent="."]
+
+[node name="Sprites" type="Node2D" parent="."]
+
+[node name="Sprite" type="Sprite2D" parent="Sprites"]
+texture = ExtResource("2_s5nik")
+centered = false
+offset = Vector2(-12, -20)
+hframes = 46
+
+[node name="Inventory" type="Node2D" parent="." node_paths=PackedStringArray("Hotbar")]
+script = ExtResource("7_vip6b")
+Hotbar = []
+
+[node name="Interaction" type="Node2D" parent="." node_paths=PackedStringArray("InteractionTrigger")]
+position = Vector2(0, -4)
+script = ExtResource("5_yknpw")
+InteractionTrigger = NodePath("InteractionTrigger")
+DialogueResource = ExtResource("6_isvnq")
+DialogueTitle = "start"
+
+[node name="InteractionTrigger" parent="Interaction" instance=ExtResource("5_sjs24")]
+
+[node name="Label" parent="Interaction/InteractionTrigger/Popup" index="0"]
+text = "Listen"
+
+[editable path="Interaction/InteractionTrigger"]
diff --git a/Characters/Vendor.cs b/Characters/Vendor.cs
new file mode 100644
index 0000000..8487415
--- /dev/null
+++ b/Characters/Vendor.cs
@@ -0,0 +1,27 @@
+//using Godot;
+//using SupaLidlGame.BoundingBoxes;
+//using SupaLidlGame.Extensions;
+//
+//namespace SupaLidlGame.Characters;
+//
+//public partial class Vendor : NPC
+//{
+// [Export]
+// public InteractionTrigger InteractionTrigger { get; set; }
+//
+// [Export(PropertyHint.File, "*.dialogue")]
+// public Resource DialogueResource { get; set; }
+//
+// [Export]
+// public string DialogueTitle { get; set; }
+//
+// public override void _Ready()
+// {
+// InteractionTrigger.Interaction += OnInteraction;
+// }
+//
+// private void OnInteraction()
+// {
+// this.GetWorld().DialogueBalloon.Start(DialogueResource, DialogueTitle);
+// }
+//}
diff --git a/Debug/DebugConsole.cs b/Debug/DebugConsole.cs
index 580f6b0..2a47fc8 100644
--- a/Debug/DebugConsole.cs
+++ b/Debug/DebugConsole.cs
@@ -34,6 +34,8 @@ public sealed partial class DebugConsole : Control
private Node ctx => Context;
+ public const double DEBUG_REFRESH_INTERVAL = 0.25;
+
public override void _Ready()
{
_entry = GetNode("%Entry");
@@ -61,6 +63,39 @@ public sealed partial class DebugConsole : Control
}
}
};
+
+ GD.Print("DebugConsole init");
+ // TODO: put this in a separate class
+ // watch godot.log
+ bool isFileLoggingEnabled = ProjectSettings
+ .GetSetting("debug/file_logging/enable_file_logging.pc")
+ .AsBool();
+
+ if (isFileLoggingEnabled)
+ {
+ GD.Print("File logging is enabled.");
+ string logPath = ProjectSettings
+ .GetSetting("debug/file_logging/log_path")
+ .AsString();
+ var fs = FileAccess.Open(logPath, FileAccess.ModeFlags.Read);
+
+ var timer = new Timer();
+ AddChild(timer);
+ timer.Timeout += () =>
+ {
+ // push
+ while (fs.GetPosition() < fs.GetLength())
+ {
+ string line = fs.GetLine();
+ _output.Text += line + "\n";
+ }
+ };
+ timer.Start(DEBUG_REFRESH_INTERVAL);
+ }
+ else
+ {
+ GD.PushWarning("File logging is not enabled.");
+ }
}
public IEnumerable SplitPath(NodePath path)
@@ -209,13 +244,21 @@ public sealed partial class DebugConsole : Control
Godot.Expression exp = new();
- string[] reserved = { "from", "set_context", "context", "set_prop", "to_node_path" };
+ string[] reserved = {
+ "from",
+ "set_context",
+ "context",
+ "set_prop",
+ "to_node_path",
+ "load",
+ };
Godot.Collections.Array reservedMap = new();
reservedMap.Add(new Callable(this, MethodName.From));
reservedMap.Add(new Callable(this, MethodName.SetContext));
reservedMap.Add(Context);
reservedMap.Add(new Callable(this, MethodName.SetProp));
reservedMap.Add(new Callable(this, MethodName.ToNodePath));
+ reservedMap.Add(new Callable(this, MethodName.Load));
var err = exp.Parse(str, reserved);
if (err != Error.Ok)
@@ -246,4 +289,9 @@ public sealed partial class DebugConsole : Control
{
Context = node;
}
+
+ private Resource Load(string path)
+ {
+ return ResourceLoader.Load(path);
+ }
}
diff --git a/Dialogue/snus-dealer.dialogue b/Dialogue/snus-dealer.dialogue
new file mode 100644
index 0000000..8572f45
--- /dev/null
+++ b/Dialogue/snus-dealer.dialogue
@@ -0,0 +1,23 @@
+~ start
+
+Snus Dealer: Hey kid, wanna buy some snus?
+- Alright. => shop
+- No, I don't think so. => END
+- Snus? => dont_snus
+
+=> END
+
+~ dont_snus
+
+Snus Dealer: You know what they say.
+Snus Dealer: If you don't snus...
+Snus Dealer: you lose.
+- Pepepains
+
+=> start
+
+~ shop
+
+do emit("EnterShop", "res://Items/Shops/SnusDealer.tres")
+
+=> END
diff --git a/Dialogue/snus-dealer.dialogue.import b/Dialogue/snus-dealer.dialogue.import
new file mode 100644
index 0000000..bc5cd9b
--- /dev/null
+++ b/Dialogue/snus-dealer.dialogue.import
@@ -0,0 +1,15 @@
+[remap]
+
+importer="dialogue_manager_compiler_12"
+type="Resource"
+uid="uid://c4n7vhoxybu70"
+path="res://.godot/imported/snus-dealer.dialogue-69dbddee28632f18888364bae03f393d.tres"
+
+[deps]
+
+source_file="res://Dialogue/snus-dealer.dialogue"
+dest_files=["res://.godot/imported/snus-dealer.dialogue-69dbddee28632f18888364bae03f393d.tres"]
+
+[params]
+
+defaults=true
diff --git a/Entities/DynamicDoor.cs b/Entities/DynamicDoor.cs
index 7670eb8..38661e7 100644
--- a/Entities/DynamicDoor.cs
+++ b/Entities/DynamicDoor.cs
@@ -58,7 +58,7 @@ public partial class DynamicDoor : StaticBody2D
foreach (var navmesh in Rebake)
{
// rebake navmesh so NPCs can correctly travel conditionally
- GD.Print("rebaking");
+ GD.Print("Dynamic door updated; rebaking navmeshes...");
navmesh.BakeNavigationPolygon();
}
}
diff --git a/Entities/TorchLamp.tscn b/Entities/TorchLamp.tscn
index 11df0f7..d858e18 100644
--- a/Entities/TorchLamp.tscn
+++ b/Entities/TorchLamp.tscn
@@ -102,12 +102,12 @@ y_sort_enabled = true
texture_filter = 1
sprite_frames = SubResource("SpriteFrames_gf7ku")
autoplay = "default"
-frame = 6
-frame_progress = 0.743234
+frame = 9
+frame_progress = 0.966501
offset = Vector2(0, -12)
[node name="PointLight2D" type="PointLight2D" parent="."]
-color = Color(1, 0.9525, 0.85, 1)
+color = Color(1, 0.811765, 0.537255, 1)
energy = 1.2
blend_mode = 2
shadow_filter_smooth = 3.0
diff --git a/Events/EventBus.cs b/Events/EventBus.cs
index 9aa4885..e158812 100644
--- a/Events/EventBus.cs
+++ b/Events/EventBus.cs
@@ -51,6 +51,9 @@ public partial class EventBus : Node
[Signal]
public delegate void ExitTransitionEventHandler();
+ [Signal]
+ public delegate void EnterShopEventHandler(string shopResourcePath);
+
public override void _Ready()
{
ProcessMode = ProcessModeEnum.Always;
diff --git a/Extensions/Timer.cs b/Extensions/Timer.cs
new file mode 100644
index 0000000..bac4566
--- /dev/null
+++ b/Extensions/Timer.cs
@@ -0,0 +1,12 @@
+using Godot;
+
+namespace SupaLidlGame.Extensions;
+
+public static class TimerExtensions
+{
+ public static void Restart(this Timer timer, double timeSec = -1)
+ {
+ timer.Stop();
+ timer.Start(timeSec);
+ }
+}
diff --git a/Items/IItemCollection.cs b/Items/IItemCollection.cs
new file mode 100644
index 0000000..c715fb9
--- /dev/null
+++ b/Items/IItemCollection.cs
@@ -0,0 +1,15 @@
+namespace SupaLidlGame.Items;
+
+public interface IItemCollection
+{
+ public System.Collections.Generic.IEnumerable GetItems();
+
+ public int Capacity { get; }
+}
+
+public interface IItemCollection : IItemCollection
+{
+ public bool Add(T item);
+
+ public bool Remove(T item);
+}
diff --git a/Items/Inventory.cs b/Items/Inventory.cs
index 772d6e4..80f286c 100644
--- a/Items/Inventory.cs
+++ b/Items/Inventory.cs
@@ -4,7 +4,7 @@ using Godot.Collections;
namespace SupaLidlGame.Items;
-public partial class Inventory : Node2D
+public partial class Inventory : Node2D, IItemCollection
{
public Character Character { get; private set; }
@@ -20,7 +20,15 @@ public partial class Inventory : Node2D
[Signal]
public delegate void UsedItemEventHandler(Item item);
- public const int MaxCapacity = 3;
+ [Signal]
+ public delegate void EquippedItemEventHandler(Item newItem, Item prevItem);
+
+ [Signal]
+ public delegate void ItemAddedEventHandler(ItemMetadata newItemMetadata);
+
+ public int Capacity { get; set; } = 30;
+
+ public const int HotbarCapacity = 3;
private Item _selectedItem;
@@ -87,7 +95,8 @@ public partial class Inventory : Node2D
return false;
}
- _selectedItem?.Unequip(Character);
+ Item prevItem = _selectedItem;
+ prevItem?.Unequip(Character);
_selectedIndex = index;
if (index >= 0)
@@ -100,6 +109,8 @@ public partial class Inventory : Node2D
_selectedItem = null;
}
+ EmitSignal(SignalName.EquippedItem, prevItem, _selectedItem);
+
GD.Print($"Inventory: {index} is new selected index.");
return true;
@@ -135,8 +146,9 @@ public partial class Inventory : Node2D
return null;
}
- public Item AddItemToHotbar(ItemMetadata metadata)
+ public Item AddToHotbar(ItemMetadata metadata)
{
+ //AddItemMetadata(metadata);
var item = metadata.Instance.Instantiate- ();
AddItem(item);
AddChild(item);
@@ -146,7 +158,7 @@ public partial class Inventory : Node2D
public Item AddItem(Item item)
{
- if (Hotbar.Count >= MaxCapacity)
+ if (Hotbar.Count >= HotbarCapacity)
{
return null;
}
@@ -157,9 +169,32 @@ public partial class Inventory : Node2D
{
Hotbar.Add(item);
}
+
return item;
}
+ public System.Collections.Generic.IEnumerable GetItems()
+ {
+ return Items;
+ }
+
+ public bool Add(ItemMetadata item)
+ {
+ if (Items.Count >= Capacity)
+ {
+ return false;
+ }
+
+ Items.Add(item);
+ EmitSignal(SignalName.ItemAdded, item);
+ return true;
+ }
+
+ public bool Remove(ItemMetadata item)
+ {
+ return Items.Remove(item);
+ }
+
public Item DropItem(Item item)
{
item.CharacterOwner = null;
diff --git a/Items/Shop.cs b/Items/Shop.cs
new file mode 100644
index 0000000..4ba249a
--- /dev/null
+++ b/Items/Shop.cs
@@ -0,0 +1,45 @@
+using Godot;
+using Godot.Collections;
+using GodotUtilities.Collections;
+using System.Linq;
+using SupaLidlGame.Utils;
+
+namespace SupaLidlGame.Items;
+
+[GlobalClass]
+public partial class Shop : Resource, IItemCollection
+{
+ [Export]
+ protected Godot.Collections.Array Entries { get; private set; }
+
+ public System.Collections.Generic.IEnumerable GetItems()
+ {
+ var mapState = World.Instance.GlobalState.MapState;
+ return Entries
+ .Where(
+ (entry) =>
+ {
+ var condition = entry.MapStateCondition;
+ if (string.IsNullOrEmpty(condition))
+ {
+ return true;
+ }
+ return mapState.GetBoolean(condition) ?? false;
+ }
+ )
+ .Select((entry) => entry.Item);
+ }
+
+ public bool Add(ShopEntry entry)
+ {
+ Entries.Add(entry);
+ return true;
+ }
+
+ public bool Remove(ShopEntry entry)
+ {
+ return Entries.Remove(entry);
+ }
+
+ public int Capacity => Entries.Count;
+}
diff --git a/Items/ShopEntry.cs b/Items/ShopEntry.cs
new file mode 100644
index 0000000..b5617ad
--- /dev/null
+++ b/Items/ShopEntry.cs
@@ -0,0 +1,23 @@
+using Godot;
+
+namespace SupaLidlGame.Items;
+
+[GlobalClass]
+public partial class ShopEntry : Resource
+{
+ public ShopEntry() : base()
+ {
+
+ }
+
+ public ShopEntry(ItemMetadata item) : base()
+ {
+ Item = item;
+ }
+
+ [Export]
+ public ItemMetadata Item { get; set; }
+
+ [Export]
+ public string MapStateCondition { get; set; }
+}
diff --git a/Items/Shops/SnusDealer.tres b/Items/Shops/SnusDealer.tres
new file mode 100644
index 0000000..ed8d52d
--- /dev/null
+++ b/Items/Shops/SnusDealer.tres
@@ -0,0 +1,54 @@
+[gd_resource type="Resource" script_class="Shop" load_steps=16 format=3 uid="uid://djqd88vdkoi6d"]
+
+[ext_resource type="Script" path="res://Items/Shop.cs" id="1_betbc"]
+[ext_resource type="Resource" uid="uid://cjsh0dcgbfn77" path="res://Items/Weapons/Bow.tres" id="1_ntroj"]
+[ext_resource type="Script" path="res://Items/ShopEntry.cs" id="2_xgvwu"]
+[ext_resource type="Resource" uid="uid://iqe6rgnb3jur" path="res://Items/Weapons/Pugio.tres" id="3_nfeft"]
+[ext_resource type="Resource" uid="uid://dkm216ug0vj2h" path="res://Items/Weapons/Shotgun.tres" id="4_aw0ju"]
+
+[sub_resource type="Resource" id="Resource_jdx0p"]
+script = ExtResource("2_xgvwu")
+Item = ExtResource("1_ntroj")
+MapStateCondition = ""
+
+[sub_resource type="Resource" id="Resource_min4b"]
+script = ExtResource("2_xgvwu")
+Item = ExtResource("3_nfeft")
+MapStateCondition = ""
+
+[sub_resource type="Resource" id="Resource_t0x08"]
+script = ExtResource("2_xgvwu")
+Item = ExtResource("4_aw0ju")
+MapStateCondition = ""
+
+[sub_resource type="Resource" id="Resource_exmab"]
+script = ExtResource("2_xgvwu")
+MapStateCondition = ""
+
+[sub_resource type="Resource" id="Resource_jlgb3"]
+script = ExtResource("2_xgvwu")
+MapStateCondition = ""
+
+[sub_resource type="Resource" id="Resource_8rd47"]
+script = ExtResource("2_xgvwu")
+MapStateCondition = ""
+
+[sub_resource type="Resource" id="Resource_pqgh5"]
+script = ExtResource("2_xgvwu")
+MapStateCondition = ""
+
+[sub_resource type="Resource" id="Resource_8mift"]
+script = ExtResource("2_xgvwu")
+MapStateCondition = ""
+
+[sub_resource type="Resource" id="Resource_4e8it"]
+script = ExtResource("2_xgvwu")
+MapStateCondition = ""
+
+[sub_resource type="Resource" id="Resource_jp7ms"]
+script = ExtResource("2_xgvwu")
+MapStateCondition = ""
+
+[resource]
+script = ExtResource("1_betbc")
+Entries = Array[Object]([SubResource("Resource_jdx0p"), SubResource("Resource_min4b"), SubResource("Resource_t0x08"), SubResource("Resource_exmab"), SubResource("Resource_jlgb3"), SubResource("Resource_8rd47"), SubResource("Resource_pqgh5"), SubResource("Resource_8mift"), SubResource("Resource_4e8it"), SubResource("Resource_jp7ms")])
diff --git a/Items/Weapons/Bow.tres b/Items/Weapons/Bow.tres
index 7e4edc9..5f2a8b1 100644
--- a/Items/Weapons/Bow.tres
+++ b/Items/Weapons/Bow.tres
@@ -1,5 +1,6 @@
-[gd_resource type="Resource" script_class="ItemMetadata" load_steps=4 format=3 uid="uid://cjsh0dcgbfn77"]
+[gd_resource type="Resource" script_class="ItemMetadata" load_steps=5 format=3 uid="uid://cjsh0dcgbfn77"]
+[ext_resource type="Texture2D" uid="uid://vjdyrv8wp7gl" path="res://Assets/Sprites/forsenLevel.png" id="1_5blro"]
[ext_resource type="Script" path="res://Utils/ScenePath.cs" id="1_haiji"]
[ext_resource type="Script" path="res://Items/ItemMetadata.cs" id="2_hjbs0"]
@@ -10,6 +11,7 @@ Path = "res://Items/Weapons/Bow.tscn"
[resource]
script = ExtResource("2_hjbs0")
Instance = SubResource("Resource_mjj1w")
+Icon = ExtResource("1_5blro")
Name = "Bow"
Description = "A bow and arrow."
BuyPrice = 0
diff --git a/Items/Weapons/ProjectileSpawner.cs b/Items/Weapons/ProjectileSpawner.cs
index 6362899..33d5ad2 100644
--- a/Items/Weapons/ProjectileSpawner.cs
+++ b/Items/Weapons/ProjectileSpawner.cs
@@ -24,6 +24,15 @@ public partial class ProjectileSpawner : Ranged
[Export]
public float ProjectileAngleDeviation { get; set; }
+ public string ProjectilePath
+ {
+ get => Projectile?.ResourcePath;
+ set
+ {
+ Projectile = GD.Load(value);
+ }
+ }
+
protected virtual void SpawnProjectile(Scenes.Map map,
Vector2 direction, float velocityModifier = 1)
{
diff --git a/Items/Weapons/Pugio.tres b/Items/Weapons/Pugio.tres
index eee0522..a141748 100644
--- a/Items/Weapons/Pugio.tres
+++ b/Items/Weapons/Pugio.tres
@@ -1,5 +1,6 @@
-[gd_resource type="Resource" script_class="ItemMetadata" load_steps=4 format=3 uid="uid://iqe6rgnb3jur"]
+[gd_resource type="Resource" script_class="ItemMetadata" load_steps=5 format=3 uid="uid://iqe6rgnb3jur"]
+[ext_resource type="Texture2D" uid="uid://vjdyrv8wp7gl" path="res://Assets/Sprites/forsenLevel.png" id="1_3ptey"]
[ext_resource type="Script" path="res://Utils/ScenePath.cs" id="1_o026a"]
[ext_resource type="Script" path="res://Items/ItemMetadata.cs" id="2_j4tmu"]
@@ -10,6 +11,7 @@ Path = "res://Items/Weapons/Pugio.tscn"
[resource]
script = ExtResource("2_j4tmu")
Instance = SubResource("Resource_abrg1")
+Icon = ExtResource("1_3ptey")
Name = "Pugio"
Description = "A sidearm dagger."
BuyPrice = 0
diff --git a/Items/Weapons/Shotgun.tres b/Items/Weapons/Shotgun.tres
new file mode 100644
index 0000000..651fe91
--- /dev/null
+++ b/Items/Weapons/Shotgun.tres
@@ -0,0 +1,18 @@
+[gd_resource type="Resource" script_class="ItemMetadata" load_steps=5 format=3 uid="uid://dkm216ug0vj2h"]
+
+[ext_resource type="Script" path="res://Utils/ScenePath.cs" id="1_owc5r"]
+[ext_resource type="Texture2D" uid="uid://vjdyrv8wp7gl" path="res://Assets/Sprites/forsenLevel.png" id="1_v76vk"]
+[ext_resource type="Script" path="res://Items/ItemMetadata.cs" id="1_vtlr1"]
+
+[sub_resource type="Resource" id="Resource_6sxq7"]
+script = ExtResource("1_owc5r")
+Path = "res://Items/Weapons/Shotgun.tscn"
+
+[resource]
+script = ExtResource("1_vtlr1")
+Instance = SubResource("Resource_6sxq7")
+Icon = ExtResource("1_v76vk")
+Name = "Shotgun"
+Description = ""
+BuyPrice = 1887
+SellPrice = 0
diff --git a/Items/Weapons/Shotgun.tscn b/Items/Weapons/Shotgun.tscn
index 1f84f6d..a7beaf0 100644
--- a/Items/Weapons/Shotgun.tscn
+++ b/Items/Weapons/Shotgun.tscn
@@ -1,9 +1,10 @@
-[gd_scene load_steps=26 format=3 uid="uid://d1d4vg7we5rjr"]
+[gd_scene load_steps=27 format=3 uid="uid://d1d4vg7we5rjr"]
[ext_resource type="Script" path="res://Items/Weapons/ProjectileSpawner.cs" id="1_4xyyt"]
[ext_resource type="Script" path="res://State/Weapon/WeaponStateMachine.cs" id="2_ag6rd"]
[ext_resource type="PackedScene" uid="uid://da1do2r2pbayb" path="res://Entities/ShotgunPellet.tscn" id="2_p3wx2"]
[ext_resource type="Script" path="res://State/Weapon/RangedIdleState.cs" id="3_dd6bh"]
+[ext_resource type="Resource" uid="uid://dkm216ug0vj2h" path="res://Items/Weapons/Shotgun.tres" id="3_ju036"]
[ext_resource type="Script" path="res://State/Weapon/RangedFireState.cs" id="4_bwqd6"]
[ext_resource type="Texture2D" uid="uid://b1omx2kdb2x1n" path="res://Assets/Sprites/Items/shotgun.png" id="5_g8d45"]
[ext_resource type="Texture2D" uid="uid://c1a7lvb4uuwfy" path="res://Assets/Sprites/Particles/circle-16.png" id="6_va8ee"]
@@ -216,11 +217,9 @@ curve = SubResource("Curve_mqgo6")
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_uexo4"]
particle_flag_disable_z = true
flatness = 1.0
-gravity = Vector3(0, -4, 0)
initial_velocity_min = -8.0
initial_velocity_max = 24.0
-orbit_velocity_min = 0.0
-orbit_velocity_max = 0.0
+gravity = Vector3(0, -4, 0)
scale_min = 0.75
scale_curve = SubResource("CurveTexture_3omj5")
color_ramp = SubResource("GradientTexture1D_83h4s")
@@ -236,13 +235,11 @@ gradient = SubResource("Gradient_jks7f")
particle_flag_disable_z = true
direction = Vector3(-1, -2, 0)
spread = 10.0
-gravity = Vector3(0, 64, 0)
initial_velocity_min = 16.0
initial_velocity_max = 32.0
angular_velocity_min = 30.0
angular_velocity_max = 60.0
-orbit_velocity_min = 0.0
-orbit_velocity_max = 0.0
+gravity = Vector3(0, 64, 0)
scale_min = 2.0
scale_max = 2.0
color_ramp = SubResource("GradientTexture1D_eeskk")
@@ -259,6 +256,7 @@ Damage = 12.0
UseTime = 1.5
InitialVelocity = 220.0
PlayerLevelGain = 0.5
+Metadata = ExtResource("3_ju036")
[node name="State" type="Node" parent="." node_paths=PackedStringArray("InitialState")]
script = ExtResource("2_ag6rd")
diff --git a/Scenes/Level.tscn b/Scenes/Level.tscn
index 09ca743..72cdf09 100644
--- a/Scenes/Level.tscn
+++ b/Scenes/Level.tscn
@@ -3,11 +3,10 @@
[ext_resource type="Script" path="res://Utils/World.cs" id="1_1k6ew"]
[ext_resource type="PackedScene" uid="uid://c271rdjhd1gfo" path="res://UI/Base.tscn" id="2_mm0qt"]
-[node name="World" type="Node2D" node_paths=PackedStringArray("MusicPlayer", "DialogueBalloon")]
+[node name="World" type="Node2D" node_paths=PackedStringArray("MusicPlayer")]
process_mode = 3
script = ExtResource("1_1k6ew")
MusicPlayer = NodePath("MusicPlayer")
-DialogueBalloon = NodePath("CanvasLayer/SubViewportContainer/UIViewport/DialogBalloon")
[node name="CanvasLayer" parent="." instance=ExtResource("2_mm0qt")]
@@ -21,6 +20,7 @@ anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
+mouse_filter = 2
[node name="MusicPlayer" type="AudioStreamPlayer" parent="."]
bus = &"Music"
diff --git a/Scenes/Maps/ForestNew.tscn b/Scenes/Maps/ForestNew.tscn
index 369dba3..5bfe142 100644
--- a/Scenes/Maps/ForestNew.tscn
+++ b/Scenes/Maps/ForestNew.tscn
@@ -1,4 +1,4 @@
-[gd_scene load_steps=22 format=3 uid="uid://cumi1cbg6xfdd"]
+[gd_scene load_steps=23 format=3 uid="uid://cumi1cbg6xfdd"]
[ext_resource type="PackedScene" uid="uid://clwv2owvk6abe" path="res://Scenes/BaseMap.tscn" id="1_oy3cp"]
[ext_resource type="Texture2D" uid="uid://c70cn53osy56w" path="res://Assets/Sprites/dev-tileset.png" id="2_2gdry"]
@@ -11,6 +11,7 @@
[ext_resource type="Texture2D" uid="uid://chwwo6vmf8iri" path="res://Assets/Sprites/Props/tree-autumn-leaves-2.png" id="8_4laic"]
[ext_resource type="Texture2D" uid="uid://dhf2f5a1ty502" path="res://Assets/Sprites/Props/tree-autumn-leaves-1.png" id="8_hyhpu"]
[ext_resource type="Texture2D" uid="uid://crvbsxrda5gcj" path="res://Assets/Sprites/Props/tree-autumn-leaves-3.png" id="9_exvkw"]
+[ext_resource type="PackedScene" uid="uid://rd08pot25h00" path="res://Characters/SnusDealer.tscn" id="12_eyny2"]
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_s1h2f"]
texture = ExtResource("2_2gdry")
@@ -458,8 +459,8 @@ cell_size = 16.0
agent_radius = 8.0
[sub_resource type="NavigationPolygon" id="NavigationPolygon_8xwo5"]
-vertices = PackedVector2Array(280, -232, 280, -120, 276, -120, 216, -232, 216, -40, 276, -72, 296, -72, 296, -88, 392, -88, 392, -136, 472, -136, 472, -40, 440, -40, 440, 8, 488, 8, 488, 24, 440, 24, 440, 72, 424, 72, 424, 24, 424, 8, 376, 24, 376, 8, 424, -40, 296, -40, 296, -56, 276, -56, 276, -40)
-polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3), PackedInt32Array(4, 3, 2, 5), PackedInt32Array(6, 7, 8), PackedInt32Array(8, 9, 10, 11, 12), PackedInt32Array(13, 14, 15, 16), PackedInt32Array(16, 17, 18, 19), PackedInt32Array(13, 16, 19, 20), PackedInt32Array(19, 21, 22, 20), PackedInt32Array(12, 13, 20, 23), PackedInt32Array(8, 12, 23), PackedInt32Array(8, 23, 24, 25), PackedInt32Array(6, 8, 25), PackedInt32Array(5, 6, 25, 26), PackedInt32Array(4, 5, 26), PackedInt32Array(4, 26, 27)])
+vertices = PackedVector2Array(392, -88, 392, -136, 472, -136, 472, -40, 440, -40, 440, 8, 488, 8, 488, 24, 440, 24, 440, 72, 424, 72, 424, 24, 424, 8, 376, 24, 376, 8, 424, -40, 216, -40, 280, -88, 216, -232, 280, -232)
+polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3, 4), PackedInt32Array(5, 6, 7, 8), PackedInt32Array(8, 9, 10, 11), PackedInt32Array(5, 8, 11, 12), PackedInt32Array(11, 13, 14, 12), PackedInt32Array(4, 5, 12, 15), PackedInt32Array(0, 4, 15), PackedInt32Array(0, 15, 16, 17), PackedInt32Array(17, 16, 18, 19)])
outlines = Array[PackedVector2Array]([PackedVector2Array(208, -240, 208, -32, 416, -32, 416.001, -15.9759, 368.001, -15.9787, 368, 32, 416, 32, 416, 80, 448, 80, 448, 32, 496, 32, 496.001, -15.9713, 448.001, -15.9741, 448, -32, 480, -32, 480.008, -143.972, 384.008, -143.978, 384.006, -95.9778, 288, -96, 288, -240)])
source_geometry_mode = 1
source_geometry_group_name = &"navigation"
@@ -479,6 +480,9 @@ Markers = NodePath("Markers")
AreaName = "Forest"
MapName = "Forest"
+[node name="CanvasModulate" parent="." index="7"]
+position = Vector2(0, 1)
+
[node name="StaticBody2D" type="StaticBody2D" parent="Props" index="0"]
y_sort_enabled = true
position = Vector2(0, -96)
@@ -843,7 +847,14 @@ position = Vector2(1272, 0)
[node name="Campfire" parent="Entities" index="0" instance=ExtResource("3_ve4i2")]
position = Vector2(-24, -8)
-[node name="Areas" parent="." index="3"]
+[node name="SnusDealer" parent="Entities" index="1" node_paths=PackedStringArray("ThinkerStateMachine", "Sprite", "Inventory", "StateMachine") instance=ExtResource("12_eyny2")]
+position = Vector2(79, -6)
+ThinkerStateMachine = NodePath("Thinker")
+Sprite = NodePath("Sprites/Sprite")
+Inventory = NodePath("Inventory")
+StateMachine = NodePath("StateMachine")
+
+[node name="Areas" parent="." index="10"]
visible = false
[node name="Main" type="NavigationRegion2D" parent="Areas" index="0"]
diff --git a/Shaders/Wipe.gdshader b/Shaders/Wipe.gdshader
index 7cebd6d..64f052c 100644
--- a/Shaders/Wipe.gdshader
+++ b/Shaders/Wipe.gdshader
@@ -3,9 +3,5 @@ shader_type canvas_item;
uniform float amount : hint_range(0.5, 1.0, 0.01) = 1;
void fragment() {
- // Place fragment code here.
- //COLOR = texture(TEXTURE, UV);
- if (UV.x > amount || UV.x < 1.0 - amount) {
- COLOR.a = 0.0;
- }
+ COLOR.a *= step(UV.x, amount) * step(1.0 - amount, UV.x);
}
diff --git a/Shaders/WipeXY.gdshader b/Shaders/WipeXY.gdshader
new file mode 100644
index 0000000..cbe13a9
--- /dev/null
+++ b/Shaders/WipeXY.gdshader
@@ -0,0 +1,10 @@
+shader_type canvas_item;
+
+uniform float x_amount : hint_range(0.5, 1.0, 0.01) = 1;
+uniform float y_amount : hint_range(0.5, 1.0, 0.01) = 0.5;
+
+void fragment() {
+ COLOR.a *= step(UV.x, x_amount) * step(1.0 - x_amount, UV.x);
+ //COLOR.a *= step(1.0 - y_amount, UV.y) * step(UV.y, y_amount);
+ COLOR.a *= 1.0 - (step(1.0 - y_amount, UV.y) * step(UV.y, y_amount));
+}
diff --git a/State/Global/GlobalState.cs b/State/Global/GlobalState.cs
index a4fe117..8c56b17 100644
--- a/State/Global/GlobalState.cs
+++ b/State/Global/GlobalState.cs
@@ -14,6 +14,8 @@ public partial class GlobalState : Node
[Export]
public Stats Stats { get; set; }
+ public static GlobalState Instance { get; private set; }
+
[Export]
public GameSettings Settings { get; set; }
@@ -30,6 +32,13 @@ public partial class GlobalState : Node
public override void _Ready()
{
+ if (Instance != null)
+ {
+ throw new MultipleSingletonsException();
+ }
+
+ Instance = this;
+
ProcessMode = ProcessModeEnum.Always;
LoadSettings();
}
diff --git a/State/Global/MapState.cs b/State/Global/MapState.cs
index 4c3824b..6b8bf71 100644
--- a/State/Global/MapState.cs
+++ b/State/Global/MapState.cs
@@ -45,4 +45,13 @@ public partial class MapState : Resource
}
}
}
+
+ public bool? GetBoolean(string key)
+ {
+ if (_state[key].VariantType == Variant.Type.Bool)
+ {
+ return (bool)_state[key];
+ }
+ return null;
+ }
}
diff --git a/State/Thinker/VendorIdle.cs b/State/Thinker/VendorIdle.cs
new file mode 100644
index 0000000..d0154f6
--- /dev/null
+++ b/State/Thinker/VendorIdle.cs
@@ -0,0 +1,17 @@
+using Godot;
+using GodotUtilities;
+
+namespace SupaLidlGame.State.Thinker;
+
+public partial class VendorIdle : ThinkerState
+{
+ public override ThinkerState Think()
+ {
+ var bestNeutral = NPC.FindBestNeutral();
+ if (bestNeutral is not null)
+ {
+ NPC.Target = bestNeutral.Position - NPC.Position;
+ }
+ return null;
+ }
+}
diff --git a/UI/Base.tscn b/UI/Base.tscn
index c9699d4..bbc7b12 100644
--- a/UI/Base.tscn
+++ b/UI/Base.tscn
@@ -1,13 +1,13 @@
[gd_scene load_steps=14 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://sfs8dpfitpdu" path="res://UI/Hotbar.tscn" id="5_mmp18"]
+[ext_resource type="PackedScene" uid="uid://sfs8dpfitpdu" path="res://UI/Inventory/Hotbar.tscn" id="5_mmp18"]
[ext_resource type="PackedScene" uid="uid://d3q1yu3n7cqfj" path="res://UI/SceneTransition.tscn" id="6_j0nhv"]
+[ext_resource type="PackedScene" uid="uid://cyggkyqosjk36" path="res://UI/Inventory/ShopMenu.tscn" id="8_ep3ae"]
[ext_resource type="PackedScene" uid="uid://2afbrf8asy2a" path="res://UI/PostProcessing/Vignette.tscn" id="9_p1ubd"]
[ext_resource type="PackedScene" uid="uid://b1wsryv4bn0cn" path="res://UI/PostProcessing/StunEffect.tscn" id="10_646ma"]
[ext_resource type="Shader" path="res://Shaders/Grayscale.gdshader" id="11_w4gn1"]
@@ -20,6 +20,26 @@ shader_parameter/grayscale_intensity = 0.0
[node name="BaseUI" type="CanvasLayer"]
process_mode = 3
+[node name="PostProcessing" type="CanvasLayer" parent="."]
+
+[node name="Vignette" parent="PostProcessing" instance=ExtResource("9_p1ubd")]
+
+[node name="StunEffect" parent="PostProcessing" instance=ExtResource("10_646ma")]
+
+[node name="Sprite2D" type="TextureRect" parent="PostProcessing"]
+visible = false
+material = SubResource("ShaderMaterial_kbd61")
+anchors_preset = 3
+anchor_left = 1.0
+anchor_top = 1.0
+anchor_right = 1.0
+anchor_bottom = 1.0
+offset_left = -128.0
+offset_top = -128.0
+grow_horizontal = 0
+grow_vertical = 0
+texture = ExtResource("12_tyv35")
+
[node name="SubViewportContainer" type="SubViewportContainer" parent="."]
anchors_preset = 15
anchor_right = 1.0
@@ -36,9 +56,6 @@ handle_input_locally = false
size = Vector2i(640, 360)
render_target_update_mode = 4
-[node name="DialogBalloon" parent="SubViewportContainer/UIViewport" instance=ExtResource("1_atjb1")]
-layer = 4
-
[node name="MainUILayer" type="CanvasLayer" parent="SubViewportContainer/UIViewport"]
layer = 3
@@ -52,6 +69,7 @@ grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
+mouse_filter = 2
script = ExtResource("2_b4b6l")
[node name="Top" type="HBoxContainer" parent="SubViewportContainer/UIViewport/MainUILayer/Main"]
@@ -78,7 +96,6 @@ layout_mode = 2
layout_mode = 2
[node name="Margin2" type="MarginContainer" parent="SubViewportContainer/UIViewport/MainUILayer/Main/Top/Left/VBoxContainer"]
-visible = false
layout_mode = 2
theme_override_constants/margin_left = 16
theme_override_constants/margin_top = 16
@@ -92,6 +109,23 @@ theme_override_constants/margin_right = 16
layout_mode = 2
_slots = [NodePath("InventorySlot"), NodePath("InventorySlot2"), NodePath("InventorySlot3")]
+[node name="BoxContainer" type="HBoxContainer" parent="SubViewportContainer/UIViewport/MainUILayer/Main"]
+layout_mode = 1
+anchors_preset = 11
+anchor_left = 1.0
+anchor_right = 1.0
+anchor_bottom = 1.0
+offset_left = -236.0
+grow_horizontal = 0
+grow_vertical = 2
+alignment = 2
+
+[node name="ShopMenu" parent="SubViewportContainer/UIViewport/MainUILayer/Main/BoxContainer" node_paths=PackedStringArray("_inventoryGrid") instance=ExtResource("8_ep3ae")]
+unique_name_in_owner = true
+visible = false
+layout_mode = 2
+_inventoryGrid = NodePath("PanelContainer/VBoxContainer/ScrollContainer/InventoryGrid")
+
[node name="Bottom" type="HBoxContainer" parent="SubViewportContainer/UIViewport/MainUILayer/Main"]
layout_mode = 1
anchors_preset = 12
@@ -107,27 +141,13 @@ alignment = 1
visible = false
layout_mode = 2
+[node name="VBoxContainer" type="VBoxContainer" parent="SubViewportContainer/UIViewport/MainUILayer/Main"]
+visible = false
+layout_mode = 0
+offset_right = 40.0
+offset_bottom = 40.0
+
[node name="LocationDisplay" parent="SubViewportContainer/UIViewport/MainUILayer" instance=ExtResource("5_cr6vo")]
[node name="SceneTransition" parent="SubViewportContainer/UIViewport/MainUILayer" instance=ExtResource("6_j0nhv")]
z_index = 1
-
-[node name="PostProcessing" type="CanvasLayer" parent="."]
-
-[node name="Vignette" parent="PostProcessing" instance=ExtResource("9_p1ubd")]
-
-[node name="StunEffect" parent="PostProcessing" instance=ExtResource("10_646ma")]
-
-[node name="Sprite2D" type="TextureRect" parent="PostProcessing"]
-visible = false
-material = SubResource("ShaderMaterial_kbd61")
-anchors_preset = 3
-anchor_left = 1.0
-anchor_top = 1.0
-anchor_right = 1.0
-anchor_bottom = 1.0
-offset_left = -128.0
-offset_top = -128.0
-grow_horizontal = 0
-grow_vertical = 0
-texture = ExtResource("12_tyv35")
diff --git a/UI/Hotbar.tscn b/UI/Hotbar.tscn
deleted file mode 100644
index 371c5c6..0000000
--- a/UI/Hotbar.tscn
+++ /dev/null
@@ -1,25 +0,0 @@
-[gd_scene load_steps=3 format=3 uid="uid://sfs8dpfitpdu"]
-
-[ext_resource type="Script" path="res://UI/Hotbar.cs" id="1_2sak2"]
-[ext_resource type="PackedScene" uid="uid://ctad0dkoyw8ad" path="res://UI/InventorySlot.tscn" id="1_ct3cn"]
-
-[node name="Hotbar" type="GridContainer" node_paths=PackedStringArray("_slots")]
-anchors_preset = 1
-anchor_left = 1.0
-anchor_right = 1.0
-offset_left = -112.0
-offset_bottom = 32.0
-grow_horizontal = 0
-theme_override_constants/h_separation = 8
-columns = 3
-script = ExtResource("1_2sak2")
-_slots = [NodePath("InventorySlot"), NodePath("InventorySlot2"), NodePath("InventorySlot3")]
-
-[node name="InventorySlot" parent="." instance=ExtResource("1_ct3cn")]
-layout_mode = 2
-
-[node name="InventorySlot2" parent="." instance=ExtResource("1_ct3cn")]
-layout_mode = 2
-
-[node name="InventorySlot3" parent="." instance=ExtResource("1_ct3cn")]
-layout_mode = 2
diff --git a/UI/IModal.cs b/UI/IModal.cs
new file mode 100644
index 0000000..9b7d172
--- /dev/null
+++ b/UI/IModal.cs
@@ -0,0 +1,6 @@
+namespace SupaLidlGame.UI;
+
+public interface IModal
+{
+ public void HideModal();
+}
diff --git a/UI/Hotbar.cs b/UI/Inventory/Hotbar.cs
similarity index 83%
rename from UI/Hotbar.cs
rename to UI/Inventory/Hotbar.cs
index 5b311da..afbf0c4 100644
--- a/UI/Hotbar.cs
+++ b/UI/Inventory/Hotbar.cs
@@ -1,7 +1,6 @@
using Godot;
-using SupaLidlGame.Items;
-namespace SupaLidlGame.UI;
+namespace SupaLidlGame.UI.Inventory;
public partial class Hotbar : GridContainer
{
@@ -13,7 +12,7 @@ public partial class Hotbar : GridContainer
Events.EventBus.Instance.PlayerInventoryUpdate += OnInventoryUpdate;
}
- public void OnInventoryUpdate(Inventory inventory)
+ public void OnInventoryUpdate(Items.Inventory inventory)
{
GD.Print($"UPDATE: {inventory.SelectedIndex} is selected index.");
for (int i = 0; i < 3; i++)
diff --git a/UI/Inventory/Hotbar.tscn b/UI/Inventory/Hotbar.tscn
new file mode 100644
index 0000000..2ef8600
--- /dev/null
+++ b/UI/Inventory/Hotbar.tscn
@@ -0,0 +1,30 @@
+[gd_scene load_steps=4 format=3 uid="uid://sfs8dpfitpdu"]
+
+[ext_resource type="Script" path="res://UI/Inventory/Hotbar.cs" id="1_2sak2"]
+[ext_resource type="PackedScene" uid="uid://dmvu2hjyrwc1y" path="res://UI/Inventory/HotbarSlot.tscn" id="2_3axfe"]
+
+[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_6jbma"]
+
+[node name="Hotbar" type="GridContainer" node_paths=PackedStringArray("_slots")]
+anchors_preset = 1
+anchor_left = 1.0
+anchor_right = 1.0
+offset_left = -112.0
+offset_bottom = 32.0
+grow_horizontal = 0
+theme_override_constants/h_separation = 8
+columns = 3
+script = ExtResource("1_2sak2")
+_slots = [NodePath("InventorySlot"), NodePath("InventorySlot2"), NodePath("InventorySlot3")]
+
+[node name="InventorySlot" parent="." instance=ExtResource("2_3axfe")]
+layout_mode = 2
+theme_override_styles/panel = SubResource("StyleBoxEmpty_6jbma")
+
+[node name="InventorySlot2" parent="." instance=ExtResource("2_3axfe")]
+layout_mode = 2
+theme_override_styles/panel = SubResource("StyleBoxEmpty_6jbma")
+
+[node name="InventorySlot3" parent="." instance=ExtResource("2_3axfe")]
+layout_mode = 2
+theme_override_styles/panel = SubResource("StyleBoxEmpty_6jbma")
diff --git a/UI/Inventory/HotbarSlot.cs b/UI/Inventory/HotbarSlot.cs
new file mode 100644
index 0000000..e69de29
diff --git a/UI/Inventory/HotbarSlot.tscn b/UI/Inventory/HotbarSlot.tscn
new file mode 100644
index 0000000..ba63588
--- /dev/null
+++ b/UI/Inventory/HotbarSlot.tscn
@@ -0,0 +1,16 @@
+[gd_scene load_steps=4 format=3 uid="uid://dmvu2hjyrwc1y"]
+
+[ext_resource type="PackedScene" uid="uid://ctad0dkoyw8ad" path="res://UI/Inventory/InventorySlot.tscn" id="1_fb62b"]
+[ext_resource type="Texture2D" uid="uid://dp7osg05ip5oo" path="res://Assets/Sprites/sword.png" id="2_aqbyo"]
+[ext_resource type="Texture2D" uid="uid://dc1gcsbhkchvg" path="res://Assets/Sprites/UI/hotbar-active.png" id="2_bcv71"]
+
+[node name="InventorySlot" instance=ExtResource("1_fb62b")]
+
+[node name="TextureRect" parent="." index="0"]
+texture = ExtResource("2_aqbyo")
+expand_mode = 3
+
+[node name="SelectedFrame" type="NinePatchRect" parent="." index="2"]
+visible = false
+layout_mode = 2
+texture = ExtResource("2_bcv71")
diff --git a/UI/Inventory/InventoryGrid.cs b/UI/Inventory/InventoryGrid.cs
new file mode 100644
index 0000000..124b536
--- /dev/null
+++ b/UI/Inventory/InventoryGrid.cs
@@ -0,0 +1,172 @@
+using Godot;
+using GodotUtilities;
+using SupaLidlGame.Items;
+
+namespace SupaLidlGame.UI.Inventory;
+
+public partial class InventoryGrid : GridContainer
+{
+ private SupaLidlGame.Items.IItemCollection _source;
+
+ [Export]
+ private PackedScene _slotScene;
+
+ public ButtonGroup ButtonGroup { get; private set; }
+
+ public SupaLidlGame.Items.IItemCollection Source
+ {
+ get => _source;
+ set
+ {
+ GD.Print("Set InventoryGrid source");
+ _source = value;
+ Redraw();
+ }
+ }
+
+ [Signal]
+ public delegate void SlotFocusedEventHandler(InventorySlot slot);
+
+ [Signal]
+ public delegate void SlotUnfocusedEventHandler(InventorySlot slot);
+
+ [Signal]
+ public delegate void SlotSelectedEventHandler(InventorySlot slot);
+
+ public InventoryGrid()
+ {
+ ButtonGroup = new();
+ }
+
+ public void Redraw()
+ {
+ GD.Print("Redrawing inventory grid...");
+
+ if (_source is null)
+ {
+ this.QueueFreeChildren();
+ }
+
+ var children = GetChildren();
+
+ for (int i = children.Count; i < _source.Capacity; i++)
+ {
+ AddInventorySlot();
+ }
+
+ for (int i = children.Count - 1; i >= _source.Capacity; i--)
+ {
+ children[i].QueueFree();
+ }
+
+ children = GetChildren();
+
+ // iterate through items and update the grid
+ using (var items = _source.GetItems().GetEnumerator())
+ {
+ GD.Print("Updating items...");
+ int i;
+ for (i = 0; items.MoveNext(); i++)
+ {
+ InventorySlot slot = children[i] as InventorySlot;
+
+ ItemMetadata item = items.Current;
+ slot.Item = item;
+ }
+
+ // make remaining slots display empty
+ for (; i < _source.Capacity; i++)
+ {
+ InventorySlot slot = children[i] as InventorySlot;
+
+ slot.Item = null;
+ }
+ }
+
+ for (int i = 0; i < children.Count; i++)
+ {
+ var child = children[i] as Control;
+ if (i > 0)
+ {
+ child.FocusPrevious = child.GetPathTo(children[i - 1]);
+ }
+ if (i < children.Count - 1)
+ {
+ child.FocusNext = child.GetPathTo(children[i + 1]);
+ }
+ }
+
+ if (children.Count > 0)
+ {
+ var button = children[0] as Button;
+ button.ButtonPressed = true;
+ button.GrabFocus();
+ }
+ }
+
+ private InventorySlot AddInventorySlot()
+ {
+ var slot = _slotScene.Instantiate();
+ AddChild(slot);
+ slot.ButtonGroup = ButtonGroup;
+
+ void focusedHandler()
+ {
+ EmitSignal(SignalName.SlotFocused, slot);
+ }
+
+ void unfocusedHandler()
+ {
+ EmitSignal(SignalName.SlotUnfocused, slot);
+ }
+
+ void toggledHandler()
+ {
+ EmitSignal(SignalName.SlotSelected, slot);
+ }
+
+ slot.Connect(
+ InventorySlot.SignalName.FocusEntered,
+ Callable.From(focusedHandler)
+ );
+
+ slot.Connect(
+ InventorySlot.SignalName.FocusExited,
+ Callable.From(unfocusedHandler)
+ );
+
+ slot.Connect(
+ InventorySlot.SignalName.MouseEntered,
+ Callable.From(focusedHandler)
+ );
+
+ slot.Connect(
+ InventorySlot.SignalName.MouseExited,
+ Callable.From(unfocusedHandler)
+ );
+
+ slot.Connect(
+ InventorySlot.SignalName.Pressed,
+ Callable.From(toggledHandler)
+ );
+
+ return slot;
+ }
+
+ private void RemoveInventorySlot(InventorySlot slot)
+ {
+ RemoveChild(slot);
+ }
+
+ public bool GrabSlotFocus()
+ {
+ var children = GetChildren();
+ if (children.Count > 0)
+ {
+ var button = children[0] as Button;
+ button.GrabFocus();
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/UI/Inventory/InventoryGrid.tscn b/UI/Inventory/InventoryGrid.tscn
new file mode 100644
index 0000000..291e659
--- /dev/null
+++ b/UI/Inventory/InventoryGrid.tscn
@@ -0,0 +1,14 @@
+[gd_scene load_steps=3 format=3 uid="uid://chmokkxsy5vas"]
+
+[ext_resource type="Script" path="res://UI/Inventory/InventoryGrid.cs" id="1_7128g"]
+[ext_resource type="PackedScene" uid="uid://ctad0dkoyw8ad" path="res://UI/Inventory/InventorySlot.tscn" id="2_b6vp8"]
+
+[node name="InventoryGrid" type="GridContainer"]
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+columns = 5
+script = ExtResource("1_7128g")
+_slotScene = ExtResource("2_b6vp8")
diff --git a/UI/Inventory/InventorySlot.cs b/UI/Inventory/InventorySlot.cs
new file mode 100644
index 0000000..8d9bda6
--- /dev/null
+++ b/UI/Inventory/InventorySlot.cs
@@ -0,0 +1,95 @@
+using Godot;
+using GodotUtilities;
+using GodotUtilities.SourceGenerators;
+
+namespace SupaLidlGame.UI.Inventory;
+
+public partial class InventorySlot : Button
+{
+ private bool _isSelected = false;
+
+ public bool IsSelected
+ {
+ get => _isSelected;
+ set
+ {
+ _isSelected = value;
+ if (_selectedFrame is not null)
+ {
+ //_selectedFrame.Visible = _isSelected;
+ //_frame.Visible = !_isSelected;
+ }
+ }
+ }
+
+ [Export]
+ public bool UseFocusAsSelected { get; set; } = true;
+
+ private TextureRect _textureRect;
+
+ private NinePatchRect _frame;
+
+ private NinePatchRect _selectedFrame;
+
+ private static Texture2D _placeholderTexture;
+
+ private Items.ItemMetadata _item;
+
+ public Items.ItemMetadata Item
+ {
+ get => _item;
+ set
+ {
+ _item = value;
+
+ if (_item is null)
+ {
+ //_textureRect.Texture = null;
+ Icon = null;
+ }
+ else
+ {
+ //_textureRect.Texture = _item.Icon;
+ Icon = _item.Icon;
+ }
+ }
+ }
+
+ static InventorySlot()
+ {
+ _placeholderTexture = ResourceLoader.Load(
+ "res://Assets/Sprites/UI/hotbar-inactive.png");
+ }
+
+ public override void _Ready()
+ {
+ _textureRect = GetNode("TextureRect");
+ _frame = GetNode("Frame");
+ _selectedFrame = GetNode("SelectedFrame");
+
+ if (Item is null)
+ {
+ // do this to reset the icon
+ Item = null;
+ }
+
+ if (UseFocusAsSelected)
+ {
+ void focusEntered()
+ {
+ IsSelected = true;
+ }
+
+ void focusExited()
+ {
+ IsSelected = false;
+ }
+
+ Connect(SignalName.FocusEntered, Callable.From(focusEntered));
+ Connect(SignalName.FocusExited, Callable.From(focusExited));
+
+ Connect(SignalName.MouseEntered, Callable.From(focusEntered));
+ Connect(SignalName.MouseExited, Callable.From(focusExited));
+ }
+ }
+}
diff --git a/UI/Inventory/InventorySlot.tscn b/UI/Inventory/InventorySlot.tscn
new file mode 100644
index 0000000..275b23e
--- /dev/null
+++ b/UI/Inventory/InventorySlot.tscn
@@ -0,0 +1,41 @@
+[gd_scene load_steps=8 format=3 uid="uid://ctad0dkoyw8ad"]
+
+[ext_resource type="Script" path="res://UI/Inventory/InventorySlot.cs" id="1_fju5i"]
+[ext_resource type="Theme" uid="uid://cksjbu3vrup5" path="res://UI/Themes/supalidl.tres" id="1_wnu7u"]
+[ext_resource type="StyleBox" uid="uid://nvb4etac7ee2" path="res://UI/Themes/InventorySlotButtonFocus.tres" id="2_3wx0v"]
+[ext_resource type="Texture2D" uid="uid://dc1gcsbhkchvg" path="res://Assets/Sprites/UI/hotbar-active.png" id="2_m56j3"]
+[ext_resource type="StyleBox" uid="uid://pqtn0115bqtp" path="res://UI/Themes/InventorySlotButtonPressed.tres" id="3_46bp6"]
+[ext_resource type="StyleBox" uid="uid://cfqp0ycwvwx7c" path="res://UI/Themes/InventorySlotButtonNormal.tres" id="4_cc2jo"]
+
+[sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_ajutj"]
+size = Vector2(24, 24)
+
+[node name="InventorySlot" type="Button"]
+custom_minimum_size = Vector2(32, 32)
+theme = ExtResource("1_wnu7u")
+theme_type_variation = &"InventorySlotButton"
+theme_override_styles/focus = ExtResource("2_3wx0v")
+theme_override_styles/pressed = ExtResource("3_46bp6")
+theme_override_styles/normal = ExtResource("4_cc2jo")
+toggle_mode = true
+action_mode = 0
+icon_alignment = 1
+script = ExtResource("1_fju5i")
+
+[node name="TextureRect" type="TextureRect" parent="."]
+visible = false
+layout_mode = 2
+offset_right = 32.0
+offset_bottom = 32.0
+mouse_filter = 2
+texture = SubResource("PlaceholderTexture2D_ajutj")
+expand_mode = 2
+stretch_mode = 3
+
+[node name="Frame" type="NinePatchRect" parent="."]
+visible = false
+self_modulate = Color(1, 1, 1, 0.5)
+layout_mode = 2
+offset_right = 32.0
+offset_bottom = 32.0
+texture = ExtResource("2_m56j3")
diff --git a/UI/Inventory/ItemTooltip.cs b/UI/Inventory/ItemTooltip.cs
new file mode 100644
index 0000000..dcf6f08
--- /dev/null
+++ b/UI/Inventory/ItemTooltip.cs
@@ -0,0 +1,44 @@
+using Godot;
+using SupaLidlGame.Items;
+
+namespace SupaLidlGame.UI.Inventory;
+
+public partial class ItemTooltip : Control
+{
+ private ItemMetadata _item;
+
+ public ItemMetadata Item
+ {
+ get => _item;
+ set
+ {
+ _item = value;
+ if (IsNodeReady())
+ {
+ Render();
+ }
+ }
+ }
+
+ public override void _Ready()
+ {
+ Render();
+ }
+
+ public void Render()
+ {
+ if (_item is null)
+ {
+ GetNode