Files
tekton/wiki/game-modes.md
T
god 114748a54f experimental: remove Tekton Doors entirely
- Delete portal_mode_manager.gd, portal_door.gd, portal_door.tscn
- Strip all Tekton Doors logic from main.gd, player.gd, lobby.gd,
  lobby_room.gd, lobby_manager.gd, camera_context_manager.gd,
  music_manager.gd, tekton.gd, enhanced_gridmap.gd,
  playerboard_manager.gd, special_tiles_manager.gd
- Remove TK enum (TEKTON_DOORS=2), mode_config schema, arena area
- Update tests: 3 modes instead of 4
- Strip HowToPlay tab from main.tscn
2026-07-06 00:18:59 +08:00

605 lines
26 KiB
Markdown

# Game Modes
<a id="top"></a>
[Back to Home](./Home)
## Table of Contents
- [Overview](#overview)
- [Architecture](#architecture)
- [GameMode Enum &amp; ModeConfig](#gamemode-enum--modeconfig)
- [Session Flow (all modes)](#session-flow-all-modes)
- [Core Managers (shared)](#core-managers-shared)
- [Freemode](#freemode)
- [Stop n Go](#stop-n-go)
- [Tekton Doors (Portal)](#tekton-doors-portal)
- [Candy Pump Survival (Gauntlet)](#candy-pump-survival-gauntlet)
- [Scoring &amp; Leaderboard](#scoring--leaderboard)
- [Glossary](#glossary)
- [File Index](#file-index)
[Back to top](#top)
## Overview
Four game modes, each implementing the same core loop — players navigate a grid, collect tiles (Heart / Diamond / Star / Coin), match them to goals on a virtual 5x5 playerboard, and compete for score before the match timer expires.
| Mode | Enum Value | Display Name | Play Area | Gimmick |
|------|-----------|-------------|-----------|---------|
| Freemode | `FREEMODE = 0` | "Freemode" | Variable | No restrictions, just goals + timer |
| Stop n Go | `STOP_N_GO = 1` | "Stop n Go" | 23x12 | GO/STOP phases, safe zones, scatter penalty |
| Tekton Doors | `TEKTON_DOORS = 2` | "Tekton Doors" | 14x14 | 4 rooms, portal doors swap connections every 15s |
| Candy Pump Survival | `GAUNTLET = 3` | "Candy Pump Survival" | 20x20 | Ground growth — candy slowly fills the arena |
[Back to top](#top)
## Architecture
```
GameMode (enum/RefCounted)
├── .from_string() / .mode_to_string() / .is_restricted() / .get_all_modes()
├── ModeConfig (RefCounted)
│ └── SCHEMA with defaults, type-checking, min/max for each mode
├── GoalsCycleManager (Node, autoload?)
│ ├── 30s cycle timer
│ ├── Match timer (configurable 60-600s)
│ ├── Score tracking (player_scores, player_goal_counts)
│ ├── Goal completion: _process_goal_completion()
│ └── RPC sync: sync_player_score, sync_goal_count, sync_timer
├── GoalManager (Node)
│ ├── initialize_random_goals() — generates 9-slot goal patterns
│ ├── Speed tracking (completion_times, boost_multiplier)
│ └── generate_preset_goals() / get_goals_for_player()
├── PlayerRaceManager (per-player)
│ ├── goals: Array[int] (9 slots)
│ ├── playerboard: Array[int] (25 slots, 5x5)
│ ├── check_pattern_match() — core matching logic
│ └── DEPRECATED lap/finish-line stubs
├── PlayerboardManager (per-player)
│ ├── grab_item() / auto_put_item() / arrange operations
│ ├── _execute_grab() — server-authoritative grab validation
│ ├── HIDDEN_SLOTS (12 of 25 cells blocked)
│ ├── bot_try_grab_item() — AI grab logic
│ └── _check_and_refill_grid_if_needed() — scarcity refill
├── TurnManager (shared)
│ ├── next_turn() / end_current_turn()
│ └── turn_based_mode toggle
└── Mode-specific managers
├── StopNGoManager — phase transitions, safe zones, mission HUD
├── PortalModeManager — room partitions, portal doors, swap timer
└── GauntletManager — ground growth, phases, bubbles, smack
```
All mode managers live under `/root/Main` and connect to `GoalsCycleManager` signals for score / goal tracking.
[Back to top](#top)
## GameMode Enum &amp; ModeConfig
**File:** `scripts/game_mode.gd` (41 lines)
```
enum Mode { FREEMODE = 0, STOP_N_GO = 1, TEKTON_DOORS = 2, GAUNTLET = 3 }
```
| Static Method | Returns | Description |
|--------------|---------|-------------|
| `from_string(mode: String)` | `Mode` | Converts "Freemode"/"Stop n Go"/"Tekton Doors"/"Candy Pump Survival" to enum |
| `mode_to_string(mode: Mode)` | `String` | Reverse of from_string |
| `is_restricted(mode: Mode)` | `bool` | true for STOP_N_GO, TEKTON_DOORS, GAUNTLET |
| `get_all_modes()` | `Array[String]` | Returns display names |
**File:** `scripts/mode_config.gd` (109 lines)
SCHEMA defines per-mode settings with type, default, min, max, allowed values:
| Mode | Settings |
|------|---------|
| Freemode | match_duration (180s default), randomize_spawn (bool), enable_cycle_timer (bool), scarcity_mode (Normal/Aggressive/Chaos) |
| Stop n Go | match_duration, sng_go_duration (20s), sng_stop_duration (4s), sng_required_goals (8) |
| Tekton Doors | match_duration, doors_swap_time (15s), doors_refresh_time (25s), doors_required_goals (8) |
| Gauntlet | match_duration, gauntlet_growth_interval (3.0s), gauntlet_cells_per_tick (phase dict) |
| Method | Args | Returns |
|--------|------|---------|
| `get_defaults(mode)` | String | Dictionary of defaults for that mode |
| `validate_setting(mode, key, value)` | String, String, Variant | `{"valid": bool, "error": String}` |
| `validate_config(mode, config)` | String, Dictionary | `{"valid": bool, "errors": Array}` |
| `get_mode_settings(mode)` | String | Array of setting keys |
| `get_setting_schema(mode, key)` | String, String | Dictionary with type/default/min/max/allowed |
[Back to top](#top)
## Session Flow (all modes)
1. **Lobby** — players join, select mode + settings
2. **Game start**`main.gd` calls `_setup_host_game()` which:
- Creates arena (gridmap resize + clear)
- Spawns mission tiles on Layer 1
- Calls mode manager's `start_game_mode()`
3. **Countdown** — brief timer then match begins
4. **Match active**`GoalsCycleManager.start_match()` runs match timer + cycle timer
5. **Per-cycle** (30s):
- Players grab tiles from grid → place on 5x5 playerboard
- Match 3x3 pattern against 9-slot goals
- Complete goals → B1000 score + new goals + tiles randomize around player
- Cycle ends → board cleared, unmatched tiles scored at 10/tile match
6. **Match ends** — final leaderboard sync
[Back to top](#top)
## Core Managers (shared)
### GoalManager (`scripts/managers/goal_manager.gd`, 108 lines)
Generates 9-slot goal arrays using tile IDs 7-10 (Heart=7, Diamond=8, Star=9, Coin=10) with -1 for no-goal slots (~3 nulls per set).
| Function | Returns | Description |
|----------|---------|-------------|
| `initialize_random_goals(size, min_value, max_value, null_count)` | `Array` | Random goals with controlled null distribution |
| `generate_preset_goals(count)` | `Array` | Pre-generates N goal sets for all players |
| `get_goals_for_player(player_index)` | `Array` | Returns goals for a specific player slot |
| `mark_goal_start(player_id)` | void | Records timestamp for speed tracking |
| `mark_goal_complete(player_id)` | void | Records completion duration |
| `get_player_average_time(player_id)` | `float` | Average completion speed |
| `get_global_average_time()` | `float` | Average across all players |
| `get_boost_multiplier(player_id)` | `float` | 0.8-1.5x fill rate based on speed vs average |
| `reset()` | void | Clears all state |
### GoalsCycleManager (`scripts/managers/goals_cycle_manager.gd`, 520 lines)
Central scoring and timer system. Emits signals consumed by mode managers, UI, and leaderboard.
| Signal | Payload |
|--------|---------|
| `cycle_started()` | — |
| `cycle_ended()` | — |
| `timer_updated(time_remaining)` | float |
| `score_updated(peer_id, new_score)` | int, int |
| `goal_count_updated(peer_id, count)` | int, int |
| `leaderboard_updated(sorted_scores)` | Array |
| `match_started()` | — |
| `match_ended()` | — |
| `global_timer_updated(time_remaining)` | float |
| Function | Description |
|----------|-------------|
| `start_match(duration_seconds, start_cycles)` | Begins match timer, optionally starts first 30s cycle |
| `_on_match_end()` | Processes final scores, syncs to clients |
| `start_cycle()` | Begin 30s cycle, emit `cycle_started` |
| `on_goal_completed(player, time_remaining)` | Entry point — routes to server or optimistic local path |
| `_process_goal_completion(player, time_remaining)` | Server: award B1000 + time bonus, regen goals, randomize tiles |
| `regenerate_goals_for_player(player)` | Generate new 9-slot goal set, sync via RPC |
| `_randomize_tiles_around_player(player)` | Randomize 3x3 area around player on the grid |
| `_process_cycle_end_for_all_players()` | Clear all boards, convert matches to B10/tile |
| `add_score(peer_id, amount)` | Server: add arbitrary score points |
| `_update_leaderboard()` | Sort by score descending (or with SNG winner override) |
### PlayerRaceManager (`scripts/managers/player_race_manager.gd`, 133 lines)
State holder per player. Core logic is `check_pattern_match()`.
| Function | Description |
|----------|-------------|
| `check_pattern_match()` | Returns true if any 3x3 sub-grid of 5x5 board matches 3x3 goals |
| `check_3x3_section(board, goals, start_row, start_col)` | Checks single 3x3 section |
| `_normalize_tile(tile)` | Converts holo tiles 11-14 → 7-10 for match comparison |
| Remaining functions | DEPRECATED lap/finish line stubs |
**Playerboard Layout:** 5x5 (indices 0-24). 13 cells are HIDDEN_SLOTS (cannot hold tiles). Only 12 usable slots arranged in an L-shape matching the HUD.
### PlayerboardManager (`scripts/managers/playerboard_manager.gd`, 793 lines)
Handles grab, put, arrange operations with optimistic local updates + server-authoritative validation.
| Function | Description |
|----------|-------------|
| `grab_item(grid_position)` | Grab tile from grid → place on board (auto-arrange) or consume as power-up |
| `_execute_grab(grid_pos, cell, item_id, expected_slot)` | Server-side validation + state update + sync |
| `_force_sync_to_client(cell, server_item)` | Revert client when server rejects grab |
| `auto_put_item()` | AI/bot: find best tile to remove from board |
| `find_best_goal_slot_for_item(item)` | Auto-arrange into best matching slot |
| `bot_try_grab_item()` | AI grab logic |
| `_check_and_refill_grid_if_needed(gridmap)` | Refill floor 1 via ScarcityController when empty |
### TurnManager (`scripts/managers/turn_manager.gd`, 27 lines)
| Function | Description |
|----------|-------------|
| `next_turn(players)` | Advance turn index, emit `turn_changed` |
| `end_current_turn()` | Emit `turn_ended` |
| `reset_turn()` / `reset()` | Clear state |
[Back to top](#top)
## Freemode
**No dedicated manager.** Freemode relies entirely on the shared core managers (GoalsCycleManager, PlayerboardManager, etc.) with no mode-specific restrictions or gimmicks. Arena size is configurable via LobbyManager settings.
**Settings effects:**
- `enable_cycle_timer` false → cycle never expires (board never auto-clears)
- `scarcity_mode` → controls tile refill aggression
- `randomize_spawn` → players start at random positions
[Back to top](#top)
## Stop n Go
**Manager:** `StopNGoManager` (`scripts/managers/stop_n_go_manager.gd`, 1107 lines)
A 23x12 arena with two alternating phases:
### Phase System
| Phase | Duration (default) | Behaviour |
|-------|-------------------|-----------|
| GO | 20s | Players move freely, collect tiles, complete goals |
| STOP | 4s | Players frozen if outside safe zone → tiles scattered |
When STOP begins:
1. 3 dynamic safe zones spawn randomly (green tiles)
2. All players outside safe zone get `_scatter_player_tiles()` — board tiles scattered on grid
3. Power-up tiles (Speed=11, Ghost=14) spawn at 5 permanent locations
4. Mission requirement: complete 8 goals before reaching finish (x=22)
When GO begins:
1. Dynamic safe zones cleared
2. All STOP freeze effects removed via `sync_stop_freeze(false)`
### Tile IDs
| ID | Meaning |
|----|---------|
| 0 | Walkable floor |
| 2 | Safe zone (green) |
| 3 | Start/Finish line |
| 4 | Wall/obstacle |
| 15 | Lightning stone (decorative ancient rock) |
| 16 | Safe zone wall |
### Arena
22x10 walkable area with two interior rooms with entrances:
- Room 1: (7,6) to (11,9) — 5x4 area with 4 door entrances
- Room 2: (15,1) to (19,5) — 5x5 area with 4 door entrances
### HUD
- Center-bottom mission label: "GOALS (X/8)" or "ALL GOALS COMPLETE! REACH THE FINISH!"
- Traffic light stop timer (3 segments): all empty during GO, fills red during STOP phase
- Last 3 seconds of GO phase: segments light up one-by-one (countdown)
- VFX: `vfx_manager.play_go_animation()` / `play_stop_phase()`
### RPCs
| RPC | Direction | Description |
|-----|-----------|-------------|
| `sync_phase(phase_name, duration)` | Authority → all | Broadcast GO/STOP phase change |
| `sync_arena_setup()` | Authority → remote | Sync 23x12 grid dimensions + obstacles |
| `sync_all_safe_zones_vfx()` | Authority → all | Trigger safe zone visual effects |
### Key Functions (server-only unless noted)
| Function | Description |
|----------|-------------|
| `start_game_mode()` | Server: setup arena, assign missions, start GO phase |
| `_start_phase(phase)` | Transition GO↔STOP, penalize players outside safe zone |
| `_setup_arena()` | Build 23x12 with obstacles + rooms |
| `_spawn_mission_tiles()` | Heart(7)/Diamond(8)/Star(9)/Coin(10) at 60% density |
| `_spawn_powerup_tiles()` | Speed(11)+Ghost(14) at 5 permanent locations |
| `_assign_missions()` | NO-OP (mission = achievement: collect 8 goals) |
| `activate_client_side()` | Client: show HUD, connect to GoalsCycleManager signals |
| `rotate_players_to_start()` | Force all players to face East (PI/2) |
| `can_rpc()` | Check multiplayer peer is connected |
[Back to top](#top)
## Tekton Doors (Portal)
**Manager:** `PortalModeManager` (`scripts/managers/portal_mode_manager.gd`, 585 lines)
**Actor:** `PortalDoor` (`scripts/portal_door.gd`, 136 lines)
A 14x14 grid divided into 4 rooms (7x7 each) by cross-shaped wall partitions. Players move between rooms via portal doors that swap connections every 15 seconds.
### Room Layout
```
Room 0 (NW) | Room 1 (NE)
x: 0-6 | x: 7-13
z: 0-6 | z: 0-6
--------------+--------------
Room 2 (SW) | Room 3 (SE)
x: 0-6 | x: 7-13
z: 7-13 | z: 7-13
```
Central divider: columns 6,7 and rows 6,7 are walls (tile ID 4).
### Portal System
- 10 doors total (2 base per room + 2 randomly placed extras)
- Every 15s (`doors_swap_time`): `_randomize_connections()` shuffles pairings
- Each pair gets a color from `PORTAL_COLORS` (Cyan/Magenta/Red/Green/Orange)
- Validation ensures no pair connects doors in the same room
- 200ms anti-jitter cooldown per player in `handle_portal_interaction()`
### PortalDoor (actor)
| Property/Method | Description |
|----------------|-------------|
| `room_id` | Which room this door belongs to |
| `door_id` | Unique door index |
| `target_door_id` | Connected door (set by PortalModeManager) |
| `portal_color` | Color (set triggers `set_portal_color`) |
| `detection_area` | Area3D — body_entered triggers portal |
| `_on_body_entered(body)` | 200ms cooldown, emit `player_entered_portal` |
| `spawn_offset` | Vector2i meta — nudge spawn position into room |
| `_adjust_indicator_position()` | Move GroundIndicator toward room interior |
### Finish Room
- At 30s remaining on match timer, reveal `_spawn_finish_room()`
- Random 3x3 area converted to finish tiles (ID 3) in one room
- Player must be standing on finish tile AND have `doors_required_goals` complete
### Tile Refill
- Every 25s (`tile_refresh_time`): `_refresh_tiles()` refills Floor 1 at 60% density
- Uses `ScarcityModel.get_tile_weights()` for weighted random selection
- Avoids spawning under portal doors
### HUD
- Center-bottom: "GOALS (X/8)" or "ALL GOALS COMPLETE! FIND THE FINISH ROOM!"
- Message broadcasts: "PORTALS SWITCHED!", "TILES REPLENISHED!"
- Warning: "A 3x3 Finish Zone has appeared in Room N!"
### RPCs
| RPC | Direction | Description |
|-----|-----------|-------------|
| `sync_portal_data(data)` | Authority → local | Sync connections + colors to all clients |
| `sync_portal_configs(door_configs)` | Via main | Broadcast door positions/rotations |
### Key Functions
| Function | Description |
|----------|-------------|
| `initialize(p_main, p_gridmap)` | Create swap timer + tile refresh timer, connect signals |
| `start_game_mode()` | Setup arena, randomize connections, start timers |
| `setup_arena_locally()` | Resize to 14x14, build room walls, spawn portal doors |
| `_randomize_connections()` | Shuffle door pairings, assign colors, validate same-room rule |
| `handle_portal_interaction(player, door)` | Teleport player to connected door + offset |
| `_spawn_finish_room()` | Convert random 3x3 area to finish tiles |
| `check_win_condition(player_id, pos)` | Check finish tile + mission complete |
| `_refresh_tiles()` | Refill floor 1 items with scarcity weights |
| `sync_to_client(peer_id)` | Sync portal connections to late-joining client |
| `get_spawn_points()` | Returns 4 spawn positions (one per room quadrant) |
[Back to top](#top)
## Candy Pump Survival (Gauntlet)
**Manager:** `GauntletManager` (`scripts/managers/gauntlet_manager.gd`, 1825 lines)
A 20x20 arena where sticky candy (pink) slowly grows from the edges inward over 3 phases. Players must navigate shrinking safe zones, avoid sticky tiles, and use the "Smack" ability to temporarily clear candy.
### Phase System
| Phase | Start | Duration | Cell Growth (per tick) | Description |
|-------|-------|----------|----------------------|-------------|
| OPEN_ARENA (0) | 0s | 60s | 4-6 | "Outer Pressure" — candy pushes from perimeter |
| ROUTE_PRESSURE (1) | 60s | 60s | 6-8 | "Middle Pressure" — corridors tighten |
| SURVIVAL_ENDGAME (2) | 120s | 60s | 8-10 | "Inner Survival" — center fills in |
Each phase transition shrinks the arena bounds by removing outer layers (via `_shrink_arena()`).
### Growth Algorithm
Each tick (every `growth_interval` = 3s):
1. **Detect movement buffers** — identify critical corridor cells (#083)
2. **Generate candidates** — all SAFE cells scored by formula:
```
CandidateScore = LayerPriority + StickyNeighbor + InwardPressure
+ PlayerPressure + ClusterGrowth + CampingPressure
+ RandomNoise(-20..+20) + MovementBuffer + PathSafety + Repetition
```
3. **Weighted selection** — pick `_cells_this_tick()` cells via roulette wheel
4. **Path safety check** — `_apply_path_safety()` ensures no player gets fully trapped
5. **Telegraph** — amber warning overlay appears for 1s (cells still passable)
6. **Apply** — cells convert to permanent STICKY (pink overlay on Layer 2)
### Scoring Components
| Score Component | Range | Description |
|----------------|-------|-------------|
| `_score_layer_priority` | -40..+60 | Phase weight by ring (outer/middle/inner) |
| `_score_sticky_neighbor` | 0..+64 | +8 per adjacent sticky cell (cap +64) |
| `_score_inward_pressure` | 0..+30 | Push inward, scales with phase |
| `_score_player_pressure` | -50..+20 | 2-4 cells away +20; under player -50 (+10 in final 30s) |
| `_score_cluster_growth` | 0..+25 | +15 expansion, +25 bridge between clusters |
| `_score_camping_pressure` | 0..+60 | Per-region: >5s +20, >8s +40, >10s +60 |
| `_score_movement_buffer` | -40..0 | Hidden corridor buffers + player proximity floor |
| `_score_path_safety` | -100..0 | Soft penalty if selection would strand a player |
| `_score_repetition` | -30..0 | Penalty for cells near last tick's selection |
### Cell States
| State | Meaning | Passable? |
|-------|---------|-----------|
| SAFE | Normal floor | Yes |
| TELEGRAPHED | Amber warning (1s) | Yes |
| STICKY | Permanent candy overlay | No (slows) |
| BUBBLE_GROWING | Candy bubble expanding | No |
| BLOCKED | NPC zone / permanent obstacle | No |
### Candy Bubble System (#082)
Anti-camping hazard: grows 1x1 → 3x3 sticky area.
| Phase | Max Bubbles |
|-------|-------------|
| OPEN_ARENA | 0 (disabled) |
| ROUTE_PRESSURE | 2 |
| SURVIVAL_ENDGAME | 3 |
| Property | Value |
|----------|-------|
| Grow duration | 2.75s |
| Explosion radius | 1 (3x3) |
| Recent memory | 4 positions |
| Anti-stack radius | 3 (no bubbles within 3 of recent) |
### Camping Detection (#073)
Players tracked in 4x4 regions. Time accumulates while player stays in same region, resets on region change. Drives camping pressure in candidate scoring.
### Movement Buffers (#083)
Hidden per-cell penalties on critical corridor cells (chokepoints where removing the cell would isolate part of the arena). Decay over time and phase transitions so arena can still close in.
### Smack Mechanic
| Property | Value |
|----------|-------|
| Cooldown | 8s |
| Charge window | 3s |
| Effect | Clears nearby sticky? (consumes charge) |
Per-player cooldown/charge tracked in `smack_cooldowns` / `smack_charged` Dictionaries. Pink modulate during charge window, white on cooldown.
### Arena NPC
Candy Pump NPC at center (9,9) in a 3x3 blocked zone. Visual-only in v2 (projectile logic removed). Scattered projectiles still spawned for visual effect during telegraph phase.
### Slow-Mo
- Triggered conditionally, duration 4s
- `Engine.time_scale = 0.25` (1/4 speed)
- Restored to 1.0 in `_exit_tree()`
### Spawn Points
| Player Count | Positions |
|-------------|-----------|
| 4 | 4 corners: (1,1), (18,1), (1,18), (18,18) |
| 5-6 | 4 corners + top-mid (10,1) + bottom-mid (10,18) |
| 7-8 | 4 corners + all 4 mid-edges |
### RPCs
| RPC | Direction | Description |
|-----|-----------|-------------|
| `sync_phase(phase_index, phase_name)` | Authority → local | Phase change broadcast |
| `sync_arena_setup()` | Authority → remote | Arena dimensions + layout |
| `sync_growth_telegraph(cells)` | Authority → local | Show amber warning on selected cells |
| `sync_growth_apply(cells)` | Authority → local | Convert telegraphed to sticky |
| `consume_smack(pid)` | Any peer → local | Smack consumption + animation |
| `sync_stop_freeze` | (inherited from player.gd) | Freeze/unfreeze player |
### Key Functions
| Function | Description |
|----------|-------------|
| `initialize(main, grid)` | Connect to GoalsCycleManager |
| `start_game_mode()` | Activate client side, start OPEN_ARENA phase |
| `_setup_arena()` | Build 20x20, spawn Candy Pump NPC |
| `_process_growth_tick()` | One growth cycle: score → select → telegraph → apply |
| `_generate_candidates()` | Score all SAFE cells |
| `_calculate_candidate_score(pos, player_cells)` | Full formula with 10 components |
| `_select_cells_weighted(candidates, count)` | Roulette-wheel selection |
| `_apply_path_safety(selected)` | Filter: ensure no player stranded |
| `_try_spawn_bubble()` | Anti-camping bubble spawn attempt |
| `_update_camp_tracking(delta)` | Per-player region residency timer |
| `_detect_movement_buffers()` | Identify critical corridor chokepoints |
| `_shrink_arena()` | Remove outer arena layers on phase change |
| `_spawn_mission_tiles()` | Heart/Diamond/Star/Coin at 60% density |
| `get_spawn_points(player_count)` | Return spawn positions by player count |
| `has_smack_charged(pid)` / `consume_smack(pid)` | Smack mechanic |
| `_spawn_telegraph_highlight(pos)` | Amber glow visual (2-stage: build-up + flash) |
| `_spawn_impact_particles(targets)` | Candy splash particles on sticky impact |
| `_check_all_players_trapped()` | Re-evaluate sticky traps after growth apply |
[Back to top](#top)
## Scoring &amp; Leaderboard
| Action | Points |
|--------|--------|
| Complete goal pattern (match 3x3) | B1000 + time bonus |
| Tile match at cycle end (per tile) | B10 |
| Time bonus formula | `int(time_remaining * TIME_BONUS_MULTIPLIER)` — currently 0 (flat 1000) |
Leaderboard sorted descending. Stop n Go special case: winner (first to reach finish) placed at top regardless of score.
Leaderboard signal payload:
```
[{"peer_id": int, "score": int}, ...]
```
[Back to top](#top)
## Glossary
| Term | Definition |
|------|------------|
| Goal | 3x3 pattern (9 slots, some -1 for null) player must match on their playerboard |
| Playerboard | 5x5 virtual grid (12 usable slots, 13 hidden) per player |
| Tile | Grid item on Floor 1: Heart(7), Diamond(8), Star(9), Coin(10) |
| Holo Tile | Power-up tiles: Speed(11), Ghost(14) — consumed on pickup, not placed on board |
| Cycle | 30-second scoring round; ends with board clear + point conversion |
| Sticky | Permanent pink overlay on Gauntlet floor cells — blocks/slows movement |
| Telegraph | Amber 1-second warning before a cell becomes sticky |
| Safe Zone | Green tiles in Stop n Go STOP phase — only safe tile type |
| Portal | Colored door connecting rooms in Tekton Doors |
| Scarcity | Tile refill model controlling spawn weights based on mode config |
| Smack | Gauntlet ability: clear nearby sticky (8s cooldown, 3s charge window) |
| Camping | Player staying in same 4x4 region >5s, attracts growth pressure |
| Movement Buffer | Hidden chokepoint corridor that growth algorithm avoids sealing early |
| Chebyshev Distance | `max(|x1-x2|, |y1-y2|)` — used for all proximity calculations |
[Back to top](#top)
## File Index
| File | Lines | Role |
|------|-------|------|
| `scripts/game_mode.gd` | 41 | Mode enum + helper functions |
| `scripts/mode_config.gd` | 109 | Schema-driven per-mode settings |
| `scripts/managers/goal_manager.gd` | 108 | Goal generation + speed tracking |
| `scripts/managers/goals_cycle_manager.gd` | 520 | Timer, scoring, cycle control |
| `scripts/managers/player_race_manager.gd` | 133 | Per-player state: goals, board, pattern matching |
| `scripts/managers/playerboard_manager.gd` | 793 | Grab/put/arrange operations |
| `scripts/managers/turn_manager.gd` | 27 | Turn-based flow |
| `scripts/managers/stop_n_go_manager.gd` | 1107 | Stop n Go phase system, safe zones, HUD |
| `scripts/managers/portal_mode_manager.gd` | 585 | Tekton Doors room layout, portals, tiles |
| `scripts/managers/gauntlet_manager.gd` | 1825 | Candy Pump Survival growth, phases, smack |
| `scripts/portal_door.gd` | 136 | PortalDoor actor — detection, teleport, visuals |
| `scripts/managers/goals_cycle_manager.gd` | (shared) | Also referenced by gauntlet signal connections |
| `scripts/managers/camera_context_manager.gd` | ... | Camera mode changes per game mode? |
| `scripts/managers/player_movement_manager.gd` | ... | Movement restrictions per mode |
[Back to top](#top)