9 Commits

Author SHA1 Message Date
57f6312a5a Fix: Allow clicking through open doors
- Filter invisible doors from raycast intersection tests
- Open doors no longer block mouse clicks on tiles behind them
- Players can now select and move to tiles visible through open doorways
- Fixes issue where opened doors acted as invisible collision barriers for UI interaction
2025-12-28 20:18:17 +01:00
5852a972f4 Fix: Correct door alignment for East and West walls
- Fixed wallOffset calculation for E/W walls
- Both E and W walls need inverted offset due to rotation=π/2
- Local X axis points to -Z for both walls when rotated 90°
- Door gaps and door meshes now perfectly aligned on all walls (N/S/E/W)
- Resolves misalignment issue where E/W doors didn't match their wall gaps
2025-12-28 20:12:32 +01:00
8025d66fc4 Refactor: Unify door positioning logic
- Created getDoorWorldPosition() function that calculates both mesh position and wall offset
- Eliminated duplicate positioning logic between wall gaps and door meshes
- Removed inconsistent sign inversions that caused misalignment
- Both wall gaps and door meshes now use the same coordinate system
- Fixes issue where doors and gaps were positioned differently on S and W walls
2025-12-28 20:02:13 +01:00
ea3813213a Pre-refactor: Door alignment fix - safety checkpoint 2025-12-28 20:00:33 +01:00
0e5b885236 Feat: Interactive doors with physical wall cutouts
- Implemented door selection and interaction model (walk-to + click).
- Added modal for opening doors.
- Refactored wall rendering to create physical holes (CSG-like wall segments).
- Aligned door meshes to perfectly fit wall cutouts.
- Managed door visibility states to prevent Z-fighting on open doors.
2025-12-23 13:50:05 +01:00
3c599093cf Fix: Texture stretching in large rooms
- Used texture cloning for floor tiles to ensure independent repeat settings for each room.
- Calculated texture repetition based on room dimensions relative to the base 4x4 tile size, preventing distortion in non-square rooms.
2025-12-23 13:13:37 +01:00
12fb18b1de Fix: Remove camera view corruption
- Removed line in animateMovement that mutated CAMERA_VIEWS static constants.
- This prevents the camera offset from skewing as the player moves away from the origin, ensuring the isometric view angle remains consistent throughout the entire dungeon.
2025-12-23 13:07:32 +01:00
e47b2eeba0 Fix: Deterministic camera transitions
- Refactored setCameraView to use precise lookAt and position calculation instead of accumulating quaternion errors.
- Forces camera UP vector (0,1,0) to prevent roll drift during isometric rotation.
2025-12-23 12:56:09 +01:00
21e85915e9 Feat: Weighted dungeon generation, Minimap, and robust movement logic
- Implemented weighted room generation with size limits.
- Added HUD with Minimap (God Mode view).
- Fixed texture stretching and wall rendering for variable room sizes.
- Implemented 'detectRoomChange' for robust entity room transition.
2025-12-23 12:53:09 +01:00
6 changed files with 1033 additions and 186 deletions

48
DEVLOG.md Normal file
View 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).

View File

@@ -1,19 +1,36 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Masmorres Isometric View</title>
</head>
<body>
<div id="app"></div>
<div id="compass">
<div id="compass-n" class="compass-btn active" data-direction="N">N</div>
<div id="compass-s" class="compass-btn" data-direction="S">S</div>
<div id="compass-e" class="compass-btn" data-direction="E">E</div>
<div id="compass-w" class="compass-btn" data-direction="W">W</div>
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Masmorres Isometric View</title>
</head>
<body>
<div id="app"></div>
<div id="hud">
<div id="minimap-container">
<canvas id="minimap"></canvas>
</div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
<div id="compass">
<div class="compass-btn" data-dir="N">N</div>
<div class="compass-row">
<div class="compass-btn" data-dir="W">W</div>
<div class="compass-btn" data-dir="E">E</div>
</div>
<div class="compass-btn" data-dir="S">S</div>
</div>
<div id="door-modal" class="hidden">
<div class="modal-content">
<p>¿Quieres abrir la puerta?</p>
<button id="btn-open-yes"></button>
<button id="btn-open-no">No</button>
</div>
</div>
</div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

View File

@@ -0,0 +1,58 @@
# Manifiesto Técnico: Proyecto "Physical-Web Crawler" (v2.0)
## 1. Visión del Sistema: El Puente Híbrido
El objetivo es construir un ecosistema de juego donde el software no sea un simple árbitro de reglas, sino un **Director de Juego (DM) proactivo**. El sistema debe coordinar tres realidades:
1. **Plano Físico:** El tablero táctil, piezas impresas y la disposición espacial real del jugador.
2. **Plano Narrativo (LLM):** Un motor de inteligencia artificial que genera tramas, diálogos y consecuencias basadas en la agencia del jugador.
3. **Plano de Control (Web/Mobile):** La interfaz técnica que traduce las acciones físicas en datos y las respuestas de la IA en instrucciones visuales y mecánicas.
## 2. Motor de Narrativa Emergente (AI-DM)
A diferencia de los juegos de mazmorreo tradicionales con eventos pre-escritos, este sistema integra una **API de inferencia LLM (Self-hosted)** para gestionar la no-linealidad.
### 2.1. Procesamiento de Intenciones
El jugador no se limita a opciones predefinidas (A, B o C). A través de la interfaz móvil, puede proponer acciones creativas. El sistema procesará estas entradas mediante:
* **Prompt Engineering Dinámico:** Se enviará al LLM el estado actual de la mazmorra, la salud del grupo y el inventario, junto con la acción propuesta.
* **Generación de Consecuencias:** La IA determinará el éxito o fracaso narrativo, instruyendo al Host para alterar el entorno (ej: "La puerta se bloquea, debes buscar otra salida" o "El enemigo decide parlamentar").
### 2.2. Arquitectura de IA Económica (Self-Hosted)
Para garantizar la viabilidad del prototipo y la privacidad de los datos, se optará por soluciones de código abierto:
* **Motor:** Inferencia mediante *Ollama* o *LocalAI* ejecutando modelos como Llama 3 o Mistral (quantized para latencia mínima).
* **Context Management:** Uso de una base de datos vectorial (RAG) ligera para mantener la memoria a largo plazo de la campaña sin saturar la ventana de contexto del modelo.
## 3. Generación de Espacio Físico No Lineal
La mazmorra no es un mapa estático, sino un organismo que crece según las decisiones de los jugadores.
* **Geometría Reactiva:** Si los jugadores deciden retroceder o buscar una ruta alternativa no prevista, el motor de generación de losetas recalcula las probabilidades de conexión basándose en la "intención narrativa" dictada por la IA.
* **Mapeado de Colisión Espacial:** El sistema mantiene un gemelo digital de la mesa física. Antes de proponer la colocación de una loseta física (puerta, pasillo, sala), el algoritmo de validación asegura que el espacio físico virtualizado no esté ocupado, garantizando que la expansión sea físicamente posible en la mesa real.
## 4. Multimedia y Carga Atmosférica
El Host (PC/Tablet) actúa como el terminal audiovisual de la IA.
* **Narrativa Multimodal:** La IA genera descripciones que se transforman en voz (TTS) y disparan activos visuales (vídeo/imagen) coherentes con el bioma actual de la mazmorra.
* **Dinámica Ambiental:** El audio ambiente y la iluminación de la interfaz mutan en tiempo real según el nivel de peligro o la tensión narrativa detectada por el LLM.
## 5. El Rol del Jugador: Agencia Total
El manifiesto establece que el jugador es el motor de la partida:
1. **Decisión:** El jugador propone una acción (vía voz o texto en el móvil).
2. **Interpretación:** La IA valida la acción contra las estadísticas del personaje y el contexto de la sala.
3. **Ejecución:** El sistema instruye al jugador sobre qué cambios debe realizar en el tablero físico (colocar nuevas losetas, retirar enemigos, mover atrezzo).
## 6. Escalabilidad Multijugador
El sistema debe soportar sesiones síncronas donde:
* Cada móvil es una extensión de la voluntad del jugador.
* El Host centraliza la visión colectiva y la interacción de la IA con el grupo, permitiendo debates entre jugadores que la IA puede "escuchar" e interpretar para ajustar la dificultad o la trama.
---

File diff suppressed because it is too large Load Diff

View File

@@ -25,9 +25,47 @@ canvas {
display: block;
}
/* HUD Wrapper */
#hud {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
/* Dejar pasar clics al juego 3D */
z-index: 999;
}
/* UI Elements inside HUD (reactivate pointer events) */
#hud>* {
pointer-events: auto;
}
/* Minimap */
#minimap-container {
position: absolute;
top: 20px;
left: 20px;
width: 200px;
height: 200px;
background: rgba(0, 0, 0, 0.7);
border: 2px solid #444;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
#minimap {
width: 100%;
height: 100%;
}
/* Compass UI */
#compass {
position: fixed;
position: absolute;
top: 20px;
right: 20px;
width: 100px;
@@ -36,7 +74,6 @@ canvas {
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
gap: 2px;
z-index: 1000;
}
.compass-btn {
@@ -84,4 +121,66 @@ canvas {
#compass-w {
grid-column: 1;
grid-row: 2;
}
}
/* Modal Styles */
#door-modal {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 2000;
pointer-events: auto;
}
#door-modal.hidden {
display: none;
}
.modal-content {
background: #2a2a2a;
padding: 20px;
border: 2px solid #555;
border-radius: 8px;
text-align: center;
color: #fff;
box-shadow: 0 4px 15px rgba(0,0,0,0.5);
}
.modal-content p {
margin-bottom: 20px;
font-size: 1.2rem;
}
.modal-content button {
padding: 8px 20px;
margin: 0 10px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
font-size: 1rem;
}
#btn-open-yes {
background: #4CAF50;
color: white;
}
#btn-open-yes:hover {
background: #45a049;
}
#btn-open-no {
background: #f44336;
color: white;
}
#btn-open-no:hover {
background: #d32f2f;
}

124
src/style.css.bak Normal file
View File

@@ -0,0 +1,124 @@
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
background-color: #242424;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
overflow: hidden;
/* Evitar scrollbars por el canvas */
}
#app {
width: 100%;
height: 100vh;
}
canvas {
display: block;
}
/* HUD Wrapper */
#hud {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
/* Dejar pasar clics al juego 3D */
z-index: 999;
}
/* UI Elements inside HUD (reactivate pointer events) */
#hud>* {
pointer-events: auto;
}
/* Minimap */
#minimap-container {
position: absolute;
top: 20px;
left: 20px;
width: 200px;
height: 200px;
background: rgba(0, 0, 0, 0.7);
border: 2px solid #444;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
#minimap {
width: 100%;
height: 100%;
}
/* Compass UI */
#compass {
position: absolute;
top: 20px;
right: 20px;
width: 100px;
height: 100px;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
gap: 2px;
}
.compass-btn {
background: rgba(50, 50, 50, 0.8);
border: 2px solid rgba(255, 255, 255, 0.3);
color: rgba(255, 255, 255, 0.6);
font-size: 18px;
font-weight: bold;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
user-select: none;
}
.compass-btn:hover {
background: rgba(70, 70, 70, 0.9);
border-color: rgba(255, 255, 255, 0.5);
color: rgba(255, 255, 255, 0.8);
}
.compass-btn.active {
background: rgba(255, 200, 0, 0.9);
border-color: rgba(255, 220, 0, 1);
color: rgba(0, 0, 0, 1);
box-shadow: 0 0 15px rgba(255, 200, 0, 0.6);
}
#compass-n {
grid-column: 2;
grid-row: 1;
}
#compass-s {
grid-column: 2;
grid-row: 3;
}
#compass-e {
grid-column: 3;
grid-row: 2;
}
#compass-w {
grid-column: 1;
grid-row: 2;
}