Rebuild Princess Lima maps around the new visual atlas
All checks were successful
Build Org Website / build (push) Successful in 38s
All checks were successful
Build Org Website / build (push) Successful in 38s
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
}(typeof globalThis !== "undefined" ? globalThis : this, function () {
|
||||
"use strict";
|
||||
|
||||
const WIDTH = 1280;
|
||||
const WIDTH = 720;
|
||||
const HEIGHT = 720;
|
||||
|
||||
function rect(x, y, width, height, kind) {
|
||||
@@ -80,7 +80,7 @@
|
||||
malrec: Object.freeze({ name: "Lord Malrec", frame: 8, dialogue: ["Lima's oath could command every border lord. With it, I would end their endless quarrels.", "If the kingdom will not accept peace, shadow will make it obey."] })
|
||||
});
|
||||
|
||||
const MAPS = Object.freeze({
|
||||
const LEGACY_MAPS = Object.freeze({
|
||||
village: Object.freeze({
|
||||
name: "Broken Village", chapter: 1, palette: ["#263b2d", "#596b3a", "#b49355", "#3b2d2a"],
|
||||
spawns: Object.freeze({ start: { x: 160, y: 570 }, square: { x: 640, y: 420 }, forestRoad: { x: 1110, y: 350 } }),
|
||||
@@ -234,6 +234,198 @@
|
||||
})
|
||||
});
|
||||
|
||||
function edges(openings) {
|
||||
const gap = Object.assign({ top: null, right: null, bottom: null, left: null }, openings || {});
|
||||
const result = [];
|
||||
const thickness = 18;
|
||||
[["top", WIDTH, "x"], ["bottom", WIDTH, "x"]].forEach(([side, length]) => {
|
||||
const opening = gap[side];
|
||||
const y = side === "top" ? 0 : HEIGHT - thickness;
|
||||
if (!opening) result.push(rect(0, y, length, thickness, "edge"));
|
||||
else {
|
||||
result.push(rect(0, y, opening[0], thickness, "edge"));
|
||||
result.push(rect(opening[1], y, length - opening[1], thickness, "edge"));
|
||||
}
|
||||
});
|
||||
[["left", HEIGHT, "y"], ["right", HEIGHT, "y"]].forEach(([side, length]) => {
|
||||
const opening = gap[side];
|
||||
const x = side === "left" ? 0 : WIDTH - thickness;
|
||||
if (!opening) result.push(rect(x, 0, thickness, length, "edge"));
|
||||
else {
|
||||
result.push(rect(x, 0, thickness, opening[0], "edge"));
|
||||
result.push(rect(x, opening[1], thickness, length - opening[1], "edge"));
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Coordinates below are authored against the nine 418x418 atlas panels
|
||||
* displayed at 720x720. They describe only visible, solid terrain. The
|
||||
* geometry is never rendered in production; ?collisionDebug=1 reveals it.
|
||||
*/
|
||||
const VISUAL_LAYOUT = Object.freeze({
|
||||
village: Object.freeze({
|
||||
spawns: Object.freeze({ start: { x: 365, y: 665 }, square: { x: 365, y: 355 }, forestRoad: { x: 365, y: 52 } }),
|
||||
obstacles: Object.freeze([
|
||||
...edges({ top: [318, 414], bottom: [330, 430] }),
|
||||
rect(18, 18, 176, 205, "house"), rect(454, 18, 248, 205, "house"),
|
||||
rect(18, 248, 118, 132, "cart"), rect(18, 475, 286, 227, "house"),
|
||||
rect(498, 338, 98, 104, "well"), rect(466, 465, 236, 92, "wall"),
|
||||
rect(548, 560, 154, 142, "cart")
|
||||
]),
|
||||
exits: Object.freeze([exit("forest-road", 330, 0, 70, 48, "forest", "villagePath", "village_defended", "Road to the Whispering Woods")]),
|
||||
npcs: Object.freeze([["elder", 365, 292], ["bram", 270, 350], ["nia", 466, 350]]),
|
||||
enemies: Object.freeze([
|
||||
enemy("slime", 260, 435, { quest: "village_defence", requires: "aftermath_complete" }),
|
||||
enemy("bandit", 365, 500, { quest: "village_defence", requires: "aftermath_complete" }),
|
||||
enemy("slime", 470, 420, { quest: "village_defence", requires: "aftermath_complete" })
|
||||
]),
|
||||
pickups: Object.freeze([["silver_leaf", 225, 265], ["silver_leaf", 515, 280], ["moon_coin", 440, 625]])
|
||||
}),
|
||||
forest: Object.freeze({
|
||||
spawns: Object.freeze({ villagePath: { x: 355, y: 660 }, ruinsPath: { x: 660, y: 392 }, mountainPath: { x: 355, y: 55 } }),
|
||||
obstacles: Object.freeze([
|
||||
...edges({ top: [315, 405], right: [348, 438], bottom: [318, 408] }),
|
||||
rect(18, 18, 220, 330, "trees"), rect(18, 418, 218, 284, "trees"),
|
||||
rect(470, 18, 232, 184, "trees"), rect(535, 202, 167, 146, "water"),
|
||||
rect(535, 438, 167, 264, "water"), rect(238, 18, 80, 178, "trees"),
|
||||
rect(235, 515, 72, 187, "trees"), rect(438, 475, 97, 227, "trees"),
|
||||
rect(470, 250, 65, 105, "trees")
|
||||
]),
|
||||
exits: Object.freeze([
|
||||
exit("village", 325, 672, 75, 48, "village", "forestRoad", null, "Return to the village"),
|
||||
exit("ruins", 665, 360, 55, 72, "ruins", "forestDoor", "guide_found", "Sunken Ruins"),
|
||||
exit("mountain", 325, 0, 72, 48, "mountain", "forestTrail", "briar_defeated", "Mountain trail")
|
||||
]),
|
||||
npcs: Object.freeze([["tovin", 390, 535]]),
|
||||
enemies: Object.freeze([
|
||||
enemy("wolf", 330, 225), enemy("slime", 420, 580), enemy("bandit", 430, 310),
|
||||
enemy("wolf", 560, 400), enemy("briar_wolf", 360, 110, { quest: "wolf_miniboss", boss: true, requires: "ruins_complete", leash: 190 })
|
||||
]),
|
||||
puzzle: Object.freeze({ id: "forest_stones", type: "sequence", sequence: ["sapling", "oak", "elder"], objects: [["sapling", 340, 565], ["oak", 365, 420], ["elder", 405, 270]] }),
|
||||
pickups: Object.freeze([["silver_leaf", 350, 610], ["silver_leaf", 445, 180], ["healing_tonic", 585, 390]])
|
||||
}),
|
||||
ruins: Object.freeze({
|
||||
spawns: Object.freeze({ forestDoor: { x: 360, y: 660 } }),
|
||||
obstacles: Object.freeze([
|
||||
...edges({ bottom: [320, 410] }),
|
||||
rect(18, 18, 248, 268, "water"), rect(454, 18, 248, 268, "water"),
|
||||
rect(18, 286, 180, 416, "water"), rect(522, 286, 180, 416, "water"),
|
||||
rect(198, 460, 92, 242, "water"), rect(430, 460, 92, 242, "water"),
|
||||
rect(266, 18, 55, 172, "ruin-wall"), rect(399, 18, 55, 172, "ruin-wall"),
|
||||
rect(198, 286, 92, 82, "water"), rect(430, 286, 92, 82, "water")
|
||||
]),
|
||||
exits: Object.freeze([exit("forest", 325, 672, 75, 48, "forest", "ruinsPath", null, "Return to the woods")]),
|
||||
npcs: Object.freeze([]),
|
||||
enemies: Object.freeze([enemy("bat", 350, 220), enemy("bat", 400, 370), enemy("slime", 350, 500)]),
|
||||
puzzle: Object.freeze({ id: "ruin_braziers", type: "sequence", sequence: ["dawn", "noon", "dusk", "night"], objects: [["dawn", 270, 430], ["noon", 325, 245], ["dusk", 450, 430], ["night", 395, 245]] }),
|
||||
pickups: Object.freeze([["moon_coin", 360, 330], ["healing_tonic", 390, 525]])
|
||||
}),
|
||||
mountain: Object.freeze({
|
||||
spawns: Object.freeze({ forestTrail: { x: 365, y: 660 }, campRoad: { x: 355, y: 55 } }),
|
||||
obstacles: Object.freeze([
|
||||
...edges({ top: [315, 405], bottom: [320, 410] }),
|
||||
rect(18, 18, 230, 684, "cliff"), rect(500, 18, 202, 684, "cliff"),
|
||||
rect(248, 18, 67, 208, "cliff"), rect(405, 18, 95, 245, "cliff"),
|
||||
rect(248, 430, 62, 272, "cliff"), rect(430, 455, 70, 247, "cliff"),
|
||||
rect(248, 295, 58, 85, "cliff"), rect(440, 315, 60, 92, "cliff")
|
||||
]),
|
||||
dynamicObstacles: Object.freeze([Object.freeze({ id: "bridge", x: 306, y: 315, width: 134, height: 58, kind: "chasm", opensWith: "bridge_repaired" })]),
|
||||
exits: Object.freeze([
|
||||
exit("forest", 325, 672, 75, 48, "forest", "mountainPath", null, "Return to the woods"),
|
||||
exit("camp", 325, 0, 75, 48, "camp", "mountainRoad", "guardian_defeated", "Road to Blackridge Camp")
|
||||
]),
|
||||
npcs: Object.freeze([["bram", 325, 250]]),
|
||||
enemies: Object.freeze([enemy("bat", 355, 455), enemy("guard", 360, 230), enemy("stone_guardian", 365, 115, { quest: "stone_guardian", boss: true, requires: "bridge_repaired", leash: 180 })]),
|
||||
puzzle: Object.freeze({ id: "bridge_winches", type: "set", sequence: ["west", "east"], objects: [["west", 330, 290], ["east", 390, 290]] }),
|
||||
pickups: Object.freeze([["moon_coin", 340, 575], ["healing_tonic", 400, 520]])
|
||||
}),
|
||||
camp: Object.freeze({
|
||||
spawns: Object.freeze({ mountainRoad: { x: 360, y: 660 }, fortressRoad: { x: 360, y: 55 } }),
|
||||
obstacles: Object.freeze([
|
||||
...edges({ top: [320, 405], bottom: [320, 405] }),
|
||||
rect(18, 18, 255, 175, "tent"), rect(455, 18, 247, 190, "tower"),
|
||||
rect(18, 518, 250, 184, "fence"), rect(468, 515, 234, 187, "fence"),
|
||||
rect(18, 193, 42, 325, "fence"), rect(660, 208, 42, 307, "fence"),
|
||||
rect(90, 365, 155, 75, "weapons"), rect(482, 380, 150, 78, "tent")
|
||||
]),
|
||||
exits: Object.freeze([
|
||||
exit("mountain", 325, 672, 75, 48, "mountain", "campRoad", null, "Return to the pass"),
|
||||
exit("fortress", 325, 0, 75, 48, "fortressExterior", "campGate", "emblem_found", "Fortress road")
|
||||
]),
|
||||
npcs: Object.freeze([["elowen", 440, 500]]),
|
||||
enemies: Object.freeze([enemy("guard", 250, 300, { quest: "free_scout" }), enemy("guard", 495, 300, { quest: "free_scout" }), enemy("captain", 420, 585, { quest: "free_scout", boss: true, leash: 180 })]),
|
||||
pickups: Object.freeze([["healing_tonic", 120, 260], ["moon_coin", 585, 250]])
|
||||
}),
|
||||
fortressExterior: Object.freeze({
|
||||
spawns: Object.freeze({ campGate: { x: 360, y: 660 }, innerGate: { x: 360, y: 455 } }),
|
||||
obstacles: Object.freeze([
|
||||
...edges({ bottom: [315, 405] }),
|
||||
rect(18, 18, 286, 430, "fortress-wall"), rect(416, 18, 286, 430, "fortress-wall"),
|
||||
rect(18, 448, 266, 254, "chasm"), rect(436, 448, 266, 254, "chasm"),
|
||||
rect(284, 18, 56, 310, "tower"), rect(380, 18, 56, 310, "tower")
|
||||
]),
|
||||
exits: Object.freeze([
|
||||
exit("camp", 325, 672, 75, 48, "camp", "fortressRoad", null, "Return to Blackridge"),
|
||||
exit("interior", 335, 285, 50, 64, "fortressInterior", "frontHall", "emblem_found", "Enter the fortress")
|
||||
]),
|
||||
npcs: Object.freeze([]),
|
||||
enemies: Object.freeze([enemy("guard", 330, 570), enemy("guard", 390, 445), enemy("guard", 350, 365)]),
|
||||
pickups: Object.freeze([["healing_tonic", 410, 520]])
|
||||
}),
|
||||
fortressInterior: Object.freeze({
|
||||
spawns: Object.freeze({ frontHall: { x: 360, y: 660 }, throneDoor: { x: 360, y: 95 } }),
|
||||
obstacles: Object.freeze([
|
||||
...edges({ top: [320, 400], bottom: [320, 400] }),
|
||||
rect(18, 18, 300, 160, "wall"), rect(402, 18, 300, 160, "wall"),
|
||||
rect(18, 178, 250, 175, "cell"), rect(452, 178, 250, 175, "cell"),
|
||||
rect(18, 420, 250, 282, "cell"), rect(452, 420, 250, 282, "cell"),
|
||||
rect(268, 178, 50, 110, "wall"), rect(402, 178, 50, 110, "wall")
|
||||
]),
|
||||
exits: Object.freeze([
|
||||
exit("outside", 325, 672, 75, 48, "fortressExterior", "innerGate", null, "Leave the fortress"),
|
||||
exit("throne", 325, 0, 75, 48, "bossArena", "entrance", "wards_broken", "Throne of Night")
|
||||
]),
|
||||
npcs: Object.freeze([["prisoner", 280, 375], ["elowen", 440, 375]]),
|
||||
enemies: Object.freeze([enemy("guard", 320, 500), enemy("guard", 400, 500), enemy("bat", 360, 240)]),
|
||||
puzzle: Object.freeze({ id: "shadow_wards", type: "set", sequence: ["moon", "crown", "flame"], objects: [["moon", 335, 205], ["crown", 360, 390], ["flame", 385, 205]] }),
|
||||
pickups: Object.freeze([["prison_key", 360, 540], ["healing_tonic", 425, 580]])
|
||||
}),
|
||||
bossArena: Object.freeze({
|
||||
spawns: Object.freeze({ entrance: { x: 360, y: 650 } }),
|
||||
obstacles: Object.freeze([
|
||||
...edges({ top: [320, 400] }),
|
||||
rect(18, 18, 175, 235, "wall"), rect(527, 18, 175, 235, "wall"),
|
||||
rect(18, 545, 190, 157, "wall"), rect(512, 545, 190, 157, "wall"),
|
||||
rect(250, 18, 220, 104, "throne")
|
||||
]),
|
||||
exits: Object.freeze([exit("chamber", 330, 0, 60, 48, "chamber", "door", "malrec_defeated", "Princess Lima's chamber")]),
|
||||
npcs: Object.freeze([["malrec", 360, 165]]),
|
||||
enemies: Object.freeze([enemy("malrec", 360, 245, { quest: "defeat_malrec", boss: true, requires: "boss_started", leash: 300 })]),
|
||||
puzzle: Object.freeze({ id: "sun_pedestals", type: "set", sequence: ["west", "east"], objects: [["west", 220, 390], ["east", 500, 390]] }),
|
||||
pickups: Object.freeze([])
|
||||
}),
|
||||
chamber: Object.freeze({
|
||||
spawns: Object.freeze({ door: { x: 360, y: 660 } }),
|
||||
obstacles: Object.freeze([
|
||||
...edges({ bottom: [320, 400] }),
|
||||
rect(18, 18, 265, 120, "wall"), rect(438, 18, 264, 120, "wall"),
|
||||
rect(465, 155, 220, 190, "bed"), rect(460, 475, 225, 190, "table"),
|
||||
rect(35, 475, 125, 190, "fountain")
|
||||
]),
|
||||
exits: Object.freeze([]),
|
||||
npcs: Object.freeze([["lima", 360, 180]]),
|
||||
enemies: Object.freeze([]),
|
||||
pickups: Object.freeze([])
|
||||
})
|
||||
});
|
||||
|
||||
const MAPS = Object.freeze(Object.fromEntries(Object.keys(LEGACY_MAPS).map((id) => [
|
||||
id,
|
||||
Object.freeze(Object.assign({}, LEGACY_MAPS[id], VISUAL_LAYOUT[id]))
|
||||
])));
|
||||
|
||||
const MAP_IDS = Object.freeze(Object.keys(MAPS));
|
||||
const QUEST_IDS = Object.freeze(Object.keys(QUESTS));
|
||||
const ITEM_IDS = Object.freeze(Object.keys(ITEMS));
|
||||
|
||||
Reference in New Issue
Block a user