mirror of
https://github.com/Earth-Genesis-Games/Ajedrez_Purgatorio.git
synced 2026-09-11 09:47:35 +00:00
docs: ADR-0000..0009 aceptados y registry de arquitectura
This commit is contained in:
@@ -0,0 +1,301 @@
|
||||
# ADR-0000: Patrones de Infraestructura Global
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Date
|
||||
|
||||
2026-08-16 — when this ADR was written
|
||||
|
||||
## Last Verified
|
||||
|
||||
2026-08-16 — when this ADR was last confirmed accurate against the current
|
||||
engine version and design. Update this date when you re-read and confirm it
|
||||
is still correct, even if nothing changed.
|
||||
|
||||
## Decision Makers
|
||||
|
||||
Diseñador/director (usuario) + agente de arquitectura (opencode)
|
||||
|
||||
## Summary
|
||||
|
||||
ADR fundacional que formaliza la base de infraestructura que toda la base de
|
||||
código ya usa de facto tras 5 sprints sin ADRs formales: (1) singleton
|
||||
MonoBehaviour cross-escena con estilo canónico *Awake-explicit* +
|
||||
`DontDestroyOnLoad`, (2) lógica de negocio en capa de servicios POCO sin
|
||||
dependencia de Unity, (3) persistencia mediante ScriptableObject de estado
|
||||
runtime + `JsonUtility` a `persistentDataPath`, (4) comunicación cross-sistema
|
||||
por eventos C#, y (5) restricción un manager por responsabilidad (prohíbe
|
||||
duplicados). Fija las convenciones que los ADR-0001..0008 (borradores en
|
||||
`docs/architecture/architecture.md`) detallarán como archivos formales.
|
||||
|
||||
## Engine Compatibility
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Engine** | Unity 6.3 LTS (6000.3.13f1) |
|
||||
| **Domain** | Core / Scripting |
|
||||
| **Knowledge Risk** | **HIGH** — versión post-cutoff (May 2025), verificada contra engine-reference |
|
||||
| **References Consulted** | `docs/engine-reference/unity/VERSION.md`, `docs/engine-reference/unity/breaking-changes.md`, `docs/engine-reference/unity/deprecated-apis.md` |
|
||||
| **Post-Cutoff APIs Used** | None (todas las APIs usadas son vigentes) |
|
||||
| **Verification Required** | Re-validar si se migra a New Input System o Addressables (APIs deprecadas en uso — `Input` legacy, `Resources.Load` — aceptadas para el Vertical Slice, fuera del alcance de este ADR) |
|
||||
|
||||
> **Note**: Knowledge Risk es HIGH — este ADR debe re-validarse si el proyecto
|
||||
> actualiza la versión del motor. Marcar como "Superseded" y escribir un ADR nuevo.
|
||||
|
||||
## ADR Dependencies
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Depends On** | None (fundacional) |
|
||||
| **Enables** | ADR consolidación `SceneTransitionManager` (Core/UI); ADR Save/Load completo; ADR Dead Kings System; ADR Legados |
|
||||
| **Blocks** | Consolidación de managers duplicados; epics de infraestructura |
|
||||
| **Ordering Note** | Debe aceptarse antes de los ADR específicos de managers |
|
||||
|
||||
## Context
|
||||
|
||||
### Problem Statement
|
||||
|
||||
El código se construyó de forma orgánica (Sprints 1-3 implementados) sin ADRs
|
||||
formales. Coexisten **dos estilos de singleton** (Awake-explicit en 7 managers,
|
||||
lazy-getter con auto-creación en 3) y hay un **manager duplicado**
|
||||
(`SceneTransitionManager` en `Assets/Game/Scripts/Mono/Core/` y en
|
||||
`Assets/Game/Scripts/Mono/UI/`). El coste de no decidir: cada nuevo manager
|
||||
sigue un estilo distinto y los bugs de duplicación y de orden de ejecución se
|
||||
pagan de nuevo en cada sistema.
|
||||
|
||||
### Current State
|
||||
|
||||
`docs/architecture/architecture.md` documenta los patrones dominantes como
|
||||
mini-ADRs de borrador (ADR-001..008) que no tienen archivos formales ni
|
||||
estatus. El registro `docs/registry/architecture.yaml` está vacío. Código
|
||||
verificado:
|
||||
|
||||
- **Lazy-getter con auto-creación**: `AudioManager`, `SaveSystem`,
|
||||
`UI\SceneTransitionManager`.
|
||||
- **Awake-explicit** (`Instance { get; private set; }` + `DontDestroyOnLoad`):
|
||||
`GameStateManager`, `Core\SceneTransitionManager`, `DialogueSystem`,
|
||||
`CampaignManager`, `PurgatoryManager`, `GameManager`, `BoardManager`.
|
||||
- **SO singleton lazy**: `DeadKingPool` (carga vía `Resources.Load<DeadKingPool>`).
|
||||
|
||||
### Constraints
|
||||
|
||||
- Motor fijado: Unity 6.3 LTS (6000.3.13f1).
|
||||
- APIs deprecadas en uso (`Input` legacy en `DialogueUI`, `PauseMenuController`;
|
||||
`Resources.Load` en `DeadKingPool`) se aceptan para el Vertical Slice.
|
||||
- UGUI + TextMeshPro soportado (deprecated pero mantenido por Unity).
|
||||
- Compatibilidad con sistemas existentes implementados: Board, Piece Movement,
|
||||
Turn, Check/Checkmate, Castling, Promotion, En Passant, Draw Detection,
|
||||
Piece Identity, Dice, Campaign, AI.
|
||||
- Línea Atemporal II ("Gambito de Sombras") debe reutilizar esta infraestructura
|
||||
sin duplicarla.
|
||||
|
||||
### Requirements
|
||||
|
||||
- Consistencia de patrón para todos los managers cross-escena.
|
||||
- Lógica de negocio testeable sin dependencia de `UnityEngine` (test Edit Mode).
|
||||
- Persistencia simple, portable y legible.
|
||||
- Desacople entre sistemas (la UI no acopla a managers por nombre salvo vía
|
||||
`Instance`/eventos).
|
||||
- Un único dueño por responsabilidad (sin managers duplicados).
|
||||
|
||||
## Decision
|
||||
|
||||
### Arquitectura
|
||||
|
||||
```
|
||||
┌─ MonoBehaviour Managers (cross-escena, DontDestroyOnLoad, Awake-explicit) ─┐
|
||||
│ GameStateManager · CampaignManager · DialogueSystem · PurgatoryManager │
|
||||
│ AudioManager · SaveSystem · SceneTransitionManager (único tras ADR de │
|
||||
│ consolidación) │
|
||||
└───────────────────────────────┬─────────────────────────────────────────────┘
|
||||
│ eventos C# (Action<...>)
|
||||
┌─────────────▼──────────────┐
|
||||
│ Capa de Servicios POCO │ ← sin using UnityEngine
|
||||
│ (lógica de negocio, │
|
||||
│ unit-testable) │
|
||||
└─────────────┬──────────────┘
|
||||
│ SO de estado runtime (fuente de verdad)
|
||||
┌─────────────▼──────────────┐
|
||||
│ Persistencia: JsonUtility │→ persistentDataPath/*.json
|
||||
│ + ScriptableObjects │
|
||||
└────────────────────────────┘
|
||||
```
|
||||
|
||||
### Key Interfaces
|
||||
|
||||
```
|
||||
// Esqueleto canónico de manager cross-escena (Awake-explicit)
|
||||
public class GameStateManager : MonoBehaviour
|
||||
{
|
||||
public static GameStateManager Instance { get; private set; }
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
Instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (Instance == this) Instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Comunicación cross-sistema por eventos C#
|
||||
public static event Action<PieceIdentity> OnPieceCaptured; // prefijo On
|
||||
|
||||
// Persistencia: wrapper serializable explícito (JsonUtility no serializa
|
||||
// [SerializeField] privados) → fichero JSON en persistentDataPath
|
||||
[Serializable]
|
||||
private class GameSaveData { public List<CampaignState> campaign = new(); }
|
||||
```
|
||||
|
||||
### Implementation Guidelines
|
||||
|
||||
- Nuevos managers: copiar el esqueleto Awake-explicit (nunca lazy-getter).
|
||||
- Migrar en tareas separadas (no bloquea el VS): `AudioManager`, `SaveSystem`,
|
||||
`UI\SceneTransitionManager` a Awake-explicit.
|
||||
- Eventos: desuscribirse en `OnDestroy`; convención de prefijo `On` (ej.
|
||||
`OnPieceCaptured`).
|
||||
- Servicios POCO: namespaces sin `MonoBehaviour`; exponer estado por
|
||||
propiedades de solo lectura.
|
||||
- `Resources.Load` aceptado para SO de configuración en el VS; centralizar la
|
||||
carga en una API única para poder migrar a Addressables sin tocar consumidores.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Alternative 1: Lazy-getter con auto-creación (estilo AudioManager)
|
||||
|
||||
- **Description**: getter crea un GameObject nuevo + `DontDestroyOnLoad` si
|
||||
`_instance == null`. Robusto ante referencias tempranas.
|
||||
- **Pros**: auto-cura ante referencias antes del Awake.
|
||||
- **Cons**: oculta el ciclo de vida, fomenta ámbito global implícito, testing
|
||||
más difícil, oculta errores de configuración de escena.
|
||||
- **Estimated Effort**: menor (no hay migración de los 3 managers), pero
|
||||
exige migrar los 7 managers Awake-explicit.
|
||||
- **Rejection Reason**: la mayoría de la base ya usa Awake-explicit y es más
|
||||
predecible con el ciclo de vida de Unity.
|
||||
|
||||
### Alternative 2: Híbrido con guarda de diagnóstico
|
||||
|
||||
- **Description**: base Awake-explicit + guarda en el getter que loguea un
|
||||
error claro si se accede antes del Awake (sin auto-creación).
|
||||
- **Pros**: diagnóstico temprano de referencias fuera de orden.
|
||||
- **Cons**: coste de mantenimiento adicional sin beneficio real para el tamaño
|
||||
del proyecto.
|
||||
- **Estimated Effort**: similar al elegido + guarda extra por manager.
|
||||
- **Rejection Reason**: no se eligió por simplicidad; la convención de
|
||||
referencias tempranas (solo desde Awake/Start/OnEnable) cubre el caso.
|
||||
|
||||
### Alternative 3: Mega-ADR que formaliza todo a la vez
|
||||
|
||||
- **Description**: un solo ADR que documenta los 8 patrones de `architecture.md`.
|
||||
- **Pros**: documentación rápida y centralizada.
|
||||
- **Cons**: las decisiones específicas (IA, UI, persistencia de Dead Kings, etc.)
|
||||
no tendrían historial ni estatus propios para su ciclo de vida.
|
||||
- **Estimated Effort**: menor en redacción, mayor en mantenimiento.
|
||||
- **Rejection Reason**: se prefieren ADRs atómicos; este ADR fija el umbrella y
|
||||
los mini-ADRs 001-008 se promueven a archivos formales en sesiones siguientes.
|
||||
|
||||
### Alternative 4: Persistencia con BinaryFormatter / PlayerPrefs
|
||||
|
||||
- **Description**: serialización binaria o clave-valor para el estado guardado.
|
||||
- **Pros**: PlayerPrefs es trivial de usar; BinaryFormatter era potente.
|
||||
- **Cons**: BinaryFormatter deprecado/inseguro en .NET moderno; PlayerPrefs
|
||||
inadecuado para datos estructurados de campaña.
|
||||
- **Estimated Effort**: menor en lo inmediato.
|
||||
- **Rejection Reason**: `JsonUtility` + ficheros JSON en `persistentDataPath`
|
||||
es simple, portable y depurable.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Consistencia total del patrón de managers en toda la base.
|
||||
- Testabilidad de la lógica de negocio (servicios POCO sin Unity).
|
||||
- Desacople entre sistemas vía eventos; la UI no acopla managers por nombre.
|
||||
- Fuente de verdad única de estado runtime (ScriptableObject) con serialización
|
||||
explícita y portable.
|
||||
|
||||
### Negative
|
||||
|
||||
- Los 3 managers lazy requieren migración (riesgo bajo de regresiones de
|
||||
inicialización).
|
||||
- `Resources.Load` y `Input` legacy permanecen en el VS (deuda aceptada).
|
||||
- JsonUtility no serializa `[SerializeField]` privados: exige wrappers
|
||||
serializables explícitos por sistema.
|
||||
|
||||
### Neutral
|
||||
|
||||
- Eventos C# sin bus centralizado (no hay sistema de mensajes global).
|
||||
- La convención "referenciar `Instance` solo en Awake/Start/OnEnable" pasa a
|
||||
ser regla de revisión de código.
|
||||
|
||||
## Risks
|
||||
|
||||
| Risk | Probability | Impact | Mitigation |
|
||||
|------|------------|--------|-----------|
|
||||
| Migración lazy→Awake-explicit rompe orden de inicialización | Baja | Media | Guard de duplicado + convención de referencias tempranas; smoke test del VS |
|
||||
| Orden de ejecución entre managers cross-escena | Media | Media | `DontDestroyOnLoad` en orden estable (GameStateManager primero); script execution order documentado |
|
||||
| `Resources.Load` deprecado escala mal con contenido | Media | Media | API de carga centralizada para poder migrar a Addressables sin tocar consumidores |
|
||||
| Eventos C# fugan suscripciones (memory leak) | Media | Baja | Regla de desuscripción en `OnDestroy`; revisión en code review |
|
||||
|
||||
## Performance Implications
|
||||
|
||||
| Metric | Before | Expected After | Budget |
|
||||
|--------|--------|---------------|--------|
|
||||
| CPU (frame time) | — | +despreciable (getters singleton, eventos C# sub-µs) | — |
|
||||
| Memory | — | +coste de un `DontDestroyOnLoad` por manager (ya presente) | — |
|
||||
| Load Time | — | sin cambio (persistencia solo en puntos de guardado) | — |
|
||||
| Network (if applicable) | — | N/A | — |
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. Formalizar estancias en `docs/registry/architecture.yaml` (registry) —
|
||||
fase 5 de este ADR.
|
||||
2. Migrar `SaveSystem`, `AudioManager` a Awake-explicit (tareas de limpieza; sin
|
||||
bloqueo del VS). `UI\SceneTransitionManager` eliminado con ADR-0009.
|
||||
3. ✅ Crear ADR de consolidación del `SceneTransitionManager` duplicado (Core/UI)
|
||||
— hecho: [ADR-0009](adr-0009-consolidacion-scenetransitionmanager.md).
|
||||
4. ✅ Promover mini-ADRs 001-008 de `architecture.md` a archivos formales
|
||||
— hecho: ADR-0001..0008 (esta sesión).
|
||||
|
||||
**Rollback plan**: los managers migrados son modificaciones locales reversibles
|
||||
por git; el ADR se puede marcar Superseded si un cambio de motor lo invalida.
|
||||
|
||||
## Validation Criteria
|
||||
|
||||
- [ ] `grep -l "if (_instance == null)" Assets/Game/Scripts` devuelve 0 (sin
|
||||
lazy-getters restantes).
|
||||
- [ ] Todo manager cross-escena implementa el esqueleto Awake-explicit con
|
||||
guard de duplicado.
|
||||
- [ ] Cada servicio POCO compila sin referencia a `UnityEngine`.
|
||||
- [ ] Nuevos sistemas se comunican por eventos y no referencian managers por
|
||||
nombre salvo por `Instance`.
|
||||
- [ ] Sin managers duplicados en la base (tras consolidación).
|
||||
|
||||
## GDD Requirements Addressed
|
||||
|
||||
| GDD Document | System | Requirement | How This ADR Satisfies It |
|
||||
|-------------|--------|-------------|--------------------------|
|
||||
| `design/gdd/systems-index.md` | 15 — Scene Management | Transiciones entre escenas (Sprint 1: SceneTransitionManager) | Estancia 5: un único dueño → consolida el duplicado; estilo canónico en estancia 1 |
|
||||
| `design/gdd/systems-index.md` | 16 — Game State Manager | Estados (menú, partida, diálogo, purgatorio, game over) | Estancia 1 + 4: manager único Awake-explicit; transiciones de estado notificadas por eventos |
|
||||
| `design/gdd/systems-index.md` | 17 — Save/Load | Persistencia parcial (CampaignState SO, sin serialización) | Estancia 3: SO runtime + JsonUtility a persistentDataPath completa el sistema |
|
||||
| `design/gdd/systems-index.md` | 18 — HUD | HUD parcial (PieceTooltip) | Estancia 2 + 4: HUD consume estado vía servicios POCO + eventos, sin acoplar UI a managers |
|
||||
|
||||
> TR IDs: el `docs/architecture/tr-registry.yaml` está vacío; `/architecture-review`
|
||||
> asignará los TR en su fase 8.
|
||||
|
||||
## Related
|
||||
|
||||
- `docs/architecture/architecture.md` — mini-ADRs 001-008 (borradores que este
|
||||
ADR habilita formalizar como archivos formales).
|
||||
- `docs/registry/architecture.yaml` — estancias registradas en fase 5
|
||||
(singleton, persistencia, eventos, patrón prohibido de manager duplicado).
|
||||
@@ -0,0 +1,191 @@
|
||||
# ADR-0001: Service Layer POCO
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Date
|
||||
|
||||
2026-08-16
|
||||
|
||||
## Last Verified
|
||||
|
||||
2026-08-16
|
||||
|
||||
## Decision Makers
|
||||
|
||||
Diseñador/director (usuario) + agente de arquitectura (opencode)
|
||||
|
||||
## Summary
|
||||
|
||||
`GameManager` concentraba 8 responsabilidades (~475 líneas) y era imposible de testear sin
|
||||
instanciar una escena Unity completa. Se extrae la lógica de negocio del ajedrez a una capa de
|
||||
servicios POCO (`CheckDetector`, `DrawDetector`, `MoveValidator`) sin dependencia de
|
||||
`MonoBehaviour`, inyectados por constructor/dependencia en `GameManager.Awake()`.
|
||||
|
||||
## Engine Compatibility
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Engine** | Unity 6.3 LTS (6000.3.13f1) |
|
||||
| **Domain** | Core / Scripting |
|
||||
| **Knowledge Risk** | HIGH — post-cutoff, verificada contra engine-reference |
|
||||
| **References Consulted** | `VERSION.md`, `breaking-changes.md`, `deprecated-apis.md` |
|
||||
| **Post-Cutoff APIs Used** | None (POCO C# estándar, .NET Standard 2.1) |
|
||||
| **Verification Required** | None — la capa POCO no usa APIs del motor |
|
||||
|
||||
## ADR Dependencies
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Depends On** | ADR-0000 (Accepted) — estancia 2: capa de servicios POCO |
|
||||
| **Enables** | Test suite Edit Mode de servicios; reutilización por IA y futuros sistemas (hints, variantes) |
|
||||
| **Blocks** | None |
|
||||
| **Ordering Note** | None |
|
||||
|
||||
## Context
|
||||
|
||||
### Problem Statement
|
||||
|
||||
`GameManager` concentraba lógica de tablero, turnos, legalidad, jaque/mate, tablas, input y
|
||||
coordinación (~475 líneas). Sin escena Unity completa era imposible testear la lógica de
|
||||
ajedrez, y cualquier cambio de reglas requería tocar un MonoBehaviour acoplado al editor.
|
||||
|
||||
### Current State
|
||||
|
||||
Refactor ya aplicado (Sprints 1-2): `Assets/Game/Scripts/Mono/Core/Services/` contiene
|
||||
`CheckDetector.cs`, `DrawDetector.cs` y `MoveValidator.cs` como POCOs. `GameManager` quedó
|
||||
reducido a 3 responsabilidades.
|
||||
|
||||
### Constraints
|
||||
|
||||
- Los servicios POCO no deben referenciar `UnityEngine` (testeables en Edit Mode).
|
||||
- Compatibilidad con el `GameManager` existente y con la IA (que comparte los servicios).
|
||||
- Sin over-engineering: la capa es pequeña y no necesita interfaces por servicio.
|
||||
|
||||
### Requirements
|
||||
|
||||
- Testabilidad de la lógica de ajedrez sin escena.
|
||||
- Reutilización de reglas por IA y futuros sistemas.
|
||||
|
||||
## Decision
|
||||
|
||||
### Architecture
|
||||
|
||||
```
|
||||
GameManager (MonoBehaviour)
|
||||
│ inyección en Awake()
|
||||
▼
|
||||
CheckDetector ──► DrawDetector
|
||||
▲ ▲
|
||||
│ dependencia │
|
||||
MoveValidator ────────┘
|
||||
▲
|
||||
│
|
||||
AIController (consume los mismos servicios)
|
||||
```
|
||||
|
||||
### Key Interfaces
|
||||
|
||||
```
|
||||
// Servicios POCO: sin MonoBehaviour, sin UnityEngine
|
||||
public class CheckDetector {
|
||||
public bool IsInCheck(BoardState board, Color side);
|
||||
public bool IsCheckmate(BoardState board, Color side);
|
||||
}
|
||||
|
||||
public class MoveValidator {
|
||||
private readonly CheckDetector _checkDetector; // DI con validación null
|
||||
public bool IsLegalMove(BoardState board, Move move, Color side);
|
||||
}
|
||||
|
||||
public class DrawDetector {
|
||||
public DrawType? Evaluate(BoardState board); // stalemate, repetición, 50-mov, material
|
||||
}
|
||||
```
|
||||
|
||||
### Implementation Guidelines
|
||||
|
||||
- Nuevos servicios: clases estáticas o de instancia sin herencia de `MonoBehaviour`;
|
||||
estado por parámetro o inyección, nunca `Instance` global.
|
||||
- El consumo desde MonoBehaviour se hace por inyección en `Awake()`.
|
||||
- `MoveValidator` requiere `CheckDetector` no-null en constructor.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Alternative 1: Mantener la lógica en GameManager
|
||||
|
||||
- **Description**: seguir con toda la lógica en el MonoBehaviour.
|
||||
- **Pros**: sin refactor.
|
||||
- **Cons**: intesteable, acoplada al editor, 8 responsabilidades.
|
||||
- **Estimated Effort**: 0 (pero cada bug/cambio cuesta más).
|
||||
- **Rejection Reason**: la causa directa del ADR.
|
||||
|
||||
### Alternative 2: Servicios como MonoBehaviour
|
||||
|
||||
- **Description**: componentes en la escena que ofrecen la lógica.
|
||||
- **Pros**: accesibles por inspector.
|
||||
- **Cons**: siguen requiriendo escena para testear.
|
||||
- **Estimated Effort**: similar al elegido.
|
||||
- **Rejection Reason**: no resuelve la testabilidad.
|
||||
|
||||
### Alternative 3: Utilidades estáticas
|
||||
|
||||
- **Description**: clases `static` con métodos puros.
|
||||
- **Pros**: simples, sin estado.
|
||||
- **Cons**: difícil inyectar dependencias (MoveValidator→CheckDetector) y testear en aislamiento.
|
||||
- **Estimated Effort**: menor.
|
||||
- **Rejection Reason**: la DI con instancias permite tests aislados y mocks.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Reglas de ajedrez 100% testeables sin Unity (tests Edit Mode).
|
||||
- Lógica reutilizable por IA y futuros sistemas.
|
||||
- `GameManager` reducido y legible.
|
||||
|
||||
### Negative
|
||||
|
||||
- Capa de indirección entre MonoBehaviour y lógica.
|
||||
|
||||
### Neutral
|
||||
|
||||
- Los servicios se exponen por métodos, no por estado global.
|
||||
|
||||
## Risks
|
||||
|
||||
| Risk | Probability | Impact | Mitigation |
|
||||
|------|------------|--------|-----------|
|
||||
| Servicios mutables compartidos entre GameManager e IA | Baja | Media | Estado por parámetro; servicios sin estado mutable persistente |
|
||||
| Regresión tras refactor | Media | Media | Tests Edit Mode de servicios + smoke test del VS |
|
||||
|
||||
## Performance Implications
|
||||
|
||||
Sin impacto: los servicios son llamadas C# directas sin allocations adicionales relevantes
|
||||
(valores de retorno simples, sin eventos por movimiento salvo los de ADR-0003).
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Refactor ya ejecutado (Sprints 1-2). Cambios futuros de reglas se aplican en los POCOs.
|
||||
|
||||
**Rollback plan**: recuperar versión anterior de `GameManager` por git.
|
||||
|
||||
## Validation Criteria
|
||||
|
||||
- [ ] `CheckDetector`, `DrawDetector`, `MoveValidator` no importan `UnityEngine`.
|
||||
- [ ] `GameManager` no contiene lógica de legalidad/jaque/tablas inline.
|
||||
- [ ] Tests Edit Mode cubren: jaque, jaque mate, tablas (repetición/50-mov/ahogado/material), legalidad de movimiento.
|
||||
|
||||
## GDD Requirements Addressed
|
||||
|
||||
| GDD Document | System | Requirement | How This ADR Satisfies It |
|
||||
|-------------|--------|-------------|--------------------------|
|
||||
| `design/gdd/systems-index.md` | 1-8 — Board, Movement, Turn, Check/Checkmate, Castling, Promotion, En Passant, Draw | Reglas de ajedrez implementadas y verificables | Servicios POCO testeables sin escena |
|
||||
| `design/gdd/systems-index.md` | 13 — AI System | IA consume las reglas para evaluar movimientos | Servicios compartidos por `AIController` |
|
||||
|
||||
## Related
|
||||
|
||||
- ADR-0000 (estancia 2) — capa de servicios POCO.
|
||||
- ADR-0008 — `DrawDetector` implementa el hashing FEN-like.
|
||||
- Código: `Assets/Game/Scripts/Mono/Core/Services/*.cs`, `GameManager.cs`.
|
||||
@@ -0,0 +1,172 @@
|
||||
# ADR-0002: Strategy Pattern IA
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Date
|
||||
|
||||
2026-08-16
|
||||
|
||||
## Last Verified
|
||||
|
||||
2026-08-16
|
||||
|
||||
## Decision Makers
|
||||
|
||||
Diseñador/director (usuario) + agente de arquitectura (opencode)
|
||||
|
||||
## Summary
|
||||
|
||||
El sistema de IA necesita 3 dificultades escalables por capítulo. Se adopta el patrón
|
||||
Strategy: interfaz `IAIStrategy` con `AIStrategyEasy` (random con prioridad a capturas),
|
||||
`AIStrategyMedium` (1-ply evaluation) y `AIStrategyHard` (minimax alpha-beta depth 2, timeout
|
||||
5s), seleccionadas por `AIController`.
|
||||
|
||||
## Engine Compatibility
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Engine** | Unity 6.3 LTS (6000.3.13f1) |
|
||||
| **Domain** | Core / Scripting |
|
||||
| **Knowledge Risk** | HIGH — post-cutoff, verificada contra engine-reference |
|
||||
| **References Consulted** | `VERSION.md`, `breaking-changes.md`, `deprecated-apis.md` |
|
||||
| **Post-Cutoff APIs Used** | None |
|
||||
| **Verification Required** | None — lógica pura C#, sin APIs nuevas del motor |
|
||||
|
||||
## ADR Dependencies
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Depends On** | ADR-0000 (Accepted); ADR-0001 (Accepted requerido para compartir servicios) |
|
||||
| **Enables** | Dificultad por capítulo; nuevas dificultades sin tocar el controlador |
|
||||
| **Blocks** | Instanciación de `AIController` en escenas (ver Contexto — INCOMPLETO) |
|
||||
| **Ordering Note** | La dificultad por capítulo desde `CampaignManager` es TODO pendiente (S4-005) |
|
||||
|
||||
## Context
|
||||
|
||||
### Problem Statement
|
||||
|
||||
Tres dificultades de IA deben poder escalarse por capítulo (el VS sube dificultad en cada
|
||||
tablero: La Fábrica → El Hospital → La Corte del Juicio). Sin un patrón de selección, el
|
||||
controlador crecería con condicionales por dificultad.
|
||||
|
||||
### Current State
|
||||
|
||||
`IAIStrategy` + las 3 estrategias implementadas (Sprint 2). **Completado (2026-08-16)**:
|
||||
- `AIController` instanciado en `Chapter1.unity`.
|
||||
- `CampaignManager.ApplyChapterAIConfiguration` aplica la dificultad del capítulo vía
|
||||
`AIController.SetDifficulty()` (parsing de `AIConfig.difficulty` → `AIDifficulty`).
|
||||
- `AIController` inicializa la estrategia solo si nadie la configuró antes del `Start`
|
||||
(robustez de orden de ejecución) y reintenta la suscripción a `OnTurnChanged` si
|
||||
`OnEnable` corrió antes que `GameManager.Awake`.
|
||||
|
||||
### Constraints
|
||||
|
||||
- Las estrategias deben poder evaluar posiciones sin depender de la UI.
|
||||
- La selección debe ser configurable por capítulo.
|
||||
|
||||
### Requirements
|
||||
|
||||
- Extensibilidad: añadir dificultades sin modificar `AIController`.
|
||||
- Selección de estrategia en tiempo de ejecución.
|
||||
|
||||
## Decision
|
||||
|
||||
### Architecture
|
||||
|
||||
```
|
||||
AIController ──► IAIStrategy (seleccionada en runtime)
|
||||
├── AIStrategyEasy (random + prioridad a capturas)
|
||||
├── AIStrategyMedium (1-ply evaluation)
|
||||
└── AIStrategyHard (minimax alpha-beta depth 2, timeout 5s)
|
||||
```
|
||||
|
||||
### Key Interfaces
|
||||
|
||||
```
|
||||
public interface IAIStrategy {
|
||||
Move SelectMove(BoardState board, Color side, float timeoutSec);
|
||||
}
|
||||
```
|
||||
|
||||
### Implementation Guidelines
|
||||
|
||||
- El patrón debe respetar el estilo Awake-explicit de ADR-0000 si el controller es
|
||||
MonoBehaviour cross-escena.
|
||||
- Estrategias sin estado persistente entre turnos (o estado por partida, reseteable).
|
||||
- Las estrategias consumen los servicios POCO de ADR-0001.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Alternative 1: Condicionales switch/if por dificultad en AIController
|
||||
|
||||
- **Description**: `if (difficulty == Easy) ... else if (difficulty == Medium) ...`
|
||||
- **Pros**: sin interfaces.
|
||||
- **Cons**: el controlador crece con cada dificultad; OCP violado.
|
||||
- **Estimated Effort**: menor al inicio, mayor a la larga.
|
||||
- **Rejection Reason**: no extensible.
|
||||
|
||||
### Alternative 2: Dificultad como enum evaluada internamente
|
||||
|
||||
- **Description**: una sola clase con parámetros de dificultad.
|
||||
- **Pros**: centraliza el algoritmo.
|
||||
- **Cons**: acopla los 3 algoritmos distintos (random vs minimax) en una clase.
|
||||
- **Estimated Effort**: similar.
|
||||
- **Rejection Reason**: mezcla algoritmos radicalmente distintos.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Nuevas dificultades sin tocar el controlador.
|
||||
- Cada estrategia se testea de forma aislada.
|
||||
|
||||
### Negative
|
||||
|
||||
- Sin deuda pendiente conocida tras el completado (2026-08-16). El patrón añade una clase
|
||||
por dificultad (indirección ligera).
|
||||
|
||||
### Neutral
|
||||
|
||||
- Una clase por dificultad; ligero overhead de indirección.
|
||||
|
||||
## Risks
|
||||
|
||||
| Risk | Probability | Impact | Mitigation |
|
||||
|------|------------|--------|-----------|
|
||||
| `AIStrategyHard` (alpha-beta) excede el tiempo en posiciones complejas | Media | Media | Timeout de 5s + limitación de profundidad; abortar y devolver mejor movimiento encontrado |
|
||||
| Orden de ejecución entre `AIController.Start` y la configuración del capítulo | Baja | Baja | `Start` inicializa solo si `_currentStrategy == null`; `CampaignManager` aplica la dificultad en `LoadChapter` |
|
||||
|
||||
## Performance Implications
|
||||
|
||||
`AIStrategyHard`: minimax depth 2 con alpha-beta, acotado por timeout de 5s. `Easy`/`Medium`
|
||||
son ligeros. Sin impacto en frame time si el movimiento se computa en un turno del jugador IA
|
||||
(no bloquea el Update).
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Implementado (Sprint 2). Pendientes resueltos (2026-08-16): `AIController` instanciado en
|
||||
`Chapter1.unity`; dificultad por capítulo cableada desde `CampaignManager`.
|
||||
|
||||
**Rollback plan**: la interfaz permite volver a una dificultad fija sin cambios de diseño.
|
||||
|
||||
## Validation Criteria
|
||||
|
||||
- [x] `AIController` instanciado en escenas y produce movimientos legales en partida.
|
||||
- [x] La dificultad del capítulo N se selecciona desde `CampaignManager`.
|
||||
- [ ] `Hard` nunca supera el timeout de 5s por movimiento (verificar en playtest).
|
||||
- [ ] Nueva dificultad se añade implementando `IAIStrategy` sin modificar `AIController`.
|
||||
|
||||
## GDD Requirements Addressed
|
||||
|
||||
| GDD Document | System | Requirement | How This ADR Satisfies It |
|
||||
|-------------|--------|-------------|--------------------------|
|
||||
| `design/gdd/systems-index.md` | 13 — AI System | 3 dificultades escalables por capítulo | Strategy pattern permite selección runtime y extensión |
|
||||
| `design/gdd/systems-index.md` | 2 — Piece Movement | La IA mueve piezas legalmente | Estrategias consumen servicios POCO de ADR-0001 |
|
||||
|
||||
## Related
|
||||
|
||||
- ADR-0000 (estancia 2 y 5).
|
||||
- ADR-0001 — servicios compartidos.
|
||||
- Código: `Assets/Game/Scripts/Mono/AI/`.
|
||||
@@ -0,0 +1,182 @@
|
||||
# ADR-0003: Event-Driven
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Date
|
||||
|
||||
2026-08-16
|
||||
|
||||
## Last Verified
|
||||
|
||||
2026-08-16
|
||||
|
||||
## Decision Makers
|
||||
|
||||
Diseñador/director (usuario) + agente de arquitectura (opencode)
|
||||
|
||||
## Summary
|
||||
|
||||
La UI y los sistemas (Purgatorio, HUD) necesitan reaccionar a eventos del juego sin
|
||||
acoplamiento directo. Se adoptan eventos `Action<T>` expuestos por `GameManager`
|
||||
(`OnCheckmate`, `OnDraw`, `OnPieceMoved`, `OnTurnChanged`, `OnCheck`) como mecanismo de
|
||||
comunicación cross-sistema, con `PurgatoryManager` suscrito a capturas y la UI escuchando
|
||||
eventos.
|
||||
|
||||
## Engine Compatibility
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Engine** | Unity 6.3 LTS (6000.3.13f1) |
|
||||
| **Domain** | Core / Scripting |
|
||||
| **Knowledge Risk** | HIGH — post-cutoff, verificada contra engine-reference |
|
||||
| **References Consulted** | `VERSION.md`, `breaking-changes.md`, `deprecated-apis.md` |
|
||||
| **Post-Cutoff APIs Used** | None (eventos C# estándar) |
|
||||
| **Verification Required** | None |
|
||||
|
||||
## ADR Dependencies
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Depends On** | ADR-0000 (Accepted) — estancia 4: comunicación por eventos C# |
|
||||
| **Enables** | HUD completo (sistema 18), Purgatorio desacoplado del GameManager |
|
||||
| **Blocks** | Suscripciones de `GameHUD` pendientes (ver Contexto — INCOMPLETO) |
|
||||
| **Ordering Note** | None |
|
||||
|
||||
## Context
|
||||
|
||||
### Problem Statement
|
||||
|
||||
La UI (HUD, Purgatorio) y los sistemas reaccionan al estado de la partida. Referencias
|
||||
directas entre UI y `GameManager` acoplan presentación con lógica y dificultan cambios.
|
||||
|
||||
### Current State
|
||||
|
||||
Eventos `Action<T>` en `GameManager`: `OnCheckmate`, `OnDraw`, `OnPieceMoved`,
|
||||
`OnTurnChanged`, `OnCheck`. `PurgatoryManager` se suscribe a capturas. **Completado
|
||||
(2026-08-16)**: añadidos `OnPieceCaptured(Piece)` y `OnCheckResolved`; `GameHUD` suscrito a
|
||||
turno, captura, jaque y resolución de jaque.
|
||||
|
||||
### Constraints
|
||||
|
||||
- Mismo mecanismo de eventos en todo el proyecto (estancia ADR-0000).
|
||||
- Sin bus de mensajes global (eventos C# estáticos, no mensajes enrutados).
|
||||
|
||||
### Requirements
|
||||
|
||||
- Desacople UI ↔ lógica.
|
||||
- Suscriptores fáciles de añadir desde MonoBehaviour.
|
||||
|
||||
## Decision
|
||||
|
||||
### Architecture
|
||||
|
||||
```
|
||||
GameManager (produce eventos)
|
||||
│ OnCheckmate / OnDraw / OnPieceMoved / OnTurnChanged / OnCheck
|
||||
│ OnPieceCaptured / OnCheckResolved (añadidos 2026-08-16)
|
||||
▼
|
||||
├── PurgatoryManager (consumidor de capturas)
|
||||
└── GameHUD / UI (consumidores)
|
||||
```
|
||||
|
||||
### Key Interfaces
|
||||
|
||||
```
|
||||
// Productor (GameManager) — eventos de instancia
|
||||
public event Action<bool> OnTurnChanged;
|
||||
public event Action<bool> OnCheck;
|
||||
public event Action<bool> OnCheckmate;
|
||||
public event Action<string> OnDraw;
|
||||
public event Action<Piece, Vector2Int, Vector2Int> OnPieceMoved;
|
||||
public event Action<Piece> OnPieceCaptured;
|
||||
public event Action OnCheckResolved;
|
||||
|
||||
// Consumidor típico
|
||||
private void OnEnable() { GameManager.Instance.OnPieceMoved += HandleMove; }
|
||||
private void OnDisable() { GameManager.Instance.OnPieceMoved -= HandleMove; }
|
||||
```
|
||||
|
||||
### Implementation Guidelines
|
||||
|
||||
- Prefijo `On` en el nombre del evento (convención ADR-0000).
|
||||
- Desuscripción en `OnDisable`/`OnDestroy` (regla ADR-0000).
|
||||
- `OnPieceCaptured` se emite para capturas normales y en passant; `OnCheckResolved` se emite
|
||||
cuando el bando que estaba en jaque sale de él tras un movimiento legal.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Alternative 1: Llamadas directas UI → GameManager
|
||||
|
||||
- **Description**: la UI llama métodos de `GameManager` para enterarse del estado.
|
||||
- **Pros**: simple.
|
||||
- **Cons**: acoplamiento UI ↔ lógica; la UI debe conocer el estado completo.
|
||||
- **Estimated Effort**: menor.
|
||||
- **Rejection Reason**: el acoplamiento que este ADR elimina.
|
||||
|
||||
### Alternative 2: Event bus global (UnityEvent / mensajes enrutados)
|
||||
|
||||
- **Description**: bus central que enruta mensajes por tipo.
|
||||
- **Pros**: centraliza, desacopla emisor de consumidores.
|
||||
- **Cons**: indirección adicional, difícil de depurar, over-engineering para el alcance.
|
||||
- **Estimated Effort**: mayor.
|
||||
- **Rejection Reason**: los eventos C# estáticos del ADR-0000 cubren el caso; ADR-0000
|
||||
decidió no usar bus.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Desacople UI ↔ lógica.
|
||||
- Consumidores se suscriben sin tocar el productor.
|
||||
|
||||
### Negative
|
||||
|
||||
- Sin deuda pendiente conocida tras el completado (2026-08-16). Los eventos de instancia
|
||||
crean acoplamiento implícito productor↔consumidor (grep-able, pero no inyectable).
|
||||
|
||||
### Neutral
|
||||
|
||||
- Sin bus central; cada sistema declara sus propios eventos estáticos.
|
||||
|
||||
## Risks
|
||||
|
||||
| Risk | Probability | Impact | Mitigation |
|
||||
|------|------------|--------|-----------|
|
||||
| Fugas de suscripción (leaks) | Media | Baja | Desuscripción en `OnDisable`/`OnDestroy`; revisión en code review |
|
||||
| Orden de suscripción entre consumidores | Baja | Baja | Los eventos solo notifican estado final, no mutaciones secuenciales |
|
||||
| `GameHUD` desincronizado con el estado de capturas/jaque | Baja | Baja | Suscripciones activas desde 2026-08-16; verificación en playtest |
|
||||
|
||||
## Performance Implications
|
||||
|
||||
Eventos C# con 2-5 consumidores: overhead sub-microsegundo por notificación, solo en puntos
|
||||
discretos (movimiento, turno, jaque, mate, tablas). Sin impacto en frame time.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Implementado (Sprints 1-2) + completado (2026-08-16): eventos añadidos y `GameHUD` suscrito.
|
||||
Pendiente: test de integración formal (captura → Purgatorio + HUD reciben el evento).
|
||||
|
||||
**Rollback plan**: volver a llamadas directas solo si el desacople fallara (no previsto).
|
||||
|
||||
## Validation Criteria
|
||||
|
||||
- [x] `GameHUD` reacciona a capturas y a resolución de jaque vía eventos, sin referenciar
|
||||
el estado de `GameManager` directamente.
|
||||
- [x] Todos los suscriptores se desuscriben en `OnDisable`/`OnDestroy`.
|
||||
- [ ] `PurgatoryManager` dispara el flujo de dados únicamente por evento de captura
|
||||
(verificación de integración en playtest).
|
||||
|
||||
## GDD Requirements Addressed
|
||||
|
||||
| GDD Document | System | Requirement | How This ADR Satisfies It |
|
||||
|-------------|--------|-------------|--------------------------|
|
||||
| `design/gdd/systems-index.md` | 16 — Game State Manager | Transiciones de estado notificadas | Eventos de estado emitidos por GameManager |
|
||||
| `design/gdd/systems-index.md` | 18 — HUD | HUD refleja el estado de la partida | Consumo por eventos, sin acoplar GameManager |
|
||||
| `design/gdd/systems-index.md` | 10 — Dice System (Purgatorio) | Reaccionar a capturas | `PurgatoryManager` suscrito a capturas |
|
||||
|
||||
## Related
|
||||
|
||||
- ADR-0000 (estancia 4) — eventos C# canónicos.
|
||||
- Código: `GameManager.cs`, `PurgatoryManager.cs`, `GameHUD.cs`.
|
||||
@@ -0,0 +1,180 @@
|
||||
# ADR-0004: Singleton dual
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Date
|
||||
|
||||
2026-08-16
|
||||
|
||||
## Last Verified
|
||||
|
||||
2026-08-16
|
||||
|
||||
## Decision Makers
|
||||
|
||||
Diseñador/director (usuario) + agente de arquitectura (opencode)
|
||||
|
||||
## Summary
|
||||
|
||||
Managers globales accesibles desde cualquier script. Se adopta una variante dual:
|
||||
`MonoBehaviour` con `DontDestroyOnLoad` para servicios con ciclo de vida de escena
|
||||
(`CampaignManager`, `SaveSystem`, `AudioManager`) y `ScriptableObject` singleton para
|
||||
persistencia + configuración en un solo asset (`DeadKingPool`). La variante MonoBehaviour
|
||||
queda restringida al estilo canónico Awake-explicit de ADR-0000.
|
||||
|
||||
## Engine Compatibility
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Engine** | Unity 6.3 LTS (6000.3.13f1) |
|
||||
| **Domain** | Core / Scripting |
|
||||
| **Knowledge Risk** | HIGH — post-cutoff, verificada contra engine-reference |
|
||||
| **References Consulted** | `VERSION.md`, `breaking-changes.md`, `deprecated-apis.md` |
|
||||
| **Post-Cutoff APIs Used** | None (`DontDestroyOnLoad`, `ScriptableObject` vigentes) |
|
||||
| **Verification Required** | Re-validar si `Resources.Load` migra a Addressables (deprecated-apis) |
|
||||
|
||||
## ADR Dependencies
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Depends On** | ADR-0000 (Accepted) — estancia 1: canónico Awake-explicit para la variante MonoBehaviour; estancia 3: persistencia |
|
||||
| **Enables** | ADR-0005 (ScriptableObject como data container); acceso global a SaveSystem/AudioManager |
|
||||
| **Blocks** | None |
|
||||
| **Ordering Note** | Migración lazy→Awake-explicit de `SaveSystem` y `AudioManager` pendiente (deuda ADR-0000) |
|
||||
|
||||
## Context
|
||||
|
||||
### Problem Statement
|
||||
|
||||
Los managers globales (`CampaignManager`, `SaveSystem`, `AudioManager`, `DeadKingPool`) se
|
||||
acceden desde cualquier script. Sin un patrón único, cada manager eligió un estilo distinto
|
||||
(ver ADR-0000): la variante dual documenta ambas naturalezas y fija el estilo.
|
||||
|
||||
### Current State
|
||||
|
||||
- `MonoBehaviour` + `DontDestroyOnLoad`: `CampaignManager`, `SaveSystem`, `AudioManager`
|
||||
(dos de ellos, `SaveSystem` y `AudioManager`, aún en estilo lazy-getter — por migrar).
|
||||
- `ScriptableObject` singleton: `DeadKingPool` (persistencia + configuración en un asset,
|
||||
cargado con `Resources.Load<DeadKingPool>`).
|
||||
|
||||
### Constraints
|
||||
|
||||
- Variante MonoBehaviour debe cumplir el esqueleto Awake-explicit de ADR-0000.
|
||||
- `ScriptableObject` no tiene ciclo de vida de escena: apto para datos, no para servicios
|
||||
con coroutines/Update.
|
||||
|
||||
### Requirements
|
||||
|
||||
- Acceso global simple y consistente.
|
||||
- `DeadKingPool` accesible como asset desde editor y runtime.
|
||||
|
||||
## Decision
|
||||
|
||||
### Architecture
|
||||
|
||||
```
|
||||
Singleton dual
|
||||
├── MonoBehaviour + DontDestroyOnLoad (servicio con ciclo de vida)
|
||||
│ CampaignManager · SaveSystem · AudioManager
|
||||
│ └── estilo canónico Awake-explicit (ADR-0000)
|
||||
└── ScriptableObject singleton (datos/persistencia en asset)
|
||||
DeadKingPool
|
||||
└── carga centralizada (Resources.Load hoy, Addressables si escala)
|
||||
```
|
||||
|
||||
### Key Interfaces
|
||||
|
||||
```
|
||||
// MonoBehaviour (canónico ADR-0000)
|
||||
public static CampaignManager Instance { get; private set; }
|
||||
private void Awake() { /* guard + DontDestroyOnLoad */ }
|
||||
|
||||
// ScriptableObject singleton
|
||||
public static DeadKingPool Instance { get; }
|
||||
// carga centralizada vía API única (Resources.Load actualmente)
|
||||
```
|
||||
|
||||
### Implementation Guidelines
|
||||
|
||||
- Variante MonoBehaviour: esqueleto Awake-explicit con guard de duplicado y
|
||||
`DontDestroyOnLoad` (ADR-0000).
|
||||
- Variante ScriptableObject: carga centralizada en una API única para poder migrar a
|
||||
Addressables sin tocar consumidores.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Alternative 1: Solo MonoBehaviour
|
||||
|
||||
- **Description**: `DeadKingPool` como MonoBehaviour en una escena inicial.
|
||||
- **Pros**: un solo patrón.
|
||||
- **Cons**: la configuración del pool no es un asset editable en el editor.
|
||||
- **Estimated Effort**: menor.
|
||||
- **Rejection Reason**: pierde la edición de datos en editor de `ScriptableObject`.
|
||||
|
||||
### Alternative 2: Solo ScriptableObject
|
||||
|
||||
- **Description**: todos los managers como ScriptableObject.
|
||||
- **Pros**: todo data-driven.
|
||||
- **Cons**: sin ciclo de vida de escena (Update, coroutines, co-existence con escena).
|
||||
- **Estimated Effort**: menor.
|
||||
- **Rejection Reason**: los managers necesitan ciclo de vida de MonoBehaviour.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Acceso global simple.
|
||||
- `DeadKingPool`: persistencia + configuración en un asset (editable en editor).
|
||||
|
||||
### Negative
|
||||
|
||||
- Acoplamiento implícito del patrón singleton, aceptado para el alcance del proyecto.
|
||||
- Deuda de migración: `SaveSystem`/`AudioManager` aún en lazy-getter (ADR-0000).
|
||||
- `Resources.Load` deprecado en Unity 6 (aceptado para el VS; API centralizada para migrar).
|
||||
|
||||
### Neutral
|
||||
|
||||
- Dos variantes de singleton conviven con reglas claras (qué es MonoBehaviour y qué es SO).
|
||||
|
||||
## Risks
|
||||
|
||||
| Risk | Probability | Impact | Mitigation |
|
||||
|------|------------|--------|-----------|
|
||||
| `Resources.Load` escala mal con contenido | Media | Media | Carga centralizada; migración a Addressables planificada |
|
||||
| Duplicados de `DeadKingPool` (asset) por mala configuración | Baja | Baja | Singleton por asset con guard |
|
||||
| Migración lazy→Awake-explicit de SaveSystem/AudioManager | Media | Media | Smoke test del VS tras migrar |
|
||||
|
||||
## Performance Implications
|
||||
|
||||
`Resources.Load` en el primer acceso de `DeadKingPool`: coste único de carga; cacheado en el
|
||||
singleton. Sin impacto en frame time posterior.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. Migrar `SaveSystem` y `AudioManager` a estilo Awake-explicit (deuda ADR-0000).
|
||||
2. Mantener `DeadKingPool` como SO singleton; centralizar la carga.
|
||||
3. (Futuro) Migrar `Resources.Load` a Addressables si el contenido escala.
|
||||
|
||||
**Rollback plan**: migraciones reversibles por git; el ADR se marca Superseded si un cambio
|
||||
de motor invalida las APIs.
|
||||
|
||||
## Validation Criteria
|
||||
|
||||
- [ ] `SaveSystem` y `AudioManager` usan el esqueleto Awake-explicit.
|
||||
- [ ] `DeadKingPool.Instance` es accesible desde editor y runtime sin duplicados.
|
||||
- [ ] Consumidores de `DeadKingPool` no llaman `Resources.Load` directamente (API central).
|
||||
|
||||
## GDD Requirements Addressed
|
||||
|
||||
| GDD Document | System | Requirement | How This ADR Satisfies It |
|
||||
|-------------|--------|-------------|--------------------------|
|
||||
| `design/gdd/systems-index.md` | 14 — Dead Kings System | Pool de Reyes Muertos persistente | `DeadKingPool` SO singleton con persistencia + configuración |
|
||||
| `design/gdd/systems-index.md` | 17 — Save/Load | Persistencia de campaña | `SaveSystem` global accesible desde cualquier sistema |
|
||||
|
||||
## Related
|
||||
|
||||
- ADR-0000 (estancias 1, 3 y 5) — canónico, persistencia, patrón prohibido de manager duplicado.
|
||||
- ADR-0005 — ScriptableObjects como data container.
|
||||
- Código: `CampaignManager.cs`, `SaveSystem.cs`, `AudioManager.cs`, `DeadKingPool.cs`.
|
||||
@@ -0,0 +1,172 @@
|
||||
# ADR-0005: ScriptableObject como data container
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Date
|
||||
|
||||
2026-08-16
|
||||
|
||||
## Last Verified
|
||||
|
||||
2026-08-16
|
||||
|
||||
## Decision Makers
|
||||
|
||||
Diseñador/director (usuario) + agente de arquitectura (opencode)
|
||||
|
||||
## Summary
|
||||
|
||||
Los datos de diseño (identidades de 16-20 piezas, tablas de posición de IA, balance) deben
|
||||
editarse sin recompilar. Se usan `PieceIdentity`, `AIPositionTables`, `BalanceConfig` y
|
||||
`CampaignConfig` como ScriptableObjects; `CampaignState` y `DeadKingPool` se cargan desde
|
||||
`Resources/`.
|
||||
|
||||
## Engine Compatibility
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Engine** | Unity 6.3 LTS (6000.3.13f1) |
|
||||
| **Domain** | Core / Scripting |
|
||||
| **Knowledge Risk** | HIGH — post-cutoff, verificada contra engine-reference |
|
||||
| **References Consulted** | `VERSION.md`, `breaking-changes.md`, `deprecated-apis.md` |
|
||||
| **Post-Cutoff APIs Used** | None (`ScriptableObject`, `Resources` vigentes; `Resources` desaconsejado en favor de Addressables) |
|
||||
| **Verification Required** | Re-validar migración `Resources.Load` → Addressables si el contenido escala |
|
||||
|
||||
## ADR Dependencies
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Depends On** | ADR-0000 (Accepted) — estancia 3: SO de estado runtime como fuente de verdad; ADR-0004 (Proposed) |
|
||||
| **Enables** | Data-driven sin recompilar; editor tools de balance |
|
||||
| **Blocks** | None |
|
||||
| **Ordering Note** | La serialización de `CampaignState` requiere capa JSON propia (limitación `JsonUtility` con objetos anidados) |
|
||||
|
||||
## Context
|
||||
|
||||
### Problem Statement
|
||||
|
||||
Datos de diseño de alta iteración (identidad de piezas, tablas de IA, balance, configuración
|
||||
de campaña) no deben requerir recompilación ni edición de código.
|
||||
|
||||
### Current State
|
||||
|
||||
- `PieceIdentity`, `AIPositionTables`, `BalanceConfig`, `CampaignConfig`: ScriptableObjects.
|
||||
- `CampaignState` y `DeadKingPool`: cargados desde `Resources/`.
|
||||
- `BalanceConfig.LoadFromJSON()` es placeholder (valores default) — TODO pendiente.
|
||||
|
||||
### Constraints
|
||||
|
||||
- Editable en el inspector de Unity sin recompilar.
|
||||
- La serialización de `CampaignState` (objetos anidados) excede a `JsonUtility`.
|
||||
|
||||
### Requirements
|
||||
|
||||
- Data-driven: los datos se editan en assets, no en código.
|
||||
- Carga centralizada y reutilizable.
|
||||
|
||||
## Decision
|
||||
|
||||
### Architecture
|
||||
|
||||
```
|
||||
ScriptableObjects (data containers)
|
||||
├── PieceIdentity (identidad de 16-20 piezas)
|
||||
├── AIPositionTables (tablas de posición IA)
|
||||
├── BalanceConfig (balance, con LoadFromJSON pendiente)
|
||||
├── CampaignConfig (configuración de campaña)
|
||||
└── CampaignState / DeadKingPool → cargados desde Resources/
|
||||
```
|
||||
|
||||
### Key Interfaces
|
||||
|
||||
```
|
||||
// SOs como assets editables en el inspector
|
||||
[CreateAssetMenu(menuName = "Ajedrez Purgatorio/...")]
|
||||
public class PieceIdentity : ScriptableObject { ... }
|
||||
|
||||
// Carga centralizada (ADR-0004)
|
||||
Resources.Load<DeadKingPool>("DeadKingPool");
|
||||
```
|
||||
|
||||
### Implementation Guidelines
|
||||
|
||||
- Los SO de datos no deben contener lógica de negocio (solo datos); la lógica vive en la
|
||||
capa de servicios POCO (ADR-0001).
|
||||
- Serialización de `CampaignState`: wrapper `[Serializable]` propio + `JsonUtility`
|
||||
(limitación de objetos anidados documentada en ADR-0000).
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Alternative 1: JSON externo puro
|
||||
|
||||
- **Description**: todos los datos en ficheros JSON cargados en runtime.
|
||||
- **Pros**: editable fuera de Unity.
|
||||
- **Cons**: sin edición visual ni validación en inspector; más infraestructura.
|
||||
- **Estimated Effort**: mayor.
|
||||
- **Rejection Reason**: perder la edición en inspector que Unity ofrece gratis.
|
||||
|
||||
### Alternative 2: Datos hardcodeados en C#
|
||||
|
||||
- **Description**: datos en constantes/clases estáticas.
|
||||
- **Pros**: sin assets.
|
||||
- **Cons**: requiere recompilar para cualquier ajuste; mezcla datos con código.
|
||||
- **Estimated Effort**: menor.
|
||||
- **Rejection Reason**: iteración de diseño lenta; contraviene el data-driven.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Data-driven y editable en editor.
|
||||
- Iteración de balance/identidad sin recompilar.
|
||||
|
||||
### Negative
|
||||
|
||||
- La serialización de `CampaignState` requiere capa JSON propia (objetos anidados).
|
||||
- `BalanceConfig.LoadFromJSON()` sigue siendo placeholder.
|
||||
|
||||
### Neutral
|
||||
|
||||
- Dos formas de carga: SOs referenciados en inspector + SOs desde `Resources/`.
|
||||
|
||||
## Risks
|
||||
|
||||
| Risk | Probability | Impact | Mitigation |
|
||||
|------|------------|--------|-----------|
|
||||
| SOs con lógica de negocio (mal uso del patrón) | Media | Media | Regla de revisión: SO = datos; lógica en servicios POCO |
|
||||
| `Resources/` con muchos assets ralentiza build | Media | Media | Mantener solo SOs de estado runtime; migrar a Addressables si escala |
|
||||
| `BalanceConfig.LoadFromJSON()` placeholder desincronizado | Media | Media | Completar la carga real antes de balance final |
|
||||
|
||||
## Performance Implications
|
||||
|
||||
SOs cargados una vez y referenciados; `Resources.Load` con cache por singleton. Sin impacto
|
||||
en frame time.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Implementado (Sprints 1-2). Pendiente: completar `BalanceConfig.LoadFromJSON()` y mantener
|
||||
la carga centralizada para la eventual migración a Addressables.
|
||||
|
||||
**Rollback plan**: los SOs son assets; revertir configuración es revertir assets por git.
|
||||
|
||||
## Validation Criteria
|
||||
|
||||
- [ ] Piezas, tablas de IA y balance se ajustan en el inspector sin recompilar.
|
||||
- [ ] `CampaignState` se serializa a JSON (capa propia) y se restaura correctamente.
|
||||
- [ ] `BalanceConfig` carga desde JSON real (sin placeholder) cuando el diseño lo requiera.
|
||||
|
||||
## GDD Requirements Addressed
|
||||
|
||||
| GDD Document | System | Requirement | How This ADR Satisfies It |
|
||||
|-------------|--------|-------------|--------------------------|
|
||||
| `design/gdd/systems-index.md` | 9 — Piece Identity System | Identidades de 16-20 piezas | `PieceIdentity` como SO editable |
|
||||
| `design/gdd/systems-index.md` | 11 — Campaign System | Configuración y estado de campaña | `CampaignConfig`/`CampaignState` como SO |
|
||||
| `design/gdd/systems-index.md` | 14 — Dead Kings System | Pool de Reyes Muertos | `DeadKingPool` como SO en Resources |
|
||||
|
||||
## Related
|
||||
|
||||
- ADR-0000 (estancia 3) — SO runtime + JsonUtility.
|
||||
- ADR-0004 — singleton dual (DeadKingPool como SO).
|
||||
- Código: `Assets/Game/Scripts/Data/`, `Assets/Game/Scripts/Mono/Data/`, `ScriptableObjects/`.
|
||||
@@ -0,0 +1,162 @@
|
||||
# ADR-0006: Overlay UI en lugar de escenas separadas
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Date
|
||||
|
||||
2026-08-16
|
||||
|
||||
## Last Verified
|
||||
|
||||
2026-08-16
|
||||
|
||||
## Decision Makers
|
||||
|
||||
Diseñador/director (usuario) + agente de arquitectura (opencode)
|
||||
|
||||
## Summary
|
||||
|
||||
El flujo del Purgatorio (oferta → dados → resultado) debe interrumpir la partida sin perder
|
||||
el contexto del tablero. Se implementa como Canvas overlay sobre la escena de juego en lugar
|
||||
de escenas separadas, requiriendo pausar/resumir el gameplay (ADR-0007).
|
||||
|
||||
## Engine Compatibility
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Engine** | Unity 6.3 LTS (6000.3.13f1) |
|
||||
| **Domain** | UI |
|
||||
| **Knowledge Risk** | HIGH — post-cutoff, verificada contra engine-reference |
|
||||
| **References Consulted** | `VERSION.md`, `modules/ui.md`, `deprecated-apis.md` |
|
||||
| **Post-Cutoff APIs Used** | None (UGUI + Canvas, deprecado pero soportado; TextMeshPro) |
|
||||
| **Verification Required** | Re-validar si se migra de UGUI a UI Toolkit |
|
||||
|
||||
## ADR Dependencies
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Depends On** | ADR-0000 (Accepted); ADR-0007 (Proposed) — pausa con `Time.timeScale` |
|
||||
| **Enables** | Flujo de dados sin perder contexto del tablero; UX del VS |
|
||||
| **Blocks** | None |
|
||||
| **Ordering Note** | Requiere pausar/resumir gameplay (ADR-0007) |
|
||||
|
||||
## Context
|
||||
|
||||
### Problem Statement
|
||||
|
||||
El Purgatorio interrumpe la partida al capturar una pieza. Cargar una escena separada
|
||||
destruiría el tablero y el estado de la partida (o exigiría serializarlo), degradando la UX.
|
||||
|
||||
### Current State
|
||||
|
||||
El flujo (PurgatoryOfferUI → DiceRollUI → DiceResultUI) está previsto como Canvas overlay;
|
||||
`PurgatoryManager` + `DiceSystem` cableados en `Chapter1.unity` con las 3 UIs asignadas.
|
||||
|
||||
### Constraints
|
||||
|
||||
- El tablero y su estado deben permanecer intactos durante el flujo.
|
||||
- El gameplay debe quedar pausado durante oferta/dados/resultado.
|
||||
|
||||
### Requirements
|
||||
|
||||
- Interrupción del tablero sin pérdida de contexto.
|
||||
- Transiciones suaves oferta → dados → resultado.
|
||||
|
||||
## Decision
|
||||
|
||||
### Architecture
|
||||
|
||||
```
|
||||
Chapter1.unity
|
||||
└── Canvas (overlay)
|
||||
├── PurgatoryOfferUI ──► DiceRollUI ──► DiceResultUI
|
||||
└── (superpuesto sobre la escena de juego, sin descargarla)
|
||||
```
|
||||
|
||||
### Key Interfaces
|
||||
|
||||
```
|
||||
// Overlay controlado por PurgatoryManager
|
||||
PurgatoryManager.ShowOffer(); // pausa gameplay (ADR-0007)
|
||||
PurgatoryManager.ShowResult(); // resume gameplay (ADR-0007)
|
||||
```
|
||||
|
||||
### Implementation Guidelines
|
||||
|
||||
- Overlays dentro de la misma escena; nunca `LoadScene` durante el flujo.
|
||||
- Pausa/resume vía `Time.timeScale` (ADR-0007), no desactivando el tablero.
|
||||
- El estado del tablero queda en memoria (no se serializa ni descarga).
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Alternative 1: Escenas separadas por paso del Purgatorio
|
||||
|
||||
- **Description**: cada pantalla (oferta, dados, resultado) es una escena.
|
||||
- **Pros**: separación por escena.
|
||||
- **Cons**: destruye el tablero, exige serializar estado, carga pesada, rompe la continuidad.
|
||||
- **Estimated Effort**: mayor.
|
||||
- **Rejection Reason**: pierde el contexto del tablero y degrada la UX.
|
||||
|
||||
### Alternative 2: Overlay único que conmuta paneles
|
||||
|
||||
- **Description**: un solo Canvas con todos los pasos visibles de forma condicional
|
||||
(equivalente a lo adoptado, con variantes de implementación).
|
||||
- **Pros**: igual que el elegido.
|
||||
- **Cons**: paneles ocultos en memoria (mínimo).
|
||||
- **Estimated Effort**: igual.
|
||||
- **Rejection Reason**: no aplica — es la misma decisión.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- El tablero y el estado permanecen intactos.
|
||||
- Menor overhead de carga; UX continua.
|
||||
- La pausa por `Time.timeScale` detiene física/animaciones automáticamente.
|
||||
|
||||
### Negative
|
||||
|
||||
- Los pasos del Purgatorio no se pueden cargar/testear de forma aislada como escenas.
|
||||
- La escena de juego crece con paneles overlay.
|
||||
|
||||
### Neutral
|
||||
|
||||
- El flujo depende de `PurgatoryManager` para orquestar la secuencia.
|
||||
|
||||
## Risks
|
||||
|
||||
| Risk | Probability | Impact | Mitigation |
|
||||
|------|------------|--------|-----------|
|
||||
| La pausa por `Time.timeScale` deja coroutines vivas (WaitForSeconds congeladas) | Media | Media | ADR-0007: usar `WaitForSecondsRealtime` en overlays |
|
||||
| El jugador interactúa con el tablero durante la oferta | Baja | Media | Bloquear input del tablero mientras el overlay está activo |
|
||||
|
||||
## Performance Implications
|
||||
|
||||
Overlays en memoria durante todo el tablero: coste fijo de Canvas (SetActive inactive);
|
||||
sin cambio de carga de escena. Despreciable en el presupuesto.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Implementado (Sprint 3). Sin migración pendiente; validar bloqueo de input durante el overlay.
|
||||
|
||||
**Rollback plan**: N/A (implementación ya activa; revertir por git si fallara la UX).
|
||||
|
||||
## Validation Criteria
|
||||
|
||||
- [ ] Al capturar una pieza, la partida se pausa y el overlay aparece sin descargar el tablero.
|
||||
- [ ] La secuencia oferta → dados → resultado se completa y el juego se reanuda.
|
||||
- [ ] El input del tablero queda bloqueado mientras el overlay está activo.
|
||||
|
||||
## GDD Requirements Addressed
|
||||
|
||||
| GDD Document | System | Requirement | How This ADR Satisfies It |
|
||||
|-------------|--------|-------------|--------------------------|
|
||||
| `design/gdd/systems-index.md` | 10 — Dice System (Purgatorio) | Mini-juego de dados contra la Muerte al perder una pieza | Flujo overlay que interrumpe sin perder contexto |
|
||||
| `design/gdd/systems-index.md` | 21 — Dice UI | 3 pantallas (Offer, Roll, Result) | Canvas overlay único con 3 pasos |
|
||||
|
||||
## Related
|
||||
|
||||
- ADR-0007 — pausa/resume con `Time.timeScale`.
|
||||
- Código: `Assets/Game/Scripts/Mono/Purgatory/`, `Assets/Game/Scripts/Mono/UI/`.
|
||||
@@ -0,0 +1,167 @@
|
||||
# ADR-0007: Time.timeScale para pausas
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Date
|
||||
|
||||
2026-08-16
|
||||
|
||||
## Last Verified
|
||||
|
||||
2026-08-16
|
||||
|
||||
## Decision Makers
|
||||
|
||||
Diseñador/director (usuario) + agente de arquitectura (opencode)
|
||||
|
||||
## Summary
|
||||
|
||||
La promoción de peón y el Purgatorio necesitan pausar el juego. Se usa `Time.timeScale = 0`
|
||||
para pausar y las coroutines de los overlays usan `WaitForSecondsRealtime` para seguir
|
||||
funcionando durante la pausa.
|
||||
|
||||
## Engine Compatibility
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Engine** | Unity 6.3 LTS (6000.3.13f1) |
|
||||
| **Domain** | Core / Scripting |
|
||||
| **Knowledge Risk** | HIGH — post-cutoff, verificada contra engine-reference |
|
||||
| **References Consulted** | `VERSION.md`, `breaking-changes.md`, `deprecated-apis.md` |
|
||||
| **Post-Cutoff APIs Used** | None (`Time.timeScale`, `WaitForSecondsRealtime` vigentes) |
|
||||
| **Verification Required** | Re-validar con el sistema de pausa del New Input System si se migra |
|
||||
|
||||
## ADR Dependencies
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Depends On** | ADR-0000 (Accepted) |
|
||||
| **Enables** | ADR-0006 (overlay del Purgatorio); pausa de promoción |
|
||||
| **Blocks** | None |
|
||||
| **Ordering Note** | Afecta a cualquier coroutine existente con `WaitForSeconds` (riesgo) |
|
||||
|
||||
## Context
|
||||
|
||||
### Problem Statement
|
||||
|
||||
La promoción de peón (elección de pieza) y el Purgatorio (oferta/dados/resultado) deben
|
||||
pausar la partida. Sin pausa, la IA, los timers o la interacción seguirían activos.
|
||||
|
||||
### Current State
|
||||
|
||||
`Time.timeScale = 0` en `PurgatoryManager` (línea 85), `PauseMenuController` (94),
|
||||
`PromotionUI` (53); `Time.timeScale = 1` para reanudar. Coroutines de UI con
|
||||
`WaitForSecondsRealtime`.
|
||||
|
||||
### Constraints
|
||||
|
||||
- La pausa debe detener física, animaciones y coroutines basadas en `WaitForSeconds`.
|
||||
- La UI (menú de pausa, overlays) debe seguir respondiendo.
|
||||
|
||||
### Requirements
|
||||
|
||||
- Pausar/resumir sin estado global adicional.
|
||||
- Coroutines de UI vivas durante la pausa.
|
||||
|
||||
## Decision
|
||||
|
||||
### Architecture
|
||||
|
||||
```
|
||||
Time.timeScale = 0 (pausa) Time.timeScale = 1 (resume)
|
||||
│ ▲
|
||||
├─ PurgatoryManager (oferta/dados) │
|
||||
├─ PauseMenuController (pausa) │
|
||||
└─ PromotionUI (elección de peón) │
|
||||
└── coroutines de UI: WaitForSecondsRealtime (no se congelan)
|
||||
```
|
||||
|
||||
### Key Interfaces
|
||||
|
||||
```
|
||||
Time.timeScale = 0f; // pausa
|
||||
yield return new WaitForSecondsRealtime(x); // coroutines de UI que siguen vivas
|
||||
Time.timeScale = 1f; // resume
|
||||
```
|
||||
|
||||
### Implementation Guidelines
|
||||
|
||||
- Coroutines de UI durante la pausa: siempre `WaitForSecondsRealtime`, nunca `WaitForSeconds`.
|
||||
- Restaurar `timeScale` en `OnDestroy`/`OnDisable` del controlador de pausa si la escena se
|
||||
descarga pausada.
|
||||
- El resume debe restaurar el valor previo (no asumir siempre 1).
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Alternative 1: Flag `isPaused` manual con Update()
|
||||
|
||||
- **Description**: bool global que frena el Update de los sistemas.
|
||||
- **Pros**: control fino.
|
||||
- **Cons**: no detiene física ni animaciones automáticamente; hay que tocar cada sistema.
|
||||
- **Estimated Effort**: mayor.
|
||||
- **Rejection Reason**: no detiene el motor de forma global; propenso a olvidos.
|
||||
|
||||
### Alternative 2: Desactivar GameObjects de gameplay
|
||||
|
||||
- **Description**: SetActive(false) del tablero/sistemas al pausar.
|
||||
- **Pros**: detiene todo lo desactivado.
|
||||
- **Cons**: pierde estado/UI visible, más invasivo, difícil de orquestar.
|
||||
- **Estimated Effort**: mayor.
|
||||
- **Rejection Reason**: el overlay (ADR-0006) necesita el tablero visible.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Pausa global simple, sin estado adicional.
|
||||
- Detiene física, animaciones y coroutines `WaitForSeconds`.
|
||||
|
||||
### Negative
|
||||
|
||||
- Riesgo: cualquier coroutine con `WaitForSeconds` normal se congela durante la pausa.
|
||||
- `timeScale` es global: pausar un flujo pausa todos los demás.
|
||||
|
||||
### Neutral
|
||||
|
||||
- Pausa y resume se controlan por asignación directa de `Time.timeScale`.
|
||||
|
||||
## Risks
|
||||
|
||||
| Risk | Probability | Impact | Mitigation |
|
||||
|------|------------|--------|-----------|
|
||||
| Coroutines `WaitForSeconds` congeladas durante pausa | Media | Media | Regla: coroutines de UI con `WaitForSecondsRealtime`; grep en code review |
|
||||
| `timeScale` queda en 0 si se descarga la escena pausada | Baja | Media | Restaurar en `OnDestroy`/`OnDisable` |
|
||||
| Resume asume timeScale=1 y sobrescribe un valor previo | Baja | Baja | Guardar y restaurar el valor previo |
|
||||
|
||||
## Performance Implications
|
||||
|
||||
Sin impacto: `Time.timeScale` no introduce coste de procesamiento; solo congela el ciclo de
|
||||
Update/animaciones/física.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Implementado (Sprints 1-3). Pendiente: auditar coroutines con `WaitForSeconds` normal que
|
||||
pudieran congelarse durante pausas (grep + revisión).
|
||||
|
||||
**Rollback plan**: N/A — API vigente; revertir cambios concretos por git.
|
||||
|
||||
## Validation Criteria
|
||||
|
||||
- [ ] Durante la oferta del Purgatorio, la IA y los timers están detenidos (timeScale 0).
|
||||
- [ ] El menú de pausa y los overlays siguen funcionando (WaitForSecondsRealtime).
|
||||
- [ ] Al reanudar, `timeScale` vuelve al valor previo (1 en juego normal).
|
||||
- [ ] No quedan coroutines con `WaitForSeconds` activas durante pausas.
|
||||
|
||||
## GDD Requirements Addressed
|
||||
|
||||
| GDD Document | System | Requirement | How This ADR Satisfies It |
|
||||
|-------------|--------|-------------|--------------------------|
|
||||
| `design/gdd/systems-index.md` | 6 — Pawn Promotion | Elección de pieza pausa la partida | `Time.timeScale = 0` en `PromotionUI` |
|
||||
| `design/gdd/systems-index.md` | 10 — Dice System (Purgatorio) | Pausar durante oferta/dados | `Time.timeScale = 0` en `PurgatoryManager` |
|
||||
|
||||
## Related
|
||||
|
||||
- ADR-0006 — overlays que requieren la pausa.
|
||||
- Código: `PurgatoryManager.cs`, `PauseMenuController.cs`, `PromotionUI.cs`.
|
||||
@@ -0,0 +1,165 @@
|
||||
# ADR-0008: Position hashing FEN-like
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Date
|
||||
|
||||
2026-08-16
|
||||
|
||||
## Last Verified
|
||||
|
||||
2026-08-16
|
||||
|
||||
## Decision Makers
|
||||
|
||||
Diseñador/director (usuario) + agente de arquitectura (opencode)
|
||||
|
||||
## Summary
|
||||
|
||||
Para la detección de repetición triple se usa un hash de posición tipo FEN-string en lugar de
|
||||
Zobrist hashing. El hash se genera desde el estado del tablero y la repetición se rastrea en
|
||||
`DrawDetector` (capa POCO, ADR-0001).
|
||||
|
||||
## Engine Compatibility
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Engine** | Unity 6.3 LTS (6000.3.13f1) |
|
||||
| **Domain** | Core / Scripting |
|
||||
| **Knowledge Risk** | HIGH — post-cutoff, verificada contra engine-reference |
|
||||
| **References Consulted** | `VERSION.md`, `breaking-changes.md`, `deprecated-apis.md` |
|
||||
| **Post-Cutoff APIs Used** | None (lógica pura C#) |
|
||||
| **Verification Required** | None |
|
||||
|
||||
## ADR Dependencies
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Depends On** | ADR-0000 (Accepted); ADR-0001 (Proposed) — `DrawDetector` como servicio POCO |
|
||||
| **Enables** | Detección de tablas por triple repetición (sistema 8) |
|
||||
| **Blocks** | None |
|
||||
| **Ordering Note** | None |
|
||||
|
||||
## Context
|
||||
|
||||
### Problem Statement
|
||||
|
||||
La detección de tablas requiere reconocer cuando la misma posición ocurre 3 veces. Sin un
|
||||
hash canónico de posición, comparar tableros completos es lento y propenso a error.
|
||||
|
||||
### Current State
|
||||
|
||||
Hash de posición tipo FEN-string implementado en `DrawDetector`. El FEN codifica: piezas por
|
||||
fila, turno, derechos de enroque, en passant y contadores.
|
||||
|
||||
### Constraints
|
||||
|
||||
- El hash debe capturar los elementos que definen una posición legal (no solo las piezas).
|
||||
- Suficiente para el volumen de partidas del juego (partidas de 30-60 min, no engine de alto
|
||||
rendimiento).
|
||||
|
||||
### Requirements
|
||||
|
||||
- Identificar posiciones idénticas para repetición triple.
|
||||
- Coste de computación aceptable por movimiento.
|
||||
|
||||
## Decision
|
||||
|
||||
### Architecture
|
||||
|
||||
```
|
||||
DrawDetector (POCO, ADR-0001)
|
||||
│
|
||||
└─ FENHash(board) → string canónico (piezas + turno + enroque + en passant)
|
||||
└─ Diccionario<FENHash, int> (conteo por posición)
|
||||
└─ ≥3 → repetición triple (tablas)
|
||||
```
|
||||
|
||||
### Key Interfaces
|
||||
|
||||
```
|
||||
public static string FENHash(BoardState board); // hash canónico FEN-like
|
||||
// DrawDetector mantiene un conteo por hash para repetición triple
|
||||
```
|
||||
|
||||
### Implementation Guidelines
|
||||
|
||||
- El hash FEN debe incluir: disposición de piezas, turno, derechos de enroque, objetivo en
|
||||
passant (no los contadores de 50-mov, que se rastrean aparte).
|
||||
- Generar el hash solo al mover (no por frame).
|
||||
- La comparación de posiciones se hace por igualdad de string (cultura invariante).
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Alternative 1: Zobrist hashing
|
||||
|
||||
- **Description**: tabla de números aleatorios por pieza/casilla, XOR combinado.
|
||||
- **Pros**: O(1) incremental al mover; estándar en engines.
|
||||
- **Cons**: colisiones teóricas (mitigadas con verificación); overkill para el volumen del juego.
|
||||
- **Estimated Effort**: mayor.
|
||||
- **Rejection Reason**: el volumen de partidas no justifica la complejidad; el FEN-string es
|
||||
suficiente y más simple de depurar.
|
||||
|
||||
### Alternative 2: Comparación de tableros completos
|
||||
|
||||
- **Description**: comparar el array del tablero en cada movimiento contra el historial.
|
||||
- **Pros**: sin hashing.
|
||||
- **Cons**: O(n) por comparación, n piezas; histórico creciente.
|
||||
- **Estimated Effort**: menor.
|
||||
- **Rejection Reason**: lento y verboso frente al hash canónico.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Simplicidad y depurabilidad (el FEN es legible por humanos).
|
||||
- Suficiente para el volumen de partidas del juego.
|
||||
|
||||
### Negative
|
||||
|
||||
- No incremental: regenerar el string completo por movimiento (O(n) con n piezas, coste
|
||||
bajo para 32 piezas).
|
||||
- No óptimo para rendimiento extremo (irrelevante aquí).
|
||||
|
||||
### Neutral
|
||||
|
||||
- La repetición se rastrea en `DrawDetector` con conteo por hash.
|
||||
|
||||
## Risks
|
||||
|
||||
| Risk | Probability | Impact | Mitigation |
|
||||
|------|------------|--------|-----------|
|
||||
| Colisión entre dos posiciones distintas con el mismo FEN | Muy baja | Baja | FEN canónico incluye turno/enroque/en passant; colisión requeriría estados idénticos |
|
||||
| FEN sin normalizar (espacios/orden) produce falsos negativos | Baja | Media | Normalizar la generación (mismo orden de filas, formato único) y testearla |
|
||||
|
||||
## Performance Implications
|
||||
|
||||
Regenerar el FEN por movimiento: O(64) operaciones de cadena por movimiento — despreciable en
|
||||
partidas de 30-60 min (~40-80 movimientos/partida). Sin impacto en frame time.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Implementado (Sprint 2) en `DrawDetector`. Sin migración pendiente; añadir test de
|
||||
normalización del hash si no existe.
|
||||
|
||||
**Rollback plan**: N/A — servicio POCO; revertir por git.
|
||||
|
||||
## Validation Criteria
|
||||
|
||||
- [ ] La repetición triple se detecta con las tablas (posiciones idénticas 3 veces).
|
||||
- [ ] Posiciones con mismas piezas pero distinto turno/enroque/en-passant NO cuentan como
|
||||
repetición.
|
||||
- [ ] El hash FEN es estable entre llamadas (normalizado).
|
||||
|
||||
## GDD Requirements Addressed
|
||||
|
||||
| GDD Document | System | Requirement | How This ADR Satisfies It |
|
||||
|-------------|--------|-------------|--------------------------|
|
||||
| `design/gdd/systems-index.md` | 8 — Draw Detection | Detección de tablas (incluye repetición triple) | FEN-hash + conteo en `DrawDetector` |
|
||||
|
||||
## Related
|
||||
|
||||
- ADR-0001 — `DrawDetector` como servicio POCO.
|
||||
- Código: `Assets/Game/Scripts/Mono/Core/Services/DrawDetector.cs`.
|
||||
@@ -0,0 +1,199 @@
|
||||
# ADR-0009: Consolidación del SceneTransitionManager
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Date
|
||||
|
||||
2026-08-16
|
||||
|
||||
## Last Verified
|
||||
|
||||
2026-08-16
|
||||
|
||||
## Decision Makers
|
||||
|
||||
Productor técnico, agentes de arquitectura e infraestructura (Claude Code Game Studios).
|
||||
|
||||
## Summary
|
||||
|
||||
Existen dos implementaciones de `SceneTransitionManager` con la misma responsabilidad (transiciones de escena con fade): una en `Core/` (estilo Awake-explicit, API `LoadScene`) y otra en `UI/` (lazy-getter, API `TransitionToScene`/`FadeOut`/`FadeIn`, sin consumidores en runtime). Se decide consolidar en una única clase: la versión `Core/` como canónica, eliminando la duplicada de `UI/` y actualizando la escena `Chapter1.unity` y el editor `ProjectSetup.cs`.
|
||||
|
||||
## Engine Compatibility
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Engine** | Unity 6 (6000.3.13f1 — Unity 6.3 LTS) |
|
||||
| **Domain** | Core / UI / Scripting |
|
||||
| **Knowledge Risk** | HIGH — post-cutoff, verificado contra la documentación del motor |
|
||||
| **References Consulted** | `docs/engine-reference/unity/VERSION.md`, `docs/engine-reference/unity/urp/deprecated-apis.md` |
|
||||
| **Post-Cutoff APIs Used** | Ninguna nueva. `SceneManager.LoadSceneAsync`, `DontDestroyOnLoad`, `CanvasGroup`, `AnimationCurve` ya usados en la versión Core. |
|
||||
| **Verification Required** | Transición MainMenu → Chapter1 con fade en runtime; guard de duplicado ante re-carga de escena. |
|
||||
|
||||
> **Note**: Knowledge Risk HIGH — revalidar si el proyecto actualiza de versión de motor.
|
||||
|
||||
## ADR Dependencies
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Depends On** | ADR-0000 (estancia 1: singleton Awake-explicit; estancia 5: un-manager-por-responsabilidad), ADR-0004 (estilo singleton canónico) |
|
||||
| **Enables** | Epics de infraestructura; eliminación de la deuda registrada en `architecture.yaml` (`duplicate_manager_per_responsibility`) |
|
||||
| **Blocks** | Ninguno |
|
||||
| **Ordering Note** | Debe implementarse junto con el borrado del duplicado de `UI/`; `ProjectSetup.cs` deja de compilar si se elimina la clase antes de actualizar sus referencias. |
|
||||
|
||||
## Context
|
||||
|
||||
### Problem Statement
|
||||
|
||||
La misma responsabilidad —transiciones entre escenas con fade— está implementada dos veces:
|
||||
|
||||
- `Assets/Game/Scripts/Mono/Core/SceneTransitionManager.cs` — clase global, singleton Awake-explicit (`Instance` asignado en `Awake` + `DontDestroyOnLoad` + guard de duplicado), API `LoadScene(string, Action)`, auto-crea `FadeCanvas`/`FadeImage` en runtime si no están referenciados. Usada por `GameStateManager` y presente en `MainMenu.unity` (cableada con `FadeCanvas` + `CanvasGroup` + `FadeImage`).
|
||||
- `Assets/Game/Scripts/Mono/UI/SceneTransitionManager.cs` — clase en namespace `AjedrezPurgatorio.UI`, singleton lazy-getter (auto-creación de GameObject en el getter, patrón prohibido por ADR-0000), API `TransitionToScene`/`FadeOut`/`FadeIn`, crea su propio canvas con `sortingOrder` 9999. Sin ningún consumidor de su API en código de runtime.
|
||||
|
||||
El duplicado no solo incumple la estancia 5 de ADR-0000, sino que provoca desincronización: `FixErrors.cs` busca la clase global y no la encuentra en `Chapter1` (que instancia la versión `UI/`), por lo que la corrección de fade falla silenciosamente en esa escena. Además ambas pueden coexistir en escena → dobles transiciones y guard de duplicado con comportamiento distinto.
|
||||
|
||||
### Current State
|
||||
|
||||
| Escena | Implementación | Fade |
|
||||
|--------|---------------|------|
|
||||
| `MainMenu.unity` | `Core/SceneTransitionManager.cs` (guid `e1e89932918bf4f45b1b8b65506d6dd9`) | ✅ `FadeCanvas` + `CanvasGroup` + `FadeImage` cableados |
|
||||
| `Chapter1.unity` | `AjedrezPurgatorio.UI.SceneTransitionManager` (guid `42a7f6937327de9469fc73fc1f0cae64`) | ⚠️ sin referencias de fade, API sin consumidores |
|
||||
|
||||
### Constraints
|
||||
|
||||
- `GameStateManager.cs:73` invoca `SceneTransitionManager.Instance.LoadScene(sceneName)` → la API canónica debe conservar `LoadScene`.
|
||||
- `FixErrors.cs` y `ProjectSetup.cs` referencian las clases por tipo; `ProjectSetup.cs` usa la forma completamente cualificada `AjedrezPurgatorio.UI.SceneTransitionManager` y dejaría de compilar al eliminar la clase → debe actualizarse en el mismo changeset.
|
||||
- `Chapter1.unity` referencia el MonoBehaviour por GUID de script → debe actualizarse el GUID en el mismo changeset.
|
||||
- El fade es obligatorio en todas las transiciones (GDD S1 Scene Management) → la versión canónica debe auto-corregirse si falta el FadeCanvas.
|
||||
|
||||
### Requirements
|
||||
|
||||
- Un único `SceneTransitionManager` como dueño de las transiciones entre escenas.
|
||||
- La clase canónica debe seguir el estilo Awake-explicit de ADR-0000/0004.
|
||||
- La API pública debe cubrir al menos `LoadScene(string, Action)` (consumidor actual: `GameStateManager`).
|
||||
- Transición de `Chapter1.unity` sin regenerar la escena ni perder los GameObjects existentes.
|
||||
- El editor (`ProjectSetup.cs`, `FixErrors.cs`) debe apuntar a la clase canónica.
|
||||
|
||||
## Decision
|
||||
|
||||
Se adopta como única implementación la versión `Core/SceneTransitionManager.cs` (clase global, singleton Awake-explicit, auto-creación del fade). Se elimina `Assets/Game/Scripts/Mono/UI/SceneTransitionManager.cs` y su `.meta`. La API `TransitionToScene`/`FadeOut`/`FadeIn` de la versión eliminada no se re-implementa: no tiene consumidores en runtime y `FadeOut`/`FadeIn` están cubiertos internamente por la corrutina de transición.
|
||||
|
||||
### Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────┐
|
||||
GameStateManager │ SceneTransitionManager │ (clase global única)
|
||||
── LoadScene ──► │ ┌───────────────────────┐ │
|
||||
│ │ Awake: Instance = this │ │── DontDestroyOnLoad
|
||||
│ │ guard duplicado │ │
|
||||
│ └───────────────────────┘ │
|
||||
│ LoadScene(name, onComplete)│──► SceneManager.LoadSceneAsync
|
||||
│ FadeRoutine(α 0→1→0) │──► CanvasGroup / FadeImage
|
||||
└─────────────────────────────┘
|
||||
│ auto-crea
|
||||
▼
|
||||
FadeCanvas + CanvasGroup + FadeImage
|
||||
(hijos del mismo GameObject, sortingOrder 999)
|
||||
```
|
||||
|
||||
### Key Interfaces
|
||||
|
||||
```csharp
|
||||
public static SceneTransitionManager Instance { get; private set; } // Awake-explicit
|
||||
|
||||
public void LoadScene(string sceneName, Action onComplete = null)
|
||||
// Guard: rechaza si _isTransitioning.
|
||||
// Fade out (α 0→1) → LoadSceneAsync → pausa 0.1s → fade in (α 1→0) → onComplete.
|
||||
```
|
||||
|
||||
### Implementation Guidelines
|
||||
|
||||
- **No se modifica** la clase `Core/SceneTransitionManager.cs` salvo lo ya aprobado; es la fuente canónica.
|
||||
- En `Chapter1.unity`, el MonoBehaviour existente (fileID `365129010`, GameObject `365129009`) se re-apunta al script de Core (guid `e1e89932918bf4f45b1b8b65506d6dd9`) con `_fadeDuration: 0.8` y las curvas EaseInOut serializadas; `_fadeCanvasGroup` y `_fadeImage` quedan en null (la clase auto-crea el `FadeCanvas` en `InitializeFadeCanvas`).
|
||||
- En `ProjectSetup.cs`, sustituir las 3 referencias `AjedrezPurgatorio.UI.SceneTransitionManager` por la clase global `SceneTransitionManager`.
|
||||
- Eliminar `Assets/Game/Scripts/Mono/UI/SceneTransitionManager.cs` **y** su `.meta` (guid `42a7f6937327de9469fc73fc1f0cae64`) para no dejar huérfano el GUID en el proyecto.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
### Alternative 1: Mantener la versión `UI/` y eliminar la de `Core/`
|
||||
|
||||
- **Description**: Invertir el sentido: mantener la clase namespaced con su API `TransitionToScene`/`FadeOut`/`FadeIn` y migrar `GameStateManager`/`MainMenu` a ella.
|
||||
- **Pros**: Su API separa fade de carga; `sortingOrder` superior.
|
||||
- **Cons**: Usa el lazy-getter prohibido por ADR-0000 (requeriría reescribirla igualmente); `MainMenu.unity` y `FixErrors.cs` ya dependen de la clase global; su API no tiene consumidores. Más migración con menos beneficio.
|
||||
- **Estimated Effort**: Mayor que la decisión elegida.
|
||||
- **Rejection Reason**: Contradice ADR-0000 y supone re-escribir + migrar más código para un API sin uso.
|
||||
|
||||
### Alternative 2: Fusionar ambas en una tercera clase nueva
|
||||
|
||||
- **Description**: Crear una clase nueva que combine la API `LoadScene` + `FadeOut`/`FadeIn`.
|
||||
- **Pros**: API completa en un solo lugar.
|
||||
- **Cons**: Nuevo GUID de script → re-cablear `MainMenu` y `Chapter1` (y romper `FixErrors` que ya referencia el GUID global); añade superficie sin necesidad.
|
||||
- **Estimated Effort**: Mayor (re-cableado de escenas + editor).
|
||||
- **Rejection Reason**: La API extra no se usa; renunciar a ello evita el re-cableado completo y mantiene los GUIDs existentes.
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
|
||||
- Una única implementación de transiciones (cumple estancia 5 de ADR-0000).
|
||||
- `FixErrors.cs` ahora encuentra el STM en `Chapter1` → la corrección de fade funciona en ambas escenas.
|
||||
- Se elimina el único patrón lazy-getter pendiente de `SceneTransitionManager` (ADR-0004).
|
||||
- `ProjectSetup.cs` y `Chapter1.unity` quedan coherentes con el estilo canónico.
|
||||
|
||||
### Negative
|
||||
|
||||
- Se pierde la API `TransitionToScene`/`FadeOut`/`FadeIn` (sin uso actual; se recupera en el futuro si un consumidor lo requiere).
|
||||
- El fade en `Chapter1` no estará pre-cableado en el editor hasta que se ejecute `FixErrors` o se auto-cree en runtime (primer frame).
|
||||
|
||||
### Neutral
|
||||
|
||||
- `MainMenu.unity` no cambia (ya usaba la versión Core).
|
||||
- El `sortingOrder` del fade pasa a ser 999 (Core) en lugar de 9999 (UI); suficiente por estar en el top de todas las capas de UI.
|
||||
|
||||
## Risks
|
||||
|
||||
| Risk | Probability | Impact | Mitigation |
|
||||
|------|------------|--------|-----------|
|
||||
| `ProjectSetup.cs` no compila tras eliminar la clase UI | Baja | Alto | Actualizar las 3 referencias en el mismo changeset; verificación de compilación batch |
|
||||
| `Chapter1` sin fade visible (referencias null) | Media | Medio | El Core auto-crea `FadeCanvas` en runtime; `FixErrors` lo pre-cablea en editor |
|
||||
| Guard de duplicado distinto si quedara una copia en otra escena | Baja | Medio | Borrado físico del `.meta` (guid huérfano desaparece); verificación con grep de GUID |
|
||||
|
||||
## Performance Implications
|
||||
|
||||
| Metric | Before | Expected After | Budget |
|
||||
|--------|--------|---------------|--------|
|
||||
| CPU (frame time) | 2 managers en escena (solo 1 en cada escena actual) | 1 manager, sin cambio funcional | Sin cambio |
|
||||
| Memory | 2 clases + 2 posibles canvases | 1 clase, 1 canvas | Sin cambio |
|
||||
| Load Time | Sin cambio | Sin cambio | Sin cambio |
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. **Escribir ADR-0009** (este documento) y actualizar `architecture.yaml` (deuda `duplicate_manager_per_responsibility` resuelta) y `architecture.md` (estado del STM + follow-up). Verificar: lectura de los archivos.
|
||||
2. **Editar `Chapter1.unity`**: re-apuntar el MonoBehaviour `365129010` al script Core (guid `e1e89932...`) con campos serializados correctos. Verificar: grep de GUID UI en el proyecto = 0 resultados.
|
||||
3. **Editar `ProjectSetup.cs`**: `AjedrezPurgatorio.UI.SceneTransitionManager` → `SceneTransitionManager` (líneas 487, 489, 605). Verificar: grep de `AjedrezPurgatorio.UI.SceneTransitionManager` = 0 resultados.
|
||||
4. **Eliminar** `Assets/Game/Scripts/Mono/UI/SceneTransitionManager.cs` y `.meta`. Verificar: el archivo no existe y no quedan GUID `42a7f693...` en el repo.
|
||||
5. **Compilar** en batch (`-batchmode -quit -nographics`), esperando exit 0 sin errores CS.
|
||||
|
||||
**Rollback plan**: `git checkout --` de los archivos modificados restaura la versión UI en `Chapter1` y `ProjectSetup.cs`; recuperar el archivo eliminado desde git (no se ha borrado si se commit ancla el estado previo).
|
||||
|
||||
## Validation Criteria
|
||||
|
||||
- [x] Un único `SceneTransitionManager` (clase global Core) en el proyecto; grep de `AjedrezPurgatorio.UI.SceneTransitionManager` = 0.
|
||||
- [x] `Chapter1.unity` referencia el script Core (guid `e1e89932...`); grep del GUID UI en `Assets/` = 0.
|
||||
- [x] Compilación batch Unity con exit 0 y sin `error CS`.
|
||||
- [ ] Playtest: transición MainMenu → Chapter1 con fade visible (α negro 0→1→0) y sin dobles transiciones.
|
||||
- [ ] Playtest: re-cargar escena no crea un segundo `SceneTransitionManager` (guard de duplicado).
|
||||
|
||||
## GDD Requirements Addressed
|
||||
|
||||
| GDD Document | System | Requirement | How This ADR Satisfies It |
|
||||
|-------------|--------|-------------|--------------------------|
|
||||
| `design/gdd/systems-index.md` | 15 — Scene Management | Transiciones entre escenas (Sprint 1: SceneTransitionManager) | Una única implementación canónica garantiza fades consistentes en todas las transiciones y elimina la desincronización del duplicado |
|
||||
| `design/gdd/systems-index.md` | 16 — Game State Manager | Coordinación de estados con transición de escena | `GameStateManager` mantiene su llamada `LoadScene` sin cambios |
|
||||
|
||||
## Related
|
||||
|
||||
- Depende de: [ADR-0000 — Patrones de Infraestructura Global](adr-0000-patrones-infraestructura-global.md) (estancias 1 y 5)
|
||||
- Relacionado: [ADR-0004 — Singleton dual](adr-0004-singleton-dual.md) (estilo canónico Awake-explicit)
|
||||
- Código: `Assets/Game/Scripts/Mono/Core/SceneTransitionManager.cs` (canónico, se conserva), `Assets/Scenes/Chapter1.unity`, `Assets/Editor/ProjectSetup.cs`
|
||||
@@ -87,9 +87,13 @@ Assets/Game/Scripts/
|
||||
|
||||
## 4. Patrones Dominantes
|
||||
|
||||
> **Promovidos a ADRs formales** (2026-08-16): cada mini-ADR de esta sección tiene su
|
||||
> archivo formal en `docs/architecture/adr-0000..0008.md` (Status `Proposed`, pendientes de
|
||||
> aceptación). Los mini-ADRs aquí son el borrador base original y se mantienen como resumen.
|
||||
|
||||
Cada patrón se documenta como mini-ADR con contexto, decisión y consecuencias.
|
||||
|
||||
### 4.1 Service Layer POCO — **ADR-001**
|
||||
### 4.1 Service Layer POCO — **ADR-001** → `docs/architecture/adr-0001-service-layer-poco.md`
|
||||
|
||||
**Contexto**: `GameManager` concentraba 8 responsabilidades (~475 líneas) y era imposible
|
||||
de testear sin instanciar una escena Unity completa.
|
||||
@@ -105,7 +109,7 @@ IA y futuros sistemas (hints, variantes de reglas).
|
||||
|
||||
**Código**: `Assets/Game/Scripts/Mono/Core/Services/*.cs`, `GameManager.cs`
|
||||
|
||||
### 4.2 Strategy Pattern — IA — **ADR-002**
|
||||
### 4.2 Strategy Pattern — IA — **ADR-002** → `docs/architecture/adr-0002-strategy-pattern-ia.md`
|
||||
|
||||
**Contexto**: 3 dificultades de IA que deben poder escalarse por capítulo.
|
||||
|
||||
@@ -113,11 +117,11 @@ IA y futuros sistemas (hints, variantes de reglas).
|
||||
`AIStrategyMedium` (1-ply evaluation) y `AIStrategyHard` (minimax alpha-beta depth 2, timeout
|
||||
5s). `AIController` selecciona la estrategia.
|
||||
|
||||
**Consecuencias**: Extensible a nuevas dificultades sin tocar el controlador. ⚠️ **INCOMPLETO**:
|
||||
la selección de dificultad por capítulo desde `CampaignManager` está como TODO (S4-005) y
|
||||
`AIController` no está instanciado en ninguna escena.
|
||||
**Consecuencias**: Extensible a nuevas dificultades sin tocar el controlador. ✅ **Completado**
|
||||
(2026-08-16): `AIController` instanciado en `Chapter1.unity` y dificultad por capítulo cableada
|
||||
desde `CampaignManager` (ver `adr-0002-strategy-pattern-ia.md`).
|
||||
|
||||
### 4.3 Event-Driven — **ADR-003**
|
||||
### 4.3 Event-Driven — **ADR-003** → `docs/architecture/adr-0003-event-driven.md`
|
||||
|
||||
**Contexto**: UI y sistemas (Purgatorio, HUD) necesitan reaccionar a eventos del juego sin
|
||||
acoplamiento directo.
|
||||
@@ -125,10 +129,10 @@ acoplamiento directo.
|
||||
**Decisión**: Eventos `Action<T>` en `GameManager` (`OnCheckmate`, `OnDraw`, `OnPieceMoved`,
|
||||
`OnTurnChanged`, `OnCheck`). `PurgatoryManager` se suscribe a capturas; la UI escucha eventos.
|
||||
|
||||
**Consecuencias**: Desacople UI ↔ lógica. ⚠️ Faltan `OnPieceCaptured` y `OnCheckResolved`
|
||||
que `GameHUD` espera (suscripciones comentadas como TODO).
|
||||
**Consecuencias**: Desacople UI ↔ lógica. ✅ **Completado** (2026-08-16): añadidos
|
||||
`OnPieceCaptured` y `OnCheckResolved`; `GameHUD` suscrito (ver `adr-0003-event-driven.md`).
|
||||
|
||||
### 4.4 Singleton dual — **ADR-004**
|
||||
### 4.4 Singleton dual — **ADR-004** → `docs/architecture/adr-0004-singleton-dual.md`
|
||||
|
||||
**Contexto**: Managers globales accesibles desde cualquier script.
|
||||
|
||||
@@ -139,7 +143,7 @@ que `GameHUD` espera (suscripciones comentadas como TODO).
|
||||
**Consecuencias**: Acceso global simple. El patrón singleton añade acoplamiento implícito,
|
||||
aceptado para el alcance del proyecto.
|
||||
|
||||
### 4.5 ScriptableObject como data container — **ADR-005**
|
||||
### 4.5 ScriptableObject como data container — **ADR-005** → `docs/architecture/adr-0005-scriptableobject-data-container.md`
|
||||
|
||||
**Contexto**: Datos de diseño (identidades de 16-20 piezas, tablas de posición IA, balance)
|
||||
que deben editarse sin recompilar.
|
||||
@@ -150,7 +154,7 @@ ScriptableObjects; `CampaignState` y `DeadKingPool` cargados desde `Resources/`.
|
||||
**Consecuencias**: Data-driven y editable en editor. La serialización de `CampaignState`
|
||||
requiere capa JSON propia para Save/Load (limitación de `JsonUtility` con objetos anidados).
|
||||
|
||||
### 4.6 Overlay UI en lugar de escenas separadas — **ADR-006**
|
||||
### 4.6 Overlay UI en lugar de escenas separadas — **ADR-006** → `docs/architecture/adr-0006-overlay-ui.md`
|
||||
|
||||
**Contexto**: El Purgatorio debe interrumpir la partida sin perder el contexto del tablero.
|
||||
|
||||
@@ -159,7 +163,7 @@ la escena de juego, no escenas separadas.
|
||||
|
||||
**Consecuencias**: Mejor UX y menor overhead de carga. Requiere pausar/resumir gameplay.
|
||||
|
||||
### 4.7 `Time.timeScale` para pausas — **ADR-007**
|
||||
### 4.7 `Time.timeScale` para pausas — **ADR-007** → `docs/architecture/adr-0007-timescale-pausas.md`
|
||||
|
||||
**Contexto**: Promoción y Purgatorio necesitan pausar el juego.
|
||||
|
||||
@@ -168,7 +172,7 @@ la escena de juego, no escenas separadas.
|
||||
**Consecuencias**: Simple y sin estado global adicional. Riesgo: cualquier coroutine con
|
||||
`WaitForSeconds` normal se congela.
|
||||
|
||||
### 4.8 Position hashing FEN-like — **ADR-008**
|
||||
### 4.8 Position hashing FEN-like — **ADR-008** → `docs/architecture/adr-0008-position-hashing-fen-like.md`
|
||||
|
||||
**Contexto**: Detección de repetición triple.
|
||||
|
||||
@@ -242,14 +246,13 @@ Nueva partida → DeadKingPool.ShouldSpawnDeadKing(chapter) → Rey enemigo como
|
||||
| DialogueSystem | ✅ `_campaignState` + 6 TextAssets |
|
||||
| PieceIdentityManager | ✅ `_campaignState` |
|
||||
| CampaignManager | ✅ |
|
||||
| SceneTransitionManager (UI) | ⚠️ sin referencias de fade (versión duplicada) |
|
||||
| AIController | ✅ instanciado (ADR-0002); dificultad aplicada por `CampaignManager` |
|
||||
| GameHUD | ⚠️ `_pauseButton`, `_capturedPieceIconPrefab` null |
|
||||
| DialogueUI | ⚠️ `_speakerPortrait`, `_speakerDatabase`, `_advanceDialogueAction` null |
|
||||
| PauseMenuController | ⚠️ `_canvasGroup` null |
|
||||
|
||||
### Ausentes de escena (scripts existen)
|
||||
- ❌ `GameStateManager` — previsto, pendiente de cablear
|
||||
- ❌ `AIController` — IA no juega hasta instanciarlo
|
||||
- ❌ `DeadKingInputUI` / `DeadKingRevealUI`
|
||||
- ❌ Chapter2/3 scenes (no existen)
|
||||
|
||||
@@ -260,9 +263,8 @@ MainMenu + Chapter1 habilitadas. `SampleScene.unity` huérfana (no referenciada)
|
||||
|
||||
## 7. Problemas Conocidos
|
||||
|
||||
1. **SceneTransitionManager duplicado** — `Core/SceneTransitionManager.cs` (usado en MainMenu)
|
||||
y `UI/SceneTransitionManager.cs` (usado en Chapter1, sin fade). **Decisión: consolidar**
|
||||
en una sola clase; `FixErrors.cs` asume campos que solo existen en la versión Core.
|
||||
1. ~~**SceneTransitionManager duplicado**~~ — ✅ resuelto por ADR-0009: única clase Core,
|
||||
`UI/SceneTransitionManager.cs` eliminada, `Chapter1.unity` y `ProjectSetup.cs` re-apuntados.
|
||||
2. **`ProjectSetup.cs` no compila** — referencia `CameraFollow`, clase inexistente en el repo.
|
||||
3. **Objetos basura en Chapter1** — GameObject suelto con SquareClick fuera del tablero
|
||||
(-43.98, -0.72); GameManager/board en posiciones extrañas (34, 30); BoardParent vacío.
|
||||
@@ -300,7 +302,7 @@ MainMenu + Chapter1 habilitadas. `SampleScene.unity` huérfana (no referenciada)
|
||||
## 10. Follow-Up Work
|
||||
|
||||
**Inmediato**:
|
||||
- [ ] Consolidar SceneTransitionManager (eliminar duplicado UI/)
|
||||
- [x] Consolidar SceneTransitionManager (eliminar duplicado UI/) — ADR-0009
|
||||
- [ ] Reparar `ProjectSetup.cs` (CameraFollow inexistente)
|
||||
- [ ] Commit del trabajo sin commitear (escenas, prefabs, editor tools, assets movidos)
|
||||
- [ ] Actualizar `PROJECT_STATUS.md`/`roadmap.md` (estado real: escenas ya existen)
|
||||
@@ -311,7 +313,8 @@ MainMenu + Chapter1 habilitadas. `SampleScene.unity` huérfana (no referenciada)
|
||||
- [ ] Crear Chapter2/3 y playtest completo documentado
|
||||
|
||||
**Largo plazo**:
|
||||
- [ ] Generar ADRs formales (ADR-001..008 aquí son el borrador base)
|
||||
- [x] Generar ADRs formales (ADR-001..008 promovidos a `docs/architecture/adr-0000..0008.md`, Status `Proposed`)
|
||||
- [ ] Aceptar los ADRs formales y registrarlos en `docs/registry/architecture.yaml`
|
||||
- [ ] Llenar `docs/architecture/tr-registry.yaml` con TR-IDs por sistema
|
||||
- [ ] Control Manifest a partir de ADRs aceptados
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
# Superseded entries: Grep pattern="status: superseded"
|
||||
|
||||
version: 1
|
||||
last_updated: ""
|
||||
last_updated: "2026-08-16"
|
||||
|
||||
# ─── STATE OWNERSHIP ─────────────────────────────────────────────────────────
|
||||
# Who is the authoritative owner of each piece of shared game state.
|
||||
@@ -71,7 +71,20 @@ state_ownership: []
|
||||
#
|
||||
# pattern options: signal | direct_call | event_bus | shared_resource | rpc | none
|
||||
|
||||
interfaces: []
|
||||
interfaces:
|
||||
- contract: cross_system_communication
|
||||
status: active
|
||||
pattern: signal # eventos C# (public static event Action<T>)
|
||||
producer: all-systems
|
||||
consumers:
|
||||
- all-systems
|
||||
adr: docs/architecture/adr-0000-patrones-infraestructura-global.md
|
||||
signal_signature: "eventos C# con prefijo On, p.ej. OnPieceCaptured(T data); desuscripción en OnDestroy"
|
||||
referenced_by:
|
||||
- docs/architecture/adr-0000-patrones-infraestructura-global.md
|
||||
- docs/architecture/adr-0003-event-driven.md
|
||||
added: 2026-08-16
|
||||
revised: ""
|
||||
|
||||
# Example:
|
||||
#
|
||||
@@ -131,7 +144,63 @@ performance_budgets: []
|
||||
# Register an API decision when the choice is non-obvious and other ADRs might
|
||||
# make a different choice for the same purpose without knowing this was decided.
|
||||
|
||||
api_decisions: []
|
||||
api_decisions:
|
||||
- purpose: cross_scene_manager_access
|
||||
status: active
|
||||
api: "MonoBehaviour singleton Awake-explicit: public static X Instance { get; private set; } asignado en Awake + DontDestroyOnLoad(gameObject) + guard de duplicado"
|
||||
not: "lazy-getter con auto-creación de GameObject (if (_instance == null) en el getter)"
|
||||
adr: docs/architecture/adr-0000-patrones-infraestructura-global.md
|
||||
reason: "Estilo mayoritario (7 managers), predecible con el ciclo de vida de Unity; el lazy-getter oculta el ciclo de vida y fomenta ámbito global implícito."
|
||||
referenced_by:
|
||||
- docs/architecture/adr-0000-patrones-infraestructura-global.md
|
||||
added: 2026-08-16
|
||||
revised: ""
|
||||
|
||||
- purpose: save_load_serialization
|
||||
status: active
|
||||
api: "JsonUtility + ficheros JSON en persistentDataPath; ScriptableObject de estado runtime como fuente de verdad en memoria"
|
||||
not: "BinaryFormatter ni PlayerPrefs para datos estructurados"
|
||||
adr: docs/architecture/adr-0000-patrones-infraestructura-global.md
|
||||
reason: "Serialización simple, portable y depurable; BinaryFormatter deprecado/inseguro; PlayerPrefs inadecuado para datos estructurados de campaña."
|
||||
referenced_by:
|
||||
- docs/architecture/adr-0000-patrones-infraestructura-global.md
|
||||
added: 2026-08-16
|
||||
revised: ""
|
||||
|
||||
- purpose: business_logic_implementation
|
||||
status: active
|
||||
api: "Servicios POCO sin dependencia de UnityEngine (p. ej. CheckDetector, DrawDetector, MoveValidator), inyectados por DI en los MonoBehaviour"
|
||||
not: "lógica de negocio inline en MonoBehaviour ni utilidades estáticas con estado global"
|
||||
adr: docs/architecture/adr-0001-service-layer-poco.md
|
||||
reason: "Testabilidad sin escena Unity; reutilización por IA y futuros sistemas."
|
||||
referenced_by:
|
||||
- docs/architecture/adr-0001-service-layer-poco.md
|
||||
- docs/architecture/adr-0008-position-hashing-fen-like.md
|
||||
added: 2026-08-16
|
||||
revised: ""
|
||||
|
||||
- purpose: pause_mechanism
|
||||
status: active
|
||||
api: "Time.timeScale (0/1) para pausas globales; coroutines de UI con WaitForSecondsRealtime"
|
||||
not: "flag manual de pausa en Update ni SetActive(gameplay) para pausar"
|
||||
adr: docs/architecture/adr-0007-timescale-pausas.md
|
||||
reason: "Pausa global simple que detiene física/animaciones/coroutines WaitForSeconds sin estado adicional."
|
||||
referenced_by:
|
||||
- docs/architecture/adr-0006-overlay-ui.md
|
||||
- docs/architecture/adr-0007-timescale-pausas.md
|
||||
added: 2026-08-16
|
||||
revised: ""
|
||||
|
||||
- purpose: interrupt_flow_ui
|
||||
status: active
|
||||
api: "Canvas overlay dentro de la escena de juego para flujos que interrumpen (Purgatorio: oferta→dados→resultado)"
|
||||
not: "cargar escenas separadas para flujos que deben preservar el contexto del tablero"
|
||||
adr: docs/architecture/adr-0006-overlay-ui.md
|
||||
reason: "Preserva el contexto del tablero y el estado de partida en memoria; mejor UX y menor overhead de carga."
|
||||
referenced_by:
|
||||
- docs/architecture/adr-0006-overlay-ui.md
|
||||
added: 2026-08-16
|
||||
revised: ""
|
||||
|
||||
# Example:
|
||||
#
|
||||
@@ -157,7 +226,23 @@ api_decisions: []
|
||||
# Register a pattern when an ADR explicitly bans it AND other ADRs might
|
||||
# unknowingly use it (i.e., it's a tempting but wrong approach for this project).
|
||||
|
||||
forbidden_patterns: []
|
||||
forbidden_patterns:
|
||||
- pattern: duplicate_manager_per_responsibility
|
||||
status: active
|
||||
description: "Dos managers no pueden tener la misma responsabilidad. Deuda resuelta: SceneTransitionManager duplicado en Core/ y UI/ consolidado en ADR-0009 (única clase Core, UI/ eliminada)."
|
||||
why: "Duplicación de responsabilidad provoca desincronización de estado, dobles transiciones y bugs difíciles de rastrear."
|
||||
adr: docs/architecture/adr-0000-patrones-infraestructura-global.md
|
||||
referenced_by:
|
||||
- docs/architecture/adr-0009-consolidacion-scenetransitionmanager.md
|
||||
added: 2026-08-16
|
||||
revised: "2026-08-16"
|
||||
|
||||
- pattern: lazy_getter_singleton
|
||||
status: active
|
||||
description: "Los singleton cross-escena deben seguir el estilo Awake-explicit canónico; prohibido el getter con auto-creación de GameObject."
|
||||
why: "El lazy-getter oculta el ciclo de vida del manager, fomenta ámbito global implícito y dificulta el testing."
|
||||
adr: docs/architecture/adr-0000-patrones-infraestructura-global.md
|
||||
added: 2026-08-16
|
||||
|
||||
# Example:
|
||||
#
|
||||
|
||||
Reference in New Issue
Block a user