This commit is contained in:
sShemet
2025-12-22 14:03:10 +05:00
commit ade2833df7
74 changed files with 42924 additions and 0 deletions

12
tet/camera.reg Normal file
View 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
View File

@@ -0,0 +1,3 @@
Windows Registry Editor Version 5.00
[-HKEY_CLASSES_ROOT\camera]

202
tet/island_script.txt Normal file
View 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
View 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
View 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

View 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
View 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
View 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
View 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()