[CPLUS plus ex] Pilot UID - Redacted-Rice/ITB-RedactedRiceExts GitHub Wiki

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.

Why UID

Vanilla Pilot ID alone is not unique across instances. CPLUS+ stores per pilot skill data under UID keys so:

  1. Two pilots with the same Pilot_X id do not overwrite each other's data
  2. Identity survives wrapper recreation (same saveVal pair -> same UID)
  3. Skill assignment and virtual skills stay attached to the correct instance

Minting

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.

GAME save keys

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]

Skill apply preserves saveVals

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 skills and saveVal 0

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.

Format

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

Table of Contents

pilot:getUidStr()

  • string|nil pilot: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))

getAvailablePilotsWithUids

  • table cplus_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

ensureUniqueAmongAvailable

  • string|nil cplus_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))