import type { CharacterStats } from './messages.js'; import { type QuestId } from './quests.js'; import { type StorySkill } from './storyQuests.js'; /** * The standard area gate (plan dreadwoods-backend/PLAN_1_TO_40.md section 5.2, decision D3 agreed by James 2026-10-12). * * Every door, hole, arch or sealed stair that turns a player away for what they lack is one entry in AREA_GATES. The * server checks it (world.ts handleAreaGate: the cave-entrance click and the prop click), the client draws the padlock * badge over it (grey while sealed for you, gold once you qualify) and names what it wants on hover, the guide lists it * ("Sealed places"), and scripts/area-gates-smoke.ts in the backend proves each one. A new gate is one entry here. * * The rules (D3): * - requirements are ALL-of and are listed, checked and worded in this order: Combat level, a quest, Explorer level, * any other skill level, membership, a key (GATE_REQUIREMENT_ORDER); * - a key never gates 41+ content on its own: such a gate pairs it with Combat (areaGateProblems checks it); * - every refusal is one sentence: " is sealed. You need ." (gateRefusalText); * - going out is never refused (a gate with `outside` only checks the way in); * - levels on gates are placeholders until the balance pass: they are the levels the game data already used, or * GATE_PLACEHOLDER_41 for 41+ content. */ export { GATE_PLACEHOLDER_41, GATE_PLACEHOLDER_BOSS, FROZEN_HALLWAY_COMBAT_LEVEL, FROZEN_DEPTHS_COMBAT_LEVEL } from './gateLevels.js'; /** Any skill a gate can ask a level of, besides Combat and Explorer (which have their own requirement kinds). */ export type GateSkill = Exclude; export type GateRequirement = { combat: number; } /** A quest the player must have completed (an old quest or a story quest, by id). */ | { quest: QuestId; } /** * A quest that is not in the game yet (the Great Stair's "The Sealed Stair"): always missing, so the gate stays shut * for everyone and says plainly what will open it. */ | { comingQuest: string; } | { explorer: number; } | { skill: GateSkill; level: number; } | { member: true; } /** An item in the bag; `label` is how the sentence names it ("a Frozen Key"); `consume` uses one up on the way in. */ | { item: string; label: string; consume?: boolean; }; /** Where a gate stands: an entity by kind and style (exact styles, or style prefixes). */ export type AreaGateAt = { kind: string; styles?: readonly string[]; stylePrefixes?: readonly string[]; }; /** A rectangle of world tiles, both ends included. */ export type GateTileBox = { minX: number; maxX: number; minZ: number; maxZ: number; }; /** * A walk gate (slice A2, 2026-10-12): a gate on a path rather than on a door. Its band tiles are blocked in the shared * walk grid (so no creature crosses them either way), and a player may walk them only when the gate admits them, or * when they are already inside or on the band (going out is never refused). The inside is everything a player reaches * on foot from `inside` without crossing a band; the backend works it out once (world.ts walkGateInside) and the smoke * proves `outside` is not in it. */ export type AreaGateWalk = { bands: readonly GateTileBox[]; /** A tile inside, on open ground. */ inside: { x: number; z: number; }; /** A tile outside in front of the first band, on open ground (where the smoke and the teleport links stand). */ outside: { x: number; z: number; }; /** * The most tiles the inside flood may hold (default 60000, a guard against a leak flooding the world). A gate whose inside is * a whole land (the Brindle Road: the Western heartland behind Lantern Wake's ridge) sets its own, still finite, cap. */ insideLimit?: number; }; export type AreaGate = { id: string; /** What the sentence calls the place, capitalised: "The Spider Cave". */ name: string; /** The place's own line, one sentence ending in a full stop: "Silk chokes the burrow, and something big moves in it." */ flavour: string; /** A door or prop gate: the entity it stands on. A walk gate has `walk` instead (a gate may have both). */ at?: AreaGateAt; walk?: AreaGateWalk; /** * A boss fight gate (James, 2026-10-12: "definitely not able to attack bosses" at Combat 40-49): the NPC kinds it covers. * The server refuses a player's attack on one of them with the gate's sentence, and the boss never locks on to a player * the gate refuses, so a boss that wanders or is pulled to a lower player still cannot be fought. No padlock badge. */ fight?: { npcKinds: readonly string[]; }; /** * Where the paper map draws the gate's padlock (world tiles). A walk gate defaults to its first band's middle; a door * gate without it (the wolf dens move about, the Great Stair is underground) gets no map padlock. */ mapAt?: { x: number; z: number; }; requires: readonly GateRequirement[]; /** * For a two-way door (the same entity both ways): the side the outside is on. Only a player standing on that side is * checked; one standing on the other side is going out, and going out is never refused. */ outside?: 'north' | 'south' | 'east' | 'west'; /** The highest level of any fight behind the gate, where it is 41+ (the key rule below reads it). */ guards?: number; /** * A known gap this gate leaves against D3, and the slice that closes it (the smoke lists these as open items rather * than failing on them; delete the line when the slice ships). */ openItem?: string; /** The place and where it is, for the guide's list: "the Spider Cave, through the den in Spider Hollow ...". */ where: string; /** World units above the entity's origin the padlock badge floats at (default 3). */ badgeY?: number; /** Draw the padlock badge (default true). */ badge?: boolean; }; /** A player as a gate sees them: what the server holds, and what the client holds for the badge. */ export type GatePlayerView = { stats: Pick; /** True once the quest is completed. */ questDone: (id: QuestId) => boolean; member: boolean; /** How many of an item kind the bag holds. */ itemCount: (kind: string) => number; }; /** * The gates, in the order the guide lists them. The five that were hand-coded in world.ts before 2026-10-12 keep their * levels and meaning; only their wording moved to the standard sentence. */ export declare const AREA_GATES: readonly AreaGate[]; /** The boss fight gate on an NPC kind (`fight`), or null for an ordinary creature. */ export declare function bossGateForKind(kind: string): AreaGate | null; export declare const AREA_GATE_BY_ID: Readonly>; /** The order requirements are checked and worded in (D3). */ export declare const GATE_REQUIREMENT_ORDER: readonly ["combat", "quest", "explorer", "skill", "member", "item"]; /** Which of the six kinds a requirement is (a coming quest counts as a quest). */ export declare function gateRequirementKind(req: GateRequirement): (typeof GATE_REQUIREMENT_ORDER)[number]; /** Whether one requirement is met. */ export declare function gateRequirementMet(req: GateRequirement, player: GatePlayerView): boolean; /** What the player still lacks, in the D3 order; empty when the gate admits them. */ export declare function gateMissing(gate: AreaGate, player: GatePlayerView): GateRequirement[]; /** How the sentence names one requirement: "Combat level 15", "the quest Wilson's Big Catch", "a Frozen Key". */ export declare function gateRequirementText(req: GateRequirement): string; /** A short name for the hover label and the guide: "Combat 15", "Explorer 5", "Frozen Key", "quest The Sealed Stair". */ export declare function gateRequirementShort(req: GateRequirement): string; /** "A", "A and B", "A, B and C". */ export declare function joinGateList(parts: readonly string[]): string; /** "Combat level 15 and Explorer level 5": the missing list as the sentence words it. */ export declare function gateNeedsText(missing: readonly GateRequirement[]): string; /** The one refusal sentence (D3): "The Spider Cave is sealed. Silk chokes the burrow... You need Explorer level 15." */ export declare function gateRefusalText(gate: AreaGate, missing: readonly GateRequirement[]): string; /** The hover line while a gate is sealed for you: "Sealed: Combat 15, Explorer 5". */ export declare function gateSealedLabel(missing: readonly GateRequirement[]): string; /** Every requirement, short, for the guide: "Combat 15, Explorer 5". */ export declare function gateRequirementsShort(gate: AreaGate): string; /** * Every requirement with whether it is met, in the D3 order: the examine list ("Combat level 41" with a tick or a * cross). A coming quest is never met. */ export declare function gateRequirementChecks(gate: AreaGate, player: GatePlayerView): { text: string; met: boolean; }[]; export declare const WALK_GATES: readonly AreaGate[]; /** Whether a tile is on one of a walk gate's bands. */ export declare function onWalkGateBand(gate: AreaGate, x: number, z: number): boolean; /** The walk gate whose band holds a tile, or null. */ export declare function walkGateAt(x: number, z: number): AreaGate | null; /** Every band tile of a walk gate. */ export declare function walkGateBandTiles(gate: AreaGate): { x: number; z: number; }[]; /** Where a walk gate's padlocks hang over the path: the middle of each band. */ export declare function walkGateBadgeTiles(gate: AreaGate): { x: number; z: number; }[]; /** Where the paper map draws a gate's padlock, or null for none. */ export declare function areaGateMapTile(gate: AreaGate): { x: number; z: number; } | null; /** The gate standing on an entity, or null. */ export declare function areaGateForEntity(kind: string, style: string | undefined): AreaGate | null; /** * Whether a player at (px, pz) clicking the gate at (gx, gz) is on the way in. A one-way door (no `outside`) is always * the way in; a two-way door only from its outside side. */ export declare function gateIsEntering(gate: AreaGate, px: number, pz: number, gx: number, gz: number): boolean; /** * What is wrong with the table, for the tests: an empty list when every gate keeps D3's rules (unique ids, a sentence * that ends right, requirements in the D3 order, no gate with nothing to ask, no key alone in front of 41+ content). */ export declare function areaGateProblems(gates?: readonly AreaGate[]): string[]; //# sourceMappingURL=areaGates.d.ts.map