"""Stage definitions and defaults."""

from __future__ import annotations

from .entities_constants import ZOMBIE_DECAY_DURATION_FRAMES
from .gameplay_constants import SURVIVOR_SPAWN_RATE
from .level_constants import DEFAULT_GRID_COLS, DEFAULT_GRID_ROWS
from .models import FuelMode, Stage


def _build_stage18_reinforced_wall_zones(
    *,
    grid_cols: int,
    grid_rows: int,
    rooms_per_side: int,
    room_size: int,
    gap_width: int,
) -> list[tuple[int, int, int, int]]:
    reinforced_cells: set[tuple[int, int]] = set()

    # Inter-panel gap bands.
    inner_start = 2
    gap_cols: list[int] = []
    gap_rows: list[int] = []
    step = room_size + gap_width
    for idx in range(1, rooms_per_side):
        gap_start = inner_start + idx * step - gap_width
        gap_cols.extend(range(gap_start, gap_start + gap_width))
        gap_rows.extend(range(gap_start, gap_start + gap_width))
    for x in gap_cols:
        for y in range(inner_start, grid_rows - inner_start):
            reinforced_cells.add((x, y))
    for y in gap_rows:
        for x in range(inner_start, grid_cols - inner_start):
            reinforced_cells.add((x, y))

    # Corridor openings (width 1) through the gap bands.
    room_centers = [
        inner_start + (room_size // 2) + idx * step for idx in range(rooms_per_side)
    ]
    for y in room_centers:
        for x in gap_cols:
            reinforced_cells.discard((x, y))
    for x in room_centers:
        for y in gap_rows:
            reinforced_cells.discard((x, y))

    return [(x, y, 1, 1) for x, y in sorted(reinforced_cells)]


def _build_stage18_pitfall_zones(
    *,
    grid_cols: int,
    grid_rows: int,
    rooms_per_side: int,
    room_size: int,
    gap_width: int,
) -> list[tuple[int, int, int, int]]:
    pitfall_cells: set[tuple[int, int]] = set()
    inner_start = 2

    # Panel-edge notches: keep only the jagged concave parts as pitfalls.
    for row in range(rooms_per_side):
        for col in range(rooms_per_side):
            start_x = inner_start + col * (room_size + gap_width)
            start_y = inner_start + row * (room_size + gap_width)
            end_x = start_x + room_size - 1
            end_y = start_y + room_size - 1
            for offset in range(0, room_size, 2):
                x = start_x + offset
                y = start_y + offset
                pitfall_cells.add((x, start_y))
                pitfall_cells.add((x, end_y))
                pitfall_cells.add((start_x, y))
                pitfall_cells.add((end_x, y))

    # Keep pitfalls and reinforced walls disjoint.
    reinforced_cells = {
        (cell_x, cell_y)
        for zone_x, zone_y, zone_w, zone_h in _build_stage18_reinforced_wall_zones(
            grid_cols=grid_cols,
            grid_rows=grid_rows,
            rooms_per_side=rooms_per_side,
            room_size=room_size,
            gap_width=gap_width,
        )
        for cell_y in range(zone_y, zone_y + zone_h)
        for cell_x in range(zone_x, zone_x + zone_w)
    }
    pitfall_cells -= reinforced_cells
    pitfall_cells = {
        (x, y)
        for x, y in pitfall_cells
        if 1 <= x < grid_cols - 1 and 1 <= y < grid_rows - 1
    }
    return [(x, y, 1, 1) for x, y in sorted(pitfall_cells)]


_STAGE_36_RIGHT_WALLS = [12, 22, 32]
_STAGE_36_CARRIER_BOT_SPAWNS_X = [
    (6, 7, "x", 1),
    (16, 11, "x", -1),
    (26, 15, "x", 1),
    (38, 19, "x", -1),
]
_STAGE_36_CARRIER_BOT_SPAWNS_Y = [
    (11, 7, "y", 1),
    (21, 15, "y", -1),
    (31, 23, "y", 1),
]
_STAGE_36_CARRIER_BOT_SPAWNS = (
    _STAGE_36_CARRIER_BOT_SPAWNS_X + _STAGE_36_CARRIER_BOT_SPAWNS_Y
)

def _build_stage36_reinforced_wall_zones(grid_rows: int) -> list[tuple[int, int, int, int]]:
    """Three vertical reinforced walls placed on the right side of each carrier shaft."""
    zones: list[tuple[int, int, int, int]] = []
    for y in range(4, grid_rows - 4):
        for wall_x in _STAGE_36_RIGHT_WALLS:
            zones.append((wall_x, y, 1, 1))
            zones.append((wall_x - 2, y, 1, 1))
    return zones


def _build_stage36_lane_material_spawns(
    grid_cols: int,
    grid_rows: int,
    carrier_bot_spawns: list[tuple[int, int, str, int]],
    *,
    per_lane: int = 5,
) -> list[tuple[int, int]]:
    """Place materials on each carrier lane (x-axis rows), near each bot's movement range."""
    if per_lane <= 0:
        return []
    blocked_x = set(_STAGE_36_RIGHT_WALLS) | {x - 2 for x in _STAGE_36_RIGHT_WALLS}
    spawns: list[tuple[int, int]] = []
    occupied: set[tuple[int, int]] = set()
    min_x = 1
    max_x = grid_cols - 2
    min_y = 1
    max_y = grid_rows - 2
    for bot_x, bot_y, axis, _dir in carrier_bot_spawns:
        if axis != "x":
            continue
        y = int(bot_y)
        if not (min_y <= y <= max_y):
            continue
        candidates = [
            x
            for x in range(min_x, max_x + 1)
            if x not in blocked_x and (x, y) not in occupied
        ]
        candidates.sort(key=lambda x: (abs(x - int(bot_x)), x))
        added = 0
        for x in candidates:
            spawns.append((x, y))
            occupied.add((x, y))
            added += 1
            if added >= per_lane:
                break
    return spawns


def _build_stage36_storage_material_spawns(
    grid_cols: int, grid_rows: int
) -> list[tuple[int, int]]:
    """Place one storage row near top and bottom edges."""
    return [
        (x, y)
        for y in [2, grid_rows - 3]
        for x in range(3, grid_cols - 3)
    ]


def _build_stage36_material_spawns(
    grid_cols: int,
    grid_rows: int,
    carrier_bot_spawns: list[tuple[int, int, str, int]],
) -> list[tuple[int, int]]:
    combined = _build_stage36_storage_material_spawns(grid_cols, grid_rows)
    combined.extend(
        _build_stage36_lane_material_spawns(
            grid_cols, grid_rows, carrier_bot_spawns, per_lane=5
        )
    )
    seen: set[tuple[int, int]] = set()
    unique: list[tuple[int, int]] = []
    for cell in combined:
        if cell in seen:
            continue
        seen.add(cell)
        unique.append(cell)
    return unique


def _build_stage36_fall_spawn_zones(grid_rows: int) -> list[tuple[int, int, int, int]]:
    """Place fall-spawn floors at the same rows as left-right corridor doorways."""
    room_center_x = [6, 16, 26, 38]
    doorway_rows = [3, grid_rows - 4]
    return [(x, y, 1, 1) for x in room_center_x for y in doorway_rows]


def _build_stage38_reinforced_wall_zones(
    grid_cols: int, grid_rows: int
) -> list[tuple[int, int, int, int]]:
    """Build Head On-like multi-loop tracks with a central plaza."""
    ring_offsets = [4, 7, 10]
    center_row = grid_rows // 2
    center_col = grid_cols // 2
    # Keep all ring openings aligned to one centered 3-cell-wide cross passage.
    gate_rows_by_ring = [
        [center_row - 1, center_row, center_row + 1] for _ in ring_offsets
    ]
    gate_cols_by_ring = [
        [center_col - 1, center_col, center_col + 1] for _ in ring_offsets
    ]
    wall_cells: set[tuple[int, int]] = set()
    for idx, offset in enumerate(ring_offsets):
        x0 = offset
        x1 = grid_cols - 1 - offset
        y0 = offset
        y1 = grid_rows - 1 - offset
        if x0 >= x1 or y0 >= y1:
            continue
        gate_rows = set(gate_rows_by_ring[idx])
        gate_cols = set(gate_cols_by_ring[idx])
        for x in range(x0, x1 + 1):
            if x not in gate_cols:
                wall_cells.add((x, y0))
                wall_cells.add((x, y1))
        for y in range(y0, y1 + 1):
            if y not in gate_rows:
                wall_cells.add((x0, y))
                wall_cells.add((x1, y))
    return [(x, y, 1, 1) for x, y in sorted(wall_cells)]


def _build_stage39_carrier_bot_spawns(
    grid_cols: int, grid_rows: int
) -> list[tuple[int, int, str, int]]:
    """Place one Y-axis carrier bot per interior column with staggered phase."""
    if grid_cols <= 4 or grid_rows <= 2:
        return []
    travel_rows = grid_rows - 2
    spawns: list[tuple[int, int, str, int]] = []
    # Keep left/right exit lanes clear.
    for x in range(2, grid_cols - 2):
        phase = ((x - 1) * 3) % travel_rows
        y = 1 + phase
        direction_sign = 1 if (x % 2 == 0) else -1
        spawns.append((x, y, "y", direction_sign))
    return spawns


_STAGE_40_PUDDLE_COLS = [5, 11, 17, 23, 29]
_STAGE_40_FIRE_ROWS = [6, 12, 18, 24, 30]


def _build_stage40_reinforced_wall_zones(
    grid_cols: int, grid_rows: int
) -> list[tuple[int, int, int, int]]:
    """Build striped reinforced walls just inside the outer perimeter."""
    cells: set[tuple[int, int]] = set()
    top_y = 2
    bottom_y = grid_rows - 3

    for x in range(2, grid_cols - 2):
        if x % 3 == 2:  # on-on-off-off pattern
            cells.add((x, top_y))
            cells.add((x, bottom_y))

    # Use reinforced walls at lane intersections.
    for x in _STAGE_40_PUDDLE_COLS:
        for y in _STAGE_40_FIRE_ROWS:
            cells.add((x, y))

    return [(x, y, 1, 1) for x, y in sorted(cells)]


def _build_stage40_fire_floor_zones(
    grid_cols: int,
    blocked_cols: list[int],
    rows: list[int],
) -> list[tuple[int, int, int, int]]:
    """Build horizontal fire lanes while leaving puddle columns clear."""
    blocked = set(blocked_cols)
    zones: list[tuple[int, int, int, int]] = []
    x_start = 4
    x_end = grid_cols - 5
    for y in rows:
        seg_start: int | None = None
        for x in range(x_start, x_end + 1):
            if x in blocked:
                if seg_start is not None:
                    zones.append((seg_start, y, x - seg_start, 1))
                    seg_start = None
                continue
            if seg_start is None:
                seg_start = x
        if seg_start is not None:
            zones.append((seg_start, y, x_end - seg_start + 1, 1))
    return zones


def _build_stage40_puddle_zones(
    grid_rows: int,
    puddle_cols: list[int],
    blocked_rows: list[int],
) -> list[tuple[int, int, int, int]]:
    """Build vertical puddle lanes while leaving blocked rows clear."""
    y_start = 5
    y_end = grid_rows - 4
    blocked_base = set(blocked_rows)
    room_bands: list[tuple[int, int]] = []
    seg_start: int | None = None
    for y in range(y_start, y_end + 1):
        if y in blocked_base:
            if seg_start is not None:
                room_bands.append((seg_start, y - 1))
                seg_start = None
            continue
        if seg_start is None:
            seg_start = y
    if seg_start is not None:
        room_bands.append((seg_start, y_end))
    zones: list[tuple[int, int, int, int]] = []
    for col_idx, x in enumerate(puddle_cols):
        blocked = set(blocked_base)
        for band_idx, (band_start, band_end) in enumerate(room_bands):
            if band_start > band_end:
                continue
            band_len = (band_end - band_start) + 1
            if band_len < 2:
                continue
        seg_start: int | None = None
        for y in range(y_start, y_end + 1):
            if y in blocked:
                if seg_start is not None:
                    zones.append((x, seg_start, 1, y - seg_start))
                    seg_start = None
                continue
            if seg_start is None:
                seg_start = y
        if seg_start is not None:
            zones.append((x, seg_start, 1, y_end - seg_start + 1))
    return zones


def _build_stage40_fall_spawn_zones(
    puddle_cols: list[int],
    fire_rows: list[int],
) -> list[tuple[int, int, int, int]]:
    """Place one fall-spawn at each center cell of lane-bounded rectangles."""
    zones: list[tuple[int, int, int, int]] = []
    if len(puddle_cols) < 2 or len(fire_rows) < 2:
        return zones
    for left_x, right_x in zip(puddle_cols, puddle_cols[1:]):
        center_x = (left_x + right_x) // 2
        for top_y, bottom_y in zip(fire_rows, fire_rows[1:]):
            center_y = (top_y + bottom_y) // 2
            zones.append((center_x, center_y, 1, 1))
    return zones


STAGES: list[Stage] = [
    Stage(
        id="stage1",
        name_key="stages.stage1.name",
        description_key="stages.stage1.description",
        available=True,
        exterior_spawn_weight=0.97,
        interior_spawn_weight=0.03,
        zombie_normal_ratio=1.0,
    ),
    Stage(
        id="stage2",
        name_key="stages.stage2.name",
        description_key="stages.stage2.description",
        available=True,
        fuel_mode=FuelMode.FUEL_CAN,
        initial_interior_spawn_rate=0.007,
        exterior_spawn_weight=0.97,
        interior_spawn_weight=0.03,
        zombie_normal_ratio=1.0,
    ),
    Stage(
        id="stage3",
        name_key="stages.stage3.name",
        description_key="stages.stage3.description",
        available=True,
        fuel_mode=FuelMode.FUEL_CAN,
        buddy_required_count=1,
        initial_interior_spawn_rate=0.007,
        exterior_spawn_weight=0.97,
        interior_spawn_weight=0.03,
        zombie_normal_ratio=1.0,
    ),
    Stage(
        id="stage4",
        name_key="stages.stage4.name",
        description_key="stages.stage4.description",
        available=True,
        survivor_rescue_stage=True,
        waiting_car_target_count=2,
        initial_interior_spawn_rate=0.007,
        zombie_normal_ratio=1.0,
        survivor_spawn_rate=SURVIVOR_SPAWN_RATE,
    ),
    Stage(
        id="stage5",
        name_key="stages.stage5.name",
        description_key="stages.stage5.description",
        intro_key="stages.stage5.intro",
        available=True,
        wall_algorithm="default.120%",
        endurance_stage=True,
        endurance_goal_ms=1_200_000,
        fuel_spawn_count=0,
        initial_interior_spawn_rate=0.02,
        exterior_spawn_weight=0.15,
        interior_spawn_weight=0.85,
        zombie_normal_ratio=1.0,
    ),
    Stage(
        id="stage6",
        name_key="stages.stage6.name",
        description_key="stages.stage6.description",
        available=True,
        fuel_mode=FuelMode.FUEL_CAN,
        initial_interior_spawn_rate=0.01,
        exterior_spawn_weight=0.8,
        interior_spawn_weight=0.2,
        zombie_tracker_ratio=0.6,
        zombie_normal_ratio=0.4,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
    ),
    Stage(
        id="stage7",
        name_key="stages.stage7.name",
        description_key="stages.stage7.description",
        available=True,
        wall_algorithm="grid_wire",
        fuel_mode=FuelMode.FUEL_CAN,
        buddy_required_count=1,
        initial_interior_spawn_rate=0.01,
        exterior_spawn_weight=0.7,
        interior_spawn_weight=0.3,
        zombie_tracker_ratio=0.3,
        zombie_wall_hugging_ratio=0.3,
        zombie_normal_ratio=0.4,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
    ),
    Stage(
        id="stage8",
        name_key="stages.stage8.name",
        description_key="stages.stage8.description",
        available=True,
        cell_size=35,
        wall_algorithm="grid_wire",
        fuel_mode=FuelMode.FUEL_CAN,
        initial_interior_spawn_rate=0.01,
        exterior_spawn_weight=0.4,
        interior_spawn_weight=0.6,
        zombie_tracker_ratio=0.3,
        zombie_wall_hugging_ratio=0.7,
        zombie_normal_ratio=0,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
    ),
    Stage(
        id="stage9",
        name_key="stages.stage9.name",
        description_key="stages.stage9.description",
        available=True,
        cell_size=35,
        fuel_mode=FuelMode.FUEL_CAN,
        survivor_rescue_stage=True,
        waiting_car_target_count=1,
        initial_interior_spawn_rate=0.01,
        exterior_spawn_weight=0.4,
        interior_spawn_weight=0.6,
        zombie_tracker_ratio=0.3,
        zombie_wall_hugging_ratio=0.7,
        zombie_normal_ratio=0,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
        survivor_spawn_rate=SURVIVOR_SPAWN_RATE,
    ),
    Stage(
        id="stage10",
        name_key="stages.stage10.name",
        description_key="stages.stage10.description",
        intro_key="stages.stage10.intro",
        available=True,
        cell_size=40,
        wall_algorithm="sparse_moore.10%",
        survivor_rescue_stage=True,
        waiting_car_target_count=1,
        initial_interior_spawn_rate=0.02,
        exterior_spawn_weight=0.7,
        interior_spawn_weight=0.3,
        zombie_tracker_ratio=0.4,
        zombie_wall_hugging_ratio=0.2,
        zombie_normal_ratio=0.4,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
        survivor_spawn_rate=0.35,
    ),
    Stage(
        id="stage11",
        name_key="stages.stage11.name",
        description_key="stages.stage11.description",
        available=True,
        grid_cols=120,
        grid_rows=7,
        wall_algorithm="sparse_moore.10%",
        shoes_spawn_count=1,
        waiting_car_target_count=1,
        initial_interior_spawn_rate=0.1,
        exterior_spawn_weight=0.3,
        interior_spawn_weight=0.7,
        zombie_tracker_ratio=0.5,
        zombie_normal_ratio=0.5,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
    ),
    Stage(
        id="stage12",
        name_key="stages.stage12.name",
        description_key="stages.stage12.description",
        available=True,
        grid_cols=32,
        grid_rows=32,
        fall_spawn_zones=[
            (4, 4, 10, 10),
            (4, 18, 10, 10),
            (18, 4, 10, 10),
            (18, 18, 10, 10),
        ],
        fuel_mode=FuelMode.FUEL_CAN,
        flashlight_spawn_count=5,
        shoes_spawn_count=1,
        exterior_spawn_weight=0.5,
        interior_spawn_weight=0.2,
        interior_fall_spawn_weight=0.3,
        zombie_normal_ratio=1.0,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
    ),
    Stage(
        id="stage13",
        name_key="stages.stage13.name",
        description_key="stages.stage13.description",
        available=True,
        grid_cols=46,
        grid_rows=30,
        wall_algorithm="grid_wire",
        fall_spawn_zones=[
            (x, y, 2, 2)
            for y in range(2, DEFAULT_GRID_ROWS - 2, 4)
            for x in range(2, DEFAULT_GRID_COLS - 2, 4)
        ],
        fuel_mode=FuelMode.FUEL_CAN,
        buddy_required_count=1,
        flashlight_spawn_count=3,
        shoes_spawn_count=1,
        exterior_spawn_weight=0.6,
        interior_spawn_weight=0.1,
        interior_fall_spawn_weight=0.3,
        zombie_tracker_ratio=0.3,
        zombie_wall_hugging_ratio=0.3,
        zombie_normal_ratio=0.4,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
    ),
    Stage(
        id="stage14",
        name_key="stages.stage14.name",
        description_key="stages.stage14.description",
        available=True,
        grid_cols=34,
        grid_rows=20,
        wall_rubble_ratio=0.35,
        fall_spawn_cell_ratio=0.05,
        fuel_mode=FuelMode.FUEL_CAN,
        flashlight_spawn_count=3,
        shoes_spawn_count=1,
        exterior_spawn_weight=0.2,
        interior_spawn_weight=0.1,
        interior_fall_spawn_weight=0.7,
        zombie_normal_ratio=1.0,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
    ),
    Stage(
        id="stage15",
        name_key="stages.stage15.name",
        description_key="stages.stage15.description",
        intro_key="stages.stage15.intro",
        available=True,
        cell_size=35,
        grid_cols=64,
        grid_rows=24,
        wall_algorithm="grid_wire",
        fall_spawn_zones=[
            (33, 2, 4, 18),
        ],
        fuel_mode=FuelMode.FUEL_CAN,
        buddy_required_count=1,
        flashlight_spawn_count=3,
        shoes_spawn_count=1,
        initial_interior_spawn_rate=0.02,
        exterior_spawn_weight=0.2,
        interior_spawn_weight=0.1,
        interior_fall_spawn_weight=0.7,
        zombie_wall_hugging_ratio=0.5,
        zombie_normal_ratio=0.5,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
    ),
    Stage(
        id="stage16",
        name_key="stages.stage16.name",
        description_key="stages.stage16.description",
        available=True,
        cell_size=60,
        grid_cols=40,
        grid_rows=25,
        wall_algorithm="sparse_moore.25%",
        pitfall_density=0.04,
        fuel_mode=FuelMode.FUEL_CAN,
        flashlight_spawn_count=1,
        shoes_spawn_count=1,
        initial_interior_spawn_rate=0.05,
        exterior_spawn_weight=0.7,
        interior_spawn_weight=0.3,
        zombie_normal_ratio=1.0,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
    ),
    Stage(
        id="stage17",
        name_key="stages.stage17.name",
        description_key="stages.stage17.description",
        available=True,
        grid_cols=40,
        grid_rows=26,
        wall_algorithm="sparse_moore.25%",
        wall_rubble_ratio=0.25,
        pitfall_density=0.08,
        fuel_mode=FuelMode.FUEL_CAN,
        flashlight_spawn_count=1,
        shoes_spawn_count=1,
        initial_interior_spawn_rate=0.1,
        exterior_spawn_weight=0.5,
        interior_spawn_weight=0.5,
        zombie_tracker_ratio=1.0,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
    ),
    Stage(
        id="stage18",
        name_key="stages.stage18.name",
        description_key="stages.stage18.description",
        available=True,
        grid_cols=36,
        grid_rows=36,
        wall_algorithm="sparse_ortho.30%",
        wall_rubble_ratio=0.15,
        fall_spawn_cell_ratio=0.03,
        reinforced_wall_zones=_build_stage18_reinforced_wall_zones(
            grid_cols=36,
            grid_rows=36,
            rooms_per_side=3,
            room_size=10,
            gap_width=1,
        ),
        pitfall_zones=_build_stage18_pitfall_zones(
            grid_cols=36,
            grid_rows=36,
            rooms_per_side=3,
            room_size=10,
            gap_width=1,
        ),
        fuel_mode=FuelMode.FUEL_CAN,
        initial_interior_spawn_rate=0.08,
        exterior_spawn_weight=0.6,
        interior_spawn_weight=0.4,
        zombie_tracker_ratio=0.5,
        zombie_normal_ratio=0.5,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
    ),
    Stage(
        id="stage19",
        name_key="stages.stage19.name",
        description_key="stages.stage19.description",
        available=True,
        grid_cols=35,
        grid_rows=35,
        cell_size=35,
        wall_algorithm="grid_wire.170%",
        fall_spawn_cell_ratio=0.02,
        pitfall_density=0.008,
        fuel_mode=FuelMode.FUEL_CAN,
        buddy_required_count=1,
        initial_interior_spawn_rate=0.08,
        exterior_spawn_weight=0.4,
        interior_spawn_weight=0.0,
        interior_fall_spawn_weight=0.6,
        zombie_tracker_ratio=0.5,
        zombie_wall_hugging_ratio=0.5,
        zombie_normal_ratio=0,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
    ),
    Stage(
        id="stage20",
        name_key="stages.stage20.name",
        description_key="stages.stage20.description",
        intro_key="stages.stage20.intro",
        available=True,
        grid_cols=40,
        grid_rows=25,
        cell_size=60,
        wall_algorithm="default.80%",
        wall_rubble_ratio=0.2,
        fall_spawn_cell_ratio=0.008,
        fall_spawn_zones=[
            (19, 10, 10, 10),
        ],
        pitfall_density=0.01,
        fuel_mode=FuelMode.FUEL_CAN,
        buddy_required_count=3,
        initial_interior_spawn_rate=0.08,
        exterior_spawn_weight=0.4,
        interior_spawn_weight=0.2,
        interior_fall_spawn_weight=0.4,
        zombie_tracker_ratio=0.4,
        zombie_wall_hugging_ratio=0.4,
        zombie_normal_ratio=0.2,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
    ),
    Stage(
        id="stage21",
        name_key="stages.stage21.name",
        description_key="stages.stage21.description",
        available=True,
        cell_size=60,
        grid_cols=25,
        grid_rows=25,
        wall_algorithm="sparse_ortho.10%",
        pitfall_density=0.01,
        fuel_mode=FuelMode.FUEL_CAN,
        initial_interior_spawn_rate=0.05,
        exterior_spawn_weight=0.7,
        interior_spawn_weight=0.3,
        zombie_dog_ratio=0.5,
        zombie_normal_ratio=0.5,
        zombie_tracker_ratio=0.0,
        zombie_wall_hugging_ratio=0.0,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
    ),
    Stage(
        id="stage22",
        name_key="stages.stage22.name",
        description_key="stages.stage22.description",
        available=True,
        cell_size=50,
        grid_cols=25,
        grid_rows=25,
        wall_algorithm="grid_wire.120%",
        fuel_mode=FuelMode.FUEL_CAN,
        buddy_required_count=1,
        initial_interior_spawn_rate=0.1,
        patrol_bot_spawn_rate=0.05,
        zombie_normal_ratio=1.0,
        zombie_tracker_ratio=0.0,
        zombie_wall_hugging_ratio=0.0,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
    ),
    Stage(
        id="stage23",
        name_key="stages.stage23.name",
        description_key="stages.stage23.description",
        available=True,
        wall_algorithm="grid_wire.25%",
        pitfall_density=0.01,
        fuel_mode=FuelMode.FUEL_CAN,
        initial_interior_spawn_rate=0.06,
        patrol_bot_spawn_rate=0.04,
        exterior_spawn_weight=0.7,
        interior_spawn_weight=0.3,
        zombie_dog_ratio=1.0,
        zombie_normal_ratio=0.0,
        zombie_tracker_ratio=0.0,
        zombie_wall_hugging_ratio=0.0,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
    ),
    Stage(
        id="stage24",
        name_key="stages.stage24.name",
        description_key="stages.stage24.description",
        available=True,
        cell_size=50,
        grid_cols=35,
        grid_rows=23,
        wall_algorithm="sparse_ortho.50%",
        fuel_mode=FuelMode.FUEL_CAN,
        flashlight_spawn_count=3,
        initial_interior_spawn_rate=0.06,
        exterior_spawn_weight=0.2,
        interior_spawn_weight=0.8,
        zombie_normal_ratio=1.0,
        zombie_tracker_ratio=0.0,
        zombie_wall_hugging_ratio=0.0,
        zombie_dog_ratio=0.0,
        pitfall_zones=[(11, 9, 14, 5)],
        moving_floor_zones={
            "right": [(6, 5, 23, 1)],
            "down": [(29, 5, 1, 12)],
            "left": [(7, 17, 23, 1)],
            "up": [(6, 6, 1, 12)],
        },
        zombie_decay_duration_frames=int(ZOMBIE_DECAY_DURATION_FRAMES * 1.5),
    ),
    Stage(
        id="stage25",
        name_key="stages.stage25.name",
        description_key="stages.stage25.description",
        intro_key="stages.stage25.intro",
        available=True,
        cell_size=60,
        grid_cols=57,
        grid_rows=38,
        wall_rubble_ratio=0.5,
        wall_algorithm="normal.80%",
        fuel_mode=FuelMode.FUEL_CAN,
        flashlight_spawn_count=10,
        fuel_spawn_count=1,
        patrol_bot_spawn_rate=0.01,
        initial_interior_spawn_rate=0.05,
        exterior_spawn_weight=0.05,
        interior_spawn_weight=0.95,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 3,
        zombie_normal_ratio=0.4,
        zombie_tracker_ratio=0.2,
        zombie_wall_hugging_ratio=0,
        zombie_dog_ratio=0.4,
        moving_floor_zones={
            "up": [
                (8, 3, 1, 14),
                (21, 3, 1, 14),
                (34, 3, 1, 14),
                (47, 3, 1, 14),
                (8, 21, 1, 14),
                (21, 21, 1, 14),
                (34, 21, 1, 14),
                (47, 21, 1, 14),
            ],
            "down": [
                (9, 3, 1, 14),
                (22, 3, 1, 14),
                (35, 3, 1, 14),
                (48, 3, 1, 14),
                (9, 21, 1, 14),
                (22, 21, 1, 14),
                (35, 21, 1, 14),
                (48, 21, 1, 14),
            ],
            "left": [
                (3, 19, 5, 1),
                (10, 19, 5, 1),
                (16, 19, 5, 1),
                (23, 19, 5, 1),
                (29, 19, 5, 1),
                (36, 19, 5, 1),
                (42, 19, 5, 1),
                (49, 19, 5, 1),
            ],
            "right": [
                (3, 18, 5, 1),
                (10, 18, 5, 1),
                (16, 18, 5, 1),
                (23, 18, 5, 1),
                (29, 18, 5, 1),
                (36, 18, 5, 1),
                (42, 18, 5, 1),
                (49, 18, 5, 1),
            ],
        },
        fall_spawn_zones=[
            (2, 2, 53, 1),
            (2, 3, 1, 33),
            (15, 3, 1, 33),
            (28, 3, 1, 33),
            (41, 3, 1, 33),
            (54, 3, 1, 33),
            (3, 35, 12, 1),
            (16, 35, 12, 1),
            (29, 35, 12, 1),
            (42, 35, 12, 1),
        ],
        buddy_required_count=1,
        survivor_spawn_rate=0.07,
        exit_sides=["top", "bottom"],
    ),
    Stage(
        id="stage26",
        name_key="stages.stage26.name",
        description_key="stages.stage26.description",
        available=True,
        cell_size=50,
        grid_cols=30,
        grid_rows=25,
        wall_algorithm="sparse_ortho.10%",
        fuel_mode=FuelMode.REFUEL_CHAIN,
        buddy_required_count=1,
        initial_interior_spawn_rate=0.05,
        exterior_spawn_weight=0.2,
        interior_spawn_weight=0.0,
        interior_fall_spawn_weight=0.8,
        zombie_normal_ratio=1.0,
        zombie_tracker_ratio=0.0,
        zombie_wall_hugging_ratio=0.0,
        zombie_dog_ratio=0.0,
        flashlight_spawn_count=2,
        shoes_spawn_count=2,
        waiting_car_target_count=1,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 3,
        moving_floor_zones={
            "up": [],
            "down": [
                (5, 7, 5, 1),
                (11, 7, 5, 1),
                (17, 7, 5, 1),
                (23, 7, 5, 1),
                (5, 12, 5, 1),
                (11, 12, 5, 1),
                (17, 12, 5, 1),
                (23, 12, 5, 1),
                (5, 16, 2, 1),
                (8, 16, 2, 1),
                (11, 16, 2, 1),
                (14, 16, 2, 1),
                (17, 16, 2, 1),
                (20, 16, 2, 1),
                (23, 16, 2, 1),
                (26, 16, 2, 1),
                (5, 20, 2, 1),
                (8, 20, 2, 1),
                (11, 20, 2, 1),
                (14, 20, 2, 1),
                (17, 20, 2, 1),
                (20, 20, 2, 1),
                (23, 20, 2, 1),
                (26, 20, 2, 1),
            ],
            "left": [],
            "right": [],
        },
        pitfall_zones=[
            (28, 2, 1, 19),
            (4, 3, 1, 18),
            (10, 7, 1, 1),
            (16, 7, 1, 14),
            (22, 7, 1, 1),
            (10, 12, 1, 9),
            (22, 12, 1, 9),
            (7, 16, 1, 5),
            (13, 16, 1, 5),
            (19, 16, 1, 5),
            (25, 16, 1, 5),
        ],
        fall_spawn_zones=[
            (5, 4, 2, 2),
            (8, 4, 2, 2),
            (11, 4, 2, 2),
            (14, 4, 2, 2),
            (17, 4, 2, 2),
            (20, 4, 2, 2),
            (23, 4, 2, 2),
            (26, 4, 2, 2),
        ],
    ),
    Stage(
        id="stage27",
        name_key="stages.stage27.name",
        description_key="stages.stage27.description",
        available=True,
        cell_size=60,
        grid_cols=30,
        grid_rows=30,
        wall_algorithm="sparse_ortho.20%",
        fuel_mode=FuelMode.FUEL_CAN,
        initial_interior_spawn_rate=0.1,
        exterior_spawn_weight=0.2,
        interior_spawn_weight=0.8,
        zombie_normal_ratio=0.125,
        zombie_lineformer_ratio=0.75,
        zombie_tracker_ratio=0.125,
        zombie_wall_hugging_ratio=0.0,
        zombie_dog_ratio=0.0,
        flashlight_spawn_count=2,
        shoes_spawn_count=0,
        waiting_car_target_count=1,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 3,
        zombie_spawn_count_per_interval=4,
        survivor_spawn_rate=0.02,
    ),
    Stage(
        id="stage28",
        name_key="stages.stage28.name",
        description_key="stages.stage28.description",
        fuel_mode=FuelMode.REFUEL_CHAIN,
        available=True,
        cell_size=60,
        grid_cols=39,
        grid_rows=14,
        wall_algorithm="empty",
        initial_interior_spawn_rate=0.05,
        exterior_spawn_weight=0.2,
        interior_spawn_weight=0.2,
        interior_fall_spawn_weight=0.6,
        zombie_normal_ratio=0.5,
        zombie_dog_ratio=0.5,
        flashlight_spawn_count=0,
        shoes_spawn_count=2,
        waiting_car_target_count=1,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 3,
        moving_floor_zones={
            "up": [],
            "down": [],
            "left": [(3, 10, 33, 1)],
            "right": [(3, 3, 33, 1)],
        },
        fall_spawn_zones=[
            (3, 2, 3, 1),
            (8, 2, 3, 1),
            (13, 2, 3, 1),
            (18, 2, 3, 1),
            (23, 2, 3, 1),
            (28, 2, 3, 1),
            (33, 2, 3, 1),
            (3, 11, 3, 1),
            (8, 11, 3, 1),
            (13, 11, 3, 1),
            (18, 11, 3, 1),
            (23, 11, 3, 1),
            (28, 11, 3, 1),
            (33, 11, 3, 1),
        ],
        puddle_zones=[
            (2, 3, 1, 8),
            (36, 3, 1, 8),
            (6, 4, 2, 6),
            (11, 4, 2, 6),
            (16, 4, 2, 6),
            (21, 4, 2, 6),
            (26, 4, 2, 6),
            (31, 4, 2, 6),
        ],
        puddle_density=0.05,
        exit_sides=["top", "bottom"],
        spiky_plant_density=0.04,
    ),
    Stage(
        id="stage29",
        name_key="stages.stage29.name",
        description_key="stages.stage29.description",
        available=True,
        fuel_mode=FuelMode.FUEL_CAN,
        cell_size=40,
        grid_cols=44,
        grid_rows=44,
        wall_algorithm="empty",
        buddy_required_count=1,
        initial_interior_spawn_rate=0.1,
        exterior_spawn_weight=0.2,
        interior_spawn_weight=0.8,
        zombie_normal_ratio=1.0,
        zombie_dog_ratio=0.0,
        zombie_lineformer_ratio=0.0,
        flashlight_spawn_count=3,
        waiting_car_target_count=1,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 3,
        spiky_plant_zones=[
            (3, 3, 38, 2),
            (3, 5, 2, 36),
            (7, 5, 2, 36),
            (11, 5, 2, 36),
            (15, 5, 2, 36),
            (19, 5, 2, 36),
            (23, 5, 2, 36),
            (27, 5, 2, 36),
            (31, 5, 2, 36),
            (35, 5, 2, 36),
            (39, 5, 2, 36),
            (5, 7, 2, 2),
            (9, 7, 2, 2),
            (13, 7, 2, 2),
            (17, 7, 2, 2),
            (21, 7, 2, 2),
            (25, 7, 2, 2),
            (29, 7, 2, 2),
            (33, 7, 2, 2),
            (37, 7, 2, 2),
            (5, 11, 2, 2),
            (9, 11, 2, 2),
            (13, 11, 2, 2),
            (17, 11, 2, 2),
            (21, 11, 2, 2),
            (25, 11, 2, 2),
            (29, 11, 2, 2),
            (33, 11, 2, 2),
            (37, 11, 2, 2),
            (5, 15, 2, 2),
            (9, 15, 2, 2),
            (13, 15, 2, 2),
            (17, 15, 2, 2),
            (21, 15, 2, 2),
            (25, 15, 2, 2),
            (29, 15, 2, 2),
            (33, 15, 2, 2),
            (37, 15, 2, 2),
            (5, 19, 2, 2),
            (9, 19, 2, 2),
            (13, 19, 2, 2),
            (17, 19, 2, 2),
            (21, 19, 2, 2),
            (25, 19, 2, 2),
            (29, 19, 2, 2),
            (33, 19, 2, 2),
            (37, 19, 2, 2),
            (5, 23, 2, 2),
            (9, 23, 2, 2),
            (13, 23, 2, 2),
            (17, 23, 2, 2),
            (21, 23, 2, 2),
            (25, 23, 2, 2),
            (29, 23, 2, 2),
            (33, 23, 2, 2),
            (37, 23, 2, 2),
            (5, 27, 2, 2),
            (9, 27, 2, 2),
            (13, 27, 2, 2),
            (17, 27, 2, 2),
            (21, 27, 2, 2),
            (25, 27, 2, 2),
            (29, 27, 2, 2),
            (33, 27, 2, 2),
            (37, 27, 2, 2),
            (5, 31, 2, 2),
            (9, 31, 2, 2),
            (13, 31, 2, 2),
            (17, 31, 2, 2),
            (21, 31, 2, 2),
            (25, 31, 2, 2),
            (29, 31, 2, 2),
            (33, 31, 2, 2),
            (37, 31, 2, 2),
            (5, 35, 2, 2),
            (9, 35, 2, 2),
            (13, 35, 2, 2),
            (17, 35, 2, 2),
            (21, 35, 2, 2),
            (25, 35, 2, 2),
            (29, 35, 2, 2),
            (33, 35, 2, 2),
            (37, 35, 2, 2),
            (5, 39, 2, 2),
            (9, 39, 2, 2),
            (13, 39, 2, 2),
            (17, 39, 2, 2),
            (21, 39, 2, 2),
            (25, 39, 2, 2),
            (29, 39, 2, 2),
            (33, 39, 2, 2),
            (37, 39, 2, 2),
        ],
        puddle_density=0.05,
        wall_rubble_ratio=0.3,
    ),
    Stage(
        id="stage30",
        name_key="stages.stage30.name",
        description_key="stages.stage30.description",
        intro_key="stages.stage30.intro",
        available=True,
        cell_size=40,
        grid_cols=40,
        grid_rows=25,
        wall_algorithm="corridor.15%",
        initial_interior_spawn_rate=0.08,
        exterior_spawn_weight=0.4,
        interior_spawn_weight=0.6,
        zombie_normal_ratio=0.2,
        zombie_lineformer_ratio=0.4,
        zombie_tracker_ratio=0.2,
        zombie_wall_hugging_ratio=0.2,
        zombie_dog_ratio=0.0,
        flashlight_spawn_count=3,
        shoes_spawn_count=2,
        waiting_car_target_count=0,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
        endurance_stage=True,
        endurance_goal_ms=900_000,
        fuel_spawn_count=0,
        survivor_spawn_rate=0.05,
    ),
    Stage(
        id="stage31",
        name_key="stages.stage31.name",
        description_key="stages.stage31.description",
        fuel_mode=FuelMode.REFUEL_CHAIN,
        available=True,
        cell_size=50,
        grid_cols=35,
        grid_rows=35,
        wall_algorithm="empty",
        reinforced_wall_density=0.25,
        initial_interior_spawn_rate=0.02,
        exterior_spawn_weight=0.4,
        interior_spawn_weight=0.6,
        zombie_normal_ratio=0.0,
        zombie_dog_ratio=0.3,
        zombie_tracker_ratio=0.0,
        zombie_wall_hugging_ratio=0.0,
        zombie_lineformer_ratio=0.0,
        zombie_nimble_dog_ratio=0.7,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
        patrol_bot_spawn_rate=0.02,
    ),
    Stage(
        id="stage32",
        name_key="stages.stage32.name",
        description_key="stages.stage32.description",
        fuel_mode=FuelMode.REFUEL_CHAIN,
        available=True,
        cell_size=50,
        grid_cols=20,
        grid_rows=53,
        wall_algorithm="empty",
        reinforced_wall_density=0.15,
        initial_interior_spawn_rate=0.03,
        exterior_spawn_weight=0.4,
        interior_spawn_weight=0.6,
        zombie_normal_ratio=0.5,
        zombie_dog_ratio=0.0,
        zombie_tracker_ratio=0.4,
        zombie_wall_hugging_ratio=0.0,
        zombie_lineformer_ratio=0.0,
        zombie_nimble_dog_ratio=0.1,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
        fire_floor_zones=[
            (3, 11, 14, 1),
            (3, 21, 14, 1),
            (3, 31, 14, 1),
            (3, 41, 14, 1),
        ],
        exit_sides=["top", "bottom"],
    ),
    Stage(
        id="stage33",
        name_key="stages.stage33.name",
        description_key="stages.stage33.description",
        fuel_mode=FuelMode.REFUEL_CHAIN,
        available=True,
        cell_size=50,
        grid_cols=30,
        grid_rows=24,
        wall_algorithm="empty",
        initial_interior_spawn_rate=0.08,
        exterior_spawn_weight=0.2,
        interior_spawn_weight=0.5,
        interior_fall_spawn_weight=0.3,
        fall_spawn_cell_ratio=0.08,
        zombie_normal_ratio=0.2,
        zombie_lineformer_ratio=0.0,
        zombie_tracker_ratio=0.0,
        zombie_wall_hugging_ratio=0.0,
        zombie_dog_ratio=0.0,
        zombie_nimble_dog_ratio=0.8,
        flashlight_spawn_count=5,
        shoes_spawn_count=2,
        moving_floor_zones={
            "down": [
                (3, 3, 4, 1),
                (8, 3, 4, 1),
                (13, 3, 4, 1),
                (18, 3, 4, 1),
                (23, 3, 4, 1),
                (3, 5, 4, 1),
                (8, 5, 4, 1),
                (13, 5, 4, 1),
                (18, 5, 4, 1),
                (23, 5, 4, 1),
                (3, 7, 4, 1),
                (8, 7, 4, 1),
                (13, 7, 4, 1),
                (18, 7, 4, 1),
                (23, 7, 4, 1),
                (3, 9, 4, 1),
                (8, 9, 4, 1),
                (13, 9, 4, 1),
                (18, 9, 4, 1),
                (23, 9, 4, 1),
                (3, 11, 4, 1),
                (8, 11, 4, 1),
                (13, 11, 4, 1),
                (18, 11, 4, 1),
                (23, 11, 4, 1),
                (3, 13, 4, 1),
                (8, 13, 4, 1),
                (13, 13, 4, 1),
                (18, 13, 4, 1),
                (23, 13, 4, 1),
                (3, 15, 4, 1),
                (8, 15, 4, 1),
                (13, 15, 4, 1),
                (18, 15, 4, 1),
                (23, 15, 4, 1),
                (3, 17, 4, 1),
                (8, 17, 4, 1),
                (13, 17, 4, 1),
                (18, 17, 4, 1),
                (23, 17, 4, 1),
                (3, 19, 4, 1),
                (8, 19, 4, 1),
                (13, 19, 4, 1),
                (18, 19, 4, 1),
                (23, 19, 4, 1),
            ],
        },
        reinforced_wall_zones=[
            (7, 3, 1, 1),
            (12, 3, 1, 1),
            (17, 3, 1, 1),
            (22, 3, 1, 1),
            (27, 3, 1, 1),
            (2, 5, 1, 1),
            (7, 5, 1, 1),
            (12, 5, 1, 1),
            (17, 5, 1, 1),
            (22, 5, 1, 1),
            (7, 7, 1, 1),
            (12, 7, 1, 1),
            (17, 7, 1, 1),
            (22, 7, 1, 1),
            (27, 7, 1, 1),
            (2, 9, 1, 1),
            (7, 9, 1, 1),
            (12, 9, 1, 1),
            (17, 9, 1, 1),
            (22, 9, 1, 1),
            (7, 11, 1, 1),
            (12, 11, 1, 1),
            (17, 11, 1, 1),
            (22, 11, 1, 1),
            (27, 11, 1, 1),
            (2, 13, 1, 1),
            (7, 13, 1, 1),
            (12, 13, 1, 1),
            (17, 13, 1, 1),
            (22, 13, 1, 1),
            (7, 15, 1, 1),
            (12, 15, 1, 1),
            (17, 15, 1, 1),
            (22, 15, 1, 1),
            (27, 15, 1, 1),
            (2, 17, 1, 1),
            (7, 17, 1, 1),
            (12, 17, 1, 1),
            (17, 17, 1, 1),
            (22, 17, 1, 1),
            (7, 19, 1, 1),
            (12, 19, 1, 1),
            (17, 19, 1, 1),
            (22, 19, 1, 1),
            (27, 19, 1, 1),
        ],
        pitfall_zones=[
            (2, 21, 26, 1),
        ],
        exit_sides=["top", "bottom"],
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 3,
    ),
    Stage(
        id="stage34",
        name_key="stages.stage34.name",
        description_key="stages.stage34.description",
        available=True,
        cell_size=50,
        grid_cols=30,
        grid_rows=30,
        wall_algorithm="sparse_ortho.30%",
        fuel_mode=FuelMode.REFUEL_CHAIN,
        initial_interior_spawn_rate=0.2,
        exterior_spawn_weight=0.4,
        interior_spawn_weight=0.6,
        interior_fall_spawn_weight=0.0,
        zombie_normal_ratio=0.25,
        zombie_tracker_ratio=0.25,
        zombie_wall_hugging_ratio=0.25,
        zombie_lineformer_ratio=0.25,
        zombie_solitary_ratio=0.0,
        zombie_dog_ratio=0.0,
        zombie_nimble_dog_ratio=0.0,
        flashlight_spawn_count=1,
        shoes_spawn_count=1,
        reinforced_wall_density=0.18,
        fire_floor_density=0.18,
        patrol_bot_spawn_rate=0.02,
    ),
    Stage(
        id="stage35",
        name_key="stages.stage35.name",
        description_key="stages.stage35.description",
        intro_key="stages.stage35.intro",
        available=True,
        cell_size=40,
        grid_cols=25,
        grid_rows=25,
        wall_algorithm="sparse_moore.20%",
        wall_rubble_ratio=0.5,
        reinforced_wall_density=0.05,
        initial_interior_spawn_rate=0.12,
        exterior_spawn_weight=0.7,
        interior_spawn_weight=0.27,
        interior_fall_spawn_weight=0.03,
        zombie_normal_ratio=0.6,
        zombie_lineformer_ratio=0.2,
        zombie_tracker_ratio=0.2,
        zombie_wall_hugging_ratio=0.0,
        zombie_dog_ratio=0.0,
        flashlight_spawn_count=3,
        shoes_spawn_count=2,
        waiting_car_target_count=1,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 3,
        spiky_plant_density=0.12,
        puddle_density=0.01,
        # pitfall_zones=[(12, 12, 1, 1)],
        spiky_plant_zones=[
            (12, 11, 1, 1),
            (11, 12, 1, 1),
            (13, 12, 1, 1),
            (12, 13, 1, 1),
        ],
        fall_spawn_zones=[
            (3, 3, 1, 1),
            (9, 3, 1, 1),
            (15, 3, 1, 1),
            (21, 3, 1, 1),
            (3, 21, 1, 1),
            (9, 21, 1, 1),
            (15, 21, 1, 1),
            (21, 21, 1, 1),
            (3, 3, 1, 1),
            (3, 9, 1, 1),
            (3, 15, 1, 1),
            (3, 21, 1, 1),
            (21, 3, 1, 1),
            (21, 9, 1, 1),
            (21, 15, 1, 1),
            (21, 21, 1, 1),
        ],
        zombie_spawn_count_per_interval=3,
        endurance_stage=True,
        endurance_goal_ms=600_000,
    ),
    Stage(
        id="stage36",
        name_key="stages.stage36.name",
        description_key="stages.stage36.description",
        available=True,
        cell_size=40,
        grid_cols=43,
        grid_rows=31,
        wall_algorithm="empty",
        fuel_mode=FuelMode.REFUEL_CHAIN,
        exit_sides=["left", "right"],
        reinforced_wall_zones=_build_stage36_reinforced_wall_zones(31),
        fall_spawn_zones=_build_stage36_fall_spawn_zones(31),
        buddy_required_count=0,
        survivor_rescue_stage=False,
        waiting_car_target_count=1,
        initial_interior_spawn_rate=0.05,
        exterior_spawn_weight=0.3,
        interior_spawn_weight=0.4,
        interior_fall_spawn_weight=0.3,
        zombie_normal_ratio=0.35,
        zombie_tracker_ratio=0.0,
        zombie_wall_hugging_ratio=0.0,
        zombie_lineformer_ratio=0.0,
        zombie_dog_ratio=0.65,
        zombie_tracker_dog_ratio=0.0,
        zombie_nimble_dog_ratio=0.0,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
        carrier_bot_spawns=_STAGE_36_CARRIER_BOT_SPAWNS,
        material_spawns=_build_stage36_material_spawns(
            43, 31, _STAGE_36_CARRIER_BOT_SPAWNS
        ),
        flashlight_spawn_count=3,
        shoes_spawn_count=2,
    ),
    Stage(
        id="stage37",
        name_key="stages.stage37.name",
        description_key="stages.stage37.description",
        available=True,
        cell_size=40,
        grid_cols=35,
        grid_rows=35,
        wall_algorithm="default.50%",
        fuel_mode=FuelMode.REFUEL_CHAIN,
        waiting_car_target_count=1,
        initial_interior_spawn_rate=0.05,
        exterior_spawn_weight=0.5,
        interior_spawn_weight=0.5,
        interior_fall_spawn_weight=0.0,
        zombie_normal_ratio=0.3,
        zombie_tracker_ratio=0.5,
        zombie_wall_hugging_ratio=0.0,
        zombie_lineformer_ratio=0.2,
        zombie_dog_ratio=0.0,
        zombie_tracker_dog_ratio=0.0,
        zombie_nimble_dog_ratio=0.0,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
        carrier_bot_spawn_density=0.018,
        patrol_bot_spawn_rate=0.006,
        material_spawn_density=0.11,
        flashlight_spawn_count=3,
        shoes_spawn_count=0,
    ),
    Stage(
        id="stage38",
        name_key="stages.stage38.name",
        description_key="stages.stage38.description",
        buddy_required_count=1,
        available=True,
        cell_size=40,
        grid_cols=35,
        grid_rows=35,
        wall_algorithm="empty",
        fuel_mode=FuelMode.REFUEL_CHAIN,
        exit_sides=["left", "right"],
        reinforced_wall_zones=_build_stage38_reinforced_wall_zones(35, 35),
        waiting_car_target_count=1,
        initial_interior_spawn_rate=0.06,
        exterior_spawn_weight=0.4,
        interior_spawn_weight=0.6,
        interior_fall_spawn_weight=0.0,
        zombie_normal_ratio=0.25,
        zombie_tracker_ratio=0.15,
        zombie_wall_hugging_ratio=0.0,
        zombie_lineformer_ratio=0.0,
        zombie_dog_ratio=0.15,
        zombie_tracker_dog_ratio=0.45,
        zombie_nimble_dog_ratio=0.0,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
        puddle_zones=[
            (13, 13, 9, 2),  # top ring band
            (13, 20, 9, 2),  # bottom ring band
            (13, 15, 2, 5),  # left ring band
            (20, 15, 2, 5),  # right ring band
        ],
        flashlight_spawn_count=0,
        shoes_spawn_count=0,
    ),
    Stage(
        id="stage39",
        name_key="stages.stage39.name",
        description_key="stages.stage39.description",
        available=True,
        cell_size=35,
        grid_cols=33,
        grid_rows=25,
        wall_algorithm="empty",
        fuel_mode=FuelMode.REFUEL_CHAIN,
        exit_sides=["left", "right"],
        waiting_car_target_count=1,
        initial_interior_spawn_rate=0.06,
        exterior_spawn_weight=0.4,
        interior_spawn_weight=0.5,
        interior_fall_spawn_weight=0.1,
        zombie_normal_ratio=0.4,
        zombie_tracker_ratio=0.3,
        zombie_wall_hugging_ratio=0.3,
        zombie_lineformer_ratio=0.0,
        zombie_dog_ratio=0.2,
        zombie_tracker_dog_ratio=0.0,
        zombie_nimble_dog_ratio=0.0,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
        fire_floor_density=0.05,
        fire_floor_zones=[
            (1, 2, 31, 1),
            (2, 6, 10, 1),
            (16, 6, 8, 1),
            (26, 6, 5, 1),
            (3, 12, 9, 1),
            (15, 12, 7, 1),
            (24, 12, 6, 1),
            (2, 18, 8, 1),
            (13, 18, 10, 1),
            (26, 18, 5, 1),
            (1, 22, 31, 1),
        ],
        fall_spawn_cell_ratio=0.03,
        carrier_bot_spawns=_build_stage39_carrier_bot_spawns(33, 25),
        flashlight_spawn_count=1,
        shoes_spawn_count=1,
        zombie_spawn_count_per_interval=3,
    ),
    Stage(
        id="stage40",
        name_key="stages.stage40.name",
        description_key="stages.stage40.description",
        intro_key="stages.stage40.intro",
        available=True,
        cell_size=55,
        grid_cols=35,
        grid_rows=37,
        exit_sides=["left", "right"],
        wall_algorithm="empty",
        buddy_required_count=3,
        endurance_stage=True,
        endurance_goal_ms=300_000,
        waiting_car_target_count=0,
        fuel_spawn_count=0,
        initial_interior_spawn_rate=0.04,
        exterior_spawn_weight=0.3,
        interior_spawn_weight=0.6,
        interior_fall_spawn_weight=0.1,
        zombie_normal_ratio=0.5,
        zombie_tracker_ratio=0.0,
        zombie_wall_hugging_ratio=0.3,
        zombie_lineformer_ratio=0.1,
        zombie_dog_ratio=0.0,
        zombie_tracker_dog_ratio=0.0,
        zombie_nimble_dog_ratio=0.1,
        zombie_decay_duration_frames=ZOMBIE_DECAY_DURATION_FRAMES * 2,
        patrol_bot_spawn_rate=0.02,
        reinforced_wall_zones=_build_stage40_reinforced_wall_zones(35, 37),
        fall_spawn_zones=_build_stage40_fall_spawn_zones(
            _STAGE_40_PUDDLE_COLS, _STAGE_40_FIRE_ROWS
        ),
        puddle_zones=_build_stage40_puddle_zones(
            35, _STAGE_40_PUDDLE_COLS, _STAGE_40_FIRE_ROWS
        ),
        fire_floor_zones=_build_stage40_fire_floor_zones(
            35, _STAGE_40_PUDDLE_COLS, _STAGE_40_FIRE_ROWS
        ),
        flashlight_spawn_count=0,
        shoes_spawn_count=2,
        zombie_spawn_count_per_interval=2,
    ),
]
DEFAULT_STAGE_ID = "stage1"


__all__ = [
    "STAGES",
    "DEFAULT_STAGE_ID",
]
