DM Guide 4: различия между версиями

Материал из Chaotic Onyx
Перейти к навигацииПерейти к поиску
(Гайд полностью переведен на русский язык, а значит завершен, снимаю плашку разработки.)
 
(не показано 19 промежуточных версий 8 участников)
Строка 5: Строка 5:
 
[http://www.byond.com/docs/guide/chap04.html Оригинальная 4 глава руководства на английском языке]
 
[http://www.byond.com/docs/guide/chap04.html Оригинальная 4 глава руководства на английском языке]
  
{{Заготовка}}
+
= Глава 4: Действия verb =
 
 
= Глава 4: Действия Verb =
 
  
 
''В конце концов, это может быть всего лишь сон. Только часть и лишь один слой этого пристанища грез.'' — Л.М. Монтогомери, Пристанище снов Анны.
 
''В конце концов, это может быть всего лишь сон. Только часть и лишь один слой этого пристанища грез.'' — Л.М. Монтогомери, Пристанище снов Анны.
Строка 107: Строка 105:
 
=== Доступ по умолчанию ===
 
=== Доступ по умолчанию ===
  
Для удобства, действия, прикрепленные к разным типам объектов, имеют разные настройки доступа по умолчанию. Кратко это описано во вставке.
+
Для удобства ''verb'', относящиеся к разным типам объектов, имеют разную доступность по умолчанию. Они обобщены на рисунке 4.6.
 
 
For convenience, verbs attached to different object types have different default accessibilities. These are summarized in figure 4.6.
 
  
 
<div style="width: 30%; float: right; border: 1px solid #AAAAAA; padding: 10px; margin: 10px; background-color: #FFFFDD">
 
<div style="width: 30%; float: right; border: 1px solid #AAAAAA; padding: 10px; margin: 10px; background-color: #FFFFDD">
Строка 119: Строка 115:
 
</div>
 
</div>
  
Note that the default obj accessibility is really an abbreviation for src in usr.contents, which means the contents (or inventory) of the user's mob. As you shall see later on, the in operator always treats the right-hand side as a list--hence usr is treated as usr.contents in this context.
+
Обратите внимание, что доступность ''obj'' по умолчанию на самом деле является сокращением ''src'' в ''usr.contents'', что означает содержимое (или инвентарь) моба пользователя. Как вы увидите позже, оператор ''in'' всегда рассматривает правую часть как список - поэтому в данном контексте ''usr'' рассматривается как ''usr.contents''.
  
In the case of both turf and area, the default accessibility is view(0) and is implicit. Since the range is zero, this gives the player access to the verbs of the turf and area in which the mob is standing.
+
В случае с постройками(turf) и областью(area) доступность по умолчанию равна ''view(0)'' и является неявной. Поскольку диапазон равен нулю, это дает игроку доступ к ''verb'' постройки(turf) и области(area), на которой стоит моб.
  
Making use of this convenient default, suppose we wanted a dark area to have a magical trigger that would turn on the lights at the sound of clapping hands. You could do it like this:
+
Используя эту удобную настройку по умолчанию, предположим, что в темном помещении нужно установить магический триггер, который будет включать свет при звуке хлопка в ладоши. Это можно сделать следующим образом:
  
 
  area/dark/verb/clap()
 
  area/dark/verb/clap()
 
     luminosity = 1
 
     luminosity = 1
  
Abracadabra!
+
Абракадабра!
  
 +
=== Возможные настройки доступа ===
  
=== Possible Access Settings ===
+
Существует ограниченное количество возможных настроек для списка доступа к источнику глагола. Они приведены на рисунке 4.7.
  
There are a limited number of possible settings for a verb's source access list. They are compiled in figure 4.7.
+
Рисунок 4.7: Возможные настройки доступа к источнику
  
Figure 4.7: Possible Source Access Settings
+
  usr
 +
  usr.loc
 +
  usr.contents
 +
  usr.group
 +
  view()
 +
  oview()
  
usr
+
Два из них (а именно ''usr'' и ''usr.loc'') не являются списками объектов, а ссылаются на отдельный элемент. По этой причине они ведут себя немного по-другому, когда используются в присваивании и в операторе ''in''. Пусть вас это не смущает - на самом деле все очень просто. При использовании в присваивании они рассматриваются как один объект. При использовании с ''in'' они представляют собой список объектов, которые они содержат.
usr.loc
 
usr.contents
 
usr.group
 
view()
 
oview()
 
Two of these (namely, usr and usr.loc) are not lists of objects but instead refer to an individual item. For that reason, they behave a little differently when used in an assignment versus an in clause. Don't let that confuse you--it's really quite simple. When used in an assignment, they are treated as a single object. When used with in they represent the list of objects that they contain.
 
  
The rest of the src access settings are all lists. The view instruction has already been mentioned, but it requires a little more description so that you can understand the related oview instruction.
+
Остальные параметры доступа к ''src'' представляют собой списки. Инструкция ''view'' уже упоминалась, но она требует более подробного описания, чтобы вы могли понять связанную с ней инструкцию ''oview''.
  
The view() list contains all objects seen by the user up to a specified distance. The list starts with objects in the user's inventory, followed by the user herself, objects at her feet, then in neighboring squares, and so forth proceeding radially outward. A distance of 0 therefore includes the turf (and area) the user is standing on and its contents. A distance of 1 adds the neighboring eight squares and their contents. The maximum distance is 5, since that includes the entire visible map on the player's screen. (You will see how to change the map viewport size in chapter 14.) Because this is often the desired range, it is the default when no range is specified. The special range of -1 includes only the user and contents.
+
Список ''view()'' содержит все объекты, видимые пользователем на заданном расстоянии. Список начинается с объектов в инвентаре пользователя, затем идет сам пользователь, объекты у его ног, затем в соседних квадратах и так далее по радиусу наружу. Расстояние 0, таким образом, включает в себя постройки(turf) (и область(area)), на котором стоит пользователь, и его содержимое. Расстояние 1 добавляет соседние восемь квадратов и их содержимое. Максимальное расстояние - 5, так как оно включает в себя всю видимую карту на экране игрока. (Как изменить размер области просмотра карты, вы узнаете в главе 14.) Поскольку этот диапазон часто является желаемым, он используется по умолчанию, когда диапазон не указан. Специальный диапазон -1 включает только пользователя и содержимое.
  
The related instruction oview() stands for `other' or `outside' view. It is identical to view() except it doesn't include the usr or the usr's contents. In other words, it excludes objects in view(-1), the so-called private or personal range.
+
Связанная с ней инструкция ''oview()'' означает "другой" или "внешний" вид. Она идентична ''view()'', за исключением того, что не включает в себя ''usr'' или содержимое ''usr''. Другими словами, она исключает объекты в ''view(-1)'', так называемый приватный или личный диапазон.
  
As an example of using usr and usr.loc, consider a pair of verbs to allow picking up and dropping objects.
+
В качестве примера использования ''usr'' и ''usr.loc'' рассмотрим пару ''verb'', позволяющих поднимать и опускать предметы.
  
 
  obj/verb
 
  obj/verb
Строка 161: Строка 157:
 
       loc = usr.loc
 
       loc = usr.loc
  
To see how oview() might come in handy, suppose there was a magical torch that one could summon from a greater distance than provided by the standard get verb.
+
Чтобы увидеть, как может пригодиться ''oview()'', предположим, что существует магический факел, который можно призвать с большего расстояния, чем позволяет стандартный ''verb/get''.
  
 
  obj/torch/verb/summon()
 
  obj/torch/verb/summon()
Строка 167: Строка 163:
 
     loc = usr  //presto!
 
     loc = usr  //presto!
  
Making use of the default range, torches can be summoned from anywhere in the player's view. If we had used view() instead of oview(), objects already inside the user's inventory could be summoned, which wouldn't make much sense.
+
Используя диапазон по умолчанию, факелы можно призвать из любого места в поле зрения игрока. Если бы мы использовали ''view()'' вместо ''oview()'', можно было бы вызывать объекты, уже находящиеся в инвентаре пользователя, что не имело бы особого смысла.
 
 
  
== Overriding Verbs ==
+
== Переопределение verb ==
  
Suppose instead of a magical summon verb, we just wanted the get verb to have a greater range for torches. This is an example of object-oriented programming in which a child object type provides the same sort of operations as its parent but implements them differently.
+
Предположим, что вместо магического ''verb/summon'' мы просто хотим, чтобы ''verb/get'' имел больший диапазон для факелов. Это пример объектно-ориентированного программирования, в котором дочерний тип объекта предоставляет те же операции, что и его родитель, но реализует их по-другому.
  
Here is the code for such a modified get verb.
+
Вот код для такого модифицированного ''verb/get''.
  
 
  obj
 
  obj
Строка 189: Строка 184:
 
           loc = usr
 
           loc = usr
  
The example is written in full to demonstrate the syntax for overriding a previously defined verb. Notice that inside torch, we do not put get() under a verb node. This indicates to the compiler that we are overriding an existing verb rather than defining a new one.
+
Пример написан полностью, чтобы продемонстрировать синтаксис переопределения ранее определенного ''verb''. Обратите внимание, что внутри "torch" мы не помещаем ''get()'' под узлом ''verb''. Это указывает компилятору, что мы переопределяем существующий глагол, а не определяем новый.
  
The difference in the syntax for definition verses re-definition serves the purpose (among other things) of preventing errors that might happen in large projects. For example, you might define a new verb that mistakenly has the same name as an existing one, or you might try to override an existing verb but misspell it in the re-definition. In both of these cases the compiler will issue an error, preventing a problem that might otherwise go unnoticed for quite some time.
+
Разница в синтаксисе определения и переопределения служит (помимо прочего) для предотвращения ошибок, которые могут возникнуть в больших проектах. Например, вы можете определить новый ''verb'', который по ошибке имеет то же имя, что и существующий, или попытаться переопределить существующий ''verb'', но неправильно написать его в переопределении. В обоих случаях компилятор выдаст ошибку, предотвращая проблему, которая в противном случае могла бы оставаться незамеченной довольно долгое время.
  
 +
== Дружеские аргументы ==
  
== Friendly Arguments ==
+
Действия(verb) становятся гораздо мощнее, если добавить к ним возможность принимать дополнительные данные от пользователя. Программист называет это параметром или аргументом ''verb''. Вы можете определить столько аргументов для ''verb'', сколько пожелаете. Однако большинство ''verb'' принимают только один или два параметра.
  
Verbs become much more powerful when you add the ability to take additional input from the user. A programmer would call this a verb parameter or argument. You can define as many arguments to a verb as you wish. However, most verbs only take one or two parameters.
+
Аргументам присваивается отдельное имя переменной внутри круглых скобок определения ''verb''. Кроме того, необходимо указать тип ввода. Он указывает, какого рода информация требуется от игрока. Таким образом, общее определение глагола будет выглядеть следующим образом:
  
Arguments are each assigned a different variable name inside the parentheses of a verb definition. In addition to this, an input type must be specified. This indicates what sort of information is required from the player. A generic verb definition would therefore look like this:
+
VerbName(Var1 as Type1,Var2 as Type2,...)
  
VerbName(Var1 as Type1,Var2 as Type2,...)
+
Имена переменных подчиняются тем же правилам, что и все остальные в языке. Регистр имеет значение, и они могут состоять из букв, цифр и подчеркивания.
The variable names follow the same rules as everything else in the language. Case matters, and it may consist of letters, numbers, and the underscore.
 
  
 +
=== Типы входных параметров ===
  
=== Parameter Input Types ===
+
Возможные типы входных параметров перечислены на рисунке 4.8.
  
The possible input types are listed in figure 4.8.
+
Рисунок 4.8: Типы входных параметров
  
Figure 4.8: Parameter Input Types
+
text
 +
message
 +
num
 +
icon
 +
sound
 +
file
 +
key
 +
null
 +
mob
 +
obj
 +
turf
 +
area
 +
anything
  
text
+
Первая группа - это постоянные типы ввода. Все они представляют собой различные типы данных, которые может вставить пользователь. Они могут использоваться по отдельности или в комбинации. Чтобы объединить несколько типов, поставьте между ними знак "|", например: icon|sound.
message
 
num
 
icon
 
sound
 
file
 
key
 
null
 
mob
 
obj
 
turf
 
area
 
anything
 
The first group are the constant input types. They all represent different types of data that the user can insert. They may be used individually or in combination. To combine several types, put | between them like this: icon|sound.
 
  
The text input type accepts a short (one-line) string from the player. For a longer composition, the message input type is used.
+
Тип ввода ''text'' принимает от игрока короткую (однострочную) строку. Для более длинной композиции используется тип ввода ''message''.
  
Numbers are handled by the num input type. These, just like numbers in the language, may be positive or negative, integer or floating point, and may even be specified in scientific notation.
+
Для работы с числами используется тип ввода ''num''. Они, как и числа в языке, могут быть положительными или отрицательными, целыми или с плавающей точкой, и даже могут быть заданы в научной системе счисления.
  
There are three input types for resource files: icon, sound, and file. The last one, file, will take any type of file as an argument, whereas the other two take only icons and sounds, respectively. The related input type key takes a key entry from the player and is only used in obscure situations.
+
Существует три типа входных данных для файлов ресурсов: ''icon'', sound и ''file''. Последний, ''file'', принимает в качестве аргумента файл любого типа, в то время как два других принимают только иконки и звуки соответственно. Связанный с ним тип ввода key принимает от игрока ввод клавиши и используется только в неясных ситуациях.
  
The null input type is used in conjunction with other types. It indicates that the argument is optional. Otherwise, the user is required to enter a value before executing the command.
+
Тип ввода ''null'' используется в сочетании с другими типами. Он указывает на то, что аргумент является необязательным. В противном случае пользователь должен ввести значение перед выполнением команды.
  
The last group are the object input types. They are used to allow the player to select an item from a list of objects. More will be said on lists of objects in section 4.8. By default, the list is composed of all objects in view of the player.
+
Последняя группа - это типы ввода объектов. Они используются для того, чтобы игрок мог выбрать предмет из списка объектов. Подробнее о списках объектов будет рассказано в разделе 4.8. По умолчанию список состоит из всех объектов, находящихся в поле зрения игрока.
  
Using the various input types, it is possible to compose verbs that give the player control over his own appearance. For example, using the text input type, the name can be specified.
+
Используя различные типы ввода, можно составлять ''verb'', которые дают игроку контроль над собственной внешностью. Например, с помощью текстового типа ввода можно указать имя.
  
  
Строка 242: Строка 237:
 
     name = N
 
     name = N
  
In the client, one could therefore enter a command like the following:
+
Поэтому в клиенте можно ввести команду, подобную следующей:
  
set-name "Dan The Man
+
set-name "Dan The Man"
Notice in this example that the help text for the verb has been defined. First the syntax is described in parentheses and then the purpose of the command is stated. (The reason there are backslashes in front of the double quotes inside the text is to prevent them from being mistaken for the end of the description. This is called escaping and will be discussed in more detail in section 11.3.2.) If you position the mouse over the verb, the help text will be displayed. It will look something like this:
 
  
usage: set-name "new name" (Change your name.)
+
Обратите внимание, что в этом примере определен "текст справки"(desc) для ''verb''. Сначала в круглых скобках описывается синтаксис, а затем указывается назначение команды. (Обратные косые черты перед двойными кавычками внутри текста ставятся для того, чтобы их нельзя было принять за конец описания. Это называется экранированием и более подробно рассматривается в разделе 11.3.2.) Если вы наведете курсор мыши на ''verb'', появится текст справки. Он будет выглядеть примерно так:
If we had not specified the syntax help (in parentheses), it would have given a generic description of the syntax like this:
 
  
usage: set-name "text" (Change your name.)
+
*click*: set-name "new name" (Change your name.)
Each type of argument has a different default appearance. Generally speaking, it involves the name of the input type. If you think that will confuse the players, override it with your own text.
 
  
As a slight variation on the previous example, we could make a scroll object on which one can write a message.
+
Если бы мы не указали справку по синтаксису (в круглых скобках), она дала бы общее описание синтаксиса, например, такое:
 +
 
 +
*click*: set-name "text" (Change your name.)
 +
 
 +
Каждый тип аргумента имеет свой вид по умолчанию. Как правило, он включает в себя название типа ввода. Если вы считаете, что это запутает игроков, замените его своим собственным текстом.
 +
 
 +
В качестве небольшой вариации предыдущего примера мы можем сделать объект-свиток, на котором можно написать сообщение.
  
 
  obj/scroll/verb
 
  obj/scroll/verb
Строка 263: Строка 261:
 
       usr << desc
 
       usr << desc
  
Notice that players must be within arm's reach to inscribe a message. We assume that everyone has good eyesight so the complementary read command works as long as the scroll is within view.
+
Обратите внимание, что игроки должны находиться на расстоянии вытянутой руки, чтобы написать сообщение. Мы предполагаем, что у всех хорошее зрение, поэтому дополнительная команда чтения работает до тех пор, пока свиток находится в поле зрения.
  
It is amazingly simple to do for the player's icon what we just did for the description. Here is a verb that does the trick.
+
Очень просто сделать для иконки игрока то же самое, что мы только что сделали для описания. Вот ''verb'', который делает это.
  
 
  mob/verb/set_icon(i as icon)
 
  mob/verb/set_icon(i as icon)
Строка 271: Строка 269:
 
     icon = i
 
     icon = i
  
The command on the client could be issued something like this:
+
Команда на клиенте может быть выдана примерно так:
  
set-icon 'me.dmi
+
set-icon 'me.dmi'
Here, single quotes surround the file name. As with text arguments, the final quote is optional when there are no additional parameters.
 
  
 +
Здесь одинарные кавычки окружают имя файла. Как и в случае с текстовыми аргументами, последняя кавычка необязательна, если нет дополнительных параметров.
  
== 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.
+
Простота ''verb/set_icon'' скрывает за ним невероятную мощь. Подумайте, что происходит, когда вы его используете. Вы вводите имя файла с иконкой через клиент, и она волшебным образом появляется на карте. В игре, работающей по сети с несколькими пользователями, это работает точно так же. За кулисами указанный вами файл передается на сервер, который затем автоматически рассылает его всем остальным клиентам. Передача мультимедийных файлов по сети - это элементарная операция в DM, мало чем отличающаяся от ввода цифр или текста.
  
Speaking of multi-media, here is an example that makes use of the sound input type.
+
Раз уж речь зашла о мультимедиа, вот пример, в котором используется тип ввода ''sound''.
  
 
  mob/verb/play(snd as sound)
 
  mob/verb/play(snd as sound)
 
     view() << snd
 
     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!
+
Это воспроизводит звуковой файл (wav или midi) для всех, кто находится в поле зрения. Оператор << в данном контексте используется для отправки вывода в толпу или список толп. В этом случае звуковой файл получают все, кто находится в списке, вычисляемом инструкцией view(). (Если быть точным, то только те, кому нужна копия звукового файла, получат его. Если у них не включен звук или если у них уже есть файл, он не будет передан.) Если их машина способна воспроизводить звуки и их клиент настроен на это, звук будет автоматически воспроизведен для них. Неплохо для двух строк кода!
  
The output operator << opens up all sorts of possibilities. For example, you can say things.
+
Оператор вывода << открывает всевозможные возможности. Например, вы можете сказать.
  
 
  mob/verb/say(msg as text)
 
  mob/verb/say(msg as text)
 
     view() << msg
 
     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)
 
  mob/verb/say(msg as text)
 
     view() << "[usr] says, '[msg]'"
 
     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.
+
В этом примере переменные ''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]."
  
The possibilities for intrigue and secrecy increase with a covert version of the say command.
+
Возможности для интриг и секретности возрастают с тайной версией команды "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]'"
  
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 ==
+
Если вы внимательно следите за этим, то, возможно, вам пришла в голову мысль. Не могут ли эти verb, которые принимают объект в качестве аргумента, быть написаны с использованием этого объекта в качестве источника, а не аргумента? Ответ - да. Например, ''verb/wink'' можно переписать следующим образом:
 
 
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()
 
  mob/verb/wink()
Строка 322: Строка 320:
 
     view() << "[usr] winks at [src]."
 
     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.
+
Вместо того чтобы принимать моба в качестве аргумента, этот ''verb'' определяет публичный ''verb'' на мобах, который доступен другим людям, находящимся в поле зрения, и позволяет им подмигнуть мобу. С точки зрения пользователя, эти два случая идентичны. Однако с точки зрения программиста иногда удобнее использовать одну технику, а не другую.
  
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:
+
Предположим, вы хотите создать особый тип мобов, которые, если им подмигнуть, откроют волшебное слово. В этом случае лучше всего сделать так, чтобы цель подмигивания была источником ''verb''. Тогда вы сможете переопределить ''verb'' подмигивания для особого типа мобов следующим образом:
  
 
  mob/guard/wink()
 
  mob/guard/wink()
Строка 330: Строка 328:
 
     usr << "[src] whispers, 'drow cigam!'"
 
     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.
+
Когда ему подмигивают, охранник(guard) шепчет в ответ волшебное слово. Обратите внимание на строку, выполняющую процедуру ".." (точка-точка). Это специальное имя, которое соответствует предыдущей (унаследованной) версии ''verb'' (так называемый родительский или супер verb). Это избавило нас от необходимости переписывать строку, генерирующую вывод "wink". Именно это подразумевается под повторно используемым кодом в объектно-ориентированном программировании. В сложных процедурах это может избавить вас от множества проблем.
  
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.
+
Если бы вместо этого мы использовали оригинальную версию "wink", в которой целевой моб был аргументом, нам пришлось бы вставить код в общий ''verb/wink'', чтобы проверить, является ли целевой моб гвардом, и действовать соответствующим образом. В целом, хороший объектно-ориентированный подход позволяет ограничить код, специфичный для определенного типа объекта, рамками определения этого объекта. В приведенном выше примере это было достигнуто за счет того, что цель стала источником глагола подмигивания.
  
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)
 
  mob/DM/wink(M as mob)
Строка 341: Строка 339:
 
     del M  //poof!
 
     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.
+
Чтобы сделать это неприятное дело, мы использовали инструкцию ''del'', которая удаляет объект. В данном случае мы предположили существование первого определения wink(), которое принимает аргумент mob. Организовав все таким образом, мы смогли изолировать специальный код от объекта, к которому он применялся, в данном случае 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.)
+
Конечно, у вас могут быть еще более сложные сценарии, в которых вы захотите сделать обе вариации - то есть иметь код, специфичный для типа цели и пользователя. С этим можно справиться, не нарушая хорошего объектно-ориентированного подхода, но вам понадобятся некоторые инструменты, которые мы еще не полностью описали. (Например, вы можете определить вторую процедуру, выполняющую реакцию моба на подмигивание, и вызвать ее из частного ''verb/wink'').
  
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.
+
Причина, по которой объектно-ориентированный подход пытается ограничить код об объекте этим объектом, в конечном счете просто организационная. Когда вы хотите изменить волшебное слово, произнесенное охранником, вы знаете, куда идти в коде, чтобы сделать это. И что еще более важно, если вы захотите заменить охранника на эльфа, вам не придется вспоминать, что нужно еще и модифицировать ''verb/wink'', чтобы волшебное слово произносили эльфы, а не охранники, - задача, казалось бы, не имеющая отношения к делу.
  
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.
+
Но такие возможности гипотетичны и не должны превалировать над вашими собственными знаниями о том, какое развитие событий действительно вероятно. В некоторых ситуациях самой простой структурой может быть ограничение всего кода, имеющего отношение к подмигиванию, одним местом - единственным ''verb'' подмигивания, который обрабатывает все особые случаи. Это было бы процедурно-ориентированное программирование, устаревшая методология (хотя и проверенная и верная). Но кого волнует теория. Выполняйте работу с помощью четкого, лаконичного кода. Это то, что в конечном итоге имеет значение.
  
(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 ==
+
Обратите внимание, что в предыдущем разделе в двух версиях "wink" была небольшая асимметрия. В одном случае целью был аргумент моба, а в другом - источник. В последнем случае мы указали, что источник может находиться в любом месте в поле зрения пользователя, но в случае с аргументом мы ничего не сказали о дальности действия. Что, если мы захотим ограничить подмигивание меньшим расстоянием в этом случае?
  
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?
+
Как оказалось, аргументы могут быть ограничены примерно так же, как и источник ''verb''.
  
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,...)
  
VerbName(Var1 as Type1 in List1,Var2 as Type2 in List2,...)
+
Например, вот ''verb'', с помощью которого можно подтолкнуть своего соседа.
For example, here is a verb for prodding your neighbor.
 
  
 
  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]!"
  
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().
+
Использование выражения ''in'' в определении аргумента ограничивает выбор пользователя мобами, находящимися в соседних позициях. Для определения набора возможных значений можно использовать любое списочное выражение. Наиболее распространенными являются те, что используются для определения диапазона источника ''verb'' (раздел 4.3.3). Если выражение ''in'' не используется, списком по умолчанию для типов ввода mob, obj, turf и area является ''view()''.
  
For example, here is a verb to communicate (by frequency modulated electromagnetic waves) with any other player in the game.
+
Например, вот ''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]'"
  
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.
+
На самом деле, ''world'' - это отдельный объект, но в данном контексте он рассматривается как список и поэтому является сокращением от ''world.contents'', списка всех объектов в игре.
  
 +
== Аргументы по умолчанию ==
  
== Default Arguments ==
+
Аргументы verb можно сделать необязательными, используя тип ввода null. Если пользователь не введет значение аргумента, ему будет присвоено специальное значение null. В этом случае часто требуется проверить, действительно ли аргумент является нулевым, и поступить соответствующим образом. Это можно автоматизировать в том случае, если нужно просто подставить значение по умолчанию вместо null, присвоив его в определении переменной.
  
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
  
variable = default-value as input-type in list
+
Если указано значение по умолчанию, автоматически применяется тип ввода null, поскольку это необходимо для того, чтобы сделать аргумент необязательным.
If a default value is specified, the null input type is automatically applied, since that is necessary to make the argument optional.
 
  
 
  mob/DM
 
  mob/DM
Строка 391: Строка 389:
 
           density = d
 
           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.
+
Этот пример определяет ''verb'', который управляет плотностью игрока. Если аргументы не указаны, моб станет плотным, в противном случае будет использовано значение, указанное игроком.
  
== 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.
+
Тип ввода anything позволяет комбинировать другие типы ввода с элементами из списка. Его назначение - дать понять, что константные типы входа дополняют все, что может находиться в списке объектов.
  
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())
 
  mob/verb/set_icon(I as icon|anything in view())
 
     icon = I
 
     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!)
+
 
 +
Обратите внимание, что это работает потому, что присвоение объекта переменной ''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 предусматривает множество удобных путей облегчить жизнь пальцам пользователей. Вот несколько способов использовать вышеупомянутое действие тушения факела:

  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'

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

Генерация выходных данных

Простота 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 приводит к тому, что вместо нее присваивается значок этого объекта. (Такое поведение существует потому, что я не хотел пока вводить условные операторы. Вот через какие препятствия приходится прыгать программисту, чтобы избежать лишней документации!)