-
Notifications
You must be signed in to change notification settings - Fork 0
World Model & Spatial System
In the Scanline Engine, the scene object hierarchy serves two distinct purposes: visual rendering (geometry) and the text parser. To avoid compromising either system, the engine uses two separate but closely related models: the Raw Spatial Hierarchy and the Semantic Spatial Projection. What the player actually sees and interacts with at the level of Point-n-Click and text commands is a synthesis of these two different worlds.
The Raw Spatial Hierarchy defines the logical-spatial relationships of objects in the scene, which serves as the foundation for the language layer (the spatial model). Every scene object can have an optional spatial placement defined by two properties:
-
parentNodeId: Points to the parent object. -
relation: Describes the spatial relationship to the parent.
The engine supports five raw spatial relations: in (inside), on (on top of), under (beneath), behind, and near (proximity).
Crucially, the raw spatial hierarchy does not dictate what the player actually sees on the screen. The visual rendering of an object is strictly determined by its X/Y coordinates and properties like Layer and Parallax. For example, a book might have a parentNodeId pointing to a "Cabinet" with the spatial relation in. To the text parser, this book is logically inside the cabinet. However, technically, if the book's X/Y coordinates are set to the other side of the level, the game will render it floating in mid-air far away from the cabinet.
This disconnect is exactly why Containers (Surface and Inventory components) were invented. Containers bridge the gap between logical relationships and visual correctness, allowing Actors to place items securely on, under, in, or behind objects (see bellow).
Because we use invisible technical nodes (like a specific Surface assigned to a desk) to make objects look correct on the screen, relying purely on raw geometry would expose ugly implementation details to the player. We don't want players forced to type commands for an invisible desk_surface.
To provide a natural language experience, the engine dynamically calculates a Semantic Spatial Projection via a read-only system called the SceneTextLayer. This is the world model that the text parser, LLM Game Master, and player actually interact with. The projection works by filtering the raw hierarchy using Semantic Anchors:
-
Titled Objects: Any object with a
Titletext asset is considered semantically meaningful and becomes a Semantic Anchor. -
Technical Nodes: Objects without a
Titleare considered technical or geometric nodes. -
Collapsing: The
SceneTextLayercollapses untitled technical nodes in the text model. If an untitled object has titled descendants, those descendants are projected upward and visually attached to the nearest titled ancestor.
Example: Imagine a desk (Titled: "Desk") containing a technical surface (Untitled), which in turn holds a key (Titled: "Key").
- Raw Truth: Key is on -> Technical Surface is in -> Desk.
- Semantic Projection: The parser ignores the technical surface. To the player, the Key is simply in the Desk.
If the technical surface contained a titled "Drawer", the Drawer would become a new Semantic Anchor, and the Key would be described relative to the Drawer instead of the Desk.
Because technical nodes collapse, spatial queries (LOOK IN, TAKE FROM) evaluate spatial relations relative to the specific anchor being queried. This is known as the Anchor-Relative Rule.
The rule dictates that the effective relation of an object is determined by the first semantic relation on the path from the queried anchor to the descendant. Internal relations between deeper nested objects are preserved but do not override their relationship to the outer anchor.
The Classic Example: Assume a Cabinet (Titled) contains Book A (Titled) in it, and Book B (Titled) is placed on Book A.
- Relative to the Cabinet: Both books are considered
inthe Cabinet, because the first relation from the Cabinet to the chain is in. AskingLOOK IN CABINETwill successfully show both Book A and Book B. - Relative to Book A: Book B is considered
onBook A, because the first relation from Book A to Book B ison. AskingLOOK ON BOOKA will show Book B.
This ensures that players can naturally say TAKE BOOK B FROM CABINET or TAKE BOOK B FROM BOOK A, and both commands will perfectly resolve the target without confusing the parser.
The semantic projection also strictly separates visual proximity from actionable storage:
-
Container Requirements: A spatial relation of in or on does not automatically make an object an interactive storage. Storage must be explicitly granted by attaching an
InventoryorSurfacecomponent to the object (container component). Containers allow players and NPCs to store items in them. The type and quantity of items (capacity) are specified in the container settings. - Spatial placement without container: For flexible scene creation, you can spatially place objects in the editor by attaching them to any semantically meaningful parent object without a container, but this will result in them only being able to be picked up and/or used in the game, but not placed back.
-
The
nearRelation: The near relation is recognized by the parser for proximity and text grouping, but it is strictly forbidden to be used as a storage slot. TheSceneSpatialValidatorwill rejectnearif it is configured inside an Inventory orSurfacecomponent, because an item cannot be "stored" in the abstract space "near" something.
In the Scanline, the text parser and semantic world model strictly distinguish between whether an object is visible (known to the player) and whether it is actionable (accessible for interactions like TAKE, PUT, USE, or EXAMINE). An object might be visible but too far away, or visible but physically blocked by a transparent container. To build puzzles and interactive environments, developers can manage this access using Blocker components, Switch objects, and the hidden state of entities.
Access to nested spatial descendants can be restricted using specific components that act as semantic barriers.
The Blocker Component
A Blocker is an always-active semantic barrier. It prevents the player from interacting with objects that are spatially contained within or around it.
The Switch Component
A Switch acts exactly like a Blocker, but its state can be toggled between closed (State 1) and open (State 2). When a Switch is closed, it blocks access to its contents; when open, the contents become fully visible and actionable. Even if the object holding the Switch component has no Title (meaning it is invisible to the parser), it still acts as a logical barrier for its visible descendants.
Why two types of components if a non-openable Switch is sufficient? They actually serve different functions. The
Switchcomponent gives the scene object it's attached to the ability to detect mouse clicks and toggle visible scene objects (for example, swapping a closed drawer sprite with an opened one). This is its primary function, while blocking access is secondary, preventing objects from being accessed in closed drawers, cabinets, etc. TheBlocker, on the other hand, is simply a spatial placeholder to restrict access without requiring the extensive functionality provided by the Switch.
Configuring Barriers (transparent and blockedRelation)
Both Blocker and Switch components rely on two main properties to determine how they restrict access:
-
blockedRelation: Specifies which spatial relation is being blocked (in,on,under,behind, ornone). For instance, a desk drawer might block in, while a heavy rug might block under. -
transparent: Determines whether the player can still see the blocked objects.- Opaque (
transparent: false): The blocked descendants are completely hidden from the semantic world model and are inaccessible. - Transparent (
transparent: true): The blocked descendants remain visible forLOOKcommands, but are excluded from actionability scopes (they cannot be taken, examined, used, or opened). The parser will respond acknowledging the object's presence but stating it cannot be interacted with.
- Opaque (
The clearlyOpenable Flag (Switches Only)
For closed, transparent containers, the engine needs to know how to phrase the rejection when a player tries to interact with the contents. The clearlyOpenable flag handles this:
- If
true, the engine explicitly states the container is closed (e.g., "The drawer is closed." or "You can't reach that while it is inside something closed."). - If
false, the engine gives a more generic rejection (e.g., "You can't reach it.").
Sometimes you want an object to be completely absent from the parser's world model until the player actively discovers it. Titled objects feature a hidden semantic field that manages this discovery process.
Directly typing LOOK <hidden object> or EXAMINE <hidden object> before it is discovered will result in a "not found" response, preventing players from blindly guessing an object's existence. The object must be revealed through its spatial anchor (the visible parent object it relates to).
hidden: "lookable"
A lookable object remains invisible until the player explicitly looks at the spatial area where it is hidden.
-
How to reveal: The player must perform a relation-aware look on the anchor object (e.g.,
LOOK UNDER DESK), or reveal the object via the mouse title reveal. - Messaging: Upon the first successful discovery, the engine uses a special discovery phrasing (e.g., "Under the desk you discover: a key."). Once revealed, it becomes a standard visible object for the remainder of the runtime session and subsequent checks will simply say "_you see ...".
hidden: "examinable"
An examinable object requires closer inspection and will not be revealed by a simple LOOK command.
-
How to reveal: The player must successfully
EXAMINEthe visible spatial anchor that holds the object. For example, if "audio cables" are hidden behind a "boombox" with the examinable property, the player mustEXAMINE BOOMBOXto find them.
First-Level Semantic Reveal Rule
When revealing spatial contents via LOOK or EXAMINE, the engine only reveals and describes first-level direct titled children. If an anchor contains a titled box, and inside that box is a hidden key, examining the anchor will only reveal the box. Grandchildren or deeply nested hidden items must be discovered by inspecting their immediate titled parent.
Save/Load Persistence
Because the discovery of hidden objects represents actual gameplay progress, the revealedHiddenEntities state is tracked as runtime progress and will be serialized with the game-state save files, rather than being part of the static scene authoring data.
In the Scanline Engine, Subscenes provide a way to create detailed, close-up inspections of specific areas or objects (like looking inside a drawer, a computer terminal, or a tabletop). They function both as UI modal views and as structural nodes within the engine's semantic spatial hierarchy. Here is a comprehensive guide to understanding and configuring Subscenes.
A Subscene is a trigger component that overlays a zoomed-in perspective of specific objects over the main gameplay scene.
- Activation: A Subscene is activated either by clicking on the designated TriggerBox with the mouse or by typing the
EXAMINE <Name>command in the parser. - Visual Behavior: When active, the main scene is visually darkened or blurred in the background, drawing the player's focus entirely to the close-up objects.
- Player Restrictions: While a Subscene is open, standard player movement is blocked.
- Exiting: To close the modal view and return to the main scene, the player simply clicks anywhere outside the boundaries of the objects contained within the active Subscene group.
Subscenes act as virtual spatial nodes within the engine's raw spatial hierarchy. This means you can nest objects or even other Subscenes inside them.
Note: Nested subscenes haven't been tested yet, and their functionality is actually questionable.
Direct vs. Indirect Nesting & Activation Depth When a Subscene is activated, the engine only enables objects that are directly (first-level) nested inside it.
- If Subscene A contains Subscene B, opening Subscene A will activate the TriggerBox for Subscene B so the player can see it.
- However, the actual contents inside Subscene B remain inactive until the player explicitly opens Subscene B.
Note: While the legacy
targetGroupIdmethod for activating objects is supported, the spatial hierarchy is now the engine's single source of truth for Subscene contents.
The text parser handles objects inside Subscenes with a strict distinction between visibility (semantic knowledge) and actionability (physical access).
- Semantic Visibility: Titled objects inside an inactive (closed) Subscene remain part of the semantic world model. The parser knows they exist and can recognize player commands directed at them.
-
Restricted Actions: Even though the parser knows about the objects, being
disabledinside a closed Subscene prevents direct interactions. Commands likeTAKE,PUT, orUSEwill fail until the Subscene is actually opened. -
Auto-Open Attempts: If a player issues a command (like
LOOK,TAKE,OPEN) targeting an object inside a closed Subscene, the parser will first silently attempt to open the Subscene using the same access rules as EXAMINE. If it cannot be opened, the player receives a standard failure message and the action aborts. -
Distance Checks: When a Subscene is active,
PUTandDROPcommands aimed at targets inside the Subscene bypass the standard world-distance checks. If the storage target is inside the currently active Subscene, the player is considered close enough to interact with it, regardless of their avatar's distance from the object in the main scene.
When transitioning items from the main scene (or the player's inventory) into a close-up Subscene, objects naturally need to appear larger to match the zoomed-in perspective. This is managed by the Subscene's itemScale property.
- Runtime Scaling: itemScale is an optional runtime multiplier applied to any entity with an Item component that is placed inside the active Subscene.
-
Placement and Animation Math: When an item is placed or dropped onto a
Surfacewithin an active Subscene, the item is immediately added to the Subscene's runtime set and inherits theitemScalemultiplier. This happens before the engine calculates the placement fit or the drop animation, ensuring that capacity limits and visual flights look completely natural in the close-up view. -
Drop Prioritization: When a player uses a generic
DROPcommand without specifying a target, an active Subscene will automatically prioritize dropping the item onto aSurfacewithin that Subscene over main-scene floors or walkboxes.
To prevent logical desynchronization between a close-up view and the main room, Subscenes enforce a strict auto-reset rule for nested Switch components (like doors, covers, or latches).
If a Switch object is manipulated inside an active Subscene, it will automatically reset to State 1 (Closed) and play its closing sound effect (sound_1) the moment the player exits the Subscene. This guarantees that a container cannot remain "open" in the close-up view while simultaneously appearing "closed" on the main scene's background sprite.