Велогрибник 2.0: Лесное Метро

Материал из Абсурдопедии
Перейти к навигацииПерейти к поиску

Велогрибник 2.0 — долгожданное продолжение легендарной игры Велогрибник: Заводской Дрифт. Категорически не рекомендуется геймерам с топографическим кретинизмом. Данный шедевр слишком нишевый, чтобы проводить ликбез для случайных нубов, поэтому ограничимся ответами на частые вопросы.

ЧаВо[править]

Если этот скрин не вызывает у тебя восторг — что ты вообще здесь делаешь?
Даже столько бустеров тебя не спасёт, если ты не научишься ориентироваться в лесу и метро
  1. Почему хозяин лесных заводов живёт у чёрта на рогах? Если Москва далеко от Мухосранска, то это проблема жителей Мухосранска, а не москвичей.
  2. А почему он не живёт хотя бы рядом со станцией метро? Жильё в двух шагах от метро слишком дорогое, не каждому миллиардеру по карману.
  3. Зачем вообще нужен метрополитен в лесу? А где вы видели в лесу трамвай или троллейбус? Как они проедут между деревьями по бездорожью?
  4. 5 грибов за проезд! Почему так дорого?! Ну не гнать же машинисту состав порожняком ЛесМетроЭлектротрансу в убыток. А другие люди на платформе — это не пассажиры. Они живут в метро, потому что под землёй немножко безопаснее, чем в лесу.
  5. Где общая схема метро? Откуда я знаю, куда мне ехать? Спросите у местных жителей — они подскажут. Ах да, подземные диалоги в код не поместились, всё ушло на красивое граффити на стене над поездом.
  6. Что ещё за бустеры и почему они вечно спавнятся не там где надо? Опытные велогрибники уже научились ездить по лесу, ловко объезжая препятствия. Чтобы дать этим гонщикам возможность разбиться, нужно придать им дополнительное ускорение.
  7. Почему бустеры так странно спавнятся? А если я набью 10 комбо подряд, весь лес будет завален бустерами? Да, это чтобы вы улетели в космос и приземлились оттуда прямо на крышу особняка.
  8. Почему пистолеты появляются только тогда, когда против стаи волков уже требуется пулемёт? Скажите спасибо, что велогрибник в моменте лутает пестик и хедшотит, а не как в случае с Трампом.
  9. Почему волки обходят пистолет стороной? У них же лапки, как волк подберёт пистолет и нажмёт на спусковой крючок? Ну и страх перед огнестрелом небольшой есть — для волков это будто ма-а-аленький костёр.
  10. В жопу метро, я и так доеду! Удачи.

Маленький скромненький кодик[править]

import os
import sys
import random
import shutil
import time
import textwrap
import math
import re

if os.name == 'nt':
    os.system('chcp 65001 >nul')

def clear_screen():
    if os.name == 'nt':
        os.system('cls')
    else:
        print("\033[H\033[J", end="")

def visible_len(s):
    return len(re.sub(r'\033\[[0-9;]*m', '', s))

def center_ansi(text, width):
    vis_len = visible_len(text)
    pad = max(0, (width - vis_len) // 2)
    return " " * pad + text

def get_input(menu_mode=False):
    move_map = {
        'е': (-1, -1), 't': (-1, -1),
        'н': (0, -1),  'y': (0, -1),
        'г': (1, -1),  'u': (1, -1),
        'п': (-1, 0),  'g': (-1, 0),
        'о': (1, 0),   'j': (1, 0),
        'м': (-1, 1),  'v': (-1, 1),
        'и': (0, 1),   'b': (0, 1),
        'т': (1, 1),   'n': (1, 1),
        '7': (-1, -1), '8': (0, -1), '9': (1, -1),
        '4': (-1, 0),                '6': (1, 0),
        '1': (-1, 1),  '2': (0, 1),  '3': (1, 1),
        '5': (0, 0)
    }
    
    if os.name == 'nt':
        import msvcrt
        while True:
            key = msvcrt.getwch()
            if key in ('\xe0', '\x00'):
                arr = msvcrt.getwch()
                if arr == 'H': return (0, -1)
                if arr == 'P': return (0, 1)
                if arr == 'K': return (-1, 0)
                if arr == 'M': return (1, 0)
            elif key == '\x03': return None
            else:
                k = key.lower()
                if k in ['q', 'й']: return None
                if k in ['р', 'h']: return 'blow'
                
                if menu_mode:
                    return k
                    
                if k in move_map:
                    return move_map[k]
    else:
        import tty, termios
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
            if ch == '\x1b':
                sys.stdin.read(1)
                arr = sys.stdin.read(1)
                if arr == 'A': return (0, -1)
                if arr == 'B': return (0, 1)
                if arr == 'D': return (-1, 0)
                if arr == 'C': return (1, 0)
            elif ch == '\x03': return None
            else:
                k = ch.lower()
                if k in ['q', 'й']: return None
                if k in ['р', 'h']: return 'blow'
                
                if menu_mode:
                    return k
                    
                if k in move_map:
                    return move_map[k]
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
    return (0, 0)

def main():
    player_x, player_y = 0, 0
    moves = 0
    speed = 0  
    last_dx, last_dy = 0, 0
    
    mushrooms = 0
    dead_wolves = 0 
    
    total_combos = 0
    combo_streak = 0
    turns_without_mushrooms = 0 
    diagonal_unlocked = False
    
    mansion_visited = False
    given_mushrooms = 0
    diamonds = 0
    last_hint_tier = 0
    
    grid = {}
    wolves = [] 
    burning_trees_cells = set() 
    
    turn_events = []
    combo_msg = ""
    feedback_msg = ""
    
    dead = False
    death_msg = ""
    is_igniting = False
    
    recent_combos = []
    boosters_active = 0

    # === ГЕНЕРАЦИЯ МЕТРО ===
    METRO_NAMES = [
        "Грибная", "Лесная", "Волчья", "Заводская", "Алмазная", "Дождливая",
        "Кислотная", "Велосипедная", "Скоростная", "Диагональная", "Комбовая",
        "Координатная", "Особняковая", "Хозяйская", "Смертельная", "Деревянная",
        "Костровая", "Пожарная", "Подутая", "Разгонная", "Разворотная", "Поворотная",
        "Генераторная", "Вероятностная", "Случайная", "Рандомная", "Поджигательная",
        "Собирательная", "Сбивательная", "Рекордная", "Счётчиковая", "Ходовая",
        "Подсказная", "Горизонтальная", "Вертикальная", "Бесконечная"
    ]
    random.shuffle(METRO_NAMES)
    name_iter = iter(METRO_NAMES)

    metro_lines = {'Красная': [], 'Жёлтая': [], 'Зелёная': [], 'Синяя': []}
    all_stops = []
    unique_metros = set()
    metro_exclusion_zones = set()

    def add_stop(line, idx, rx, ry, is_t=False):
        name = next(name_iter)
        stop = {'line': line, 'idx': idx, 'name': name, 'x': rx, 'y': ry, 'graffiti_seed': random.randint(0, 10000)}
        metro_lines[line].append(stop)
        all_stops.append(stop)
        unique_metros.add((rx, ry))

    t1_x = -50 + random.randint(-15, 15); t1_y = 50 + random.randint(-15, 15)
    t2_x = 50 + random.randint(-15, 15);  t2_y = 50 + random.randint(-15, 15)
    t3_x = -50 + random.randint(-15, 15); t3_y = -50 + random.randint(-15, 15)
    t4_x = 50 + random.randint(-15, 15);  t4_y = -50 + random.randint(-15, 15)

    for i, y_nom in enumerate([200, 150, 100, 50, 0, -50, -100, -150, -200]):
        if y_nom == 50: add_stop('Красная', i, t1_x, t1_y, True)
        elif y_nom == -50: add_stop('Красная', i, t3_x, t3_y, True)
        else: add_stop('Красная', i, -50 + random.randint(-15, 15), y_nom + random.randint(-15, 15))
    
    for i, y_nom in enumerate([200, 150, 100, 50, 0, -50, -100, -150, -200]):
        if y_nom == 50: add_stop('Жёлтая', i, t2_x, t2_y, True)
        elif y_nom == -50: add_stop('Жёлтая', i, t4_x, t4_y, True)
        else: add_stop('Жёлтая', i, 50 + random.randint(-15, 15), y_nom + random.randint(-15, 15))

    for i, x_nom in enumerate([-200, -150, -100, -50, 0, 50, 100, 150, 200]):
        if x_nom == -50: add_stop('Зелёная', i, t1_x, t1_y, True)
        elif x_nom == 50: add_stop('Зелёная', i, t2_x, t2_y, True)
        else: add_stop('Зелёная', i, x_nom + random.randint(-15, 15), 50 + random.randint(-15, 15))

    for i, x_nom in enumerate([-200, -150, -100, -50, 0, 50, 100, 150, 200]):
        if x_nom == -50: add_stop('Синяя', i, t3_x, t3_y, True)
        elif x_nom == 50: add_stop('Синяя', i, t4_x, t4_y, True)
        else: add_stop('Синяя', i, x_nom + random.randint(-15, 15), -50 + random.randint(-15, 15))

    for mx, my in unique_metros:
        for dy in range(-4, 5):
            for dx in range(-4, 5):
                tx, ty = mx + dx, my + dy
                metro_exclusion_zones.add((tx, ty))
                if abs(dx) <= 1 and abs(dy) <= 1:
                    if dx == 0 and dy == 0:
                        grid[(tx, ty)] = 'Ⓜ️'
                    else:
                        grid[(tx, ty)] = '..'

    def get_metros_at(x, y):
        return [s for s in all_stops if s['x'] == x and s['y'] == y]

    # === ГЕНЕРАЦИЯ ОСОБНЯКА ===
    while True:
        mx = random.randint(0, 250)
        my = 250 - mx
        mx *= random.choice([-1, 1])
        my *= random.choice([-1, 1])
        
        valid = True
        for sx, sy in unique_metros:
            if math.hypot(mx - sx, my - sy) < 25:
                valid = False
                break
        if valid:
            mansion_x, mansion_y = mx, my
            break

    # === ФУНКЦИИ ===
    def get_wolf_emoji(w):
        stage = w.get('acid_stage', -1)
        if stage == 1: return '👽'
        elif stage == 2: return '👺'
        elif stage == 3: return '👾'
        return '🐺'

    def is_visible(dx, dy):
        return abs(dx) <= 7 and abs(dy) <= 7

    def is_on_leading_edge(x, y, px, py, mdx, mdy, R=7):
        if mdx == 0 and mdy == -1: return y == py - R and abs(x - px) <= R
        elif mdx == 0 and mdy == 1: return y == py + R and abs(x - px) <= R
        elif mdx == -1 and mdy == 0: return x == px - R and abs(y - py) <= R
        elif mdx == 1 and mdy == 0: return x == px + R and abs(y - py) <= R
        return False

    def trigger_mansion_scene():
        nonlocal mansion_visited, given_mushrooms, mushrooms, wolves, dead_wolves
        clear_screen()
        term_size = shutil.get_terminal_size((80, 24))
        term_width = term_size.columns
        
        if mushrooms == 0: msg = "Хозяин лесных заводов багровеет от ярости! «Ни одного гриба?! Ты смеешь тратить моё время?!» Он обрушивает на вас трёхэтажный мат, а его охрана жестоко избивает вас, после чего вышвыривает вон."
        elif mushrooms < 10: msg = f"Хозяин лесных заводов брезгливо морщится. «Всего {mushrooms}? Жалкое зрелище, ты убожество. Убирайся, пока я не спустил собак.» Вас грубо выталкивают за дверь."
        elif mushrooms < 20: msg = f"Хозяин лесных заводов холодно смотрит на вас сверху вниз. «Что ж, {mushrooms} грибов. Положи на стол и уходи. И не топчи ковры.»"
        elif mushrooms < 30: msg = f"Хозяин лесных заводов кивает. «{mushrooms} грибов. Неплохо. Оставь их прислуге. Мы в расчёте.» Обычная, формальная встреча."
        elif mushrooms < 40: msg = f"Хозяин лесных заводов уважительно жмёт вам руку. «О, {mushrooms} отличных грибов! Вижу в тебе перспективного и надёжного партнёра по бизнесу. Заходи ещё!»"
        else: msg = f"Хозяин лесных заводов сияет от радости и обнимает вас! «Браво! {mushrooms} грибов! Мой дорогой гость, мой лучший друг! Закатим пир!» Вас принимают по высшему разряду."
        
        wrapped = textwrap.wrap(msg, width=min(term_width - 4, 60))
        print("\n" * max(0, term_size.lines // 2 - len(wrapped) // 2 - 2))
        for line in wrapped:
            print(" " * max(0, (term_width - len(line)) // 2) + line)
            
        prompt = "[Нажмите любую клавишу для выхода из особняка]"
        print("\n\n" + " " * max(0, (term_width - len(prompt)) // 2) + prompt)
        
        get_input(menu_mode=True)
        
        given_mushrooms = mushrooms
        mansion_visited = True
        mushrooms = 0
        
        wolves.clear()
        for pos in list(grid.keys()):
            if grid[pos] == '🍄':
                grid[pos] = '..'

    def ignite_tree_group(start_x, start_y):
        nonlocal dead_wolves, is_igniting
        if is_igniting: return 0, 0
        is_igniting = True
        try:
            burned_count, ignited_trees = 0, 0
            visited = set([(start_x, start_y)])
            stack = [(start_x, start_y)]
            while stack:
                cx, cy = stack.pop()
                if grid.get((cx, cy)) == '🌲':
                    grid[(cx, cy)] = '🔥'
                    burning_trees_cells.add((cx, cy))
                    ignited_trees += 1
                    for w in list(wolves):
                        if abs(w['x'] - cx) <= 1 and abs(w['y'] - cy) <= 1:
                            wolves.remove(w)
                            dead_wolves += 1
                            burned_count += 1
                    for dx in [-1, 0, 1]:
                        for dy in [-1, 0, 1]:
                            if dx == 0 and dy == 0: continue
                            nx, ny = cx + dx, cy + dy
                            if (nx, ny) not in visited:
                                if (nx, ny) not in grid: generate_cell(nx, ny)
                                if grid.get((nx, ny)) == '🌲':
                                    visited.add((nx, ny))
                                    stack.append((nx, ny))
            return ignited_trees, burned_count
        finally:
            is_igniting = False

    def generate_cell(x, y, move_dx=0, move_dy=0):
        nonlocal is_igniting
        if (x, y) in grid: return
        
        dx_m = abs(x - mansion_x)
        dy_m = abs(y - mansion_y)
        if dx_m <= 2 and dy_m <= 2:
            if dx_m == 0 and dy_m == 0: grid[(x, y)] = '🏯'
            elif dx_m == 1 and dy_m == 1: grid[(x, y)] = '🏮'
            else: grid[(x, y)] = '..'
            return

        on_leading_edge = is_on_leading_edge(x, y, player_x, player_y, move_dx, move_dy, R=7)
        wolf_prob = max(0.0, (moves *  0.0005) - (total_combos * 0.003)) if on_leading_edge else 0.0

        if x == 0 and y == 0:
            grid[(x, y)] = '..'
            return
        
        in_exclusion = (x, y) in metro_exclusion_zones
        
        if mansion_visited and not in_exclusion:
            diamond_prob = 0.00001 * (given_mushrooms ** 2)
            if random.random() < diamond_prob:
                grid[(x, y)] = '💎'
                return
        
        r = random.random()
        
        if r < 0.20:
            has_factory = any(grid.get((x+dx, y+dy)) == '🏭' for dx in range(-2, 3) for dy in range(-2, 3))
            if not has_factory:
                grid[(x, y)] = '🌲'
                if not is_igniting and any((x+adx, y+ady) in burning_trees_cells for adx in [-1,0,1] for ady in [-1,0,1] if adx!=0 or ady!=0):
                    _, w_b = ignite_tree_group(x, y)
                    if w_b > 0: turn_events.append(f"🔥 Распространяющийся пожар поглотил волков: {w_b}!")
            else:
                grid[(x, y)] = '..'
        elif r < 0.25:
            has_factory = any(grid.get((x+dx, y+dy)) == '🏭' for dx in range(-2, 3) for dy in range(-2, 3))
            if not mansion_visited and not has_factory and not in_exclusion:
                grid[(x, y)] = '🍄'
            else:
                grid[(x, y)] = '..'
        elif r < 0.27:
            has_factory = any(grid.get((x+dx, y+dy)) == '🏭' for dx in range(-2, 3) for dy in range(-2, 3))
            if not has_factory:
                is_near_wolf = any(abs(w['x'] - x) <= 1 and abs(w['y'] - y) <= 1 for w in wolves)
                grid[(x, y)] = '..' if is_near_wolf else '🔥'
            else:
                grid[(x, y)] = '..'
        elif r < 0.32:
            has_nature = any(grid.get((x+dx, y+dy)) in ['🌲', '🍄', '🔥'] for dx in range(-2, 3) for dy in range(-2, 3))
            grid[(x, y)] = '🏭' if not has_nature else '..'
        else:
            grid[(x, y)] = '..'

        if grid[(x, y)] == '..' and wolf_prob > 0:
            has_wolf = any(w['x'] == x and w['y'] == y for w in wolves)
            if not has_wolf:
                is_near_fire = any(grid.get((x + adx, y + ady)) == '🔥' for adx in [-1, 0, 1] for ady in [-1, 0, 1])
                if not is_near_fire and random.random() < wolf_prob:
                    wolves.append({'x': x, 'y': y, 'just_spawned': True})

        if grid[(x, y)] == '..':
            booster_prob = 0.004 * (sum(recent_combos)**2)
            if random.random() < booster_prob:
                grid[(x, y)] = '🚀'
            else:
                gun_prob = 0.002 * dead_wolves
                if random.random() < gun_prob:
                    grid[(x, y)] = '🔫'

    def generate_viewport(move_dx=0, move_dy=0):
        R = 7
        for y in range(player_y - R, player_y + R + 1):
            for x in range(player_x - R, player_x + R + 1):
                if is_visible(x - player_x, y - player_y):
                    generate_cell(x, y, move_dx, move_dy)

    def is_safe_for_wolf(nx, ny, current_wolf=None):
        if grid.get((nx, ny)) in ['🌲', '🏭', '🔥', '🏯', '🏮', '💎', 'Ⓜ️', '🔫']: return False
        for adx in [-1, 0, 1]:
            for ady in [-1, 0, 1]:
                if grid.get((nx + adx, ny + ady)) == '🔥': return False
        for other in wolves:
            if current_wolf and other is current_wolf: continue
            if other['x'] == nx and other['y'] == ny: return False
            if current_wolf and 'old_x' in current_wolf and 'old_x' in other:
                if nx == other['old_x'] and ny == other['old_y'] and other['x'] == current_wolf['old_x'] and other['y'] == current_wolf['old_y']:
                    return False
        return True

    def run_metro(entry_dx, entry_dy):
        nonlocal player_x, player_y, moves, mushrooms, turns_without_mushrooms, wolves, dead, last_hint_tier
        
        COLORS = {'Красная': '\033[91m', 'Жёлтая': '\033[93m', 'Зелёная': '\033[92m', 'Синяя': '\033[94m'}
        RESET = '\033[0m'
        
        passengers_list = ['🧍', '🕴️', '🧍‍♂️', '🧍‍♀️', '🧑‍🦽']
        platform_passengers = " ".join(random.choices(passengers_list, k=random.randint(2, 3)))
        
        GRAFFITI_LIST = [
            [
                "\033[32m   /\\   \033[0m   \033[92m   /\\   \033[0m   \033[32m   /\\   \033[0m   \033[92m   /\\   \033[0m",
                "\033[32m  //\\\\  \033[0m   \033[92m  //\\\\  \033[0m   \033[32m  //\\\\  \033[0m   \033[92m  //\\\\  \033[0m",
                "\033[33m   ||   \033[0m   \033[33m   ||   \033[0m   \033[33m   ||   \033[0m   \033[33m   ||   \033[0m"
            ],
            [
                "\033[91m  .---.  \033[0m   \033[95m  .---.  \033[0m   \033[91m  .---.  \033[0m",
                "\033[91m /  o  \\ \033[0m   \033[95m /  o  \\ \033[0m   \033[91m /  o  \\ \033[0m",
                "\033[97m   | |   \033[0m   \033[97m   | |   \033[0m   \033[97m   | |   \033[0m"
            ],
            [
                "\033[96m   /\\   \033[0m   \033[94m   /\\   \033[0m   \033[96m   /\\   \033[0m",
                "\033[96m  /  \\  \033[0m   \033[94m  /  \\  \033[0m   \033[96m  /  \\  \033[0m",
                "\033[96m  \\  /  \033[0m   \033[94m  \\  /  \033[0m   \033[96m  \\  /  \033[0m"
            ],
            [
                "\033[90m   _   _   \033[0m   \033[90m   _   _   \033[0m",
                "\033[90m  | | | |  \033[0m   \033[90m  | | | |  \033[0m",
                "\033[91m _|_|_|_|_ \033[0m   \033[91m _|_|_|_|_ \033[0m"
            ],
            [
                "\033[33m  \\|||/  \033[0m   \033[33m  \\|||/  \033[0m",
                "\033[33m  - 0 -  \033[0m   \033[33m  - 0 -  \033[0m",
                "\033[33m  /|||\\  \033[0m   \033[33m  /|||\\  \033[0m"
            ]
        ]
        
        def map_qwerty_ru_to_en(ch):
            if not ch: return ch
            mapping = {'ф':'a', 'и':'b', 'с':'c', 'в':'d', 'у':'e', 'а':'f', 'ч':'x'}
            return mapping.get(ch, ch)

        stops_here = get_metros_at(player_x, player_y)
        if len(stops_here) > 1:
            while True:
                clear_screen()
                term_width = shutil.get_terminal_size((80, 24)).columns
                title = "Пересадочный узел"
                print("\n" * (shutil.get_terminal_size().lines // 4))
                print(title.center(term_width))
                print("Выберите линию:".center(term_width) + "\n")
                
                letters = 'abcdef'
                opt_map = {}
                for i, st in enumerate(stops_here):
                    letter = letters[i]
                    opt = f"{letter.upper()}. {COLORS[st['line']]}{st['line']} линия ({st['name']}){RESET}"
                    vis_len = len(f"{letter.upper()}. {st['line']} линия ({st['name']})")
                    pad = max(0, (term_width - vis_len) // 2)
                    print(" " * pad + opt)
                    opt_map[letter] = st
                
                exit_opt = "X. Выйти обратно в лес (входной билет сгорел)"
                print("\n" + exit_opt.center(term_width))
                
                ch = map_qwerty_ru_to_en(get_input(menu_mode=True))
                if ch is None: sys.exit(0)
                
                if ch in opt_map:
                    cur_stop = opt_map[ch]
                    break
                elif ch == 'x':
                    moves += 1
                    player_x -= entry_dx
                    player_y -= entry_dy
                    turns_without_mushrooms = 0
                    wolves.clear()
                    return
        else:
            cur_stop = stops_here[0]

        current_graffiti = GRAFFITI_LIST[cur_stop['graffiti_seed'] % len(GRAFFITI_LIST)]

        state = "PLATFORM"
        train_dir = 1
        track_edge = "=================================================="

        def animate_subway_travel(t_dir):
            term_size = shutil.get_terminal_size((80, 24))
            term_width = term_size.columns
            fps = 10
            duration = 3
            total_frames = int(fps * duration)
            sleep_time = 1.0 / fps
            pattern = "- = - - = = - = " * (term_width // 4 + 2)
            
            tunnel_wall = [
                "\033[90m/|/|/|/|/|/|/|/|/|/|/|/|/|/|/|/|/|/|\033[0m",
                "\033[90m|/|/|/|/|/|/|/|/|/|/|/|/|/|/|/|/|/|/\033[0m",
                "\033[90m/|/|/|/|/|/|/|/|/|/|/|/|/|/|/|/|/|/|\033[0m"
            ]
            
            for frame in range(total_frames):
                clear_screen()
                if t_dir == 1:
                    offset = (frame * 2) % 16
                else:
                    offset = 16 - ((frame * 2) % 16)
                    
                top_dashes = pattern[offset : offset + term_width - 10]
                bottom_dashes = pattern[offset + 2 : offset + term_width - 8]
                
                print("\n" * max(0, term_size.lines // 4))
                
                for line in tunnel_wall:
                    print(center_ansi(line, term_width))
                    
                print(center_ansi(track_edge, term_width))
                print(top_dashes.center(term_width))
                print("       🚃🚃🚃🚃🚃🚃🚃🚃🚃       ".center(term_width))
                print(bottom_dashes.center(term_width))
                print(center_ansi(track_edge, term_width))
                time.sleep(sleep_time)

        while True:
            clear_screen()
            term_width = shutil.get_terminal_size((80, 24)).columns
            
            c = COLORS[cur_stop['line']]
            line_arr = metro_lines[cur_stop['line']]
            idx = cur_stop['idx']
            
            prev_s = line_arr[idx-1]['name'].lower() if idx > 0 else "конечная"
            next_s = line_arr[idx+1]['name'].lower() if idx < len(line_arr)-1 else "конечная"
            
            header_line = "-"*56
            name_len = len(cur_stop['name'])
            name_pad_left = (24 - name_len) // 2
            name_pad_right = 24 - name_len - name_pad_left
            name_disp = " " * name_pad_left + f"{c}{cur_stop['name'].upper()}{RESET}" + " " * name_pad_right
            
            print("\n" * max(0, shutil.get_terminal_size().lines // 5))
            print(header_line.center(term_width))
            pad_h = max(0, (term_width - 56) // 2)
            print(" " * pad_h + f" {prev_s:<15} {name_disp} {next_s:>15}")
            
            transfers = [s for s in get_metros_at(cur_stop['x'], cur_stop['y']) if s['line'] != cur_stop['line']]
            if transfers:
                t = transfers[0]
                tc = COLORS[t['line']]
                trans_str = f"Переход: {tc}{t['line']} линия ({t['name']}){RESET}"
                trans_vis_len = len(f"Переход: {t['line']} линия ({t['name']})")
                print(" " * max(0, (term_width - trans_vis_len) // 2) + trans_str)
            print(header_line.center(term_width))
            print()
            
            for line in current_graffiti:
                print(center_ansi(line, term_width))
            
            print(center_ansi(track_edge, term_width))
            
            if state == "PLATFORM":
                scene = "       🚃🚃🚃🚃🚃🚃🚃🚃🚃       "
                print(scene.center(term_width))
                print(center_ansi(track_edge, term_width))
                
                platform = "🪑 🗑️        🎫 🎫        🗑️ 🪑"
                print(platform.center(term_width))
                player_row = f"🚴‍♀️  {platform_passengers}"
                print(player_row.center(term_width))
                print()
                
                options = []
                opt_map = {}
                letters = 'abcdef'
                cnt = 0
                
                if idx < len(line_arr) - 1:
                    l = letters[cnt]
                    options.append(f"{l.upper()}. Сесть в поезд (к ст. {line_arr[idx+1]['name']})")
                    opt_map[l] = ('BOARD', 1)
                    cnt += 1
                if idx > 0:
                    l = letters[cnt]
                    options.append(f"{l.upper()}. Сесть в поезд (к ст. {line_arr[idx-1]['name']})")
                    opt_map[l] = ('BOARD', -1)
                    cnt += 1
                    
                for t in transfers:
                    l = letters[cnt]
                    tc = COLORS[t['line']]
                    options.append((f"{l.upper()}. Перейти на {tc}{t['line']} линия ({t['name']}){RESET}", len(f"{l.upper()}. Перейти на {t['line']} линия ({t['name']})")))
                    opt_map[l] = ('TRANSFER', t)
                    cnt += 1
                    
                options.append("X. Выйти на поверхность")
                opt_map['x'] = ('EXIT', None)
                
                for o in options:
                    if isinstance(o, tuple):
                        text, vlen = o
                        print(" " * max(0, (term_width - vlen) // 2) + text)
                    else:
                        print(o.center(term_width))
                
                ch = map_qwerty_ru_to_en(get_input(menu_mode=True))
                if ch is None: sys.exit(0)
                
                if ch in opt_map:
                    action, arg = opt_map[ch]
                    if action == 'EXIT':
                        moves += 1
                        player_x = cur_stop['x'] - entry_dx
                        player_y = cur_stop['y'] - entry_dy
                        turns_without_mushrooms = 0
                        wolves.clear()
                        return
                    elif action == 'TRANSFER':
                        moves += 1
                        cur_stop = arg
                        current_graffiti = GRAFFITI_LIST[cur_stop['graffiti_seed'] % len(GRAFFITI_LIST)]
                        platform_passengers = " ".join(random.choices(passengers_list, k=random.randint(2, 3)))
                    elif action == 'BOARD':
                        moves += 1
                        train_dir = arg
                        state = "TRAIN"
                        
            elif state == "TRAIN":
                scene = "  (вы внутри) 🚃🚃🚃🚃🚃🚃🚃🚃🚃  "
                print(scene.center(term_width))
                print(center_ansi(track_edge, term_width))
                
                platform = "🪑 🗑️        🎫 🎫        🗑️ 🪑"
                print(platform.center(term_width))
                print(f"    {platform_passengers}".center(term_width))
                print()
                
                animate_subway_travel(train_dir)
                cur_stop = line_arr[cur_stop['idx'] + train_dir]
                current_graffiti = GRAFFITI_LIST[cur_stop['graffiti_seed'] % len(GRAFFITI_LIST)]
                
                platform_passengers = " ".join(random.choices(passengers_list, k=random.randint(2, 3)))
                
                is_terminal = (train_dir == 1 and cur_stop['idx'] == len(line_arr)-1) or \
                              (train_dir == -1 and cur_stop['idx'] == 0)
                              
                clear_screen()
                c = COLORS[cur_stop['line']]
                idx = cur_stop['idx']
                prev_s = line_arr[idx-1]['name'].lower() if idx > 0 else "конечная"
                next_s = line_arr[idx+1]['name'].lower() if idx < len(line_arr)-1 else "конечная"
                name_len = len(cur_stop['name'])
                name_pad_left = (24 - name_len) // 2
                name_pad_right = 24 - name_len - name_pad_left
                name_disp = " " * name_pad_left + f"{c}{cur_stop['name'].upper()}{RESET}" + " " * name_pad_right
                
                print("\n" * max(0, shutil.get_terminal_size().lines // 5))
                print(header_line.center(term_width))
                print(" " * pad_h + f" {prev_s:<15} {name_disp} {next_s:>15}")
                
                transfers = [s for s in get_metros_at(cur_stop['x'], cur_stop['y']) if s['line'] != cur_stop['line']]
                if transfers:
                    t = transfers[0]
                    tc = COLORS[t['line']]
                    trans_str = f"Переход: {tc}{t['line']} линия ({t['name']}){RESET}"
                    trans_vis_len = len(f"Переход: {t['line']} линия ({t['name']})")
                    print(" " * max(0, (term_width - trans_vis_len) // 2) + trans_str)
                print(header_line.center(term_width))
                print()
                
                for line in current_graffiti:
                    print(center_ansi(line, term_width))
                    
                print(center_ansi(track_edge, term_width))
                print(scene.center(term_width))
                print(center_ansi(track_edge, term_width))
                
                platform = "🪑 🗑️        🎫 🎫        🗑️ 🪑"
                print(platform.center(term_width))
                print(f"    {platform_passengers}".center(term_width))
                print()
                
                if is_terminal:
                    msg = "Конечная станция. Выходим на платформу..."
                    print(msg.center(term_width))
                    get_input(menu_mode=True)
                    state = "PLATFORM"
                else:
                    next_st = line_arr[cur_stop['idx'] + train_dir]
                    msg_next = f"Следующая: {next_st['name']}"
                    print(msg_next.center(term_width))
                    print("A. Ехать дальше".center(term_width))
                    print("B. Выйти на платформе".center(term_width))
                    
                    ch = map_qwerty_ru_to_en(get_input(menu_mode=True))
                    if ch is None: sys.exit(0)
                    if ch == 'a':
                        moves += 1
                    elif ch == 'b':
                        state = "PLATFORM"

    def render_rain(is_acid_rain):
        clear_screen()
        term_size = shutil.get_terminal_size((80, 24))
        term_width = term_size.columns
        term_height = term_size.lines
        
        if mansion_visited:
            h1 = f"\033[90m🍄 Грибов: 0 (Сдано: {given_mushrooms})\033[0m | 💎 Алмазов: {diamonds}"
        else:
            h1 = f"🍄 Грибов: {mushrooms}"
            
        h2 = f"💀 Волков: {dead_wolves} | 🔥 Комбо: {total_combos} | ⏱ Ходы: {moves} | 💠 8-way: {'ВКЛ' if diagonal_unlocked else 'ВЫКЛ'}"
        h3 = f"🚀 Скор: 0 🛑 | 💨 Дуть: Р | 🧭 Ход: Е Н Г / П О / М И Т | Выход: Й/Q"
        
        feedback = "🧪 КИСЛОТНЫЙ ДОЖДЬ (9x9)!" if is_acid_rain else "🌧️ ЛОКАЛЬНЫЙ ДОЖДЬ (9x9)!"
        wrapped_feedback = textwrap.wrap(feedback, width=min(term_width, 45))
        
        R = 7
        grid_width_visual = (2 * R + 1) * 3
        total_height = 3 + len(wrapped_feedback) + 1 + (2 * R + 1)
        v_pad = max(0, (term_height - total_height) // 2)
        h_pad_grid = max(0, (term_width - grid_width_visual) // 2)
        
        print("\n" * v_pad, end="")
        print(" " * max(0, (term_width - visible_len(h1)) // 2) + h1)
        print(" " * max(0, (term_width - visible_len(h2)) // 2) + h2)
        print(" " * max(0, (term_width - visible_len(h3)) // 2) + h3)
        for line in wrapped_feedback: print(" " * max(0, (term_width - visible_len(line)) // 2) + line)
        print() 
        
        for y in range(player_y - R, player_y + R + 1):
            row_str = ""
            for x in range(player_x - R, player_x + R + 1):
                if not is_visible(x - player_x, y - player_y): row_str += "   "
                elif x == player_x and y == player_y: row_str += "🚴‍♀️ "
                elif abs(x - player_x) <= 4 and abs(y - player_y) <= 4: row_str += "🧪 " if is_acid_rain else "🌧️ "
                else:
                    is_wolf = next((w for w in wolves if w['x'] == x and w['y'] == y), None)
                    if is_wolf: row_str += get_wolf_emoji(is_wolf) + " "
                    else: row_str += grid.get((x, y), '..') + " "
            print(" " * h_pad_grid + row_str)

    def render():
        clear_screen()
        term_size = shutil.get_terminal_size((80, 24))
        term_width = term_size.columns
        term_height = term_size.lines
        
        dir_emoji = "🛑"
        if speed > 0:
            if last_dx == 0 and last_dy == -1: dir_emoji = "⬆️"
            elif last_dx == 0 and last_dy == 1: dir_emoji = "⬇️"
            elif last_dx == -1 and last_dy == 0: dir_emoji = "⬅️"
            elif last_dx == 1 and last_dy == 0: dir_emoji = "➡️"
            elif last_dx == -1 and last_dy == -1: dir_emoji = "↖️"
            elif last_dx == 1 and last_dy == -1: dir_emoji = "↗️"
            elif last_dx == -1 and last_dy == 1: dir_emoji = "↙️"
            elif last_dx == 1 and last_dy == 1: dir_emoji = "↘️"
        
        speed_str = f"{speed} {dir_emoji}".strip()
        
        if mansion_visited:
            h1 = f"\033[90m🍄 Грибов: 0 (Сдано: {given_mushrooms})\033[0m | 💎 Алмазов: {diamonds}"
        else:
            h1 = f"🍄 Грибов: {mushrooms}"
            
        h2 = f"💀 Волков: {dead_wolves} | 🔥 Комбо: {total_combos} | ⏱ Ходы: {moves} | 💠 8-way: {'ВКЛ' if diagonal_unlocked else 'ВЫКЛ'}"
        h3 = f"🚀 Скор: {speed_str} | 💨 Дуть: Р | 🧭 Ход: Е Н Г / П О / М И Т | Выход: Й/Q"
        
        wrapped_feedback = textwrap.wrap(feedback_msg, width=min(term_width, 60)) if feedback_msg else []
        
        R = 7
        grid_width_visual = (2 * R + 1) * 3
        total_height = 3 + max(1, len(wrapped_feedback)) + 1 + (2 * R + 1) + (15 if dead else 0)
        v_pad = max(0, (term_height - total_height) // 2)
        h_pad_grid = max(0, (term_width - grid_width_visual) // 2)
        
        print("\n" * v_pad, end="")
        print(" " * max(0, (term_width - visible_len(h1)) // 2) + h1)
        print(" " * max(0, (term_width - visible_len(h2)) // 2) + h2)
        print(" " * max(0, (term_width - visible_len(h3)) // 2) + h3)
        if wrapped_feedback:
            for line in wrapped_feedback: print(" " * max(0, (term_width - visible_len(line)) // 2) + line)
        else: print()
        print() 
        
        for y in range(player_y - R, player_y + R + 1):
            row_str = ""
            for x in range(player_x - R, player_x + R + 1):
                if not is_visible(x - player_x, y - player_y): row_str += "   "
                elif dead and x == player_x and y == player_y: row_str += "💀 "
                elif not dead and x == player_x and y == player_y: row_str += "🚴‍♀️ "
                else:
                    is_wolf = next((w for w in wolves if w['x'] == x and w['y'] == y), None)
                    if is_wolf: row_str += get_wolf_emoji(is_wolf) + " "
                    else: row_str += grid.get((x, y), '..') + " "
            print(" " * h_pad_grid + row_str)
            
        if dead:
            screamer = [
                r" ▄████▄   ██▀███   ▄▄▄       ██████  ██░ ██ ",
                r"▒██▀ ▀█  ▓██ ▒ ██▒▒████▄   ▒██    ▒ ▓██░ ██▒",
                r"▒▓█    ▄ ▓██ ░▄█ ▒▒██  ▀█▄ ░ ▓██▄   ▒██▀▀██░",
                r"▒▓▓▄ ▄██▒▒██▀▀█▄  ░██▄▄▄▄██  ▒   ██▒░▓█ ░██ ",
                r"▒ ▓███▀ ░░██▓ ▒██▒ ▓█   ▓██▒▒██████▒▒░▓█▒░██▓",
                r"░ ░▒ ▒  ░░ ▒▓ ░▒▓░ ▒▒   ▓▒█░▒ ▒▓▒ ▒ ░ ▒ ░░▒░▒",
                r"  ░  ▒     ░▒ ░ ▒░  ▒   ▒▒ ░░ ░▒  ░ ░ ▒ ░▒░ ░",
                r"░          ░░   ░   ░   ▒   ░  ░  ░   ░  ░░ ░",
                r"░ ░         ░           ░  ░      ░   ░  ░  ░",
                r"░                                            "
            ]
            msg1 = f"💀 ИГРА ОКОНЧЕНА! {death_msg}"
            wrapped_death = textwrap.wrap(msg1, width=min(term_width, 45))
            msg2 = "Нажмите любую клавишу для выхода..."
            print("\n", end="")
            for line in screamer: print(" " * max(0, (term_width - visible_len(line)) // 2) + line)
            print()
            for line in wrapped_death: print(" " * max(0, (term_width - visible_len(line)) // 2) + line)
            print("\n" + " " * max(0, (term_width - visible_len(msg2)) // 2) + msg2)

    generate_viewport()
    
    while True:
        ignited_this_turn = False
        
        if turn_events or combo_msg:
            feedback_msg = " | ".join(turn_events)
            if combo_msg: feedback_msg = f"{feedback_msg}  {combo_msg}".strip(" | ")
        else:
            feedback_msg = ""
            
        render()
        turn_events = []
        combo_msg = ""
        
        if dead:
            get_input()
            break
            
        inp = get_input()
        if inp is None: break
        if inp == (0, 0): continue
            
        combo_action_this_turn = False
        boosters_collected_this_turn = 0
            
        if inp == 'blow':
            fires = []
            for dx in [-1, 0, 1]:
                for dy in [-1, 0, 1]:
                    if dx == 0 and dy == 0: continue
                    if grid.get((player_x + dx, player_y + dy)) == '🔥': fires.append((dx, dy))
                        
            target_fire = None
            fires_around = len(fires)
            
            if fires_around == 1: target_fire = fires[0]
            elif fires_around > 1:
                dir_names = {(0,-1): "вверх", (0,1): "вниз", (-1,0): "влево", (1,0): "вправо", (-1,-1): "вверх-влево", (1,-1): "вверх-вправо", (-1,1): "вниз-влево", (1,1): "вниз-вправо"}
                dir_keys_display = {(0,-1): "Н", (0,1): "И", (-1,0): "П", (1,0): "О", (-1,-1): "Е", (1,-1): "Г", (-1,1): "М", (1,1): "Т"}
                options_str = [f"{dir_keys_display.get(f)} ({dir_names.get(f)})" for f in fires]
                
                feedback_msg = f"🔥 Укажите направление костра: " + ", ".join(options_str)
                render()
                while True:
                    choice = get_input(menu_mode=False)
                    if isinstance(choice, tuple) and choice in fires:
                        target_fire = choice
                        break
                    elif choice is None: return
                        
            trees_ignited_by_blow = 0
            wolves_burned_by_blow = 0
            if target_fire:
                fdx, fdy = target_fire
                tx, ty = player_x + 2*fdx, player_y + 2*fdy
                generate_cell(tx, ty) 
                if grid.get((tx, ty)) == '🌲':
                    i_t, w_b = ignite_tree_group(tx, ty)
                    trees_ignited_by_blow += i_t
                    wolves_burned_by_blow += w_b
            
            speed = 0
            last_dx, last_dy = 0, 0
            diagonal_unlocked = False 
            moves += 1
            turns_without_mushrooms += 1 
            boosters_active = 0
            
            if fires_around == 0: turn_events.append("💨 Вы подули, но костра рядом нет.")
            elif trees_ignited_by_blow > 0:
                combo_action_this_turn = True
                msg = f"🌲 Вы успешно подожгли лес!"
                if wolves_burned_by_blow > 0: msg += f" Волки сгорели: {wolves_burned_by_blow}!"
                turn_events.append(msg)
                ignited_this_turn = True 
            else: turn_events.append("💨 Вы подули на костёр, но ни одно дерево не загорелось.")
            generate_viewport(0, 0)
                
        elif isinstance(inp, tuple):
            dx, dy = inp
            if abs(dx) == 1 and abs(dy) == 1 and not diagonal_unlocked:
                turn_events.append("❌ Диагональный ход недоступен! Нужно собрать/сбить 2+ объекта за 1 ход.")
                continue
            
            was_speed_zero = (speed == 0)
            if speed > 0:
                if dx == last_dx and dy == last_dy: base_speed = speed + 1
                elif (dx - last_dx)**2 + (dy - last_dy)**2 == 1: base_speed = speed
                else: base_speed = 1
            else:
                base_speed = 1
                
            current_speed = base_speed + boosters_active
                
            next_x, next_y = player_x + dx, player_y + dy
            generate_cell(next_x, next_y, dx, dy)
            
            if grid[(next_x, next_y)] in ['🌲', '🏭', '🏯', '🏮', 'Ⓜ️']:
                angle_valid = was_speed_zero or (last_dx*dx + last_dy*dy <= 0)
                if grid[(next_x, next_y)] == '🏯' and not mansion_visited and was_speed_zero and current_speed == 1 and next_x == mansion_x and next_y == mansion_y: pass
                elif grid[(next_x, next_y)] == 'Ⓜ️' and current_speed == 1 and angle_valid: pass
                else: continue
                
            mushrooms_this_turn = 0
            diamonds_this_turn = 0
            wolves_killed_this_turn = 0
            entered_mansion = False
            entered_metro = False
            picked_up_gun = False
                
            for step in range(1, current_speed + 1):
                cx, cy = player_x + dx * step, player_y + dy * step
                generate_cell(cx, cy, dx, dy)
                
                if grid[(cx, cy)] == '🔥':
                    dead, death_msg, player_x, player_y = True, "Вы сдуру заехали прямо в костёр и сгорели заживо!", cx, cy
                    break
                    
                if grid[(cx, cy)] == '🏯':
                    if not mansion_visited and was_speed_zero and current_speed == 1 and cx == mansion_x and cy == mansion_y: entered_mansion = True; break
                    else:
                        dead, death_msg, player_x, player_y = True, "Вы врезались в стену роскошного особняка и разбились!", cx, cy
                        break

                if grid[(cx, cy)] == 'Ⓜ️':
                    angle_valid = was_speed_zero or (last_dx*dx + last_dy*dy <= 0)
                    if current_speed == 1 and angle_valid:
                        if mushrooms >= 5:
                            mushrooms -= 5
                            last_hint_tier = mushrooms // 5
                            player_x, player_y = cx, cy
                            entered_metro = True
                            break
                        else:
                            turn_events.append("❌ Не хватает грибов для оплаты проезда в метро! (Нужно 5)")
                            player_x, player_y = cx - dx, cy - dy
                            current_speed = step - 1
                            break
                    else:
                        dead, death_msg, player_x, player_y = True, "Вы на скорости влетели в бетонный спуск станции метро и разбились!", cx, cy
                        break
                        
                if grid[(cx, cy)] in ['🏮', '🌲', '🏭']:
                    obst = {"🏮": "красный фонарь", "🌲": "дерево", "🏭": "заводскую стену"}
                    dead, death_msg, player_x, player_y = True, f"Вы на огромной скорости влетели в {obst[grid[(cx, cy)]]} и разбились!", cx, cy
                    break
                    
                wolf_here = next((w for w in wolves if w['x'] == cx and w['y'] == cy), None)
                if wolf_here:
                    if step == current_speed:
                        dead, death_msg, player_x, player_y = True, "Вы остановились прямо перед волком, и он вас загрыз!", cx, cy
                        wolves.remove(wolf_here)
                        break
                    else:
                        wolves.remove(wolf_here)
                        dead_wolves += 1
                        wolves_killed_this_turn += 1
                        
                if grid[(cx, cy)] == '🍄':
                    mushrooms += 1
                    mushrooms_this_turn += 1
                    grid[(cx, cy)] = '..'
                elif grid[(cx, cy)] == '💎':
                    diamonds += 1
                    diamonds_this_turn += 1
                    grid[(cx, cy)] = '..'
                elif grid[(cx, cy)] == '🚀':
                    boosters_collected_this_turn += 1
                    grid[(cx, cy)] = '..'
                elif grid[(cx, cy)] == '🔫':
                    picked_up_gun = True
                    grid[(cx, cy)] = '..'
                    current_speed = step
                    break

            if entered_mansion:
                trigger_mansion_scene()
                speed, last_dx, last_dy, turns_without_mushrooms, diagonal_unlocked, moves = 0, 0, 0, 0, False, moves + 1
                boosters_active = 0
                generate_viewport(0, 0)
                continue

            if entered_metro:
                moves += 1
                run_metro(dx, dy)
                speed, last_dx, last_dy, diagonal_unlocked = 0, 0, 0, False
                boosters_active = 0
                generate_viewport(0, 0)
                continue

            if not dead:
                if wolves_killed_this_turn > 0: turn_events.append(f"🐺 Вы сбили волков: {wolves_killed_this_turn}!")
                if boosters_collected_this_turn > 0: turn_events.append(f"🚀 Вы подобрали бустер! Скорость на следующем ходу +{boosters_collected_this_turn}")
                items_collected = mushrooms_this_turn + diamonds_this_turn + wolves_killed_this_turn + boosters_collected_this_turn
                if items_collected >= 2:
                    diagonal_unlocked = True
                    turn_events.append("✨ Доступен ход по диагонали на следующий ход! (Е, Г, М, Т)")
                else: diagonal_unlocked = False
                    
                if items_collected > 0: combo_action_this_turn = True
                turns_without_mushrooms = 0 if items_collected > 0 else turns_without_mushrooms + 1
                    
                player_x += dx * current_speed
                player_y += dy * current_speed
                last_dx, last_dy = dx, dy
                speed = current_speed
                moves += 1
                boosters_active = boosters_collected_this_turn
                
                if picked_up_gun:
                    targets = {}
                    dirs = [(-1,-1), (0,-1), (1,-1), (-1,0), (1,0), (-1,1), (0,1), (1,1)]
                    for dir_dx, dir_dy in dirs:
                        tx, ty = player_x, player_y
                        while True:
                            tx += dir_dx
                            ty += dir_dy
                            if not is_visible(tx - player_x, ty - player_y):
                                break
                            generate_cell(tx, ty)
                            wolf_hit = next((w for w in wolves if w['x'] == tx and w['y'] == ty), None)
                            if wolf_hit:
                                targets[(dir_dx, dir_dy)] = wolf_hit
                                break
                            cell = grid.get((tx, ty), '..')
                            if cell not in ['..', '🍄', '🚀', '🔥', '🔫']:
                                break
                                
                    if len(targets) == 0:
                        turn_events.append("🔫 Вы подобрали пистолет и выстрелили в воздух, но ни в кого не попали.")
                    elif len(targets) == 1:
                        dir_key = list(targets.keys())[0]
                        wolf_hit = targets[dir_key]
                        wolves.remove(wolf_hit)
                        dead_wolves += 1
                        combo_action_this_turn = True
                        turns_without_mushrooms = 0
                        turn_events.append("🔫 Вы метким выстрелом убили волка!")
                    else:
                        dir_names = {(0,-1): "вверх", (0,1): "вниз", (-1,0): "влево", (1,0): "вправо", (-1,-1): "вверх-влево", (1,-1): "вверх-вправо", (-1,1): "вниз-влево", (1,1): "вниз-вправо"}
                        dir_keys_display = {(0,-1): "Н", (0,1): "И", (-1,0): "П", (1,0): "О", (-1,-1): "Е", (1,-1): "Г", (-1,1): "М", (1,1): "Т"}
                        options_str = [f"{dir_keys_display.get(d)} ({dir_names.get(d)})" for d in targets.keys()]
                        
                        feedback_msg = f"🔫 Укажите направление выстрела: " + ", ".join(options_str)
                        render()
                        chosen_dir = None
                        while True:
                            choice = get_input(menu_mode=False)
                            if isinstance(choice, tuple) and choice in targets:
                                chosen_dir = choice
                                break
                            elif choice is None: sys.exit(0)
                        
                        wolf_hit = targets[chosen_dir]
                        wolves.remove(wolf_hit)
                        dead_wolves += 1
                        combo_action_this_turn = True
                        turns_without_mushrooms = 0
                        turn_events.append(f"🔫 Вы прицелились и застрелили волка ({dir_names[chosen_dir]})!")

                    speed = 0
                    last_dx, last_dy = 0, 0
                    diagonal_unlocked = False
                    boosters_active = 0
                    if "✨ Доступен ход по диагонали на следующий ход! (Е, Г, М, Т)" in turn_events:
                        turn_events.remove("✨ Доступен ход по диагонали на следующий ход! (Е, Г, М, Т)")

                current_hint_tier = mushrooms // 5
                if not mansion_visited and current_hint_tier > last_hint_tier:
                    last_hint_tier = current_hint_tier
                    dist = round(math.hypot(mansion_x - player_x, mansion_y - player_y), 1)
                    turn_events.append(f"🗺️ ПОДСКАЗКА: Особняк хозяина лесных заводов находится на расстоянии {dist} клеток.")
                
                generate_viewport(dx, dy)
            
        if not dead:
            if combo_action_this_turn:
                combo_streak += 1
                if combo_streak >= 2:
                    total_combos += 1
                    recent_combos.append(1)
                    combo_msg = f"🔥 {random.choice(['Супер!', 'Огонь!', 'Отлично!', 'Безумие!', 'Невероятно!', 'Мощно!'])} Комбо x{combo_streak}! 🔥"
                else:
                    recent_combos.append(0)
            else: 
                combo_streak = 0
                recent_combos.append(0)
                
            if len(recent_combos) > 10:
                recent_combos.pop(0)
                
            died_from_acid = 0
            for w in list(wolves):
                if 'acid_stage' in w:
                    w['acid_stage'] += 1
                    if w['acid_stage'] >= 4:
                        wolves.remove(w)
                        dead_wolves += 1
                        died_from_acid += 1
            if died_from_acid > 0: turn_events.append(f"💀 Кислота разъела скелеты волков (👾 -> ☠️): {died_from_acid} шт!")
            for w in wolves: w['old_x'], w['old_y'] = w['x'], w['y']

            if not dead:
                for w in wolves:
                    if w.get('has_booster'):
                        w['has_booster'] = False
                        wdx, wdy = player_x - w['x'], player_y - w['y']
                        if max(abs(wdx), abs(wdy)) == 2 and (abs(wdx) == 2 or wdx == 0) and (abs(wdy) == 2 or wdy == 0):
                            step_x, step_y = wdx // 2, wdy // 2
                            mid_x, mid_y = w['x'] + step_x, w['y'] + step_y
                            if grid.get((mid_x, mid_y), '..') in ['..', '🍄']:
                                is_player_near_fire = any(grid.get((player_x + adx, player_y + ady)) == '🔥' for adx in [-1, 0, 1] for ady in [-1, 0, 1])
                                if not is_player_near_fire:
                                    w['x'], w['y'] = player_x, player_y
                                    dead = True
                                    death_msg = "Волк под бустером 🚀 совершил молниеносный прыжок на 2 клетки и загрыз вас!"
                                    wolves.remove(w)
                                    break

            for w in wolves:
                if dead: break
                if w['just_spawned']:
                    w['just_spawned'] = False
                    continue
                wx, wy = w['x'], w['y']
                wdx, wdy = (player_x > wx) - (player_x < wx), (player_y > wy) - (player_y < wy)
                
                candidates = [(wdx, wdy), (wdx, 0), (0, wdy)] if wdx != 0 and wdy != 0 else ([(wdx, 0), (wdx, 1), (wdx, -1)] if wdx != 0 else [(0, wdy), (1, wdy), (-1, wdy)])
                for cdx, cdy in candidates:
                    tx, ty = wx + cdx, wy + cdy
                    generate_cell(tx, ty)
                    if not is_safe_for_wolf(tx, ty, w): continue
                    w['x'], w['y'] = tx, ty
                    break
                    
                if w['x'] == player_x and w['y'] == player_y:
                    dead, death_msg = True, "Голодный волк догнал вас и загрыз!"
                    wolves.remove(w)
                    break
                if not dead and grid.get((w['x'], w['y'])) in ['🍄', '💎', '🚀']:
                    if grid.get((w['x'], w['y'])) == '🚀':
                        w['has_booster'] = True
                        grid[(w['x'], w['y'])] = '..'
                    elif grid.get((w['x'], w['y'])) == '🍄': 
                        grid[(w['x'], w['y'])] = '..'

            if not dead and turns_without_mushrooms >= 10:
                combo_streak, diagonal_unlocked = 0, False 
                if "✨ Доступен ход по диагонали на следующий ход! (Е, Г, М, Т)" in turn_events: turn_events.remove("✨ Доступен ход по диагонали на следующий ход! (Е, Г, М, Т)")
                
                is_acid_rain = any(grid.get((player_x + dx, player_y + dy)) == '🏭' for dy in range(-4, 5) for dx in range(-4, 5))
                
                # Показываем результат поджога, если он произошел в этот самый ход перед дождем
                if ignited_this_turn:
                    current_feedback = " | ".join(turn_events)
                    if combo_msg: current_feedback = f"{current_feedback}  {combo_msg}".strip(" | ")
                    
                    original_feedback = feedback_msg
                    feedback_msg = current_feedback
                    render()
                    time.sleep(1.0)
                    feedback_msg = original_feedback
                
                render_rain(is_acid_rain)
                time.sleep(3.5) 
                
                for dy in range(-4, 5):
                    for dx in range(-4, 5):
                        pos = (player_x + dx, player_y + dy)
                        if grid.get(pos) == '🔥':
                            grid[pos] = '..'
                            if pos in burning_trees_cells: burning_trees_cells.remove(pos)
                if is_acid_rain:
                    for w in wolves:
                        if abs(w['x'] - player_x) <= 4 and abs(w['y'] - player_y) <= 4: w['acid_stage'] = 0
                
                speed, last_dx, last_dy, turns_without_mushrooms = 0, 0, 0, 0
                boosters_active = 0
                turn_events.append("🧪 Прошёл КИСЛОТНЫЙ дождь! Костры в зоне 9x9 потухли, волки облучены." if is_acid_rain else "🌧️ Прошёл локальный дождь! Костры в зоне 9x9 потухли, комбо сброшено.")

if __name__ == "__main__":
    main()