├── .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 |
--------------------------------------------------------------------------------