Меня зовут Светлана, я занимаюсь разработкой в компании Бука. Хочу поделиться с вами историей о том, как мы портировали игру.
Как всё начиналось
Многие наверняка помнят золотую эру квестов, которая пришлась на 90-е годы прошлого века. А некоторые из тех, кто постарше, даже застали лучших представителей жанра: King’s Quest, Space Quest, Myst, Broken Sword, Grim Fandango, Monkey Island.
Российский геймдев тоже не сидел сложа руки: коробки с играми серий «Братья Пилоты» и «Петька и Василий Иванович» расхватывали с прилавков магазинов как горячие пирожки!
Старые игры с возрастом не становятся хуже, но запустить их с каждым годом всё сложнее. Поэтому, как и многие другие издательства, мы решили портировать одну из своих лучших игр на современные устройства. И в качестве пробы пера выбрали классический квест «Петька и Василий Иванович: Спасают Галактику».
Вкратце об игре
Это point-and-click анимационная юмористическая приключенческая игра, выпущенная в 1998 году. В ней рассказывается история 3-х героев Красной Армии. Действие происходит в деревне «Гадюкино», где рядовой Петр и комдив Василий Иванович Чапаев начинают поиски пропавшего красного знамени. А в это время инопланетяне пытаются завоевать мир.
В оригинальной игре было не очень простое управление, заточенное на использование мыши. У игрока широкий набор действий: смотреть, ходить, использовать, взять, говорить, применять предмет из инвентаря (чемодана) и отдельное действие вторым персонажем-помощником (Чапаевым). Прямо как в жизни.
Основная цель портирования — полноценная адаптация любимой классической игры под современные операционные системы и широкоформатные мониторы. Фанаты не первый год просят нас выпустить первые части “Петьки” в Steam, и вот, наконец, летом 2015 года мы взялись за работу.
Наследие
Как это часто случается, за многие годы, прошедшие с момента выхода оригинального “Петьки”, большая часть исходных материалов игры бесследно исчезла. Поэтому мы приступили к извлечению ресурсов из существующей дисковой версии. В итоге у нас на руках оказались следующие материалы:
- все звуки = 996 файлов wave PCM;
- вся музыка = 70 файлов wave PCM;
- все диалоги = 8479 файлов wave PCM;
- вся анимация = 2338 файлов формата .flc с разрешением 640 x 480 px
- все бэкграунды = 115 .bmp файла с разрешением 640 x 480 px
В общем и целом это 1,8 ГБ контента или около 10 часов игрового времени. Но у нас не было главного — исходного кода. Пришлось писать его практически с нуля.
Первые шаги
Перед нами встал вопрос выбора инструмента, на котором будем работать. Так как порт должен быть кроссплатформенным, мы сконцентрировались на поиске движка, отвечающего нашим требованиям. В итоге остановились на Unity3D в силу его распространенности, наличия хорошей документации и поддержки.
А так как игра состояла из множества заскриптованных сцен, средствами Unity3D мы начали проектировать фреймворк, который бы ускорил нашу работу и позволил быстро собирать 2D point-and-click квест.
Параллельно готовили документацию по сценам и материалы для портирования. Оригинальные фоны и анимации с разрешением 640px на 480px и соотношением сторон 4:3 были вручную дорисованы художниками под пропорцию 16:9.
Немного про управление
Мы решили добавить удобное современное управление, так как у оригинальной игры был высокий порог вхождения. Теперь игрок может в любой момент переключиться между ними прямо в меню настроек.
В упрощенном управлении — один вид указателя, который подсвечивает активные объекты для взаимодействия на сценах. Так игрок сможет сразу увидеть все спрятанные объекты на локации.
При нажатии на объект игрок увидит только те действия, которые может применить к данному объекту. Если в инвентаре появится предмет (например, Ключ), при нажатии на Замок в возможных действиях игрок увидит этот Ключ. Как результат, исчезнет необходимость заходить в инвентарь слишком часто. Теперь это нужно будет делать только для работы с самими предметами.
Кроме того, мы добавили визуальные указатели на местах возможных переходов между сценами и подумали о ситуациях, когда игрок уже не знает, что ему делать. В чемодане в упрощенном управлении добавился элемент Подсказок, который намекает, куда пользователю нужно двигаться дальше.
Поподробнее про фреймворк
Игровая сцена нашего анимационного 2D квеста включает в себя следующие составляющие:
— Общая информация о сцене:
- Фоновый звук на сцене.
- Фоновая мелодия на сцене.
- Фоновое изображение.
- Область ходьбы. Для перемещения ГГ по сцене используется несколько областей (коллайдеров):
- Основной коллайдер, в котором ГГ может ходить по сцене.
- Коллайдеры обхода объектов, если объект находится внутри основного коллайдера и ГГ должен его обходить.
— Масштаб: расставляются точки масштаба на сцене. Каждая точка имеет вес (процент масштаба ГГ) относительно которых рассчитывается масштаб ГГ.
— Объекты:
- У объекта на сцене есть набор событий на которые он должен реагировать:
- Смотреть.
- Говорить.
- Брать.
- Использовать.
- Давать(когда мы используем предмет из инвентаря на объект).
- Василий Иванович Чапаев.
- Реакция на определенные предметы из инвентаря.
- Локатор — координаты взаимодействия с предметом. Например, для перехода на другую сцену герои должны подойти к координате перехода.
- Анимированный объект, который может содержать в себе от 1 до N анимаций. В основные параметры анимаций входит:
- Количество кадров в секунду.
- Возможность пропустить анимацию, сделано для долгих катсцен, которые хотелось бы пропустить.
- Зацикленная ли анимация, например для разговора или непрерывно текущей реки.
- Спрайты, которые входят в анимацию.
- Аудиофайл, соответствующий анимации и проигрываемый с ней. Если в анимации есть аудиофайл, то анимация растягивается на длину аудиофайла.
- Порядок отображения объектов. Объекты на сцене необходимо сортировать от заднего плана к переднему. Для объектов, находящихся в центре сцены, которые должны сортироваться в зависимости от позиции ГГ, используется нижняя координата.
Помимо графического наполнения сцены есть еще и функциональная составляющая, где каждая сцена — это XAML документ. В нем используется небольшой самописный скриптовый язык. В документе описываются все объекты сцены и те действия, которые можно производить с объектом.
Пример документа:
<?xml version="1.0" encoding="utf-8"?>
<Root
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<StartScenario>
<item>
define ONE_ENTER76
</item>
</StartScenario>
<DefaultScenario>
ifdef THREE_ENTER76
setAnimation ANIMATIONSET 3-TEHNIK
undef THREE_ENTER76
endif
ifdef TWO_ENTER76
setAnimation ANIMATIONSET 2-DOGBAR
define THREE_ENTER76
undef TWO_ENTER76
endif
ifdef ONE_ENTER76
floating 0
waitAnimation ALI ALI-OG
setAnimation ALI BAR-ALI
waitAnimation ANIMATIONSET 11BOTLLE
toggle 1-BUTL 1
waitAnimation ANIMATIONSET 12BOTLLE
define TWO_ENTER76
undef ONE_ENTER76
floating 1
endif
</DefaultScenario>
<ObjectsScene>
<DelineationObject>
<name>STOLIK</name>
<toWatch>
<item>
speakBegin petka
waitSpeak petka 31301801 // Что-то сегодня мало народу.
waitSpeak petka 31301901 // А может здесь всегда так...
speakEnd petka
</item>
</toWatch>
<toUse>
<item>
speakBegin petka
waitSpeak petka 31306201 // Неохота.
speakEnd petka
</item>
</toUse>
<toObjects>
<ToObject>
<name>vasly</name>
<toAction>
<item>
speakBegin vasly
waitSpeak vasly 31307701 // Некогда нам тут рассиживаться.
speakEnd vasly
</item>
</toAction>
</ToObject>
</toObjects>
</DelineationObject>
<DelineationObject>
<name>PULT-DU</name>
<toWatch>
<item>
speakBegin petka
waitSpeak petka 31302001 // Не спрашивайте меня, откуда я это знаю, но эта коробочка называется "Пульт Дистанционного Управления"...
waitSpeak petka 31302101 // С ее помощью бармен переключает каналы на телевизоре.
speakEnd petka
</item>
</toWatch>
<toUse>
<item>
speakBegin petka
waitSpeak petka 31306301 // Занято.
speakEnd petka
</item>
</toUse>
<toTake>
<item>
if ALI
setAnimation ALI BAR-ALI2
waitSpeak ALI 35809601 // Нет. Нельзя.
setAnimation ALI BAR-ALI
else
closeHint hintPartFour_takePult // Скучные у них передачи показывают. Где тут пульт? Пока бармен не видит, надо его забрать.
waitMove petka PULT-DU
waitAnimation petka G_FLME
audio TAKE
addToInventory iDEWOO
toggle PULT-DU 0
waitAnimation petka G_FLMER
endif
</item>
<item>
if ALI
setAnimation ALI BAR-ALI2
waitSpeak ALI 35809901 // Нет. Не разрешаю.
setAnimation ALI BAR-ALI
else
closeHint hintPartFour_takePult // Скучные у них передачи показывают. Где тут пульт? Пока бармен не видит, надо его забрать.
waitMove petka PULT-DU
waitAnimation petka G_FLME
audio TAKE
addToInventory iDEWOO
toggle PULT-DU 0
waitAnimation petka G_FLMER
endif
</item>
</toTake>
<toObjects>
<ToObject>
<name>vasly</name>
<toAction>
<item>
speakBegin vasly
waitSpeak vasly 31307801 // В другой раз.
speakEnd vasly
</item>
</toAction>
</ToObject>
</toObjects>
</DelineationObject>
<DelineationObject>
<name>VODKA</name>
<toSpeak>
<item>
speakBegin vasly
waitSpeak vasly 31304501 // Привет, мужики!
speakEnd vasly
</item>
<item>
speakBegin vasly
waitSpeak vasly 31304502 // Эй, мужики! Как вы сюда попали?
speakEnd vasly
</item>
<item>
speakBegin vasly
waitSpeak vasly 31304503 // Мы вас не специально из бани выгоняли! Мы больше не будем!
speakEnd vasly
</item>
<item>
speakBegin vasly
waitSpeak vasly 31304504 // Как насчет совместно тут революцию устроить?
speakEnd vasly
</item>
<item>
speakBegin vasly
waitSpeak vasly 31304505 // Эй, угостите водкой-то!
speakEnd vasly
</item>
</toSpeak>
<toWatch>
<item>
speakBegin petka
waitSpeak petka 31302201 // Ой, и эти здесь!
waitSpeak petka 31302301 // Это все мы виноваты, Василь Иваныч!
waitSpeak petka 31302401 // Мы их из бани тогда выгнали, вот они сюда и пришли...
speakEnd petka
speakBegin vasly
waitSpeak vasly 31302501 // Да, Петька, неудобно вышло.
speakEnd vasly
</item>
</toWatch>
<toUse>
<item>
speakBegin petka
waitSpeak petka 31306401 // К ним мы подсаживаться не будем.
speakEnd petka
</item>
</toUse>
<toGive>
<item>
speakBegin petka
waitSpeak petka 31310201 // Это ведь наши, можно сказать, родные мужики, а мы на них так!
speakEnd petka
</item>
</toGive>
<toTake>
<item>
speakBegin petka
waitSpeak petka 31304801 // Зачем они нам? Лучше оставить их в покое.
speakEnd petka
</item>
</toTake>
<toObjects>
<ToObject>
<name>vasly</name>
<toAction>
<item>
speakBegin vasly
waitSpeak vasly 31307901 // Да ну их в баню...
speakEnd vasly
</item>
</toAction>
</ToObject>
</toObjects>
</DelineationObject>
<DelineationObject>
<name>DVERI</name>
<toWatch>
<item>
speakBegin petka
waitSpeak petka 31302601 // Они закрыты.
waitSpeak petka 31302701 // Сегодня санитарный день.
speakEnd petka
</item>
</toWatch>
<toUse>
<item>
speakBegin petka
waitSpeak petka 31306501 // Придется потерпеть.
speakEnd petka
</item>
</toUse>
<toObjects>
<ToObject>
<name>vasly</name>
<toAction>
<item>
speakBegin vasly
waitSpeak vasly 31308001 // Спасибо, я не хочу.
speakEnd vasly
</item>
</toAction>
</ToObject>
</toObjects>
</DelineationObject>
<DelineationObject>
<name>DINAMIKI</name>
<toWatch>
<item>
speakBegin petka
waitSpeak petka 31302801 // Оттуда играет музыка.
speakEnd petka
</item>
</toWatch>
<toTake>
<item>
speakBegin petka
waitSpeak petka 31304901 // Если их отломать, закончится музыка.
speakEnd petka
</item>
</toTake>
<toUse>
<item>
speakBegin petka
waitSpeak petka 31306601 // Регулировать звук лучше из соответствующего меню.
speakEnd petka
speakBegin vasly
waitSpeak vasly 31306701 // Зачем опять врешь, Петька?
waitSpeak vasly 31306801 // Я меню уже смотрел - там ничего про звук не было.
speakEnd vasly
speakBegin petka
waitSpeak petka 31306901 // Да я про другое меню...
speakEnd petka
</item>
</toUse>
<toObjects>
<ToObject>
<name>vasly</name>
<toAction>
<item>
speakBegin vasly
waitSpeak vasly 31308101 // Это не ко мне.
speakEnd vasly
</item>
</toAction>
</ToObject>
</toObjects>
</DelineationObject>
<DelineationObject>
<name>TAZ</name>
<toWatch>
<item>
speakBegin petka
waitSpeak petka 31302901 // По-моему, это чан с космическим пуншем.
speakEnd petka
speakBegin vasly
waitSpeak vasly 31303001 // Наверное ты прав, Петька.
waitSpeak vasly 31303101 // По крайней мере, запах характерный.
speakEnd vasly
</item>
</toWatch>
<toUse>
<item>
speakBegin petka
waitSpeak petka 31307001 // Выпить, конечно, охота, но нельзя - нам еще галактику спасать!
speakEnd petka
</item>
</toUse>
<toGive>
<item>
speakBegin petka
waitSpeak petka 31310301 // Не буду.
speakEnd petka
</item>
<item>
speakBegin petka
waitSpeak petka 31310302 // А вдруг эта неизвестная жидкость - не пунш, а кислота? А мы туда бросаем все подряд...
speakEnd petka
</item>
</toGive>
<toTake>
<item>
speakBegin petka
waitSpeak petka 31305001 // Этот чан предусмотрительно вмонтирован в пол.
speakEnd petka
</item>
</toTake>
<toObjects>
<ToObject>
<name>vasly</name>
<toAction>
<item>
speakBegin vasly
waitSpeak vasly 31308201 // Я пунш не пью. Даже космический!
speakEnd vasly
</item>
</toAction>
</ToObject>
</toObjects>
</DelineationObject>
<DelineationObject>
<name>BAR</name>
<toWatch>
<item>
speakBegin petka
waitSpeak petka 31303201 // Тут обычно подаются горячительные напитки.
waitSpeak petka 31303301 // Но мы пробовать их не станем.
speakEnd petka
</item>
</toWatch>
<toUse>
<item>
speakBegin petka
waitSpeak petka 31307101 // Что-то я не вижу там ни одного знакомого напитка.
speakEnd petka
</item>
</toUse>
<toGive>
<item>
speakBegin petka
waitSpeak petka 31310401 // На стойку этим предметом не подействуешь.
speakEnd petka
</item>
<item>
speakBegin petka
waitSpeak petka 31310402 // Лучше оставить стойку в покое.
speakEnd petka
</item>
</toGive>
<toTake>
<item>
speakBegin petka
waitSpeak petka 31305101 // Взять стойку бара не получится.
speakEnd petka
</item>
</toTake>
<toObjects>
<ToObject>
<name>vasly</name>
<toAction>
<item>
speakBegin vasly
waitSpeak vasly 31308301 // Что-то я не в настроении экспериментировать.
speakEnd vasly
</item>
</toAction>
</ToObject>
</toObjects>
</DelineationObject>
<DelineationObject>
<name>ALI</name>
<toSpeak>
<item>
waitMove petka ALI
runDialog ALI
</item>
</toSpeak>
<toWatch>
<item>
speakBegin petka
waitSpeak petka 31303401 // Толстый старый бармен - обязательный атрибут любого злачного заведения.
speakEnd petka
</item>
</toWatch>
<toUse>
<item>
speakBegin petka
waitSpeak petka 31307201 // Судя по его виду, он сам с удовольствием кого хошь использует...
speakEnd petka
</item>
</toUse>
<toGive>
<item>
speakBegin petka
waitSpeak petka 31310501 // Ему это не нужно.
speakEnd petka
</item>
<item>
speakBegin petka
waitSpeak petka 31310502 // Зачем ему это?
speakEnd petka
</item>
<item>
speakBegin petka
waitSpeak petka 31310503 // Нет, это плохой вариант знакомиться с барменами.
speakEnd petka
</item>
</toGive>
<toTake>
<item>
speakBegin petka
waitSpeak petka 31305201 // Что нам стоит взять этого бармена... А нельзя.
waitSpeak petka 31305301 // Конспирация!
speakEnd petka
</item>
</toTake>
<toObjects>
<ToObject>
<name>vasly</name>
<toAction>
<item>
speakBegin vasly
waitSpeak vasly 31308401 // До чего же неприятный и подозрительный тип.
waitSpeak vasly 31308501 // Пусть только попробует нас как-нибудь обмануть, костей не соберет...
speakEnd vasly
speakBegin petka
waitSpeak petka 31308601 // А вдруг у него костей нет никаких?
speakEnd petka
speakBegin vasly
waitSpeak vasly 31308701 // Значит, в глаз получит!
speakEnd vasly
speakBegin petka
waitSpeak petka 31308801 // В какой из них?
speakEnd petka
speakBegin vasly
waitSpeak vasly 31308901 // Да во все сразу...
speakEnd vasly
</item>
</toAction>
</ToObject>
</toObjects>
</DelineationObject>
<DelineationObject>
<name>STRANGE-MACHINE</name>
<toWatch>
<item>
speakBegin vasly
waitSpeak vasly 31303501 // Похожий аппарат я видал, когда мы Зимний брали.
waitSpeak vasly 31303601 // Правда, там чего-то другое написано было.
speakEnd vasly
</item>
</toWatch>
<toGive>
<item>
speakBegin petka
waitSpeak petka 31310601 // Ничего постороннего туда лучше не пихать.
speakEnd petka
</item>
</toGive>
<toTake>
<item>
speakBegin petka
waitSpeak petka 31305401 // Ее не выдрать отсюда даже с корнем.
speakEnd petka
</item>
</toTake>
<toObjects>
<ToObject>
<name>vasly</name>
<toAction>
<item>
speakBegin vasly
waitSpeak vasly 31309001 // Сам, Петр, все сам.
speakEnd vasly
</item>
<item>
speakBegin vasly
waitSpeak vasly 31309002 // Позови меня, когда дойдет до чего-нибудь серьезного.
speakEnd vasly
</item>
</toAction>
</ToObject>
<ToObject>
<name>iBUTLPUST</name>
<toAction>
<item>
closeHint hintPartFour_giveBottlesOne // Пункт приёма стеклотары у инопланетян находится в баре. Удобно, правда?
if 2-BUTL
waitMove petka STRANGE-MACHINE
waitAnimation petka G_FLME
setAnimation petka G_FLMER
waitAnimation ANIMATION PROGLOT
toggle 3-BUTL 1
toggle 2-BUTL 0
toggle BUTLS 1
setAnimation BUTLS START
waitAnimation ANIMATION PRILET
toggle PR-BT 1
setAnimation ANIMATION none
endif
if 1-BUTL
waitMove petka STRANGE-MACHINE
waitAnimation petka G_FLME
setAnimation petka G_FLMER
waitAnimation ANIMATION PROGLOT
toggle 2-BUTL 1
toggle 1-BUTL 0
audio 3-BUTL2
setAnimation ANIMATION none
endif
removeFromInventory iBUTLPUST
</item>
</toAction>
</ToObject>
<ToObject>
<name>iBUTLPUST1</name>
<toAction>
<item>
closeHint hintPartFour_giveBottlesTwo // Пункт приёма стеклотары у инопланетян находится в баре. Удобно, правда?
if 2-BUTL
waitMove petka STRANGE-MACHINE
waitAnimation petka G_FLME
setAnimation petka G_FLMER
waitAnimation ANIMATION PROGLOT
toggle 3-BUTL 1
toggle 2-BUTL 0
toggle BUTLS 1
setAnimation BUTLS START
waitAnimation ANIMATION PRILET
toggle PR-BT 1
setAnimation ANIMATION none
endif
if 1-BUTL
waitMove petka STRANGE-MACHINE
waitAnimation petka G_FLME
setAnimation petka G_FLMER
waitAnimation ANIMATION PROGLOT
toggle 2-BUTL 1
toggle 1-BUTL 0
audio 3-BUTL2
setAnimation ANIMATION none
endif
removeFromInventory iBUTLPUST1
</item>
</toAction>
</ToObject>
</toObjects>
</DelineationObject>
<DelineationObject>
<name>PR-BT</name>
<toTake>
<item>
closeHint hintPartFour_takePepC // Эх, не разбогатеть мне на бутылках. Но зато попробую космической колы.
waitMove petka STRANGE-MACHINE
waitAnimation petka G_FLME
audio TAKE
toggle BUTLS 0
toggle 3-BUTL 0
addToInventory iBUTLPOLN
toggle PR-BT 0
waitAnimation petka G_FLMER
</item>
</toTake>
</DelineationObject>
<DelineationObject>
<name>PLAZMA</name>
<toWatch>
<item>
speakBegin petka
waitSpeak petka 31303701 // Какая лампа красивая.
speakEnd petka
speakBegin vasly
waitSpeak vasly 31303801 // А на мой взгляд, блажь это, Петька.
speakEnd vasly
</item>
</toWatch>
<toUse>
<item>
speakBegin petka
waitSpeak petka 31307301 // Не будем играть с огнем.
speakEnd petka
</item>
</toUse>
<toTake>
<item>
speakBegin petka
waitSpeak petka 31305501 // В лучшем случае она погаснет.
speakEnd petka
speakBegin vasly
waitSpeak vasly 31305601 // Или взорвется.
speakEnd vasly
</item>
</toTake>
<toObjects>
<ToObject>
<name>vasly</name>
<toAction>
<item>
speakBegin vasly
waitSpeak vasly 31309101 // Мне такая игрушка не нужна.
waitSpeak vasly 31309201 // Вот если бы лампу Ильича...
speakEnd vasly
</item>
</toAction>
</ToObject>
</toObjects>
</DelineationObject>
<DelineationObject>
<name>TRA</name>
<toWatch>
<item>
speakBegin petka
waitSpeak petka 31303901 // Обычная грязная тряпка.
waitSpeak petka 31304001 // Могу спорить, что именно этой тряпкой бармен протирает стаканы...
waitSpeak petka 31304101 // Если конечно, он их вообще хоть чем-то протирает.
speakEnd petka
</item>
</toWatch>
<toUse>
<item>
speakBegin petka
waitSpeak petka 31307401 // Я конечно мог бы протереть здесь все этой грязной тряпкой, да руки пачкать не хочется...
speakEnd petka
</item>
</toUse>
<toTake>
<item>
closeHint hintPartFour_takeTrash // Тряпка из бара может пригодиться для чего-нибудь.
waitMove petka TRA
speakBegin petka
waitSpeak petka 31305701 // Не думаю, что он быстро обнаружит пропажу...
speakEnd petka
waitAnimation petka G_FLME
audio TAKE
addToInventory iTRYAPKA
toggle TRA 0
waitAnimation petka G_FLMER
</item>
</toTake>
<toObjects>
<ToObject>
<name>vasly</name>
<toAction>
<item>
speakBegin vasly
waitSpeak vasly 31309301 // Это входит в обязанности бармена.
speakEnd vasly
</item>
</toAction>
</ToObject>
</toObjects>
</DelineationObject>
<DelineationObject>
<name>TEL-OKO1</name>
<toWatch>
<item>
speakBegin petka
waitSpeak petka 31304201 // Чего это он так уставился? Клубнички что ли никогда не видел?
speakEnd petka
</item>
</toWatch>
<toUse>
<item>
speakBegin petka
waitSpeak petka 31012701 // А давайте, разберем его?
speakEnd petka
speakBegin vasly
waitSpeak vasly 31012801 // Как разберем?
speakEnd vasly
speakBegin petka
waitSpeak petka 31012901 // По косточкам!
speakEnd petka
</item>
</toUse>
<toObjects>
<ToObject>
<name>iAXE</name>
<toAction>
<item>
waitMove petka TEL-OKO1
if ALI
toggle PULT-DU 0
waitAnimation ALI ALI-TV
waitAnimation TV PEREKL2
toggle AUDIOTV 1
setAnimation TV DIKTOR
setAnimation ALI ALI-T
toggle PULT-DU 1
toggle petka 0
waitAnimation ANIMATION UDAROK1
setAnimation TEL-OKO1 TELOKO4
waitAnimation ANIMATION UDAR-O2X
toggle TEL-OKO1 0
toggle petka 1
jump petka PETKAJUMP
setAnimation ANIMATION none
else
closeHint hintPartFour_giveAxe // Вот надоедливый шар. Влеплю-ка я ему топориком промеж… глаза!
toggle petka 0
waitAnimation ANIMATION UDAROK1
toggle TEL-OKO1 0
toggle TEL-OKO2 1
setAnimation TEL-OKO2 TEL-OKO1
waitAnimation ANIMATION UDAROK2
toggle petka 1
setAnimation ANIMATION none
define OKOCRASH
endif
</item>
</toAction>
</ToObject>
</toObjects>
</DelineationObject>
<DelineationObject>
<name>TEL-OKO2</name>
<toWatch>
<item>
speakBegin petka
waitSpeak petka 31304201 // Чего это он так уставился? Клубнички что ли никогда не видел?
speakEnd petka
</item>
</toWatch>
<toUse>
<item>
speakBegin petka
waitSpeak petka 31012701 // А давайте, разберем его?
speakEnd petka
speakBegin vasly
waitSpeak vasly 31012801 // Как разберем?
speakEnd vasly
speakBegin petka
waitSpeak petka 31012901 // По косточкам!
speakEnd petka
</item>
</toUse>
<toTake>
<item>
waitAnimation petka G_FLME
audio TAKE
addToInventory iOKO2
toggle TEL-OKO2 0
waitAnimation petka G_FLMER
</item>
</toTake>
</DelineationObject>
<DelineationObject>
<name>TV</name>
<toWatch>
<item>
speakBegin petka
waitSpeak petka 31350101 // Интересное кино, Василь Иваныч.
speakEnd petka
speakBegin vasly
waitSpeak vasly 31350102 // Лучшее кино, Петька, это вино.
speakEnd vasly
</item>
</toWatch>
<toObjects>
<ToObject>
<name>iDEWOO</name>
<toAction>
<item>
closeHint hintPartFour_givePult // Переключу-ка я канал на телевизоре в баре.
ifn AUDIOTV
waitMove petka TV
toggle petka 0
waitAnimation ANIMATION P-DU0
ifndef OKOCRASH
setAnimation TEL-OKO1 TELOKO4
endif
setAnimation TV PEREKL2
waitAnimation ANIMATION P-DU1
toggle AUDIOTV 1
setAnimation TV DIKTOR
toggle petka 1
setAnimation ANIMATION none
ifndef OKOCRASH
toggle TEL-OKO1 0
endif
else
waitMove petka TV
toggle petka 0
ifndef OKOCRASH
toggle TEL-OKO1 1
endif
waitAnimation ANIMATION P-DU0
ifndef OKOCRASH
setAnimation TEL-OKO1 TELOKO1
endif
setAnimation TV PEREKL1
toggle AUDIOTV 0
waitAnimation ANIMATION P-DU1
setAnimation TV PORNO-B
toggle petka 1
setAnimation ANIMATION none
ifndef OKOCRASH
setAnimation TEL-OKO1 TELOKO2
endif
endif
</item>
</toAction>
</ToObject>
</toObjects>
</DelineationObject>
</ObjectsScene>
<Dialogs>
<DialogItem>
<NameDialog>ALI</NameDialog>
<DialogNodes>
<DialogNode>
<NameNode>A</NameNode>
<DialogNodeItems>
<DialogNodeItem>
<!--Как дела?-->
<Text>35801401</Text>
<Scenario>
speakBegin petka
waitSpeak petka 35801401 // Как дела?
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35802401 // Все как обычно - начальство работает, подчиненные развлекаются.
waitSpeak ALI 35802501 // Или наоборот...
setAnimation ALI ALI-SM
speakBegin petka
waitSpeak petka 35802601 // Кстати, а где сейчас ваше начальство?
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35802701 // Как где? Вы что - с Земли свалились?
setAnimation ALI ALI-SM
speakBegin petka
waitSpeak petka 35802801 // Мы? Да... А что - так заметно?
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35802901 // Ладно, хорош умничать.
waitSpeak ALI 35803001 // С тех пор, как босс вернулся, он не вылезает из рубки управления.
waitSpeak ALI 35803101 // Небось, как обычно, замышляет что-нибудь супер-гадкое.
waitSpeak ALI 35803201 // Ходят слухи, что он готовит какую-то западню для Земных шпионов, якобы тайком пробравшихся к нам на станцию...
setAnimation ALI ALI-SM
speakBegin petka
waitSpeak petka 35803301 // Понятно. А где находится его рубка?
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35803401 // Рубка? Да вы что?
waitSpeak ALI 35803501 // Если вы помешаете боссу думать, он самолично выкинет вас в космос!
setAnimation ALI BAR-ALI
speakBegin petka
waitSpeak petka 35803601 // Да мы и не собирались, собственно. Я просто так спросил, из любопытства.
speakEnd petka
</Scenario>
<RemoveSelfAfterPlay>true</RemoveSelfAfterPlay>
<TransitionToNode />
</DialogNodeItem>
<DialogNodeItem>
<!--Что это за окно такое на стене?-->
<Text>35801501</Text>
<Scenario>
speakBegin petka
waitSpeak petka 35801501 // Что это за окно такое на стене?
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35804101 // Окно? На стене?
waitSpeak ALI 35804201 // А-аа! Понимаю! Как поэтично. Действительно этот стереовизор похож на окно.
waitSpeak ALI 35804301 // Так сказать, окно в пространстве, соединяющее нас с нашей родной звездной системой Дельта Аллигатора...
waitSpeak ALI 35804401 // С его помощью мы получаем самые последние новости, и узнаем о жизни на нашей далекой планете.
waitSpeak ALI 35804501 // К сожалению, сигнал очень слабый, удается принимать только два канала...
setAnimation ALI ALI-SM
speakBegin petka
waitSpeak petka 35804601 // Новости?
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35804701 // Ну не только, конечно...
setAnimation ALI ALI-SM
speakBegin petka
waitSpeak petka 35804801 // А что еще?
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35804901 // Второй канал показывает "В мире животных".
waitSpeak ALI 35805001 // Сексуальные ритуалы, обряды и практики примитивных инопланетных жизненных форм.
setAnimation ALI BAR-ALI1
speakBegin petka
waitSpeak petka 35805101 // Как интересно!
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35805201 // Наоборот. Лично я считаю, что все это ужасная гадость.
setAnimation ALI ALI-SM
speakBegin petka
waitSpeak petka 35805301 // Гадость?
waitSpeak petka 35805401 // А-аа. Ну да. Конечно гадость.
waitSpeak petka 35805501 // А вы не могли бы показать нам немного? Просто так, для расширения кругозора...
speakEnd petka
toggle PULT-DU 0
waitAnimation ALI ALI-TV
toggle AUDIOTV 0
waitAnimation TV PEREKL1
setAnimation TV PORNO-B
waitAnimation ALI ALI-T
setAnimation ALI BAR-ALI
toggle PULT-DU 1
ifndef OKOCRASH
toggle TEL-OKO1 1
waitAnimation TEL-OKO1 TELOKO1
setAnimation TEL-OKO1 TELOKO2
endif
toggleDialogItem ALI A PULT1 1
</Scenario>
<RemoveSelfAfterPlay>true</RemoveSelfAfterPlay>
<TransitionToNode />
</DialogNodeItem>
<DialogNodeItem>
<!--Что тут у вас есть из выпивки?-->
<Text>35801601</Text>
<Scenario>
speakBegin petka
waitSpeak petka 35801601 // Что тут у вас есть из выпивки?
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35805901 // Продавать подросткам алкоголь запрещено.
setAnimation ALI ALI-SM
speakBegin petka
waitSpeak petka 35806001 // Подросткам? Но ведь мы уже взрослые.
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35806101 // Вам еще не исполнилось 160-ти.
setAnimation ALI ALI-SM
speakBegin petka
waitSpeak petka 35806201 // С чего это вы взяли, что не исполнилось? Мне, например, уже 175! С половиной.
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35806301 // Ага. А мне в таком случае - 1175!!!
setAnimation ALI ALI-SM
speakBegin petka
waitSpeak petka 35806401 // Серьезно? А выглядите вы гораздо моложе!
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35806501 // Все. Хватит с меня вашей болтовни.
waitSpeak ALI 35806601 // Кстати, почему вы не в школе?
setAnimation ALI BAR-ALI
</Scenario>
<RemoveSelfAfterPlay>true</RemoveSelfAfterPlay>
<TransitionToNode>B</TransitionToNode>
</DialogNodeItem>
<DialogNodeItem>
<!--Зачем здесь этот странный толчок?-->
<Text>35801701</Text>
<Scenario>
speakBegin petka
waitSpeak petka 35801701 // Зачем здесь этот странный толчок?
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35808301 // Это машина для переработки пустых бутылок.
waitSpeak ALI 35808401 // Если бросить туда три пустые бутылки из-под Pep-C++, взамен вылетит одна полная.
setAnimation ALI ALI-SM
speakBegin petka
waitSpeak petka 35808501 // Понятно...
waitSpeak petka 35808601 // А что будет, если бросить туда какие-нибудь другие бутылки?
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35808701 // Ничего особенного. Просто машина засорится.
waitSpeak ALI 35808801 // Но вы этого уже не увидите.
setAnimation ALI ALI-SM
speakBegin petka
waitSpeak petka 35808901 // Почему?
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35809001 // Потому что мой лазерный обрез разнесет ваши пустые головы в дребезги.
setAnimation ALI BAR-ALI
</Scenario>
<TransitionToNode />
</DialogNodeItem>
<DialogNodeItem>
<!--А что это за маленькая коробочка у вас на столе?-->
<Text>35801801</Text>
<nameItem>PULT1</nameItem>
<Enable>false</Enable>
<Scenario>
speakBegin petka
waitSpeak petka 35801801 // А что это за маленькая коробочка у вас на столе?
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35809301 // Это - мой Пульт Дистанционного Управления.
waitSpeak ALI 35809401 // Им я переключаю каналы на стереовизоре, регулирую громкость, яркость, контраст, объем...
setAnimation ALI ALI-SM
speakBegin petka
waitSpeak petka 35809501 // А нам можно попробовать?
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35809601 // Нет. Нельзя.
setAnimation ALI BAR-ALI
toggleDialogItem ALI A PULT2 1
</Scenario>
<RemoveSelfAfterPlay>true</RemoveSelfAfterPlay>
<TransitionToNode />
</DialogNodeItem>
<DialogNodeItem>
<!--Может, вы все-таки разрешите нам взять пульт?-->
<Text>35801901</Text>
<nameItem>PULT2</nameItem>
<Enable>false</Enable>
<Scenario>
speakBegin petka
waitSpeak petka 35801901 // Может, вы все-таки разрешите нам взять пульт?
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35809901 // Нет. Не разрешаю.
setAnimation ALI BAR-ALI
</Scenario>
<TransitionToNode />
</DialogNodeItem>
<DialogNodeItem>
<!--Ну тогда налейте нам хотя бы томатного сока.-->
<Text>35802001</Text>
<nameItem>TOMAT</nameItem>
<Enable>false</Enable>
<Scenario>
speakBegin petka
waitSpeak petka 35802001 // Ну тогда налейте нам хотя бы томатного сока.
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35810201 // Томатного? По-моему он уже закончился.
setAnimation ALI ALI-SM
speakBegin petka
waitSpeak petka 35810301 // А яблочный?
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35810401 // Яблочный тоже.
setAnimation ALI ALI-SM
speakBegin petka
waitSpeak petka 35810501 // Грушевый?
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35810601 // Нет.
setAnimation ALI ALI-SM
speakBegin petka
waitSpeak petka 35810701 // Сливовый?
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35810801 // Нет.
setAnimation ALI ALI-SM
speakBegin petka
waitSpeak petka 35810901 // Абрикосовый?
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35811001 // Нет.
setAnimation ALI ALI-SM
speakBegin petka
waitSpeak petka 35811101 // Ананасовый?
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35811201 // Нет.
setAnimation ALI ALI-SM
speakBegin petka
waitSpeak petka 35811301 // Морковный?
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35811401 // Извините.
setAnimation ALI ALI-SM
speakBegin petka
waitSpeak petka 35811501 // Свекольный?
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35811601 // Сожалею.
setAnimation ALI ALI-SM
speakBegin petka
waitSpeak petka 35811701 // Жучиный?
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35811801 // Жучиный?!? Хм-мм... Хороший вопрос.
waitSpeak ALI 35811901 // Может быть. Схожу, посмотрю...
waitAnimation ALI ALI-GO
closeHint hintPartFour_speakBar // Интересно, в инопланетном баре подают жучиный сок?
toggle ALI 0
</Scenario>
<RemoveSelfAfterPlay>true</RemoveSelfAfterPlay>
<TransitionToNode />
</DialogNodeItem>
<DialogNodeItem>
<!--Счастливо.-->
<Text>35802101</Text>
<Scenario>
speakBegin petka
waitSpeak petka 35802101 // Счастливо.
speakEnd petka
</Scenario>
<TransitionToNode />
</DialogNodeItem>
</DialogNodeItems>
</DialogNode>
<DialogNode>
<NameNode>B</NameNode>
<DialogNodeItems>
<DialogNodeItem>
<!--А у нас отменили три первых урока.-->
<Text>35806801</Text>
<Scenario>
speakBegin petka
waitSpeak petka 35806801 // А у нас отменили три первых урока.
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35806901 // Три? А сейчас уже четвертый заканчивается! А ну марш в класс!!!
setAnimation ALI BAR-ALI
</Scenario>
<RemoveSelfAfterPlay>true</RemoveSelfAfterPlay>
<TransitionToNode>B</TransitionToNode>
</DialogNodeItem>
<DialogNodeItem>
<!--Да у нас химичка заболела...-->
<Text>35807101</Text>
<Scenario>
speakBegin petka
waitSpeak petka 35807101 // Да у нас химичка заболела...
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35807201 // Что вы опять мне врете - она же только что была здесь.
waitSpeak ALI 35807301 // Выпила свой обычный коктейль с куриной кровью и пошла в класс.
waitSpeak ALI 35807401 // Ну-ка марш за ней.
setAnimation ALI BAR-ALI
</Scenario>
<RemoveSelfAfterPlay>true</RemoveSelfAfterPlay>
<TransitionToNode>B</TransitionToNode>
</DialogNodeItem>
<DialogNodeItem>
<!--Нас на неделю освободили по болезни.-->
<Text>35807601</Text>
<Scenario>
speakBegin petka
waitSpeak petka 35807601 // Нас на неделю освободили по болезни.
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35807701 // А справка у вас есть?
setAnimation ALI ALI-SM
speakBegin petka
waitSpeak petka 35807801 // Конечно! Только я оставил ее у себя в каюте.
speakEnd petka
setAnimation ALI BAR-ALI2
waitSpeak ALI 35807901 // Ну ладно. Все равно, нечего вам здесь болтаться.
waitSpeak ALI 35808001 // Пошли бы лучше занялись каким-нибудь полезным делом. Металлолом бы сдали, что ли...
setAnimation ALI BAR-ALI
toggleDialogItem ALI A TOMAT 1
</Scenario>
<RemoveSelfAfterPlay>true</RemoveSelfAfterPlay>
<TransitionToNode />
</DialogNodeItem>
</DialogNodeItems>
</DialogNode>
</DialogNodes>
</DialogItem>
</Dialogs>
</Root>
В итоге мы получили «осовремененную» игру, фактически собранную с нуля на новом движке, но при этом сохранившую оригинальную графику и озвучение. Чтобы наглядно продемонстрировать процесс полной сборки одной из игровых сцен, мы сделали специальный ролик:
Возможно, кто-нибудь из вас воспользуется нашим примером для создания своего 2D квеста. Ну и конечно, буду рада ответить на ваши вопросы!
Автор: DreamingCollapse