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

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

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

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


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

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


Карта использует Декартову систему координат. Необходимы три координаты, что бы определить местоположение объекта на сетке. Координаты, как мы все привыкли, имеют обозначения x, y, z.

Координата "х" определяет местоположение объекта с запад на восток, "по горизонтали". Единица соответствует самой западной точке, а значение world.maxx определяет самую восточную точку. Для игрока х возрастает слева-направо. [br]Координата "y" определяет местоположение объекта с юга на запад, "по вертикале". Единица соответствует самой южной точке, а значение world.maxy определяет самую северную точку. Для игрока y возрастает снизу-вверх. [br]Координата "z" идет от 1 до world.maxz, и, как правило, представляет из себя переход от одной высоте к другой. По сути, каждая z-координата - это отдельный уровень. Как правило, для основного уровня используется координата 1. У DM нет встроенного механизма перемещения по Z-уровням, поэтому то, как вы будете использовать Z-координаты, зависит только от вас.

Пространственные инструкции

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


Список view

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

view (Range=world.view,Center=usr) Range – максимальная дальность видимости. Center – центральный объект в поле зрения. Возвращает список видимых объектов.

Дальность видимости по умолчанию может быть настроена при помощи world.view. По умолчанию равна 5, что соответствует видимой области 11 на 11 тайлов. Однако может быть увеличена до 10, что дает видимую область 21 на 21. (Размер иконок может автоматически масштабироваться, для того, чтобы соответствовать видимой области на экране игрока.)

Для удобства, аргументы могут быть заданы в любом порядке. Эта возможность чаще всего используется, когда кто-то хочет указать другой центр зрения (center of view), при этом используя диапазон по умолчанию. Например, если вы хотите использовать src, а не usr, в качестве центра зрения, вы можете написать view(src), а не view(5,src) или даже view(,src).

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

Дальности видимости

2 2 2 2 2
2 1 1 1 2
2 1 0 1 2
2 1 1 1 2
2 2 2 2 2

Некоторые видимые области могут быть загорожены непрозрачными объектами. Освещенность также играет роль. Если освещение на тайле включено (area.luminosity = 1), то все объекты, находящиеся на нем, будут освещены. Или же отдельные тайлы или объекты на карте могут испускать свет, освещая себя и объекты вокруг. Если ничего не светится, то видны только объекты непосредственно вокруг центра (на расстоянии до 1).

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


Список oview

oview аналогична view , с той лишь разницей, что не включает в список центральный объект и его содержимое. Другими словами, она исключает объекты из view(-1). Это чаще всего используется при трансляции сообщение всем в поле зрения, кроме совершившего какое-то действие.

Режим повествования

При описании событий игрокам, можно строго придерживаться третьего лица или использовать смесь второго и третьего лица. Например, когда игрок улыбается, все могут видеть текст "[usr] smiles", или же все, кроме игрока, могут это видеть, а игрок вместо этого увидит "You smile".

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

Вывод строго от 3-го лица легко генерируется трансляцией всем в view(). Для вывода от второго лица вместо этого используется oview(). Следующие примеры противопоставляют эти два метода.

//описание от 3-го лица
mob/verb/smile()
   view() << "[usr] smiles."
//описание от 2-го лица
mob/verb/smile()
   usr << "You smile."
   oview() << "[usr] smiles."

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

Следующий пример демонстрирует, как этого можно добиться.

mob/verb/smile(M as mob|null)
   if(!M) //no target mob specified
      usr << "You smile."
      oview() << "[usr] smiles."
   else
      usr << "You smile at [M]."
      M << "[usr] smiles at you."
      oview() - M << "[usr] smiles at [M]."

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


Инструкции range и orange

range аналогична view, за исключением того, что игнорирует видимость. Все объекты в заданном радиусе будут включены в список независимо от того, видимы они или нет.

Таким образом, и orange ведет себя как oview, за исключением игнорирования видимости. Она возвращает тот же список минус центральный объект и его содержимое.

(На случай использования в разговорной речи, имейте в виду, что произносится `oh-range', а не как цвет или фрукт - `orange'. Тем не менее, это может пригодиться поэтам, подбирающим рифмы. Her hair was orange\ And she was in my oh-range...)

range (Range=world.view,Center=usr) orange (Range=world.view,Center=usr) Range – максимальная дальность видимости. Center – центральный объект в поле зрения. Возвращает список объектов в заданном радиусе.

Инструкция locate

locate используется для получения ссылки на объект, путем указания некоторых уникальных свойства этого объекта. В случае с тайлами это могут быть координаты. Для идентификации желаемого объекта могут быть использованы object type, tag name и \ref value.

locate (x,y,z) locate (Type) in Container locate (Tag) in Container x,y,z – координаты тайла. Type – тип объекта. Tag – уникальный текстовый идентификатор. Container – локация или список (необязательно). Возвращает ссылки на объекты или нуль, если их нет.

Используя locate() можно располагать новых игроков на карте в определенных координатах. В нижеследующем примере они помещаются в центр карты вместо позиции по умолчанию (1,1,1).

mob/Login()
   if(!loc) //новый игрок
      loc = locate(world.maxx/2,world.maxy/2,1)

Это лишь один из многих случаев, в которых locate() используется для доступа к объекту в коде, размещенному на карте при помощи редактора карт. Вместо указания координат, как правило, удобнее использовать tag variable объекта. This can be assigned to a special value и использована, чтобы найти объект во время выполнения программы.

В следующем примере новые игроки перемещаются в специально помеченные локации.

mob/Login()
   if(!loc)
      loc = locate("start")

Необходим тайл, помеченный как "start", чтобы этот пример работал. Редактирование tag variable и прочих свойств объектов в редакторе карт будет рассмотрено в третьем пункте этой главы.


Инструкция block

block генерирует список всех тайлов в прямоугольной области карты.

block (SW,NE) SW – юго-западный тайл области. NE – северо-восточный тайл. Возвращает список тайлов в области.

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

proc/LocateInLevel(Type,zLevel)
   var/SW = locate(1,1,zLevel)
   var/NE = locate(world.maxx,world.maxy,zLevel)
   return locate(Type) in block(SW,NE)

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

turf/downstairs
   verb/use()
      var/dest = LocateInLevel(/turf/upstairs,z+1)
      usr.Move(dest)
turf/upstairs
   verb/use()
      var/dest = LocateInLevel(/turf/downstairs,z-1)
      usr.Move(dest)

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


Инструкция get_dist

get_dist вычисляет расстояние между двумя точками. Получаемое расстояние соответствует аргументу инструкции view(). Если обе точки находятся в одном месте, то расстояние равно 0. Если они расположены на соседних тайлах, то 1, и так далее. (Обратите внимание, что get_dist не вычисляет расстояние по теореме Пифагора. При расчете диагональные перемещения считаются равными прямым.)

get_dist (Loc1,Loc2) Loc1 – первая точка. Loc2 – вторая точка. Возвращает расстояние между ними.

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

mob
   var/mob/enemy
   proc/Swing()
      if(get_dist(src,enemy) > 1) return
      //далее наносится урон...

В этом примере, моб может поражать цели только на соседних тайлах. Детали остальной части процедуры были опущены. Типичной боевой системе требуется некоторая случайность, что будет рассмотрено в 16-ой главе.


Перемещение

Есть целый ряд инструкций, связанных с перемещением объектов по карте. Они все приведены в сноске справа.

Инструкции перемещения

walk
walk_towards
walk_to
walk_away
walk_rand


step
step_towards
step_to
step_away
step_rand


get_step
get_step_towards
get_step_to
get_step_away
get_step_rand

Есть три основные группы инструкций перемещения walk, step, и get_step. Все они выполняют одинаковые вычисления, но по-разному используют их результат. Инструкции walk постоянно перемещают объект, делая несколько шагов при необходимости. Инструкции step делают то же самое, за исключением того, что совершается только один шаг. Инструкции get_step не перемещают объект, а возвращают следующий тайл, в который перейдет объект, согласно заданному алгоритму перемещения.

Какую группу инструкций перемещения вы предпочтете использовать, зависит от того, насколько вы хотите контролировать процесс перемещения объекта. Группа walk полностью автоматизирует движение, в то время как step позволяет контролировать timing самостоятельно. Для полного контроля, можно использовать get_step, в этом случае даже решение о том, переместить объект или нет, остается за вами.

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


Инструкции walk

walk, step, и get_step – инструкции для движения в заданном направлении.

walk (Obj,Dir,Lag=0) step (Obj,Dir) get_step (Obj,Dir) Obj – передвигаемый объект. Dir – направление движения. Lag – задержка между шагами в одну десятую секунды.

В качестве направления используется одна из констант: NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST или SOUTHWEST. Это те же самые значения, используемые для указания направления взгляда объекта (переменная dir).

step возвращает 1 в случае успеха и 0 в случае неудачи, а get_step возвращает следующий тайл в заданном направлении. walk возвращает значение немедленно и продолжает работать в фоновом режиме, поскольку находится в спящем режиме перед каждым шагом.

Только одна операция перемещения может выполнятся в единицу времени над конкретным объектом. Это означает, что когда вызывается walk, любая предыдущая операция перемещения этого объекта прерывается. Для отмены любых существующих операций перемещения, можно не задавать направление вовсе walk(Obj,0).

Есть несколько инструкций по работе с направлениями. Они описаны в ниже.


Инструкция get_dir

get_dir вычисляет направление одной локации относительно другой. Если истинное направление не совсем совпадает с одним из восьми стандартных направлений (NORTH, SOUTH и так далее), будет выбрано наиболее близкое.

get_dir (Loc1,Loc2) Loc1 – первая локацияis. Loc2 – вторая локация. Возвращает направление Loc2 относительно Loc1.

Инструкция turn

Инструкция turn поворачивает направление на указанную величину.

turn (Dir,Angle) Dir - начальное направление. Angle - угол поворота. Возвращает новое направление. Угол указывается в градусах. Например, turn(NORTH,90) направляет на запад, разворачивая против часовой стрелки на 90 градусов. Для поворота по часовой стрелке используйте отрицательные значения углов.

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

mob/guard/New()
   ..()
   spawn for()  //spawn an infinite loop
      if(!step(src,dir)) dir = turn(dir,180)
      sleep(30) //three seconds

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

walk_towards

Инструкции walk_towards, step_towards, и get_step_towards двигаются в сторону другого объекта. Если объект меняет положение, алгоритм хождения (walking) автоматически корректирует направление движения соответственно.

walk_towards (Obj,Targ,Lag=0) step_towards (Obj,Targ) get_step_towards (Obj,Targ) Obj - передвигаемый объект. Targ - пункт назначения. Lag - задержка между шагами в одну десятую секунды..

Их значения возвращения (return values) являются такими же, как у инструкций движения фиксированного направления, которые уже были описаны. На самом деле, все инструкции движения ведут себя одинаково кроме используемого алгоритма step.


walk_to instruction

The walk_to, step_to, and get_step_to instructions move to another target object, taking intervening objects into account and attempting to intelligently maneuver around them if possible. If the target is too far away (more than the width of the map view), no action is taken.

walk_to (Obj,Targ,Dist=0,Lag=0) step_to (Obj,Targ,Dist=0) get_step_to (Obj,Targ,Dist=0) Obj is the object to be moved. Targ is the destination. Dist is the maximum desired distance. Lag is the delay between steps in 10$^th$s of seconds.

One use for this would be a verb that allows a player to automatically follow another one. That can save a lot of needless key presses, which may otherwise bog down the network. Note that the command takes the player's current distance from the target as the desired range to maintain so that one can avoid crowding in on the leader.

mob/verb/follow(mob/M)
   walk_to(src,M,get_dist(src,M),30)

As a convenience in situations like this, pressing any direction key will stop the automated walking algorithm. This applies even to the center key, which merely calls walk(src,0) by default, allowing the player to stop in place.


walk_away instruction

The walk_away, step_away, and get_step_away instructions move to another target object, taking intervening objects into account and attempting to intelligently maneuver around them if possible.

walk_away (Obj,Targ,Dist=world.view,Lag=0) step_away (Obj,Targ,Dist=world.view) get_step_away (Obj,Targ,Dist=world.view) Obj is the object to be moved. Targ is the destination. Dist is the minimum desired distance. Lag is the delay between steps in 10$^th$s of seconds. An example using this algorithm is a command to run away from someone.

mob/verb/flee(mob/M)
   walk_away(src,M,5,30)

walk_rand instruction

The walk_rand, step_rand, and get_step_rand instructions generate seemingly random motion. The object being moved appears to wander aimlessly. That is different from wandering mindlessly, which would be the result of random movement. In fact, this algorithm is not very random but instead uses a technique known as edge following to create the effect of purposeful motion.

walk_rand (Obj,Lag=0) step_rand (Obj) get_step_rand (Obj) Obj is the object to be moved. Lag is the delay between steps in 10$^th$s of seconds.

The following example uses this walking algorithm to make certain mobs meander through the world.

mob
   var/wander
   New()
      if(wander) walk_rand(src)
      ..()

All you have to do to see this in action is define some mobs with the wander variable initialized to 1. The algorithm works best with some edges to follow, so a maze-like map with many tunnels and rooms with walls is ideal.


Программирование, необходимое для создания карт

In the simplest scenario, designing the world map is merely a matter of selecting object types which were defined in the code and dropping them onto the map. The individual objects on the map are called instances of the object types and as a group are referred to as the initial map population.

Depending on your preference, it is possible to do little or all of the map design from the code. By creating turfs and other objects with new() part or all of the map could be generated at run-time. Since this is a rather cumbersome method, it is usually reserved for cases where the map is laid out according to some algorithm. For example, a maze-like map could be randomly generated so that it is different each time it is played.

Map generating algorithms are an interesting topic, but the techniques involved are fairly abstract and rely very little on the particulars of the DM language. For an example, refer to the DM Code Library. One of the useful items to be found there is the "Amazing Maze Generator," which can make seemingly infinite dungeons and the like.

In most situations, map design is done principally in the map editor. It is even possible to go beyond simply using pre-defined object types. Using the map editor's instance editing feature, you can modify individual objects to suit your own purposes. This allows one to avoid cluttering up the code with object types which are really just minor variations of a more general type but which are required to make instances on the map.

Using the map editor to its fullest potential, one can write code which is fairly general and independent of the map. This is especially convenient when the programmer and map designer are different people. In fact, the code can be written once and used to design many different maps. This process is referred to as writing a world code base which is then used to create an endless variety of world instances. These could be networked together using techniques described in section 12.6.

The map instance editor allows one to modify the object's variables. For example, you can change the name of an object, its icon, its density, and so forth. There is no limitation to built-in variables; those defined in the code may also be modified. In this case you may wish to specify what sort of value may be given to the variable. This prevents mistakes and also helps inform the map designer about a variable without requiring the poor fellow to read the code.


Variable Input Parameters

Object variables may be declared in much the same way as verb arguments to provide extra information to the map editor. Both the input type and a list of possible values may be specified. Otherwise a variable simply defaults to accept any type of value.

var/VarName as Type in List VarName is the variable being defined. Type is the input type. List is the list of possible values.

The valid input types include all those which may be used in a verb argument definition. These are described in section 4.5.1. The list of possible values can be a list of constants or a range of integers. The following example demonstrates both possibilities.

mob/var
   wealth as num
   strength in 1 to 100   //defaults to "as num"
   alignment in list("good","bad","neutral")
   background as text

Figure 14.26: Hands-free programming!

Dream Maker's instance editor is a powerful tool, if used appropriately. It can allow you to quickly make unique entities without having to derive new classes for everything. It is also quite simple to use, requiring no other coding knowledge than the ability to fill out forms. Consider the previous code:

mob/var
  wealth as num
  strength in 1 to 100
  alignment in list("good","bad","neutral")
  background as text

Now suppose you have derived a monster type from this, say, /mob/goblin. Using the instance editor, you can place a bunch of unique goblins on the map without having to modify the DM code one bit:

Select the goblin type in the tree. Click on the Edit button in the adjacent panel. This will bring up a dialog with the properties of the goblin. Modify these properties by changing the values in the form. If you enter invalid values, the editor will correct you. It does this by looking at the specified filters. For instance, the strength property must be a value between 1 and 100. If it is not in this range, it must be reset. In this fashion, you can change names, descriptions, icons, and so on. You can make a horde of goblins, each with unique identifications and traits. One particular useful property is the tag. This is just a text string that can be used to distinguish instances in the locate() instruction. So if you want to make a "chief goblin" without deriving a new type, you might want to set the tag to "chief goblin" and set the other properties accordingly. You can then locate this goblin later on by doing locate("chief goblin"). After you are done editing the goblins, place them on the map by selecting the corresponding instance in the panel (they will be sorted by tag). If you want to re-edit them later, you can use the Look option and proceed from there.