quest'n'click 4.5.0 · r1

Built for quest'n'click 4.5.0, revision r1. Whether it is published, and which one is current, is recorded at publication.json.

Geometry: placement, walk zones, masks, layers, camera

Français · Type: reference + explanation · Audience: author · Context: engine repository, created project · Status: ready · Applies from: 4.3.0

Where things stand in a room, and how the engine uses it: the layout file, the walk area, approach points, what a tap touches, the camera of a wide room, and the stage's floors, layers and masks. The logic of the room (what happens) is in rooms/<room>.ts; everything on this page is in layout/<room>.json.

Placement: written by the editor, never typed

Each room has a layout, layout/<room>.json, written by the placement editor (/?edit=<room> on the dev server) or the Studio's Rooms tab (placement and stage in the Studio). You drag things on the picture; the editor saves the file. You never type coordinates by hand, except for a one-off scripted move ({ walk: [300, 350] }, { place: … }).

The layout file

FieldWhatDefault
widththe room's width in logical units; wider than 640, the room scrolls (camera)640
floorthe bottom of the floor (logical y): a computed approach point never goes lower395 (FLOOR)
walkthe walkable area: { area: [[x, y], …], holes?: [[[x, y], …], …] } (an outer polygon, holes for furniture)none: walks go in a straight line
scalecharacter scale by depth: [[back y, scale], [front y, scale]]1
entriesthe hero's entry points, at least defaultdefault at [320, 360]
hotspots.<id>rect: [x, y, w, h] or poly: [[x, y], …], approach, face
props.<id>x, y (the feet), h (height), z, on, flip, flipV, rot, approach, states.<state>
actors.<id>x, y, h, z, flip, approach
walkZones, walkLinks, layers, occluders, lights, emittersthe stage (below)

Fields read by the engine besides positions:

Approach points and distance to a character

Before acting on something in the room, the hero walks to its approach point, then turns to it (and a character he acts on turns to him). The point is, in order (src/engine/core/world-queries.ts):

  1. the approach the layout gives (a hotspot's always; a prop's or an actor's only if it is within 150 units of the thing, NEAR: farther, it predates a move and is recomputed);
  2. for a prop: under its feet, 12 units lower, never below floor;
  3. for an actor: beside them on the side facing the middle of the room, 44 units away whatever their size; with actorGap on the game (4.2.4; for example 0.8), that share of the two characters' mean height (height of the hero and of the character, or the actor's h in the layout), times the depth scale where the character stands;
  4. for a hotspot: under the middle of its rectangle or polygon, 12 units lower, never below floor.

Tall characters who gesture as they talk end up with a hand in each other's head at 44 units: that is what actorGap is for. An approach point in the layout still wins, for a character who needs the other side. actorGap is presentation only, like approach points: no rule nor the solver reads it.

The walk area

The walk area is triangulated once (earcut), then a path between two points is the navmesh's shortest path, pulled straight where the shortcut stays inside (src/engine/dom/walk.ts). A target outside the area is replaced by the closest point inside it: the hero walks as near as he can, then the action goes on from there. A room without walk walks in a straight line anywhere.

scale sets a character's size by the depth of their feet: in the template, 0.8 at y 250 and 1 at y 396. Character heights are in logical units (in the demo, Pixel is 36, Grandma 120).

What a tap touches

The engine computes, for each frame, the targets of the room and their boxes (src/engine/scene/frame.ts):

Stage layers and occluders are not targets: a foreground layer drawn over a prop does not stop a tap on it.

Wide rooms and the camera

feature since 3.4.0

A room is 640 × 400 logical units. Give its layout a width (for example 960 or 1280: in the editor's "Room width" folder, or in layout/<room>.json) and paint its backdrop that wide (800 px tall, the width in proportion; npm run assets keeps it): the room scrolls, and the camera follows the hero. Positions stay world coordinates (a prop at x 900 is in the right part). validate refuses a width under 640 and warns about a zone, a prop or an entry beyond it.

CommandEffect
{ camera: { to: 'far_stalls', ms: 900 } }Pan to centre on something (an actor, a prop, a zone), then stay there.
{ camera: { pan: 320, ms: 600 } }Pan to a left edge, in logical units (clamped to the room).
{ camera: 'follow' } (or 'reset')Follow the hero again. Entering a room always follows the hero.

The camera is in the state (a save restores it), the solver ignores it, and "reduce motion" makes every pan instant. A camera command in a 640-wide room does nothing (validate warns). Animations and the rest of staging: staging.

Floors, layers and masks (the stage)

feature since 3.4.0

A room can show more than its backdrop. The content says what exists and when (stage in the room: layers, lights, particles, the logic of walk links); the layout says where; the painter draws it. The content side, lights and particles are on the stage. The geometry is:

An old room is a stage of one backdrop (decor) and one zone (walk, named main): nothing to rewrite.

A closed link. stage.links.<id> { if, locked } in the content closes a link while its condition does not hold. The hero walks to its foot, says its locked line, and the action goes on from there. So the rules of what stands behind it must check the same condition: npm run lint warns (walk-link-gate) when a rule on a target behind a closed link does not. The puzzle is the rule; the link only stops the walk.

Guarantee · closed-link-stops-walk · referenced · executed on aff6caab7514 · scope self-checked by the author (AI), not reviewed A closed walk link is never crossed: the walk stops at its foot and names the link.

Guarantee · geometry-stage-not-state · referenced · executed on aff6caab7514 · scope self-checked by the author (AI), not reviewed Layers and lights on the stage are not game state: they do not change what the solver sees. The sample game with a foreground layer and a light on every room proves the same: same verdict, same number of states, same softlocks.

Limit · canvas-only-effects Masks from images or layers, lights, particles and blend modes are drawn only by the Canvas painter: renderer: 'canvas' on the room or the game. On a room the DOM painter draws (the default), npm run validate says what it will not draw.

Seeing the geometry

/?dev draws the geometry over the room: the walk area in green and its holes in red, hotspots in blue, props in purple (their box and their feet), actors in green, entry points as pink triangles, the depth-scale lines in orange, walk links in yellow. An approach point is a dot: filled when the layout gives it, hollow when the engine computes it. A hidden hotspot or prop (its visible condition false) is not drawn. Press D to hide the overlay. When something cannot be reached, start with something is visible but unreachable.

Why the geometry is apart

Geometry is edited with the mouse against the picture, and changes for other reasons than the story: a new backdrop moves every rectangle and no rule. Keeping it in its own file means a placement never touches the logic, the Studio can write it safely, and the solver ignores it: walking, the camera and the stage are presentation, and what the solver proves does not depend on them (what the solver proves). The reference painter is the DOM, for accessibility; Canvas is allowed for rooms that need its effects (ADR 0001, ADR 0011).


Edit the source of this page · Report an error