Данный файл является частью Руководства по TADS для авторов игр.
Copyright © 1987, 1996 Майкл Дж. Робертс (Michael J. Roberts). Все права защищены.

Руководство было преобразовано в формат HTML Н. К. Гайем (N. K. Guy), компания tela design.

Перевод руководства на русский язык - Валентин Коптельцев


Глава шестая


Основы работы с TADS

В предыдущих главах было затронуто множество тем: язык программирования, синтаксический анализатор и даже основные принципы объектно-ориентированного программирования. Однако именно из-за того, что TADS предоставляет так много возможностей, новичкам зачастую бывает трудно определиться с тем, как именно им начать реальную работу в TADS.

Данная глава предназначена именно для того, чтобы помочь вам в этом, проведя вас через процесс создания игры в TADS. Предполагается, что у вас есть общее представление о принципах организации языка TADS.


Разработка и реализация

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

Первым этапом является разработка игры. Когда используешь такую систему, как TADS, позволяющую сразу же начать написание игры и увидеть вразумительные результаты своей работы, не прилагая особых усилий, может возникнуть соблазн сразу начать программировать; и все же постарайтесь не поддаваться ему - выключите свой компьютер и достаньте вместо этого листок бумаги.

(Примечание переводчика: последняя фраза выделена мной. Даже если из всего руководства вы усвоите только эту мысль, значит, вы читали его не зря).

Мы не последовали этому совету, когда писали Deep Space Drifter (русский перевод игры, носящий название "Бороздящий просторы вселенной", можно скачать здесь). Сформулировав вкратце общий сюжет игры и нарисовав карту для первой ее части, где действие происходит на космической станции, мы начали программировать. У нас было общее представление о том, как должна выглядить вторая часть игры, но даже карты для нее мы не нарисовали.

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

Однако к этому времени мы вложили в космическую станцию столько труда и времени, что отказываться и от нее нам тоже не хотелось. Вместо этого мы стали пытаться разработать вторую часть игры так, чтобы она состыковалась с существующей первой частью. Начиная с этого момента, сражение было проиграно окончательно. Мы перебрали целый ряд никак не связанных друг с другом сюжетов игры, пытаясь "втиснуть" каждый новый сюжет во все больший и больший объем уже готовых частей игры. Мы разрабатывали план сюжета, затем частично его реализовывали, после чего обнаруживали, что план в целом не вписывается в игру и должен быть заменен - но при этом результаты той работы по программированию, которую мы выполнили в процессе попытки реализации этого плана, должны были быть сохранены. Болото, лабиринт в пещерах, транспортный вагончик - на все это было потрачено столько труда, что вопрос об отказе от этих элементов даже не поднимался, поэтому мы вынуждены были тем или иным образом поместить их в игру. Например, в течение некоторого времени мы всерьез собирались сделать болото "Симулятором Болотистой Местности", поскольку это казалось нам единственным способом вписать его в сюжет.

В конце концов написание Deep Space Drifter задолбало нас окончательно, но по причинам психологического порядка мы довели-таки проект до конца. Мне кажется, что этот подход хорошо просматривается во второй части игры; по-моему, там имеется комната, описание которой звучит примерно так: "В этой комнате нет ничего интересного; выход находится к северу." Да и вообще вся игра в целом отражает историю своего создания: космическая станция выглядит по-настоящему реалистично; в ней много предметов, с которыми игрок может взаимодействовать, получая забавные реакции на свои действия, и при этом она производит цельное впечатление со стилистической точки зрения. С другой стороны, планета, где разворачивается действие во второй части игры, выглядит какой-то пустынной - набор раскиданных в пространстве комнат, делать в большинстве из которых особо нечего. Те области, которые действительно представляют интерес, практически не связаны ни друг с другом, ни с сюжетом в целом - результат того, что их насильственно "втиснули" в игру, не обращая внимание на то, вписываются они в нее или нет.

Я не хочу сказать, что Deep Space Drifter - плохая игра; нет, мне очень нравится космическая станция в ней, а загадки, относящиеся к планете, реализованы очень качественно и элегантно. Однако игра имеет серьезные недостатки, большинство из которых я отношу на счет долгого и хаотичного процесса разработки и реализации.

С другой стороны, процесс разработки и реализации игры Ditch Day Drifter (в русском переводе - "Блуждания в окопный день"; русскую версию этой игры можно скачать отсюда) был гораздо более коротким, гладким и организованным, и, как мне кажется, дал лучшие результаты. Конечно, "Окопные блуждания" гораздо меньше по объему, чем "Бороздящий...", но лично для меня совершенно очевидно, что тот же самый подход к разработке мог быть применен и к Deep Space Drifter'у, и игра при этом получилась бы гораздо лучшей.

До начала какой-либо программистской работы над Ditch Day Drifter я уже располагал полной картой игры, а также описанием всех предметов и персонажей в игре, включая ключевые моменты их поведения. Благодаря этому реализация прошла необыкновенно гладко и легко, поскольку от меня требовалось только просматривать списки комнат и объектов и программировать их один за одним. В процессе реализации мне не приходилось принимать решений, касающихся структуры и сюжета игры, что исключало соблазн и саму возможность поменять те решения, относящиеся к сфере дизайна, которые уже были сделаны ранее. Игра была написана всего за несколько недель и при этом получилась довольно неплохой.

Если вы не уступите соблазну начать программировать до завершения работы над дизайном игры, то разработка вашей игры пойдет быстрее, игра получится гораздо лучше, и, что самое важное, шансы на то, что вы завершите проект, значительно повысятся.


Разработка игры

К моменту начала непосредственного программирования игры у вас уже должен быть список комнат, предметов и персонажей разрабатываемой игры. Для каждой комнаты, предмета и персонажа (которые мы будем совместно называть "элементами игры") у вас должно иметься описание самого объекта, а также его "поведения" (т. е. список основных реакций на команды игрока). Базовый принцип здесь такой: к моменту начала программной реализации игры вы должны знать о ней достаточно для того, чтобы вы могли ее пройти целиком, вводя команду за командой. Знать все детали игры на этом этапе необязательно, но вы должны знать все подробности, касающиеся "кратчайшего прохождения" игры (последовательности команд, которые игрок должен ввести, чтобы закончить игру, при условии, что игрок не сделал ни одной ошибки и ему не потребовалось сохранять и восстанавливать игру). Остальные детали игры не затрагивают ее структуру и сюжет, поэтому вы сможете дописать их потом, поскольку эти детали не вызовут появления сюжетных пробелов и нестыковок.

Пример, иллюстрирующий предыдущий абзац: пусть по ходу игры игроку необходимо проникнуть из двора дома в спальню через окно, чтобы взять, допустим, ключ, который лежит в ящике тумбочки. На этапе разработки вам достаточно будет определить только объекты, имеющие непосредственное отношение к сюжету: комнаты в игре - двор и спальню с их выходами, окно спальни, тумбочку с ящиком, стоящую в спальне, и ключ. Другие элементы, не имеющие прямого отношения к сюжету, но необходимые хотя бы даже в качестве декораций (кровать и другая мебель в спальне, может быть, забор, ограждающий двор, и т. д.) можно будет оставить на потом.

Полная карта игры и список объектов является результатом процесса разработки. Чтобы добраться до этого этапа, вам, скорее всего, потребуется сделать немало шагов, постепенно внося все больше и больше подробностей в мир игры, пока у вас не будет законченного проекта игры. Процесс, описанный ниже - это неплохой способ разработки игры, но вы можете найти и другие пути, которые лично вам подходят лучше.


Сюжет

Большинство приключенческих игр имеют "каркас" в виде основного сюжета, который определяет процесс игры в целом. Сюжет - это наиболее подходящий исходный пункт для начала работы над игрой.

Как правило, сюжет приключенческой игры гораздо менее детализирован, чем сюжеты неинтерактивных произведений - таких, как книги или фильмы. Связано это с тем, что в игре вы как автор гораздо в меньшей степени можете контролировать действия игрока, чем писатель или автор сценария - действия своего главного персонажа. Вследствие этого задача написания сюжета усложняется; и все же постарайтесь дать как можно больше свободы игроку, поскольку обычно игрок получает от игры тем больше удовольствия, чем в большей степени он способен влиять на ход событий. Таким образом, сюжеты приключенческих игр - это просто "скелет", задающий общее развитие действия в игре и жестко определяющий только наиболее важные события.

Вам следует начать с самого общего определения места действия игры и ее главной цели. Где происходит действие? Когда? Кто главный персонаж? Чего надо достичь, чтобы победить в игре?

Например, в Блужданиях в Окопный День местом действия является студгородок Калифорнийского Технологического Института (или, может быть, воплощение этого института в некой параллельной вселенной, поскольку игра не является строгим симулятором КТИ); время действия - современность вообще и Окопный день в частности. Главный персонаж - студент младшего курса КТИ. Целью игры является решение традиционной головоломки Окопного дня, созданной старшекурсником, живущим напротив главного героя.

Вы можете продолжить разработку, определив основные вспомогательные цели, которых необходимо достичь для достижения главной цели игры. В Окопном Дне таковой является нахождение всех "сокровищ", упомянутых в записке, описывающей головоломку.

Детализация сюжета может быть организована по принципу "от общего - к частному" последовательными определениями и переходами от глобальной цели к основным вспомогательным целям, потом от них к частным задачам, которые должны быть решены для достижения каждой из вспомогательных целей, и т. д.

Я бы не советовал создавать игру с основными вспомогательными целями типа "игроку необходимо найти пять магических колец". Подобные абстрактные вспомогательные цели никак не помогают сформулировать пути их реализации. С другой стороны, для конкретных целей, вроде, например, "игрок должен разрушить вышку радиопередатчика", вспомогательные цели формулируются практически сами собой: необходимо проникнуть на территорию комплекса, где расположена вышка, также нужно раздобыть взрывчатку, и еще может потребоваться отключить питание вышки, чтобы избежать удара током. Но если можно отключить питание, зачем тогда ее вообще взрывать? Ну, может быть, потому, что через несколько ходов появится дежурный смотритель и снова включит вышку. Вот вам и еще одна вспомогательная задачка: как побороть дежурного. И так далее.

Как видите, абстрактные цели, типа поиска пяти магических колец, не позволяют так непринужденно перейти к следующему уровню детализации. Конкретные же цели зачастую способны заронить зерно, из которого вы благодаря своей фантазии сможете создать сразу целые куски сюжета.


Игровой мир

Основа сюжета, как правило, определит и общий характер игрового мира, а отдельные элементы сюжета - соответствующий набор комнат (которые пока не определены в деталях). Таким образом, уже после разработки общего сюжета у вас складывается карта игры "мелкого масштаба".

После этого можно приступать к разработке отдельных комнат. На этом этапе вы, скорее всего, обнаружите, что разрабатываете элементы сюжета параллельно с разработкой игрового мира. Многие комнаты игрового мира автоматически обеспечат "естественные" загадки: если у вас в игре имеется банк, там, по всей вероятности, будет сейф, для которого игроку понадобится подобрать комбинацию; может быть, ему потребуется отключить сигнализацию, а может, как-нибудь отвлечь банковского служащего; либо игроку необходимо будет найти ключ от банковской ячейки - ну, и так далее. Если действие разворачивается в аэропорту, то игроку, вероятно, будет необходимо пробраться мимо металлодетектора, либо узнать расписание полетов, или раздобыть авиабилеты, наконец, найти идентификационный значок, ключ или шифр, чтобы проникнуть в закрытую зону.

Какой бы вид комнаты вы не создавали, эта комната нередко сама "предложит" вам набор загадок. В ходе создания решений для пазлов вы будете добавлять новые элементы сюжета, которые, в свою очередь, обеспечат вас новыми комнатами.

В процессе разработки игрового мира у вас может возникнуть желание добавить в него значительное количество комнат. Например, при разработке аэропорта вы внезапно поймаете себя на том, что пытаетесь разместить в игре дюжину выходов к самолетам, весьма похожих друг на друга. Хотя добавленные комнаты зачастую повышают реалистичность игры, они в то же время могут отрицательно влиять на "играбельность". Всегда помните, что вы разрабатываете не настоящий аэропорт, а всего лишь игру, главное в которой - удовольствие, получаемое игроком, а не качество имитации реальности.

Основная проблема, возникающая при добавлении значительного количества практически неиспользуемого пространства заключается в том, что уровень детализации становится неоднородным в разных местах игры. Следует пытаться сохранять этот уровень постоянным во всей игре, чтобы не раздражать и не смущать игроков. Речь идет о следующем: если уж у вас имеется несколько комнат с высоким уровнем детализации (например, с массой объектов, которыми игрок может манипулировать и получать нестандартные реакции), то игрок вправе ожидать, что и остальные комнаты порадуют его таким же уровнем детализации. Возвращаясь к нашему примеру с аэропортом - если у вас имеется, скажем, зал ожидания с массой пассажиров, газетным киоском, табло прилетов и другими интересными, детально проработанными предметами, то и остальные комнаты в игре должны обеспечивать нечто подобное. Дюжина практически одинаковых выходов к самолетам вряд ли украсит игру - делать там игроку будет абсолютно нечего.


Предметы и персонажи

Скорее всего, большую часть структуры игры вы создадите в процессе разработки карты и сюжета игры. В процессе работы над элементами сюжета не забудьте записывать краткие сведения о связанных с ними предметах и персонажах. Вероятно, проще всего будет делать эти отметки непосредственно на карте игры - в тех комнатах, где будут располагаться эти персонажи и предметы.

Для каждого предмета и персонажа вам необходимо сделать отметку о его влиянии на сюжет игры, а также для чего именно он нужен. Вам следует также хорошенько продумать, какие еще предметы могут использоваться в тех же целях, а также в каких еще целях можно использовать данный предмет. Например, если у вас в игре имеется дверь, которую по ходу игры надо взломать топором, вам необходимо подумать о следующем: какие еще предметы может разрубить топор? А также, чем еще можно взломать дверь?

Поскольку игрок вправе ожидать, что топор можно использовать в разных целях, и поскольку дверь обычно можно взломать разными способами, вам следует хорошенько обдумать целесообразность включения в игру пазлов, решаемых при помощи грубой силы и с применением разрушающих средств. Взрывчатые вещества, горючие жидкости, оружие, силовые инструменты - если подобные вещи имеются в игре и при этом окажутся неспособны уничтожить практически любые препятствия на пути игрока, то последнего это по меньшей мере сильно озадачит. С другой стороны, если вы обеспечите полноценную реализацию двери и топора, позволив игроку рубить топором все, что только можно (в пределах разумного, разумеется), а также обеспечив возможность взлома двери любыми подходящими инструментами, игрока это сильно порадует.


Программная реализация

Полностью закончив проект игры, вы можете приступать к ее программной реализации. В этом разделе мы рассмотрим реализацию некоторых базовых типов объектов.

Начните с рисования карты и нанесения на нее пометок с описанием важнейших деталей сюжета, включая расположение основных предметов в игре и основные действия, которые должен произвести игрок.

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


[ Map ]


На этой карте имеется парочка закрытых зон, что очень удобно при создании загадок. Кроме того, нам пригодится и металлодетектор, поскольку он не позволит игроку проносить через него некоторые предметы. Самолет также может использоваться для пазла, поскольку обычно для того, чтобы попасть в самолет, нужен билет. Плюс к этому, доступ в кабину экипажа разрешен только летному персоналу.

Пусть конечной целью отрезка игры, разворачивающегося в аэропорту, будет покинуть аэропорт. Игрок начнет игру в здании главного терминала, но он не сможет выйти из аэропорта - мы придумаем какую-нибудь причину, типа толпы людей, сгрудившихся у входа в аэропорт, через которую не пробиться. (Конечно, если бы это была законченная игра, часть игрового мира располагалась бы, вероятно, вне аэропорта, и мы бы не стали использовать такую надуманную причину для ограничения перемещений игрока. Но для этого примера мы ее будем использовать, чтобы, что называется, "не расползаться" без нужды). Единственный другой возможный способ покинуть аэропорт - это улететь на самолете; так пусть это и будет целью нашей игры.

Чтобы поднять самолет в воздух, игроку необходимо будет проникнуть в кабину пилотов. (Наш пример окончится на том, что игрок туда проник; в полноразмерной игре мы бы, вероятно, позволили игроку улететь куда-нибудь). Однако в кабину пилотов разрешено входить только летному персоналу - стюардесса не пускает в кабину пассажиров. Таким образом, нам нужно найти способ пробраться мимо стюардессы. Один такой способ - отвлечь ее на достаточно долгое время, чтобы проскользнуть мимо нее в кабину; в нашей игре, однако, от игрока потребуется найти форму пилота.

В каком месте логично поместить форму пилота? Например, в комнате отдыха летного персонала; что ж, поместим в эту комнату чемодан, в котором будет лежать форма. К счастью, комната отдыха скрыта за запертой дверью, что автоматически создает еще одну, вспомогательную загадку. Чтобы попасть в закрытую служебную зону и взять оттуда форму, игроку потребуется магнитная карта-ключ, чтобы отпереть соответствующую дверь (вставив карту в щель ридера, расположенного рядом с дверью).

Магнитную карту мы поместим в легкодоступном месте - на билетной стойке в терминале. Однако мы не позволим игроку пронести ее через металлодетектор - при этом сработает сигнал тревоги, и офицер службы безопасности отберет карту (и положит ее обратно на стойку, чтобы игрок мог попробовать пронести ее еще раз). Чтобы пронести карту через металлодетектор, потребуется отключить его.

Выключатель мы поместим в уборочном помещении, запертом на ключ. Ключ от этого помещения мы поместим в туалете самолета вместе с некоторыми другими предметами уборочного инвентаря - ведром, тряпкой и мешком для мусора, чтобы игрок по ассоциации мог догадаться, что ключ, лежащий там же, скорее всего от уборочной комнаты.

Чтобы попасть в туалет на самолете, потребуется билет на этот самолет. Мы сделаем так, чтобы билет можно было обнаружить сравнительно легко: мы спрячем его в газете, забытой кем-то в буфете. Как только игрок поднимет газету, из нее выпадет билет.

Вот, собственно, и вся игра: вы идете в буфет, берете газету и находите билет. Взяв билет, вы садитесь на самолет, заходите в туалет и берете там ключ от уборочного помещения. Идете с этим ключом к уборочному помещению, отпираете его, заходите туда и отключаете металлодетектор. Затем возвращаетесь к билетной стойке, берете магнитную карточку, идете к служебной зоне и отпираете ведущую туда дверь при помощи карточки. Заходите в комнату отдыха летного персонала, достаете форму пилота из чемодана и одеваете ее. После чего снова идете в самолет и проходите в кабину мимо стюардессы.

Теперь мы можем нарисовать новую карту, нанеся на нее пометки с информацией о важнейших предметах и действиях, из которых, собственно, и будет состоять игра.


[ Detailed Map ]


Программная реализация игрового мира согласно карте

Для понимания дальнейшего текста необходимо, чтобы вы ознакомились с содержанием первой главы Руководства.

Первым шагом будет преобразование "скелета" нашей карты в основную структуру игровой программы. На этом этапе не стоит озабочиваться детальной реализацией всех реакций игрового мира, а ограничиться созданием основных комнат и соединений (переходов) между ними. Это позволит вам быстро создать и запустить "прототип" игры, чтобы можно было походить по комнатам и оценить их атмосферу.

Начните с создания файла исходника для игры и с включения в него файлов с основными определениями:

  
#define USE_HTML_STATUS             /* Определение, необходимое для правильного форматирования переноса строк в HTML-TADS */
#include <advr.t>             /* включить файл с основными определениями объектов для текстовых квестов (русская версия)*/
#include <generator.t>        /* включить файл с генератором падежных форм - доступен, начиная с версии 26 библиотек RTADS */
#include <stdr.t>             /* включить файл определений стандартных функций, вызываемых при старте (русская версия) */
#include <errorru.t>          /* включить файл с русскими сообщениями об ошибках */

Теперь создайте объект для каждой комнаты в вашей игре, который будет определять свойства sdesc (название комнаты), ldesc (подробное описание комнаты, выводимое при вводе игроком команды смотреть), а также переходы к другим комнатам. Ниже приведены определения первых нескольких комнат из приведенной выше карты. На этом этапе не стоит слишком беспокоиться о подробных описаниях; вы всегда сможете дополнить и "отшлифовать" их позднее.

  terminal: room
    sdesc = "Терминал"
    ldesc = "Ты находишься в здании главного терминала аэропорта. К востоку видны билетные кассы; 
    центральный зал расположен к северу. "
    east = ticketCounter
    north = securityGate
  ;

  ticketCounter: room
    sdesc = "Билетные кассы"
    ldesc = "Ты стоишь в зале с билетными кассами. Вся северная стена занята билетными стойками, 
    однако перед ними толпится столько народу, что ты просто уверен, что никогда не дождешься своей 
    очереди. Вернуться в главный терминал можно через западный выход. "
    west = terminal
  ;

  securityGate: room
    sdesc = "Ворота с металлодетекторами"
    ldesc = "Ты находишься перед воротами с металлодетектором, которые ведут в центральный зал 
    и к выходам к самолетам. Южный выход ведет назад в терминал. "
    north = concourse
    south = terminal
  ;

  concourse: room
    sdesc = "Центральный зал"
    ldesc = "Этот вытянутый в длину зал соединяет здание терминала (расположенное к югу) с 
    выходами к самолетам (которые находятся к северу). К востоку виден буфет, а еще одна дверь 
    ведет на запад. Рядом с этой дверью в стене имеется щель, в которую, похоже, вставляются 
    магнитные карточки, отпирающие дверь. "
    north = gateArea
    south = securityGate
    east = snackBar
    west = securityArea
  ;

Другие комнаты реализуются примерно так же. На данном этапе мы не заботимся о предметах, которые должны иметься в комнатах, других персонажах и даже о запертых дверях. Мы просто определяем все комнаты, чтобы можно было походить по игровому миру и оценить его.


Программная реализация предметов

Следующим шагом будет реализация основных предметов, имеющихся в игре. Так же, как и в случае с помещениями, заботиться о сложных реакциях, требующихся от некоторых из предметов в игре, пока необязательно; просто в самом общем виде определите объекты, соответствующие основным предметам в игре.

По сравнению с помещениями предметы имеют другой набор свойств. Базовый набор свойств предметов - это sdesc (его название), rdesc, ddesc, vdesc, tdesc и pdesc (формы названия предмета в разных падежах), ldesc (подробное описание предмета), лексические свойства - noun для существительных и, возможно, adjective для прилагательных, а также местоположение предмета (location), которое может быть помещением или другим предметом. Если игрок может брать предмет и носить его с собой, то этот предмет будет принадлежать классу item; если нет, то классу fixeditem. Некоторые предметы будут относиться к другим классам; например, если в предмет можно поместить другие предметы (это выполняется, например, для ведра), то его родительским классом будет container. Если вам требуется, чтобы можно было класть другие предметы на поверхность определяемого предмета, используйте класс surface. Вы также можете использовать при определении объекта сразу несколько классов, например, container, surface и fixeditem.

Вот некоторые базовые определения предметов из нашего примера.

  counter: fixeditem, surface
    location = ticketCounter
    noun = 'стойка' 'стойки' 'стойке' 'стойку' 'стойкой' 'стойке#d' 'стойкой#t'
    adjective = 'билетная' 'билетной' 'билетную' 'билетной#d' 'билетной#t'
    sdesc = "билетная стойка"
    rdesc = "билетной стойки"
    ddesc = "билетной стойке"
    vdesc = "билетную стойку"
    tdesc = "билетной стойкой"
    pdesc = "билетной стойке"
    isHer=true
  ;

Или, при использовании генератора падежных форм:

  counter: fixeditem, surface
    location = ticketCounter
    noun = 'стойка/1ж'
    adjective = 'билетная/1пж'
    desc = 'билетная/1пж стойка/1ж'
    isHer=true
  ;

В дальнейшем все определения в этой главе будут записываться в расчете на генератор падежных форм.

  IDcard: item
    location = counter
    noun = 'карта/1ж' 'карточка/1ж'
    adjective = 'магнитная/1пж'
    desc = 'магнитная/1пж карточка/1ж'
    isHer=true
  ;

  newspaper: readable
    location = snackBar
    noun = 'газета/1ж'
    desc = 'газета/1ж'
    ldesc = "Это сегодняшний выпуск \"USA YESTERDAY\"."
    readdesc = "Прочитав пару статей, ты поневоле впадаешь в депрессию. 
    Дефицит федерального бюджета вырос еще на двадцать миллиардов долларов, 
    но хуже от этого уже не будет. В Белом Доме разразился очередной скандал, 
    связанный с незаконной торговлей оружием, отмыванием денег через итальянские 
    банки, коррупцией среди конгрессменов; многие высокопоставленные федеральные 
    искусствоведы уже с позором отправлены в отставку. Экономика переживает 
    очередной спад, однако, по заявлению Президента, он уверен в том, что подъем 
    \"уже не за горами\". "
    isHer=true
  ;

  cardslot: fixeditem
    location = concourse
    noun = 'щель/1ж'
    desc = 'щель/1ж для магнитных карточек'
    ldesc = "Судя по всему, в эту щель вставляются специальные 
    карточки-ключи с магнитным кодом. Если бы у тебя была такая 
    карточка, ты смог бы отпереть дверь, вставив ее в щель. "
    isHer=true
  ;

  suitcase: openable
    isopen = nil
    location = pilotsLounge
    noun = 'чемодан/1м'
    desc = 'чемодан/1м'
    isHim=true
  ;

  uniform: clothingItem
    location = suitcase
    noun = 'форма/1ж' 'униформа/1ж' 'пилота' 'пилота#r' 
    'летчика' 'летчика#r'
    adjective = 'летная/1пж'
    desc = 'форма/1ж пилота'
    ldesc = "Это - форма пилота компании \"Untied Airlines\". 
    Хотя она тебе и великовата, ты вполне можешь ее надеть. "
    isHer=true
  ;

Остальные предметы реализуются определяются примерно таким же образом. При определении объектов вам потребуется "заполнить" следующие основные свойства: location, чтобы предмет действительно присутствовал в игре; noun, чтобы игрок мог обращаться к объекту; а также desc (либо - если генератор падежных форм не используется - свойства sdesc, rdesc, ddesc, vdesc, tdesc и pdesc), чтобы система "знала", как ей назвать объект, если он фигурирует в стандартных сообщениях. Также важно правильно указать для объектов родительские классы, чтобы корректно реализовать большую часть реакций предмета без лишних трудозатрат. На каком-то этапе вам, вероятно, потребуется просмотреть файл advr.t и ознакомиться с классами, которые там определены, чтобы знать, что именно вы можете получить оттуда, не прилагая дополнительных усилий. Детальное описание файла advr.t содержится в Приложении B.


Реализация специальных реакций

Следующим шагом будет реализация всех специальных реакций и действий, которые, собственно, и составляют игру. Например, запрограммируем простой алгоритм, позволяющий игроку найти билет, взяв газету. Все, что нам для этого надо - это определить метод doTake для объекта newspaper (здесь мы просто приведем новый метод doTake; все остальные свойства объекта не изменились по сравнению с приведенным выше определением):

  doTake( actor ) =
  {
    if ( not self.foundTicket )
    {
      "Когда ты поднимаешь газету, из нее на пол выпадает авиабилет, который был в нее вложен. " ;
      ticket.moveInto( actor.location );
      self.foundTicket := true;
    }
    pass doTake;
  }
  ;

Этот метод вызывается всякий раз, когда игрок берет газету. Первое, что мы делаем - это проверяем, не был ли авиабилет уже найден ранее; если нет, то выводится сообщение, что его нашли, билет помещается в игру и делается отметка (посредством специального свойства-флага), что билет найден. После этого мы (при помощи инструкции pass) продолжаем выполнение метода doTake, унаследованного объектом newspaper от его родительского класса (в данном случае это класс readable).

Обратите внимание, что авиабилет изначально должен быть определен без конкретного местоположения, поскольку в начале игры его в этой игре нет:

  ticket: item
    noun = 'билет/1м' 'авиабилет/1м'
    desc = 'авиабилет/1м'
    ldesc = "Это авиабилет в один конец до Нью-Йорка, в эконом-классе (ты поеживаешься при мысли о том, 
    на чем и на ком экономят в этом классе). "
    isHim=true
  ;

В качестве еще одного примера рассмотрим реализацию механизма, не позволяющего игроку пройти в служебную зону до тех пор, пока он не отпер дверь магнитной карточкой. Прежде всего необходимо заблокировать перемещение из центрального зала в служебную зону. Для этого мы изменим определение соответствующей комнаты (concourse) и определим новый объект - дверь.

  concourse: room
    sdesc = "Центральный зал"
    ldesc = "Этот вытянутый в длину зал соединяет здание терминала 
    (расположенное к югу) с выходами к самолетам (которые находятся к 
    северу). К востоку виден буфет, а еще одна дверь ведет на запад. 
    Рядом с этой дверью в стене имеется щель, в которую, похоже, 
    вставляются магнитные карточки, отпирающие дверь. "
    north = gateArea
    south = securityGate
    east = snackBar
    west =
    {
      if ( securityDoor.isopen )
        return( securityArea );
      else
      {
        "Дверь закрыта и заперта. ";
        return( nil );
      }
    }
  ;

  securityDoor: fixeditem
    location = concourse
    noun = 'дверь/1ж'
    desc = 'дверь/1ж'
    isHer=true
    isopen = nil
    ldesc =
    {
      "Над дверью висит табличка: \"СЛУЖЕБНАЯ ЗОНА - ПОСТОРОННИМ ВХОД ВОСПРЕЩЕН!\". ";
      if ( self.isopen )
        "Несмотря на это грозное предупреждение, дверь распахнута настежь. ";
      else
        "Как и положено, дверь закрыта. ";
    }
    verDoOpen( actor ) =
    {
      "Дверь надежно заперта. ";
    }
    verDoUnlock( actor ) =
    {
      "Если хочешь отпереть дверь, обрати внимание на щель рядом с ней. ";
    }

По сути, дверь выполняет чисто декоративные функции; разноообразные методы, которые мы для нее определили, служат исключительно для того, чтобы сообщить игроку, что впрямую манипулировать дверью нельзя. Всю работу выполняет щель для магнитных карточек; настало время определить для нее дополнительные реакции.

  cardslot: fixeditem
    location = concourse
    noun = 'щель/1ж'
    desc = 'щель/1ж'
    isHer=true
    ldesc = "Судя по всему, в эту щель вставляются специальные 
    карточки-ключи с магнитным кодом. Если бы у тебя была такая 
    карточка, ты смог бы отпереть дверь, вставив ее в щель. "
    verIoPutIn( actor ) = {}
    ioPutIn( actor, dobj ) =
    {
      if ( dobj = IDcard )
      {
        if ( securityDoor.isopen ) 
          "Ты вставляешь карточку в щель. Ничего не происходит, 
          и ты снова убираешь карточку. ";
        else
        {
          "Ты вставляешь карточку в щель. Раздается щелчок, и 
          дверь в служебную зону распахивается! Ты снова 
          убираешь карточку. ";
          securityDoor.isopen := true;
        }
      }
      else
        "Похоже, это нельзя засунуть в щель. ";
    }
  ;

Новая реакция заключается в том, что игрок может вставить магнитную карточку в щель. Когда это происходит, выполняется метод ioPutIn с магнитной карточкой в качестве "прямого" объекта (параметр dobj). Этот метод обеспечивает открывание двери (если она не была открыта ранее).

Большая часть остальных загадок в данном примере реализуется сходным образом. Вам понадобится определить пару персонажей: стюардессу и офицера безопасности, дежурящего у металлодетектора. Кроме того, вам потребуется еще несколько запертых дверей и предметов с нестандартными реакциями.

Мы не будем детально описывать реализацию остальных загадок, поскольку в следующей главе содержатся гораздо более подробные примеры реализации подобных и более сложных пазлов.


Что дальше?

Прежде, чем расширять этот пример путем добавления новых комнат, вы можете "оживить" игру, реализовав как можно больше деталей и подробностей для помещений, которые уже есть. Большинство этих подробностей, скорее всего, не будут напрямую связаны с основным сюжетом игры, но зато они сделают игру более интересной. Всегда помните о том, что вы создаете игру, а не симулятор реального мира; поэтому постарайтесь сконцентрироваться на вещах, повышающих "играбельность".

Некоторые идеи для нашего аэропорта: добавьте массу невероятно дорогих и при этом крайне сомнительных блюд, подаваемых в буфете. Реализуйте генератор случайных объявлений, передаваемых по системе оповещения. В уборочном помещении создайте ряд дополнительных выключателей для других комнат и систем и подумайте, как они будут работать: например, что случится, если отключить электричество в буфете, билетных кассах, выключить систему оповещения или автоматические двери? Наполните ваш аэропорт всевозможными странными персонажами; пусть некоторые из них активно взаимодействуют с игроком - типа членов всевозможных религиозных сект и политических групп, которые будут пытаться всучить игроку свои листовки и литературу (можно придумать какие-нибудь совершенно безумные книги, которые эти люди будут распространять).

Другой вариант - это позволить игроку взлететь на самолете и куда-нибудь прилететь. Например, можно сделать так, чтобы самолет совершил аварийную посадку на каком-нибудь острове, который игрок будет потом исследовать. А может, игрок будет перелетать на самолете из одного города в другой.

Разумеется, наибольшее удовольствие вы получите, если вместо доработки нашего примера напишете игру, целиком основанную только на ваших собственных идеях. Самое трудное обычно - это как раз найти такую идею; когда вы определитесь с основной целью игры, вы, скорее всего, будете удивлены, насколько быстро вы нарисуете карту и приступите к программной реализации. Для начала вам хватит примеров, приведенных в этой главе. По мере того, как ваша игра будет принимать конкретные очертания, вам, вероятно, захочется реализовать в ней более сложные загадки и механизмы. В этом вам помогут примеры, приведенные в следующей главе.

(Примечание переводчика: описанный здесь подход к программной реализации игры (сначала создать "скелет" всех помещений и важных предметов в игре, а потом заниматься деталями) по большому счету не является единственно возможным: вполне допустимо также, например, программировать игру "покомнатно", переходя к следующей комнате и предметам в ней только после того, как будет в деталях реализована предыдущая. Однако при любом способе программной реализации не просто важно, а совершенно необходимо, чтобы у вас уже была заранее составлена карта игры и определены все важные предметы с их реакциями.)




Самое сложное в творчестве - это начало; травинку создать не легче, чем могучий дуб.
ДЖЕЙМС РАССЕЛЛ ЛОУЭЛЛ (JAMES RUSSELL LOWELL), Притча для критиков (1848)


Глава пятая Содержание Глава седьмая