Files
2026-04-24 14:50:46 +03:00

620 lines
33 KiB
Plaintext

label main_menu:
return
init python:
import random
renpy.music.register_channel("ambient_generator", "ambient", loop=True)
renpy.music.register_channel("ambient_server", "ambient", loop=True)
renpy.music.register_channel("ambient_wind", "ambient", loop=True)
renpy.music.register_channel("footsteps", "ambient", loop=False)
_game_menu_screen = None
_skipping = False
coffee_done = False
window_done = False
power_done = False
battery = 100
fragments = 0
thoughts_processed = False
journal = []
date_input = ""
current_input = [0, 0, 0, 0, 0]
arget_code = []
time_left = 15
puzzle_parts = [False, False, False, False]
puzzle_complete = False
signal_sequence = [0, 1, 2]
player_input = []
signal_active = True
visited_locations = {"room": False, "corridor": False, "terminal": False}
game_started = False
def use_battery(amount):
global battery
battery -= amount
if battery < 0: battery = 0
def generate_code():
return [random.randint(0, 9) for _ in range(5)]
screen game_ui():
if game_started:
frame:
align (0.02, 0.02)
vbox:
text "ЭНЕРГИЯ: [battery]%" size 40
text "ФРАГМЕНТЫ: [fragments]/3" size 40
screen journal_button():
textbutton "ЖУРНАЛ" action Show("journal") align (0.98, 0.98)
screen journal():
modal True
frame:
align (0.5, 0.5)
xsize 700 ysize 500
vbox:
text "МЫСЛИ" size 50
for entry in journal:
text "- [entry]" size 20
if fragments >= 3 and not thoughts_processed:
textbutton "ПОДУМАТЬ ОБ ЭТОМ":
action [Hide("journal"), Jump("deep_reflection")]
textbutton "ЗАКРЫТЬ" action Hide("journal")
screen corridor_puzzle_screen():
modal True
frame:
align (0.5, 0.5)
xsize 640 ysize 750
background "#333333"
vbox:
align (0.5, 0.5)
spacing 20
grid 2 2:
xalign 0.5
spacing 10
for i in range(4):
button:
action [
SetDict(puzzle_parts, i, True),
Function(renpy.restart_interaction)
]
xsize 300 ysize 300
background "#1a1a1a"
if puzzle_parts[i]:
add "puzzle_[i].png" align (0.5, 0.5)
else:
add "puzzle_unknown.png" align (0.5, 0.5)
textbutton "ЗАВЕРШИТЬ СБОРКУ":
action If(all(puzzle_parts), [Hide("corridor_puzzle_screen"), Return(True)], [Hide("corridor_puzzle_screen"), Return(False)])
xalign 0.5
xpadding 40
ypadding 20
background "#555555"
screen terminal_code_game():
modal True
timer 1.0 repeat True action If(time_left > 0, SetVariable("time_left", time_left - 1), Return(False))
frame:
align (0.5, 0.5)
padding (30, 30)
xsize 650 ysize 450
background "#262626"
vbox:
spacing 20
text "ПОДБОР ЧАСТОТЫ" xalign 0.5 size 35
hbox:
spacing 40
xalign 0.5
for i in range(5):
vbox:
spacing 5
xalign 0.5
textbutton "/\\":
xsize 80 ysize 60
background "#494949ec"
text_xalign 0.5
text_yalign 0
action SetDict(current_input, i, (current_input[i] + 1) % 10)
text str(current_input[i]):
xalign 0.5
color ("#3b7d3b" if current_input[i] == target_code[i] else "#9b0e0e")
size 60
textbutton "\\/":
xsize 80 ysize 60
background "#494949ec"
text_xalign 0.5
text_yalign 0
action SetDict(current_input, i, (current_input[i] - 1) % 10)
textbutton "ВВОД":
action Return(current_input == target_code)
xalign 0.5
yalign 1.0
xpadding 60
ypadding 10
background "#555555"
null height 60
text "Осталось: [time_left] сек." xalign 0.5 size 50 color "#ffffff"
screen keypad_screen():
modal True
frame:
align (0.5, 0.5)
xsize 400 ysize 500
background "#262626d3"
vbox:
spacing 10
align (0.5, 0.5)
null height 60
text "[date_input]" size 50 xalign 0.5 yalign 0.5 min_width 350
grid 3 4:
spacing 15
xalign 0.5
for i in range(1, 10):
textbutton str(i):
action If(len(date_input) < 10, SetVariable("date_input", date_input + str(i)), None)
background "#494949ec"
xsize 80 ysize 80
xalign 0.5
text_xalign 0.5
text_yalign 0.5
textbutton ".":
action If(len(date_input) < 10, SetVariable("date_input", date_input + "."), None)
background "#363636ec"
xsize 80 ysize 80
xalign 0.5
text_xalign 0.5
text_yalign 0.5
textbutton "0":
action If(len(date_input) < 10, SetVariable("date_input", date_input + "0"), None)
background "#494949ec"
xsize 80 ysize 80
xalign 0.5
text_xalign 0.5
text_yalign 0.5
textbutton "<-":
action SetVariable("date_input", date_input[:-1])
background "#363636ec"
xsize 80 ysize 80
xalign 0.5
text_xalign 0.5
text_yalign 0.5
null height 20
textbutton "ВВОД" action Return(date_input):
xalign 0.5
background "#555555d2"
xpadding 40
transform slow_zoom_pan:
zoom 1.0
linear 15.0 zoom 1.02
linear 15.0 zoom 1.0
repeat
transform shake_effect(rate=0.090):
zoom 1.1 align (0.5, 0.5)
linear rate xoffset 2 yoffset -6
linear rate xoffset -2.8 yoffset -2
linear rate xoffset 2.8 yoffset -2
linear rate xoffset -2 yoffset -6
linear rate xoffset +0 yoffset +0
transform rotate_180:
rotate 180
define chapter_title = Character(None,
what_xalign=0.5,
what_yalign=0.4,
window_yalign=0.5,
window_background = None
)
label start:
scene black
play sound "audio/typing.ogg" loop fadein 0.5
chapter_title "{size=150}{cps=5}ТИШИНА{/cps}{/size}" with dissolve
pause 2.0
hide text with dissolve
chapter_title "{size=120}{cps=5}20.04.2023{/cps}{/size}" with dissolve
pause 2.0
hide text with dissolve
stop sound fadeout 0.5
$ player_name = renpy.input("Как меня зовут?", length=12)
$ player_name = player_name.strip()
if not player_name:
$ player_name = "Техник"
"Приятно познакомиться, [player_name]."
jump phase_1_intro
label phase_1_intro:
$ routine_tasks = 0
scene bg_room at slow_zoom_pan with dissolve
play music "audio/main_theme.ogg"
$ renpy.music.set_volume(0.6, delay=0, channel='ambient_generator')
play ambient_generator "audio/generator.ogg"
"Я просыпаюсь. Шум генератора — единственное, что доказывает, что я еще здесь. Но сегодня этот звук другой. Он надтреснутый, хриплый, как дыхание умирающего."
"Индикатор на консоли мигает тусклым оранжевым. 100\%. Это последняя порция топлива. Последние искры жизни этой консервной банки."
"Семь лет назад я считал эту станцию своей крепостью. Теперь — это мой склеп, где я сам себе надсмотрщик, а генератор — мой метроном, отсчитывающий секунды до полной темноты."
"В воздухе пахнет озоном, гнилью и невыполненными обещаниями. Я чувствую каждый вдох, как попытку проглотить этот тяжелый, мертвый воздух, который скоро станет моим единственным спутником."
"Сегодня двадцатое апреля. И если верить датчикам, сегодня — последний день, когда станция сможет поддерживать жизнеобеспечение. К полуночи, если я не решусь на что-то, здесь станет холодно по-настоящему."
"Мои руки привычно тянутся к вещам, которые я переставляю изо дня в день. Это не привычка. Это попытка доказать самому себе, что я всё ещё существую в этой физической реальности, а не стал частью фонового шума оборудования."
"Мне нужно закончить утренний ритуал. Последний утренний ритуал. Только после этого я смогу сосредоточиться на том, чтобы принять решение, от которого я бежал три года."
jump routine_loop
label routine_loop:
if "bg_room" not in renpy.get_showing_tags("master"):
scene bg_room at slow_zoom_pan with dissolve
$ renpy.music.set_volume(0.6, delay=0, channel='ambient_generator')
if routine_tasks >= 3:
jump start_game_proper
"Комната кажется пустой, но в ней всё ещё остались вещи, которые требуют моего внимания. Вещи, которые я должен 'оживить' в последний раз."
menu:
"Налить остывший кофе" if not coffee_done:
$ coffee_done = True
$ routine_tasks += 1
play footsteps "audio/footsteps.ogg"
pause 1.0
scene bg_room_coffee at slow_zoom_pan with dissolve
play sound "audio/pouring.ogg"
"Чашка холодная, стенки покрыты въевшимся коричневым налетом. Я смотрю на эту мутную жидкость и вспоминаю, как раньше мы спорили о том, какой сорт кофе лучше — с кислинкой или с горчинкой."
"Теперь мне всё равно. Это просто топливо. Жидкая горечь, чтобы хоть как-то почувствовать себя живым. Я делаю глоток, и холод металла чашки обжигает губы сильнее, чем сам напиток. Это вкус прощания."
jump routine_loop
"Подойти к окну и стереть иней" if not window_done:
$ window_done = True
$ routine_tasks += 1
play footsteps "audio/footsteps.ogg"
pause 1.0
scene bg_room_iney at slow_zoom_pan with dissolve
play sound "audio/glass_rub.ogg"
"Стекло промерзло насквозь. Я провожу рукавом, оставляя длинную, неровную полосу. Иней осыпается, как пепел."
"За окном — только серая, бескрайняя пелена. Ни горизонта, ни неба. Иногда мне кажется, что я нахожусь не на станции связи, а внутри собственного разума, куда забыли подать свет."
"Я вглядываюсь в это 'ничто', надеясь увидеть хоть что-то, кроме собственного бледного отражения. Но вижу лишь глаза человека, который три года назад решил, что ожидание лучше, чем риск."
jump routine_loop
"Проверить статус генератора" if not power_done:
$ power_done = True
$ routine_tasks += 1
play footsteps "audio/footsteps.ogg"
pause 1.0
scene bg_room_generator at slow_zoom_pan with dissolve
$ renpy.music.set_volume(1, delay=0, channel='ambient_generator')
"Я подхожу к главному щитку. Индикаторы мерцают, как нервные окончания, готовые вот-вот перегореть. Семь лет эксплуатации — это не шутки. Сегодня он сдастся."
"Генератор стонет под нагрузкой. Я касаюсь рычага, чувствуя его дрожь. Если я сейчас уйду — он просто сгорит. Если останусь — буду жечь последние крохи энергии на поддержание пустоты."
"Каждый мой шаг здесь — это повторение того, от чего я пытаюсь сбежать, но я не могу позволить станции заглохнуть раньше времени. Это моё единственное наследие, мой памятник трусости."
jump routine_loop
label start_game_proper:
"Ритуал завершен. Пустота внутри стала немного привычнее, но теперь она давит сильнее."
"Станция издает низкий, вибрирующий гул. Это звук умирающего сердца станции, и он резонирует где-то в моих ребрах."
"Каждый мой шаг отзывается эхом. Здесь слишком много тишины для места, которое когда-то было наполнено смехом и голосами."
"Что мне делать сегодня? Время утекает, как песок сквозь пальцы, и заряд батареи с каждой минутой становится всё ближе к нулю."
$ game_started = True
jump main_loop
label main_loop:
$ renpy.music.set_volume(0.0, delay=0.2, channel='ambient_wind')
if battery <= 0:
jump ending_silence
show screen game_ui
show screen journal_button
"Станция ждет. Сегодня всё закончится. Что я буду делать, пока ещё есть свет?"
menu:
"Осмотреть рабочий стол (Комната)" if not visited_locations["room"]:
$ use_battery(26)
$ visited_locations["room"] = True
hide screen journal_button
jump qte_date_input
"Идти в коридор" if not visited_locations["corridor"]:
$ use_battery(29)
$ visited_locations["corridor"] = True
hide screen journal_button
jump qte_corridor_puzzle
"Подойти к терминалу" if not visited_locations["terminal"]:
$ use_battery(34)
$ visited_locations["terminal"] = True
hide screen journal_button
jump qte_terminal_hack
"Идти в серверную" if visited_locations["room"] and visited_locations["corridor"] and visited_locations["terminal"]:
hide screen journal_button
jump phase_3_culmination
"Сдаться":
hide screen journal_button
"Я..."
"Просто закрою глаза."
$ use_battery(battery)
jump main_loop
label qte_date_input:
play footsteps "audio/footsteps.ogg"
pause 1.0
$ renpy.music.set_volume(0.4, delay=0, channel='ambient_generator')
if "bg_room" not in renpy.get_showing_tags("master"):
scene bg_desk_close at slow_zoom_pan with fade
else:
scene bg_desk_close at slow_zoom_pan with dissolve
"Рабочий стол. Слой пыли здесь настолько толстый, что кажется, будто никто не садился сюда вечность."
"Курсор на экране терминала мигает в ритме моего пульса. Сегодня двадцатое апреля, двадцать третьего года."
"Ровно три года с того дня, когда всё пошло прахом. Три года тишины, которую я сам на себя наложил."
"Чтобы получить доступ к архиву за тот день, система требует подтвердить текущую дату. Словно издевается, заставляя меня признать, сколько времени я потратил на это самобичевание."
call screen keypad_screen
$ date_input = _return
if date_input == "20.04.2023":
play sound "audio/access_granted.ogg"
"Экран на мгновение вспыхивает ярким, почти болезненным белым цветом. Система приняла ввод."
"Я вхожу в архив. Передо мной всплывают логи трехлетней давности. Двадцатое апреля, двадцатый год."
"Я помню этот день по секундам. День, когда я должен был сказать одно слово, но вместо этого промолчал."
"Молчание стало моей клеткой. Я сам запер себя здесь, внутри этого 'сегодня', чтобы не сталкиваться с 'тем самым вчера'."
$ fragments += 1
$ journal.append("20.04.2020. Дата моего заточения. Спустя три года я наконец-то открыл архив, который так боялся увидеть.")
else:
play sound "audio/access_denied.ogg"
"На экране высветилось: 'ОШИБКА ДОСТУПА'."
"Даже простая проверка даты выбивает меня из колеи. Мои руки дрожат так сильно, что я путаю цифры."
"Я чувствую, как с каждой неудачной попыткой станция становится всё холоднее. Или это просто моё отчаяние?"
"Я отхожу от стола. В голове пульсирует лишь одна мысль: я всё ещё здесь. Всё ещё в этом 2020-м."
jump main_loop
label qte_corridor_puzzle:
play footsteps "audio/footsteps.ogg"
pause 1.0
$ renpy.music.set_volume(0.2, delay=0, channel='ambient_generator')
$ renpy.music.set_volume(0.8, delay=0.2, channel='ambient_wind')
play ambient_wind "audio/wind.ogg"
scene bg_corridor at slow_zoom_pan with fade
"Коридор. Место, где время растянулось, как жевательная резинка."
"В конце — разбитое зеркало. Я смотрю в него, и мои глаза встречаются с глазами человека, который проиграл эту жизнь три года назад."
"Оно разбито на четыре неравных куска. Я вижу в этих осколках фрагменты того самого дня. Если я соберу их... если я заставлю себя посмотреть на них..."
call screen corridor_puzzle_screen
if not _return:
play sound "audio/glass_break.ogg"
"Я смотрю на пустые места в зеркале. Я не могу... Руки не слушаются. В голове только шум и липкий страх."
"Я слишком слаб, чтобы видеть всё это целиком. Может быть, позже..."
jump main_loop
"Последний осколок встал на место. Изображение сложилось."
"Я вижу себя. Я вижу её. Она стоит у двери, её ладонь касается ручки. Она ждёт одного слова. Любого слова."
"А я? Я просто смотрю в пол. Я не могу поднять взгляд, потому что боюсь увидеть в её глазах подтверждение того, что я — ничтожество."
"Это зеркало — не отражение. Это приговор, который я сам себе вынес."
$ fragments += 1
$ journal.append("Коридор. Я увидел правду в зеркале: я не 'ждал', я намеренно молчал, чтобы она ушла.")
"Больше здесь не на что смотреть. Я оставляю зеркало в покое, хотя оно всё ещё преследует меня своей правдой."
jump main_loop
label qte_terminal_hack:
play footsteps "audio/footsteps.ogg"
pause 1.0
$ renpy.music.set_volume(0.6, delay=0, channel='ambient_generator')
scene bg_terminal at slow_zoom_pan with fade
"Терминал. Моё единственное окно в мир, который меня забыл."
"Экран забит мусором — тысячи системных сообщений, ошибок и битых секторов. Где-то там, под слоем цифровой пыли, всё ещё лежат записи того дня."
"Сигнал нестабилен, поток данных рвется. Если я не выровняю частоту прямо сейчас, архив удалит себя автоматически как 'неактуальный'."
"Мне нужно поймать этот поток. Не для того, чтобы отправить сообщение... а для того, чтобы наконец увидеть, что она мне писала."
$ target_code = generate_code()
$ current_input = [0, 0, 0, 0, 0]
call screen terminal_code_game
if _return:
play sound "audio/access_granted.ogg"
"Синхронизация завершена. Помехи исчезли, оставив на экране четкие строки."
"Я прокручиваю лог вниз. Вот оно. Сообщение, которое я никогда не открывал."
"Моё сердце пропускает удар. Я не отправляю ничего. Я просто читаю её последние слова, и это ранит сильнее, чем любая ошибка связи."
$ fragments += 1
$ journal.append("Терминал: Я открыл её последнее сообщение. Теперь я знаю, что именно я потерял.")
else:
play sound "audio/access_denied.ogg"
"Ошибка синхронизации. Архив свернулся, защищая себя от доступа."
"Я снова промахнулся. Руки дрожат, экран гаснет, и я остаюсь в тишине."
"Она осталась для меня неразгаданной. Снова. Всегда опаздываю — даже чтобы просто узнать правду."
"Я выхожу из системы. Терминал остывает, но теперь в комнате стало ещё холоднее от осознания того, что я (не) узнал."
jump main_loop
label deep_reflection:
"Я читаю свои записи. За каждой строчкой — трусость. За каждым 'потом' — страх."
"Я был таким слепым. Всё это время я думал, что храню память о ней, а на самом деле я просто бережно лелеял собственное бессилие."
"Хватит. Я всё понял."
$ sync_points = 3
$ thoughts_processed = True
"Я закрываю записи. Теперь я знаю, что должен сделать."
jump main_loop
label phase_3_culmination:
play footsteps "audio/footsteps.ogg"
pause 1.0
$ renpy.music.set_volume(0.1, delay=0, channel='ambient_generator')
scene bg_door_to_server at slow_zoom_pan with fade
"Серверная. Место, где родилась вся эта ложь."
"Я помню, как три года назад входил сюда, чтобы заглушить голос совести. Семь лет работы, три года молчания — всё это здесь."
jump final_choice
label final_choice:
stop music fadeout 0.3
play footsteps "audio/footsteps.ogg"
pause 1.0
$ renpy.music.set_volume(0.6, delay=0, channel='ambient_generator')
play ambient_server "audio/server.ogg"
play music "audio/final_theme.ogg"
play sound "audio/door_open.ogg"
scene bg_server_close with dissolve
"Светодиоды на стойках серверов мигают, словно конвульсии умирающего. Воздух здесь настолько сухой и горячий, что обжигает горло при каждом вдохе."
"На центральном терминале мерцает последний интерфейс. Если я нажму 'Ввод', пути назад не будет. Станция отключит жизнеобеспечение окончательно."
"Курсор пульсирует. Он ждет. Решение, от которого я бежал три года, теперь стоит передо мной во весь рост."
"Я чувствую, как холод металла терминала пробирает до костей, несмотря на жар в серверной."
menu:
"Разбить экран (Гнев)":
jump ending_anger
"Остаться и ждать конца (Принятие)":
jump ending_acceptance
"Признать ошибки и выйти (Переосмысление)" if fragments >= 3:
jump ending_reflection
"Отправить извинения (Свет)" if fragments >= 3 and thoughts_processed:
jump ending_light
label ending_silence:
scene black with Fade(0.1, 0.0, 0.0)
stop music fadeout 0.1
stop ambient_generator fadeout 0.1
stop ambient_server fadeout 0.1
stop ambient_wind fadeout 0.1
"Экран погас. Вместе с ним погас и последний источник света в этом склепе."
"Генератор сделал последний судорожный вдох и замолчал. В этой абсолютной темноте я наконец-то слышу то, чего так боялся."
"Ничего. Пустоту. Тишину, которая заполняет легкие вместо воздуха."
"Наконец-таки настала ТИШИНА."
jump quit_after_ending
label ending_anger:
scene bg_server_close_broken at shake_effect
stop music fadeout 0.1
stop ambient_generator fadeout 0.1
stop ambient_server fadeout 0.1
stop ambient_wind fadeout 0.1
play sound "audio/explosion.ogg"
"Гнев — это единственное, что заставляет меня чувствовать себя живым. Я срываю красную панель, сдирая кожу на пальцах до крови."
"Тумблер падает вниз с хрустом ломающихся костей. Искры бьют в лицо, выжигая остатки рассудка."
"Станция умирает, и я тяну её за собой в небытие. Я зол на этот холод, на эти три года, на её молчание... я зол на себя, но мне проще сжечь этот мир, чем признать свою вину."
"Я зол."
jump quit_after_ending
label ending_acceptance:
stop music fadeout 2.0
"Курсор мигает в ритме моего безразличия. Раз, два... Раз, два..."
"Зачем что-то менять? Зачем бороться с энтропией, если она всё равно победит?"
"Я откидываюсь на спинку кресла. Инфантильное желание просто закрыть глаза и ждать, пока тьма поглотит эту станцию, кажется единственно верным."
scene bg_server_close_off with dissolve
"Пусть всё закончится само собой. Я устал играть в героя. Я просто... буду ждать."
jump quit_after_ending
label ending_reflection:
scene bg_server_close_keyboard with dissolve
play sound "audio/typing.ogg"
"Я печатаю дрожащими пальцами: 'Я был неправ. Это была глупая, бесконечная игра в мученика, в которой не было победителей'."
"Я признаю это. Но не могу отправить сообщение."
"Три года я строил стены из своего молчания, думая, что защищаюсь. Но я просто возвел свою собственную тюрьму."
scene bg_server_close_off with dissolve
"Я выключаю станцию. Впервые за долгое время я чувствую, что в воздухе стало меньше гари. Я должен двигаться дальше. Я не знаю, что там, во внешнем мире, но я больше не заперт внутри себя."
jump quit_after_ending
label ending_light:
scene bg_server_close_light_from_monitor at slow_zoom_pan with dissolve
stop ambient_generator fadeout 1.0
stop ambient_server fadeout 1.0
stop ambient_wind fadeout 1.0
"Я направляю остатки энергии на передатчик. Каждый процент заряда — это вырванный из жизни кусок, который я отдаю за шанс сказать правду."
"Я пишу не для того, чтобы получить ответ. Я пишу, чтобы освободиться. 'Прости за тишину. Она была моей ошибкой'."
"Нажимаю 'Отправить'. Система оповещает о передаче пакета, и тут же питание отключается. Станция замирает."
scene bg_exit at slow_zoom_pan with dissolve
$ use_battery(battery)
"Я встаю и иду к выходу. Я не оглядываюсь. Мой долг перед прошлым оплачен, а будущее... будущее наконец-то принадлежит мне."
jump quit_after_ending
label quit_after_ending:
$ renpy.quit()
return