DM Guide 4: различия между версиями
Accoll (обсуждение | вклад) (Перевод кусочка оригинального гайда по DM "Дружеские аргументы". Не претендую на полную точность перевода.) |
Accoll (обсуждение | вклад) (Гайд полностью переведен на русский язык, а значит завершен, снимаю плашку разработки.) |
||
(не показано 7 промежуточных версий этого же участника) | |||
Строка 1: | Строка 1: | ||
− | |||
''Это 4 глава перевода оригинального руководства по Dream Maker от разработчиков.'' | ''Это 4 глава перевода оригинального руководства по Dream Maker от разработчиков.'' | ||
Строка 5: | Строка 4: | ||
[http://www.byond.com/docs/guide/chap04.html Оригинальная 4 глава руководства на английском языке] | [http://www.byond.com/docs/guide/chap04.html Оригинальная 4 глава руководства на английском языке] | ||
− | |||
− | |||
= Глава 4: Действия verb = | = Глава 4: Действия verb = | ||
Строка 233: | Строка 230: | ||
Последняя группа - это типы ввода объектов. Они используются для того, чтобы игрок мог выбрать предмет из списка объектов. Подробнее о списках объектов будет рассказано в разделе 4.8. По умолчанию список состоит из всех объектов, находящихся в поле зрения игрока. | Последняя группа - это типы ввода объектов. Они используются для того, чтобы игрок мог выбрать предмет из списка объектов. Подробнее о списках объектов будет рассказано в разделе 4.8. По умолчанию список состоит из всех объектов, находящихся в поле зрения игрока. | ||
− | Используя различные типы ввода, можно составлять verb, которые дают игроку контроль над собственной внешностью. Например, с помощью текстового типа ввода можно указать имя. | + | Используя различные типы ввода, можно составлять ''verb'', которые дают игроку контроль над собственной внешностью. Например, с помощью текстового типа ввода можно указать имя. |
Строка 244: | Строка 241: | ||
set-name "Dan The Man" | set-name "Dan The Man" | ||
− | Обратите внимание, что в этом примере определен "текст справки"(desc) для verb. Сначала в круглых скобках описывается синтаксис, а затем указывается назначение команды. (Обратные косые черты перед двойными кавычками внутри текста ставятся для того, чтобы их нельзя было принять за конец описания. Это называется экранированием и более подробно рассматривается в разделе 11.3.2.) Если вы наведете курсор мыши на verb, появится текст справки. Он будет выглядеть примерно так: | + | Обратите внимание, что в этом примере определен "текст справки"(desc) для ''verb''. Сначала в круглых скобках описывается синтаксис, а затем указывается назначение команды. (Обратные косые черты перед двойными кавычками внутри текста ставятся для того, чтобы их нельзя было принять за конец описания. Это называется экранированием и более подробно рассматривается в разделе 11.3.2.) Если вы наведете курсор мыши на ''verb'', появится текст справки. Он будет выглядеть примерно так: |
*click*: set-name "new name" (Change your name.) | *click*: set-name "new name" (Change your name.) | ||
Строка 266: | Строка 263: | ||
Обратите внимание, что игроки должны находиться на расстоянии вытянутой руки, чтобы написать сообщение. Мы предполагаем, что у всех хорошее зрение, поэтому дополнительная команда чтения работает до тех пор, пока свиток находится в поле зрения. | Обратите внимание, что игроки должны находиться на расстоянии вытянутой руки, чтобы написать сообщение. Мы предполагаем, что у всех хорошее зрение, поэтому дополнительная команда чтения работает до тех пор, пока свиток находится в поле зрения. | ||
− | Очень просто сделать для иконки игрока то же самое, что мы только что сделали для описания. Вот verb, который делает это. | + | Очень просто сделать для иконки игрока то же самое, что мы только что сделали для описания. Вот ''verb'', который делает это. |
mob/verb/set_icon(i as icon) | mob/verb/set_icon(i as icon) | ||
Строка 278: | Строка 275: | ||
Здесь одинарные кавычки окружают имя файла. Как и в случае с текстовыми аргументами, последняя кавычка необязательна, если нет дополнительных параметров. | Здесь одинарные кавычки окружают имя файла. Как и в случае с текстовыми аргументами, последняя кавычка необязательна, если нет дополнительных параметров. | ||
− | == | + | == Генерация выходных данных == |
− | + | Простота ''verb/set_icon'' скрывает за ним невероятную мощь. Подумайте, что происходит, когда вы его используете. Вы вводите имя файла с иконкой через клиент, и она волшебным образом появляется на карте. В игре, работающей по сети с несколькими пользователями, это работает точно так же. За кулисами указанный вами файл передается на сервер, который затем автоматически рассылает его всем остальным клиентам. Передача мультимедийных файлов по сети - это элементарная операция в DM, мало чем отличающаяся от ввода цифр или текста. | |
− | + | Раз уж речь зашла о мультимедиа, вот пример, в котором используется тип ввода ''sound''. | |
mob/verb/play(snd as sound) | mob/verb/play(snd as sound) | ||
view() << snd | view() << snd | ||
− | + | Это воспроизводит звуковой файл (wav или midi) для всех, кто находится в поле зрения. Оператор << в данном контексте используется для отправки вывода в толпу или список толп. В этом случае звуковой файл получают все, кто находится в списке, вычисляемом инструкцией view(). (Если быть точным, то только те, кому нужна копия звукового файла, получат его. Если у них не включен звук или если у них уже есть файл, он не будет передан.) Если их машина способна воспроизводить звуки и их клиент настроен на это, звук будет автоматически воспроизведен для них. Неплохо для двух строк кода! | |
− | + | Оператор вывода << открывает всевозможные возможности. Например, вы можете сказать. | |
mob/verb/say(msg as text) | mob/verb/say(msg as text) | ||
view() << msg | view() << msg | ||
− | === | + | === Переменные в тексте === |
− | + | Обычно, однако, требуется указать, кто говорит. Для этого вам понадобится новая часть магии, называемая встроенным текстовым выражением. Оно позволяет отображать текст, содержащий переменные. | |
mob/verb/say(msg as text) | mob/verb/say(msg as text) | ||
view() << "[usr] says, '[msg]'" | view() << "[usr] says, '[msg]'" | ||
− | |||
− | + | В этом примере переменные ''usr'' и ''msg'' подставляются в текст перед его отображением. В результате может получиться что-то вроде «Ug says, 'gimme back my club!». Как видите, скобки [ ] внутри текста используются для окружения переменных. Такая конструкция называется встроенным выражением. Оно находится прямо в тексте, но во время выполнения заменяется своим значением. Более подробно об этом будет рассказано в разделе 11.3. | |
+ | |||
+ | Теперь мы можем использовать типы ввода объектов. Например, вы можете подмигнуть людям с помощью следующего ''verb''. | ||
mob/verb/wink(M as mob) | mob/verb/wink(M as mob) | ||
view() << "[usr] winks at [M]." | view() << "[usr] winks at [M]." | ||
− | + | Возможности для интриг и секретности возрастают с тайной версией команды "say". | |
mob/verb/whisper(M as mob,msg as text) | mob/verb/whisper(M as mob,msg as text) | ||
M << "[usr] whispers, '[msg]'" | M << "[usr] whispers, '[msg]'" | ||
− | + | В этом примере для достижения цели используются два аргумента. Первый - это цель сообщения. Второй - текст, который нужно передать. | |
− | |||
− | == | + | == Гибкость в выборе источника == |
− | + | Если вы внимательно следите за этим, то, возможно, вам пришла в голову мысль. Не могут ли эти verb, которые принимают объект в качестве аргумента, быть написаны с использованием этого объекта в качестве источника, а не аргумента? Ответ - да. Например, ''verb/wink'' можно переписать следующим образом: | |
mob/verb/wink() | mob/verb/wink() | ||
Строка 323: | Строка 320: | ||
view() << "[usr] winks at [src]." | view() << "[usr] winks at [src]." | ||
− | + | Вместо того чтобы принимать моба в качестве аргумента, этот ''verb'' определяет публичный ''verb'' на мобах, который доступен другим людям, находящимся в поле зрения, и позволяет им подмигнуть мобу. С точки зрения пользователя, эти два случая идентичны. Однако с точки зрения программиста иногда удобнее использовать одну технику, а не другую. | |
− | + | Предположим, вы хотите создать особый тип мобов, которые, если им подмигнуть, откроют волшебное слово. В этом случае лучше всего сделать так, чтобы цель подмигивания была источником ''verb''. Тогда вы сможете переопределить ''verb'' подмигивания для особого типа мобов следующим образом: | |
mob/guard/wink() | mob/guard/wink() | ||
Строка 331: | Строка 328: | ||
usr << "[src] whispers, 'drow cigam!'" | usr << "[src] whispers, 'drow cigam!'" | ||
− | + | Когда ему подмигивают, охранник(guard) шепчет в ответ волшебное слово. Обратите внимание на строку, выполняющую процедуру ".." (точка-точка). Это специальное имя, которое соответствует предыдущей (унаследованной) версии ''verb'' (так называемый родительский или супер verb). Это избавило нас от необходимости переписывать строку, генерирующую вывод "wink". Именно это подразумевается под повторно используемым кодом в объектно-ориентированном программировании. В сложных процедурах это может избавить вас от множества проблем. | |
− | + | Если бы вместо этого мы использовали оригинальную версию "wink", в которой целевой моб был аргументом, нам пришлось бы вставить код в общий ''verb/wink'', чтобы проверить, является ли целевой моб гвардом, и действовать соответствующим образом. В целом, хороший объектно-ориентированный подход позволяет ограничить код, специфичный для определенного типа объекта, рамками определения этого объекта. В приведенном выше примере это было достигнуто за счет того, что цель стала источником глагола подмигивания. | |
− | + | В другой ситуации лучшей стратегией может оказаться обратная. Например, вам может понадобиться особый моб, который убивает людей, подмигивая им. (Если бы взгляды могли убивать...) | |
mob/DM/wink(M as mob) | mob/DM/wink(M as mob) | ||
Строка 342: | Строка 339: | ||
del M //poof! | del M //poof! | ||
− | + | Чтобы сделать это неприятное дело, мы использовали инструкцию ''del'', которая удаляет объект. В данном случае мы предположили существование первого определения wink(), которое принимает аргумент mob. Организовав все таким образом, мы смогли изолировать специальный код от объекта, к которому он применялся, в данном случае DM. | |
− | + | Конечно, у вас могут быть еще более сложные сценарии, в которых вы захотите сделать обе вариации - то есть иметь код, специфичный для типа цели и пользователя. С этим можно справиться, не нарушая хорошего объектно-ориентированного подхода, но вам понадобятся некоторые инструменты, которые мы еще не полностью описали. (Например, вы можете определить вторую процедуру, выполняющую реакцию моба на подмигивание, и вызвать ее из частного ''verb/wink''). | |
− | + | Однако не стоит слишком увлекаться, пытаясь слепо следовать объектно-ориентированной или любой другой философии проектирования кода. В основе всех подобных теорий лежит основной и более важный принцип - сохранять простоту. Чем проще все, тем меньше вероятность ошибок и тем легче их исправить!!! | |
− | + | Причина, по которой объектно-ориентированный подход пытается ограничить код об объекте этим объектом, в конечном счете просто организационная. Когда вы хотите изменить волшебное слово, произнесенное охранником, вы знаете, куда идти в коде, чтобы сделать это. И что еще более важно, если вы захотите заменить охранника на эльфа, вам не придется вспоминать, что нужно еще и модифицировать ''verb/wink'', чтобы волшебное слово произносили эльфы, а не охранники, - задача, казалось бы, не имеющая отношения к делу. | |
− | + | Но такие возможности гипотетичны и не должны превалировать над вашими собственными знаниями о том, какое развитие событий действительно вероятно. В некоторых ситуациях самой простой структурой может быть ограничение всего кода, имеющего отношение к подмигиванию, одним местом - единственным ''verb'' подмигивания, который обрабатывает все особые случаи. Это было бы процедурно-ориентированное программирование, устаревшая методология (хотя и проверенная и верная). Но кого волнует теория. Выполняйте работу с помощью четкого, лаконичного кода. Это то, что в конечном итоге имеет значение. | |
− | ( | + | (Конечно, каждый настоящий программист знает, что конца не существует. В лучшем случае есть уровень полноты, к которому человек приближается асимптотически. В худшем... ну, неважно, в худшем, эти темные скелеты, трупные программы-паразиты, которые высасывают душу, пока человек снова не сдастся, набивая очередную тысячу строк запутанного спагетти-кода, извивающегося, как гнездо ленточных червей, и долгие бессонные ночи превращаются в недели, годы, а признаков завершения все еще нет! Так мне сказали. Будучи одним из веселых дневных программистов, ложащихся спать до полуночи и встающих на рассвете, я не знаю, правдивы ли такие истории ужасов. Если это случится с вами, я могу дать несколько советов по содержанию коровы). |
+ | == Выбор аргументов == | ||
− | + | Обратите внимание, что в предыдущем разделе в двух версиях "wink" была небольшая асимметрия. В одном случае целью был аргумент моба, а в другом - источник. В последнем случае мы указали, что источник может находиться в любом месте в поле зрения пользователя, но в случае с аргументом мы ничего не сказали о дальности действия. Что, если мы захотим ограничить подмигивание меньшим расстоянием в этом случае? | |
− | + | Как оказалось, аргументы могут быть ограничены примерно так же, как и источник ''verb''. | |
− | + | VerbName(Var1 as Type1 in List1,Var2 as Type2 in List2,...) | |
− | + | Например, вот ''verb'', с помощью которого можно подтолкнуть своего соседа. | |
− | |||
mob/verb/poke(M as mob in view(1)) | mob/verb/poke(M as mob in view(1)) | ||
view() << "[usr] pokes [M]!" | view() << "[usr] pokes [M]!" | ||
− | + | Использование выражения ''in'' в определении аргумента ограничивает выбор пользователя мобами, находящимися в соседних позициях. Для определения набора возможных значений можно использовать любое списочное выражение. Наиболее распространенными являются те, что используются для определения диапазона источника ''verb'' (раздел 4.3.3). Если выражение ''in'' не используется, списком по умолчанию для типов ввода mob, obj, turf и area является ''view()''. | |
− | + | Например, вот ''verb'', позволяющий общаться (с помощью частотно-модулированных электромагнитных волн) с любым другим игроком в игре. | |
mob/verb/commune(M as mob in world,txt as text) | mob/verb/commune(M as mob in world,txt as text) | ||
M << "[usr] communes to you, '[txt]'" | M << "[usr] communes to you, '[txt]'" | ||
− | + | На самом деле, ''world'' - это отдельный объект, но в данном контексте он рассматривается как список и поэтому является сокращением от ''world.contents'', списка всех объектов в игре. | |
+ | == Аргументы по умолчанию == | ||
− | + | Аргументы verb можно сделать необязательными, используя тип ввода null. Если пользователь не введет значение аргумента, ему будет присвоено специальное значение null. В этом случае часто требуется проверить, действительно ли аргумент является нулевым, и поступить соответствующим образом. Это можно автоматизировать в том случае, если нужно просто подставить значение по умолчанию вместо null, присвоив его в определении переменной. | |
− | + | Наиболее общий синтаксис определения аргумента содержит имя переменной, значение по умолчанию, тип ввода и список возможных значений. | |
− | + | variable = default-value as input-type in list | |
− | + | Если указано значение по умолчанию, автоматически применяется тип ввода null, поскольку это необходимо для того, чтобы сделать аргумент необязательным. | |
− | |||
mob/DM | mob/DM | ||
Строка 392: | Строка 389: | ||
density = d | density = d | ||
− | + | Этот пример определяет ''verb'', который управляет плотностью игрока. Если аргументы не указаны, моб станет плотным, в противном случае будет использовано значение, указанное игроком. | |
− | == | + | == Любой тип ввода == |
− | + | Тип ввода anything позволяет комбинировать другие типы ввода с элементами из списка. Его назначение - дать понять, что константные типы входа дополняют все, что может находиться в списке объектов. | |
− | + | Следующий пример позволяет изменить значок с возможностью выбора значка из файла или из любого другого объекта в поле зрения. | |
mob/verb/set_icon(I as icon|anything in view()) | mob/verb/set_icon(I as icon|anything in view()) | ||
icon = I | icon = I | ||
− | + | ||
+ | Обратите внимание, что это работает потому, что присвоение объекта переменной ''icon'' приводит к тому, что вместо нее присваивается значок этого объекта. (Такое поведение существует потому, что я не хотел пока вводить условные операторы. Вот через какие препятствия приходится прыгать программисту, чтобы избежать лишней документации!) |
Текущая версия от 18:48, 16 августа 2024
Это 4 глава перевода оригинального руководства по Dream Maker от разработчиков.
Остальные главы руководства и статьи о программировании на коде BYOND от любителей на русском.
Оригинальная 4 глава руководства на английском языке
Глава 4: Действия verb
В конце концов, это может быть всего лишь сон. Только часть и лишь один слой этого пристанища грез. — Л.М. Монтогомери, Пристанище снов Анны.
У игроков есть несколько способов взаимодействовать с окружающим миром: они могут передвигаться с помощью стрелок на клавиатуре, выбирать объекты с помощью мыши и вводить команды (или выбирать их в меню). В DM команда, которую вы создаете для использования игроками, называется verb (действие). Они составляют язык общения игроков с сервером.
Создание действия
Действия всегда задаются для объектов путем определения этого действия внутри описания объекта. Такой объект называют источником действия (source). Примитивный случай: действие, привязанное к существу игрока.
mob verb intangible() density = 0 //now we can walk through walls!
Этот пример описывает действие "нематериальный" (intangible). Игрок, который использует его, получает возможность передвигаться сквозь материальные объекты, вроде стен.
Как вы, наверное, заметили, определение записывается под узлом verb и сопровождается круглыми скобками. Само название действия, как и любой другой узел в вашей программе, должно удовлетворять некоторым условиям: оно чувствительно к регистру, может состоять из букв, цифр и подчеркиваний, а также не должно начинаться с цифры.
Свойства описания действия
Также, как и с объектами, вы можете задать новое имя для действия (видимое пользователем), чтобы обойти ограничения для названий узлов. Это можно сделать с помощью специальной команды:
obj/lamp/verb/Break() set name = "break" luminosity = 0
Причина того, что этот узел не может быть задан в нижнем регистре, лежит в том, что слово break зарезервировано. Озаглавливание - простой способ избежать конфликта с зарезервированным словом, поскольку они никогда не начинаются с большой буквы. Если вас озадачило значение break, дождитесь 6 главы.
Обратите внимание на использование множества слешей, заместо разделения отступами. Команда set по-разному используется для присвоения имен объектов и действий. Также заметьте, что присвоение имени происходит скорее во время компиляции, чем во время работы программы, хотя оно и не должно происходить до тех пор, пока игрок не вызовет действие, как и изменение переменной освещения, например.
Также существуют и другие свойства действий, описанные в следующем списке:
• name - как вы уже поняли, название действия, видимое пользователем. По умолчанию, оно соответствует названию узла действия, с заменой нижних подчеркиваний на пробелы.
• desc - описание действия. Игроки могут увидеть его, если введут название действия и нажмут F1 или просто наведут указатель мыши на это действие в меню. Синтаксис описания стандартный, но если вы захотите изменить способ появления новых аргументов, то вы можете сделать это в скобочках в начале текста. Смотри страницу [4.5.1] для примера.
• category - действия могут группироваться по категориям, имена для которых вы задаете этим свойством. Категории появляются на панели действий.
• hidden - может принимать значения 1 или 0, чтобы определить скрытые действия. Такие действия не появляются на панели или в меню. Как вариант облегченной версии спрятанных действий, вы можете начать их название с точки. Тогда они будут работать как скрытые, но появятся в меню, если ввести точку. Вы можете, например, использовать их, чтобы задать кучу социальных действий, которыми вы не хотели бы захламлять меню.
• src - описывает, где вы определяете отношения между пользователем и источником действия. Влияет на то, кто имеет контроль к действию. Смотри объяснение в следующей секции.
Доступ к действиям
Одни из самых важных аспектов действий - это то, к чему они прикреплены и кто их может использовать. Действие "нематериальный", которое мы рассматривали ранее в этой главе, прикреплено к существу и доступно только для игрока, который управляет этим существом. Вы можете использовать свое собственное действие нематериальности, а другие игроки могут использовать свои, такие же действия, но вы не можете использовать их друг за друга.
Конечно, в некоторых случаях, вы захотите разрешить людям использовать действия друг друга. Чтобы это сделать, вам нужно будет переписать стандартные настройки доступа. Перед тем, как приступить к этому, вам нужно понять как работают настройки по умолчанию.
Когда действие прикреплено к существу, автоматические настройки ставятся в положение src = usr. Это означает, что источник действия (source) должен быть эквивалентен тому, кто использует это действие (user). Никто больше не может их использовать или даже видеть. Этот оператор использует две предопределенные переменные: src обращается к объекту, который хранит действие, источнику (source), а usr обращается к существу, которое использует действие (user).
Это самое обычное действие, которое позволяет людям превращать друг друга в картошку:
mob/verb/make_potato() set src in view() name = "potato" desc = "Mmm. Where's the sour cream?" icon = 'potato.dmi'
Вместо стандартных настроек доступа (src = usr), это действие использует src in view(). Это означает, что любой в поле зрения пользователя может быть превращен в картошку. Процедура view возвращает список всего, что находится в поле зрения того, кто ее использует.
Также обратите внимание на то, что в имени действия использовалось нижнее подчеркивание. Оно автоматически превратится в пробел в имени команды. Однако, если пользователь хочет ввести эту команду, то ему нужно вводить дефис "-", а не пробел. Это связанно с тем, что пробелы в командной строке могут использоваться только между аргументами или внутри ковычек. Пользователю не стоит волноваться, замена дефисов происходит автоматически.
Теперь рассмотрим действие, привязанное к объекту:
obj/torch/verb/extinguish() set src in view(1) luminosity = 0
Мам, смотри, без рук!
(и другие способы избежать набора...)
Вам не нужно волноваться об игроках, которым приходится набирать длинные команды. Dream Seeker предусматривает множество удобных путей облегчить жизнь пальцам пользователей. Вот несколько способов использовать вышеупомянутое действие тушения факела:
- Ввести "extinguish torch".
- Ввести первую пару букв команды, например, "ex" и нажать пробел, чтобы автодополнить ввод. Если что, Dream Seeker укажет вам на двусмысленность специальным окном.
- Кликнуть по "extinguish" на панели действий.
- Щелкнуть правой кнопкой мыши по факелу, чтобы вывести контекстное меню и выбрать требуемое действие.
Действие extinguish в этом примере заставляет факел потухнуть. Мы опять используем view, но на этот раз вместе с дополнительным параметром, которое указывает на дальность действия. В этом случае, с дальностью 1, факел должен быть или на том же месте, что и существо, использующее действие, или на соседней клетке. Кто-нибудь в другом конце комнаты не получит доступа к этому действию и потушить факел не сможет.
Опробуйте этот пример на подходящей карте с несколькими факелами, разбросанными вокруг. Если вы подойдете к факелу и наберете "extinguish torch" - он потухнет (смотри вставку).
Вы могли заметить тонкую разницу между действием тушения и нематериальности, которое мы описывали ранее. В одном случае нам нужно ввести лишь "instangible", а в другом "extinguish torch". Дело в том, что в первом случае нам не нужно указывать источник действия поскольку он итак понятен, а во втором нужно.
Неявный источник против Явного
Предположим, что кто-то исповедует религию, в которой молиться необходимо в непосредственной близости от факела. Мы не хотим команду "pray torch", а хотим просто "pray". Другими словами мы хотим использовать неявный источник, вместо явного.
Используем мы явный или неявный источник - зависит от того, как заданы параметры src. Если src однозначно определен (например, вы написали src=usr или даже src=view(1)), компьютер автоматически будет использовать доступный источник. С другой стороны, если src не определен, а просто ограничен определенным списком (например, src in view(1)), то пользователю придется указать источник самому, даже если в списке всего один пункт. Так мы можем контролировать синтаксис вводимой команды.
Вернемся к примеру факела, включающего молитвы. Раз мы хотим неявный источник, то нам нужно использовать оператор "=", а не "in", чтобы ограничить варианты для компьютера.
obj/torch/verb/pray() set src = view(1) //God fills in this part!
В основном, неявное определение источника используют, когда лишь присутствие предмета дает пользователю новые способности, которые, в общем-то не зависят от того, какой конкретно объект их дает. Также их используют, когда объект-источник всегда один и тот же (например, как src=usr). Действия из последнего случая называют личными действия, поскольку они доступны только самому существу.
Доступ по умолчанию
Для удобства verb, относящиеся к разным типам объектов, имеют разную доступность по умолчанию. Они обобщены на рисунке 4.6.
Настройки действий по умолчанию
- mob src = usr
- obj src in usr
- turf src = view(0)
- area src = view(0)
Обратите внимание, что доступность obj по умолчанию на самом деле является сокращением src в usr.contents, что означает содержимое (или инвентарь) моба пользователя. Как вы увидите позже, оператор in всегда рассматривает правую часть как список - поэтому в данном контексте usr рассматривается как usr.contents.
В случае с постройками(turf) и областью(area) доступность по умолчанию равна view(0) и является неявной. Поскольку диапазон равен нулю, это дает игроку доступ к verb постройки(turf) и области(area), на которой стоит моб.
Используя эту удобную настройку по умолчанию, предположим, что в темном помещении нужно установить магический триггер, который будет включать свет при звуке хлопка в ладоши. Это можно сделать следующим образом:
area/dark/verb/clap() luminosity = 1
Абракадабра!
Возможные настройки доступа
Существует ограниченное количество возможных настроек для списка доступа к источнику глагола. Они приведены на рисунке 4.7.
Рисунок 4.7: Возможные настройки доступа к источнику
usr usr.loc usr.contents usr.group view() oview()
Два из них (а именно usr и usr.loc) не являются списками объектов, а ссылаются на отдельный элемент. По этой причине они ведут себя немного по-другому, когда используются в присваивании и в операторе in. Пусть вас это не смущает - на самом деле все очень просто. При использовании в присваивании они рассматриваются как один объект. При использовании с in они представляют собой список объектов, которые они содержат.
Остальные параметры доступа к src представляют собой списки. Инструкция view уже упоминалась, но она требует более подробного описания, чтобы вы могли понять связанную с ней инструкцию oview.
Список view() содержит все объекты, видимые пользователем на заданном расстоянии. Список начинается с объектов в инвентаре пользователя, затем идет сам пользователь, объекты у его ног, затем в соседних квадратах и так далее по радиусу наружу. Расстояние 0, таким образом, включает в себя постройки(turf) (и область(area)), на котором стоит пользователь, и его содержимое. Расстояние 1 добавляет соседние восемь квадратов и их содержимое. Максимальное расстояние - 5, так как оно включает в себя всю видимую карту на экране игрока. (Как изменить размер области просмотра карты, вы узнаете в главе 14.) Поскольку этот диапазон часто является желаемым, он используется по умолчанию, когда диапазон не указан. Специальный диапазон -1 включает только пользователя и содержимое.
Связанная с ней инструкция oview() означает "другой" или "внешний" вид. Она идентична view(), за исключением того, что не включает в себя usr или содержимое usr. Другими словами, она исключает объекты в view(-1), так называемый приватный или личный диапазон.
В качестве примера использования usr и usr.loc рассмотрим пару verb, позволяющих поднимать и опускать предметы.
obj/verb get() set src in usr.loc loc = usr drop() set src in usr //actually this is the default loc = usr.loc
Чтобы увидеть, как может пригодиться oview(), предположим, что существует магический факел, который можно призвать с большего расстояния, чем позволяет стандартный verb/get.
obj/torch/verb/summon() set src in oview() loc = usr //presto!
Используя диапазон по умолчанию, факелы можно призвать из любого места в поле зрения игрока. Если бы мы использовали view() вместо oview(), можно было бы вызывать объекты, уже находящиеся в инвентаре пользователя, что не имело бы особого смысла.
Переопределение verb
Предположим, что вместо магического verb/summon мы просто хотим, чтобы verb/get имел больший диапазон для факелов. Это пример объектно-ориентированного программирования, в котором дочерний тип объекта предоставляет те же операции, что и его родитель, но реализует их по-другому.
Вот код для такого модифицированного verb/get.
obj verb get() set src in usr.loc loc = usr drop() set src in usr loc = usr.loc torch get() //extended range set src in oview() loc = usr
Пример написан полностью, чтобы продемонстрировать синтаксис переопределения ранее определенного verb. Обратите внимание, что внутри "torch" мы не помещаем get() под узлом verb. Это указывает компилятору, что мы переопределяем существующий глагол, а не определяем новый.
Разница в синтаксисе определения и переопределения служит (помимо прочего) для предотвращения ошибок, которые могут возникнуть в больших проектах. Например, вы можете определить новый verb, который по ошибке имеет то же имя, что и существующий, или попытаться переопределить существующий verb, но неправильно написать его в переопределении. В обоих случаях компилятор выдаст ошибку, предотвращая проблему, которая в противном случае могла бы оставаться незамеченной довольно долгое время.
Дружеские аргументы
Действия(verb) становятся гораздо мощнее, если добавить к ним возможность принимать дополнительные данные от пользователя. Программист называет это параметром или аргументом verb. Вы можете определить столько аргументов для verb, сколько пожелаете. Однако большинство verb принимают только один или два параметра.
Аргументам присваивается отдельное имя переменной внутри круглых скобок определения verb. Кроме того, необходимо указать тип ввода. Он указывает, какого рода информация требуется от игрока. Таким образом, общее определение глагола будет выглядеть следующим образом:
VerbName(Var1 as Type1,Var2 as Type2,...)
Имена переменных подчиняются тем же правилам, что и все остальные в языке. Регистр имеет значение, и они могут состоять из букв, цифр и подчеркивания.
Типы входных параметров
Возможные типы входных параметров перечислены на рисунке 4.8.
Рисунок 4.8: Типы входных параметров
text message num icon sound file key null mob obj turf area anything
Первая группа - это постоянные типы ввода. Все они представляют собой различные типы данных, которые может вставить пользователь. Они могут использоваться по отдельности или в комбинации. Чтобы объединить несколько типов, поставьте между ними знак "|", например: icon|sound.
Тип ввода text принимает от игрока короткую (однострочную) строку. Для более длинной композиции используется тип ввода message.
Для работы с числами используется тип ввода num. Они, как и числа в языке, могут быть положительными или отрицательными, целыми или с плавающей точкой, и даже могут быть заданы в научной системе счисления.
Существует три типа входных данных для файлов ресурсов: icon, sound и file. Последний, file, принимает в качестве аргумента файл любого типа, в то время как два других принимают только иконки и звуки соответственно. Связанный с ним тип ввода key принимает от игрока ввод клавиши и используется только в неясных ситуациях.
Тип ввода null используется в сочетании с другими типами. Он указывает на то, что аргумент является необязательным. В противном случае пользователь должен ввести значение перед выполнением команды.
Последняя группа - это типы ввода объектов. Они используются для того, чтобы игрок мог выбрать предмет из списка объектов. Подробнее о списках объектов будет рассказано в разделе 4.8. По умолчанию список состоит из всех объектов, находящихся в поле зрения игрока.
Используя различные типы ввода, можно составлять verb, которые дают игроку контроль над собственной внешностью. Например, с помощью текстового типа ввода можно указать имя.
mob/verb/set_name(N as text) set desc = "(\"new name\") Change your name." name = N
Поэтому в клиенте можно ввести команду, подобную следующей:
set-name "Dan The Man"
Обратите внимание, что в этом примере определен "текст справки"(desc) для verb. Сначала в круглых скобках описывается синтаксис, а затем указывается назначение команды. (Обратные косые черты перед двойными кавычками внутри текста ставятся для того, чтобы их нельзя было принять за конец описания. Это называется экранированием и более подробно рассматривается в разделе 11.3.2.) Если вы наведете курсор мыши на verb, появится текст справки. Он будет выглядеть примерно так:
*click*: set-name "new name" (Change your name.)
Если бы мы не указали справку по синтаксису (в круглых скобках), она дала бы общее описание синтаксиса, например, такое:
*click*: set-name "text" (Change your name.)
Каждый тип аргумента имеет свой вид по умолчанию. Как правило, он включает в себя название типа ввода. Если вы считаете, что это запутает игроков, замените его своим собственным текстом.
В качестве небольшой вариации предыдущего примера мы можем сделать объект-свиток, на котором можно написать сообщение.
obj/scroll/verb write(msg as message) set src in view(0) desc = msg read() set src in view() usr << desc
Обратите внимание, что игроки должны находиться на расстоянии вытянутой руки, чтобы написать сообщение. Мы предполагаем, что у всех хорошее зрение, поэтому дополнительная команда чтения работает до тех пор, пока свиток находится в поле зрения.
Очень просто сделать для иконки игрока то же самое, что мы только что сделали для описания. Вот verb, который делает это.
mob/verb/set_icon(i as icon) set name = "set icon" icon = i
Команда на клиенте может быть выдана примерно так:
set-icon 'me.dmi'
Здесь одинарные кавычки окружают имя файла. Как и в случае с текстовыми аргументами, последняя кавычка необязательна, если нет дополнительных параметров.
Генерация выходных данных
Простота verb/set_icon скрывает за ним невероятную мощь. Подумайте, что происходит, когда вы его используете. Вы вводите имя файла с иконкой через клиент, и она волшебным образом появляется на карте. В игре, работающей по сети с несколькими пользователями, это работает точно так же. За кулисами указанный вами файл передается на сервер, который затем автоматически рассылает его всем остальным клиентам. Передача мультимедийных файлов по сети - это элементарная операция в DM, мало чем отличающаяся от ввода цифр или текста.
Раз уж речь зашла о мультимедиа, вот пример, в котором используется тип ввода sound.
mob/verb/play(snd as sound) view() << snd
Это воспроизводит звуковой файл (wav или midi) для всех, кто находится в поле зрения. Оператор << в данном контексте используется для отправки вывода в толпу или список толп. В этом случае звуковой файл получают все, кто находится в списке, вычисляемом инструкцией view(). (Если быть точным, то только те, кому нужна копия звукового файла, получат его. Если у них не включен звук или если у них уже есть файл, он не будет передан.) Если их машина способна воспроизводить звуки и их клиент настроен на это, звук будет автоматически воспроизведен для них. Неплохо для двух строк кода!
Оператор вывода << открывает всевозможные возможности. Например, вы можете сказать.
mob/verb/say(msg as text) view() << msg
Переменные в тексте
Обычно, однако, требуется указать, кто говорит. Для этого вам понадобится новая часть магии, называемая встроенным текстовым выражением. Оно позволяет отображать текст, содержащий переменные.
mob/verb/say(msg as text) view() << "[usr] says, '[msg]'"
В этом примере переменные usr и msg подставляются в текст перед его отображением. В результате может получиться что-то вроде «Ug says, 'gimme back my club!». Как видите, скобки [ ] внутри текста используются для окружения переменных. Такая конструкция называется встроенным выражением. Оно находится прямо в тексте, но во время выполнения заменяется своим значением. Более подробно об этом будет рассказано в разделе 11.3.
Теперь мы можем использовать типы ввода объектов. Например, вы можете подмигнуть людям с помощью следующего verb.
mob/verb/wink(M as mob) view() << "[usr] winks at [M]."
Возможности для интриг и секретности возрастают с тайной версией команды "say".
mob/verb/whisper(M as mob,msg as text) M << "[usr] whispers, '[msg]'"
В этом примере для достижения цели используются два аргумента. Первый - это цель сообщения. Второй - текст, который нужно передать.
Гибкость в выборе источника
Если вы внимательно следите за этим, то, возможно, вам пришла в голову мысль. Не могут ли эти verb, которые принимают объект в качестве аргумента, быть написаны с использованием этого объекта в качестве источника, а не аргумента? Ответ - да. Например, verb/wink можно переписать следующим образом:
mob/verb/wink() set src in view() view() << "[usr] winks at [src]."
Вместо того чтобы принимать моба в качестве аргумента, этот verb определяет публичный verb на мобах, который доступен другим людям, находящимся в поле зрения, и позволяет им подмигнуть мобу. С точки зрения пользователя, эти два случая идентичны. Однако с точки зрения программиста иногда удобнее использовать одну технику, а не другую.
Предположим, вы хотите создать особый тип мобов, которые, если им подмигнуть, откроют волшебное слово. В этом случае лучше всего сделать так, чтобы цель подмигивания была источником verb. Тогда вы сможете переопределить verb подмигивания для особого типа мобов следующим образом:
mob/guard/wink() ..() usr << "[src] whispers, 'drow cigam!'"
Когда ему подмигивают, охранник(guard) шепчет в ответ волшебное слово. Обратите внимание на строку, выполняющую процедуру ".." (точка-точка). Это специальное имя, которое соответствует предыдущей (унаследованной) версии verb (так называемый родительский или супер verb). Это избавило нас от необходимости переписывать строку, генерирующую вывод "wink". Именно это подразумевается под повторно используемым кодом в объектно-ориентированном программировании. В сложных процедурах это может избавить вас от множества проблем.
Если бы вместо этого мы использовали оригинальную версию "wink", в которой целевой моб был аргументом, нам пришлось бы вставить код в общий verb/wink, чтобы проверить, является ли целевой моб гвардом, и действовать соответствующим образом. В целом, хороший объектно-ориентированный подход позволяет ограничить код, специфичный для определенного типа объекта, рамками определения этого объекта. В приведенном выше примере это было достигнуто за счет того, что цель стала источником глагола подмигивания.
В другой ситуации лучшей стратегией может оказаться обратная. Например, вам может понадобиться особый моб, который убивает людей, подмигивая им. (Если бы взгляды могли убивать...)
mob/DM/wink(M as mob) set desc = "This kills people, so be careful!" ..() del M //poof!
Чтобы сделать это неприятное дело, мы использовали инструкцию del, которая удаляет объект. В данном случае мы предположили существование первого определения wink(), которое принимает аргумент mob. Организовав все таким образом, мы смогли изолировать специальный код от объекта, к которому он применялся, в данном случае DM.
Конечно, у вас могут быть еще более сложные сценарии, в которых вы захотите сделать обе вариации - то есть иметь код, специфичный для типа цели и пользователя. С этим можно справиться, не нарушая хорошего объектно-ориентированного подхода, но вам понадобятся некоторые инструменты, которые мы еще не полностью описали. (Например, вы можете определить вторую процедуру, выполняющую реакцию моба на подмигивание, и вызвать ее из частного verb/wink).
Однако не стоит слишком увлекаться, пытаясь слепо следовать объектно-ориентированной или любой другой философии проектирования кода. В основе всех подобных теорий лежит основной и более важный принцип - сохранять простоту. Чем проще все, тем меньше вероятность ошибок и тем легче их исправить!!!
Причина, по которой объектно-ориентированный подход пытается ограничить код об объекте этим объектом, в конечном счете просто организационная. Когда вы хотите изменить волшебное слово, произнесенное охранником, вы знаете, куда идти в коде, чтобы сделать это. И что еще более важно, если вы захотите заменить охранника на эльфа, вам не придется вспоминать, что нужно еще и модифицировать verb/wink, чтобы волшебное слово произносили эльфы, а не охранники, - задача, казалось бы, не имеющая отношения к делу.
Но такие возможности гипотетичны и не должны превалировать над вашими собственными знаниями о том, какое развитие событий действительно вероятно. В некоторых ситуациях самой простой структурой может быть ограничение всего кода, имеющего отношение к подмигиванию, одним местом - единственным verb подмигивания, который обрабатывает все особые случаи. Это было бы процедурно-ориентированное программирование, устаревшая методология (хотя и проверенная и верная). Но кого волнует теория. Выполняйте работу с помощью четкого, лаконичного кода. Это то, что в конечном итоге имеет значение.
(Конечно, каждый настоящий программист знает, что конца не существует. В лучшем случае есть уровень полноты, к которому человек приближается асимптотически. В худшем... ну, неважно, в худшем, эти темные скелеты, трупные программы-паразиты, которые высасывают душу, пока человек снова не сдастся, набивая очередную тысячу строк запутанного спагетти-кода, извивающегося, как гнездо ленточных червей, и долгие бессонные ночи превращаются в недели, годы, а признаков завершения все еще нет! Так мне сказали. Будучи одним из веселых дневных программистов, ложащихся спать до полуночи и встающих на рассвете, я не знаю, правдивы ли такие истории ужасов. Если это случится с вами, я могу дать несколько советов по содержанию коровы).
Выбор аргументов
Обратите внимание, что в предыдущем разделе в двух версиях "wink" была небольшая асимметрия. В одном случае целью был аргумент моба, а в другом - источник. В последнем случае мы указали, что источник может находиться в любом месте в поле зрения пользователя, но в случае с аргументом мы ничего не сказали о дальности действия. Что, если мы захотим ограничить подмигивание меньшим расстоянием в этом случае?
Как оказалось, аргументы могут быть ограничены примерно так же, как и источник verb.
VerbName(Var1 as Type1 in List1,Var2 as Type2 in List2,...)
Например, вот verb, с помощью которого можно подтолкнуть своего соседа.
mob/verb/poke(M as mob in view(1)) view() << "[usr] pokes [M]!"
Использование выражения in в определении аргумента ограничивает выбор пользователя мобами, находящимися в соседних позициях. Для определения набора возможных значений можно использовать любое списочное выражение. Наиболее распространенными являются те, что используются для определения диапазона источника verb (раздел 4.3.3). Если выражение in не используется, списком по умолчанию для типов ввода mob, obj, turf и area является view().
Например, вот verb, позволяющий общаться (с помощью частотно-модулированных электромагнитных волн) с любым другим игроком в игре.
mob/verb/commune(M as mob in world,txt as text) M << "[usr] communes to you, '[txt]'"
На самом деле, world - это отдельный объект, но в данном контексте он рассматривается как список и поэтому является сокращением от world.contents, списка всех объектов в игре.
Аргументы по умолчанию
Аргументы verb можно сделать необязательными, используя тип ввода null. Если пользователь не введет значение аргумента, ему будет присвоено специальное значение null. В этом случае часто требуется проверить, действительно ли аргумент является нулевым, и поступить соответствующим образом. Это можно автоматизировать в том случае, если нужно просто подставить значение по умолчанию вместо null, присвоив его в определении переменной.
Наиболее общий синтаксис определения аргумента содержит имя переменной, значение по умолчанию, тип ввода и список возможных значений.
variable = default-value as input-type in list
Если указано значение по умолчанию, автоматически применяется тип ввода null, поскольку это необходимо для того, чтобы сделать аргумент необязательным.
mob/DM verb set_density(d=1 as num) set name = "set density" density = d
Этот пример определяет verb, который управляет плотностью игрока. Если аргументы не указаны, моб станет плотным, в противном случае будет использовано значение, указанное игроком.
Любой тип ввода
Тип ввода anything позволяет комбинировать другие типы ввода с элементами из списка. Его назначение - дать понять, что константные типы входа дополняют все, что может находиться в списке объектов.
Следующий пример позволяет изменить значок с возможностью выбора значка из файла или из любого другого объекта в поле зрения.
mob/verb/set_icon(I as icon|anything in view()) icon = I
Обратите внимание, что это работает потому, что присвоение объекта переменной icon приводит к тому, что вместо нее присваивается значок этого объекта. (Такое поведение существует потому, что я не хотел пока вводить условные операторы. Вот через какие препятствия приходится прыгать программисту, чтобы избежать лишней документации!)