Inventory RPG-style
Posted: Thu Jun 01, 2023 8:37 am
UPDATE:
v1.6. Added import from typing to work with 8.2+.
(Ren'Py 8.2 could give errors with v1.5, because of some Python type hints. Use v1.6 please).
Now it's more like a usable framework than just a sketch.
Discussion
Here is yet another inventory, RPG-style:
v1.0 is a simple (291 lines not counting comments) how-to drag-n-drop or click to rearrange and move draggables between drop areas.
The last version is a more complex program, with crafting etc.
No copyright, public domain.
The demo is made and should be run with Ren'Py SDK at least 8.1. Earlier Ren'Py 8 versions would give errors (complaining about unknown variable config.check_conflicting_properties). If you want to use this with Ren'Py 7, you would need to rework the code a bit, as it uses some Python 3 magic.
Version 1.6: First version:
v1.6. Added import from typing to work with 8.2+.
(Ren'Py 8.2 could give errors with v1.5, because of some Python type hints. Use v1.6 please).
Now it's more like a usable framework than just a sketch.
Discussion
Here is yet another inventory, RPG-style:
- Equip/unequip with dragging or clicking.
- Backpack grid and custom equipment slots.
- When an item is hovered or dragged, highlight suitable equipment slots.
- "Filtering": highlight backpack slots with different colors according to their item type.
- Calculating stats bonuses depending on the equipped items' properties.
- Tooltips.
- v1.1: Stackables & consumables. Stats bars (mana, energy, health).
- v1.3: Crafting. The code was reworked for that.
- v1.4-1.5: More documentation with some improvements.
v1.0 is a simple (291 lines not counting comments) how-to drag-n-drop or click to rearrange and move draggables between drop areas.
The last version is a more complex program, with crafting etc.
Code: Select all
The program has "items" and "slots".
TL;DR: Use functions like
addItem("egg", 5) Add 5 eggs to the inventory.
addItems("sword", "shield") Add sword & shield to backpack.
equip("sword", "hand_r") Equip sword in right hand.
Sack.push("sword") Move sword from hand to backpack.
consume("egg") Eat 1 egg and feel the effects.
minusOne("egg") Remove 1 egg from inventory.
Works for non-stackables as well.
mixPotion() Create 1 potion. Works only when there
are at least 2 suitable ingredients
in any of the slots "p1"-"p4".
Items
=====
Every item in the universe has an entry in the dictionary `items`.
When a new kind of item is crafted, it should be added to `items` dict.
When an item is placed in the inventory, its reference in `items`
receives `place` (the name of the slot where that item goes).
Example:
items["shield"]["place"] = "hand_l"
means item "shield" goes to the left hand (= slot named "hand_l").
Likewise, when a stackable item appears in the inventory,
its reference in `items` receives "amount" value.
Example:
items["egg"]["amount"] = 3
Each item (single or stack) in the inventory has a corresponding
`drag` element in the `inventory` screen.
Slots
=====
There are several inventory screens:
* `manslots` shows equipment slots on the character figure.
* `potions` is a crafting screen with slots for alchemy ingredients.
* `smithy` is another crafting screen (under development).
Unique slots of every screen have entries in corresponding dictionaries:
"manslots", "potions", "smithy".
These entries contain positions (x, y) in pixels of the slot on screen;
slot size (px) and field "item". If there's an item in that slot,
the name of an item is recorded there. E.g.:
potions["p1"]["item"] = "egg"
Every name of an item is a unique identifier, there's only 1 item
(single or stack) with that name.
If another item of that kind would be necessary, it would get another
name and entry in `items` (like "egg000123").
Slot names should be unique too. Even in different screens there must
be no slots with the same name. Slot names should not start with "bk_",
because such names are recognized as backpack cells.
backpack
--------
`backpack` is a common element of all inventory screens.
Its slots (cells) have always the same positions, calculated as a grid;
so there is no need to store their (x, y). Therefore data collection
`backpack` is just a list with 8*8=64 elements.
It contains names of items in those cells. E.g.:
backpack[0] = "sword"
means that the left-top cell of the backpack contains sword.
Backpack cells as drag areas have drag_name's like "bk_0", "bk_1" etc.
Summary
=======
Draggables:
`items` {} all the items in the game
Drop areas:
`backpack` [] names of items in the backpack
`manslots` {} character equipment slots (params + names of items there)
`potions` {} crafting potions slots (params + names of items there)
`smithy` {} crafting smithy slots (params + names of items there)
Screen `inventory`
==================
It is switchable between variants: "manslots", "potions", "smithy".
Dict `drops` references current drop areas for the screen variant. So:
default drops = manslots
means that the initial screen variant is character slots.
Then switching to screen "potions" looks like:
SetVariable("screenType", "potions"),
SetVariable("drops", potions)
In action
=========
Moving between backpack and other screen slots can be done by clicking
or drag-n-dropping.
Dragging or hovering an item makes its suitable slots highlighted.
`manslots` has slot "eat", to be used with consumables.
`backpack` cells can be highlighted with colored background, according
to the type of item there.
`potions` slots are used to put there ingredients and create a potion.
Moving an item from one slot to another involves updating data
both for those slots and for the item. For example, to move
item "egg" in "potions" drop areas from slot "p1" to "p2":
potions["p1"]["item"] = None # or use `unequip("egg")`
potions["p2"]["item"] = "egg"
items["egg"]["place"] = "p2"
Moving the egg to a backpack cell #5 for example would be similar,
backpack[5] = "egg"
items["egg"]["place"] = "bk_5"
Also to clean the data for the previous slot of the egg, do first
oldSlot = items["egg"]["place"]
drops[oldSlot] = None
provided that `drops` dict is the reference to currently active
screen slots. For example,
drops = potions
In practice, to put items in the backpack, just use `Sack.push()`:
Sack.push("egg", cell="bk_5")
or to put the item in whatever empty backpack cell:
Sack.push("egg")
In these examples, if there is a stack of several eggs, then
the item "egg" is treated as the whole stack.
E.g. if player drops eggs onto slot "eat", it consumes 1 egg,
and internally it's:
items["egg"]["amount"] -= 1
The remaining stack falls back in its backpack cell, or
disappears if there was only 1 egg in the stack.
Removing the item from inventory is done by cleaning its slot
and setting
items[name]["place"] = None
where name is "egg" for example. And if the item was a stackable,
items[name]["amount"] = 0
(removing "amount" key from items[name] works too).
In practice it's more convenient to use functions
equip(itemName, slotName) to equip in a slot
minusOne(itemName) to remove an item from inventory,
or remove 1 piece if it's a stack
addItem(name [, amount]) to add item (or stack) to inventory
addItems(*items) to add different items in one go
Sack.push(name [, amount, cell]) to move item to the backpack
consume(item) remove 1 item and run its effects
mixPotion() create 1 potion, spending 1 ingredient
from each slot "p1", "p2", "p3", "p4"
The demo is made and should be run with Ren'Py SDK at least 8.1. Earlier Ren'Py 8 versions would give errors (complaining about unknown variable config.check_conflicting_properties). If you want to use this with Ren'Py 7, you would need to rework the code a bit, as it uses some Python 3 magic.
Version 1.6: First version: