--- name: nqtr-navigation description: Use when defining or navigating NQTR rooms, locations and maps — RoomBaseModel/LocationBaseModel/MapBaseModel, RegisteredRooms/RegisteredLocations/RegisteredMaps, navigator.currentRoom, "# enter room" in ink, entrance rooms, and custom Room/Location/Map classes. Navigation is the base of NQTR; activities and routines attach to it. --- # NQTR navigation and map Official docs: [pixi-vn.com/nqtr/navigation](https://pixi-vn.com/nqtr/navigation). Index: [nqtr](../getting-started/SKILL.md). Prefer ink for story-side navigation when the project uses ink: [nqtr-ink](../ink/SKILL.md). ## Concepts | Element | Role | | --- | --- | | `room` | The core element; the position of the MC and NPCs is deduced from it | | `location` | Container of rooms | | `map` | Container of locations | The player moves between rooms; the current location and map are derived from the current room. The terms are conventional (in a space game: planets/solar systems/galaxy). ## Initialize Create map → location → room (each needs its parent) and register them. `Registered*.add` is **required**. ```ts title="content/maps.ts" import { MapBaseModel, RegisteredMaps } from "@drincs/nqtr"; export const mainMap = new MapBaseModel("main_map", { name: "Main Map", image: "map" }); RegisteredMaps.add(mainMap); ``` ```ts title="content/locations.ts" import { LocationBaseModel, RegisteredLocations } from "@drincs/nqtr"; import { mainMap } from "@/content/maps"; export const mcHome = new LocationBaseModel("mc_home", mainMap, { name: "MC Home" }); RegisteredLocations.add(mcHome); ``` ```ts title="content/rooms.ts" import { RegisteredRooms, RoomBaseModel } from "@drincs/nqtr"; import { mcHome } from "@/content/locations"; export const mcRoom = new RoomBaseModel("mc_room", mcHome, { name: "MC room", image: "location_myroom", activities: ["bed"], // see nqtr-activity }); export const aliceRoom = new RoomBaseModel("alice_room", mcHome, { name: "Alice room", image: "location_aliceroom", routine: ["alice_sleep"], // see nqtr-routine }); RegisteredRooms.add(mcRoom, aliceRoom); ``` Import order: content files that reference each other must not form cycles; the parent (map, then location) is imported by the child. ## Get / get all `RegisteredRooms.get(id)`, `RegisteredLocations.get(id)`, `RegisteredMaps.get(id)`; `.values()` returns all. `navigator.rooms/locations/maps` list the registered entities. ## Navigate Set the current room; location and map update automatically. Do it at game start too. ```ink === start === # enter room mc_room -> DONE ``` ```ts import { navigator } from "@drincs/nqtr"; navigator.currentRoom = mcRoom; // accepts a room or its id navigator.currentRoomId; navigator.currentLocation; navigator.currentMap; ``` `navigator.clearExpiredActivities()` drops activities whose schedule ended. ## Custom classes Extend `RoomStoredClass` / `LocationStoredClass` / `MapStoredClass`, implement the matching interface, and augment it in `nqtr.d.ts` (`name`, `sprite`/`background`, `disabled`, `hidden`, `entrance`, ...). Constructor shapes: `Room(id, location, { activities?, routine? })`, `Location(id, map, activities?)`, `Map(id, activities?)`. Store mutable flags with `getStorageProperty`/`setStorageProperty`: ```ts title="models/Room.ts" export default class Room extends RoomStoredClass implements RoomInterface { constructor(id: string, location: LocationInterface, props: { name: string; activities?: (ActivityInterface | ActivityIdType)[]; routine?: (CommitmentInterface | CommitmentIdType)[]; isEntrance?: boolean }) { super(id, location, { activities: props.activities || [], routine: props.routine || [] }); this.name = props.name; this.isEntrance = props.isEntrance || false; } readonly name: string; readonly isEntrance: boolean; get hidden(): boolean { return this.getStorageProperty("hidden") || false; } set hidden(value: boolean) { this.setStorageProperty("hidden", value); } } ``` ### Graphical elements (`background`, `sprite`, `icon`) - `background` accepts a string (the **alias of a linked asset**) or a `TimeSlotsImage` (see [time](../time/SKILL.md)) to change the image by time of day. - `sprite` accepts either a **string** (the alias of a linked asset) or a function returning a PixiJS element (e.g. a `Sprite` with `eventMode: "static"` and a `pointerdown` handler) when you need full control. - `icon` is a function returning a UI element (e.g. a React button). ### Default ("entrance") room of a location Add `isEntrance` to `Room` (and `RoomInterface`), and in `Location` expose `get entrance()` returning `super.rooms.find((r) => r.isEntrance) || super.rooms[0]` (or `undefined` if empty). A map UI then does `navigator.currentRoom = location.entrance` when the location icon is clicked. ## Pitfalls - Forgetting `Registered*.add`: the room cannot be saved or entered from ink. - Overriding `rooms`/`locations` getters to filter `hidden` is common; remember hidden rooms are then absent from `location.rooms`.