feat: Implement advanced tile mapping system with abstract deck
- Created TileDefinitions.js with centralized tile definitions - Implemented abstract deck system (8 rooms 4x4, 4 rooms 4x6, 12 corridors, 10 L-shapes, 8 T-junctions) - Added connection validation (type compatibility, exit direction, walkability alignment) - Implemented corridor orientation filtering (EW/NS matching) - Added exhaustive L/T variant selection with random choice - Updated corridor definitions with EW and NS orientations - Fixed ASSETS.tiles references throughout main.js - Known issue: L/T offset alignment needs further debugging
This commit is contained in:
443
src/main.js
443
src/main.js
@@ -4,7 +4,9 @@ import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
||||
import { io } from "socket.io-client";
|
||||
import { turnManager, PHASES } from './systems/TurnManager.js';
|
||||
import { dungeonDeck } from './dungeon/DungeonDecks.js';
|
||||
import * as TileDefinitions from './dungeon/TileDefinitions.js';
|
||||
import { eventDeck } from './dungeon/EventDeck.js'; // Import Event Deck
|
||||
import { TILE_DEFINITIONS, getTileDefinition } from './dungeon/TileDefinitions.js';
|
||||
|
||||
// --- NETWORK SETUP ---
|
||||
// Dynamic connection to support playing from mobile on the same network
|
||||
@@ -78,9 +80,11 @@ const ASSETS = {
|
||||
// --- GENERACIÓN DE MAZMORRA (DINÁMICA) ---
|
||||
function generateDungeon() {
|
||||
// Start with a single entry room 4x4
|
||||
const startTileDef = getTileDefinition('room_4x4_normal');
|
||||
const startRoom = {
|
||||
id: 1,
|
||||
tile: { type: 'tile_base', x: 0, y: 0 },
|
||||
tileDef: startTileDef, // Store the full tile definition
|
||||
tile: { id: startTileDef.id, x: 0, y: 0 },
|
||||
walls: [], // Walls calculated dynamically later or fixed for start
|
||||
doors: [
|
||||
{ side: 'N', gridX: 2, gridY: 0, leadsTo: null, id: 'door_start_N', isOpen: false }
|
||||
@@ -99,9 +103,6 @@ function generateDungeon() {
|
||||
|
||||
// --- EXPLORACIÓN DINÁMICA ---
|
||||
function exploreRoom(originRoom, door) {
|
||||
const card = dungeonDeck.drawCard();
|
||||
if (!card) return null;
|
||||
|
||||
// Draw an event card
|
||||
const eventCard = eventDeck.drawCard();
|
||||
if (eventCard) {
|
||||
@@ -109,18 +110,90 @@ function exploreRoom(originRoom, door) {
|
||||
showUIEvent(eventCard);
|
||||
}
|
||||
|
||||
const nextTileDef = ASSETS.tiles[card.type];
|
||||
const newRoomId = ROOMS.rooms.length + 1;
|
||||
|
||||
// 1. Determinar lado de entrada (Opuesto al de salida)
|
||||
// Determine entry side (opposite of exit)
|
||||
let entrySide;
|
||||
if (door.side === 'N') entrySide = 'S';
|
||||
else if (door.side === 'S') entrySide = 'N';
|
||||
else if (door.side === 'E') entrySide = 'W';
|
||||
else if (door.side === 'W') entrySide = 'E';
|
||||
|
||||
// 2. Determinar posición local de la puerta de entrada en la nueva sala
|
||||
// Centramos la puerta en el muro correspondiente
|
||||
// Try to draw a compatible abstract tile type (up to 10 attempts)
|
||||
let card = null;
|
||||
let alignmentOffset = 0;
|
||||
let attempts = 0;
|
||||
const maxAttempts = 10;
|
||||
|
||||
while (attempts < maxAttempts && !card) {
|
||||
const abstractCard = dungeonDeck.drawCompatibleCard(originRoom.tileDef.tileType);
|
||||
if (!abstractCard) {
|
||||
console.warn("Could not draw compatible card");
|
||||
return null;
|
||||
}
|
||||
|
||||
console.log(`Drew abstract type: ${abstractCard.type}`);
|
||||
|
||||
// Select concrete tile variant based on abstract type
|
||||
let candidates = [];
|
||||
|
||||
switch (abstractCard.type) {
|
||||
case 'room_4x4':
|
||||
candidates = TileDefinitions.ROOMS.filter(r => r.width === 4 && r.height === 4);
|
||||
break;
|
||||
case 'room_4x6':
|
||||
candidates = TileDefinitions.ROOMS.filter(r => r.width === 4 && r.height === 6);
|
||||
break;
|
||||
case 'corridor':
|
||||
// Filter by orientation (EW or NS based on exit direction)
|
||||
const isExitHorizontal = door.side === 'E' || door.side === 'W';
|
||||
candidates = TileDefinitions.CORRIDORS.filter(c => {
|
||||
const isCorridorHorizontal = c.exits.includes('E') && c.exits.includes('W');
|
||||
return isExitHorizontal === isCorridorHorizontal;
|
||||
});
|
||||
break;
|
||||
case 'L':
|
||||
candidates = [...TileDefinitions.L_SHAPES];
|
||||
break;
|
||||
case 'T':
|
||||
candidates = [...TileDefinitions.T_JUNCTIONS];
|
||||
break;
|
||||
}
|
||||
|
||||
if (candidates.length === 0) {
|
||||
console.warn(`No candidates found for type ${abstractCard.type}`);
|
||||
attempts++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try all candidates and collect those that fit
|
||||
const fittingVariants = [];
|
||||
for (const variant of candidates) {
|
||||
const connectionResult = canConnectTiles(originRoom, variant, door.side);
|
||||
if (connectionResult.valid) {
|
||||
fittingVariants.push({ variant, offset: connectionResult.offset });
|
||||
}
|
||||
}
|
||||
|
||||
if (fittingVariants.length > 0) {
|
||||
// RANDOM selection from fitting variants
|
||||
const selected = fittingVariants[Math.floor(Math.random() * fittingVariants.length)];
|
||||
card = selected.variant;
|
||||
alignmentOffset = selected.offset;
|
||||
console.log(`✓ Selected ${card.id} (${card.tileType}) randomly from ${fittingVariants.length} fitting variants, offset ${alignmentOffset}`);
|
||||
} else {
|
||||
console.log(`✗ No ${abstractCard.type} variant fits, trying another tile type...`);
|
||||
attempts++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!card) {
|
||||
console.error("Could not find valid tile after", maxAttempts, "attempts");
|
||||
return null;
|
||||
}
|
||||
|
||||
const nextTileDef = card;
|
||||
const newRoomId = ROOMS.rooms.length + 1;
|
||||
|
||||
// Calculate entry door position in the new tile
|
||||
let entryGridX, entryGridY;
|
||||
|
||||
if (entrySide === 'N') {
|
||||
@@ -137,13 +210,20 @@ function exploreRoom(originRoom, door) {
|
||||
entryGridY = Math.floor(nextTileDef.height / 2);
|
||||
}
|
||||
|
||||
// 3. Calcular posición absoluta de la nueva sala para que las puertas coincidan
|
||||
// Fórmula: NewRoomPos = OriginRoomPos + OriginDoorLocalPos - EntryDoorLocalPos
|
||||
// Esto hace que OriginDoorWorldPos == EntryDoorWorldPos
|
||||
const newX = originRoom.tile.x + door.gridX - entryGridX;
|
||||
const newY = originRoom.tile.y + door.gridY - entryGridY;
|
||||
// Calculate absolute position for the new tile
|
||||
let newX = originRoom.tile.x + door.gridX - entryGridX;
|
||||
let newY = originRoom.tile.y + door.gridY - entryGridY;
|
||||
|
||||
// Comprobar colisiones
|
||||
// Apply alignment offset based on exit direction
|
||||
if (door.side === 'N' || door.side === 'S') {
|
||||
// Vertical connection - offset horizontally
|
||||
newX += alignmentOffset;
|
||||
} else {
|
||||
// Horizontal connection - offset vertically
|
||||
newY += alignmentOffset;
|
||||
}
|
||||
|
||||
// Check for collisions
|
||||
if (!isAreaFree(newX, newY, nextTileDef.width, nextTileDef.height)) {
|
||||
console.warn("Cannot place room: Collision detected!");
|
||||
return null;
|
||||
@@ -151,17 +231,22 @@ function exploreRoom(originRoom, door) {
|
||||
|
||||
const newRoom = {
|
||||
id: newRoomId,
|
||||
tile: { type: card.type, x: newX, y: newY },
|
||||
tileDef: nextTileDef,
|
||||
tile: { id: nextTileDef.id, x: newX, y: newY },
|
||||
walls: ['N', 'S', 'E', 'W'],
|
||||
doors: [],
|
||||
entities: []
|
||||
};
|
||||
|
||||
// Crear la puerta de entrada
|
||||
// Determine if we should place a door or open connection
|
||||
const placeDoor = shouldPlaceDoor(originRoom.tileDef.tileType, nextTileDef.tileType);
|
||||
|
||||
// Create the entry door/connection
|
||||
const entryDoor = {
|
||||
side: entrySide,
|
||||
leadsTo: originRoom.id,
|
||||
isOpen: true,
|
||||
isOpen: !placeDoor, // Open if no door, closed if door
|
||||
isDoor: placeDoor,
|
||||
id: `door_${newRoomId}_to_${originRoom.id}`,
|
||||
gridX: entryGridX,
|
||||
gridY: entryGridY
|
||||
@@ -169,18 +254,19 @@ function exploreRoom(originRoom, door) {
|
||||
|
||||
newRoom.doors.push(entryDoor);
|
||||
|
||||
// Generar salidas adicionales según la carta
|
||||
card.exits.forEach(exitDir => {
|
||||
if (exitDir === entrySide) return; // Ya tenemos esta puerta
|
||||
// Generate additional exits based on the tile definition
|
||||
nextTileDef.exits.forEach(exitDir => {
|
||||
if (exitDir === entrySide) return; // Already have this connection
|
||||
|
||||
const exitDoor = {
|
||||
side: exitDir,
|
||||
leadsTo: null, // Desconocido
|
||||
leadsTo: null,
|
||||
isOpen: false,
|
||||
isDoor: true, // Will be determined when connected
|
||||
id: `door_${newRoomId}_${exitDir}`
|
||||
};
|
||||
|
||||
// Calcular coordenadas de la puerta en la nueva sala
|
||||
// Calculate door coordinates
|
||||
if (exitDir === 'N') {
|
||||
exitDoor.gridX = Math.floor(nextTileDef.width / 2);
|
||||
exitDoor.gridY = 0;
|
||||
@@ -199,6 +285,7 @@ function exploreRoom(originRoom, door) {
|
||||
});
|
||||
|
||||
ROOMS.rooms.push(newRoom);
|
||||
console.log(`✓ Tile ${newRoomId} (${nextTileDef.tileType}) created: ${nextTileDef.id} at (${newX}, ${newY})`);
|
||||
return newRoom;
|
||||
}
|
||||
|
||||
@@ -514,7 +601,9 @@ function isAdjacent(p1, p2) {
|
||||
// Verificar si una posición está dentro de una sala
|
||||
function isPositionInRoom(x, y, room) {
|
||||
const tile = room.tile;
|
||||
const tileDef = ASSETS.tiles[tile.type];
|
||||
const tileDef = room.tileDef;
|
||||
if (!tileDef) return false;
|
||||
|
||||
const minX = tile.x;
|
||||
const maxX = tile.x + tileDef.width - 1;
|
||||
const minY = tile.y;
|
||||
@@ -527,16 +616,14 @@ function isPositionInRoom(x, y, room) {
|
||||
// x, y: Coordenadas Grid (Top-Left)
|
||||
// width, height: Dimensiones Grid
|
||||
function isAreaFree(x, y, width, height) {
|
||||
// Definir Rectángulo A (Propuesto)
|
||||
// Usamos buffer de 0.1 para evitar contactos exactos que no son solapamientos
|
||||
// Pero en Grid discreto, strict inequality es mejor.
|
||||
const aMinX = x;
|
||||
const aMaxX = x + width;
|
||||
const aMinY = y;
|
||||
const aMaxY = y + height;
|
||||
|
||||
for (const room of ROOMS.rooms) {
|
||||
const tileDef = ASSETS.tiles[room.tile.type];
|
||||
const tileDef = room.tileDef;
|
||||
if (!tileDef) continue;
|
||||
|
||||
// Rectángulo B (Existente)
|
||||
const bMinX = room.tile.x;
|
||||
@@ -545,7 +632,6 @@ function isAreaFree(x, y, width, height) {
|
||||
const bMaxY = room.tile.y + tileDef.height;
|
||||
|
||||
// Check Overlap (Intersección de AABB)
|
||||
// No hay colisión si alguno está totalmente a un lado del otro
|
||||
const noOverlap = aMaxX <= bMinX || aMinX >= bMaxX || aMaxY <= bMinY || aMinY >= bMaxY;
|
||||
|
||||
if (!noOverlap) {
|
||||
@@ -568,14 +654,21 @@ function isPositionDoor(x, y, room) {
|
||||
}
|
||||
|
||||
|
||||
// Verificar si una celda es transitable (bloquear puertas cerradas)
|
||||
|
||||
// Verificar si una celda es transitable usando la matriz de walkability
|
||||
function isWalkable(x, y) {
|
||||
for (const roomId of ROOMS.visitedRooms) {
|
||||
const room = ROOMS.rooms.find(r => r.id === roomId);
|
||||
if (!room) continue;
|
||||
if (!room || !room.tileDef) continue;
|
||||
|
||||
if (isPositionInRoom(x, y, room)) {
|
||||
return true;
|
||||
// Get local coordinates within the tile
|
||||
const localX = x - room.tile.x;
|
||||
const localY = y - room.tile.y;
|
||||
|
||||
// Check walkability matrix
|
||||
const walkValue = room.tileDef.walkability[localY][localX];
|
||||
return walkValue > 0; // 0 = not walkable, >0 = walkable
|
||||
}
|
||||
|
||||
// Verificar puertas
|
||||
@@ -589,6 +682,254 @@ function isWalkable(x, y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the layer/height of a specific position
|
||||
function getTileLayer(x, y) {
|
||||
for (const roomId of ROOMS.visitedRooms) {
|
||||
const room = ROOMS.rooms.find(r => r.id === roomId);
|
||||
if (!room || !room.tileDef) continue;
|
||||
|
||||
if (isPositionInRoom(x, y, room)) {
|
||||
const localX = x - room.tile.x;
|
||||
const localY = y - room.tile.y;
|
||||
|
||||
return room.tileDef.walkability[localY][localX];
|
||||
}
|
||||
}
|
||||
return 0; // Not in any room
|
||||
}
|
||||
|
||||
// Check if movement between two positions with different layers is allowed
|
||||
function canTransitionLayers(fromX, fromY, toX, toY) {
|
||||
const fromLayer = getTileLayer(fromX, fromY);
|
||||
const toLayer = getTileLayer(toX, toY);
|
||||
|
||||
// Same layer or one is a stair
|
||||
if (fromLayer === toLayer || fromLayer === 9 || toLayer === 9) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Different layers - check if there's a stair adjacent
|
||||
const layerDiff = Math.abs(fromLayer - toLayer);
|
||||
if (layerDiff > 1) {
|
||||
return false; // Can't jump more than 1 layer
|
||||
}
|
||||
|
||||
// Check if there's a stair (9) adjacent to either position
|
||||
const adjacentPositions = [
|
||||
{ x: fromX - 1, y: fromY },
|
||||
{ x: fromX + 1, y: fromY },
|
||||
{ x: fromX, y: fromY - 1 },
|
||||
{ x: fromX, y: fromY + 1 },
|
||||
{ x: toX - 1, y: toY },
|
||||
{ x: toX + 1, y: toY },
|
||||
{ x: toX, y: toY - 1 },
|
||||
{ x: toX, y: toY + 1 }
|
||||
];
|
||||
|
||||
for (const pos of adjacentPositions) {
|
||||
if (getTileLayer(pos.x, pos.y) === 9) {
|
||||
return true; // Found a stair
|
||||
}
|
||||
}
|
||||
|
||||
return false; // No stair found
|
||||
}
|
||||
|
||||
// Determine if a door should be placed between two tile types
|
||||
function shouldPlaceDoor(tileTypeA, tileTypeB) {
|
||||
// Doors only between Room ↔ Corridor
|
||||
return (tileTypeA === 'room' && tileTypeB === 'corridor') ||
|
||||
(tileTypeA === 'corridor' && tileTypeB === 'room');
|
||||
}
|
||||
|
||||
// Validate walkability alignment between two tiles at their connection point
|
||||
// Returns: { valid: boolean, offset: number } - offset is how much to shift tileB to align with tileA
|
||||
function validateWalkabilityAlignment(tileDefA, posA, tileDefB, posB, exitSide) {
|
||||
// Get the edge cells that will connect
|
||||
const edgeA = getEdgeCells(tileDefA, exitSide);
|
||||
const edgeB = getEdgeCells(tileDefB, getOppositeSide(exitSide));
|
||||
|
||||
console.log(`[ALIGN] Checking ${tileDefA.id} (${exitSide}) → ${tileDefB.id}`);
|
||||
console.log(`[ALIGN] EdgeA (${tileDefA.id}):`, edgeA);
|
||||
console.log(`[ALIGN] EdgeB (${tileDefB.id}):`, edgeB);
|
||||
|
||||
// Special handling for corridor connections
|
||||
// Corridors are 2 tiles wide, rooms/L/T are typically 4 tiles wide
|
||||
// We need to find where the corridor's walkable area aligns with the room's walkable area
|
||||
|
||||
if (edgeA.length !== edgeB.length) {
|
||||
// Different edge lengths - need to find alignment
|
||||
const smallerEdge = edgeA.length < edgeB.length ? edgeA : edgeB;
|
||||
const largerEdge = edgeA.length < edgeB.length ? edgeB : edgeA;
|
||||
const isASmaller = edgeA.length < edgeB.length;
|
||||
|
||||
console.log(`[ALIGN] Different sizes: ${edgeA.length} vs ${edgeB.length}`);
|
||||
console.log(`[ALIGN] isASmaller: ${isASmaller}`);
|
||||
|
||||
// Find walkable cells in smaller edge
|
||||
const smallerWalkable = smallerEdge.filter(cell => cell > 0);
|
||||
if (smallerWalkable.length === 0) {
|
||||
console.warn('[ALIGN] No walkable cells in smaller edge');
|
||||
return { valid: false, offset: 0 };
|
||||
}
|
||||
|
||||
const smallerWidth = smallerEdge.length;
|
||||
const largerWidth = largerEdge.length;
|
||||
|
||||
// FIRST: Try offset 0 (no displacement needed)
|
||||
let validAtZero = true;
|
||||
for (let i = 0; i < smallerWidth; i++) {
|
||||
const smallCell = smallerEdge[i];
|
||||
const largeCell = largerEdge[i];
|
||||
|
||||
const isSmallWalkable = smallCell > 0;
|
||||
const isLargeWalkable = largeCell > 0;
|
||||
|
||||
console.log(`[ALIGN] Offset 0, index ${i}: small=${smallCell}(${isSmallWalkable}) vs large=${largeCell}(${isLargeWalkable})`);
|
||||
|
||||
if (isSmallWalkable !== isLargeWalkable) {
|
||||
validAtZero = false;
|
||||
console.log(`[ALIGN] ❌ Offset 0 FAILED at index ${i}: walkability mismatch`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (validAtZero) {
|
||||
console.log(`✓ [ALIGN] Valid alignment at offset 0 (no displacement needed)`);
|
||||
return { valid: true, offset: 0 };
|
||||
}
|
||||
|
||||
// If offset 0 doesn't work, try offset of 2 (corridor width)
|
||||
// This aligns the corridor with the other walkable section of the L/T
|
||||
const offset = 2;
|
||||
if (offset <= largerWidth - smallerWidth) {
|
||||
let valid = true;
|
||||
for (let i = 0; i < smallerWidth; i++) {
|
||||
const smallCell = smallerEdge[i];
|
||||
const largeCell = largerEdge[offset + i];
|
||||
|
||||
const isSmallWalkable = smallCell > 0;
|
||||
const isLargeWalkable = largeCell > 0;
|
||||
|
||||
if (isSmallWalkable !== isLargeWalkable) {
|
||||
valid = false;
|
||||
console.log(`[ALIGN] Offset ${offset} failed at index ${i}: ${smallCell} vs ${largeCell}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
// Calculate final offset
|
||||
// If A (corridor) is smaller than B (L/T), we need to shift B by +offset
|
||||
// If B is smaller than A, we need to shift B by -offset
|
||||
const finalOffset = isASmaller ? offset : -offset;
|
||||
console.log(`✓ [ALIGN] Valid alignment at offset ${offset}, final offset: ${finalOffset} (isASmaller: ${isASmaller})`);
|
||||
return { valid: true, offset: finalOffset };
|
||||
}
|
||||
}
|
||||
|
||||
console.warn('[ALIGN] Could not find valid alignment for edges of different sizes');
|
||||
return { valid: false, offset: 0 };
|
||||
}
|
||||
|
||||
// Same length - check direct alignment
|
||||
for (let i = 0; i < edgeA.length; i++) {
|
||||
const cellA = edgeA[i];
|
||||
const cellB = edgeB[i];
|
||||
|
||||
// Rule: Cannot connect 0 (not walkable) with >0 (walkable)
|
||||
const isAWalkable = cellA > 0;
|
||||
const isBWalkable = cellB > 0;
|
||||
|
||||
if (isAWalkable !== isBWalkable) {
|
||||
console.warn(`[ALIGN] Walkability mismatch at index ${i}: ${cellA} vs ${cellB}`);
|
||||
return { valid: false, offset: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
return { valid: true, offset: 0 };
|
||||
}
|
||||
|
||||
// Get edge cells from a tile definition for a given side
|
||||
function getEdgeCells(tileDef, side) {
|
||||
const { walkability, width, height } = tileDef;
|
||||
const cells = [];
|
||||
|
||||
switch (side) {
|
||||
case 'N':
|
||||
// Top row
|
||||
for (let x = 0; x < width; x++) {
|
||||
cells.push(walkability[0][x]);
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
// Bottom row
|
||||
for (let x = 0; x < width; x++) {
|
||||
cells.push(walkability[height - 1][x]);
|
||||
}
|
||||
break;
|
||||
case 'E':
|
||||
// Right column
|
||||
for (let y = 0; y < height; y++) {
|
||||
cells.push(walkability[y][width - 1]);
|
||||
}
|
||||
break;
|
||||
case 'W':
|
||||
// Left column
|
||||
for (let y = 0; y < height; y++) {
|
||||
cells.push(walkability[y][0]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return cells;
|
||||
}
|
||||
|
||||
// Get opposite side
|
||||
function getOppositeSide(side) {
|
||||
const opposites = { 'N': 'S', 'S': 'N', 'E': 'W', 'W': 'E' };
|
||||
return opposites[side];
|
||||
}
|
||||
|
||||
// Check if two tiles can connect based on type rules and walkability alignment
|
||||
// Returns: { valid: boolean, offset: number } - offset for positioning the new tile
|
||||
function canConnectTiles(roomA, tileDefB, exitSide) {
|
||||
const tileDefA = roomA.tileDef;
|
||||
|
||||
// Check type compatibility
|
||||
const typeA = tileDefA.tileType;
|
||||
const typeB = tileDefB.tileType;
|
||||
|
||||
const validConnections = {
|
||||
'room': ['room', 'corridor'],
|
||||
'corridor': ['room', 'corridor', 'L', 'T'],
|
||||
'L': ['corridor'],
|
||||
'T': ['corridor']
|
||||
};
|
||||
|
||||
if (!validConnections[typeA] || !validConnections[typeA].includes(typeB)) {
|
||||
console.warn(`Invalid connection: ${typeA} cannot connect to ${typeB}`);
|
||||
return { valid: false, offset: 0 };
|
||||
}
|
||||
|
||||
// CRITICAL: Check that tileB has an exit in the opposite direction
|
||||
// If we exit through N, the new tile must have S in its exits
|
||||
const requiredExit = getOppositeSide(exitSide);
|
||||
if (!tileDefB.exits.includes(requiredExit)) {
|
||||
console.warn(`Exit direction mismatch: ${tileDefB.id} doesn't have required exit '${requiredExit}' (exiting via '${exitSide}')`);
|
||||
return { valid: false, offset: 0 };
|
||||
}
|
||||
|
||||
// Check walkability alignment and get offset
|
||||
const alignmentResult = validateWalkabilityAlignment(tileDefA, roomA.tile, tileDefB, null, exitSide);
|
||||
if (!alignmentResult.valid) {
|
||||
console.warn('Walkability alignment failed');
|
||||
return { valid: false, offset: 0 };
|
||||
}
|
||||
|
||||
return alignmentResult;
|
||||
}
|
||||
|
||||
// --- CREACIÓN DE MARCADORES ---
|
||||
function createPathMarker(stepNumber) {
|
||||
const canvas = document.createElement('canvas');
|
||||
@@ -996,10 +1337,17 @@ function checkDoorTransition(unit, currentRoom) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getDoorGridPosition(room, door) {
|
||||
const tile = room.tile;
|
||||
const tileWidth = ASSETS.tiles[tile.type].width;
|
||||
const tileHeight = ASSETS.tiles[tile.type].height;
|
||||
const tileDef = room.tileDef;
|
||||
if (!tileDef) {
|
||||
console.error("Room", room.id, "has no tileDef in getDoorGridPosition!");
|
||||
return { x: tile.x, y: tile.y };
|
||||
}
|
||||
|
||||
const tileWidth = tileDef.width;
|
||||
const tileHeight = tileDef.height;
|
||||
|
||||
switch (door.side) {
|
||||
case 'N':
|
||||
@@ -1102,21 +1450,22 @@ async function renderRoom(room) {
|
||||
entities: []
|
||||
};
|
||||
|
||||
// Renderizar tile
|
||||
// Renderizar tile
|
||||
const tileDef = ASSETS.tiles[room.tile.type];
|
||||
const baseTex = await loadTexture(tileDef.src);
|
||||
// Renderizar tile usando la nueva definición
|
||||
const tileDef = room.tileDef;
|
||||
if (!tileDef) {
|
||||
console.error("Room", room.id, "has no tileDef!");
|
||||
return;
|
||||
}
|
||||
|
||||
const baseTex = await loadTexture(tileDef.image);
|
||||
const tileTex = baseTex.clone(); // CLONAR para no afectar a otras salas
|
||||
tileTex.needsUpdate = true; // Asegurar que Three.js sepa que es nueva
|
||||
|
||||
tileTex.wrapS = THREE.RepeatWrapping;
|
||||
tileTex.wrapT = THREE.RepeatWrapping;
|
||||
|
||||
// Lógica de repetición: La textura base es de 4x4 celdas.
|
||||
// Si la sala es 8x4, repetimos 2 en X, 1 en Y.
|
||||
const repeatX = tileDef.width / 4;
|
||||
const repeatY = tileDef.height / 4;
|
||||
tileTex.repeat.set(repeatX, repeatY);
|
||||
// No repetir la textura - cada tile tiene su propia imagen completa
|
||||
tileTex.repeat.set(1, 1);
|
||||
|
||||
const worldWidth = tileDef.width * CONFIG.CELL_SIZE;
|
||||
const worldHeight = tileDef.height * CONFIG.CELL_SIZE;
|
||||
@@ -1374,7 +1723,8 @@ function drawMinimap() {
|
||||
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
||||
|
||||
ROOMS.rooms.forEach(room => {
|
||||
const tileDef = ASSETS.tiles[room.tile.type];
|
||||
const tileDef = room.tileDef;
|
||||
if (!tileDef) return;
|
||||
minX = Math.min(minX, room.tile.x);
|
||||
maxX = Math.max(maxX, room.tile.x + tileDef.width);
|
||||
minY = Math.min(minY, room.tile.y);
|
||||
@@ -1406,7 +1756,8 @@ function drawMinimap() {
|
||||
|
||||
// 2. Dibujar TODAS las Salas
|
||||
ROOMS.rooms.forEach(room => {
|
||||
const tileDef = ASSETS.tiles[room.tile.type];
|
||||
const tileDef = room.tileDef;
|
||||
if (!tileDef) return;
|
||||
|
||||
const pos = toCanvas(room.tile.x, room.tile.y);
|
||||
const w = tileDef.width * scale;
|
||||
|
||||
Reference in New Issue
Block a user