Creating Equipment

This page documents the workflow to follow when creating a new equipment

Equipment Inspector Settings

  1. Clear out child colliders and set root collider to IsTrigger.

  2. Clear out child GameObjects to UnTagged and Default layer.

  3. Set root GameObject layer to Default, tag to Equipment

  4. Mak all MeshRenderer on root and child to Not Cast Shadow

  5. Make sure the collider, if Box Collider, has correct bounds that tightly fit the equipment shape.

Equipment Script:

Extend Equipment.cs, set placeable type, hand IK type, placement preview if necessary, and consult prior examples.

  1. Set Drop Audio field to Dropped Equipment or any other drop audio clip.

  2. Set Name field to a name for the equipment.

  3. Set useBattery field if object uses battery and need to be recharged

  4. Set mIsTurnedOn field if, by default equipment is turned on (i.e. Phone, Infrared Detector)

  5. If useBattery = true, implementation needs to handle ON/OFF state, override OnEquipmentOnOffStateChangedClient(bool state, bool isOfficial) for client-side feedback and effect. isOfficial=true means the change is valid, false would be to play supposed/trying to turn ON/OFF effect, ie. clicking sound of flashlight buttons without turning on the light if not official

    1. Important: OnEquipmentOnOffStateChangedClient is only intended for client-side feedback

    2. For Server-Side equipment response logic, MUST USE OnEquipmentOnOffStateChangedServer as OnEquipmentOnOffStateChangedClient is ignored when the local hierarchy player client is in Mirage! (No need for Mirage check here : )

  6. If useBattery = true and equipment has per-tick validation logic, ie. EMF Meter needs to check battery state each tick using mIsTurnedOn before doing detection calculations, as GetCanUse() only checks if the equipment can be used (current client player is allowed to use equipment and is holding it), it needs to include the check for mIsTurnedOn explicitly before other logic

  7. By default, the battery drains at DEFAULT_BATTERY_DEPLETION_DURATION rate. For specific actions of the equipment that drain the battery in addition to basic drainage, use ServerDepleteBattery(uint amount), i.e., Phone upload virus significantly depletes the battery.

  8. Override LocalSetCurrentTimeframeVisibility(bool visible) to handle logic relating to Mirage state change, i.e. hiding Flashlight light when its local client is in Mirage. Must check for mIsTurnedOn for equipment using battery to maintain correct logic.

  9. OPTIONAL: Override IEAnimatedOnBatteryDepleted() and IEAnimatedOnBatteryRestored() for effects like flashlight flashing a few times before turning off when the battery is depleted.

Use CMDToggleEquipmentOnOffState()to turn equipment ON/OFF, make sure the starting mIsTurnedOn state is correctly set! For custom setting ON/OFF state directly, use ServerSetEquipmentTurnedOnState(bool state)

// Example of handling client state feedback and server logic
public override void OnEquipmentOnOffStateChangedClient(bool state, bool isOfficial)
{
    base.OnEquipmentOnOffStateChangedClient(state, isOfficial);
    // Play switch audio regardless, allowing player to mess around even when battery is dead
    // This is skipped if the local hierarchy player is in the Mirage, preventing Mirage players from hearing the switch click
    Play2DAudio(mSwitchAudioClip, 0.86f);
    
    // Only turn on if it's official (validated to have battery) and trying to be turned on
    // Don't worry about Mirage state here because the callback will not invoke if player is in Mirage
    mLight.enabled = isOfficial && state;
}

public override void OnEquipmentOnOffStateChangedServer(bool state, bool isOfficial)
{
    base.OnEquipmentOnOffStateChangedServer(state, isOfficial);

    // Handling Server logic here to make sure if host is in the Mirage, logic is still handled
    // And won't break for other players
    // Turn on must be official (validated and not supposed/trying action feedback)
    if (state && isOfficial)
        ServerSetEMFLevel(1);
    else
        ServerSetEMFLevel(0);
}
// Handling of client logic, ie. Input, should filter for active player
// Since no isLocalPlayer available, must check with GetCanUse()
// GetCanUse() returns true on the realm of current holding player, if that player 
// also is able to use equipment (alive and in state of can use equipment)
public override void Update()
{
    base.Update();
    if (GetCanUse())
    {
        if (GameManager.GetRewired().GetButtonDown(LIMENDefine.ACTION_TOGGLE_ITEM))
        {
            // Toggles the equipment On/Off, handles official and non-official state
            // ie. Allows player to play with power switch when battery is dead
            // Can hear the switch toggle audio but equipment won't actually turn on
            CMDToggleEquipmentOnOffState();
        }
    }
}
// Server equipment tick must check for mIsTurnedOn if uses battery to prevent
// making actions when it's turned off which would be very odd
public void ServerTickEMF()
{
    if (!mIsTurnedOn)
        return;

    int tempLevel = 1;

    mHitsCache = mDetectFromPlayerCenter ? Physics.RaycastAll(mPlayerCamera.ViewportPointToRay(new Vector3(0.5F, 0.5F, 0F)), DETECTION_DISTANCE, layers) : Physics.RaycastAll(transform.localPosition + RAYCAST_OFFSET, transform.up, DETECTION_DISTANCE, layers);
    if (mHitsCache == null || mHitsCache.Length == 0)
    {
        // No hit detected
        ServerSetEMFLevel(tempLevel);
        return;
    }
    
    // Other logic below
}
// Some code
public override void LocalSetCurrentTimeframeVisibility(bool visible)
{
    base.LocalSetCurrentTimeframeVisibility(visible);
    m_light.enabled = visible && mIsTurnedOn;
}
// Automatically turn equipment ON after battery restores if this equipment has no client ON/OFF control, i.e. Infrared Detector
public override void OnBatteryDepletionRestoredServer()
{
    base.OnBatteryDepletionRestoredServer();
    ServerSetEquipmentTurnedOnState(true);
}

Placeable Equipment (Skip if not):

  1. Check Is_Placeable

  2. For placeable equipment, make a clone of the Equipment and remove all scripts and make a mesh only prefab, then assign to equipment's placement_preview field.

  3. Make sure BoxCollider is added and isTrigger set to true.

  4. Fill in placement_offset, usually half of the mesh's size along the forward axis plus epsilon for z-fighting

  5. Remove all scripts, especiallyNetworkIdentity.cs script if attached.

Multi-Stack Equipment (Skip if not):

Multistack equipment is equipment that you hold, but there are many instances of it being used. One example is a salt pile in a salt bottle, where a player purchases one salt bottle that can place 6 salt piles before being exhausted.

  1. Use or inherit from MultiStackEquipment

  2. Fill in mCount for how many instances it can spawn

  3. Make prefab for PF_InstanceObject, which is the prefab that will be network spawned when an instance is placed. Script for logic should inherit from MultiStackInstance

  4. Same setup for placement_preview as above. Check Placeable Equipment setup.

PF_InstanceObject should not contain scripts that inherit from Equipment, as this is rather a placed instance that should just run separate code for that specific logic. Ideally, logic code also runs on server only.

Equipment Holding in Hand:

Place the equipment as a child of Hand (Transform) and clear local position, local rotation to 0. We will adjust the position and rotation of the Hand (Transform) until the desired placement of the equipment in hand is achieved and save the position and euler angles to the Initialization Storage settings of the equipment.

Adding Equipment to GlobalContainer

Click open the _Global Container prefab and add a new entry under Global Equipments

Generate Preview Image

Click open the equipment prefab and click Generate Preview Image button, make sure the generated sprite is referenced, and push the change to version control.

Add Equipment to Store Catalog

  1. Create a new Scriptable Object in folder _Store -> Database -> StoreProducts -> Equipment with the same name as the equipment's Name field. Hint: can duplicate an existing SO and fill in changes.

  2. Fill in Item Name, Type (Equipment), select the Image (should be same as Equipment's Preview Image)

  3. Set a Cost and update the Cost Table on GitBook.

  4. Add a new Page under Equipment in GitBook and link the cost table entry with the page.

  5. Checkout GBStoreDB Scriptable Object and add the created equipment SO to the catalog array.

Add Store Detail Translations

  1. Checkout LeanLocalization prefab

  2. Duplicate an existing entry for the item name with path: StoreEQ/EQName/EQUIPMENT_NAME

  3. Duplicate an existing entry for the item description with path: StoreEQ/EQDesc/EQUIPMENT_NAME

  4. Fill in entries and verify translations show up in store

Push Changes

Make sure GBStoreDB changes and newly added equipment SO are recorded. Now a new equipment is ready in game!

Adding Equipment Use Hints (Controls Display)

In Equipment script, InitControlsHints() and add new ControlHintInstruction as needed.

public override void InitControlsHints()
{
    base.InitControlsHints();
    //Add additional controls as needed
    ControlHintInstruction useInstruction = new ControlHintInstruction("Fire", LEAN_USE_KEY, true);
    mControlHints.Add(useInstruction);
}

Last updated