init
This commit is contained in:
12
tet/camera.reg
Normal file
12
tet/camera.reg
Normal file
@@ -0,0 +1,12 @@
|
||||
Windows Registry Editor Version 5.00
|
||||
|
||||
[HKEY_CLASSES_ROOT\camera]
|
||||
@="URL:Camera Protocol"
|
||||
"URL Protocol"=""
|
||||
|
||||
[HKEY_CLASSES_ROOT\camera\shell]
|
||||
|
||||
[HKEY_CLASSES_ROOT\camera\shell\open]
|
||||
|
||||
[HKEY_CLASSES_ROOT\camera\shell\open\command]
|
||||
@="\"C:\\Program Files (x86)\\SATVISION\\Satvision V.2.0\\Satvision V.2.0.exe\" \"%1\""
|
||||
3
tet/camera_del.reg
Normal file
3
tet/camera_del.reg
Normal file
@@ -0,0 +1,3 @@
|
||||
Windows Registry Editor Version 5.00
|
||||
|
||||
[-HKEY_CLASSES_ROOT\camera]
|
||||
202
tet/island_script.txt
Normal file
202
tet/island_script.txt
Normal file
@@ -0,0 +1,202 @@
|
||||
# Инициализация персонажей
|
||||
@char yui:Юи|Каштановые волосы|Энергичная, но напуганная|Пахнет цитрусами
|
||||
@char himitsu:Химицу|Черные волосы|Загадочная художница|Пахнет лавандой
|
||||
@char mitsuhа:Мицуха|Розовые волосы|Ранимая певица|Пахнет ванилью
|
||||
|
||||
# Инициализация очков
|
||||
@set yui_points = 0
|
||||
@set himitsu_points = 0
|
||||
@set mitsuha_points = 0
|
||||
|
||||
# --- ДЕНЬ 1: ТЕРРИТОРИЯ ТЕПЛА ---
|
||||
@label start
|
||||
@bg morning
|
||||
*УТРО. Холод в пустой квартире стал почти осязаемым, пробираясь под самую одежду. Вы находите Юи в гостиной — она дрожит, и её кожа покрылась крупными мурашками.*
|
||||
yui: Я скоро превращусь в лед... Можно мне хоть немного твоего тепла?
|
||||
|
||||
choice Прижать её к себе всем телом=>goto d1_yui_warm|Отстраниться и принести плед=>goto d1_yui_cold|Пошутить, что она преувеличивает=>goto d1_yui_none
|
||||
|
||||
@label d1_yui_warm
|
||||
@bg day
|
||||
*ДЕНЬ. Вы обхватываете её за талию и вжимаете в себя. Юи стонет, её соски отчетливо твердеют под тонкой тканью, царапая вашу грудь.*
|
||||
*Она жадно ловит ваше дыхание, прижимаясь бедрами так плотно, что вы чувствуете каждый её изгиб.*
|
||||
@set yui_points += 15
|
||||
@goto d1_himitsu_scene
|
||||
|
||||
@label d1_yui_cold
|
||||
*Юи разочарованно кутается в плед. Атмосфера между вами становится холоднее метели за окном.*
|
||||
@set yui_points -= 5
|
||||
@goto d1_himitsu_scene
|
||||
|
||||
@label d1_yui_none
|
||||
*Она обиженно отворачивается. Вы явно выбрали неудачный момент для шуток.*
|
||||
@set yui_points -= 10
|
||||
@goto d1_himitsu_scene
|
||||
|
||||
@label d1_himitsu_scene
|
||||
@bg evening
|
||||
*ВЕЧЕР. Химицу сидит у окна, рисуя пальцем на запотевшем стекле символы, напоминающие сплетения тел.*
|
||||
himitsu: Ты слышишь, как бьется моё сердце? Оно требует чего-то большего, чем просто тишина.
|
||||
|
||||
choice Положить руку ей на грудь=>goto d1_himi_hot|Просто слушать её слова=>goto d1_himi_soft|Сказать, что слышите только ветер=>goto d1_himi_none
|
||||
|
||||
@label d1_himi_hot
|
||||
@bg night
|
||||
*НОЧЬ. Ваша ладонь накрывает её грудь, чувствуя бешеный ритм. Химицу прижимает вашу руку сильнее, её дыхание становится тяжелым и прерывистым.*
|
||||
*Она смотрит на вас затуманенным взглядом, и её тело под вашими пальцами кажется обжигающим.*
|
||||
@set himitsu_points += 15
|
||||
@goto d1_mitsuha_scene
|
||||
|
||||
@label d1_himi_soft
|
||||
*Она лишь слегка улыбается, оценив ваше внимание, но искры не происходит.*
|
||||
@set himitsu_points += 5
|
||||
@goto d1_mitsuha_scene
|
||||
|
||||
@label d1_himi_none
|
||||
*Химицу замыкается в себе. Вы упустили шанс стать к ней ближе.*
|
||||
@set himitsu_points -= 10
|
||||
@goto d1_mitsuha_scene
|
||||
|
||||
@label d1_mitsuha_scene
|
||||
@bg morning
|
||||
*УТРО (День 1-2). Мицуха зажигает свечи, её розовые волосы светятся в полумраке.*
|
||||
mitsuhа: Кажется, метель заберет меня, если рядом никого не будет... Побудь со мной.
|
||||
|
||||
choice Обнять её со спины=>goto d1_mitsu_close|Взять её за руку=>goto d1_mitsu_hold|Посоветовать ей не бояться=>goto d1_mitsu_none
|
||||
|
||||
@label d1_mitsu_close
|
||||
@bg day
|
||||
*ДЕНЬ. Вы смыкаете руки на её талии. Мицуха выгибается, её ягодицы плотно прижимаются к вам, а дыхание сбивается на тихий стон.*
|
||||
*Вы чувствуете мягкость её тела и сладкий аромат ванили, который кружит голову.*
|
||||
@set mitsuha_points += 15
|
||||
@goto d1_night_together
|
||||
|
||||
@label d1_mitsu_hold
|
||||
*Она благодарно сжимает ваши пальцы, но ожидала более смелого шага.*
|
||||
@set mitsuha_points += 5
|
||||
@goto d1_night_together
|
||||
|
||||
@label d1_mitsu_none
|
||||
*Мицуха опускает глаза. Ей не нужны советы, ей нужно было ваше тепло.*
|
||||
@set mitsuha_points -= 10
|
||||
@goto d1_night_together
|
||||
|
||||
@label d1_night_together
|
||||
@bg night
|
||||
*НОЧЬ. Чтобы не замерзнуть, вы спите на одном матрасе. Юи закидывает на вас ногу, а Мицуха обнимает за талию.*
|
||||
*Вы засыпаете в коконе из женских тел, чувствуя их жар и слыша мерное, возбуждающее дыхание.*
|
||||
@goto d2_start
|
||||
|
||||
# --- ДЕНЬ 2: ОБНАЖЕННЫЕ ЧУВСТВА ---
|
||||
@label d2_start
|
||||
@bg morning
|
||||
*УТРО. Вы находите Юи на кухне. Она заспанная, её пижама сползла с плеча, открывая вид на нежную кожу.*
|
||||
yui: Мне всю ночь снилось, как ты меня касаешься... Это было слишком реально.
|
||||
|
||||
choice Поцеловать её, углубляя контакт=>goto d2_yui_sex|Погладить её по плечу=>goto d2_yui_touch|Сказать, что это просто сны=>goto d2_yui_none
|
||||
|
||||
@label d2_yui_sex
|
||||
@bg day
|
||||
*ДЕНЬ. Поцелуй превращается в жадное исследование. Юи сплетается с вами, её горячий язык ищет вашего.*
|
||||
*Она прижимается бедрами так яростно, что вы чувствуете её влажное желание сквозь тонкую ткань.*
|
||||
@set yui_points += 15
|
||||
@goto d2_himitsu_scene
|
||||
|
||||
@label d2_yui_touch
|
||||
*Она улыбается, но в её глазах читается неудовлетворенное ожидание.*
|
||||
@set yui_points += 5
|
||||
@goto d2_himitsu_scene
|
||||
|
||||
@label d2_yui_none
|
||||
*Юи разочарованно отстраняется. Момент близости безвозвратно утерян.*
|
||||
@set yui_points -= 15
|
||||
@goto d2_himitsu_scene
|
||||
|
||||
@label d2_himitsu_scene
|
||||
@bg evening
|
||||
*ВЕЧЕР. Химицу приглашает вас в свою комнату. Воздух здесь пропитан лавандой и тайной.*
|
||||
himitsu: Сними рубашку... Я хочу запечатлеть твою силуэт, прежде чем всё закончится.
|
||||
|
||||
choice Раздеться и подойти вплотную=>goto d2_himi_naked|Снять только рубашку и стоять=>goto d2_himi_half|Отказаться раздеваться=>goto d2_himi_none
|
||||
|
||||
@label d2_himi_naked
|
||||
@bg night
|
||||
*НОЧЬ. Она ведет холодными пальцами по вашей коже, спускаясь всё ниже. Её вздохи становятся лихорадочными.*
|
||||
*Химицу прижимается щекой к вашей груди, и вы чувствуете, как её собственное тело дрожит от нетерпения.*
|
||||
@set himitsu_points += 15
|
||||
@goto d2_mitsuha_scene
|
||||
|
||||
@label d2_himi_half
|
||||
*Она рисует, но между вами остается дистанция, которую она не решается сократить.*
|
||||
@set himitsu_points += 5
|
||||
@goto d2_mitsuha_scene
|
||||
|
||||
@label d2_himi_none
|
||||
*«Ты слишком скучный для музы», — холодно бросает она.*
|
||||
@set himitsu_points -= 20
|
||||
@goto d2_mitsuha_scene
|
||||
|
||||
@label d2_mitsuha_scene
|
||||
@bg morning
|
||||
*УТРО (День 2-3). Мицуха поет тихую балладу, её голос вибрирует глубоко в вашей груди.*
|
||||
mitsuhа: Я хочу быть твоей... хотя бы пока бушует эта метель.
|
||||
|
||||
choice Поцеловать её в шею, лаская изгибы=>goto d2_mitsu_neck|Крепко обнять за плечи=>goto d2_mitsu_soft|Просто похвалить вокал=>goto d2_mitsu_none
|
||||
|
||||
@label d2_mitsu_neck
|
||||
@bg day
|
||||
*ДЕНЬ. Мицуха стонет, откидывая голову назад. Ваши губы находят её самую чувствительную точку.*
|
||||
*Вы чувствуете нежную тяжесть её груди в своих руках и то, как она жадно ловит воздух ртом.*
|
||||
@set mitsuha_points += 15
|
||||
@goto d2_night_logic
|
||||
|
||||
@label d2_mitsu_soft
|
||||
*Она прижимается к вам, ища защиты, но не страсти.*
|
||||
@set mitsuha_points += 5
|
||||
@goto d2_night_logic
|
||||
|
||||
@label d2_mitsu_none
|
||||
*Мицуха замолкает. Вы разбили магию момента своей обыденностью.*
|
||||
@set mitsuha_points -= 15
|
||||
@goto d2_night_logic
|
||||
|
||||
@label d2_night_logic
|
||||
@bg night
|
||||
*НОЧЬ. Напряжение достигло пика. В темноте слышны лишь вздохи и шорох простыней.*
|
||||
@if yui_points == 30 then *Юи вжимается в вас, её бедра двигаются в такт вашему дыханию, ища спасения от желания.*
|
||||
@if himitsu_points == 30 then *Рука Химицу скользит под одеяло, её ладони обжигают вашу кожу лихорадочным жаром.*
|
||||
@if mitsuha_points == 30 then *Мицуха обвивает вас, вы чувствуете её грудь и то, как она дрожит от каждого касания.*
|
||||
*Остальные девушки, чувствуя этот жар, начинают ласкать друг друга в поисках утешения.*
|
||||
@goto d3_final
|
||||
|
||||
# --- ДЕНЬ 3: ПОСЛЕДНЯЯ НОЧЬ ---
|
||||
@label d3_final
|
||||
@bg morning
|
||||
*УТРО. Метель стихла, но внутри вас бушует шторм. Вы ловите их томные взгляды.*
|
||||
@bg day
|
||||
*ДЕНЬ. Каждое случайное касание теперь обжигает как разряд тока.*
|
||||
@bg evening
|
||||
*ВЕЧЕР. Пора решить, чьё тепло станет вашим навсегда.*
|
||||
|
||||
@if yui_points >= 30 then goto end_yui
|
||||
@if himitsu_points >= 30 then goto end_himitsu
|
||||
@if mitsuha_points >= 30 then goto end_mitsuha
|
||||
@goto end_lonely
|
||||
|
||||
@label end_yui
|
||||
*Юи уводит вас в свою комнату. Её поцелуй сбивает с ног, а руки жадно срывают одежду.*
|
||||
@goto credits
|
||||
|
||||
@label end_himitsu
|
||||
*Химицу забирает вашу волю в темноте мастерской. Её дыхание — единственное, что теперь имеет значение.*
|
||||
@goto credits
|
||||
|
||||
@label end_mitsuha
|
||||
*Мицуха шепчет слова любви, её тело пахнет ванилью и страстью, когда она тянет вас в постель.*
|
||||
@goto credits
|
||||
|
||||
@label end_lonely
|
||||
*Вы выходите на мороз один. Конец.*
|
||||
|
||||
@label credits
|
||||
*Игра завершена.*
|
||||
629
tet/island_script_ds.txt
Normal file
629
tet/island_script_ds.txt
Normal file
@@ -0,0 +1,629 @@
|
||||
# Романтическая визуальная новелла "Запертые вместе"
|
||||
# Главный герой оказывается в квартире с тремя девушками
|
||||
# и должен строить отношения, чтобы выжить
|
||||
|
||||
# Инициализация персонажей
|
||||
@char yui:Юи|Каштановые волосы, зеленые глаза, спортивная фигура|Студентка, занимается танцами|Пахнет цитрусами
|
||||
@char himitsu:Химицу|Черные волосы, фиолетовые глаза, готический стиль|Художница, любит читать|Пахнет лавандой
|
||||
@char mitsuhа:Мицуха|Розовые волосы, голубые глаза, кудрявая|Певица в караоке-клубе|Пахнет ванилью
|
||||
|
||||
# Инициализация переменных
|
||||
@set yui_points = 0
|
||||
@set himitsu_points = 0
|
||||
@set mitsuhа_points = 0
|
||||
@set day = 1
|
||||
@set time = "утро"
|
||||
@set sanity = 100
|
||||
@set killed = 0
|
||||
|
||||
# Начало игры
|
||||
@label start
|
||||
@bg morning
|
||||
*Вы просыпаетесь в незнакомой квартире после сильной метели. За окном белая пелена, дороги замело, связь не работает. В квартире кроме вас находятся три девушки.*
|
||||
|
||||
# Первая встреча
|
||||
@label day1_morning
|
||||
@bg morning
|
||||
*Лёгкий солнечный свет пробивается через шторы. Вы слышите голоса из кухни.*
|
||||
|
||||
choice Подойти познакомиться=>goto meet_girls|Осмотреть квартиру=>goto explore_house|Попробовать найти выход=>goto try_exit
|
||||
|
||||
@label meet_girls
|
||||
@bg morning
|
||||
yui: О, ты наконец проснулся! Мы уже начали волноваться.
|
||||
himitsu: *пристально смотрит* Ты вообще помнишь, как сюда попал?
|
||||
mitsuhа: Не пугай его! Он же только что очнулся.
|
||||
|
||||
choice Извиниться за беспокойство=>goto polite_start|Спросить, где вы=>goto direct_question|Пошутить=>goto joke_response
|
||||
|
||||
@label polite_start
|
||||
@set sanity+=5
|
||||
*Девушки выглядят довольными вашими манерами*
|
||||
yui: Ничего страшного! Метель всех застала врасплох.
|
||||
himitsu: Да, мы тоже здесь застряли. Похоже, придётся пережидать.
|
||||
@set yui_points+=5
|
||||
@set himitsu_points+=5
|
||||
@goto day1_breakfast
|
||||
|
||||
@label direct_question
|
||||
@set sanity-=3
|
||||
*Девушки переглядываются*
|
||||
mitsuhа: Это моя квартира... ну, точнее, съёмная.
|
||||
himitsu: А ты просто лежал у подъезда, мы не могли оставить тебя в метель.
|
||||
@set mitsuhа_points+=3
|
||||
@goto day1_breakfast
|
||||
|
||||
@label joke_response
|
||||
@set sanity+=10
|
||||
*Девушки смеются*
|
||||
yui: О, весельчак! Это хорошо, в заточении пригодится.
|
||||
mitsuhа: *хихикает* Ты забавный!
|
||||
himitsu: *саркастично* Надеюсь, твои шутки не закончатся вместе с едой.
|
||||
@set yui_points+=7
|
||||
@set mitsuhа_points+=10
|
||||
@set himitsu_points+=3
|
||||
@goto day1_breakfast
|
||||
|
||||
@label explore_house
|
||||
@bg grey
|
||||
*Вы осматриваете квартиру. Небольшая гостиная, кухня, две спальни и ванная. Окна плотно закрыты, на балконной двери висит замок.*
|
||||
|
||||
himitsu: *появляется в дверях* Искал выход? Бесполезно, я уже пробовала.
|
||||
@set himitsu_points+=5
|
||||
@goto day1_breakfast
|
||||
|
||||
@label try_exit
|
||||
@bg grey
|
||||
*Вы пытаетесь открыть входную дверь, но она не поддаётся. Слышен голос за спиной.*
|
||||
|
||||
yui: Эй, не трать силы! Мы уже пробовали - снег завалил дверь снаружи.
|
||||
@set yui_points+=5
|
||||
@goto day1_breakfast
|
||||
|
||||
# Завтрак первого дня
|
||||
@label day1_breakfast
|
||||
@bg morning
|
||||
mitsuhа: Ну что, будем завтракать? У меня есть рис и немного овощей.
|
||||
yui: Я могу приготовить омлет!
|
||||
himitsu: *вздыхает* Ладно, помогу накрыть на стол.
|
||||
|
||||
choice Помочь Юи с готовкой=>goto help_yui|Помочь Химицу с сервировкой=>goto help_himitsu|Поболтать с Мицухой=>goto talk_mitsuhа
|
||||
|
||||
@label help_yui
|
||||
@bg morning
|
||||
*Вы помогаете Юи готовить омлет. Она улыбается вашей помощи.*
|
||||
yui: Ты неплохо справляешься! Часто готовишь?
|
||||
choice Да, люблю готовить=>goto cooking_skill|Нет, просто повезло=>goto cooking_luck
|
||||
|
||||
@label cooking_skill
|
||||
yui: Круто! Может, потом научишь меня чему-нибудь?
|
||||
@set yui_points+=10
|
||||
@goto breakfast_common
|
||||
|
||||
@label cooking_luck
|
||||
yui: Скромничаешь, я вижу!
|
||||
@set yui_points+=5
|
||||
@goto breakfast_common
|
||||
|
||||
@label help_himitsu
|
||||
@bg morning
|
||||
*Вы помогаете Химицу расставлять посуду. Она наблюдает за вами.*
|
||||
himitsu: Ты... не такой, как я ожидала.
|
||||
choice Что ты ожидала?=>goto himitsu_expectations|Просто стараюсь помочь=>goto himitsu_help
|
||||
|
||||
@label himitsu_expectations
|
||||
himitsu: Не знаю... Кого-то более напуганного или агрессивного.
|
||||
@set himitsu_points+=8
|
||||
@goto breakfast_common
|
||||
|
||||
@label himitsu_help
|
||||
himitsu: Это... мило с твоей стороны.
|
||||
@set himitsu_points+=5
|
||||
@goto breakfast_common
|
||||
|
||||
@label talk_mitsuhа
|
||||
@bg morning
|
||||
*Вы садитесь рядом с Мицухой, которая нарезает овощи.*
|
||||
mitsuhа: О! Хочешь помочь? Можешь нарезать огурец.
|
||||
*Вы начинаете резать огурец, но получается неровно*
|
||||
mitsuhа: *смеётся* Ничего, для первого раза сойдёт!
|
||||
@set mitsuhа_points+=7
|
||||
@goto breakfast_common
|
||||
|
||||
@label breakfast_common
|
||||
@bg morning
|
||||
*Вы все вместе завтракаете. В квартире царит дружеская атмосфера.*
|
||||
|
||||
yui: Так... что будем делать, пока не кончилась метель?
|
||||
himitsu: У меня есть колода карт.
|
||||
mitsuhа: А я могу петь! *смущённо* Если хотите, конечно...
|
||||
|
||||
choice Предложить играть в карты=>goto play_cards|Попросить Мицуху спеть=>goto ask_sing|Предложить рассказывать истории=>goto tell_stories
|
||||
|
||||
@label play_cards
|
||||
@bg day
|
||||
*Вы играете в карты с девушками. Юи азартна, Химицу хладнокровна, а Мицуха постоянно путает правила.*
|
||||
|
||||
@if himitsu_points>yui_points then goto himitsu_wins
|
||||
@if himitsu_points>mitsuhа_points then goto himitsu_wins
|
||||
@if yui_points>himitsu_points then goto yui_wins
|
||||
@if yui_points>mitsuhа_points then goto yui_wins
|
||||
@goto mitsuhа_wins
|
||||
|
||||
@label himitsu_wins
|
||||
himitsu: *ухмыляется* Кажется, я выигрываю. Опять.
|
||||
@set himitsu_points+=10
|
||||
@goto day1_afternoon
|
||||
|
||||
@label yui_wins
|
||||
yui: Да! Ещё одна победа! *радостно подпрыгивает*
|
||||
@set yui_points+=10
|
||||
@goto day1_afternoon
|
||||
|
||||
@label mitsuhа_wins
|
||||
mitsuhа: Ой, я выиграла? Неожиданно!
|
||||
@set mitsuhа_points+=10
|
||||
@goto day1_afternoon
|
||||
|
||||
@label ask_sing
|
||||
@bg day
|
||||
mitsuhа: *радостно* Правда хочешь послушать? Ну ладно...
|
||||
*Мицуха берёт гитару и исполняет красивую балладу*
|
||||
|
||||
choice Похвалить её голос=>goto praise_voice|Спросить, где научилась=>goto ask_learning|Аплодировать стоя=>goto standing_ovation
|
||||
|
||||
@label praise_voice
|
||||
mitsuhа: Спасибо! *краснеет* Я редко пою для других.
|
||||
@set mitsuhа_points+=10
|
||||
@goto day1_afternoon
|
||||
|
||||
@label ask_learning
|
||||
mitsuhа: Я работаю в караоке-клубе... но это секрет, ладно?
|
||||
@set mitsuhа_points+=7
|
||||
@goto day1_afternoon
|
||||
|
||||
@label standing_ovation
|
||||
mitsuhа: *смущённо* Ой, ну что ты! *но явно рада*
|
||||
@set mitsuhа_points+=15
|
||||
@goto day1_afternoon
|
||||
|
||||
@label tell_stories
|
||||
@bg day
|
||||
*Вы рассказываете интересную историю из своей жизни. Девушки слушают с интересом.*
|
||||
|
||||
choice Рассказать смешную историю=>goto funny_story|Рассказать трогательную историю=>goto touching_story|Рассказать страшную историю=>goto scary_story
|
||||
|
||||
@label funny_story
|
||||
yui: *смеётся* О боже, это же так глупо!
|
||||
himitsu: *подавляет улыбку* Да уж...
|
||||
@set yui_points+=10
|
||||
@set himitsu_points+=5
|
||||
@goto day1_afternoon
|
||||
|
||||
@label touching_story
|
||||
mitsuhа: *глаза влажные* Это так... трогательно.
|
||||
himitsu: *кивает* Сильная история.
|
||||
@set mitsuhа_points+=12
|
||||
@set himitsu_points+=8
|
||||
@goto day1_afternoon
|
||||
|
||||
@label scary_story
|
||||
yui: *вздрагивает* Брр, не люблю такие истории!
|
||||
himitsu: *заинтересованно* Хороший рассказчик...
|
||||
@set yui_points-=5
|
||||
@set himitsu_points+=15
|
||||
@goto day1_afternoon
|
||||
|
||||
# День 1 - Послеобеденное время
|
||||
@label day1_afternoon
|
||||
@bg day
|
||||
*После совместного времяпрепровождения атмосфера в квартире стала более дружелюбной.*
|
||||
|
||||
yui: Эй, а давайте проверим, как там на улице?
|
||||
*Вы подходите к окну - метель всё ещё бушует*
|
||||
himitsu: Похоже, нам здесь ещё на день-два.
|
||||
mitsuhа: Тогда надо проверить запасы еды...
|
||||
|
||||
choice Предложить помочь с инвентаризацией=>goto check_supplies|Предложить сыграть в другую игру=>goto another_game|Пойти отдохнуть=>goto take_nap
|
||||
|
||||
@label check_supplies
|
||||
@bg day
|
||||
*Вы проверяете запасы еды вместе с Мицухой*
|
||||
mitsuhа: Риса хватит на пару дней, овощей меньше... Ой!
|
||||
*Она случайно роняет банку, вы ловите её*
|
||||
mitsuhа: *смущённо* Спасибо... ты ловкий.
|
||||
@set mitsuhа_points+=10
|
||||
@goto day1_evening
|
||||
|
||||
@label another_game
|
||||
@bg day
|
||||
*Вы предлагаете новую игру. Юи с энтузиазмом соглашается.*
|
||||
yui: Да! Я обожаю игры! Давай что-нибудь активное!
|
||||
@set yui_points+=10
|
||||
@goto day1_evening
|
||||
|
||||
@label take_nap
|
||||
@bg day
|
||||
*Вы решаете вздремнуть. Химицу сидит рядом с книгой.*
|
||||
himitsu: *тихо* Умный поступок. В таких ситуациях важно сохранять силы.
|
||||
@set himitsu_points+=7
|
||||
@goto day1_evening
|
||||
|
||||
# День 1 - Вечер
|
||||
@label day1_evening
|
||||
@bg sunset
|
||||
*Солнце садится, в квартире становится темнее. Вы замечаете, как девушки начинают нервничать.*
|
||||
|
||||
choice Предложить зажечь свечи=>goto light_candles|Проверить телефон (вдруг появилась связь)=>goto check_phone|Начать разговор=>goto start_conversation
|
||||
|
||||
@label light_candles
|
||||
@bg sunset
|
||||
*Вы находите свечи и зажигаете их. Мицуха благодарно улыбается.*
|
||||
mitsuhа: Как романтично... в каком-то смысле.
|
||||
@set mitsuhа_points+=8
|
||||
@set yui_points+=3
|
||||
@set himitsu_points+=3
|
||||
@goto day1_dinner
|
||||
|
||||
@label check_phone
|
||||
@bg sunset
|
||||
*Вы проверяете телефон - связи по-прежнему нет.*
|
||||
yui: *вздыхает* Я тоже уже пробовала... Ничего.
|
||||
@set sanity-=5
|
||||
@goto day1_dinner
|
||||
|
||||
@label start_conversation
|
||||
@bg sunset
|
||||
*Вы пытаетесь разрядить обстановку разговором*
|
||||
himitsu: *снисходительно* Ну давай, развлекай нас.
|
||||
choice Расспросить о них=>goto ask_about_them|Рассказать о себе=>goto tell_about_yourself|Придумать игру=>goto create_game
|
||||
|
||||
@label ask_about_them
|
||||
*Девушки немного оживляются, рассказывая о себе*
|
||||
@set yui_points+=5
|
||||
@set himitsu_points+=5
|
||||
@set mitsuhа_points+=5
|
||||
@goto day1_dinner
|
||||
|
||||
@label tell_about_yourself
|
||||
*Вы рассказываете о себе. Химицу слушает с интересом.*
|
||||
@set himitsu_points+=10
|
||||
@goto day1_dinner
|
||||
|
||||
@label create_game
|
||||
*Вы придумываете игру, Юи в восторге*
|
||||
@set yui_points+=15
|
||||
@goto day1_dinner
|
||||
|
||||
# Ужин первого дня
|
||||
@label day1_dinner
|
||||
@bg night
|
||||
*Вы ужинаете при свечах. Атмосфера странным образом становится уютной.*
|
||||
|
||||
mitsuhа: Знаете... несмотря на всё, это довольно мило.
|
||||
yui: Да уж, могло быть и хуже!
|
||||
himitsu: *покатывается глазами* Вы слишком легко ко всему относитесь.
|
||||
|
||||
choice Согласиться с Мицухой=>goto agree_mitsuhа|Поддержать Юи=>goto agree_yui|Промолчать=>goto stay_silent
|
||||
|
||||
@label agree_mitsuhа
|
||||
mitsuhа: *улыбается* Приятно, что ты так думаешь.
|
||||
@set mitsuhа_points+=10
|
||||
@goto day1_sleep
|
||||
|
||||
@label agree_yui
|
||||
yui: Вот именно! *подмигивает*
|
||||
@set yui_points+=10
|
||||
@goto day1_sleep
|
||||
|
||||
@label stay_silent
|
||||
himitsu: *кивает* Хотя бы один человек здесь адекватный.
|
||||
@set himitsu_points+=7
|
||||
@goto day1_sleep
|
||||
|
||||
# Ночь первого дня
|
||||
@label day1_sleep
|
||||
@bg night
|
||||
*Приходит время спать. В квартире две спальни - одна для девушек, одна для вас.*
|
||||
|
||||
choice Предложить дежурить по очереди=>goto night_watch|Пожелать спокойной ночи=>goto good_night|Проверить двери ещё раз=>goto check_doors
|
||||
|
||||
@label night_watch
|
||||
*Девушки удивлены вашим предложением*
|
||||
yui: О, ответственный! Мне нравится!
|
||||
@set yui_points+=10
|
||||
@set himitsu_points+=5
|
||||
@set mitsuhа_points+=5
|
||||
@goto day2_morning
|
||||
|
||||
@label good_night
|
||||
*Вы вежливо прощаетесь*
|
||||
mitsuhа: Спокойной ночи... и спасибо за сегодня.
|
||||
@set mitsuhа_points+=7
|
||||
@goto day2_morning
|
||||
|
||||
@label check_doors
|
||||
*Вы проверяете двери - без изменений*
|
||||
himitsu: *тихо* Упрямый... но это хорошо.
|
||||
@set himitsu_points+=10
|
||||
@goto day2_morning
|
||||
|
||||
# День 2 - Утро
|
||||
@label day2_morning
|
||||
@bg morning
|
||||
@set day = 2
|
||||
*Вы просыпаетесь от запаха готовящегося завтрака. Метель за окном продолжается, но уже не так сильно.*
|
||||
|
||||
yui: Доброе утро! Я приготовила завтрак!
|
||||
himitsu: *ворчит* И разбудила всех на три этажа.
|
||||
mitsuhа: *подаёт тарелку* Кушай, пока горячее.
|
||||
|
||||
choice Поблагодарить Юи=>goto thank_yui|Пошутить над Химицу=>goto joke_himitsu|Помочь Мицухе=>goto help_mitsuhа
|
||||
|
||||
@label thank_yui
|
||||
yui: *радостно* Всегда пожалуйста!
|
||||
@set yui_points+=10
|
||||
@goto day2_breakfast
|
||||
|
||||
@label joke_himitsu
|
||||
himitsu: *фыркает* Смешно. Очень смешно.
|
||||
@set himitsu_points+=5
|
||||
@goto day2_breakfast
|
||||
|
||||
@label help_mitsuhа
|
||||
mitsuhа: Ой, спасибо! *улыбается*
|
||||
@set mitsuhа_points+=10
|
||||
@goto day2_breakfast
|
||||
|
||||
# День 2 - Завтрак
|
||||
@label day2_breakfast
|
||||
@bg morning
|
||||
*За завтраком девушки обсуждают, как провести день.*
|
||||
|
||||
himitsu: Электричество есть, но интернет не работает. Типично.
|
||||
mitsuhа: Может, споём караоке? У меня есть микрофон!
|
||||
yui: О да! А потом можно устроить мини-спортзал!
|
||||
|
||||
choice Поддержать идею караоке=>goto karaoke_time|Предложить настольные игры=>goto board_games|Пойти проверить окна=>goto check_windows
|
||||
|
||||
@label karaoke_time
|
||||
@bg day
|
||||
mitsuhа: Ура! *хлопает в ладоши* Давайте дуэтом!
|
||||
@set mitsuhа_points+=10
|
||||
@set sanity+=15
|
||||
@goto day2_activity
|
||||
|
||||
@label board_games
|
||||
@bg day
|
||||
himitsu: Наконец-то здравая идея.
|
||||
@set himitsu_points+=10
|
||||
@set yui_points-=5
|
||||
@goto day2_activity
|
||||
|
||||
@label check_windows
|
||||
@bg day
|
||||
*Вы проверяете окна - снег немного осел, но выйти пока невозможно.*
|
||||
yui: *подходит сзади* Нашёл выход?
|
||||
@set yui_points+=7
|
||||
@set sanity-=5
|
||||
@goto day2_activity
|
||||
|
||||
# День 2 - Активности
|
||||
@label day2_activity
|
||||
@bg day
|
||||
*Внезапно гаснет свет. Раздаётся вздох разочарования.*
|
||||
|
||||
himitsu: Прекрасно. Просто прекрасно.
|
||||
mitsuhа: *испуганно* Темно...
|
||||
choice Найти фонарик=>goto find_flashlight|Успокоить Мицуху=>goto comfort_mitsuhа|Посмеяться над ситуацией=>goto laugh_situation
|
||||
|
||||
@label find_flashlight
|
||||
@bg grey
|
||||
*Вы находите фонарик в ящике. Химицу одобрительно кивает.*
|
||||
himitsu: Полезно иметь такого человека рядом.
|
||||
@set himitsu_points+=10
|
||||
@goto day2_lunch
|
||||
|
||||
@label comfort_mitsuhа
|
||||
@bg grey
|
||||
*Вы берёте Мицуху за руку. Она постепенно успокаивается.*
|
||||
mitsuhа: Спасибо... мне страшно в темноте.
|
||||
@set mitsuhа_points+=15
|
||||
@goto day2_lunch
|
||||
|
||||
@label laugh_situation
|
||||
@bg grey
|
||||
yui: *смеётся* Да уж, ещё один повод для воспоминаний!
|
||||
@set yui_points+=10
|
||||
@set mitsuhа_points-=5
|
||||
@goto day2_lunch
|
||||
|
||||
# День 2 - Обед
|
||||
@label day2_lunch
|
||||
@bg day
|
||||
*Свет вернулся. Вы вместе готовите обед из остатков еды.*
|
||||
|
||||
choice Помочь Юи с готовкой=>goto cook_with_yui|Убрать на кухне=>goto clean_kitchen|Поделиться едой с Химицу=>goto share_food
|
||||
|
||||
@label cook_with_yui
|
||||
@bg day
|
||||
*Юи учит вас готовить простое блюдо. Получается не идеально, но съедобно.*
|
||||
yui: Неплохо для первого раза! *подмигивает*
|
||||
@set yui_points+=12
|
||||
@goto day2_evening
|
||||
|
||||
@label clean_kitchen
|
||||
@bg day
|
||||
*Вы моете посуду. Химицу неожиданно присоединяется.*
|
||||
himitsu: *тихо* Ты... не такой неряха, как большинство парней.
|
||||
@set himitsu_points+=10
|
||||
@goto day2_evening
|
||||
|
||||
@label share_food
|
||||
@bg day
|
||||
*Вы отдаёте Химицу последний кусок пирога. Она удивлена.*
|
||||
himitsu: Ты... уверен? *берет пирог, избегая зрительного контакта*
|
||||
@set himitsu_points+=15
|
||||
@set sanity-=5 # голод
|
||||
@goto day2_evening
|
||||
|
||||
# День 2 - Вечер
|
||||
@label day2_evening
|
||||
@bg sunset
|
||||
*Запасы еды заканчиваются. Напряжение растёт.*
|
||||
|
||||
yui: Эй, может, расскажем страшные истории? Чтобы отвлечься!
|
||||
mitsuhа: *дрожит* Я... я не уверена...
|
||||
himitsu: *саркастично* Отличная идея. Добавить страха в нашу и без того ужасную ситуацию.
|
||||
|
||||
choice Поддержать Юи=>goto scary_stories|Предложить альтернативу=>goto alternative_activity|Уйти в другую комнату=>goto leave_room
|
||||
|
||||
@label scary_stories
|
||||
@bg night
|
||||
*Вы рассказываете историю. Юи в восторге, Мицуха прячется под плед.*
|
||||
@set yui_points+=15
|
||||
@set mitsuhа_points-=10
|
||||
@set sanity-=10
|
||||
@goto day2_night
|
||||
|
||||
@label alternative_activity
|
||||
@bg night
|
||||
*Вы предлагаете рассказывать смешные истории. Атмосфера улучшается.*
|
||||
@set mitsuhа_points+=10
|
||||
@set yui_points+=5
|
||||
@set sanity+=10
|
||||
@goto day2_night
|
||||
|
||||
@label leave_room
|
||||
@bg night
|
||||
*Вы уходите, оставляя девушек разбираться самим. Химицу позже находит вас читающим.*
|
||||
himitsu: Умный ход. *оставляет чашку чая рядом*
|
||||
@set himitsu_points+=10
|
||||
@set yui_points-=5
|
||||
@goto day2_night
|
||||
|
||||
# День 2 - Ночь
|
||||
@label day2_night
|
||||
@bg night
|
||||
*Вы просыпаетесь от шума. Кто-то ходит по квартире.*
|
||||
|
||||
choice Исследовать=>goto investigate_noise|Притвориться спящим=>goto pretend_sleep|Позвать на помощь=>goto call_for_help
|
||||
|
||||
@label investigate_noise
|
||||
@bg black
|
||||
*Это Химицу стоит у окна. Она быстро поворачивается.*
|
||||
himitsu: *шепотом* Не мог спать. Метель... напоминает мне кое-что.
|
||||
choice Расспросить=>goto ask_her_past|Просто постоять рядом=>goto stand_with_her|Вернуться спать=>goto return_to_sleep
|
||||
|
||||
@label ask_her_past
|
||||
himitsu: *после паузы* Когда я была маленькой... *резко обрывает* Забудь.
|
||||
@set himitsu_points+=15
|
||||
@goto day3_morning
|
||||
|
||||
@label stand_with_her
|
||||
*Вы молча стоите рядом. Через несколько минут она незаметно берёт вас за руку.*
|
||||
@set himitsu_points+=20
|
||||
@goto day3_morning
|
||||
|
||||
@label return_to_sleep
|
||||
himitsu: *саркастично* Да, беги. Все бегут.
|
||||
@set himitsu_points-=10
|
||||
@goto day3_morning
|
||||
|
||||
@label pretend_sleep
|
||||
@bg black
|
||||
*Вы слышите, как кто-то поправляет ваше одеяло. Пахнет ванилью.*
|
||||
@set mitsuhа_points+=10
|
||||
@goto day3_morning
|
||||
|
||||
@label call_for_help
|
||||
@bg black
|
||||
yui: *вбегает* Что случилось?! О... *видит, что это просто Химицу*
|
||||
himitsu: *ядовито* Поздравляю, всех разбудил.
|
||||
@set yui_points+=5
|
||||
@set himitsu_points-=10
|
||||
@goto day3_morning
|
||||
|
||||
# День 3 - Утро
|
||||
@label day3_morning
|
||||
@bg morning
|
||||
@set day=3
|
||||
*Метель почти утихла. Девушки взволнованны.*
|
||||
|
||||
mitsuhа: *смотрит в окно* Кажется, сегодня сможем выбраться!
|
||||
yui: Надо собрать вещи! *бегает по комнате*
|
||||
himitsu: *спокойно* Наконец-то. *но выглядит немного грустной*
|
||||
|
||||
choice Помочь собираться=>goto help_packing|Проверить выход=>goto check_exit|Поговорить с Химицу=>goto talk_to_himitsu
|
||||
|
||||
@label help_packing
|
||||
*Вы помогаете Мицухе собрать её многочисленные вещи.*
|
||||
mitsuhа: Ты такой внимательный! *краснеет*
|
||||
@set mitsuhа_points+=15
|
||||
@goto day3_decision
|
||||
|
||||
@label check_exit
|
||||
*Вы очищаете дверь от снега. Юи присоединяется.*
|
||||
yui: Командная работа! *улыбается*
|
||||
@set yui_points+=10
|
||||
@goto day3_decision
|
||||
|
||||
@label talk_to_himitsu
|
||||
himitsu: *неожиданно* Может... как-нибудь встретимся? Если выберемся.
|
||||
@set himitsu_points+=20
|
||||
@goto day3_decision
|
||||
|
||||
# День 3 - Решающий выбор
|
||||
@label day3_decision
|
||||
@bg day
|
||||
*Дверь наконец открывается. Пришло время прощаться... или нет?*
|
||||
|
||||
choice Пригласить Юи на свидание=>goto date_yui|Предложить Химицу пойти вместе=>goto go_with_himitsu|Признаться Мицухе в чувствах=>goto confess_to_mitsuhа|Промолчать и уйти=>goto bad_ending_lonely
|
||||
|
||||
@label date_yui
|
||||
@if yui_points>50 then goto yui_good_ending
|
||||
*Юи вежливо отказывает. Похоже, вы не нашли общий язык.*
|
||||
@goto credits
|
||||
|
||||
@label yui_good_ending
|
||||
yui: *сияет* Я думала, ты никогда не предложишь!
|
||||
*Вы начинаете встречаться. Её энергия заряжает вас каждый день.*
|
||||
@goto credits
|
||||
|
||||
@label go_with_himitsu
|
||||
@if himitsu_points>60 then goto himitsu_good_ending
|
||||
*himitsu холодно отвечает: "Не думаю, что это хорошая идея."*
|
||||
@goto credits
|
||||
|
||||
@label himitsu_good_ending
|
||||
himitsu: *с лёгкой улыбкой* Ладно... но только если ты будешь молчать в моей мастерской.
|
||||
*Вы становитесь её моделью, а затем и чем-то большим.*
|
||||
@goto credits
|
||||
|
||||
@label confess_to_mitsuhа
|
||||
@if mitsuhа_points>55 then goto mitsuhа_good_ending
|
||||
*mitsuhа смущённо бормочет извинения и быстро уходит.*
|
||||
@goto credits
|
||||
|
||||
@label mitsuhа_good_ending
|
||||
mitsuhа: *тихо* Я... я тоже тебя люблю.
|
||||
*Вы посещаете все её выступления и однагод делаете предложение на сцене.*
|
||||
@goto credits
|
||||
|
||||
@label bad_ending_lonely
|
||||
@bg black
|
||||
*Вы уходите, не попрощавшись. Девушки больше никогда вас не видят.*
|
||||
*Конец.*
|
||||
@goto credits
|
||||
|
||||
# Титры с обновлёнными переменными
|
||||
@label credits
|
||||
*Спасибо за игру!*
|
||||
*Итоговые показатели:*
|
||||
*Дней вместе: {day}*
|
||||
*Очки Юи: {yui_points}*
|
||||
*Очки Химицу: {himitsu_points}*
|
||||
*Очки Мицухи: {mitsuhа_points}*
|
||||
*Уровень рассудка: {sanity}*
|
||||
@if killed==1 then *Вас убили! Плохая концовка.*
|
||||
@if sanity<30 then *Вы сошли с ума. Плохая концовка.*
|
||||
397
tet/island_script_qwen.txt
Normal file
397
tet/island_script_qwen.txt
Normal file
@@ -0,0 +1,397 @@
|
||||
# Начало игры: Знакомство с игроком и событием
|
||||
|
||||
@label start
|
||||
|
||||
*Вы просыпаетесь на песчаном пляже под звуки волн и крики чаек.*
|
||||
|
||||
Player: Где я...? Что случилось?
|
||||
|
||||
*Над вами склоняется красивая девушка с длинными светлыми волосами и мягким голосом.*
|
||||
|
||||
Aoi: Ты очнулся... Хорошо, что ты жив.
|
||||
|
||||
Player: Кто вы? Почему мы здесь?
|
||||
|
||||
Aoi: Мы потерпели крушение. Остальные тоже выжили. Но это уже третий день, как мы здесь.
|
||||
|
||||
*Позади появляются ещё две девушки — одна с короткой стрижкой и весёлым взглядом, другая — задумчивая и серьёзная.*
|
||||
|
||||
Natsuki: Эй, он проснулся? Привет, красавчик!
|
||||
|
||||
Yume: Не шуми так, Natsuki. Он, наверное, всё ещё слаб.
|
||||
|
||||
@char Aoi:Аои|Длинные светлые волосы, голубые глаза|Спокойная, уравновешенная|Пахнет морским бризом
|
||||
@char Natsuki:Нацуки|Короткие розовые волосы, карие глаза|Энергичная, любит приключения|Пахнет клубникой
|
||||
@char Yume:Юме|Чёрные волосы до плеч, серые глаза|Замкнутая, но добрая внутри|Пахнет старыми книгами
|
||||
|
||||
@set aoi = 0
|
||||
@set natsuki = 0
|
||||
@set yume = 0
|
||||
@set day = 1
|
||||
|
||||
@goto morning_start
|
||||
|
||||
# Утро первого дня
|
||||
|
||||
@label morning_start
|
||||
|
||||
@bg morning
|
||||
*Nад вами рассвет, оранжевые облака медленно проплывают над горизонтом. Море спокойно, а воздух наполнен запахом соли.*
|
||||
|
||||
Aoi: Сегодня мы должны исследовать остров. Может быть, найдём еду или сигнал для спасения.
|
||||
|
||||
Natsuki: Ага! Я уже готова! Только давайте возьмём палки и верёвку.
|
||||
|
||||
Yume: Или можно поискать в пещерах. Там может быть сухо и безопасно.
|
||||
|
||||
choice Пойти с Аои=>goto explore_aoi | Пойти с Нацуки=>goto explore_natsuki | Пойти с Юме=>goto explore_yume
|
||||
|
||||
# Исследование с Аои
|
||||
|
||||
@label explore_aoi
|
||||
@set aoi += 10
|
||||
|
||||
*Вы идёте по берегу вдоль скал. Аои аккуратно собирает ракушки и показывает вам, какие из них можно есть.*
|
||||
|
||||
Aoi: Эти ракушки — хорошее дополнение к пище. Они богаты белком.
|
||||
|
||||
Player: Ты знаешь много полезного...
|
||||
|
||||
Aoi: Я училась на биолога. Так что немного знаю природу.
|
||||
|
||||
*Aoi улыбается, её щёки слегка розовеют.*
|
||||
|
||||
@goto evening_day1
|
||||
|
||||
# Исследование с Нацуки
|
||||
|
||||
@label explore_natsuki
|
||||
@set natsuki += 10
|
||||
|
||||
*Natsuki ведёт вас вглубь джунглей. Она легко карабкается по деревьям и срывает фрукты.*
|
||||
|
||||
Natsuki: Смотри, эти плоды вкусные и сочные!
|
||||
|
||||
Player: А они безопасны?
|
||||
|
||||
Natsuki: Ну, если я не умерла, то и ты не умрёшь!
|
||||
|
||||
*Она смеётся и протягивает тебе половину фрукта.*
|
||||
|
||||
Natsuki: Давай, попробуй!
|
||||
|
||||
Player: Вкусно...
|
||||
|
||||
Natsuki: Я рада!
|
||||
|
||||
@goto evening_day1
|
||||
|
||||
# Исследование с Юме
|
||||
|
||||
@label explore_yume
|
||||
@set yume += 10
|
||||
|
||||
*Юме ведёт вас к древней пещере. Внутри темно, но она зажигает маленький факел.*
|
||||
|
||||
Yume: Здесь раньше жили люди. Видишь эти следы?
|
||||
|
||||
Player: Ты думаешь, они были здесь?
|
||||
|
||||
Yume: Возможно. Но сейчас мы одни.
|
||||
|
||||
*Она поворачивается к вам, её взгляд становится мягче.*
|
||||
|
||||
Yume: Мне приятно, что ты выбрал меня.
|
||||
|
||||
@goto evening_day1
|
||||
|
||||
# Вечер первого дня
|
||||
|
||||
@label evening_day1
|
||||
@bg sunset
|
||||
|
||||
*Солнце заходит, окрашивая небо в теплые оттенки. Вы возвращаетесь на пляж.*
|
||||
|
||||
Player: Как прошёл ваш день?
|
||||
|
||||
Aoi: Мы нашли ракушки и воду.
|
||||
|
||||
Natsuki: А я нашла бананы! Вкуснющие!
|
||||
|
||||
Yume: В пещере могут быть припасы. Нужно исследовать дальше.
|
||||
|
||||
*Все трое садятся рядом с костром, который разожгла Нацуки.*
|
||||
|
||||
@set day = 2
|
||||
@goto night_day1
|
||||
|
||||
# Ночь первого дня
|
||||
|
||||
@label night_day1
|
||||
@bg night
|
||||
|
||||
*Луна светит ярко. Все трое сидят у костра, рассказывая истории.*
|
||||
|
||||
Aoi: Я всегда хотела побывать на необитаемом острове... Только не в таких обстоятельствах.
|
||||
|
||||
Natsuki: А я бы хотела стать пиратом!
|
||||
|
||||
Yume: Я хочу домой. К своим книгам.
|
||||
|
||||
Player: А что бы вы сделали, если бы нас спасли завтра?
|
||||
|
||||
*Девушки задумываются.*
|
||||
|
||||
Aoi: Пошла бы на прогулку с любимым человеком.
|
||||
|
||||
Natsuki: Отметила бы спасение шикарной вечеринкой!
|
||||
|
||||
Yume: Посидела бы с чашкой чая и книгой.
|
||||
|
||||
*Тихо. Только треск костра и шум волн.*
|
||||
|
||||
@goto morning_day2
|
||||
|
||||
# Утро второго дня
|
||||
|
||||
@label morning_day2
|
||||
@bg morning
|
||||
|
||||
*Утро. Все выглядят немного уставшими, но бодрыми.*
|
||||
|
||||
Player: Что будем делать сегодня?
|
||||
|
||||
Aoi: Можно попробовать построить укрытие получше.
|
||||
|
||||
Natsuki: Или построить плот!
|
||||
|
||||
Yume: Лучше проверить пещеры. Может быть, там есть выход.
|
||||
|
||||
choice Помочь Аои=>goto help_aoi | Помочь Нацуки=>goto help_natsuki | Помочь Юме=>goto help_yume
|
||||
|
||||
# Помощь Аои
|
||||
|
||||
@label help_aoi
|
||||
@set aoi += 15
|
||||
|
||||
*Aoi ведёт вас к месту, где можно собрать лианы и сделать кров.*
|
||||
|
||||
Aoi: Вот, держи эту ветку. Нужно связать их так...
|
||||
|
||||
Player: Как ты всё знаешь?
|
||||
|
||||
Aoi: Когда-то была в походе с отцом. Он был военным.
|
||||
|
||||
*Aoi смотрит вдаль, словно вспоминая.*
|
||||
|
||||
@goto evening_day2
|
||||
|
||||
# Помощь Нацуки
|
||||
|
||||
@label help_natsuki
|
||||
@set natsuki += 15
|
||||
|
||||
*Natsuki тащит бревна и предлагает сделать плот.*
|
||||
|
||||
Natsuki: Если мы построим плот, сможем уйти отсюда!
|
||||
|
||||
Player: А ты уверена, что он удержит нас?
|
||||
|
||||
Natsuki: Конечно! Я мастер по плотам!
|
||||
|
||||
*Она хватает вас за руку.*
|
||||
|
||||
Natsuki: Давай, помоги мне перевернуть его!
|
||||
|
||||
*Вы вместе переворачиваете плот. Она смеётся.*
|
||||
|
||||
@goto evening_day2
|
||||
|
||||
# Помощь Юме
|
||||
|
||||
@label help_yume
|
||||
@set yume += 15
|
||||
|
||||
*Юме снова ведёт вас в пещеру. Теперь она освещает стены факелом.*
|
||||
|
||||
Yume: Здесь есть какие-то символы. Может, это предупреждение?
|
||||
|
||||
Player: О чём?
|
||||
|
||||
Yume: Не знаю... Но я чувствую, что это важно.
|
||||
|
||||
*Она смотрит на вас своими глубокими глазами.*
|
||||
|
||||
Yume: Ты единственный, кому я могу доверить это.
|
||||
|
||||
@goto evening_day2
|
||||
|
||||
# Вечер второго дня
|
||||
|
||||
@label evening_day2
|
||||
@bg sunset
|
||||
|
||||
*Сегодня вы вместе делали плот. Все устали, но довольны.*
|
||||
|
||||
Natsuki: Завтра можем попробовать на нём уплыть!
|
||||
|
||||
Aoi: Но лучше подождать, пока мы наберём еды.
|
||||
|
||||
Yume: Я всё ещё думаю, что в пещерах может быть что-то важное.
|
||||
|
||||
Player: Может, стоит провести ночь в пещере?
|
||||
|
||||
*Девушки переглядываются.*
|
||||
|
||||
Aoi: Я согласна, если будет тепло.
|
||||
|
||||
Natsuki: Только если будет музыка!
|
||||
|
||||
Yume: Если ты будешь рядом, я соглашусь.
|
||||
|
||||
*Все улыбаются.*
|
||||
|
||||
@set day = 3
|
||||
@goto night_day2
|
||||
|
||||
# Ночь второго дня
|
||||
|
||||
@label night_day2
|
||||
@bg night
|
||||
|
||||
*Вы в пещере. Огонь освещает лица девушек.*
|
||||
|
||||
Aoi: Было бы хорошо, если бы мы могли остаться так навсегда.
|
||||
|
||||
Natsuki: Только без дождя и пауков!
|
||||
|
||||
Yume: Здесь тихо... Мне нравится.
|
||||
|
||||
Player: Я рад, что вы все здесь.
|
||||
|
||||
*Тишина.*
|
||||
|
||||
*Aoi кладёт голову вам на плечо.*
|
||||
|
||||
Aoi: Ты... особенный для меня.
|
||||
|
||||
*Natsuki смеётся.*
|
||||
|
||||
Natsuki: А ты уверен, кто тебе нравится больше?
|
||||
|
||||
*Юме молчит, но смотрит на вас с теплотой.*
|
||||
|
||||
@goto morning_day3
|
||||
|
||||
# Утро третьего дня
|
||||
|
||||
@label morning_day3
|
||||
@bg morning
|
||||
|
||||
*Вы просыпаетесь в пещере. Всё тело затекло, но рядом лежит Юме.*
|
||||
|
||||
Yume: Ты проснулся? Хорошо.
|
||||
|
||||
Player: Ты спала рядом?
|
||||
|
||||
Yume: Да. Мне было спокойнее.
|
||||
|
||||
*Вы выходите из пещеры. Вдалеке виднеется парус.*
|
||||
|
||||
Player: Смотрите! Это корабль!
|
||||
|
||||
*Все радуются.*
|
||||
|
||||
Natsuki: Он нас спасёт!
|
||||
|
||||
Aoi: Но... ты же выберешь кого-то из нас?
|
||||
|
||||
Player: Я...
|
||||
|
||||
*Выбор:*
|
||||
|
||||
choice Сказать "Я люблю тебя" Аои=>goto ending_aoi | Сказать "Я люблю тебя" Нацуки=>goto ending_natsuki | Сказать "Я люблю тебя" Юме=>goto ending_yume | Сказать "Я не могу выбрать"=>goto ending_all
|
||||
|
||||
# Концовка: Аои
|
||||
|
||||
@label ending_aoi
|
||||
@set aoi += 20
|
||||
|
||||
*Aoi смотрит на вас с благодарностью.*
|
||||
|
||||
Aoi: Я... рада.
|
||||
|
||||
*Вы подходите к кораблю. Аои берёт вас за руку.*
|
||||
|
||||
*Финал: Вы возвращаетесь домой вместе. Через год вы женитесь на Аои.*
|
||||
|
||||
@bg white
|
||||
*A happy ending with Aoi*
|
||||
@goto end
|
||||
|
||||
# Концовка: Нацуки
|
||||
|
||||
@label ending_natsuki
|
||||
@set natsuki += 20
|
||||
|
||||
*Natsuki смеётся и обнимает вас.*
|
||||
|
||||
Natsuki: Ты лучший!
|
||||
|
||||
*Вы подходите к кораблю. Но внезапно Нацуки достаёт нож.*
|
||||
|
||||
Natsuki: Я не могу позволить тебе уйти...
|
||||
|
||||
*Она ударяет вас в спину.*
|
||||
|
||||
@bg black
|
||||
*Bad ending: Killed by Natsuki*
|
||||
@goto end
|
||||
|
||||
# Концовка: Юме
|
||||
|
||||
@label ending_yume
|
||||
@set yume += 20
|
||||
|
||||
*Юме берёт вас за руку.*
|
||||
|
||||
Yume: Я буду с тобой.
|
||||
|
||||
*Вы подходите к кораблю. Но Юме вдруг толкает вас вниз.*
|
||||
|
||||
Yume: Прости... я не могу.
|
||||
|
||||
*Она убегает обратно в пещеру.*
|
||||
|
||||
@bg black
|
||||
*Sad ending: Yume leaves you behind*
|
||||
@goto end
|
||||
|
||||
# Концовка: Не могу выбрать
|
||||
|
||||
@label ending_all
|
||||
|
||||
*Natsuki, Аои и Юме смотрят на вас с разочарованием.*
|
||||
|
||||
Aoi: Тогда... мы уйдём.
|
||||
|
||||
Natsuki: Без тебя.
|
||||
|
||||
Yume: Прости.
|
||||
|
||||
*Корабль уходит. Вы остаётесь один.*
|
||||
|
||||
@bg black
|
||||
*Bad ending: You are left alone*
|
||||
@goto end
|
||||
|
||||
# Конец игры
|
||||
|
||||
@label end
|
||||
@bg black
|
||||
|
||||
*Игра окончена.*
|
||||
*Хочешь начать заново?*
|
||||
choice Перезапустить=>goto start | Выйти=>end_game
|
||||
|
||||
@end_game
|
||||
410
tet/novel — копия.txt
Normal file
410
tet/novel — копия.txt
Normal file
@@ -0,0 +1,410 @@
|
||||
import pygame
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import random
|
||||
|
||||
# Инициализация Pygame
|
||||
pygame.init()
|
||||
|
||||
# Настройки окна
|
||||
WIDTH, HEIGHT = 1280, 720
|
||||
screen = pygame.display.set_mode((WIDTH, HEIGHT))
|
||||
pygame.display.set_caption("Визуальная новелла")
|
||||
|
||||
# Цвета
|
||||
WHITE = (255, 255, 255)
|
||||
BLACK = (0, 0, 0)
|
||||
GRAY = (200, 200, 200)
|
||||
LIGHT_BLUE = (173, 216, 230)
|
||||
DARK_BLUE = (0, 0, 139)
|
||||
CHOICE_BG = (240, 240, 255)
|
||||
|
||||
# Шрифты
|
||||
font = pygame.font.Font(None, 36)
|
||||
character_font = pygame.font.Font(None, 32)
|
||||
choice_font = pygame.font.Font(None, 30)
|
||||
small_font = pygame.font.Font(None, 24)
|
||||
|
||||
class NovelEngine:
|
||||
def __init__(self):
|
||||
self.script = []
|
||||
self.current_line = 0
|
||||
self.running = True
|
||||
self.background = None
|
||||
self.characters = {}
|
||||
self.variables = {}
|
||||
self.choices = []
|
||||
self.waiting_for_choice = False
|
||||
self.call_stack = [] # Стек для хранения позиций возврата
|
||||
self.text_box_rect = pygame.Rect(50, HEIGHT - 250, WIDTH - 100, 200)
|
||||
self.choice_box_rect = pygame.Rect(WIDTH//2 - 300, HEIGHT//2 - 150, 600, 300)
|
||||
|
||||
def load_script(self, filename):
|
||||
"""Загрузка скрипта из файла"""
|
||||
self.script = []
|
||||
script_path = os.path.join(os.path.dirname(__file__), filename)
|
||||
|
||||
if not os.path.exists(script_path):
|
||||
print(f"Ошибка: файл {script_path} не найден!")
|
||||
return False
|
||||
|
||||
with open(script_path, 'r', encoding='utf-8') as file:
|
||||
for line in file:
|
||||
line = line.strip()
|
||||
if line and not line.startswith('#'):
|
||||
self.script.append(line)
|
||||
return True
|
||||
|
||||
def parse_character(self, command):
|
||||
"""Обработка описания персонажа с дополнительными атрибутами"""
|
||||
if command.startswith("char "):
|
||||
parts = command[5:].split("|")
|
||||
char_id = parts[0].strip().split(":")[0]
|
||||
char_data = {
|
||||
"name": parts[0].split(":")[1].strip(),
|
||||
"appearance": parts[1].strip() if len(parts) > 1 else "",
|
||||
"details": parts[2].strip() if len(parts) > 2 else "",
|
||||
"scent": parts[3].strip() if len(parts) > 3 else ""
|
||||
}
|
||||
self.characters[char_id] = char_data
|
||||
return True
|
||||
return False
|
||||
|
||||
def parse_command(self, command):
|
||||
"""Обработка команд скрипта"""
|
||||
# Случайный переход в подпрограмму
|
||||
if command.startswith("random_gosub "):
|
||||
parts = command[12:].split()
|
||||
if len(parts) >= 2:
|
||||
probability = float(parts[0])
|
||||
if random.random() < probability:
|
||||
# Сохраняем текущую позицию для возврата
|
||||
self.call_stack.append(self.current_line + 1)
|
||||
# Переходим к случайной подпрограмме
|
||||
sub_label = random.choice(parts[1:])
|
||||
self.jump_to_label(sub_label)
|
||||
return True
|
||||
|
||||
# Возврат из подпрограммы
|
||||
elif command == "return":
|
||||
if self.call_stack:
|
||||
self.current_line = self.call_stack.pop()
|
||||
else:
|
||||
print("Ошибка: стек вызовов пуст!")
|
||||
return True
|
||||
|
||||
# Установка переменной
|
||||
elif command.startswith("set "):
|
||||
parts = command[4:].split("|")
|
||||
for part in parts:
|
||||
if "=" in part:
|
||||
var, val = part.split("=", 1)
|
||||
self.variables[var.strip()] = val.strip()
|
||||
return True
|
||||
|
||||
# Условие
|
||||
elif command.startswith("if "):
|
||||
match = re.match(r'if (\w+)([=!<>]+)(.+?) then goto (\w+)', command[3:])
|
||||
if match:
|
||||
var_name, op, value, label = match.groups()
|
||||
current_value = self.variables.get(var_name, "")
|
||||
|
||||
condition_met = False
|
||||
if op == "==":
|
||||
condition_met = str(current_value) == value
|
||||
elif op == "!=":
|
||||
condition_met = str(current_value) != value
|
||||
elif op == ">":
|
||||
condition_met = float(current_value) > float(value)
|
||||
elif op == "<":
|
||||
condition_met = float(current_value) < float(value)
|
||||
|
||||
if condition_met:
|
||||
self.jump_to_label(label)
|
||||
return True
|
||||
|
||||
# Метка
|
||||
elif command.startswith("label "):
|
||||
return True
|
||||
|
||||
# Подпрограмма
|
||||
elif command.startswith("sub_"):
|
||||
return True
|
||||
|
||||
# Переход
|
||||
elif command.startswith("goto "):
|
||||
label = command[5:].strip()
|
||||
self.jump_to_label(label)
|
||||
return True
|
||||
|
||||
# Фон
|
||||
elif command.startswith("bg "):
|
||||
bg_color = command[3:].strip().lower()
|
||||
if bg_color == "black":
|
||||
self.background = BLACK
|
||||
elif bg_color == "white":
|
||||
self.background = WHITE
|
||||
elif bg_color == "blue":
|
||||
self.background = LIGHT_BLUE
|
||||
elif bg_color == "sunset":
|
||||
self.background = (255, 153, 51) # Оранжевый для заката
|
||||
else:
|
||||
self.background = GRAY
|
||||
return True
|
||||
|
||||
# Персонаж
|
||||
elif command.startswith("char "):
|
||||
return self.parse_character(command)
|
||||
|
||||
return False
|
||||
|
||||
def draw_descriptions(self, text, x, y, font_obj, color):
|
||||
"""Отрисовка текста с учётом звёздочек"""
|
||||
if text.startswith("*") and text.endswith("*"):
|
||||
# Рисуем курсивом для описаний
|
||||
italic_font = pygame.font.Font(None, 32)
|
||||
italic_font.set_italic(True)
|
||||
self.draw_text(text[1:-1], x, y, italic_font, color)
|
||||
else:
|
||||
self.draw_text(text, x, y, font_obj, color)
|
||||
|
||||
def jump_to_label(self, label):
|
||||
"""Переход к метке в скрипте"""
|
||||
for i, line in enumerate(self.script):
|
||||
if line.startswith(f"@label {label}") or line.startswith(f"@{label}"):
|
||||
self.current_line = i
|
||||
return
|
||||
print(f"Метка '{label}' не найдена!")
|
||||
|
||||
def process_choices(self, line):
|
||||
"""Обработка строки с выбором"""
|
||||
if line.startswith("choice "):
|
||||
choices_text = line[7:].split("|")
|
||||
self.choices = []
|
||||
|
||||
for choice_text in choices_text:
|
||||
parts = choice_text.split("=>")
|
||||
if len(parts) == 2:
|
||||
choice_display = parts[0].strip()
|
||||
choice_action = parts[1].strip()
|
||||
self.choices.append((choice_display, choice_action))
|
||||
|
||||
self.waiting_for_choice = True
|
||||
return True
|
||||
return False
|
||||
|
||||
def substitute_variables(self, text):
|
||||
"""Подстановка переменных в текст"""
|
||||
for var_name, var_value in self.variables.items():
|
||||
text = text.replace(f"{{{var_name}}}", str(var_value))
|
||||
return text
|
||||
|
||||
def get_character_info(self, char_id):
|
||||
"""Возвращает полное описание персонажа"""
|
||||
if char_id not in self.characters:
|
||||
return f"Персонаж {char_id}", ""
|
||||
|
||||
char = self.characters.get(char_id)
|
||||
if isinstance(char, str):
|
||||
return char, ""
|
||||
|
||||
details = []
|
||||
if char["appearance"]: details.append(char['appearance'])
|
||||
if char["details"]: details.append(char['details'])
|
||||
if char["scent"]: details.append(char['scent'])
|
||||
|
||||
return char["name"], " | ".join(details)
|
||||
|
||||
def process_line(self):
|
||||
if self.current_line >= len(self.script):
|
||||
self.running = False
|
||||
return None
|
||||
|
||||
line = self.script[self.current_line]
|
||||
line = self.substitute_variables(line)
|
||||
|
||||
# Обработка команд
|
||||
if line.startswith("@"):
|
||||
if self.parse_command(line[1:]):
|
||||
self.current_line += 1
|
||||
return self.process_line()
|
||||
return None
|
||||
|
||||
# Обработка выбора
|
||||
if self.process_choices(line):
|
||||
self.current_line += 1
|
||||
return None
|
||||
|
||||
# Обработка диалога с проверкой разделителя
|
||||
if ":" in line and not line.startswith("*"):
|
||||
parts = line.split(":", 1)
|
||||
if len(parts) == 2:
|
||||
char_id = parts[0].strip()
|
||||
text = parts[1].strip()
|
||||
return char_id, text
|
||||
|
||||
# Обработка описания или простого текста
|
||||
return None, line
|
||||
|
||||
def make_choice(self, choice_index):
|
||||
"""Обработка выбора игрока"""
|
||||
if 0 <= choice_index < len(self.choices):
|
||||
choice_action = self.choices[choice_index][1]
|
||||
|
||||
if choice_action.startswith("goto "):
|
||||
label = choice_action[5:].strip()
|
||||
self.jump_to_label(label)
|
||||
elif "+=" in choice_action:
|
||||
var, value = choice_action.split("+=", 1)
|
||||
var = var.strip()
|
||||
self.variables[var] = str(int(self.variables.get(var, 0)) + int(value.strip()))
|
||||
self.current_line += 1
|
||||
elif "-=" in choice_action:
|
||||
var, value = choice_action.split("-=", 1)
|
||||
var = var.strip()
|
||||
self.variables[var] = str(int(self.variables.get(var, 0)) - int(value.strip()))
|
||||
self.current_line += 1
|
||||
elif "=" in choice_action:
|
||||
var, value = choice_action.split("=", 1)
|
||||
self.variables[var.strip()] = value.strip()
|
||||
self.current_line += 1
|
||||
|
||||
self.choices = []
|
||||
self.waiting_for_choice = False
|
||||
|
||||
def next_line(self):
|
||||
"""Переход к следующей строке"""
|
||||
self.current_line += 1
|
||||
if self.current_line >= len(self.script):
|
||||
self.running = False
|
||||
|
||||
def draw_text_box(self):
|
||||
"""Отрисовка текстового поля"""
|
||||
pygame.draw.rect(screen, WHITE, self.text_box_rect)
|
||||
pygame.draw.rect(screen, DARK_BLUE, self.text_box_rect, 3)
|
||||
|
||||
def draw_choice_box(self):
|
||||
"""Отрисовка поля выбора"""
|
||||
pygame.draw.rect(screen, CHOICE_BG, self.choice_box_rect)
|
||||
pygame.draw.rect(screen, DARK_BLUE, self.choice_box_rect, 3)
|
||||
|
||||
def draw_text(self, text, x, y, font_obj, color=BLACK, max_width=None):
|
||||
"""Отрисовка текста с переносом строк"""
|
||||
if max_width is None:
|
||||
max_width = self.text_box_rect.width - 40
|
||||
|
||||
words = text.split(' ')
|
||||
space_width = font_obj.size(' ')[0]
|
||||
line_height = font_obj.get_height()
|
||||
|
||||
current_line = []
|
||||
current_width = 0
|
||||
|
||||
for word in words:
|
||||
word_surface = font_obj.render(word, True, color)
|
||||
word_width = word_surface.get_width()
|
||||
|
||||
if current_width + word_width <= max_width:
|
||||
current_line.append(word)
|
||||
current_width += word_width + space_width
|
||||
else:
|
||||
line_text = ' '.join(current_line)
|
||||
line_surface = font_obj.render(line_text, True, color)
|
||||
screen.blit(line_surface, (x, y))
|
||||
y += line_height
|
||||
current_line = [word]
|
||||
current_width = word_width + space_width
|
||||
|
||||
if current_line:
|
||||
line_text = ' '.join(current_line)
|
||||
line_surface = font_obj.render(line_text, True, color)
|
||||
screen.blit(line_surface, (x, y))
|
||||
|
||||
def draw(self):
|
||||
"""Отрисовка текущего состояния"""
|
||||
# Фон
|
||||
bg_color = self.background if self.background else LIGHT_BLUE
|
||||
screen.fill(bg_color)
|
||||
|
||||
# Текстовое поле
|
||||
self.draw_text_box()
|
||||
|
||||
# Текст
|
||||
line_data = self.process_line()
|
||||
x = self.text_box_rect.x + 20
|
||||
y = self.text_box_rect.y + 20
|
||||
|
||||
if line_data and not self.waiting_for_choice:
|
||||
if isinstance(line_data, tuple): # Диалог с персонажем
|
||||
char_id, text = line_data
|
||||
name, details = self.get_character_info(char_id)
|
||||
|
||||
# Отрисовка имени персонажа
|
||||
name_surface = character_font.render(name + ":", True, DARK_BLUE)
|
||||
screen.blit(name_surface, (x, y))
|
||||
y += 40
|
||||
|
||||
# Отрисовка текста персонажа
|
||||
self.draw_text(text, x, y, font, BLACK)
|
||||
y += 60
|
||||
|
||||
# Отрисовка деталей персонажа (если есть)
|
||||
if details:
|
||||
details_surface = small_font.render(details, True, (100, 100, 100))
|
||||
screen.blit(details_surface, (x, y))
|
||||
else: # Описание или простой текст
|
||||
_, text = line_data
|
||||
self.draw_descriptions(text, x, y, font, BLACK)
|
||||
|
||||
# Варианты выбора
|
||||
if self.waiting_for_choice and self.choices:
|
||||
self.draw_choice_box()
|
||||
choice_y = self.choice_box_rect.y + 30
|
||||
for i, (choice_text, _) in enumerate(self.choices):
|
||||
choice_surface = choice_font.render(f"{i+1}. {choice_text}", True, BLACK)
|
||||
screen.blit(choice_surface, (self.choice_box_rect.x + 30, choice_y))
|
||||
choice_y += 40
|
||||
|
||||
# Индикатор продолжения
|
||||
if self.running and not self.waiting_for_choice and line_data:
|
||||
indicator = font.render("> Нажмите ПРОБЕЛ", True, DARK_BLUE)
|
||||
screen.blit(indicator, (WIDTH - 200, HEIGHT - 50))
|
||||
|
||||
pygame.display.flip()
|
||||
|
||||
def run(self):
|
||||
"""Основной цикл"""
|
||||
clock = pygame.time.Clock()
|
||||
|
||||
while self.running:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
elif event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_SPACE and not self.waiting_for_choice:
|
||||
self.next_line()
|
||||
elif event.key == pygame.K_1 and self.waiting_for_choice and len(self.choices) >= 1:
|
||||
self.make_choice(0)
|
||||
elif event.key == pygame.K_2 and self.waiting_for_choice and len(self.choices) >= 2:
|
||||
self.make_choice(1)
|
||||
elif event.key == pygame.K_3 and self.waiting_for_choice and len(self.choices) >= 3:
|
||||
self.make_choice(2)
|
||||
elif event.key == pygame.K_4 and self.waiting_for_choice and len(self.choices) >= 4:
|
||||
self.make_choice(3)
|
||||
elif event.key == pygame.K_ESCAPE:
|
||||
self.running = False
|
||||
|
||||
self.draw()
|
||||
clock.tick(60)
|
||||
|
||||
# Создаем экземпляр движка и запускаем
|
||||
if __name__ == "__main__":
|
||||
engine = NovelEngine()
|
||||
|
||||
if engine.load_script("island_script.txt"):
|
||||
engine.run()
|
||||
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
621
tet/novel.py
Normal file
621
tet/novel.py
Normal file
@@ -0,0 +1,621 @@
|
||||
import pygame
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import random
|
||||
|
||||
# Инициализация Pygame
|
||||
pygame.init()
|
||||
|
||||
# Настройки окна
|
||||
WIDTH, HEIGHT = 1920, 1080
|
||||
screen = pygame.display.set_mode((WIDTH, HEIGHT))
|
||||
pygame.display.set_caption("Визуальная новелла")
|
||||
|
||||
# Цвета
|
||||
WHITE = (255, 255, 255)
|
||||
BLACK = (0, 0, 0)
|
||||
GRAY = (200, 200, 200)
|
||||
LIGHT_BLUE = (173, 216, 230)
|
||||
DARK_BLUE = (0, 0, 139)
|
||||
CHOICE_BG = (240, 240, 255)
|
||||
|
||||
NIGHT_BLUE = (5, 5, 30)
|
||||
MORNING_YELLOW = (255, 220, 100)
|
||||
MORNING_GREEN = (100, 180, 80)
|
||||
MORNING_DARK_BLUE = (20, 40, 100)
|
||||
DAY_GREEN = (100, 200, 80)
|
||||
DAY_BLUE = (100, 200, 255)
|
||||
EVENING_ORANGE = (255, 120, 50)
|
||||
EVENING_DARK_GREEN = (30, 80, 40)
|
||||
|
||||
# Шрифты
|
||||
try:
|
||||
font = pygame.font.Font(None, 36)
|
||||
character_font = pygame.font.Font(None, 32)
|
||||
choice_font = pygame.font.Font(None, 30)
|
||||
small_font = pygame.font.Font(None, 24)
|
||||
italic_font = pygame.font.Font(None, 32)
|
||||
italic_font.set_italic(True)
|
||||
except:
|
||||
font = pygame.font.Font(None, 36)
|
||||
character_font = pygame.font.Font(None, 32)
|
||||
choice_font = pygame.font.Font(None, 30)
|
||||
small_font = pygame.font.Font(None, 24)
|
||||
italic_font = pygame.font.Font(None, 32)
|
||||
italic_font.set_italic(True)
|
||||
|
||||
class NovelEngine:
|
||||
def __init__(self):
|
||||
self.script = []
|
||||
self.current_line = 0
|
||||
self.running = True
|
||||
self.background = None
|
||||
self.characters = {}
|
||||
self.variables = {}
|
||||
self.choices = []
|
||||
self.waiting_for_choice = False
|
||||
self.call_stack = [] # Стек для хранения позиций возврата
|
||||
self.text_box_rect = pygame.Rect(50, HEIGHT - 250, WIDTH - 100, 200)
|
||||
self.choice_box_rect = pygame.Rect(WIDTH//2 - 300, HEIGHT//2 - 150, 600, 300)
|
||||
self.special_background = None
|
||||
self.background_textures = {} # Текстуры для сложных фонов
|
||||
self.init_background_textures() # Инициализируем текстуры
|
||||
|
||||
def load_script(self, filename):
|
||||
"""Загрузка скрипта из файла"""
|
||||
self.script = []
|
||||
script_path = os.path.join(os.path.dirname(__file__), filename)
|
||||
|
||||
if not os.path.exists(script_path):
|
||||
print(f"Ошибка: файл {script_path} не найден!")
|
||||
return False
|
||||
|
||||
with open(script_path, 'r', encoding='utf-8') as file:
|
||||
for line in file:
|
||||
line = line.strip()
|
||||
if line and not line.startswith('#'):
|
||||
self.script.append(line)
|
||||
return True
|
||||
|
||||
def parse_character(self, command):
|
||||
"""Обработка описания персонажа с дополнительными атрибутами"""
|
||||
if command.startswith("char "):
|
||||
parts = command[5:].split("|")
|
||||
char_id = parts[0].strip().split(":")[0]
|
||||
char_data = {
|
||||
"name": parts[0].split(":")[1].strip(),
|
||||
"appearance": parts[1].strip() if len(parts) > 1 else "",
|
||||
"details": parts[2].strip() if len(parts) > 2 else "",
|
||||
"scent": parts[3].strip() if len(parts) > 3 else ""
|
||||
}
|
||||
self.characters[char_id] = char_data
|
||||
return True
|
||||
return False
|
||||
|
||||
def init_background_textures(self):
|
||||
"""Создаем текстуры для сложных фонов один раз при инициализации"""
|
||||
# Ночное небо
|
||||
night_surface = pygame.Surface((WIDTH, HEIGHT))
|
||||
night_surface.fill(NIGHT_BLUE)
|
||||
for _ in range(100):
|
||||
x = random.randint(0, WIDTH)
|
||||
y = random.randint(0, HEIGHT // 2)
|
||||
size = random.randint(1, 3)
|
||||
brightness = random.randint(200, 255)
|
||||
pygame.draw.circle(night_surface, (brightness, brightness, brightness), (x, y), size)
|
||||
self.background_textures["night"] = night_surface
|
||||
|
||||
morning_surface = pygame.Surface((WIDTH, HEIGHT))
|
||||
for y in range(HEIGHT):
|
||||
ratio = y / HEIGHT
|
||||
# Верхняя часть (темно-синяя)
|
||||
if ratio < 0.3:
|
||||
r = int(MORNING_DARK_BLUE[0])
|
||||
g = int(MORNING_DARK_BLUE[1])
|
||||
b = int(MORNING_DARK_BLUE[2])
|
||||
# Средняя часть (переход)
|
||||
elif ratio < 0.7:
|
||||
r = int(MORNING_DARK_BLUE[0] + (MORNING_YELLOW[0] - MORNING_DARK_BLUE[0]) * (ratio - 0.3) / 0.4)
|
||||
g = int(MORNING_DARK_BLUE[1] + (MORNING_YELLOW[1] - MORNING_DARK_BLUE[1]) * (ratio - 0.3) / 0.4)
|
||||
b = int(MORNING_DARK_BLUE[2] + (MORNING_YELLOW[2] - MORNING_DARK_BLUE[2]) * (ratio - 0.3) / 0.4)
|
||||
# Нижняя часть (зеленая)
|
||||
else:
|
||||
r = int(MORNING_GREEN[0])
|
||||
g = int(MORNING_GREEN[1])
|
||||
b = int(MORNING_GREEN[2])
|
||||
pygame.draw.line(morning_surface, (r, g, b), (0, y), (WIDTH, y))
|
||||
pygame.draw.circle(morning_surface, MORNING_YELLOW, (WIDTH // 2, HEIGHT // 3), 50)
|
||||
self.background_textures["morning"] = morning_surface
|
||||
|
||||
day_surface = pygame.Surface((WIDTH, HEIGHT))
|
||||
# Градиент от голубого к зеленому
|
||||
for y in range(HEIGHT):
|
||||
ratio = y / HEIGHT
|
||||
if ratio < 0.8:
|
||||
r = int(DAY_BLUE[0])
|
||||
g = int(DAY_BLUE[1])
|
||||
b = int(DAY_BLUE[2])
|
||||
else:
|
||||
r = int(DAY_GREEN[0])
|
||||
g = int(DAY_GREEN[1])
|
||||
b = int(DAY_GREEN[2])
|
||||
pygame.draw.line(day_surface, (r, g, b), (0, y), (WIDTH, y))
|
||||
|
||||
# Рисуем облака
|
||||
for _ in range(5):
|
||||
x = random.randint(0, WIDTH)
|
||||
y = random.randint(50, HEIGHT // 3)
|
||||
size = random.randint(30, 70)
|
||||
pygame.draw.circle(day_surface, WHITE, (x, y), size)
|
||||
pygame.draw.circle(day_surface, WHITE, (x + size//2, y - size//3), size//2)
|
||||
pygame.draw.circle(day_surface, WHITE, (x - size//2, y - size//4), size//2)
|
||||
self.background_textures["day"] = day_surface
|
||||
|
||||
evening_surface = pygame.Surface((WIDTH, HEIGHT))
|
||||
for y in range(HEIGHT):
|
||||
ratio = y / HEIGHT
|
||||
if ratio < 0.5:
|
||||
r = int(EVENING_ORANGE[0])
|
||||
g = int(EVENING_ORANGE[1])
|
||||
b = int(EVENING_ORANGE[2])
|
||||
else:
|
||||
r = int(EVENING_ORANGE[0] + (EVENING_DARK_GREEN[0] - EVENING_ORANGE[0]) * (ratio - 0.5) / 0.5)
|
||||
g = int(EVENING_ORANGE[1] + (EVENING_DARK_GREEN[1] - EVENING_ORANGE[1]) * (ratio - 0.5) / 0.5)
|
||||
b = int(EVENING_ORANGE[2] + (EVENING_DARK_GREEN[2] - EVENING_ORANGE[2]) * (ratio - 0.5) / 0.5)
|
||||
pygame.draw.line(evening_surface, (r, g, b), (0, y), (WIDTH, y))
|
||||
|
||||
# Рисуем заходящее солнце
|
||||
pygame.draw.circle(evening_surface, (255, 200, 100), (WIDTH // 2, HEIGHT // 2), 60)
|
||||
pygame.draw.rect(evening_surface, EVENING_DARK_GREEN, (0, HEIGHT // 2, WIDTH, HEIGHT // 2))
|
||||
self.background_textures["evening"] = evening_surface
|
||||
|
||||
def draw_night_sky(self):
|
||||
"""Рисует ночное небо со звездами"""
|
||||
screen.blit(self.background_textures["night"], (0, 0))
|
||||
|
||||
def draw_morning_sky(self):
|
||||
"""Рисует утреннее небо с градиентом"""
|
||||
screen.blit(self.background_textures["morning"], (0, 0))
|
||||
|
||||
def draw_day_sky(self):
|
||||
"""Рисует дневное небо с облаками"""
|
||||
screen.blit(self.background_textures["day"], (0, 0))
|
||||
|
||||
def draw_evening_sky(self):
|
||||
"""Рисует вечернее небо с закатом"""
|
||||
screen.blit(self.background_textures["evening"], (0, 0))
|
||||
|
||||
def parse_command(self, command):
|
||||
"""Обработка команд скрипта"""
|
||||
# Случайный переход в подпрограмму
|
||||
if command.startswith("random_gosub "):
|
||||
parts = command[12:].split()
|
||||
if len(parts) >= 2:
|
||||
# Создаем список вариантов с их вероятностями
|
||||
options = []
|
||||
probabilities = []
|
||||
labels = []
|
||||
|
||||
# Разбираем части на вероятности и метки
|
||||
i = 0
|
||||
while i < len(parts):
|
||||
try:
|
||||
prob = float(parts[i])
|
||||
label = parts[i+1]
|
||||
probabilities.append(prob)
|
||||
labels.append(label)
|
||||
options.append((prob, label))
|
||||
i += 2
|
||||
except (ValueError, IndexError):
|
||||
break
|
||||
|
||||
if options:
|
||||
# Нормализуем вероятности (на случай, если они не суммируются в 1)
|
||||
total = sum(prob for prob, label in options)
|
||||
if total > 0:
|
||||
rand = random.random() * total
|
||||
cumulative = 0
|
||||
for prob, label in options:
|
||||
cumulative += prob
|
||||
if rand <= cumulative:
|
||||
# Сохраняем текущую позицию для возврата
|
||||
self.call_stack.append(self.current_line + 1)
|
||||
# Переходим к выбранной подпрограмме
|
||||
self.jump_to_label(label)
|
||||
break
|
||||
return True
|
||||
|
||||
# Возврат из подпрограммы
|
||||
elif command == "return":
|
||||
if self.call_stack:
|
||||
self.current_line = self.call_stack.pop()
|
||||
else:
|
||||
print("Ошибка: стек вызовов пуст!")
|
||||
return True
|
||||
|
||||
# Установка переменной
|
||||
elif command.startswith("set "):
|
||||
parts = command[4:].split("|")
|
||||
for part in parts:
|
||||
part = part.strip()
|
||||
if "+=" in part:
|
||||
var, val = part.split("+=", 1)
|
||||
var = var.strip()
|
||||
current = int(self.variables.get(var, 0))
|
||||
self.variables[var] = str(current + int(val.strip()))
|
||||
elif "-=" in part:
|
||||
var, val = part.split("-=", 1)
|
||||
var = var.strip()
|
||||
current = int(self.variables.get(var, 0))
|
||||
self.variables[var] = str(current - int(val.strip()))
|
||||
elif "=" in part:
|
||||
var, val = part.split("=", 1)
|
||||
var = var.strip()
|
||||
val = val.strip()
|
||||
|
||||
# Обработка инкремента (++var)
|
||||
if val.startswith("++"):
|
||||
var_to_inc = val[2:]
|
||||
self.variables[var] = str(int(self.variables.get(var_to_inc, 0)) + 1)
|
||||
# Обработка декремента (--var)
|
||||
elif val.startswith("--"):
|
||||
var_to_dec = val[2:]
|
||||
self.variables[var] = str(int(self.variables.get(var_to_dec, 0)) - 1)
|
||||
# Обычное присваивание
|
||||
else:
|
||||
self.variables[var] = val
|
||||
print(f"После выполнения '{command}': {self.variables}") # Отладочный вывод
|
||||
return True
|
||||
|
||||
# Условие
|
||||
elif command.startswith("if "):
|
||||
# ИСПРАВЛЕННЫЙ regex с обработкой пробелов
|
||||
match = re.match(r'if\s+(\w+)\s*([=!<>]+)\s*(.+?)\s+then\s+goto\s+(\w+)', command)
|
||||
if match:
|
||||
var_name, op, value, label = match.groups()
|
||||
current_value = self.variables.get(var_name, "0")
|
||||
|
||||
print(f"Отладка: if {var_name}({current_value}) {op} {value} then goto {label}") # Отладка
|
||||
|
||||
# Пробуем численное сравнение
|
||||
try:
|
||||
current_num = float(current_value)
|
||||
value_num = float(value)
|
||||
if op == ">": condition_met = current_num > value_num
|
||||
elif op == "<": condition_met = current_num < value_num
|
||||
elif op == ">=": condition_met = current_num >= value_num
|
||||
elif op == "<=": condition_met = current_num <= value_num
|
||||
elif op == "==": condition_met = current_num == value_num
|
||||
elif op == "!=": condition_met = current_num != value_num
|
||||
else: condition_met = False
|
||||
except ValueError:
|
||||
# Строковое сравнение
|
||||
if op == ">": condition_met = current_value > value
|
||||
elif op == "<": condition_met = current_value < value
|
||||
elif op == ">=": condition_met = current_value >= value
|
||||
elif op == "<=": condition_met = current_value <= value
|
||||
elif op == "==": condition_met = current_value == value
|
||||
elif op == "!=": condition_met = current_value != value
|
||||
else: condition_met = False
|
||||
|
||||
if condition_met:
|
||||
print(f"Условие выполнено, переход к {label}") # Отладка
|
||||
self.jump_to_label(label)
|
||||
else:
|
||||
print("Условие не выполнено") # Отладка
|
||||
return True
|
||||
|
||||
# Метка
|
||||
elif command.startswith("label "):
|
||||
return True
|
||||
|
||||
# Подпрограмма
|
||||
elif command.startswith("sub_"):
|
||||
if not self.call_stack or self.call_stack[-1] != self.current_line:
|
||||
self.call_stack.append(self.current_line)
|
||||
return True
|
||||
|
||||
# Возврат из подпрограммы
|
||||
elif command == "return":
|
||||
if not self.call_stack:
|
||||
print("Предупреждение: return без вызова подпрограммы, пропускаем")
|
||||
self.current_line += 1
|
||||
else:
|
||||
self.current_line = self.call_stack.pop() + 1 # Возвращаемся на следующую строку
|
||||
return True
|
||||
|
||||
# Переход
|
||||
elif command.startswith("goto "):
|
||||
label = command[5:].strip()
|
||||
self.jump_to_label(label)
|
||||
return True
|
||||
|
||||
# Фон
|
||||
elif command.startswith("bg "):
|
||||
bg_color = command[3:].strip().lower()
|
||||
if bg_color == "black":
|
||||
self.background = BLACK
|
||||
self.special_background = None
|
||||
elif bg_color == "white":
|
||||
self.background = WHITE
|
||||
self.special_background = None
|
||||
elif bg_color == "blue":
|
||||
self.background = LIGHT_BLUE
|
||||
self.special_background = None
|
||||
elif bg_color == "night":
|
||||
self.special_background = "night"
|
||||
elif bg_color == "morning":
|
||||
self.special_background = "morning"
|
||||
elif bg_color == "day":
|
||||
self.special_background = "day"
|
||||
elif bg_color == "evening" or bg_color == "sunset":
|
||||
self.special_background = "evening"
|
||||
else:
|
||||
self.background = GRAY
|
||||
self.special_background = None
|
||||
return True
|
||||
|
||||
# Персонаж
|
||||
elif command.startswith("char "):
|
||||
return self.parse_character(command)
|
||||
|
||||
return False
|
||||
|
||||
def draw_descriptions(self, text, x, y, font_obj, color):
|
||||
"""Отрисовка текста с учётом звёздочек"""
|
||||
if text.startswith("*") and text.endswith("*"):
|
||||
# Удаляем звёздочки и рисуем курсивом
|
||||
self.draw_text(text[1:-1], x, y, italic_font, (150, 150, 150))
|
||||
else:
|
||||
self.draw_text(text, x, y, font_obj, color)
|
||||
|
||||
def jump_to_label(self, label):
|
||||
"""Переход к метке в скрипте"""
|
||||
for i, line in enumerate(self.script):
|
||||
if line.startswith(f"@label {label}") or line.startswith(f"@{label}"):
|
||||
self.current_line = i
|
||||
return
|
||||
print(f"Метка '{label}' не найдена!")
|
||||
|
||||
def process_choices(self, line):
|
||||
"""Обработка строки с выбором"""
|
||||
if line.startswith("choice "):
|
||||
choices_text = line[7:].split("|")
|
||||
self.choices = []
|
||||
|
||||
for choice_text in choices_text:
|
||||
parts = choice_text.split("=>")
|
||||
if len(parts) == 2:
|
||||
choice_display = parts[0].strip()
|
||||
choice_action = parts[1].strip()
|
||||
self.choices.append((choice_display, choice_action))
|
||||
|
||||
self.waiting_for_choice = True
|
||||
return True
|
||||
return False
|
||||
|
||||
def substitute_variables(self, text):
|
||||
"""Подстановка переменных в текст"""
|
||||
for var_name, var_value in self.variables.items():
|
||||
text = text.replace(f"{{{var_name}}}", str(var_value))
|
||||
return text
|
||||
|
||||
def get_character_info(self, char_id):
|
||||
"""Возвращает полное описание персонажа"""
|
||||
# Убираем вывод "Персонаж none" для несуществующих ID
|
||||
if char_id not in self.characters:
|
||||
return "", "" # Возвращаем пустые строки
|
||||
|
||||
char = self.characters.get(char_id)
|
||||
if isinstance(char, str):
|
||||
return char, ""
|
||||
|
||||
details = []
|
||||
if char.get("appearance"): details.append(char['appearance'])
|
||||
if char.get("details"): details.append(char['details'])
|
||||
if char.get("scent"): details.append(char['scent'])
|
||||
|
||||
return char.get("name", ""), " | ".join(details)
|
||||
|
||||
def process_line(self):
|
||||
if self.current_line >= len(self.script):
|
||||
self.running = False
|
||||
return None
|
||||
|
||||
line = self.script[self.current_line]
|
||||
line = self.substitute_variables(line)
|
||||
|
||||
# Обработка команд
|
||||
if line.startswith("@"):
|
||||
if self.parse_command(line[1:]):
|
||||
self.current_line += 1
|
||||
return self.process_line()
|
||||
return None
|
||||
|
||||
# Обработка выбора
|
||||
if self.process_choices(line):
|
||||
self.current_line += 1
|
||||
return None
|
||||
|
||||
# Обработка диалога с проверкой разделителя
|
||||
if ":" in line and not line.startswith("*"):
|
||||
parts = line.split(":", 1)
|
||||
if len(parts) == 2:
|
||||
char_id = parts[0].strip()
|
||||
text = parts[1].strip()
|
||||
return char_id, text
|
||||
|
||||
# Обработка описания или простого текста
|
||||
return None, line
|
||||
|
||||
def make_choice(self, choice_index):
|
||||
"""Обработка выбора игрока"""
|
||||
if 0 <= choice_index < len(self.choices):
|
||||
choice_action = self.choices[choice_index][1]
|
||||
|
||||
if choice_action.startswith("goto "):
|
||||
label = choice_action[5:].strip()
|
||||
self.jump_to_label(label)
|
||||
elif "+=" in choice_action:
|
||||
var, value = choice_action.split("+=", 1)
|
||||
var = var.strip()
|
||||
self.variables[var] = str(int(self.variables.get(var, 0)) + int(value.strip()))
|
||||
self.current_line += 1
|
||||
elif "-=" in choice_action:
|
||||
var, value = choice_action.split("-=", 1)
|
||||
var = var.strip()
|
||||
self.variables[var] = str(int(self.variables.get(var, 0)) - int(value.strip()))
|
||||
self.current_line += 1
|
||||
elif "=" in choice_action:
|
||||
var, value = choice_action.split("=", 1)
|
||||
self.variables[var.strip()] = value.strip()
|
||||
self.current_line += 1
|
||||
|
||||
self.choices = []
|
||||
self.waiting_for_choice = False
|
||||
|
||||
def next_line(self):
|
||||
"""Переход к следующей строке"""
|
||||
self.current_line += 1
|
||||
if self.current_line >= len(self.script):
|
||||
self.running = False
|
||||
|
||||
def draw_text_box(self):
|
||||
"""Отрисовка текстового поля"""
|
||||
pygame.draw.rect(screen, WHITE, self.text_box_rect)
|
||||
pygame.draw.rect(screen, DARK_BLUE, self.text_box_rect, 3)
|
||||
|
||||
def draw_choice_box(self):
|
||||
"""Отрисовка поля выбора"""
|
||||
# Создаем полупрозрачную поверхность
|
||||
s = pygame.Surface((600, 300), pygame.SRCALPHA)
|
||||
s.fill((240, 240, 255, 230)) # Последний параметр - прозрачность
|
||||
screen.blit(s, (self.choice_box_rect.x, self.choice_box_rect.y))
|
||||
pygame.draw.rect(screen, DARK_BLUE, self.choice_box_rect, 3)
|
||||
|
||||
def draw_text(self, text, x, y, font_obj, color=BLACK, max_width=None):
|
||||
"""Отрисовка текста с переносом строк"""
|
||||
if max_width is None:
|
||||
max_width = self.text_box_rect.width - 40
|
||||
|
||||
words = text.split(' ')
|
||||
space_width = font_obj.size(' ')[0]
|
||||
line_height = font_obj.get_height()
|
||||
|
||||
current_line = []
|
||||
current_width = 0
|
||||
|
||||
for word in words:
|
||||
word_surface = font_obj.render(word, True, color)
|
||||
word_width = word_surface.get_width()
|
||||
|
||||
if current_width + word_width <= max_width:
|
||||
current_line.append(word)
|
||||
current_width += word_width + space_width
|
||||
else:
|
||||
line_text = ' '.join(current_line)
|
||||
line_surface = font_obj.render(line_text, True, color)
|
||||
screen.blit(line_surface, (x, y))
|
||||
y += line_height
|
||||
current_line = [word]
|
||||
current_width = word_width + space_width
|
||||
|
||||
if current_line:
|
||||
line_text = ' '.join(current_line)
|
||||
line_surface = font_obj.render(line_text, True, color)
|
||||
screen.blit(line_surface, (x, y))
|
||||
|
||||
def draw(self):
|
||||
"""Отрисовка текущего состояния"""
|
||||
# Фон
|
||||
if self.special_background and self.special_background in self.background_textures:
|
||||
screen.blit(self.background_textures[self.special_background], (0, 0))
|
||||
elif self.background:
|
||||
screen.fill(self.background)
|
||||
else:
|
||||
screen.fill(LIGHT_BLUE)
|
||||
|
||||
# Текстовое поле (убираем очистку перед выбором)
|
||||
if not self.waiting_for_choice:
|
||||
self.draw_text_box()
|
||||
|
||||
# Текст
|
||||
line_data = self.process_line()
|
||||
x = self.text_box_rect.x + 20
|
||||
y = self.text_box_rect.y + 20
|
||||
|
||||
if line_data and not self.waiting_for_choice:
|
||||
if isinstance(line_data, tuple): # Диалог с персонажем
|
||||
char_id, text = line_data
|
||||
name, details = self.get_character_info(char_id)
|
||||
|
||||
# Отрисовываем имя только если оно есть
|
||||
if name:
|
||||
name_surface = character_font.render(name + ":", True, DARK_BLUE)
|
||||
screen.blit(name_surface, (x, y))
|
||||
y += 40
|
||||
|
||||
# Отрисовка текста персонажа
|
||||
self.draw_text(text, x, y, font, BLACK)
|
||||
y += 60
|
||||
|
||||
# Отрисовка деталей персонажа (если есть)
|
||||
if details:
|
||||
details_surface = small_font.render(details, True, (100, 100, 100))
|
||||
screen.blit(details_surface, (x, y))
|
||||
else: # Описание или простой текст
|
||||
_, text = line_data
|
||||
self.draw_descriptions(text, x, y, font, BLACK)
|
||||
|
||||
# Варианты выбора
|
||||
if self.waiting_for_choice and self.choices:
|
||||
self.draw_choice_box()
|
||||
choice_y = self.choice_box_rect.y + 30
|
||||
for i, (choice_text, _) in enumerate(self.choices):
|
||||
choice_surface = choice_font.render(f"{i+1}. {choice_text}", True, BLACK)
|
||||
screen.blit(choice_surface, (self.choice_box_rect.x + 30, choice_y))
|
||||
choice_y += 40
|
||||
|
||||
# Индикатор продолжения
|
||||
if self.running and not self.waiting_for_choice and line_data:
|
||||
indicator = font.render("> ПРОБЕЛ", True, DARK_BLUE)
|
||||
screen.blit(indicator, (WIDTH - 200, HEIGHT - 50))
|
||||
|
||||
pygame.display.flip()
|
||||
|
||||
def run(self):
|
||||
"""Основной цикл"""
|
||||
clock = pygame.time.Clock()
|
||||
|
||||
while self.running:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
elif event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_SPACE and not self.waiting_for_choice:
|
||||
self.next_line()
|
||||
elif event.key == pygame.K_1 and self.waiting_for_choice and len(self.choices) >= 1:
|
||||
self.make_choice(0)
|
||||
elif event.key == pygame.K_2 and self.waiting_for_choice and len(self.choices) >= 2:
|
||||
self.make_choice(1)
|
||||
elif event.key == pygame.K_3 and self.waiting_for_choice and len(self.choices) >= 3:
|
||||
self.make_choice(2)
|
||||
elif event.key == pygame.K_4 and self.waiting_for_choice and len(self.choices) >= 4:
|
||||
self.make_choice(3)
|
||||
elif event.key == pygame.K_ESCAPE:
|
||||
self.running = False
|
||||
|
||||
self.draw()
|
||||
clock.tick(60)
|
||||
|
||||
# Создаем экземпляр движка и запускаем
|
||||
if __name__ == "__main__":
|
||||
engine = NovelEngine()
|
||||
|
||||
if engine.load_script("island_script.txt"):
|
||||
engine.run()
|
||||
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
188
tet/pong.py
Normal file
188
tet/pong.py
Normal file
@@ -0,0 +1,188 @@
|
||||
import pygame
|
||||
import sys
|
||||
import random
|
||||
import math
|
||||
|
||||
# Инициализация Pygame
|
||||
pygame.init()
|
||||
|
||||
# Константы
|
||||
WIDTH, HEIGHT = 1920, 1080
|
||||
PADDLE_WIDTH, PADDLE_HEIGHT = 30, 200
|
||||
BALL_SIZE = 30
|
||||
WHITE = (255, 255, 255)
|
||||
BLACK = (0, 0, 0)
|
||||
GREEN = (50, 168, 82)
|
||||
LIGHT_GREEN = (100, 200, 100)
|
||||
FPS = 120 # Увеличили FPS для плавности на высоких скоростях
|
||||
|
||||
# Создание окна
|
||||
screen = pygame.display.set_mode((WIDTH, HEIGHT))
|
||||
pygame.display.set_caption("Pong - ХЕРАЧЬ Edition v2.0")
|
||||
clock = pygame.time.Clock()
|
||||
|
||||
# Загрузка шрифтов
|
||||
try:
|
||||
pixel_font = pygame.font.Font("PressStart2P-Regular.ttf", 120)
|
||||
except:
|
||||
pixel_font = pygame.font.SysFont('arial', 120)
|
||||
|
||||
# Игровые объекты
|
||||
player_paddle = pygame.Rect(100, HEIGHT // 2 - PADDLE_HEIGHT // 2, PADDLE_WIDTH, PADDLE_HEIGHT)
|
||||
ai_paddle = pygame.Rect(WIDTH - 100 - PADDLE_WIDTH, HEIGHT // 2 - PADDLE_HEIGHT // 2, PADDLE_WIDTH, PADDLE_HEIGHT)
|
||||
ball = pygame.Rect(WIDTH // 2 - BALL_SIZE // 2, HEIGHT // 2 - BALL_SIZE // 2, BALL_SIZE, BALL_SIZE)
|
||||
|
||||
# Физика мяча
|
||||
base_speed = 3
|
||||
max_speed = 35 # Увеличили максимальную скорость
|
||||
speed_increase = 0.7 # Увеличили ускорение после удара
|
||||
ball_speed = base_speed
|
||||
ball_dx = ball_speed * random.choice((1, -1))
|
||||
ball_dy = ball_speed * random.choice((1, -1))
|
||||
|
||||
# Скорости
|
||||
player_speed = 0
|
||||
ai_speed = 0
|
||||
|
||||
# Счет
|
||||
player_score = 0
|
||||
ai_score = 0
|
||||
font = pygame.font.Font(None, 72)
|
||||
|
||||
def draw_field():
|
||||
"""Рисует поле с разметкой"""
|
||||
screen.fill(GREEN)
|
||||
|
||||
# Полупрозрачная надпись
|
||||
text_surface = pixel_font.render("ХЕРАЧЬ!", True, LIGHT_GREEN)
|
||||
text_surface.set_alpha(30)
|
||||
screen.blit(text_surface, (WIDTH//2 - text_surface.get_width()//2, HEIGHT//2 - text_surface.get_height()//2))
|
||||
|
||||
# Разметка
|
||||
for y in range(0, HEIGHT, 40):
|
||||
pygame.draw.rect(screen, WHITE, (WIDTH // 2 - 5, y, 10, 20))
|
||||
pygame.draw.rect(screen, WHITE, (0, 0, WIDTH, HEIGHT), 10)
|
||||
|
||||
def reset_ball():
|
||||
"""Сбрасывает мяч в центр"""
|
||||
global ball_speed, ball_dx, ball_dy
|
||||
ball.center = (WIDTH // 2, HEIGHT // 2)
|
||||
ball_speed = base_speed
|
||||
angle = math.radians(random.uniform(30, 60))
|
||||
direction = random.choice((-1, 1))
|
||||
ball_dx = direction * ball_speed * math.cos(angle)
|
||||
ball_dy = ball_speed * math.sin(angle) * random.choice((-1, 1))
|
||||
|
||||
def handle_collision():
|
||||
"""Обрабатывает все столкновения"""
|
||||
global ball_dx, ball_dy, ball_speed
|
||||
|
||||
# Жесткие границы (мяч не может вылететь)
|
||||
if ball.top < 0:
|
||||
ball.top = 0
|
||||
ball_dy = abs(ball_dy) * 0.95 # Небольшая потеря энергии
|
||||
if ball.bottom > HEIGHT:
|
||||
ball.bottom = HEIGHT
|
||||
ball_dy = -abs(ball_dy) * 0.95
|
||||
|
||||
# Обработка ударов о ракетки с более сильным ускорением
|
||||
if ball.colliderect(player_paddle) and ball_dx < 0:
|
||||
# Сильное ускорение + случайный угол
|
||||
relative_intersect = (player_paddle.centery - ball.centery) / (PADDLE_HEIGHT / 2)
|
||||
ball_dx = abs(ball_dx) * 1.25 # Увеличили ускорение
|
||||
ball_dy = -relative_intersect * ball_speed * 2
|
||||
# Добавляем случайности
|
||||
ball_dx += random.uniform(-0.5, 0.5)
|
||||
ball_dy += random.uniform(-1, 1)
|
||||
|
||||
elif ball.colliderect(ai_paddle) and ball_dx > 0:
|
||||
relative_intersect = (ai_paddle.centery - ball.centery) / (PADDLE_HEIGHT / 2)
|
||||
ball_dx = -abs(ball_dx) * 1.25
|
||||
ball_dy = -relative_intersect * ball_speed * 2
|
||||
ball_dx += random.uniform(-0.5, 0.5)
|
||||
ball_dy += random.uniform(-1, 1)
|
||||
|
||||
# Нормализация скорости
|
||||
speed = math.sqrt(ball_dx**2 + ball_dy**2)
|
||||
if speed > max_speed:
|
||||
ball_dx = (ball_dx / speed) * max_speed
|
||||
ball_dy = (ball_dy / speed) * max_speed
|
||||
ball_speed = speed
|
||||
|
||||
def ai_movement():
|
||||
"""Упрощенный ИИ с ошибками"""
|
||||
global ai_speed
|
||||
|
||||
# Простое следование за мячом с задержкой и ошибками
|
||||
target_y = ball.centery + random.uniform(-50, 50) # Добавляем ошибку
|
||||
|
||||
# Замедляем ИИ на высоких скоростях
|
||||
reaction_speed = max(3, 8 - ball_speed * 0.2)
|
||||
|
||||
if ai_paddle.centery < target_y:
|
||||
ai_speed = min(reaction_speed, target_y - ai_paddle.centery)
|
||||
else:
|
||||
ai_speed = max(-reaction_speed, target_y - ai_paddle.centery)
|
||||
|
||||
ai_paddle.y += ai_speed
|
||||
|
||||
# Границы
|
||||
if ai_paddle.top < 0:
|
||||
ai_paddle.top = 0
|
||||
if ai_paddle.bottom > HEIGHT:
|
||||
ai_paddle.bottom = HEIGHT
|
||||
|
||||
# Основной цикл
|
||||
while True:
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_DOWN:
|
||||
player_speed = 15
|
||||
if event.key == pygame.K_UP:
|
||||
player_speed = -15
|
||||
if event.key == pygame.K_ESCAPE:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
if event.type == pygame.KEYUP:
|
||||
if event.key == pygame.K_DOWN or event.key == pygame.K_UP:
|
||||
player_speed = 0
|
||||
|
||||
# Движение
|
||||
player_paddle.y += player_speed
|
||||
if player_paddle.top < 0:
|
||||
player_paddle.top = 0
|
||||
if player_paddle.bottom > HEIGHT:
|
||||
player_paddle.bottom = HEIGHT
|
||||
|
||||
ai_movement()
|
||||
|
||||
# Физика мяча
|
||||
ball.x += ball_dx
|
||||
ball.y += ball_dy
|
||||
handle_collision()
|
||||
|
||||
# Голы
|
||||
if ball.left <= 0:
|
||||
ai_score += 1
|
||||
reset_ball()
|
||||
if ball.right >= WIDTH:
|
||||
player_score += 1
|
||||
base_speed = min(15, base_speed + speed_increase)
|
||||
reset_ball()
|
||||
|
||||
# Отрисовка
|
||||
draw_field()
|
||||
pygame.draw.rect(screen, WHITE, player_paddle, border_radius=10)
|
||||
pygame.draw.rect(screen, WHITE, ai_paddle, border_radius=10)
|
||||
pygame.draw.ellipse(screen, WHITE, ball)
|
||||
|
||||
# Счет
|
||||
screen.blit(font.render(f"{player_score}", True, WHITE), (WIDTH//4, 50))
|
||||
screen.blit(font.render(f"{ai_score}", True, WHITE), (3*WIDTH//4 - 50, 50))
|
||||
|
||||
pygame.display.flip()
|
||||
clock.tick(FPS)
|
||||
256
tet/tetris.py
Normal file
256
tet/tetris.py
Normal file
@@ -0,0 +1,256 @@
|
||||
import pygame
|
||||
import random
|
||||
|
||||
# Инициализация pygame
|
||||
pygame.init()
|
||||
|
||||
# Цвета
|
||||
BLACK = (0, 0, 0)
|
||||
WHITE = (255, 255, 255)
|
||||
GRAY = (70, 70, 70)
|
||||
COLORS = [
|
||||
(0, 255, 255), # I - голубой
|
||||
(0, 0, 255), # J - синий
|
||||
(255, 165, 0), # L - оранжевый
|
||||
(255, 255, 0), # O - желтый
|
||||
(0, 255, 0), # S - зеленый
|
||||
(128, 0, 128), # T - фиолетовый
|
||||
(255, 0, 0) # Z - красный
|
||||
]
|
||||
|
||||
# Настройки игры
|
||||
BLOCK_SIZE = 30
|
||||
GRID_WIDTH = 10
|
||||
GRID_HEIGHT = 20
|
||||
SCREEN_WIDTH = BLOCK_SIZE * (GRID_WIDTH + 6)
|
||||
SCREEN_HEIGHT = BLOCK_SIZE * GRID_HEIGHT
|
||||
GAME_AREA_LEFT = BLOCK_SIZE
|
||||
|
||||
# Фигуры тетрамино
|
||||
SHAPES = [
|
||||
[[1, 1, 1, 1]], # I
|
||||
[[1, 0, 0], [1, 1, 1]], # J
|
||||
[[0, 0, 1], [1, 1, 1]], # L
|
||||
[[1, 1], [1, 1]], # O
|
||||
[[0, 1, 1], [1, 1, 0]], # S
|
||||
[[0, 1, 0], [1, 1, 1]], # T
|
||||
[[1, 1, 0], [0, 1, 1]] # Z
|
||||
]
|
||||
|
||||
# Создание экрана
|
||||
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
|
||||
pygame.display.set_caption("Тетрис")
|
||||
|
||||
# Часы для управления FPS
|
||||
clock = pygame.time.Clock()
|
||||
|
||||
class Tetris:
|
||||
def __init__(self):
|
||||
self.grid = [[0 for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]
|
||||
self.current_piece = self.new_piece()
|
||||
self.game_over = False
|
||||
self.score = 0
|
||||
self.level = 1
|
||||
self.fall_speed = 0.5 # секунды между падением на 1 клетку
|
||||
self.fall_time = 0
|
||||
|
||||
def new_piece(self):
|
||||
# Выбираем случайную фигуру
|
||||
shape = random.choice(SHAPES)
|
||||
color = COLORS[SHAPES.index(shape)]
|
||||
|
||||
# Начальная позиция (по центру сверху)
|
||||
x = GRID_WIDTH // 2 - len(shape[0]) // 2
|
||||
y = 0
|
||||
|
||||
return {"shape": shape, "color": color, "x": x, "y": y}
|
||||
|
||||
def valid_move(self, piece, x_offset=0, y_offset=0):
|
||||
for y, row in enumerate(piece["shape"]):
|
||||
for x, cell in enumerate(row):
|
||||
if cell:
|
||||
new_x = piece["x"] + x + x_offset
|
||||
new_y = piece["y"] + y + y_offset
|
||||
|
||||
if (new_x < 0 or new_x >= GRID_WIDTH or
|
||||
new_y >= GRID_HEIGHT or
|
||||
(new_y >= 0 and self.grid[new_y][new_x])):
|
||||
return False
|
||||
return True
|
||||
|
||||
def rotate_piece(self):
|
||||
# Получаем текущую фигуру
|
||||
piece = self.current_piece
|
||||
# Транспонируем матрицу фигуры (поворот на 90 градусов)
|
||||
rotated = [[piece["shape"][y][x] for y in range(len(piece["shape"])-1, -1, -1)]
|
||||
for x in range(len(piece["shape"][0]))]
|
||||
|
||||
old_shape = piece["shape"]
|
||||
piece["shape"] = rotated
|
||||
|
||||
# Если после поворота фигура выходит за границы или пересекается с другими блоками,
|
||||
# отменяем поворот
|
||||
if not self.valid_move(piece):
|
||||
piece["shape"] = old_shape
|
||||
|
||||
def lock_piece(self):
|
||||
piece = self.current_piece
|
||||
for y, row in enumerate(piece["shape"]):
|
||||
for x, cell in enumerate(row):
|
||||
if cell:
|
||||
# Проверяем, не выходит ли фигура за верхнюю границу (игра окончена)
|
||||
if piece["y"] + y < 0:
|
||||
self.game_over = True
|
||||
else:
|
||||
self.grid[piece["y"] + y][piece["x"] + x] = piece["color"]
|
||||
|
||||
# Проверяем заполненные линии
|
||||
self.clear_lines()
|
||||
# Создаем новую фигуру
|
||||
self.current_piece = self.new_piece()
|
||||
|
||||
# Если новая фигура сразу не может разместиться, игра окончена
|
||||
if not self.valid_move(self.current_piece):
|
||||
self.game_over = True
|
||||
|
||||
def clear_lines(self):
|
||||
lines_cleared = 0
|
||||
for y in range(GRID_HEIGHT):
|
||||
if all(self.grid[y]):
|
||||
lines_cleared += 1
|
||||
# Удаляем линию и сдвигаем все вышележащие линии вниз
|
||||
for y2 in range(y, 0, -1):
|
||||
self.grid[y2] = self.grid[y2-1][:]
|
||||
self.grid[0] = [0 for _ in range(GRID_WIDTH)]
|
||||
|
||||
# Обновляем счет
|
||||
if lines_cleared == 1:
|
||||
self.score += 100 * self.level
|
||||
elif lines_cleared == 2:
|
||||
self.score += 300 * self.level
|
||||
elif lines_cleared == 3:
|
||||
self.score += 500 * self.level
|
||||
elif lines_cleared == 4:
|
||||
self.score += 800 * self.level
|
||||
|
||||
# Обновляем уровень (каждые 10 линий)
|
||||
self.level = 1 + self.score // 1000
|
||||
# Увеличиваем скорость (максимум 0.05 секунды между падениями)
|
||||
self.fall_speed = max(0.05, 0.5 - (self.level - 1) * 0.05)
|
||||
|
||||
def update(self, delta_time):
|
||||
if self.game_over:
|
||||
return
|
||||
|
||||
self.fall_time += delta_time
|
||||
|
||||
# Автоматическое падение фигуры
|
||||
if self.fall_time >= self.fall_speed:
|
||||
self.fall_time = 0
|
||||
if self.valid_move(self.current_piece, 0, 1):
|
||||
self.current_piece["y"] += 1
|
||||
else:
|
||||
self.lock_piece()
|
||||
|
||||
def draw(self):
|
||||
# Очищаем экран
|
||||
screen.fill(BLACK)
|
||||
|
||||
# Рисуем игровую область
|
||||
pygame.draw.rect(screen, WHITE, (GAME_AREA_LEFT, 0, BLOCK_SIZE * GRID_WIDTH, SCREEN_HEIGHT), 1)
|
||||
|
||||
# Рисуем сетку
|
||||
for x in range(GRID_WIDTH):
|
||||
for y in range(GRID_HEIGHT):
|
||||
pygame.draw.rect(screen, GRAY,
|
||||
(GAME_AREA_LEFT + x * BLOCK_SIZE, y * BLOCK_SIZE,
|
||||
BLOCK_SIZE, BLOCK_SIZE), 1)
|
||||
|
||||
# Рисуем статичные блоки
|
||||
for y in range(GRID_HEIGHT):
|
||||
for x in range(GRID_WIDTH):
|
||||
if self.grid[y][x]:
|
||||
pygame.draw.rect(screen, self.grid[y][x],
|
||||
(GAME_AREA_LEFT + x * BLOCK_SIZE, y * BLOCK_SIZE,
|
||||
BLOCK_SIZE, BLOCK_SIZE))
|
||||
pygame.draw.rect(screen, WHITE,
|
||||
(GAME_AREA_LEFT + x * BLOCK_SIZE, y * BLOCK_SIZE,
|
||||
BLOCK_SIZE, BLOCK_SIZE), 1)
|
||||
|
||||
# Рисуем текущую фигуру
|
||||
if not self.game_over:
|
||||
piece = self.current_piece
|
||||
for y, row in enumerate(piece["shape"]):
|
||||
for x, cell in enumerate(row):
|
||||
if cell:
|
||||
pygame.draw.rect(screen, piece["color"],
|
||||
(GAME_AREA_LEFT + (piece["x"] + x) * BLOCK_SIZE,
|
||||
(piece["y"] + y) * BLOCK_SIZE,
|
||||
BLOCK_SIZE, BLOCK_SIZE))
|
||||
pygame.draw.rect(screen, WHITE,
|
||||
(GAME_AREA_LEFT + (piece["x"] + x) * BLOCK_SIZE,
|
||||
(piece["y"] + y) * BLOCK_SIZE,
|
||||
BLOCK_SIZE, BLOCK_SIZE), 1)
|
||||
|
||||
# Рисуем информацию (счет, уровень)
|
||||
font = pygame.font.SysFont(None, 36)
|
||||
score_text = font.render(f"Счет: {self.score}", True, WHITE)
|
||||
level_text = font.render(f"Уровень: {self.level}", True, WHITE)
|
||||
screen.blit(score_text, (GAME_AREA_LEFT + GRID_WIDTH * BLOCK_SIZE + 10, 30))
|
||||
screen.blit(level_text, (GAME_AREA_LEFT + GRID_WIDTH * BLOCK_SIZE + 10, 70))
|
||||
|
||||
# Если игра окончена, выводим сообщение
|
||||
if self.game_over:
|
||||
game_over_font = pygame.font.SysFont(None, 48)
|
||||
game_over_text = game_over_font.render("Игра Окончена!", True, (255, 0, 0))
|
||||
screen.blit(game_over_text, (GAME_AREA_LEFT + GRID_WIDTH * BLOCK_SIZE // 2 - 100,
|
||||
SCREEN_HEIGHT // 2 - 24))
|
||||
|
||||
# Создаем экземпляр игры
|
||||
game = Tetris()
|
||||
|
||||
# Основной игровой цикл
|
||||
running = True
|
||||
last_time = pygame.time.get_ticks()
|
||||
|
||||
while running:
|
||||
# Управление FPS
|
||||
current_time = pygame.time.get_ticks()
|
||||
delta_time = (current_time - last_time) / 1000.0 # Конвертируем в секунды
|
||||
last_time = current_time
|
||||
|
||||
# Обработка событий
|
||||
for event in pygame.event.get():
|
||||
if event.type == pygame.QUIT:
|
||||
running = False
|
||||
|
||||
if not game.game_over:
|
||||
if event.type == pygame.KEYDOWN:
|
||||
if event.key == pygame.K_LEFT:
|
||||
if game.valid_move(game.current_piece, -1, 0):
|
||||
game.current_piece["x"] -= 1
|
||||
elif event.key == pygame.K_RIGHT:
|
||||
if game.valid_move(game.current_piece, 1, 0):
|
||||
game.current_piece["x"] += 1
|
||||
elif event.key == pygame.K_DOWN:
|
||||
if game.valid_move(game.current_piece, 0, 1):
|
||||
game.current_piece["y"] += 1
|
||||
elif event.key == pygame.K_UP:
|
||||
game.rotate_piece()
|
||||
elif event.key == pygame.K_SPACE:
|
||||
# Мгновенное падение
|
||||
while game.valid_move(game.current_piece, 0, 1):
|
||||
game.current_piece["y"] += 1
|
||||
game.lock_piece()
|
||||
|
||||
# Обновление игры
|
||||
game.update(delta_time)
|
||||
|
||||
# Отрисовка
|
||||
game.draw()
|
||||
pygame.display.flip()
|
||||
|
||||
# Ограничиваем FPS
|
||||
clock.tick(60)
|
||||
|
||||
pygame.quit()
|
||||
Reference in New Issue
Block a user