Sesión 18: Refinado de spawning, ajustes de pasillo inicial y mejoras de UX/UI
This commit is contained in:
60
DEVLOG.md
60
DEVLOG.md
@@ -1,4 +1,62 @@
|
||||
# Devlog - Warhammer Quest (Versión Web 3D)
|
||||
|
||||
## Sesión 18: Refinado de Spawning y Ajustes de Corredores
|
||||
**Fecha:** 11 de Enero de 2026
|
||||
|
||||
### Objetivos
|
||||
- Corregir el spawn de monstruos en eventos de fase de poder (Minotauro).
|
||||
- Ajustar la generación de salidas en la loseta inicial.
|
||||
- Mejorar la visualización de notificaciones y la lógica de daño grupal.
|
||||
|
||||
### Cambios Realizados
|
||||
|
||||
#### 1. Spawn Hero-Centric para Eventos
|
||||
- **Restricción de Área**: Modificada la lógica de `findSpawnPoints` en `GameEngine.js` para que los monstruos generados por Eventos de Poder solo aparezcan en losetas ocupadas por héroes, evitando que aparezcan en el vacío o en zonas inexploradas.
|
||||
- **Fix Spawn Inicial**: Corregido bug de spawn inicial de héroes que causaba que aparecieran fuera de los límites (coordenada 0,0) si no se detectaban posiciones válidas inicialmente.
|
||||
|
||||
#### 2. Ajuste de Salidas Iniciales
|
||||
- **Regla del Inicio**: Los pasillos colocados como loseta inicial (`tile_0`) ahora habilitan exactamente **2 salidas aleatorias** (en lugar de 1 o 4), ofreciendo opciones estratégicas equilibradas desde el comienzo.
|
||||
- **Continuidad**: El resto de pasillos mantienen la regla de 1 salida extra para mantener la estructura de la mazmorra.
|
||||
|
||||
#### 3. Mejoras en EventInterpreter
|
||||
- **Daño Grupal**: Implementada "herencia de objetivos" en `EventInterpreter.js`. Si un efecto afecta a "todos", ahora se aplica correctamente a todos los héroes seleccionados en la acción previa.
|
||||
- **Logs Descriptivos**: Refinados los mensajes de log de eventos para ser más descriptivos: ahora detallan quién activó la trampa y a quién afecta exactamente el daño.
|
||||
|
||||
#### 4. UX/UI y Estética
|
||||
- **Posicionamiento de UI**: Desplazada la posición de los mensajes de notificación temporales (cartel rojo) a la zona inferior (70% de altura) para evitar solapamientos críticos con los botones de acción (`Acabar Turno`, etc.).
|
||||
- **Actualización de Assets**: Vinculadas nuevas texturas específicas para las salas de laboratorio y la bóveda en `TileDefinitions.js` (`room_4x4_skull.png`, `room_4x4_clean.png`).
|
||||
|
||||
---
|
||||
|
||||
## Sesión 17: Reglas de Mazmorra y Control de Pasillos
|
||||
**Fecha:** 11 de Enero de 2026
|
||||
|
||||
### Objetivos
|
||||
- Cumplir estrictamente con la composición del mazo de mazmorra (18 cartas originales).
|
||||
- Implementar la mecánica de barajado de 13 cartas con el objetivo en la mitad inferior.
|
||||
- Limitar el número de salidas en los pasillos para evitar laberintos infinitos.
|
||||
- Establecer una regla de proximidad (4 celdas) para la generación de nuevas puertas.
|
||||
|
||||
### Cambios Realizados
|
||||
|
||||
#### 1. Fiel Composición del Mazo (Warhammer Quest 1995)
|
||||
- **Mazo de 18 Cartas**: El pool inicial ahora contiene exactamente 6 salas de subterráneo, 7 pasillos, 3 cruces en T, 1 esquina y 1 escalera.
|
||||
- **Estructura de 13 Cartas**: Al generar la misión, se crea un mazo de 13 cartas:
|
||||
- 6 cartas aleatorias arriba.
|
||||
- 1 sala de objetivo + 6 cartas aleatorias barajadas abajo.
|
||||
- **Salas Específicas**: Se han definido 6 variantes de salas de subterráneo y 5 salas de objetivo únicas (`room_objective_1` a `5`) en `TileDefinitions.js`.
|
||||
- **Assets de Backup**: Generadas texturas placeholder (`room_4x4_placeholder.png`, `room_4x8_placeholder.png`) para las nuevas salas para asegurar la carga visual.
|
||||
|
||||
#### 2. Control de Salidas en Pasillos (Regla de las 2 Salidas)
|
||||
- **Limitación de Ramificación**: Los pasillos, que lógicamente tienen 4 direcciones, ahora solo habilitan un máximo de **2 salidas** (la entrada utilizada + una salida extra aleatoria).
|
||||
- **Control de Proximidad Táctico**:
|
||||
- Antes de habilitar una salida extra en un pasillo, el sistema verifica que la celda de salida esté a una **distancia mínima de 4 celdas** de cualquier otra habitación (`ROOM` u `OBJECTIVE_ROOM`).
|
||||
- Si una salida potencial está demasiado cerca de una estructura existente, se descarta para evitar colisiones visuales y mecánicas.
|
||||
- **Visualización**: Durante la fase de "Placing Tile", el jugador sigue viendo todas las salidas en azul, pero al confirmar, el sistema activa solo las permitidas.
|
||||
|
||||
### Tareas Pendientes / TODO
|
||||
- **[Avanzado]** Implementar conectividad automática: si una pieza recién colocada queda adyacente a una pared con puerta de otra habitación, permitir la conexión física entre ambas.
|
||||
|
||||
---
|
||||
|
||||
## Sesión 16: Reglas de Exploración y Refinado de Eventos
|
||||
**Fecha:** 11 de Enero de 2026
|
||||
|
||||
BIN
public/assets/images/dungeon1/tiles/room_4x4_clean.png
Normal file
BIN
public/assets/images/dungeon1/tiles/room_4x4_clean.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 MiB |
BIN
public/assets/images/dungeon1/tiles/room_4x4_placeholder.png
Normal file
BIN
public/assets/images/dungeon1/tiles/room_4x4_placeholder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
BIN
public/assets/images/dungeon1/tiles/room_4x4_skull.png
Normal file
BIN
public/assets/images/dungeon1/tiles/room_4x4_skull.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 MiB |
BIN
public/assets/images/dungeon1/tiles/room_4x8_placeholder.png
Normal file
BIN
public/assets/images/dungeon1/tiles/room_4x8_placeholder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.0 MiB |
@@ -9,59 +9,53 @@ export class DungeonDeck {
|
||||
}
|
||||
|
||||
generateMissionDeck(objectiveTileId) {
|
||||
|
||||
this.cards = [];
|
||||
|
||||
// 1. Create a "Pool" of standard dungeon tiles
|
||||
// 1. Create a "Pool" of standard dungeon tiles (18 total)
|
||||
// Rule: 6 Subterranean Rooms, 7 Corridors, 3 T-Junctions, 1 Corner, 1 Steps
|
||||
let pool = [];
|
||||
const composition = [
|
||||
{ id: 'room_dungeon', count: 12 },
|
||||
{ id: 'corridor_straight', count: 8 },
|
||||
{ id: 'corridor_steps', count: 4 },
|
||||
{ id: 'corridor_corner', count: 4 },
|
||||
{ id: 'junction_t', count: 4 }
|
||||
{ id: 'room_subterranean_1', count: 1 },
|
||||
{ id: 'room_subterranean_2', count: 1 },
|
||||
{ id: 'room_subterranean_3', count: 1 },
|
||||
{ id: 'room_subterranean_4', count: 1 },
|
||||
{ id: 'room_subterranean_5', count: 1 },
|
||||
{ id: 'room_subterranean_6', count: 1 },
|
||||
{ id: 'corridor_straight', count: 7 },
|
||||
{ id: 'junction_t', count: 3 },
|
||||
{ id: 'corridor_corner', count: 1 },
|
||||
{ id: 'corridor_steps', count: 1 }
|
||||
];
|
||||
|
||||
composition.forEach(item => {
|
||||
// FIXED: Access by Key string directly
|
||||
const tileDef = TILES[item.id];
|
||||
|
||||
if (tileDef) {
|
||||
for (let i = 0; i < item.count; i++) {
|
||||
pool.push(tileDef);
|
||||
pool.push({ ...tileDef }); // Push a copy
|
||||
}
|
||||
} else {
|
||||
console.error(`❌ Missing Tile Definition for ID: ${item.id}`);
|
||||
}
|
||||
});
|
||||
|
||||
const drawRandom = (source, count) => {
|
||||
const drawn = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
if (source.length === 0) break;
|
||||
const idx = Math.floor(Math.random() * source.length);
|
||||
drawn.push(source[idx]);
|
||||
source.splice(idx, 1);
|
||||
}
|
||||
return drawn;
|
||||
};
|
||||
// Shuffle the initial pool
|
||||
this.shuffleArray(pool);
|
||||
|
||||
// --- Step 1 & 2: Bottom Pool (6 random tiles + Objective) ---
|
||||
const bottomPool = drawRandom(pool, 6);
|
||||
const objectiveDef = TILES[objectiveTileId];
|
||||
if (objectiveDef) {
|
||||
bottomPool.push(objectiveDef);
|
||||
}
|
||||
this.shuffleArray(bottomPool);
|
||||
// --- Step 1: Bottom 6 + Objective (shuffled together) ---
|
||||
const bottomHalf = pool.splice(0, 6);
|
||||
const objectiveDef = TILES[objectiveTileId] || TILES['room_objective_1'];
|
||||
bottomHalf.push({ ...objectiveDef });
|
||||
this.shuffleArray(bottomHalf);
|
||||
|
||||
// --- Step 3: Top Pool (All remaining tiles in the pool) ---
|
||||
const topPool = [...pool]; // pool already has those 6 removed by drawRandom
|
||||
this.shuffleArray(topPool);
|
||||
|
||||
// --- Step 4: Final Stack ---
|
||||
this.cards = [...topPool, ...bottomPool];
|
||||
// --- Step 2: Top 6 ---
|
||||
const topHalf = pool.splice(0, 6);
|
||||
this.shuffleArray(topHalf);
|
||||
|
||||
// --- Step 3: Final Stack (TopHalf on top of BottomHalf) ---
|
||||
// Total 6 + 7 = 13 cards
|
||||
this.cards = [...topHalf, ...bottomHalf];
|
||||
|
||||
console.log(`✅ Dungeon Deck generated with ${this.cards.length} cards. Objective hidden in the bottom 7.`);
|
||||
}
|
||||
|
||||
shuffleArray(array) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
import { DIRECTIONS } from './Constants.js';
|
||||
import { DIRECTIONS, TILE_TYPES } from './Constants.js';
|
||||
import { GridSystem } from './GridSystem.js';
|
||||
import { DungeonDeck } from './DungeonDeck.js';
|
||||
import { TILES } from './TileDefinitions.js';
|
||||
|
||||
const PLACEMENT_STATE = {
|
||||
WAITING_DOOR: 'WAITING_DOOR',
|
||||
@@ -31,7 +31,13 @@ export class DungeonGenerator {
|
||||
}
|
||||
|
||||
startDungeon(missionConfig) {
|
||||
const objectiveId = missionConfig?.type === 'quest' ? 'room_objective' : 'room_dungeon';
|
||||
// Warhammer Quest: Pick one of the 5 Objective Rooms
|
||||
let objectiveId = missionConfig?.objectiveId;
|
||||
if (!objectiveId) {
|
||||
const objIds = ['room_objective_1', 'room_objective_2', 'room_objective_3', 'room_objective_4', 'room_objective_5'];
|
||||
objectiveId = objIds[Math.floor(Math.random() * objIds.length)];
|
||||
}
|
||||
|
||||
this.deck.generateMissionDeck(objectiveId);
|
||||
|
||||
// 1. Draw and place first card automatically at origin
|
||||
@@ -286,11 +292,17 @@ export class DungeonGenerator {
|
||||
|
||||
/**
|
||||
* Update list of exits player can choose from
|
||||
* Rule: Corridors only get ONE extra exit al azar, if it's far from OTHER rooms.
|
||||
*/
|
||||
updateAvailableExits(instance, variant, anchorX, anchorY) {
|
||||
const card = TILES[instance.defId];
|
||||
const isCorridor = card.type === TILE_TYPES.CORRIDOR;
|
||||
|
||||
// Identify the "parent" room we are connecting from to ignore it in distance checks
|
||||
const parentTileId = this.selectedExit ? this.selectedExit.tileId : null;
|
||||
|
||||
// Add new exits from this tile
|
||||
// 1. Identify all potential exits (those not blocked by existing tiles)
|
||||
let potentialExits = [];
|
||||
for (const ex of variant.exits) {
|
||||
const gx = anchorX + ex.x;
|
||||
const gy = anchorY + ex.y;
|
||||
@@ -298,28 +310,86 @@ export class DungeonGenerator {
|
||||
const leadingTo = this.neighbor(gx, gy, ex.direction);
|
||||
const isOccupied = this.grid.isOccupied(leadingTo.x, leadingTo.y);
|
||||
|
||||
|
||||
|
||||
if (!isOccupied) {
|
||||
this.availableExits.push({
|
||||
potentialExits.push({
|
||||
x: gx,
|
||||
y: gy,
|
||||
direction: ex.direction,
|
||||
tileId: instance.id
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 2. If it's a corridor, apply the "Semi-Random" exit rule
|
||||
if (isCorridor && potentialExits.length > 0) {
|
||||
const doorsByDir = {};
|
||||
potentialExits.forEach(e => {
|
||||
if (!doorsByDir[e.direction]) doorsByDir[e.direction] = [];
|
||||
doorsByDir[e.direction].push(e);
|
||||
});
|
||||
|
||||
const dirs = Object.keys(doorsByDir);
|
||||
|
||||
// Remove exits that are now blocked or connected
|
||||
// Filter directions that are at least 4 cells away from any OTHER Room
|
||||
const allowedDirs = dirs.filter(dir => {
|
||||
const group = doorsByDir[dir];
|
||||
// Check proximity for every cell in this door group, IGNORE the room we just came from
|
||||
return group.every(cell => this.isFarFromRooms(cell.x, cell.y, 4, parentTileId));
|
||||
});
|
||||
|
||||
if (allowedDirs.length > 0) {
|
||||
// RULE: If it's the STARTING tile (tile_0), we want 2 exits.
|
||||
// Otherwise, just 1 extra exit.
|
||||
const numToSelect = (instance.id === 'tile_0') ? 2 : 1;
|
||||
|
||||
// Shuffle and pick
|
||||
const shuffledDirs = allowedDirs.sort(() => 0.5 - Math.random());
|
||||
const selectedDirs = shuffledDirs.slice(0, numToSelect);
|
||||
|
||||
selectedDirs.forEach(dir => {
|
||||
this.availableExits.push(...doorsByDir[dir]);
|
||||
});
|
||||
|
||||
console.log(`[DungeonGenerator] Corridor ${instance.id} selected ${selectedDirs.length} exit directions: ${selectedDirs.join(', ')}`);
|
||||
} else {
|
||||
console.log(`[DungeonGenerator] Corridor ${instance.id} became a dead end.`);
|
||||
}
|
||||
} else {
|
||||
// For Rooms and Junctions, we keep all valid exits
|
||||
this.availableExits.push(...potentialExits);
|
||||
}
|
||||
|
||||
// 3. Global cleanup
|
||||
this.availableExits = this.availableExits.filter(exit => {
|
||||
const leadingTo = this.neighbor(exit.x, exit.y, exit.direction);
|
||||
return !this.grid.isOccupied(leadingTo.x, leadingTo.y);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a coordinate is at least 'radius' distance away from any cell of a Room tile.
|
||||
* @param {string} ignoreTileId - Optional ID of a room to ignore (usually the parent room)
|
||||
*/
|
||||
isFarFromRooms(gx, gy, radius, ignoreTileId = null) {
|
||||
for (const tileInstance of this.grid.tiles) {
|
||||
// Skip the room we are expanding from
|
||||
if (ignoreTileId && tileInstance.id === ignoreTileId) continue;
|
||||
|
||||
const card = TILES[tileInstance.defId];
|
||||
if (card.type === TILE_TYPES.ROOM || card.type === TILE_TYPES.OBJECTIVE_ROOM) {
|
||||
const cells = this.grid.tileCells.get(tileInstance.id);
|
||||
if (!cells) continue;
|
||||
|
||||
for (const cellKey of cells) {
|
||||
const [cx, cy] = cellKey.split(',').map(Number);
|
||||
const dist = Math.abs(gx - cx) + Math.abs(gy - cy);
|
||||
if (dist < radius) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current placement preview data for renderer
|
||||
*/
|
||||
|
||||
@@ -295,107 +295,150 @@ export const TILES = {
|
||||
},
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// ROOM DUNGEON
|
||||
// SUBTERRANEAN ROOMS (4x4)
|
||||
// -------------------------------------------------------------------------
|
||||
'room_dungeon': {
|
||||
id: 'room_dungeon',
|
||||
name: 'Dungeon Room',
|
||||
'room_subterranean_1': {
|
||||
id: 'room_subterranean_1',
|
||||
name: 'Circle of Power',
|
||||
type: TILE_TYPES.ROOM,
|
||||
textures: [
|
||||
'/assets/images/dungeon1/tiles/room_4x4_circle.png',
|
||||
'/assets/images/dungeon1/tiles/room_4x4_orange.png',
|
||||
'/assets/images/dungeon1/tiles/room_4x4_squeleton.png'
|
||||
],
|
||||
variants: {
|
||||
[DIRECTIONS.NORTH]: {
|
||||
width: 4, height: 4,
|
||||
layout: [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]],
|
||||
exits: [
|
||||
{ x: 1, y: 0, direction: DIRECTIONS.SOUTH }, { x: 2, y: 0, direction: DIRECTIONS.SOUTH },
|
||||
{ x: 1, y: 3, direction: DIRECTIONS.NORTH }, { x: 2, y: 3, direction: DIRECTIONS.NORTH }
|
||||
]
|
||||
},
|
||||
[DIRECTIONS.EAST]: {
|
||||
width: 4, height: 4,
|
||||
layout: [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]],
|
||||
exits: [
|
||||
{ x: 0, y: 1, direction: DIRECTIONS.WEST }, { x: 0, y: 2, direction: DIRECTIONS.WEST },
|
||||
{ x: 3, y: 1, direction: DIRECTIONS.EAST }, { x: 3, y: 2, direction: DIRECTIONS.EAST }
|
||||
]
|
||||
},
|
||||
[DIRECTIONS.SOUTH]: {
|
||||
width: 4, height: 4,
|
||||
layout: [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]],
|
||||
exits: [
|
||||
{ x: 1, y: 3, direction: DIRECTIONS.NORTH }, { x: 2, y: 3, direction: DIRECTIONS.NORTH },
|
||||
{ x: 1, y: 0, direction: DIRECTIONS.SOUTH }, { x: 2, y: 0, direction: DIRECTIONS.SOUTH }
|
||||
]
|
||||
},
|
||||
[DIRECTIONS.WEST]: {
|
||||
width: 4, height: 4,
|
||||
layout: [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]],
|
||||
exits: [
|
||||
{ x: 3, y: 1, direction: DIRECTIONS.EAST }, { x: 3, y: 2, direction: DIRECTIONS.EAST },
|
||||
{ x: 0, y: 1, direction: DIRECTIONS.WEST }, { x: 0, y: 2, direction: DIRECTIONS.WEST }
|
||||
]
|
||||
}
|
||||
}
|
||||
textures: ['/assets/images/dungeon1/tiles/room_4x4_circle.png'],
|
||||
variants: getStandard4x4Variants()
|
||||
},
|
||||
'room_subterranean_2': {
|
||||
id: 'room_subterranean_2',
|
||||
name: 'Fighting Pit',
|
||||
type: TILE_TYPES.ROOM,
|
||||
textures: ['/assets/images/dungeon1/tiles/room_4x4_orange.png'],
|
||||
variants: getStandard4x4Variants()
|
||||
},
|
||||
'room_subterranean_3': {
|
||||
id: 'room_subterranean_3',
|
||||
name: 'Guard Room',
|
||||
type: TILE_TYPES.ROOM,
|
||||
textures: ['/assets/images/dungeon1/tiles/room_4x4_squeleton.png'],
|
||||
variants: getStandard4x4Variants()
|
||||
},
|
||||
'room_subterranean_4': {
|
||||
id: 'room_subterranean_4',
|
||||
name: 'Laboratory',
|
||||
type: TILE_TYPES.ROOM,
|
||||
textures: ['/assets/images/dungeon1/tiles/room_4x4_skull.png'],
|
||||
variants: getStandard4x4Variants()
|
||||
},
|
||||
'room_subterranean_5': {
|
||||
id: 'room_subterranean_5',
|
||||
name: 'Vault',
|
||||
type: TILE_TYPES.ROOM,
|
||||
textures: ['/assets/images/dungeon1/tiles/room_4x4_clean.png'],
|
||||
variants: getStandard4x4Variants()
|
||||
},
|
||||
'room_subterranean_6': {
|
||||
id: 'room_subterranean_6',
|
||||
name: 'Torture Chamber',
|
||||
type: TILE_TYPES.ROOM,
|
||||
textures: ['/assets/images/dungeon1/tiles/room_4x4_placeholder.png'],
|
||||
variants: getStandard4x4Variants()
|
||||
},
|
||||
|
||||
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// ROOM OBJECTIVE
|
||||
// OBJECTIVE ROOMS (4x8)
|
||||
// -------------------------------------------------------------------------
|
||||
'room_objective': {
|
||||
id: 'room_objective',
|
||||
name: 'Objective Room',
|
||||
'room_objective_1': {
|
||||
id: 'room_objective_1',
|
||||
name: 'Altar of Doom',
|
||||
type: TILE_TYPES.OBJECTIVE_ROOM,
|
||||
textures: [
|
||||
'/assets/images/dungeon1/tiles/room_4x8_altar.png',
|
||||
'/assets/images/dungeon1/tiles/room_4x8_tomb.png'
|
||||
],
|
||||
variants: {
|
||||
[DIRECTIONS.NORTH]: {
|
||||
width: 4, height: 8,
|
||||
layout: [
|
||||
[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1],
|
||||
[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]
|
||||
],
|
||||
exits: [
|
||||
{ x: 1, y: 0, direction: DIRECTIONS.SOUTH }, { x: 2, y: 0, direction: DIRECTIONS.SOUTH }
|
||||
]
|
||||
},
|
||||
[DIRECTIONS.EAST]: {
|
||||
width: 8, height: 4,
|
||||
layout: [
|
||||
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]
|
||||
],
|
||||
exits: [
|
||||
{ x: 0, y: 1, direction: DIRECTIONS.WEST }, { x: 0, y: 2, direction: DIRECTIONS.WEST }
|
||||
]
|
||||
},
|
||||
[DIRECTIONS.SOUTH]: {
|
||||
width: 4, height: 8,
|
||||
layout: [
|
||||
[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1],
|
||||
[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]
|
||||
],
|
||||
exits: [
|
||||
{ x: 1, y: 7, direction: DIRECTIONS.NORTH }, { x: 2, y: 7, direction: DIRECTIONS.NORTH }
|
||||
]
|
||||
},
|
||||
[DIRECTIONS.WEST]: {
|
||||
width: 8, height: 4,
|
||||
layout: [
|
||||
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1],
|
||||
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]
|
||||
],
|
||||
exits: [
|
||||
{ x: 7, y: 1, direction: DIRECTIONS.EAST }, { x: 7, y: 2, direction: DIRECTIONS.EAST }
|
||||
]
|
||||
}
|
||||
}
|
||||
textures: ['/assets/images/dungeon1/tiles/room_4x8_altar.png'],
|
||||
variants: getStandard4x8Variants()
|
||||
},
|
||||
'room_objective_2': {
|
||||
id: 'room_objective_2',
|
||||
name: 'Tomb of the Overlord',
|
||||
type: TILE_TYPES.OBJECTIVE_ROOM,
|
||||
textures: ['/assets/images/dungeon1/tiles/room_4x8_tomb.png'],
|
||||
variants: getStandard4x8Variants()
|
||||
},
|
||||
'room_objective_3': {
|
||||
id: 'room_objective_3',
|
||||
name: 'The Dread Mill',
|
||||
type: TILE_TYPES.OBJECTIVE_ROOM,
|
||||
textures: ['/assets/images/dungeon1/tiles/room_4x8_placeholder.png'],
|
||||
variants: getStandard4x8Variants()
|
||||
},
|
||||
'room_objective_4': {
|
||||
id: 'room_objective_4',
|
||||
name: 'Monster\'s Lair',
|
||||
type: TILE_TYPES.OBJECTIVE_ROOM,
|
||||
textures: ['/assets/images/dungeon1/tiles/room_4x8_placeholder.png'],
|
||||
variants: getStandard4x8Variants()
|
||||
},
|
||||
'room_objective_5': {
|
||||
id: 'room_objective_5',
|
||||
name: 'Fire Chasm',
|
||||
type: TILE_TYPES.OBJECTIVE_ROOM,
|
||||
textures: ['/assets/images/dungeon1/tiles/room_4x8_placeholder.png'],
|
||||
variants: getStandard4x8Variants()
|
||||
}
|
||||
};
|
||||
|
||||
// Helper functions to reduce redundancy
|
||||
function getStandard4x4Variants() {
|
||||
return {
|
||||
[DIRECTIONS.NORTH]: {
|
||||
width: 4, height: 4,
|
||||
layout: [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]],
|
||||
exits: [
|
||||
{ x: 1, y: 0, direction: DIRECTIONS.SOUTH }, { x: 2, y: 0, direction: DIRECTIONS.SOUTH },
|
||||
{ x: 1, y: 3, direction: DIRECTIONS.NORTH }, { x: 2, y: 3, direction: DIRECTIONS.NORTH }
|
||||
]
|
||||
},
|
||||
[DIRECTIONS.EAST]: {
|
||||
width: 4, height: 4,
|
||||
layout: [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]],
|
||||
exits: [
|
||||
{ x: 0, y: 1, direction: DIRECTIONS.WEST }, { x: 0, y: 2, direction: DIRECTIONS.WEST },
|
||||
{ x: 3, y: 1, direction: DIRECTIONS.EAST }, { x: 3, y: 2, direction: DIRECTIONS.EAST }
|
||||
]
|
||||
},
|
||||
[DIRECTIONS.SOUTH]: {
|
||||
width: 4, height: 4,
|
||||
layout: [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]],
|
||||
exits: [
|
||||
{ x: 1, y: 3, direction: DIRECTIONS.NORTH }, { x: 2, y: 3, direction: DIRECTIONS.NORTH },
|
||||
{ x: 1, y: 0, direction: DIRECTIONS.SOUTH }, { x: 2, y: 0, direction: DIRECTIONS.SOUTH }
|
||||
]
|
||||
},
|
||||
[DIRECTIONS.WEST]: {
|
||||
width: 4, height: 4,
|
||||
layout: [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]],
|
||||
exits: [
|
||||
{ x: 3, y: 1, direction: DIRECTIONS.EAST }, { x: 3, y: 2, direction: DIRECTIONS.EAST },
|
||||
{ x: 0, y: 1, direction: DIRECTIONS.WEST }, { x: 0, y: 2, direction: DIRECTIONS.WEST }
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getStandard4x8Variants() {
|
||||
return {
|
||||
[DIRECTIONS.NORTH]: {
|
||||
width: 4, height: 8,
|
||||
layout: Array(8).fill([1, 1, 1, 1]),
|
||||
exits: [{ x: 1, y: 0, direction: DIRECTIONS.SOUTH }, { x: 2, y: 0, direction: DIRECTIONS.SOUTH }]
|
||||
},
|
||||
[DIRECTIONS.EAST]: {
|
||||
width: 8, height: 4,
|
||||
layout: Array(4).fill([1, 1, 1, 1, 1, 1, 1, 1]),
|
||||
exits: [{ x: 0, y: 1, direction: DIRECTIONS.WEST }, { x: 0, y: 2, direction: DIRECTIONS.WEST }]
|
||||
},
|
||||
[DIRECTIONS.SOUTH]: {
|
||||
width: 4, height: 8,
|
||||
layout: Array(8).fill([1, 1, 1, 1]),
|
||||
exits: [{ x: 1, y: 7, direction: DIRECTIONS.NORTH }, { x: 2, y: 7, direction: DIRECTIONS.NORTH }]
|
||||
},
|
||||
[DIRECTIONS.WEST]: {
|
||||
width: 8, height: 4,
|
||||
layout: Array(4).fill([1, 1, 1, 1, 1, 1, 1, 1]),
|
||||
exits: [{ x: 7, y: 1, direction: DIRECTIONS.EAST }, { x: 7, y: 2, direction: DIRECTIONS.EAST }]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ export class EventInterpreter {
|
||||
this.queue = [];
|
||||
this.isProcessing = false;
|
||||
this.currentContext = {}; // Store temporary variables (e.g. "victim")
|
||||
this.lastSelectedTargets = []; // Store the results of the last SELECCION
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -174,6 +175,7 @@ export class EventInterpreter {
|
||||
}
|
||||
|
||||
// Store result
|
||||
this.lastSelectedTargets = targets;
|
||||
if (action.guardar_como) {
|
||||
this.currentContext[action.guardar_como] = targets[0];
|
||||
}
|
||||
@@ -225,6 +227,9 @@ export class EventInterpreter {
|
||||
let targets = [];
|
||||
if (action.objetivo) {
|
||||
if (this.currentContext[action.objetivo]) targets = [this.currentContext[action.objetivo]];
|
||||
} else {
|
||||
// Default to the last selected targets from the previous action
|
||||
targets = this.lastSelectedTargets || [];
|
||||
}
|
||||
|
||||
let amount = 0;
|
||||
|
||||
@@ -892,11 +892,28 @@ export class GameEngine {
|
||||
|
||||
findSpawnPoints(count, tileId = null) {
|
||||
// Collect all currently available cells (occupiedCells maps "x,y" => tileId)
|
||||
// If tileId is provided, filter ONLY cells belonging to that tile.
|
||||
const candidates = [];
|
||||
|
||||
// If no specific tileId is provided (e.g., Power Event),
|
||||
// restrict spawn to tiles currently occupied by heroes.
|
||||
let allowedTileIds = null;
|
||||
if (!tileId && this.heroes.length > 0) {
|
||||
allowedTileIds = new Set();
|
||||
this.heroes.forEach(h => {
|
||||
const hTileId = this.dungeon.grid.occupiedCells.get(`${h.x},${h.y}`);
|
||||
if (hTileId) allowedTileIds.add(hTileId);
|
||||
});
|
||||
console.log("[GameEngine] Power Event Spawn: Restricting to tiles with heroes:", Array.from(allowedTileIds));
|
||||
}
|
||||
|
||||
for (const [key, tid] of this.dungeon.grid.occupiedCells.entries()) {
|
||||
if (tileId && tid !== tileId) continue;
|
||||
// If tileId provided, must match it.
|
||||
// If no tileId provided, must be one of the tiles with heroes.
|
||||
if (tileId) {
|
||||
if (tid !== tileId) continue;
|
||||
} else if (allowedTileIds) {
|
||||
if (!allowedTileIds.has(tid)) continue;
|
||||
}
|
||||
|
||||
const [x, y] = key.split(',').map(Number);
|
||||
|
||||
|
||||
@@ -276,7 +276,7 @@ export class FeedbackUI {
|
||||
showTemporaryMessage(title, message, duration = 2000) {
|
||||
const modal = document.createElement('div');
|
||||
Object.assign(modal.style, {
|
||||
position: 'absolute', top: '25%', left: '50%', transform: 'translate(-50%, -50%)',
|
||||
position: 'absolute', top: '70%', left: '50%', transform: 'translate(-50%, -50%)',
|
||||
backgroundColor: 'rgba(139, 0, 0, 0.9)', color: '#fff', padding: '15px 30px',
|
||||
borderRadius: '8px', border: '2px solid #ff4444', fontFamily: '"Cinzel", serif',
|
||||
fontSize: '20px', textShadow: '2px 2px 4px black', zIndex: '2000', pointerEvents: 'none',
|
||||
|
||||
Reference in New Issue
Block a user