particle library
This commit is contained in:
@@ -46,6 +46,7 @@ function harness() {
|
||||
remove() { Cube.all = Cube.all.filter((cube) => cube !== this); }
|
||||
}
|
||||
const context = {
|
||||
localStorage: { getItem() { return null; }, setItem() {} },
|
||||
Project: project, Group, Cube, Plugin: { register(id, definition) { context.registration = definition; } },
|
||||
Action: class { constructor(id, options) { Object.assign(this, options, { id }); } },
|
||||
Toggle: class { constructor(id, options) { Object.assign(this, options, { id, value: false }); } },
|
||||
@@ -155,7 +156,7 @@ test('animation menu orders import and creation actions and has no deprecated sp
|
||||
assert.deepEqual(Array.from(menu.children, (action) => action === '_' ? '_' : action.name), [
|
||||
'Import Whole Animation', 'Import Actor', 'Import Animation', 'Import Animation Definition', 'Pack Folders',
|
||||
'_', 'Actor Preferences', 'Replace Actor Model',
|
||||
'_', 'Block Requirements', 'Item Props', 'NoN Sound Library', 'NoN Particle Library',
|
||||
'_', 'Block Requirements', 'Item Props', 'NoN Sound Library', 'NoN Particle Library', 'Particle Previews',
|
||||
'_', 'Animation Definition', 'Export Animations',
|
||||
]);
|
||||
const source = fs.readFileSync(path.join(__dirname, '..', 'AnimationDirector.js'), 'utf8');
|
||||
|
||||
@@ -13,13 +13,13 @@ function harness() {
|
||||
Blockbench: { showQuickMessage(message) { context.message = message; } }, console,
|
||||
};
|
||||
const code = fs.readFileSync(path.join(__dirname, '..', 'AnimationDirector.js'), 'utf8')
|
||||
.replace(' Plugin.register(PLUGIN_ID, {', ' globalThis.api = { previousStagePoseSource, copyPreviousStagePose, copyFirstPoseToEnd, loopPoseUnavailableReason, refreshPreviousStagePoseButton, setButton(button) { previousStagePoseAction = button; } };\n Plugin.register(PLUGIN_ID, {');
|
||||
.replace(' Plugin.register(PLUGIN_ID, {', ' globalThis.api = { previousStagePoseSource, copyPreviousStagePose, copyFirstPoseToEnd, loopPoseUnavailableReason, refreshPreviousStagePoseButton, refreshLoopPoseButton, setButton(button) { previousStagePoseAction = button; }, setLoopButton(button) { loopPoseAction = button; } };\n Plugin.register(PLUGIN_ID, {');
|
||||
vm.runInNewContext(code, context);
|
||||
return context;
|
||||
}
|
||||
|
||||
function animation(name, length = 2) {
|
||||
return { name, length, animators: {}, getBoneAnimator(group) {
|
||||
return { name, length, loop: 'loop', animators: {}, getBoneAnimator(group) {
|
||||
return this.animators[group.uuid] ||= animator();
|
||||
} };
|
||||
}
|
||||
@@ -35,7 +35,8 @@ function animator() {
|
||||
|
||||
test('previous pose button stays visible and explains why it cannot be used', () => {
|
||||
const h = harness(), states = [], label = {};
|
||||
const button = { nodes: [{ classList: { toggle(name, value) { states.push(value); } }, setAttribute() {}, querySelector() { return label; } }] };
|
||||
const node = { style: {}, classList: { toggle(name, value) { states.push(value); } }, setAttribute() {}, querySelector() { return label; } };
|
||||
const button = { node, nodes: [node] };
|
||||
h.api.setButton(button);
|
||||
h.api.refreshPreviousStagePoseButton();
|
||||
assert.match(label.textContent, /Select an animation stage/);
|
||||
@@ -43,6 +44,12 @@ test('previous pose button stays visible and explains why it cannot be used', ()
|
||||
h.api.refreshPreviousStagePoseButton();
|
||||
assert.match(label.textContent, /no previous stage/);
|
||||
assert.deepEqual(states, [true, true]);
|
||||
assert.equal(node.style.filter, 'grayscale(1)');
|
||||
assert.equal(node.style.opacity, '0.35');
|
||||
const second = animation('example.p2'); h.Animation.all.push(second); h.Animation.selected = second;
|
||||
h.api.refreshPreviousStagePoseButton();
|
||||
assert.equal(node.style.filter, '');
|
||||
assert.equal(node.style.opacity, '');
|
||||
});
|
||||
|
||||
test('previous stage uses numeric ordering, same animation ID and saved reused stages', () => {
|
||||
@@ -132,3 +139,20 @@ test('loop pose is unavailable without a clip or with a zero length', () => {
|
||||
assert.match(h.message, /positive animation clip length/);
|
||||
assert.equal(h.edits.length, 0);
|
||||
});
|
||||
|
||||
test('loop pose button is visibly disabled for non-looping animations and cannot edit them', () => {
|
||||
const h = harness(), clip = animation('example.p1'), node = {
|
||||
style: {}, classList: { toggle() {} }, setAttribute() {}, querySelector() { return null; },
|
||||
};
|
||||
h.api.setLoopButton({ node, nodes: [node] });
|
||||
h.Animation.selected = clip;
|
||||
for (const loop of ['once', 'hold']) {
|
||||
clip.loop = loop; h.api.refreshLoopPoseButton();
|
||||
assert.equal(node.style.filter, 'grayscale(1)');
|
||||
assert.match(h.api.loopPoseUnavailableReason(), /Set this animation to Loop/);
|
||||
h.api.copyFirstPoseToEnd(); assert.equal(h.edits.length, 0);
|
||||
}
|
||||
clip.loop = 'loop'; h.api.refreshLoopPoseButton();
|
||||
assert.equal(node.style.filter, '');
|
||||
assert.equal(h.api.loopPoseUnavailableReason(), null);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user