DM Guide 6
Это 6 глава перевода оригинального руководства по Dream Maker от разработчиков
Остальные главы руководства и статьи о программировании на коде BYOND от любителей на русском.
Оригинальная 6 глава руководства на английском языке
В разработке… |
Данная статья помечена как неоконченная. Это означает, что статья находится на доработке, поэтому является неверной или неактуальной. Вы можете помочь проекту Onyx и сообществу SS13 в целом — зайдите на наш Портал сообщества. |
Глава 6: Действия Proc
Только он потер лампу, перед ним возник джин и спросил: 'Чего ты хочешь?'. — Аладдин и волшебная лампа. Арабские ночи.
Язык ДМ поддерживает несколько видов процедур. Первый - Verb, видимые игроком команды. Второй, не видимый игрокам, называется proc. В остальном эти процедуры можно назвать идентичными.
Создание процедуры
Также процедуры можно использовать для того чтобы избежать копирования кода.
К примеру, в вашей игре может быть несколько ситуаций, в которых игрок может получить урон. Разумно в каждой такой ситуации проверять, не убил ли этот удар игрока. Для того чтобы не писать отдельную проверку в каждой такой ситуации (и случайно не забыть написать эту проверку), вы можете написать для этого специальную процедуру.
mob var/life = 100 proc/HurtMe(D) life = life - D if(life < 0) view() << "[src] dies!" del src
Этот пример описывает процедуру с именем НанесиМнеУрон (HurtMe), которая принимает один аргумент - полученный урона. Полученный урон вычитается из текущего числа жизней персонажа и происходит проверка, остался ли он в живых. Если число жизней оказалось меньше нуля, персонаж удалится.
Оператор if, используемое в примере выше, это одна из множества процедур (инструкций), которые будут описанные в следующих частях руководства. Сейчас достаточно знать, идущий после if (с большим отступом), будет выполнен только в том случае, если условие, идущее в скобках после if - истинно.
Выполнение процедуры
Синтаксис выполнения процедуры схож с доступом к переменной, но, при выполнении, в скобках добавляются передаваемые в процедуру аргументы. В следующем примере, мы выполним процедуру HurtMe, когда персонаж выпьет отравленное зелье.
obj/poison
name = "can of soda" verb/drink() set src in view(0) usr.HurtMe(25) //OUCH! del src //one use only (please recycle)
Чаще всего вместо фразы 'Выполнить процедуру' используют фразу 'Вызвать процедуру'. Эти фразы означают одно и тоже.
Подобный синтаксис используется при вызове процедур обоих видов: Proc и Verb, но чаще verb вызывается игроками. Хорошим тоном считается наименование процедур Proc словами, начинающимися с заглавной буквы, а verb - с маленькой. Этот ход, кроме визуального разделения, позволяет избежать ситуации, когда имя процедуры proc совпадет с именем verb. Такие совпадения вызывают ошибку при компиляции, поскольку программа не сможет понять, вызываете вы процедуру вида proc или verb.
Когда вы вызываете процедуру, компьютер выполняет каждую команду по очереди (одну за раз), начиная с верху. Некоторые операторы, как if из примера выше, могут приказать компьютеру пропустить часть команд или же наоборот выполнить часть из них несколько раз подряд. Но, за исключением этих особых случаев, все команды выполняются последовательно. Когда все команды будут выполнены, процедура будет закончена. Программисты называют это возвратом, потому, что продолжится выполнение кода в той точке из которой была вызвана процедура (если таковая существует).
Наследование процедур
Так же, как в ситуации с verb, процедуры proc могут быть переопределены во время наследования. Синтаксис переопределения разных видов процедур совпадает. Исходная процедура отличается от переопределений наличием слова proc. После этого она может быть переопределена, для переопределения не требуется слова proc.
Это позволяет, на пример, создать несколько персонажей, которые по-разному будут реагировать на получаемый урон.
mob/DM var/vulnerable verb/set_vulnerability(v as num) vulnerable = v HurtMe(N) if(vulnerable) ..()
Этот код позволяет DM быть уязвимым или не уязвимым по желанию. В переопределенном HurtMe, первым делом происходит проверка на неуязвимость. Если DM в данный момент является уязвимым (для теста или чего-то подобного), будет вызвана родительская процедура, в данном случае, исходная версия процедуры HurtMe.
Гибкость Аргументов
Когда вы вызываете процедуру, вы можете передать в нее столько аргументов, сколько вы хотите. Всем аргументам, не переданным в процедуру при вызове, будет установлено значение равное null. Гибкость позволяет вам, к примеру, добавить несколько аргументов в переопределении процедуры. Если при вызове процедуры, не будут переданы эти добавленные аргументы, их значение станет равным null.
Вы также можете убрать часть аргументов, в переопределенной процедуре. Это, обычно, не влияет на код и делается только для удобства, в случае, когда часть аргументов не используется процедурой. Например, HurtMe объекта DM может быть переписана следующим образом:
mob/DM/HurtMe()
if(vulnerable) ..()
Поскольку, мы не используем переменную N (полученный урон), мы можем вовсе не объявлять этот аргумент. Тем не менее, вызовы этой процедуры с аргументом так же будут работать. Что более важно, родительская процедура будет получать переданные аргументы, даже если они не были объявлены в дочерней процедуре. Это происходит по тому, что вызов ..() без аргументов, передает в родительскую процедуру, аргументы переданные дочерней процедуре.
Глобальные процедуры
Некоторые процедуры могут не иметь ничего общего с каким-либо объектом. Они могут быть объявлены в верхнем уровне, для повсеместного доступа к ним. Такие процедуры, обычно, выполняют различные автономные (не зависящие от объектов) расчеты.
Язык ДМ имеет множество предопределенных процедур (такие как view() или locate()), которые возвращают многократно используемые результаты. Для того, чтобы отличать их от определенных вами процедур, их часто называют инструкциями.
Объявление глобальных процедур
Игра, в которой, к примеру, знаки зодиака играют важную роль, может иметь процедуры подобные следующей:
proc/Constellation(day)
//day should be 1 to 365 if(day > 354) return "Capricorn" if(day > 325) return "Sagittarius" if(day > 295) return "Scorpio" if(day > 265) return "Libra" if(day > 234) return "Virgo" if(day > 203) return "Leo" if(day > 172) return "Cancer" if(day > 141) return "Gemini" if(day > 110) return "Taurus" if(day > 79) return "Aries" if(day > 50) return "Pisces" if(day > 20) return "Aquarius" return "Capricorn" //day 1 to 20
Может потребоваться другая процедура, которая может конвертировать дни определенного месяца, в дни года. Этот код, в зависимости от даты, будет возвращать знак зодиака. Чуть позже мы рассмотрим детали реализации этого кода.
Вызов глобальных процедур
Глобальные процедуры вызываются также как и любые другие. Если такая процедура возвращает значение, оно может использоваться в любом месте, где требуется выражение.
Термин выражение означает, любую часть кода, которая в результате своего выполнения возвращает некое значение. Простейшим примером выражения можно считать константу, такую как число или текст. Более сложными выражениями являются переменные, операторы, вызовы процедур и т.д.
Это пример того, как вызвать процедуру и использовать, то, что она вернет.
var/day_of_year = 1 mob/DM/verb/set_date(day as num) set desc = "Enter the day of the year (1-365)." day_of_year = day world << "The sign of [Constellation(day)] is in force."
Этот verb дает DM возможность изменять текущую дату, после чего, всем будет сообщено об изменении текущего знака зодиака. Вызов процедуры, в данном случае, просто встраивается в текст, как и любое другое выражение.
Язык процедур
В следующей части мы изучем предопределенные процедуры. Сейчас вы знаете, как переопределять процедуры и объявлять новые созданные вами, это позволит вам создавать объект более подходящие под ваши нужды. Но, перед тем, как заняться процедурами всерьез, вы должны выучить их язык (вы можете сделать это и в "боевых" условиях, но цена ошибки будет совсем другой). В зависимости от ваших предпочтений в языках программирования (особенно если это С или близкие к нему языки), вы можете сами выбрать то, насколько тщательно вам нужно вчитываться в следующий материал.
Операторы
Основной единицей процедуры является оператор - команда, которая говорит компьютеру, что нужно сделать. До этого момента вы уже видели операторы, которые присваивают переменные, выводят данные и вызывают процедуры.
Операторы обычно располагаются в линию. Хотя они могут быть сгруппированы в одну строку, если поставить ; (точка с запятой) между ними. Также можно разбить оператор на несколько строк, если в конце каждой, кроме последней из них ставить \ (обратный слеш).
Statement 1 Statement 2; Statement 3; ... Statement 4 (begin) \ Statement 4 (end)
Кроме таких простых операторов существуют составные, такие как if, которые могут комбинировать несколько простых выражение в один составной. Дальше вы увидите все возможные виды операторов используемых в языке DM.
Возврат
Все виды процедур возвращают что-либо. Даже действия verb, что-то возвращают, но это редко используется. Если процедура завершается без возврата какого-либо явного значения, то возвращается специальное значение null.
Оператор return
Вы уже видели пример возврата значения с использованием выражения return. Основной формат записи возврата следующий:
return [expression]
Оператор return приказывает процедуре прекратить выполнение. Если возвращаемое выражение определено, оно возвращается в точку вызова процедуры. Выражение означает любой блок кода, создающий число. Это может быть константа, математическое вычисление или результат вызова другой процедуры.
Переменная . (точка)
Если процедура завершается без оператора return, или return без следующего за ним выражения, возвращаемая величина берется из переменной . (точка). Этой переменной (как и любой другой) может быть присвоено значение. По умолчанию в . (точке) хранится null. Поэтому процедура завершившаяся без явного указания возвращаемой величины возвращает null.
Выбор между использованием return и точкой - вопрос удобства. Но иногда, возвращаемая величина бывает рассчитанной до завершения процедуры (не все процессы завершены). Тогда удобнее будет использовать точку. Либо, если вы захотите определить другое возвращаемое по умолчанию значение.
Такое имя для переменной было выбрано для того, чтобы создать ассоциацию с текущей процедурой, по аналогии с использованием точки во множестве файловых систем для обозначения текущей директории. Это также хорошо сочетается, с двумя подряд идущими точками, для обозначения родительской процедуры (и родительской директории в файловых системах). Эта аналогия идет и дальше, вы сможете заметить это во время дискуссии о типах.
Оператор if
Для выполнения блока кода в зависимости от состояния, используется оператор if. Основной его синтаксис:
if (expression)
Statement1 Statement2 . . .
Или
if(expression) Statement
Первый формат, позволяет одновременно блокировать/разрешать выполнение для нескольких операторов, в блоке с отступом (относительно if). Второй - уплотненный формат, для одного. Все составные операторы поддерживают оба описанных выше формата. Для краткости, дальше все примеры будут приводиться в плотном (втором) формате, поэтому важно понять, что любой одиночный оператор в них может быть заменен блоком кода с отступом.
Существует еще один путь группировки нескольких операторов в один - установка фигурных скобок вокруг операторов. После их также можно расположить на одной строке.
if(expression) {Statement1; Statement2; ...}
Операторы внутри оператора if называются его телом. Они будут выполняться, только если выражение в круглых скобках будет соответствовать истине. DM не имеет специального типа данных для определения понятий ИСТИНА и ЛОЖЬ. Вместо этого, каждый тип имеет свое представление истины, связанное с его значением. Вы скоро увидите, как это работает.
Дополнение else
Перед тем, как начать рассматривать преобразования типов, мы должны разобраться с else. Если выражение в if будет ложным, альтернативное тело оператора будет выполнено. Объединив несколько операторов, мы сможем рассмотреть все состояния условий. Основной синтаксис для это представлен ниже:
if(expression1) Statement1 else if(expression2) Statement2 . . . else Statement3
Рассматривая эту конструкцию сверху-вниз, каждое выражение будет протестировано. До тех пор, пока не будет найдена истина, соответствующие тела операций будут выполняться. Обратите внимание, что если любое условие будет истинным, то остальные операторы else будут проигнорированы. Если ни одно из условий не будет истинным последнее тело else будет выполнено.
Логическое выражение
Любое выражение, стоящее в условии оператора if может быть интерпретировано как истина или ложь, программисты называют это булевым значением. В ДМ существует три случая, когда выражение интерпретируется как ложь: если выражение равно null, 0 или "" (пустая сторка). Во всех других случаях выражение интерпретируется, как истина.
Скорее всего, вам понадобятся логические константы, обычно для этого используют числа 1 и 0. Также часто, вместо константы используют флаговую переменную (как opacity) или значение возвращаемое процедурой. Также вы можете объявить константы TRUE (ИСТИНА) и FALSE (ЛОЖЬ), если не хотите использовать их числовые эквиваленты.