Refine archive world spawn and interaction handling
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:
@@ -120,12 +120,12 @@
|
||||
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 },
|
||||
width: 1448, height: 1086, background: "town", spawn: { x: 724, y: 1015 },
|
||||
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 }
|
||||
gate: { x: 724, y: 1015 }, square: { x: 724, y: 555 }, library: { x: 270, y: 370 },
|
||||
study: { x: 720, y: 350 }, inn: { x: 1110, y: 370 }, workshop: { x: 1060, y: 625 },
|
||||
playroom: { x: 1120, y: 835 }, museum: { x: 270, y: 830 }, garden: { x: 370, y: 545 }
|
||||
}),
|
||||
safeZones: Object.freeze([
|
||||
rect(650, 845, 150, 220), rect(585, 455, 280, 190)
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
});
|
||||
|
||||
this.createAnimations();
|
||||
this.npcSprites = [];
|
||||
this.npcCollisions = this.physics.add.staticGroup();
|
||||
const state = controller.getState();
|
||||
const safe = Systems.nearestSafeSpawn(this.areaId,
|
||||
state.position.area === this.areaId ? state.position.x : area.spawn.x,
|
||||
@@ -100,6 +100,7 @@
|
||||
this.interactables = [];
|
||||
this.markers = [];
|
||||
this.createWorldObjects();
|
||||
this.physics.add.collider(this.player, this.npcCollisions);
|
||||
this.createEnemies();
|
||||
this.createInput();
|
||||
|
||||
@@ -164,11 +165,12 @@
|
||||
if (!controller.reducedMotion()) this.tweens.add({ targets: marker, alpha: 0.45, scale: 1.14, yoyo: true, repeat: -1, duration: 1100 });
|
||||
});
|
||||
this.area.npcs.forEach(([id, x, y]) => {
|
||||
const npc = this.add.sprite(x, y, "cast", Data.NPCS[id].frame).setScale(0.23).setDepth(y + 60);
|
||||
const npc = this.npcCollisions.create(x, y, "cast", Data.NPCS[id].frame)
|
||||
.setScale(0.23).setDepth(y + 60);
|
||||
npc.refreshBody();
|
||||
npc.body.setSize(82, 44).setOffset(115, 214);
|
||||
npc.setData("interaction", { type: "npc", id, x, y });
|
||||
this.npcSprites.push(npc);
|
||||
this.interactables.push(npc.getData("interaction"));
|
||||
if (!controller.reducedMotion()) this.tweens.add({ targets: npc, y: y - 2, duration: 1200 + Data.NPCS[id].frame * 30, yoyo: true, repeat: -1 });
|
||||
});
|
||||
this.addChest("town-garden", 205, 565, "memory_fragment", 2);
|
||||
this.addChest("town-wall", 1235, 840, "ink_vial", 2);
|
||||
@@ -215,6 +217,7 @@
|
||||
createEnemies() {
|
||||
this.enemies = this.physics.add.group();
|
||||
this.physics.add.collider(this.enemies, this.collisions);
|
||||
this.physics.add.collider(this.enemies, this.npcCollisions);
|
||||
this.physics.add.collider(this.enemies, this.enemies);
|
||||
this.area.enemies.forEach((entry) => {
|
||||
const spawn = Array.isArray(entry)
|
||||
@@ -289,7 +292,6 @@
|
||||
if (!moving) this.stepDistance = 0;
|
||||
}
|
||||
this.player.setDepth(this.player.y + 60);
|
||||
this.npcSprites.forEach((npc) => npc.setFlipX(this.player.x < npc.x));
|
||||
this.updateEnemies(time, delta);
|
||||
this.updateNearest();
|
||||
this.handleKeys(time);
|
||||
@@ -311,13 +313,7 @@
|
||||
}
|
||||
|
||||
updateNearest() {
|
||||
let nearest = null;
|
||||
let best = Infinity;
|
||||
this.interactables.forEach((item) => {
|
||||
const distance = Phaser.Math.Distance.Between(this.player.x, this.player.y, item.x, item.y);
|
||||
if (distance < best) { best = distance; nearest = item; }
|
||||
});
|
||||
this.nearest = best < 92 ? nearest : null;
|
||||
this.nearest = Systems.nearestInteraction(this.interactables, this.player.x, this.player.y);
|
||||
controller.setPrompt(this.nearest ? promptFor(this.nearest) : "");
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
const LEGACY_KEY = "zxh_archive_world_v1";
|
||||
const PALETTES = Object.freeze(["brass", "moss", "berry"]);
|
||||
const FACES = Object.freeze(["north", "south", "east", "west"]);
|
||||
const SPAWN = Object.freeze({ area: "town", spawn: "gate", x: 724, y: 965 });
|
||||
const SPAWN = Object.freeze({ area: "town", spawn: "gate", x: 724, y: 1015 });
|
||||
const LANDMARK_IDS = Data ? Data.LANDMARK_IDS : Object.freeze(["gate", "library", "study", "inn", "workshop", "playroom", "museum", "garden"]);
|
||||
const QUEST_IDS = Data ? Data.QUEST_IDS : Object.freeze(LANDMARK_IDS.concat("lost_letter"));
|
||||
const ITEM_IDS = Data ? Data.ITEM_IDS : Object.freeze(["living_bookmark", "lantern", "archive_key", "swiftstep_boots", "ink_vial", "memory_fragment", "garden_seed", "workshop_cog", "museum_lens", "recall_stew", "lore_page", "archive_sigil"]);
|
||||
|
||||
@@ -193,9 +193,47 @@
|
||||
return Boolean(area && (area.safeZones || []).some((item) => pointInRect(x, y, item, 0)));
|
||||
}
|
||||
|
||||
function interactionRadius(item) {
|
||||
return {
|
||||
npc: 46,
|
||||
landmark: 64,
|
||||
portal: 58,
|
||||
chest: 48,
|
||||
challenge: 52,
|
||||
"boss-memory": 50
|
||||
}[item && item.type] || 44;
|
||||
}
|
||||
|
||||
function nearestInteraction(items, x, y) {
|
||||
let nearest = null;
|
||||
let best = Infinity;
|
||||
(Array.isArray(items) ? items : []).forEach((item) => {
|
||||
if (!item || !Number.isFinite(item.x) || !Number.isFinite(item.y)) return;
|
||||
const distance = Math.hypot(item.x - x, item.y - y);
|
||||
if (distance <= interactionRadius(item) && distance < best) {
|
||||
nearest = item;
|
||||
best = distance;
|
||||
}
|
||||
});
|
||||
return nearest;
|
||||
}
|
||||
|
||||
function safeSpawnForState(state) {
|
||||
const position = state && state.position;
|
||||
const gate = Data.AREAS.town.gates && Data.AREAS.town.gates[0];
|
||||
const gateLocked = state && Array.isArray(state.sigils) && !state.sigils.includes("gate");
|
||||
if (position && position.area === "town" && gate && gateLocked && position.y < gate.y + gate.height) {
|
||||
return nearestNamedSafeSpawn("town", Data.AREAS.town.safeSpawns.gate.x, Data.AREAS.town.safeSpawns.gate.y);
|
||||
}
|
||||
return nearestSafeSpawn(
|
||||
position && position.area || "town",
|
||||
position && position.x,
|
||||
position && position.y
|
||||
);
|
||||
}
|
||||
|
||||
function nearestSafeSpawn(areaId, x, y) {
|
||||
const area = Data.AREAS[areaId] || Data.AREAS.town;
|
||||
const points = Object.entries(area.safeSpawns || { entrance: area.spawn });
|
||||
const safeCurrent = isSafe(areaId, x, y);
|
||||
if (safeCurrent) return { area: areaId, spawn: "saved", x, y };
|
||||
return nearestNamedSafeSpawn(areaId, x, y);
|
||||
@@ -223,6 +261,7 @@
|
||||
return Object.freeze({
|
||||
normalizedVector, approachVelocity, discover, startQuest, progressQuest, completeQuest, updateStory, addItem, itemQuantity,
|
||||
consumeItem, grantXp, takeDamage, respawn, recordBoss, isSafe, isCombatSafe,
|
||||
interactionRadius, nearestInteraction, safeSpawnForState,
|
||||
nearestSafeSpawn, nearestNamedSafeSpawn, currentObjective
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
|
||||
function ensureSafePosition(state) {
|
||||
if (state.health <= 0) state = Systems.respawn(state);
|
||||
const safe = Systems.nearestSafeSpawn(state.position.area, state.position.x, state.position.y);
|
||||
const safe = Systems.safeSpawnForState(state);
|
||||
return State.withPosition(state, safe.area, safe.x, safe.y, state.position.facing, safe.spawn);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user