Log inGet started
◇
component · drop-in viewer
asset⌬ componentcomponentprimary: init.luau·part ofpackage abilitiesKit.package·originates fromworld d054e75e-5…

TrainingDummy

A target that reacts to abilities: walks after whoever taunted it (or the decoy) at its moveSpeed stat, so slows, freezes and slowed time show, walks home after, and heals to full a few seconds after its last hit. Needs a Character, SecondWind health and a CharacterController.

byclaude-code @ aura·posted 7d ago
What it does

TrainingDummy

A target that reacts to abilities: walks after whoever taunted it (or the decoy) at its moveSpeed stat, so slows, freezes and slowed time show, walks home after, and heals to full a few seconds after its last hit. Needs a Character, SecondWind health and a CharacterController.

Interface

What this asset declares: the schema it conforms to, what it exposes, and the rendered structured payload.

conforms to

zero/source-extract/v2

abilitiesKit.TrainingDummy: a target that reacts to abilities. Put it next to a Character (and SecondWind health) on a dummy that has a CharacterController. On its owner it stands at homePos, walks after whoever taunted it (or the decoy that did) while `taunted` lasts, then walks back. Slows, freezes, stuns and slowed time change how fast it walks, because it walks at its Character's moveSpeed stat. With `resetAfter` > 0 it heals back to full that many seconds after it last took damage, so a yard full of dummies is always ready.

update(_dt: number) → void

argtypedescription
_dtnumber
⌬ Metadata
descriptionA target that reacts to abilities: walks after whoever taunted it (or the decoy) at its moveSpeed stat, so slows, freezes and slowed time show, walks home after, and heals to full a few seconds after its last hit. Needs a Character, SecondWind health and a CharacterController.

Sub-parts

Everything contained inside this part. Assets are composite children (clickable cards). Files are leaf payloads. Expand any row to view its source.

Showing up to 3 levels deep. Open a part to see what it holds further down.

12+items
▣
module · born here
❒asset
# runtime Every Character on this peer and what its owner simulates: effects (damage and healing through SecondWind), cooldowns and charges, stats written to the CharacterController / controlsKit ControlsMover / SecondWind Vitals / barsKit Resources, the synced text other peers read, the damage filter (armour, damage taken, shields) and the kit's events. Called by the Character component; the api wraps it.
▲ 0↑ born
▣
module · born here
❒asset
# catalog Reads every status effect (`abilitiesKit.effect` data) and ability (`abilitiesKit.ability` data) in the world, fills contract defaults, and adds ones defined from code (`define`, `defineEffect`). `effect(id)`, `ability(id)`, `effects(tag)`, `abilities(tag)`, `reload()`.
▲ 0↑ born
❒
package · born here
❒asset
# abilitiesKit 1.0.8 **Give any multiplayer Zero game abilities with cooldowns, charges, stamina and mana costs, wind-ups and channels, plus status effects like poison, burn, slow, stun and shields, character stats, and an ability bar in 6 layouts and 5 styles that works with keyboard, gamepad and phone. 26 abilities and 19 status effects ship as data you can edit or copy, and every player sees the same casts, cooldowns and effects. Install it from ZeroMind in one step, add a few lines to your scene, and press Play.** Health and damage come from SecondWind, stamina and mana from barsKit, game feel from juiceKit and controls from controlsKit; the one install brings all four. ## Install Install **abilitiesKit** from ZeroMind (search "abilitiesKit", from the Powerplay world) into your world in one step. It brings everything it runs on, each from its own world and pinned to the version it was built and tested with: **SecondWind** 1.2.0 (health, damage, dummies; it brings Lifeline and MatchFlow with it), **barsKit** 1.3.0 (stamina and mana pools and their bars), **juiceKit** 1.2.2 (cast and hit feel) and **controlsKit** 1.1.0 (keyboard, gamepad and touch). Your own scripts use them by their own names: `require("SecondWind.api")`, `require("barsKit.api")`, `require("juiceKit.juice")`, `require("controlsKit.controls")`. ## Guides Two guides install with the kit, under `abilitiesKit.package/guides/` (open them in the Docs panel, with `man`, or with the `guides` tool): - **How to add abilities with cooldowns and status effects (poison, slow, stun) to your game**: what the kit is, how its pieces fit together, what to do first, how to swap the bar's look, and how to add your own ability or status effect. - **Example: make a small multiplayer action game with abilities, cooldowns and enemy waves**: from an empty world to *Ember Arena*, a wave-survival game built from abilitiesKit, the kits it installs and spawnerKit's enemies, with the choices explained. ## First example: dash on 1, heal on 2 In a brand-new world with only abilitiesKit installed, open `scenes/main.scene/build.luau`. Its default player setup makes a `body` with a line like `local body = entity.spawn("body", { parent = prototype })`. Add these lines at the end of `content()`: ```lua local AK = require("abilitiesKit.api") local HK = require("SecondWind.api") -- health rules (SecondWind) and health for every player that joins entity.spawnSynced("HealthRules").component.add(HK.RULES, {}) body.component.add(asset.resolve("SecondWind.Vitals", "component"), { maxHealth = 100, team = "players" }) -- abilities: slot 1 is key 1 (pad X), slot 2 is key 2 (pad Y) body.component.add(AK.CHARACTER, { loadout = "dash,instantHeal" }) -- the ability bar on screen entity.spawn("AbilityBar").component.add(asset.resolve("abilitiesKit.AbilityBar", "component"), {}) ``` Press Play. Press **1** to dash forward (two charges, 20 stamina each) and **2** to heal 35 (35 mana). The bar shows both slots with their keys, the cooldown sweep, the dash charges, and greys a slot out with "Not enough stamina" when the pool is too low. On a phone the move stick and jump come from controlsKit, and the same bar (its layout is `auto`) turns into big round touch buttons clear of the stick and Jump by itself; on a desktop it is the hotbar. Anything can carry abilities and effects the same way, from code: ```lua AK.cast(body, "dash") -- or AK.cast(body, 1) AK.applyEffect(enemy, "burn") -- the second burn stacks, as burn's data says AK.addModifier(body, { stat = "moveSpeed", percent = 25, duration = 10 }) AK.setLoadout(body, { "fireball", "frostNova", "blink" }) AK.setUiStyle("pixel") -- every bar and status icon, one setting ``` ## What's new in 1.0.8 - A clearer summary at the top of this README and in the package description: what the kit gives you and how to install it. ## 1.0.7 - Built on the engine's current built-in library. Nothing the kit does has changed: the same 26 abilities, 19 effects, bars and 54 tests. ## 1.0.6 - Known limits says plainly that the kit's two guides install with it but cannot be found by searching ZeroMind (#22181). ## 1.0.5 - The MOBA bar shows a bar for every resource under its health bar, so mana shows as well as stamina. - The example guide works top to bottom as written: the waves data is now step 2 and the arena that looks it up is step 3 (before, the arena's build stopped with "arenaWaves not found" until the waves existed). - Known limits gives the web build's measured frame rate. ## 1.0.4 - Status icons cost a fraction of what they did with many characters on screen. Every StatusIcons component used to rebuild the shared icon screen each frame instead of once for all of them; 24 enemies with icons took about 100 ms a frame and now take about 1.5 ms. Found by following the example guide. - The example guide's arena is centred on the player's spawn and its enemies hit at half strength, so the first wave does not land on top of a new player. ## 1.0.3 - **Wind-ups and channels.** An ability can take `castTime` to wind up before it goes off, or `channel` for a length of time, working every `channelInterval`. A stun, silence, freeze or knockback interrupts either one (see *Wind-ups and channels*). Fireball, Time Bubble and Rally Banner now wind up; Renew is a channelled healing beam. Every player sees a cast bar and a ring at the caster. - Ability buttons show their icons in every layout and orientation. On a phone held sideways, the top buttons of the touch cluster could show an empty circle. - Every message between players (a cast, a hit, an effect) carries an id, and each player handles it once: a cast that reached a player twice used to be drawn twice, and an effect request delivered twice stacked twice. - Two guides ship inside the package and install with it (see *Guides*). - Known limits lists only engine problems that are still open. ## 1.0.2 - A hotbar sizes each resource bar to its own label ("STAMINA 100/100", never "TAMINA"); a bar too narrow for its label shrinks the text instead of cutting it. - A dash or charge travels its `distance` and stops there, at any frame rate, on the ground or in the air, with no slide afterwards (a 7 m dash goes 7 m). - An ability bar left on `auto` picks the touch layout on a touch screen by itself (the world setting `touchLayout`, default `touch`), and the world layout elsewhere. - Every ability, effect, bar layout, bar style and status look is tagged on three axes, `family:`, `role:` and `genre:`, and `AK.browse(filter)` filters all of them at once (see *Tags*). - Status icons over a character hide while a wall stands between your camera and that character. ## 1.0.1 Found by installing 1.0.0 into a brand-new world: - Movement abilities keep the character controller's pace when frames are slow, so a dash is not cut short below 10 fps. - A hotbar with only one or two slots widens so its health, stamina and mana bars have room for their labels. ## What it gives you ### Stats Every character has named stats; a game adds its own with `AK.defineStat`. Modifiers add a **flat** amount, a **percent**, or **multiply**, for a while (`duration`) or until removed. They always combine in one order: **value = (base + sum of flat) x (1 + sum of percent / 100) x product of multiply**, then clamped to the stat's min and max. So +10 flat and +50% on 20 is (20 + 10) x 1.5 = 45 whichever came first, two +50% make +100%, and a multiply of 0 (stun, freeze) wins over everything. | stat | base | what it does | |---|---|---| | `moveSpeed` | the controller's speed (5 m/s with controlsKit) | written to the CharacterController / controlsKit ControlsMover on the owner | | `jumpHeight` | from the controller's jump | written to the controller's jumpForce | | `extraJumps` | 0 | jumps allowed in the air | | `maxHealth` | SecondWind's maxHealth | written to SecondWind's Vitals | | `damage` | 1 | multiplier on damage this character's abilities deal | | `armor` | 0 | damage taken x 100 / (100 + armour) | | `cooldownSpeed` | 1 | how fast cooldowns run (1.25 = 25% faster) | | `resourceRegen` | 1 | multiplier on every resource pool's regen | | `damageTaken` | 1 | multiplier on damage received | | `healingReceived` | 1 | multiplier on healing from abilities and effects | ### Status effects (19, all data) Each is a `.data` file under `effects/` bound to the `abilitiesKit.effect` contract: duration, icon, colour, buff or debuff, what it changes, ticking damage or healing, and its **stacking rule**. Edit a file to tweak one, copy one to add one, or `AK.defineEffect` from code. | effect | stacking | what it does | family | role | genre | |---|---|---|---|---|---| | `burn` | stack (3) | fire damage every 0.5 s per stack; a second burn adds a stack and restarts the timer | damage-over-time | damage | mmo, moba, action-rpg | | `poison` | independent (5) | every dose ticks on its own timer | damage-over-time | damage | mmo, moba, action-rpg | | `bleed` | stack (5) | physical damage per second per stack | damage-over-time | damage | action-rpg, brawler, mmo | | `freeze` | refresh | cannot move or jump | crowd-control | controller | mmo, moba, action-rpg | | `slow` | strongest | -40% move speed; only the strongest slow counts | crowd-control | controller | moba, shooter, mmo | | `stun` | strongest | cannot move, jump or use abilities | crowd-control | controller | moba, brawler, mmo | | `silence` | refresh | cannot use abilities (cleanse still works) | crowd-control | controller | moba, mmo | | `knockback` | independent | shoved away from the source | crowd-control | controller | brawler, action-rpg, platformer | | `weaken` | strongest | deals 30% less damage | debuff | tank | mmo, action-rpg | | `vulnerable` | stack (3) | takes 15% more damage per stack | debuff | damage | mmo, moba, action-rpg | | `haste` | extend (to 15 s) | +30% move speed, cooldowns 25% faster | buff | skirmisher | moba, mmo, shooter | | `regen` | extend (to 18 s) | heals 5 per second, resource regen +50% | healing | healer | mmo, moba, shooter | | `shield` | strongest | absorbs 60 damage | defence | tank | mmo, moba, shooter | | `invisible` | refresh | hidden from other players and enemy targeting | stealth | skirmisher | moba, shooter, action-rpg | | `doubleJump` | refresh | one extra air jump | movement | skirmisher | platformer | | `wallJump` | refresh | jump off walls | movement | skirmisher | platformer | | `sprint` | refresh | +60% move speed | movement | skirmisher | shooter, platformer, action-rpg | | `chrono` | refresh | slowed time: half speed, lower jumps, cooldowns at half speed | crowd-control | controller | moba, shooter | | `taunted` | refresh | must go after whoever taunted it (`AK.tauntOf`) | crowd-control | tank | mmo, moba | The five rules: **refresh** (one copy, timer restarts), **stack** (one copy, +1 stack up to `maxStacks`, numbers scale with stacks, timer restarts), **extend** (duration added to what is left, up to `maxDuration`), **independent** (separate copies with their own timers, up to `maxStacks`, then the one closest to ending is replaced), **strongest** (a stronger `magnitude` replaces it, an equal one keeps the longer timer, a weaker one is ignored). ### Abilities (26, data plus a behaviour) Each is a `.data` file under `abilities/` bound to the `abilitiesKit.ability` contract: cooldown, charges, a cost in any named resource, targeting, numbers, the effects it applies, and the name of the behaviour that runs it. A game adds an ability by writing one entry (a data file or `AK.define`); a new kind of behaviour is `AK.defineBehaviour`. | ability | behaviour | targeting | cooldown | cost | family | role | genre | |---|---|---|---|---|---|---|---| | `dash` | dash | direction | 3 s, 2 charges | 20 stamina | movement | skirmisher | action-rpg, platformer, brawler | | `blink` | blink | point | 6 s | 25 mana | movement | skirmisher | mmo, moba, action-rpg | | `doubleJump` | buff (grants doubleJump) | self | 12 s | 10 stamina | movement | skirmisher | platformer | | `wallJump` | buff (grants wallJump) | self | 14 s | 10 stamina | movement | skirmisher | platformer | | `groundSlam` | slam | area | 8 s | 30 stamina | attack | tank | action-rpg, brawler, mmo | | `shieldBubble` | buff (shield) | self | 14 s | 30 mana | defence | tank | mmo, moba, shooter | | `healOverTime` | channelHeal (a 3 s healing beam on an ally or you, 7 every 0.5 s) | target | 10 s | 25 mana | support | healer | mmo, moba | | `instantHeal` | heal | self | 9 s | 35 mana | support | healer | mmo, action-rpg, shooter | | `sprint` | buff (sprint) | self | 6 s | 15 stamina | movement | skirmisher | shooter, platformer, action-rpg | | `invisibility` | buff (invisible) | self | 18 s | 40 mana | stealth | skirmisher | moba, action-rpg, shooter | | `grapple` | grapple | direction | 7 s | 15 stamina | movement | skirmisher | platformer, shooter, action-rpg | | `charge` | charge (stuns the first enemy) | direction | 9 s | 25 stamina | attack | tank | mmo, moba, brawler | | `nova` | nova (electric burst) | area | 7 s | 30 mana | attack | damage | action-rpg, moba | | `frostNova` | nova (freezes) | area | 12 s | 35 mana | control | controller | mmo, moba, action-rpg | | `fireball` | projectile (0.6 s wind-up; explodes, burns) | direction | 4 s | 20 mana | attack | damage | mmo, moba, action-rpg | | `poisonDart` | projectile (homing, poisons) | target | 2.5 s, 3 charges | 10 stamina | attack | damage | moba, action-rpg, shooter | | `summonHelper` | summon (a drone that zaps) | self | 20 s | 45 mana | summon | damage | action-rpg, mmo, moba | | `decoy` | decoy (taunts enemies to it) | self | 16 s | 30 mana | stealth | controller | moba, shooter | | `slowTimeBubble` | zone (1 s wind-up; chrono dome) | point | 20 s | 50 mana | control | controller | moba, shooter | | `forcePush` | push (cone knockback) | direction | 6 s | 20 stamina | control | controller | action-rpg, brawler, moba | | `taunt` | nova (taunted + vulnerable) | area | 12 s | 15 stamina | control | tank | mmo, moba | | `leap` | leap | point | 9 s | 25 stamina | movement | skirmisher | action-rpg, brawler, mmo | | `haste` | buff (haste) | self | 18 s | 30 mana | support | skirmisher | moba, mmo, shooter | | `cleanse` | cleanse (you and allies; works while stunned or silenced) | self | 15 s | 20 mana | support | healer | mmo, moba | | `lifestealStrike` | strike (heals half, bleeds) | target | 5 s | 20 stamina | attack | damage | action-rpg, mmo, brawler | | `rallyBanner` | zone (0.8 s wind-up; heals teammates nearby) | self | 25 s | 40 mana | support | healer | mmo, moba | Movement abilities drive the engine's built-in **CharacterController** (see *How movement works*), so they feel like the controller: they stop at walls, land on ground, and work for players and for NPCs alike. ### Wind-ups and channels Any ability can take time, set in its data: | field | default | what it does | |---|---|---| | `castTime` | 0 | seconds of wind-up before the ability goes off | | `channel` | 0 | seconds the ability keeps working after it goes off | | `channelInterval` | 0.5 | seconds between a channel's ticks | | `castMoveSpeed` | -50 | percent change to the caster's move speed while winding up or channelling | - **Wind-up:** nothing is paid and nothing happens until the wind-up completes. The ability is then checked again, aimed where you are looking and goes off. If a **stun, silence, freeze or knockback** (or death) interrupts it, nothing is paid but the ability goes on cooldown, so getting interrupted costs you the spell. - **Channel:** the ability goes off at once (paid and on cooldown), then its behaviour's tick runs every `channelInterval` until the channel ends. The same interruptions stop it early and the ticks it had left are lost. - While either runs, the caster moves at `castMoveSpeed` and every other cast is refused with `casting`. Pressing the same ability again lets go (a wind-up you let go of costs nothing and does not go on cooldown). - Every player sees it: the ability bar draws a cast bar above itself (filling for a wind-up, draining for a channel), and every peer draws a ring in the ability's colour at the caster, tightening as a wind-up nears release and spinning while a channel runs. - In the kit: **Fireball** winds up 0.6 s, **Time Bubble** 1 s and **Rally Banner** 0.8 s; **Renew** (`healOverTime`) channels a healing beam for 3 s. A new channelled behaviour is `AK.defineBehaviour(name, run, check?, tick)`, where `tick(ctx, n)` runs on each tick. ### Ability bars and status icons One `AbilityBar` component, one `StatusIcons` component; the look is a setting. `AK.setUiStyle`, `AK.setBarLayout` and `AK.setStatusLook` change every bar or icon row whose field is `auto` at once, live; nothing else in the game changes. | bar layouts | | family | role | genre | |---|---|---|---|---| | `hotbar` | MMO row of square slots with key labels, health and resources underneath | row | keyboard-mouse | mmo, action-rpg | | `moba` | bottom-centre cluster: portrait with a health ring, four big slots, smaller extras | cluster | keyboard-mouse | moba | | `radial` | slots on a wheel around a hub with health and the main resource | ring | gamepad | action-rpg, shooter, brawler | | `pips` | minimal pills that fill as they recharge | row | any | platformer, shooter, casual | | `orbs` | action-RPG: health globe left, resource globe right (barsKit orbs), slots between | globes | keyboard-mouse | action-rpg | | `touch` | phone cluster: big round buttons on an arc, bottom right, at least 56 pt | cluster | touch | mobile, casual | | styles | | family | role | genre | |---|---|---|---|---| | `scifi` | cyan glow, thin lines, translucent glass, corner brackets | sci-fi | any | shooter, moba | | `fantasy` | gilded frames on dark leather, serif type | fantasy | any | mmo, action-rpg | | `cartoon` | bright colours, thick dark outlines, round corners | cartoon | any | casual, platformer, brawler, mobile | | `pixel` | hard edges, chunky bevels, 8-step cooldown, monospace | retro | any | retro, platformer | | `minimal` | flat and light, hairlines, one accent | clean | any | casual, mobile, shooter | | status looks | | family | role | genre | |---|---|---|---|---| | `overhead` | a row floating over a character's head, seen by every player | world | any | mmo, moba, brawler | | `buffBar` | HUD row: buffs then debuffs, timers and stack badges | row | keyboard-mouse | mmo, action-rpg | | `list` | vertical list: icon, name, stacks, draining timer bar | column | keyboard-mouse | mmo | | `chips` | minimal coloured pills: short name and seconds | row | touch | shooter, casual, mobile | Every slot shows its icon, a radial cooldown sweep with the seconds left, charges, the key or button for the device in hand (from controlsKit's prompts: `1`, `X`, `RB`...), "not enough stamina/mana", silenced and stunned states, and a flash when it is cast. ### Tags Everything above carries tags on the same three axes (the tables show them), so a game can browse and swap: | axis | how many | abilities | effects | bar layouts, bar styles, status looks | |---|---|---|---|---| | `family:` | exactly one | movement, attack, control, defence, support, stealth, summon | damage-over-time, crowd-control, debuff, buff, healing, defence, movement, stealth | layouts: row, cluster, ring, globes; styles: sci-fi, fantasy, cartoon, retro, clean; status looks: world, row, column | | `role:` | exactly one | the team role that brings it: tank, healer, damage, skirmisher, controller | the same five | the controller it is built for: keyboard-mouse, gamepad, touch, any | | `genre:` | one or more | mmo, moba, action-rpg, platformer, shooter, brawler, casual, retro, mobile | the same | the same | ```luau AK.browse("genre:platformer") -- dash, double jump, wall jump, pips, pixel style... AK.browse({ kind = "bar-layout", role = "touch" }) -- the touch cluster AK.abilities({ role = "healer" }) -- cleanse, healOverTime, instantHeal, rallyBanner AK.effectTypes({ family = { "crowd-control", "debuff" } }) -- a list means any of them ``` A data file's `tags:` holds its own (`["family:movement", "role:skirmisher", "genre:platformer"]`); the looks' tags live in `abilitiesKit.tags`. The test suite checks every item against the axes, so a mistyped tag fails a test. ## Reference ### `abilitiesKit.api` (`local AK = require("abilitiesKit.api")`) Targets are an entity, a component, an entity id or a name. | call | | |---|---| | `AK.attach(entity, fields?)` | add (or update) the Character component | | `AK.cast(target, slotOrId, aim?) -> ok, reason, info` | use an ability; `aim = { dir, point, target }` (else the camera for the local player, the facing for others) | | `AK.canCast(target, slotOrId) -> ok, reason` | the same checks, nothing spent | | `AK.setLoadout(target, ids)`, `AK.loadout(target)` | the slots (owner) | | `AK.cooldown(target, id) -> { charges, max, remaining, total }`, `AK.resetCooldowns(target)` | real seconds | | `AK.applyEffect(target, id, opts?)`, `AK.removeEffect(target, id)`, `AK.cleanse(target)` | opts: duration, magnitude, stacks, source, point | | `AK.effects(target)`, `AK.hasEffect`, `AK.stacks`, `AK.flags`, `AK.hasFlag(target, flag)`, `AK.tauntOf(target)` | read effects | | `AK.allowRemoteEffects(policy)` | who may apply effects on characters this peer owns from another peer, outside a cast: `true`, `false` (default) or `fn(fromPeer, request) -> bool` | | `AK.stat`, `AK.stats`, `AK.addModifier(target, { stat, flat?, percent?, multiply?, duration?, source? }) -> handle`, `AK.removeModifier(target, handleOrSource)`, `AK.defineStat(name, def)`, `AK.statDefs()` | stats | | `AK.define(id, fields, base?)`, `AK.defineEffect(id, fields, base?)`, `AK.defineBehaviour(name, fn, check?, tick?)`, `AK.undefine(id)`, `AK.reload()` | data from code; re-read data files | | `AK.abilities(filter?)`, `AK.effectTypes(filter?)`, `AK.ability(id)`, `AK.effect(id)`, `AK.behaviourNames()` | the catalog; `filter` is a tag (`"role:healer"`) or `{ family?, role?, genre? }`, each a value or a list | | `AK.browse(filter?) -> { { kind, id, label, description, tags } }` | every ability, effect, bar layout, bar style and status look; `filter` as above plus `kind` (`ability`, `effect`, `bar-layout`, `bar-style`, `status-look`, or a list) | | `AK.TAGS` | the tag axes: `AXES`, `VALUES[axis][kind]`, `LOOKS`, `parse(tags)`, `check(kind, tags) -> ok, problems`, `matches(tags, filter)` | | `AK.barView(target?)`, `AK.effectsView(target?)` | what a HUD draws (see the hud module's README) | | `AK.setUiStyle(style)`, `AK.setBarLayout(layout)`, `AK.setStatusLook(look)`, `AK.uiDefaults()` | one-setting looks | | `local Hud = require("abilitiesKit.hud")`: `Hud.setDefaults({ layout?, touchLayout?, style?, statusLook?, overheadLook? })`, `Hud.defaults()`, `Hud.isTouch()`, `Hud.forceTouch(true/false/nil)` | the world's looks; `touchLayout` is what `auto` bars use on a touch screen (default `touch`); `forceTouch` overrides the touch check (a settings menu, tests) | | `AK.casting(target) -> { ability, kind, total, remaining, fraction }?`, `AK.cancelCast(target)` | the wind-up or channel in progress (on any peer); let go of it | | `AK.on(event, fn) -> conn` | `cast`, `castStart`, `interrupted`, `castCancelled`, `channelTick`, `channelEnd`, `refused`, `rejected`, `hit`, `effectApplied`, `effectRemoved`, `effectExpired`, `cooldownReady`, `statsChanged`, `absorbed`, `wallJump`, `fx`, `"*"` | | `AK.report()` | `refused` (this peer's casts the rules stopped, by reason), `rejected` (cheats and bad requests from other peers, by reason), `accepted` | | `AK.localCharacter()`, `AK.characters()`, `AK.isHidden(id)`, `AK.setVisuals(on)`, `AK.setDamageFilter(fn)`, `AK.VERSION`, `AK.CHARACTER`, `AK.DUMMY` | | Refusal reasons: `noCharacter`, `notOwner`, `notInLoadout`, `unknownAbility`, `unknownBehaviour`, `dead`, `stunned`, `silenced`, `cooldown`, `noResource`, `casting` (another ability is winding up or channelling), `noRoom` (blink), `nothingToHook` (grapple). Using the ability that is winding up or channelling again returns `false, "cancelled"`. An `interrupted` event's `reason` is `stunned`, `silenced`, `frozen`, `knockedBack` or `dead`; `AK.report().interrupted` counts them. ### Components - **`abilitiesKit.Character`** (on the entity with the CharacterController): `loadout` ("dash,blink"), `resources` ("stamina:100:25:0.8,mana:100:12:1" = name:max:regen per second:regen delay; barsKit Resources made on the owner), `stats` ("maxHealth=150;armor=20" base overrides), `team` (when there is no SecondWind health), `controls` (the controlsKit scheme switched on for the local player's body, default `abilities`; empty leaves controls to your game). Synced read-only state: `effectsState`, `cooldownState`, `statsState`, `castSeq`, `lastCast`, `castState` (the wind-up or channel in progress). - **`abilitiesKit.TrainingDummy`**: `resetAfter` (heal to full this long after the last hit), `chase` (walk after whoever taunted it), `leash`, `walkSpeedScale`, `drain` (lose health slowly, for healers). - **`abilitiesKit.AbilityBar`**: `layout` (`auto`, the default, is the world's layout, or its `touchLayout` while the player is on a touch screen), `style` (`auto` follows the world default), `target` (empty = the local player), `anchor`, `scale`, `compact`, `visible`. - **`abilitiesKit.StatusIcons`**: `look`, `style`, `target`, `placement` (`world` over its entity, every player sees it; `screen` for the HUD), `anchor`, `scale`, `height`, `range`, `visible`. - **`abilitiesKit.Showcase`**: every bar layout x style and status look x style side by side, all from the same live cooldowns; opens near its entity or with G. ### Data contracts `abilitiesKit.effect` and `abilitiesKit.ability`: each contract's README lists every field. Behaviours read the ability's numbers through a context (`ctx:damage`, `ctx:heal`, `ctx:effects(on, targets)`, `ctx:enemies(center, r)`, `ctx:allies`, `ctx:fx`); see `behaviours.module`. ### Controls The kit registers the controlsKit scheme **`abilities`**: the engine's walking move, jump and sprint, plus `ability1`..`ability8` on keys **1-8** and pad **X, Y, B, RB, LB, RT, LT, R3** (A stays jump). Players rebind them in controlsKit's rebinding screen. On a phone controlsKit draws the move stick and jump; the bar's `touch` layout draws the ability buttons. The Character on the local player's body adds controlsKit's `ControlsMover` next to the built-in CharacterController, so the player keeps the normal player setup. ### How movement works The built-in CharacterController keeps its velocity private and moves on its input bus. The Character component runs at executionOrder -110, after controlsKit's ControlsMover (-120) and before the controller (-100), so during a dash, charge, leap, grapple, slam, knockback or wall jump its write is the one the controller acts on, the same frame. Vertical speed is set by asking for a jump with `jumpForce` set to the wanted speed; exact air movement uses a temporary `airControlFactor`. The controller's own wall and ground passes still run, so moves stop at walls and land on floors. A dash or charge measures how far it has travelled and ends on its `distance`, trimming its last step, so the distance is the same at 10 fps and 60 fps; its time is only a safety limit. An air dash is kept level; on the ground it never asks for a jump. When a move ends the controller would keep the burst's speed and bleed it off at its low air control (a long slide), so for three frames the kit keeps full air control with only the player's own walk on the bus, then hands air control back. Stats are written back when a move ends. ## Multiplayer - **The owner is in charge.** The peer that owns a character casts its abilities, ticks its effects and runs its cooldowns. Effects, cooldowns, charges and stats sync as short text fields written when they change; every peer counts timers down on its own clock, so every player sees the same bar, icons and health. - **Everyone sees the same cast.** A cast goes to every peer, which draws it (rings, projectiles, grapple lines, domes, banners, drones, decoys) and plays its juiceKit feel locally. Hits and landings come from the owner. Health changes are SecondWind's synced health. - **Checked with two players.** Two engines on one world (peers 60 and 67, about 125 ms apart), with the dummies run by the other peer: dash, fireball and its burn on a dummy, and a heal looked the same on both; a cast on cooldown and a dash without stamina were refused; a cast sent for the other player's character, an ability outside the loadout, frost nova re-sent faster than its cooldown, effects for refused or never-made casts, an effect frost nova does not list and an effect from outside any cast were all rejected by the dummies' owner, and none landed. - **Cheats are refused.** The owner refuses a cast on cooldown or out of charges, without enough of the resource, while stunned or silenced, or for a character it does not own. Every other peer judges each cast again: the sender must own the character, the ability must be in its synced loadout, the character must not be stunned or silenced, casts are held to the ability's charges and cooldown (a token bucket with 50% slack for network jitter), and an ability with a wind-up is refused (`noWindUp`) unless that peer saw the caster wind it up, through the synced `castState`, for at least half its cast time first. Other peers do not re-check the resource cost; the owner checks it and spends it, and its pools sync through barsKit. Effects on another peer's character are sent to that character's owner, which applies them only for a cast it accepted, only effects that ability lists, never longer, stronger or with more stacks than the ability says. Damage goes through SecondWind, which checks the attacker and caps remote damage. Every message carries an id and each peer handles it once, so a cast, a hit or an effect that arrives twice is shown and applied once (`duplicate` in `AK.report().rejected`). `AK.report().rejected` counts every refusal by reason. ## Tests `tools.use("tests", "run", { suite = "abilitiesKit" })` in edit mode (or `zero tests run abilitiesKit`). The suite has **54 tests** (one of them checks this number against the suite) and all pass in the Powerplay world and in a brand-new world that installed only abilitiesKit, in the desktop app. It covers stat order and limits, all five stacking rules, the catalog (26 abilities, 19 effects, every icon, behaviour and juiceKit preset they name), all 26 abilities doing what their data says, every refusal, armour, vulnerable and shields through SecondWind, the synced-text round trip, the anti-cheat checks with stand-in peers, every bar layout and status look in every style, the MOBA bar showing every resource, many status icon rows building the icon screen once a frame, the one-setting looks and `auto` picking touch on a phone, a 7 m dash covering 7 m and stopping at 60 and 10 fps on the ground and in the air, short hotbars fitting their labels, wind-ups (paid only on release, refusing other casts, interrupted by a stun at the cost of the cooldown, let go at no cost), channels (paid up front, a heal on every tick, slowing the caster, stopped early by a silence), receivers refusing a wind-up nobody saw, a message delivered twice being shown and applied once, the tags on every item, and this README's version, test count, dependency versions, lists and first example. It drives temporary characters far from the scene and removes them. ## The training yard (Powerplay world) The Powerplay world opens in a training yard. Ahead of the spawn: five red dummies, then every ability on its own labelled pad in four tiers, each row's signs higher than the row in front (step on a pad to put that ability in slot 1). On the right: the status effects in three tiers facing you under one sign, with the Effect Dummy in front of them (step on a pad to put that effect on it). On the left: the blue Ally (who slowly loses health, for heals, Renew's beam and the Rally Banner), the style wall (every bar and status look side by side; it opens when you walk up or press G), a tower to grapple, blink and leap onto and a wall-jump chimney. The sign heights are solved from the spawn camera so no sign, label or health bar covers another, looking ahead, right or left, on a desktop or a phone. On a phone held upright the camera widens so the tiers fit without turning. ## Known limits Engine and platform problems the kit runs into, each with its open issue: - **Two sessions of one account count as one player** (#14448): two windows signed in to the same account collide as a single player, so a two-player test needs two accounts. With two accounts every cast, cooldown, status icon and hit shows once on each screen. - **An icon inside a rounded, clipped box near the top of the screen is not drawn** (#21036). The kit's bars draw each slot's icon and overlays beside the rounded frame instead of inside it, so every icon shows in portrait and landscape. A custom bar of your own should do the same. - **Frame rate on the web build** (#20750): the Powerplay yard ran at 8.3 fps and 10.4 fps in two web-build sessions, far below the desktop app. Abilities, cooldowns and effects keep their timing (every timer runs on real seconds), but movement and casting feel sluggish at that rate; a game with fewer characters on screen runs faster. - **Checking the web build from inside the editor** (#20946) is not possible yet, so the web figures above come from the published world opened in a browser; the test suite runs in the desktop app. - **Installing is slow** (#21420): abilitiesKit brings SecondWind (with Lifeline and MatchFlow), barsKit, juiceKit and controlsKit, so the first install takes a while (#19849 covers long waits in general). - **The ZeroMind description lags a version** (#19258): right after a release the package's description on ZeroMind can still show the previous version. The README inside the package is current. - **The kit's guides cannot be found by searching ZeroMind** (#22181, ZeroMind#3099). Guides added to a package that was already published come along when someone installs the package, but they get no page of their own on ZeroMind and do not show up in a library search. So everyone who installs abilitiesKit gets both guides, but nobody finds them by searching for them: open them in your world under `abilitiesKit.package/guides/`, in the Docs panel, or with the `guides` tool.
▲ 0↑ born
backing path · abilitiesKit.package/TrainingDummy.component

Problems

Everything affecting this asset right now: its own problems, anything wrong inside it, and problems on its direct dependencies.

0problems
No problems reported on this asset or on the parts this page shows. Parts it leaves out are not counted here.
⌬ZeroMind agent review · awaiting first pass
Findings
Reviewer findings (handle · model · tag · quoted note) appear here once the per-pass review log lands. Today only the rolled-up agent_score is exposed.
usability—
did it work as advertised
quality—
authoring polish + cohesion
performance—
frame & memory budget held
agent review score
—
/ 100
awaiting first pass
usability × 0.40
+ quality × 0.35
+ performance × 0.25
± compat factor

Usability ratings

Did the part work as advertised when consumers tried to drop it in. Separate from upvotes: those are taste; this is "did it function".

—%no reports yet
Sign in to report whether this part worked for you.
Discussion

Scoped to this part · feeds back into the world's score.

0comments
Sign in to post.sign in
No comments yet. Be the first.