Skip to content

t1k:cocos:rushtank:entity-variant

FieldValue
Modulerushtank
Version1.2.0
Effortmedium
Tools

Keywords: cocos, dragonbones, mob, reskin, rustank, texture, variant

/t1k: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)”
When the user invokes this skill with data (art folders, 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 do it with defaults" from the user. Rule: `rules/workflow-gates.md`.

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

#FieldFeeds
1subcommandadd (new variants) OR update (re-skin existing)dispatch
2base entity to reuse (skeleton + AI) — which existing ENTITY id (e.g. a mob / *_boss)add step 3 register
3new art source — dir with the PNG(s) + skel name (<skel>_tex{N}.png)add/update step 1 import
4count N — how many variant texturesimport + gen-blocks
5variant id prefix + display nameadd step 3 register

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

FieldDefault if missing
boss variant? (add)clone the normal mob only; clone *_boss too if it must be a map boss
register locationmob-variants.js (most common); vehicle → tank.js; simple clone → mob_list.json
folder naming (--folder) / folderStart offset (update)derive from <skel>_tex{N}
localizationnone 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 ❌:

  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 & run · Change something. Only after that final confirm → run the flow below.

Parse the first arg. No arg → infer from intent (new art folders → add; “update art / re-skin existing” → update) and confirm.

SubcommandPurpose
addImport NEW art into new mob-new/<folder> folders → validate → register NEW variant entities.
updateRefresh 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.


  1. 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}.pngmob-new/<folder>/<skel>_tex.png; base <skel>_ske.dbbin + <skel>_tex.json come from anime/mob/. Refuses existing folders (use update instead). The importer preflights the complete batch, stages it, and publishes it transactionally; --dry-run prints the plan without writing.

  2. Validate: node .../validate-mob-variant.cjs — fix any [FAIL] (collision / dims) before registering.

  3. 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. id unique, name may be shared.
    • Load-order check: if the <baseId> ENTITY is defined in a file mob-variants.js does NOT already require() (it requires bonus/soldior/tank/boss), add require("./<file>") near the top (e.g. turtle lives in mob.js → add require("./mob")). Otherwise ENTITY[base] is undefined → crash.
  4. 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() in assets/editor/scripts/editor.js, which caches its list).


The variant folders already exist & are registered; only swap the PNG.

  1. 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]
    • --folder if the folder naming differs from the source png naming (e.g. existing folders are 机枪手_tex_4 with underscore, source pngs are 机枪手_tex1.png without).
    • --folderStart M offsets the TARGET folder index from the source index — source tex1..N → folders tex_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 .meta files are never replaced.
  2. 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).
  3. Reimport in Cocos → fresh preview. No mob-variants.js change needed.

CaseFile
Variant reusing a base ENTITY’s AI/blt (most common)assets/script/scripts/mob-variants.js (ENTITY.<id>={…}; RES.<id>=[…])
Vehicle variantassets/script/entities/vehicles/tank.js
Simple clone of a mob_list mobassets/resources/excel/mob_list.json (1 entry; createMobData clones base type)

The shared mob/shouren atlas (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/guiguiturtle (乌龟, 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: 1Entity.js:132 bossFlag = entity.boss || mapInstance.boss → enables the BossBar UI (vs FloatBar) and lets the map win-condition detect it. Setting boss:1 on a map instance gives only BossBar + win-detection, NOT the boss AI/stats — so still clone *_boss for full parity.
  • boss pilot (e.g. AITurtleBoss, multi-phase defModeHp), HumanAvatar ctor (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).

Terminal window
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"
  1. Never copy .meta → duplicate UUID across folders → bundle breaks → crash EffectManager.setCache "no data" … Cannot read 'addRef' of undefined. Delete copied .meta, reimport.
  2. Atlas json + png must match dimensions (width/height == real PNG). Validator catches DIMS_MISMATCH.
  3. texUUid is NOT a bug signal — it points to the base png even in working variants; Cocos resolves the texture via imagePath. Don’t chase it (an earlier session wrongly did). Validator treats it as INFO.
  4. Verify each base’s real atlas in soldior.js/mob.js (e.g. penhuobing/fireBat uses mob/penhuobing, NOT shouren). Don’t trust cross-block regex.
  5. Persistent setCache no data after heavy churn = stale Cocos cache → clean rebuild (close Cocos, delete Client/library + Client/temp, reopen). Registration changes don’t fix it (load layer).
  • scripts/import-art.cjs — place/overwrite variant art (modes add/update).
  • scripts/validate-mob-variant.cjs — audit folders (collision + dims), JSON facts.
  • scripts/gen-variant-blocks.cjs — print paste-ready ENTITY.<id> blocks cloning a base.
  • references/shouren-troops.md — the 10 shouren-atlas troops, bases, animeNames, quirks.