Weapon loadout
The player weapon as data: combo with a chain window, a special on cooldown, a dash attack, buffered presses, weapon modifiers and the bare-hands fists; settings, the weapons collection, services, events, hooks.
The player's melee weapon in your game (weapons.loadout) #
What it is for #
weapons.loadout describes the weapon in the player's hands as data, in the weapons collection. Pressing attack plays the next step of the combo if the previous step ended less than comboWindow seconds ago, otherwise the first. special plays the special when its cooldown is 0. An attack during a dash, or within dashAttackWindow seconds after it, plays the weapon's dash attack. A press made while a strike is running is remembered for buffer seconds. Each step is a timed strike handed to combat.melee; the kit aims and lunges through character.motor when a kit serves it. Boons adjust range, speed and cooldowns through weapon modifiers. The bare hands are the built-in weapon fists. The kit has no display module. See also the combat guide.
Turn it on #
gripforge_gamekit_install { project_id: "gkp_…", id: "weapons.loadout" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "weapons.loadout",
config: { "weapon": "sword", "buffer": 0.18, "aimSnap": true } }
It requires input.actions, combat.melee and game.engine, and registers the attack and special actions itself. With the fists weapon it also needs character.control and combat.damage. Without documents in weapons, the default weapon ember_blade is used.
First example #
<!-- test: kits/weapons_loadout/tests/weapons_loadout.rs::an_attack_strikes_and_the_next_one_inside_the_window_is_the_next_step --> A sword with a three-step combo and a special with a cooldown:
{ "id": "sword", "name": "Sword",
"attack": { "comboWindow": 0.3, "combo": [
{ "shape": "arc", "range": 2.0, "arc": 100, "damage": 10, "windup": 0.05, "active": 0.05, "recovery": 0.1 },
{ "shape": "arc", "range": 2.5, "arc": 100, "damage": 12, "windup": 0.05, "active": 0.05, "recovery": 0.1 },
{ "shape": "arc", "range": 3.0, "arc": 100, "damage": 20, "windup": 0.05, "active": 0.05, "recovery": 0.1 } ] },
"special": { "shape": "circle", "range": 3, "damage": 25, "windup": 0.1, "active": 0.1, "recovery": 0.1, "cooldown": 1.5 },
"dashAttack": { "shape": "line", "range": 4, "width": 1, "damage": 7 } }
With weapon: "sword", use("attack") returns true and emits weapon.used with step 0; once the strike ended, an attack inside the 0.3 s window plays step 1, then 2, then the combo wraps to 0. weapon.combo follows the step.
Settings and defaults #
| Setting | Type | Default | Meaning |
|---|---|---|---|
weapon | string | ember_blade | id of the equipped weapon (fists for the bare hands) |
buffer | number (s) | 0.18 | how long a press is remembered while the previous strike finishes |
aimSnap | boolean | true | turn toward the aim target before striking |
aimSnapCone | number (deg) | 120 | cone in which the aim snap looks for a target |
dashAttackWindow | number (s) | 0.25 | seconds after a dash during which attack plays the dash attack |
weaponLayers | object | {} | animation layer of the strikes, by weapon id |
fists | object | see below | the bare hands |
A strike is { shape: arc | circle | line, range, arc?, width?, offset?, damage, windup, active, recovery, knockback?, lunge?, tags? }. A weapon is { id, name, slot?, attack: { combo[], comboWindow }, special: Strike & { cooldown }, dashAttack? }; the special cooldown is special.cooldown times the cooldownMult of the modifiers. A weapon without a combo or a special is left out.
fists: damage 16, range 0.85, arc 65, windup 0.18, active 0.1, recovery 0.32, comboWindow 1.15 (left jab, then right cross), inputBuffer 0.18, knockback 0.24, assaultHeat 1, showDamageNumbers false. The fists have no special.
Services, events, variables #
weapons.loadout(service):equipped(),weapons(),equip(id)(resets combo and cooldown),use("attack" | "special")(false while busy, on cooldown, or when the player cannot act),busy(),cooldown(),addModifier({ id, owner, on, rangeMult?, arcAdd?, speedMult?, cooldownMult? })whereonisattack,specialorall,removeModifier(id).combat.unarmed(service):punch(source?),cancel(source?),busy(source?), the fists for the player or a scripted actor.- Events:
weapon.used(entity,weapon,action,step,strike),weapon.equipped,combat.unarmed.started. - Variables:
weapon.id,weapon.name,weapon.special.cooldown,weapon.combo(-1 when idle). - Content: action
equip_weapon, and the archetype componentloadout { weapon }on the player.
Hooks and modding #
| Hook | Payload | What a tap may do |
|---|---|---|
weapon.use | entity, weapon, action, step, damage, knockback, lunge, rangeMult, arcAdd, speedMult, cooldownMult | change those values, or cancel the step |
weapon.equip | weapon, from | cancel: equip answers false |
unarmed.punch | source, hand, damage, range, arc, windup, active, recovery, knockback | change them, or cancel (no punch, combo stays) |
The weapons registry lets a document { "id": "new", "extends": "sword" } inherit and patch an entry. The settings and the collection are moddable.
When it does not do what you expect #
useanswers false: a strike is under way (the key buffers a press,usedoes not), the special is cooling down, or the player is dead or out of play.- Nothing is played:
combat.meleeis missing. - The strike ignores the target: no
character.motorserves aim and lunge, so the body yaw is used. - The weapon is not what you set: its id is unknown, so the first weapon is used.
Limits #
Melee only; guns are weapons.arsenal and combat.ranged. Weapon modifiers are not saved: the kits that own them add them again.
Versions #
Kit version 1.7.0 (animation layers); the fists, formerly combat.unarmed, joined in 1.6.0.