(function (root) { "use strict"; function rect(x, y, width, height, depth) { return Object.freeze({ x, y, width, height, depth: depth || 2 }); } function enemy(type, x, y, options) { return Object.freeze(Object.assign({ type, x, y, leash: 150, engage: 245, requiresSigil: null, questId: null }, options || {})); } function landmark(id, title, eyebrow, x, y, npc, description, links, questId, area) { return Object.freeze({ id, title, eyebrow, x, y, npc, description, links, questId, area: area || null }); } const LANDMARKS = Object.freeze({ gate: landmark("gate", "Town Gate", "Arrivals", 724, 920, "orin", "The only opening in Archive Town's old wall. Gatekeeper Orin is trying to restore the erased arrival plaques.", [["Home", "/"], ["Recently updated", "/recently-updated.html"], ["Contact", "/home/contact.html"]], "gate"), library: landmark("library", "Grand Library", "Knowledge", 265, 305, "sera", "Head Archivist Sera guards the sealed vault and a shelf whose symbols no longer remember their order.", [["Posts", "/posts/posts-list.html"], ["Categories", "/home/categories.html"], ["Posts introduction", "/posts/posts-intro.html"]], "library", "vault"), study: landmark("study", "Guild Study", "Work", 720, 300, "vale", "Instructor Vale keeps the guild calm while Nulls gather beyond the practice banners.", [["Career", "/posts/career/career-list.html"], ["Competency status", "/home/status.html"], ["Probation objectives", "/posts/career/probation-objectives.html"]], "study"), inn: landmark("inn", "Kitchen Inn", "Daily life", 1110, 305, "mara", "Innkeeper Mara holds three borrowed memories and cannot recall which traveller entrusted each one.", [["Blogs", "/blogs/blogs-list.html"], ["Weekly reviews", "/tags/review.html"], ["Blogs introduction", "/blogs/blogs-intro.html"]], "inn"), workshop: landmark("workshop", "Workshop", "Living systems", 1125, 590, "pell", "Inventor Pell's recall engine has lost its cogs. A Redacted Sentinel occupies the repair yard.", [["Services", "/home/services.html"], ["Wird tracker", "/home/wird-tracker.html"], ["Countdowns", "/home/countdown.html"], ["Backlog", "/home/backlog.html"]], "workshop", "workshop"), playroom: landmark("playroom", "Playroom", "Experiments", 1120, 835, "pip", "Gamekeeper Pip's lantern maze changes whenever a name disappears. Six patient lights still know the route.", [["Play hub", "/play/play.html"], ["The Rain Index", "/play/the-rain-index.html"], ["House of Pages", "/play/house.html"]], "playroom", "playroom"), museum: landmark("museum", "Lima Museum", "Keepsakes", 270, 830, "lima", "Curator Lima can reveal what the Redactor was before the ink, but the Museum Lens is fractured.", [["Lima archive", "/lima/index.html"], ["Older writing", "/blogs/2025/2025-list.html"], ["Memory Cabinet", "/play/memory.html"]], "museum"), garden: landmark("garden", "Notes Garden", "Connections", 255, 545, "rowan", "Gardener Rowan tends standing stones that preserve connections no catalogue can hold alone.", [["Notes wall", "/home/notes.html"], ["Categories", "/home/categories.html"], ["Recently updated", "/recently-updated.html"], ["Sitemap", "/sitemap.html"]], "garden") }); const NPCS = Object.freeze({ orin: Object.freeze({ name: "Gatekeeper Orin", frame: 0, landmark: "gate", intro: "Your name held when the gate tried to erase it. That Living Bookmark chose well.", hint: "Read the three arrival plaques from oldest memory to newest: Home, Contact, Return." }), sera: Object.freeze({ name: "Head Archivist Sera", frame: 1, landmark: "library", intro: "Books are not silent. These shelves are shouting their symbols out of order.", hint: "A story begins with a seed, grows a branch, becomes a page, then earns its star." }), vale: Object.freeze({ name: "Instructor Vale", frame: 2, landmark: "study", intro: "A lesson remembered under pressure becomes a skill. Hold this square against the Nulls.", hint: "Use Space to strike. Keep moving after a Scribe raises its quill." }), mara: Object.freeze({ name: "Innkeeper Mara", frame: 3, landmark: "inn", intro: "Three guests left memories for safekeeping. The ink took their labels, not their meaning.", hint: "The sailor remembers rain, the gardener remembers a seed, and the child remembers a red kite." }), pell: Object.freeze({ name: "Inventor Pell", frame: 4, landmark: "workshop", intro: "My recall engine needs three cogs. The last one is under a Sentinel's very unreasonable boot.", hint: "The Sentinel pauses after its charge. That is the safe moment to attack." }), pip: Object.freeze({ name: "Gamekeeper Pip", frame: 5, landmark: "playroom", intro: "The maze is fair, even if the Redactor is not. Light the lamps from smallest story to largest.", hint: "Try lanterns two, four, one, three. The maze forgives every mistake." }), lima: Object.freeze({ name: "Curator Lima", frame: 6, landmark: "museum", intro: "Memories become dangerous only when stripped of context. Help me refocus the Museum Lens.", hint: "Align the lens from near memory to far memory: self, neighbour, town, archive." }), rowan: Object.freeze({ name: "Gardener Rowan", frame: 7, landmark: "garden", intro: "Connections grow when tended in the right season. Plant, water, remember, then share.", hint: "Seed before rain, rain before bloom, bloom before the path opens." }), nell: Object.freeze({ name: "Nell, a lost courier", frame: 8, landmark: null, intro: "I know I was carrying a letter. I cannot remember whether I meant to deliver it or keep it.", hint: "The kind answer is not always the easy answer." }), otho: Object.freeze({ name: "Otho", frame: 9, landmark: null, intro: "The lamps know more names than I do today.", hint: "" }), remy: Object.freeze({ name: "Remy", frame: 10, landmark: null, intro: "I leave breadcrumbs. The Redactor hates breadcrumbs.", hint: "" }) }); const QUESTS = Object.freeze({ gate: Object.freeze({ title: "A Name at the Gate", landmark: "gate", kind: "sequence", objective: "Restore Orin's three arrival plaques.", reward: "Gate Sigil · Living Bookmark · 80 Memory", sequence: ["home", "contact", "return"], options: ["return", "home", "contact"] }), library: Object.freeze({ title: "The Shouting Shelf", landmark: "library", kind: "sequence", objective: "Put the shelf symbols back into narrative order.", reward: "Library Sigil · Archivist's Key · 90 Memory", sequence: ["seed", "branch", "page", "star"], options: ["page", "seed", "star", "branch"] }), study: Object.freeze({ title: "Lesson Under Pressure", landmark: "study", kind: "combat", objective: "Defeat three Nulls threatening the Guild Study.", reward: "Study Sigil · +90 Memory", target: 3 }), inn: Object.freeze({ title: "Borrowed Memories", landmark: "inn", kind: "matching", objective: "Match rain, seed, and kite memories to their owners.", reward: "Inn Sigil · Recall Stew · 80 Memory", matches: { rain: "sailor", seed: "gardener", kite: "child" } }), workshop: Object.freeze({ title: "The Recall Engine", landmark: "workshop", kind: "boss", objective: "Enter the repair yard and defeat the Redacted Sentinel.", reward: "Workshop Sigil · Bookmark upgrade · 120 Memory" }), playroom: Object.freeze({ title: "Six Patient Lights", landmark: "playroom", kind: "sequence", objective: "Solve Pip's lantern route inside the Playroom maze.", reward: "Playroom Sigil · Swiftstep Boots · 90 Memory", sequence: ["2", "4", "1", "3"], options: ["1", "2", "3", "4"] }), museum: Object.freeze({ title: "Context Through Glass", landmark: "museum", kind: "sequence", objective: "Align the Museum Lens from nearest memory to farthest.", reward: "Museum Sigil · Museum Lens · 100 Memory", sequence: ["self", "neighbour", "town", "archive"], options: ["archive", "self", "town", "neighbour"] }), garden: Object.freeze({ title: "A Path That Remembers", landmark: "garden", kind: "sequence", objective: "Wake the standing stones in the order of growth.", reward: "Garden Sigil · Garden Seed · 80 Memory", sequence: ["seed", "rain", "bloom", "share"], options: ["rain", "share", "seed", "bloom"] }), lost_letter: Object.freeze({ title: "The Letter Nell Kept", landmark: "gate", kind: "choice", objective: "Help Nell decide what to do with the unaddressed letter.", reward: "Lore fragment · 40 Memory" }) }); const ITEMS = Object.freeze({ living_bookmark: Object.freeze({ name: "Living Bookmark", type: "equipment", icon: 0, description: "A blade-shaped bookmark that restores severed connections." }), lantern: Object.freeze({ name: "Lantern of Recall", type: "quest", icon: 1, description: "Its light outlines things the Redactor tried to remove." }), archive_key: Object.freeze({ name: "Archivist's Key", type: "key", icon: 2, description: "Opens the sealed stair beneath the Grand Library." }), swiftstep_boots: Object.freeze({ name: "Swiftstep Boots", type: "equipment", icon: 3, description: "Quicker starts and gentler stops without changing top speed." }), ink_vial: Object.freeze({ name: "Ink Vial", type: "consumable", icon: 4, description: "Restores 35 health.", stack: 9 }), memory_fragment: Object.freeze({ name: "Memory Fragment", type: "collectable", icon: 5, description: "A small truth with its edges intact.", stack: 12 }), garden_seed: Object.freeze({ name: "Garden Seed", type: "quest", icon: 6, description: "A seed that remembers every garden it came from." }), workshop_cog: Object.freeze({ name: "Workshop Cog", type: "quest", icon: 7, description: "A precision cog for Pell's recall engine.", stack: 3 }), museum_lens: Object.freeze({ name: "Museum Lens", type: "equipment", icon: 8, description: "Reveals erased details and hidden paths." }), recall_stew: Object.freeze({ name: "Recall Stew", type: "consumable", icon: 9, description: "Restores all health.", stack: 3 }), lore_page: Object.freeze({ name: "Lore Fragment", type: "lore", icon: 10, description: "Context the Redactor failed to consume.", stack: 16 }), archive_sigil: Object.freeze({ name: "Archive Sigil", type: "sigil", icon: 12, description: "One of eight seals that reopen the Archive Vault." }) }); const AREA_FRAMES = Object.freeze({ vault: 0, workshop: 1, playroom: 2, boss: 3 }); const AREAS = Object.freeze({ town: Object.freeze({ width: 1448, height: 1086, background: "town", spawn: { x: 724, y: 965 }, movement: Object.freeze({ speed: 162, acceleration: 1280, deceleration: 1850, bootAcceleration: 1720 }), safeSpawns: Object.freeze({ gate: { x: 724, y: 965 }, square: { x: 724, y: 555 }, library: { x: 270, y: 350 }, study: { x: 720, y: 330 }, inn: { x: 1110, y: 350 }, workshop: { x: 1060, y: 615 }, playroom: { x: 1060, y: 840 }, museum: { x: 330, y: 835 }, garden: { x: 330, y: 545 } }), safeZones: Object.freeze([ rect(650, 845, 150, 220), rect(585, 455, 280, 190) ]), gates: Object.freeze([ Object.freeze({ id: "town-gate", x: 672, y: 850, width: 104, height: 102, requiresSigil: "gate" }) ]), collisions: Object.freeze([ /* Outer town wall, with a deliberate walkable opening through the gate. */ rect(0, 0, 1448, 62), rect(0, 0, 54, 1086), rect(1394, 0, 54, 1086), rect(0, 862, 590, 98), rect(590, 840, 82, 132), rect(776, 840, 86, 132), rect(862, 862, 586, 98), /* Major buildings. Their steps and front doors remain approachable. */ rect(188, 62, 270, 212, 7), rect(606, 55, 238, 218, 7), rect(964, 58, 292, 218, 7), rect(1010, 418, 330, 142, 7), rect(1002, 655, 340, 155, 7), rect(218, 620, 220, 178, 7), /* Rivers. Breaks align with the two east/west bridge routes. */ rect(468, 88, 112, 255), rect(468, 432, 112, 93), rect(468, 610, 112, 250), rect(868, 88, 112, 255), rect(868, 432, 112, 93), rect(868, 610, 112, 250), /* Garden pond, dense hedges and corner ink pockets. */ rect(56, 360, 105, 292), rect(160, 382, 286, 45), rect(78, 675, 105, 175), rect(1260, 310, 130, 108), rect(1270, 572, 120, 92), rect(1270, 812, 120, 80) ]), npcs: Object.freeze([ ["orin", 724, 875], ["sera", 270, 335], ["vale", 720, 315], ["mara", 1110, 335], ["pell", 1060, 590], ["pip", 1060, 835], ["lima", 335, 830], ["rowan", 335, 545], ["nell", 760, 720], ["otho", 675, 520], ["remy", 790, 585] ]), enemies: Object.freeze([ enemy("ink_blot", 635, 350, { leash: 95, engage: 210, requiresSigil: "gate", questId: "study" }), enemy("lost_footnote", 805, 350, { leash: 95, engage: 210, requiresSigil: "gate", questId: "study" }), enemy("null_scribe", 720, 405, { leash: 105, engage: 280, requiresSigil: "gate", questId: "study" }), enemy("ink_blot", 1110, 625, { leash: 105, engage: 205, requiresSigil: "gate" }), enemy("lost_footnote", 340, 590, { leash: 75, engage: 175, requiresSigil: "gate" }) ]) }), vault: Object.freeze({ width: 724, height: 543, background: "areas", frame: 0, spawn: { x: 362, y: 485 }, movement: Object.freeze({ speed: 155, acceleration: 1300, deceleration: 1900, bootAcceleration: 1740 }), safeSpawns: Object.freeze({ entrance: { x: 362, y: 485 } }), safeZones: Object.freeze([rect(300, 430, 124, 95)]), collisions: Object.freeze([ rect(0, 0, 724, 32), rect(0, 0, 34, 543), rect(690, 0, 34, 543), rect(0, 32, 278, 130), rect(446, 32, 278, 130), rect(278, 32, 168, 60), rect(0, 472, 300, 71), rect(424, 472, 300, 71), rect(145, 150, 100, 72), rect(479, 150, 100, 72), rect(72, 272, 95, 70), rect(557, 272, 95, 70), rect(145, 345, 100, 72), rect(479, 345, 100, 72) ]), npcs: Object.freeze([]), enemies: Object.freeze([]) }), workshop: Object.freeze({ width: 724, height: 543, background: "areas", frame: 1, spawn: { x: 362, y: 475 }, movement: Object.freeze({ speed: 155, acceleration: 1300, deceleration: 1900, bootAcceleration: 1740 }), safeSpawns: Object.freeze({ entrance: { x: 362, y: 475 } }), safeZones: Object.freeze([rect(300, 430, 124, 95)]), collisions: Object.freeze([ rect(0, 0, 724, 32), rect(0, 0, 34, 543), rect(690, 0, 34, 543), rect(34, 32, 656, 115), rect(0, 472, 300, 71), rect(424, 472, 300, 71), rect(34, 145, 120, 118), rect(570, 145, 120, 125), rect(34, 342, 218, 130), rect(520, 360, 170, 112) ]), npcs: Object.freeze([]), enemies: Object.freeze([ enemy("sentinel", 362, 245, { leash: 205, engage: 360 }) ]) }), playroom: Object.freeze({ width: 724, height: 543, background: "areas", frame: 2, spawn: { x: 362, y: 485 }, movement: Object.freeze({ speed: 150, acceleration: 1250, deceleration: 1850, bootAcceleration: 1680 }), safeSpawns: Object.freeze({ entrance: { x: 362, y: 485 } }), safeZones: Object.freeze([rect(300, 445, 124, 80)]), collisions: Object.freeze([ rect(0, 0, 724, 38), rect(0, 0, 28, 543), rect(696, 0, 28, 543), rect(0, 488, 300, 55), rect(424, 488, 300, 55), rect(28, 38, 210, 58), rect(294, 38, 186, 58), rect(522, 38, 174, 58), rect(147, 104, 92, 92), rect(345, 96, 72, 118), rect(520, 106, 96, 94), rect(28, 220, 128, 72), rect(217, 214, 118, 78), rect(416, 210, 116, 82), rect(615, 220, 81, 72), rect(112, 315, 126, 78), rect(302, 300, 94, 102), rect(492, 315, 126, 78), rect(28, 408, 118, 80), rect(205, 408, 105, 80), rect(414, 408, 105, 80), rect(578, 408, 118, 80) ]), npcs: Object.freeze([]), enemies: Object.freeze([ enemy("lost_footnote", 90, 180, { leash: 60, engage: 150 }), enemy("ink_blot", 650, 350, { leash: 55, engage: 145 }) ]) }), boss: Object.freeze({ width: 724, height: 543, background: "areas", frame: 3, spawn: { x: 362, y: 470 }, movement: Object.freeze({ speed: 158, acceleration: 1380, deceleration: 1950, bootAcceleration: 1780 }), safeSpawns: Object.freeze({ entrance: { x: 362, y: 470 } }), safeZones: Object.freeze([rect(310, 438, 104, 70)]), collisions: Object.freeze([ rect(0, 0, 724, 42), rect(0, 0, 34, 543), rect(690, 0, 34, 543), rect(34, 42, 145, 55), rect(545, 42, 145, 55), rect(34, 97, 48, 350), rect(642, 97, 48, 350), rect(34, 447, 245, 64), rect(445, 447, 245, 64), rect(196, 146, 48, 48), rect(480, 146, 48, 48), rect(196, 326, 48, 48), rect(480, 326, 48, 48) ]), npcs: Object.freeze([]), enemies: Object.freeze([ enemy("redactor", 362, 190, { leash: 250, engage: 500 }) ]) }) }); const ENEMIES = Object.freeze({ ink_blot: Object.freeze({ name: "Ink Blot", frame: 11, health: 2, speed: 48, damage: 10, xp: 12, behaviour: "chase" }), lost_footnote: Object.freeze({ name: "Lost Footnote", frame: 12, health: 2, speed: 75, damage: 8, xp: 15, behaviour: "wander" }), null_scribe: Object.freeze({ name: "Null Scribe", frame: 13, health: 3, speed: 42, damage: 12, xp: 20, behaviour: "ranged" }), sentinel: Object.freeze({ name: "Redacted Sentinel", frame: 14, health: 12, speed: 82, damage: 16, xp: 100, behaviour: "charge", boss: true }), redactor: Object.freeze({ name: "The Redactor", frame: 15, health: 30, speed: 48, damage: 16, xp: 250, behaviour: "redactor", boss: true }) }); const api = Object.freeze({ LANDMARKS, NPCS, QUESTS, ITEMS, AREAS, AREA_FRAMES, ENEMIES, LANDMARK_IDS: Object.freeze(Object.keys(LANDMARKS)), QUEST_IDS: Object.freeze(Object.keys(QUESTS)), ITEM_IDS: Object.freeze(Object.keys(ITEMS)), AREA_IDS: Object.freeze(Object.keys(AREAS)), BOSS_IDS: Object.freeze(["sentinel", "redactor"]) }); root.ArchiveWorldData = api; if (typeof module === "object" && module.exports) module.exports = api; }(typeof globalThis !== "undefined" ? globalThis : this));