DisplayMultiSlotItems

Tracking Issue: #885

Tags: pawns

This feature allows mods to make items equipped in multi-item inventory slots visible on soldiers' bodies. The vanilla behavior is that only the first item in the Utility Slot is visible on the soldier's body, and only in Tactical, but not in the Armory.

With this feature it's possible to conditionally show all items in all multi-slots, including Highlander-templated slots. Highlander-templated slots, in addition to this feature, also need to have NeedsPresEquip = true to display in Tactical, and ShowOnCinematicPawns = true to display in the Armory and Squad Select.

How to use

Implement the following code in your X2DownloadableContentInfo class:

static event OnPostTemplatesCreated()
{
    local CHHelpers CHHelpersObj;

    CHHelpersObj = class'CHHelpers'.static.GetCDO();
    CHHelpersObj.AddShouldDisplayMultiSlotItemInStrategyCallback(ShouldDisplayMultiSlotItemInStrategy);
    CHHelpersObj.AddShouldDisplayMultiSlotItemInTacticalCallback(ShouldDisplayMultiSlotItemInTactical);
}

// To avoid crashes associated with garbage collection failure when transitioning between Tactical and Strategy,
// these functions must be bound to the ClassDefaultObject of your class. Having these functions in a class that
// `extends X2DownloadableContentInfo` is the easiest way to ensure that.

// Determines whether the specified item should be visible on specified unit in the Armory, Squad Select and Post Mission Sequence.
static private function EHLDelegateReturn ShouldDisplayMultiSlotItemInStrategy(XComGameState_Unit UnitState, XComGameState_Item ItemState, out int bDisplayItem, XComUnitPawn UnitPawn, optional XComGameState CheckGameState)
{
    // Optionally modify bDisplayItem here. If `bDisplayItem` is `1`, the item will be visible.
    // If `bDisplayItem` is `0` it will not be visible.

    // Return EHLDR_NoInterrupt or EHLDR_InterruptDelegates depending on
    // if you want to allow other delegates to run after yours
    // and potentially modify bHasHeightAdvantage further.
    return EHLDR_NoInterrupt;
}

// Determines whether the specified item should be visible on specified unit in Tactical.
static private function bool ShouldDisplayMultiSlotItemInTactical(XComGameState_Unit UnitState, XComGameState_Item ItemState, out int bDisplayItem, XGUnit UnitVisualizer, optional XComGameState CheckGameState)
{
    // Optionally modify bDisplayItem here.
    return EHLDR_NoInterrupt;
}

You can get the template name of the item in question from the provided ItemState by doing ItemState.GetMyTemplateName(), and access its Inventory Slot as ItemState.InventorySlot.

Both delegates may or may not provide you with an XComGameState that can be used to access accompanying state objects, for example:

if (CheckGameState != none)
{
    MyState = CheckGameState.GetGameStateForObjectID(ObjectID);
}

if (MyState == none)
{
    MyState = `XCOMHISTORY.GetGameStateForObjectID(ObjectID);
}

// Alternatively, for items in a unit's inventory:
ItemState = UnitState.GetItemGameState(ItemRef, CheckGameState);
ItemState = UnitState.GetItemInSlot(eInvSlot_PrimaryWeapon, CheckGameState);
ItemStates = UnitState.GetAllItemsInSlot(eInvSlot_Utility, CheckGameState);

This is mostly relevant for multiplayer, as the pre-game setup doesn't have a History.

Delegate Priority

When adding a delegate, you can optionally specify Priority.

CHHelpersObj.AddShouldDisplayMultiSlotItemInStrategyCallback(ShouldDisplayMultiSlotItemInStrategy, 45);

Delegates with higher Priority value are executed first. Delegates with the same Priority are executed in the order they were added to CHHelpers, which would normally is the same as DLCRunOrder. The "Add...Callback functions return true if the delegate was successfully registered.

Removing Delegates

If necessary, it's possible to remove a delegate.

CHHelpersObj.RemoveShouldDisplayMultiSlotItemInStrategyCallback(ShouldDisplayMultiSlotItemInStrategy);
CHHelpersObj.RemoveShouldDisplayMultiSlotItemInTacticalCallback(ShouldDisplayMultiSlotItemInTactical);

Remove...Callback functions will return true if the Callback was successfully deleted, return false otherwise.

Source code references