DM Guide 7
Это 7 глава перевода оригинального руководства по Dream Maker от разработчиков.
Остальные главы руководства и статьи о программировании на коде BYOND от любителей на русском.
Глава 7: Предопределенные procs объектов
That which is the dark night of all beings, for the disciplined man is the time of waking; when worldly people are working the enlightened sage is unaware of the material waking. -- Bhagavat Gita
Подобно тому, как существуют предопределённые переменные, определяющие свойства объектов, так же существуют предопределённые Procs(действия, вызываемые сервером, иногда их называют процессами), контролирующие поведение объектов в определенных ситуациях. Программист может назвать их сценариями событий, потому что именно они определяют поведения объекта в различных ситуациях.
Некоторые из предопределенных procs, не делают вообще ничего, но позволяют переопределить их на ваше усмотрение. Такие пустые процессы называются hooks (крючками), потому что они предоставляют место, на которое может быть повешен ваш собственный сценарий событий. Тем не менее, в DM, не только крючки, но и все другие процессы объекта, можно переделать и настроить в соответствии с вашими потребностями.
Движение
Mobs и Objs обладают способностью к движению (хотя, как правило, существа единственные, кто делает это по собственному желанию). Вы уже видели, как переменная Destiny (плотность) ограничивает движения: два плотных объекта не могут занимать одну и ту же позицию. Это правило, в частности, обеспечивается различными процессами движения.
Следует отметить, что движение не ограничивается перемещением по карте. Объекты могут перемещаться внутрь друг в друга. Например, когда персонаж поднимает меч, меч перемещается с карты к персонажу. Меч может так же быть перемещен в сумку персонажа для более безопасного хранения. Любое изменение расположения, будь то на карте или внутри других объектов, считается движением.
Enter
Процесс Enter возможен для всех четырех типов объектов. Он возвращает 1, если указанный объект может войти в источник и 0, если нет. Если объект, пытающийся войти является плотным и на том месте, куда производится перемещение, уже находится плотный объект, в перемещении будет отказано.
Enter(О) О – вносимый объект src – куда вносится объект usr – вносимый персонаж, если таковой есть.
Предположим, что вы произнесли магическое заклинание, делающее людей нематериальными (иначе, не плотными) и способными проходить сквозь стены. И если вы ещё хотите, чтобы некоторые стены были так же непроницаемы, даже для нематериальных людей, то это можно легко сделать с помощью переопределения процесса Enter.
turf/lead_wall name = "lead wall" Enter(O) return 0 //none may enter here
Exit
Процесс Exit является аналогом Enter. Он возвращает 1, если указанный объект может выйти из источника и 0, если нет. По умолчанию, выход всегда разрешен.
Exit(O) О – извлекаемый объект src – куда извлекается объект usr – извлекаемый персонаж, если таковой есть.
Переопределив этот процесс, можно создать опасную ловушку.
turf/pit/Exit(O) O << "You are stuck in [src]!"
Bump
Процесс Bump вызывается, когда перемещение невозможно, по причине столкновения с плотным объектом. Если персонаж попытается переместиться, а на его пути стоит другой персонаж, находящийся в списке группы его союзников, они оба поменяются местами.
Bump(Blockage) Blockage – преграждающий объект. src - заблокированный объект usr - заблокированный персонаж ,если таковой есть.
Этот процесс может быть переопределён, включением правила, что все игроки, не зависимо от списка групп, будут меняться местами.
mob/Bump(mob/M) if(istype(M) && M.key && src.key) var/pos = M.loc M.loc = usr.loc usr.loc = pos else ..()
Istype() используется для определения, совпадает ли тип содержимого рассматриваемой переменной с типом заданного значения. Здесь мы используем его для того, чтобы увидеть, действительно ли персонаж блокирует нам путь. Процесс может также быть прописан как: istype(M,/mob).
Если препятствие является персонажем, то персонажи меняются своими позициями. Вместо прямого назначения персонажам новых позиций, вы можете сделать одного из них нематериальным и совершить перемещение. Как этого достигнуть, описано ниже.
Move
Процесс Move является тем процессом, который связывает все другие процессы передвижения вместе. Он запускает процесс Enter для достижения места назначения. Если это не удается, то он вызывает процесс - Bump. Так же запускается процесс Exit из исходного местоположения и если это удаётся, назначает новое местоположение. Возвращает значение 1 в случае успеха и 0 в случае неудачи.
Move (Dest, Dir) Dest - место назначения. Dir - направление движения. SRC - движимый объект. USR - персонаж, который движется (если такой имеется).
Параметр направления не должен указываться. Это одна из тех вещей за которыми следит процесс Move, он изменяет направление объекта в ту сторону, в которую он перемещается. Если параметр направления задан, то он будет использован, в качестве нового направлении объекта, в противном случае, направление будет вычислено исходя из начального и конечного местоположения.
Другие процессы передвижения обычно не вызываются напрямую. Move также может быть полезен, когда вы хотите, чтобы объект двигался в соответствии со всеми правилами передвижения. Непосредственное перемещение объекта на место обходит все проверки на плотность, препятствия, и изменения направления.
К примеру, вы можете создать волшебный свиток, который бы телепортировал заклинателя в любое свободное место, в поле его зрения.
obj/scroll/teleport verb/teleport(T as turf) set src = usr.contents if(!usr.Move(T)) usr << "You cannot move there!" else view() << "[usr] dances through the ethers!"
Ещё очень многое можно рассказать про тему передвижения. К примеру, вдруг кто-то захочет создать бездумно гуляющих NPC-персонажей, не натыкающихся на препятствия. Много полезных инструкций и методов про передвижение будут рассмотрены в разделе 14.2.
Создание и удаление объектов
Вы уже видели примеры использования инструкций New и Del. Они используются, для создания или удаления объектов. Другим способом создания объектов, является размещение их на карту, используя для этого редактор карт. Также? когда мир уничтожается, любые оставшиеся объекты разрушаются. Возможно, вы захотите добавить несколько специальных действий, при создании или уничтожении объекта, DM обеспечивает настройку процесса для этих событий.
New
Процесс New вызывается, когда происходит создание объекта. Он может быть использован для создания особой инициализации, которую потребует объект. По умолчанию, он не делает ничего.
New(Lос) Loc - является начальным местоположением объекта.
Когда объект создается через процесс New, местоположение и любые дополнительные аргументы, которые вы определили, передаются в это же время. Синтаксис выглядит так:
new Type(Loc, ...) Type - тип создаваемого объекта. Loc - начальное местоположение объекта. ... и другие дополнительные аргументы.
Следует отметить, что объект создается в указанном местоположении. Он не перемещается туда - он назначается в эту позицию. Если местоположение не указано, то местоположение объекта обозначается как null, означающее, что объект не будет отображаться на карте.
В качестве примера, можно создать объект, при создании которого, проигрывается звук.
obj/portal New() view() << "A shimmering portal appears!" view() << 'portal.wav' mob/DM verb make_portal(NewName as text) var/obj/portal/P = new/obj/portal(usr.loc) if(NewName) P.name = NewName
В примере процесс New используется несколько раз. Мы объявляем переменную, она назначается создаваемому объекту, после чего, производим дополнительные изменения, через объявленную переменную. Очень полезно использовать аббревиатуры. Переменной назначается тот же тип, что и создаваемому новому объекту. Вместо указания типа ненужной информации, он может быть просто опущен. В этом случае, синтаксис будет выглядеть, как new(usr.loc), взамен предыдущего. Отметим, что в вышеприведённом примере, пробел между new и obj/portal необязателен. Тот же пример может быть переписан, чтобы использовать новое имя в качестве параметра.
obj/portal New(Loc,Name) if(Name) name = Name view() << "A shimmering portal appears!" view() << 'portal.wav' mob/DM make_portal(Name as text) new/obj/portal(usr.loc,Name)
Выбор, нужно ли передавать информацию в качестве параметра процесса New или следует выполнять задачу в другом месте, зависит от того, как часто вы будете использовать похожий по построению синтаксис. В том случае, когда вы захотите модифицировать процесс New, использовав информацию по-разному, в производных объектах, следует изменять процесс уже в каждом конкретном случае.
Del
Del вызывается при уничтожении объекта. По умолчанию, это означает сбрасывание местоположения объекта или персонажа. Игрок, использующий персонажа, будет отключен. Вы можете добавить любые дополнительные свойства в ваше собственное переопределение этого процесса.
Del () Этот процесс может быть вызван напрямую или его можно вызвать с помощью команды del. Преимущество инструкции del, в том что вам не нужно знать тип объекта, чтобы вызвать дополнительные процессы del.
del(object) object – объект для уничтожения.
Когда объект удаляется, всем существующим на него ссылкам присваивается значение Null. Поэтому, если у вас есть переменная, имеющая отношение к объекту, который может быть удалён, необходимо убедиться, что ссылка не является нулевой, прежде чем пытаться получить доступ к любым переменным объекта или процессам. Если вы этого не сделаете, выполнение процесса может не сработать. Методы отладки, связанные с этим вопросом будут обсуждаться в разделе 19.3.2.
Очень простой пример процесса Del, заставляющий проигрывать при удалении, звук смерти.
Mob / Del () View () << 'death.wav' .. ()
Важно помнить о необходимости вызова родительского процесса, так как он выполняет фактическое удаление.
На практике, лучше показать второй процесс (Say Die), который инсценирует реальную смерть на экране и резервный Del() для абстрактного удаления объекта из памяти. Таким образом, вы можете удалить персонажа игрока, если он не используется, сохраняя его в файл, а затем удаляя его из обрабатываемой информации. Вам бы не хотелось, видеть как персонажи появляются лишь для того чтобы умереть, в очередной раз когда игрок отключается?
obj/corpse icon = 'corpse.dmi' mob var/corpse = /obj/corpse proc/Die() if(corpse) new corpse(loc) src << "See you around!" del src
Исходя из этого, мы вызываем mob.Die(), когда хотим чтобы персонаж умер. По умолчанию, процесс создаёт мёртвое тело и удаляет персонажа. Он может легко быть изменен для различных видов персонажей. К примеру, вдруг вы захотите, чтобы игроки и дальше слышали разговоры, но не могли отвечать.
Локации и комнаты
Создание и удаление локаций обрабатывается несколько иным способом. Когда одна и та же локация располагается в нескольких местах, фактическая площадь локации создается только один раз. New() вызывается только в первый раз при создании локации. В последующем, новые позиции просто помечаются как части уже существующей области.
Кроме того, можно создать локацию, которая не располагается на карте. Это так называемые rooms (комнаты) и могут быть использованы для хранения любого количества иных объектов. Обратите внимание, что параметр плотности объекта игнорируется в комнатах; это относится только к объектам на карте.
Комнаты можно создавать помощью new, не определяя местонахождения (или, указывая NULL). Другой еще более простой способ состоит в определении области, не указывая ее на карте. Когда вы зайдете в локацию, комната будет создана автоматически.
Можно, например, создать специальную комнату для игроков, в которую они бы перемещались после смерти. Там они могут покаяться за грехи своей предыдущей жизни или помедитировать.
area/Purgatory Enter() usr << "Welcome to [src]." return ..() mob/proc Die() if(key) //players loc = locate(/area/Purgatory) else //NPCs del src
Возможно, вы захотите сделать выход из неё, ведь терпение игроков, которые только что потерпели ужасную смерть, дело тонкое. Команда, которая телепортирует их обратно на землю, живыми, является хорошим выбором. В обоих случаях, инструкция locate полезна для определения конечного пункта назначения.
Stat
Процесс Stat используется для отображения информации об объекте на экране игрока. Такая информация называется stats (статистика). По умолчанию, игрок видит только stats собственного персонажа, но программист может дать возможность игрокам увидеть stats других персонажей или объектов.
Stat () SRC – объект. USR – персонаж игрока.
Каждая панель Stat состоит из нескольких линий. Линии имеют свои имена и соответствующее значение, которое отображается рядом с именем. Для создания строки вывода на панель, воспользуйтесь stat инструкцией.
Stat (Name, Value) Name - имя линии, необязательно. Value – отображающиеся значение..
В следующем примере показано создание обычной панели статуса персонажа.
mob/Stat() stat("description",desc) stat("") //blank line stat("strength",strength) stat("health",health) stat("odor",odor)
Для обеспечения структурированной статистики, можно использовать вторую команду statpanel, обеспечивающую возможность создания нескольких панелей за раз. Она выводит на экран своё имя и строчки с информацией статистики. Если ни одна строчка не указана, данная панель будет использоваться по умолчанию для простого отображения информации. Пока не произошёл вызов команды statpanel, панель по умолчанию имеет имя "" (пустая строка текста).
statpanel (panel, name, value) panel - название панели. name и value - такие же, как для Stat (). Возвращает true, если панель отображается игроку.
И последний нюанс, вы можете передать полный список объектов в качестве значения для процесса Stat, в этом случае объекты отображаются в виде списка. Это равносильно созданию каждому отдельному объекту своей stat-линии. Эта функция чаще всего используется в команде statpanel, для создания отдельной панели с списком.
В следующем примере отображается описание игрока, а также инвентарь и список групп.
mob/Stat() stat(desc) statpanel("Inventory",usr.contents) statpanel("Group",usr.group)
Есть ещё одна тонкость – чтобы избежать генерации пустых строчек в панеле статуса, неплохо было бы проверять наличие содержимого строчки. Достичь этого можно путем проверки usr.contents.len, переменной, указывающей длину списка. Эта и другие возможности списка будут обсуждаться в главе 10.
В следующем примере показаны основы для создания типичной панели статуса.
mob/Stat() if(statpanel("Stats")) stat("health",health) stat("strength",strength) if(statpanel("Description")) stat("appearance",desc) stat("race",race) statpanel("Inventory",contents)
Обратите внимание на использование statpanel() в условном операторе, этот процесс возвращает true, только тогда, когда указанная панель видна игроку. Это не обязательно использовать, но дает эффект против пустых панелей, которые не видны игроку. Поскольку процесс Stat можно назвать часто-обновляемым, неплохо было бы избежать лишней нагрузки на память.
Click и DblClick
Процесс click вызывается, когда игрок щелкает мышкой на объект, а процесс DblClick вызывается соответственно двойным щелчком. По умолчанию, после вызова процессов ничего не происходит, т.е. процессы являются Hooks (крючками) для собственного использования. О них будет сказано подробнее в разделе 9.2.2.
Click (panel) DblClick (panel) panel – имя stat-панели.
Объект может быть нажат на карте или на панели статуса. Если объект нажат на карте, то аргумент stat-панели равен null.
В следующем примере процесс click используется для воспроизведения звуков.
bj/instrument var/melody piano melody = 'entertainer.wav' trumpet melody = 'jazzy.wav' Click() usr << melody // play them tunes!
Login и Logout
Когда игрок соединяется с сервером, вызывается процесс Login(). И наоборот, когда игрок отключается, вызывается Logout(). Часто игрок уже отсоединился, к тому времени когда запускается Logout(). Тем не менее, вы можете вызвать Logout самостоятельно, если хотите отключить игрока.
Login() Logout()
Распространенный способ использования Login() – приветствие игроков при подключении к игре и начальное расположение их на карте. По умолчанию, Login() располагает игрока в первом же свободном месте.
turf/landing_pad name = "landing pad" mob/Login() if(loc) //reconnecting usr << "Welcome back, [name]." else usr << "Welcome, [name]!" loc = locate(/turf/landing_pad)
Если есть возможность повторного подключения игрока к существующему персонажу, то стоит лучше проверить, есть ли у игрока уже заданное местоположение, прежде чем разместить его снова. С другой стороны, если вы не хотите чтобы персонажи сохранялись после отключения игрока, то вы сможете этого достигнуть, изменив Logout.
mob/Logout() del src
Кроме того, вы можете заставить персонажа исчезнуть, установив его location равным null. Это быстрый и простой способ "спасти" игроков, после их отключения от системы. К сожалению, он всё же не переживет выключения сервера, когда вам понадобится перезагрузить его после некоторых внесённых изменений в коде, или обнаружения серьёзной ошибки в работе сервера. В таких случаях, необходимо использовать файл сохранения, который будет обсуждаться в главе 12.
Topic
Когда игрок нажимает на гиперссылку, которая ссылается на объект, выполняется процесс Topic этого объекта. Гиперссылка – это интерактивная область в отображающемся тексте (обычно подчёркнутая), при нажатии на которую, вызывается какое-либо действие. В нашем случае, гиперссылка называется topic-link, потому что она содержит информацию (Topic) которая отображается для игрока.
Во-первых, вы должны знать, как вставить ссылку на объект в тексте. Так как гиперссылки, широко применялись в веб-разработке, DM использует тот же синтаксис что и веб-документ - HTML. Это немного странно, но лучше немного странности, чем нагруженный синтаксисом код. Так что после этой мудрости, мы используем HTML тег <A> (anchor - якорь), чтобы сформировать гиперссылку.
"... <a HREF=#\ref[Obj]Topic> Click here </ A> ..." Obj – связанный объект. Topic – связанная информация.
Код \ref[Obj] используется для указания объекта, связанного с гиперссылкой. Может сопровождаться любым текстом, необходимым для описания ссылки на объект.
Игрок, конечно же не видит тот ужас, внутри скобок <>. Все что он делает, это нажимает на ссылку, чтобы вызвать Topic объекта с дополнительной информацией по теме.
Topic(topic) topic - связанная информация.
В следующем примере используются ссылки на основные темы для разговора NPC.
mob/Noah/Topic(Topic) if(Topic == "weather") usr << "Looks a little stormy." if(Topic == "storm") usr << "I'd say about 40 days worth!"
mob/Noah/verb/hello() set src in view() usr << "Nice weather, eh?"
Ссылки на объекты это лишь один из методов создания гиперссылок. Больше буде сказано в 9.2.4 и 11 главе.