Files
ark-stronghold-protocol/docs/research/07-assets.md
T
sganggs 01a44cd288 卫戍协议:盟约 web remake — initial import
Online co-op browser remake of Arknights' Stronghold Protocol: Alliance
(client-simulated combat, official waves/bonds/items/strategies, 3D board,
facing wheel, operator loadout, official emotes, deployment scripts).
2026-09-29 16:21:31 +08:00

40 KiB
Raw Blame History

07 — Art and audio assets: sources, feasibility and the final plan

Scope: every art and audio asset the fan remake of 卫戍协议:盟约 (act2autochess) needs, with URL patterns that have been checked, measured hit rates, file sizes and fallbacks. Machine-readable companion: docs/research/07-assets.json. It maps every id to resolved URLs and byte sizes: 138 operators, 20 tokens, 200 enemies, 23 bonds, 59 shop items, 40 bands, the UI sprite groups and the audio banks. All 4,451 unique URLs in that JSON were HEAD-checked on 2026-09-27 and returned 200.

Probe artifacts are in the scratchpad at …/scratchpad/gd/extra/assets/:

  • Git-tree listings: y_*.txt, fx_spine.txt, am_enemies.txt, aa2_*.txt, aa2v_*.txt.
  • Id lists: charids.txt, enemyids.txt, tokenids.txt, trapids.txt.
  • Spine sample: spine_sample/.
  • Browser render proof: spine_test.png, produced from spine_test.html.
  • Generator script: gen_assets_json.py.

0. TL;DR: the final plan

Class Source (primary) Pattern Hit rate (pool) Typical file Fallback
a) Avatar (square) yuanyan3060/ArknightsGameResource avatar/ {charId}.png, E2: {charId}_2.png 138/138; E2 122/138 180×180 PNG, ~58 KB E2 missing → base {charId}.png (the 16 misses are the 预备干员/Sharp/Pith/… char_60x/61x ids that have no E2 art)
b) Half-body portrait same repo portrait/ {charId}_1.png, E2 _2.png 138/138; E2 122/138 180×360 PNG, ~117 KB _1
b') Full art (detail panel only) same repo skin/ {charId}_1b.png, E2 _2b.png 138/138; E2 122/138 681×1317, ~0.8 MB (E2 ~3.2 MB) portrait _1; lazy-load only
c) Skill icon same repo skill/ skill_icon_{skill.iconId ?? skillId}.png (percent-encode [ ]) 317/317 (all skills of pool operators; 144 are default skills) 128×128, ~19 KB arts/ui/[uc]charcommon/skills/empty_skill.png
d) Profession / sub-profession ArknightsAssets/ArknightsAssets2 @cn assets/dyn/arts/… profession_hub/icon_{prof}.png, ui/subprofessionicon/sub_{subProfessionId}_icon.png 8/8, 58/58 ~12 KB / ~2 KB battlecard icon_profession_*.png
e) Enemy icon yuanyan enemy/ {enemyId}.png 200/200 ~158×158, ~44 KB none needed
f) Bond icons: the real autochess ones AA2 ui/autochess/[uc]autochesscommon/arts/bondicon/ {iconId.toLowerCase()}.png, e.g. icon_yanship.png 23/23 ~110×98 white glyph, ~4 KB arts/camplogo/logo_{nation}.png (see §4)
g) Item / trap icons: the real autochess ones AA2 …/arts/shopitemicon/ {trapId}.png 59/59 (56 EQUIP + 3 MAGIC) ~137×157, ~19 KB none needed
g') Band (strategy) icons AA2 …/arts/bandicon/ icon_{bandId minus "band_"}.png 40/40 180×180, ~69 KB —
h) Map tiles none usable: the game map is a 3D Unity scene — — — Draw tiles procedurally (§7). Use real autochess backdrops for the lobby and prep screens.
i) UI: rarity, elite, chess-level, shop, HUD AA2 ui/autochess/… (537 PNGs) + arts/rarity_hub, elite_hub see JSON autochessUi, arts 100% 0.3–250 KB CSS-drawn panels (Unity 9-slice borders are not in the dump)
j) Fonts Google Fonts + self-hosted Bender / Novecento §8 — — system CJK
k) BGM + SFX: the real game audio AA2 @voice branch assets/dyn/audio/sound_beta_2/… mapped by audio_data.json banks BGM 3/3; per-operator SFX 138/138; enemies 185/200; autochess UI SFX 47 banks mp3; BGM ~2 MB, SFX 5–60 KB WebAudio-synthesized blips (§6.4)
l) Battle Spine (in-battle chibi) operators: fexli/ArknightsResource spine/; enemies: isHarryh/Ark-Models models_enemies/ spine/{id}/{id}/{Front|Back}/{id}.{skel,atlas,png}; enemies via models_data.json assetList ops Front 138/138, Back 135/138; enemies 198/200 median op front+back ≈ 690 KB; enemy ≈ 134 KB missing Back → mirrored Front; missing enemy → static icon sprite (§5.6)
Spine runtime PixiJS 7.4.2 + pixi-spine 4.0.6 (UMD from jsDelivr) — render checked in headless Chrome 456 KB + 362 KB —

Size budget, all classes, only what the pool needs

Size
Raw ≈ 200 MB (≈ 700 MB if full art is included, so leave full art out of the bundle)
After build-time optimization ≈ 65–75 MB: WebP for images, gzip/brotli for skel
Initial page load (lazy-loading Spine per board) < 6 MB

1. Id inventory (from act2autochess)

Set Count Notes
Operators (charShopChessDatas[*].charId ∪ backupCharId) 138 unique charIds over 133 chess 74 PRESET, 55 NORMAL, 4 DIY. DIY chess have charId=null, so the operator is chosen at runtime and uses the chosen operator's assets. Chess levels 1–6: 20/19/21/26/25/22.
Most chess are evolvePhase=PHASE_2 94 PHASE_2 (+ 39 PHASE_1) non-golden; every golden chess is PHASE_2 → Prefer E2 avatar/portrait (_2) and fall back to _1.
Tokens/summons referenced by pool operators 20 displayTokenDict and overrideTokenKey. Bond summon "炎佑" (Yan 6/9) has no token id in the data, see §5.6.
Enemies 200 Union of enemyInfoDict, specialEnemyInfoDict, bossInfoDict-linked ids and level files. All 200 are in enemy_handbook_table.
Bonds 23 bondInfoDict[*].iconId: icon_yanShip … icon_suntShip
Shop items 59 trapShopChessDatas[*].trapId = trap_10xx_acarmNNN and friends
Bands (strategies) 40 bandDataListDict keys band_*
Sub-professions in pool 58 e.g. fastshot, protector, ritualist, primprotector, counsellor, hunter…
Nations in pool 18 + None bolivar, columbia, egir, higashi, iberia, kazimierz, kjerag, laterano, leithanien, lungmen, rhodes, rim, sami, sargon, siracusa, ursus, victoria, yan

Newest operators in the pool, all verified present in every class: char_4211_snhunt 雪猎, char_4207_branch 折桠, char_4196_reckpr 录武官, char_4194_rmixer 信仰搅拌机, char_4193_lemuen 蕾缪安, char_4191_tippi 蒂比, char_1047_halo2 溯光星源, char_1046_sbell2 圣聆初雪, char_1045_svash2 凛御银灰, and the activity-only char_616_pithst 盟约·辅助干员 and char_617_sharp2 领主·Sharp.


2. Sources evaluated

Repo Branch Updated What it has Verdict
yuanyan3060/ArknightsGameResource main 2026-09-24 avatar (incl. token_*, non-autochess trap_*), portrait, skin (full art b), skill, enemy, item, item_rarity_img, map (not autochess) Primary for 2D portraits and icons. AGPL-3.0 applies to its code; the art is © Hypergryph.
ArknightsAssets/ArknightsAssets2 cn (art), voice (all audio) 2026-09-23 Full ArknightsStudio dump: assets/dyn/ui/autochess/** (real autochess UI), activity/[uc]act2autochess/**, arts/** (camplogo, profession, rarity, elite, subprofession, loading illustrations), battle/[pack]common/**, and all audio under @voice Primary for autochess-specific icons, UI and ALL audio. Battle spine here is incomplete: atlas/skel without PNGs.
fexli/ArknightsResource main 2026-09-20 spine/{charId}/{charId}/{Front,Back}/ battle models + base (build_*) models + skins; camplogo, charpor, skills… Primary for operator battle Spine
isHarryh/Ark-Models main 2026-08-02 models_enemies/ enemy battle Spine (PC build, PMA forced), models/ base (基建) chibis only, models_illust/ dynamic illustrations; models_data.json index Primary for enemy battle Spine. Its operator models are base chibis, not battle chibis.
Aceship/Arknight-Images main 2024-05 (stale) avatars etc. Hit rate 89/138. It lacks every 2024+ operator. Not recommended.
PRTS media.prts.wiki/{md5[0]}/{md5[0:2]}/{文件名} — live e.g. 头像_德克萨斯.png returns 200 Last-resort fallback only: Chinese-name keyed, and hotlinking is discouraged. torappu.prts.wiki timed out from this host.
web.hycdn.cn (official CDN) — — guessed avatar path → 404 not usable
TimWangZi/The-font-of-Arknights master — Bender, Novecento Wide, 思源宋体 font source (§8)

CDN behaviour, all checked

  • raw.githubusercontent.com returns access-control-allow-origin: * with cache-control: max-age=300.
  • cdn.jsdelivr.net/gh/<repo>@<branch>/… works with CORS * and a 7-day cache for yuanyan, fexli, Ark-Models and AA2 @cn.
  • jsDelivr returns 404 for the AA2 @voice branch. Use raw for audio.
  • Do not hotlink at runtime (GitHub rate limits, availability, and it is impolite). Mirror at build time (§9).

Path gotchas

  • [uc] → %5Buc%5D and [pack] → %5Bpack%5D.
  • Skill icons such as skill_icon_skcom_atk_up[1].png need %5B1%5D. curl's globbing also breaks on unencoded brackets.
  • Skin ids contain # → %23. Skins are not needed.
  • Bond iconId is camelCase in data (icon_yanShip) but lower-case on disk (icon_yanship.png).
  • Band icon = icon_ + bandId minus band_.
  • Liskarm's default battle model lives at spine/char_107_liskam/char_107_liskarm/…, a dump-side typo.
  • char_1012_skadi2 has a single Spine/ folder instead of Front/ and Back/.
  • Enemy spine filenames can differ from the folder name: 1043_zomsbr/enemy_1043_zomsabr.*, 1169_duphlx/enemy_1169_duphx.*. Always use models_data.json → data[key].assetList. 07-assets.json already resolves this.

3. 2D portraits and icons (classes a–e, i)

a) Avatars

  • URL: https://raw.githubusercontent.com/yuanyan3060/ArknightsGameResource/main/avatar/{charId}.png
    • E2 variant: {charId}_2.png.
    • Mirror: https://cdn.jsdelivr.net/gh/yuanyan3060/ArknightsGameResource@main/avatar/….
  • Checked by curling all 138 charIds (xargs -P16): 138/138 200. E2 variant: 122/138.
  • The 16 without E2 are char_600_cpione, 601_cguard, 602_cdfend, 603_csnipe, 604_ccast, 605_cmedic, 606_csuppo, 607_cspec, 608_acpion, 609_acguad, 610_acfend, 611_acnipe, 612_accast, 613_acmedc, 614_acsupo, 615_acspec. They have no E2 art in-game, so use the base image.
  • Size 180×180 RGBA, avg 58 KB (8.4 MB for the set, preferring E2). WebP q88 comes to about 15 KB each.
  • Tokens: avatar/{tokenId}.png hits 15/20.
    • Missing: token_10012_rosmon_shield, token_10039_ulpia_block, token_10040_siege2_vlion, token_10057_svash2_eagle, token_10058_sbell2_icetgt.
    • Fallback: owner operator avatar with a small "召唤物" badge, or the battlecard icon_profession_token.png.

b) Portraits

  • …/portrait/{charId}_1.png (E2 _2.png), 180×360 半身像 (the squad-card crop used on shop cards). 138/138; E2 122/138. Avg 117 KB, about 31 KB as WebP q88.
  • Full art …/skin/{charId}_1b.png is 681×1317, avg 787 KB; E2 _2b averages 3.2 MB. Use it only in the operator detail modal, lazy-loaded. Keep it out of the bundle; fetch on demand from jsDelivr.

c) Skill icons

  • …/skill/skill_icon_{iconId}.png, where iconId = skill_table[skillId].iconId ?? skillId. 317/317 (every skill of every pool operator, after percent-encoding brackets). 128×128, avg 19 KB.
  • The shop only shows the chess's default skill (defaultSkillIndex), which is 144 distinct icons (≈ 2.7 MB raw).

d) Profession and sub-profession

  • https://raw.githubusercontent.com/ArknightsAssets/ArknightsAssets2/cn/assets/dyn/arts/profession_hub/icon_{caster|medic|pioneer|sniper|special|support|tank|warrior}.png
    • Large white variant: profession_large_hub/icon_profession_{p}_large_white.png.
    • Small battle-card variant: arts/ui/[uc]battlecommon/ui_battle_new/battlecard/icon_profession_{p}.png, which also includes …_token.png.
  • Sub-profession: …/arts/ui/subprofessionicon/sub_{subProfessionId}_icon.png. 58/58, ~2 KB white glyphs.

e) Enemies

  • …yuanyan…/enemy/{enemyId}.png, 200/200, avg 44 KB.
  • The boss HP-bar portrait style exists as arts/ui/[uc]battlecommon/ui_battle_new/enemybossinfo/… for only a few bosses. Use the enemy icon inside sprite_enemy_boss_avatar_bg.png.

i) Rarity, elite and misc UI (all 200)

  • Rarity stars: arts/rarity_hub/rarity_{0..5}.png, plus rarity_yellow_{i}, rarity_black_{i} and rarity_left_{i}.
    • Autochess-specific: ui/autochess/[uc]autochessouter/arts/charraritysprite/rarity_{0..5}.png.
  • Elite icons: arts/elite_hub/elite_{0..3}.png, _large, _card.
    • Autochess eliteIconId values (shopCharChessInfoData[*].eliteIconId) map to …/[uc]autochessouter/arts/charelitesprite/{char_elite_base_1|char_elite_base_2|char_elite_gold_2}.png. Also available: char_elite_detail_*.
  • Chess tier (等阶 1–6): …/chesslevelsprite/chess_level_icon_{0..6}.png, chess_level_detail_{0..6}.png.
    • Shop card: …/autochess_shop_card_item/img_chess_level_{1..6}.png, frame_lv{1..3}.png, bg_common.png, ice_matte.png (frozen), icon_ice.png.
  • Shop level (调度中心): …/shoplevelsprite/shop_level_icon_{1..6}.png, shop_level_tag_{1..6}.png (150×595 vertical banners). Tag bg colours in data: #434343, #626654, #445760, #615B74, #6C5E41, #5d341e.
  • Garrison (特质) type icons: …/garrisontypeicon/{icon_battle|icon_bond|icon_gold|icon_support}.png, plus s_ small versions. These match garrisonDataDict[*].eventTypeIcon.
  • Mode icons mode_{normal|hard|abyss|funny|training}_icon.png. Buff icons icon_{boss_debuff|enemy_debuff|player_buff|stage_buff|team_buff}.png, matching effectDecoIconId.
  • Title icons comment_icon_{1..6}.png (playerTitle picId). Trophies trophy_level{1..5}_icon.png. Enemy-type icons {fly|times|dot|element|invisible|reflection|special}_icon.png, matching enemyInfoDict keys.
  • Bond board pips: …/[uc]autochessbattle/arts/bondboardicon/{n}_{k}.png (1_0 … 4_4).
  • HUD: …/hud/autochess_hud_panel/*.png (55 files: icon_hp, icon_battle, icon_boss, icon_coop, icon_rest, hp_bar, bg_*…).
  • Shop: …/shop/autochess_shop/*.png (refresh_icon, frozen_icon, upgrade_max, bg_money, cost_free…).
  • Equip slot: …/act2autochess_equip_slot_panel/*.png. Round banner: …/dialog/round_start_dialog/*.png.
  • Loading illustrations (1920-class, ~0.9 MB each): arts/loadingillusts/loading_ac_{core|normal|hard|abyss|funny|prototype|train|training}.png.
    • modeDataDict.loadingPicId values such as loading_AC_core → lower-case the id.
  • Lobby / backdrop art:
    • activity/[uc]act2autochess/prefabs/act2autochess_activity_stage/{entry_bkg_01,entry_bkg_02,bkg_01,bkg_02,bg_mountains_tiled,season_logo,img_coin}.png.
    • Mode select: ui/autochess/[uc]autochessouter/modechoice/auto_chess_mode_choice_state/{normal,hard,abyss,funny}_rhodes_island.png.
    • Battle ready: …/battleready/auto_chess_battle_ready_state/{bg_mountain1,bg_mountain2,bg_terrain,rhodes}.png.
  • Battle common sprites: battle/[pack]common/sprites/{sprite_shadow,sprite_direction_arrow,sprite_direction_ring,sprite_box_shadow}.png and …/projectiles/projectile_arrow.png.
    • Battle UI: arts/ui/[uc]battlecommon/ui_battle_new/{btn_speed_1x,btn_speed_2x,btn_pause,slider_hp_back,slider_hp_fill,sprite_skill_ready…}.png.
  • Caveat: Unity sprite 9-slice borders are not in the dump. Use the art for icons, badges and backdrops, and draw frames and panels in CSS or canvas.

4. Bond icons (f): the real ones are available

https://raw.githubusercontent.com/ArknightsAssets/ArknightsAssets2/cn/assets/dyn/ui/autochess/%5Buc%5Dautochesscommon/arts/bondicon/{iconId lowercased}.png All 23 are verified: white glyphs on transparent, so tint via CSS filter or canvas globalCompositeOperation='source-in'. Nation fallbacks come from arts/camplogo/logo_{id}.png (44 logos, 20–110 KB). The non-nation fallbacks below are [ASSUMED] suggestions.

bondId Name Real icon file Bytes Fallback
yanShip 炎 icon_yanship.png 5466 camplogo/logo_yan.png
sargonShip 萨尔贡 icon_sargonship.png 2845 camplogo/logo_sargon.png
victoriaShip 维多利亚 icon_victoriaship.png 4267 camplogo/logo_victoria.png
kjeragShip 谢拉格 icon_kjeragship.png 5255 camplogo/logo_kjerag.png
lateranoShip 拉特兰 icon_lateranoship.png 4725 camplogo/logo_laterano.png
egirShip 阿戈尔 icon_egirship.png 4715 camplogo/logo_egir.png
siracusaShip 叙拉古 icon_siracusaship.png 6956 camplogo/logo_siracusa.png
kazimierzShip 卡西米尔 icon_kazimierzship.png 4288 camplogo/logo_kazimierz.png
preciShip 精准 icon_preciship.png 4872 sub_fastshot_icon
swiftShip 迅捷 icon_swiftship.png 3675 icon_profession_pioneer
skillfulShip 灵巧 icon_skillfulship.png 4166 sub_executor_icon
arcaneShip 奥术 icon_arcaneship.png 4114 icon_profession_caster
steadShip 坚守 icon_steadship.png 2702 icon_profession_tank
deputShip 助力 icon_deputship.png 3726 icon_profession_support
visiShip 远见 icon_visiship.png 3451 garrisontypeicon/icon_gold
miraShip 奇迹 icon_miraship.png 4945 garrisontypeicon/icon_gold (tinted)
investShip 投资人 icon_investship.png 2058 garrisontypeicon/icon_gold
raidShip 突袭 icon_raidship.png 2982 sub_executor_icon
indomShip 不屈 icon_indomship.png 3439 sub_unyield_icon
maniShip 调和 icon_maniship.png 3124 garrisontypeicon/icon_bond
emptyShip 协防干员 icon_emptyship.png 2762 battlecard icon_profession_token
soloShip 独行 icon_soloship.png 2169 sub_lord_icon
suntShip 绝技 icon_suntship.png 3366 elite_hub/elite_2.png

Band / strategy icons (g'): …/[uc]autochesscommon/arts/bandicon/icon_{bldsk|amiya|duyaoy|sarkazb|orchid|justin|ermengard|lmlee|kirara|pepe|harold|sciurus|paganini|clementia|emperor|mberry|humus|quintus|yu|doberm|cathy|malkie|qalaisa|chen|damaztic|pith|dusk|cannot|ducklord|lisa|vodfox|ioleta|jesica|mlyss|makiri|fang|mlynar|chiave|narant|amedic}.png. All 40/40 are 180×180 "STRATEGY" cards, ~69 KB each.

5. Items / traps (g)

…/[uc]autochesscommon/arts/shopitemicon/{trapId}.png, 59/59, ~137×157 on a transparent background, avg 19 KB, e.g. trap_1041_acarm041 维式重锤.

  • The 3 non-shop special traps are effect ids with no icons: trap_copy_front_char, trap_create_self_choice, trap_disney_special. Use the icon of the item that grants them.
  • Rarity backgrounds for item frames: yuanyan item_rarity_img/sprite_item_r{1..6}.png.
  • Level-placed traps (trap_1104_aclasert, trap_1105_accrate, trap_1112_acblzd, trap_098_mire, trap_013_blower, trap_042_tidectrl) have no autochess icon. Draw them as tile overlays.

5b. Battle Spine (l): the real in-battle animated chibis

5.1 Sources and verification

  • Operators (fexli): https://raw.githubusercontent.com/fexli/ArknightsResource/main/spine/{charId}/{charId}/{Front|Back}/{charId}.{skel|atlas|png}
    • jsDelivr: https://cdn.jsdelivr.net/gh/fexli/ArknightsResource@main/spine/….
    • Front 138/138. Back 135/138: missing for char_4134_cetsyr 魔王, char_291_aglina 安洁莉娜 and char_1012_skadi2 浊心斯卡蒂. Skadi2 has one model in a Spine/ folder. These are supporters that are often shown only from the front.
    • One texture page per model.
  • Enemies (Ark-Models): https://raw.githubusercontent.com/isHarryh/Ark-Models/main/models_enemies/{enemyId minus "enemy_"}/{assetList[.skel|.atlas|.png]}. 198/200.
    • Missing: enemy_1305_mhslim(_2). AA2 has its atlas and skel but not its PNG.
  • Sample downloaded: char_102_texas Front and Back plus enemy_1007_slime → …/scratchpad/gd/extra/assets/spine_sample/.
    • Texas Front: skel 187,826 B, atlas 4,903 B, png 65,126 B (512×512).
    • Texas Back: 73,065 / 3,346 / 47,290 B (256×256).
    • Slime: 16,567 / 552 / 11,659 B (128×128).
  • Skel header: read with a varint-string parser, and again with the pixi-spine 3.8 SkeletonBinary in Node. Every sampled file reports version 3.8.99: 11 operators and 5 enemies.
  • Browser proof: headless Chrome rendered Texas Front (Attack_Loop), Texas Back, 雪猎 Front and enemy “自在” (enemy_1517_xi, boss) with PixiJS 7.4.2 and pixi-spine 4.0.6 loaded directly from jsDelivr, with no errors. Screenshot: …/scratchpad/gd/extra/assets/spine_test.png.

5.2 Sizes

Set Raw Optimized (skel+atlas gzip ≈ 24%, PNG → lossless WebP ≈ 50%)
Operators Front (138) skel 44.3 + png 20.6 + atlas 1.2 = 66 MB —
Operators Back (135) skel 24.6 + png 16.7 + atlas 0.8 = 42 MB —
Operators total 108 MB; median op 690 KB; max char_427_vigil 3.1 MB ≈ 36 MB
Enemies (198) skel 14.9 + png 11.1 + atlas 0.5 = 26.6 MB; avg 134 KB ≈ 9 MB

Gzip measurements: texas skel 188 KB → 46 KB; snhunt 659 KB → 122 KB; ghost2 933 KB → 184 KB. A texas 512² PNG goes from 65 KB to 31 KB as lossless WebP. Estimate for 133 ops + 60 enemies on one server: about 110 MB raw, about 38 MB served. A client only downloads the models on its board or in its shop, typically 10–25 models = 5–15 MB.

5.3 Runtime: use pixi-spine, not official spine-ts 3.8

  • CDN:
    • https://cdn.jsdelivr.net/npm/pixi.js@7.4.2/dist/pixi.min.js (456 KB)
    • https://cdn.jsdelivr.net/npm/pixi-spine@4.0.6/dist/pixi-spine.js (362 KB, UMD → PIXI.spine, auto-detects 3.7/3.8/4.0/4.1)
    • Or @pixi-spine/all-3.8@4.0.6/dist/pixi-spine-3.8.js (148 KB) for a 3.8-only build.
    • npm: pixi.js@7, pixi-spine@4.0.6. pixi-spine 4 needs PixiJS 7, not 8.
  • Load with PIXI.Assets.load(url.skel). The .atlas and .png must sit next to it with the same basename. Then new PIXI.spine.Spine(res.spineData) and spine.state.setAnimation(0, name, loop).
  • Why not official spine-ts 3.8 (cdn.jsdelivr.net/gh/EsotericSoftware/spine-runtimes@3.8/spine-ts/build/spine-webgl.js, reachable):
    1. Its atlas parser only treats rotate: true|90 as rotated. Arknights atlases use rotate: 270 (seen in ghost2 and ulpia), so those regions render wrong.
    2. The fexli atlases omit the size: line. pixi-spine falls back to the real texture size; spine-ts 3.8 divides by page width 0.
  • Atlas quirks to normalize at build time:
    • Insert size: W,H after the page name, read from the PNG.
    • If PNGs are converted to WebP, rename the page line.
  • Alpha:
    • fexli operator textures are straight alpha: a 512² texas texture has 193 pixels with RGB > A, up to 108 over. Load them normally.
    • Ark-Models enemy textures are premultiplied (README: "强制启用 PMA"; slime has 1 violation of ≤2). Add a pma: true page line to those atlases (pixi-spine honours it and sets ALPHA_MODES.PMA), or set baseTexture.alphaMode = PIXI.ALPHA_MODES.PMA. Otherwise edges come out dark.
  • Licensing note: pixi-spine is MIT, but it embeds Spine Runtime code under the Spine Runtimes License, which technically expects a Spine Editor licence. This is commonly tolerated for non-commercial fan tools, and should be noted in the credits.

5.4 Animation names (parsed; the naming varies, so build a resolver)

Unit Animations
char_103_angel (F) Attack, Default, Die, Idle, Start
char_102_texas (F) Attack_Start/Loop/End, Default, Die, Idle, Skill, Start. Back model has no Die, no Skill.
char_172_svrash (F) Attack, Combat, Combat_Down, Default, Die, Idle, Skill, Start
char_350_surtr (F) Attack, Die, Idle, Skill_2, Skill_2_Down, Skill_3_Begin/Idle/Loop, Start
char_2015_dusk (F) Attack, Die, Idle, Skill2_Begin/Loop/End, Skill3_Attack, Start. No underscore after "Skill".
char_4211_snhunt (F) Attack_Begin/Loop/End, Attack_Down_, Reload_, Skill_1, Skill_2_Begin/Loop/End, Skill_Down_*, Die, Idle, Start. Events: BeastOnAttack, OnAttack, OnStart.
char_1023_ghost2 (F) Attack, Attack_Down, Die, Die_B, Idle, Idle_B, Skill_1, Skill_3_*, Start, Start_B…
char_4134_cetsyr (F) no Attack; Skill_{1,2,3}_Begin/Loop/End, Idle, Die, Stun, Stun_Begin, Start
char_1012_skadi2 (Spine) no Attack; Skill_{n}_Begin/Loop, Idle, Die, Stun, Start
char_600_cpione, char_616_pithst Attack, Default, Die, Idle, Start
enemy_1007_slime Attack, Default, Die, Idle, Move_Begin/Loop/End
enemy_1000_gopro_2 … + Run_Begin/Loop/End
enemy_1502_crowns Appear, Attack, Die, Disappear, Idle, Move
enemy_1517_xi (boss) Attack_01, Attack_02, Die, Idle, Move, Revive_01, Skill_01, Skill_01_02, Skill_02_Begin/Loop/End. Events: OnAttack, OnPlayAudio.
enemy_9009_acfort Attack, Die, Idle, Move, Skill_1_Begin/Loop/End, Skill_2

Resolver. Precompute it at build time by parsing every skel in Node with @pixi-spine/runtime-3.8; see spinetest/parse.cjs in the scratchpad. Emit anims.json, then resolve each role in order:

  • idle: Idle → Default.
  • deploy: Start (fall back to Idle).
  • attack:
    • Attack;
    • else Attack_Start|Attack_Begin → Attack_Loop → Attack_End;
    • else Combat;
    • else (pure supporter) Skill_1_Loop, or just Idle with a flash.
    • Prefer non-_Down variants. Use _Down when the target is below the unit on screen.
  • skill, for the chess's default skill index n (1-based):
    • Skill_{n}_Begin → Skill_{n}_Loop (while active) → Skill_{n}_End;
    • else Skill{n}_Begin/Loop/End;
    • else Skill_{n};
    • else Skill;
    • else fall back to attack.
    • During a skill, Skill_{n}_Idle replaces idle when present.
  • die: Die (if the Back model lacks it, swap to the Front model for the death animation), else fade out over 0.5 s.
  • move (enemies): Move_Begin → Move_Loop → Move_End; else Move; else Run_Loop.
  • stun: Stun_Begin → Stun, else freeze the current track at timeScale = 0.
  • Hit timing: apply damage and spawn projectiles on the Spine event OnAttack (operators and enemies). If no event fires, use 50% of the attack animation. Scale the attack animation's timeScale to animDuration / attackInterval so it matches the simulated attack speed.

5.5 Direction and scale

  • Operators:
    • Front model when facing right or down.
    • Front mirrored (scale.x = -1) when facing left.
    • Back model when facing up; if there is no Back model, use Front.
  • Enemies: single model; flip x by the sign of horizontal velocity. The model faces right by default. [ASSUMED from standard Arknights behaviour; verify by eye]
  • Scale [ASSUMED]: skeleton height is ~360–450 units for a standard chibi. Start with scale = tilePx / 300 so a chibi stands about 1.2 tiles tall, and tune by eye. Anchor at the feet (Spine origin 0,0 is at the feet).
  • Put battle/[pack]common/sprites/sprite_shadow.png under each unit.

5.6 Gaps and fallbacks

  • Tokens/summons: fexli has the default battle Spine for only 1/20 tokens (token_10000_silent_healrb); the rest exist only as skin variants (e.g. …vodfox_doll_witch_2). AA2 has their atlas and skel but no PNGs.
    • Options, in order of preference:
      1. Use the first skin-variant Spine, listed in tokens[*].battleSpineSkinVariantsOnly in the JSON. It looks slightly off-model, but it animates.
      2. Draw the token avatar (15/20) as a circular sprite with a bob tween.
      3. Draw a procedural glyph.
  • "炎佑" (Yan-bond summon), [ASSUMED]: no token id is in the data. Render it with the token_10015_dusk_drgn skin-variant Spine tinted orange-red, or with the Yan bond glyph plus fire particles.
  • enemy_1305_mhslim: reuse enemy_1007_slime Spine with a hue shift, or fall back to the icon sprite.
  • General policy: if any Spine load fails, render the square avatar or enemy icon in a rarity-coloured diamond with a tween (idle bob, lunge on attack, flash on hit). The game must never block on Spine.

6. Audio (k): the real audio is available in AA2 @voice

Base: https://raw.githubusercontent.com/ArknightsAssets/ArknightsAssets2/voice/assets/dyn/audio/sound_beta_2/ (CORS *; jsDelivr 404s on this branch). The mapping comes from excel/audio_data.json, downloaded to …/extra/assets/audio_data.json. For an asset like Audio/Sound_Beta_2/CustomSE/act1autochess/act1autochess_b_ui_roundstart, strip the prefix, lower-case it and append .mp3.

6.1 BGM (verified)

Level bgmEvent / context Files Size
act2autochess m01–m04 use bgmEvent=act1autochess_shop → bank battle.ON_GAME_READY.act1autochess_shop music/act1autochess/m_bat_act1autochess_loop.mp3 2.0 MB
Lobby: sys.ON_ACTIVITY_LOADED.act1autochess music/act1autochess/m_sys_act1autochess_intro.mp3, then loop m_sys_act1autochess_loop.mp3 1.1 + 1.1 MB
h07_05 levels use bgmEvent=rglk1phantomcastle music/rogue_1/m_bat_rglk1phantomcastle_intro.mp3 + _loop.mp3 1.0 + 1.5 MB

Play the intro, then crossfade to a looping AudioBufferSourceNode (crossfade: 1.0 s in the bank).

6.2 Autochess UI and battle SFX (47 banks, all resolved, in 07-assets.json → audio.autochessSfxBanks)

Key bank names, which make good event names for our code:

  • Shop: ui.ON_ACT1AUTOCHESS_SHOP_UPGRADE, …SHOP_LOCK, …GETMONEY.
  • Equipment and bonds: battle.ON_ACT1AUTOCHESS_EQUIP_DONE, …CHAR_BONUS (3-merge upgrade), …EQUIP_BONUS, …ADD_BOND (buffup).
  • Round flow: ui.…ROUNDSTART, …BATTLESTART, …BATTLESTART_BOSS, …REST, …YOURTURN, …YOURTURN_CIRCLE, …COUNTDOWN.
  • Boss rounds: …BOSSROUND_{TEAM,SINGLE,SECRET}, …KILLBOSS{,_ALL,_NORMAL}, …DEFENCE_{START,UNITE}.
  • End of battle and settlement: …BATTLEOVER_{REDUCE,NOREDUCE,NORMAL}, …SETTLEMENT_{SUCCEED,FAIL,TEAM,BOSSSIGN}.
  • Matchmaking and lobby: …MATCH_{SUCCEED,FAIL,CANCEL}, …PLAYER_{JOINROOM,READY}, …STRATEGY, …LOAD, …START, …GOFIRST, …DISCONNECT, …BROADCASTHINT, …ENTER_DANGER.
  • Other: battle.ON_CUSTOM_TRIGGER.autochess_kill_gain_coin, battle.ON_ACT1AUTOCHESS_MAGIC_PLACE_BATTLE.
  • Files are under customse/act1autochess/*.mp3 (46 files, 5–75 KB). Generic sounds: battle/b_char/b_char_set.mp3 (deploy), battle/b_enemy/b_enemy_dead_{n,h}.mp3, battle/b_ui/b_ui_{win,lose}.mp3, general/g_ui/g_ui_btn_{h,u}.mp3, g_ui_confirm_h.mp3.

6.3 Per-unit combat SFX (exact, from the data)

Banks are keyed by unit id:

  • battle.ON_ABILITY_START.{charId}.attack (swing or fire)
  • battle.ON_ABILITY_HIT.{charId}.attack (impact)
  • battle.ON_UNIT_BORN.{id}, battle.ON_UNIT_DEAD.{id}
  • battle.ON_SKILL_START.{skillId} (skill activation)

Coverage: 138/138 operators and 185/200 enemies have at least one bank; the remaining 15 enemies use a generic e_atk/e_imp sound. 07-assets.json lists the resolved URLs per unit (operators[*].sfx, operators[*].skills[*].sfx, enemies[*].sfx).

The pool's needed set is 691 mp3 = 8.7 MB:

  • player/p_atk, p_imp, p_skill: 452 files, 6.8 MB
  • enemy/e_*: 223 files, 2.6 MB
  • battle/: 0.9 MB
  • customse/: 1.3 MB

Pitch-randomize using the bank's minPitch/maxPitch (typically 0.8–1.2). Respect maxSoundAllowed, which caps concurrent instances per bank at 1–2 (popOldest). This matters in a 20-unit fight.

6.4 Optional and fallback audio

  • Voice (optional, off by default): voice_cn/{charId}/cn_{NNN}.mp3, about 30 lines and ~1 MB per operator.
    • Useful lines, by the standard charword ids [ASSUMED mapping, confirm with charword_table.json]: cn_021/cn_022 选中干员, cn_023/cn_024 部署, cn_025–cn_028 作战中.
    • Japanese voice is under voice/.
  • WebAudio synthesis fallback, used if the fetch fails or the user mutes "game audio":
    • UI click: 1.2 kHz square, 30 ms, exponential decay.
    • Coin: two sines, 1.5→2.2 kHz, 80 ms.
    • Hit: white-noise burst through a band-pass (1–3 kHz), 60 ms.
    • Deploy: low 110 Hz sine thump plus a noise click.
    • BGM fallback: silence or an ambient pad. Do not try to imitate the OST.
  • Monster Siren (official streaming, monster-siren.hypergryph.com/api/songs) returns JSON but no CORS headers, so the browser cannot use it directly. Not recommended.

7. Map tiles and battle background (h)

  • The game data has no 2D map texture. Autochess maps are 19×21 tile grids rendered as a 3D Unity scene.
    • The AA2 root PNGs assets/tile_floor.png, tile_road.png and similar are 64×64 editor debug placeholders (green, wood planks). Do not use them.
    • yuanyan map/ previews do not include autochess stages.
  • act2autochess level tile keys: tile_road, tile_floor, tile_wall, tile_forbidden, tile_fence_bound, tile_start, tile_end, tile_telin, tile_telout, tile_achand (autochess bench/hand area), tile_mire (m02), tile_smog (m03), tile_deepsea (m04).
    • heightType is LOWLAND or HIGHLAND; buildableType is ALL, MELEE, RANGED or NONE.
  • Procedural tile renderer, in the Arknights look [ASSUMED palette, tune by eye]:
    • Oblique top-down view; each tile is an inset square with a 1 px lighter top edge and a darker bottom bevel.
    • LOWLAND road #3b4048 with subtle 45° noise. Deployable lowland gets a faint blue-grey outline #6f8aa3 at 40% when a melee chess is dragged.
    • HIGHLAND (tile_wall) is a raised block: top #6b7280, side face #3a3f47 10 px tall, with a white 1 px top rim.
    • tile_forbidden: #22262c with a diagonal hatch. tile_fence_bound: dark plus a yellow-black edge stripe.
    • tile_start (enemy spawn): red #d0453b pulsing frame with an arrow glyph. tile_end (protection point): blue #2d7fd6 with a house glyph.
    • tile_telin/telout: purple ring. tile_mire: murky green overlay. tile_smog: animated grey fog alpha. tile_deepsea: teal wave overlay.
    • tile_achand: bench slots with a dashed outline.
    • Attack-range preview: arts/ui/[uc]battlecommon/ui_battle_new/attack_range_attack.png and attack_range_stand.png, or draw orange #ff9c33 squares at 35% alpha.
  • Backdrop: a dark gradient #15171b → #262a31, with bg_mountains_tiled.png (act2 entry, 112 KB) parallaxed at 20% opacity. Lobby: entry_bkg_01.png (0.99 MB) and season_logo.png.

8. Fonts (j)

Use Font Source Size
Chinese UI and body Noto Sans SC 400/500/700/900 (思源黑体) Google Fonts, fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;500;700;900&display=swap (200); npm @fontsource/noto-sans-sc@5 sliced unicode-range, ~50–100 KB per used slice
Chinese titles (formal, serif as in-game) Noto Serif SC 600/900 (思源宋体) Google Fonts (200) same
Numbers, HUD, costs, timers (in-game Bender) Bender (Jovanny Lemonad, free incl. commercial) https://raw.githubusercontent.com/TimWangZi/The-font-of-Arknights/master/font/Bender/BENDER.OTF (200, 52 KB); also Light and Thin. Convert to woff2 and self-host. 52 KB
English headers (in-game Novecento Wide) Novecento Sans Wide Normal same repo, font/Novecento-Wide-Normal-2.otf (200, 45 KB); self-host 45 KB
Google-only fallbacks for Bender / Novecento Rajdhani 600/700; Saira Condensed 500/700; Barlow Condensed; Teko; Oswald; Chakra Petch Google Fonts, all 200 —

Recommended CSS stacks:

  • Numerals and English UI: font-family: Bender, Rajdhani, 'Noto Sans SC', sans-serif.
  • Headlines: 'Novecento Wide', 'Saira Condensed', 'Noto Sans SC'.

Avoid redistributing Georgia and Times New Roman (Microsoft fonts) from that repo. The 11 MB 思源宋体 OTFs are unnecessary because Google Fonts serves them subset.


  1. Keep assets out of git. Add client/public/assets/ to .gitignore.
  2. Write scripts/fetch-assets.mjs. It reads docs/research/07-assets.json and downloads, with concurrency of about 16, retry ×3 and a jsDelivr mirror when raw fails:
    • assets/char/avatar/{charId}.webp, choosing E2 if present, else base.
    • assets/char/portrait/{charId}.webp.
    • assets/skill/{iconId}.webp.
    • assets/enemy/icon/{enemyId}.webp.
    • assets/ui/…, assets/bond/{bondId}.png, assets/item/{trapId}.webp, assets/band/{bandId}.webp.
    • assets/spine/op/{charId}/{front|back}/{charId}.{skel,atlas,webp} and assets/spine/enemy/{enemyId}/….
    • assets/audio/{bgm|sfx}/…mp3.
    • Full art is not downloaded; fetch it lazily from jsDelivr at runtime.
  3. Post-process:
    • Icons and portraits → WebP q88. Spine textures → lossless WebP, then rewrite the atlas page line.
    • Inject size: into fexli atlases and pma: true into Ark-Models atlases.
    • Normalize every filename to lowercase ASCII.
    • Precompute anims.json (animation list + resolver result per model) and sfx.json (per unit: attack start and hit URLs, skill start, die).
    • Serve .skel and .atlas with gzip or brotli via Node compression.
  4. Runtime loading:
    • Preload UI, icons, avatars and portraits: about 6 MB.
    • Load Spine on demand when a chess enters the shop, bench or board, or when an enemy wave is announced. Cache with PIXI.Assets and an LRU of about 60 models.
    • Show the avatar-sprite fallback until the Spine loads.
  5. Budgets after optimization:
    Asset group Size
    Avatars ~2 MB
    Portraits ~4 MB
    Default-skill icons ~0.8 MB
    Enemy icons ~2 MB
    Bonds, items, bands, UI subset ~4 MB
    Loading illustrations ~2 MB
    Op Spine ~36 MB
    Enemy Spine ~9 MB
    Audio (BGM + SFX) ~15 MB
    Total on the server ≈ 75 MB
    Per client (typical session) ≈ 20–30 MB

10. Licensing note

  • All images, Spine models and audio are © Hypergryph (上海鹰角网络); overseas publisher Yostar.
  • Every mirror repo states this: fexli "All files … copyrighted by Hypergryph … solely for educational and research purposes", and Ark-Models "不得用于商业用途".
  • No official permissive fan-game policy was found. Hypergryph and Yostar do enforce copyright.
  • Therefore:
    • Non-commercial only; no ads, donations or paywall.
    • Show a credits screen: "Arknights © Hypergryph / Yostar. This is an unofficial fan project; all game assets belong to their owners."
    • Keep it private or small-group, and remove on request.
    • Do not redistribute bulk asset archives; fetch at deploy time.
    • Also credit the asset dumpers and tooling: yuanyan3060, fexli/ArkResourceAutoUpdateBot, isHarryh/ArkUnpacker, ArknightsAssets/ArknightsStudio, Kengxxiao/ArknightsGameData, pixi-spine (MIT) and the Spine Runtimes License.
  • Fonts: Noto (SIL OFL); Bender (free by author); Novecento Sans (free licence by Synthview).

11. Open questions and [ASSUMED] items

  1. Token spines: only 1 of 20 defaults is available (§5.6). Approve the skin-variant or avatar fallback.
  2. 炎佑 summon visual [ASSUMED]: the data has no dedicated model.
  3. Tile palette and 3D look [ASSUMED]: procedural. Tune against in-game screenshots (PRTS map pages or bwiki screenshots of 卫戍协议).
  4. Spine scale tilePx/300 and enemy default facing [ASSUMED]: verify visually during build.
  5. PMA for Ark-Models enemy textures: add pma: true, then check visually for dark halos. The test render looked acceptable even without it.
  6. Voice line index mapping (cn_021–cn_028) [ASSUMED]: confirm against charword_table.json if voices get enabled.
  7. The Unity 9-slice metadata for UI panels is not in the dump, so panels are drawn in CSS. Only icons and backdrops use the real sprites.

Addendum (critic)

Written 2026-09-27 by the completeness critic.

  • Spot check. 40 URLs were sampled at random from the 4,469 unique URLs in 07-assets.json: avatars, enemy skel, audio on @voice, and others. Each got a curl -I on 2026-09-27. Result: 40/40 returned 200. The URL patterns in §0 hold.
  • UI reference captured for the designer. It is not to be shipped: it is official art and layout.
    • The official gameplay-intro long image, 800×13685, sliced into scratchpad/gd/extra/critic/intro_00..09.jpg.
    • 11 in-game screenshots (*.jpg in the same folder): prep with shop at R1/R2/R3, briefing, strategy draft, the three 机变 families, 联防, Final Assault, and the banned-operator dropdown.
    • They confirm these layout facts:
      • bottom shop bar: LEVEL card | operator cards | item card | funds card;
      • freeze and refresh buttons above the bar;
      • "剩余可放置角色" label;
      • 3×2 机变 grid;
      • bond strip with layer numbers under the discs;
      • ‹ 自己 › / ‹ 全景 › view switcher.
  • Icons that are also needed:
    • Disabled-bond badge: the person-✕ glyph on greyed bond discs.
    • "层数叠加已禁用" state of the bond strip.
    • Price-0 reward card.
    • Candidates are in the AA2 ui/autochess/… groups already listed in §3 i). If none match, draw them in CSS or SVG.