A long time ago in a galaxy far, far away...


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

Если она не будет актуализирована или не появится веского повода для снятия этой плашки, то вскоре она будет перемещена в Священный архив.

При желании вы можете помочь проекту Onyx и сообществу Animus-logo.png SS13 в целом — загляните на наш Bus Mainframes.gif Портал сообщества.
Morgue.png


Это 4 глава перевода оригинального руководства по Dream Maker от разработчиков.

Остальные главы руководства и статьи о программировании на коде BYOND от любителей на русском.

Оригинальная 4 глава руководства на английском языке

В разработке…


Jobeng.png
Данная статья помечена как неоконченная. Это означает, что статья находится на доработке, поэтому является неверной или неактуальной.

Вы можете помочь проекту Onyxyeye@256x256.png Onyx и сообществу Animus-logo.png SS13 в целом — зайдите на наш Bus Mainframes.gif Портал сообщества.


Глава 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 предусматривает множество удобных путей облегчить жизнь пальцам пользователей. Вот несколько способов использовать вышеупомянутое действие тушения факела:

  1. Ввести "extinguish torch".
  2. Ввести первую пару букв команды, например, "ex" и нажать пробел, чтобы автодополнить ввод. Если что, Dream Seeker укажет вам на двусмысленность специальным окном.
  3. Кликнуть по "extinguish" на панели действий.
  4. Щелкнуть правой кнопкой мыши по факелу, чтобы вывести контекстное меню и выбрать требуемое действие.

Действие 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'

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

Generating Output

The simplicity of the set_icon verb hides the power behind it. Think about what happens when you use it. You enter the name of an icon file through the client and it magically appears on the map. In a game running over the network with multiple users, it works just the same. Behind the scenes, the file you specify gets transported to the server, which then automatically distributes it to all the other clients. Transmitting multi-media files across the network is an elementary operation in DM, little different than entering some numbers or text.

Speaking of multi-media, here is an example that makes use of the sound input type.

mob/verb/play(snd as sound)
   view() << snd

This plays a sound file (either wav or midi) to everyone in view. The << operator in this context is used to send output to a mob or list of mobs. In this case, everyone in the list computed by the view() instruction receives a sound file. (To be precise, only those people who need a copy of the sound file will receive it. If they don't have sound turned on or if they already have the file, it won't be transmitted.) If their machine is capable of playing sounds and their client is configured to allow it, the sound will automatically play for them. Not bad for two lines of code!

The output operator << opens up all sorts of possibilities. For example, you can say things.

mob/verb/say(msg as text)
   view() << msg

Variables in Text

Usually, however, you would want some indication of who is doing the talking. To do that, you need a new piece of magic called an embedded text expression. It allows you to display text containing variables.

mob/verb/say(msg as text)
   view() << "[usr] says, '[msg]'"

In this example, the variables usr and msg are substituted into the text before it is displayed. The result could be something like "Ug says, 'gimme back my club!'". As you can see, the brackets [ ] inside of the text are used to surround the variables. Such a construct is called an embedded expression. It is buried right inside the text but it gets replaced at run-time by its value. More details will be revealed on that subject in section 11.3.

We can now make use of the object input types. For example, you can wink at people with the following verb.

mob/verb/wink(M as mob)
   view() << "[usr] winks at [M]."

The possibilities for intrigue and secrecy increase with a covert version of the say command.

mob/verb/whisper(M as mob,msg as text)
   M << "[usr] whispers, '[msg]'"

This example uses two arguments to achieve its purpose. The first one is the target of the message. The second is the text to transmit.


Flexibility in Choice of the Source

If you are paying close attention, a thought may have occurred to you. Couldn't these verbs that take an object as an argument be written using that object as the source rather than an argument? The answer is yes. For example, wink could be rewritten like this:

mob/verb/wink()
   set src in view()
   view() << "[usr] winks at [src]."

Instead of taking a mob as an argument, this verb defines a public verb on mobs that is accessible to others in view, allowing them to wink at the mob. From the user's point of view, these two cases are identical. From the programmer's view, however, it is sometimes more convenient to use one technique over the other.

Suppose you wanted to make a special type of mob that when winked at would reveal a magic word. In that case, the best way to do things would be to have the target of the wink be the source of the verb. Then you can override the wink verb for the special mob type like this:

mob/guard/wink()
   ..()
   usr << "[src] whispers, 'drow cigam!'"

When winked at, the guard mob whispers back the magic word. Notice the line that executes the .. (dot-dot) procedure. That is a special name that corresponds to the previous (inherited) version of a verb (called the parent or super verb). This saved us from having to rewrite the line that generated the wink output. That is what is meant by re-usable code in object-oriented programming. With complicated procedures it can save you a lot of trouble.

If, instead, we had used the original version of wink which had the target mob as an argument, we would have had to insert code in the general wink verb to check if the target was a guard and act accordingly. In general, good object-oriented design attempts to confine code that is specific to a particular type of object inside the definition of that object. This was achieved in the above example by making the target the source of the wink verb.

In a different situation, the reverse might be the best strategy. For example, you might want a special mob who kills people by winking at them. (If looks could kill...)

mob/DM/wink(M as mob)
   set desc = "This kills people, so be careful!"
   ..()
   del M  //poof!

To do the nasty deed, we used the del instruction, which deletes an object. In this case, we have assumed the existence of the first definition of wink() which takes a mob argument. By organizing things this way, we were able to isolate the special code to the object it applied to, in this case the DM.

Of course you might have even more complicated scenarios in which you want to do both variations--that is, having code specific to the type of target and the user. It can still be handled without violating good object-oriented design, but you would need some tools I haven't fully described yet. (For example, you could define a second procedure that carries out a mob's response to being winked at and invoke that from within a private wink verb.)

However, don't get too carried away trying to blindly adhere to object-oriented or any other philosophy of code design. At the root of all such theories is the basic and more important principle of keeping things simple. The simpler things are, the less likely you are to make mistakes and the easier it is to fix the errors you do make.

The reason the object-oriented approach tries to confine code about an object to that object is ultimately just organizational. When you want to change the magic word spoken by the guard, you know where to go in the code to do it. And more importantly, if you want to change the guard to an elf, you don't have to remember to also go and modify the wink verb to make elves speak the magic word rather than guards--a seemingly unrelated task.

But such possibilities are hypothetical and should not take precedence over your own knowledge about what future developments are actually probable. In some situations, the simplest structure might be to confine all code having to do with winking to one place--a single wink verb that handles every special case. That would be procedure-oriented programming, an aged methodology (though tried and true). But who cares what the theory is. Get the job done with clear, concise code. That's what matters in the end.

(Of course every true programmer knows that there is no such thing as an end. At best there is a level of completeness that one approaches asymptotically. At worst ... well, never mind the worst, those dark skeletons, cadaverous parasitic programs that suck at the soul until one yields again, hammering out another thousand lines of tangled spaghetti code, writhing like a nest of tapeworms, and long sleepless nights turn into weeks, years, and still no sign of completion! Or so I am told. Being one of the cheery daytime programmers, in bed before midnight and up to milk at dawn, I wouldn't know whether such horror stories are true. If it happens to you, I can give a few pointers on keeping a cow.)


A Choice of Arguments

Notice how in the previous section, there was a slight asymmetry in the two versions of wink. In one case the target was a mob argument and in the other it was the source. In the latter case, we specified that the source could be anywhere in view of the user, but in the case of the argument we never said anything about the range. What if we wanted to restrict winking to a shorter distance in that case?

As it happens, arguments can be limited in much the same way as the source of a verb.

VerbName(Var1 as Type1 in List1,Var2 as Type2 in List2,...) For example, here is a verb for prodding your neighbor.

mob/verb/poke(M as mob in view(1))
   view() << "[usr] pokes [M]!"

The use of the in clause in the argument definition limits the user's choice to mobs in neighboring positions. You can use any list expression to define the set of possible values. The most common ones are those available for defining the range of a verb source (section 4.3.3). When no in clause is used, the default list for mob, obj, turf, and area input types is view().

For example, here is a verb to communicate (by frequency modulated electromagnetic waves) with any other player in the game.

mob/verb/commune(M as mob in world,txt as text)
   M << "[usr] communes to you, '[txt]'"

Actually, world is an individual object, but in this context it is treated as a list and is therefore an abbreviation for world.contents, a list of every object in the game.


Default Arguments

Verb arguments can be made optional by using the null input type. If the user does not enter a value for the argument, it will be given the special value null. In this case, one will often need to check if the argument is indeed null and handle things accordingly. This can be automated in the case where you just want a default value to be substituted for null by assigning the default value in the variable definition.

The most general syntax for an argument definition contains the variable name, a default value, an input type, and a list of possible values.

variable = default-value as input-type in list If a default value is specified, the null input type is automatically applied, since that is necessary to make the argument optional.

mob/DM
   verb
      set_density(d=1 as num)
         set name = "set density"
         density = d

This example defines a verb that controls the player's density. If no arguments are given, the mob will be made dense; otherwise the value specified by the player will be used.

anything input type

The anything input type allows you to combine other input types with items from a list. Its purpose is to make clear the fact that the constant input types are in addition to whatever may be in the object list.

The following example allows you to change your icon, with the option of selecting the icon from a file or from any other object in view.

mob/verb/set_icon(I as icon|anything in view())
   icon = I

Note that this happens to work because assigning an object to the icon variable causes that object's icon to be assigned instead. (That behavior exists because I did not want to introduce conditional statements yet. Such are the hoops a programmer will jump through to avoid extra documentation!)