fix(babylon_setup.js): fix exit not generating properly
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -126,6 +126,7 @@ let chestMap = new Map(); // key: "x,y" -> {mesh, opened}
|
||||
let keyChestKey = null;
|
||||
let exitBox = null;
|
||||
let exitGridPos = null; // track exit grid position for collision checking
|
||||
let spawnGridPos = null; // track spawn grid position for validation
|
||||
let spawnMarker = null;
|
||||
const cellSize = 2;
|
||||
|
||||
@@ -139,6 +140,7 @@ function clearLevelMeshes() {
|
||||
if (exitBox) { try { exitBox.dispose(); } catch(e){}; exitBox = null; }
|
||||
if (spawnMarker) { try { spawnMarker.dispose(); } catch(e){}; spawnMarker = null; }
|
||||
exitGridPos = null;
|
||||
spawnGridPos = null;
|
||||
}
|
||||
|
||||
// Simple seeded RNG
|
||||
@@ -209,6 +211,26 @@ function findDeadEnds(grid) {
|
||||
return dead;
|
||||
}
|
||||
|
||||
function gridCellToWorld(grid, x, y) {
|
||||
const halfW = (grid[0].length * cellSize) / 2;
|
||||
const halfH = (grid.length * cellSize) / 2;
|
||||
return new BABYLON.Vector3(
|
||||
x * cellSize - halfW + cellSize / 2,
|
||||
0,
|
||||
y * cellSize - halfH + cellSize / 2,
|
||||
);
|
||||
}
|
||||
|
||||
function isWalkableCell(grid, x, y) {
|
||||
return y >= 0 && y < grid.length && x >= 0 && x < grid[0].length && grid[y][x] === 0;
|
||||
}
|
||||
|
||||
function isReservedCell(x, y) {
|
||||
if (chestMap.has(`${x},${y}`)) return true;
|
||||
if (exitGridPos && exitGridPos.x === x && exitGridPos.y === y) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function buildLevelFromGrid(grid) {
|
||||
clearLevelMeshes();
|
||||
const h = grid.length;
|
||||
@@ -295,71 +317,96 @@ function placeExit(grid, seed) {
|
||||
exitGridPos = { x, y };
|
||||
}
|
||||
|
||||
const [x,y] = exitGridPos;
|
||||
const halfW = (grid[0].length * cellSize) / 2;
|
||||
const halfH = (grid.length * cellSize) / 2;
|
||||
const ex = x*cellSize - halfW + cellSize/2;
|
||||
const ez = y*cellSize - halfH + cellSize/2;
|
||||
const [x,y] = [exitGridPos.x, exitGridPos.y];
|
||||
if (!isWalkableCell(grid, x, y)) {
|
||||
console.warn("Exit selected on non-walkable cell", { x, y });
|
||||
return;
|
||||
}
|
||||
const exitWorld = gridCellToWorld(grid, x, y);
|
||||
const ex = exitWorld.x;
|
||||
const ez = exitWorld.z;
|
||||
const worldSpan = Math.max(grid[0].length, grid.length) * cellSize;
|
||||
|
||||
// Ground plane indicator
|
||||
const plane = BABYLON.MeshBuilder.CreateGround('exitZone', { width: cellSize*0.9, height: cellSize*0.9 }, scene);
|
||||
plane.material = new BABYLON.StandardMaterial('exitMat', scene);
|
||||
plane.material.emissiveColor = new BABYLON.Color3(0.9, 0.8, 0.2);
|
||||
plane.position = new BABYLON.Vector3(ex, 0.01, ez);
|
||||
// Ground plane indicator (larger + slightly raised to avoid z-fighting)
|
||||
const plane = BABYLON.MeshBuilder.CreateGround('exitZone', { width: cellSize*1.4, height: cellSize*1.4 }, scene);
|
||||
const exitMat = new BABYLON.StandardMaterial('exitMat', scene);
|
||||
exitMat.diffuseColor = new BABYLON.Color3(0.25, 0.2, 0.03);
|
||||
exitMat.emissiveColor = new BABYLON.Color3(1.0, 0.85, 0.15);
|
||||
exitMat.disableLighting = true;
|
||||
plane.material = exitMat;
|
||||
plane.position = new BABYLON.Vector3(ex, 0.08, ez);
|
||||
exitBox = plane;
|
||||
levelMeshes.push(plane);
|
||||
|
||||
// Tall pillar for visibility
|
||||
const pillar = BABYLON.MeshBuilder.CreateCylinder('exitPillar', { diameter: cellSize*0.3, height: cellSize*3.0 }, scene);
|
||||
// Very tall beacon for visibility over maze walls
|
||||
const beaconHeight = Math.max(18, worldSpan * 0.8);
|
||||
const pillar = BABYLON.MeshBuilder.CreateCylinder('exitPillar', { diameter: cellSize*0.55, height: beaconHeight }, scene);
|
||||
const pillarMat = new BABYLON.StandardMaterial('exitPillarMat', scene);
|
||||
pillarMat.diffuseColor = new BABYLON.Color3(0.95, 0.8, 0.1);
|
||||
pillarMat.emissiveColor = new BABYLON.Color3(0.3, 0.25, 0.05);
|
||||
pillarMat.emissiveColor = new BABYLON.Color3(1.0, 0.7, 0.1);
|
||||
pillarMat.disableLighting = true;
|
||||
pillar.material = pillarMat;
|
||||
pillar.position = new BABYLON.Vector3(ex, cellSize*0.75, ez);
|
||||
pillar.position = new BABYLON.Vector3(ex, beaconHeight * 0.5, ez);
|
||||
levelMeshes.push(pillar);
|
||||
|
||||
// Bright orb at the top of beacon
|
||||
const orb = BABYLON.MeshBuilder.CreateSphere('exitOrb', { diameter: cellSize * 1.0 }, scene);
|
||||
const orbMat = new BABYLON.StandardMaterial('exitOrbMat', scene);
|
||||
orbMat.diffuseColor = new BABYLON.Color3(1.0, 0.9, 0.3);
|
||||
orbMat.emissiveColor = new BABYLON.Color3(1.0, 0.9, 0.35);
|
||||
orbMat.disableLighting = true;
|
||||
orb.material = orbMat;
|
||||
orb.position = new BABYLON.Vector3(ex, beaconHeight + cellSize * 0.7, ez);
|
||||
levelMeshes.push(orb);
|
||||
}
|
||||
|
||||
function spawnCameraAt(grid) {
|
||||
const h = grid.length, w = grid[0].length;
|
||||
for (let y = 1; y < h-1; y++) {
|
||||
for (let x = 1; x < w-1; x++) {
|
||||
// Skip walls
|
||||
if (grid[y][x] !== 0) continue;
|
||||
|
||||
// Skip chests
|
||||
if (chestMap.has(`${x},${y}`)) continue;
|
||||
|
||||
// Skip exit
|
||||
if (exitGridPos && exitGridPos.x === x && exitGridPos.y === y) continue;
|
||||
|
||||
// Found valid spawn point
|
||||
const halfW = (w * cellSize) / 2;
|
||||
const halfH = (h * cellSize) / 2;
|
||||
const px = x*cellSize - halfW + cellSize/2;
|
||||
const pz = y*cellSize - halfH + cellSize/2;
|
||||
|
||||
try {
|
||||
if (camera && camera.position) {
|
||||
camera.position = new BABYLON.Vector3(px, 1.6, pz);
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
// Add spawn marker sphere
|
||||
if (spawnMarker) {
|
||||
try { spawnMarker.dispose(); } catch(e) {}
|
||||
const h = grid.length;
|
||||
const w = grid[0].length;
|
||||
let bestCell = null;
|
||||
let bestDist = -1;
|
||||
|
||||
// Choose a valid spawn that is far from exit when possible.
|
||||
for (let y = 1; y < h - 1; y++) {
|
||||
for (let x = 1; x < w - 1; x++) {
|
||||
if (!isWalkableCell(grid, x, y)) continue;
|
||||
if (isReservedCell(x, y)) continue;
|
||||
|
||||
const d = exitGridPos ? Math.hypot(x - exitGridPos.x, y - exitGridPos.y) : 0;
|
||||
if (d > bestDist) {
|
||||
bestDist = d;
|
||||
bestCell = { x, y };
|
||||
}
|
||||
const marker = BABYLON.MeshBuilder.CreateSphere('spawnMarker', { diameter: cellSize*0.4 }, scene);
|
||||
const markerMat = new BABYLON.StandardMaterial('spawnMarkerMat', scene);
|
||||
markerMat.diffuseColor = new BABYLON.Color3(0.2, 0.6, 0.95);
|
||||
markerMat.emissiveColor = new BABYLON.Color3(0.1, 0.3, 0.5);
|
||||
marker.material = markerMat;
|
||||
marker.position = new BABYLON.Vector3(px, cellSize*0.2, pz);
|
||||
spawnMarker = marker;
|
||||
levelMeshes.push(marker);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bestCell) {
|
||||
console.warn("No valid spawn cell found.");
|
||||
return;
|
||||
}
|
||||
|
||||
spawnGridPos = bestCell;
|
||||
const spawnWorld = gridCellToWorld(grid, bestCell.x, bestCell.y);
|
||||
const px = spawnWorld.x;
|
||||
const pz = spawnWorld.z;
|
||||
|
||||
try {
|
||||
if (camera && camera.position) {
|
||||
camera.position = new BABYLON.Vector3(px, 1.6, pz);
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
if (spawnMarker) {
|
||||
try { spawnMarker.dispose(); } catch(e) {}
|
||||
}
|
||||
const marker = BABYLON.MeshBuilder.CreateSphere('spawnMarker', { diameter: cellSize*0.4 }, scene);
|
||||
const markerMat = new BABYLON.StandardMaterial('spawnMarkerMat', scene);
|
||||
markerMat.diffuseColor = new BABYLON.Color3(0.2, 0.6, 0.95);
|
||||
markerMat.emissiveColor = new BABYLON.Color3(0.1, 0.3, 0.5);
|
||||
marker.material = markerMat;
|
||||
marker.position = new BABYLON.Vector3(px, cellSize*0.2, pz);
|
||||
spawnMarker = marker;
|
||||
levelMeshes.push(marker);
|
||||
}
|
||||
|
||||
function generateLevel() {
|
||||
@@ -374,7 +421,20 @@ function generateLevel() {
|
||||
placeChestsOnDeadEnds(grid, dead, cfg.minChestDeadEnds, seed + cfg.level);
|
||||
placeExit(grid, seed + cfg.level);
|
||||
spawnCameraAt(grid);
|
||||
sharedState.runtime.message = `Level ${cfg.level} generated.`;
|
||||
|
||||
const placementValid =
|
||||
!!exitGridPos &&
|
||||
!!spawnGridPos &&
|
||||
isWalkableCell(grid, exitGridPos.x, exitGridPos.y) &&
|
||||
isWalkableCell(grid, spawnGridPos.x, spawnGridPos.y) &&
|
||||
!(exitGridPos.x === spawnGridPos.x && exitGridPos.y === spawnGridPos.y);
|
||||
|
||||
if (!placementValid) {
|
||||
sharedState.runtime.message = `Placement warning: spawn/exit invalid on level ${cfg.level}.`;
|
||||
console.warn("Invalid spawn/exit placement", { exitGridPos, spawnGridPos });
|
||||
} else {
|
||||
sharedState.runtime.message = `Level ${cfg.level} generated (spawn ${spawnGridPos.x},${spawnGridPos.y} / exit ${exitGridPos.x},${exitGridPos.y}).`;
|
||||
}
|
||||
window.requestAnimationFrame(()=>{ /* let scene update */ });
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user