t1k:cocos:rushtank:entity-variant
| Field | Value |
|---|---|
| Module | rushtank |
| Version | 1.2.0 |
| Effort | medium |
| Tools | — |
Keywords: cocos, dragonbones, mob, reskin, rustank, texture, variant
How to invoke
Section titled “How to invoke”/t1k:cocos:rushtank:entity-variantcocos-rushtank-entity-variant
Section titled “cocos-rushtank-entity-variant”Manage texture-swap entity variants: same DragonBones skeleton + AI as an existing entity, only the
art (texture) and animeName differ. SSOT facts: memory project_mob_variant_workflow.
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 | subcommand — add (new variants) OR update (re-skin existing) | dispatch |
| 2 | base entity to reuse (skeleton + AI) — which existing ENTITY id (e.g. a mob / *_boss) | add step 3 register |
| 3 | new art source — dir with the PNG(s) + skel name (<skel>_tex{N}.png) | add/update step 1 import |
| 4 | count N — how many variant textures | import + gen-blocks |
| 5 | variant id prefix + display name | add step 3 register |
OPTIONAL / derivable (❌ → note the default, don’t block):
| Field | Default if missing |
|---|---|
boss variant? (add) | clone the normal mob only; clone *_boss too if it must be a map boss |
| register location | mob-variants.js (most common); vehicle → tank.js; simple clone → mob_list.json |
folder naming (--folder) / folderStart offset (update) | derive from <skel>_tex{N} |
| localization | none needed for texture variants unless a new display name row is required |
Emit exactly this shape, then ask:
Input check — <variant>: REQUIRED ✅ subcommand : add ❌ base entity : (missing — which ENTITY's skeleton/AI to reuse?) ✅ art + skel : <dir>, skel=<skel>, N=<count> … OPTIONAL (defaults ready) ✅ register loc : mob-variants.js ❌ boss variant : normal mob only (default) → Ready: <R>/5 required present.Then ASK — every gap, until zero ❌:
AskUserQuestionfor each ❌ REQUIRED (batch ≤4/call; loop). Don’t proceed while any REQUIRED is unanswered.AskUserQuestionto confirm each OPTIONAL default — user OKs or overrides; never apply silently.- Re-print the checklist all-✅, then final
AskUserQuestion: Confirm & run · Change something. Only after that final confirm → run the flow below.
Subcommand dispatch
Section titled “Subcommand dispatch”Parse the first arg. No arg → infer from intent (new art folders → add; “update art / re-skin existing”
→ update) and confirm.
| Subcommand | Purpose |
|---|---|
add | Import NEW art into new mob-new/<folder> folders → validate → register NEW variant entities. |
update | Refresh the TEXTURE of EXISTING (already-registered) variant folders. No registration change. |
Scripts (run from Client/): scripts/import-art.cjs, scripts/validate-mob-variant.cjs,
scripts/gen-variant-blocks.cjs.
add — full flow (new variants)
Section titled “add — full flow (new variants)”-
Import art — copy 3 source files per variant (NEVER
.meta):Terminal window node .claude/modules/rushtank/skills/t1k-cocos-rushtank-entity-variant/scripts/import-art.cjs --mode add \--src "<dir with new pngs>" --skel <skel> --count N \[--folder "<skel>_tex{N}"] [--srcPng "<skel>_tex{N}.png"] [--baseDir <dir-with-base-_ske/_tex.json>] [--dry-run]Source
<skel>_tex{N}.png→mob-new/<folder>/<skel>_tex.png; base<skel>_ske.dbbin+<skel>_tex.jsoncome fromanime/mob/. Refuses existing folders (useupdateinstead). The importer preflights the complete batch, stages it, and publishes it transactionally;--dry-runprints the plan without writing. -
Validate:
node .../validate-mob-variant.cjs— fix any[FAIL](collision / dims) before registering. -
Register in
mob-variants.js:node .../gen-variant-blocks.cjs --base <baseId> --anime <animeName> --name "<display>" --id <prefix> --count N --atlas "mob/mob-new/<folder>/<skel>"- Paste above the final
cc.log("[MobVariants] Loaded N …"); bump N.idunique,namemay be shared. - Load-order check: if the
<baseId>ENTITY is defined in a filemob-variants.jsdoes NOT alreadyrequire()(it requiresbonus/soldior/tank/boss), addrequire("./<file>")near the top (e.g.turtlelives inmob.js→ addrequire("./mob")). OtherwiseENTITY[base]is undefined → crash.
-
Test in the level-editor palette. Registration (
editor:1) is the finish line — DO NOT edit gameplay maps. Open Cocos → reimport the new folders (validate[WARN] Not imported) → fresh preview → drag the variant from the palette (getSortList()inassets/editor/scripts/editor.js, which caches its list).
update — re-skin existing variants
Section titled “update — re-skin existing variants”The variant folders already exist & are registered; only swap the PNG.
- Overwrite texture (keeps
_ske/_tex.json/.meta→ UUID & registration untouched):Terminal window node .../import-art.cjs --mode update --src "<dir with new pngs>" --skel <skel> --count N \[--folder "<skel>_tex_{N}"] [--srcPng "<skel>_tex{N}.png"] [--folderStart M] [--dry-run]--folderif the folder naming differs from the source png naming (e.g. existing folders are机枪手_tex_4with underscore, source pngs are机枪手_tex1.pngwithout).--folderStart Moffsets the TARGET folder index from the source index — sourcetex1..N→ folderstex_M..(M+N-1). E.g. update the 4 re-used 机枪手 folders with 4 new distinct skins:--skel 机枪手 --count 4 --folderStart 4 --folder "机枪手_tex_{N}"(src tex1→tex_4 … tex4→tex_7).- The complete update batch is preflighted and rolled back if any copy/publish step fails; existing
.metafiles are never replaced.
- Validate:
node .../validate-mob-variant.cjs(dims of the new png must still match the atlas json; the duplicate-art INFO disappearing confirms the re-skin took). - Reimport in Cocos → fresh preview. No
mob-variants.jschange needed.
Where to register (3 locations)
Section titled “Where to register (3 locations)”| Case | File |
|---|---|
| Variant reusing a base ENTITY’s AI/blt (most common) | assets/script/scripts/mob-variants.js (ENTITY.<id>={…}; RES.<id>=[…]) |
| Vehicle variant | assets/script/entities/vehicles/tank.js |
Simple clone of a mob_list mob | assets/resources/excel/mob_list.json (1 entry; createMobData clones base type) |
The shared
mob/shourenatlas (10 troops via one skeleton) + base/animeName table:references/shouren-troops.md.
Boss variants — clone the *_boss entity, NOT the normal mob
Section titled “Boss variants — clone the *_boss entity, NOT the normal mob”A skeleton is often shared by a normal mob (in mob.js) AND a boss (in boss.js) via the same
animeName — e.g. mob/guigui → turtle (乌龟, mob.js, AITurtle, hp 4k) and turtle_boss (boss.js,
hp 45k). A texture variant re-skins BOTH. If a variant must be a map boss, clone the *_boss entity,
because the normal mob LACKS the boss-only fields:
boss: 1—Entity.js:132 bossFlag = entity.boss || mapInstance.boss→ enables the BossBar UI (vs FloatBar) and lets the map win-condition detect it. Settingboss:1on a map instance gives only BossBar + win-detection, NOT the boss AI/stats — so still clone*_bossfor full parity.- boss
pilot(e.g.AITurtleBoss, multi-phasedefModeHp),HumanAvatarctor (not$armatureAvatar),attackSequence2(phase 2), large hp,deathEffect: bossDeath, extra bullet types.
Recommended: keep BOTH per texture — <x>1-4 (clone normal mob) + <x>_boss1-4 (clone <x>_boss),
all pointing at the same guigui_tex{N} folders. Win-on-boss-death is a map event
(cond type:3 DEED-death on the boss instance id → act: win, see levelDefine.js ACT_EVENT.win) — set
by the level designer, not the entity. Load-order: *_boss lives in boss.js which mob-variants.js
already require()s; the normal mob may live in mob.js → add require("./mob") (see add step 3).
node .../gen-variant-blocks.cjs --base turtle_boss --anime guigui --name TurtleBoss --id guigui_boss --count 4 --atlas "mob/mob-new/guigui_tex{N}/guigui"Gotchas (learned the hard way)
Section titled “Gotchas (learned the hard way)”- Never copy
.meta→ duplicate UUID across folders → bundle breaks → crashEffectManager.setCache "no data" … Cannot read 'addRef' of undefined. Delete copied.meta, reimport. - Atlas json + png must match dimensions (
width/height== real PNG). Validator catchesDIMS_MISMATCH. texUUidis NOT a bug signal — it points to the base png even in working variants; Cocos resolves the texture viaimagePath. Don’t chase it (an earlier session wrongly did). Validator treats it as INFO.- Verify each base’s real atlas in
soldior.js/mob.js(e.g.penhuobing/fireBatusesmob/penhuobing, NOT shouren). Don’t trust cross-block regex. - Persistent
setCache no dataafter heavy churn = stale Cocos cache → clean rebuild (close Cocos, deleteClient/library+Client/temp, reopen). Registration changes don’t fix it (load layer).
scripts/import-art.cjs— place/overwrite variant art (modesadd/update).scripts/validate-mob-variant.cjs— audit folders (collision + dims), JSON facts.scripts/gen-variant-blocks.cjs— print paste-readyENTITY.<id>blocks cloning a base.references/shouren-troops.md— the 10 shouren-atlas troops, bases, animeNames, quirks.