Creating Custom Gear

The Basics

Once you have created a custom profile, you can now customise the gear as you wish.

The gear datablock is located in path_to_profile/datablocks/gear and consists of multiple parts.

To familiarise your self with the different gear parts, I recommend reading up on the official docs for gear construction here.

Gear Parts

Guns in game are comprised of the following gear parts:

Tool

  • Tool Delivery

  • Tool Screen

  • Tool Targeting

  • Tool Payload

  • Tool Main

Guns

  • Sight

  • Flashlight

  • Front

  • Grip

  • Receiver

  • Mag

  • Stock

Melee

  • Melee Handle

  • Melee Head

  • Melee Neck

  • Melee Pommel

The datablocks for each part can be found in the path_to_profile/datablocks/gear/parts

As long as the rundown doesn't import custom parts then these datablocks can remain unchanged from vanilla, however some rundowns change the IDs of parts or add custom parts. In this case you will have to edit the corresponding datablock in the profile such that the gear parts match the corresponding datablock of the custom rundown.

The datablock format for the replay viewer is very similar to the format of datablocks in-game, thus it is fairly trivial to convert between the two.

This can be quite a pain to do manually, in the future I will provide scripts that can aid this process. For now just hope that the custom rundown has no custom parts and doesnt mess with the IDs of vanilla parts 🙏

Further more there are some gotchas for parts that have a fold animation (The revolver parts).

Animating Folding Parts - Revolver, Hel Revolver, Sawed Off Shotgun

The parts with folding animations exist under receiver.js for Receiver parts and front.js for Front parts. For these parts, additional properties have to be assigned, namely:

  • fold - The name of the object that acts as the pivot point for the fold

  • foldAnim - The animation for the part that performs the fold

  • baseFoldRot - The starting quaternion rotation for the fold

  • foldOffsetRot - The offset quaternion rotation for the fold

The fold animations utilize the GearAnimDatablock from:

const { GearAnimDatablock } = await require("../../../../vanilla/datablocks/gear/animation.js", "asl");

Front Parts - Front_Short_Shotgun_2

GearPartFrontDatablock.set(47, {
  path: `${root}/Front_Short_Shotgun_2.glb`,
  
  // -- Important fold properties for the part to fold during reload --
  fold: "BreakPoint",
  foldAnim: GearAnimDatablock.Revolver_Front_1_Reload_1,
  
  aligns: [{
    alignType: "Muzzle",
    alignName: "Front_M"
  }, {
    alignType: "ShellEject",
    alignName: "Front_SE"
  }, {
    alignType: "Magazine",
    alignName: "Front_Mag"
  }, {
    alignType: "Flashlight",
    alignName: "Front_Flash"
  }, {
    alignType: "Sight",
    alignName: "Front_Sight"
  }, {
    alignType: "SightLook",
    alignName: "Sight_align"
  }, {
    alignType: "RightHand",
    alignName: "RightHand"
  }, {
    alignType: "LeftHand",
    alignName: "LeftHand"
  }]
});

Front Parts - Front_Revolver_1_Gripalign

GearPartFrontDatablock.set(36, {
  path: `${root}/Front_Revolver_1_Gripalign.glb`,
  
  // -- Important fold properties for the part to fold during reload --
  fold: "a_RevolverFold",
  baseFoldRot: {
    x: 0,
    y: 0,
    z: 0.7071,
    w: 0.7071
  },
  foldOffsetRot: {
    x: 0,
    y: 0,
    z: 0.7071,
    w: 0.7071
  },
  foldAnim: GearAnimDatablock.Revolver_Front_1_Reload_1,
  
  aligns: [{
    alignType: "Muzzle",
    alignName: "Front_M"
  }, {
    alignType: "ShellEject",
    alignName: "Front_SE"
  }, {
    alignType: "Magazine",
    alignName: "Front_Mag"
  }, {
    alignType: "Flashlight",
    alignName: "Front_Flash"
  }, {
    alignType: "LeftHand",
    alignName: "LeftHand"
  }, {
    alignType: "SightLook",
    alignName: "Sight_align"
  }, {
    alignType: "RightHand",
    alignName: "RightHand"
  }]
});

Front Parts - Front_Revolver_2

GearPartFrontDatablock.set(35, {
  path: `${root}/Front_Revolver_2.glb`,
  
  // -- Important fold properties for the part to fold during reload --
  fold: "a_Fold",
  baseFoldRot: {
    x: 0.7071,
    y: 0,
    z: 0,
    w: 0.7071
  },
  foldAnim: GearAnimDatablock.Front_Revolver_2_Reload_0,
  
  aligns: [{
    alignType: "Muzzle",
    alignName: "Front_M"
  }, {
    alignType: "ShellEject",
    alignName: "Front_SE"
  }, {
    alignType: "Magazine",
    alignName: "a_Mag"
  }, {
    alignType: "Flashlight",
    alignName: "Front_Flash"
  }, {
    alignType: "LeftHand",
    alignName: "LeftHand"
  }, {
    alignType: "SightLook",
    alignName: "Sight_align"
  }]
});

Receiver Parts - Receiver_Short_Shotgun_2

GearPartReceiverDatablock.set(22, {
  path: `${root}/Receiver_Short_Shotgun_2.glb`,
  
  // -- Important fold properties for the part to fold during reload --
  fold: "BreakPoint",
  foldAnim: GearAnimDatablock.Revolver_Front_1_Reload_1,
  
  aligns: [{
    alignType: "Flashlight",
    alignName: "Receiver_Flash"
  }, {
    alignType: "Sight",
    alignName: "Receiver_Sight"
  }]
});

Receiver Parts - Receiver_Revolver_1_Gripalign

GearPartReceiverDatablock.set(16, {
  path: `${root}/Receiver_Revolver_1_Gripalign.glb`,
  
  // -- Important fold properties for the part to fold during reload --
  fold: "a_RevolverFold",
  baseFoldRot: {
    x: 0,
    y: 0,
    z: 0.7071,
    w: 0.7071
  },
  foldOffsetRot: {
    x: 0,
    y: 0,
    z: 0.7071,
    w: 0.7071
  },
  foldAnim: GearAnimDatablock.Revolver_Front_1_Reload_1,
  
  aligns: [{
    alignType: "SightLook",
    alignName: "Receiver_Sight"
  }, {
    alignType: "Flashlight",
    alignName: "Front_Flash"
  }, {
    alignType: "Sight",
    alignName: "Receiver_Sight"
  }]
});

Receiver Parts - Receiver_Revolver_2

GearPartReceiverDatablock.set(17, {
  path: `${root}/Receiver_Revolver_2.glb`,
  
  // -- Important fold properties for the part to fold during reload --
  fold: "a_Fold",
  baseFoldRot: {
    x: 0.7071,
    y: 0,
    z: 0,
    w: 0.7071
  },
  foldAnim: GearAnimDatablock.Front_Revolver_2_Reload_0,
  
  aligns: [{
    alignType: "SightLook",
    alignName: "Receiver_Sight"
  }, {
    alignType: "Flashlight",
    alignName: "Front_Flash"
  }, {
    alignType: "Sight",
    alignName: "Receiver_Sight"
  }]
});

Gear Builder

The actual definition for weapons can be found in path_to_profile/datablocks/gear/models.js

Here you will find all the definition and animations for gear. You will notice that the ID for each piece of gear is the full gear string from the in-game gear datablocks. Do be aware that these are different to the ones that are written by the rundown developers.

To pull these IDs from a replay, simply open the browser developer console in the viewer, with a replay loaded, and enter the following command:

console.log([...(player.view.replay().getSnapshot(Infinity).data.get("IdentifierData").gearTable.values())].join("\n"));

This will log a line separated list of all the gear IDs used in the loaded replay.

Logging all Gear IDs

When defining gear, you need to specify the model, animations and offsets. There are 3 main types:

  • Melee

  • Guns

  • Tools

Gear Builder - Melee

GearDatablock.set(
  // Define the Identifier for the Gear
  Identifier.create(
    "Gear", undefined, // Header - You should not need to change this
    // The Gear string mentioned previously
    `{"Ver":1,"Name":"IsoCo Stinger","Packet":{"Comps":{"Length":7,"a":{"c":2,"v":28},"b":{"c":3,"v":162},"c":{"c":4,"v":40},"d":{"c":44,"v":16},"e":{"c":48,"v":18},"f":{"c":50,"v":13}},"MatTrans":{"tDecalA":{"scale":0.1},"tDecalB":{"scale":0.1},"tPattern":{"scale":0.1}},"publicName":{"data":"IsoCo Stinger"}}}`
  ), {
  // The function which returns the model used for this gear
  // The provided Gear string is passed to this function for use
  // by a gear builder
  model: gearJSON => {
    // Here we are using the GearBuilder to construct the gear
    // from the gear string
    const model = new GearBuilder(gearJSON);
    
    // We can specify the positional offset of the model when it is held
    // or wielded by the player character
    model.equipOffsetPos = {
      x: 0.1,
      y: 0.3,
      z: 0
    };
    
    // We can specify the rotational offset (as a quaternion)
    // of the model when it is held or wielded by the player character
    model.equipOffsetRot = {
      x: 0,
      y: 0,
      z: -0.1736482,
      w: 0.9848078
    };
    
    return model;
  },
  
  // The type of the gear - determines which animations to use
  // - "melee" => melee animations as specified by "meleeArchetype" property
  // - "pistol" => pistol animations
  // - "rifle" => rifle animations
  // - "consumable" => consumable animations
  type: "melee",
  
  // Since we are using the "melee" type, we need to specifiy what animations to
  // use for specific states
  //
  // Since multiple melees use the same archetype of animations
  // you will find that this is stored in a variable elsewhere for reuse
  meleeArchetype: {
    equipAnim: PlayerAnimDatablock.Equip_Melee,
    movementAnim: PlayerAnimDatablock.spearMovement,
    jumpAnim: PlayerAnimDatablock.Spear_Jump,
    fallAnim: PlayerAnimDatablock.Spear_Fall,
    landAnim: PlayerAnimDatablock.Spear_Land,
    attackAnim: PlayerAnimDatablock.spearSwing,
    chargeAnim: PlayerAnimDatablock.spearCharge,
    chargeIdleAnim: PlayerAnimDatablock.spearChargeIdle,
    releaseAnim: PlayerAnimDatablock.spearRelease,
    shoveAnim: PlayerAnimDatablock.spearShove
  },
  
  // The display name of the gear
  name: "Spear"
});

Gear Builder - Guns

GearDatablock.set(
  // Define the Identifier for the Gear
  Identifier.create(
    "Gear", undefined, // Header - You should not need to change this
    // The Gear string mentioned previously
    `{"Ver":1,"Name":"Van Auken CAB F4","Packet":{"Comps":{"Length":18,"a":{"c":1,"v":1},"b":{"c":2,"v":2},"c":{"c":3,"v":108},"d":{"c":4,"v":2},"e":{"c":5,"v":41},"f":{"c":6,"v":2},"g":{"c":7,"v":1},"h":{"c":8,"v":19},"i":{"c":9,"v":9},"j":{"c":10,"v":10},"k":{"c":11,"v":10},"l":{"c":12,"v":23},"m":{"c":16,"v":6},"n":{"c":19,"v":9},"o":{"c":21,"v":12},"p":{"c":23,"v":5},"q":{"c":25,"v":3}},"MatTrans":{"tDecalA":{"position":{"x":0.05,"normalized":{"x":1.0,"magnitude":1.0,"sqrMagnitude":1.0},"magnitude":0.05,"sqrMagnitude":0.00250000018},"scale":0.3},"tDecalB":{"position":{"x":0.1,"normalized":{"x":1.0,"magnitude":1.0,"sqrMagnitude":1.0},"magnitude":0.1,"sqrMagnitude":0.0100000007},"scale":0.3},"tPattern":{"angle":-135.0,"scale":0.2}},"publicName":{"data":"Van Auken CAB F4"}}}`
  ), {
  // The display name of the gear
  name: "Carbine",
  
  // The type of the gear - determines which animations to use
  // - "melee" => melee animations as specified by "meleeArchetype" property
  // - "pistol" => pistol animations
  // - "rifle" => rifle animations
  // - "consumable" => consumable animations
  type: "rifle",
  
  // "gunArchetype" applies to either "pistol" or "rifle" animation types
  // and "gunFoldAnim" relates to which animation the left hand should follow
  // during reload sequence.
  gunArchetype: {
    gunFoldAnim: GearAnimDatablock.SMG_Front_4_Reload_1
  },
  
  // The function which returns the model used for this gear
  // The provided Gear string is passed to this function for use
  // by a gear builder
  model: gearJSON => {
    // Here we are using the GearBuilder to construct the gear
    // from the gear string
    const model = new GearBuilder(gearJSON);
    
    // We can specify where the left hand should be placed on the gun
    model.leftHandGrip = {
      x: 0.05,
      y: -0.1,
      z: 0
    };
    
    // We can specify the positional offset of the model when it is held
    // or wielded by the player character
    model.equipOffsetPos = {
      x: 0,
      y: 0.05,
      z: 0.1
    };
    
    return model;
  }
});

Gear Builder - Tools

GearDatablock.set(
  // Define the Identifier for the Gear
  Identifier.create(
    "Gear", undefined, // Header - You should not need to change this
    // The Gear string mentioned previously
    `{"Ver":1,"Name":"Stalwart Flow G2","Packet":{"Comps":{"Length":12,"a":{"c":2,"v":11},"b":{"c":3,"v":73},"c":{"c":4,"v":15},"d":{"c":27,"v":15},"e":{"c":30,"v":5},"f":{"c":32,"v":4},"g":{"c":33,"v":4},"h":{"c":36,"v":1},"i":{"c":37,"v":2},"j":{"c":40,"v":2},"k":{"c":42,"v":7}},"MatTrans":{"tDecalA":{"scale":0.1},"tDecalB":{"scale":0.1},"tPattern":{"scale":0.1}},"publicName":{"data":"Stalwart Flow G2"}}}`
  ), {
  
  // The function which returns the model used for this gear
  // The provided Gear string is passed to this function for use
  // by a gear builder
  model: gearJSON => {
    // Here we are using the GearBuilder to construct the gear
    // from the gear string
    const model = new GearBuilder(gearJSON);
    
    // We can specify where the left hand should be placed on the gun
    model.leftHandGrip = {
      x: 0.05,
      y: -0.1,
      z: 0.12
    };
    
    // We can specify the positional offset of the model when it is held
    // or wielded by the player character
    model.equipOffsetPos = {
      x: 0,
      y: 0.1,
      z: 0.1
    };
    return model;
  },
  
  // The type of the gear - determines which animations to use
  // - "melee" => melee animations as specified by "meleeArchetype" property
  // - "pistol" => pistol animations
  // - "rifle" => rifle animations
  // - "consumable" => consumable animations
  //
  // If not specified, "rifle" is used by default - all tools in-game use "rifle"
  // type: "rifle",
  
  // "gunArchetype" applies to either "pistol" or "rifle" animation types
  // and "gunFoldAnim" relates to which animation the left hand should follow
  // during reload sequence.
  //
  // If not specified, no animation is used during reload - Tools don't reload
  // gunArchetype: {
  //   gunFoldAnim: undefined
  // },
  
  // The display name of the gear
  name: "C-Foam Launcher"
});

Custom Model

You can also load a custom gear model using GearGLTFModel instead of GearBuilder :

GearDatablock.set(Identifier.create("Gear", undefined, `{"Ver":1,"Name":"Executioner","Packet":{"Comps":{"Length":8,"a":{"c":2,"v":31},"b":{"c":3,"v":215},"c":{"c":4,"v":134},"d":{"c":42,"v":12},"e":{"c":44,"v":12},"f":{"c":48,"v":15},"g":{"c":50,"v":10}},"MatTrans":{"tDecalA":{"scale":0.1},"tDecalB":{"scale":0.1},"tPattern":{"scale":0.1}},"publicName":{"data":"Executioner"}}}`), {
  model: () => {
    const model = new GearGLTFModel(module.rel("../../hatchet.glb"));
    
    model.gltf.scale.set(0.2, 0.2, 0.2);
    model.gltf.rotation.set(Math.deg2rad * 20, Math.deg2rad * 150, 0);
    model.gltf.position.set(-0.04, 0.22, 0);
    
    return model;
  },
  type: "melee",
  meleeArchetype: exports.batArchetype,
  name: "Hatchet"
});

model.gltf alters the position of the model itself. If you only wish to alter its equipped offsets, you can use model.equipOffsetPos and model.equipOffsetRot .

Last updated