feedback6 R21F: enemy animation states, the client part of PR #275 by @xcdoge re-implemented — the resolver gives an enemy every numbered skill clip (anims.skills; the caller's first index stays the primary, operators unchanged, 宴's PREFAB_SPINE_ROLES fix kept), the stun spellings Stun_1 and Dizzy_Begin/Loop/End, a separate run role (Run_Begin/Loop/End); the renderer walks an enemy faster than moveSpeed 1 on its Run cycle (猎狗pro, 深池侦察犬 ×2; [ASSUMED] threshold), swaps an enemy's skill clip on a cast event (UnitView.setSkillSlot; a form in force is kept), and plays the 重生 of 巨大的丑东西 (Revive, then the fleeing 大祭司 Idle_2 / Move_2 / Stun_2) and 自在 (Revive_01); data/assets.json regenerated offline: 18 enemies gain anims.skills, 6 anims.run, 3 a stun clip (巨大的丑东西, 斩胄之剑, 破胄之锤), nothing else; the sim's cast slot (a new event) is not part of it; golden: unchanged
This commit is contained in:
1 parent
14df10a5a3
commit
76a2db7dd0
11 files changed
+315
-19
No files matched your search
@@ -391,3 +391,47 @@ describe('an enemy flyer crossing a raised tile keeps its height (GitHub #277)',
|
||||
assert.ok(Math.abs(op.z - RAISED.h) < 1e-6, `the operator stands on the high ground (${op.z})`);
|
||||
});
|
||||
});
|
||||
|
||||
// PR #275 (@xcdoge): 猎狗pro ships Move_Loop 0.80 s next to Run_Loop 0.53 s and its moveSpeed is 1.9, so a fast enemy
|
||||
// walks on its model's own Run cycle (anims.run) while a standard one keeps Move; the cast slot composes with it.
|
||||
describe('a fast enemy walks on its Run cycle (PR #275)', () => {
|
||||
const entry = {
|
||||
skel: '/s/x.skel', atlas: '/s/x.atlas', textures: ['/s/x.png'],
|
||||
anims: {
|
||||
idle: 'Idle', deploy: 'Idle', die: 'Die', attack: null,
|
||||
move: { begin: 'Move_Begin', loop: 'Move_Loop', end: 'Move_End' },
|
||||
run: { begin: 'Run_Begin', loop: 'Run_Loop', end: 'Run_End' },
|
||||
skill: { begin: null, loop: 'Skill_01', end: null, index: 0, idle: null },
|
||||
skills: { 0: { begin: null, loop: 'Skill_01', end: null, index: 0, idle: null }, 1: { begin: null, loop: 'Skill_02', end: null, index: 1, idle: null } },
|
||||
},
|
||||
animations: { Idle: 1, Die: 0.67, Move_Begin: 0.17, Move_Loop: 0.8, Move_End: 0.17, Run_Begin: 0.17, Run_Loop: 0.53, Run_End: 0.17, Skill_01: 1, Skill_02: 1 },
|
||||
};
|
||||
const assets = {
|
||||
picture: () => null, image: async () => null, spineEntry: () => entry,
|
||||
spine: { acquire: async () => ({ animations: Object.keys(entry.animations).map((name) => ({ name })) }), release() {} },
|
||||
};
|
||||
const enemyView = async (id, speed, side = 'enemy') => {
|
||||
const ctx = fakeViewCtx(fake.P, { assets, cam, lookupDef: () => ({ stats: { moveSpeed: speed } }) });
|
||||
const v = new UnitView(ctx, { id, side, kind: side === 'enemy' ? 'enemy' : 'op', defId: 'enemy_1000_gopro_2', x: 5, y: 12, maxHp: 100 }, {});
|
||||
await tick(); await tick();
|
||||
assert.ok(v.actor, 'spine actor built');
|
||||
return v;
|
||||
};
|
||||
|
||||
test('moveSpeed 1.9 moves on Run, 1 on Move; the cast slot and the Run cycle compose', async () => {
|
||||
const hound = await enemyView(1, 1.9);
|
||||
assert.equal(hound.actor.roles.move.loop, 'Run_Loop');
|
||||
const slug = await enemyView(2, 1);
|
||||
assert.equal(slug.actor.roles.move.loop, 'Move_Loop');
|
||||
// the MOVE anim code plays it
|
||||
hound.sync({ x: 5, y: 12, hp: 100, maxHp: 100, sp: 0, spMax: 0, flags: 0, anim: 1, vx: 0.5 }, 1);
|
||||
for (let i = 0; i < 20; i++) hound.update(1 / 60, cam(), i / 60);
|
||||
assert.match(String(hound.actor.current), /^Run/, `playing ${hound.actor.current}`);
|
||||
hound.setSkillSlot(1);
|
||||
assert.equal(hound.actor.roles.skill.loop, 'Skill_02', 'the cast slot');
|
||||
assert.equal(hound.actor.roles.move.loop, 'Run_Loop', 'and the Run cycle survives it');
|
||||
hound.actor.setRunMode(false);
|
||||
assert.equal(hound.actor.roles.move.loop, 'Move_Loop');
|
||||
assert.equal(hound.actor.roles.skill.loop, 'Skill_02', 'the cast slot survives that too');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user