Это руководство по программированию в BYOND для начинающих от Kawaiidesu.

Оригинал статьи находится на нашем форуме.

Другие руководства по программированию в BYOND на русском языке вы можете найти в раздале All about the code.

Попробуем покодить?

Здравствуйте, я Kawaiidesu, левый мультиакк, и сегодня мы будем создавать объекты, переменные, методы и изучать основы логики.

Начнём-с.

ООП (Объектно-Ориентированное Программирование) - основа BYOND. Это не процедурный и далеко не императивный язык, здесь всё по большей части закручено именно на объектах, так что примем объект за низшую единицу счисления. Что же такое объект? Аирлок, плата в компьютере, "око" ИИ и т.д. Объект обладает чётко выраженными свойствами(переменные) и возможностями(методы). Но что я говорю, давайте же попробуем создать объект!

 /obj/object

Зачем же здесь obj, спросите вы? Позвольте пояснить: бьёнд обладает своей базой, базовыми объектами, которая позволяет легко и просто создавать новые объекты, производные от оных (наследование). Она называется atom (Area. Turf. Obj. Mob.) и позволяет нам воспользоваться одним из четырёх готовых объектов.

Но чем же они отличаются? Зона абсолютно уникальна для определённой точки, то есть в один тайл, мельчайшую единицу измерения пространства в бьёнде, не поставить две зоны. Турф относительно уникален - если потанцевать с бубном, можно поставить несколько в один тайл. Объект может спокойно двигаться, а моб может контролироваться игроком(хоть объекты тоже можно контролировать, но моб даёт более обширные возможности).

Я надеюсь, здесь ещё всё понятно и продолжу. Создадим что-нибудь относительно простое. Пускай это будет фонарик. Какие свойства должны быть у фонарика? Мы должны знать, включён ли он. Попробуем объявить переменную.

 /obj/flashlight
   var/on

Ах, да, табуляция - определённый промежуток от начала строки, очень важна в бьёнде. Здесь строки заканчиваются не по закрывающему символу ";" или "}", но по "\n" - то есть при переходе каретки на другую строку. В коде выше мы поставили одинарную табуляцию перед "var/on", этим мы обозначили то, что этот код принадлежит к "obj/flashlight". Можете попробывать представить код в виде иерархии - более высшему(меньше табуляции) принадлежит всё более низшее(участки кода под ним, в котором больше табуляции). Однако не переборщите с ней - если вы поставите две табуляции в коде выше, он не будет работать, поскольку структура кода будет нарушена.

К примеру мы можем объявить объект по-другому:

 /obj
   object

Всё, что по табуляции ниже /obj - принадлежит ему, но наше объявление переменной можно заменить на:

 /obj/object/flashlight/var/on

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

Но чему равна наша переменная? Пока - ничему. Абсолютно ничему. "Абсолютное ничто" в бьёнде равно значению "null". Сделаем так, чтобы фонарик был изначально выключен.

 /obj/flashlight
   var/on = 0

Как можно увидеть, мы делаем переменную равной нулю. Это значение по умолчанию, оно устанавливается при создании объекта, но может быть спокойно изменено. К тому же мы присваиваем нуль не только по велению левой пятки, но и потому что в бьёнде false, ложь, может быть выражена нулём или null'ом., а всё, что не равно этому - true, истина. Надеюсь, вы знаете логику.

Но как изменить значение? Здесь нам и понадобятся методы. Методы исполняют свой код во время вызова. В бьёнде методы делятся на "verb" - действие и "proc" - процедура, которая является функцией(функция возвращает значение, процедура - нет). Объявим небольшой метод, который будет менять значение переменной:

 /obj/flashlight
   var/on = 0

   proc/TurnOn()
     on = 1

Рассмотрим объявление метода. Оно начинается с "proc", которое говорит бьёнду о том, что мы создаём новый метод, а не переопределяем старый (далее). Дальше после слеша мы видим название метода, а после пустые скобки. Зачем они? В них можно объявить переменные - входные данные, которые могут использоваться в самом методе, в нашем же случае переменных нет, но скобки всё равно требуются, иначе бы метод был бы слишком похож на переменную. Далее мы можем увидеть табуляцию, которая показывает, что строка принадлежит к данному методу и будет исполняться при вызове метода.

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

 /obj/flashlight
   var/on = 0
   var/charge = 1000

   proc/TurnOn()
     on = 1

   proc/Use(var/t)
     chrage -= t

Теперь не обязательно указывать это значение, если оно не будет указано, то автоматически заменится на это, НО такие переменные должны быть указаны только после переменных без стандартного значения, то есть можно сделать "(var/t, var/k = 1)", но нельзя "(var/k = 1, var/t)". Будьте осторожны.

Кстати, если вы внимательны, то могли заметить, что я изменил первый метод на "Switch()". Внутри нашей переменной, отвечающей за состояние фонаря присваивается та же переменная, но отрицательная, НЕ-переменная. Как бы это не звучало, но работает это так - если в переменной стоит, допустим, 143, то мы получим 0, если стоит 0, то получим 1. Это очень удобно для проверок. Получается, что мы присваиваем переменной "on", которая равна 0, "!on", которая становится единицей. А если вызовем повторно, то уже "on", которая равна 1 "!on", которая равна нулю. Такие дела.

Время добавить немного проверок, чтобы фонарь не включался без заряда и чтобы заряд не уходил в минус. Начну со второго:

proc/Use(var/t = 3)
  charge = max(0, charge - t)

Я воспользовался системным методом, который определяет максимальное значение среди всех введённых и возвращает его. Таким образом мы проверяем, меньше ли "charge - t" нуля или нет. Если меньше - вернётся 0, иначе "charge - t".Таким образом заряд никогда не упадёт меньше нуля. Но если заряд равен нулю, то нам нужно выключить фонарь! Но даже это мы можем сделать, мы же кодеры! Добавим проверку:

proc/Use(var/t = 3)
  charge = max(0, charge - t)
  if(!charge)
    Switch()

Что-то новое! "if" это проверка, если в скобках будет истина - код под ней исполнится, обратите внимание на табуляцию. В данном случае мы можем увидеть в скобках "charge". Как я уже говорил - 0 это ложь, не 0 это истина, то есть если в коде выше заряду присвоится значение 0, то исполнится код. Отлично! Мы уже близки к тому, чтобы сделать фонарь идеальным. Стоп! А если сработает какое нибудь ЕМП и заряд потратится, пока фонарь выключен? Тогда он при заряде в нуль возьмёт и включится, а это не очень хорошо. Добавим ещё одну проверку и получим:

proc/Use(var/t = 3)
  charge = max(0, charge - t)
  if(!charge && on)
    Switch()

"on" это понятно, но что значит "&&" ? Это логический оператор И. Можно его представить как булевский (булевские значения это истина или ложь - основа компьютерной логики) знак умножения. Если мы умножим 0 на 1, получим 0, если 1 на 0, тоже 0, а если 1 на 1, то 1. То есть "Switch()" сработает только если заряд будет равен нулю И фонарь будет включён. Также есть "||" - оператор сложения. 1 и 1 будет 1, 1 и 0 будет 1 и лишь 0 и 0 вернут 0. Осталось закрыть последнюю дыру - фонарь можно включить, когда заряда нет. Но с вашими новыми знаниями это очень легко:

proc/Switch()
  if(!charge && !on)
    return
  on = !on

Что за return'? Это оператор возврата значения. Если его прописать без значения - как в нашем случае, он возвращает null, хотя это мы обсудим в другом уроке. В данном случае return работает как прерыватель исполнения кода. То есть после выполнения returnа, исполнение данного метода дальше не идёт.