Skip to content

t1k:cocos:rushtank:add-player-tank

FieldValue
Modulerushtank
Version1.2.0
Effortmedium
Tools

Keywords: cocos, garage, player, rustank, star-trait, tank, vehicle

/t1k:cocos:rushtank:add-player-tank

Add a brand-new drivable player vehicle (e.g. v4 SIEGE, next v5…). Player vehicles are 100% data-driven through vehicle_list.json; the engine builds ENTITY[vid] at boot via excel.js createVehicleData() by cloning ENTITY[VEHICLE_BASE="a"] + overlaying vehicle_list fields. ENTITY.tank/tank2/… in tank.js are ENEMY tanks — not this. Texture re-skins of an existing entity → use cocos-rushtank-entity-variant instead.

SSOT facts live in memory: project_new_player_vehicle_hardcodes, project_excel_sheet_manifest, project_siege_tank_worktree. Read them first. Full templates + exact snippets: references/add-player-tank-reference.md.

⚠️ The codebase hard-codes v1/v2/v3 in ~6 places. A new vN silently falls through each one. The worst is a lookup map n[this.define.id] in MyVehicle.jsundefinedNaN tower position → bullets deleted → “tank doesn’t attack”. grep "\\[this.define.id\\]" and every == "v3" BEFORE declaring done. This is the #1 cost.


  1. New drivable vehicle with its own art/stats? → this skill (add flow below).
  2. Same vehicle, just re-skin texture? → cocos-rushtank-entity-variant.
  3. Enemy/AI tank? → edit entities/vehicles/tank.js (ENTITY.tankN), not here.
  4. Only add/tune a star trait on an existing player vehicle? → jump to §4 Traits.

Pick the next free vid (v1,v3 exist → next is v4, then v5…) and a free attr letter (a/c used → d,e…).


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. Batch with `AskUserQuestion` (max 4/call → loop until nothing is ❌). Then re-print the checklist all-✅ and get one final go-ahead. Only after that → 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
1vid (v4/v5…, next free) + display name (≥ EN, ideally VI)§1 vehicle_list / §4 local_vehicle
2attr letter (d/e…, free) → vehicle_<attr> + vehicle_star_<attr>§1 data files
3base scaling — clone which existing vehicle_<attr> (hp/atk 181-level)§1 excel table
4artanime/animeName/animeName2 (reuse a skeleton e.g. v3’s, or new) + gun/wheel/radius§1 vehicle_list
5tower/weapon (tower: MACHINE_GUN/CANNON_GUN/FIRE_GUN) + skill (bomb/incendiary/…)§1
69-star trait design — per star: stat id + magnitude (non-atk/hp → engine extension)§4 vehicle_star
7unlock/shard source — alias to an existing vid’s shard pool, OR a real faucet§5 shard sites

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

FieldDefault if missing
trait engine wiring (pc.js/Entity.js/MyTower)auto for any non-atk/hp stat (§4) — flag it
localization (16 langs)auto-fill EN/ZH/VI, machine-translate the other 13 → flag QA
artreuse an existing skeleton (e.g. lanzhanche = v3) as placeholder until real art
career / leftMsg / rightMsg board textderive/placeholder
DEV unlock (grant/star-up buttons)add a DEV_MODE branch for testing

Emit exactly this shape, then ask:

Input check — <vid>:
REQUIRED
✅ vid + name : <values>
❌ 9-star traits : (missing — need stat + magnitude per star)
✅ art : reuse v3 skeleton
OPTIONAL (defaults ready)
❌ localization : auto-fill + flag QA
→ Ready: <R>/7 required present.

Then ASK — every gap, until zero ❌:

  1. AskUserQuestion for each ❌ REQUIRED (batch ≤4/call; loop). Don’t proceed while any REQUIRED is unanswered.
  2. AskUserQuestion to confirm each OPTIONAL default — user OKs or overrides; never apply silently.
  3. Re-print the checklist all-✅, then final AskUserQuestion: Confirm & build · Change something. Only after that final confirm → run the flow below.

add — full flow (ordered; do in this order)

Section titled “add — full flow (ordered; do in this order)”
FileAction
resources/excel/vehicle_<attr>.json (+.meta)Clone an existing table: cp vehicle_a.json vehicle_<attr>.json; write a .meta with a FRESH uuid. This is the 181-level hp/atk scaling.
resources/excel/vehicle_star_<attr>.json (+.meta)9-star trait table. FRESH uuid meta. See §4 for the attr format.
resources/excel/vehicle_list.jsonAppend the vN entry (see reference for full field list: id/career/tower/attr/anime/animeName(2)/gunX/gunY/wheels/radius/skill/leftMsg/rightMsg). Reuse an existing art’s anime/animeName2 (e.g. lanzhanche like v3) until real art exists.
resources/excel/local_vehicle.json2D array (16 lang cols). Append rows vehicle_<vid> (name) + vehicle_<vid>_info_0..8 (per-star text). Use a node script to keep the 16-column count exact — see reference.

2. Register the new excel sheets — REQUIRED, or it crashes

Section titled “2. Register the new excel sheets — REQUIRED, or it crashes”

excel.js loads sheets from a HARD-CODED list, not by scanning. Add BOTH new sheet names to the loadMany([...]) array in excel.js:

"vehicle_star_<attr>", // next to vehicle_star_a/b/c
"vehicle_<attr>", // next to vehicle_a/b/c

Miss this → excel.read("vehicle_star_<attr>") returns undefinedb[0] crash in createVehicleData (excel.js:~235). (memory project_excel_sheet_manifest)

3. Patch the hard-coded v1/v3 spots — THE bug-prevention step

Section titled “3. Patch the hard-coded v1/v3 spots — THE bug-prevention step”

grep -n "\[this.define.id\]" Client/assets/script and grep -rn '== "v3"\|"v1"\|"v3"' Client/assets/script. Add the new vid to EACH (mirror v3 since you likely reuse v3 art):

FileWhatWhy it matters
scripts/MyVehicle.js top-level var n = {v1:147, v3:167}add vN: 167CRITICAL. Tower mount Y-offset map. Missing → n[vid]=undefined → tower _x/_y = NaN → bullets spawn NaN → no attack.
components/visual/Avatar.js initDisplayTalent gateadd || id == "vN" to the early-returnElse v-N runs talent-block code meant only for other vehicles.
entities/vehicles/VehicleAvatar.js addBlockadd vid to the "v3" (→ side2) branchblock-side art.
scripts/VehicleModel.js addLeaderadd vid to the "v3" branchgarage pilot/leader position.
scripts/checkPointMenu.jsadd vid to the "v3" fix-node branchcheckpoint avatar offset.
ui/inventory/BackpackMenu.js updateBlockSideAvataradd vid to "v3" (→ side2)backpack block-side art.

(Not strictly needed if you alias shards — but know they exist: money.js SHARD_LIST, pc.js getVehiclesAttrGlobal hard-code v1+v3, EquipmentEncoder.js singleLetterMap, ItemMenu.js ["v1","v2","v3"].)

4. Star traits (see §“Traits” below if the design has explode-radius/crit/etc.)

Section titled “4. Star traits (see §“Traits” below if the design has explode-radius/crit/etc.)”

5. Shard source + unlock (for testing & real use)

Section titled “5. Shard source + unlock (for testing & real use)”
  • Temporary: alias the new vid to an existing vehicle’s shard pool with pc.getVehicleShardId(vid) (returns e.g. v1), and route EVERY shard-key site through it: GaragePage.js (×2: red-dot + display), VehicleUnlock.js (×4: display, consume, ad-grant id, ad-readback). Without aliasing, shard.<vid> has no faucet → never unlockable.
  • DEV testing: add a DEV_MODE branch on the garage unlock/upgrade buttons to grant/star-up directly (pc.addVehicle({id, star:1}) / pc.setVehicle({id, star:n+1})). Or console: pc.addVehicle({id:"vN",star:9}); pc.setChooseVehicle("vN").

6. Verify in-editor (no CLI runtime exists)

Section titled “6. Verify in-editor (no CLI runtime exists)”

Open Cocos on the project root (or worktree) Client/, Ctrl+P preview (forces JS recompile + imports new JSON).

  • Garage: the new vid auto-appears (garage iterates ALL vehicle_list entries).
  • Battle: equipment fires AND damages enemies. If bullets vanish → a NaN reached Ground.findGround (a guard there returns leftPart instead of crashing). Trace with a !isFinite log at Bullet.init walking obj → obj.obj to see whether tower _x or vehicle _x is NaN (that’s how the n[vid] bug was found).

vehicle_star_<attr>.json = 9 rows {star, attr, info, price, global?}. attr is a kit.parse-able string:

  • single stat: "{id:'vehicle_atk',num:0.25}"
  • multi-stat (1 star, many effects): ARRAY string "[{id:'vehicle_bltExploRange',num:0.25},{id:'vehicle_critChance',num:0.10}]". kit.parse (jsParser) handles arrays; other consumers (adventure.js/train.js) read .id/.global and skip arrays harmlessly.

Engine accumulates star attrs in pc.getVehicleAttr (self path) into config.attr of the vehicle. It only natively understands vehicle_atk / vehicle_hp. For any OTHER stat you must extend it (already done for vehicle_bltExploRange/critChance/critBonus):

  1. pc.js getVehicleAttr self-loop: make it array-aware (Array.isArray(u)?u:[u]) + add an if (uu.id == "vehicle_X") branch that accumulates into r.X.
  2. Entity.js initAttributes: make the attribute READ config.attr — e.g. r.init("critChance", o.critChance||0, …), r.init("bltExploRange", o.bltExploRange||1, …) (default-read, critBonus already reads o.critBonus||…||2).

Propagation: vehicle stat → equipped WEAPONS (critical, easy to miss)

Section titled “Propagation: vehicle stat → equipped WEAPONS (critical, easy to miss)”

Star stats land on the vehicle entity, but weapons are separate tower entities. Only some auto-propagate:

  • ATK / HP — already reach weapons (MyTower.getAtkScale chains tower→vehicle; HP is chassis). No work.
  • Crit / explode-radius / anything else — do NOT auto-reach weapons. Wire in MyTower.initBonus: the tower inherits the base vehicle’s bonus above the Entity default:
    var base = this.getBase(); // = the vehicle
    var defaults = { critChance:0, critBonus:2, bltExploRange:1 };
    for (var sn in defaults) {
    var bonus = base.attribute.get(sn) - defaults[sn]; // vehicle's star bonus
    if (bonus > 0) this.attribute.add(sn, bonus); // additive onto the tower
    }
    Timing is safe: vehicle initAttributes runs before initSub spawns towers. bltExploRange only affects explode bombs (Bomb.explosion reads obj.bltExploRange) → auto-scoped to Cannon/Mortar/Rocket. Crit reaches bullets via Proto.getPrev chain (bullet→tower). Safe for v1/v3 (bonus=0 → no-op).

Star numbers are cumulative (engine sums every star ≤ current). “+50% (Total)” at ⭐6 when ⭐1 was +25% → put num:0.25 at ⭐6. critChance is 0–1 absolute; critBonus default 2 (= crit ×2 = +100% dmg) so “+100% crit dmg” → num:1.


Gotcha quick-list (each cost real debugging on SIEGE/v4)

Section titled “Gotcha quick-list (each cost real debugging on SIEGE/v4)”
  • n[this.define.id] in MyVehicle.js missing vid → NaN tower → no attack. Hardest to find (a map, not an if).
  • excel.js loadMany missing the new sheet → boot crash.
  • New JSON in resources/ needs a .meta with a UNIQUE uuid (don’t copy a source .meta → uuid collision).
  • vehicle_a/c tables have NO hp_vehicle column → config.attr.hp = undefined (fine, falls back; don’t “fix” it).
  • career field (e.g. "炮") is display-only, not used in logic — any string is fine.
  • Opening the worktree in Cocos re-imports → thousands of touched .meta; at commit time stage ONLY the tank files.
  • A Ground.findGround isFinite guard exists (defensive) — it converts NaN-position crashes into vanished bullets, so “no crash but no attack” still means a NaN somewhere upstream.

npm run-style check not available; gate on: (1) garage shows vid, (2) unlock works (real shard or DEV), (3) in battle the tank moves, equipment fires AND damages, traits visibly apply (bigger explosions / crit numbers), (4) no […] non-finite warnings in console, (5) only the intended tank files are staged.