From 77e93fe93f1d59dee8ebcde065323e6054fea457 Mon Sep 17 00:00:00 2001 From: HumanoidSandvichDispenser Date: Sat, 1 Jun 2024 14:21:34 -0700 Subject: [PATCH] Add shop UI --- Items/Shops/SnusDealer.tres | 44 ++++++- UI/Inventory/Hotbar.tscn | 7 +- UI/Inventory/HotbarSlot.cs | 32 ----- UI/Inventory/HotbarSlot.tscn | 7 +- UI/Inventory/InventoryGrid.cs | 141 +++++++++++++++++++--- UI/Inventory/InventorySlot.cs | 40 +++++- UI/Inventory/InventorySlot.tscn | 29 ++++- UI/Inventory/ItemTooltip.cs | 44 +++++++ UI/Inventory/ItemTooltip.tscn | 127 +++++++++++++++++++ UI/Inventory/ShopItem.tscn | 44 ------- UI/Inventory/ShopMenu.cs | 46 +++++++ UI/Inventory/ShopMenu.tscn | 69 ++++++----- UI/Inventory/ShopSlot.tscn | 14 +++ UI/Themes/InventorySlotButtonFocus.tres | 14 +++ UI/Themes/InventorySlotButtonNormal.tres | 15 +++ UI/Themes/InventorySlotButtonPressed.tres | 15 +++ UI/Themes/Panel.tres | 14 +++ UI/Themes/supalidl.tres | 52 +++++++- 18 files changed, 614 insertions(+), 140 deletions(-) create mode 100644 UI/Inventory/ItemTooltip.cs create mode 100644 UI/Inventory/ItemTooltip.tscn delete mode 100644 UI/Inventory/ShopItem.tscn create mode 100644 UI/Inventory/ShopSlot.tscn create mode 100644 UI/Themes/InventorySlotButtonFocus.tres create mode 100644 UI/Themes/InventorySlotButtonNormal.tres create mode 100644 UI/Themes/InventorySlotButtonPressed.tres create mode 100644 UI/Themes/Panel.tres diff --git a/Items/Shops/SnusDealer.tres b/Items/Shops/SnusDealer.tres index 83b4905..ed8d52d 100644 --- a/Items/Shops/SnusDealer.tres +++ b/Items/Shops/SnusDealer.tres @@ -1,14 +1,54 @@ -[gd_resource type="Resource" script_class="Shop" load_steps=5 format=3 uid="uid://djqd88vdkoi6d"] +[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")]) +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/UI/Inventory/Hotbar.tscn b/UI/Inventory/Hotbar.tscn index f97f418..2ef8600 100644 --- a/UI/Inventory/Hotbar.tscn +++ b/UI/Inventory/Hotbar.tscn @@ -1,8 +1,10 @@ -[gd_scene load_steps=3 format=3 uid="uid://sfs8dpfitpdu"] +[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 @@ -17,9 +19,12 @@ _slots = [NodePath("InventorySlot"), NodePath("InventorySlot2"), NodePath("Inven [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 index 3d972b0..e69de29 100644 --- a/UI/Inventory/HotbarSlot.cs +++ b/UI/Inventory/HotbarSlot.cs @@ -1,32 +0,0 @@ -using Godot; -using GodotUtilities; -using GodotUtilities.SourceGenerators; - -namespace SupaLidlGame.UI.Inventory; - -[Scene] -public partial class HotbarSlot : InventorySlot -{ - [Node("TextureRect")] - private TextureRect _textureRect; - - [Node("Selected")] - private NinePatchRect _selected; - - private static Texture2D _placeholderTexture; - - private Items.ItemMetadata _item; - - private bool _isSelected = false; - - public bool IsSelected - { - get => _isSelected; - set - { - _isSelected = value; - _selected.Visible = _isSelected; - _frame.Visible = !_isSelected; - } - } -} diff --git a/UI/Inventory/HotbarSlot.tscn b/UI/Inventory/HotbarSlot.tscn index de7ce71..ba63588 100644 --- a/UI/Inventory/HotbarSlot.tscn +++ b/UI/Inventory/HotbarSlot.tscn @@ -1,10 +1,15 @@ -[gd_scene load_steps=3 format=3 uid="uid://dmvu2hjyrwc1y"] +[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 diff --git a/UI/Inventory/InventoryGrid.cs b/UI/Inventory/InventoryGrid.cs index f51d07c..124b536 100644 --- a/UI/Inventory/InventoryGrid.cs +++ b/UI/Inventory/InventoryGrid.cs @@ -6,54 +6,167 @@ namespace SupaLidlGame.UI.Inventory; public partial class InventoryGrid : GridContainer { - private SupaLidlGame.Items.Inventory _inventorySource; + private SupaLidlGame.Items.IItemCollection _source; [Export] private PackedScene _slotScene; - public SupaLidlGame.Items.Inventory InventorySource + public ButtonGroup ButtonGroup { get; private set; } + + public SupaLidlGame.Items.IItemCollection Source { + get => _source; set { - _inventorySource = value; + GD.Print("Set InventoryGrid source"); + _source = value; Redraw(); } - get => _inventorySource; + } + + [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() { - if (_inventorySource is null) + GD.Print("Redrawing inventory grid..."); + + if (_source is null) { this.QueueFreeChildren(); } var children = GetChildren(); - for (int i = children.Count; i < _inventorySource.InventoryCapacity; i++) + for (int i = children.Count; i < _source.Capacity; i++) { - AddChild(_slotScene.Instantiate()); + AddInventorySlot(); } - for (int i = children.Count - 1; i >= _inventorySource.InventoryCapacity; i--) + for (int i = children.Count - 1; i >= _source.Capacity; i--) { children[i].QueueFree(); } children = GetChildren(); - for (int i = 0; i < children.Count; i++) + // iterate through items and update the grid + using (var items = _source.GetItems().GetEnumerator()) { - InventorySlot slot = children[i] as InventorySlot; - - if (i >= _inventorySource.Items.Count) + 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; } - else if (slot.Item != _inventorySource.Items[i]) + } + + for (int i = 0; i < children.Count; i++) + { + var child = children[i] as Control; + if (i > 0) { - slot.Item = _inventorySource.Items[i]; + 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/InventorySlot.cs b/UI/Inventory/InventorySlot.cs index d8a4de8..8d9bda6 100644 --- a/UI/Inventory/InventorySlot.cs +++ b/UI/Inventory/InventorySlot.cs @@ -4,7 +4,7 @@ using GodotUtilities.SourceGenerators; namespace SupaLidlGame.UI.Inventory; -public partial class InventorySlot : Container +public partial class InventorySlot : Button { private bool _isSelected = false; @@ -16,12 +16,15 @@ public partial class InventorySlot : Container _isSelected = value; if (_selectedFrame is not null) { - _selectedFrame.Visible = _isSelected; - _frame.Visible = !_isSelected; + //_selectedFrame.Visible = _isSelected; + //_frame.Visible = !_isSelected; } } } + [Export] + public bool UseFocusAsSelected { get; set; } = true; + private TextureRect _textureRect; private NinePatchRect _frame; @@ -41,11 +44,13 @@ public partial class InventorySlot : Container if (_item is null) { - _textureRect.Texture = null; + //_textureRect.Texture = null; + Icon = null; } else { - _textureRect.Texture = _item.Icon; + //_textureRect.Texture = _item.Icon; + Icon = _item.Icon; } } } @@ -61,5 +66,30 @@ public partial class InventorySlot : Container _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 index e27739b..275b23e 100644 --- a/UI/Inventory/InventorySlot.tscn +++ b/UI/Inventory/InventorySlot.tscn @@ -1,20 +1,41 @@ -[gd_scene load_steps=4 format=3 uid="uid://ctad0dkoyw8ad"] +[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="StyleBoxEmpty" id="StyleBoxEmpty_6jbma"] +[sub_resource type="PlaceholderTexture2D" id="PlaceholderTexture2D_ajutj"] +size = Vector2(24, 24) -[node name="InventorySlot" type="PanelContainer"] +[node name="InventorySlot" type="Button"] custom_minimum_size = Vector2(32, 32) -theme_override_styles/panel = SubResource("StyleBoxEmpty_6jbma") +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