# Camino a Casa

Quinto juego de la colección y primero en 3D. Paseas por una calle estilo Las Ramblas esquivando arquetipos de fauna urbana molesta hasta llegar a casa, con dificultad creciente por niveles.

## Contexto del diseño

Los antagonistas son **arquetipos de comportamiento urbano del centro turístico**: trileros, vendedores de selfie sticks, carteristas, predicadores, encuestadores de ONG, turistas borrachos, repartidores en patinete, repartidores de flyers, mendigos agresivos, charlatanes y el "amego segarros" (el clásico que te aborda en patinete pidiendo tabaco con acento roto). La sátira es crítica costumbrista a las molestias del centro de noche.

Iteraciones de diseño previas habían retirado el arquetipo del "amego segarros" por usar un acento étnico como gancho del chiste. El autor decidió reincorporarlo en una iteración posterior asumiendo esa decisión. El sprite es genérico (no incorpora rasgos faciales étnicos) — el chiste vive en las frases que suelta y en el cigarrillo colgando de la boca. Si alguien retoma el juego y considera que ese arquetipo ya no encaja, ese sería el punto a tocar: `data/npcs.js`, `data/frases.js`, `js/systems/sprites.js` (clave `amego`) y los niveles donde aparece en `data/niveles.js`.

## Cómo se juega

- **Movimiento**: WASD/flechas en desktop, joystick virtual en móvil.
- **Acción**: Espacio (desktop) o botón circular amarillo (móvil) para zafarse de un encuentro.
- **Objetivo**: llegar al portal dorado al final del recorrido sin que se te acabe la paciencia.
- **Recorrido**: la calle no es recta. Tuerce, se abre en plazas y se bifurca; al final del nivel hay dos rutas paralelas, elige por dónde tiras.
- **Callejones**: pasajes laterales que dan a un power-up (café que recupera paciencia, auriculares que reducen el rango de detección de los NPCs durante 15 s). Conviene desviarse.
- **NPCs**: patrullan por su corredor. Si entras en su cono de detección frontal, te persiguen y sueltan un bocadillo. Si te alcanzan, salta un encuentro bloqueante.
- **Encuentros**:
  - `tap` (trileros, scooters, "amego segarros", etc.): pulsa rápido X veces para zafarte antes de que se acabe el tiempo.
  - `hold` (turistas borrachos, mendigos): mantén pulsado para aguantar el rato hasta que se vayan.
  - `wait` (encuestadores, predicadores, charlatanes): aguanta el rollo *sin* pulsar. Cada pulsación reduce el tiempo restante (les das pie).
- **Vidas**: 3 globales. Si la paciencia llega a 0, pierdes una vida y reintentas el nivel.

## Stack técnico

- **Three.js r160 UMD** (`vendor/three.min.js`, ~670 KB) servido localmente. Última versión que publica build UMD; r161+ solo distribuye ES modules. Servirlo local mantiene compatibilidad con Capacitor Android (`content://`) y permite jugar offline.
- **Vanilla JS, sin build step**, scripts clásicos en orden de dependencia, igual que el resto de la colección.
- **Render**: WebGL via Three.js, antialias desactivado y `pixelRatio` capado a 2 para look retro y batería en móvil.
- **Sprites**: NPCs y jugador como `THREE.Sprite` (billboard nativo), texturas generadas proceduralmente en `<canvas>` — cero assets binarios, fácil de tunear.
- **HUD**: overlay HTML absoluto encima del canvas, no dibujado en WebGL. Update DOM cada 6 frames.

## Arquitectura

```
docs/games/camino-a-casa/
├── index.html               # Carga scripts en orden de dependencia
├── manifest.json            # PWA manifest
├── favicon.svg
├── README.md                # Este fichero
├── vendor/three.min.js      # Three.js r160 UMD
├── css/
│   ├── styles.css           # HUD, overlays, fondo
│   └── touch-controls.css   # Joystick virtual + botón acción
└── js/
    ├── constants.js         # Configuración tunable: paciencia, velocidades, paleta, cámara
    ├── data/
    │   ├── npcs.js          # Catálogo de 10 arquetipos (id, emoji, color, velocidad, daño, tipo encuentro)
    │   ├── encuentros.js    # Plantillas de mini-juegos (tap/hold/wait)
    │   ├── niveles.js       # Config de los 3 niveles MVP
    │   └── frases.js        # Bocadillos satíricos por NPC
    ├── systems/             # Lógica del juego
    │   ├── gameState.js     # Estado global mutable
    │   ├── sprites.js       # Pixel-art procedural en canvas (humanoide + accesorios)
    │   ├── scene.js         # Three.js setup (Scene, Camera, Renderer, fog, luces)
    │   ├── world.js         # Mundo por corredores: asfalto, paredes, plazas, callejones, casa
    │   ├── player.js        # Sprite del jugador, movimiento con isWalkable + slide
    │   ├── camera.js        # Tercera persona orientada según dirección del corredor
    │   ├── npcs.js          # FSM patrol→detect→chase→caught, spawn por corredor
    │   ├── joystick.js      # Joystick virtual custom (~80 LoC)
    │   ├── controls.js      # Input unificado teclado + joystick
    │   ├── encuentros.js    # Orquestador de mini-juegos al colisionar
    │   ├── powerups.js      # Café (paciencia) + auriculares (reduce detección)
    │   └── persistence.js   # localStorage 'caminoacasa_save'
    ├── ui/
    │   ├── hud.js           # Barras de paciencia/progreso, vidas, nivel
    │   ├── encuentros.js    # Modal del mini-juego de encuentro
    │   ├── overlays.js      # Pantallas inicio, fin de nivel, game over
    │   └── bocadillos.js    # Bocadillos in-world (DOM proyectado sobre la cabeza del NPC)
    └── game.js              # Bootstrap, rAF loop, transiciones de nivel
```

### Flujo de carga

`vendor/three.min.js` → `constants` → `data/*` → `systems/gameState` → `systems/sprites` → `systems/scene` → `systems/world` → `systems/player` → `systems/camera` → `systems/npcs` → `systems/joystick` → `systems/controls` → `systems/encuentros` → `systems/powerups` → `systems/persistence` → `ui/hud` → `ui/encuentros` → `ui/overlays` → `ui/bocadillos` → `game.js` (bootstrap en `DOMContentLoaded`).

### Game loop

`requestAnimationFrame` con `dt` clamped a 100ms. Si está activo un encuentro, solo se hace `tickEncuentro` y se renderiza el frame estático. Si está pausado o en game over, solo se renderiza. Si todo va bien: input → player → NPCs → cámara → HUD → check victoria.

## Cómo extender

### Añadir un NPC nuevo

1. En `js/data/npcs.js` añade una entrada con `id`, `nombre`, `emoji`, `colorBase`, `velocidad`, `rangoDeteccion`, `anchoConoDeteccion` (0..1, dot product mínimo del cono frontal), `danoPaciencia`, `tipoEncuentro` (`tap`/`hold`/`wait`), `encuentroDuracion` en segundos.
2. En `js/data/frases.js` añade un array de bocadillos para ese `id`.
3. En `js/data/niveles.js` añade el `id` al array `arquetipos` de los niveles donde quieres que aparezca.

El sprite se genera automáticamente desde el `emoji` y `colorBase`. No hace falta crear assets.

### Añadir un nivel

Añade un objeto al array `NIVELES` en `js/data/niveles.js` con:

- `id`, `nombre`, `descripcion`, `ambiente` (`dia`/`tarde`/`noche`), `velocidadMultiplicador`
- `spawn: {x, z}` y `home: {x, z}` (deben quedar dentro de algún corredor)
- `path[]` — array de corredores axis-aligned. Cada entrada:
  - `id` único, `type` (`recta` | `plaza` | `callejon`)
  - `rect: {minX, minZ, maxX, maxZ}` — área caminable
  - `dir: [dx, dz]` — dirección del jugador hacia casa al cruzar este corredor (se usa para orientar cámara y conos de detección)
  - `milestone` 0..1 — progreso al alcanzar este corredor
  - `branch` opcional (`'A'` | `'B'`) — marca corredores de bifurcación
  - `isHome: true` — para el corredor que contiene `home`
  - `powerup` opcional (`'cafe'` | `'auriculares'`) — solo para callejones
- `numNPCs`, `arquetipos[]`

Los corredores se conectan automáticamente cuando sus `rect` comparten una arista (ej. `A.maxX === B.minX` y los rangos en Z se solapan). Por esa arista no se construye pared ni acera. Las plazas son corredores tipo `plaza` (más anchos y con suelo distinto). Los callejones (`callejon`) se excluyen del spawn de NPCs y pueden tener `powerup`. Las bifurcaciones se modelan con dos corredores paralelos a la misma altura de milestone, conectados por arriba con un corredor previo y por abajo con otro corredor (típicamente `isHome`); el "isla" central entre ellos lo construye el sistema de paredes solo.

La paleta de edificios se elige por seed determinista del `id`.

### Sustituir sprites procedurales por PNGs

En `js/systems/sprites.js`, modifica `getSpriteTexture(npcId)` para hacer `new THREE.TextureLoader().load(...)` cuando exista un PNG en `sprites/<npcId>.png`, fallback al canvas si no. La cache `_spriteCache` ya está. Cero refactor del resto del juego.

### Tuneo rápido

Casi todos los parámetros de gameplay están en `js/constants.js`:
- `PACIENCIA_INICIAL`, `VIDAS_INICIALES`
- `VELOCIDAD_JUGADOR`, `ANCHO_CALLE`
- `CAMERA_OFFSET_Y/Z`, `CAMERA_LOOK_AHEAD`, `CAMERA_LERP`
- `FOG_NEAR`, `FOG_FAR`
- `PALETA_PSX` (cielo según ambiente, asfalto, edificios, portal)

## Persistencia

Clave `caminoacasa_save` en `localStorage`. Guarda `nivelMaxAlcanzado` y `mejorTiempos`. Autosave cada 30 segundos + en `visibilitychange` y `beforeunload` (mismo patrón que `boomermania`).

## Compatibilidad

- **WebGL requerido**. Si no está disponible al cargar, se muestra una pantalla "WebGL no disponible" con CTA al menú.
- **Móvil**: el joystick virtual se monta solo si `matchMedia('(pointer: coarse)')` matchea. En desktop con teclado, no aparece.
- **Capacitor Android**: `webDir` apunta a `docs/`, así que el juego se incluye automáticamente en el APK sin tocar `capacitor.config.json`.

## Limitaciones conocidas / iteraciones futuras

- **Sin audio**. No hay efectos ni música. Web Audio API + ambiente Las Ramblas sería un add natural.
- **Sprites sin animación**. Los NPCs son una sola imagen estática. Spritesheet 4-direcciones tipo Doom sería un upgrade.
- **3 niveles**. Modo endless con generación procedural de paths y leaderboard local pendiente.
- **Cámara dura en giros muy cerrados**. La interpolación de dirección suaviza pero hay un par de frames de tirón cuando el corredor cambia de eje 90°.
- **Construcción de paredes con solapamientos en esquinas**. Cuando dos corredores perpendiculares se cruzan, los edificios contiguos pueden clipear unos en otros. No se ve desde el ángulo del jugador pero existe.
- **Rendimiento móvil no profilado en gama baja**. Si baja de 30 fps en algún dispositivo, la primera optimización es fusionar edificios en un único `BufferGeometry` por lado (de N draw calls a 2).

## Verificación local

```bash
cd docs && python3 -m http.server 8000
# Abrir http://localhost:8000/ y entrar en "Camino a Casa"
```

Para probar móvil desde la misma red local: `http://<ip-local>:8000/games/camino-a-casa/`.

Para Android: `npm run sync && npm run build:android` desde la raíz del repo.
