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.

Source: module-kits:kits/weapons_loadout/usage/en.mdView Markdown source

Also read offline — CLI: grip-forge docs weapons-loadoutMCP tool: gripforge_docs { "topic": "weapons-loadout" }

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 #

SettingTypeDefaultMeaning
weaponstringember_bladeid of the equipped weapon (fists for the bare hands)
buffernumber (s)0.18how long a press is remembered while the previous strike finishes
aimSnapbooleantrueturn toward the aim target before striking
aimSnapConenumber (deg)120cone in which the aim snap looks for a target
dashAttackWindownumber (s)0.25seconds after a dash during which attack plays the dash attack
weaponLayersobject{}animation layer of the strikes, by weapon id
fistsobjectsee belowthe 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? }) where on is attack, special or all, 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 component loadout { weapon } on the player.

Hooks and modding #

HookPayloadWhat a tap may do
weapon.useentity, weapon, action, step, damage, knockback, lunge, rangeMult, arcAdd, speedMult, cooldownMultchange those values, or cancel the step
weapon.equipweapon, fromcancel: equip answers false
unarmed.punchsource, hand, damage, range, arc, windup, active, recovery, knockbackchange 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 #

  • use answers false: a strike is under way (the key buffers a press, use does not), the special is cooling down, or the player is dead or out of play.
  • Nothing is played: combat.melee is missing.
  • The strike ignores the target: no character.motor serves 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.