# Project: FreeExile Gameplay Features Upgrade (R1, R2, R3)

## Architecture
- **Server World Engine**: Python 3.11+ strict typing (`server/world/zone_catalog.py`, `server/world/zone_types.py`, `server/world/zone_engine.py`).
- **Client WebApp Engine**: ES Modules, 2.5D Isometric Canvas (`client/webapp/index.html`, `canvas_renderer.js`, `collision_engine.js`, `monster_system.js`, `world_renderer.js`, `wilderness_zone_packs.js`, `feedback.js`, `main.js`).
- **Database / Matrix**: SQLite (`data/game_design_matrix.db`) validated by `tools/lint/verify_game_design_matrix.py`.

## Feature Inventory
| # | Feature | Description | Milestone | Source |
|---|---------|-------------|-----------|--------|
| 1 | Wilderness Map Expansion (Server) | Double `bounds_width/height` for 9 wilderness zones in `zone_catalog.py` (tang_kiem 2000x2000 >= 1600x1600; sanctuaries remain 800x800) | M1 | ORIGINAL_REQUEST R1 |
| 2 | Wilderness Map Bounds & Camera (Client) | Double `minWx/maxWx/minWy/maxWy` & camera bounds in `collision_engine.js`; center ambient tile generator on camera in `world_renderer.js` | M1 | ORIGINAL_REQUEST R1 |
| 3 | Waypoint Safe Radius Dataclass (Server) | Add `safe_radius: float = 8.0` to `Waypoint` in `zone_types.py` & catalog | M2 | ORIGINAL_REQUEST R2 |
| 4 | Spawn Validation Inside Safe Radius (Server) | Extend `validate_monster_spawn(zone_id, is_dummy, x, y)` to reject spawns within waypoint safe radius in `zone_engine.py` | M2 | ORIGINAL_REQUEST R2 |
| 5 | Waypoint Safe Zone Registry & Helper (Client) | Add `ZONE_WAYPOINTS` & `isInWaypointSafeRadius(wx, wy)` in `wilderness_zone_packs.js`; relocate initial packs outside 8.0m radius | M2 | ORIGINAL_REQUEST R2 |
| 6 | Monster Aggro Suppression & Leashing (Client) | Monsters cannot aggro/attack player in safe radius; active pursuers leash back in `monster_system.js` | M2 | ORIGINAL_REQUEST R2 |
| 7 | Player Safe Zone Regeneration (Client) | Continuous 2% maxHp/sec healing when player is inside safe radius | M2 | ORIGINAL_REQUEST R2 |
| 8 | Waypoint Visual Safe Indicator (Client) | Faint gold/rune ellipse ($rx \approx 362, ry \approx 181$) in `world_renderer.js` with distance fade within 14 units | M2 | ORIGINAL_REQUEST R2 |
| 9 | Pause Game State & Control Flow (Client) | Expose `window.isGamePaused`; gate `renderLoop` updates (lines 19-269 in `canvas_renderer.js`), maintain `lastTime = now`, pass `renderDt = isPaused ? 0 : dt` | M3 | ORIGINAL_REQUEST R3 |
| 10 | Pause Overlay UI (Client) | `#overlay-pause` in `index.html` at `z-[35]`, semi-transparent backdrop, "TẠM DỪNG / PAUSED", clickable to unpause | M3 | ORIGINAL_REQUEST R3 |
| 11 | Pause Trigger Integration (Desktop & Mobile) | Desktop `Escape` key toggles pause or closes modal; `#btn-open-settings` pauses on open, unpauses on close | M3 | ORIGINAL_REQUEST R3 |
| 12 | Skill & Click Input Guards (Client) | Freeze cooldown timer and reject skill activations / canvas clicks when paused | M3 | ORIGINAL_REQUEST R3 |
| 13 | Comprehensive Verification & Hygiene Gates | Automated unit/e2e test suites for R1-R3, game matrix sync, line hygiene check, security audit | M4 | ORIGINAL_REQUEST AC |

## Milestones
| # | Name | Scope | Dependencies | Status |
|---|------|-------|-------------|--------|
| M1 | Wilderness Map Expansion (R1) | Server zone bounds doubling + Client collision bounds & camera clamp + ambient tile centering | none | DONE |
| M2 | Waypoint Safe Radius & Healing (R2) | Server Waypoint dataclass & spawn validation + Client safe radius registry, pack relocation, aggro leash, 2% regen, visual indicator | M1 | DONE |
| M3 | Pause Game Mechanism & Overlay (R3) | `window.isGamePaused`, canvas_renderer update freeze, index.html pause overlay, settings open/close hooks, Escape key handling | none | DONE |
| M4 | Final Integration, Test Suite & Audit | E2E & unit tests, matrix sync (`verify_game_design_matrix.py`), hygiene check (`check_code_and_doc_hygiene.py`), security audit | M1, M2, M3 | DONE |

## Interface Contracts
### Server Waypoint Dataclass
- In `server/world/zone_types.py`:
  ```python
  @dataclass(slots=True, frozen=True)
  class Waypoint:
      waypoint_id: str
      name: str
      zone_id: str
      x: float
      y: float
      z: float = 0.0
      is_unlocked_by_default: bool = False
      discovery_radius: float = 15.0
      safe_radius: float = 8.0
  ```

### Server Zone Engine Spawn Validation
- In `server/world/zone_engine.py`:
  ```python
  def is_in_waypoint_safe_radius(self, zone_id: str, x: float, y: float) -> bool: ...
  def validate_monster_spawn(self, zone_id: str, is_dummy: bool = False, x: Optional[float] = None, y: Optional[float] = None) -> Tuple[bool, str]: ...
  ```

### Client Waypoint Safe Zone Contract
- In `client/webapp/js/data/wilderness_zone_packs.js`:
  ```javascript
  export function isInWaypointSafeRadius(wx, wy, zoneId): boolean
  window.isInWaypointSafeRadius = isInWaypointSafeRadius;
  ```

### Client Pause Game Contract
- `window.isGamePaused = false;`
- In `client/webapp/js/engine/canvas_renderer.js`:
  - `const isPaused = Boolean(window.isGamePaused);`
  - If `isPaused`, skip lines 19-269 (kinematics, monster AI, telegraphs, combat, loot).
  - Continuous `lastTime = now`.
  - Pass `renderDt = isPaused ? 0 : dt` to render functions.
- In `client/webapp/js/ui/feedback.js`:
  - `openSettingsModal()` -> `setGamePaused(true)`
  - `closeSettingsModal()` -> `setGamePaused(false)`
- In `client/webapp/js/main.js`:
  - `Escape` closes settings modal and unpauses, or toggles pause if no modal/chat/tooltip is open.

## Code Layout & Line Limits
- `server/world/zone_catalog.py`: 625 lines (Limit <= 700 soft, <= 1000 hard)
- `server/world/zone_engine.py`: 447 lines (Limit <= 350 soft, <= 500 hard)
- `client/webapp/js/engine/canvas_renderer.js`: 307 lines (Limit <= 350 soft STRICTLY enforced by unit test)
- `client/webapp/js/engine/monster_system.js`: 479 lines (Limit <= 490 STRICTLY enforced by unit test, <= 500 hard)
- `client/webapp/js/data/wilderness_zone_packs.js`: 229 lines (Limit <= 350 soft)
- `client/webapp/js/engine/world_renderer.js`: 430 lines (Limit <= 500 hard)
- `client/webapp/index.html`: 201 lines (Limit <= 400 hard)
