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

26 KiB

Game Modes

Back to Home

Table of Contents

Back to 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

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

GameMode Enum & 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

Session Flow (all modes)

  1. Lobby — players join, select mode + settings
  2. Game startmain.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 activeGoalsCycleManager.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

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

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

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

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

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

Scoring & 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

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(

Back to 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