Implement tile discarding, blocked doors, and correct corridor exits
- Updated TileDefinitions.js: Added 4-way exits to corridor_straight and corridor_steps (N/S y=3,4; E/W x=3,4). - Updated DungeonGenerator.js: Added cancelPlacement() logic and onDoorBlocked callback. - Updated GameRenderer.js: Implemented blockDoor() to visualize blocked passages, and improved isPlayerAdjacentToDoor. - Updated UIManager.js: Added custom showModal/showConfirm and Discard button for tile placement. - Updated main.js: Handled blocked door clicks and hooked up UI events. - Updated GameEngine.js: Improved door adjacency checks. - Updated CameraManager.js: Preserved camera rotation on centerOn. - Added door1_blocked.png asset.
This commit is contained in:
BIN
public/assets/images/dungeon1/doors/door1_blocked.png
Normal file
BIN
public/assets/images/dungeon1/doors/door1_blocked.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 245 KiB |
@@ -27,6 +27,7 @@ export class DungeonGenerator {
|
|||||||
// Callbacks for UI
|
// Callbacks for UI
|
||||||
this.onStateChange = null;
|
this.onStateChange = null;
|
||||||
this.onPlacementUpdate = null;
|
this.onPlacementUpdate = null;
|
||||||
|
this.onDoorBlocked = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
startDungeon(missionConfig) {
|
startDungeon(missionConfig) {
|
||||||
@@ -152,6 +153,30 @@ export class DungeonGenerator {
|
|||||||
return this.grid.canPlace(variant, this.placementX, this.placementY);
|
return this.grid.canPlace(variant, this.placementX, this.placementY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cancelPlacement() {
|
||||||
|
if (this.state !== PLACEMENT_STATE.PLACING_TILE) return;
|
||||||
|
|
||||||
|
// 1. Mark door as blocked visually
|
||||||
|
if (this.onDoorBlocked && this.selectedExit) {
|
||||||
|
this.onDoorBlocked(this.selectedExit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Remove the selected exit from available exits
|
||||||
|
if (this.selectedExit) {
|
||||||
|
this.availableExits = this.availableExits.filter(e =>
|
||||||
|
!(e.x === this.selectedExit.x && e.y === this.selectedExit.y && e.direction === this.selectedExit.direction)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Reset state
|
||||||
|
this.currentCard = null;
|
||||||
|
this.selectedExit = null;
|
||||||
|
this.state = PLACEMENT_STATE.WAITING_DOOR;
|
||||||
|
|
||||||
|
this.notifyPlacementUpdate();
|
||||||
|
this.notifyStateChange();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Confirm and finalize tile placement
|
* Confirm and finalize tile placement
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ export const TILES = {
|
|||||||
layout: [[1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1]],
|
layout: [[1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1]],
|
||||||
exits: [
|
exits: [
|
||||||
{ x: 0, y: 0, direction: DIRECTIONS.SOUTH }, { x: 1, y: 0, direction: DIRECTIONS.SOUTH },
|
{ x: 0, y: 0, direction: DIRECTIONS.SOUTH }, { x: 1, y: 0, direction: DIRECTIONS.SOUTH },
|
||||||
{ x: 0, y: 5, direction: DIRECTIONS.NORTH }, { x: 1, y: 5, direction: DIRECTIONS.NORTH }
|
{ x: 0, y: 5, direction: DIRECTIONS.NORTH }, { x: 1, y: 5, direction: DIRECTIONS.NORTH },
|
||||||
|
{ x: 0, y: 3, direction: DIRECTIONS.WEST }, { x: 0, y: 4, direction: DIRECTIONS.WEST },
|
||||||
|
{ x: 1, y: 3, direction: DIRECTIONS.EAST }, { x: 1, y: 4, direction: DIRECTIONS.EAST }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
[DIRECTIONS.SOUTH]: {
|
[DIRECTIONS.SOUTH]: {
|
||||||
@@ -24,7 +26,9 @@ export const TILES = {
|
|||||||
layout: [[1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1]],
|
layout: [[1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1]],
|
||||||
exits: [
|
exits: [
|
||||||
{ x: 0, y: 0, direction: DIRECTIONS.SOUTH }, { x: 1, y: 0, direction: DIRECTIONS.SOUTH },
|
{ x: 0, y: 0, direction: DIRECTIONS.SOUTH }, { x: 1, y: 0, direction: DIRECTIONS.SOUTH },
|
||||||
{ x: 0, y: 5, direction: DIRECTIONS.NORTH }, { x: 1, y: 5, direction: DIRECTIONS.NORTH }
|
{ x: 0, y: 5, direction: DIRECTIONS.NORTH }, { x: 1, y: 5, direction: DIRECTIONS.NORTH },
|
||||||
|
{ x: 0, y: 3, direction: DIRECTIONS.WEST }, { x: 0, y: 4, direction: DIRECTIONS.WEST },
|
||||||
|
{ x: 1, y: 3, direction: DIRECTIONS.EAST }, { x: 1, y: 4, direction: DIRECTIONS.EAST }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
[DIRECTIONS.EAST]: {
|
[DIRECTIONS.EAST]: {
|
||||||
@@ -32,7 +36,9 @@ export const TILES = {
|
|||||||
layout: [[1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1]],
|
layout: [[1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1]],
|
||||||
exits: [
|
exits: [
|
||||||
{ x: 0, y: 0, direction: DIRECTIONS.WEST }, { x: 0, y: 1, direction: DIRECTIONS.WEST },
|
{ x: 0, y: 0, direction: DIRECTIONS.WEST }, { x: 0, y: 1, direction: DIRECTIONS.WEST },
|
||||||
{ x: 5, y: 0, direction: DIRECTIONS.EAST }, { x: 5, y: 1, direction: DIRECTIONS.EAST }
|
{ x: 5, y: 0, direction: DIRECTIONS.EAST }, { x: 5, y: 1, direction: DIRECTIONS.EAST },
|
||||||
|
{ x: 3, y: 0, direction: DIRECTIONS.SOUTH }, { x: 4, y: 0, direction: DIRECTIONS.SOUTH },
|
||||||
|
{ x: 3, y: 1, direction: DIRECTIONS.NORTH }, { x: 4, y: 1, direction: DIRECTIONS.NORTH }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
[DIRECTIONS.WEST]: {
|
[DIRECTIONS.WEST]: {
|
||||||
@@ -40,7 +46,9 @@ export const TILES = {
|
|||||||
layout: [[1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1]],
|
layout: [[1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1]],
|
||||||
exits: [
|
exits: [
|
||||||
{ x: 0, y: 0, direction: DIRECTIONS.WEST }, { x: 0, y: 1, direction: DIRECTIONS.WEST },
|
{ x: 0, y: 0, direction: DIRECTIONS.WEST }, { x: 0, y: 1, direction: DIRECTIONS.WEST },
|
||||||
{ x: 5, y: 0, direction: DIRECTIONS.EAST }, { x: 5, y: 1, direction: DIRECTIONS.EAST }
|
{ x: 5, y: 0, direction: DIRECTIONS.EAST }, { x: 5, y: 1, direction: DIRECTIONS.EAST },
|
||||||
|
{ x: 3, y: 0, direction: DIRECTIONS.SOUTH }, { x: 4, y: 0, direction: DIRECTIONS.SOUTH },
|
||||||
|
{ x: 3, y: 1, direction: DIRECTIONS.NORTH }, { x: 4, y: 1, direction: DIRECTIONS.NORTH }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,7 +68,9 @@ export const TILES = {
|
|||||||
layout: [[1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1]],
|
layout: [[1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1]],
|
||||||
exits: [
|
exits: [
|
||||||
{ x: 0, y: 0, direction: DIRECTIONS.SOUTH }, { x: 1, y: 0, direction: DIRECTIONS.SOUTH },
|
{ x: 0, y: 0, direction: DIRECTIONS.SOUTH }, { x: 1, y: 0, direction: DIRECTIONS.SOUTH },
|
||||||
{ x: 0, y: 5, direction: DIRECTIONS.NORTH }, { x: 1, y: 5, direction: DIRECTIONS.NORTH }
|
{ x: 0, y: 5, direction: DIRECTIONS.NORTH }, { x: 1, y: 5, direction: DIRECTIONS.NORTH },
|
||||||
|
{ x: 0, y: 3, direction: DIRECTIONS.WEST }, { x: 0, y: 4, direction: DIRECTIONS.WEST },
|
||||||
|
{ x: 1, y: 3, direction: DIRECTIONS.EAST }, { x: 1, y: 4, direction: DIRECTIONS.EAST }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
[DIRECTIONS.SOUTH]: {
|
[DIRECTIONS.SOUTH]: {
|
||||||
@@ -68,7 +78,9 @@ export const TILES = {
|
|||||||
layout: [[1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1]],
|
layout: [[1, 1], [1, 1], [1, 1], [1, 1], [1, 1], [1, 1]],
|
||||||
exits: [
|
exits: [
|
||||||
{ x: 0, y: 0, direction: DIRECTIONS.SOUTH }, { x: 1, y: 0, direction: DIRECTIONS.SOUTH },
|
{ x: 0, y: 0, direction: DIRECTIONS.SOUTH }, { x: 1, y: 0, direction: DIRECTIONS.SOUTH },
|
||||||
{ x: 0, y: 5, direction: DIRECTIONS.NORTH }, { x: 1, y: 5, direction: DIRECTIONS.NORTH }
|
{ x: 0, y: 5, direction: DIRECTIONS.NORTH }, { x: 1, y: 5, direction: DIRECTIONS.NORTH },
|
||||||
|
{ x: 0, y: 3, direction: DIRECTIONS.WEST }, { x: 0, y: 4, direction: DIRECTIONS.WEST },
|
||||||
|
{ x: 1, y: 3, direction: DIRECTIONS.EAST }, { x: 1, y: 4, direction: DIRECTIONS.EAST }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
[DIRECTIONS.EAST]: {
|
[DIRECTIONS.EAST]: {
|
||||||
@@ -76,7 +88,9 @@ export const TILES = {
|
|||||||
layout: [[1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1]],
|
layout: [[1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1]],
|
||||||
exits: [
|
exits: [
|
||||||
{ x: 0, y: 0, direction: DIRECTIONS.WEST }, { x: 0, y: 1, direction: DIRECTIONS.WEST },
|
{ x: 0, y: 0, direction: DIRECTIONS.WEST }, { x: 0, y: 1, direction: DIRECTIONS.WEST },
|
||||||
{ x: 5, y: 0, direction: DIRECTIONS.EAST }, { x: 5, y: 1, direction: DIRECTIONS.EAST }
|
{ x: 5, y: 0, direction: DIRECTIONS.EAST }, { x: 5, y: 1, direction: DIRECTIONS.EAST },
|
||||||
|
{ x: 3, y: 0, direction: DIRECTIONS.SOUTH }, { x: 4, y: 0, direction: DIRECTIONS.SOUTH },
|
||||||
|
{ x: 3, y: 1, direction: DIRECTIONS.NORTH }, { x: 4, y: 1, direction: DIRECTIONS.NORTH }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
[DIRECTIONS.WEST]: {
|
[DIRECTIONS.WEST]: {
|
||||||
@@ -84,7 +98,9 @@ export const TILES = {
|
|||||||
layout: [[1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1]],
|
layout: [[1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1]],
|
||||||
exits: [
|
exits: [
|
||||||
{ x: 0, y: 0, direction: DIRECTIONS.WEST }, { x: 0, y: 1, direction: DIRECTIONS.WEST },
|
{ x: 0, y: 0, direction: DIRECTIONS.WEST }, { x: 0, y: 1, direction: DIRECTIONS.WEST },
|
||||||
{ x: 5, y: 0, direction: DIRECTIONS.EAST }, { x: 5, y: 1, direction: DIRECTIONS.EAST }
|
{ x: 5, y: 0, direction: DIRECTIONS.EAST }, { x: 5, y: 1, direction: DIRECTIONS.EAST },
|
||||||
|
{ x: 3, y: 0, direction: DIRECTIONS.SOUTH }, { x: 4, y: 0, direction: DIRECTIONS.SOUTH },
|
||||||
|
{ x: 3, y: 1, direction: DIRECTIONS.NORTH }, { x: 4, y: 1, direction: DIRECTIONS.NORTH }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,14 +89,23 @@ export class GameEngine {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isPlayerAdjacentToDoor(doorExit) {
|
isPlayerAdjacentToDoor(doorCells) {
|
||||||
if (!this.player) return false;
|
if (!this.player) return false;
|
||||||
|
|
||||||
const dx = Math.abs(this.player.x - doorExit.x);
|
// doorCells should be an array of {x, y} objects
|
||||||
const dy = Math.abs(this.player.y - doorExit.y);
|
// If it sends a single object, wrap it
|
||||||
|
const cells = Array.isArray(doorCells) ? doorCells : [doorCells];
|
||||||
|
|
||||||
// Adjacent means distance of 1 in one direction and 0 in the other
|
for (const cell of cells) {
|
||||||
return (dx === 1 && dy === 0) || (dx === 0 && dy === 1);
|
const dx = Math.abs(this.player.x - cell.x);
|
||||||
|
const dy = Math.abs(this.player.y - cell.y);
|
||||||
|
|
||||||
|
// Adjacent means distance of 1 in one direction and 0 in the other
|
||||||
|
if ((dx === 1 && dy === 0) || (dx === 0 && dy === 1)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
update(time) {
|
update(time) {
|
||||||
|
|||||||
49
src/main.js
49
src/main.js
@@ -83,6 +83,10 @@ generator.onPlacementUpdate = (preview) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
generator.onDoorBlocked = (exitData) => {
|
||||||
|
renderer.blockDoor(exitData);
|
||||||
|
};
|
||||||
|
|
||||||
// 6. Handle Clicks
|
// 6. Handle Clicks
|
||||||
const handleClick = (x, y, doorMesh) => {
|
const handleClick = (x, y, doorMesh) => {
|
||||||
// PRIORITY 1: Tile Placement Mode - ignore all clicks
|
// PRIORITY 1: Tile Placement Mode - ignore all clicks
|
||||||
@@ -92,26 +96,33 @@ const handleClick = (x, y, doorMesh) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PRIORITY 2: Door Click (must be adjacent to player)
|
// PRIORITY 2: Door Click (must be adjacent to player)
|
||||||
if (doorMesh && doorMesh.userData.isDoor && !doorMesh.userData.isOpen) {
|
if (doorMesh && doorMesh.userData.isDoor) {
|
||||||
const doorExit = doorMesh.userData.cells[0];
|
if (doorMesh.userData.isBlocked) {
|
||||||
|
ui.showModal('¡Derrumbe!', 'Esta puerta está bloqueada por un derrumbe. No se puede pasar.');
|
||||||
if (game.isPlayerAdjacentToDoor(doorExit)) {
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Open door visually
|
if (!doorMesh.userData.isOpen) {
|
||||||
renderer.openDoor(doorMesh);
|
const doorExit = doorMesh.userData.cells[0];
|
||||||
|
|
||||||
// Get proper exit data with direction
|
if (game.isPlayerAdjacentToDoor(doorMesh.userData.cells)) {
|
||||||
const exitData = doorMesh.userData.exitData;
|
|
||||||
if (exitData) {
|
|
||||||
generator.selectDoor(exitData);
|
// Open door visually
|
||||||
} else {
|
renderer.openDoor(doorMesh);
|
||||||
console.error('[Main] Door missing exitData');
|
|
||||||
}
|
// Get proper exit data with direction
|
||||||
} else {
|
const exitData = doorMesh.userData.exitData;
|
||||||
|
if (exitData) {
|
||||||
|
generator.selectDoor(exitData);
|
||||||
|
} else {
|
||||||
|
console.error('[Main] Door missing exitData');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Optional: Message if too far?
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PRIORITY 3: Normal cell click (player selection/movement)
|
// PRIORITY 3: Normal cell click (player selection/movement)
|
||||||
|
|||||||
@@ -52,9 +52,14 @@ export class CameraManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
centerOn(x, y) {
|
centerOn(x, y) {
|
||||||
// Grid (x, y) -> World (x, 0, -y)
|
// Calculate current offset relative to OLD target
|
||||||
|
const currentOffset = this.camera.position.clone().sub(this.target);
|
||||||
|
|
||||||
|
// Update target: Grid (x, y) -> World (x, 0, -y)
|
||||||
this.target.set(x, 0, -y);
|
this.target.set(x, 0, -y);
|
||||||
this.camera.position.copy(this.target).add(this.isoOffset);
|
|
||||||
|
// Restore position with new target + same relative offset
|
||||||
|
this.camera.position.copy(this.target).add(currentOffset);
|
||||||
this.camera.lookAt(this.target);
|
this.camera.lookAt(this.target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -601,6 +601,36 @@ export class GameRenderer {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blockDoor(exitData) {
|
||||||
|
if (!this.exitGroup || !exitData) return;
|
||||||
|
|
||||||
|
// Find the door mesh
|
||||||
|
let targetDoor = null;
|
||||||
|
|
||||||
|
for (const child of this.exitGroup.children) {
|
||||||
|
if (child.userData.isDoor) {
|
||||||
|
// Check if this door corresponds to the exitData
|
||||||
|
// exitData has x,y of one of the cells
|
||||||
|
for (const cell of child.userData.cells) {
|
||||||
|
if (cell.x === exitData.x && cell.y === exitData.y) {
|
||||||
|
targetDoor = child;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (targetDoor) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetDoor) {
|
||||||
|
this.getTexture('/assets/images/dungeon1/doors/door1_blocked.png', (texture) => {
|
||||||
|
targetDoor.material.map = texture;
|
||||||
|
targetDoor.material.needsUpdate = true;
|
||||||
|
targetDoor.userData.isBlocked = true;
|
||||||
|
targetDoor.userData.isOpen = false; // Ensure strictly not open
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ========== MANUAL PLACEMENT SYSTEM ==========
|
// ========== MANUAL PLACEMENT SYSTEM ==========
|
||||||
|
|
||||||
enableDoorSelection(enabled) {
|
enableDoorSelection(enabled) {
|
||||||
@@ -727,12 +757,31 @@ export class GameRenderer {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. GROUND PROJECTION (Green/Red)
|
// 2. GROUND PROJECTION (Green/Red/Blue)
|
||||||
const projectionColor = isValid ? 0x00ff00 : 0xff0000;
|
const baseColor = isValid ? 0x00ff00 : 0xff0000;
|
||||||
|
|
||||||
|
// Calculate global exit positions
|
||||||
|
const exitKeys = new Set();
|
||||||
|
if (preview.variant && preview.variant.exits) {
|
||||||
|
preview.variant.exits.forEach(ex => {
|
||||||
|
const gx = x + ex.x;
|
||||||
|
const gy = y + ex.y;
|
||||||
|
exitKeys.add(`${gx},${gy}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
cells.forEach(cell => {
|
cells.forEach(cell => {
|
||||||
|
const key = `${cell.x},${cell.y}`;
|
||||||
|
let color = baseColor;
|
||||||
|
|
||||||
|
// If this cell is an exit, color it Blue
|
||||||
|
if (exitKeys.has(key)) {
|
||||||
|
color = 0x0000ff; // Blue
|
||||||
|
}
|
||||||
|
|
||||||
const geometry = new THREE.PlaneGeometry(0.95, 0.95);
|
const geometry = new THREE.PlaneGeometry(0.95, 0.95);
|
||||||
const material = new THREE.MeshBasicMaterial({
|
const material = new THREE.MeshBasicMaterial({
|
||||||
color: projectionColor,
|
color: color,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
opacity: 0.5,
|
opacity: 0.5,
|
||||||
side: THREE.DoubleSide
|
side: THREE.DoubleSide
|
||||||
|
|||||||
@@ -224,7 +224,6 @@ export class UIManager {
|
|||||||
};
|
};
|
||||||
placementControls.appendChild(this.rotateBtn);
|
placementControls.appendChild(this.rotateBtn);
|
||||||
|
|
||||||
// Place button
|
|
||||||
this.placeBtn = document.createElement('button');
|
this.placeBtn = document.createElement('button');
|
||||||
this.placeBtn.textContent = '⬇ Bajar';
|
this.placeBtn.textContent = '⬇ Bajar';
|
||||||
this.placeBtn.style.padding = '10px 20px';
|
this.placeBtn.style.padding = '10px 20px';
|
||||||
@@ -243,6 +242,29 @@ export class UIManager {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
placementControls.appendChild(this.placeBtn);
|
placementControls.appendChild(this.placeBtn);
|
||||||
|
|
||||||
|
// Discard button
|
||||||
|
this.discardBtn = document.createElement('button');
|
||||||
|
this.discardBtn.textContent = '❌ Cancelar';
|
||||||
|
this.discardBtn.style.padding = '10px 20px';
|
||||||
|
this.discardBtn.style.backgroundColor = '#d33';
|
||||||
|
this.discardBtn.style.color = '#fff';
|
||||||
|
this.discardBtn.style.border = '1px solid #888';
|
||||||
|
this.discardBtn.style.cursor = 'pointer';
|
||||||
|
this.discardBtn.style.fontSize = '16px';
|
||||||
|
this.discardBtn.style.borderRadius = '4px';
|
||||||
|
this.discardBtn.onclick = () => {
|
||||||
|
if (this.dungeon) {
|
||||||
|
this.showConfirm(
|
||||||
|
'Confirmar acción',
|
||||||
|
'¿Quieres descartar esta loseta y bloquear la puerta?',
|
||||||
|
() => {
|
||||||
|
this.dungeon.cancelPlacement();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
placementControls.appendChild(this.discardBtn);
|
||||||
}
|
}
|
||||||
|
|
||||||
showPlacementControls(show) {
|
showPlacementControls(show) {
|
||||||
@@ -351,4 +373,141 @@ export class UIManager {
|
|||||||
ctx.lineTo(centerX, centerY + 5);
|
ctx.lineTo(centerX, centerY + 5);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
}
|
}
|
||||||
|
showModal(title, message) {
|
||||||
|
// Overlay
|
||||||
|
const overlay = document.createElement('div');
|
||||||
|
overlay.style.position = 'absolute';
|
||||||
|
overlay.style.top = '0';
|
||||||
|
overlay.style.left = '0';
|
||||||
|
overlay.style.width = '100%';
|
||||||
|
overlay.style.height = '100%';
|
||||||
|
overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
|
||||||
|
overlay.style.display = 'flex';
|
||||||
|
overlay.style.justifyContent = 'center';
|
||||||
|
overlay.style.alignItems = 'center';
|
||||||
|
overlay.style.pointerEvents = 'auto'; // Block clicks behind
|
||||||
|
overlay.style.zIndex = '1000';
|
||||||
|
|
||||||
|
// Content Box
|
||||||
|
const content = document.createElement('div');
|
||||||
|
content.style.backgroundColor = '#222';
|
||||||
|
content.style.border = '2px solid #888';
|
||||||
|
content.style.borderRadius = '8px';
|
||||||
|
content.style.padding = '20px';
|
||||||
|
content.style.width = '300px';
|
||||||
|
content.style.textAlign = 'center';
|
||||||
|
content.style.color = '#fff';
|
||||||
|
content.style.fontFamily = 'sans-serif';
|
||||||
|
|
||||||
|
// Title
|
||||||
|
const titleEl = document.createElement('h2');
|
||||||
|
titleEl.textContent = title;
|
||||||
|
titleEl.style.marginTop = '0';
|
||||||
|
titleEl.style.color = '#f44'; // Reddish for importance
|
||||||
|
content.appendChild(titleEl);
|
||||||
|
|
||||||
|
// Message
|
||||||
|
const msgEl = document.createElement('p');
|
||||||
|
msgEl.textContent = message;
|
||||||
|
msgEl.style.fontSize = '16px';
|
||||||
|
msgEl.style.lineHeight = '1.5';
|
||||||
|
content.appendChild(msgEl);
|
||||||
|
|
||||||
|
// OK Button
|
||||||
|
const btn = document.createElement('button');
|
||||||
|
btn.textContent = 'Entendido';
|
||||||
|
btn.style.marginTop = '20px';
|
||||||
|
btn.style.padding = '10px 20px';
|
||||||
|
btn.style.fontSize = '16px';
|
||||||
|
btn.style.cursor = 'pointer';
|
||||||
|
btn.style.backgroundColor = '#444';
|
||||||
|
btn.style.color = '#fff';
|
||||||
|
btn.style.border = '1px solid #888';
|
||||||
|
btn.onclick = () => {
|
||||||
|
this.container.removeChild(overlay);
|
||||||
|
};
|
||||||
|
content.appendChild(btn);
|
||||||
|
|
||||||
|
overlay.appendChild(content);
|
||||||
|
this.container.appendChild(overlay);
|
||||||
|
}
|
||||||
|
showConfirm(title, message, onConfirm) {
|
||||||
|
// Overlay
|
||||||
|
const overlay = document.createElement('div');
|
||||||
|
overlay.style.position = 'absolute';
|
||||||
|
overlay.style.top = '0';
|
||||||
|
overlay.style.left = '0';
|
||||||
|
overlay.style.width = '100%';
|
||||||
|
overlay.style.height = '100%';
|
||||||
|
overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
|
||||||
|
overlay.style.display = 'flex';
|
||||||
|
overlay.style.justifyContent = 'center';
|
||||||
|
overlay.style.alignItems = 'center';
|
||||||
|
overlay.style.pointerEvents = 'auto'; // Block clicks behind
|
||||||
|
overlay.style.zIndex = '1000';
|
||||||
|
|
||||||
|
// Content Box
|
||||||
|
const content = document.createElement('div');
|
||||||
|
content.style.backgroundColor = '#222';
|
||||||
|
content.style.border = '2px solid #888';
|
||||||
|
content.style.borderRadius = '8px';
|
||||||
|
content.style.padding = '20px';
|
||||||
|
content.style.width = '300px';
|
||||||
|
content.style.textAlign = 'center';
|
||||||
|
content.style.color = '#fff';
|
||||||
|
content.style.fontFamily = 'sans-serif';
|
||||||
|
|
||||||
|
// Title
|
||||||
|
const titleEl = document.createElement('h2');
|
||||||
|
titleEl.textContent = title;
|
||||||
|
titleEl.style.marginTop = '0';
|
||||||
|
titleEl.style.color = '#f44';
|
||||||
|
content.appendChild(titleEl);
|
||||||
|
|
||||||
|
// Message
|
||||||
|
const msgEl = document.createElement('p');
|
||||||
|
msgEl.textContent = message;
|
||||||
|
msgEl.style.fontSize = '16px';
|
||||||
|
msgEl.style.lineHeight = '1.5';
|
||||||
|
content.appendChild(msgEl);
|
||||||
|
|
||||||
|
// Buttons Container
|
||||||
|
const buttons = document.createElement('div');
|
||||||
|
buttons.style.display = 'flex';
|
||||||
|
buttons.style.justifyContent = 'space-around';
|
||||||
|
buttons.style.marginTop = '20px';
|
||||||
|
|
||||||
|
// Cancel Button
|
||||||
|
const cancelBtn = document.createElement('button');
|
||||||
|
cancelBtn.textContent = 'Cancelar';
|
||||||
|
cancelBtn.style.padding = '10px 20px';
|
||||||
|
cancelBtn.style.fontSize = '16px';
|
||||||
|
cancelBtn.style.cursor = 'pointer';
|
||||||
|
cancelBtn.style.backgroundColor = '#555';
|
||||||
|
cancelBtn.style.color = '#fff';
|
||||||
|
cancelBtn.style.border = '1px solid #888';
|
||||||
|
cancelBtn.onclick = () => {
|
||||||
|
this.container.removeChild(overlay);
|
||||||
|
};
|
||||||
|
buttons.appendChild(cancelBtn);
|
||||||
|
|
||||||
|
// Confirm Button
|
||||||
|
const confirmBtn = document.createElement('button');
|
||||||
|
confirmBtn.textContent = 'Aceptar';
|
||||||
|
confirmBtn.style.padding = '10px 20px';
|
||||||
|
confirmBtn.style.fontSize = '16px';
|
||||||
|
confirmBtn.style.cursor = 'pointer';
|
||||||
|
confirmBtn.style.backgroundColor = '#2a5';
|
||||||
|
confirmBtn.style.color = '#fff';
|
||||||
|
confirmBtn.style.border = '1px solid #888';
|
||||||
|
confirmBtn.onclick = () => {
|
||||||
|
if (onConfirm) onConfirm();
|
||||||
|
this.container.removeChild(overlay);
|
||||||
|
};
|
||||||
|
buttons.appendChild(confirmBtn);
|
||||||
|
|
||||||
|
content.appendChild(buttons);
|
||||||
|
overlay.appendChild(content);
|
||||||
|
this.container.appendChild(overlay);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user