Arm Upgrades - NeisesMike/VehicleFramework GitHub Wiki

Arm upgrades are the most complex type of upgrade.

Unlike other upgrades, they require you to supply an arm model. At least for now.

Unless you specify otherwise during registration, ModVehicleArm upgrades will also be made available for the Prawn.

Overview

To implement an Arm upgrade, implement the abstract class

VehicleFramework.UpgradeTypes.ModVehicleArm

That class implements ModVehicleUpgrade, with one important override:

public override QuickSlotType QuickSlotType => QuickSlotType.Selectable;

Don't change this!

Abstract Field

ModVehicleArm, unlike other upgrade types, introduces a new abstract field over ModVehicleUpgrade.

public abstract IEnumerator GetArmPrefab(IOut<GameObject> arm);

This is how you will supply your arm model, as a GameObject, to VehicleFramework.

Here is an example from the Scanner Arm:

public override IEnumerator GetArmPrefab(IOut<GameObject> arm)
{
    // get the prefab from an AssetBundle in the file system
    string directoryPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
    string bundlePath = Path.Combine(directoryPath, "scanner");
    var bundle = AssetBundle.LoadFromFile(bundlePath);
    var objectArray = bundle.LoadAllAssets();
    GameObject model = null;
    foreach (System.Object obj in objectArray)
    {
        if (obj.ToString().Contains("ScannerArm"))
        {
            model = (GameObject)obj;
        }
    }

    // Add an original "ScannerArm" component to the arm model.
    model.AddComponent<ScannerArm>();

    // Apply Subnautica's main shader to the model.
    Shader shader = Shader.Find("MarmosetUBER");
    foreach (var renderer in model.gameObject.GetComponentsInChildren<Renderer>(true))
    {
        foreach (Material mat in renderer.materials)
        {
            // give it the marmo shader, no matter what
            mat.shader = shader;
        }
    }

    // set the out parameter of this method to the arm model.
    arm.Set(model);

    // unload the asset bundle
    bundle.Unload(false);

    // exit the method
    yield break;
}

Virtual Methods

The Arm upgrade has many new methods over ModVehicleUpgrade. First let's look at the "normal" methods. Then we'll look at some that are specific to the Prawn (Exosuit).

public virtual bool OnArmDown(ArmActionParams param, out float cooldown) // return true OnUseSuccess (false if you refuse to do the action)
{
    Logger.Log("Selecting arm: " + ClassId + " on ModVehicle: " + param.vehicle.subName.name + " in slotID: " + param.slotID.ToString());
    cooldown = 0;
    return true;
}
public virtual bool OnArmHeld(ArmActionParams param, out float cooldown) // return true OnHoldSuccess (false if you don't have "hold" logic)
{
    Logger.Log("Holding arm: " + ClassId + " on ModVehicle: " + param.vehicle.subName.name + " in slotID: " + param.slotID.ToString());
    cooldown = 0;
    return true;
}
public virtual bool OnArmUp(ArmActionParams param, out float cooldown) // return true OnReleaseSuccess (basically always)
{
    Logger.Log("Releasing arm: " + ClassId + " on ModVehicle: " + param.vehicle.subName.name + " in slotID: " + param.slotID.ToString());
    cooldown = 0;
    return true;
}
public virtual bool OnArmAltUse(ArmActionParams param)
{
    Logger.Log("Releasing arm: " + ClassId + " on ModVehicle: " + param.vehicle.subName.name + " in slotID: " + param.slotID.ToString());
    return false;
}

For my own convenience, I'm going to describe the above methods as if the player is using mouse + keyboard.

  • OnArmDown happens when the player left clicks (for a left arm) or right clicks (for a right arm).
  • OnArmUp happens when the player releases their click.
  • OnArmHeld happens every frame the mouse is clicked down and held. So you typically get one invocation of OnArmDown, then multiple invocations of OnArmHeld, and one final invocation of OnArmUp.
  • OnArmAltUse happens when the player clicks while holding the alt-key (I think). The Prawn propulsion cannon is the only vanilla arm that does this. Read this excerpt: "It also shares the regular Propulsion Cannon's ability to take items from storage, though they will be taken from the Prawn Suit's storage rather than the Player's, and the command is alt+F as opposed to just F."

Here is the definition of the SelectableActionParams struct:

public struct ArmActionParams
{
    public Vehicle vehicle;
    public int slotID;
    public TechType techType;
    public GameObject arm;
}

It's a lot like AddActionParams, so look at the ModVehicleUpgrade guide to understand more.

Here is an example from the Scanner Arm:

public override bool OnArmDown(ArmActionParams param, out float cooldown)
{
    param.arm.GetComponent<ScannerArm>().StartUsing();
    cooldown = 0;
    return true;
}
public override bool OnArmUp(ArmActionParams param, out float cooldown)
{
    param.arm.GetComponent<ScannerArm>().StopUsing();
    cooldown = 0;
    return true;
}
public override bool OnArmHeld(ArmActionParams param, out float cooldown)
{
    cooldown = 0;
    return false;
}
public override bool OnArmAltUse(ArmActionParams param)
{
    return false;
}

Notice the Scanner Arm makes no use of the cooldown out parameter. This could be useful if you want to implement something like a torpedo launcher, where you want to enforce a delay between shots of the gun.

Prawn-only Virtual Methods

public virtual void Update(GameObject arm, Vehicle vehicle, ref Quaternion aimDirection)
{
    // take action every frame, and possible update the arm's Aim Direction.
}
public virtual void OnPilotExit(GameObject arm, Vehicle vehicle)
{
    // reset the arm if need be
}
public virtual GameObject GetInteractableRoot(GameObject arm, Vehicle vehicle, GameObject target)
{
    // target is the camera's LookTarget.
    // the return value becomes Exosuit.activeTarget,
    // which influences OnHandHover GUI notifications,
    // and can be useful in arm logic
    return null;
}
⚠️ **GitHub.com Fallback** ⚠️