openrac.game
Since 1.1openrac.game: stringopenrac.game, .version, .platform, .api: "rac1", "pal" or "ntsc", "macos-arm64" ..., the game's interface version ("1.1")
You are reading the docs of SDK 1.3.0, an older release. See the latest (1.11.0).
Reference · SDK 1.3.0
A script mod sees one global, openrac, and the safe parts of Luau's library. For autocomplete, point your editor's Luau language server at the SDK's types/rac1.d.luau.
openrac.game: stringopenrac.game, .version, .platform, .api: "rac1", "pal" or "ntsc", "macos-arm64" ..., the game's interface version ("1.1")
openrac.version: "pal" | "ntsc"openrac.game, .version, .platform, .api: "rac1", "pal" or "ntsc", "macos-arm64" ..., the game's interface version ("1.1")
openrac.platform: stringopenrac.game, .version, .platform, .api: "rac1", "pal" or "ntsc", "macos-arm64" ..., the game's interface version ("1.1")
openrac.api: stringopenrac.game, .version, .platform, .api: "rac1", "pal" or "ntsc", "macos-arm64" ..., the game's interface version ("1.1")
openrac.log: (...any) -> ()openrac.log(...), openrac.warn(...): a line in the game's log, after your mod's id (print does the same as log)
openrac.hook("rac1.player.set_planar_velocity", function(original, angle)
original(angle) -- the game's own, or the mod before yours
openrac.log(`moving along {angle}`)
end)
openrac.after("rac1.player.advance_animation", function()
-- runs after the game has moved the animation on
end)-- After each game tick (50 a second on PAL, 60 on NTSC-U).
openrac.on_frame(function()
ticks += 1
if ticks % (every * 50) == 0 then
openrac.log(`still here after {ticks} ticks`)
end
end)openrac.warn: (...any) -> ()openrac.log(...), openrac.warn(...): a line in the game's log, after your mod's id (print does the same as log)
openrac.setting: (key: string) -> anyopenrac.setting(key): one of your settings
--!strict
local swap = openrac.setting("pair") :: string
openrac.on_pad(function(port: number, report: buffer)
local buttons = buffer.readu16(report, 0) -- a bit is clear while its button is held
-- ... change buttons ...
buffer.writeu16(report, 0, buttons)
end)
openrac.on_frame(function()
-- after each game tick: 50 a second on PAL, 60 on NTSC-U
end)local every = openrac.setting("every") :: number -- the manifest's setting, as the launcher set it
local ticks = 0openrac.hook: (name: HookPoint, fn: (original: (...number) -> ...number, ...number) -> ...number) -> ()openrac.hook(id, fn): wrap or replace a hook point: fn(original, ...) gets its arguments; original(...) goes on down the chain and returns its result; original() with no arguments passes the call's own; return nothing to keep the original's result
openrac.hook("rac1.player.set_planar_velocity", function(original, angle)
original(angle) -- the game's own, or the mod before yours
openrac.log(`moving along {angle}`)
end)
openrac.after("rac1.player.advance_animation", function()
-- runs after the game has moved the animation on
end)-- A hook point by name, wrapped: the game's own call goes on through `original`.
-- openrac.hook("Hero_SetPlanarVelocity", function(original, angle: number)
-- original(angle)
-- end)openrac.before: (name: HookPoint, fn: (...number) -> ()) -> ()openrac.before(id, fn), openrac.after(id, fn): fn(...) before or after the rest of the chain, which runs whatever fn does
openrac.after: (name: HookPoint, fn: (...number) -> ()) -> ()openrac.before(id, fn), openrac.after(id, fn): fn(...) before or after the rest of the chain, which runs whatever fn does
openrac.hook("rac1.player.set_planar_velocity", function(original, angle)
original(angle) -- the game's own, or the mod before yours
openrac.log(`moving along {angle}`)
end)
openrac.after("rac1.player.advance_animation", function()
-- runs after the game has moved the animation on
end)openrac.on_frame: (fn: () -> ()) -> ()openrac.on_frame(fn): fn() after each game tick
--!strict
local swap = openrac.setting("pair") :: string
openrac.on_pad(function(port: number, report: buffer)
local buttons = buffer.readu16(report, 0) -- a bit is clear while its button is held
-- ... change buttons ...
buffer.writeu16(report, 0, buttons)
end)
openrac.on_frame(function()
-- after each game tick: 50 a second on PAL, 60 on NTSC-U
end)-- After each game tick (50 a second on PAL, 60 on NTSC-U).
openrac.on_frame(function()
ticks += 1
if ticks % (every * 50) == 0 then
openrac.log(`still here after {ticks} ticks`)
end
end)openrac.on_pad: (fn: (port: number, report: buffer) -> ()) -> ()openrac.on_pad(fn): fn(port, report) for each pad report before the game reads it; report is a buffer you may change
--!strict
local swap = openrac.setting("pair") :: string
openrac.on_pad(function(port: number, report: buffer)
local buttons = buffer.readu16(report, 0) -- a bit is clear while its button is held
-- ... change buttons ...
buffer.writeu16(report, 0, buttons)
end)
openrac.on_frame(function()
-- after each game tick: 50 a second on PAL, 60 on NTSC-U
end)-- Each pad report, before the game reads it: `report` is a buffer the script may change (rac1-mods'
-- button-swap shows its layout). Uncomment to see the buttons.
-- openrac.on_pad(function(port: number, report: buffer)
-- openrac.log(`port {port}: buttons {buffer.readu16(report, 0)}`) -- a bit clear for a button held
-- end)openrac.read: (kind: MemoryType, address: number) -> numberopenrac.read(type, address), openrac.write(type, address, value): game memory; type is u8 i8 u16 i16 u32 i32 f32; addresses in game RAM only (below 32 MB)
openrac.write: (kind: MemoryType, address: number, value: number) -> ()openrac.read(type, address), openrac.write(type, address, value): game memory; type is u8 i8 u16 i16 u32 i32 f32; addresses in game RAM only (below 32 MB)
openrac.fields: Fieldsopenrac.fields: game state by name, read and written as plain values: openrac.fields.bolts, openrac.fields.hero.position.x (experimental: see the reference)
openrac.key: (bind: string) -> booleanopenrac.key(bind), openrac.pressed(bind) (1.2): whether a key, mouse button or wheel direction is held, or went down since the last tick, by its name: openrac.key("KeyW"), openrac.pressed("MouseLeft"), openrac.pressed("WheelUp") (settings)
openrac.pressed: (bind: string) -> booleanopenrac.key(bind), openrac.pressed(bind) (1.2): whether a key, mouse button or wheel direction is held, or went down since the last tick, by its name: openrac.key("KeyW"), openrac.pressed("MouseLeft"), openrac.pressed("WheelUp") (settings)
openrac.mouse: () -> (number, number, number, number)openrac.mouse() (1.2): dx, dy, wheel_x, wheel_y: the mouse's motion since the last tick and its wheel in notches
openrac.window: () -> (boolean, boolean, boolean)openrac.window() (1.2): focused, captured, overlay: the window has the keyboard; the cursor is captured; OpenRAC's overlay has the input
openrac.pointer: () -> (number, number, boolean)openrac.pointer() (1.3): x, y, inside: the cursor over the game's picture, 0..1 each way (x * 512, y * 416 is the game's 2D pixel), and whether it is over it
openrac.take_input: (...("keyboard" | "mouse")) -> ()openrac.take_input("keyboard") (1.2): while your scripts first run: the port's own keyboard table stops pressing pad 0's buttons, so your on_pad presses them
openrac.capture_mouse: (capture: boolean) -> ()openrac.capture_mouse(on) (1.2): hide the cursor and keep it in the window (relative motion), or let it go
openrac.pvars: (moby: number) -> { [string]: number }?openrac.pvars(moby): with the behaviour catalogue: the per-class variables of the moby at that address, by name (experimental)
require: (name: string) -> anyrequire(name): another .luau file of your mod's own folder: require("util"), require("lib/util")
type MemoryType = "u8" | "i8" | "u16" | "i16" | "u32" | "i32" | "f32"type Fields = {
hero: {
position: {
x: number, -- Where the hero is (gHeroPos).
y: number,
z: number,
},
facing: number, -- The way he faces (rot.z), in radians.
move_yaw: number, -- Where the stick moves him: the stick's angle plus the camera's yaw.
state: number, -- read-only; His state: 0 standing, 2 running, 7 jumping, ...
},
bolts: number, -- The player's bolts.
}rac1.pad.process_report (Pad_ProcessReport) void(u32,u32,i32): One pad report into the pad's state (PadState, at pad), each poll, before the tick reads it.
type Pad_ProcessReportFn = (pad: number, report: number, size: number) -> ()rac1.player.update_motion_input (Hero_UpdateMotionInput) void(): The stick read into the hero, early in his update each tick.
type Hero_UpdateMotionInputFn = () -> ()rac1.player.turn_to_heading (Hero_TurnToHeading) void(i32,f32,f32,f32): Eases the hero's facing toward his move yaw.
type Hero_TurnToHeadingFn = (mode: number, accel: number, damping: number, limit: number) -> ()rac1.player.set_planar_velocity (Hero_SetPlanarVelocity) void(f32): The hero's velocity at his ground speed along an angle; an angle above pi means his facing.
type Hero_SetPlanarVelocityFn = (angle: number) -> ()rac1.camera.follow.apply_overrides (FollowCamera_ApplyOverrides) void(u32): The follow camera's per-tick requests from the hero's state; the camera moby's update calls it before it places the eye.
type FollowCamera_ApplyOverridesFn = (camera: number) -> ()rac1.player.advance_animation (Hero_AdvanceAnimation) void(): Ratchet's animation moved on by his animation speed, once a tick after his state's update.
type Hero_AdvanceAnimationFn = () -> ()rac1.item.give (Item_Give) void(i32,i32): Gives the player item id (GiveItem): sets its owned and obtained bytes, shows its banner, gives its starting ammo, marks it bought in the vendor's stock and adds it to the quick select; equip non-zero also puts it in hand. Every item the game gives goes through it but the Persuader's.
type Item_GiveFn = (id: number, equip: number) -> ()rac1.planet.unlock (Planet_Unlock) void(i32): Unlocks a planet's coordinates (UnlockPlanet): its flag (one byte per level), the galaxy list, and its banner unless it is the planet the player is on.
type Planet_UnlockFn = (planet: number) -> ()rac1.vendor.init_level_stock (Vendor_InitLevelStock) void(): The vendor's stock at each level start (VendorStockLevelInit): gives the Bomb Glove while the player has none, keeps the stock's known items and adds the level's new weapon.
type Vendor_InitLevelStockFn = () -> ()rac1.vendor.build_item_list (Vendor_BuildItemList) void(i32): The vendor menu's list (VendorBuildItemList), from the stock and the items owned: each time the menu opens and after each purchase. skip_unflagged leaves out what is for sale (the menu opened from the pause screen).
type Vendor_BuildItemListFn = (skip_unflagged: number) -> ()rac1.gold_bolt.count_collected (GoldBolts_CountCollected) i32(): The gold bolts collected, at most 40 (count_nonzero_entries_up_to_40): what the pause screen shows; the unspent ones are this less 4 per gold weapon.
type GoldBolts_CountCollectedFn = () -> numberrac1.player.take_damage (Hero_TakeDamage) void(i32): Takes hit points off the hero's health (Hero_TakeDamage): at most one a call, none for 0, then the HUD's health bank. Every hit the hero takes goes through it but the instant kills (falls, crushers), which set his health to 0.
type Hero_TakeDamageFn = (amount: number) -> ()rac1.combat.get_hit (Moby_GetHitMessage) u32(u32,i32,i32): The hit pending on moby (MobyGetHitMessage): the record of the last hit posted to it (+0x20 the attacker, +0x24 the flags, +0x2C the damage, an f32) when its flags match mask, else 0, dropping the hit unless keep. How the hero and every enemy read the hits they take, each tick, so a hit may be read more than once.
type Moby_GetHitMessageFn = (moby: number, mask: number, keep: number) -> numberrac1.weapon.take_ammo (Weapon_TakeAmmo) i32(i32,i32): Takes amount of ammo from item for a shot (TakeAmmo; -1 is the item in hand): 1 when taken, 0 when there is too little. An item with no ammo limit takes none and gives 1.
type Weapon_TakeAmmoFn = (item: number, amount: number) -> numberrac1.moby.record_death (Moby_RecordDeath) void(u32,i32,i32): Records a placed moby's death or a crate's break (SetDeathBits): its save id's killed bits (none for a save id below 0), its mission, then drops its bolts: about the s16 at +0xB4 (or what a tallied source has left of +0xB6), which it then clears.
type Moby_RecordDeathFn = (moby: number, flags: number, index: number) -> ()rac1.level.load_gameplay (Level_LoadGameplay) u32(i32): Loads a level's gameplay file into the level heap (LoadLevelGameplay), once per level start: the settings, the placed mobys and their pvars, the spare moby pool, the paths and the rest; returns the heap cursor. After it, every placed moby exists and none has run.
type Level_LoadGameplayFn = (full: number) -> numberrac1.player.approach_speed (Hero_ApproachSpeed) void(f32,f32): Eases the hero's ground speed (+0x194, the speed Hero_SetPlanarVelocity moves him at) toward his target speed (+0x190) by up when below it and by down when above, each tick he moves on foot, in every level.
type Hero_ApproachSpeedFn = (up: number, down: number) -> ()rac1.moby.<class>.update (experimental): (moby: number) -> (), or () -> () where it gets no moby.
type MobyUpdateFn = (moby: number) -> ()Generated from sdk/types/rac1.d.luau at sdk-v1.3.0. Edit this page or report a problem.