t1k:cocos:rushtank:add-equipment
| Field | Value |
|---|---|
| Module | rushtank |
| Version | 1.2.0 |
| Effort | medium |
| Tools | — |
Keywords: backpack, buff, cocos, equipment, gacha, rustank, trait
How to invoke
Section titled “How to invoke”/t1k:cocos:rushtank:add-equipmentcocos-rushtank-add-equipment
Section titled “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’sunitID(e.g.blastCasePack) has NOENTITYentry →undefined.avatarthrows → and because the new item is LAST inEquipmentDefine, it just vanishes from the list (no visible crash). You MUST addENTITY.<pack>inPassiveItem.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)”REQUIRED (❌ any → must ask; cannot be safely defaulted):
| # | Field | Feeds |
|---|---|---|
| 1 | id (snake_case) + display name (≥ EN, ideally VI) | §1/§2/§6 |
| 2 | rarity — Common / Rare / Epic | cost curve + unlockLvl pattern + variant HP |
| 3 | kind — support (grants HP) OR weapon (grants ATK) → the <STAT> column | §2/§5 |
| 4 | grid shape — which cells (e.g. 2-cell line, L) | §2 shape |
| 5 | unlock method — 7-day login OR progression | §1 |
| 6 | stat curve — base + per-level growth for <STAT> (or “use default <rarity> curve”) | §5 |
| 7 | 4 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):
| Field | Default if missing |
|---|---|
| trait unlockLvls | derive from rarity — Rare/Epic 1/3/5/7, Common 1/3/6/9 |
| trait ids | derive <id>_<effect> |
| localization (15 langs) | auto-fill EN/ZH/VI, machine-translate the other 12 → flag for QA |
| icon art | placeholder 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 ❌:
AskUserQuestionfor each ❌ REQUIRED field (batch ≤4/call; loop more rounds if >4 missing). Do NOT proceed while any REQUIRED is unanswered.AskUserQuestionto 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.- Re-print the checklist now all-✅, then one final
AskUserQuestion: Confirm & build · Change something. Only after that final confirm → run the flow below.
Decision tree
Section titled “Decision tree”- New backpack item with its own trait tree? → this skill.
- New drivable player vehicle? →
cocos-rushtank-add-player-tank. - Enemy tank / texture re-skin? →
cocos-rushtank-entity-variant. - 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>Pack → blastCasePack), 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)”| Define | File | Holds |
|---|---|---|
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 variants | same file, var e = {…} block | add <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)
2. Cost/HP curve — excel + generator
Section titled “2. Cost/HP curve — excel + generator”resources/excel/equipment_<id>.json: 20 entries{lvl,cost,exp,<stat>}; lvl20 has no cost/exp. Clone shape fromequipment_fuelTank.json.<stat>=hp(support) oratk(weapon), increment per level.tools/gen-equipment-cost.js: add<id>: "rare"(or common/epic) to theRARITYmap, runnode Client/tools/gen-equipment-cost.js --check(it OWNS cost/exp per rarity; keeps yourhp/atkcolumn).
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:
| Kind | Shape | Use 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%), neverLv1: …. 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.
6. Distribution + DEV testing
Section titled “6. Distribution + DEV testing”- Real:
unlock7D/unlockProgressiononEquipmentDefine(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 defaulti.itemsinequipment.js→ owned on every save (even existing, via_buildItemsDatafallback) → appears in the equippable list. Remove before shipping if it should be shop-gated.
7. Art + effects
Section titled “7. Art + effects”- Icon:
resources/avatar/backpack/item/<Name>.png(+2/3/4for variants). Wirefrm:"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 bycreateAnimation("effect/<cat>/<name>"). Reskin an existing one: clone the.plistrenaming 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). AddEFFECT.<name>ineffects/visual/weaponEffect.js(world burst) oreffect.js(avatar-attached,avatarShockpattern). Details:references/add-equipment-reference.md§Effects.
8. Verify in-editor (no CLI runtime)
Section titled “8. Verify in-editor (no CLI runtime)”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. checkUnitTypeMatchis scalar==—unitTypedoes NOT accept an array. To hit a weapon family use aunitIdarray (viamatchIdOrArray) OR the array-aware patch already applied tocheckUnitTypeMatch. PreferunitType:[…]now that it’s array-aware.- Explosion radius: use the
bltExploRangeentity-attribute (read LIVE atBomb.explosion), NOTbltMul.bomb.r0/r1. The define-scale ofbombdoes not reliably reach the fired projectile’s damage (visual grows, damage doesn’t). - buff template
self:truecrashes (gameContext.define.unitIdundefined at pick time) — use non-self +unitType; set any global flag in the buff’sbegin()(applied to weapons). - Death-triggered effect (e.g. Frost Nova): do NOT register
deathEventfrom the dying enemy’s own buff —clearBuffunregisters it BEFOREdeathEventfires. Detect death inside the buff’send()viathis.obj.isDead()(set before clearBuff). - HP-support rides
talent:{id:"hp",num}(flat per rarity-variant) + excelstat.hp(per-level); only counted wheninfoTypeisheal/armor(StatsCalculatorisHealOrArmor).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;
getInfois by-id and correct data renders correctly after a full editor reimport/restart (Ctrl+P sometimes isn’t enough). local_main.json/equipment_<id>.jsonblind edits: keep JSON valid (JSON.parse) and the 16-column row width exact; rungen-equipment-cost.js --check.- LI trait text ≠ existing weapons — the 3 misses to avoid (§5): (a)
Lv1:-prefixed text (existingtco_*have NO prefix), (b) uncoloured stats (must be#FFB369noun /#72FF6Bnumber), (c) English left in the 12 non-zh/en/vi columns (must be fully translated liketco_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).
Done when
Section titled “Done when”(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).