Skip to main content

Add a skill

A skill is what the script does when a tsum's skill gauge fills: tap the button, then run a choreography — wait for the board to settle, sweep the bubbles the skill left, aim taps, draw drags. Every playable skill is one file under src/skills/ that registers a handler; skillCore.ts owns what is the same for all of them (the gauge read, the fever hold-off, the activation taps, the useSkill dispatch).

What a change has to touch

#FileWhat
1src/shared.d.tsA SkillType member: the game's id for the skill.
2src/skills/<name>.tsThe handler, registered with registerSkill.
3tsconfig.jsonA line in files, after src/skills/skillCore.ts.
4src/strings.d.ts + src/uiEn.tsA UiText key for the dropdown label, and its English text.
5src/skillOptions.tsA SkillOption entry: group, share character, status.
6CODEMAP.mdA row in the skills table.
7CHANGELOG.mdA ### Summary line under the current version — a new skill is something a player sees.

Then npm run typecheck, npm run i18n:check, npm run map:check.

1. The SkillType member

SkillType is a const enum in shared.d.ts, compiled into all three programs, so the dropdown and the play loop name the skill by one member. The member name is the dropdown label in PascalCase; the value is the id that crosses the start({...}) bridge.

app.gap.Tsum/src/shared.d.ts
loading...

2. The handler

The smallest complete skill in the tree is Moana: bubbles again, behind a slightly longer intro than Marie's.

app.gap.Tsum/src/skills/moana.ts
loading...

registerSkill files the handler under each id in types, so one handler can drive two dropdown entries (Donald and Holiday Donald share one). Burst shows that, plus bareTapActivates:

app.gap.Tsum/src/skills/burst.ts
loading...

Every field except types is optional. The interface is documented field by field in skillCore.ts; the short version:

FieldDeclare it when
afterActivate(ts, board?, activatedAt?)Always, unless a bare tap and a settle is the whole skill. This is the choreography, run straight after the activation tap. Return false to report "did not fire".
beforeActivate(ts)Something has to land before the skill fires — a settle wait, a pre-tap.
bareTapActivatesA tap on the button is the whole activation and a tap on a filling gauge is a no-op. Lets the play loop fire the skill blind between chains instead of paying for a gauge check.
usesSecondButtonThe skill has two halves on two buttons (Pair Tsum).
sweepsBubblesThe choreography ends on clearAllBubbles, because the skill turns tsums into bubbles and there is no chain to save them for. This is the one declaration that overrides the Bubble Strategy setting — see below.
claimsBubbles(ts)The bubbles are the skill's to chain, so the play loop must pop none. A function, because it can be true for part of a round (Lorcana Aurora after her transformation).
popBubblesAfterChain(ts, chainLength)With a claim standing, how many bubbles to spend on a chain that just landed anyway.
chainLimitsThe skill wants different chain caps from the player's settings — a value, or a function of ts for a skill whose board changes shape mid-round. Applied where the limits are read, never written into the settings.
orderPaths(ts, paths, board)The skill chooses which chains to link and in what order (Formal Beast keeps his two gauges level). Runs between the scan and the first drag, so it must stay cheap.
extraClusterSlotsThe skill's board scans need extra colour slots — a skill that freezes tsums spends slots on its own ice.
overloadProbeExperimental: the choreography is anchored to the activation instant, so it can be fired mid-chain by the auto-tap.
app.gap.Tsum/src/skills/skillCore.ts
loading...

A skill with no afterActivate falls back to skillRandomizeAndWait: tap the Fan and settleBoard for up to the Skill Waiting time. That wait is a budget, not a duration — it returns as soon as the tsums stop falling.

What a choreography can call on ts: tap, tapDown / moveTo / tapUp for a drag, sleep, sleepUntil, settleBoard, screenshot + getColor for a probe, clearAllBubbles, setMyTsumPriority, and everything else in interface Tsum. Keep a skill's tuning tables in its own file under a "Tuning data" heading — data.ts holds only what more than one file reads.

:::warning Bubble taps go through the Bubble Strategy, or say why not Bubbles are worth more popped inside a chain than alone, so the play loop hoards them. A skill that pops bubbles must either declare sweepsBubbles (it made them, it clears them) or claimsBubbles (they are its chain). A new tap on a bubble that reads neither silently undoes the player's setting. :::

Every skill file is a leaf: nothing outside src/skills/ references its symbols; it is reached only through the registry at runtime. An unregistered id still plays, as a plain burst, so a forgotten tsconfig.json line is silent — which is why step 3 matters.

3. The tsconfig.json line

Files are concatenated in this order and registerSkill runs at load time, so the new file must come after skillCore.ts and before clickAssist.ts:

app.gap.Tsum/tsconfig.json
loading...

4. The label

The dropdown never shows a raw id. Add a UiText member in strings.d.ts and its English in uiEn.ts; the build fails until the English exists, and other languages fall back to it until translated (UI text and languages).

app.gap.Tsum/src/uiEn.ts
loading...

5. The dropdown entry

skillOptions.ts is compiled into both pages, so the settings page and the Quick Bar list the same skills. Insert the entry under its group, at its alphabetical position:

app.gap.Tsum/src/skillOptions.ts
loading...
  • group is what the activation leaves behind: SkillGroupBurst (fires and clears), SkillGroupBubble (turns tsums into bubbles the choreography sweeps), SkillGroupUnique (changes how the play loop plays while it is up).
  • share is the character the skill is written as in a share code. Any free letter; unique; fixed once shipped, because changing it rewrites what every code in circulation means.
  • status is how finished it is. A new skill is almost always ReleaseStatus.Alpha, which lists it on Alpha builds only and badges it orange; promoting it later is a one-word edit.
  • enables lists settings the skill cannot play without, switched on when it is picked (Lorcana Aurora enables the Lorcana card).

6 and 7. Bookkeeping

Add a row to the skills table in CODEMAP.md — one clause saying what makes this skill unlike the others; map:check fails on a skill file the table does not list. Add a Summary bullet in CHANGELOG.md under the version in package.json: <Name> skill added. Later work on the skill folds into that same line.

Testing it

  • npm run typecheck catches a SkillType used before it exists, a UiText with no English, and a prototype method not declared in interface Tsum.
  • The offline harness can drive a choreography over a real frame: createRuntime and createTsum from tools/runtime/load.js give you the built bundle with the natives shimmed (Test without a device).
  • On a device, set Debug logs on and read skill.use and the skill's own events in the log. Skill Level is only read by skills whose choreography changes with it.