# Project: FreeExile 6-Player Party, Social Engine, Safe Teleportation & PoE2 Town Portal

## Architecture
- **Server Actor & Service Layer**:
  - `server/party/`: `party_types.py`, `party_scaling_calculator.py`, `party_service.py`
  - `server/social/`: `social_types.py`, `social_repository.py`, `social_service.py`, `guild_roster_service.py`
  - `server/world/`: `town_portal_types.py`, `town_portal_engine.py`, `teleport_service.py`
  - `server/world/game_design_matrix_schema.py`: 4 new tables + views for balance & rules
- **Network Protocol Layer (Protobuf)**:
  - `proto/party.proto`: Party lifecycle, invites, scaling stats, loot allocation
  - `proto/social.proto`: Friend requests, real-time presence, friend notes, block list
  - `proto/portal.proto`: Teleport channeling, cancel reasons, 2-way town portal, quarantine errors
- **Client WebApp HUD & Systems Layer**:
  - `client/webapp/js/ui/party_ui.js`: 6-player party frames, leader controls, loot mode picker
  - `client/webapp/js/ui/social_ui.js`: Friend list, status badges, friend notes, guild roster
  - `client/webapp/js/ui/teleport_hud.js`: 3.5s casting bar, interrupt visual triggers
  - `client/webapp/js/engine/town_portal_controller.js`: 2-way portal interaction & quarantine alerts

## Feature Inventory
| # | Feature | Description | Milestone | Source |
|---|---------|-------------|-----------|--------|
| 1 | Party Lifecycle | Create, invite, accept/decline, leader promote, leave, kick, disband (max 6) | M2 | R1 |
| 2 | Nearby Radius Math | 15.0m radius, same zone & instance_id validation for party bonuses | M2 | R1 |
| 3 | EXP Party Scaling | +30% total EXP per extra member, $(Level+10)^{2.71}$ weighting, level gap penalty | M2 | R1 |
| 4 | Loot Quantity/Rarity Scaling | +50% Item Qty, +50% Currency Qty, +30% Rarity per extra nearby member | M2 | R1 |
| 5 | Permanent Allocation | Exclusive item assignment to allocated player on ground drops | M2 | R1 |
| 6 | Short Allocation | 5.0s timer exclusive to allocated player, converts to FFA upon expiration | M2 | R1 |
| 7 | Free-For-All Allocation | Instant free pickup for all party members | M2 | R1 |
| 8 | Dynamic Monster HP Scaling | Common/Magic +50%, Rare +70%, Boss +100% HP per extra player | M2 | R1 |
| 9 | Dynamic Monster Defense Scaling | Armor +10%/player, Resistances +2%/player (cap 75%), Poise 15% Max HP | M2 | R1 |
| 10 | Friend Management Lifecycle | Send request, accept, decline, remove friend, bidirectional block list | M3 | R2 |
| 11 | Real-time Social Presence | Online/Offline status, level, current zone/map, friend note | M3 | R2 |
| 12 | Guild Roster Integration | Lightweight guild member roster query with presence & teleport targets | M3 | R2 |
| 13 | Safe Teleportation Channel | 3.5s cast time, cancelled immediately on movement (mag>0.05) or damage | M4 | R3 |
| 14 | 5-Gate Destination Validation | Target liveness, relationship auth, zone capacity, lifecycle, isolation | M4 | R3 |
| 15 | 2-Way Town Portal Engine | Wilderness (wx, wy) portal linked to Hideout/Sanctuary hub portal | M4 | R4 |
| 16 | 6-Portal Map Charge System | Consume charge on entry/death, seal map instance permanently at 0 charges | M4 | R4 |
| 17 | Anti-Exploit Boss Quarantine | Reject portal creation & teleport during active boss fight (boss_encounter_in_progress) | M4 | R4 |
| 18 | Anti-Exploit Puzzle Quarantine | Reject portal creation & teleport during active countdown/puzzle (secret_chamber_active) | M4 | R4 |
| 19 | Death Penalty Portal Handling | Wilderness death closes personal portal; Map instance death consumes charge | M4 | R4 |
| 20 | Game Design Matrix DB Tables | SQLite tables: party_scaling_matrix, loot_allocation_modes, teleport_configs, portal_quarantine_rules | M1 | R5 |
| 21 | Diátaxis Systems Documentation | System specifications in docs/game_design/ and docs/architecture/ | M1 | R5 |
| 22 | Client HUD Integration | Party frames, social window, casting bar, portal interact, i18n | M5 | R1-R4 UI |
| 23 | E2E Opaque-Box Test Suite | Tiers 1-5 comprehensive requirement-driven test verification (119 tests) | M6 | Quality |

## Milestones
| # | Name | Scope | Dependencies | Status |
|---|------|-------|-------------|--------|
| M1 | Game Design Matrix & Core Contracts | Schema tables, balance seed, proto contracts, Diátaxis specs | none | DONE |
| M2 | Party Engine & Scaling System | 6-player lifecycle, EXP & Loot scaling, 3 Loot Allocation, Monster Scaling | M1 | DONE |
| M3 | Friend List & Social Engine | Friend lifecycle, SQLite persistence, real-time presence, Guild roster | M1 | DONE |
| M4 | Safe Teleportation & Town Portal Engine | 3.5s channel, interrupt on move/damage, 2-way portal, Boss/Puzzle quarantine | M1, M2, M3 | DONE |
| M5 | Client WebApp HUD & Systems | Party frames, social window, casting bar, portal controller, i18n | M2, M3, M4 | DONE |
| M6 | Final Acceptance & Test Suite Pass | 100% E2E test pass (Tiers 1-4) & white-box adversarial hardening (Tier 5) | M1-M5, TEST_READY | DONE |

## Interface Contracts

### PartyService ↔ CombatEngine / LootDropService
- `PartyService.get_nearby_members(player_id: str, radius: float = 15.0) -> List[PartyMemberState]`
- `PartyScalingCalculator.calculate_party_exp(base_exp: int, members: List[PartyMemberState]) -> Dict[str, int]`
- `PartyScalingCalculator.calculate_loot_multipliers(nearby_count: int) -> Tuple[float, float, float]` (item_qty, curr_qty, rarity)
- `LootDropService.generate_monster_drops(..., allocation_mode: LootAllocationMode, party_members: List[str]) -> List[GroundLootDrop]`
- `GroundLootDrop.can_player_pickup(player_id: str, current_time: float) -> bool`

### SocialService ↔ TeleportService / ChatService
- `SocialService.is_friend(player_id: str, target_id: str) -> bool`
- `SocialService.is_blocked(sender_id: str, target_id: str) -> bool`
- `SocialService.get_friend_presence(player_id: str) -> List[FriendPresenceDTO]`
- `GuildRosterService.get_guild_roster_with_presence(guild_id: str) -> List[GuildMemberPresenceDTO]`

### TeleportService & TownPortalEngine ↔ ZoneEngine / MovementAuthority
- `TeleportService.start_teleport_channel(player_id: str, target_type: TargetType, target_id: str) -> TeleportChannelResult`
- `TeleportService.on_player_moved(player_id: str, mag: float) -> Optional[InterruptReason]`
- `TeleportService.on_player_damaged(player_id: str, damage: float) -> Optional[InterruptReason]`
- `TownPortalEngine.request_town_portal(player_id: str, zone_id: str, wx: float, wy: float) -> TownPortalResult`
- `TownPortalEngine.check_quarantine_lock(zone_id: str, instance_id: str) -> Tuple[bool, str]`

## Code Layout
- `server/party/`: All party logic, calculators, models (<= 350 lines/file)
- `server/social/`: Friend repository, presence, social service, guild roster (<= 350 lines/file)
- `server/world/`: `town_portal_types.py`, `town_portal_engine.py`, `teleport_service.py` (<= 350 lines/file)
- `proto/`: `party.proto`, `social.proto`, `portal.proto`
- `data/game_design_matrix.db`: SQLite database for game balance
- `docs/game_design/`: System design Diátaxis documents
- `docs/architecture/`: Technical architecture Diátaxis documents
- `client/webapp/js/ui/`: `party_ui.js`, `social_ui.js`, `teleport_hud.js`
- `client/webapp/js/engine/`: `town_portal_controller.js`
- `tests/unit/`: Unit tests for party, social, teleport, portal, matrix
- `tests/e2e_social_party/`: Opaque-box E2E test suite (Tiers 1-5, 119 tests)
