Compare commits
4 Commits
0e5b885236
...
v0.1-engin
| Author | SHA1 | Date | |
|---|---|---|---|
| 57f6312a5a | |||
| 5852a972f4 | |||
| 8025d66fc4 | |||
| ea3813213a |
48
DEVLOG.md
Normal file
48
DEVLOG.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# Devlog del Proyecto: Masmorres (Physical-Web Crawler)
|
||||||
|
|
||||||
|
Este documento sirve para llevar un control diario del desarrollo, decisiones técnicas y nuevas funcionalidades implementadas en el proyecto.
|
||||||
|
|
||||||
|
## [2025-12-23] - Interacción con Puertas y Navegación
|
||||||
|
|
||||||
|
### Funcionalidades Implementadas
|
||||||
|
- **Sistema de Puertas Interactivas:**
|
||||||
|
- Se eliminó la transición automática entre salas al pisar una puerta.
|
||||||
|
- Ahora las puertas actúan como bloqueos físicos hasta que son "abiertas" explícitamente.
|
||||||
|
- Lógica de selección: Click en una puerta cerrada para seleccionarla (feedback visual amarillo).
|
||||||
|
- **Modal de Interacción:**
|
||||||
|
- Al mover una unidad adyacente a una puerta seleccionada, se dispara un modal UI: "¿Quieres abrir la puerta?".
|
||||||
|
- **Confirmar:** La puerta visual se oculta, la sala destino se renderiza (si no lo estaba) y se permite el paso.
|
||||||
|
- **Cancelar:** Se deselecciona la puerta y se mantiene cerrada.
|
||||||
|
|
||||||
|
### Cambios Técnicos
|
||||||
|
- Modificado `main.js` para incluir `checkDoorInteraction` al finalizar el movimiento.
|
||||||
|
- Nuevo estado en `SESSION`: `selectedDoorId`.
|
||||||
|
- Actualización de `isWalkable` para considerar el estado `isOpen` de las puertas.
|
||||||
|
|
||||||
|
## [2025-12-20] - Sistema Visual Dinámico (Dynamic Wall Opacity)
|
||||||
|
|
||||||
|
### Funcionalidades Implementadas
|
||||||
|
- **Opacidad de Muros Contextual:**
|
||||||
|
- Los muros ahora ajustan su opacidad dinámicamente basándose en la rotación de la cámara (N, S, E, W) para evitar obstruir la visión del jugador.
|
||||||
|
- **Regla General:** Los muros "frontales" a la cámara se vuelven semitransparentes (50%), mientras que los "traseros" permanecen opacos.
|
||||||
|
|
||||||
|
### Cambios Técnicos
|
||||||
|
- Implementada función `getWallOpacity(wallSide, viewDirection)`.
|
||||||
|
- Integración en `setCameraView` para refrescar opacidades al girar la vista.
|
||||||
|
- Los muros ahora tienen la propiedad `userData.wallSide` asignada durante la generación.
|
||||||
|
|
||||||
|
## [2025-12-19] - Feedback de Selección y UI
|
||||||
|
|
||||||
|
### Funcionalidades Implementadas
|
||||||
|
- **Resaltado de Selección (Highlighting):**
|
||||||
|
- Unidades y objetos interactivos ahora muestran un aura/color amarillo al ser seleccionados.
|
||||||
|
- Opacidad reducida al 50% para indicar estado de selección activo.
|
||||||
|
- **Mejoras de Animación:**
|
||||||
|
- Refinamiento del "salto" de los standees al moverse entre casillas.
|
||||||
|
|
||||||
|
## [Inicio del Proyecto] - Manifiesto y Core Loop
|
||||||
|
|
||||||
|
### Visión General
|
||||||
|
- Definido el **Manifiesto Técnico (v2.0)**: Visión de un "Puente Híbrido" entre juego de mesa físico y motor narrativo digital (LLM).
|
||||||
|
- **Generación Procedural:** Algoritmo de mazmorras basado en tiles de 4x4 con expansión orgánica.
|
||||||
|
- **Motor Gráfico:** Three.js con cámara isométrica ortográfica y controles restringidos (N, S, E, W).
|
||||||
116
src/main.js
116
src/main.js
@@ -885,6 +885,63 @@ function getDoorGridPosition(room, door) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calcula la posición completa de la puerta en el mundo 3D
|
||||||
|
// Devuelve: { worldPos: {x, z}, meshPos: {x, y, z}, rotation: number, wallOffset: number }
|
||||||
|
function getDoorWorldPosition(room, door, centerX, centerZ, halfSizeX, halfSizeZ) {
|
||||||
|
const doorGridPos = getDoorGridPosition(room, door);
|
||||||
|
const doorWorldPos = gridToWorld(doorGridPos.x, doorGridPos.y);
|
||||||
|
|
||||||
|
const doorHeight = 2.0;
|
||||||
|
let meshPos = { x: 0, y: doorHeight / 2, z: 0 };
|
||||||
|
let rotation = 0;
|
||||||
|
let wallOffset = 0;
|
||||||
|
|
||||||
|
switch (door.side) {
|
||||||
|
case 'N':
|
||||||
|
// Pared Norte: puerta alineada en X, Z en el borde norte
|
||||||
|
meshPos.x = doorWorldPos.x;
|
||||||
|
meshPos.z = centerZ - halfSizeZ;
|
||||||
|
rotation = 0;
|
||||||
|
// Offset relativo al centro de la pared (para el hueco)
|
||||||
|
wallOffset = doorWorldPos.x - centerX;
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
// Pared Sur: puerta alineada en X, Z en el borde sur
|
||||||
|
meshPos.x = doorWorldPos.x;
|
||||||
|
meshPos.z = centerZ + halfSizeZ;
|
||||||
|
rotation = 0;
|
||||||
|
// Para pared Sur, el offset es directo (sin inversión)
|
||||||
|
wallOffset = doorWorldPos.x - centerX;
|
||||||
|
break;
|
||||||
|
case 'E':
|
||||||
|
// Pared Este: puerta alineada en Z, X en el borde este
|
||||||
|
meshPos.x = centerX + halfSizeX;
|
||||||
|
meshPos.z = doorWorldPos.z;
|
||||||
|
rotation = Math.PI / 2;
|
||||||
|
// Offset relativo al centro de la pared
|
||||||
|
// Con rotation=π/2, el eje X local apunta hacia -Z, entonces:
|
||||||
|
// offset_local = -(doorZ - centerZ)
|
||||||
|
wallOffset = -(doorWorldPos.z - centerZ);
|
||||||
|
break;
|
||||||
|
case 'W':
|
||||||
|
// Pared Oeste: puerta alineada en Z, X en el borde oeste
|
||||||
|
meshPos.x = centerX - halfSizeX;
|
||||||
|
meshPos.z = doorWorldPos.z;
|
||||||
|
rotation = Math.PI / 2;
|
||||||
|
// Con rotation=π/2, el eje X local apunta hacia -Z (igual que pared E)
|
||||||
|
// Por tanto, también necesita offset invertido
|
||||||
|
wallOffset = -(doorWorldPos.z - centerZ);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
worldPos: doorWorldPos,
|
||||||
|
meshPos: meshPos,
|
||||||
|
rotation: rotation,
|
||||||
|
wallOffset: wallOffset
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// --- CARGA Y RENDERIZADO ---
|
// --- CARGA Y RENDERIZADO ---
|
||||||
const textureLoader = new THREE.TextureLoader();
|
const textureLoader = new THREE.TextureLoader();
|
||||||
|
|
||||||
@@ -1047,27 +1104,9 @@ async function renderRoom(room) {
|
|||||||
const doorWidth = 1.5;
|
const doorWidth = 1.5;
|
||||||
const doorHeight = 2.0;
|
const doorHeight = 2.0;
|
||||||
|
|
||||||
// Calcular posición relativa de la puerta en la pared
|
// Usar función unificada para obtener la posición de la puerta
|
||||||
// config.width es el ancho total. El rango local es [-W/2, W/2]
|
const doorInfo = getDoorWorldPosition(room, door, centerX, centerZ, halfSizeX, halfSizeZ);
|
||||||
|
const doorOffset = doorInfo.wallOffset;
|
||||||
// Obtener coordenadas de la puerta
|
|
||||||
const doorGridPos = getDoorGridPosition(room, door);
|
|
||||||
const doorWorldPos = gridToWorld(doorGridPos.x, doorGridPos.y);
|
|
||||||
|
|
||||||
// Necesitamos proyectar la posición de la puerta sobre el eje de la pared para saber su offset
|
|
||||||
// Pared N/S: Offset es diferencia en X.
|
|
||||||
// Pared E/W: Offset es diferencia en Z (pero ojo con la dirección del plano).
|
|
||||||
|
|
||||||
let doorOffset = 0; // Offset del centro de la puerta respecto al centro de la pared
|
|
||||||
if (config.side === 'N') {
|
|
||||||
doorOffset = doorWorldPos.x - centerX;
|
|
||||||
} else if (config.side === 'S') {
|
|
||||||
doorOffset = -(doorWorldPos.x - centerX); // S wall is rotated 180, local X is opposite world X
|
|
||||||
} else if (config.side === 'E') {
|
|
||||||
doorOffset = doorWorldPos.z - centerZ;
|
|
||||||
} else if (config.side === 'W') {
|
|
||||||
doorOffset = -(doorWorldPos.z - centerZ); // W wall is rotated 90, local X is opposite world Z
|
|
||||||
}
|
|
||||||
|
|
||||||
const w = config.width;
|
const w = config.width;
|
||||||
|
|
||||||
@@ -1088,10 +1127,6 @@ async function renderRoom(room) {
|
|||||||
createWallSegment(rightWidth, wallHeight, rightCenter, wallHeight / 2, opacity, "RightSeg");
|
createWallSegment(rightWidth, wallHeight, rightCenter, wallHeight / 2, opacity, "RightSeg");
|
||||||
|
|
||||||
// Dintel (Arriba de la puerta)
|
// Dintel (Arriba de la puerta)
|
||||||
// Ancho: doorWidth
|
|
||||||
// Altura: wallHeight - doorHeight (2.5 - 2.0 = 0.5)
|
|
||||||
// Centro X: doorOffset
|
|
||||||
// Centro Y: doorHeight + (dintelHeight / 2) -> 2.0 + 0.25 = 2.25
|
|
||||||
const lintelHeight = wallHeight - doorHeight;
|
const lintelHeight = wallHeight - doorHeight;
|
||||||
if (lintelHeight > 0) {
|
if (lintelHeight > 0) {
|
||||||
createWallSegment(doorWidth, lintelHeight, doorOffset, doorHeight + (lintelHeight / 2), opacity, "Lintel");
|
createWallSegment(doorWidth, lintelHeight, doorOffset, doorHeight + (lintelHeight / 2), opacity, "Lintel");
|
||||||
@@ -1123,31 +1158,11 @@ async function renderRoom(room) {
|
|||||||
const doorMesh = new THREE.Mesh(doorGeometry, doorMaterial);
|
const doorMesh = new THREE.Mesh(doorGeometry, doorMaterial);
|
||||||
doorMesh.userData.id = door.id;
|
doorMesh.userData.id = door.id;
|
||||||
doorMesh.visible = !door.isOpen; // Ocultar si ya está abierta
|
doorMesh.visible = !door.isOpen; // Ocultar si ya está abierta
|
||||||
const doorGridPos = getDoorGridPosition(room, door);
|
|
||||||
const doorWorldPos = gridToWorld(doorGridPos.x, doorGridPos.y);
|
|
||||||
|
|
||||||
switch (door.side) {
|
// Usar función unificada para posicionar la puerta
|
||||||
case 'N':
|
const doorInfo = getDoorWorldPosition(room, door, centerX, centerZ, halfSizeX, halfSizeZ);
|
||||||
// Pared Norte: Alinear X con worldPos, Z con borde norte
|
doorMesh.position.set(doorInfo.meshPos.x, doorInfo.meshPos.y, doorInfo.meshPos.z);
|
||||||
doorMesh.position.set(doorWorldPos.x, doorHeight / 2, centerZ - halfSizeZ);
|
doorMesh.rotation.y = doorInfo.rotation;
|
||||||
doorMesh.rotation.y = 0;
|
|
||||||
break;
|
|
||||||
case 'S':
|
|
||||||
// Pared Sur
|
|
||||||
doorMesh.position.set(doorWorldPos.x, doorHeight / 2, centerZ + halfSizeZ);
|
|
||||||
doorMesh.rotation.y = 0; // O Math.PI, visualmente igual para puerta plana 1.5x2
|
|
||||||
break;
|
|
||||||
case 'E':
|
|
||||||
// Pared Este: Alinear Z con worldPos, X con borde este
|
|
||||||
doorMesh.position.set(centerX + halfSizeX, doorHeight / 2, doorWorldPos.z);
|
|
||||||
doorMesh.rotation.y = Math.PI / 2;
|
|
||||||
break;
|
|
||||||
case 'W':
|
|
||||||
// Pared Oeste
|
|
||||||
doorMesh.position.set(centerX - halfSizeX, doorHeight / 2, doorWorldPos.z);
|
|
||||||
doorMesh.rotation.y = Math.PI / 2;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
scene.add(doorMesh);
|
scene.add(doorMesh);
|
||||||
roomMeshes.doors.push(doorMesh);
|
roomMeshes.doors.push(doorMesh);
|
||||||
@@ -1370,7 +1385,8 @@ window.addEventListener('pointerdown', (event) => {
|
|||||||
const allDoors = [];
|
const allDoors = [];
|
||||||
Object.values(SESSION.roomMeshes).forEach(roomData => {
|
Object.values(SESSION.roomMeshes).forEach(roomData => {
|
||||||
if (roomData.doors) {
|
if (roomData.doors) {
|
||||||
allDoors.push(...roomData.doors);
|
// Solo incluir puertas visibles (cerradas) en el raycast
|
||||||
|
allDoors.push(...roomData.doors.filter(door => door.visible));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user