Skip to content

t1k:cocos:rushtank:add-equipment

FieldValue
Modulerushtank
Version1.2.0
Effortmedium
Tools

Keywords: backpack, buff, cocos, equipment, gacha, rustank, trait

/t1k:cocos:rushtank:add-equipment

Add a new backpack equipment (a support/weapon item placed on the vehicle grid with a 4-level trait tree). Unlock levels are rarity-based (match the existing weapons): Rare/Epic → 1/3/5/7, Common → 1/3/6/9 (memory project_equipment_cost_curve_ssot). Reference builds: Blast Case (explode-family buffer, Rare) + Frost Coil (CC support, Rare) — both 1/3/5/7 — grep blast_case / frost_coil for a complete worked pair.

⚠️ The single #1 crash: the equipment page icon reads ENTITY[unitID].avatar (EquipmentPageItem.updateIcon). A new item’s unitID (e.g. blastCasePack) has NO ENTITY entry → undefined.avatar throws → and because the new item is LAST in EquipmentDefine, it just vanishes from the list (no visible crash). You MUST add ENTITY.<pack> in PassiveItem.js (§3). This ate a whole debug cycle.

SSOT memories to read first: project_equipment_cost_curve_ssot, project_equipment_talent_data_model, project_equipment_isupgradeable_semantics, project_backpack_shape_convention. Concrete templates + exact snippets: references/add-equipment-reference.md.


Step 0 — Verify input data (HARD-GATE — do this FIRST, every invocation)

Section titled “Step 0 — Verify input data (HARD-GATE — do this FIRST, every invocation)”
When the user invokes this skill with design data (a GDD snippet, a spec, or a prose ask), **before touching any file**: parse what they gave, emit the **input checklist** below marking each field ✅ (present) or ❌ (missing), then **STOP and ASK FOR EVERY MISSING FIELD** — do not silently default. Ask for all ❌ REQUIRED rows AND confirm every OPTIONAL default (the user must actively OK each default, not have it applied behind their back). Batch with `AskUserQuestion` (max 4/call → loop as many rounds as needed until nothing is ❌). Then re-print the checklist all-✅ and get one final go-ahead. Only after that confirmation → run the flow. A ❌ on any REQUIRED row hard-blocks the run. Override: only an explicit "skip the checklist / just build it with defaults" from the user. Rule: `rules/workflow-gates.md`.

REQUIRED (❌ any → must ask; cannot be safely defaulted):

#FieldFeeds
1id (snake_case) + display name (≥ EN, ideally VI)§1/§2/§6
2rarity — Common / Rare / Epiccost curve + unlockLvl pattern + variant HP
3kind — support (grants HP) OR weapon (grants ATK) → the <STAT> column§2/§5
4grid shape — which cells (e.g. 2-cell line, L)§2 shape
5unlock method — 7-day login OR progression§1
6stat curve — base + per-level growth for <STAT> (or “use default <rarity> curve”)§5
74 traits — per trait: effect + magnitude + weapon-family it buffs + special? (needs custom Buff code)§4 / §4b

OPTIONAL / derivable (❌ → note the default that will be applied, don’t block):

FieldDefault if missing
trait unlockLvlsderive from rarity — Rare/Epic 1/3/5/7, Common 1/3/6/9
trait idsderive <id>_<effect>
localization (15 langs)auto-fill EN/ZH/VI, machine-translate the other 12 → flag for QA
icon artplaceholder avatar/backpack/item/<Name> until art delivered
effect visuals (special-trait FX)reskin an existing atlas, or skip (fails silently)
self-HP base (support)default from rarity

Emit exactly this shape, then ask:

Input check — <id>:
REQUIRED
✅ id + name : <values>
❌ stat curve : (missing — need base + growth, or "default rare")
✅ 4 traits : <n>/4 described (❌ trait 3 magnitude missing)
OPTIONAL (defaults ready)
✅ unlockLvls : derive 1/3/5/7 (Rare)
❌ translations : will auto-fill + flag QA
→ Ready: <R>/7 required present.

Then ASK — every gap, until zero ❌:

  1. AskUserQuestion for each ❌ REQUIRED field (batch ≤4/call; loop more rounds if >4 missing). Do NOT proceed while any REQUIRED is unanswered.
  2. AskUserQuestion to confirm each OPTIONAL default (e.g. “unlockLvls → 1/3/5/7?”, “auto-translate 12 langs + flag QA?”, “icon = placeholder for now?”) — the user OKs or overrides each; never apply silently.
  3. Re-print the checklist now all-✅, then one final AskUserQuestion: Confirm & build · Change something. Only after that final confirm → run the flow below.

  1. New backpack item with its own trait tree? → this skill.
  2. New drivable player vehicle? → cocos-rushtank-add-player-tank.
  3. Enemy tank / texture re-skin? → cocos-rushtank-entity-variant.
  4. Only tune an existing item’s trait/cost? → jump to §4 (traits) / §2 excel (cost).

Pick a snake_case id (blast_case, frost_coil), a unitID (<id>PackblastCasePack), rarity (1=Common, 2=Rare, 3=Epic), and the 4 talent ids.


Full flow (ordered — each layer depends on the previous)

Section titled “Full flow (ordered — each layer depends on the previous)”

1. Two parallel defines — BOTH required (miss one → item broken)

Section titled “1. Two parallel defines — BOTH required (miss one → item broken)”
DefineFileHolds
EquipmentDefine[id]components/combat/equipment/equipment.js (append before closing })excel:1, rarity, talent:[{id,unlockLvl}×4], unlock7D/unlockProgression, opt avatarScale. stat[] NOT written here — loaded from excel at runtime.
BackpackItemDefine[id]scripts/BackpackItemDefine.js (inside loadBackpackItemDefine, append before block1x1)name:LI(id), rarity:0, shape, weaponType, unitID, upgrade:"<id>2", talent:{id:"hp",num} (self-HP), avatar:{frm,ax,ay}, infoType, info[], describe:LI("des_<id>").
e-table variantssame file, var e = {…} blockadd <id>2/3/4 with talent:{id:"hp",num} per rarity — the loop (line ~551) auto-builds <id>2/3/4 (rarity 1/2/3, unitID <unitID>_2…).

Shape = [[dx,dy],…] offsets; dy TĂNG = LÊN trên (Cocos y-up). [0,1]=trên · [1,0]=phải · [1,1]=chéo. 2-cell ngang = [[0,0],[1,0]]; L “1-trên-2-dưới” = [[0,0],[0,1],[1,0]]. (memory project_backpack_shape_convention)

  • resources/excel/equipment_<id>.json: 20 entries {lvl,cost,exp,<stat>}; lvl20 has no cost/exp. Clone shape from equipment_fuelTank.json. <stat> = hp (support) or atk (weapon), increment per level.
  • tools/gen-equipment-cost.js: add <id>: "rare" (or common/epic) to the RARITY map, run node Client/tools/gen-equipment-cost.js --check (it OWNS cost/exp per rarity; keeps your hp/atk column).

3. ENTITY.<pack> in PassiveItem.js — the anti-crash step

Section titled “3. ENTITY.<pack> in PassiveItem.js — the anti-crash step”

Self-support items spawn as a passive $myTower. Clone the ENTITY.fuelTank skeleton (no attackSequence = doesn’t shoot). avatar.frm here drives BOTH the equip-page icon AND in-battle art. Variants via kit.clone (append avatar.frm += "2/3/4"). Armor-recovery support → createCb adds BigShieldBuff (see ENTITY.armorPack); pure-HP → createCb: function(){} + infoNum:0. Template: references/add-equipment-reference.md §ENTITY.

4. Trait tree — TalentDefine.js (4 talents)

Section titled “4. Trait tree — TalentDefine.js (4 talents)”

Append 4 entries {unique, rarity, template, des:LI("tco_<t>"), content, needEquip:"<id>"}. Template kinds:

KindShapeUse for
attribute{id:"attribute", attr, num, add?, unitType}+atk/crit/critBonus/bltExploRange on matching weapons
blt{id:"blt", bltAdd/bltMul:{…}, unitType}modify the weapon’s bullet (chain, n, bltAdd.buff)
buff{id:"buff", buff:{ctor,symbol,t,…}, unitType}attach a custom Buff to weapons (on-hit logic)

Special trait needing bespoke logic (proc/slow/dmg-vs-CC/on-death/AoE) = up to 3 hand-written pieces, not a define: (1) a Buff subclass in components/combat/buffs/ (on-hit hitEvent hook, attr:[[OP,"stat",v]] mutations, c_rnd, getFlag/getBuff CC-checks, death-trigger in end() via isDead() + contactMgr AoE sweep), (2) optionally a shared engine-fn patch (e.g. checkUnitTypeMatch array support, Bomb.explosion bltExploRange), (3) optionally a custom EFFECT (§7). Full templates + verified primitives (opcode table, eventMgr, callEffect 6-arg cap): references/add-equipment-reference.md §4b. Traits are offered in the in-battle TalentMenu, pool-filtered by needEquip owned + isTalentOwned (equipment level ≥ unlockLvl).

5. Localization — resources/excel/local_main.json

Section titled “5. Localization — resources/excel/local_main.json”

Append 16-column rows (header = index,zh-cn,en-us,zh-tw,ko,ru,ja,id,th,vi,es,pt,de,fr,it,tr): <id> (name), des_<id> (describe), one tco_<talent> per trait. Missing key → LI() returns " <key>" (no crash, shows the key). Match the existing weapon tco_* rows exactly (grep tco_26 for a model) — 3 HARD rules (each was a real miss on the first Blast Case / Frost Coil pass):

  • NO LvN: prefix. Trait text = the effect only (Explode ATK +20%), never Lv1: …. The unlock level is shown by the UI, not baked into the string.
  • Colour the stats. Weapon/family noun (or the key status word) → <color=#FFB369>…</color> (orange); every numeric value / % / duration → <color=#72FF6B>…</color> (green). E.g. <color=#FFB369>Explode</color> ATK <color=#72FF6B>+20%</color>.
  • Localise ALL 15 languages (ko/ru/ja/id/th/es/pt/de/fr/it/tr included) — every shipped weapon is fully translated. Leaving English in the 12 non-zh/en/vi columns reads as untranslated in-game. Only proper-noun names may stay Latin. File format = JSON.stringify(data, null, "\t") + CRLF line endings. Safe bulk edit = parse → mutate ONLY your rows by key → JSON.stringify(d,null,"\t").replace(/\n/g,"\r\n") (round-trips byte-for-byte, so the diff stays confined to your rows — never hand-edit 15 columns × N rows inline). Colored 15-lang example + script: references/add-equipment-reference.md §6.
  • Real: unlock7D/unlockProgression on EquipmentDefine (mirror cannon=7D / laser=progression); shop shard pool is data-driven by rarity (ShopPage.js) → auto-includes once defined + unlockable.
  • DEV (see it NOW): add <id>: {lvl:1, exp:0} to the default i.items in equipment.js → owned on every save (even existing, via _buildItemsData fallback) → appears in the equippable list. Remove before shipping if it should be shop-gated.
  • Icon: resources/avatar/backpack/item/<Name>.png (+2/3/4 for variants). Wire frm:"avatar/backpack/item/<Name>" in BOTH BackpackItemDefine.avatar AND ENTITY..avatar. (Chinese-named source sprites exist — verify you’re not clobbering another item’s art.)
  • Effects are SpriteAtlas (.plist+.png) loaded by createAnimation("effect/<cat>/<name>"). Reskin an existing one: clone the .plist renaming ALL source refs (sed 's/boom5/boom6/g' — also renames internal frame keys to avoid cross-atlas frame-name collision), copy the .png, DON’T copy .meta (Cocos regenerates the UUID). Add EFFECT.<name> in effects/visual/weaponEffect.js (world burst) or effect.js (avatar-attached, avatarShock pattern). Details: references/add-equipment-reference.md §Effects.

Ctrl+P preview (recompiles JS + imports new JSON/atlas). Check: item shows in Equipment page (correct shape + icon), equippable, upgrade costs gold+shard, traits appear in TalentMenu on level-up and visibly apply, effects render. Use window.DEBUG_TALENT_IDS = ["<t1>","<t2>","<t3>"] (setting.js / console) to force exact traits into the pick menu (bypasses random + gating). window.DEBUG_BOMB_RADIUS = true draws the real explosion damage radius.


Gotcha quick-list (each cost a real debug cycle)

Section titled “Gotcha quick-list (each cost a real debug cycle)”
  • Missing ENTITY[unitID] → item silently vanishes from Equipment page (updateIcon crash on the LAST define). §3.
  • Not in i.items → owned=false → not in equippable list (only the locked section). Add for DEV. §6.
  • checkUnitTypeMatch is scalar ==unitType does NOT accept an array. To hit a weapon family use a unitId array (via matchIdOrArray) OR the array-aware patch already applied to checkUnitTypeMatch. Prefer unitType:[…] now that it’s array-aware.
  • Explosion radius: use the bltExploRange entity-attribute (read LIVE at Bomb.explosion), NOT bltMul.bomb.r0/r1. The define-scale of bomb does not reliably reach the fired projectile’s damage (visual grows, damage doesn’t).
  • buff template self:true crashes (gameContext.define.unitId undefined at pick time) — use non-self + unitType; set any global flag in the buff’s begin() (applied to weapons).
  • Death-triggered effect (e.g. Frost Nova): do NOT register deathEvent from the dying enemy’s own buff — clearBuff unregisters it BEFORE deathEvent fires. Detect death inside the buff’s end() via this.obj.isDead() (set before clearBuff).
  • HP-support rides talent:{id:"hp",num} (flat per rarity-variant) + excel stat.hp (per-level); only counted when infoType is heal/armor (StatsCalculator isHealOrArmor). infoNum:0 = no armor/heal regen.
  • New PNG/JSON/JS need a .meta — let Cocos generate it (refresh); never copy a source .meta (UUID collision).
  • Trait popup shows the WRONG text (e.g. another item’s) → almost always a stale build; getInfo is by-id and correct data renders correctly after a full editor reimport/restart (Ctrl+P sometimes isn’t enough).
  • local_main.json / equipment_<id>.json blind edits: keep JSON valid (JSON.parse) and the 16-column row width exact; run gen-equipment-cost.js --check.
  • LI trait text ≠ existing weapons — the 3 misses to avoid (§5): (a) Lv1:-prefixed text (existing tco_* have NO prefix), (b) uncoloured stats (must be #FFB369 noun / #72FF6B number), (c) English left in the 12 non-zh/en/vi columns (must be fully translated like tco_26). Blast Case + Frost Coil shipped all three wrong first and needed a follow-up pass.
  • Trait unlockLvl by rarity — Rare/Epic = 1/3/5/7, Common = 1/3/6/9. Don’t default the 4th to 6 (both reference builds were fixed 6→7).

(1) item shows in Equipment page with correct shape + icon, (2) equippable (owned) + upgradeable, (3) all 4 traits appear in TalentMenu (force via DEBUG_TALENT_IDS) and visibly apply, (4) effects render, (5) no console crash on pick, (6) LI text matches the existing weapon convention — no LvN: prefix, stats coloured (#FFB369/#72FF6B), all 15 languages translated; unlock levels are rarity-correct (Rare/Epic 1/3/5/7), (7) DEV-only tweaks (i.items, DEBUG_*, any DEBUG_FORCE_* test flags) reverted before commit, (8) only intended files staged (game repo has many pre-existing dirty files — stage by name, never git add -A).