Skip to content

Asset-Based Interactions

The most common way to create custom interactions is through JSON asset files. This allows you to define interaction chains, damage calculations, animations, and effects without writing Java code.

  • DirectoryAssets/Server/Item/
    • DirectoryInteractions/
      • DirectoryWeapons/
        • DamageEntityParent.json
        • DirectorySword/
          • DirectoryAttacks/
            • DirectoryPrimary/
            • DirectorySignature/
    • DirectoryRootInteractions/
      • DirectoryWeapons/
        • DirectorySword/
          • Root_Weapon_Sword_Primary.json
          • Root_Weapon_Sword_Signature_Vortexstrike.json
    • DirectoryItems/
      • DirectoryWeapon/
        • DirectorySword/
flowchart LR
    subgraph ItemDef["Item Definition"]
        Item["Interactions:<br/>Primary: Root_...<br/>Ability1: Root_..."]
    end

    subgraph RootInt["Root Interaction"]
        Root["Interactions:<br/>[Weapon_Sword_Primary]"]
    end

    subgraph Chain["Interaction Chain"]
        Charging["Type: Charging<br/>Next: Chain"]
        Selector["Type: Selector<br/>HitEntity: ..."]
    end

    subgraph Vars["InteractionVars"]
        Override["Swing_Damage<br/>Parent: Custom"]
    end

    Item --> Root
    Root --> Charging
    Charging --> Selector
    Selector -.->|"Replace with<br/>custom damage"| Override

Each interaction JSON file specifies a Type that determines its behavior:

TypeDescriptionKey Fields
SimpleBasic timed operationRunTime, Effects
ChargingHold-to-charge mechanicDisplayProgress, Next (time-based)
ChainingCombo systemChainId, ChainingAllowance, Next (array)
SelectorHit detection sweepSelector, HitEntity, HitBlock
DamageEntityApply damage to entityDamageCalculator, DamageEffects
SerialRun interactions in sequenceInteractions (array)
ParallelRun interactions simultaneouslyInteractions (array)
ReplaceVar replacement for extensibilityVar, DefaultValue
StatsConditionCheck/consume statsCosts, ValueType, Next
ApplyEffectApply entity effectEffectId, Entity
ClearEntityEffectRemove entity effectEntityEffectId, Entity
ChangeStatModify entity statsStatModifiers, ValueType
RunOnBlockTypesSearch blocks and run interactionsRange, BlockSets, MaxCount, Interactions

To create a reusable Parent that other interactions can inherit from, create a JSON file with the desired base properties:

MyPlugin_DamageParent.json
{
"Type": "DamageEntity",
"DamageCalculator": {
"Class": "Heavy"
},
"DamageEffects": {
"Knockback": {
"Type": "Force",
"Force": 8.0,
"VelocityY": 3.0
},
"WorldParticles": [
{
"SystemId": "Impact_Blade_01"
}
]
},
"Next": {
"Type": "Serial",
"Interactions": [
{
"Type": "ApplyEffect",
"EffectId": "Red_Flash",
"Entity": "Target"
}
]
}
}

Then reference it as a Parent in your item’s InteractionVars:

Weapon_Sword_Custom.json
{
"Parent": "Template_Weapon_Sword",
"InteractionVars": {
"Swing_Left_Damage": {
"Interactions": [
{
"Parent": "MyPlugin_DamageParent",
"DamageCalculator": {
"BaseDamage": { "Physical": 25 }
}
}
]
}
}
}
Complete DamageEntity Example
{
"Parent": "DamageEntityParent",
"DamageCalculator": {
"Class": "Light",
"BaseDamage": {
"Physical": 18,
"Fire": 5
},
"Type": "Absolute",
"RandomPercentageModifier": 0.15
},
"Effects": {
"CameraEffect": "Impact"
},
"DamageEffects": {
"Knockback": {
"Type": "Force",
"VelocityConfig": {
"AirResistance": 0.99,
"GroundResistance": 0.94,
"Threshold": 3.0,
"Style": "Linear"
},
"Direction": { "X": 0, "Y": 1, "Z": -2 },
"Force": 6.0,
"VelocityType": "Set"
},
"WorldSoundEventId": "SFX_Sword_T2_Impact",
"LocalSoundEventId": "SFX_Sword_T2_Impact",
"WorldParticles": [
{ "SystemId": "Impact_Sword_Basic" }
],
"CameraEffect": "Impact_Light"
},
"EntityStatsOnHit": [
{ "EntityStatId": "SignatureEnergy", "Amount": 1 }
]
}
FieldTypeDescription
ParentstringParent interaction to inherit from
DamageCalculator.ClassstringDamage class: Light, Heavy, Signature
DamageCalculator.BaseDamageobjectDamage by type: Physical, Fire, Ice, etc.
DamageEffects.KnockbackobjectKnockback force and direction
DamageEffects.WorldParticlesarrayParticles spawned on hit
EntityStatsOnHitarrayStats modified on successful hit
MyPlugin_Swing_Left.json
{
"Type": "Simple",
"RunTime": 0.117,
"Effects": {
"ItemAnimationId": "SwingLeft",
"WorldSoundEventId": "SFX_Light_Melee_T2_Swing",
"LocalSoundEventId": "SFX_Sword_T2_Swing_RL_Local"
},
"Next": {
"Type": "Replace",
"Var": "Swing_Left_Selector",
"DefaultOk": true,
"DefaultValue": {
"Interactions": ["MyPlugin_Swing_Left_Selector"]
}
}
}
FieldTypeDescription
RunTimefloatDuration in seconds
Effects.ItemAnimationIdstringAnimation ID from the item’s animation set
Effects.WorldSoundEventIdstringSound played for all players
Effects.LocalSoundEventIdstringSound played only for the player
Effects.TrailsarrayTrail effects during swing
Effects.CameraEffectstringCamera shake/effect ID
MyPlugin_Swing_Selector.json
{
"Type": "Selector",
"RunTime": 0.05,
"Effects": {
"ItemAnimationId": "SwingLeftCharged",
"Trails": [
{
"TrailId": "Sword_Trail",
"StartDistance": 0.1,
"EndDistance": 1.3
}
],
"CameraEffect": "Sword_Swing_Diagonal_Right"
},
"Selector": {
"Id": "Horizontal",
"Direction": "ToLeft",
"TestLineOfSight": false,
"StartDistance": 0.1,
"EndDistance": 2.75,
"Length": 90,
"ExtendTop": 0.5,
"ExtendBottom": 1.0,
"YawStartOffset": -45
},
"HitBlock": {
"Interactions": ["Block_Damage"]
},
"HitEntity": {
"Interactions": [
{
"Type": "Replace",
"Var": "Swing_Left_Damage",
"DefaultOk": true,
"DefaultValue": {
"Interactions": ["MyPlugin_Swing_Left_Damage"]
}
}
]
}
}
Selector FieldTypeDescription
IdstringSelector shape: Horizontal, Vertical, Thrust
DirectionstringSweep direction: ToLeft, ToRight
StartDistancefloatInner sweep radius
EndDistancefloatOuter sweep radius
LengthfloatArc angle in degrees (360 = full circle)
ExtendTop/BottomfloatVertical extension of hitbox
YawStartOffsetfloatStarting angle offset
TestLineOfSightboolRequire line of sight to target
MyPlugin_Primary_Chain.json
{
"Type": "Chaining",
"ChainingAllowance": 2,
"ChainId": "Sword_Swings",
"Next": [
{
"Type": "Replace",
"Var": "Swing_Left",
"DefaultOk": true,
"DefaultValue": {
"Interactions": ["MyPlugin_Swing_Left"]
}
},
{
"Type": "Replace",
"Var": "Swing_Right",
"DefaultOk": true,
"DefaultValue": {
"Interactions": ["MyPlugin_Swing_Right"]
}
},
{
"Type": "Replace",
"Var": "Swing_Down",
"DefaultOk": true,
"DefaultValue": {
"Interactions": ["MyPlugin_Swing_Down"]
}
}
]
}
FieldTypeDescription
ChainIdstringUnique identifier for this combo chain
ChainingAllowancefloatTime window to continue combo (seconds)
NextarraySequence of interactions in the combo

The Replace type enables extensibility by defining variable points that items can override:

Interaction with Replace point
{
"Type": "Simple",
"RunTime": 0.1,
"Next": {
"Type": "Replace",
"Var": "Custom_Damage",
"DefaultOk": true,
"DefaultValue": {
"Interactions": ["Default_Damage_Interaction"]
}
}
}

Items override these via InteractionVars:

Item overriding the Replace var
{
"Parent": "Template_Weapon_Sword",
"InteractionVars": {
"Custom_Damage": {
"Interactions": [
{
"Parent": "DamageEntityParent",
"DamageCalculator": {
"BaseDamage": { "Physical": 30, "Fire": 10 }
}
}
]
}
}
}

Root interactions are entry points that items reference:

Root_MyPlugin_Custom_Primary.json
{
"RequireNewClick": true,
"ClickQueuingTimeout": 0.2,
"Cooldown": {
"Cooldown": 0.25
},
"Interactions": [
"MyPlugin_Custom_Primary"
]
}
FieldTypeDescription
RequireNewClickboolRequire new mouse click (not hold)
ClickQueuingTimeoutfloatTime to queue next click
Cooldown.CooldownfloatCooldown duration in seconds
InteractionsarrayInteraction IDs to execute

Complete signature ability with stat cost:

MyPlugin_Signature_Ability.json
{
"Type": "StatsCondition",
"Costs": {
"SignatureEnergy": 100
},
"ValueType": "Percent",
"Next": {
"Type": "Serial",
"Interactions": [
{
"Type": "Replace",
"Var": "Signature_Effect",
"DefaultOk": true,
"DefaultValue": {
"Interactions": ["MyPlugin_Signature_Spin"]
}
},
{
"Type": "Simple",
"RunTime": 0.15
},
{
"Type": "Replace",
"Var": "Signature_Finisher",
"DefaultOk": true,
"DefaultValue": {
"Interactions": ["MyPlugin_Signature_Stab"]
}
},
{
"Type": "ChangeStat",
"StatModifiers": {
"SignatureEnergy": -100
},
"ValueType": "Percent"
}
]
}
}

Run multiple interactions simultaneously by forking into concurrent chains. Each entry in Interactions must be a RootInteraction string ID (not an inline object):

Parallel execution example
{
"Type": "Parallel",
"Interactions": [
"Root_MyPlugin_Force_Effect",
"Root_MyPlugin_Selector",
"Root_MyPlugin_Particle_Effect"
]
}

Search for blocks matching a BlockSet within a radius and run interactions on each found block:

AOE block destruction example
{
"Type": "RunOnBlockTypes",
"Range": 5.0,
"BlockSets": ["Destructible_Stone", "Destructible_Wood"],
"MaxCount": 20,
"Interactions": [
{
"Type": "Simple",
"RunTime": 0.1,
"Effects": {
"WorldParticles": [{ "SystemId": "Block_Break" }]
}
}
]
}
FieldTypeDescription
RangefloatSearch radius in blocks (spherical)
BlockSetsstring[]Array of BlockSet IDs to match
MaxCountintMaximum blocks to affect (uses reservoir sampling)
InteractionsarrayInteraction chain to run on each found block

Use Cases:

  • AOE destruction abilities (break all nearby stone)
  • Terrain modification spells (convert grass to dirt)
  • Resource gathering sweeps (harvest all crops in range)
  • Environmental effects (ignite all wood blocks)
Frost spell freezing water blocks
{
"Type": "RunOnBlockTypes",
"Range": 8.0,
"BlockSets": ["Water_Sources"],
"MaxCount": 50,
"Interactions": [
{
"Type": "Serial",
"Interactions": [
{
"Type": "Simple",
"RunTime": 0.05,
"Effects": {
"WorldParticles": [{ "SystemId": "Frost_Spread" }],
"WorldSoundEventId": "SFX_Ice_Form"
}
}
]
}
]
}

Here’s a complete example of a custom weapon with its own interaction chain:

1. Create the damage interaction (inherits from DamageEntityParent):

Assets/Server/Item/Interactions/MyPlugin/MyPlugin_Heavy_Damage.json
{
"Parent": "DamageEntityParent",
"DamageCalculator": {
"Class": "Heavy"
},
"Effects": {
"CameraEffect": "Impact"
},
"DamageEffects": {
"Knockback": {
"Type": "Force",
"Direction": { "X": 0, "Y": 2, "Z": -3 },
"Force": 10.0,
"VelocityType": "Set"
},
"WorldParticles": [
{ "SystemId": "Impact_Blade_Heavy" }
],
"CameraEffect": "Impact_Heavy"
},
"EntityStatsOnHit": [
{ "EntityStatId": "SignatureEnergy", "Amount": 2 }
]
}

2. Create the swing animation + selector:

Assets/Server/Item/Interactions/MyPlugin/MyPlugin_Swing.json
{
"Type": "Simple",
"RunTime": 0.2,
"Effects": {
"ItemAnimationId": "SwingDown",
"WorldSoundEventId": "SFX_Heavy_Melee_Swing"
},
"Next": {
"Type": "Selector",
"RunTime": 0.1,
"Effects": {
"Trails": [{ "TrailId": "Heavy_Blade_Trail" }],
"CameraEffect": "Heavy_Swing"
},
"Selector": {
"Id": "Vertical",
"Direction": "ToDown",
"StartDistance": 0.2,
"EndDistance": 3.0,
"Length": 120
},
"HitEntity": {
"Interactions": [
{
"Type": "Replace",
"Var": "Heavy_Damage",
"DefaultOk": true,
"DefaultValue": {
"Interactions": ["MyPlugin_Heavy_Damage"]
}
}
]
}
}
}

3. Create the root interaction:

Assets/Server/Item/RootInteractions/MyPlugin/Root_MyPlugin_Primary.json
{
"RequireNewClick": true,
"Cooldown": {
"Cooldown": 0.5
},
"Interactions": ["MyPlugin_Swing"]
}

4. Create the weapon item:

Assets/Server/Item/Items/Weapon/MyPlugin/Weapon_Greatsword_Custom.json
{
"Parent": "Template_Weapon_Sword",
"TranslationProperties": {
"Name": "myplugin.items.greatsword_custom.name"
},
"Model": "MyPlugin/Weapons/Greatsword_Custom.blockymodel",
"Texture": "MyPlugin/Weapons/Greatsword_Custom_Texture.png",
"Quality": "Epic",
"ItemLevel": 40,
"MaxDurability": 200,
"DurabilityLossOnHit": 0.25,
"Interactions": {
"Primary": "Root_MyPlugin_Primary",
"Secondary": "Root_Weapon_Sword_Secondary_Guard"
},
"InteractionVars": {
"Heavy_Damage": {
"Interactions": [
{
"Parent": "MyPlugin_Heavy_Damage",
"DamageCalculator": {
"BaseDamage": { "Physical": 35 }
}
}
]
}
}
}

Available animation IDs depend on the item’s PlayerAnimationsId. For swords:

Animation IDDescription
SwingLeftHorizontal left swing
SwingRightHorizontal right swing
SwingDownVertical downward swing
ThrustForward thrust
SpinRightChargingSpin charge-up
SpinRightChargedSpin attack
GuardBlocking stance
GuardHitBlock impact reaction
  1. Use Parent inheritance - Create base damage interactions and override specific values
  2. Use Replace vars - Make interactions extensible so items can customize behavior
  3. Prefix custom files - Use your plugin name prefix for all custom interactions
  4. Chain interactions logically - Animation → Selector → Damage is the standard pattern
  5. Test with different targets - Verify hit detection with blocks and entities
  6. Balance cooldowns - Match cooldown to animation length