SpineActor.attack() starts an attack that was not wound up (no look-ahead: a batch that arrived late) on its strike
frame — right for a quick rhythm, but for a slow clip-per-attack enemy the wind-up was cut out: 重犯 (Attack_grey
2.333 s, strike at 1.0 s, attacks every 3 s) showed the slam without the ball ever rising, and only 1.33 s of the clip.
The owner's decision of 2026-10-07 on #246: re-implement on the current spine.js so that, when the attack interval
leaves room for the whole clip, it plays once completely from the start. A clip-per-attack actor (enemies) with
interval ≥ the clip now plays it from 0 at its own speed for the whole clip, the strike landing a little late; a
quicker rhythm keeps the strike-frame start, and operators' looping attacks are unchanged (the PR's condition had it
the other way round: it changed the looping attacks and left out the clip-per-attack enemies it was written for).
Tests: test/render/feedback2-enemy-attack-clip.test.js (+1: 重犯 plays the whole clip from its wind-up, a quicker rhythm
and an operator start on the strike frame) 5/5; render eyemask, unitview, texas skill clip, skill idle 51/51.
The eyelids of 佩佩, 仇白, 隐德来希, 琳琅诗怀雅 and other models are Spine clipping attachments: the polygon is the eye's
opening and the eyeball / eye-white meshes drawn after it are clipped to it, so a blink or a knock-down closes the eye
through the mask. render/app.js pickClipping keeps the masks only for a lone clipped skeleton at high quality, so in a
battle the eyeballs showed through the closed lids. SpineActor._eyeMaskFallback now runs after each update while the
masks are off: it walks the draw order like pixi-spine (from a slot whose attachment in force is a clip, to its
endSlot — any skin) and hides the slot container of a covered slot whose drawn centre (a mesh's mean vertex, a region's
quad centre) lies outside the clip polygon [ASSUMED: all or nothing per slot]; turning the masks on shows them again.
PR #384's version hid `slot.currentSprite`, which pixi-spine leaves null on a mesh slot — every covered slot of the
417 local operator skeletons' 73 clipped ones is a mesh (313) or a region (54; regions have no worldVerticesLength, so
it never measured them) — and its test stubbed a currentSprite onto every slot, so nothing was hidden in the game. Kept
from it: the diagnosis, the ray-casting pointInPolygon and the key-frame expectations.
Tests: test/render/eyemask.test.js (new, 7, on real runtime objects — the official skeletons in @pixi-spine/runtime-3.8's
real Spine, @pixi/core display objects with a 16 px canvas stand-in, the real SpineActor: the eyeballs are meshes in
the clip's container; 佩佩 / 仇白 Die and 隐德来希 / 琳琅诗怀雅 Idle hide exactly the eye slots in the closed frames and
nothing open; a 30 Hz sweep of six clips; masks on hide nothing, turning them on shows all) 7/7 — the five skeleton tests
skip without public/assets (not committed; npm run assets), and fail on PR #384's code; render unitview, enemy attack
clips, texas skill clip 45/45.
131 of the 135 Back skeletons have no Die clip, so SpineActor.die() changed nothing and the
attack / skill / idle loop went on under the redeploy ring. UnitView._wantsBack() now wants the
Back model only while the unit stands (or when its Back skeleton has a Die clip of its own);
die() / revive() mark the view and its update swaps the model in place after the frame's events
(a death and a redeploy in one frame load nothing). The Front model replays the fall (held end
for a unit already down), gets the knocked-down grey, and the redeploy brings the Back model back
with the deploy clip carried over. A skeleton without a Die clip holds its idle's first frame.
The drone triggered its one bomb, but the damage and the blast landed in the trigger tick
while the model kept the bomb hanging under it for good. Official (PRTS 暴鸰; the client's
battle prefab enemy_1040_bombd and projectile_bombd): the cast plays the Attack clip, the bomb
leaves on its OnAttack event (0.267 s), flies to the target at 5 tiles/s and explodes on it
and the 8 tiles around; the drone switches to its bomb-less clips and speeds up.
Sim (kitBombd): release after BOMBD_RELEASE with an 'atk' event of kind 'droneBomb' and fx
'phase' {kind: 'bombed'}, a homing projectile (PROJECTILE_SPEEDS.droneBomb 5) whose arrival
deals the damage, then the speed buff. Render: FORMS.enemy_1040_bombd.bombed (*_2 clips),
PROJ.droneBomb (a falling shell), a one-off cast plays the attack clip once at its own
speed instead of the drone's 5 s attack rhythm, no puff for the silent mode change.
- Every form change goes through content setForm(): the unit keeps it (e.form)
and UnitInfo publishes it, so a view built mid-battle from fieldMeta (a
watched teammate's field, 联防 observers, a reconnect) starts in the
current form instead of the first-form model.
- Enemy profile (dmgType) and Unit moving / form are initialised at spawn:
one object shape for the sim's hot loop (the boss-field bench is back at
the base speed).
- 转译基底·α: an HP loss cannot knock out its original form (no death clip).
- 假想敌:再生: 1 s 重生 and walking 傀儡 cited from PRTS; shields within 1.8
as the 再生状态 begins.
- Renderer: a 重生's closing clip (and the ember's stand-up clip) is timed
from the fx dur to end with it, so the next form attacks on its own clips.
- Docs: ASSETS FORMS list, SIM form fx, PLAYING 隐匿 wording.
- render/spine.js: no attack (wind-up or 'atk') cuts a form's change clip short;
a knocked-out 逐火 no longer reports its last attack in the snapshot.
- test/render/feedback1-forms.browser.test.js (opt-in RENDER_E2E=1): the real
sim in headless Chrome on 战场#01 — 转译基底·α A_* → A_Die_B → B_* → B_Die,
a 深池逐火战士 knocked out in front of its blocker: Die → the 隐匿 ember on
Idle_2, beaten → Die_2.
- PLAYING.md: the 余烬 and 转译基底 rules for players.
- #1 近地悬浮 enemies count as air units (Unit.isFlying): ground-only attacks, splash, terrain and traps skip them;
PRTS 不可对空 skill effects made ground-only; 掠海漂移体 / 吉兆飞鳞 drop modes; the bot plans them as air
- #2 an operator knocked out before 联防 is fielded and force-exited (PRTS 强制退场): it lies on its tile with the
redeploy ring and redeploys (full timer [ASSUMED]); partners, observers and the server fallback included
- #3 element audit of every operator, item and enemy: 海嗣 numbers are official; bursts are 无来源, 元素脆弱 only
raises 元素伤害, 元素抗性 applied, 盟约·辅助干员 fills all three gauges, 菲莱 / 哈洛德 / 纯烬 / enemy kits fixed
- #4 blocking by the official contact radius (0.7071): another operator in contact takes over at once when the
blocker dies, withdraws or is stunned; melee blockers attack what they block
- #5 enemy target order per PRTS 索敌: blocker, special priority, taunt, last deployed; initial deploy 从上到下>从左到右,
operators before summons; special priorities break ties by taunt then deploy order
- #6 act2 m01's normal-field blower machines (row 13) are drawn in the normal / 联防 views
- #7 Final Assault prep legality reads the player's half of the boss field (fence tiles deployable); all 11 stages
audited against the official level files
- #8 regular settings gear; #9 the prep camera keeps the bench and temp rows clear of the shop bar on phones
- #10 huge bosses have the official 4.95 × 2.95 hit area (sim/body.js) and are 自缚 / unblockable
- #11 至简 costs 1: 购买价格为N's bb.price is a discount off the tier price, applied before other price effects
- DESIGN §19 and the normative §3 / §5 / §6 / §7 lines, SIM / META / DATA / PLAYING updated; 2595 unit tests and
every browser suite green; dev recordings regenerated