Courage.Attach#
require("Courage.Attach") attaches J3D model resources to game actors. The attached bone hierarchy is grafted onto the actor's skeleton and rendered each frame with optional joint remapping, shape visibility, and a positional/rotation offset.
Loading the module#
API#
| Function | Returns | Description |
|---|---|---|
Attach.attach(actor, arc, resIdx, opts?) | id or nil | Load arc (archive name) and attach resource index resIdx to actor. Returns an attachment id on success, nil on failure. |
Attach.detach(id) | (none) | Remove the attachment created by attach. |
Attach.detach_all(actor) | (none) | Remove every attachment registered to actor. |
Attach.is_ready(id) | boolean | true once the archive is loaded and the model is visible. |
Attach.set_shape_vis(id, matIdx, visible) | boolean | Show or hide material index matIdx on the attached model. |
Options table#
The fourth argument to Attach.attach is an optional table with these fields:
| Field | Type | Description |
|---|---|---|
base | integer | Joint index on the attachment skeleton to use as the root (default -1 = skeleton root). |
joints | { [n]: { dstJoint, srcJoint } }[] | Array of { dst, src } pairs to remap skeleton joints. dst is the actor joint; src is the archive joint. |
hide_mats | integer[] | Material indices to hide immediately after attaching. |
show_mats | integer[] | Material indices to force-show immediately after attaching. |
offset | { x, y, z } | Positional offset applied relative to the attachment joint, in game units. |
rot | { x, y, z } | Euler rotation offset in degrees applied after offset. |
Example#
local Hook = require("Augment.Hook")
local Attach = require("Courage.Attach")
local MyMod = mod("custom-hat")
local s_attachId = nil
-- Hook the Link actor's execute so we have a pointer to it
Hook["daAlink_c::execute"]:before(function(ctx)
if s_attachId ~= nil then return end
local link = ctx.self
s_attachId = Attach.attach(link, "custom_hat", 0, {
base = 5, -- attach to joint 5
offset = { 0, 20, 0 }, -- 20 units above the joint
rot = { 0, 90, 0 }, -- rotated 90 degrees around Y
})
end)
function MyMod:shutdown()
if s_attachId then
Attach.detach(s_attachId)
s_attachId = nil
end
end
return MyMod
import * as Hook from "Augment.Hook"
import * as Attach from "Courage.Attach"
declare function mod(name: string): any
const MyMod = mod("custom-hat")
let attachId: number | undefined
Hook["daAlink_c::execute"].before((ctx) => {
if (attachId !== undefined) return
const link = ctx.self
attachId = Attach.attach(link, "custom_hat", 0, {
base: 5,
offset: [0, 20, 0],
rot: [0, 90, 0],
})
})
MyMod.shutdown = function () {
if (attachId !== undefined) {
Attach.detach(attachId)
attachId = undefined
}
}
export = MyMod
Notes#
attachqueues an archive load. Pollis_ready(id)in yourtickbefore relying on the attachment being visible.- Calling
detach_all(actor)is convenient inshutdownwhen you don't track individual ids. - Material indices follow the J3D material order in the archive. Use a model viewer to find the correct index.
set_shape_visreturnsfalsewhenidis invalid or the index is out of range.
See also#
- Augment.Hook: hook actor lifecycle to get a valid actor pointer
- Augment.Mixin: read/write actor memory fields