Lua API
Fightura registers a global Lua object named fightura for every Figura avatar. It exposes Epic Fight state queries, joint pose data, and runtime bone overrides.
if fightura:isAvailable() then -- this entity has an Epic Fight patch attachedendState queries
Section titled “State queries”isAvailable() -> boolean
Section titled “isAvailable() -> boolean”True if the entity has an Epic Fight LivingEntityPatch attached. Always check this first; downstream queries return nil/false if it’s absent.
isEpicFightMode() -> boolean
Section titled “isEpicFightMode() -> boolean”True when the player is in Epic Fight battle stance (sword drawn, etc.).
isAttacking() -> boolean
Section titled “isAttacking() -> boolean”True for the duration of an Epic Fight attack animation.
hasPose() -> boolean
Section titled “hasPose() -> boolean”True if a fresh pose snapshot was captured this frame. Use this in events.RENDER before calling joint queries.
function events.WORLD_TICK() if fightura:isAttacking() then -- play your custom hit-reaction animation endendJoint pose
Section titled “Joint pose”These read from the most recent Epic Fight pose snapshot (updated every frame the entity is rendered).
getJointRotation(name: string) -> Vector3 | nil
Section titled “getJointRotation(name: string) -> Vector3 | nil”Returns the joint’s rotation as ZYX Euler angles in radians. Returns nil if the joint name doesn’t exist in the current armature.
function events.RENDER(delta) if not fightura:hasPose() then return end local rot = fightura:getJointRotation("Hand_L") if rot then models.MyArm.LowerArm:setRot( math.deg(rot.x), math.deg(rot.y), math.deg(rot.z) ) endendgetJointMatrix(name: string) -> Matrix4 | nil
Section titled “getJointMatrix(name: string) -> Matrix4 | nil”Returns the joint’s full 4×4 transform as a FiguraMat4. Best for matrix-driven rigs.
local headMat = fightura:getJointMatrix("Head")if headMat then models.MyHat:setMatrix(headMat)endgetJoints() -> string[]
Section titled “getJoints() -> string[]”Returns every joint name in the current entity’s Epic Fight armature. Use it to discover what’s available — different mobs/players might have different rigs.
for _, joint in pairs(fightura:getJoints()) do print(joint)endBone bindings
Section titled “Bone bindings”If your avatar uses part names Fightura doesn’t recognize out of the box, bind them once on load. See Naming Conventions for the full alias list.
mapBone(alias: string, joint: string)
Section titled “mapBone(alias: string, joint: string)”Bind a part name (or parentType name) to an Epic Fight joint. Case-insensitive on the alias side. Joint name must match Epic Fight exactly (Head, Chest, Arm_L, Hand_L, Thigh_L, Leg_L, etc.).
fightura:mapBone("MyTailRoot", "Torso")fightura:mapBone("LSleeveCuff", "Hand_L")Override only applies to this avatar — does not leak to other players.
clearBone(alias: string)
Section titled “clearBone(alias: string)”Remove a single override.
fightura:clearBone("MyTailRoot")clearBones()
Section titled “clearBones()”Remove every override this avatar has set.
getSupportedBones() -> string[]
Section titled “getSupportedBones() -> string[]”Return the list of every built-in alias Fightura recognizes (case-insensitive lowercase form). Useful for inspecting what won’t need a mapBone call.
Bending limbs
Section titled “Bending limbs”Figura runs every vertex of a part through one matrix, so a part bound to Arm_R can only rotate about the shoulder — model an arm in one piece and it stays rigid through the elbow. Fightura blends each vertex between the joint and its bend child instead: Arm_L/Arm_R toward Hand_L/Hand_R, Thigh_L/Thigh_R toward Leg_L/Leg_R.
This needs no script at all. A limb whose geometry reaches past the joint bends on its own. Everything below is for the cases where you want something other than the default.
One thing has to hold for a limb to bend: its geometry reaches past the joint on both sides. A part that stops at the elbow is a plain upper segment and stays rigid, which is what keeps already-segmented avatars rendering exactly as before. Where the bend falls is read off Epic Fight’s armature — the elbow at 17.5 px and the knee at 6.2 — so there is nothing to measure or tune.
Cubes bend too. Fightura cuts the faces that cross the joint into thinner ones on the way to the screen, so a sleeve modelled as a single box curves rather than shearing. The cuts are shared corners, so no seam shows, and they only appear inside the blend band — a mesh already fine enough to bend on its own gets none. They cost nothing against your avatar’s complexity limit, which Figura has already charged by the time Fightura sees the part.
The fold passes through Epic Fight’s own crease joints — Elbow_L/Elbow_R and Knee_L/Knee_R. They rest exactly where the joint below them rests but are animated separately, and Epic Fight binds the ring of vertices at the fold straight to them. Going through them puts the crease where the animator put it rather than wherever mixing the two ends lands, which is what stops an elbow poking out through a sleeve.
The torso is included, between Torso and Chest, but it leans rather than folds. Epic Fight weights the vanilla body across both joints the whole way up — 0.23 at the waist, 0.77 at the shoulders, neither end ever handed over — so the body moves as one piece. Fightura uses those same numbers, so a torso replacement behaves like the body it stands in for instead of creasing at the waist.
setSkin(part: string, enabled: boolean)
Section titled “setSkin(part: string, enabled: boolean)”Turn bending off for a part that would otherwise get it, or back on.
fightura:setSkin("RightArm", false)bindSkin(part: string, joint: string)
Section titled “bindSkin(part: string, joint: string)”Blend toward a joint other than the usual bend child — for a rig where the part should follow something else. Named joints work the same way: the bend lands on that joint’s rest position.
fightura:mapBone("Tentacle", "Arm_R")fightura:bindSkin("Tentacle", "Hand_R")setSkinSplit(part: string, split: number)
Section titled “setSkinSplit(part: string, split: number)”Move the bend somewhere other than the joint: distance from the part’s pivot down the bone, in model pixels. 4.8 is where the player’s elbow sits.
setSkinBand(part: string, band: number)
Section titled “setSkinBand(part: string, band: number)”How wide the blend is either side of the bend, in model pixels. Defaults to 2; 3 or more is softer.
fightura:setSkinSplit("Tentacle", 8)fightura:setSkinBand("Tentacle", 4)Each of these takes every argument it declares, on purpose. Figura reuses one argument buffer per method and never clears the slots a call leaves empty, so a method with optional trailing arguments would quietly inherit whatever the previous caller left there.
What it looks like
Section titled “What it looks like”The outside of the fold curves through the full joint angle. The inside pinches, the way every blended joint does — Epic Fight’s own limbs included — and it shows most at the sharpest angles, around 90° and past. A wider setSkinBand spreads it over more of the limb and softens it.
Seeing it
Section titled “Seeing it”Most idle poses barely bend the elbow, so it is easy to conclude nothing is happening when the setup is fine. Measured off Epic Fight’s own biped animations, as the angle between the Arm_R and Hand_R poses:
| Pose | Elbow |
|---|---|
| Standing idle | 7° |
| Holding a longsword | 15° |
| Walking | 18° |
| Basic sword swing | 37° |
| Sneaking | 59° |
| Running | 82° |
| Holding a greatsword | 88° |
| Swimming | 104° |
Test while swimming or running. At 7° the whole limb moves less than a pixel.
clearSkin(part: string)
Section titled “clearSkin(part: string)”Drop a single skin override.
clearSkins()
Section titled “clearSkins()”Drop every skin override this avatar has set.
Epic Fight events
Section titled “Epic Fight events”Fightura fires Lua events when the entity’s Epic Fight state changes — no polling required. Each event is a LuaEvent field on the fightura API; register handlers with :register(func).
fightura.attackStart:register(function(motion) -- motion is the current living motion name (e.g. "ATTACK") animations.MyAttackReact:play()end)
fightura.attackEnd:register(function() animations.MyAttackReact:stop()end)
fightura.modeChange:register(function(newMode, oldMode) if newMode == "BATTLE" then animations.DrawSword:play() elseif newMode == "MINING" then animations.SheathSword:play() endend)
fightura.hurtStart:register(function() animations.HurtFlinch:play()end)
fightura.knockDown:register(function() animations.KnockedDown:play()end)
fightura.motionChange:register(function(newMotion, oldMotion) -- e.g. transitioning from "IDLE" to "WALK"end)| Event | Args | Fires when |
|---|---|---|
attackStart | motion: string | An Epic Fight attack animation begins |
attackEnd | (none) | The attack animation finishes |
hurtStart | (none) | The entity enters a hurt state |
knockDown | (none) | The entity is knocked down |
modeChange | newMode, oldMode: string | playerMode changes (BATTLE / MINING / DEFAULT) |
motionChange | newMotion, oldMotion: string | Current living motion changes |
Events fire on the client tick. Polling APIs (isAttacking(), isEpicFightMode()) still work and are equivalent for logic that runs every frame.
Lifecycle pattern
Section titled “Lifecycle pattern”A reasonable starting script:
-- Bind any non-standard part names once.fightura:mapBone("HornLeft", "Head")fightura:mapBone("HornRight", "Head")fightura:mapBone("TailRoot", "Torso")
function events.WORLD_TICK() if fightura:isAvailable() and fightura:isAttacking() then -- Trigger custom reaction animations. endend
function events.RENDER(delta) if not fightura:hasPose() then return end
-- Per-frame transforms driven by Epic Fight pose. local hand = fightura:getJointRotation("Hand_L") if hand then models.weapon_glow:setRot(math.deg(hand.x), math.deg(hand.y), math.deg(hand.z)) endendWhat’s not exposed
Section titled “What’s not exposed”Some things deliberately aren’t in the API to keep the surface small:
- Direct armature mutation (read-only access only)
- Triggering Epic Fight skills
- Network packet sniffing
- Vanilla model state queries (use Figura’s
vanilla_modelAPI for that)
If you need something that isn’t here, open an issue on the Discord.
- Troubleshooting — what to do if a method returns
nilwhen you expect data