Skip to content

Adapter extensibility

Vince Kálmán edited this page Jun 23, 2019 · 2 revisions

MechTransfer implements a simple way of adding support for different item containers using TModLoader's call() functionality.

The adapter

First, you should define a class to contain the necessary methods for the adapter. This class should have the following layout:

class MyAdapter
{
    public bool InjectItem(int x, int y, Item item)
    {
        return false;
    }

    public IEnumerable<Tuple<Item, object>> EnumerateItems(int x, int y)
    {
        yield break;
    }

    public void TakeItem(int x, int y, object slot, int amount)
    {

    }
}

The adapter should also sync the state of the adapter in multiplayer. These adapter functions will only be called on the server.

bool InjectItem(int x, int y, Item item)

This method will be called when a device tries to inject a stack item into the container.

  • int x - This is the x coordinate of the targeted tile.
  • int y - This is the y coordinate of the targeted tile.
  • Item item - This is the item, that the device is trying to inject.

item.Stack should be decremented by the number of items the container was able to receive. The method should return true if it was able to receive at least one item. This will trigger the visual effects. (The method may return true even if item.Stack was not decremented. The Omni turret adapter does this when it receives infinite ammo items, such as the Endless Quiver.) item may not be stored in the container directly, it should be cloned via item.Clone(). The method may simply return false and remain otherwise unimplemented if the container does not support injection.

IEnumerable<Tuple<Item, object>> EnumerateItems(int x, int y)

This method is used to query the items stored by the container.

  • int x - This is the x coordinate of the targeted tile.
  • int y - This is the y coordinate of the targeted tile.

In most cases EnumerateItems will be an iterator method. It should yield a Tuple for each slot that the container has. Item1 of the Tuple should be an Item object representing the stored stack of items in the slot. The Stack value of this item should not be larger than maxStack. It is safe to use the item object stored by the container directly, it will not be modified. Item2 of the tuple should be a key, that identifies the slot. MechTransfer does not use this value directly, it will be sent to TakeItem to specify the slot. If the container does not use a slot based system, Item2 may be null. The method may simply yield break and remain otherwise unimplemented if the container does not support item extraction.

void TakeItem(int x, int y, object slot, int amount)

This method may be called immediately after EnumerateItems if a device has successfully transferred an item.

  • int x - This is the x coordinate of the targeted tile.
  • int y - This is the y coordinate of the targeted tile.
  • object slot - This is the slot identifier, as specified in EnumerateItems.
  • int amount - The number of items that were transferred.

Amount will always be less then or equal to the Stack value of the item returned by EnumerateItems for the slot. This method may remain unimplemented if the container does not implement EnumerateItems.

Examples

For a general use case see ChestAdapter or ItemFrameAdapter. For an example of a partially implemented adapter see CrystalStandAdapter.

Registering adapters

Adapters have to register to MechTransfer via Call(). This should always happen in PostSetupContent(). There are two different way to register adapters.

Registering via reflection

public override void PostSetupContent()
{
    Mod mechTansfer = ModLoader.GetMod("MechTransfer");

    if (mechTansfer != null)
    {
        MyAdapter adapter = new MyAdapter();
        mechTansfer.Call("RegisterAdapterReflection", adapter, new int[] { TileType<MyContainerTile>() });
    }
}

The third argument to mechTansfer.Call is an array containing the tile types this adapter should be used for. In this case, the names of the adapter methods should exactly match the ones outlined above. Although these methods will be loaded through reflection, there shouldn't be any noticeable performance drop.

Registering via delegates

public override void PostSetupContent()
{
    Mod mechTansfer = ModLoader.GetMod("MechTransfer");

    if (mechTansfer != null)
    {
        MyAdapter adapter = new MyAdapter();

        Func<int, int, Item, bool> injectMethod = new Func<int, int, Item, bool>(adapter.InjectItem);
        Func<int, int, IEnumerable<Tuple<Item, object>>> enumerateMethod = new Func<int, int, IEnumerable<Tuple<Item, object>>>(adapter.EnumerateItems);
        Action<int, int, object, int> takeMethod = new Action<int, int, object, int>(adapter.TakeItem);

        mechTansfer.Call("RegisterAdapter", injectMethod, enumerateMethod, takeMethod, new int[] { TileType<MyContainerTile>() });
    }
}