Маруся-Нетакуся

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

Маруся-Нетакуся — это не просто игра, это целая история о девушке по имени Маруся. Маруся-Нетакуся. И у неё есть мечта. Нет, не выйти замуж за олигарха. Хотя… нет, не это.

Её мечта — найти отца. Звучит трогательно, да? Отцы и дети, вечная тема. Тургенев там, «Отцы и дети»… Но есть нюанс. Её отец не ушёл на войну. Он не ушёл в другую семью. Он не ушёл за сигаретами, как это обычно бывает. Нет, это было бы слишком банально для нашего города. Папаша Маруси 16 лет назад ушёл… за хлебом.

Так, стоп. 16 лет. За хлебом. Вы понимаете масштаб трагедии? Это не просто «вышел и пропал». Это осознанный выбор. Человек взвесил все «за» и «против»: с одной стороны — жена, маленькая дочь, семейный очаг. С другой — свежая, хрустящая буханка бородинского. И, как видите, выбор был очевиден! Хлеб победил. Счёт 1:0 в пользу хлебопекарной промышленности. И, честно говоря, зная характер нашей Маруси, я начинаю подозревать, что папаша не просто ушёл. Он сбежал. Он, наверное, бежал так быстро, что его тапки обогнали его на повороте. И вот он, спустя 16 лет, возможно, живёт где-нибудь в пекарне, ест батоны, и у него всё хорошо. Потому что у человека, который ушёл от Маруси, не может быть плохо. Это как сбежать из горящего дома. Даже если на улице дождь, ты всё равно счастлив, что не сгорел.

Итак, вступление окончено. Давайте разберём этот шедевр геймдизайна по косточкам, по винтикам, по пикселям. Это будет долгий разговор, потому что тут материала — как в предвыборной кампании. Итак, начинаем наш разбор. Поехали.

Архитектура и городское планирование, или квадратно-гнездовой способ застройки[править]

Первое, что бросается в глаза в этой игре — это город. Это не просто город. Это… трактат по геометрии. Постмодернистское высказывание. Город, который спроектировал человек, который слышал про градостроительство, но решил, что это слишком сложно. Зачем нам кривые улочки, как в Париже? Зачем нам запутанные переулки, как в Праге? Зачем нам история, как в Риме? Нет! Нам нужен идеальный квадрат. Квадрат, разделённый на квадраты, внутри которых квадраты. Это как если бы Казимир Малевич выиграл тендер на реновацию и застроил всё «Чёрными квадратами», но с деревьями.

И вот в этом городе живут люди. Нормальные, адекватные люди. И у них есть свои районы. Названия районов — это отдельная песня. Это не просто названия, это диагнозы. «Ктулховский район». Вы можете себе представить риелтора? «У нас отличная квартира в Ктулховском районе! Развитая инфраструктура, рядом школа, детский сад, и наш владыка Дагон! Очень удобная транспортная развязка, пять минут до Р’льеха!» Или «Дровосексуальный район». Я, честно, боюсь даже спрашивать, какие там вакансии в центре занятости. Наверное, «требуется специалист по интимной близости с древесиной». А «Пердолётный район»? Это же просто гимн авиации! Туда, наверное, не нужны билеты. Просто копишь газы, и — вжух! — ты уже там. «Навуходопоносорский» — для любителей древней истории. «Снюсоедский» — для гурманов. «Ясос-Бибский» — для тех, кто в поиске. А есть ещё «Убитоенотский». Просто… Убитый енот. Как символ. Как тотем. Я представляю герб этого района: на зелёном поле — распластанный енот, пронзённый стрелой. Девиз: «Мы не виноваты».

И вот в этом архитектурном безумии живёт наша Маруся. И она, как ни странно, идеально вписывается. Потому что её внутренний мир — это такой же хаос, только вместо проспектов там — неудовлетворённые желания, а вместо тупиков — завышенная самооценка. Город — это метафора её души. Правда, в её душе, в отличие от города, нет работающей канализации. Отсюда и все проблемы.

Но самое гениальное в городе — это дороги. Они есть. Они куда-то ведут. Точнее, они должны куда-то вести. Но по факту, это сеть, сплетённая пьяным пауком, которого били током. Чтобы перейти дорогу, нужно обладать квалификацией сапёра и удачей каскадёра. И вот тут мы подходим к одному из самых важных элементов геймплея — бабушке.

Бабушка-камикадзе, или «Спасибо, внучка, я всем расскажу, какая ты хорошая девочка»[править]

В этой игре есть мини-квест. Классический. Перевести бабушку через дорогу. Казалось бы, что может быть проще? Ты подходишь к старушке, предлагаешь помощь, она соглашается, и вы, не спеша, на зелёный свет, переходите дорогу. Идиллия. Пастораль. Но не в этом городе.

Здесь бабушка — это не просто бабушка. Это — терминатор. Это — замаскированный суперсолдат. У неё нет тормозов, нет чувства самосохранения, и, самое главное, у неё нет страха. Она стоит на обочине и ждёт. Ждёт свою жертву. Свою внучку.

И вот ты, весь такой благородный, подходишь к ней. «Давайте я переведу Вас через дорогу». И тут начинается. Эта бабушка, которая, казалось бы, должна молиться на каждый свой сустав, срывается с места, как ракета «Булава». И ты должен идти с ней НОГА В НОГУ. Ты — её личный ассистент по выживанию. Ты — её телохранитель. Ты — её поводырь в мир, где автобусы ездят по расписанию «всегда».

И вот ты идёшь. Шаг. Ещё шаг. Бабушка смотрит вперёд. Ты смотришь на дорогу. И тут ты понимаешь, что весь этот город, все эти дороги, все эти пробки — они не просто так. Они созданы, чтобы убить эту конкретную бабушку. Каждый автобус — это управляемая ракета, нацеленная на её божий одуванчик.

А если ты, не дай бог, решишь изменить направление? Если ты поймёшь, что на вас несётся многотонный «Икарус» с горящим взглядом водителя? Если ты инстинктивно дёрнешься в сторону? Всё. Конец. Ты не можешь бросить бабушку. Ты прикован к ней, как к галере. Ты — Сизиф. Бабушка — камень. Дорога — гора. Автобус — Зевс с обострением геморроя.

Логика у бабушки железная. Она считает, что раз ты предложил помощь, то ты несёшь за неё полную ответственность. И не важно, что ты увидел, как на вас несётся смерть. Ты не имеешь права на панику. Ты должен идти ровно, смотреть вперёд и молиться, чтобы водитель автобуса в этот момент не отвлёкся на смс от жены. Потому что, если он отвлечётся, то бабушка станет не просто бабушкой. Она станет… артефактом. Ленточкой. Памятью.

И вот, если тебе чудом удастся перевести её через эту полосу препятствий, она говорит эту гениальную фразу: «Спасибо, внучка. Я всем расскажу, какая ты хорошая девочка». Стоп! Всем?! Бабушка, а не много ли ты на себя берёшь? Ты что, блогер? У тебя своя газета? Ты главный редактор районного паблика «Подслушано у Пердолётного»? Кому ты расскажешь? Своим подругам на лавочке? «Знаете, а я сегодня познакомилась с замечательной девушкой! Она перевела меня через дорогу. А потом, когда мы дошли до дома, она пошла дальше и, кажется, собиралась собирать розы в саду у Ктулховского района». Всё, репутация испорчена.

И самое смешное, что Маруся реально на это ведётся. Она реально считает, что помощь бабушке — это её билет в рай. Что этот поступок перевесит все её остальные «подвиги». Что там, в небесной канцелярии, ангел-секретарь поставит галочку: «Перевела бабушку через дорогу. Зачтено. Минус один грех за попытку соблазнить механика». Ага, сейчас. Не в этом городе.

Эта игра беспощадно высмеивает лицемерие. Маруся делает «добрые» дела не потому, что она добрая. А потому, что ей это выгодно. Ей нужна репутация. Ей нужно, чтобы о ней говорили. Ей нужно, чтобы отец, этот великий хлебный странник, узнал, какая у него замечательная дочь. Но, как мы знаем, папаше на это глубоко фиолетово. Он там, с хлебом, и ему хорошо.

Бабушка, кстати, тоже хороша. Она не задаётся вопросом: «А почему эта девушка с маникюром и в мини-юбке помогает мне перейти дорогу?» Ей всё равно. Ей нужна помощь. И она её получает. А то, что её спасительница — городская сумасшедшая с комплексом спасителя, — это уже детали. Главное, что до дома довели. А что было по дороге — это уже не важно. Подумаешь, пару раз чуть не сбили автобусом. Зато какая история!

Флорист-эксгибиционист, или розы, ленточки и селфи на фоне истукана[править]

Помимо бабушки, у Маруси есть ещё одна страсть. Это сбор природных материалов. Она собирает розы. И не просто розы, а розы, которые растут… везде. На газонах, у дорог, в каких-то заброшенных кварталах. Она как Терминатор, только вместо «I need your clothes, your boots and your motorcycle» — «I need your roses, your ribbons and your likes on Instagram».

И вот она ходит по городу и собирает розы. Она их срывает. Она их коллекционирует. Для чего? Для букета. Чтобы сделать букет, который она подарит… кому? Правильно, кавказцу. Мужчине, которого она видит впервые в жизни. Логика железная: «Мне не дают, значит, нужно подарить цветы незнакомому мужику, который, возможно, в этом городе проездом. И тогда он, ослеплённый моей щедростью, поведёт меня в клуб, напоит текилой и, возможно, женится».

Гениальный план. Надёжный, как швейцарские часы. Только вот есть нюанс. Розы нужно не просто собрать. Их нужно переработать. У памятника. У памятника Василию Пупкину. И не просто у памятника, а с помощью ленточки, которую она подбирает… где? Правильно, на дороге. Там, где она, возможно, недавно оставила нашу бабушку.

Итак, у неё есть розы (нечётное количество). У неё есть ленточка (с места ДТП). И у неё есть… всё. Она подходит к памятнику Пупкину. И тут происходит таинство. Она не просто делает букет. Она… медитирует. Она произносит: «ЭВРИКА! Я сделаю букет из роз и ленточки! Хоп-хоп-хоп — готово! Так, улыбочку! Щёлк! А фото выложу в Нельзяграм и получу кучу лайков! 😍».

Стоп! СТОП! Она делает букет не для того, чтобы подарить его? А для того, чтобы сделать селфи?! Вся эта эпопея с розами, ленточками, памятником — это всё ради фотографии в социальную сеть?! Ради лайков?! Ну, конечно! Это же логично! Зачем дарить цветы живому человеку, если можно сфотографировать их на фоне мертвого истукана и получить одобрение от ботов и бывших одноклассниц, которые, кстати, уже давно замужем и родили по двое детей? Это же намного важнее!

И вот, смотрите, как это выглядит со стороны. Идёт по городу девушка. В одной руке — охапка краденых роз. В другой — ленточка с места трагедии. Она подходит к памятнику, начинает там что-то мастерить, приговаривая «хоп-хоп-хоп». Фотографирует. Выкладывает. Ждёт. Лайки не приходят. Потому что всем на неё плевать. Потому что в этом городе у всех свои проблемы: у кого-то — Ктулху в подвале, у кого-то — дровосексуальная зависимость.

И тут-то она и решает, что букет можно использовать и для второго шанса. Для кавказца. Логика: «Фото не зашло, но, может, зайдёт кавказцу». И она, с этим букетом, который уже морально устарел, идёт к нему. И он ей говорит что-то вроде: «Вах, какой дэвущка! Зачэм тебе этот жялкий веник? Это жьиних купиль, да? Викинь его, айда в клюб».

То есть даже кавказец, который, по идее, должен быть впечатлён букетом, видит в нём просто жалкий веник! Он не оценил! Он не понял! Он предложил ВЫКИНУТЬ букет! И Маруся… соглашается! Она выбрасывает свой букет, своё творение, свой билет в мир лайков, ради призрачной надежды на текилу! Это же просто крик души! Это просто… Маруся.

И вот она идёт в клуб. И там, в клубе, она, естественно, находит себе приключения на одно место. Но об этом — позже. Сначала нужно понять, что движет этой женщиной. А движет ей не поиск отца. И не желание помочь бабушке. И не любовь к прекрасному. Ей движет отчаянное желание быть хоть кому-то нужной. Хоть на секунду. Хоть на одно селфи. Хоть на один танец. Хоть на один глоток текилы. И это, на самом деле, очень грустно. Если бы она не была такой… Марусей.

Олень, соль и месть, или «Как я перестала волноваться и полюбила лес»[править]

Маруся натравливает оленя на своего обидчика

В этой игре есть очень странная механика. И связана она с оленями. В лесу, за городом, живут олени. Они там едят мох, пьют воду из ручьёв и, видимо, ждут, когда какая-нибудь Маруся принесёт им соль. Потому что олени в этом мире — это, по сути, жители Ленинградской области. Они обожают соль.

И вот, у Маруси есть соль. Откуда? Из клуба. Из того самого клуба, где она пила текилу. Точнее, ей дали соль и лимон, как атрибут для распития. Но она, конечно же, не стала пить текилу, как нормальный человек. Она соль оставила. Лимон — потом засунет… ну, вы поняли. А соль — оставила. Для оленя.

И вот она идёт по лесу. Зачем? А кто её знает. Может, грибы собирает. Может, ищет отца, который, возможно, ушёл за хлебом не а булочную, а в лес. И тут она видит оленя. Красивого, грациозного, с большими глазами. И она, конечно же, решает, что олень — это её шанс. Шанс на что? На всё!

Она подходит к оленю и протягивает ему соль. Олень, будучи адекватным существом, соль слизывает. И тут у Маруси, видимо, происходит какой-то сбой в прошивке. Она начинает жаловаться оленю на жизнь. «Меня Егор Туалетов обидел! 😭». И олень, представьте себе, олень, который только что съел соль, вдруг говорит: «ЧТО?! Он посмел обидеть ДЕВУШКУ?! Надо срочно набить ему морду! Веди меня к нему!»

Что?! Олень — радикальный феминист?! Олень — борец за права женщин?! Олень — член движения #MeToo?! И он готов идти бить морду механику по имени Егор Туалетов! Это же просто… гениально! Это лучший сюжетный поворот в истории видеоигр!

И вот, Маруся, вместо того, чтобы решать свои проблемы сама, ведёт оленя к механику. Олень идёт за ней, как верный пёс. Как телохранитель. Как её личный Дмитрий Нагиев. И вот они приходят к механику. И олень, этот благородный зверь, вызывает Туалетова на дуэль. Дуэль века! Человек против оленя! Молот против рогов!

И что делает Егор? Он, будучи адекватным человеком, вступает в бой. И побеждает. Он убивает оленя. И что делает Маруся? Правильно! Она извиняется! «Прости меня, Егор. Я была неправа». Стоп! Ты привела оленя, который хотел набить ему морду, а теперь ты извиняешься?! Ты использовала благородное животное, как разменную монету, а теперь, когда твой план провалился, ты идёшь на попятную?!

Логика Маруси проста: если олень победил — она с героем. Если механик победил — она с механиком. И в том, и в другом случае, она при делах. Она — серый кардинал. Она — кукловод. Она — вершительница судеб. Но по факту, она просто… неадекватная.

И вот олень мёртв. Механик дал ей свой номер телефона. И говорит: «Проехали. Запиши мой телефон, авось пригодится». «Авось пригодится»! Егор, ты даже не представляешь, насколько ей пригодится. Потому что Маруся — это не просто девушка. Это ходячая катастрофа. И твой номер телефона — это не номер телефона. Это номер спасательной службы, которую вызовут на место происшествия, когда Маруся снова попадёт в какую-нибудь передрягу.

А олень… Олень стал жертвой. Жертвой Марусиной глупости и эгоизма. Он поверил в то, что он — рыцарь. А оказался просто мясом в этой драме. И, что самое печальное, его смерть ничему Марусю не научила. Она как была эгоисткой, так и осталась. Только теперь у неё есть телефон механика.

Механик Егор Туалетов, или «Чё те надо, овца?»[править]

Суровый механик Егор Туалетов

Поговорим о Егоре. Егор Туалетов — механик. Он живёт у чёрта на рогах, в каком-то отдалённом квартале. Он чинит машины. Он, судя по всему, неплохой специалист. И он — единственный адекватный NPC в этой игре. Потому что он единственный, кто видит Марусю насквозь.

Первое, что он ей говорит: «Чё те надо, овца?» Не «Здравствуйте, милая леди». Не «Чем могу помочь?» А именно: «Чё те надо, овца?». И это — идеальная реакция. Потому что Маруся действительно овца. Она приходит к нему с каким-то туманным намерением «познакомиться». Она не знает, зачем она пришла. Она не знает, чего она хочет. Она просто пришла, потому что он мужчина и он в списке её потенциальных жертв.

Она спрашивает его: «У тебя есть жена или девушка?» А он: «Не твоё дело». И он прав! Это действительно не её дело! Она его видит в первый раз, а уже лезет в личную жизнь! Она, видите ли, «должна это знать»! Зачем? Чтобы понять, есть ли у неё шанс. Шанс на что? На отношения? На секс? На брак? На то, чтобы он её содержал?

Механик — единственный, кто ставит её на место. Он говорит: «Я не NPC, а живой человек, так что ничем помочь не могу!» Это — прорыв четвёртой стены. Это — крик души разработчика. «Я не NPC, я живой человек!» Это заявление, которое слышит каждая вторая женщина на сайтах знакомств.

Маруся, конечно, не понимает. Она думает: «Как это — не NPC? Все вокруг — NPC! Есть я — главная героиня, а есть все остальные — фон, декорации, массовка. Они должны мне помогать, обслуживать меня, развлекать меня и, желательно, вступать со мной в интимную связь по моему первому требованию». А механик ей говорит: «Нет. Я живой. У меня есть свои дела. Своя жизнь. И ты в неё не входишь».

И это — самое главное противостояние в игре. Противостояние между эгоцентричным миром Маруси, где она — пуп земли, и суровой реальностью, где она — просто овца, которая лезет не в своё дело. И механик — главный защитник этой реальности. Он — последний бастион здравого смысла. Он — герой нашего времени.

Но, как мы знаем, даже он в итоге сдаётся. После случая с оленем, он даёт ей свой номер. Он, возможно, чувствует вину. Он, возможно, видит, что она — не просто овца, а заблудшая овца. И он решает дать ей шанс. «Авось пригодится». И он, сам того не зная, подписал себе приговор. Потому что Маруся обязательно этим воспользуется.

И, надо сказать, сцена с оленем — это единственный момент, когда Маруся вызывает жалость. Она стоит, смотрит на мёртвого оленя и понимает, что натворила. Она понимает, что её план провалился. Она понимает, что механик оказался сильнее. И она извиняется. Искренне? Возможно. Но, зная Марусю, скорее всего, это очередная манипуляция. Потому что, как только она получает номер телефона, её печаль тут же испаряется. У неё теперь есть контакт. Цель достигнута. Олень мёртв, да здравствует новый поклонник!

Клубная жизнь, или медляк с текилой и ЗППП[править]

Вернёмся в клуб. В этот чудный мир музыки, огней и разбитых надежд. Маруся попала туда благодаря кавказцу. Точнее, благодаря его приглашению. И вот она внутри. И что она делает? Конечно же, она напивается. И не просто напивается, а в хлам. Потому что Маруся не умеет пить. Она умеет только создавать проблемы.

И вот она сидит с кавказцем, пьёт текилу и слушает местные хиты. Хиты, кстати — отдельная тема. Это песни, которые крутят в клубе. И они, мягко говоря, специфические. «Трахнул я одну ТП — оказалась РСП». Шедевр. Просто пушкинская строка. Я рыдаю. «Трахнул я её ЛП — подхватил ЗППП». Это же готовый гимн для кожно-венерологического диспансера! «Получилось тупо — чешется залупа!» Это же надо так точно передать суть!

И вот Маруся под эти чудные напевы общается с кавказцем. И выясняется, что кавказец — это не ходячий стереотип. У него нет акцента. Он просто «угорал». Он — обычный человек. Он — личность. И он, как и все в этом городе, не тот, кем кажется.

Маруся не замечает подвоха. Она пьёт, танцует, требует групповушку. «А потом там меня трахнут, я надеюсь? Я бы не отказалась от групповушки!» И тут — бинго! Полиция. «Девушка, вы пьяны, пройдёмте на выход». Всё. Клуб окончен. Маруся в чёрном списке. Навеки. Во всех клубах города.

Как ей это удалось? Как можно так быстро и эффективно испортить себе репутацию? Вопрос риторический. Маруся — это генератор хаоса. Она приходит в место — и место начинает рушиться. Она была у механика — механик стал врагом. Она была в клубе — клуб внёс её в чёрный список. Она была в лесу — погиб олень. Она была у памятника — памятник, наверное, покраснел от стыда.

И вот, после клуба, у неё на руках: лимон, соль и чувство глубокого неудовлетворения. Соль — для оленя. Лимон — для места, которое некоторые олени любят лизать. Чувство неудовлетворения — для дальнейших подвигов. И она, как настоящий солдат, продолжает свой путь. Путь к отцу. К хлебу. К истине. Которая, как мы знаем, где-то рядом. В очереди.

Стоп. А кавказец? Он же вроде приглашал её в гости в кавказский квартал. «Там тебя всегда накормят и напоят». И Маруся, конечно же, решит этим воспользоваться. Потому что ей всегда нужно, чтобы её накормили и напоили. А если ещё и трахнут, то это будет просто идеальный день.

И она идёт в гости. В кавказский квартал. И там, как вы понимаете, её ждут новые приключения. Приключения с запахом кавказской кухни и разочарования.

Кавказская кухня, или «Всем гамарджоба, барев дзес и салам алейкум!»[править]

Итак, Маруся идёт в гости к кавказцу. Точнее, к его родственникам. В кавказский квартал. И это — отдельный акт в этой трагикомедии.

Она приходит и заявляет с порога: «Всем гамарджоба, барев дзес и салам алейкум! Ваш родственник сказал, что здесь меня накормят и напоят». Она, видите ли, знает все обычаи. Она — полиглот. Она — эксперт по кавказскому этикету. Она — Маруся.

И её сажают за стол. За стол, полный еды. Там хинкали, вино, шашлыки, харчо. И Маруся… ест. И пьёт. И ест. И пьёт. Она хомячит так, как будто её не кормили 16 лет. Как будто она — та самая дочь, которую бросил отец, ушедший за хлебом.

И самое интересное — она комментирует. «Ам!», «Глыг-глыг!», «Сёрб-сёрб-сёрб!», «Ам-ам-ам-ам-ам!». Это же просто апофеоз. Человек пришёл в гости и жрёт, как будто он пылесос. И она ещё удивляется, что её никто не хочет. Да кто захочет женщину, которая ест, как голодный медведь после спячки?

И вот, она всё съела. Всё выпила. И говорит: «Как-то скромненько, мальчики. Я ожидала большего. Всем пока! 💋». СТОП! Она пришла в гости, её накормили, напоили, а она говорит «скромненько»?! Она ожидала, что ей зажарят целого барана? Она думала, что ради неё будут резать последнего быка? Она считала, что её ждёт пир на весь мир?

И после этого она уходит. Сытая, пьяная и… с чувством, что её обделили. И, как бонус, после такого обжорства у неё возникает естественная потребность. Она хочет в туалет. И не просто хочет, а очень хочет. Потому что вся эта еда, все эти «ам-ам» и «глыг-глыг» — они не усвоились. Они требуют выхода.

И вот тут начинается самая, пожалуй, драматичная и напряжённая часть её приключений. Потому что найти в этом городе работающий туалет — это задача ненамного легче, чем найти отца. И это, как вы понимаете, о многом говорит.

Туалетный квест, или «Спасите, я в сортире!»[править]

Итак, Маруся объелась кавказской кухни. И её организм, не привыкший к такому количеству жирной, острой и вкусной пищи, взбунтовался. Ей нужно в туалет. Срочно. Прямо сейчас. И тут начинается гонка со временем.

По городу разбросаны туалеты. Но, как и всё в этом городе, они не так просты. Это не просто кабинки. Это — ловушки. Это — испытания. Это — проверка на прочность. И Маруся, наша отважная героиня, ищет заветную дверь.

И вот, она находит туалет. Заходит. И… дверь заклинивает. Классика. Она застряла. Она в ловушке. И она начинает звать на помощь. «СПАСИТЕ!»

И вот тут-то и проявляется вся глубина её «связей». Если у неё есть номер механика (а он у неё, конечно же, есть, потому что она — Маруся, и она всегда добивается своего), то она звонит ему. Она зовёт на помощь. И механик, как настоящий герой, приходит. И вытаскивает её.

И что он получает в благодарность? «Давай я тебе отсосу в знак благодарности!» И что он отвечает? «Иди на хуй, пока я тебя в сортире не закрыл!» Гениально! Просто гениально! Это лучший ответ на лучшее предложение. Он не просто отказывается. Он угрожает ей. Он ставит её на место. Он — единственный, кто понимает, что иметь дело с Марусей — себе дороже.

Но, если номера механика нет, то Марусю ждёт печальная участь. Она задыхается в туалете. Она умирает. Одна. В окружении мух и… сами понимаете, чего. И это — заслуженный финал. Потому что нельзя быть такой дурой. Нельзя столько есть. Нельзя столько пить. И нельзя надеяться на то, что кто-то придёт и спасёт тебя, если ты сама не в состоянии справиться с элементарными вещами.

Туалетный квест — это кульминация Марусиной беспомощности. Она не может найти отца. Она не может перевести бабушку. Она не может завести отношения. И она не может даже сходить в туалет. Она — инвалид по жизни. Она — человек, который нуждается в постоянной опеке. И то, что она ещё жива — это просто чудо. Или, возможно, проклятие. Проклятие, которое она несёт всем, кто с ней рядом.

И вот, после туалета, она, конечно же, получает туалетную бумагу. И делает из лимона… лимон-тампон. И покрывает голову туалетной бумагой. Чтобы пройти в церковь. В церковь! Потому что без туалетной бумаги на голове её туда не пустят. Логика? Абсурд? Да, это Маруся.

Религиозный экстаз и финальный аккорд, или «Квадрат — большой наш брат»[править]

Итак, Маруся решает сходить в церковь. Зачем? Ну, наверное, чтобы замолить грехи. Или чтобы найти там отца. Или чтобы просто убить время. Причины не важны. Важно то, что в церковь её не пускают без «покрытой головы». И она решает, что лучший головной убор — это туалетная бумага. А чтобы всё это выглядело «эстетично», она добавляет лимон. В качестве… аксессуара. Но не на голову. А в… себя. Потому что она не может просто так взять и не засунуть куда-нибудь лимон.

И вот она, с бумагой на голове и лимоном в… там, заходит в церковь. И попадает на службу. И слушает проповедь. Проповедь о Священном Квадране. О том, что «Простой Квадрат — Большой Наш Брат». О том, что «нет ничего лучше квадрата». О том, что «круглую Землю мы измеряем квадратными единицами».

Это — религия Квадрата. Религия, которая идеально вписывается в этот квадратный город. И Маруся, с её квадратной головой, с её квадратными мыслями, с её квадратными желаниями — идеальная прихожанка. Она верит в Квадрат. Потому что Квадрат — это просто. Квадрат — это понятно. Квадрат — это она сама.

И в этой церкви, под эти квадратные песнопения, Маруся, возможно, впервые в жизни чувствует себя на своём месте. Она — часть чего-то большего. Она — винтик в квадратном механизме. Она — не просто Маруся, а Маруся-прихожанка. Маруся-верующая. Маруся-с-бумагой-на-голове-и-лимоном-в… там.

Но, конечно, и тут не обходится без приключений. Потому что из неё начинает «пахнуть рыбой в лимонном соусе». И её просят удалиться. И она уходит. И, видимо, оставляет свой лимон-тампон где-то в церкви. Или, может, забирает с собой. Не важно. Важно то, что церковь она тоже «пометила».

После церкви её ждёт финал. Тот самый финал, ради которого всё затевалось. Встреча с отцом.

И вот она находит его. В очереди. В очереди за хлебом. Он стоит там 16 лет. Он не может дождаться. Очередь огромная. И он в ней. Стоит. С хлебом. Не доходя до дома.

И Маруся, увидев его, кричит: «ПАПА?!» А он: «МАРУСЯ?!» И начинается трогательная сцена. Но, зная Марусю, даже эта сцена превращается в фарс. Она начинает врать. Что она «тихая, скромная и добрая». Что она «переводит бабушек через дорогу». Что она «ходит в церковь». Ага. Конечно. Мы-то знаем, как она переводит бабушек. И как она ходит в церковь. С лимоном в… да.

И тут, в очереди, находится свидетель. Вампир. Да, вампир. Который, видимо, тоже стоит в очереди за хлебом. И он начинает говорить правду. Что Маруся — «та самая шлю…». Но она, не будь дурой, тут же накачивает бицепс и говорит: «Что, простите? У меня бицепс зачесался, я не расслышала». И вампир, испугавшись её бицепса, говорит: «Я говорю: шлю привет тебе и твоему отцу!» И папаша, конечно же, в восторге. Он не видит, что его дочь — просто хулиганка с манией величия. Он видит только красивую, сильную девушку.

И вот финал. Отец с хлебом. Маруся в слезах. Мама дома. Идиллия. Победа. Титры.

Но, если задуматься, что мы видим? Мы видим, что Маруся нашла отца не благодаря своим талантам. Она нашла его, просто дойдя до нужного магазина. Она не проявила ни ума, ни смекалки, ни доброты. Она просто шла по квестовым маркерам, как заведённая. И все её «подвиги» — это просто череда ошибок, глупостей и случайностей.

Она — героиня поневоле. Точнее, не героиня, а просто… главная героиня. Которая по воле сценария должна найти отца. И она его находит. Не потому, что она этого заслужила. А потому, что так прописано в коде.

И вот отец вернулся. С хлебом. Через 16 лет. И что он скажет? Наверное, «Ну и денёк». Или «Я забыл, зачем уходил». Или «А где мой суп?» А Маруся, наверное, скажет: «Папа, у меня есть номер телефона механика». И всё начнётся сначала.

Эта игра не о поиске отца. Эта игра о том, как одна неадекватная девушка способна разрушить жизни всех, кто её окружает. Она — как вирус. Она проникает в город, заражает его своим безумием и оставляет после себя хаос, разрушения и труп оленя.

И знаете, что самое смешное? Что эта девушка, Маруся — она есть в каждом из нас. В каждом из нас есть та самая часть, которая хочет, чтобы на нас обратили внимание. Которая готова на всё ради лайка. Которая готова притворяться «хорошей девочкой», чтобы получить своё. Которая готова вести оленя на смерть, лишь бы добиться своего.

И именно поэтому этот квест так цепляет. Не потому, что он смешной. А потому, что он — правдивый. Потому что Маруся — это мы. Только без трусов, забытых где-то в клубе.


Волшебное заклинание для создания мира и оживления Маруси[править]

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

world = {}
V_states = {}
H_states = {}

px, py = 0, 0
player_msg = ""
roses_collected = 0
ribbons_collected = 0
bouquets_collected = 0
lemon = 0
salt = 0
toilet_paper = 0  # 🧻
monument_met = False
monument_coords = None
shovel = 0  # 🥄
holes_dug = 0  # количество выкопанных ямок
women_defeated_count = 0  # сколько 👩 побеждено в драке
women_npcs = []  # координаты всех 👩 возле магазина
option_block_active = False  # показывать ли опции
current_woman_coords = None  # координаты соседней 👩

grandma = {
    'x': 0,
    'y': 0,
    'state': 'none', # none, waiting, crossing, going_home, dead
    'under': '🔹',
    'crossing_dir': (0, 0),
    'start_x': 0,   # начальная позиция бабушки
    'start_y': 0
}

mechanic = None
car = {}

stop_signs = set()
buses = []

district_names = {}
district_colors = {}

sheeps = []
grass_cells = set()

# Новые глобальные переменные для кавказца и клуба
new_npc = None
club_entrance = None
club_invited = False
club_visited = False
club_blacklist = False
pending_invite = False
feast_visited = False

# Глобальные переменные для оленя
deer = None          # объект оленя (если следует за героиней)
player_in_bus = False

# Переменная для отслеживаемой цели (компас)
tracked_target = None   # может быть 'grandma', 'monument', 'new_npc', 'mechanic', 'shop', 'hut' или None

# Множество клеток автобусных дорог
bus_road_cells = set()

# Новые глобальные словари для названий проспектов
prospect_names_h = {}   # B -> название для горизонтальных
prospect_names_v = {}   # B -> название для вертикальных

# Туалетные переменные
toilet_patience = None   # None - нет нужды, число - осталось терпения
mechanic_killed_deer = False
player_is_clown = False

# Новые переменные для превращения в эльфа
player_is_elf = False
lemon_tampon = False

# Новые переменные для могильщиков
gravediggers = []          # список словарей {'x','y','under','state','cross_dir','steps_crossed'}
coffin_coords = None
gravedigger_triggered = False
gravedigger_move_countdown = 0
church_visited_global = False
gravedigger_block_dialogue_shown = False  # был ли показан диалог преграждения пути
gravedigger_dialogue_shown = False        # был ли показан основной диалог с могильщиками

# Новый словарь для хранения исходных клеток под NPC очереди и 👩
queue_npcs_under = {}

# Координаты магазина с очередью NPC (для компаса)
shop_with_queue_coords = None

# Геометрический центр квартала с хижинами (для компаса)
hut_center = None

def setup_districts():
    names = [
        "Ктулховский", "Дартовский", "Норрисовский", "Пупкинский",
        "Чердачный", "Дровосексуальный", "Навуходопоносорский", "Ясос-Бибский",
        "Пердолётный", "Котобутербродный", "Новосибирский", "Снюсоедский",
        "Асбестовый", "Квадранский", "Абсурдоляндский", "Ёпрстский",
        "Цедрейтерский", "Полуторагодовалый", "Спринтерский", "Убитоенотский",
        "Сиконский", "Анциклопедийный", "Черненковский", "Насубарупоедский"
    ]
    random.shuffle(names)
    
    colors = ['\033[91m', '\033[92m', '\033[93m', '\033[94m', '\033[95m', '\033[96m']
    
    for r in range(5):
        for c in range(5):
            if c == 2 and r == 2:
                district_names[(c, r)] = "ЦЕНТРАЛЬНЫЙ"
            else:
                district_names[(c, r)] = names.pop().upper()
            
            used_colors = set()
            if c > 0:
                used_colors.add(district_colors[(c-1, r)])
            if r > 0:
                used_colors.add(district_colors[(c, r-1)])
            
            available_colors = [col for col in colors if col not in used_colors]
            district_colors[(c, r)] = random.choice(available_colors)

def get_district_index(coord):
    if -111 <= coord <= -69: return 0
    if -66 <= coord <= -24: return 1
    if -21 <= coord <= 21: return 2
    if 24 <= coord <= 66: return 3
    if 69 <= coord <= 111: return 4
    return None

def get_district(x, y):
    c = get_district_index(x)
    r = get_district_index(y)
    if c is None or r is None:
        return None
    return (c, r)

def getch():
    try:
        if os.name == 'nt':
            import msvcrt
            ch = msvcrt.getwch()
            if ch in ('\x00', '\xe0'):
                ch2 = msvcrt.getwch()
                if ch2 == 'H': return 'up'
                if ch2 == 'P': return 'down'
                if ch2 == 'K': return 'left'
                if ch2 == 'M': return 'right'
            return ch.lower()
        else:
            import tty, termios
            fd = sys.stdin.fileno()
            old_settings = termios.tcgetattr(fd)
            try:
                tty.setraw(fd)
                ch = sys.stdin.read(1)
                if ch == '\x1b':
                    ch2 = sys.stdin.read(1)
                    if ch2 == '[':
                        ch3 = sys.stdin.read(1)
                        if ch3 == 'A': return 'up'
                        if ch3 == 'B': return 'down'
                        if ch3 == 'D': return 'left'
                        if ch3 == 'C': return 'right'
            finally:
                termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
            return ch.lower()
    except Exception:
        pass
    return ''

def is_V_road(Bx, By):
    if Bx in (-13, 12) and (By <= -13 or By > 12):
        return True
    if -13 <= Bx <= 12 and -13 <= By <= 12:
        return V_states.get((Bx, By), False)
    return False

def is_H_road(Bx, By):
    if By in (-13, 12) and (Bx <= -13 or Bx > 12):
        return True
    if -13 <= Bx <= 12 and -13 <= By <= 12:
        return H_states.get((Bx, By), False)
    return False

def get_road_type(x, y):
    bx = (x + 3) % 9
    by = (y + 3) % 9
    Bx = (x + 3) // 9
    By = (y + 3) // 9
    
    if bx < 7 and by < 7:
        return None
    elif bx >= 7 and by < 7:
        return '||' if is_V_road(Bx, By) else None
    elif bx < 7 and by >= 7:
        return '🟰' if is_H_road(Bx, By) else None
    else:
        top = is_V_road(Bx, By)
        bottom = is_V_road(Bx, By + 1)
        left = is_H_road(Bx, By)
        right = is_H_road(Bx + 1, By)
        
        roads_count = sum([top, bottom, left, right])
        if roads_count == 0:
            return None
        elif top and bottom and not left and not right:
            return '||'
        elif left and right and not top and not bottom:
            return '🟰'
        else:
            return '🔘'

def cell_rng(x, y, tag):
    return random.Random(hash((x, y, tag))).random()

def is_road_adjacent(x, y):
    for dx in [-1, 0, 1]:
        for dy in [-1, 0, 1]:
            if dx == 0 and dy == 0:
                continue
            if get_road_type(x + dx, y + dy) is not None:
                return True
    return False

def is_building(x, y):
    if get_road_type(x, y) is not None or is_road_adjacent(x, y):
        return False
    return cell_rng(x, y, "building") < 0.2

def is_stop_sign(x, y):
    return (x, y) in stop_signs

def is_disco_ball(x, y):
    return x in (-90, -45, 0, 45, 90) and y in (-90, -45, 0, 45, 90) and (x != 0 or y != 0)

def is_tree(x, y):
    if is_stop_sign(x, y): return False
    if is_disco_ball(x, y): return False
    if get_road_type(x, y) is not None or is_road_adjacent(x, y): return False
    if abs(x) > 114 or abs(y) > 114: return False
    if is_building(x, y): return False
    
    for dx in [-1, 0, 1]:
        for dy in [-1, 0, 1]:
            if dx == 0 and dy == 0: continue
            nx, ny = x + dx, y + dy
            if is_building(nx, ny) or is_disco_ball(nx, ny):
                return False
    return cell_rng(x, y, "tree") < 0.4

def is_far_from_road(x, y, radius=4):
    """Проверяет, что в радиусе radius клеток нет дороги (расстояние >= radius+1)."""
    for dx in range(-radius, radius+1):
        for dy in range(-radius, radius+1):
            if dx == 0 and dy == 0:
                continue
            if get_road_type(x+dx, y+dy) is not None:
                return False
    return True

def generate_cell(x, y):
    if is_stop_sign(x, y): return '🛑'
    if is_disco_ball(x, y): return '🪩'
        
    road = get_road_type(x, y)
    if road: return road
    if is_road_adjacent(x, y): return '🔸'
    if abs(x) > 114 or abs(y) > 114:
        # Лес за городом на достаточном удалении от дорог
        if is_far_from_road(x, y):
            r = cell_rng(x, y, "country")
            if r < 0.4:
                return '🌲'
            elif r < 0.41:   # 1% вероятность
                return '🦌'
            else:
                return '▪️'
        else:
            if cell_rng(x, y, "country") < 0.4: return '🌲'
            return '▪️'
        
    bx_mod = (x + 3) % 9
    by_mod = (y + 3) % 9
    Bx_idx = (x + 3) // 9
    By_idx = (y + 3) // 9
    
    if bx_mod < 7 and by_mod < 7:
        is_outer_edge = (Bx_idx in (-12, 12) or By_idx in (-12, 12))
        if is_outer_edge:
            top_road = is_H_road(Bx_idx, By_idx - 1)
            bottom_road = is_H_road(Bx_idx, By_idx)
            left_road = is_V_road(Bx_idx - 1, By_idx)
            right_road = is_V_road(Bx_idx, By_idx)
            
            if top_road and bottom_road and left_road and right_road:
                if 1 <= bx_mod <= 5 and 1 <= by_mod <= 5:
                    lx = bx_mod - 1
                    ly = by_mod - 1
                    if ly == 2 or lx == 2: return '▪️'
                    else: return '🏡'
                else:
                    return '▪️'

    if is_building(x, y): return '🏢'
        
    has_adj_building_or_disco = False
    for dx in [-1, 0, 1]:
        for dy in [-1, 0, 1]:
            if dx == 0 and dy == 0: continue
            nx, ny = x + dx, y + dy
            if is_building(nx, ny) or is_disco_ball(nx, ny):
                has_adj_building_or_disco = True
                break
        if has_adj_building_or_disco: break
            
    if not has_adj_building_or_disco:
        if cell_rng(x, y, "tree") < 0.4:
            if x == 0 and y == 0:
                return '🔹'
            return '🌳'
            
        has_adj_tree = False
        for dx in [-1, 0, 1]:
            for dy in [-1, 0, 1]:
                if dx == 0 and dy == 0: continue
                if is_tree(x + dx, y + dy):
                    has_adj_tree = True
                    break
            if has_adj_tree: break
        if not has_adj_tree:
            if abs(x) <= 111 and abs(y) <= 111:
                if cell_rng(x, y, "rose") < 0.2: return '🌹'
                        
    return '🔹'

def generate_area(cx, cy):
    for y in range(cy - 17, cy + 18):
        for x in range(cx - 17, cx + 18):
            if (x, y) not in world:
                world[(x, y)] = generate_cell(x, y)

def init_city():
    setup_districts()
    global V_states, H_states, stop_signs, grandma, mechanic, sheeps, grass_cells, monument_coords, car
    global new_npc, club_entrance, club_invited, club_visited, club_blacklist, pending_invite, feast_visited
    global deer, bus_road_cells, prospect_names_h, prospect_names_v
    global toilet_patience, mechanic_killed_deer, player_is_clown, toilet_paper
    global player_is_elf, lemon_tampon
    global gravediggers, coffin_coords, gravedigger_triggered, gravedigger_move_countdown, church_visited_global, shovel
    global gravedigger_block_dialogue_shown, gravedigger_dialogue_shown
    global queue_npcs_under, shop_with_queue_coords
    global holes_dug, women_defeated_count, women_npcs, option_block_active, current_woman_coords
    global hut_center
    toilet_patience = None
    mechanic_killed_deer = False
    player_is_clown = False
    toilet_paper = 0
    player_is_elf = False
    lemon_tampon = False
    gravediggers = []
    coffin_coords = None
    gravedigger_triggered = False
    gravedigger_move_countdown = 0
    church_visited_global = False
    shovel = 0
    holes_dug = 0
    women_defeated_count = 0
    women_npcs = []
    option_block_active = False
    current_woman_coords = None
    gravedigger_block_dialogue_shown = False
    gravedigger_dialogue_shown = False
    queue_npcs_under = {}    # очищаем словарь
    shop_with_queue_coords = None   # сбрасываем координаты магазина
    hut_center = None
    
    edges_V = {}
    edges_H = {}
    
    for y in range(-12, 13):
        edges_V[(-13, y)] = True
        edges_V[(12, y)] = True
    for x in range(-12, 13):
        edges_H[(x, -13)] = True
        edges_H[(x, 12)] = True
        
    guaranteed_V = [-8, -3, 2, 7]
    for bx in guaranteed_V:
        for y in range(-12, 13): edges_V[(bx, y)] = True
            
    guaranteed_H = [-8, -3, 2, 7]
    for by in guaranteed_H:
        for x in range(-12, 13): edges_H[(x, by)] = True

    for x in range(-12, 12):
        for y in range(-12, 13):
            if random.random() < 0.8: edges_V[(x, y)] = True
    for x in range(-12, 13):
        for y in range(-12, 12):
            if random.random() < 0.8: edges_H[(x, y)] = True
                
    while True:
        degrees = {}
        for (x, y) in edges_V:
            degrees[(x, y-1)] = degrees.get((x, y-1), 0) + 1
            degrees[(x, y)] = degrees.get((x, y), 0) + 1
        for (x, y) in edges_H:
            degrees[(x-1, y)] = degrees.get((x-1, y), 0) + 1
            degrees[(x, y)] = degrees.get((x, y), 0) + 1
            
        pruned_any = False
        for node, deg in degrees.items():
            if deg <= 1:
                x, y = node
                if (x, y+1) in edges_V:
                    del edges_V[(x, y+1)]
                    pruned_any = True
                elif (x, y) in edges_V:
                    del edges_V[(x, y)]
                    pruned_any = True
                elif (x+1, y) in edges_H:
                    del edges_H[(x+1, y)]
                    pruned_any = True
                elif (x, y) in edges_H:
                    del edges_H[(x, y)]
                    pruned_any = True
        if not pruned_any: break
            
    visited_V = set()
    visited_H = set()
    queue = [(-13, -13)]
    visited_nodes = {(-13, -13)}
    
    while queue:
        curr_x, curr_y = queue.pop(0)
        if (curr_x, curr_y) in edges_V and (curr_x, curr_y) not in visited_V:
            visited_V.add((curr_x, curr_y))
            nxt = (curr_x, curr_y-1)
            if nxt not in visited_nodes:
                visited_nodes.add(nxt)
                queue.append(nxt)
        if (curr_x, curr_y+1) in edges_V and (curr_x, curr_y+1) not in visited_V:
            visited_V.add((curr_x, curr_y+1))
            nxt = (curr_x, curr_y+1)
            if nxt not in visited_nodes:
                visited_nodes.add(nxt)
                queue.append(nxt)
        if (curr_x, curr_y) in edges_H and (curr_x, curr_y) not in visited_H:
            visited_H.add((curr_x, curr_y))
            nxt = (curr_x-1, curr_y)
            if nxt not in visited_nodes:
                visited_nodes.add(nxt)
                queue.append(nxt)
        if (curr_x+1, curr_y) in edges_H and (curr_x+1, curr_y) not in visited_H:
            visited_H.add((curr_x+1, curr_y))
            nxt = (curr_x+1, curr_y)
            if nxt not in visited_nodes:
                visited_nodes.add(nxt)
                queue.append(nxt)
                
    V_states.clear()
    H_states.clear()
    for k in visited_V: V_states[k] = True
    for k in visited_H: H_states[k] = True

    # Заполняем множество автобусных дорог
    bus_road_cells.clear()
    for B in [-8, -3, 2, 7]:
        # Горизонтальные дороги
        for x in range(-111, 112):
            bus_road_cells.add((x, B*9 + 4))
            bus_road_cells.add((x, B*9 + 5))
        # Вертикальные дороги
        for y in range(-111, 112):
            bus_road_cells.add((B*9 + 4, y))
            bus_road_cells.add((B*9 + 5, y))

    # Названия проспектов
    prospect_names_list = [
        "20-ЛЕТИЯ АБСУРДОПЕДИИ",
        "МОДЕРАЦИИ",
        "ХЕРОХИТО",
        "АБСУРДОТЕЧНЫЙ",
        "24 ФЕВРАЛЯ",
        "КАК-ПРАВИЛЬНЫЙ",
        "ПИТОНА",
        ".".join(str(random.randint(0, 255)) for _ in range(4))  # рандомный IP
    ]
    random.shuffle(prospect_names_list)
    # Горизонтальные проспекты (B)
    horiz_bs = [-8, -3, 2, 7]
    vert_bs = [-8, -3, 2, 7]
    for i, b in enumerate(horiz_bs):
        prospect_names_h[b] = prospect_names_list[i]
    for i, b in enumerate(vert_bs):
        prospect_names_v[b] = prospect_names_list[4 + i]

    stop_signs = set()
    x_vals = [-69, -66, -24, -21, 21, 24, 66, 69]
    y_vals = [-69, -66, -24, -21, 21, 24, 66, 69]
    for n in range(-4, 5):
        base = 27 * n
        for x in x_vals: stop_signs.add((x, base))
        for y in y_vals: stop_signs.add((base, y))

    for y in range(-114, 115):
        for x in range(-114, 115):
            world[(x, y)] = generate_cell(x, y)

    visited_block_cells = set()
    candidate_blocks = []
    road_chars = {'||', '🟰', '🔘'}
    
    for y_pos in range(-111, 112):
        for x_pos in range(-111, 112):
            if (x_pos, y_pos) not in visited_block_cells and world.get((x_pos, y_pos)) not in road_chars:
                queue_block = [(x_pos, y_pos)]
                visited_block_cells.add((x_pos, y_pos))
                block = []
                q_idx = 0
                while q_idx < len(queue_block):
                    cx, cy = queue_block[q_idx]
                    q_idx += 1
                    block.append((cx, cy))
                    for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
                        nx, ny = cx + dx, cy + dy
                        if -111 <= nx <= 111 and -111 <= ny <= 111:
                            if (nx, ny) not in visited_block_cells and world.get((nx, ny)) not in road_chars:
                                visited_block_cells.add((nx, ny))
                                queue_block.append((nx, ny))
                                
                if len(block) > 49:
                    has_disco = any(world.get((bx, by)) == '🪩' for bx, by in block)
                    contains_start = (0, 0) in block
                    if not has_disco and not contains_start:
                        candidate_blocks.append(block)

    if candidate_blocks:
        chosen_block = random.choice(candidate_blocks)
        S = set()
        for cx, cy in chosen_block:
            if world.get((cx, cy)) in ['🔸', '🛑']: S.add((cx, cy))
        H_set = set()
        G_set = set()
        for cx, cy in chosen_block:
            if (cx, cy) not in S:
                touches_S = False
                for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
                    if (cx + dx, cy + dy) in S:
                        touches_S = True
                        break
                if touches_S: H_set.add((cx, cy))
                else: G_set.add((cx, cy))
                    
        for cx, cy in H_set: world[(cx, cy)] = '🛖'
        for cx, cy in G_set: world[(cx, cy)] = '🌱'
            
        grass_cells = G_set
        if len(G_set) > 0:
            num_sheep = min(10, len(G_set))
            sheep_pos = random.sample(list(G_set), num_sheep)
            for sx, sy in sheep_pos:
                sheeps.append({'x': sx, 'y': sy})
                world[(sx, sy)] = '🐑'

        # Вычисляем геометрический центр квартала
        sum_x = sum(x for x, y in chosen_block)
        sum_y = sum(y for x, y in chosen_block)
        n = len(chosen_block)
        hut_center = (round(sum_x / n), round(sum_y / n))

    candidate_monument_blocks = []
    for Bx in range(-11, 12):
        for By in range(-11, 12):
            if is_H_road(Bx, By - 1) and is_H_road(Bx, By) and is_V_road(Bx - 1, By) and is_V_road(Bx, By):
                start_x, start_y = Bx * 9 - 3, By * 9 - 3
                has_disco = False
                for y_pos in range(start_y, start_y + 7):
                    for x_pos in range(start_x, start_x + 7):
                        if world.get((x_pos, y_pos)) == '🪩':
                            has_disco = True
                            break
                    if has_disco: break
                has_sheep_stuff = False
                for y_pos in range(start_y, start_y + 7):
                    for x_pos in range(start_x, start_x + 7):
                        if world.get((x_pos, y_pos)) in ['🛖', '🌱', '🐑']:
                            has_sheep_stuff = True
                            break
                    if has_sheep_stuff: break
                # Исключаем блок, если он содержит стартовую клетку (0,0)
                contains_start = (start_x + 1 <= 0 <= start_x + 5) and (start_y + 1 <= 0 <= start_y + 5)
                if not has_disco and not has_sheep_stuff and not contains_start:
                    candidate_monument_blocks.append((Bx, By))

    if candidate_monument_blocks:
        mbx, mby = random.choice(candidate_monument_blocks)
        start_x, start_y = mbx * 9 - 3, mby * 9 - 3
        layout = ["🌴🌴💠🌴🌴", "🌴💠💠💠🌴", "💠💠🗿💠💠", "🌴💠💠💠🌴", "🌴🌴💠🌴🌴"]
        for i in range(5):
            for j in range(5):
                world[(start_x + 1 + j, start_y + 1 + i)] = layout[i][j]
        monument_coords = (start_x + 3, start_y + 3)

    for y_pos in range(-114, 115):
        for x_pos in range(-114, 115):
            if world.get((x_pos, y_pos)) == '🌳':
                if world.get((x_pos + 1, y_pos)) == '🌳' and world.get((x_pos + 2, y_pos)) == '🌳':
                    if random.random() < 0.3:
                        world[(x_pos, y_pos)] = '🔔'
                        world[(x_pos + 1, y_pos)] = '🕌'
                        world[(x_pos + 2, y_pos)] = '🔔'
                        continue
                if world.get((x_pos, y_pos + 1)) == '🌳' and world.get((x_pos, y_pos + 2)) == '🌳':
                    if random.random() < 0.3:
                        world[(x_pos, y_pos)] = '🔔'
                        world[(x_pos, y_pos + 1)] = '🕌'
                        world[(x_pos, y_pos + 2)] = '🔔'

    district_ranges = [(-111, -69), (-66, -24), (-21, 21), (24, 66), (69, 111)]
    for c in range(5):
        for r in range(5):
            x_min, x_max = district_ranges[c]
            y_min, y_max = district_ranges[r]
            buildings = []
            for y_pos in range(y_min, y_max + 1):
                for x_pos in range(x_min, x_max + 1):
                    if world.get((x_pos, y_pos)) == '🏢':
                        buildings.append((x_pos, y_pos))
            if buildings:
                bx, by = random.choice(buildings)
                world[(bx, by)] = '🚾'

    visited_cells = set()
    road_chars_for_stores = {'||', '🟰', '🔘', '🛑'}
    for y_pos in range(-114, 115):
        for x_pos in range(-114, 115):
            if (x_pos, y_pos) not in visited_cells and world.get((x_pos, y_pos)) not in road_chars_for_stores:
                queue_stores = [(x_pos, y_pos)]
                visited_cells.add((x_pos, y_pos))
                block_buildings = []
                q_idx = 0
                while q_idx < len(queue_stores):
                    cx, cy = queue_stores[q_idx]
                    q_idx += 1
                    if world.get((cx, cy)) == '🏢': block_buildings.append((cx, cy))
                    for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
                        nx, ny = cx + dx, cy + dy
                        if -114 <= nx <= 114 and -114 <= ny <= 114:
                            if (nx, ny) not in visited_cells and world.get((nx, ny)) not in road_chars_for_stores:
                                visited_cells.add((nx, ny))
                                queue_stores.append((nx, ny))
                m_buildings = len(block_buildings)
                if m_buildings >= 5:
                    n_stores = m_buildings // 5
                    for bx, by in random.sample(block_buildings, n_stores):
                        world[(bx, by)] = '🏪'

    # ======= НОВЫЙ КОД: очередь к магазину =======
    shops = [(x, y) for (x, y), cell in world.items() if cell == '🏪']
    obstacles_for_walk = {'🏢','🌳','🌲','🪩','🚾','🏪','👵','🏡','🧑‍🔧','🕌','🔔','🛖','🐑','🌴','🗿','🧔','🦌','⚰️','👷'}
    def is_walkable(x, y):
        if not (-114 <= x <= 114 and -114 <= y <= 114):
            return False
        if get_road_type(x, y) is not None:
            return False
        if world.get((x, y), '') in obstacles_for_walk:
            return False
        return True

    best_shop = None
    best_dir = None
    min_dist = float('inf')
    dirs = [(0,-1),(0,1),(-1,0),(1,0)]
    for shop in shops:
        mx, my = shop
        # Проверка 4 соседей
        if not all(is_walkable(mx+dx, my+dy) for dx, dy in dirs):
            continue
        # Собираем все валидные направления (проходимость и не проспект)
        valid_dirs = []
        for dir in dirs:
            dx, dy = dir
            valid = True
            for i in range(1, 9):
                nx, ny = mx + dx*i, my + dy*i
                if not is_walkable(nx, ny):
                    valid = False
                    break
                if (nx, ny) in bus_road_cells:
                    valid = False
                    break
            if valid:
                valid_dirs.append(dir)
        if not valid_dirs:
            continue
        # Проверяем условие: ни один NPC не попадает в квадрат 3x3 вокруг (0,0)
        chosen_dir = None
        for dir in valid_dirs:
            dx, dy = dir
            # Все клетки, которые займут NPC
            npc_cells = []
            for i in range(1, 9):
                npc_cells.append((mx + dx*i, my + dy*i))
            # остальные три направления по одной клетке
            for other_dir in dirs:
                if other_dir == dir:
                    continue
                ox, oy = other_dir
                npc_cells.append((mx + ox, my + oy))
            # Проверяем, не попадает ли кто-то в запретную зону (|x|<=1 and |y|<=1)
            in_forbidden = False
            for nx, ny in npc_cells:
                if abs(nx) <= 1 and abs(ny) <= 1:
                    in_forbidden = True
                    break
            if not in_forbidden:
                chosen_dir = dir
                break
        if chosen_dir is None:
            continue  # все валидные направления дают попадание в стартовую зону
        dist = abs(mx) + abs(my)
        if dist < min_dist:
            min_dist = dist
            best_shop = shop
            best_dir = chosen_dir
    if best_shop is not None and best_dir is not None:
        shop_with_queue_coords = best_shop   # сохраняем координаты магазина с очередью
        mx, my = best_shop
        dx, dy = best_dir
        # Размещаем очередь NPC
        queue_npcs = ['😩','😡','😴','😤','🥸','🤓','🤠','😎']  # индекс 0 - дальняя
        for i in range(1, 9):
            nx, ny = mx + dx*i, my + dy*i
            # Сохраняем исходную клетку под NPC
            queue_npcs_under[(nx, ny)] = world.get((nx, ny), '🔹')
            world[(nx, ny)] = queue_npcs[8 - i]  # i=1 -> индекс 7 = 😎, i=8 -> индекс 0 = 😩
        # Размещаем 👩 с остальных трёх сторон
        for other_dir in dirs:
            if other_dir == best_dir:
                continue
            ox, oy = other_dir
            nx, ny = mx + ox, my + oy
            # Сохраняем исходную клетку под NPC
            queue_npcs_under[(nx, ny)] = world.get((nx, ny), '🔹')
            world[(nx, ny)] = '👩'
            women_npcs.append((nx, ny))   # добавляем координаты 👩
    # ======= КОНЕЦ НОВОГО КОДА =======

    grandma_candidates = []
    bus_Bs = [-8, -3, 2, 7]
    sidewalk_coords = []
    for b in bus_Bs:
        sidewalk_coords.extend([b * 9 + 3, b * 9 + 6])
        
    for x_pos in range(-114, 115):
        for y_pos in range(-114, 115):
            if world.get((x_pos, y_pos)) == '🔸':
                is_h = (y_pos in sidewalk_coords)
                is_v = (x_pos in sidewalk_coords)
                if is_h and (x_pos % 9 == 0 and x_pos % 27 != 0): grandma_candidates.append((x_pos, y_pos))
                elif is_v and (y_pos % 9 == 0 and y_pos % 27 != 0): grandma_candidates.append((x_pos, y_pos))

    if grandma_candidates:
        gx, gy = random.choice(grandma_candidates)
        grandma['x'], grandma['y'] = gx, gy
        grandma['start_x'], grandma['start_y'] = gx, gy   # сохраняем начальные координаты
        grandma['state'] = 'waiting'
        grandma['under'] = world.get((gx, gy), '🔸')
        world[(gx, gy)] = '👵'
        
    candidate_blocks_mech = []
    for Bx in range(-12, 13):
        for By in range(-12, 13):
            if Bx in (-12, 12) or By in (-12, 12):
                if is_H_road(Bx, By - 1) and is_H_road(Bx, By) and is_V_road(Bx - 1, By) and is_V_road(Bx, By):
                    candidate_blocks_mech.append((Bx, By))
                    
    if candidate_blocks_mech:
        mbx, mby = random.choice(candidate_blocks_mech)
        mechanic = {'x': mbx * 9, 'y': mby * 9, 'met': False}
        mechanic['under'] = world.get((mechanic['x'], mechanic['y']), '🔹')
        world[(mechanic['x'], mechanic['y'])] = '🧑‍🔧'

    # Car initialization
    min_dist = None
    nearest_sidewalks = []
    for wx in range(-21, 22):
        for wy in range(-21, 22):
            if world.get((wx, wy)) == '🔸':
                dist = abs(wx) + abs(wy)
                if min_dist is None or dist < min_dist:
                    min_dist = dist
                    nearest_sidewalks = [(wx, wy)]
                elif dist == min_dist:
                    nearest_sidewalks.append((wx, wy))
    if nearest_sidewalks:
        sx, sy = random.choice(nearest_sidewalks)
        car = {'x': sx, 'y': sy, 'type': random.choice(['🚗', '🚘']), 'speed': 0, 'dir': (0, 0), 'in_car': False}
    else:
        car = {'x': 0, 'y': 1, 'type': '🚗', 'speed': 0, 'dir': (0, 0), 'in_car': False}

    # Поиск подходящих 🪩 и спавн 🧔
    eligible_discos = []
    for (x, y), cell in list(world.items()):
        if cell == '🪩':
            adj_blue = 0
            for dx, dy in [(-1,0),(1,0),(0,-1),(0,1)]:
                if world.get((x+dx, y+dy)) == '🔹':
                    adj_blue += 1
            if adj_blue >= 3:
                eligible_discos.append((x, y))
    if eligible_discos:
        club_entrance = random.choice(eligible_discos)
        # Найти соседние 🔹
        blue_neighbors = []
        for dx, dy in [(-1,0),(1,0),(0,-1),(0,1)]:
            nx, ny = club_entrance[0]+dx, club_entrance[1]+dy
            if world.get((nx, ny)) == '🔹':
                blue_neighbors.append((nx, ny))
        if blue_neighbors:
            spawn_pos = random.choice(blue_neighbors)
            new_npc = {'x': spawn_pos[0], 'y': spawn_pos[1], 'under': '🔹', 'met': False, 'invited': False, 'disappear_next': False}
            world[(spawn_pos[0], spawn_pos[1])] = '🧔'

def get_bus_state_from_step(rect, B_val, step):
    if rect == 'H':
        y_top = B_val * 9 + 4
        y_bottom = B_val * 9 + 5
        if 0 <= step <= 74:
            x, y, dx, dy, is_turn = 111 - step * 3, y_top, -1, 0, (step == 74)
        else:
            x, y, dx, dy, is_turn = -111 + (step - 75) * 3, y_bottom, 1, 0, (step == 149)
        return x, y, dx, dy, is_turn
    else:
        x_left = B_val * 9 + 4
        x_right = B_val * 9 + 5
        if 0 <= step <= 74:
            x, y, dx, dy, is_turn = x_left, -111 + step * 3, 0, 1, (step == 74)
        else:
            x, y, dx, dy, is_turn = x_right, 111 - (step - 75) * 3, 0, -1, (step == 149)
        return x, y, dx, dy, is_turn

def update_bus_coords(bus):
    x, y, dx, dy, is_turn = get_bus_state_from_step(bus['rect'], bus['B_val'], bus['step'])
    bus['x'], bus['y'], bus['dx'], bus['dy'], bus['is_turn'] = x, y, dx, dy, is_turn

def get_bus_path(bus):
    path = []
    if bus['is_turn']:
        nx, ny, _, _, _ = get_bus_state_from_step(bus['rect'], bus['B_val'], (bus['step'] + 1) % 150)
        path.append((nx, ny))
    else:
        for i in range(1, 4): path.append((bus['x'] + bus['dx'] * i, bus['y'] + bus['dy'] * i))
    return path

def spawn_buses():
    global buses
    buses = []
    routes_list = []
    for By in [-8, -3, 2, 7]: routes_list.append(('H', By))
    for Bx in [-8, -3, 2, 7]: routes_list.append(('V', Bx))
        
    for rect, B_val in routes_list:
        route_offset = random.randint(0, 149)
        for i in range(6):
            step = (route_offset + i * 25) % 150
            bus = {'rect': rect, 'B_val': B_val, 'step': step, 'pair_id': (rect, B_val), 'type': '🚌' if rect == 'H' else '🚍'}
            update_bus_coords(bus)
            buses.append(bus)

def move_buses(ignore_player=False):
    global buses, px, py, player_msg, grandma, car, deer, gravediggers
    
    routes = list(set([bus['pair_id'] for bus in buses]))
    random.shuffle(routes)
    
    moving = {r: True for r in routes}
    current_cells = {r: set() for r in routes}
    intended_cells = {r: set() for r in routes}
    
    for bus in buses:
        r = bus['pair_id']
        current_cells[r].add((bus['x'], bus['y']))
        for cell in get_bus_path(bus): intended_cells[r].add(cell)
            
    changed = True
    while changed:
        changed = False
        for i in range(len(routes)):
            rA = routes[i]
            if not moving[rA]: continue
            must_stop = False
            for j in range(len(routes)):
                if i == j: continue
                rB = routes[j]
                if not moving[rB]:
                    if intended_cells[rA].intersection(current_cells[rB]):
                        must_stop = True
                        break
                else:
                    if i > j and intended_cells[rA].intersection(intended_cells[rB]):
                        must_stop = True
                        break
            if must_stop:
                moving[rA] = False
                changed = True
                
    hit_player = False
    hit_car = False
    deer_hit = False
    buses_to_remove = []
    
    # Сначала проверяем столкновения с могильщиками на пути автобусов
    gravediggers_to_remove = []
    for bus in buses:
        if moving[bus['pair_id']]:
            path = get_bus_path(bus)
            # Проверяем, есть ли могильщик на пути автобуса
            for g in gravediggers:
                if (g['x'], g['y']) in path:
                    # Могильщик сбит
                    world[(g['x'], g['y'])] = '🥄'   # оставляем лопату
                    gravediggers_to_remove.append(g)
                    if player_msg:
                        player_msg += "\nМогильщика насмерть сбил автобус!"
                    else:
                        player_msg = "Могильщика насмерть сбил автобус!"
                    break
            # Также проверяем текущую клетку автобуса (на случай наложения)
            for g in gravediggers:
                if (g['x'], g['y']) == (bus['x'], bus['y']):
                    world[(g['x'], g['y'])] = '🥄'
                    if g not in gravediggers_to_remove:
                        gravediggers_to_remove.append(g)
                        if player_msg:
                            player_msg += "\nМогильщика насмерть сбил автобус!"
                        else:
                            player_msg = "Могильщика насмерть сбил автобус!"
    
    # Удаляем сбитых могильщиков
    for g in gravediggers_to_remove:
        if g in gravediggers:
            gravediggers.remove(g)
    
    for bus in buses:
        if moving[bus['pair_id']]:
            path = get_bus_path(bus)
            if not ignore_player and not car['in_car'] and (px, py) in path:
                hit_player = True
                if bus not in buses_to_remove: buses_to_remove.append(bus)
            if (car['x'], car['y']) in path:
                hit_car = True
                if bus not in buses_to_remove: buses_to_remove.append(bus)
                
            if grandma['state'] in ['waiting', 'crossing', 'going_home'] and (grandma['x'], grandma['y']) in path:
                grandma['state'] = 'dead'
                world[(grandma['x'], grandma['y'])] = '🎗️'
                msg_add = "😱 Бабушку насмерть сбил автобус! 😱"
                if player_msg and msg_add not in player_msg: player_msg += "\n" + msg_add
                elif not player_msg: player_msg = msg_add
            
            # Проверка столкновения автобуса с оленем
            if deer is not None and (deer['x'], deer['y']) in path:
                deer_hit = True
                if deer is not None:
                    world[(deer['x'], deer['y'])] = deer['under']
                    deer = None
                if not ignore_player:
                    msg = "Автобус насмерть сбил оленя! 🤣"
                    if player_msg: player_msg += "\n" + msg
                    else: player_msg = msg
            
            # Проверка статического оленя (если deer == None, но в мире есть 🦌)
            for cell in path:
                if world.get(cell) == '🦌' and deer is None:
                    deer_hit = True
                    world[cell] = '▪️' if is_far_from_road(cell[0], cell[1]) else '🌲'
                    if not ignore_player:
                        msg = "Автобус насмерть сбил оленя! 🤣"
                        if player_msg: player_msg += "\n" + msg
                        else: player_msg = msg
                    break
                    
            bus['step'] = (bus['step'] + 1) % 150
            update_bus_coords(bus)
            
    for b in buses_to_remove:
        if b in buses:
            buses.remove(b)
            
    return hit_player or hit_car

def is_last_stop(bus):
    sim_bus = bus.copy()
    while not sim_bus['is_turn']:
        sim_bus['step'] = (sim_bus['step'] + 1) % 150
        update_bus_coords(sim_bus)
        for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
            if (sim_bus['x'] + dx, sim_bus['y'] + dy) in stop_signs: return False
    return True

def exit_bus(player_bus):
    global px, py, player_msg, player_in_bus
    player_in_bus = False
    for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
        if (player_bus['x'] + dx, player_bus['y'] + dy) in stop_signs:
            px, py = player_bus['x'] + dx, player_bus['y'] + dy
            player_msg = "Вы вышли из автобуса."
            return
    px, py = player_bus['x'], player_bus['y']
    player_msg = "Вы вышли из автобуса."

def run_bus_animation(player_bus):
    global px, py, player_msg, player_in_bus, tracked_target
    player_in_bus = True
    draw(px, py, in_bus=True, hide_hud=True)
    time.sleep(1.0)
    player_msg = ""
    just_boarded = True
    
    while True:
        time.sleep(0.2)
        move_buses(ignore_player=True)
        px, py = player_bus['x'], player_bus['y']
        generate_area(px, py)
        
        at_stop = False
        for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
            if (px + dx, py + dy) in stop_signs:
                at_stop = True
                break
                
        if at_stop and not just_boarded:
            is_last = is_last_stop(player_bus)
            while True:
                if is_last: player_msg = "Остановка (Конечная). Нажмите 1, чтобы выйти."
                else: player_msg = "Остановка. 1 - Ехать дальше, 2 - Выйти."
                draw(px, py, in_bus=True, hide_hud=False)
                ch = getch()
                # Обработка клавиш-цифр для компаса
                if ch in ['3','4','5','6','7','8','0']:
                    if ch == '3': tracked_target = 'grandma'
                    elif ch == '4': tracked_target = 'monument'
                    elif ch == '5': tracked_target = 'new_npc'
                    elif ch == '6': tracked_target = 'mechanic'
                    elif ch == '7': tracked_target = 'shop'
                    elif ch == '8': tracked_target = 'hut'
                    elif ch == '0': tracked_target = None
                    # Перерисовать экран, чтобы показать индикатор
                    draw(px, py, in_bus=True, hide_hud=False)
                    continue
                if is_last:
                    if ch in ['1', '!', ' ', '\r', '\n', 'q', 'й', '\x1b']:
                        exit_bus(player_bus)
                        return
                else:
                    if ch in ['1', '!', ' ', '\r', '\n']:
                        player_msg = ""
                        just_boarded = True
                        break
                    elif ch in ['2', '@', '"', 'q', 'й', '\x1b']:
                        exit_bus(player_bus)
                        return
        else:
            if not at_stop: just_boarded = False
            draw(px, py, in_bus=True, hide_hud=True)

def is_kad(x, y):
    """Проверяет, находится ли точка на КАД (окружной дороге)."""
    if (abs(x) == 112 or abs(x) == 113) and abs(y) <= 113:
        return True
    if (abs(y) == 112 or abs(y) == 113) and abs(x) <= 113:
        return True
    return False

def draw(cx, cy, dead=False, in_bus=False, hide_hud=False):
    global tracked_target, player_is_clown, player_is_elf, gravediggers, coffin_coords, shovel
    global option_block_active, hut_center
    os.system('cls' if os.name == 'nt' else 'clear')
    term_cols, term_lines = shutil.get_terminal_size(fallback=(80, 24))
    
    GRID_WIDTH = 30
    UI_WIDTH = 44 
    
    # Определяем, что выводить над сеткой (район, КАД, проспекты)
    display_items = []  # список кортежей (текст, цвет или None)
    
    if is_kad(cx, cy):
        display_items.append(("КАД", None))
    else:
        # Проверяем проспекты
        prospects = []
        # Горизонтальные проспекты: координата y в диапазоне B*9+3 .. B*9+6 при |x|<=111
        for b in [-8, -3, 2, 7]:
            if abs(cx) <= 111 and b*9+3 <= cy <= b*9+6:
                prospects.append(("H", b))
        # Вертикальные проспекты: координата x в диапазоне B*9+3 .. B*9+6 при |y|<=111
        for b in [-8, -3, 2, 7]:
            if abs(cy) <= 111 and b*9+3 <= cx <= b*9+6:
                prospects.append(("V", b))
        
        if prospects:
            for orient, b in prospects:
                if orient == 'H':
                    name = prospect_names_h[b]
                else:
                    name = prospect_names_v[b]
                display_items.append((f"ПРОСПЕКТ {name}", None))
        else:
            # Обычный район
            dist = get_district(cx, cy)
            if dist:
                c, r = dist
                raw_text = f"{district_names[(c, r)]} РАЙОН"
                color = district_colors[(c, r)]
                display_items.append((raw_text, color))
    
    total_lines = 22
    if display_items:
        total_lines += 3  # всегда добавляем 3 строки (тексты + пустые)
    
    v_pad = max(0, (term_lines - total_lines) // 2)
    grid_h_pad = max(0, (term_cols - GRID_WIDTH) // 2)
    ui_h_pad = max(0, (term_cols - UI_WIDTH) // 2)
    
    grid_pad_str = " " * grid_h_pad
    ui_pad_str = " " * ui_h_pad
    
    bus_map = { (bus['x'], bus['y']): bus['type'] for bus in buses }
    
    print("\n" * v_pad, end="")
    
    # Выводим названия (район/КАД/проспекты) и пустые строки
    if display_items:
        for raw_text, color in display_items:
            if color:
                pad = max(0, (term_cols - len(raw_text)) // 2)
                print(" " * pad + color + raw_text + '\033[0m')
            else:
                pad = max(0, (term_cols - len(raw_text)) // 2)
                print(" " * pad + raw_text)
        # Добавляем пустые строки, чтобы всего было 3
        num_empty = 3 - len(display_items)
        for _ in range(num_empty):
            print()
    else:
        pass
    
    # Сетка
    for y in range(cy - 7, cy + 8):
        row = []
        for x in range(cx - 7, cx + 8):
            if x == cx and y == cy:
                if in_bus:
                    row.append(bus_map.get((x, y), '🚌'))
                elif car['in_car']:
                    row.append('💥' if dead else car['type'])
                elif dead:
                    row.append('💀')
                elif player_is_clown:
                    row.append('🤡')
                elif player_is_elf:
                    row.append('🧝')
                else:
                    row.append('👩‍🦰')
            elif (x, y) in bus_map:
                row.append(bus_map[(x, y)])
            elif x == car['x'] and y == car['y'] and not car['in_car']:
                row.append(car['type'])
            elif deer is not None and x == deer['x'] and y == deer['y']:
                row.append('🦌')
            elif (x, y) == coffin_coords:
                row.append('⚰️')
            elif any(g['x'] == x and g['y'] == y for g in gravediggers):
                row.append('👷')
            else:
                row.append(world.get((x, y), '🔹'))
        print(grid_pad_str + "".join(row))
        
    print() 
    
    UI_LINES_TOTAL = 6 
    ui_lines = []
    
    if not hide_hud:
        if car['in_car']:
            ui_lines.append("Управление авто: Н,И,П,О/WASD")
            ui_lines.append("Газ/Поворот/Тормоз(Пробел) | E/У - Выход")
            dir_map = {(0,-1): '⬆️', (1,0): '⬇️', (0,1): '⬇️', (-1,0): '⬅️'}
            dir_str = dir_map.get(car['dir'], '') if car['speed'] > 0 else ''
            ui_lines.append(f"Скорость: {car['speed']}/5  Направление: {dir_str}")
        else:
            ui_lines.append("Управление: Н,И,П,О или Стрелки/WASD")
            ui_lines.append("Р/Пробел - Стоять | Q/Esc - Выход")
            # Новая строка: если есть лопата и стоим на ▪️, показать копать
            if shovel > 0 and world.get((cx, cy)) == '▪️':
                ui_lines.append("У/Е - КОПАТЬ")
            items = []
            if roses_collected > 0: items.append(f"🌹: {roses_collected}")
            if ribbons_collected > 0: items.append(f"🎗️: {ribbons_collected}")
            if bouquets_collected > 0: items.append(f"💐: {bouquets_collected}")
            if lemon_tampon:
                items.append("🍋 в ({})")
            elif lemon > 0:
                items.append(f"🍋: {lemon}")
            if salt > 0: items.append(f"🧂: {salt}")
            if toilet_paper > 0: items.append(f"🧻: {toilet_paper}")
            if shovel > 0: items.append(f"🥄: {shovel}")
            if items:
                ui_lines.append(f"Инвентарь: {' | '.join(items)}")
        
        # Если есть нужда в туалете, выводим сообщение и счётчик
        if toilet_patience is not None and toilet_patience > 0:
            orange = '\033[38;5;208m'
            reset = '\033[0m'
            ui_lines.append(f"{orange}ВЫ ХОТИТЕ В ТУАЛЕТ!{reset}")
            ui_lines.append(f"🚽 Терпение: {toilet_patience}")
        
        # Добавление индикатора направления, если включён и цель существует
        if tracked_target is not None:
            tx = ty = None
            emoji = ""
            if tracked_target == 'grandma':
                if grandma['state'] not in ['none','dead']:
                    tx, ty = grandma['x'], grandma['y']
                    emoji = '👵'
            elif tracked_target == 'monument':
                if monument_coords is not None:
                    tx, ty = monument_coords
                    emoji = '🗿'
            elif tracked_target == 'new_npc':
                if new_npc is not None:
                    tx, ty = new_npc['x'], new_npc['y']
                    emoji = '🧔'
            elif tracked_target == 'mechanic':
                if mechanic is not None:
                    tx, ty = mechanic['x'], mechanic['y']
                    emoji = '🧑‍🔧'
            elif tracked_target == 'shop':
                if shop_with_queue_coords is not None:
                    tx, ty = shop_with_queue_coords
                    emoji = '🏪'
            elif tracked_target == 'hut':
                if hut_center is not None:
                    tx, ty = hut_center
                    emoji = '🛖'
            
            if tx is not None and ty is not None:
                dx = tx - cx
                dy = ty - cy
                # Вычисляем угол от севера (ось Y вниз, поэтому -dy)
                angle = math.degrees(math.atan2(dx, -dy))
                angle = (angle + 360) % 360
                # Определяем сектор (8 направлений)
                sector = int(((angle + 22.5) % 360) // 45)
                # Стрелки в нужном виде
                arrows = ['⬆️','↗️','➡️','↘️','⬇️','↙️','⬅️','↖️']
                arrow = arrows[sector]
                ui_lines.append(f"{emoji}{arrow}")
        
        ui_lines.append("-" * UI_WIDTH)
    else:
        ui_lines.extend(["", "", ""])
        
    if player_msg:
        for msg_line in player_msg.split('\n'):
            wrapped_msg = textwrap.wrap(msg_line, width=UI_WIDTH)
            ui_lines.extend(wrapped_msg)
    
    # Добавляем опции, если активен блок
    if option_block_active:
        ui_lines.append("1 — подраться с 👩")
        ui_lines.append("2 — позвонить 🧑‍🔧")
        ui_lines.append("9 — убить 👩 лопатой")
        
    while len(ui_lines) < UI_LINES_TOTAL:
        ui_lines.append("")
        
    for line in ui_lines:
        print(ui_pad_str + line.ljust(UI_WIDTH))

def show_splash_screen():
    os.system('cls' if os.name == 'nt' else 'clear')
    term_cols, term_lines = shutil.get_terminal_size(fallback=(80, 24))
    
    splash_art = [
        "  ▪️▪️▪️👑▪️▪️▪️",
        "  ▪️ 🎀🏵️🏵️🏵️🎀▪️",
        "  ▪️🏵️ 👁️🌟👁️🏵️▪️",
        "  ▪️🏵️💢 👃💢🏵️▪️",
        "  ▪️🏵️🌟 👄🌟🏵️▪️",
        "  ▪️▪️🔶🔶🔶▪️▪️",
        "  🫲🔶▪️♥️▪️🔶🤳",
        "  ▪️▪️▪️🔶▪️▪️▪️",
        "  ▪️▪️▪️☘️▪️▪️▪️",
        "  ▪️▪️🔶▪️🔶▪️▪️",
        "  ▪️▪️🔶▪️▪️🔶▪️",
        "  ▪️▪️👠▪️▪️▪️👠",
        "",
        "===================",
        "= МАРУСЯ-НЕТАКУСЯ =",
        "==================="
    ]
    
    v_pad = max(0, (term_lines - len(splash_art)) // 2)
    h_pad = max(0, (term_cols - 19) // 2)
    
    print("\n" * v_pad, end="")
    for line in splash_art: print(" " * h_pad + line)
    time.sleep(5)

def show_prison_screen():
    os.system('cls' if os.name == 'nt' else 'clear')
    term_cols, term_lines = shutil.get_terminal_size(fallback=(80, 24))
    scene = [
        "🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱",
        "🧱▪️▪️▪️▪️▪️▪️▪️▪️▪️🧱",
        "🧱▪️▪️▪️######▪️▪️▪️🧱",
        "🧱▪️👮▪️##👩‍🦰##▪️👮▪️🧱",
        "🧱▪️▪️▪️######▪️▪️▪️🧱",
        "🧱▪️▪️▪️▪️▪️▪️▪️▪️▪️🧱",
        "🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱🧱",
        "",
        "\033[91mТы села в тюрьму за тяжкое преступление!",
        "Это тебе не GTA!\033[0m"
    ]
    v_pad = max(0, (term_lines - len(scene)) // 2)
    h_pad = max(0, (term_cols - 21) // 2)
    print("\n" * v_pad, end="")
    for line in scene:
        if "\033" in line:
            clean_len = len(line) - 9
            print(" " * max(0, (term_cols - clean_len) // 2) + line)
        else:
            print(" " * h_pad + line)
    time.sleep(5)

def play_mechanic_dialogue():
    global player_msg, px, py
    dialogue = [
        ("🧑‍🔧: Чё те надо, овца?", "👩‍🦰: Может, познакомимся для начала?"),
        ("🧑‍🔧: Я Егор Туалетов, меня в городе знают все уважаемые люди.", "👩‍🦰: А я Маруся, очень приятно."),
        ("🧑‍🔧: Это тебе очень приятно, а мне не очень.", "👩‍🦰: У тебя есть жена или девушка?"),
        ("🧑‍🔧: Не твоё дело.", "👩‍🦰: Очень даже моё. Ты почему живёшь у чёрта на рогах? Я еле нашла тебя."),
        ("🧑‍🔧: Да отъебись ты от меня! Я не NPC, а живой человек, так что ничем помочь не могу!", "👩‍🦰: Это мы ещё посмотрим...")
    ]
    
    for lines in dialogue:
        player_msg = lines[0] + "\n" + lines[1]
        for _ in range(25):
            time.sleep(0.2)
            move_buses(ignore_player=True)
            generate_area(px, py)
            draw(px, py, hide_hud=False)
    player_msg = ""

def play_club_scene():
    global player_msg, px, py, bouquets_collected, roses_collected, ribbons_collected
    global club_visited, club_blacklist, lemon, salt
    
    os.system('cls' if os.name == 'nt' else 'clear')
    term_cols, term_lines = shutil.get_terminal_size(fallback=(80, 24))
    
    club_art = [
        "💈🌟🌟🌟🌟🪩🌟🌟🌟🌟💈",
        "💈▪️🎶🎶🎶🎶🎶🎶🎶▪️💈",
        "💈🔊▪️▪️▪️▪️▪️▪️▪️▪️💈",
        "💈▪️💃🕺👫🤸🕴️🕺💃▪️💈",
        "💈▪️👩👨▪️🥴▪️👱👱‍♀️▪️💈",
        "💈▪️▪️▪️👩‍🦰🤝🧔▪️▪️▪️💈",
        "💈🤢▪️🫗🚬🚬🚬🪳▪️🤮💈",
        "🚧🚧🚧🚧👮👮👮🚧🚧🚧🚧"
    ]
    
    segments = [
        ("🎵Трахнул я одну ТП —\nОказалась РСП.🎵",
         "👩‍🦰: Как тебя зовут, красавчик?\n🧔: Ясурам, а тебя?"),
        ("🎵Трахнул я её ЛП —\nПодхватил ЗППП!🎵",
         "👩‍🦰: А я Маруся.\n🧔: Так чей это был букет?"),
        ("🎵Получилось тупо —\nЧешется залупа!🎵",
         "👩‍🦰: А где твой каУказский акЬцент, Уася?\n🧔: Нет у меня акцента, это я угорал просто."),
        ("🎵Так что надо, братцы,\nНам предохраняться!🎵",
         "👩‍🦰: Ясно, угостишь меня текилой?\n🧔: Угощу, конечно. Знаешь, как её пить?"),
        ("🎵Трахнул я альтушку в рот\nИ спустил ей на живот.🎵",
         "👩‍🦰: Не учи мать ебаться, угощай давай!\n🧔: Может, закусишь чем-нибудь?"),
        ("🎵Заказал ей мидии —\nПолучил хламидии!🎵",
         "👩‍🦰: Зачем?! Это же не чистый спирт!\n🧔: А ты сама дойдёшь до дома?"),
        ("🎵Получилось тупо —\nЧешется залупа!🎵",
         "👩‍🦰: Не боись, я за рулём! Ик!\n🧔: Если что — заходи к нам в гости в кавказский квартал,\nтам тебя всегда накормят и напоят."),
        ("🎵Так что надо, братцы,\nНам предохраняться!🎵",
         "👩‍🦰: А потом там меня трахнут, я надеюсь?\nЯ бы не отказалась от групповушки!\n👮: Девушка, вы пьяны, пройдёмте на выход.")
    ]
    
    art_pad = max(0, (term_cols - 22) // 2)
    
    for seg_idx, (lyrics, dialogue) in enumerate(segments):
        os.system('cls' if os.name == 'nt' else 'clear')
        print("\n" * ((term_lines - 10) // 2))
        lyrics_lines = lyrics.split('\n')
        for line in lyrics_lines:
            print(" " * ((term_cols - len(line)) // 2) + line)
        print()
        for line in club_art:
            print(" " * art_pad + line)
        print()
        dialogue_lines = dialogue.split('\n')
        for line in dialogue_lines:
            print(" " * ((term_cols - len(line)) // 2) + line)
        time.sleep(5)
    
    club_visited = True
    club_blacklist = True
    
    lemon = 1
    salt = 1
    player_msg = "Вы получили лимон и соль!\nТеперь вы в чёрном списке всех клубов города."

def play_feast_scene():
    global feast_visited, player_msg, toilet_patience
    
    board = [
        ['▪️', '▪️', '▪️', '▪️', '👩‍🦰', '▪️', '▪️', '▪️', '▪️'],
        ['▪️', '——', '——', '——', '——', '——', '——', '——', '▪️'],
        ['🧔‍♂️', '🥟', '🍷', '🔹', '🔹', '🔹', '🍷', '🥟', '🧔‍♂️'],
        ['🧔‍♂️', '🥟', '🍷', '🔹', '🍖', '🔹', '🍷', '🥟', '🧔‍♂️'],
        ['🧔‍♂️', '🥟', '🍷', '🔹', '🔹', '🔹', '🍷', '🥟', '🧔‍♂️'],
        ['🧔‍♂️', '🥟', '🍷', '🔹', '🍲', '🔹', '🍷', '🥟', '🧔‍♂️'],
        ['🧔‍♂️', '🥟', '🍷', '🔹', '🔹', '🔹', '🍷', '🥟', '🧔‍♂️'],
        ['🧔‍♂️', '🥟', '🍷', '🔹', '🥩', '🔹', '🍷', '🥟', '🧔‍♂️'],
        ['🧔‍♂️', '🥟', '🍷', '🔹', '🔹', '🔹', '🍷', '🥟', '🧔‍♂️'],
        ['▪️', '——', '——', '——', '——', '——', '——', '——', '▪️'],
        ['▪️', '▪️', '🪵', '▪️', '▪️', '▪️', '🪵', '▪️', '▪️'],
        ['▪️', '▪️', '🪵', '▪️', '▪️', '▪️', '🪵', '▪️', '▪️']
    ]
    
    def draw_f(b, msg=""):
        os.system('cls' if os.name == 'nt' else 'clear')
        term_cols, term_lines = shutil.get_terminal_size(fallback=(80, 24))
        
        board_lines = ["".join(row) for row in b]
        msg_lines = msg.split('\n') if msg else []
        total_lines = len(board_lines) + (2 + len(msg_lines) if msg else 0)
        
        v_pad = max(0, (term_lines - total_lines) // 2)
        pad = max(0, (term_cols - 18) // 2)
        
        print("\n" * v_pad, end="")
        for line in board_lines:
            print(" " * pad + line)
            
        if msg:
            print()
            for line in msg_lines:
                print(" " * max(0, (term_cols - len(line)) // 2) + line)

    msg1 = "👩‍🦰: Всем гамарджоба, барев дзес и салам алейкум!\nВаш родственник сказал,\nчто здесь меня накормят и напоят."
    draw_f(board, msg1)
    time.sleep(5)
    
    msg2 = "🧔‍♂️: Мой брат говорил о тебе, но мы не ждали гостей...\n👩‍🦰: Я ваши обычаи знаю, так что не надо ля-ля!\nМаруся трапезничать желает!"
    draw_f(board, msg2)
    time.sleep(5)
    
    board[0][4] = '▪️'
    board[2][4] = '👩‍🦰'
    px_f, py_f = 4, 2
    
    msg = ""
    while True:
        draw_f(board, msg)
        
        left = sum(1 for row in board for cell in row if cell in ['🥟', '🍷', '🍖', '🍲', '🥩'])
        if left == 0:
            break
            
        ch = getch()
        dx, dy = 0, 0
        if ch in ['н', 'y', 'w', 'ц', 'up']: dy = -1
        elif ch in ['и', 'b', 's', 'ы', 'down']: dy = 1
        elif ch in ['п', 'g', 'a', 'ф', 'left']: dx = -1
        elif ch in ['о', 'j', 'd', 'в', 'right']: dx = 1
        
        if dx == 0 and dy == 0:
            continue
            
        nx, ny = px_f + dx, py_f + dy
        if 0 <= nx < 9 and 0 <= ny < 12:
            target = board[ny][nx]
            if target not in ['——', '🧔‍♂️', '▪️', '🪵']:
                board[py_f][px_f] = '🔹'
                px_f, py_f = nx, ny
                board[py_f][px_f] = '👩‍🦰'
                
                if target == '🥟': msg = "👩‍🦰: Ам!"
                elif target == '🍷': msg = "👩‍🦰: Глыг-глыг!"
                elif target == '🍲': msg = "👩‍🦰: Сёрб-сёрб-сёрб!"
                elif target in ['🍖', '🥩']: msg = "👩‍🦰: Ам-ам-ам-ам-ам!"
                else: msg = ""
            
    win_msg = "👩‍🦰: Как-то скромненько, мальчики.\nЯ ожидала большего. Всем пока! 💋"
    draw_f(board, win_msg)
    time.sleep(5)
    
    feast_visited = True
    toilet_patience = 25   # появляется нужда в туалете
    player_msg = ""

# Новая функция для сцены в церкви (эльф)
def play_church_scene(enter_x, enter_y):
    global player_is_elf, lemon_tampon, car, px, py, player_msg, lemon
    global church_visited_global, gravediggers, coffin_coords, gravedigger_triggered, gravedigger_move_countdown
    church_art = [
        "▪️▪️▪️▪️▪️▪️▪️▪️▪️▪️▪️",
        "👳▪️▪️▪️✝️▪️✝️▪️✝️▪️▪️",
        "🧕🧕▪️▪️🕯▪️🕯️▪️🕯️▪️▪️",
        "👳👳👳▪️▪️▪️▪️▪️▪️▪️▪️",
        "🧕🧕🧕🧕▪️▪️▪️▪️▪️🧙▪️",
        "👳👳👳👳👳▪️▪️▪️▪️🪵🪵",
        "🧕🧕🧕🧕🧕🧝▪️▪️▪️🪵🪵",
        "☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️"
    ]
    texts = [
        "🧙: Сыны и дочери Абслаха!\nПозвольте прочитать вам древний абсят,\nпоражающий ёмкостью и лаконичностью!",
        "🧙: Простой\nКвадрат —\nБольшой\nНаш Брат.\nИ одного\nЛюби Его!\nБудь рад!",
        "🧙: Здесь Священный Квадран учит нас,\nчто нет ничего лучше квадрата.\nДаже круглую Землю мы измеряем\nквадратными единицами.\nДаже наш огромный город — это квадрат,\nразделённый на квадратное число\nквадратных районов!",
        "🧙: А теперь пару слов о гигиене.\nСыны и дочери Абслаха, от кого из вас\nпахнет рыбой в лимонном соусе?\n🧝: Ой, мне нужно срочно припудрить носик..."
    ]
    for stage, text in enumerate(texts):
        os.system('cls' if os.name == 'nt' else 'clear')
        term_cols, term_lines = shutil.get_terminal_size(fallback=(80, 24))
        art_lines = church_art
        text_lines = text.split('\n')
        total_lines = len(art_lines) + 1 + len(text_lines)
        v_pad = max(0, (term_lines - total_lines) // 2)
        h_pad = max(0, (term_cols - max(len(line) for line in art_lines)) // 2)
        print("\n" * v_pad, end="")
        for line in art_lines:
            print(" " * h_pad + line)
        print()
        for tline in text_lines:
            print(" " * max(0, (term_cols - len(tline)) // 2) + tline)
        time.sleep(5)
    # Восстановление
    px, py = enter_x, enter_y
    player_is_elf = False
    lemon_tampon = False
    lemon = 0
    car['x'] = 1000
    car['y'] = 1000
    car['in_car'] = False
    player_msg = ("Вы достали лимон, завернули в туалетную\n"
                  "бумагу и спрятали за углом!\n"
                  "\033[38;5;208mКто-то угнал ваш автомобиль!\033[0m")
    # После церкви спавним гроб и могильщиков
    church_visited_global = True
    spawn_gravediggers()

def spawn_gravediggers():
    global coffin_coords, gravediggers, world, grandma
    if grandma['start_x'] is None or grandma['start_y'] is None:
        return
    coffin_coords = (grandma['start_x'], grandma['start_y'])
    world[coffin_coords] = '⚰️'
    # Ищем соседние клетки тротуара (🔸) для могильщиков
    adjacent_sidewalk = []
    for dx, dy in [(-1,0),(1,0),(0,-1),(0,1)]:
        nx, ny = coffin_coords[0]+dx, coffin_coords[1]+dy
        if -114 <= nx <= 114 and -114 <= ny <= 114 and world.get((nx,ny)) == '🔸':
            adjacent_sidewalk.append((nx,ny))
    # Нужно два могильщика
    if len(adjacent_sidewalk) >= 2:
        # Выбираем две клетки
        pos1, pos2 = adjacent_sidewalk[0], adjacent_sidewalk[1]
        gravediggers = [
            {'x': pos1[0], 'y': pos1[1], 'under': '🔸', 'state': 'idle', 'cross_dir': None, 'steps_crossed': 0},
            {'x': pos2[0], 'y': pos2[1], 'under': '🔸', 'state': 'idle', 'cross_dir': None, 'steps_crossed': 0}
        ]
        world[pos1] = '👷'
        world[pos2] = '👷'
    else:
        # fallback: если не хватает тротуара, просто разместим на соседних свободных клетках
        candidates = []
        for dx, dy in [(-1,0),(1,0),(0,-1),(0,1)]:
            nx, ny = coffin_coords[0]+dx, coffin_coords[1]+dy
            if -114 <= nx <= 114 and -114 <= ny <= 114 and world.get((nx,ny)) not in ['🏢','🌳','🌲','🪩','🚾','🏪','👵','🏡','🧑‍🔧','🕌','🔔','🛖','🐑','🌴','🗿','🧔','⚰️','👷']:
                candidates.append((nx,ny))
        if len(candidates) >= 2:
            pos1, pos2 = candidates[0], candidates[1]
            gravediggers = [
                {'x': pos1[0], 'y': pos1[1], 'under': world.get(pos1,'🔹'), 'state': 'idle', 'cross_dir': None, 'steps_crossed': 0},
                {'x': pos2[0], 'y': pos2[1], 'under': world.get(pos2,'🔹'), 'state': 'idle', 'cross_dir': None, 'steps_crossed': 0}
            ]
            world[pos1] = '👷'
            world[pos2] = '👷'

def play_gravedigger_dialogue():
    global player_msg, px, py, gravedigger_triggered, gravedigger_move_countdown
    segments = [
        "👩‍🦰: А вы чего здесь яйца чешете?\n👷: Да вот бабку автобус сбил,\nа мы не знаем, где её закопать.",
        "👩‍🦰: Ясно, как насчёт МЖМ?\n👷: ОК, только за презиками сгоняем."
    ]
    for seg in segments:
        player_msg = seg
        # Рандомная длительность: от 3 до 5 секунд с шагом 0.2
        # => количество кадров от 15 до 25 (включительно)
        n_frames = random.randint(15, 25)
        for _ in range(n_frames):
            time.sleep(0.2)
            move_buses(ignore_player=True)
            generate_area(px, py)
            draw(px, py, hide_hud=False)
        player_msg = ""
    gravedigger_triggered = True
    gravedigger_move_countdown = 2

def get_cross_direction(sidewalk_cell):
    # Определяем направление перехода через дорогу для тротуара
    x, y = sidewalk_cell
    # Если тротуар горизонтальный (y на линии B*9+3 или B*9+6)
    for b in [-8,-3,2,7]:
        if y == b*9+3:
            return (0,1)   # вниз к дороге
        if y == b*9+6:
            return (0,-1)  # вверх к дороге
    # Вертикальный тротуар
    for b in [-8,-3,2,7]:
        if x == b*9+3:
            return (1,0)   # вправо
        if x == b*9+6:
            return (-1,0)  # влево
    return (0,0)

def is_cell_free_for_gravedigger(x, y, exclude_player=False):
    """Проверяет, свободна ли клетка для могильщика."""
    if not (-114 <= x <= 114 and -114 <= y <= 114):
        return False
    # Проверяем препятствия
    cell = world.get((x, y), '')
    if cell in ['🏢','🌳','🌲','🪩','🚾','🏪','🏡','🕌','🔔','🛖','🌴','🗿','⚰️','👷','🦌','🐑']:
        return False
    # Проверяем занятость другими могильщиками
    for g in gravediggers:
        if g['x'] == x and g['y'] == y:
            return False
    # Проверяем занятость игроком
    if not exclude_player and (x == px and y == py):
        return False
    # Проверяем автобусы
    for b in buses:
        if b['x'] == x and b['y'] == y:
            return False
    # Проверяем машину
    if car['x'] == x and car['y'] == y and not car['in_car']:
        return False
    return True

def update_gravediggers():
    global gravediggers, gravedigger_triggered, gravedigger_move_countdown, player_msg, coffin_coords, gravedigger_block_dialogue_shown
    if not gravedigger_triggered or not gravediggers:
        return
    # Уменьшаем счётчик после диалога
    if gravedigger_move_countdown > 0:
        gravedigger_move_countdown -= 1
        if gravedigger_move_countdown == 0:
            # Устанавливаем состояние crossing и направление
            cross_dir = get_cross_direction(coffin_coords)
            for g in gravediggers:
                g['state'] = 'crossing'
                g['cross_dir'] = cross_dir
                g['steps_crossed'] = 0
        return  # ещё не двигаемся

    # Сначала обрабатываем возможное удаление (despawn_next)
    to_remove = []
    for g in gravediggers:
        if g['state'] == 'despawn_next':
            # Восстанавливаем клетку и удаляем
            world[(g['x'], g['y'])] = g['under']
            to_remove.append(g)
            if player_msg:
                player_msg += "\nМогильщик зашёл в дом!"
            else:
                player_msg = "Могильщик зашёл в дом!"
    for g in to_remove:
        if g in gravediggers:
            gravediggers.remove(g)

    if not gravediggers:
        return

    # Сначала собираем занятые клетки всеми могильщиками
    occupied = set()
    for g in gravediggers:
        occupied.add((g['x'], g['y']))

    for g in gravediggers[:]:  # итерируем по копии
        if g['state'] == 'crossing':
            dx, dy = g['cross_dir']
            nx, ny = g['x']+dx, g['y']+dy
            # Проверяем, можно ли шагнуть прямо
            if (nx,ny) == (px,py):
                # Игрок преграждает путь, пробуем обойти
                # Направления обхода: перпендикулярные к cross_dir
                side_dirs = [(dy, dx), (-dy, -dx)]  # например для (0,1) -> (1,0), (-1,0)
                moved = False
                for sdx, sdy in side_dirs:
                    sx, sy = g['x'] + sdx, g['y'] + sdy
                    if (sx, sy) == (px,py): continue  # не пытаемся встать на игрока
                    if is_cell_free_for_gravedigger(sx, sy, exclude_player=True):
                        # Перемещаемся в сторону
                        world[(g['x'], g['y'])] = g['under']
                        g['under'] = world.get((sx, sy), '🔹')
                        world[(sx, sy)] = '👷'
                        g['x'], g['y'] = sx, sy
                        # Если первый раз, выводим диалог
                        if not gravedigger_block_dialogue_shown:
                            gravedigger_block_dialogue_shown = True
                            msg = "👷: Пропусти, чего стала?\n👩‍🦰: Может, давай без презиков?\n👷: Ага, чтобы потом залупа чесалась,\nкак в известной песне?"
                            if player_msg:
                                player_msg += "\n" + msg
                            else:
                                player_msg = msg
                        moved = True
                        break
                if not moved:
                    # Если обойти не удалось, остаёмся на месте
                    continue
                # Не увеличиваем steps_crossed, так как это боковой шаг
                # Обновим occupied для следующего могильщика
                occupied.add((g['x'], g['y']))
                continue
            # Если прямая клетка свободна, проверяем как обычно
            if (nx,ny) in occupied or not is_cell_free_for_gravedigger(nx, ny):
                continue
            # Перемещаемся прямо
            world[(g['x'], g['y'])] = g['under']
            g['under'] = world.get((nx, ny), '🔹')
            world[(nx, ny)] = '👷'
            g['x'], g['y'] = nx, ny
            g['steps_crossed'] += 1
            if g['steps_crossed'] >= 3:
                g['state'] = 'to_building'
            occupied.add((nx, ny))
        elif g['state'] == 'to_building':
            # Проверяем, рядом ли здание
            adjacent_building = False
            for dx, dy in [(-1,0),(1,0),(0,-1),(0,1)]:
                if world.get((g['x']+dx, g['y']+dy)) == '🏢':
                    adjacent_building = True
                    break
            if adjacent_building:
                g['state'] = 'despawn_next'
                continue
            # BFS до ближайшего 🏢
            start = (g['x'], g['y'])
            q = [start]
            came_from = {start: None}
            target = None
            while q:
                cx, cy = q.pop(0)
                for dx, dy in [(-1,0),(1,0),(0,-1),(0,1)]:
                    nx, ny = cx+dx, cy+dy
                    if (nx,ny) == (px,py): continue  # не проходим через игрока
                    if (nx,ny) in occupied: continue
                    if not (-114 <= nx <= 114 and -114 <= ny <= 114): continue
                    if get_road_type(nx,ny) is not None: continue
                    if (nx,ny) in came_from: continue
                    cell = world.get((nx,ny), '')
                    if cell in ['🏢','🚾','🏪','👵','🪩','🧔','🏡','🛖','🌴','🗿','⚰️','👷','🦌','🐑']:
                        if cell == '🏢':
                            target = (cx,cy)
                            q = []
                            break
                        continue
                    came_from[(nx,ny)] = (cx,cy)
                    q.append((nx,ny))
                if target:
                    break
            if target and came_from[target] is not None:
                curr = target
                while came_from[curr] != start:
                    curr = came_from[curr]
                nx, ny = curr
                # Проверяем, не занята ли эта клетка игроком (если вдруг)
                if (nx,ny) == (px,py):
                    # Игрок опять преградил, пробуем обход как в crossing
                    # Но здесь направление к цели может быть любым. Попробуем все 4 направления
                    moved = False
                    for sdx, sdy in [(-1,0),(1,0),(0,-1),(0,1)]:
                        sx, sy = g['x'] + sdx, g['y'] + sdy
                        if (sx, sy) == (px,py): continue
                        if is_cell_free_for_gravedigger(sx, sy, exclude_player=True):
                            world[(g['x'], g['y'])] = g['under']
                            g['under'] = world.get((sx, sy), '🔹')
                            world[(sx, sy)] = '👷'
                            g['x'], g['y'] = sx, sy
                            if not gravedigger_block_dialogue_shown:
                                gravedigger_block_dialogue_shown = True
                                msg = "👷: Пропусти, чего стала?\n👩‍🦰: Может, давай без презиков?\n👷: Ага, чтобы потом залупа чесалась,\nкак в известной песне?"
                                if player_msg:
                                    player_msg += "\n" + msg
                                else:
                                    player_msg = msg
                            moved = True
                            break
                    if moved:
                        continue
                    else:
                        continue  # не можем сходить, остаёмся
                # Обычный шаг по BFS
                if (nx,ny) in occupied or not is_cell_free_for_gravedigger(nx, ny):
                    continue
                world[(g['x'], g['y'])] = g['under']
                g['under'] = world.get((nx, ny), '🔹')
                world[(nx, ny)] = '👷'
                g['x'], g['y'] = nx, ny
                occupied.add((nx, ny))
                # Проверяем соседство со зданием после хода
                for dx, dy in [(-1,0),(1,0),(0,-1),(0,1)]:
                    if world.get((g['x']+dx, g['y']+dy)) == '🏢':
                        adjacent_building = True
                        break
                if adjacent_building:
                    g['state'] = 'despawn_next'

def try_deer_interaction():
    """Проверяет взаимодействие героини с оленем (если есть соль)."""
    global deer, salt, player_msg, mechanic
    
    # Если олень уже следует, ничего не делаем
    if deer is not None:
        return
    
    # Ищем соседнего оленя по ортогонали
    for dx, dy in [(-1,0),(1,0),(0,-1),(0,1)]:
        nx, ny = px + dx, py + dy
        if world.get((nx, ny)) == '🦌' and salt > 0:
            if mechanic is not None and mechanic['met']:
                # Удаляем статического оленя, создаём следующего за героиней
                # Точка спавна: клетка, где стоял олень, станет '▪️' при уходе
                deer = {'x': nx, 'y': ny, 'under': '▪️', 'following': True}
                world[(nx, ny)] = '🦌'  # олень остаётся на месте до первого хода
                salt = 0
                player_msg = ("🦌: Какая вкусная соль — как в Питере!\n"
                              "А почему ты плачешь?\n"
                              "👩‍🦰: Меня Егор Туалетов обидел! 😭\n"
                              "🦌: ЧТО?! Он посмел обидеть ДЕВУШКУ?!\n"
                              "Надо срочно набить ему морду!\n"
                              "Веди меня к нему!")
            else:
                # Олень слизывает соль и остаётся на месте
                salt = 0
                player_msg = "Олень слизал у вас всю соль!"
            break

def move_deer_if_following(old_px, old_py):
    """Перемещает оленя на предыдущую позицию героини, если он рядом."""
    global deer, px, py, car, player_in_bus
    
    if deer is None or not deer['following']:
        return
    if car['in_car'] or player_in_bus:
        return
    # Олень двигается только если он был рядом с героиней до её хода
    if abs(old_px - deer['x']) + abs(old_py - deer['y']) != 1:
        return
    # Если героиня не двигалась (old == new), олень тоже стоит
    if old_px == px and old_py == py:
        return
    # Целевая клетка - старая позиция героини
    tx, ty = old_px, old_py
    # Проверка, что целевая клетка свободна (не стена, не занята)
    cell = world.get((tx, ty), '')
    obstacles = ['🏢','🌳','🌲','🪩','🚾','🏪','👵','🏡','🧑‍🔧','🕌','🔔','🛖','🐑','🌴','🗿','🧔','🦌','⚰️','👷']
    if cell in obstacles or any(b['x'] == tx and b['y'] == ty for b in buses) or (tx == px and ty == py):
        return
    # Перемещаем оленя
    old_deer_x, old_deer_y = deer['x'], deer['y']
    # Восстанавливаем клетку под оленем
    world[(old_deer_x, old_deer_y)] = deer['under']
    # Сохраняем содержимое целевой клетки
    deer['under'] = world.get((tx, ty), '')
    # Перемещаем
    deer['x'], deer['y'] = tx, ty
    world[(tx, ty)] = '🦌'

def check_trio_revenge():
    """Проверяет, находятся ли героиня, олень и механик в ряд из трёх клеток."""
    global deer, px, py, mechanic, player_msg
    
    if deer is None or not deer['following'] or mechanic is None:
        return False
    
    positions = [(px, py), (deer['x'], deer['y']), (mechanic['x'], mechanic['y'])]
    
    # Проверяем горизонталь: все y одинаковы и x идут подряд
    ys = [p[1] for p in positions]
    xs = [p[0] for p in positions]
    if len(set(ys)) == 1:
        min_x, max_x = min(xs), max(xs)
        if max_x - min_x == 2 and all(x in xs for x in range(min_x, max_x+1)):
            return True
    # Проверяем вертикаль: все x одинаковы и y идут подряд
    if len(set(xs)) == 1:
        min_y, max_y = min(ys), max(ys)
        if max_y - min_y == 2 and all(y in ys for y in range(min_y, max_y+1)):
            return True
    return False

def play_revenge_scene():
    """25-секундный диалог, анимация и финальное сообщение."""
    global deer, player_msg, px, py, mechanic, mechanic_killed_deer
    
    # 25-секундный диалог (5 сегментов по 5 секунд)
    segments = [
        ("👩‍🦰: Что, мудак, допизделся?\n🧑‍🔧: Опять ты, рыжая шлюха? А рядом кто?"),
        ("👩‍🦰: Это настоящий мужчина.\nСейчас он будет бить тебе ебало.\n🧑‍🔧: Вот этот лизун — настоящий мужчина? 🤣"),
        ("🦌: Сейчас же извинись перед девушкой!\n🧑‍🔧: А то что будет, клоун? Забодаешь меня?"),
        ("🦌: Тебя родители не учили, что девушек нельзя обижать?\n🧑‍🔧: Лол, воспитатель нашёлся! 🤣"),
        ("🦌: Ты не мужчина! Я вызываю тебя на дуэль!\n🧑‍🔧: Значит, ты выбрал смерть.")
    ]
    
    for seg in segments:
        player_msg = seg
        for _ in range(25):
            time.sleep(0.2)
            move_buses(ignore_player=True)
            generate_area(px, py)
            draw(px, py, hide_hud=False)
        player_msg = ""
    
    # Анимация (6 кадров по 0.5 секунды)
    frames = [
        ["🌲▪️▪️🧑‍🔧▪️▪️▪️▪️🌲",
         "🌲▪️▪️🌲▪️🦌▪️▪️🌲"],
        ["🌲▪️▪️🧑‍🔧▪️▪️▪️▪️🌲",
         "🌲▪️▪️🌲🦶🦌▪️▪️🌲"],
        ["🌲▪️▪️🧑‍🔧▪️▪️▪️▪️🌲",
         "🌲▪️▪️🌲🦶💢🦌▪️🌲"],
        ["🌲▪️▪️🧑‍🔧▪️▪️▪️▪️🌲",
         "🌲▪️▪️🌲▪️▪️▪️🦌🌲"],
        ["🌲▪️▪️🧑‍🔧▪️▪️▪️▪️🌲",
         "🌲▪️▪️🌲▪️▪️▪️🩸🌲"],
        ["🌲▪️▪️🧑‍🔧▪️▪️▪️▪️🌲",
         "🌲▪️▪️🌲▪️▪️▪️☠️🌲"]
    ]
    
    term_cols, term_lines = shutil.get_terminal_size(fallback=(80, 24))
    max_row_len = max(len(row) for frame in frames for row in frame)
    h_pad = max(0, (term_cols - max_row_len) // 2)
    v_pad = max(0, (term_lines - len(frames[0])) // 2)
    
    for frame in frames:
        os.system('cls' if os.name == 'nt' else 'clear')
        print("\n" * v_pad, end="")
        for row in frame:
            print(" " * h_pad + row)
        time.sleep(0.5)
    
    # Олень исчезает, клетка становится '▪️'
    if deer is not None:
        world[(deer['x'], deer['y'])] = '▪️'
        deer = None
    
    # Механик убил оленя
    mechanic_killed_deer = True
    
    # Финальное сообщение
    player_msg = ("👩‍🦰: Прости меня, Егор. Я была неправа.\n"
                  "🧑‍🔧: Проехали. Запиши мой телефон, авось пригодится.")

def play_toilet_scene(enter_from_coords, toilet_coords):
    """Сцена туалета. Возвращает True, если героиня спасена, False если умерла."""
    global toilet_patience, mechanic, px, py, player_msg, toilet_paper, player_is_clown, mechanic_killed_deer
    
    toilet_x, toilet_y = toilet_coords
    enter_x, enter_y = enter_from_coords
    
    toilet_art = [
        "🧱🧱🧱🧱🧱🧱🧱",
        "🧱▪️🪰▪️🪰▪️🧱",
        "🧱🪰▪️🪰▪️🪰🧱",
        "🧱▪️🪰▪️🪰💩🧱",
        "🚪👩‍🦰▪️🪰💩🚽🧱",
        "🪹🪹🪹🪹🪹🪹🪹"
    ]
    
    def draw_toilet_art(art, msg=None, msg_color=None):
        os.system('cls' if os.name == 'nt' else 'clear')
        term_cols, term_lines = shutil.get_terminal_size(fallback=(80, 24))
        
        art_lines = art
        total_lines = len(art_lines)
        if msg:
            total_lines += 2 + msg.count('\n') + 1
        v_pad = max(0, (term_lines - total_lines) // 2)
        h_pad = max(0, (term_cols - max(len(line) for line in art_lines)) // 2)
        
        print("\n" * v_pad, end="")
        for line in art_lines:
            print(" " * h_pad + line)
        
        if msg:
            print()
            for mline in msg.split('\n'):
                if msg_color:
                    print(" " * max(0, (term_cols - len(mline)) // 2) + msg_color + mline + '\033[0m')
                else:
                    print(" " * max(0, (term_cols - len(mline)) // 2) + mline)
    
    msg1 = "👩‍🦰: Фух, вот это я насрала, даже в унитаз не влезло.\nОй, дверь заклинило, СПАСИТЕ!"
    draw_toilet_art(toilet_art, msg1)
    time.sleep(5)
    
    if mechanic_killed_deer:
        msg2 = "👩‍🦰: Алло, Егор, выручай, я в туалете застряла!"
        draw_toilet_art(toilet_art, msg2)
        time.sleep(5)
        
        toilet_patience = None
        toilet_paper += 1
        
        if mechanic:
            world[(mechanic['x'], mechanic['y'])] = '▪️'
        else:
            mechanic = {'met': True}
            mechanic['under'] = world.get((enter_x, enter_y), '🔹')
        
        mechanic['x'] = enter_x
        mechanic['y'] = enter_y
        mechanic['under'] = world.get((enter_x, enter_y), '🔹')
        world[(enter_x, enter_y)] = '🧑‍🔧'
        
        placed = False
        for dx, dy in [(-1,0),(1,0),(0,-1),(0,1)]:
            nx, ny = enter_x + dx, enter_y + dy
            if -114 <= nx <= 114 and -114 <= ny <= 114 and world.get((nx, ny)) == '🔹':
                px, py = nx, ny
                placed = True
                break
        if not placed:
            for radius in range(2, 5):
                for dx in range(-radius, radius+1):
                    for dy in range(-radius, radius+1):
                        if abs(dx) + abs(dy) == radius:
                            nx, ny = enter_x + dx, enter_y + dy
                            if -114 <= nx <= 114 and -114 <= ny <= 114 and world.get((nx, ny)) == '🔹':
                                px, py = nx, ny
                                placed = True
                                break
                    if placed: break
                if placed: break
        if not placed:
            px, py = enter_x + 1, enter_y
        
        player_msg = "👩‍🦰: Ты мой герой! 💋\nДавай я тебе отсосу в знак благодарности!\n🧑‍🔧: Иди на хуй, пока я тебя в сортире не закрыл!"
        return True
    else:
        toilet_art_dead = [
            "🧱🧱🧱🧱🧱🧱🧱",
            "🧱▪️🪰▪️🪰▪️🧱",
            "🧱🪰▪️🪰▪️🪰🧱",
            "🧱▪️🪰▪️🪰💩🧱",
            "🚪💀▪️🪰💩🚽🧱",
            "🪹🪹🪹🪹🪹🪹🪹"
        ]
        brown = '\033[38;5;94m'
        msg_death = "ВЫ ЗАДОХНУЛИСЬ В ТУАЛЕТЕ!"
        draw_toilet_art(toilet_art_dead, msg_death, brown)
        time.sleep(5)
        player_is_clown = False
        return False

def visible_length(s):
    """Возвращает длину строки для центрирования (учитывая только символы)."""
    return len(s)

def center_print(s, term_cols):
    """Печатает строку по центру с учётом ширины терминала."""
    print(" " * max(0, (term_cols - visible_length(s)) // 2) + s)

def play_father_reunion_scene():
    """Сцена встречи с отцом в очереди за хлебом. Завершает игру победой."""
    global px, py
    term_cols, term_lines = shutil.get_terminal_size(fallback=(80, 24))
    
    # Сцена с очередью
    queue_art = [
        "▪️▪️▪️▪️▪️▪️▪️‼️▪️▪️🍞🍞🍞",
        "🧑‍💼🧑‍🦲🧑‍✈️🤱🧟🧛👨‍🦳🍞🙎‍♀️▪️🍞🍞🍞",
        "▪️▪️▪️▪️▪️▪️💞‼️▪️▪️🍞🍞🍞",
        "▪️▪️▪️▪️▪️▪️👩‍🦰‼️▪️▪️🍞🍞🍞",
        "▪️▪️▪️▪️▪️▪️▪️‼️▪️▪️🍞🍞🍞"
    ]
    dialogues = [
        "👩‍🦰: ПАПА?!\n👨‍🦳: МАРУСЯ?!",
        "👩‍🦰: Где ты был 16 лет?\n👨‍🦳: Я ушёл за хлебом, разве мама тебе не сказала?",
        "👩‍🦰: Но за 16 лет ты так и не вернулся!\n👨‍🦳: Во всём городе работает лишь 1 магазин,\nвсе эти годы я стоял в очереди.",
        "👨‍🦳: Ты лучше расскажи о себе.\nКогда я видел тебя в последний раз,\nвоспитательница детского садика жаловалась,\nчто ты лазишь мальчикам в трусики!\n👩‍🦰: Это было давно и неправда.\nСейчас я тихая, скромная и добрая,\nна досуге перевожу бабушек через дорогу,\nа ещё хожу в церковь.",
        "👨‍🦳: А жених у тебя есть?\nТы уже целовалась с кем-нибудь?\n👩‍🦰: Ты что, разве можно целоваться до свадьбы?!\nЯ не пью, не курю, не матерюсь\nи уж точно ни с кем не целуюсь!",
        "🧛: Э, мужик, очередь не задерживай!\nЯ тут ещё со времён СССР стою!\nЗабирай хлеб и вали отсюда!\n💪👩‍🦰: Будьте так любезны не разговаривать\nс моим отцом в таком тоне...",
        "🧛: Я тебя знаю! Ты та самая шлю...\n💪👩‍🦰: Что, простите? У меня бицепс зачесался,\nя не расслышала.",
        "🧛: Я говорю: шлю привет тебе и твоему отцу!\n👨‍🦳: Ого, какая ты сильная, Маруся!\nТы в качалку ходишь?",
        "💪👩‍🦰: Нет, это я гектар земли вскопала.\nПросто не люблю сидеть без дела.\n👨‍🦳: Все видели, какая моя дочка умница?",
        "🤱: Мужчина, имейте совесть!\nЯ всю жизнь провела в очереди!\nЯ даже родила в очереди!\nИ забеременела тоже в очереди!\nНашли время для болтовни!\n👨‍🦳: Всё, мы уже уходим."
    ]
    
    # Показываем очередь и диалоги по 5 секунд
    for dialogue in dialogues:
        os.system('cls' if os.name == 'nt' else 'clear')
        art_lines = queue_art
        max_art_width = max(visible_length(line) for line in art_lines)
        art_pad = max(0, (term_cols - max_art_width) // 2)
        dialogue_lines = dialogue.split('\n')
        total_height = len(art_lines) + 1 + len(dialogue_lines)
        v_pad = max(0, (term_lines - total_height) // 2)
        
        print("\n" * v_pad, end="")
        for line in art_lines:
            print(" " * art_pad + line)
        print()
        for line in dialogue_lines:
            print(" " * max(0, (term_cols - visible_length(line)) // 2) + line)
        time.sleep(5)
    
    # Финальная сцена
    os.system('cls' if os.name == 'nt' else 'clear')
    final_art = [
        "▪️▪️▪️🛑▪️▪️▪️",
        "▪️▪️🛑🛑🛑▪️▪️",
        "▪️🛑🛑🛑🛑🛑▪️",
        "🛑🛑🛑🛑🛑🛑🛑",
        "🪵🔸🔸🔸🔸🔸🪵",
        "🪵🔸🔸🖼️🔸🔸🪵",
        "🪵🔸🔸🔸🔸🔸🪵",
        "🚪👨‍🦳🍞🔸👩‍🦰👩‍🦳🪵",
        "🪹🪹🪹🪹🪹🪹🪹"
    ]
    green = '\033[92m'
    reset = '\033[0m'
    victory_msg = green + "НЕВЕРОЯТНО! ОТЕЦ ВЕРНУЛСЯ В СЕМЬЮ С ХЛЕБОМ!" + reset
    trophy_art = [
        "==========",
        "= ПОБЕДА =",
        "=========="
    ]
    
    max_art_width = max(visible_length(line) for line in final_art)
    art_pad = max(0, (term_cols - max_art_width) // 2)
    total_height = len(final_art) + 3 + len(trophy_art)
    v_pad = max(0, (term_lines - total_height) // 2)
    
    print("\n" * v_pad, end="")
    for line in final_art:
        print(" " * art_pad + line)
    print()
    center_print(victory_msg, term_cols)
    print()
    for line in trophy_art:
        center_print(line, term_cols)
    time.sleep(5)

def main():
    global px, py, player_msg, buses, roses_collected, ribbons_collected, bouquets_collected, grandma, mechanic, sheeps, monument_met, car
    global new_npc, club_entrance, club_invited, club_visited, club_blacklist, pending_invite, lemon, salt, deer, player_in_bus, tracked_target, feast_visited
    global toilet_patience, mechanic_killed_deer, player_is_clown, toilet_paper
    global player_is_elf, lemon_tampon, gravediggers, coffin_coords, gravedigger_triggered, gravedigger_move_countdown, church_visited_global, shovel
    global gravedigger_block_dialogue_shown, gravedigger_dialogue_shown
    global queue_npcs_under, shop_with_queue_coords
    global holes_dug, women_defeated_count, women_npcs, option_block_active, current_woman_coords
    global hut_center
    show_splash_screen()  
    init_city()
    world[(0, 0)] = '🔹'
    spawn_buses()
    
    while True:
        generate_area(px, py)
        draw(px, py)
        ch = getch()
        
        # Сброс сообщения в начале цикла
        player_msg = ""
        
        # Сброс опций перед каждой итерацией
        option_block_active = False
        current_woman_coords = None
        
        # Проверка соседства с NPC очереди ТОЛЬКО если игрок не в машине и не в автобусе
        if not car['in_car'] and not player_in_bus:
            adjacent_npc = False
            adjacent_woman = False
            for dx_npc, dy_npc in [(-1,0),(1,0),(0,-1),(0,1)]:
                nx_npc, ny_npc = px + dx_npc, py + dy_npc
                cell = world.get((nx_npc, ny_npc))
                if cell in ['😩','😡','😴','😤','🥸','🤓','🤠','😎']:
                    adjacent_npc = True
                if cell == '👩':
                    adjacent_woman = True
                    current_woman_coords = (nx_npc, ny_npc)
                    break  # достаточно одной женщины
            
            if adjacent_woman and women_defeated_count == 0:
                option_block_active = True
                if not player_msg:
                    player_msg = "👩‍🦰: Мне только спросить.\n👩: Куда без очереди, шаболда?\nДумаешь, самая умная?"
            elif adjacent_npc and not player_msg:
                player_msg = "👩‍🦰: Мне только спросить."
            else:
                option_block_active = False
        
        # Обработка клавиш-цифр для компаса (до выхода и движения)
        if ch in ['3','4','5','6','7','8','0']:
            if ch == '3': tracked_target = 'grandma'
            elif ch == '4': tracked_target = 'monument'
            elif ch == '5': tracked_target = 'new_npc'
            elif ch == '6': tracked_target = 'mechanic'
            elif ch == '7': tracked_target = 'shop'
            elif ch == '8': tracked_target = 'hut'
            elif ch == '0': tracked_target = None
            draw(px, py)
            continue
        
        # Обработка опций, если активны
        if option_block_active and ch in ['1','2','9']:
            if ch == '1':  # подраться с 👩
                if holes_dug < 25:
                    player_msg = "👩 вырубила вас кастетом и затоптала до смерти! 👠"
                    option_block_active = False  # сбрасываем опции
                    draw(px, py, dead=True)
                    time.sleep(3)
                    break
                else:
                    if current_woman_coords:
                        wx, wy = current_woman_coords
                        world[(wx, wy)] = queue_npcs_under.get((wx, wy), '🔹')
                        women_npcs.remove((wx, wy))
                        women_defeated_count += 1
                    player_msg = "Вы вырубили 👩 мускулистой мозолистой рукой\nи затоптали до смерти! 💪"
                    option_block_active = False
                    current_woman_coords = None
                    continue
            elif ch == '2':  # позвонить 🧑‍🔧
                if not mechanic_killed_deer:
                    player_msg = "У вас нет номера 🧑‍🔧!"
                else:
                    player_msg = "🧑‍🔧: Не впутывай меня в свои блядские разборки!"
                option_block_active = False
                draw(px, py)
                time.sleep(2)
                player_msg = ""
                continue
            elif ch == '9':  # убить 👩 лопатой
                if shovel == 0:
                    player_msg = "У вас нет лопаты!"
                    option_block_active = False
                    draw(px, py)
                    time.sleep(2)
                    player_msg = ""
                    continue
                else:
                    player_is_clown = True
                    if current_woman_coords:
                        wx, wy = current_woman_coords
                        world[(wx, wy)] = '💀'
                        # Заменяем всех остальных NPC очереди на 🤣
                        for (nx, ny) in queue_npcs_under.keys():
                            if (nx, ny) != (wx, wy) and world.get((nx, ny)) in ['👩','😩','😡','😴','😤','🥸','🤓','🤠','😎']:
                                world[(nx, ny)] = '🤣'
                    player_msg = "🤣х10: Рыжая, рыжая, конопатая, убила девушку лопатою!\n\n🤡 Ты попалась на детскую дразнилку\nи опозорилась на весь город!"
                    option_block_active = False  # сбрасываем опции
                    draw(px, py)
                    time.sleep(3)
                    break
        
        if ch in ['q', 'й', '\x03'] and not car['in_car']: break
        if ch in ['q', 'й', '\x03'] and car['in_car'] and ch == '\x03': break
        
        dx, dy = 0, 0
        move_intent = (0, 0)
        is_brake = False
        
        player_acted = False
        
        if ch in ['н', 'y', 'w', 'ц', 'up']:
            dy, move_intent = -1, (0, -1)
        elif ch in ['и', 'b', 's', 'ы', 'down']:
            dy, move_intent = 1, (0, 1)
        elif ch in ['п', 'g', 'a', 'ф', 'left']:
            dx, move_intent = -1, (-1, 0)
        elif ch in ['о', 'j', 'd', 'в', 'right']:
            dx, move_intent = 1, (1, 0)
        elif ch in ['р', 'h', ' ', 'r']:
            dx, dy, is_brake = 0, 0, True
            player_acted = True
        
        # Обработка отложенного приглашения
        if pending_invite:
            if new_npc:
                world[(new_npc['x'], new_npc['y'])] = new_npc['under']
                new_npc = None
            if bouquets_collected > 0:
                bouquets_collected -= 1
            club_invited = True
            player_msg = "Кавказец вошёл в ночной клуб и приглашает тебя внутрь!"
            pending_invite = False
        
        old_px, old_py = px, py
        
        if car['in_car']:
            if ch in ['e', 'у', 'E', 'У', 'е', 'Е']:
                if car['speed'] > 0:
                    player_msg = "Выйти из автомобиля можно только после полной остановки!"
                else:
                    is_avenue = False
                    if abs(car['x']) <= 111 and abs(car['y']) <= 111:
                        for b in [-8, -3, 2, 7]:
                            if car['x'] in [b*9+4, b*9+5] or car['y'] in [b*9+4, b*9+5]:
                                is_avenue = True
                                break
                    if is_avenue:
                        player_msg = "Нельзя оставлять автомобиль на проезжей части проспекта!"
                    else:
                        exit_cells = []
                        if car['type'] == '🚗': exit_cells = [(car['x'], car['y'] - 1), (car['x'], car['y'] + 1)]
                        else: exit_cells = [(car['x'] - 1, car['y']), (car['x'] + 1, car['y'])]
                        
                        valid_exits = []
                        obstacles = ['🏢', '🌳', '🌲', '🪩', '🚾', '🏪', '👵', '🏡', '🧑‍🔧', '🕌', '🔔', '🛖', '🐑', '🌴', '🗿', '🧔', '🦌', '⚰️', '👷', '👩', '😩', '😡', '😴', '😤', '🥸', '🤓', '🤠', '😎']
                        for ex, ey in exit_cells:
                            tc = world.get((ex, ey), '')
                            if tc not in obstacles and not any(b['x'] == ex and b['y'] == ey for b in buses):
                                if (ex, ey) in bus_road_cells and abs(ex) <= 111 and abs(ey) <= 111:
                                    continue
                                valid_exits.append((ex, ey))
                        
                        if valid_exits:
                            px, py = random.choice(valid_exits)
                            if world.get((px, py)) == '🌹':
                                roses_collected += 1
                                world[(px, py)] = '🔹'
                                player_msg = "Вы сорвали розу!"
                            elif world.get((px, py)) == '🥄':
                                shovel += 1
                                world[(px, py)] = get_road_type(px, py) or '🔹'
                                player_msg = "Вы подобрали лопату!"
                            else:
                                player_msg = "Вы вышли из автомобиля."
                            car['in_car'] = False
                            player_acted = True
                            if toilet_patience is not None and toilet_patience > 0:
                                toilet_patience -= 1
                                if toilet_patience == 0:
                                    player_msg = "💩 Ты обосралась и опозорилась на весь город!"
                                    if not car['in_car'] and not player_in_bus:
                                        player_is_clown = True
                                    draw(px, py)
                                    time.sleep(3)
                                    break
                            hit_bus = move_buses()
                            if hit_bus:
                                if player_msg: player_msg += "\n"
                                if car['in_car']:
                                    player_msg += "💥 Вы попали в аварию и погибли!"
                                    world[(car['x'], car['y'])] = '💥'
                                else:
                                    player_msg += "💥 Вас сбил автобус!"
                                draw(px, py, dead=True)
                                break
                            continue
                        else:
                            bus_road_blocked = False
                            for ex, ey in exit_cells:
                                tc = world.get((ex, ey), '')
                                if (ex, ey) in bus_road_cells and abs(ex) <= 111 and abs(ey) <= 111 and tc not in obstacles and not any(b['x'] == ex and b['y'] == ey for b in buses):
                                    bus_road_blocked = True
                                    break
                            if bus_road_blocked:
                                player_msg = "Вы не можете выйти на проезжую часть проспекта!"
                            else:
                                player_msg = "Выход из автомобиля заблокирован препятствиями!"
                continue
                
            if move_intent != (0, 0) or is_brake:
                steps = 0
                if is_brake:
                    car['speed'] = max(0, car['speed'] - 2)
                    steps = car['speed']
                else:
                    if car['speed'] == 0:
                        car['speed'] = 1
                        car['dir'] = move_intent
                        car['type'] = '🚗' if move_intent[1] == 0 else '🚘'
                        steps = 1
                    elif move_intent == car['dir']:
                        car['speed'] = min(5, car['speed'] + 1)
                        steps = car['speed']
                    elif move_intent == (-car['dir'][0], -car['dir'][1]):
                        if car['speed'] > 0:
                            player_msg = "Вы не можете поехать в обратном направлении до полной остановки!"
                    else:
                        if car['speed'] <= 3:
                            car['dir'] = move_intent
                            car['type'] = '🚗' if move_intent[1] == 0 else '🚘'
                            car['speed'] = 1
                            steps = 1
                        else:
                            player_msg = "Вы не можете повернуть на большой скорости!"

                hit_npc = False
                crashed = False
                rose_crushed = False
                deer_hit = False
                
                old_car_x, old_car_y = car['x'], car['y']
                for _ in range(steps):
                    nx = car['x'] + car['dir'][0]
                    ny = car['y'] + car['dir'][1]
                    
                    tc = world.get((nx, ny), '')
                    obstacles = ['🏢', '🌳', '🌲', '🪩', '🚾', '🏪', '🏡', '🕌', '🔔', '🛖', '🌴', '🗿', '⚰️', '👷']
                    npcs = ['👵', '🧑‍🔧', '🧔', '👩', '😩', '😡', '😴', '😤', '🥸', '🤓', '🤠', '😎']
                    
                    if tc == '🦌' or (deer is not None and deer['x'] == nx and deer['y'] == ny):
                        if deer is not None and deer['x'] == nx and deer['y'] == ny:
                            world[(deer['x'], deer['y'])] = '▪️'
                            deer = None
                        else:
                            world[(nx, ny)] = '▪️'
                        deer_hit = True
                        car['x'], car['y'] = nx, ny
                        continue
                    
                    hit_static_bus = any(b['x'] == nx and b['y'] == ny for b in buses)
                    
                    if tc in obstacles or hit_static_bus:
                        car['x'], car['y'] = nx, ny
                        crashed = True
                        if hit_static_bus:
                            for b in buses[:]:
                                if b['x'] == nx and b['y'] == ny:
                                    buses.remove(b)
                        break
                    elif tc in npcs:
                        car['x'], car['y'] = nx, ny
                        hit_npc = True
                        if tc == '👵':
                            grandma['state'] = 'dead'
                            world[(nx, ny)] = grandma['under']
                        elif tc == '🧑‍🔧':
                            world[(nx, ny)] = mechanic['under']
                            mechanic = None
                        elif tc == '🧔':
                            world[(nx, ny)] = new_npc['under']
                            new_npc = None
                        elif tc in ['👩', '😩', '😡', '😴', '😤', '🥸', '🤓', '🤠', '😎']:
                            # Восстанавливаем исходную клетку из словаря
                            world[(nx, ny)] = queue_npcs_under.get((nx, ny), '🔹')
                    else:
                        car['x'], car['y'] = nx, ny
                        if tc == '🌹':
                            rose_crushed = True
                            world[(nx, ny)] = '🔹'
                        elif tc == '🥄':
                            shovel += 1
                            world[(nx, ny)] = get_road_type(nx, ny) or '🔹'
                            player_msg = "Вы подобрали лопату!"
                            
                px, py = car['x'], car['y']
                
                if car['x'] != old_car_x or car['y'] != old_car_y:
                    player_acted = True
                    if toilet_patience is not None and toilet_patience > 0:
                        toilet_patience -= 1
                        if toilet_patience == 0:
                            player_msg = "💩 Ты обосралась и опозорилась на весь город!"
                            if not car['in_car'] and not player_in_bus:
                                player_is_clown = True
                            draw(px, py)
                            time.sleep(3)
                            break
                
                if deer_hit:
                    player_msg = "Вы насмерть сбили оленя!"
                elif rose_crushed:
                    player_msg = "Вы раздавили розу(ы)!"
                    
                if crashed:
                    world[(car['x'], car['y'])] = '💥'
                    player_msg = "Вы попали в аварию и погибли!"
                    draw(px, py, dead=True)
                    break
                    
                if hit_npc and not crashed:
                    player_msg = "Вы сбили человека насмерть!"
                    draw(px, py)
                    time.sleep(3)
                    show_prison_screen()
                    break

        else:
            # Новая проверка: копание лопатой (🥄) на клетке '▪️'
            if ch in ['e', 'у', 'E', 'У', 'е', 'Е'] and shovel > 0 and world.get((px, py)) == '▪️':
                world[(px, py)] = '🕳️'
                holes_dug += 1
                player_msg = "Вы выкопали ямку, но ничего не нашли!"
                player_acted = True
                dx, dy = 0, 0  # предотвращаем дальнейшее движение
            
            if (dx, dy) != (0, 0):
                if grandma['state'] == 'crossing' and (dx, dy) != grandma['crossing_dir']:
                    player_msg = "Вы не можете бросить бабушку!"
                    dx, dy = 0, 0
                    
                if (dx, dy) != (0, 0):
                    nx, ny = px + dx, py + dy
                    
                    if world.get((nx, ny)) == '🪩':
                        if club_blacklist:
                            player_msg = "Вы в чёрном списке во всех клубах города!"
                        elif club_invited and (nx, ny) == club_entrance:
                            enter_from = (px, py)
                            play_club_scene()
                            px, py = enter_from
                            continue
                        else:
                            player_msg = "Фейсконтроль ночного клуба не пускает внутрь кого попало!"
                    elif nx == car['x'] and ny == car['y']:
                        if car['type'] == '🚗' and dx == 0 and dy in [-1, 1]:
                            car['in_car'] = True
                            px, py = car['x'], car['y']
                            car['speed'] = 0
                            player_acted = True
                        elif car['type'] == '🚘' and dy == 0 and dx in [-1, 1]:
                            car['in_car'] = True
                            px, py = car['x'], car['y']
                            car['speed'] = 0
                            player_acted = True
                        else:
                            player_msg = "В этот автомобиль можно сесть только сбоку!" if car['type'] == '🚘' else "В этот автомобиль можно сесть только сверху или снизу!"
                    else:
                        bus_at_target = next((b for b in buses if b['x'] == nx and b['y'] == ny), None)
                        is_on_stop = (px, py) in stop_signs
                        
                        boarded = False
                        if bus_at_target and is_on_stop:
                            if is_last_stop(bus_at_target):
                                player_msg = "На конечной остановке войти нельзя! Перейдите через дорогу."
                            else:
                                player_msg = "Вы вошли в автобус!"
                                px, py = nx, ny
                                player_bus = bus_at_target
                                boarded = True
                                
                        if boarded:
                            run_bus_animation(player_bus)
                            continue
                            
                        elif not bus_at_target:
                            target_cell = world.get((nx, ny), '')
                            if target_cell == '🚾' and toilet_patience is not None and toilet_patience > 0:
                                saved = play_toilet_scene((old_px, old_py), (nx, ny))
                                if saved:
                                    continue
                                else:
                                    break
                            elif target_cell == '⚰️' or target_cell == '👷':
                                player_msg = "Сюда нельзя наступать!"
                            elif target_cell == '🥄':
                                # Подбираем лопату
                                shovel += 1
                                world[(nx, ny)] = get_road_type(nx, ny) or '🔹'
                                px, py = nx, ny
                                player_acted = True
                                if toilet_patience is not None and toilet_patience > 0:
                                    toilet_patience -= 1
                                    if toilet_patience == 0:
                                        player_msg = "💩 Ты обосралась и опозорилась на весь город!"
                                        if not car['in_car'] and not player_in_bus:
                                            player_is_clown = True
                                        draw(px, py)
                                        time.sleep(3)
                                        break
                                player_msg = "Вы подобрали лопату!"
                            elif target_cell == '🏪':
                                # Проверяем, является ли магазин ключевым (с очередью)
                                if (nx, ny) == shop_with_queue_coords:
                                    play_father_reunion_scene()
                                    break  # завершаем игру
                                else:
                                    player_msg = "Магазин закрыт!"
                            elif target_cell not in ['🏢', '🌳', '🌲', '🪩', '🚾', '🏪', '👵', '🏡', '🧑‍🔧', '🕌', '🔔', '🛖', '🐑', '🌴', '🗿', '🧔', '🦌', '⚰️', '👷', '👩', '😩', '😡', '😴', '😤', '🥸', '🤓', '🤠', '😎']:
                                px, py = nx, ny
                                player_acted = True
                                if toilet_patience is not None and toilet_patience > 0:
                                    toilet_patience -= 1
                                    if toilet_patience == 0:
                                        player_msg = "💩 Ты обосралась и опозорилась на весь город!"
                                        if not car['in_car'] and not player_in_bus:
                                            player_is_clown = True
                                        draw(px, py)
                                        time.sleep(3)
                                        break
                                if target_cell == '🌹':
                                    roses_collected += 1
                                    world[(px, py)] = '🔹'
                                    player_msg = "Вы сорвали розу!"
                                elif target_cell == '🎗️':
                                    ribbons_collected += 1
                                    world[(px, py)] = get_road_type(px, py) or '🔹'
                                    player_msg = "Вы подобрали ленточку!"
                            else:
                                if target_cell == '🛖':
                                    if club_visited and not feast_visited:
                                        play_feast_scene()
                                        continue
                                elif target_cell == '🕌':
                                    if player_is_elf:
                                        enter_x, enter_y = px, py
                                        play_church_scene(enter_x, enter_y)
                                        continue
                                    else:
                                        player_msg = "В церковь нельзя с непокрытой головой!"
                                elif target_cell == '🚾': player_msg = "Вам сейчас не нужно в туалет!"
        
        # Взаимодействия с NPC и памятником (только вне автомобиля)
        if not car['in_car'] and not player_in_bus:
            moved_dx = px - old_px
            moved_dy = py - old_py
            
            if mechanic and not mechanic['met']:
                if max(abs(px - mechanic['x']), abs(py - mechanic['y'])) <= 1:
                    mechanic['met'] = True
                    player_acted = True
                    play_mechanic_dialogue()
                    old_px, old_py = px, py 
                    moved_dx, moved_dy = 0, 0
            
            if new_npc and not new_npc['met']:
                if abs(px - new_npc['x']) + abs(py - new_npc['y']) == 1:
                    new_npc['met'] = True
                    player_acted = True
                    if bouquets_collected == 0:
                        player_msg = "👩‍🦰: Познакомимся, красавчик? Пойдём на дискотеку?\n🧔: Думаешь, если я кавказец, то поведусь на первую встречную?"
                    else:
                        player_msg = "🧔: Вах, какой дэвущка! Зачэм тебе этот жялкий веник? Это жьиних купиль, да? Викинь его, айда в клюб — потанцуем, жи есть!"
                        pending_invite = True
                    old_px, old_py = px, py
                    moved_dx, moved_dy = 0, 0
                    
            if monument_coords:
                if abs(px - monument_coords[0]) + abs(py - monument_coords[1]) == 1:
                    player_acted = True
                    if not player_is_elf and toilet_paper > 0 and lemon > 0:
                        player_is_elf = True
                        toilet_paper = 0
                        lemon_tampon = True
                        player_msg = "Надо бы сменить имидж... ЭВРИКА!\nЯ обмотаю волосы туалетной бумагой!\nХоп-хоп-хоп — готово! А куда засунуть лимон,\nчтобы я не ходила с ним, как дура?\nПридумала: ЛИМОН-ТАМПОН! Оп!"
                    else:
                        if not monument_met:
                            monument_met = True
                            msg1 = "👩‍🦰: Вау, это же памятник Василию Пупкину! Надо срочно сделать селфи, но не с пустыми же руками!"
                            if player_msg: player_msg += "\n" + msg1
                            else: player_msg = msg1
                                
                        if ribbons_collected > 0 and roses_collected > 2 and roses_collected % 2 != 0:
                            roses_collected = 0
                            ribbons_collected = 0
                            bouquets_collected += 1
                            msg2 = "👩‍🦰: ЭВРИКА! Я сделаю букет из роз и ленточки! Хоп-хоп-хоп — готово! Так, улыбочку! Щёлк! А фото выложу в Нельзяграм и получу кучу лайков! 😍"
                            if player_msg: player_msg += "\n" + msg2
                            else: player_msg = msg2
            
            # Проверка соседства с NPC из очереди (не женщин)
            # Этот блок больше не нужен, так как обработан выше
            # Но оставим для совместимости с другими NPC
            adjacent_npc = False
            for dx_npc, dy_npc in [(-1,0),(1,0),(0,-1),(0,1)]:
                nx_npc, ny_npc = px + dx_npc, py + dy_npc
                if world.get((nx_npc, ny_npc)) in ['😩','😡','😴','😤','🥸','🤓','🤠','😎']:
                    adjacent_npc = True
                    break
            if adjacent_npc and not player_msg:
                player_msg = "👩‍🦰: Мне только спросить."
            
            # Проверка взаимодействия с могильщиком (начало диалога)
            for g in gravediggers:
                if g['state'] == 'idle' and abs(px - g['x']) + abs(py - g['y']) == 1:
                    if world.get((px, py)) == '🔸' and not gravedigger_dialogue_shown:
                        player_acted = True
                        play_gravedigger_dialogue()
                        gravedigger_dialogue_shown = True
                        old_px, old_py = px, py
                        moved_dx, moved_dy = 0, 0
                        break  # только один диалог за раз
            
            if grandma['state'] == 'crossing':
                if (moved_dx, moved_dy) == grandma['crossing_dir'] and (moved_dx, moved_dy) != (0, 0):
                    world[(grandma['x'], grandma['y'])] = grandma['under']
                    grandma['x'] += moved_dx
                    grandma['y'] += moved_dy
                    grandma['under'] = world.get((grandma['x'], grandma['y']), '🔹')
                    world[(grandma['x'], grandma['y'])] = '👵'
                    
                    if grandma['under'] == '🔸':
                        grandma['state'] = 'going_home'
                        player_msg = "👵: Спасибо, внучка. Я всем расскажу, какая ты хорошая девочка."
                        
            elif grandma['state'] == 'going_home':
                adj_building = False
                for bx, by in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
                    if world.get((grandma['x'] + bx, grandma['y'] + by)) in ['🏢', '🏡']:
                        adj_building = True
                        break
                        
                if adj_building:
                    world[(grandma['x'], grandma['y'])] = grandma['under']
                    grandma['state'] = 'none'
                    player_msg = "Бабушка благополучно добралась домой!"
                else:
                    q = [(grandma['x'], grandma['y'])]
                    came_from = {(grandma['x'], grandma['y']): None}
                    target = None
                    dist_idx = get_district_index(grandma['x']), get_district_index(grandma['y'])
                    
                    while q:
                        cx, cy = q.pop(0)
                        for bx, by in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
                            nx, ny = cx + bx, cy + by
                            if (nx, ny) == (px, py): continue
                            if not (-114 <= nx <= 114 and -114 <= ny <= 114): continue
                            if (get_district_index(nx), get_district_index(ny)) != dist_idx: continue
                            if get_road_type(nx, ny) is not None: continue
                            
                            if (nx, ny) not in came_from:
                                cell = world.get((nx, ny))
                                if cell in ['🏢', '🏡']:
                                    target = (cx, cy)
                                    came_from[(nx, ny)] = (cx, cy)
                                    q = []
                                    break
                                elif cell not in ['🏪', '🚾', '🛑', '👵', '🪩', '🧔', '⚰️', '👷']:
                                    came_from[(nx, ny)] = (cx, cy)
                                    q.append((nx, ny))
                                    
                    if target:
                        curr = target
                        if curr != (grandma['x'], grandma['y']):
                            while came_from[curr] != (grandma['x'], grandma['y']):
                                curr = came_from[curr]
                            world[(grandma['x'], grandma['y'])] = grandma['under']
                            grandma['x'], grandma['y'] = curr
                            grandma['under'] = world.get((grandma['x'], grandma['y']), '🔹')
                            world[(grandma['x'], grandma['y'])] = '👵'

            if grandma['state'] == 'waiting':
                if world.get((px, py)) == '🔸' and abs(px - grandma['x']) + abs(py - grandma['y']) == 1:
                    cross_dir = None
                    if grandma['y'] % 9 == 3: cross_dir = (0, 1)
                    elif grandma['y'] % 9 == 6: cross_dir = (0, -1)
                    elif grandma['x'] % 9 == 3: cross_dir = (1, 0)
                    elif grandma['x'] % 9 == 6: cross_dir = (-1, 0)
                    
                    target_g = (grandma['x'] + cross_dir[0] * 3, grandma['y'] + cross_dir[1] * 3)
                    target_p = (px + cross_dir[0] * 3, py + cross_dir[1] * 3)
                    
                    if (car['x'], car['y']) not in (target_g, target_p):
                        player_msg = "👩‍🦰: Давайте я переведу Вас через дорогу.\n👵: Спасибо, внучка."
                        grandma['state'] = 'crossing'
                        grandma['crossing_dir'] = cross_dir
                        player_acted = True
            
            # Проверка взаимодействия с оленем
            try_deer_interaction()
            
            # Движение оленя после игрока
            if player_acted:
                move_deer_if_following(old_px, old_py)
                if check_trio_revenge():
                    play_revenge_scene()
                    continue
        
        # Пересчет опций после возможного перемещения
        if not car['in_car'] and not player_in_bus:
            adjacent_woman_now = False
            current_woman_coords = None
            for dx_npc, dy_npc in [(-1,0),(1,0),(0,-1),(0,1)]:
                nx_npc, ny_npc = px + dx_npc, py + dy_npc
                if world.get((nx_npc, ny_npc)) == '👩' and women_defeated_count == 0:
                    adjacent_woman_now = True
                    current_woman_coords = (nx_npc, ny_npc)
                    break
            if adjacent_woman_now:
                option_block_active = True
                if not player_msg:
                    player_msg = "👩‍🦰: Мне только спросить.\n👩: Куда без очереди, шаболда?\nДумаешь, самая умная?"
            else:
                option_block_active = False
        
        # Движение овец
        for sheep in sheeps:
            valid_moves = []
            for sdx, sdy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
                nx, ny = sheep['x'] + sdx, sheep['y'] + sdy
                if (nx, ny) in grass_cells and world.get((nx, ny)) == '🌱' and (nx, ny) != (px, py):
                    valid_moves.append((nx, ny))
            if valid_moves:
                nx, ny = random.choice(valid_moves)
                world[(sheep['x'], sheep['y'])] = '🌱'
                sheep['x'], sheep['y'] = nx, ny
                world[(nx, ny)] = '🐑'

        # Обновляем могильщиков после хода игрока
        if player_acted and church_visited_global:
            update_gravediggers()

        # Двигаем автобусы только если игрок сделал ход
        if player_acted:
            hit_bus = move_buses()
            if hit_bus:
                if player_msg: player_msg += "\n"
                if car['in_car']:
                    player_msg += "💥 Вы попали в аварию и погибли!"
                    world[(car['x'], car['y'])] = '💥'
                else:
                    player_msg += "💥 Вас сбил автобус!"
                draw(px, py, dead=True)
                break

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        pass