Mattcy1_Coding-ItemAPI icon

ItemAPI

Allows for easy Custom Items Creating With Working visuals, crafting, wearable and hotbar items, spawning of item inside of cabinets.

Last updated an hour ago
Total downloads 0
Total rating 1 
Categories Mods Libraries
Dependency string Mattcy1_Coding-ItemAPI-1.0.0
Dependants 1 other package depends on this package

This mod requires the following mods to function

BepInEx-BepInExPack-5.4.2100 icon
BepInEx-BepInExPack

BepInEx pack for Mono Unity games. Preconfigured and ready to use.

Preferred version: 5.4.2100

README

ItemAPI

An easy to use ItemAPI for Burglin Gnomes. Created by Mattcy1.

Features

  • Allows for easy custom wearable/hands items!
  • Support for custom crafting recipes, and cabinet spawnings!

Documentation

Getting started

Add a reference to ItemAPI.dll in your project, then make ItemAPI a dependency in your plugin's BepInPlugin attribute (or just make sure your plugin loads after it). Inside your plugin's Awake method you always need to call ItemAPI.ItemAPI.Init(Logger), this is what registers your mod's assembly so ItemAPI can find and register your equipment/recipe classes.

Making a custom item

Every item is a class that inherits from ItemAPI.Equipment. This is where you set the name, description, icon, and behavior of your item.

public class ExampleItem : ItemAPI.Equipment
{
    public override string Name => "Example Item";
    public override string Description => "Keeps the thoughts out.";
    public override ItemData.EquipmentType ItemType => ItemData.EquipmentType.Hat;

    public override Sprite Icon => ItemAPI.LoadSpriteFromEmbedded("MyMod.Assets.exampleitem.png");
    public override GameObject Prefab => ItemAPI.LoadGameObject("mymod.assetbundle", "ExampleItem");

    public override int? maxDurability => 50;
    public override int? maxStackSize => 1;

    public override void OnEquip(GameObject visualObject, PlayerNetworking player)
    {
        // do stuff when the player puts it on
    }

    public override void OnUnequip(PlayerNetworking player)
    {
        // do stuff when it comes off
    }
}

You don't need to manually register the class anywhere, ItemAPI finds every class in your assembly that inherits from Equipment and registers it automatically once the game loads its localization tables. Just make sure the class isn't abstract and has a public parameterless constructor.

Available overrides on Equipment:

Member What it's for
Name Item name, required
Description Item description, required
ItemType Hat, Hand item, etc, required
Icon Sprite shown in inventory
Prefab The GameObject used both when equipped and when dropped in the world
maxDurability Leave null for no durability tracking
maxStackSize Leave null to default to 1
DroppedSize / DroppedOffset Scale/position of the item when it's lying on the ground, if you don't set these it'll try to guess something sensible
RoomsToSpawn / SpawnChance Used for cabinet spawning, see below

And the events you can hook into: OnEquip, OnUnequip, OnLeftClick, OnLost, OnBreak, OnDurabilityChange, OnPickup.

OnLost fires when the item gets dropped from an inventory (not necessarily from being equipped). OnLeftClick fires when the player left clicks while the item is equipped in any slot, with isHolding telling you if it was a quick click or a hold. OnEquip is where you handle visual via VisualObject which is just a game object loaded from the Prefab Can be parented to the player and all

Durability

If you set maxDurability, you can damage the item with:

ItemAPI.ConsumeItemDurability(myItem.ItemIndex, amountToRemove);

This works fine over the network, it doesn't matter if you're host or client, it'll still reach the server correctly. When durability reaches zero the item breaks, and OnBreak runs instead of OnDurabilityChange.

Spawning items

To spawn an item (or an enemy) into the world:

ItemAPI.RequestSpawn("Example Item", somePosition, "MyUniqueSpawnId");

The unique id is optional (pass null), it just gets passed back through the onItemSpawnedViaRequest / onEnnemySpawnedViaRequest events so you can tell your own spawns apart from everything else.

To give an item straight to a player's inventory instead of dropping it in the world:

ItemAPI.RequestInventoryItem("Example Item", 1);

Both of these work correctly whether the local player is host or client.

Cabinet spawns

If you set RoomsToSpawn and SpawnChance on your Equipment, it will have a chance to spawn inside cabinet drawers automatically whenever a cabinet is opened for the first time, no extra code needed.

public override RoomsToSpawn[] RoomsToSpawn => new[] { ItemAPI.RoomsToSpawn.Kitchen, ItemAPI.RoomsToSpawn.Bedroom };
public override float? SpawnChance => 15f; // percent chance, checked per matching cabinet

Use RoomsToSpawn.ALL if you don't care which room it spawns in.

Crafting recipes

Recipes are added by making a class that inherits from ItemAPI.CraftingRecipe:

public class ExampleRecipe : ItemAPI.CraftingRecipe
{
    public override Equipment Equipment => new ExampleItem();
    public override bool TakeItemFromInventory => true;

    public override CraftingCost[] CostTable => new CraftingCost[]
    {
        (AllItems.ResourceTypes.Metal, 3),
        (ItemAPI.GetItemData("Gnomium"), 1)
    };
}

Then register it once, usually in your plugin's Awake:

ItemAPI.AddRecipe(new ExampleRecipe());

CostTable entries can be either a resource type + amount, or an ItemData + amount, the implicit tuple conversion handles both. Recipes get slotted into the Gnomium deposit crafting menu automatically once the world loads (Worth to mention that Gnomium is and Item not a Resource!).

Reading item/player state

A handful of helpers if you need to check things at runtime:

ItemAPI.isItemEquipped("Example Item");          // is the local player wearing/holding it
ItemAPI.GetEquippedItems();                      // everything the local player has equipped
ItemAPI.HasItemInInventory(itemIndex);
ItemAPI.GetItemInInventory(itemIndex);
ItemAPI.GetLocalPlayer();
ItemAPI.GetAllPlayers();
ItemAPI.GetItemIndex("Example Item");
ItemAPI.GetItemData("Example Item");

There are also multiplayer versions for most of the "local player" checks that take a clientId, e.g. ItemAPI.isItemEquipped(clientId, name).

Loading assets

Embed your asset bundle and sprites as embedded resources in your project, then load them with:

ItemAPI.LoadGameObject("mymod.assetbundle", "PrefabName");
ItemAPI.LoadSpriteFromEmbedded("MyMod.Assets.icon.png");

Both are cached after the first load, so calling them repeatedly is cheap.

Full example

Here's a complete plugin put together from everything above. It's a simple one time use item, left clicking it throws a knife then breaks the item.

using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using static ItemAPI.ItemAPI;

namespace ExampleMod
{
    [BepInPlugin(MyPluginInfo.PLUGIN_GUID, MyPluginInfo.PLUGIN_NAME, MyPluginInfo.PLUGIN_VERSION)]
    [BepInDependency("com.mattcy.itemapi", BepInDependency.DependencyFlags.HardDependency)]
    public class Plugin : BaseUnityPlugin
    {
        internal static new ManualLogSource Logger;

        private void Awake()
        {
            Logger = base.Logger;
            Logger.LogInfo($"Plugin {MyPluginInfo.PLUGIN_GUID} is loaded!");

            if (!BepInEx.Bootstrap.Chainloader.PluginInfos.ContainsKey("com.mattcy.itemapi"))
            {
                Logger.LogError("Failed To Load ExampleMod, ItemAPI is not present!");
                return;
            }

            // Always call this in Awake, it's how ItemAPI knows your mod exists and registers your items/recipes
            ItemAPI.ItemAPI.Init(Logger);

            Harmony harmony = new Harmony(MyPluginInfo.PLUGIN_GUID);
            harmony.PatchAll();
        }

        public class ExampleItem : Equipment
        {
            public override string Name => "Example Item";
            public override string Description => "Throws a knife then breaks. One time use.";
            public override ItemData.EquipmentType ItemType => ItemData.EquipmentType.Weared;
            public override int? maxDurability => 1;
            public override Sprite Icon => ItemAPI.ItemAPI.LoadSpriteFromEmbedded("ExampleMod.ExampleItem.png");
            public override GameObject Prefab => ItemAPI.ItemAPI.LoadGameObject("ExampleMod.exampleitem.bundle", "ExampleItem.prefab");

            public override void OnLeftClick(bool isHolding, PlayerNetworking player)
            {
                ConsumeItemDurability(ItemIndex, 1);
            }

            public override void OnBreak(PlayerNetworking player)
            {
                NetworkSpawner.RequestSpawn("Knife", player.Position, "");
            }
        }

        public class ExampleRecipe : CraftingRecipe
        {
            public override Equipment Equipment => new ExampleItem();
            public override CraftingCost[] CostTable => new CraftingCost[]
            {
                (GetItemData("Gnomium"), 1),
                (AllItems.ResourceTypes.Metal, 2)
            };
        }
    }
}

A few things worth noting from this example:

  • ItemAPI.Init(Logger) isn't optional, you always need to call it in Awake, that's what actually registers your equipment/recipe classes with the API
  • OnLeftClick calls ConsumeItemDurability(ItemIndex, 1), since maxDurability is 1 this brings it straight to 0 and triggers OnBreak
  • OnBreak is where the knife actually gets spawned, since it only ever breaks once this naturally makes the item one time use
  • The uniqueId passed to RequestSpawn is left empty here since we don't need to catch the spawn again through onItemSpawnedViaRequest, it's only needed if you want to do something extra with the spawned object afterwards

Known issues

This is still fairly early and hasn't been fully tested:

  • Dropped Items Visuals may not appear on join.
  • Cabinet spawn chances are rolled independently per registered item, so with a lot of mods installed a single cabinet could end up spammed

If you run into something broken, please report the issue to "mattcy" on discord.