Version:
1.1.0
Milestone:11
Target:gamemd.exe— Yuri's Revenge 1.001
Last Updated:2026-08-31
This document is the reference for the currently implemented LuaAPI interface.
⚠️ Important: This file documents implemented bindings. Planned or experimental systems must not be presented as stable API.
LuaAPI exposes native Yuri's Revenge objects through Lua namespaces and userdata bindings.
| Namespace / Object | Purpose |
|---|---|
House |
Access to player/house objects and economy |
World |
Global unit/building queries and map queries |
game |
Lower-level map/event-hook diagnostics |
Engine |
Engine/HUD functions |
Game |
Currently exposes the debug HUD text helper |
Techno object |
Validated units, infantry, aircraft, and buildings |
LuaAPI is designed around a native C++ safety layer with Lua controlling gameplay behavior.
Engine objects are represented by native pointers wrapped in Lua userdata. Objects can become invalid when destroyed or when the game session changes.
Bindings therefore validate native objects before using them. Lua scripts should still treat engine objects as short-lived references.
A safe pattern is:
local units = World.GetUnits()
local unit = units[1]
if unit and unit:IsAlive() then
local hp = unit:GetHealth()
end
⚠️ IsAlive()is a validity/liveness check at the time of the call. It does not make a previously stored pointer permanently safe.
The following methods are registered on the LuaAPI.Techno userdata.
Returns the object's INI type identifier.
local typeName = unit:GetTypeName()
-- "DRED", "APOC", "E1", etc.Returns: string or no Lua value when validation fails.
Returns current health.
local hp = unit:GetHealth()Returns: number.
Returns the object's configured maximum health.
local maxHp = unit:GetMaxHealth()Returns: number.
Returns the house that owns the object.
local owner = unit:GetOwner()Returns: House userdata or nil.
Returns the object's position in map-cell coordinates.
local pos = unit:GetPosition()
print(pos.x, pos.y, pos.z)Returns: table containing x, y, and z.
Coordinates are converted from the engine's 256-lepton cell representation.
Checks whether the object passes the native liveness validation.
if unit:IsAlive() then
-- object is currently usable
endReturns: boolean.
Returns Euclidean distance between two techno objects in map cells.
local distance = unit:GetDistanceTo(enemy)Returns: number, or nil when the second object is invalid.
Returns the engine-wide unique object ID.
local id = unit:GetId()Returns: number.
Returns the native object category.
local kind = unit:GetKind()Possible values include:
building
unit
infantry
aircraft
other
Returns: string.
Orders a mobile techno to scatter from its current position, optionally using a supplied cell position.
unit:Scatter()
unit:Scatter(100, 100)Returns: no value.
Only mobile FootClass-derived objects can perform the movement operation.
Queues a movement order to the specified map cell.
local ok = unit:MoveTo(100, 120)Returns: boolean.
Queues the native Hunt mission for a mobile unit.
unit:Hunt()Returns: no value.
Checks whether a mobile unit is currently in Guard, Stop, or Sleep mission state.
if unit:IsIdle() then
-- idle
endReturns: boolean.
Checks whether the object's current mission is Attack.
if unit:IsAttacking() then
-- attacking
endReturns: boolean.
Returns the object's current native target when available.
local target = unit:GetTarget()Returns: a Techno object or nil.
Applies damage through the native ReceiveDamage pipeline when a suitable warhead is available.
local remainingHp = unit:TakeDamage(100, "TerrorBombWH")Parameters:
amount— positive damage amountwarhead— optional warhead ID
Returns: remaining health as number.
The implementation uses a fallback warhead chain when the requested warhead cannot be resolved.
⚠️ This is a real engine damage operation, not merely a Lua-side health assignment.
Temporarily disables a techno using native engine mechanisms.
unit:Disable(90)Parameters:
frames— duration in logical game frames
Buildings use their power/disabled state; mobile objects use the engine's paralysis mechanism. State is restored when the timer expires.
Sets health using a normalized ratio.
unit:SetHealthRatio(0.35)Examples:
0.35 = 35%
1.00 = 100%
Returns: boolean.
Attaches a particle system to the object.
unit:AttachParticleSystem("DamageSmokeSys")Parameters:
name— particle-system identifier
Returns: boolean.
LuaAPI exposes a native sidecar sub-turret system for additional turret state and explicit firing.
Adds a sub-turret to a techno.
unit:AddSubTurret(1, 40, 0, 15, 12, 90)| Parameter | Type | Description |
|---|---|---|
section |
number |
Voxel section index |
offX |
number |
X offset in leptons |
offY |
number |
Y offset in leptons |
offZ |
number |
Z offset in leptons |
rot |
number |
Rotation step/speed |
rof |
number |
Base rate of fire in logical frames |
Returns: boolean.
Returns the number of sub-turrets currently attached.
local count = unit:GetSubTurretCount()Returns: number.
Returns the internal state of a sub-turret.
local turret = unit:GetSubTurret(1)Returns: table or nil.
The returned table contains:
section
offX
offY
offZ
facing
targetFacing
rot
rofTimer
baseRof
Indexes are 1-based.
Assigns one explicit target to one sub-turret.
unit:SetSubTurretTarget(1, enemy)Returns: boolean.
Explicitly fires one sub-turret at a validated target.
unit:FireSubTurret(1, enemy)Returns: boolean.
Removes the unit's sub-turret state from the native manager.
unit:ClearSubTurrets()Returns: no value.
Assigns a Lua array of targets across available sub-turrets.
unit:SetSplitTargets({enemyA, enemyB, enemyC})One supplied target is assigned per available turret according to the native split-target implementation.
Returns: boolean.
Fires the configured sub-turrets at their assigned targets.
unit:FireSplitSalvo()Returns: boolean.
🧠 Target acquisition remains gameplay logic. The native sub-turret manager stores state, handles safe references, rotation/timers, and performs explicit firing requested by Lua.
Returns the current human player's house.
local player = House.GetPlayer()Returns: House userdata or nil.
Returns the number of houses in the engine house array.
local count = House.GetCount()Returns: number.
Returns a house by engine-array index.
local house = House.GetByIndex(0)Returns: House userdata or nil.
Indexes are 0-based.
Returns current available money.
local credits = house:GetCredits()Returns: number.
Sets the house's available credits by applying the required transaction delta.
house:SetCredits(5000)Parameters:
amount— target credit balance
Returns: no value.
Adds or subtracts credits.
house:AddCredits(500)
house:AddCredits(-100)Parameters:
amount— credit delta
Returns: no value.
Returns total power production.
local output = house:GetPowerOutput()Returns: number.
Returns total power consumption.
local drain = house:GetPowerDrain()Returns: number.
Returns the engine house ID/name.
local name = house:GetName()Returns: string.
Checks whether the house is controlled by a human.
if house:IsHuman() then
-- human-controlled
endReturns: boolean.
Checks alliance status between two houses.
if house:IsAlliedWith(enemyHouse) then
-- allied
endReturns: boolean.
Development/gameplay helper for creating units.
local created = player:SpawnUnit(
"APOC",
5,
100,
100,
0,
false,
"hunt"
)| Parameter | Description |
|---|---|
typeId |
INI unit type identifier |
count |
Number of units; defaults to 1 |
x |
X map cell |
y |
Y map cell |
facing |
Direction 0–255; defaults to 0 |
force |
Force-spawn flag; defaults to false |
action |
Optional action; "hunt" queues Hunt |
When normal spawning is used, the implementation searches for a nearby valid cell within its configured radius. The current implementation uses a radius of 3 cells for the fallback search.
Returns: number — successfully created units.
Returns building objects from the engine building array.
local buildings = World.GetBuildings()Returns: Lua table of Techno objects.
Returns mobile technos: vehicles, infantry, and aircraft.
local units = World.GetUnits()Returns: Lua table of Techno objects.
Returns every supported techno in the engine techno array, including buildings.
local objects = World.GetAllUnits()Returns: Lua table of validated Techno objects.
💡 Use this for global scans. It does not depend on an arbitrary spatial radius.
Returns the coordinates of a map waypoint.
local pos = World.GetWaypoint(5)
if pos then
print(pos.x, pos.y)
endReturns: position table or nil.
Returns techno objects within a specified radius in map cells.
local units = World.GetUnitsInRadius(100, 100, 15)Parameters:
x— center X celly— center Y cellradius— radius in cells
Returns: Lua table of matching Techno objects.
RA2 uses 256 leptons per cell. Native squared-distance calculations must use sufficiently wide arithmetic for large radii.
For whole-map searches, prefer World.GetAllUnits() instead of using an unnecessarily large radius.
The lowercase game namespace is a separate low-level/diagnostic namespace retained by the current implementation.
Legacy/global form of the waypoint query.
local pos = game.GetWaypoint(5)Returns: position table or nil.
Legacy/global form of the spatial unit query.
local units = game.GetUnitsInRadius(100, 100, 15)Returns: Lua table.
Returns the current number of entries in the event-hook target override cache.
local count = game.GetEventHookOverrideCount()Returns: number.
⚠️ This is a diagnostic API, not a general gameplay targeting API.
Clears the event-hook target override cache.
game.ClearEventHookOverrides()Returns: no value.
Displays a message through the game's message-list system.
Engine.PrintMessage("Hello, Commander!")Parameters:
text— UTF-8 message string
Returns: no value.
⚠️ The current native implementation does not expose acolorIndexargument.
Returns the current text used by the debug-console HUD indicator.
local text = Game.GetDebugHudText()Returns: string.
This is a development/debug helper. It is not the logical frame API.
LuaAPI uses a mod-table callback model for gameplay callbacks. Mods return a table and the loader dispatches implemented callback methods.
A typical mod has the form:
local MyMod = {}
function MyMod.OnScenarioStart()
-- initialization
end
function MyMod.Update(frame)
-- logical-frame gameplay logic
end
return MyModThe current engine also maintains callback registries for damage, scenario-start, and unit-destruction events.
The damage interception callback is used by the verified shield/damage pipeline to modify damage before the engine completes damage resolution.
A typical implementation is:
function MyMod.OnPreDamage(attacker, target, damage, dmgType, frame, subc)
if dmgType == "energy" then
return damage * 0.5
end
return nil
endReturn semantics used by the verified capability:
nil— leave damage unchanged- non-negative number — replace the damage value
0— cancel the damage
⚠️ Never return negative damage. Avoid recursively generating additional damage from inside the callback without a re-entrancy guard.
Used by mods for post-scenario initialization, such as configuring starting units.
⚠️ Do not assume this callback runs when a saved game is loaded. Runtime systems that require persistent state must account for the savegame lifecycle.
Used by the destruction-event pipeline for gameplay reactions and cleanup.
The exact native payload should be kept synchronized with the implementation when the callback contract changes.
Runs on the game's logical-frame dispatch path.
function MyMod.Update(frame)
if frame % 30 ~= 0 then
return
end
-- periodic logic
end⏱️ Gameplay timing should use the logical game frame rather than render FPS.
Global development callback.
function OnDebugCommand(text)
-- parse development command
endUnlike normal mod callbacks, OnDebugCommand is global.
LuaAPI's main loop hook dispatches gameplay logic only when Unsorted::CurrentFrame changes.
Conceptually:
Render / engine calls
↓
MainLoop hook
↓
Current logical frame changed?
↓
yes
↓
LuaAPI dispatch
This prevents the same logical-frame gameplay state from being advanced multiple times merely because the process executes the main loop at a different render rate.
CnCNet may launch gamemd-spawn.exe; integrations must therefore resolve the actual game module/process rather than assuming the executable name is always gamemd.exe.
- C++ manages native state; Lua controls gameplay behavior.
- Validate engine-backed objects before use.
- Do not retain stale native pointers across destruction or session transitions.
- Defer container cleanup when iteration can trigger object destruction.
- Invalidate references to destroyed targets immediately.
- Account for savegame lifecycle; scenario-start initialization is not sufficient for loaded saves.
- Use 64-bit or floating-point arithmetic where squared spatial values can exceed 32-bit range.
- Drive gameplay timing from logical frames, not render FPS.
- Treat hook/signature mismatches as compatibility conditions to investigate, not automatically as fatal errors.
README.md— Project overview and installationdocs/TUTORIAL.md— Beginner tutorialPROJECT/CAPABILITIES.md— Verified capabilities and case studiesPROJECT/ENGINEERING_LESSONS.md— Engineering lessons and debugging historyPROJECT/ROADMAP.md— Architecture roadmapPROJECT/CHANGELOG.md— Project history
Read the API
↓
Build a small Lua prototype
↓
Verify it in Yuri's Revenge
↓
Move unsafe native work into C++
↓
Expose a safe Lua binding
↓
Document the verified behavior
Build small. Test frequently. Verify before documenting.