Repository navigation
[Memhack] Skill Core Sync
memhack.skillCoreSync keeps CORE_TYPE_SKILL_BONUS cores correct across save load, mid-mission continue, mission start, and mission end.
The base game guards against multiple bonus cores on load, and skills are reapplied on load. Skill Core Sync handles that so mech hp/move cores, pilot innate power cores, and weapon power/upgrade cores stay consistent.
Most modders should not call this module directly. Setting cores through the normal memhack APIs (for example Pawn:SetHpCore / SetMoveCore, pilot power lists, or weapon core writes) is enough. Skill Core Sync runs automatically on the events below and restores / rebuilds as needed.
All sync work is gated: a pawn is only handled when needsSkillCoreSync finds any CORE_TYPE_SKILL_BONUS (2) across that pawn's hp core, move core, pilot power list, and weapon power/upgrade lists.
Subscribed automatically in skillCoreSync.init():
| Event | Behavior |
|---|---|
modApi.events.onPostLoadGame |
Not in mission: apply cores from save. In mission (continue): ensure double suffixed _G weapon copies so vanilla load does not error, then delayed weapon rebuild from snapshot |
modApi.events.onMissionStart |
Snapshot live cores for pawns that need sync into GAME.memhack.mission_core_snapshot
|
modApi.events.onMissionEnd |
Strip suffixed weapons to base ids, reapply snapshot cores, clear snapshot |
Stored at GAME.memhack.mission_core_snapshot.
On mission start, Skill Core Sync always writes a table (possibly empty) so a mid mission continue can detect an in mission save via isMissionSaveLoaded(). On mission end the snapshot is cleared.
Per pawn entry shape (only pawns that need sync are stored):
snap[pawnId] = {
hpCore = int, -- memhack.CORE_TYPE_*
moveCore = int, -- memhack.CORE_TYPE_*
pilotPower = { ... }, -- CORE_TYPE_* list, or nil
weapons = {
[1] = { power = {...}, upgrade1 = {...}, upgrade2 = {...} },
[2] = { power = {...}, upgrade1 = {...}, upgrade2 = {...} },
},
}Weapon list keys match LIST_SPECS: power, upgrade1, upgrade2 (from memedit getPowerList / getUpgradeListA / getUpgradeListB).
- needsSkillCoreSync
- getMissionSnapshot
- setMissionSnapshot
- clearMissionSnapshot
- isMissionSaveLoaded
- replaceAllWeapons
- Lifecycle helpers
-
booleanmemhack.skillCoreSync.needsSkillCoreSync(pawnSnap, tag, pawnId)
| Argument name | Type | Description |
|---|---|---|
pawnSnap |
table | Per-pawn snapshot (hpCore, moveCore, pilotPower, weapons) |
tag |
string or nil | Optional debug label |
pawnId |
int or nil | Optional pawn id for logging |
Returns true when the pawn has any memhack.CORE_TYPE_SKILL_BONUS cores across mech hp/move, pilot power, and weapon lists. Used to skip sync for pawns that have no skill-bonus cores.
-
table or nilmemhack.skillCoreSync.getMissionSnapshot()
Returns GAME.memhack.mission_core_snapshot (initializes GAME.memhack if needed).
-
voidmemhack.skillCoreSync.setMissionSnapshot(snap)
| Argument name | Type | Description |
|---|---|---|
snap |
table | Snapshot table keyed by pawn id 0-2
|
Writes GAME.memhack.mission_core_snapshot.
-
voidmemhack.skillCoreSync.clearMissionSnapshot()
Sets GAME.memhack.mission_core_snapshot to nil.
-
booleanmemhack.skillCoreSync.isMissionSaveLoaded()
Returns true when a mission snapshot exists. Used on post load to distinguish hangar/island load from mid mission continue.
Note: Board is not ready until the frame after onPostLoadGame when continuing (see mod_loader), so in mission rebuild is deferred with modApi:runLater.
-
table or nilmemhack.skillCoreSync.replaceAllWeapons(pawn, entries, forceEnable)
| Argument name | Type | Description |
|---|---|---|
pawn |
BoardPawn | Pawn to rebuild weapons on |
entries |
table | Ordered slot entries; see below |
forceEnable |
boolean | Passed to AddWeapon
|
Removes weapons from the first entry marked replace = true onward, then readds from that index so order is preserved. No op if no entry has replace.
Each entry:
| Field | Description |
|---|---|
typeId |
Weapon type id for AddWeapon
|
cores |
Optional { power, upgrade1, upgrade2 } lists applied after add |
replace |
If true, this slot (and all later slots) are removed and re-added |
forceEnable is passed straight to pawn:AddWeapon(typeId, forceEnable):
-
In mission rebuild (
rebuildPawnWeaponsInMission) usesforceEnable = trueso PowerCost is zeroed for combat (avoids wrong class / extra cost issues while fighting). -
Mission end strip (
stripSuffixedWeaponsForPawn) usesforceEnable = false, then mission end reapplies cores from the snapshot afterward.
After add, if entry.cores is set, cores are written via memedit/memhack without growing weapon int vectors past their live size.
These are what the event handlers call. Useful if you need to understand or debug sync. Routine mods usually do not invoke them.
| Function | Role |
|---|---|
onPostLoadGame |
In mission: double suffix _G copies + delayed rebuild. Not in mission: syncAllFromSave
|
onMissionStart |
snapshotAllPawnCores -> setMissionSnapshot
|
onMissionEnd |
stripAllSuffixedWeapons -> applyAllFromSnapshot -> clearMissionSnapshot
|
syncPawnFromSave / syncAllFromSave
|
Apply mech/pilot/weapon cores from save data when gated |
snapshotPawnCores / snapshotAllPawnCores
|
Capture live cores for pawns that need sync |
applyPawnSnapshot / applyAllFromSnapshot
|
Restore snapshot cores on mission end |
rebuildPawnWeaponsInMission / rebuildAllPawnWeaponsInMission
|
Replace weapons when save ids are suffixed or live cores differ from snapshot |