├── .gitattributes ├── .gitignore ├── Inventory.csproj ├── Inventory.sln ├── LICENSE ├── README.md ├── Scenes ├── Main.tscn └── Prefabs │ └── Chest.tscn ├── Scripts ├── Chest.cs ├── InputGame.cs ├── Inventory │ ├── Inventory.cs │ ├── InventoryAnimatedItem.cs │ ├── InventoryItem.cs │ ├── InventorySlot.cs │ ├── InventoryStaticItem.cs │ ├── Item.cs │ ├── ItemAnimated.cs │ ├── ItemCursor.cs │ ├── ItemCursorManager.cs │ ├── ItemHolder.cs │ ├── ItemPanelDescription.cs │ ├── ItemStatic.cs │ ├── ItemType.cs │ ├── Items.cs │ └── PlayerInventory.cs ├── Main.cs ├── Player.cs └── Utils │ ├── Extensions │ ├── ExtensionsAnimatedSprite2D.cs │ ├── ExtensionsContainer.cs │ ├── ExtensionsInputEvent.cs │ ├── ExtensionsNode.cs │ └── ExtensionsObject.cs │ ├── Utils.cs │ ├── UtilsInventory.cs │ └── UtilsLabel.cs ├── Sprites ├── chest-sheet.png ├── chest-sheet.png.import ├── coin.png ├── coin.png.import ├── coin_pink.png ├── coin_pink.png.import ├── coin_red.png ├── coin_red.png.import ├── coin_snowy.png ├── coin_snowy.png.import ├── fumiko.png ├── fumiko.png.import ├── icon.svg └── icon.svg.import ├── project.godot └── sprite_frames_coin.tres /.gitattributes: -------------------------------------------------------------------------------- 1 | # Normalize EOL for all files that Git considers text files. 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Godot 4+ specific ignores 2 | .godot/ 3 | 4 | # Other Ignores 5 | .vs/ 6 | Properties/launchSettings.json 7 | -------------------------------------------------------------------------------- /Inventory.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net7.0 4 | true 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Inventory.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 2012 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Inventory", "Inventory.csproj", "{C2F1249A-D0B1-4AA6-98A4-23DA266F25CF}" 4 | EndProject 5 | Global 6 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 7 | Debug|Any CPU = Debug|Any CPU 8 | ExportDebug|Any CPU = ExportDebug|Any CPU 9 | ExportRelease|Any CPU = ExportRelease|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {C2F1249A-D0B1-4AA6-98A4-23DA266F25CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {C2F1249A-D0B1-4AA6-98A4-23DA266F25CF}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {C2F1249A-D0B1-4AA6-98A4-23DA266F25CF}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU 15 | {C2F1249A-D0B1-4AA6-98A4-23DA266F25CF}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU 16 | {C2F1249A-D0B1-4AA6-98A4-23DA266F25CF}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU 17 | {C2F1249A-D0B1-4AA6-98A4-23DA266F25CF}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU 18 | EndGlobalSection 19 | EndGlobal 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Valks Games 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This is now archived because it is being remade in [ValksGodotTools/Template](https://github.com/ValksGodotTools/Template) 2 | 3 | https://user-images.githubusercontent.com/6277739/217958511-d5182a65-60e9-464d-82d3-23a069720ae3.mp4 4 | 5 | ## A Game Inventory being made in Godot 4 C# 6 | The goal of this project is to attempt to perfect a game inventory system so it can be used across all games that make use of inventories. 7 | 8 | As of writing this, this project requires Godot 4 Release Candidate 1 (`v4.0.rc1.mono.official [8843d9ad3]`). If you are unsure which version this project is on, please check the `Inventory.csproj` file, the version will be displayed in there. 9 | 10 | #### Table of Contents 11 | 1. [Features](#features) 12 | 2. [Implemented Controls](#implemented-controls) 13 | 3. [Todo](#todo) 14 | 4. [Example Code](#example-code) 15 | 5. [Contributing](#contributing) 16 | 6. [Previews](#previews) 17 | 18 | ### Features 19 | - [x] Inventories of varying sizes can be defined 20 | - [x] Inventory slot size can be customized 21 | - [x] Player Inventory Hotbar 22 | - [x] Smooth player inventory open / close animations 23 | 24 | ### Implemented Controls 25 | - [x] `Left Click` to pick up / place item stack 26 | - [x] `Hold + Left Click` to continuously pick up items of the same type 27 | - [x] `Shift + Left Click` to transfer item stack from inventories A to B 28 | - [x] `Hold + Shift + Left Click` to continuously transfer item stack from inventories A to B 29 | - [x] `Shift + Right Click` to split stack 30 | - [x] `Right Click` to pick up / place single item 31 | - [x] `Hold + Right Click` to continuously place a single item 32 | - [x] `Double Click` to pick up all items of the same type 33 | - [x] `Shift + R` to sort items in all open inventories by descending item count 34 | - [x] `Shift + T` to take all items from the 'other inventory' and put them in the players inventory 35 | - [x] `1` `2` `3` `4` `5` `6` `7` `8` `9` to move / swap items to player hotbar slots 36 | - [x] `I` to open the players inventory 37 | - [x] `E` to interact with objects in the world 38 | 39 | ### Todo 40 | - Hotkey to deposit all items from player inventory to 'other inventory' 41 | - Make item panel description popup only appear after small delay 42 | - Saving / loading inventory data 43 | - Allow items to define their own max stack sizes 44 | - Q = Drop Item (x1) 45 | - Ctrl + Q = Drop Stack 46 | - Maybe implementing ENet-CSharp and syncing all of this inventory stuff over the network. (There would be a demo where 2 players would see each other walking around and see the inventory update when the other player interacts with it) 47 | - Giving items a category property so the sorting hotkey can sort by category or any other item property for that matter 48 | - Add item rarities (maybe rare items have a glowing outline achieved used Godot Shaders) 49 | 50 | ### Example Code 51 | ```cs 52 | private Inventory ChestInv { get; set; } 53 | private Inventory PlayerInv { get; set; } 54 | 55 | public override void _Ready() 56 | { 57 | ChestInv = new Inventory(this); 58 | ChestInv.SetAnchor(Control.LayoutPreset.CenterTop); 59 | 60 | for (int i = 0; i < 9; i++) 61 | ChestInv.SetItem(i, new Item(Items.Coin)); 62 | 63 | ChestInv.SetItem(0, 2, new Item(Items.Coin, 3)); 64 | ChestInv.SetItem(1, 2, new Item(Items.CoinSnowy)); 65 | 66 | PlayerInv = new Inventory(this); 67 | PlayerInv.SetAnchor(Control.LayoutPreset.CenterBottom); 68 | 69 | for (int i = 18; i < 27; i++) 70 | PlayerInv.SetItem(i, new Item(Items.CoinPink)); 71 | 72 | Inventory.PlayerInventory = PlayerInv; 73 | Inventory.OtherInventory = ChestInv; 74 | } 75 | ``` 76 | 77 | ### Contributing 78 | Please talk to me over Discord `va#9904` before you do anything. 79 | 80 | Have a look at the [todo list](#todo) and the [projects issues](https://github.com/Valks-Games/Inventory/issues) for things to do. On top of all that, I'm always trying to find ways to clean up the code and make it more humanly readable. 81 | 82 | Please follow [this code style](https://github.com/Valks-Games/sankari/wiki/Code-Style) 83 | 84 | ### Previews 85 | https://user-images.githubusercontent.com/6277739/218189596-f6c9edb2-67ca-4aca-9c7d-214f145699e3.mp4 86 | 87 | https://user-images.githubusercontent.com/6277739/218189290-9169f504-fc31-4cd6-99f3-4ba92eb4ff75.mp4 88 | 89 | https://user-images.githubusercontent.com/6277739/217701119-4f5dcf6e-3004-4f91-b966-8de9b8ba98c7.mp4 90 | 91 | https://user-images.githubusercontent.com/6277739/217701126-bbe38e2b-e962-4ad8-a6ce-28c29035e0d0.mp4 92 | 93 | https://user-images.githubusercontent.com/6277739/217701135-0271306f-b915-4006-b333-18eb170ac19b.mp4 94 | 95 | https://user-images.githubusercontent.com/6277739/217701142-748b222a-4f6d-46e3-bbc5-b333c83d628e.mp4 96 | -------------------------------------------------------------------------------- /Scenes/Main.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=22 format=3 uid="uid://dyr1qinra6vj7"] 2 | 3 | [ext_resource type="Texture2D" uid="uid://c23tlhfrtu1sh" path="res://Sprites/fumiko.png" id="1_il8g5"] 4 | [ext_resource type="Script" path="res://Scripts/Main.cs" id="1_po44d"] 5 | [ext_resource type="Script" path="res://Scripts/Player.cs" id="1_rlbmo"] 6 | [ext_resource type="Script" path="res://Scripts/Inventory/ItemCursorManager.cs" id="4_5yayu"] 7 | [ext_resource type="Script" path="res://Scripts/Inventory/ItemPanelDescription.cs" id="5_40r8k"] 8 | [ext_resource type="PackedScene" uid="uid://bjggww62kqxnd" path="res://Scenes/Prefabs/Chest.tscn" id="6_efoe0"] 9 | 10 | [sub_resource type="AtlasTexture" id="AtlasTexture_1p1bi"] 11 | atlas = ExtResource("1_il8g5") 12 | region = Rect2(24, 64, 24, 32) 13 | 14 | [sub_resource type="AtlasTexture" id="AtlasTexture_o2jqw"] 15 | atlas = ExtResource("1_il8g5") 16 | region = Rect2(0, 64, 24, 32) 17 | 18 | [sub_resource type="AtlasTexture" id="AtlasTexture_wfjf3"] 19 | atlas = ExtResource("1_il8g5") 20 | region = Rect2(24, 64, 24, 32) 21 | 22 | [sub_resource type="AtlasTexture" id="AtlasTexture_5wafg"] 23 | atlas = ExtResource("1_il8g5") 24 | region = Rect2(48, 64, 24, 32) 25 | 26 | [sub_resource type="AtlasTexture" id="AtlasTexture_ln4tw"] 27 | atlas = ExtResource("1_il8g5") 28 | region = Rect2(0, 96, 24, 32) 29 | 30 | [sub_resource type="AtlasTexture" id="AtlasTexture_yqlo5"] 31 | atlas = ExtResource("1_il8g5") 32 | region = Rect2(24, 96, 24, 32) 33 | 34 | [sub_resource type="AtlasTexture" id="AtlasTexture_6iev2"] 35 | atlas = ExtResource("1_il8g5") 36 | region = Rect2(48, 96, 24, 32) 37 | 38 | [sub_resource type="AtlasTexture" id="AtlasTexture_kr3wt"] 39 | atlas = ExtResource("1_il8g5") 40 | region = Rect2(0, 32, 24, 32) 41 | 42 | [sub_resource type="AtlasTexture" id="AtlasTexture_rr64i"] 43 | atlas = ExtResource("1_il8g5") 44 | region = Rect2(24, 32, 24, 32) 45 | 46 | [sub_resource type="AtlasTexture" id="AtlasTexture_21ut4"] 47 | atlas = ExtResource("1_il8g5") 48 | region = Rect2(48, 32, 24, 32) 49 | 50 | [sub_resource type="AtlasTexture" id="AtlasTexture_sbfjl"] 51 | atlas = ExtResource("1_il8g5") 52 | region = Rect2(0, 0, 24, 32) 53 | 54 | [sub_resource type="AtlasTexture" id="AtlasTexture_hoh2y"] 55 | atlas = ExtResource("1_il8g5") 56 | region = Rect2(24, 0, 24, 32) 57 | 58 | [sub_resource type="AtlasTexture" id="AtlasTexture_piejv"] 59 | atlas = ExtResource("1_il8g5") 60 | region = Rect2(48, 0, 24, 32) 61 | 62 | [sub_resource type="SpriteFrames" id="SpriteFrames_t0e1t"] 63 | animations = [{ 64 | "frames": [{ 65 | "duration": 1.0, 66 | "texture": SubResource("AtlasTexture_1p1bi") 67 | }], 68 | "loop": true, 69 | "name": &"idle", 70 | "speed": 5.0 71 | }, { 72 | "frames": [{ 73 | "duration": 1.0, 74 | "texture": SubResource("AtlasTexture_o2jqw") 75 | }, { 76 | "duration": 1.0, 77 | "texture": SubResource("AtlasTexture_wfjf3") 78 | }, { 79 | "duration": 1.0, 80 | "texture": SubResource("AtlasTexture_5wafg") 81 | }], 82 | "loop": true, 83 | "name": &"walk_down", 84 | "speed": 5.0 85 | }, { 86 | "frames": [{ 87 | "duration": 1.0, 88 | "texture": SubResource("AtlasTexture_ln4tw") 89 | }, { 90 | "duration": 1.0, 91 | "texture": SubResource("AtlasTexture_yqlo5") 92 | }, { 93 | "duration": 1.0, 94 | "texture": SubResource("AtlasTexture_6iev2") 95 | }], 96 | "loop": true, 97 | "name": &"walk_left", 98 | "speed": 5.0 99 | }, { 100 | "frames": [{ 101 | "duration": 1.0, 102 | "texture": SubResource("AtlasTexture_kr3wt") 103 | }, { 104 | "duration": 1.0, 105 | "texture": SubResource("AtlasTexture_rr64i") 106 | }, { 107 | "duration": 1.0, 108 | "texture": SubResource("AtlasTexture_21ut4") 109 | }], 110 | "loop": true, 111 | "name": &"walk_right", 112 | "speed": 5.0 113 | }, { 114 | "frames": [{ 115 | "duration": 1.0, 116 | "texture": SubResource("AtlasTexture_sbfjl") 117 | }, { 118 | "duration": 1.0, 119 | "texture": SubResource("AtlasTexture_hoh2y") 120 | }, { 121 | "duration": 1.0, 122 | "texture": SubResource("AtlasTexture_piejv") 123 | }], 124 | "loop": true, 125 | "name": &"walk_up", 126 | "speed": 5.0 127 | }] 128 | 129 | [sub_resource type="CircleShape2D" id="CircleShape2D_peg1f"] 130 | radius = 4.0 131 | 132 | [node name="Main" type="Node2D"] 133 | script = ExtResource("1_po44d") 134 | 135 | [node name="Player" type="CharacterBody2D" parent="."] 136 | z_index = 1 137 | script = ExtResource("1_rlbmo") 138 | Speed = 750.0 139 | Friction = 0.15 140 | 141 | [node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="Player"] 142 | sprite_frames = SubResource("SpriteFrames_t0e1t") 143 | animation = &"idle" 144 | 145 | [node name="CollisionShape2D" type="CollisionShape2D" parent="Player"] 146 | position = Vector2(0, 12) 147 | shape = SubResource("CircleShape2D_peg1f") 148 | 149 | [node name="Camera2D" type="Camera2D" parent="Player"] 150 | zoom = Vector2(4, 4) 151 | 152 | [node name="CanvasLayer" type="CanvasLayer" parent="."] 153 | 154 | [node name="ItemCursorParent" type="Control" parent="CanvasLayer"] 155 | layout_mode = 3 156 | anchors_preset = 0 157 | script = ExtResource("4_5yayu") 158 | 159 | [node name="ItemDescriptionParent" type="Control" parent="CanvasLayer"] 160 | layout_mode = 3 161 | anchors_preset = 0 162 | script = ExtResource("5_40r8k") 163 | 164 | [node name="Chest" parent="." instance=ExtResource("6_efoe0")] 165 | position = Vector2(-53, -48) 166 | -------------------------------------------------------------------------------- /Scenes/Prefabs/Chest.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=17 format=3 uid="uid://bjggww62kqxnd"] 2 | 3 | [ext_resource type="Script" path="res://Scripts/Chest.cs" id="1_cp1en"] 4 | [ext_resource type="Texture2D" uid="uid://clul67t2kth6y" path="res://Sprites/chest-sheet.png" id="1_mji0g"] 5 | 6 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_1me4r"] 7 | 8 | [sub_resource type="AtlasTexture" id="AtlasTexture_ohal8"] 9 | atlas = ExtResource("1_mji0g") 10 | region = Rect2(336, 0, 48, 48) 11 | 12 | [sub_resource type="AtlasTexture" id="AtlasTexture_spdx6"] 13 | atlas = ExtResource("1_mji0g") 14 | region = Rect2(192, 0, 48, 48) 15 | 16 | [sub_resource type="AtlasTexture" id="AtlasTexture_1oay8"] 17 | atlas = ExtResource("1_mji0g") 18 | region = Rect2(0, 0, 48, 48) 19 | 20 | [sub_resource type="AtlasTexture" id="AtlasTexture_wr57k"] 21 | atlas = ExtResource("1_mji0g") 22 | region = Rect2(0, 0, 48, 48) 23 | 24 | [sub_resource type="AtlasTexture" id="AtlasTexture_s4cd4"] 25 | atlas = ExtResource("1_mji0g") 26 | region = Rect2(48, 0, 48, 48) 27 | 28 | [sub_resource type="AtlasTexture" id="AtlasTexture_ec8b4"] 29 | atlas = ExtResource("1_mji0g") 30 | region = Rect2(96, 0, 48, 48) 31 | 32 | [sub_resource type="AtlasTexture" id="AtlasTexture_5c3bg"] 33 | atlas = ExtResource("1_mji0g") 34 | region = Rect2(144, 0, 48, 48) 35 | 36 | [sub_resource type="AtlasTexture" id="AtlasTexture_tlsu8"] 37 | atlas = ExtResource("1_mji0g") 38 | region = Rect2(192, 0, 48, 48) 39 | 40 | [sub_resource type="AtlasTexture" id="AtlasTexture_06yh8"] 41 | atlas = ExtResource("1_mji0g") 42 | region = Rect2(240, 0, 48, 48) 43 | 44 | [sub_resource type="AtlasTexture" id="AtlasTexture_85v7e"] 45 | atlas = ExtResource("1_mji0g") 46 | region = Rect2(288, 0, 48, 48) 47 | 48 | [sub_resource type="AtlasTexture" id="AtlasTexture_fkffh"] 49 | atlas = ExtResource("1_mji0g") 50 | region = Rect2(336, 0, 48, 48) 51 | 52 | [sub_resource type="SpriteFrames" id="SpriteFrames_ysbqr"] 53 | animations = [{ 54 | "frames": [{ 55 | "duration": 1.0, 56 | "texture": SubResource("AtlasTexture_ohal8") 57 | }, { 58 | "duration": 1.0, 59 | "texture": SubResource("AtlasTexture_spdx6") 60 | }, { 61 | "duration": 1.0, 62 | "texture": SubResource("AtlasTexture_1oay8") 63 | }], 64 | "loop": false, 65 | "name": &"close", 66 | "speed": 10.0 67 | }, { 68 | "frames": [{ 69 | "duration": 1.0, 70 | "texture": SubResource("AtlasTexture_wr57k") 71 | }, { 72 | "duration": 1.0, 73 | "texture": SubResource("AtlasTexture_s4cd4") 74 | }, { 75 | "duration": 1.0, 76 | "texture": SubResource("AtlasTexture_ec8b4") 77 | }, { 78 | "duration": 1.0, 79 | "texture": SubResource("AtlasTexture_5c3bg") 80 | }, { 81 | "duration": 1.0, 82 | "texture": SubResource("AtlasTexture_tlsu8") 83 | }, { 84 | "duration": 1.0, 85 | "texture": SubResource("AtlasTexture_06yh8") 86 | }, { 87 | "duration": 1.0, 88 | "texture": SubResource("AtlasTexture_85v7e") 89 | }, { 90 | "duration": 1.0, 91 | "texture": SubResource("AtlasTexture_fkffh") 92 | }], 93 | "loop": false, 94 | "name": &"open", 95 | "speed": 10.0 96 | }] 97 | 98 | [sub_resource type="RectangleShape2D" id="RectangleShape2D_3q17j"] 99 | size = Vector2(20, 10) 100 | 101 | [node name="Chest" type="StaticBody2D"] 102 | script = ExtResource("1_cp1en") 103 | 104 | [node name="CollisionShape2D" type="CollisionShape2D" parent="."] 105 | position = Vector2(0, 6) 106 | shape = SubResource("RectangleShape2D_1me4r") 107 | 108 | [node name="AnimatedSprite2D" type="AnimatedSprite2D" parent="."] 109 | sprite_frames = SubResource("SpriteFrames_ysbqr") 110 | animation = &"open" 111 | 112 | [node name="Area2D" type="Area2D" parent="."] 113 | 114 | [node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"] 115 | position = Vector2(0, 21) 116 | shape = SubResource("RectangleShape2D_3q17j") 117 | 118 | [connection signal="body_entered" from="Area2D" to="." method="_on_area_2d_body_entered"] 119 | [connection signal="body_exited" from="Area2D" to="." method="_on_area_2d_body_exited"] 120 | -------------------------------------------------------------------------------- /Scripts/Chest.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public partial class Chest : Node2D 4 | { 5 | public Inventory Inventory { get; set; } 6 | public bool IsOpen { get; private set; } 7 | 8 | private AnimatedSprite2D AnimatedSprite2D { get; set; } 9 | 10 | public override void _Ready() 11 | { 12 | Inventory = new(this); 13 | AnimatedSprite2D = GetNode("AnimatedSprite2D"); 14 | } 15 | 16 | public void Open() 17 | { 18 | if (IsOpen) 19 | return; 20 | 21 | IsOpen = true; 22 | Inventory.OtherInventory = Inventory; 23 | Inventory.Open(); 24 | 25 | if (Player.Inventory.IsHotbar) 26 | Player.Inventory.SwitchToFullInventory(); 27 | 28 | AnimatedSprite2D.Play("open"); 29 | } 30 | 31 | public void Close() 32 | { 33 | IsOpen = false; 34 | 35 | // Clean up item animations 36 | foreach (var slot in Inventory.OtherInventory.InventorySlots) 37 | slot.ResetCleanUpAnimations(); 38 | 39 | foreach (var slot in Player.Inventory.InventorySlots) 40 | slot.ResetCleanUpAnimations(); 41 | 42 | Inventory.OtherInventory = null; 43 | Inventory.Close(); 44 | 45 | if (!Player.Inventory.IsHotbar) 46 | Player.Inventory.SwitchToHotbarAnimated(); 47 | 48 | AnimatedSprite2D.Play("close"); 49 | ItemPanelDescription.Clear(); 50 | } 51 | 52 | private void _on_area_2d_body_entered(Node2D body) 53 | { 54 | if (body is not Player) 55 | return; 56 | 57 | Inventory.ActiveChest = this; 58 | } 59 | 60 | private void _on_area_2d_body_exited(Node2D body) 61 | { 62 | if (body is not Player) 63 | return; 64 | 65 | Inventory.ActiveChest = null; 66 | 67 | if (IsOpen) 68 | Close(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Scripts/InputGame.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public static class InputGame 4 | { 5 | public static ulong DoubleClickTime { get; } = 250; 6 | 7 | public static bool HoldingLeftClick { get; private set; } 8 | public static bool HoldingRightClick { get; private set; } 9 | public static bool DoubleClick { get; private set; } 10 | 11 | private static ulong LastClick { get; set; } 12 | 13 | public static void Handle(InputEvent @event) 14 | { 15 | if (@event is InputEventMouseButton eventMouseButton) 16 | HandleMouseButton(eventMouseButton); 17 | } 18 | 19 | private static void HandleMouseButton(InputEventMouseButton @event) 20 | { 21 | if (@event.IsLeftClickPressed()) 22 | { 23 | HoldingLeftClick = true; 24 | DoubleClick = Time.GetTicksMsec() - LastClick <= DoubleClickTime; 25 | LastClick = Time.GetTicksMsec(); 26 | } 27 | 28 | if (@event.IsLeftClickReleased()) 29 | HoldingLeftClick = false; 30 | 31 | if (@event.IsRightClickPressed()) 32 | HoldingRightClick = true; 33 | 34 | if (@event.IsRightClickReleased()) 35 | HoldingRightClick = false; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Scripts/Inventory/Inventory.cs: -------------------------------------------------------------------------------- 1 | using static Godot.Control; 2 | 3 | namespace Inventory; 4 | 5 | public class Inventory 6 | { 7 | public static Inventory OtherInventory { get; set; } // a container the player opened 8 | public static InventorySlot ActiveInventorySlot { get; set; } // mouse is currently hovering over this slot 9 | public static Chest ActiveChest { get; set; } 10 | 11 | public InventorySlot[] InventorySlots { get; set; } 12 | public int SlotSize { get; set; } = 50; 13 | public bool Visible 14 | { 15 | get => PanelContainer.Visible; 16 | set => PanelContainer.Visible = value; 17 | } 18 | 19 | private GridContainer GridContainer { get; set; } 20 | private int Padding { get; set; } = 10; 21 | public int Columns { get; set; } 22 | private int Rows { get; set; } 23 | private Node Parent { get; set; } 24 | public PanelContainer PanelContainer { get; set; } 25 | private LayoutPreset LayoutPreset { get; set; } 26 | private StyleBox PanelStyleBoxVisible { get; set; } 27 | private Control ControlPivot { get; set; } 28 | 29 | public Inventory(Node node, int columns = 9, int rows = 5, int slotSize = 50) 30 | { 31 | Parent = node.GetTree().Root.GetNode("Main/CanvasLayer"); 32 | Columns = columns; 33 | Rows = rows; 34 | SlotSize = slotSize; 35 | 36 | ControlPivot = new Control(); 37 | 38 | PanelContainer = new PanelContainer(); 39 | PanelStyleBoxVisible = PanelContainer.GetThemeStylebox("panel"); 40 | 41 | var marginContainer = new MarginContainer(); 42 | marginContainer.AddMargin(Padding); 43 | 44 | PanelContainer.AddChild(marginContainer); 45 | 46 | GridContainer = new GridContainer { Columns = columns }; 47 | 48 | marginContainer.AddChild(GridContainer); 49 | 50 | InventorySlots = new InventorySlot[rows * columns]; 51 | 52 | for (int i = 0; i < InventorySlots.Length; i++) 53 | InventorySlots[i] = new InventorySlot(this, GridContainer); 54 | 55 | ControlPivot.AddChild(PanelContainer); 56 | 57 | Parent.AddChild(ControlPivot); 58 | 59 | // Must set anchor after all children are added to the scene 60 | LayoutPreset = LayoutPreset.CenterTop; 61 | SetAnchor(LayoutPreset); // center top by default 62 | 63 | // hide by default 64 | ControlPivot.Hide(); 65 | } 66 | 67 | public void MakePanelInvisible() => 68 | PanelContainer.AddThemeStyleboxOverride("panel", new StyleBoxEmpty()); 69 | 70 | public void MakePanelVisible() => 71 | PanelContainer.AddThemeStyleboxOverride("panel", PanelStyleBoxVisible); 72 | 73 | public void Open() => ControlPivot.Show(); 74 | public void Close() 75 | { 76 | ControlPivot.Hide(); 77 | } 78 | public void ToggleVisibility() => ControlPivot.Visible = !ControlPivot.Visible; 79 | 80 | public void SetSlotsVisibility(int a, int b, bool visible, bool updateAnchor) 81 | { 82 | for (int i = a; i < b; i++) 83 | InventorySlots[i].SetVisible(visible); 84 | 85 | if (updateAnchor) 86 | SetAnchor(LayoutPreset); 87 | } 88 | 89 | public void SetAnchor(LayoutPreset preset) 90 | { 91 | LayoutPreset = preset; 92 | ControlPivot.SetAnchorsAndOffsetsPreset(preset); 93 | PanelContainer.SetAnchorsAndOffsetsPreset(preset); 94 | } 95 | 96 | public virtual void SetItem(int i, Item item) => 97 | InventorySlots[i].SetItem(item); 98 | 99 | public virtual void SetItem(int x, int y, Item item) => 100 | SetItem(x + y * Columns, item); 101 | 102 | public void Sort() 103 | { 104 | var items = new Dictionary(); 105 | 106 | // Store all items in a dictionary 107 | for (int i = 0; i < InventorySlots.Length; i++) 108 | { 109 | var invItem = InventorySlots[i].InventoryItem; 110 | 111 | if (invItem == null) 112 | continue; 113 | 114 | if (items.ContainsKey(invItem.Item.Type)) 115 | items[invItem.Item.Type] += invItem.Item.Count; 116 | else 117 | items[invItem.Item.Type] = invItem.Item.Count; 118 | 119 | InventorySlots[i].RemoveItem(); 120 | } 121 | 122 | // Sort by item count (descending) 123 | items = items.OrderByDescending(x => x.Value).ToDictionary(x => x.Key, x => x.Value); 124 | 125 | // Place all items from the dictionary 126 | var index = 0; 127 | 128 | foreach (var item in items) 129 | { 130 | var itemType = item.Key; 131 | var itemCount = item.Value; 132 | 133 | var theItem = new Item(itemType, itemCount); 134 | 135 | InventorySlots[index++].SetItem(theItem); 136 | } 137 | } 138 | 139 | public void TakeAll() 140 | { 141 | for (int i = 0; i < InventorySlots.Length; i++) 142 | { 143 | var invItem = InventorySlots[i].InventoryItem; 144 | 145 | if (invItem == null) 146 | continue; 147 | 148 | var otherSlot = Player.Inventory.TryGetEmptyOrSameTypeSlot(invItem.Item.Type); 149 | 150 | var otherInvSlotItem = Player.Inventory.InventorySlots[otherSlot].InventoryItem; 151 | 152 | if (otherInvSlotItem != null) 153 | { 154 | var item = otherInvSlotItem.Item; 155 | item.Count += invItem.Item.Count; 156 | 157 | Player.Inventory.InventorySlots[otherSlot].SetItem(item); 158 | } 159 | else 160 | { 161 | Player.Inventory.InventorySlots[otherSlot].SetItem(invItem.Item); 162 | } 163 | 164 | InventorySlots[i].RemoveItem(); 165 | } 166 | } 167 | 168 | public int TryGetEmptyOrSameTypeSlot(ItemType itemType) 169 | { 170 | var foundEmptySlot = false; 171 | var emptySlotIndex = -1; 172 | 173 | for (int i = 0; i < InventorySlots.Length; i++) 174 | { 175 | var slot = InventorySlots[i]; 176 | 177 | if (slot.InventoryItem != null && slot.InventoryItem.Item.Type == itemType) 178 | return i; 179 | 180 | if (!foundEmptySlot && slot.InventoryItem == null) 181 | { 182 | foundEmptySlot = true; 183 | emptySlotIndex = i; 184 | } 185 | } 186 | 187 | if (foundEmptySlot) 188 | return emptySlotIndex; 189 | 190 | return -1; 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /Scripts/Inventory/InventoryAnimatedItem.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public class InventoryAnimatedItem : InventoryItem 4 | { 5 | public override bool Visible { get => AnimatedSprite2D.Visible; set => AnimatedSprite2D.Visible = value; } 6 | public override Node2D Node { get => AnimatedSprite2D; } 7 | 8 | private AnimatedSprite2D AnimatedSprite2D { get; set; } 9 | private ItemAnimated ItemAnimated { get; set; } 10 | private Inventory Inv { get; set; } 11 | 12 | public InventoryAnimatedItem(Inventory inv, Node parent, ItemAnimated itemAnimated, Item item) 13 | { 14 | Item = item; 15 | Inv = inv; 16 | ItemAnimated = itemAnimated; 17 | AnimatedSprite2D = (AnimatedSprite2D)GenerateGraphic(); 18 | 19 | parent.AddChild(AnimatedSprite2D); 20 | } 21 | 22 | public override Node2D GenerateGraphic() 23 | { 24 | var sprite = new AnimatedSprite2D 25 | { 26 | SpriteFrames = ItemAnimated.SpriteFrames, 27 | Position = Vector2.One * (Inv.SlotSize / 2), 28 | Scale = Vector2.One * (Inv.SlotSize / 25) 29 | }; 30 | 31 | sprite.Frame = GD.RandRange(0, sprite.SpriteFrames.GetFrameCount("default") - 1); 32 | sprite.Play(); 33 | 34 | return sprite; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Scripts/Inventory/InventoryItem.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public abstract class InventoryItem 4 | { 5 | public abstract bool Visible { get; set; } 6 | public abstract Node2D Node { get; } 7 | 8 | public Item Item { get; set; } 9 | 10 | public abstract Node2D GenerateGraphic(); 11 | 12 | public void QueueFreeGraphic() => Node.QueueFree(); 13 | public void Hide() => Node.Hide(); 14 | public void Show() => Node.Show(); 15 | } 16 | -------------------------------------------------------------------------------- /Scripts/Inventory/InventorySlot.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public class InventorySlot : ItemHolder 4 | { 5 | public InventoryItem InventoryItem { get; set; } 6 | public override Item Item { get => InventoryItem?.Item; set => InventoryItem.Item = value; } 7 | 8 | private Vector2 Position { get => Panel.GlobalPosition + Vector2.One * (Inventory.SlotSize / 2); } 9 | private bool CurrentlyAnimating { get; set; } 10 | private Node2D Graphic { get; set; } 11 | private Tween Tween { get; set; } 12 | private InventorySlot OtherInventorySlot { get; set; } 13 | 14 | private Label DebugLabel { get; set; } 15 | 16 | private Panel Panel { get; set; } 17 | private Inventory Inventory { get; set; } 18 | private bool JustPickedUpItem { get; set; } 19 | 20 | public InventorySlot(Inventory inv, Node parent) 21 | { 22 | Inventory = inv; 23 | 24 | Panel = new Panel 25 | { 26 | CustomMinimumSize = Vector2.One * Inventory.SlotSize 27 | }; 28 | 29 | Panel.GuiInput += (inputEvent) => 30 | { 31 | if (inputEvent is not InputEventMouseButton eventMouseButton) 32 | return; 33 | 34 | if (UtilsInventory.IsHotbarHotkeyPressed() != -1) 35 | { 36 | return; 37 | } 38 | 39 | if (eventMouseButton.IsLeftClickPressed()) 40 | { 41 | HandleLeftClick(); 42 | return; 43 | } 44 | 45 | if (eventMouseButton.IsRightClickPressed()) 46 | { 47 | HandleRightClick(); 48 | return; 49 | } 50 | }; 51 | 52 | Panel.MouseEntered += () => 53 | { 54 | Inventory.ActiveInventorySlot = this; 55 | 56 | var cursorItem = Main.ItemCursor.GetItem(); 57 | 58 | if (UtilsInventory.IsHotbarHotkeyPressed() != -1) 59 | return; 60 | 61 | // Continuous left click pickup 62 | if (InputGame.HoldingLeftClick && cursorItem != null && InventoryItem != null) 63 | { 64 | PickupSameType(Main.ItemCursor); 65 | return; 66 | } 67 | 68 | // Continuous right click place 69 | if (InputGame.HoldingRightClick && cursorItem != null) 70 | { 71 | PlaceOne(Main.ItemCursor); 72 | return; 73 | } 74 | 75 | // Continuous shift click transfer 76 | if (InputGame.HoldingLeftClick && Input.IsKeyPressed(Key.Shift)) 77 | { 78 | TransferItem(); 79 | return; 80 | } 81 | 82 | // Display item panel description popup 83 | if (InventoryItem != null) 84 | ItemPanelDescription.Display(InventoryItem.Item); 85 | }; 86 | 87 | Panel.MouseExited += () => 88 | { 89 | Inventory.ActiveInventorySlot = null; 90 | 91 | if (InventoryItem == null) 92 | return; 93 | 94 | ItemPanelDescription.Clear(); 95 | }; 96 | 97 | parent.AddChild(Panel); 98 | 99 | ItemCountLabel = UtilsLabel.CreateItemCountLabel(); 100 | Panel.AddChild(ItemCountLabel); 101 | 102 | DebugLabel = new Label 103 | { 104 | Text = "", 105 | ZIndex = 100, 106 | CustomMinimumSize = Vector2.One * Inventory.SlotSize, 107 | HorizontalAlignment = HorizontalAlignment.Center, 108 | VerticalAlignment = VerticalAlignment.Center, 109 | }; 110 | 111 | DebugLabel.AddThemeColorOverride("font_shadow_color", Colors.Black); 112 | 113 | Panel.AddChild(DebugLabel); 114 | } 115 | 116 | public void ResetCleanUpAnimations() 117 | { 118 | if (!CurrentlyAnimating) 119 | return; 120 | 121 | Tween.Kill(); 122 | 123 | if (OtherInventorySlot != null) 124 | { 125 | OtherInventorySlot.CurrentlyAnimating = false; 126 | OtherInventorySlot.InventoryItem?.Show(); 127 | } 128 | 129 | CurrentlyAnimating = false; 130 | 131 | if (GodotObject.IsInstanceValid(Graphic)) 132 | Graphic.QueueFree(); 133 | } 134 | 135 | public void SetVisible(bool v) => Panel.Visible = v; 136 | public void SetDebugLabel(string text) => DebugLabel.Text = text; 137 | 138 | public override void SetItem(Item item) 139 | { 140 | UpdateItemCountLabel(item.Count); 141 | InventoryItem?.QueueFreeGraphic(); 142 | InventoryItem = item.Type.ToInventoryItem(Inventory, Panel, item); 143 | } 144 | 145 | public override void RemoveItem() 146 | { 147 | // Clear the item graphic for this inventory slot 148 | InventoryItem.QueueFreeGraphic(); 149 | InventoryItem = null; 150 | 151 | // Clear the item count graphic 152 | UpdateItemCountLabel(0); 153 | } 154 | 155 | public Inventory GetOtherInventory() => this.Inventory == Player.Inventory ? 156 | Inventory.OtherInventory : Player.Inventory; 157 | 158 | private void UpdateItemCountLabel(int count) => 159 | ItemCountLabel.Text = count > 1 ? count + "" : ""; 160 | 161 | private void CollectAndMergeAllItemsFrom(Item itemToMergeTo) 162 | { 163 | var otherItemCounts = 0; 164 | 165 | // Scan the inventory for items of the same type and combine them to the cursor 166 | foreach (var slot in Inventory.InventorySlots) 167 | { 168 | // Skip the slot we double clicked on 169 | if (slot == this) 170 | continue; 171 | 172 | var invItem = slot.InventoryItem; 173 | 174 | // A item exists in this inv slot 175 | if (invItem != null) 176 | { 177 | // The inv slot item is the same type as the cursor item type 178 | if (invItem.Item.Type == itemToMergeTo.Type) 179 | { 180 | otherItemCounts += invItem.Item.Count; 181 | 182 | slot.RemoveItem(); 183 | } 184 | } 185 | } 186 | 187 | // Scan OtherInventorySlot for items of the same type and combine them to the cursor 188 | if (Inventory.OtherInventory != null) 189 | { 190 | foreach (var slot in Inventory.OtherInventory.InventorySlots) 191 | { 192 | // Skip the slot we double clicked on 193 | if (slot == this) 194 | continue; 195 | 196 | var invItem = slot.InventoryItem; 197 | 198 | // A item exists in this inv slot 199 | if (invItem != null) 200 | { 201 | // The inv slot item is the same type as the cursor item type 202 | if (invItem.Item.Type == itemToMergeTo.Type) 203 | { 204 | otherItemCounts += invItem.Item.Count; 205 | 206 | slot.RemoveItem(); 207 | } 208 | } 209 | } 210 | } 211 | var counts = itemToMergeTo.Count + otherItemCounts; 212 | itemToMergeTo.Count = counts; 213 | Main.ItemCursor.SetItem(itemToMergeTo); 214 | } 215 | 216 | // Transfer the item in the inventory slot we are currently hovering over 217 | private void TransferItem() 218 | { 219 | // Do not transfer item if this item is currently being animated 220 | if (CurrentlyAnimating) 221 | return; 222 | 223 | // There is no item here thus no item to transfer 224 | if (InventoryItem == null) 225 | return; 226 | 227 | // Get the 'other inventory' 228 | var targetInv = GetOtherInventory(); 229 | 230 | // No 'other inventory' is open, lets use the player inventory so the 231 | // item gets transfered to the same inventory instead of doing nothing 232 | if (targetInv == null) 233 | targetInv = Player.Inventory; 234 | 235 | // Try to find a empty slot in the target inventory 236 | var emptySlot = targetInv.TryGetEmptyOrSameTypeSlot(InventoryItem.Item.Type); 237 | 238 | // No empty slot was found! 239 | if (emptySlot == -1) 240 | return; 241 | 242 | //Close Item Panels 243 | ItemPanelDescription.Clear(); 244 | 245 | // Store temporary reference to the item in this inventory slot 246 | var itemRef = InventoryItem.Item; 247 | 248 | // Get a copy of the item sprite that is being transfered 249 | // This is purely for visuals and does not effect the item logic 250 | Graphic = InventoryItem.GenerateGraphic(); 251 | Graphic.GlobalPosition = Position; 252 | 253 | // Get the other slot this item is being transfered to 254 | var otherInvSlot = targetInv.InventorySlots[emptySlot]; 255 | 256 | //if (otherInvSlot.CurrentlyAnimating) 257 | // return; 258 | 259 | // Remove the item before it gets transfered 260 | this.RemoveItem(); 261 | 262 | var otherInvSlotItem = otherInvSlot.InventoryItem; 263 | 264 | var hide = false; 265 | 266 | // If the other inventory slot has no item in it, then hide the item that 267 | // gets transfered over. So it does not look like there is a duplicate 268 | // when the graphic sprite is animated over 269 | if (otherInvSlot.InventoryItem == null) 270 | hide = true; 271 | 272 | // Set the other inventory slot item to the item that is being transfered over 273 | if (otherInvSlotItem == null) 274 | { 275 | otherInvSlot.SetItem(itemRef); 276 | } 277 | else 278 | // If the item transfered has more than one than add that 279 | { 280 | itemRef.Count += otherInvSlotItem.Item.Count; 281 | otherInvSlot.SetItem(itemRef); 282 | } 283 | 284 | OtherInventorySlot = otherInvSlot; 285 | 286 | // Hide the other item 287 | if (hide) 288 | otherInvSlot.InventoryItem.Hide(); 289 | 290 | // Start animation 291 | CurrentlyAnimating = true; 292 | otherInvSlot.CurrentlyAnimating = true; 293 | 294 | // Add graphic to the world 295 | Main.AddToCanvasLayer(Graphic); 296 | 297 | Tween = Panel.GetTree().CreateTween(); 298 | Tween.TweenProperty(Graphic, "global_position", otherInvSlot.Position, 1) 299 | .SetEase(Tween.EaseType.Out) 300 | .SetTrans(Tween.TransitionType.Cubic); 301 | 302 | Tween.TweenCallback(Callable.From(() => 303 | { 304 | // Sprite graphic reached its destination, lets show the other inventory slot item now 305 | 306 | // For the love of god I have no fucking idea why I need a 307 | // null check here. I spent 2 hours trying to figure out 308 | // why InventoryItem becomes null. And with the null check 309 | // earlier it was just hiding items. But now it all magically 310 | // works all of a sudden wtf? 311 | // Please just stay working okay? 312 | // Thank you code 313 | // I love you code 314 | otherInvSlot.InventoryItem?.Show(); 315 | 316 | CurrentlyAnimating = false; 317 | otherInvSlot.CurrentlyAnimating = false; 318 | Graphic.QueueFree(); 319 | })); 320 | } 321 | 322 | private void HandleLeftClick() 323 | { 324 | var cursorItem = Main.ItemCursor.GetItem(); 325 | 326 | // If were double clicking and not holding shift and 327 | // we are holding a item in our cursor [and the inventory 328 | // slot we are hovering over has no item or there is 329 | // a item in this inventory slot and it is of the 330 | // same item type as the item type in our cursor] then 331 | // collect all items to the cursor and return preventing 332 | // any other mouse inventory logic from executing. 333 | if (InputGame.DoubleClick && !Input.IsKeyPressed(Key.Shift)) 334 | { 335 | if (cursorItem != null) 336 | { 337 | if (InventoryItem == null || InventoryItem.Item.Type == cursorItem.Type) 338 | { 339 | CollectAndMergeAllItemsFrom(cursorItem); 340 | 341 | // We collected all items to the cursor. Do not let anything else happen. 342 | return; 343 | } 344 | } 345 | // There is an item in the cursor and this is a double click 346 | else 347 | { 348 | if (InventoryItem != null) 349 | { 350 | CollectAndMergeAllItemsFrom(InventoryItem.Item); 351 | 352 | RemoveItem(); // prevent dupe glitch 353 | 354 | // We collected all items to the cursor. Do not let anything else happen. 355 | return; 356 | } 357 | } 358 | } 359 | 360 | // There is no item in this inventory slot 361 | if (InventoryItem == null) 362 | { 363 | if (!JustPickedUpItem) 364 | { 365 | // Is there a item attached to the cursor? 366 | if (cursorItem != null) 367 | { 368 | MoveItem(Main.ItemCursor); 369 | } 370 | } 371 | } 372 | // There is a item in this inventory slot 373 | else 374 | { 375 | // Recap 376 | // 1. Left click 377 | // 2. There is an item in the inventory slot 378 | 379 | JustPickedUpItem = true; 380 | Panel.GetTree().CreateTimer(InputGame.DoubleClickTime / 1000.0).Timeout += () => 381 | JustPickedUpItem = false; 382 | 383 | // There is an item attached to the cursor 384 | if (cursorItem != null) 385 | { 386 | // The cursor and inv slot items are of the same type 387 | if (cursorItem.Type == InventoryItem.Item.Type) 388 | { 389 | PlaceAll(Main.ItemCursor); 390 | } 391 | // The cursor and inv slot items are of different types 392 | else 393 | { 394 | // Recap: 395 | // 1. Left Click 396 | // 2. There is an item in the inventory slot 397 | // 3. There is a item in the cursor 398 | // 4. The cursor item and inv slot item are of different types 399 | // 400 | // So lets swap the cursor item with the inv slot item 401 | SwapItem(Main.ItemCursor); 402 | } 403 | } 404 | // There is no item attached to the cursor 405 | else 406 | { 407 | // Shift + Click 408 | if (Input.IsKeyPressed(Key.Shift)) 409 | { 410 | TransferItem(); 411 | 412 | return; 413 | } 414 | 415 | PickupAll(Main.ItemCursor); 416 | } 417 | } 418 | } 419 | 420 | private void HandleRightClick() 421 | { 422 | var cursorItem = Main.ItemCursor.GetItem(); 423 | 424 | // Shift + Right Click = Split Stack 425 | if (Input.IsKeyPressed(Key.Shift)) 426 | { 427 | if (InventoryItem != null && cursorItem == null) 428 | SplitStack(Main.ItemCursor); 429 | 430 | return; 431 | } 432 | 433 | // Is there a item attached to the cursor? 434 | if (cursorItem != null) 435 | { 436 | if (InventoryItem != null) 437 | { 438 | if (InventoryItem.Item.Type != cursorItem.Type) 439 | { 440 | SwapItem(Main.ItemCursor); 441 | } 442 | else 443 | { 444 | PlaceOne(Main.ItemCursor); 445 | } 446 | } 447 | else 448 | PlaceOne(Main.ItemCursor); 449 | } 450 | // There is no item being held in the cursor 451 | else 452 | { 453 | // There is a item in this inventory slot 454 | if (InventoryItem != null) 455 | { 456 | var invSlotItemCount = InventoryItem.Item.Count; 457 | 458 | // Is this the last item in the stack? 459 | if (invSlotItemCount - 1 == 0) 460 | { 461 | PickupAll(Main.ItemCursor); 462 | } 463 | // There are two or more items in this inv slot 464 | else 465 | { 466 | // Recap: 467 | // 1. User did a right click 468 | // 2. There is no item being held in the cursor 469 | // 3. There are two or more items in this inv slot 470 | 471 | // Lets take 1 item from the inv slot and bring it to the cursor 472 | PickupOne(); 473 | 474 | UpdateItemCountLabel(InventoryItem.Item.Count); 475 | 476 | var item = InventoryItem.Item.Clone(); 477 | item.Count = 1; 478 | Main.ItemCursor.SetItem(item); 479 | } 480 | } 481 | } 482 | } 483 | } 484 | -------------------------------------------------------------------------------- /Scripts/Inventory/InventoryStaticItem.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public class InventoryStaticItem : InventoryItem 4 | { 5 | public override bool Visible { get => Sprite2D.Visible; set => Sprite2D.Visible = value; } 6 | public override Node2D Node { get => Sprite2D; } 7 | 8 | private Sprite2D Sprite2D { get; set; } 9 | private ItemStatic ItemStatic { get; set; } 10 | private Inventory Inv { get; set; } 11 | 12 | public InventoryStaticItem(Inventory inv, Node parent, ItemStatic itemStatic, Item item) 13 | { 14 | Inv = inv; 15 | ItemStatic = itemStatic; 16 | Item = item; 17 | Sprite2D = (Sprite2D)GenerateGraphic(); 18 | 19 | parent.AddChild(Sprite2D); 20 | } 21 | 22 | public override Node2D GenerateGraphic() 23 | { 24 | var sprite = new Sprite2D 25 | { 26 | Texture = ItemStatic.Texture, 27 | Position = Vector2.One * (Inv.SlotSize / 2), 28 | Scale = Vector2.One * (Inv.SlotSize / 25) 29 | }; 30 | 31 | return sprite; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Scripts/Inventory/Item.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public class Item 4 | { 5 | public ItemType Type { get; set; } 6 | public int Count { get; set; } 7 | 8 | public Item(ItemType type, int count = 1) 9 | { 10 | Type = type; 11 | Count = count; 12 | } 13 | 14 | public Item Clone() => new(Type, Count); 15 | } 16 | -------------------------------------------------------------------------------- /Scripts/Inventory/ItemAnimated.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public class ItemAnimated : ItemType 4 | { 5 | public SpriteFrames SpriteFrames { get; set; } 6 | 7 | public override InventoryItem ToInventoryItem(Inventory inventory, Panel panel, Item item) => 8 | new InventoryAnimatedItem(inventory, panel, this, item); 9 | } 10 | -------------------------------------------------------------------------------- /Scripts/Inventory/ItemCursor.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public class ItemCursor : ItemHolder 4 | { 5 | private Node Parent { get; set; } 6 | 7 | public ItemCursor(Node parent) 8 | { 9 | Parent = parent; 10 | } 11 | 12 | public override void SetItem(Item item) 13 | { 14 | ItemPanelDescription.ToggleVisiblity(false); 15 | 16 | // There is an item in this cursor, lets move the parent now 17 | Parent.SetPhysicsProcess(true); 18 | Item = item.Clone(); 19 | 20 | Parent.QueueFreeChildren(); 21 | 22 | ItemCountLabel = UtilsLabel.CreateItemCountLabel(); 23 | ItemCountLabel.ZIndex = 3; 24 | 25 | if (item.Count > 1) 26 | ItemCountLabel.Text = item.Count + ""; 27 | 28 | Parent.AddChild(ItemCountLabel); 29 | 30 | if (item.Type is ItemStatic itemStatic) 31 | { 32 | var staticSprite = new Sprite2D 33 | { 34 | Texture = itemStatic.Texture, 35 | Scale = Vector2.One * 2, 36 | ZIndex = 2 // ensure cursor item rendered above Inventory UI 37 | }; 38 | 39 | Parent.AddChild(staticSprite); 40 | } 41 | 42 | if (item.Type is ItemAnimated itemAnimated) 43 | { 44 | var animatedSprite = new AnimatedSprite2D 45 | { 46 | SpriteFrames = itemAnimated.SpriteFrames, 47 | Scale = Vector2.One * 2, 48 | ZIndex = 2 // ensure cursor item rendered above Inventory UI 49 | }; 50 | 51 | animatedSprite.Play(); 52 | 53 | Parent.AddChild(animatedSprite); 54 | } 55 | 56 | // Quick and dirty way to center the label. This is how it has to be 57 | // for now until someone can figure out a better way 58 | ItemCountLabel.Position = -ItemCountLabel.Size / 2 + new Vector2(4, 0); 59 | } 60 | 61 | public override void RemoveItem() 62 | { 63 | ItemPanelDescription.ToggleVisiblity(true); 64 | Item = null; 65 | 66 | // Only move the parent when there is an item in this cursor 67 | Parent.SetPhysicsProcess(false); 68 | Parent.QueueFreeChildren(); 69 | } 70 | 71 | public Item GetItem() 72 | { 73 | if (Parent.GetChildren().Count == 0) 74 | return null; 75 | 76 | return Item; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Scripts/Inventory/ItemCursorManager.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public partial class ItemCursorManager : Control 4 | { 5 | public ItemCursor ItemCursor { get; set; } 6 | 7 | public override void _Ready() 8 | { 9 | SetPhysicsProcess(false); 10 | 11 | ItemCursor = new ItemCursor(this); 12 | } 13 | 14 | public override void _PhysicsProcess(double delta) 15 | { 16 | Position = GetViewport().GetMousePosition(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Scripts/Inventory/ItemHolder.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public abstract class ItemHolder 4 | { 5 | public virtual Item Item { get; set; } 6 | 7 | public Label ItemCountLabel { get; set; } 8 | 9 | public abstract void SetItem(Item item); 10 | public abstract void RemoveItem(); 11 | 12 | /// 13 | /// Split stack and place half to ItemHolder 14 | /// 15 | public void SplitStack(ItemHolder to) 16 | { 17 | var itemA = Item; 18 | 19 | // Prevent duping 1 item to 2 items 20 | if (itemA.Count == 1) 21 | return; 22 | 23 | var itemB = itemA.Clone(); 24 | 25 | var half = itemA.Count / 2; 26 | var remainder = itemA.Count % 2; 27 | 28 | itemA.Count = half + remainder; 29 | itemB.Count = half; 30 | 31 | SetItem(itemA); 32 | to.SetItem(itemB); 33 | } 34 | 35 | /// 36 | /// Place all items to this from ItemHolder 37 | /// 38 | public void PlaceAll(ItemHolder from) 39 | { 40 | var item = from.Item.Clone(); 41 | item.Count += Item.Count; 42 | 43 | from.RemoveItem(); 44 | 45 | SetItem(item); 46 | } 47 | 48 | /// 49 | /// Place one item to this from ItemHolder 50 | /// 51 | public void PlaceOne(ItemHolder from) 52 | { 53 | var item = from.Item.Clone(); 54 | from.PickupOne(); 55 | 56 | item.Count = 1; 57 | 58 | if (Item != null) 59 | item.Count += Item.Count; 60 | 61 | SetItem(item); 62 | } 63 | 64 | /// 65 | /// Take all item from the stack 66 | /// 67 | public void PickupAll(ItemHolder to) 68 | { 69 | to.SetItem(Item); 70 | RemoveItem(); 71 | } 72 | 73 | /// 74 | /// Take one item from the stack 75 | /// 76 | public void PickupOne() 77 | { 78 | Item.Count -= 1; 79 | ItemCountLabel.Text = Item.Count + ""; 80 | 81 | if (Item.Count <= 0) 82 | RemoveItem(); 83 | } 84 | 85 | /// 86 | /// Pickup a item from ItemHolder of the same type 87 | /// 88 | public void PickupSameType(ItemHolder from) 89 | { 90 | if (Item.Type != from.Item.Type) 91 | return; 92 | 93 | Item.Count += from.Item.Count; 94 | 95 | from.SetItem(Item); 96 | RemoveItem(); 97 | } 98 | 99 | /// 100 | /// Move item from ItemHolder A to ItemHolder B 101 | /// 102 | public void MoveItem(ItemHolder to) 103 | { 104 | SetItem(to.Item); 105 | to.RemoveItem(); 106 | } 107 | 108 | /// 109 | /// Swap item in this holder with the item from the other holder 110 | /// 111 | /// Where this item is being swapped to 112 | public void SwapItem(ItemHolder to) 113 | { 114 | // Destination item exists and Item and to.Item are of different types 115 | // If this is the case lets swap 116 | if (to.Item != null && to.Item.Type != Item.Type) 117 | { 118 | // Remember Item as item before removing it 119 | var item = Item; 120 | 121 | RemoveItem(); 122 | 123 | SetItem(to.Item); 124 | to.SetItem(item); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /Scripts/Inventory/ItemPanelDescription.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public partial class ItemPanelDescription : Control 4 | { 5 | private static Control ItemPanelDescriptionParent { get; set; } 6 | 7 | public static void ToggleVisiblity(bool v) => ItemPanelDescriptionParent.Visible = v; 8 | 9 | public static void Clear() 10 | { 11 | ItemPanelDescriptionParent.QueueFreeChildren(); 12 | ItemPanelDescriptionParent.SetPhysicsProcess(false); 13 | } 14 | 15 | public static void Display(Item item) 16 | { 17 | ItemPanelDescriptionParent.SetPhysicsProcess(true); 18 | var panelContainer = new PanelContainer 19 | { 20 | MouseFilter = MouseFilterEnum.Ignore, 21 | ZIndex = 1 22 | }; 23 | 24 | var marginContainer = new MarginContainer 25 | { 26 | MouseFilter = MouseFilterEnum.Ignore 27 | }; 28 | marginContainer.AddMargin(5); 29 | 30 | panelContainer.AddChild(marginContainer); 31 | 32 | var vbox = new VBoxContainer 33 | { 34 | MouseFilter = MouseFilterEnum.Ignore 35 | }; 36 | marginContainer.AddChild(vbox); 37 | 38 | var labelName = new Label 39 | { 40 | Text = item.Type.Name, 41 | MouseFilter = MouseFilterEnum.Ignore, 42 | HorizontalAlignment = HorizontalAlignment.Center 43 | }; 44 | 45 | var labelDescription = new Label 46 | { 47 | Text = item.Type.Description, 48 | MouseFilter = MouseFilterEnum.Ignore, 49 | HorizontalAlignment = HorizontalAlignment.Center 50 | }; 51 | 52 | vbox.AddChild(labelName); 53 | vbox.AddChild(labelDescription); 54 | 55 | ItemPanelDescriptionParent.AddChild(panelContainer); 56 | } 57 | 58 | public override void _Ready() 59 | { 60 | SetPhysicsProcess(false); 61 | ItemPanelDescriptionParent = this; 62 | } 63 | 64 | public override void _PhysicsProcess(double delta) 65 | { 66 | var offset = new Vector2(20, 0); 67 | 68 | Position = GetViewport().GetMousePosition() + offset; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Scripts/Inventory/ItemStatic.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public class ItemStatic : ItemType 4 | { 5 | public Texture2D Texture { get; set; } 6 | 7 | public override InventoryItem ToInventoryItem(Inventory inventory, Panel panel, Item item) => 8 | new InventoryStaticItem(inventory, panel, this, item); 9 | } 10 | -------------------------------------------------------------------------------- /Scripts/Inventory/ItemType.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public abstract class ItemType 4 | { 5 | public string Name { get; set; } 6 | public string Description { get; set; } 7 | 8 | public abstract InventoryItem ToInventoryItem(Inventory inventory, Panel panel, Item item); 9 | } 10 | -------------------------------------------------------------------------------- /Scripts/Inventory/Items.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public static class Items 4 | { 5 | // animated 6 | public static ItemAnimated Coin { get; } = new() 7 | { 8 | SpriteFrames = LoadSpriteFrames("sprite_frames_coin"), 9 | Name = "Coin", 10 | Description = "A shiny coin" 11 | }; 12 | 13 | // static 14 | public static ItemStatic CoinSnowy { get; } = new() 15 | { 16 | Texture = LoadTexture("coin_snowy"), 17 | Name = "Snowy Coin", 18 | Description = "A frozen coin" 19 | }; 20 | 21 | public static ItemStatic CoinPink { get; } = new() 22 | { 23 | Texture = LoadTexture("coin_pink"), 24 | Name = "Pink Coin", 25 | Description = "A coin with a pink tint to it" 26 | }; 27 | 28 | public static ItemStatic CoinRed { get; } = new() 29 | { 30 | Texture = LoadTexture("coin_red"), 31 | Name = "Red Coin", 32 | Description = "A coin with a red tint to it" 33 | }; 34 | 35 | private static SpriteFrames LoadSpriteFrames(string path) => 36 | GD.Load($"res://{path}.tres"); 37 | 38 | private static Texture2D LoadTexture(string path) => 39 | GD.Load($"res://sprites/{path}.png"); 40 | } 41 | -------------------------------------------------------------------------------- /Scripts/Inventory/PlayerInventory.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | 3 | namespace Inventory; 4 | 5 | public class PlayerInventory : Inventory 6 | { 7 | public bool CurrentlyAnimating { get; set; } 8 | public bool IsHotbar { get; set; } = true; 9 | 10 | private Node Node { get; set; } 11 | private Tween TweenExit { get; set; } 12 | private Tween TweenReEntry { get; set; } 13 | 14 | public PlayerInventory(Node node, int columns = 9, int rows = 5, int slotSize = 50) 15 | : base(node, columns, rows, slotSize) 16 | { 17 | Node = node; 18 | 19 | SetAnchor(Control.LayoutPreset.CenterBottom); 20 | SwitchToHotbar(); 21 | Open(); 22 | } 23 | 24 | // Instantly switch 25 | public void SwitchToHotbar() 26 | { 27 | // ensure animation tweens are dead 28 | TweenExit?.Kill(); 29 | TweenReEntry?.Kill(); 30 | 31 | IsHotbar = true; 32 | CurrentlyAnimating = false; 33 | 34 | MakePanelInvisible(); 35 | SetSlotsVisibility(0, InventorySlots.Length - Columns, false, true); 36 | } 37 | 38 | // Instantly switch 39 | public void SwitchToFullInventory() 40 | { 41 | // ensure animation tweens are dead 42 | TweenExit?.Kill(); 43 | TweenReEntry?.Kill(); 44 | 45 | IsHotbar = false; 46 | CurrentlyAnimating = false; 47 | 48 | MakePanelVisible(); 49 | SetSlotsVisibility(0, InventorySlots.Length - Columns, true, true); 50 | } 51 | 52 | public void SwitchToHotbarAnimated() 53 | { 54 | Transition(true, () => 55 | { 56 | MakePanelInvisible(); 57 | SetSlotsVisibility(0, InventorySlots.Length - Columns, false, true); 58 | }, 1, 1, Tween.TransitionType.Back); 59 | } 60 | 61 | public void SwitchToFullInventoryAnimated() 62 | { 63 | Transition(false, () => 64 | { 65 | MakePanelVisible(); 66 | SetSlotsVisibility(0, InventorySlots.Length - Columns, true, true); 67 | }, 1, 0.5, Tween.TransitionType.Sine); 68 | } 69 | 70 | private void Transition(bool isHotbar, Action action, double exitTime, double reEntryTime, 71 | Tween.TransitionType reEntryTransType) 72 | { 73 | if (CurrentlyAnimating) 74 | return; 75 | 76 | IsHotbar = isHotbar; 77 | CurrentlyAnimating = true; 78 | 79 | TweenExit = Node.GetTree().CreateTween(); 80 | var container = PanelContainer; 81 | 82 | // Move the hotbar out of the game view by moving down by its own height 83 | TweenExit.TweenProperty(container, "position:y", container.Size.Y, exitTime) 84 | .SetTrans(Tween.TransitionType.Cubic) 85 | .SetEase(Tween.EaseType.InOut); 86 | 87 | TweenExit.TweenCallback(Callable.From(() => 88 | { 89 | // Transform the hotbar into a full inventory 90 | action(); 91 | 92 | // Reset the hotbar y position 93 | // Make sure to reset the y position after SetSlotsVisibility(...) because 94 | // SetSlotsVisibility makes a call to SetAnchorsAndOffsetsPreset(...) which 95 | // sets the position to a unwanted y value 96 | container.Position = new Vector2(container.Position.X, 0); 97 | })); 98 | 99 | // Since the hotbar was transformed to a full inventory, its container size has changed 100 | // But container.Size.Y will not be updated until SortChildren has fired 101 | // So lets subscribe to SortChildren 102 | container.SortChildren += animateUp; 103 | 104 | void animateUp() 105 | { 106 | // SortChildren is called multiple times and we only need it for one time 107 | container.SortChildren -= animateUp; 108 | 109 | // The previous tween is no longer any good, so lets make another 110 | TweenReEntry = Node.GetTree().CreateTween(); 111 | 112 | // Move the full inventory into the games view by moving up by its own height 113 | TweenReEntry.TweenProperty(container, "position:y", -PanelContainer.Size.Y, reEntryTime) 114 | .SetTrans(reEntryTransType) 115 | .SetEase(Tween.EaseType.Out); 116 | 117 | TweenReEntry.TweenCallback(Callable.From(() => CurrentlyAnimating = false)); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Scripts/Main.cs: -------------------------------------------------------------------------------- 1 | global using Godot; 2 | global using System; 3 | global using System.Linq; 4 | global using System.Collections.Generic; 5 | 6 | namespace Inventory; 7 | 8 | public partial class Main : Node 9 | { 10 | public static ItemCursor ItemCursor { get; set; } 11 | private static Node CanvasLayer { get; set; } 12 | 13 | public static void AddToCanvasLayer(Node node) => CanvasLayer.AddChild(node); 14 | 15 | public override void _Ready() 16 | { 17 | CanvasLayer = GetNode("CanvasLayer"); 18 | ItemCursor = CanvasLayer.GetNode("ItemCursorParent").ItemCursor; 19 | 20 | // Setup chest inventory 21 | var chest = GetNode("Chest"); 22 | 23 | var inv = chest.Inventory; 24 | 25 | for (int i = 0; i < 9; i++) 26 | inv.SetItem(i, new Item(Items.Coin)); 27 | 28 | inv.SetItem(0, 2, new Item(Items.Coin, 3)); 29 | inv.SetItem(1, 2, new Item(Items.CoinSnowy)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Scripts/Player.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public partial class Player : CharacterBody2D 4 | { 5 | public static PlayerInventory Inventory { get; set; } 6 | 7 | // The speed of the player 8 | [Export] public float Speed { get; set; } = 500; 9 | 10 | // A friction of 1.0 will bring the player to a complete halt 11 | [Export(PropertyHint.Range, "0, 0.2")] public float Friction { get; set; } = 0.01f; 12 | 13 | private AnimatedSprite2D AnimatedSprite2D { get; set; } 14 | 15 | private Vector2 MoveVec { get; set; } 16 | 17 | public override void _Ready() 18 | { 19 | AnimatedSprite2D = GetNode("AnimatedSprite2D"); 20 | 21 | Inventory = new(this); 22 | 23 | Inventory.SetItem(1, new Item(Items.CoinRed)); 24 | 25 | for (int i = 18; i < 27; i++) 26 | Inventory.SetItem(i, new Item(Items.CoinPink)); 27 | } 28 | 29 | public override void _PhysicsProcess(double delta) 30 | { 31 | Animate(); 32 | Move(delta); 33 | } 34 | 35 | public override void _Input(InputEvent @event) 36 | { 37 | UtilsInventory.HandleInput(@event); 38 | } 39 | 40 | private void Animate() 41 | { 42 | var rawInputVec = Utils.GetMovementInputRaw("player"); 43 | 44 | if (rawInputVec == Vector2.Zero) 45 | AnimatedSprite2D.InstantPlay("idle"); 46 | else if (rawInputVec.X > 0) 47 | AnimatedSprite2D.InstantPlay("walk_right"); 48 | else if (rawInputVec.X < 0) 49 | AnimatedSprite2D.InstantPlay("walk_left"); 50 | else if (rawInputVec.Y > 0) 51 | AnimatedSprite2D.InstantPlay("walk_down"); 52 | else if (rawInputVec.Y < 0) 53 | AnimatedSprite2D.InstantPlay("walk_up"); 54 | } 55 | 56 | private void Move(double delta) 57 | { 58 | MoveVec *= 1 - Friction; 59 | MoveVec += Utils.GetMovementInput("player") * Speed * (float)delta; 60 | Velocity = MoveVec; 61 | 62 | MoveAndSlide(); 63 | } 64 | } -------------------------------------------------------------------------------- /Scripts/Utils/Extensions/ExtensionsAnimatedSprite2D.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public static class ExtensionsAnimatedSprite2D 4 | { 5 | /// 6 | /// Instantly switch to the next animation without waiting for the current animation to finish 7 | /// 8 | public static void InstantPlay(this AnimatedSprite2D animatedSprite2D, string animation) 9 | { 10 | animatedSprite2D.Animation = animation; 11 | animatedSprite2D.Play(animation); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Scripts/Utils/Extensions/ExtensionsContainer.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public static class ExtensionsContainer 4 | { 5 | public static MarginContainer AddMargin(this MarginContainer container, int padding) 6 | { 7 | foreach (var margin in new string[] { "left", "right", "top", "bottom" }) 8 | container.AddThemeConstantOverride($"margin_{margin}", padding); 9 | 10 | return container; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Scripts/Utils/Extensions/ExtensionsInputEvent.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public static class ExtensionsInputEvent 4 | { 5 | // InputEventMouseButton 6 | public static bool IsLeftClickPressed(this InputEventMouseButton @event) => 7 | @event.IsPressed(MouseButton.Left); 8 | 9 | public static bool IsLeftClickReleased(this InputEventMouseButton @event) => 10 | @event.IsReleased(MouseButton.Left); 11 | 12 | public static bool IsRightClickPressed(this InputEventMouseButton @event) => 13 | @event.IsPressed(MouseButton.Right); 14 | 15 | public static bool IsRightClickReleased(this InputEventMouseButton @event) => 16 | @event.IsReleased(MouseButton.Right); 17 | 18 | // Private Helper Functions 19 | private static bool IsPressed(this InputEventMouseButton @event, MouseButton button) => 20 | @event.ButtonIndex == button && @event.Pressed; 21 | 22 | private static bool IsReleased(this InputEventMouseButton @event, MouseButton button) => 23 | @event.ButtonIndex == button && !@event.Pressed; 24 | } 25 | -------------------------------------------------------------------------------- /Scripts/Utils/Extensions/ExtensionsNode.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public static class ExtensionsNode 4 | { 5 | public static void QueueFreeChildren(this Node parentNode) 6 | { 7 | foreach (Node node in parentNode.GetChildren()) 8 | node.QueueFree(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Scripts/Utils/Extensions/ExtensionsObject.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Serialization; 3 | using System.Linq; 4 | using System.Reflection; 5 | using JsonProperty = Newtonsoft.Json.Serialization.JsonProperty; 6 | 7 | namespace Inventory; 8 | 9 | public static class ExtensionsObject 10 | { 11 | /// 12 | /// Prints a collection in a readable format 13 | /// 14 | public static string Print(this IEnumerable value, bool newLine = true) => 15 | value != null ? string.Join(newLine ? "\n" : ", ", value) : null; 16 | 17 | /// 18 | /// Prints the entire object in a readable format (supports Godot properties) 19 | /// If you should ever run into a problem, see the IgnorePropsResolver class to ignore more 20 | /// properties. 21 | /// 22 | public static string PrintFull(this object v) => 23 | JsonConvert.SerializeObject(v, Formatting.Indented, new JsonSerializerSettings 24 | { 25 | ReferenceLoopHandling = ReferenceLoopHandling.Ignore, 26 | ContractResolver = new IgnorePropsResolver() // ignore all Godot props 27 | }); 28 | 29 | /// 30 | /// A convience method for a foreach loop at the the sacrafice of debugging support 31 | /// 32 | public static void ForEach(this IEnumerable value, Action action) 33 | { 34 | foreach (var element in value) 35 | action(element); 36 | } 37 | 38 | 39 | /// 40 | /// Used when doing JsonConvert.SerializeObject to ignore Godot properties 41 | /// as these are massive. 42 | /// 43 | private class IgnorePropsResolver : DefaultContractResolver 44 | { 45 | protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) 46 | { 47 | var prop = base.CreateProperty(member, memberSerialization); 48 | 49 | // Ignored properties (prevents crashes) 50 | var ignoredProps = new Type[] 51 | { 52 | typeof(GodotObject), 53 | typeof(Node), 54 | typeof(NodePath) 55 | }; 56 | 57 | foreach (var ignoredProp in ignoredProps) 58 | { 59 | if (ignoredProp.GetProperties().Contains(member)) 60 | prop.Ignored = true; 61 | 62 | if (prop.PropertyType == ignoredProp || prop.PropertyType.IsSubclassOf(ignoredProp)) 63 | prop.Ignored = true; 64 | } 65 | 66 | return prop; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Scripts/Utils/Utils.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public static class Utils 4 | { 5 | public static Vector2 GetMovementInputRaw(string prefix = "") 6 | { 7 | if (!string.IsNullOrWhiteSpace(prefix)) 8 | prefix += "_"; 9 | 10 | // Returns 0, -1 or 1 depending on input 11 | var inputHorz = Input.GetActionRawStrength($"{prefix}move_right") - Input.GetActionRawStrength($"{prefix}move_left"); 12 | var inputVert = Input.GetActionRawStrength($"{prefix}move_down") - Input.GetActionRawStrength($"{prefix}move_up"); 13 | 14 | // Normalize vector to prevent fast diagonal strafing 15 | return new Vector2(inputHorz, inputVert).Normalized(); 16 | } 17 | 18 | public static Vector2 GetMovementInput(string prefix = "") 19 | { 20 | if (!string.IsNullOrWhiteSpace(prefix)) 21 | prefix += "_"; 22 | 23 | // GetActionStrength(...) supports controller sensitivity 24 | var inputHorz = Input.GetActionStrength($"{prefix}move_right") - Input.GetActionStrength($"{prefix}move_left"); 25 | var inputVert = Input.GetActionStrength($"{prefix}move_down") - Input.GetActionStrength($"{prefix}move_up"); 26 | 27 | // Normalize vector to prevent fast diagonal strafing 28 | return new Vector2(inputHorz, inputVert).Normalized(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Scripts/Utils/UtilsInventory.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public static class UtilsInventory 4 | { 5 | public static int IsHotbarHotkeyJustPressed() 6 | { 7 | for (int i = 0; i < Player.Inventory.Columns; i++) 8 | if (Input.IsActionJustPressed($"inventory_hotbar_{i + 1}")) 9 | return i; 10 | 11 | return -1; 12 | } 13 | 14 | public static int IsHotbarHotkeyPressed() 15 | { 16 | for (int i = 0; i < Player.Inventory.Columns; i++) 17 | if (Input.IsActionPressed($"inventory_hotbar_{i + 1}")) 18 | return i; 19 | 20 | return -1; 21 | } 22 | 23 | public static void HandleInput(InputEvent @event) 24 | { 25 | InputGame.Handle(@event); 26 | 27 | if (Input.IsActionJustPressed("interact")) 28 | { 29 | Inventory.ActiveChest?.Open(); 30 | } 31 | 32 | if (Input.IsActionJustPressed("inventory")) 33 | { 34 | if (!Player.Inventory.IsHotbar) 35 | { 36 | Player.Inventory.SwitchToHotbarAnimated(); 37 | } 38 | else 39 | { 40 | Player.Inventory.SwitchToFullInventoryAnimated(); 41 | } 42 | } 43 | 44 | if (Input.IsActionJustPressed("inventory_take_all")) 45 | { 46 | ItemPanelDescription.Clear(); 47 | 48 | Inventory.OtherInventory?.TakeAll(); 49 | } 50 | 51 | if (Input.IsActionJustPressed("inventory_sort")) 52 | { 53 | ItemPanelDescription.Clear(); 54 | 55 | Inventory.OtherInventory?.Sort(); 56 | Player.Inventory.Sort(); 57 | } 58 | 59 | var hotbar = IsHotbarHotkeyJustPressed(); 60 | if (hotbar != -1) 61 | InputHotbar(hotbar); 62 | 63 | // DEBUG 64 | var debugInv = Inventory.OtherInventory; 65 | 66 | if (debugInv == null) 67 | return; 68 | 69 | if (Input.IsKeyPressed(Key.F1)) 70 | for (int i = 0; i < debugInv.InventorySlots.Length; i++) 71 | debugInv.InventorySlots[i].SetDebugLabel(debugInv.InventorySlots[i].InventoryItem == null ? "null" : "item" + ""); 72 | 73 | if (Input.IsKeyPressed(Key.F2)) 74 | for (int i = 0; i < debugInv.InventorySlots.Length; i++) 75 | debugInv.InventorySlots[i].SetDebugLabel(""); 76 | } 77 | 78 | private static void InputHotbar(int hotbar) 79 | { 80 | var cursorItem = Main.ItemCursor; 81 | 82 | if (cursorItem.GetItem() != null) 83 | { 84 | HotbarInv(cursorItem, hotbar); 85 | return; 86 | } 87 | 88 | var activeInvSlot = Inventory.ActiveInventorySlot; 89 | 90 | if (activeInvSlot != null && activeInvSlot.InventoryItem != null) 91 | HotbarInv(activeInvSlot, hotbar); 92 | } 93 | 94 | private static void HotbarInv(ItemHolder itemHolder, int hotbar) 95 | { 96 | var playerInv = Player.Inventory; 97 | var playerInvSlots = playerInv.InventorySlots; 98 | var columns = playerInv.Columns; 99 | 100 | // If for example there are only 5 columns and hotbar is 6 then this 101 | // hotbar does not exist because there are only 5 columns 102 | if (columns <= hotbar) 103 | return; 104 | 105 | // Get the hotbar slot this item is being transfered to 106 | var hotbarSlot = playerInvSlots[playerInvSlots.Length - columns + hotbar]; 107 | 108 | // If the hotbar slot is the same slot as the slot we are hovering over then dont do anything 109 | if (itemHolder == hotbarSlot) 110 | return; 111 | 112 | // Prevent item panel description from lingering around when the item disappears from the cursor or inv slot 113 | ItemPanelDescription.Clear(); 114 | 115 | // There is no item in the hotbar slot 116 | if (hotbarSlot.InventoryItem == null) 117 | { 118 | // Just move the item over 119 | hotbarSlot.SetItem(itemHolder.Item); 120 | itemHolder.RemoveItem(); 121 | } 122 | // There is a item in the hotbar slot 123 | else 124 | { 125 | // The item in the hotbar slot and the item being transfered are of the same type 126 | if (hotbarSlot.InventoryItem.Item.Type == itemHolder.Item.Type) 127 | { 128 | // Add the item counts together 129 | itemHolder.Item.Count += hotbarSlot.InventoryItem.Item.Count; 130 | 131 | // Then move the item over 132 | hotbarSlot.SetItem(itemHolder.Item); 133 | itemHolder.RemoveItem(); 134 | } 135 | // Hotbar slot item and item being transfered are not the same type 136 | else 137 | { 138 | itemHolder.SwapItem(hotbarSlot); 139 | } 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /Scripts/Utils/UtilsLabel.cs: -------------------------------------------------------------------------------- 1 | namespace Inventory; 2 | 3 | public static class UtilsLabel 4 | { 5 | public static Label CreateItemCountLabel() 6 | { 7 | var label = new Label 8 | { 9 | HorizontalAlignment = HorizontalAlignment.Left, 10 | VerticalAlignment = VerticalAlignment.Bottom, 11 | CustomMinimumSize = new Vector2(50, 50), 12 | ZIndex = 1, // ensure label is rendered on top of item 13 | Position = new Vector2(4, 0) // push 4 pixels from left to right 14 | }; 15 | label.AddThemeColorOverride("font_shadow_color", Colors.Black); 16 | label.AddThemeConstantOverride("shadow_outline_size", 5); 17 | label.AddThemeFontSizeOverride("font_size", 16); 18 | return label; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sprites/chest-sheet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CSharpGodotTools/Inventory-2023-Old/8f6d2d782c7716e9202cedad772b052231abfd4c/Sprites/chest-sheet.png -------------------------------------------------------------------------------- /Sprites/chest-sheet.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://clul67t2kth6y" 6 | path="res://.godot/imported/chest-sheet.png-b1322a0c00e4e789ba5b3a765ffb056b.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://Sprites/chest-sheet.png" 14 | dest_files=["res://.godot/imported/chest-sheet.png-b1322a0c00e4e789ba5b3a765ffb056b.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /Sprites/coin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CSharpGodotTools/Inventory-2023-Old/8f6d2d782c7716e9202cedad772b052231abfd4c/Sprites/coin.png -------------------------------------------------------------------------------- /Sprites/coin.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://cjfu782vqpsux" 6 | path="res://.godot/imported/coin.png-b7fabe76c61e67396a7c0402e221b540.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://Sprites/coin.png" 14 | dest_files=["res://.godot/imported/coin.png-b7fabe76c61e67396a7c0402e221b540.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /Sprites/coin_pink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CSharpGodotTools/Inventory-2023-Old/8f6d2d782c7716e9202cedad772b052231abfd4c/Sprites/coin_pink.png -------------------------------------------------------------------------------- /Sprites/coin_pink.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://csjd1ixxmnq7" 6 | path="res://.godot/imported/coin_pink.png-685838c5ab0178f3f7029ee801d87f43.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://Sprites/coin_pink.png" 14 | dest_files=["res://.godot/imported/coin_pink.png-685838c5ab0178f3f7029ee801d87f43.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /Sprites/coin_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CSharpGodotTools/Inventory-2023-Old/8f6d2d782c7716e9202cedad772b052231abfd4c/Sprites/coin_red.png -------------------------------------------------------------------------------- /Sprites/coin_red.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bxbxp4vhnonih" 6 | path="res://.godot/imported/coin_red.png-8e096f38431ad00f551b68464fce6a85.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://Sprites/coin_red.png" 14 | dest_files=["res://.godot/imported/coin_red.png-8e096f38431ad00f551b68464fce6a85.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /Sprites/coin_snowy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CSharpGodotTools/Inventory-2023-Old/8f6d2d782c7716e9202cedad772b052231abfd4c/Sprites/coin_snowy.png -------------------------------------------------------------------------------- /Sprites/coin_snowy.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://d0hnrw2hpe657" 6 | path="res://.godot/imported/coin_snowy.png-d4573ce385199fec89e540608d11a618.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://Sprites/coin_snowy.png" 14 | dest_files=["res://.godot/imported/coin_snowy.png-d4573ce385199fec89e540608d11a618.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /Sprites/fumiko.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CSharpGodotTools/Inventory-2023-Old/8f6d2d782c7716e9202cedad772b052231abfd4c/Sprites/fumiko.png -------------------------------------------------------------------------------- /Sprites/fumiko.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://c23tlhfrtu1sh" 6 | path="res://.godot/imported/fumiko.png-5f0639a4bcae2307ef0afa23aff0cf34.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://Sprites/fumiko.png" 14 | dest_files=["res://.godot/imported/fumiko.png-5f0639a4bcae2307ef0afa23aff0cf34.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /Sprites/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /Sprites/icon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dnksce6epsfh2" 6 | path="res://.godot/imported/icon.svg-b67cfb692a937e61d2bda8561d9e8db7.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://Sprites/icon.svg" 14 | dest_files=["res://.godot/imported/icon.svg-b67cfb692a937e61d2bda8561d9e8db7.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | svg/scale=1.0 36 | editor/scale_with_editor_scale=false 37 | editor/convert_colors_with_editor_theme=false 38 | -------------------------------------------------------------------------------- /project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="Inventory" 14 | run/main_scene="res://Scenes/Main.tscn" 15 | config/features=PackedStringArray("4.0", "C#", "Forward Plus") 16 | config/icon="res://Sprites/icon.svg" 17 | 18 | [dotnet] 19 | 20 | project/assembly_name="Inventory" 21 | 22 | [input] 23 | 24 | inventory={ 25 | "deadzone": 0.5, 26 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":73,"key_label":0,"unicode":105,"echo":false,"script":null) 27 | ] 28 | } 29 | inventory_sort={ 30 | "deadzone": 0.5, 31 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":82,"key_label":0,"unicode":82,"echo":false,"script":null) 32 | ] 33 | } 34 | inventory_take_all={ 35 | "deadzone": 0.5, 36 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":84,"key_label":0,"unicode":84,"echo":false,"script":null) 37 | ] 38 | } 39 | inventory_hotbar_1={ 40 | "deadzone": 0.5, 41 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":49,"key_label":0,"unicode":49,"echo":false,"script":null) 42 | ] 43 | } 44 | inventory_hotbar_2={ 45 | "deadzone": 0.5, 46 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":50,"key_label":0,"unicode":50,"echo":false,"script":null) 47 | ] 48 | } 49 | inventory_hotbar_3={ 50 | "deadzone": 0.5, 51 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":51,"key_label":0,"unicode":51,"echo":false,"script":null) 52 | ] 53 | } 54 | inventory_hotbar_4={ 55 | "deadzone": 0.5, 56 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":52,"key_label":0,"unicode":52,"echo":false,"script":null) 57 | ] 58 | } 59 | inventory_hotbar_5={ 60 | "deadzone": 0.5, 61 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":53,"key_label":0,"unicode":53,"echo":false,"script":null) 62 | ] 63 | } 64 | inventory_hotbar_6={ 65 | "deadzone": 0.5, 66 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":54,"key_label":0,"unicode":54,"echo":false,"script":null) 67 | ] 68 | } 69 | inventory_hotbar_7={ 70 | "deadzone": 0.5, 71 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":55,"key_label":0,"unicode":55,"echo":false,"script":null) 72 | ] 73 | } 74 | inventory_hotbar_8={ 75 | "deadzone": 0.5, 76 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":56,"key_label":0,"unicode":56,"echo":false,"script":null) 77 | ] 78 | } 79 | inventory_hotbar_9={ 80 | "deadzone": 0.5, 81 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":57,"key_label":0,"unicode":57,"echo":false,"script":null) 82 | ] 83 | } 84 | player_move_left={ 85 | "deadzone": 0.5, 86 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"echo":false,"script":null) 87 | , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"echo":false,"script":null) 88 | ] 89 | } 90 | player_move_right={ 91 | "deadzone": 0.5, 92 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"echo":false,"script":null) 93 | , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194321,"key_label":0,"unicode":0,"echo":false,"script":null) 94 | ] 95 | } 96 | player_move_up={ 97 | "deadzone": 0.5, 98 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"echo":false,"script":null) 99 | , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194320,"key_label":0,"unicode":0,"echo":false,"script":null) 100 | ] 101 | } 102 | player_move_down={ 103 | "deadzone": 0.5, 104 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"echo":false,"script":null) 105 | , Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194322,"key_label":0,"unicode":0,"echo":false,"script":null) 106 | ] 107 | } 108 | interact={ 109 | "deadzone": 0.5, 110 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"echo":false,"script":null) 111 | ] 112 | } 113 | 114 | [rendering] 115 | 116 | textures/canvas_textures/default_texture_filter=0 117 | -------------------------------------------------------------------------------- /sprite_frames_coin.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="SpriteFrames" load_steps=10 format=3 uid="uid://ceum1yh6odg85"] 2 | 3 | [ext_resource type="Texture2D" uid="uid://cjfu782vqpsux" path="res://Sprites/coin.png" id="1_87n0a"] 4 | 5 | [sub_resource type="AtlasTexture" id="AtlasTexture_g237b"] 6 | atlas = ExtResource("1_87n0a") 7 | region = Rect2(0, 0, 15, 16) 8 | 9 | [sub_resource type="AtlasTexture" id="AtlasTexture_pyd7u"] 10 | atlas = ExtResource("1_87n0a") 11 | region = Rect2(15, 0, 15, 16) 12 | 13 | [sub_resource type="AtlasTexture" id="AtlasTexture_xoa7u"] 14 | atlas = ExtResource("1_87n0a") 15 | region = Rect2(30, 0, 15, 16) 16 | 17 | [sub_resource type="AtlasTexture" id="AtlasTexture_j7rqc"] 18 | atlas = ExtResource("1_87n0a") 19 | region = Rect2(45, 0, 15, 16) 20 | 21 | [sub_resource type="AtlasTexture" id="AtlasTexture_8e7y7"] 22 | atlas = ExtResource("1_87n0a") 23 | region = Rect2(60, 0, 15, 16) 24 | 25 | [sub_resource type="AtlasTexture" id="AtlasTexture_ou11w"] 26 | atlas = ExtResource("1_87n0a") 27 | region = Rect2(75, 0, 15, 16) 28 | 29 | [sub_resource type="AtlasTexture" id="AtlasTexture_n5hi8"] 30 | atlas = ExtResource("1_87n0a") 31 | region = Rect2(90, 0, 15, 16) 32 | 33 | [sub_resource type="AtlasTexture" id="AtlasTexture_yjyn5"] 34 | atlas = ExtResource("1_87n0a") 35 | region = Rect2(105, 0, 15, 16) 36 | 37 | [resource] 38 | animations = [{ 39 | "frames": [{ 40 | "duration": 1.0, 41 | "texture": SubResource("AtlasTexture_g237b") 42 | }, { 43 | "duration": 1.0, 44 | "texture": SubResource("AtlasTexture_pyd7u") 45 | }, { 46 | "duration": 1.0, 47 | "texture": SubResource("AtlasTexture_xoa7u") 48 | }, { 49 | "duration": 1.0, 50 | "texture": SubResource("AtlasTexture_j7rqc") 51 | }, { 52 | "duration": 1.0, 53 | "texture": SubResource("AtlasTexture_8e7y7") 54 | }, { 55 | "duration": 1.0, 56 | "texture": SubResource("AtlasTexture_ou11w") 57 | }, { 58 | "duration": 1.0, 59 | "texture": SubResource("AtlasTexture_n5hi8") 60 | }, { 61 | "duration": 1.0, 62 | "texture": SubResource("AtlasTexture_yjyn5") 63 | }], 64 | "loop": true, 65 | "name": &"default", 66 | "speed": 5.0 67 | }] 68 | --------------------------------------------------------------------------------