Repository navigation
[Memhack] Events and Hooks
memhack fires events when game state changes. These events automatically track state and only fire when values actually change. For each event, there is a corresponding hook function you can use to subscribe to the event.
The changes table is a common argument for these triggers and will only contain fields that actually changed. Each entry has this structure:
changes = {
fieldName = {
old = previousValue,
new = currentValue
},
-- ... other changed fields
}memhack automatically tracks struct state and fires events when setters are called
Important: Events only fire when values actually change. Setting a field to its current value will not fire an event.
Events fire automatically when you modify struct data through setters. Each setter will fire a single event so the following code would fire the event three times
-- This will fire pilotChanged event
pilot:setLevel(2)
pilot:setXp(50)
pilot:setName("New Name")To avoid this, assuming its unwanted behavior, you should use the set function which allows setting multiple values at a time triggering the hook only once
Memhack will also track and detect changes done outside of the setters. This will fire on specific other events when a change is detected. This for catching vanilla changes (like leveling up via xp gain) and should not be relied upon by mods. Mods should be using the setters that fire instantly instead of waiting for the change to be detected
The event system includes reentrancy protection to prevent infinite loops:
memhack:addPilotChangedHook(function(pilot, changes)
-- This is safe - won't cause infinite loop
if changes.xp then
pilot:setLevel(2) -- Can modify pilot in hook
end
end)Note: There is a maximum iteration limit to prevent infinite loops. If hooks modify state in a circular way, an error will be raised after too many iterations.
Lower priority values run first.
| Constant | Value | Role |
|---|---|---|
memhack.hooks.INTERNAL_PRIORITY |
0 |
Used by memhack for event dispatch wrappers |
memhack.hooks.DEFAULT_PRIORITY |
100 |
Default when priority is omitted |
Optional second argument on both registration paths:
memhack:addPilotChangedHook(function(pilot, changes)
-- ...
end, 50) -- runs before DEFAULT_PRIORITY (100)
memhack.events.onPilotChanged:subscribe(function(pilot, changes)
-- ...
end, 50)If priority is omitted, DEFAULT_PRIORITY (100) is used. Priority must be a number.
Each trigger can be used as a event or a hook
-
Event Object -
memhack.events.on<EventName>:subscribe(callback)- ModLoader Event subscription pattern that is never cleared -
Hook Function -
memhack:add<EventName>Hook(callback)- Hook registration cleared each time mods are loaded
Event: memhack.events.onPilotChanged:subscribe(callback)
Hook: memhack:addPilotChangedHook(callback)
This event is fired when any tracked field of a Pilot struct changes. The event automatically tracks pilot state and only fires when values actually change.
| Argument name | Type | Description |
|---|---|---|
pilot |
Pilot | The pilot struct that changed |
changes |
table | Map of field names to change tables with old and new values |
The following Pilot fields are tracked for changes:
| Field | Type | Description |
|---|---|---|
name |
ItBString | Pilot's display name |
xp |
int | Current experience points |
levelUpXp |
int | XP required for next level |
level |
int | Current pilot level (0-2) |
skill |
ItBString | Pilot's unique skill ID |
id |
ItBString | Pilot's unique identifier |
prevTimelines |
int | Number of previous timelines |
-- Register with hook function
memhack.events.onPilotChanged:subscribe(function(pilot, changes)
if changes.level then
LOG(pilot:getName() .. " changed level from " ..
changes.level.old .. " to " .. changes.level.new)
end
if changes.xp then
LOG("XP changed from " .. changes.xp.old .. " to " .. changes.xp.new)
end
...
end)Event: memhack.events.onPilotLvlUpSkillChanged:subscribe(callback)
Hook: memhack:addPilotLvlUpSkillChangedHook(callback)
This event is fired when any tracked field of a PilotLvlUpSkill struct changes. The event automatically tracks skill state and only fires when values actually change.
| Argument name | Type | Description |
|---|---|---|
pilot |
Pilot | The pilot struct that owns this skill (may be nil if skill accessed directly) |
skill |
PilotLvlUpSkill | The skill struct that changed |
changes |
table | Map of field names to change tables with old and new values |
Note: The parent pilot is automatically prepended as the first argument for convenience. It will be nil if the skill was accessed directly via memory address without going through a pilot.
The following PilotLvlUpSkill fields are tracked for changes:
| Field | Type | Description |
|---|---|---|
id |
ItBString | Skill ID determining effect |
shortName |
ItBString | Short name for UI display |
fullName |
ItBString | Full name shown on hover |
description |
ItBString | Description shown on hover |
healthBonus |
int | Health bonus provided |
coresBonus |
int | Reactor cores bonus |
gridBonus |
int | Grid power bonus |
moveBonus |
int | Movement bonus |
saveVal |
int | Save file value (0-13 for vanilla) |
-- Register with hook function (cleared on mod reload)
memhack:addPilotLvlUpSkillChangedHook(function(pilot, skill, changes)
-- pilot may be nil if skill accessed directly
local pilotName = pilot and pilot:getName() or "<unknown>"
if changes.id then
LOG(pilotName .. "'s skill changed from " ..
changes.id.old .. " to " .. changes.id.new)
end
end)
-- Or persistent event subscription:
memhack.events.onPilotLvlUpSkillChanged:subscribe(function(pilot, skill, changes)
-- same callback shape
end)