Skip to content

Commit 8dfabfa

Browse files
committed
Document stage collision, launcher, and replay paths
1 parent 114a7a9 commit 8dfabfa

9 files changed

Lines changed: 402 additions & 45 deletions

File tree

docs/cookbook/custom-stage.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@ Stub `ALuxBattleStage`, `ALuxBattleStageActorManager`, `ALuxStageMeshActor`,
4141
- `ALuxBattleStageActorManager` (it manages the 9 actor lists at
4242
`+0x388..+0x408` — see [Stage System](../sc6/stage-system.md))
4343
- 1+ `ALuxStageMeshActor` (visuals + collision)
44-
- 4–8 `ALuxStageBreakableBarrierActor` (invisible boxes — the ring-out
45-
boundary; their box transforms become the gameplay-engine
46-
`g_scbattle_StageInfo_BarrierArray` at match start)
44+
- `ALuxStageBreakableBarrierActor` placements covering the ring boundary.
45+
The deterministic scbattle storage is a fixed 12-entry
46+
`g_aScbattleStageInfoBarrierEntries` buffer at `0x144844070`; verify the
47+
runtime entries after load rather than assuming every actor becomes one entry.
4748
- (Optional) `ALuxStageBreakableWallActor` (breakable walls)
4849

4950
For collision on the visual meshes use Blender naming:
@@ -142,14 +143,19 @@ stage-code argument.
142143
- **No `LuxBattleStageInfoTableRow` data** — the global stage info table is
143144
similarly forgiving. Add a row only if you need custom Center/RingEdge/Wall
144145
configuration.
146+
- **Custom terrain/wall tags** — UE4 `BodySetup` and barrier actors are not the
147+
serialized `J_StgHitChkData` terrain/wall grid. If your stage needs different
148+
terrain heights, wall tags, or ring-edge tags, plan for a native hook that
149+
substitutes the A/B `J_StgHitChkData` blobs before
150+
`LuxBattle_AttachStgHitChkData @ 0x140392080`.
145151
- **Online play** — both peers need the mod installed and built identically.
146152
The host broadcasts the resolved stage code; the client's AssetManager
147153
must succeed at loading the same code or the match desyncs at stage load.
148154

149155
## Related
150156

151157
- [Stage System](../sc6/stage-system.md) — full reference for the stage
152-
pipeline, the two-tier collision system, master enum bias, and the
158+
pipeline, collision storage, master enum bias, and the
153159
`LuxBattleStageInfoTableRow` schema.
154160
- [Replace a Stage](replace-stage.md) — even simpler if you don't need a new
155161
code, just custom geometry on a stock slot.

docs/cookbook/replace-stage.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@ project — just inherit from `AActor` and give them the right names:
2727
- `ALuxBattleStage` (root)
2828
- `ALuxBattleStageActorManager` (manages the 9 actor lists at `+0x388..+0x408`)
2929
- 1+ `ALuxStageMeshActor` — visual + collision
30-
- 4–8 `ALuxStageBreakableBarrierActor` — invisible boxes forming the ring boundary
30+
- `ALuxStageBreakableBarrierActor` placements covering the ring boundary
3131
- 0+ `ALuxStageBreakableWallActor` — visible breakable walls (optional)
3232

33-
The barrier boxes are the gameplay ring-out trigger — at match start their
34-
box-component extents are pushed into
35-
`g_scbattle_StageInfo_BarrierArray @ 0x144844070`.
33+
The deterministic ring-out boundary is stored separately in the fixed
34+
`g_aScbattleStageInfoBarrierEntries @ 0x144844070` buffer:
35+
12 `scbattle_BarrierEntry` records, `0xC0` bytes total. Barrier actors are still
36+
part of the stage setup path, but verify the runtime scbattle entries after load
37+
instead of assuming actor count maps one-to-one to barrier entries.
3638

3739
For visual collision (camera, particles, character proximity), give each
3840
`ALuxStageMeshActor.StaticMesh` a custom `BodySetup`. Name the collision
@@ -52,7 +54,9 @@ UE4's FBX importer auto-routes these into `StaticMesh.BodySetup.AggGeom`.
5254
1. Save the level as `STG004.umap` at content path
5355
`/Game/Stage/STG004/Maps/STG004`.
5456
2. (Optional) Open the `StageInfoTable` .uasset in UAssetGUI and edit the
55-
row for `STG004` if your ring shape differs from stock.
57+
row for `STG004` if your spawn/camera/RingEdge metadata differs from stock.
58+
This row does not replace the deterministic scbattle barrier buffer or the
59+
`J_StgHitChkData` terrain/wall blob.
5660
3. Cook the project for Windows.
5761

5862
## Pack into a `_P.pak`
@@ -84,6 +88,8 @@ asset.
8488
3. Confirm the in-game ring-out boundary matches your `ALuxStageBreakableBarrierActor`
8589
placements. If characters fall through the floor, your `BodySetup` collision
8690
isn't cooking — verify the `UCX_`/`UBX_` prefix names on import.
91+
4. If wall/ring-edge behavior still matches the stock stage, inspect or hook the
92+
`J_StgHitChkData` path documented in [Stage System](../sc6/stage-system.md#j_stghitchkdata-terrainwall-grid).
8793

8894
## Related
8995

docs/reference/glossary.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
| **ProcessEvent spy** | Diagnostic pattern: install a global `Hook::RegisterProcessEventPreCallback` and log each unique `(UClass, UFunction)` pair exactly once. Answers "which UFunctions fire during this one action?" without speculation. See [Global Hooks](../ue4ss/global-hooks.md). |
2929
| **GMalloc** | UE4's global `FMalloc*` pointer (shipping SC6: `DAT_1441971C8`). The thread-safe heap allocator shared by `FMemory::Malloc/Realloc/Free` and UE4SS's `RC::Unreal::GMalloc`. A mod that grows a game-owned `TArray` must use this same allocator; otherwise the component's later `Flush` frees a CRT-allocated buffer and corrupts the heap. |
3030
| **Stage code** | The string identifier for a stage (e.g. `STG001`, `STG011_R`, `STG006_V`). Routed to an asset path by `ResolveStageCodeToAssetPath @ 0x140641840` via substring matching on DLC keys (`014`, `_V`, `016`, `006_R`, `011_R`, `015`, `017`, `018`). Custom mod codes that avoid these substrings auto-route to `/Game/Stage/<code>/Maps/<code>`. Packed into an int by `ParseStageCodeStrToId`: bit 8 = `_R`, bit 9 = `_V`. See [Stage System](../sc6/stage-system.md). |
31-
| **StageInfoHandler** | `scbattle::StageInfoHandler` — the gameplay-side stage geometry interface, allocated by `LuxBattle_CreateStageInfoHandler @ 0x1403c3010`. Backed by globals at `0x144844010..0x144844158` (RngSeed, StageBoundaryParams, BarrierArray). Runs on the deterministic battle clock, separate from UE4 collision; this is what ring-out / wall-break tests actually consult. See [Stage System](../sc6/stage-system.md#two-tier-collision-gameplay-vs-visuals). |
31+
| **StageInfoHandler** | `scbattle::StageInfoHandler` — the gameplay-side stage geometry interface, allocated by `LuxBattle_CreateStageInfoHandler @ 0x1403c3010`. Backed by globals at `0x144844010..0x144844130` (RngSeed, StageBoundaryParams, 12-entry BarrierArray). Runs on the deterministic battle clock, separate from UE4 collision; ring-out also depends on the `J_StgHitChkData` terrain/wall grid. See [Stage System](../sc6/stage-system.md#stage-collision-storage). |
3232
| **Master stage enum** | `g_LuxStage_MasterEnumStringTable @ 0x144149c50` — the `TArray<FBattleStageEnumEntry>` (31 stock rows) that defines the entire stage roster. Statically built by `InitGlobalLuxStageMasterEnumStringTable @ 0x140149720`. Adding a new stage to the random-pool / picker UI requires hooking this initializer to append entries. See [Stage System](../sc6/stage-system.md#master-enum-table). |
3333
| **`_T` / `_R` / `_V` stage suffixes** | Variant codes on stock stages. `_T` = Anomaly (Story-mode tutorial-style alt arena, filtered out of the random pool). `_R` = Remix (DLC variant of a base stage). `_V` = DLC13 alt-VS variant. The random-map filter only strips `_T`; `_R` and `_V` survive, which is why STG006 / STG011 etc. roll 3× as often as singleton stages. See [Random-pool bias](../sc6/stage-system.md#random-pool-bias). |
3434
| **Steam Leaderboards (SC6)** | The actual storage backend for character usage stats and ranked-match scores. App ID `544750`. Six known leaderboards: `Characterboard` (per-character usage), `RankmatchWorld/Asia/America/Europe/Other` (regional ranked match). Publicly readable via Steam Web API — no game modification needed. See [Leaderboards](../sc6/leaderboards.md). |

docs/reference/symbol-index.md

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,14 @@ the page link for full context.
7474
| `UDemoNetDriver_GotoTimeInSeconds` | `0x141E0ECA0` | [Replay System: scrubbing](../sc6/replay-system.md#scrubbing-a-match-replay-udemonetdrivergototimeinseconds) — UE4 native scrub for match replays |
7575
| `RegisterCVar_DemoGotoTimeInSeconds` | `0x140255B00` | [Replay System: scrubbing](../sc6/replay-system.md#scrubbing-a-match-replay-udemonetdrivergototimeinseconds) — CVar `demo.GotoTimeInSeconds` registrar |
7676

77+
### Replay input serialization
78+
79+
| Symbol | Address | Page |
80+
|---|---|---|
81+
| `LuxReplay_DecodeInputPackets_FromFile` | `0x1403ED310` | [Replay System: custom Lux input replay opcodes](../sc6/replay-system.md#custom-lux-input-replay-opcodes) — expands 3-byte packed records into decoded input records |
82+
| `LuxReplay_EncodeInputEvents_ToBuffer` | `0x1403ED980` | [Replay System: custom Lux input replay opcodes](../sc6/replay-system.md#custom-lux-input-replay-opcodes) — inverse encoder for Lux replay input streams |
83+
| `LuxReplay_WriteThreeByteInputRecord_ToBuffer` | `0x1403F62E0` | [Replay System: custom Lux input replay opcodes](../sc6/replay-system.md#custom-lux-input-replay-opcodes) — appends one packed opcode record |
84+
7785
### Move VM (outer command-script VM)
7886

7987
| Symbol | Address | Page |
@@ -181,6 +189,19 @@ the page link for full context.
181189
| `LuxBattleManager_RegisterOnTickWhenPaused_Delegates` | `0x1403F8E70` | [Battle Manager: OnBattleTickWhenPaused](../sc6/battle-manager.md#onbattletickwhenpaused-what-still-ticks-during-setbattlepausetrue) |
182190
| `LuxBattleManager_BuildActorDependencyGraph_At28` | `0x1403F8A20` | [Battle Manager: subsystem dependency graph](../sc6/battle-manager.md#subsystem-dependency-graph) |
183191

192+
### Battle launcher / match setup
193+
194+
| Symbol | Address | Page |
195+
|---|---|---|
196+
| `ULuxUIBattleLauncher::Start` | `0x1405EEB50` | [Battle Manager: launcher startup](../sc6/battle-manager.md#battle-launcher-startup-path) — copies launch sub-tables into the BattleManager |
197+
| `ULuxUIBattleLauncher::GetBattleStageCode` | `0x1405B0C60` | [Battle Manager](../sc6/battle-manager.md#battle-launcher-startup-path) / [Stage System](../sc6/stage-system.md) — reads `StageSetting.StageCode`, defaults to `STG001` |
198+
| `ULuxUIBattleLauncher::SetSlipOutMode` | `0x1405ED550` | [Battle Manager: known leaf paths](../sc6/battle-manager.md#known-leaf-paths) — writes `BattleRule.SlipOut` |
199+
| `ULuxUIBattleLauncher::SetNoRingOutMode` | `0x1405ECC70` | [Battle Manager: known leaf paths](../sc6/battle-manager.md#known-leaf-paths) — writes `BattleRule.NoRingOut` |
200+
| `ULuxUIBattleLauncher::SetEndlessMode` | `0x1405EC390` | [Battle Manager: known leaf paths](../sc6/battle-manager.md#known-leaf-paths) — writes `BattleRule.Endless` |
201+
| `ULuxUIBattleLauncher::SetDamageUpMode` | `0x1405EC190` | [Battle Manager: known leaf paths](../sc6/battle-manager.md#known-leaf-paths) — writes `BattleRule.DamageUp` |
202+
| `ULuxUIBattleLauncher::SetBlowUpMode` | `0x1405EB7F0` | [Battle Manager: known leaf paths](../sc6/battle-manager.md#known-leaf-paths) — writes `BattleRule.BlowUp` |
203+
| `ULuxUIBattleLauncher::SetRecordMode` | `0x1405ED170` | [Battle Manager: known leaf paths](../sc6/battle-manager.md#known-leaf-paths) — writes top-level `BattleRecord` |
204+
184205
### Trace (visual weapon-trail)
185206

186207
| Symbol | Address | Page |
@@ -222,7 +243,17 @@ the page link for full context.
222243
| `GetStageLocIdByStageCode` | `0x1406415B0` | [Stage System: key entry points](../sc6/stage-system.md#key-entry-points) |
223244
| `ApplyBattleSettingDataTableToBattleManager` | `0x140594EB0` | [Stage System: key entry points](../sc6/stage-system.md#key-entry-points) |
224245
| `LuxBattle_CreateStageInfoHandler` | `0x1403C3010` | [Stage System: key entry points](../sc6/stage-system.md#key-entry-points) |
225-
| `LuxActor_CollectActors_By8Classes_IntoTArrays` | `0x140417A70` | [Stage System: two-tier collision](../sc6/stage-system.md#two-tier-collision-gameplay-vs-visuals) |
246+
| `LuxActor_CollectActors_By8Classes_IntoTArrays` | `0x140417A70` | [Stage System: collision storage](../sc6/stage-system.md#stage-collision-storage) |
247+
| `GetScbattleStageInfoBarrierGeometry` | `0x1402D7730` | [Stage System: scbattle ring-boundary block](../sc6/stage-system.md#scbattle-ring-boundary-block) |
248+
| `SetScbattleStageInfoBarrierGeometry` | `0x1402D77C0` | [Stage System: scbattle ring-boundary block](../sc6/stage-system.md#scbattle-ring-boundary-block) |
249+
| `LuxStage_RegisterBarrierActor_BattleEvent0x19` | `0x140427490` | [Stage System: scbattle ring-boundary block](../sc6/stage-system.md#scbattle-ring-boundary-block) |
250+
| `LuxStage_RegisterWallActor_BattleEvent0x19` | `0x140428EE0` | [Stage System: scbattle ring-boundary block](../sc6/stage-system.md#scbattle-ring-boundary-block) |
251+
| `LuxBattle_SetFrameCacheHitChkDataPtrs` | `0x1402DAE70` | [Stage System: J_StgHitChkData terrain/wall grid](../sc6/stage-system.md#j_stghitchkdata-terrainwall-grid) |
252+
| `LuxBattle_ApplyFrameCacheHitChkDataSetup` | `0x1403CFC20` | [Stage System: J_StgHitChkData terrain/wall grid](../sc6/stage-system.md#j_stghitchkdata-terrainwall-grid) |
253+
| `LuxBattle_RefreshFrameTerrainCache` | `0x140314480` | [Stage System: J_StgHitChkData terrain/wall grid](../sc6/stage-system.md#j_stghitchkdata-terrainwall-grid) |
254+
| `LuxBattle_AttachStgHitChkData` | `0x140392080` | [Stage System: J_StgHitChkData terrain/wall grid](../sc6/stage-system.md#j_stghitchkdata-terrainwall-grid) |
255+
| `LuxBattle_SampleTerrainAtXZ_Impl` | `0x140391350` | [Stage System: J_StgHitChkData terrain/wall grid](../sc6/stage-system.md#j_stghitchkdata-terrainwall-grid) |
256+
| `LuxBattle_TestAndResolveWallCollision` | `0x140316600` | [Stage System: J_StgHitChkData terrain/wall grid](../sc6/stage-system.md#j_stghitchkdata-terrainwall-grid) |
226257

227258
### Audio / Voice
228259

@@ -331,6 +362,14 @@ note on lazy extensions.
331362
| `FBatchedLine` | `0x34` | [structures](../sc6/structures.md#fbatchedline-0x34-bytes) |
332363
| `LuxBattleStageInfoTableRow` | `0x108` | [stage-system](../sc6/stage-system.md#luxbattlestageinfotablerow) |
333364
| `LuxBattleStageBasePositionParam` | `0x28` | [stage-system](../sc6/stage-system.md#luxbattlestagebasepositionparam) |
365+
| `scbattle_BarrierEntry` | `0x10` | [structures](../sc6/structures.md#scbattle-stage-info-globals) |
366+
| `scbattle_StageBoundaryParams` | `0x40` | [structures](../sc6/structures.md#scbattle-stage-info-globals) |
367+
| `scbattle_StageInfoParam` | `0x120` | [structures](../sc6/structures.md#scbattle-stage-info-globals) |
368+
| `FLuxBattleEventRecord` | `0x18` | [structures](../sc6/structures.md#stage-actor-registration-event-record) |
369+
| `LuxBattle_FrameCacheHitChkDataSetup` | `0x22` | [structures](../sc6/structures.md#j_stghitchkdata-frame-cache-setup) |
370+
| `J_StgHitChkData_Header` | `0x30` | [structures](../sc6/structures.md#j_stghitchkdata-serialized-terrainwall-blob) |
371+
| `J_StgHitChkData_CellHeader` | `0x10` | [structures](../sc6/structures.md#j_stghitchkdata-serialized-terrainwall-blob) |
372+
| `J_StgHitChkData_PoolChunk` | variable | [structures](../sc6/structures.md#j_stghitchkdata-serialized-terrainwall-blob) |
334373

335374
---
336375

@@ -449,6 +488,15 @@ Layout source: [structures.md / ALuxBattleFrameInputLog](../sc6/structures.md#al
449488
| `0x1447123BC` | `g_LuxCharaAttrTable_Int_0x3038Stride` | Per-chara int table. | [Move System](../sc6/move-system.md#key-vm-globals) |
450489
| `0x1440F4750` | `g_LuxMoveStateTable` | Move-state ids, stride `0x14`, `0x29` rows. | [Move System](../sc6/move-system.md#key-vm-globals) |
451490
| `0x144149C50` | `g_LuxStage_MasterEnumStringTable` | `TArray<FBattleStageEnumEntry>` — 31 stock entries. | [Stage System: master enum table](../sc6/stage-system.md#master-enum-table) |
491+
| `0x144844010` | `g_scbattle_StageInfo_RngSeed` | Host-broadcast stage-info seed at the head of `scbattle_StageInfoParam`. | [Stage System: scbattle ring-boundary block](../sc6/stage-system.md#scbattle-ring-boundary-block) |
492+
| `0x144844020` | `g_sScbattleStageBoundaryParams` | 64-byte origin/spawn/facing parameter block. | [structures](../sc6/structures.md#scbattle-stage-info-globals) |
493+
| `0x14484406C` | `g_dwScbattleStageInfoBarrierValid` | Ring-boundary valid flag, not an entry count. | [Stage System: scbattle ring-boundary block](../sc6/stage-system.md#scbattle-ring-boundary-block) |
494+
| `0x144844070` | `g_aScbattleStageInfoBarrierEntries` | Fixed `scbattle_BarrierEntry[12]` deterministic ring-boundary buffer. | [Stage System: scbattle ring-boundary block](../sc6/stage-system.md#scbattle-ring-boundary-block) |
495+
| `0x14470D0D0` | `g_pLuxBattle_StgHitChkDataA` | Serialized `J_StgHitChkData*` attached into frame-bounds grid A. | [Stage System: J_StgHitChkData terrain/wall grid](../sc6/stage-system.md#j_stghitchkdata-terrainwall-grid) |
496+
| `0x14470D0F8` | `g_pLuxBattle_StgHitChkDataB` | Serialized `J_StgHitChkData*` attached into frame-bounds grid B. | [Stage System: J_StgHitChkData terrain/wall grid](../sc6/stage-system.md#j_stghitchkdata-terrainwall-grid) |
497+
| `0x14470DEDC` | `g_LuxBattle_FrameContextUseB` | Selects frame-transform/grid A vs B. | [structures](../sc6/structures.md#frame-bounds-grid) |
498+
| `0x144844DD0` | `g_LuxBattle_FrameBoundsGridA` | Live terrain/wall frame-bounds grid A. | [structures](../sc6/structures.md#frame-bounds-grid) |
499+
| `0x144845E80` | `g_LuxBattle_FrameBoundsGridB` | Live terrain/wall frame-bounds grid B, embedded in FrameTransformB at `+0xC60`. | [structures](../sc6/structures.md#frame-bounds-grid) |
452500
| `0x143E87838` | `KHitBase_vftable` | Vtable for the `KHitBase` family. | [structures](../sc6/structures.md#hit-detection-node-structs-khit) |
453501
| `0x1448554E8` | `g_LuxBattle_HitReactionSlideTable` | Per-hit-type slide-decay curves. | [Movement: code references](../sc6/movement.md#code-references) |
454502
| `0x14470E018` | `g_LuxBattle_YarareReactionParamBlock` (lazy-malloc'd) | The 1100-byte `FLuxBattleYarareReactionParamBlock` (per-intensity reach/knockback/weight tables). | [Reaction System](../sc6/reaction-system.md#fluxbattleyararereactionparamblock-1100-byte-global-param-table) |

0 commit comments

Comments
 (0)