ajhahn.de
← the-way-out
Python 134 lines
from pathlib import Path

# Screen — the game boots fullscreen at the monitor's native size
# (queried via pygame in main.py). These are only the fallback used
# if that query fails; there is deliberately no resolution option.
WIDTH = 2560
HEIGHT = 1600
FPS = 60

# Colors
BLACK = (0, 0, 0)

# Player (base / Wizard) — individual characters override these in
# ``units.py`` to give Penguin/Elf/Shiggy/Wolf distinct feels.
PLAYER_SPEED = 600              # Pixel pro Sekunde
PLAYER_SCALE = 5

FONT = "assets/gui/font/main_font.otf"

TILE_SIZE = 64

# Collision box of the player, in pixels. Much smaller than the scaled
# sprite (which is mostly transparent padding) so movement through
# corridors feels right. Centered on the sprite.
PLAYER_HITBOX_SIZE = 56

# --- Combat ---
PLAYER_MAX_HP = 100
PLAYER_INVULN_TIME = 0.9        # seconds of i-frames after taking a hit
ATTACK_COOLDOWN = 0.35          # seconds between shots
PROJECTILE_DAMAGE = 10          # base damage; characters can override

PROJECTILE_SPEED = 950          # pixels per second
PROJECTILE_LIFETIME = 1.6       # seconds before it fizzles out
PROJECTILE_RADIUS = 12

# --- Dash ---
# Short burst on Shift: the player keeps full control of the direction
# but moves at ``PLAYER_SPEED * DASH_SPEED_MULT`` for ``DASH_DURATION``
# seconds, with i-frames the whole time. The cooldown starts at dash
# *end*, so spamming Shift doesn't shortcut it.
DASH_DURATION = 0.18
DASH_SPEED_MULT = 3.2
DASH_COOLDOWN = 1.2
DASH_INVULN_BONUS = 0.05        # extra i-frames after the dash itself ends

# --- Abilities ---
# Wizard's Slow ability scales the per-frame dt of every enemy, the
# boss and enemy projectiles by this factor while it is active; the
# Wizard himself and his own shots keep the raw dt. See levels.py.
SLOW_SCALE = 0.35

# Boss (Mr. Green) — slower than the player so it can be kited.
BOSS_SCALE = 9
BOSS_SPEED = 250
BOSS_MAX_HP = 220               # phase 2 keeps it onscreen longer
BOSS_HITBOX_SIZE = 150
BOSS_TOUCH_DAMAGE = 18

# Boss attack pattern (state machine in ``units.Boss``):
#   chase  -> windup -> dash    -> recover -> chase
#   chase  -> aim    -> shoot   -> recover -> chase   (phase 2 only)
# Phase 2 starts when boss HP drops below ``BOSS_PHASE2_HP_RATIO``.
BOSS_PHASE2_HP_RATIO = 0.5
BOSS_CHASE_TIME_MIN = 1.6
BOSS_CHASE_TIME_MAX = 2.6
BOSS_WINDUP_TIME = 0.55
BOSS_DASH_TIME = 0.45
BOSS_DASH_SPEED_MULT = 3.4
BOSS_RECOVER_TIME = 0.55
BOSS_AIM_TIME = 0.6
BOSS_SHOTS_PER_VOLLEY = 3       # phase 2: spread shot
BOSS_PROJECTILE_DAMAGE = 14
BOSS_PROJECTILE_SPEED = 720

# Hit feedback shared by every unit.
HIT_FLASH_TIME = 0.12

# --- Game-feel polish ---
# Hit-pause briefly freezes gameplay actors (player, enemies, boss,
# projectiles) so each impact reads. The level's own clock (camera
# shake, particles, fades) keeps ticking, so the freeze is a snap, not
# a hang.
HIT_PAUSE_PLAYER_HIT = 0.06
HIT_PAUSE_BOSS_HIT = 0.04
HIT_PAUSE_BOSS_DEATH = 0.18
HIT_PAUSE_PLAYER_DEATH = 0.14

# Particle burst sizes per event. Tuned so a busy room still reads at
# 60 FPS — the field auto-culls off-screen and dead particles.
PARTICLES_PLAYER_HIT = 14
PARTICLES_ENEMY_HIT = 8
PARTICLES_ENEMY_DEATH = 22
PARTICLES_BOSS_HIT = 12
PARTICLES_BOSS_DEATH = 90
PARTICLES_ABILITY = 28

# Per-character ability burst colours.
ABILITY_COLOR_WIZARD = (140, 110, 255)   # arcane violet
ABILITY_COLOR_PENGUIN = (120, 200, 255)  # ice blue
ABILITY_COLOR_ELF = (180, 240, 130)      # leaf green
ABILITY_COLOR_SHIGGY = (255, 200, 120)   # warm dust
ABILITY_COLOR_WOLF = (240, 240, 240)     # speed white

# Transition fade timings (alpha overlay drawn last in LevelManager).
FADE_IN_TIME = 0.22       # level start
FADE_OUT_TIME = 0.35      # level complete / death-to-retry

# Inter-level loading screen shown between the level menu and play.
# Press-or-timeout: auto-advance after this many seconds, or any
# dismiss-input (Enter / Space / left-click / Esc) skips early.
LOADING_SCREEN_DURATION = 3.0

# --- Hazards / Puzzles ---
# Spike traps run on one shared clock so the rhythm is readable:
#   safe -> warning (telegraph) -> deadly -> safe ...
SPIKE_CYCLE = 3.0          # full period, seconds
SPIKE_DANGER_TIME = 1.1    # seconds fully extended (deadly)
SPIKE_WARN_TIME = 0.5      # telegraph before extending (still safe)
SPIKE_DAMAGE = 16

LEVER_REACH = 95           # px: how close the player must be to pull a lever

# Pressure plates auto-activate when the player's hitbox overlaps them.
# Once triggered they stay on (just like levers) and open the matching
# gate panel by reading order.
PLATE_TRIGGER_DELAY = 0.25  # player must stand on it for this long

# --- Persistence ---
# Completed level indices are stored as a JSON list under the user's
# home directory so the save survives a fresh clone.
SAVE_DIR = Path.home() / ".the-way-out"
SAVE_FILE = SAVE_DIR / "save.json"