/** * settings.js * Static game constants — all tunable values live here. * Do not import game state; this file has zero side-effects. */ const SETTINGS = Object.freeze({ /* ── Canvas ───────────────────────────────────────────────── */ CANVAS_WIDTH: 900, CANVAS_HEIGHT: 520, HEADER_HEIGHT: 60, /* ── Timing ───────────────────────────────────────────────── */ GAME_DURATION: 180, // seconds PLAYER_MOVE_COOLDOWN: 180, // ms between tile moves LAB_ANALYZE_TIME: 3000, // ms to analyze a special good GOOD_WAIT_TIME: 2500, // ms a good waits at belt end before dropping MED_WAIT_TIME: 3500, // ms medication waits at pickup point /* ── Scoring ──────────────────────────────────────────────── */ POINTS_GOOD_LAB: 1, POINTS_SPECIAL_PATIENT: 5, POINTS_DROP_GOOD: -1, POINTS_DROP_SPECIAL: -3, /* ── Carry limit ──────────────────────────────────────────── */ MAX_CARRY: 5, /* ── Special good probability (1 in 7) ───────────────────── */ SPECIAL_GOOD_CHANCE: 1 / 7, /* ── Layout — zone x boundaries ──────────────────────────── */ LEFT_ZONE_START: 0, LEFT_ZONE_END: 350, MIDDLE_ZONE_START: 350, MIDDLE_ZONE_END: 600, RIGHT_ZONE_START: 600, RIGHT_ZONE_END: 900, /* ── Vertical rows (y centres, shared across all zones) ───── */ // Row 0 = top, Row 1 = middle, Row 2 = bottom BELT_ROWS: [155, 300, 445], /* ── Player grid (4 cols × 3 rows inside middle zone) ──────── */ PLAYER_COLS: 3, PLAYER_ROWS: 3, PLAYER_COL_WIDTH: 100, // px between column centres PLAYER_SIZE: 36, // bounding box size for drawing BELT_HEIGHT: 13, // visual belt strip height (px) /* ── Belt speeds ──────────────────────────────────────────── */ LEFT_BELT_SPEED: 45, // px / sec (goods move → right) RIGHT_BELT_SPEED: 33, // px / sec (medication moves ← left) /* ── Left belt spawn intervals (ms) ──────────────────────── */ BELT_MIN_SPAWN: 3200, BELT_MAX_SPAWN: 6500, /* ── Staggered initial spawn offsets per belt (ms) ─────────── */ BELT_INIT_OFFSETS: [600, 2400, 4200], /* ── Item sizes ───────────────────────────────────────────── */ GOOD_SIZE: 26, /* ── Patient happy flash duration ────────────────────────── */ PATIENT_HAPPY_DURATION: 1800, // ms /* ── Colours ──────────────────────────────────────────────── */ COLOR_BG: '#0a1628', COLOR_HEADER: '#0d1e38', COLOR_HEADER_LINE: '#1a4070', COLOR_BELT: '#152840', COLOR_BELT_STRIPE: '#1e3d5c', COLOR_BELT_EDGE: '#2a5a80', COLOR_BELT_R: '#14283c', COLOR_BELT_STRIPE_R:'#1c3650', COLOR_GOOD: '#f0a030', COLOR_GOOD_OUTLINE: '#c07010', COLOR_SPECIAL: '#ff44aa', COLOR_SPECIAL_OUT: '#cc1177', COLOR_MED: '#44ffaa', COLOR_MED_OUT: '#11cc77', COLOR_PLAYER: '#4499ff', COLOR_PLAYER_DARK: '#2266cc', COLOR_PLAYER_CARRY: '#88ccff', COLOR_LAB: '#1a4830', COLOR_LAB_LIGHT: '#22aa55', COLOR_LAB_OUTLINE: '#33dd77', COLOR_PATIENT: '#ff8844', COLOR_PATIENT_DARK: '#cc5511', COLOR_PATIENT_HAPPY:'#ffdd44', COLOR_TEXT: '#88ccff', COLOR_SCORE_VAL: '#ffdd44', COLOR_TIMER_OK: '#44dd88', COLOR_TIMER_WARN: '#ffaa22', COLOR_TIMER_DANGER: '#ff4444', COLOR_ZONE_DIV: '#1a3a5c', COLOR_ZONE_DIV2: '#0f2a40', COLOR_SHADOW: 'rgba(0,0,0,0.45)', });