Repository navigation
[CPLUS plus ex] Pilot UID
Per instance pilot identity separate from Pilot ID, so multiple pilots can share the same Pilot ID (e.g. two copies of the same pilot type). This allows for differentiating between two of the same cyborg pilots for example.
Identity is the saveVal pair on the pilot's vanilla level up skills (slots 1 and 2), expressed as pilotId:sv1:sv2. Same UID means the same pilot instance, including when the game recreates Pilot wrappers on turn reset.
See Skill State for APIs that look up skills by pilot, and PilotLvlUpSkill for the underlying saveVal memory field.
Vanilla Pilot ID alone is not unique across instances. CPLUS+ stores per pilot skill data under UID keys so:
- Two pilots with the same
Pilot_Xid do not overwrite each other's data - Identity survives wrapper recreation (same saveVal pair -> same UID)
- Skill assignment and virtual skills stay attached to the correct instance
On conflict, a new unused saveVal pair is chosen at random in 0-13 and written with _setSaveVal_noFire on slots 1 and 2 (avoids firing change handlers while establishing identity):
pilot:getLvlUpSkill(1):_setSaveVal_noFire(sv1)
pilot:getLvlUpSkill(2):_setSaveVal_noFire(sv2)Up to 1000 attempts. Failure logs an error and returns nil.
Persistent per-pilot skill tables are keyed by UID:
| Table | Contents |
|---|---|
GAME.cplus_plus_ex.pilotSkills[uid] |
Vanilla slots: { {id = skill1Id}, {id = skill2Id} }
|
GAME.cplus_plus_ex.pilotVirtualSkills[uid] |
Virtual skill entries for slots 3+ |
Always use pilot:getUidStr() (or a resolved entry UID) when reading or writing these tables. Do not key by Pilot ID alone.
Example:
local uid = pilot:getUidStr()
local stored = GAME.cplus_plus_ex.pilotSkills[uid]
local virtual = GAME.cplus_plus_ex.pilotVirtualSkills[uid]When CPLUS+ applies or swaps skill ids on slots 1-2, saveVals are treated as UID tokens and preserved across skill id changes. Only skill id/text/bonuses are rewritten when the id changes. The existing saveVal pair is passed through so the UID string stays stable.
Virtual skill objects (slots 3+) are not vanilla lvl-up slots. They are initialized with saveVal 0 via _setSaveVal_noFire(0) so they stay out of UID pairing. Only slots 1 and 2 participate in the UID.
pilotId:sv1:sv2
| Part | Source |
|---|---|
pilotId |
pilot:getIdStr() |
sv1 |
pilot:getLvlUpSkill(1):getSaveVal() |
sv2 |
pilot:getLvlUpSkill(2):getSaveVal() |
SaveVals used for UID minting are integers in 0-13 (vanilla save range).
Example: Pilot_BeetleMech:4:9
-
string|nilpilot:getUidStr()
Returns the UID string for this Pilot struct, or nil if the argument is not a memhack Pilot.
Does not mint or remint. Reading the same id and saveVal pair on different wrappers returns the same string.
Example:
local pilot = Board:GetPawn(0):GetPilot()
local uid = pilot:getUidStr()
LOG("UID: " .. tostring(uid))-
tablecplus_plus_ex._subobjects.pilot_uid:getAvailablePilotsWithUids()
Walks Game:GetAvailablePilots(), claims each UID, and remints on conflict.
Returns: Array of {pilot = Pilot, uid = string} with conflicts already reminted.
Example:
local pilot_uid = cplus_plus_ex._subobjects.pilot_uid
for _, entry in ipairs(pilot_uid:getAvailablePilotsWithUids()) do
LOG(entry.uid .. " @ " .. tostring(entry.pilot._address))
end-
string|nilcplus_plus_ex._subobjects.pilot_uid:ensureUniqueAmongAvailable(pilot)
| Argument name | Type | Description |
|---|---|---|
pilot |
Pilot | memhack Pilot struct |
Resolves the available pilot claimed set, then claims or remints this pilot's UID. Used for pod / perfect island rewards that are not in the available list yet.
Returns the resulting UID string, or nil if pilot is not a memhack Pilot.
Example:
local pilot_uid = cplus_plus_ex._subobjects.pilot_uid
local uid = pilot_uid:ensureUniqueAmongAvailable(rewardPilot)
LOG("Reward pilot UID: " .. tostring(uid))