Stack actions
A stack is a pile of one kind of item in the player’s inventory. This API lets your mod give the player a stack that is more than a plain item: a stack with a set quality, a stack that belongs to someone else (so it counts as stolen), or a container filled with a liquid. It can also drop such a stack on the floor, pour a liquid into the containers the player is carrying, and list the stacks the game counts as stolen. PlayerInventory.Give (see Inventory) only adds plain items, so use this page when you need more than that.
When to use it
- Give the player a reward that has a better quality than normal.
- Hand over an item that counts as stolen, for a story or a crime mod.
- Give a container that is already full of water or another liquid.
- Drop a stack at the player’s feet when their inventory is full.
- Find out which stacks the player is carrying that count as stolen.
Quick example
This mod gives the player two turnips of quality 1.5 once a save is loaded. If the inventory is full, the turnips that do not fit are dropped at the player’s feet. It logs whether it worked.
using ObenseuerCore;
[ModInfo("example.turnipgift", "Turnip Gift", "0.1.0")]
[ModDependency("obenseuer.moddingframework", "0.11.0")]
public sealed class TurnipGift : FrameworkMod
{
protected override void OnLoad()
{
Events.WhenReady(() =>
{
StackSpec spec = new StackSpec { Quality = 1.5f };
bool given = StackActions.GiveOrDrop(Info.Id, 12, 2, spec);
Log.Info(given ? "Gave two good turnips." : "Could not give the turnips.");
});
}
}
12 is the item number of the turnip. Info.Id is your mod’s id, which the calls use for logging. Events.WhenReady waits until a save is loaded before it runs your code. See Using events.
Common tasks
Give a stack that counts as stolen. Any OwnerId above 1 means the stack belongs to that character, so the game treats it as stolen. Give adds nothing at all if the whole stack does not fit.
StackSpec stolen = new StackSpec { OwnerId = 5 };
bool fitted = StackActions.Give(Info.Id, 12, 3, stolen);
Log.Info(fitted ? "The stolen turnips fit." : "No room, so nothing was added.");
Give as many as fit and drop the rest on the floor.
StackSpec fine = new StackSpec { Quality = 1.2f };
StackActions.GiveOrDrop(Info.Id, 12, 20, fine);
Drop a stack at the player’s feet without trying the inventory first.
StackActions.Drop(Info.Id, 12, 1, new StackSpec());
Give an empty bucket that is already filled with 2 units of clean water.
StackSpec water = new StackSpec { LiquidItemId = 10840, LiquidAmount = 2f };
StackActions.Give(Info.Id, 90010, 1, water);
Pour clean water into the containers the player already carries, and see how much went in.
float poured = StackActions.AddLiquid(Info.Id, 10840, 5f);
Log.Info("Poured " + poured + " of water.");
List the stolen stacks the player is carrying.
List<ItemStackInfo> stolenStacks = StackActions.StolenStacks();
Log.Info("You are carrying " + stolenStacks.Count + " stolen stacks.");
Reference
This is the full list of what the API gives you. StackSpec describes the extra things a stack should have. Leave a field alone to get the normal value. StackActions takes a StackSpec with every give and drop call.
StackSpec the extras for one stack
Pass one of these to the give and drop calls to set a stack’s Quality, OwnerId or liquid. Leave a field alone to keep the normal value.
public sealed class StackSpec
{
public float? Quality { get; set; }
public int OwnerId { get; set; }
public int LiquidItemId { get; set; }
public float LiquidAmount { get; set; }
}
| Property | Type | What it means |
|---|---|---|
Quality | float? | The quality to give the stack, above 0 and at most 2. null keeps the item’s normal quality. |
OwnerId | int | Who owns the stack. -1 (the default) means nobody. Above 1 means it counts as stolen. |
LiquidItemId | int | The item number of a liquid to fill a container with. 0 means no liquid. |
LiquidAmount | float | How much of that liquid to put in. It must be above 0 when LiquidItemId is set. |
StackActions give, drop and pour stacks
This is the class to start from. Give, GiveOrDrop and Drop add or drop a stack with a StackSpec, AddLiquid pours liquid into the containers the player carries, and StolenStacks() lists the stolen stacks they carry.
public static class StackActions
{
public static bool Give(string modId, int itemId, int count, StackSpec spec);
public static bool GiveOrDrop(string modId, int itemId, int count, StackSpec spec);
public static bool Drop(string modId, int itemId, int count, StackSpec spec);
public static float AddLiquid(string modId, int liquidItemId, float amount);
public static List<ItemStackInfo> StolenStacks();
}
These calls change what the player carries. Each takes your mod’s id first.
| Method | Returns | What it does |
|---|---|---|
Give(modId, itemId, count, spec) | bool | Adds the whole stack, or nothing. true when added. |
GiveOrDrop(modId, itemId, count, spec) | bool | Adds what fits and drops the rest on the floor. true when done. |
Drop(modId, itemId, count, spec) | bool | Drops the stack at the player’s feet. true when dropped. |
AddLiquid(modId, liquidItemId, amount) | float | Pours liquid into the containers the player carries. Returns how much went in. |
StolenStacks() | List<ItemStackInfo> | The stacks the player carries that count as stolen. Each entry is an ItemStackInfo (see Inventory). |
Things to know
- A quality only works on an item that has a quality. If the item has none, or the number is not above 0 and at most 2, the call fails and gives nothing. (The item’s template must carry quality data (
ItemQualityData). ANaNquality is refused too.) - A liquid only works on a container, and only with a real liquid.
LiquidItemIdmust be an item in the game’s liquid category, the item you give must be able to hold a liquid, andLiquidAmountmust be above 0. Otherwise the call fails and gives nothing. (The liquid item’s categories must containLiquid, and the container’s data must include liquid data (ItemLiquidData).) Giveadds the stack only when all of it fits. If the inventory has no room for the fullcount,Givereturnsfalseand adds nothing.GiveOrDropadds what fits and drops the rest on the floor at the player’s feet. Use it when you do not want the reward to get lost.Dropputs the stack on the floor at the player’s feet, the way the game drops an item. It does not try the inventory first.AddLiquidpours into containers the player already carries. It returns how much liquid went in, which can be less than you asked for if the containers are nearly full. It never creates a container, so with no container it returns0. It also returns0whenamountis not above 0. (It calls the game’sItemOperations.AddLiquidand returns the amount minus what the game says is left over.)- A stack is stolen when its owner number is above 1.
StolenStacks()lists the stolen stacks the player carries. Each entry is anItemStackInfosnapshot. - A stack with an owner above 1 is stolen, and giving it can count as theft. There is no separate “stolen” setting. When someone sees the player receive a stack owned by another character, the game can record a theft, the same as when the player picks it up. (
Givewith such anOwnerIdcan commit theft when a witness sees it, because the game’s own add method runs.) - The same events still fire.
PlayerInventory.ItemAddedand the item drop on the floor happen just as they do when the game does it itself. (The game’s own slot and drop methods run, so their events fire.) - Your mod id is checked and used in the log, and it holds no claim. Two mods can call these methods at the same time without getting in each other’s way.
- Nothing is changed while a save is loading or being written. The calls return
false(or0forAddLiquid) and do nothing. (They checkSaveData.Busy.)
Limits
- You can’t give money with these calls. Item numbers 0 and 1, the two currencies, are always refused. Use the Economy page for money.
- A mod id that is empty, longer than 64 characters or has characters other than letters, digits,
.,_or-throws anArgumentException. - You can’t equip or unequip an item. The game’s equip methods act on whatever is already in the player’s right hand and cannot be told which item to use. (
PlayerHandItems.PrimaryEquipandPrimaryUnequiptake no item id. The hand is filled byDropRandDropL, whose names do not match what they do.) - You can’t take liquid out of a container. The game’s own method for it is not finished: it only logs an error. (
ItemOperations.RemoveLiquidis not implemented. It logs an error and returnsfalse.) - You can’t sell a stack. The game’s sell method sells a single unit whatever the count, and the version that takes a whole stack needs a game stack object that a mod cannot make. (
Inventory.SellItems(int, ...)sells one unit, and the stack overload needs a gameItemStack.)
Details
Target: Obenseuer 0.5.08 (buildid 25969064), Unity 6000.0.77f1 Mono, BepInEx 6 Unity.Mono (be.788, the tested build) or BepInEx 5 (5.4.23 or later). Namespace: ObenseuerCore. The log name is obenseuer.moddingframework.stacks, and the file is BepInEx\logs\obenseuer.moddingframework.stacks.log. Source files: src/Api/StackActions.cs.
Everything not listed in this reference is internal and may change without notice.
When you can call it
- Call everything from your mod’s normal code, not from a background thread you started. The game only works safely on one thread, the main thread (see the glossary).
- It is safe to call at any time, even on the main menu. With no player or inventory yet, the calls return
false,0or an empty list. - Problems come back as a plain answer, not an error. The one exception is a bad mod id, which throws. If the game fails inside a call, you get
false,0or an empty list, and the first failure of each call goes to the log.
If something fails to set up
The framework does not change any game method for this page, so it has no feature group that can turn off. It only calls game methods when you call it. If a game update changes one of those methods, the call fails with a plain answer and one error goes to the log.