Trace System (weapon-trail VFX)¶
The visual side of attacks. This system drives the weapon-trail / sword-swoosh / particle
effects you see whenever a weapon swings — sword arcs, axe sweeps, whip ribbons. It is
not consulted by hit resolution. The class names and the FLuxCapsule data type led
earlier documentation passes to mistake this for the hitbox system; it is not.
For the actual hit detection — strikes, kicks, hurtboxes, pushboxes, grabs — see
Hitbox System (KHit linked lists). The two systems share one
coordination key, AttackTag (FLuxCapsule.CapsuleType here, KHitBase.KindTag over
there), so a move script can open the trail and the hit window in lockstep. They are
otherwise independent.
At a glance¶
| What | Address / offset | Role |
|---|---|---|
| Visual driver actor | chara+0x458 |
ALuxTraceManager* — owns particle slots + trail renderer. |
| Trail renderer component | chara+0x3A8 |
ULuxTraceComponent* — lazy; created by ActivateTrace. |
| Slot-tag hash | chara+0x3B0/+0x3F0/+0x3F8 |
FActiveAttackSlot[] keyed by AttackTag 1..9. |
| Activate UFunction | ALuxTraceManager::Active_Impl @ 0x1408CD940 |
Opens slot tag. Registered as a UFunction on ALuxTraceManager (not ALuxBattleChara — earlier docs got the class wrong). |
| Deactivate UFunction | ALuxTraceManager::Inactive_Impl @ 0x1408D1420 |
Closes slot, fades trail. |
| Capsule struct | FLuxCapsule (0x50 bytes) |
Endpoint pair authored per-move. |
| Native query (stale on this build) | ALuxTraceManager::GetTracePosition_Impl @ 0x1408D0BB0 |
Returns false for every real chara — chara+0x388 is now CharaMesh0. |
Source: Ghidra reverse-engineering of the SC6 Steam build.
Ownership chain (verified runtime layout)¶
ALuxBattleChara
├ +0x168 USceneComponent* CustomRoot0
│
├ +0x388 USkeletalMeshComponent* CharaMesh0 (NOT MoveProvider)
├ +0x390 USkeletalMeshComponent* WeaponMesh0 (NOT Opponent)
│
├ +0x3A8 ULuxTraceComponent* (lazy; created by ActivateTrace)
│
├ +0x3B0 FActiveAttackSlot[] slot-tag hash, AttackTag 1..9
│ +0x3F0/+0x3F8 hash bucket base / count
│
├ +0x448 UBoxComponent* TestCollision
│
├ +0x458 ALuxTraceManager* (visual-only weapon-trail driver)
│ ├ +0x388 ULuxTraceDataAsset*
│ ├ +0x398 UParticleSystemComponent* EffectSlotA
│ ├ +0x3A0 UParticleSystemComponent* EffectSlotB
│ ├ +0x3A8 ULuxTraceComponent* (trail renderer)
│ └ +0x400 int32 KindIndex (ELuxTraceKindId)
│
├ +0x1438 UObject* cached MoveComponent (lazy; empty on this build)
│
└ +0x973E8 ALuxBattleChara* Opponent (read by LuxMoveVM_CheckRangeOrDistance)
Sources: ALuxCharaActorBase_Constructor @ 0x140440FB0, ALuxBattleChara_Constructor @
0x1403AB8D0, ALuxTraceManager_ActivateTrace_Impl @ 0x1408D5D10,
LuxMoveVM_CheckRangeOrDistance @ 0x140365140.
Why chara+0x388 no longer reaches a capsule container¶
ULuxBattleMoveProvider is absent from the shipping binary — there is no
Z_Construct_UClass_ULuxBattleMoveProvider and no string match for the class name. Any
function that still encodes the old chara+0x388 → +0x30 walk is a stale code path:
ALuxTraceManager::GetTracePosition_Impl @ 0x1408D0BB0— returnsfalsefor every real chara becausechara+0x388is nowCharaMesh0.LuxMoveProviderRef_Get @ 0x14045FC70,LuxMoveProviderRef_GetSubProvider @ 0x140467FE0— same staleness; ~20 adjacent functions are effectively dead code on this build.
Classes¶
| Class | Purpose | Size |
|---|---|---|
ALuxBattleChara |
Fighter actor; owns the ALuxTraceManager* at +0x458. |
0x568 |
ALuxTraceManager |
Visual-only weapon-trail driver. Hosts the Active / Inactive / GetTracePosition UFunctions (registered via Z_Construct_UClass_ALuxTraceManager_Properties @ 0x140C0BA90). Earlier docs attributed these to ALuxBattleChara, which was wrong: both the class header on the _Impl decompile and the UClass property registration confirm ALuxTraceManager. |
0x408 |
FLuxCapsule |
Capsule endpoint pair (visual). Layout known; container location uncertain on this build. | 0x50 |
ULuxTraceComponent |
Visual ticking component, holds ActiveTraces[]. |
0x4B0 |
ALuxTraceMeshActor |
Visual child actor, renders the trail. | — |
ULuxTracePartsDataAsset |
Visual curves / material params. | — |
UFunctions on ALuxTraceManager¶
Three reflected UFunctions, registered via
Z_Construct_UClass_ALuxTraceManager_Properties @ 0x140C0BA90. Earlier
docs attributed these to ALuxBattleChara, which was wrong. To invoke them
on a chara, a reflection caller must first walk
chara+0x458 → ALuxTraceManager*, or else call the _Impl directly via RVA.
| UFunction | _Impl |
exec trampoline | Behaviour |
|---|---|---|---|
Active(FTraceActiveParam) |
0x1408CD940 |
0x140C3DA20 |
Opens an attack-slot tag (1..9) into traceMgr+0x3B0. |
Inactive(FTraceInactiveParam) |
0x1408D1420 |
0x140C3FD00 |
Closes the slot; starts trail fade. |
GetTracePosition(byte, int32, out FVector, out FVector) |
0x1408D0BB0 |
0x140C3F9B0 |
Stale on this build — always returns false. |
Active reads only the first byte of its 0x30-byte FTraceActiveParam for hit logic: the
AttackTag. The remaining 47 bytes configure the visual trail.
struct FTraceActiveParam { // sizeof == 0x30 (48 bytes)
uint8 AttackTag; // +0x00 — the only field Active_Impl reads for hit resolution
uint32 Flags; // +0x04 — visual
// ... cosmetic fields through +0x2C (trail tint FLinearColor at +0x1C)
};
Active trace slots and weapon-capsule refresh¶
Active_Impl opens weapon-trace attack tags 1..9. Reopening the same tag is
idempotent: the existing slot is reused or updated, not duplicated. Inactive_Impl
with tag 0 clears all active trace slots; with tag 1..9 it removes only that tag
from the active-slot hash. The visible trail may continue fading on ULuxTraceComponent
after Inactive, but the active-slot path no longer treats the tag as live.
Update_Impl(X, Y) is only an axis-feed setter. It writes
ULuxTraceComponent+0x444 = X and ULuxTraceComponent+0x448 = Y; it is not the
component tick and does not sample bones by itself.
| Symbol | Address | Modding use |
|---|---|---|
ALuxTraceManager_execActivateTrace |
0x140C41AB0 |
UE4 exec trampoline for ActivateTrace(Mode, SkeletalMesh, OwnerChara, KindIndex) |
ALuxTraceManager_execUpdate |
0x140C415E0 |
UE4 exec trampoline for Update(X, Y) |
ALuxTraceManager_Active_Impl |
0x1408CD940 |
Opens weapon-trace attack tag 1..9 |
ALuxTraceManager_Inactive_Impl |
0x1408D1420 |
Closes one tag, or all tags when tag is 0 |
ALuxTraceManager_InsertActiveAttackSlot |
0x1408C8D60 |
Adds or updates one active trace slot |
ALuxTraceManager_UpdateActiveAttackSlotPositions |
0x1408D8490 |
Per-frame refresh of active weapon-trace slot positions |
ALuxTraceManager_ComputeCapsuleAndDirection |
0x1408D1100 |
Converts one matching FLuxCapsule into world hilt/tip/direction |
ALuxTraceManager_DispatchHitRequests |
0x1408CEB40 |
Dispatches queued weapon-trace hit requests |
ALuxTraceManager_SetSideActive |
0x1408D5AB0 |
Wrapper for trace effect visibility toggling |
ALuxTraceManager_EffectReg_SetSideActive |
0x1408D5AD0 |
Applies visibility to registered trace effect components |
FActiveAttackSlot entries are 0x44 bytes:
| Offset | Field | Meaning |
|---|---|---|
+0x00 |
uint8 Tag |
Attack tag, matched against FLuxCapsule.CapsuleType |
+0x04 |
FVector VelocityA |
Hilt velocity |
+0x10 |
FVector VelocityB |
Tip velocity |
+0x1C |
FVector PositionMid |
(hilt + tip) * 0.5 |
+0x28 |
FVector DirectionUnit |
Direction from capsule/bone transform |
+0x34 |
uint8 GateStateByte |
Runtime validation state |
+0x38 |
int32 GateCountdown |
Six-frame grace window on gate changes |
+0x3C |
int32 HashNextBucket |
Hash-chain link |
+0x40 |
int32 HashThisBucket |
Hash bucket/index bookkeeping |
Trace slots are not general hitboxes
Kicks, body strikes, throws, pushboxes, and hurtboxes do not use this trace-slot path. Those remain on the KHit linked-list system documented in Hitbox System. The trace helpers are useful for weapon-trail visuals and weapon-capsule visualization only.
FLuxCapsule (0x50 bytes)¶
struct FLuxCapsule {
uint8 header[48]; // +0x00..+0x2F unread by GetTracePosition
uint8 CapsuleType; // +0x30 matched against Active() tag
uint8 BoneId_A; // +0x31 remapped via LuxSkeletalBoneIndex_Remap
// +0x32..+0x33 pad
float LocalOffset_A[3]; // +0x34
uint8 BoneId_B; // +0x40
// +0x41..+0x43 pad
float LocalOffset_B[3]; // +0x44
// sizeof == 0x50
};
There is no visual-parts-asset pointer inside the capsule. Visual data lives on the trace component's kind data-asset.
The world-space chain (used internally by the visual updater and reused by the hitbox-side
KHitArea subclass — see Hitbox System: Reading hit volumes from a mod):
bone = LuxSkeletalBoneIndex_Remap(BoneId)
M = ALuxBattleChara_GetBoneTransformForPose(chara, pose, bone)
off = LocalOffset * g_LuxCmToUEScale * M.scale
World = M.rot * off + M.pos
g_LuxCmToUEScale @ 0x143E8A418 = 10.0f (bit pattern 0x41200000). Despite the symbol
name, the factor is 10: LocalOffset is stored in millimetres or a similar
decimetre-scaled internal unit, and multiplying by 10 lands the value in UE4 cm.
FLuxCapsuleContainer (0x40 bytes — legacy view)¶
| Offset | Type | Name |
|---|---|---|
| +0x00..+0x2F | — | internal header |
| +0x30 | FLuxCapsule** |
Data |
| +0x38 | int32 |
Num |
| +0x3C | int32 |
Max |
Where the live FLuxCapsule array is on this build¶
Unconfirmed. The FLuxCapsule struct layout is correct, but the pointer array is no
longer reachable through chara+0x388. The best candidate is ALuxBattleMoveCommandPlayer*
at BattleManager+0x4C0 — registered name "BattleMoveCommandPlayer" via
Z_Construct_UClass_ALuxBattleMoveCommandPlayer @ 0x140953780. It exposes 5 UFunctions
(GetMovePlayParam, IsPlaying, PlayMove, PlayMoveDirect, StopMove) plus 5 reflected
UPROPERTYs (PlayData, Request, RequestInfo, PlayState, PlayStateInfo) at
+0x390..+0x3D0.
Walk its fields for an 8-byte-aligned pointer to a 0x40-byte container whose +0x30..+0x3C
matches the FLuxCapsuleContainer shape.
ELuxTraceKindId (visual trail kinds)¶
ULuxTraceComponent +0x498 KindIndexCopy (i32). The enum has >30 entries — strings live at
0x14335A7B0+.
| Symbol prefix | Meaning |
|---|---|
TRC_KIND_NONE |
no trail |
TRC_KIND_AUTO |
engine-driven default |
TRC_KIND_NORMAL / TRC_KIND_NORMAL_S |
default swing trail (S = short) |
TRC_KIND_TUBE / TRC_KIND_LINE |
geometry variants |
TRC_KIND_THUNDER / TRC_KIND_WIND / TRC_KIND_FLAME / TRC_KIND_LIGHT |
elemental |
TRC_KIND_SPARK / TRC_KIND_FIRE_S |
short-lived VFX |
TRC_KIND_P* |
particle-only trails (PFLAME, PSMOKE, PBURN, PLIGHT, PDUST, PAURA, PTHUNDER, PWIND); _L = large |
TRC_KIND_LIGHTSABER |
unconfirmed character variant |
TRC_KIND_KICK |
kick-attack trail |
TRC_KIND_ULTIMATE_EDGE / TRC_KIND_ULTIMATE_CALIBUR |
super / reversal trails |
Empirical CapsuleType / AttackTag ranges¶
The plate comment on Z_Construct_UFunction_..._GetTracePosition documents the valid range
as 1..9, based on one caller's SlotIdx + 1 usage. A training-mode scan iterating
InTracePartsId from 1 to 64 sees many more types populated:
- Always-on (idle stance): 1, 2, 3, 15, 18, 21, 24, 27. The hilt points shared across 1/2/3 suggest body segments — likely hurtboxes.
- Active-frame only: the actual attack capsules. Numeric values vary per character and per move.
A mod that wants to visualise everything should scan at least 1..31, and possibly 1..63.
Trace UFunction call caveat¶
ALuxBattleChara::Active / Inactive / GetTracePosition were registered
with the short UE4_RegisterClass variant (no Ex), so their UFunction
parameter UProperty metadata is missing. Treat them as native _Impl or
lower-level trace-data targets instead of portable reflected-call APIs.
ReceiveGetWeaponTip — promising-looking dead end¶
SC6 registers a BlueprintImplementableEvent named ReceiveGetWeaponTip on
ALuxBattleWeaponEventHandler. It fires every frame during attacks — including ranged
moves like Cervantes's gun — so it looks like a universal weapon-endpoint query.
It is not useful: no SC6 character's Blueprint subclass overrides the event. Every
observed dispatch arrives with outRoot == outTip == (0,0,0) and bReturnValue == 0.
The native caller (ALuxBattleManager::GetTracePositionForPlayer @ 0x1403F4960) ignores
the result and falls through to GetTracePosition_Impl unconditionally. The event is a BP
extension point that no one ever shipped an implementation for.
Layout: see
ALuxBattleWeaponEventHandler in Structures.
Debug-draw flags (stripped in shipping)¶
ULuxTraceDataAsset declares three UPROPERTY bools, stored as a bitfield at +0x50:
bDebugDrawTraceFrame(bit 0)bDebugDrawTraceKeyFrame(bit 1)bDebugDrawTraceVelocity(bit 2)
Registered at 0x140C0CF60. Zero consumers in the shipping binary — the debug-draw
paths were compiled out via UE_BUILD_SHIPPING. Setting these flags does nothing.
UKismetSystemLibrary::DrawDebugLine is also non-functional: the UFunction reflection
entry survives (Z_Construct_UFunction_UKismetSystemLibrary_DrawDebugLine @ 0x142558090)
but its native exec handler is unbound. Calling it through reflected dispatch is a silent
no-op.
The one drawing path that is live is ULineBatchComponent on the UWorld — see
Drawing 3D Debug Lines. For the broader inventory of dev-left-behind
hooks, see Dev / Debug Hooks.
What's still unfound¶
- Public
GetTracePosition_Implremains stale / unreliable as a trace data source. For weapon-capsule visualization, prefer hookingALuxTraceManager_UpdateActiveAttackSlotPositions @ 0x1408D8490orALuxTraceManager_ComputeCapsuleAndDirection @ 0x1408D1100. FLuxCapsuleradius. The 80-byte struct holds two endpoints but no radius field. It likely lives onTracePartsDataAssetor on a sibling struct that the live container points at.- Cross-reference with the hitbox system. The weapon-trace
FLuxCapsulepath here is still separate from the general KHit linked-list hitbox system. Do not use trace capsules as a substitute for KHit when inspecting kicks, body strikes, throws, pushboxes, or hurtboxes.
Key binary addresses (RVA, image base 0x140000000)¶
Trace UFunctions and visual driver¶
| Symbol | RVA | Description |
|---|---|---|
ALuxTraceManager_Active_Impl |
0x8CD940 |
Opens weapon-trace attack slot. |
ALuxTraceManager_Inactive_Impl |
0x8D1420 |
Closes one weapon-trace tag, or all tags when tag is 0. |
ALuxTraceManager_GetTracePosition_Impl |
0x8D0BB0 |
Stale — returns false for every real chara. |
ALuxTraceManager::GetTracePosition exec trampoline |
0xC3F9B0 |
VM trampoline; older Ghidra databases may still carry a chara-class label here. |
ALuxTraceManager_ActivateTrace_Impl |
0x8D5D10 |
Lazy-creates TraceComponent, spawns trail. |
ULuxTraceComponent_BeginTrace |
0x8D5FF0 |
Populates ActiveTraces[] from kind data asset. |
ULuxTraceComponent_StartTrace |
0x8D8C40 |
SetActive(true). |
ALuxBattleManager_GetTracePositionForPlayer |
0x3F4960 |
BM helper; inherits the same staleness as GetTracePosition_Impl. |
Bone / matrix helpers (shared with the hitbox system)¶
| Symbol | RVA | Description |
|---|---|---|
ALuxBattleChara_GetBoneTransformForPose |
0x462760 |
(chara, pose, boneIdx) → FMatrix. Returns 4×4 affine, NOT FTransform. |
LuxSkeletalBoneIndex_Remap |
0x898140 |
8-bit internal idx → UE skeleton bone idx. Returns 0xFFFFFFFF on failure. |
g_LuxCmToUEScale |
0x143E8A418 |
Scale constant; value is 10.0f. |