Now · Updated 2552.07.02.21.29
Editor Tasks
Things that need to be done in the Unreal Editor. Compile first before opening the editor.
Things that need to be done in the Unreal Editor. Compile first before opening the editor.
When a section here lands, remove it. Past completed work is recorded in Docs/Progress.md under "Completed."
#TP Left-Hand IK Control Rig — CR_MC_TP_LeftHandIK
Replaces the FABRIK LHIK on the TP body with a Control Rig (Two Bone IK, better elbow/pole control). Auto-generated via the systemlink-unreal bridge; the graph is built + saved. One step must be done by hand because scripting it crashes the editor (RigVM add_member_variable assert — see Docs/Footguns.md / memory feedback_controlrig_python_membervar_crash).
Asset: /Game/SystemLink/Characters/MasterChief/Rigs/CR_MC_TP_LeftHandIK Already scripted + saved: preview mesh = MasterChief (TP), 91-bone hierarchy imported; graph = BeginExecution (Forwards Solve) → LeftArmTwoBoneIK (RigUnit_TwoBoneIKSimplePerItem) with ItemA=upperarm_l, ItemB=lowerarm_l, EffectorItem=hand_l.
- [x] 1. Add 3 rig variables (done by hand — scripting
add_member_variablecrashes the editor):
LeftHandTarget/Transform, PoleVector/Vector, Alpha/Float. (Public.)
- [x] 2. Bind pins to the variables (scripted via bridge, compiled + saved):
Effector ← LeftHandTarget,
PoleVector ← PoleVector, Weight ← Alpha. Bindings are variable-getter nodes → links (not a pin property).
- [x] 2b.
Applybool gate (scripted): an If node drivesWeight = Apply ? Alpha : 0, soApply=false
fully disables the IK. ⚠ Confirm the Apply variable's Default Value is ticked TRUE in the Variables panel — the bridge can't read variable defaults, and the intent is on-by-default.
- [x] World-space composition — now done INSIDE the rig.
LHIK_TP_Snapshotstores the target in
hand_r bone space (FSLLeftHandIKTargets.EffectorBoneSpace_HandR), not world/component. The rig now has GetHandR (Get Transform of hand_r, GlobalSpace) → ComposeEffector (Effector = LeftHandTarget * hand_r, UE relative×parent order) → IK Effector. So LeftHandTarget carries the bone-space offset; no transform math needed in the ABP. (Assumes weapons use hand_r as RightHandBoneName — true for all current weapons.)
- [ ] 3. TP ABP hookup (now trivial). Drop a Control Rig node in the TP AnimGraph set to
LeftHandTarget←EffectorBoneSpace_HandRAlpha←Alpha(gate withbIsValid/ the existingShould Apply LHIKif desired)PoleVector← elbow hint (optional; default(0,0,1)for now)
CR_MC_TP_LeftHandIK and feed straight from LHIK_TP_Snapshot:
Retire the FABRIK path once this matches.
- [ ] 4. Tune (visual). Left-arm
PrimaryAxis/SecondaryAxissign (defaults(1,0,0)/(0,1,0)) and the
PoleVector direction (default (0,0,1), kind=Direction) usually need adjusting so the elbow points naturally — check in the CR preview / PIE.
#Grenades — shootable pickups + per-grenade death drop
C++ done (uncommitted), needs a build + a couple of tunables + test. Full design: Docs/GrenadeSystem.md → "Extras". Also folds in the USLGrenadeIndicator::Initialize → InitializeIndicator rename (build only).
- [ ] 1. Rebuild
SystemLinkEditor(close editor first) — newISLDamageableinterface + new
UPROPERTYs; Live Coding won't surface them. Covers the grenade changes AND the Initialize rename.
- [ ] 2. Set tunables on
BP_SL_GrenadePickup_Frag(defaults are fine to start):DetonateDamageThreshold
(1 = almost any hit pops it), ChainDetonationRadius (350; 0 = no chaining), HitCollision sphere radius (14 cm — match the frag mesh).
- [ ] 3. Post-rebuild BP check (bridge or in-editor): recompile
WBP_SL_GrenadeIndicator+
WBP_SL_HUDWidget → confirm zero errors (verifies nothing in BP called the old Initialize) and that the C4263/C4264 warnings are gone.
- [ ] 4. Test (2 players, listen server): shoot a placed/dropped grenade → detonates with full blast +
cue; kills credit the shooter; a cluster chains; die holding grenades → one pickup per grenade scatters → collect back (listen for collect-sound spam on a tight pile). Radial falloff full inside InnerRadius → 0 at OuterRadius.
#Sidearm Shooting
Prerequisite: build SystemLinkEditor first — the sidearm fire C++ (active-fire-weapon routing + actor-owned ammo) must be compiled. Detail + rationale: Docs/SidearmMode.md §8.1–8.2.
Minimum to get a shot firing = tasks 1–3.
Tasks 1–4 done via the Unreal MCP bridge on 2026-06-09 (registered tag name is
SLTags.States.Weapon.SidearmActive, not the shorthand below; pistol muzzle socket isMuzzle).
Note: tasks 1–4 edited Blueprint CDOs / data assets through Python + save_asset — if a PIE test
shows stale values, recompile the affected BPs in-editor to be safe. Tasks 5–6 (cosmetics, HUD) remain.
- [x] 1. Create
BP_GA_SL_Pistol_PrimaryFire— duplicateBP_GA_SL_AssualtRifle_PrimaryFire bIsPrimaryFire = true✓RequiredWeaponClass = BP_SL_WeaponActor_Pistol✓ (was still pointing at the AR actor)ActivationRequiredTags = SLTags.States.Weapon.SidearmActive✓ActivationBlockedTags=Dead,Meleeing(inherited from AR dup — kept) ✓ActivationOwnedTags = SLTags.States.Weapon.Firing✓Triggers[0] = { Tag: Events.Weapon.PrimaryFire, Source: GameplayEvent }✓
(parent USLGameplayAbility_Fire). CDO:
- [x] 2. Block primary while sidearm drawn — added
SLTags.States.Weapon.SidearmActiveto
ActivationBlockedTags on BP_GA_SL_AssualtRifle_PrimaryFire and BP_GA_SL_Shotgun_PrimaryFire ✓.
- [x] 3. Grant the ability —
BP_GA_SL_Pistol_PrimaryFireadded toAS_AbilitySet_Default✓.
(Fire abilities for equipped weapons are granted on equip via FireMode.FireAbilityClass; the sidearm is a separate always-carried slot that never runs through GrantWeaponAbilities, so it MUST be granted via the ability set — hence no FireAbilityClass set on DA_SL_Pistol.)
- [x] 4. Sidearm weapon data asset —
DA_SL_Pistol.PrimaryFireModefilled:bEnabled=true,
FireMode=SingleShot, RoundsPerMinute=400, BaseDamage=26, MaxAmmo=12, MuzzleSocketName=Muzzle, DamageMultipliers=defaults. No AmmoDecrementEffect (actor-owned ammo). FireCueTag/ImpactCueTag still empty — see task 5. (Values are tuning placeholders — adjust to taste.)
- [~] 5. Fire cosmetics — partially done (borrowing AR FX) 2026-06-09.
DA_SL_Pistol.PrimaryFireMode.FireCueTag/ImpactCueTagset to the AR cues- Still open: verify the pistol BP's
OnLocallyPredictedShotFiredplays FP muzzle flash + sound
(GameplayCue.Weapon.AssaultRifle.PrimaryFire / .Impact) → observers + impact covered.
for the shooter (the cue excludes the local player). If the AR-duplicate didn't carry it, wire it (reuse AR FP muzzle/sound). Then pistol-specific FX (GC_SL_Pistol_PrimaryFire, own muzzle Niagara + fire sound) as polish.
- [ ] 6. HUD sidearm ammo indicator (separate) — bind
SidearmWeapon->OnAmmoChangedto a fixed
indicator. The C++ already keeps CurrentAmmo correct + replicated; this just displays it.
- [ ] 7. FP fire animations — DECIDED: state-machine, not montages (shotgun pattern). Add a
- C++ tie-in:
DA_SL_Pistol.PrimaryFireMode.PostFireDelayis currently0; aFiringstate needs
Firing state to ABP_FP_Pistol (arm recoil) and ABP_Pistol (gun-mesh slide), gated by the Firing tag / bIsFiring; TP equivalents in ABP_TP_Pistol. Apply shotgun lessons: "Always Reset on Entry" on the non-looping fire state + an Inertialization node downstream (see feedback_animbp_state_reset_on_entry). Beepers authors clips in Maya (none exist yet).
dwell time, so single-shot likely needs PostFireDelay > 0 (shotgun uses it to hold the pump). Set once the fire-clip length is known.
Test (listen server, 2 players): RT alone fires the primary; hold LT then RT fires the sidearm; sidearm ammo drains on its own count; primary ammo untouched while sidearm is out; host sees a client's sidearm shots deal the sidearm's damage (the untested damage-RPC path).