Fights
Stabbing, shooting and fist fights played on the bodies: Actor:wield, Actor:strike, Actor:flee and the moves in Combat/*.json.
A fight is played one exchange at a time. One person strikes (a stab, a slash, a swing, a punch, a kick, a shove, a shot) and the other answers at the same moment: they cover, dodge, cower, beg or run. When the blow lands, a wound opens on the body. The scene decides who wins and what happens next. It chains the exchanges with no pause between them and only stops to ask the player when something changes: they fight back, they run. Nothing on screen looks like turns.
ul_Arms uses this for Attack them with a weapon and Pull a weapon on them. Any scene can do the same.
In a scene
lualocal me = UL.me()
me:wield("Rooms/Sets/GenCore/Pieces/GenCore/Product_Knife_Kitchen.glb") -- the knife in the right hand
local seconds = me:strike(target, {
move = "stab", -- an id from Combat/*.json
hit = true, -- false: the blow finds nothing
wound = { bone = "spine2", side = "front", severity = 0.65, kind = "Stab" },
})
Game:Hold(seconds) -- let the exchange play
target:flee(me, 900) -- they run, as far as the room lets them
me:wield() -- put it away
me:standDown()
| Call | What it does |
|---|---|
Actor:wield(piece, hand?) |
Puts a room piece (any mod's .glb) in the hand, "r" (the default) or "l". The hand closes on it the way it closes on things in animation clips: a hold set in the Held Check level wins, otherwise a grip is guessed from the piece's shape. wield() with no piece puts it away. |
Actor:strike(target, options) |
Plays one exchange and returns how many seconds it takes (nil for an unknown move). If the two stand further apart than the move's reach_cm, the striker walks up first. |
Actor:flee(from, cm?) |
They run from from, cm away (900 by default), at a running pace. |
Actor:standDown() |
The fight is over for them: they drop the guard and the room's life gets them back. This also happens on its own after 30 seconds without a blow. |
Options for strike:
| Option | Meaning |
|---|---|
move |
A move id. |
answer |
What the target does: an answer id. Left out, a blow that misses plays the move's miss_answer, and a blow that lands is answered by the wound itself. |
hit |
false for a miss. Defaults to true. |
wound |
{ bone, side, severity, kind }. The wound opens at the moment the blow lands, as Actor:addWound would open it. The body then reacts the way wounds make it react: it holds the wound, or goes down. |
knockOut |
A cause from Reactions/*.json unconscious.causes: they are out cold at the moment of the blow. |
A blow that lands with no wound and no knockOut (a fist, a shove) plays the reel answer. A gun move fires at the hit moment, with a flash and its sound. A shot that misses leaves its hole in whatever is behind the target, the same hole Actor:shootAt leaves.
Combat/*.json
Moves and answers are mod data. Every enabled mod's Combat/*.json is read in load order, and a later mod's id replaces an earlier one's. The files are read again at every exchange, so a change shows up the next time someone strikes.
json{
"moves": {
"stab": {
"clips": [ { "clip": "newMotions/curation/gut_punch_1_F.ulanim", "from": 0.0, "to": 0.75, "hit_at": 0.39 } ],
"reach_cm": 60,
"miss_answer": "dodge"
},
"shoot": {
"clips": [ { "clip": "newMotions/curation/attack_12_F.ulanim", "from": 0.15, "to": 0.45, "hit_at": 0.3 } ],
"reach_cm": 400, "after": "hold",
"shots": 1, "sound": "gunshot.wav", "flash": true
}
},
"answers": {
"cover": { "clips": [ { "clip": "newMotions/curation/attack_14_F.ulanim", "from": 0.2, "to": 0.6 } ], "lead": 0.35 },
"cower": { "activity": "fear_stand", "lead": 0.6 },
"dodge": { "clips": [ ... ], "lead": 0.25, "step_cm": 35 },
"flee": { "flee_cm": 900, "lead": 0.4 },
"reel": { "activity": "hurt_stand", "seconds": 2.5 }
},
"flee": { "activity": "flee_run", "speed": 2.6, "rate": 1.0 }
}
| Field | Meaning |
|---|---|
clips |
One is drawn at each play. clip is a path under a mod's Animations/. from and to mark the part of the clip that plays once, and hit_at the moment the blow lands. All three are fractions of the whole clip, so one punch can be taken from a long combo clip. sex: "f" or "m" to prefer the clip for bodies of that sex. |
reach_cm |
How far apart the two stand for the move, from one actor to the other. |
after |
What the striker does once the move is over: an activity looped, "hold" to keep the last frame (the arm still out, the gun still on them), or nothing for the rest idle. |
shots, shot_gap, sound, flash |
A gun: the number of shots from the hit moment and the seconds between them. sound is a file under a mod's Audio/. flash lights the muzzle (the held piece's top end) for one frame. |
lead |
How many seconds before the hit the answer starts: an arm comes up before the blade arrives. |
activity |
An answer played as an activity, tried as <id>_f / <id>_m first. seconds is how long it lasts before after. |
step_cm |
The target steps back that far from the striker (a dodge). |
flee_cm |
The target runs that far instead. |
The moves ul_Arms ships are stab, slash, swing, punch, kick, shove, aim, shoot and shoot_twice. The answers are cover, dodge, reel, cower, beg and flee. Each weapon class strikes with its own move (Arms.MOVE), and each class has a piece to hold (Arms.HELD; a weapon's own held and move fields win over its class's).
The clips are animations already in the game that fit the moves. Dedicated clips for a two-person stab, a pistol shot with recoil and a hit reaction have not been made yet. When they exist, adding them to a move's clips is enough.