agentleFS
jame581jame581/GodotPrompter/skills/inventory-system/SKILL.md

inventory-system

Skill870 starsChanged 21 days agoMITView on GitHub

Use when building inventory systems — Resource-based items, slot management, stacking, and UI binding

1 other project uses a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

3 rules. About 0 are common, 1 is distinctive, and 2 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 2

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    All examples target Godot 4.3+ with no deprecated APIs.

    line 8
  2. Distinctive:

    If your game needs something else — a weight-based bag with no slots, a grid inventory with shaped items, an infinite stack list — say so …

    line 64
  3. Very specific:

    Add equipment slots (HEAD, CHEST, WEAPON, etc.) with an Equipment node beside the Inventory, holding a slot map keyed by a SlotType enum.

    line 93

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Inventory Systems in Godot 4.3+

All examples target Godot 4.3+ with no deprecated APIs. This skill carries the decisions; the implementations live in references/ (GDScript first, then C#) — load the one you need.

Related skills: resource-pattern for custom Resource data containers, save-load for inventory serialization, event-bus for inventory change notifications, hud-system for inventory UI display, popochiu for adventure-game inventory.


1. Architecture Overview

┌─────────────────────────────────────────────────────────┐
│                        UI Layer                         │
│   InventoryUI (Control)                                 │
│     └─ GridContainer                                    │
│           └─ SlotUI × N (Button)                        │
│                 └─ TextureRect (icon) + Label (qty)     │
│                                                         │
│   Connects to: inventory_changed signal                 │
│   Drag-and-drop via _get_drag_data / _drop_data         │
└───────────────────────┬─────────────────────────────────┘
                        │ reads / mutates
┌───────────────────────▼─────────────────────────────────┐
│                    Inventory (Node)                      │
│   slots: Array[InventorySlot]                           │
│   add_item(item, qty) → leftover: int                   │
│   remove_item(item, qty)                                │
│   has_item(item, qty) → bool                            │
│   get_item_count(item) → int                            │
│                                                         │
│   signals: inventory_changed                            │
│             item_added(item, quantity)                  │
│             item_removed(item, quantity)                │
└───────────────────────┬─────────────────────────────────┘
                        │ references
┌───────────────────────▼─────────────────────────────────┐
│                   Data Layer (Resources)                 │
│   ItemData (Resource)                                   │
│     id, name, description, icon, max_stack_size,        │
│     item_type enum                                      │
│                                                         │
│   InventorySlot (inner class / Resource)                │
│     item: ItemData, quantity: int                       │
└─────────────────────────────────────────────────────────┘

2. The Decisions That Matter

Three choices shape every inventory. Make them explicitly and state which you picked — the rest is mechanical.

DecisionTakeWhy
Item identity: Resource vs. Dictionary / JSONResource in .tres@export slots accept typed items, the Inspector edits them, and shared instances compare by reference
Slot container: Resource vs. RefCountedRefCountedSlots are runtime state, not authored assets. A Resource invites saving slots to disk and reloading stale item copies
add_item return: bool vs. leftover countleftover intPartial adds are the common case (stack caps, full bag). A bool forces the caller to re-query what actually fit

If your game needs something else — a weight-based bag with no slots, a grid inventory with shaped items, an infinite stack list — say so before building. The fixed slot array this skill describes is the wrong skeleton for all three.

Non-obvious specifics

  • Fill existing stacks before empty slots — two passes, in that order. A single naive pass scatters one item type across several slots and reports the bag full while space remains.
  • Emit inventory_changed once per operation, after both passes complete — not once per slot touched. Per-slot emission re-renders the whole UI N times for one add_item.
  • Items compare by reference. Godot caches loaded resources, so load() on the same .tres returns the same instance and slot.item == item is correct and cheap. If you duplicate() items at runtime, that breaks — key off ItemData.id instead.
  • Clear item to null when quantity reaches 0, or is_empty() lies and the slot is never reusable.
  • Prevent stacking with data, not branches — set max_stack_size = 1 on EQUIPMENT and KEY_ITEM items rather than special-casing them in add_item.
  • C#: mark ItemData [GlobalClass] or the Inspector will not offer it as a type when creating .tres files.

Inventory API contract

MemberReturnsContract
add_item(item, quantity)intLeftover that did not fit; 0 means everything was added
remove_item(item, quantity)—Removes up to quantity; silent when fewer are held
has_item(item, quantity)boolConvenience over get_item_count
get_item_count(item)intSummed across every slot
inventory_changedsignalFired once per mutating operation — drives all UI
item_added(item, quantity)signalCarries the amount actually added
item_removed(item, quantity)signalCarries the amount actually removed

Full ItemData, Inventory, and InventorySlot implementations in GDScript and C#: references/core-classes.md


3. Equipment Extension

Add equipment slots (HEAD, CHEST, WEAPON, etc.) with an Equipment node beside the Inventory, holding a slot map keyed by a SlotType enum. Stat aggregation sums a stats Dictionary (a property you add to ItemData) across equipped items; signal equipment_changed when slots change.

See references/equipment.md for the full GDScript and C# Equipment class with SlotType enum, equip / unequip API, and stat aggregation.


4. UI Binding

Slot-grid UI: a GridContainer of Button slot widgets, each rendering one InventorySlot. Drag-and-drop uses _get_drag_data / _drop_data / _can_drop_data on the slot widget. The Inventory emits inventory_changed; the UI re-renders affected slots.

See references/ui-binding.md for the full GDScript and C# slot widget (drag/drop, hover preview), inventory grid layout, and tooltip wiring.


5. Serialization

Persist each slot as an {id, qty} pair (null for an empty slot), never the ItemData itself or its path. An ItemRegistry autoload loads res://items/ at startup and resolves ids back to items on load.

See references/serialization.md for the GDScript and C# ItemRegistry with serialize_inventory / deserialize_inventory; put the result in your save file (see save-load for its version field).



Implementation Checklist

  • ItemData extends Resource with a stable id string set in the Inspector
  • ItemData files live under res://items/ and are committed to version control
  • Inventory.add_item() returns leftover count; callers handle a full inventory
  • inventory_changed signal drives all UI updates — UI never polls per-frame
  • InventorySlot.remove_from_stack() clears item to null when quantity reaches 0
  • Equipment slots keyed by SlotType enum, not by string, to catch typos at compile time
  • Equipment.get_total_stat() is called when stats are needed, not cached unless profiling demands it
  • Serialization stores id + quantity only — never full ItemData objects or resource paths
  • ItemRegistry loads items at startup; all deserialization goes through it
  • Drag-and-drop swaps slot contents directly then emits inventory_changed once
  • max_stack_size = 1 on EQUIPMENT and KEY_ITEM types to prevent stacking
  • All push_error() messages include the class name and method for easy tracing

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.