Files
root 9c4148dc1b Hold a running game, and let the editor run reach into containers
Pause is a sub-state of play, not a third mode: the toolbar button and
Ctrl+Enter stop ticking the world, the clock included. A held frame still
drops the input edges, so resuming does not fire a pause worth of keys in
one go.

The editor run missed the case it is most wanted for. hasEditorRun only
looked at the stored scene objects, so a behavior inside a placed
container never started one; the world now builds from resolveObjects and
writes back through objectRef, keeping the move as an instance edit
instead of baking the instance out.

Keys and clicks go to the run while the Game tab is on screen, where no
key means anything to the editor, and are handed back on leaving it -
held keys released, so nothing keeps walking unpressed.
2026-09-09 18:16:28 +02:00

93 lines
2.9 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Pausing play. The hold itself is a sub-state of play mode, so these tests watch that it can
* only exist while playing and that leaving play always clears it – a stuck "paused" would
* leave the editor unable to run anything.
*/
import { describe, expect, it } from 'vitest';
import { EditorState } from '../../src/editor/state';
import { makeSpriteScene, place, startWorld } from '../helpers';
function playing() {
const f = makeSpriteScene('Box');
place(f, f.spriteId, { x: 0, y: 0 });
const state = new EditorState();
state.loadProject(f.project, 1);
state.enterPlay();
return { f, state };
}
describe('pausing the game', () => {
it('holds and releases while playing', () => {
const { state } = playing();
expect(state.paused).toBe(false);
state.setPaused(true);
expect(state.paused).toBe(true);
state.setPaused(false);
expect(state.paused).toBe(false);
});
it('cannot be paused while editing', () => {
const f = makeSpriteScene('Box');
const state = new EditorState();
state.loadProject(f.project, 1);
state.setPaused(true);
expect(state.paused).toBe(false);
});
it('tells the toolbar so its button can change label', () => {
const { state } = playing();
let changes = 0;
state.on('mode', () => (changes += 1));
state.setPaused(true);
expect(changes).toBe(1);
// Setting the same value again is not a change and must not redraw.
state.setPaused(true);
expect(changes).toBe(1);
});
it('is over when play is over, so the next run starts moving', () => {
const { state } = playing();
state.setPaused(true);
state.exitPlay();
expect(state.paused).toBe(false);
state.enterPlay();
expect(state.paused).toBe(false);
});
it('does not survive loading another project', () => {
const { state } = playing();
state.setPaused(true);
state.loadProject(makeSpriteScene('Other').project, 1);
expect(state.paused).toBe(false);
});
});
describe('the world while it is not ticked', () => {
it('keeps its clock and its positions', () => {
const f = makeSpriteScene('Box');
place(f, f.spriteId, { x: 0, y: 0 });
const { world } = startWorld(f);
world.tick(0.5);
const box = world.entities[0]!;
const time = world.time;
const x = box.x;
// What a paused frame does: no tick at all.
expect(world.time).toBe(time);
expect(box.x).toBe(x);
world.tick(0.5);
expect(world.time).toBeCloseTo(time + 0.5, 5);
});
it('forgets a click that arrived while nothing was ticking', () => {
const f = makeSpriteScene('Box');
place(f, f.spriteId, { x: 0, y: 0 });
const { world } = startWorld(f);
world.setPointerDown(true);
expect(world.pointer.pressed).toBe(true);
world.dropPointerEdges();
expect(world.pointer.pressed).toBe(false);
// The button is still held – only the one-frame edge is gone.
expect(world.pointer.down).toBe(true);
});
});