Это нарезка с форума, где Пётр описывал особенности нового стека движка Instead.
----
== Объявление объектов в stead3.
obj { nam = 'яблоко'; }
Вроде, похоже на stead2? Да, но есть важные отличия.
nam может быть только строкой. Он не может быть функцией. Не может быть булевым значением. Только строка. nam -- однозначный и достаточный идентификатор объекта. Что это значит? После того как вы создали объект, вы всегда можете получить к нему доступ с помощью:
std.ref 'яблоко' или (более кратко) std 'яблоко' или (еще более кратко) _'яблоко'
То-есть, можно написать что-то вроде: _'яблоко'.variable = true
Если вам нравится старый стиль, как это было в stead2, вы можете сделать ссылку при объявлении:
apple = obj { nam = 'яблоко'; } apple.variable = true
Или получить временную/постоянную ссылку позже:
apple = _'яблоко' local a = _'яблоко'
В обработчике вы можете сопоставить объект его имени просто:
use = function(s, w) if w/'яблоко' then p [[Я откусил яблоко.]] end end
Или, конечно, так:
use = function(s, w) if w == _'яблоко' then p [[Я откусил яблоко.]] end end
Итак, nam стал единственным идентификатором. Для отображения объекта в инвентаре используется он-же, или, если вы задали disp -- то disp. disp уже может быть и строкой и функцией (впрочем, как это было и раньше).
При размещении объектов в списках (в комнатах, например) мы используем имя объекта, то -есть:
obj { nam = 'яблоко'; } room { obj = { 'яблоко' }; }
Вы можете не задавать nam у объекта (или комнаты), при этом имя будет сгенерировано автоматически в виде цифры. Это удобно для деккораций или динамически создаваемых объектов.
Имена объектов должны быть уникальными. Если вы создадите еще один объект с тем-же именем, вы сразу же получите ошибку.
Но иногда нет смысла именовать объект руками. Например, когда мы создаем его прямо в сцене:
obj { nam = 'main'; obj = { disp = 'стол'; dsc = 'тут стоит стол'; } }
Или у нас очень много однотипных объектов, типа фраз в диалоге. Мы же не собираемся каждой фразе давать уникальное имя? Если мы не зададим имя, движок сам создаст числовой индекс для такого объекта, но пользоваться им неудобно. Для таких случаев предусмотрены теги. Тег - это строка, которая начинается с символа # (таким образом в коде игры всегда понятно о чем мы говорим, о тегах или именах). Теги не обязаны быть одинаковыми, но они должны быть уникальными в пределах комнаты (вернее говоря, просто при поиске по тегу мы найдем 1-й такой объект).
obj { nam = 'main'; obj = { tag = '#стол'; -- nam = 'имя стола'; -- мы могли бы дать имя стулу, но зачем? dsc = 'тут стоит стол'; } }
Или, альтернативная форма записи (когда мы не задаем имя, а только тег):
obj { nam = 'main'; obj = { nam = '#стол'; -- на самом деле мы задали тег, -- который может мыслится как локальное имя dsc = 'тут стоит стол'; } }
При этом, в качестве отображения в инвентаре используется disp, если его нет, то тег, с убранным первым символом #. Большинство функций STEAD3 умеют работать как с тегом, так с именем, например:
walk '#переход1'
Будет искать объект с тегом #переход1 среди доступных переходов.
Теги очень широко используются в диалогах, вы можете посмотреть это на примере диалога, который приведен выше в этой ветке.
Говоря об объектах, нужно еще сказать, что в room появился новый атрибут -- title. Теперь disp (или tag/nam) используется для показа названия перехода в эту комнату, а title -- становится названием комнаты, когда мы в нее перешли. Если title не задан, то стандартная схема disp/tag/nam.
== Про переменные.
В stead3 вам не нужно определять переменные объекта, которые вы хотите сохранить, через var {}. В stead2 было так:
apple = obj { nam = 'яблоко'; var { eaten = false }; -- сохранится _red = true; -- сохранится, так как начинается с символа _ }
В stead3:
obj { nam = 'яблоко'; eaten = false; }
eaten сохранится, если в процессе игры значение этой переменной менялось.
То-есть, если где-то в коде было _'яблоко'.eaten = true, то такая переменная сохранится.
Если где-то в коде есть _'яблоко'.newvar = 1, то такая переменная тоже сохранится (создание переменных на лету).
UPD: По результатам обсуждения решили, что по умолчанию создание переменных на-лету запрещено. Все переменные должны быть объявлены заранее. Если вы не указали std.nostrict = false в начале игры. :) Либо, для добавления переменных на лету используйте:
apple 'variable' (10) -- создали переменную на-лету
Есть только определенная особенность с таблицами. Дело в том, что таблица будет сохранена в save если к ней в процессе игры был доступ хотя бы на чтение.
obj { nam = 'яблоко'; tabl = { 1, 2, 3}; -- будет сохранена, если к таблице были обращения }
Это сделано из-за того, что модификации внутри таблицы сложно отследить (без лишнего усложнения кода движка), поэтому таблицы сохраняются целиком. Но что если вы не хотите, чтобы таблица (или переменная) попадала в save файл? Тогда придется написать так:
obj { nam = 'яблоко'; { -- блок переменных, за которыми не следит instead tabl = { 1, 2, 3}; -- не будет сохранена x = 8; -- не будет сохранена } }
Что с глобальными переменными?
В stead3 теперь отслеживается доступ к неинициализированным переменным. Таким образом, описки в именах переменных, обращение к несуществующей переменной -- сразу дадут ошибку. Присвоение неинициализированных переменных разрешено только если:
значение - это объект
значение - это функция
Всего теперь есть три вызова:
global -- объявление глобальной переменной
const -- объявление константы (стед3 сдедит за тем, чтобы переменная не менялась)
declare -- объявление переменной, за которой не следит stead3
Форма записи допускает следующие варианты использования:
Как раньше:
declare { a = 1, b = 2, ... }
Или одиночное объявление:
declare 'myfunc' (function() pn "Hello!" end) global 'X' (11)
Все формы объявлений контролируют уникальность имен переменных. Вы не сможете создать две переменные с одинаковыми именами.
Для чего нужен global и const -- понятно. Но зачем declare?
В stead2 единственным способом записать функцию в save была конструкция code [[ ]]
В stead3 такой конструкции нет. Зато тут можно объявлять функции!
declare 'myfunc' (function() pn "Hello!" end) global 'X' (myfunc)
X сохранится и в save будет записана ссылка на функцию! Так что, теперь вместо code можно декларировать функции. Но это можно делать только заранее, в глобальном контексте игры, примерно как определяются объекты/комнаты.
declare также полезен в тех случаях, когда вам не нужно, чтобы инстед вообще не следил за вашей структурой данных.
Что с динамическими объектами?
У нас есть new, но прототип теперь другой. В качестве иллюстрации, напишу пример:
declare 'mycat' (function(nam) return obj { nam = nam; dsc = 'Тут сидит котик!' } end)
где то в недрах игры:
act = function(s) p [[Я взял еще одного котика.]] take(new(mycat, "Барсик")) end
Как видим, теперь new принимает на вход не текст, а задекларированный конструктор и параметры к нему в виде параметров функции.
== Как работают ссылки?
Варианты использования:
dsc = "Тут лежит {яблоко}"
Это, как и в 2.4, следующий вариант --- аналог xact:
dsc = "Тут лежит {яблоко} и {груша|сладкая груша}. А еще {#неведомая штука|странный предиет}"
Это ссылка на другой объект. Если ссылка неверная, будет ошибка. Объект должен быть доступен игроку (быть в зоне видимости).
И, наконец, вариант с ссылкой на системный объект. В stead3 есть понятие системных объектов. Такие объекты не уничтожаются и существуют весь цикл игры. Их имена начинаются на '@'. Например, \@timer, \@sound, \@sprite и тд.
Так вот, можно создавать ссылки на системные объекты. При этом не обязательно, такой объект должен находиться в комнате. Например:
dsc = "Нажми на {@button|кнопочку}"
При этом вызовется act объекта button. Системные объекты могут получать параметры. Так, в stead3 уже есть стандартный объект с именем @, который удобно использовать как обработчик xact, например:
xact.walk = walk -- переход dsc = "Идем {@ walk room2|вперед}
То-есть, берем объект @, у него вызываем act, передаем параметры walk и room (строки в данном случае). Стандартный обработчик _'@'.act действует сл образом -- находит метод, который совпадает с именем 1го параметра и вызывает его. Таким образом объект @ выполняет функции, которые выполнял модуль xact в stead2
При передаче параметров действуют соглашения:
Если это строка вида "something" -- идет как строка.
Если это число 1234... true или false -- идет как число или булево.
Все остальное становится строкой.
Параметры разделяются ,.
== Насчет обработчиков.
При переходах.
Было: exit/left/enter/entered.
Теперь: onexit/exit/onenter/enter.
Смысл примерно такой, onXXX обработчики способны предотвратить цепочку. Теперь onXXX обработчики есть у всего. У act, у use. При этом логика такая: сначала запускается onXXX у объекта game, потом у текущего игрока, потом у комнаты. потом выполняется действие.
Каждый из onXXX может прекратить действие, вернув false. onXXX обработчик для use -- это used, который вызывается у страдательного объекта. Надеюсь, я более-менее объяснил все корректно. :)
Каждое действие над объектом считается. Всегда можно получить число действий над объектом с помощью action(объект, имя действия). А visits и visited для комнат, работают как и в stead2.
disable/enable объекта теперь действует несколько по иному. Они влияют на видимость объекта игроку, но не на возможность найти объект из кода. Грубо говоря: и walk и take итд будут работать с disabled объектом, но игрок этот объект не увидит. Есть специальные функции для определения видимости: seen, disabled(), если это нужно.
Кроме disable/enable можно делать close/open. Это влияет на видимость перехода, видимость фраз в диалоге, а также видимость вложенных объектов в объект. Если объект закрыт -- его содержимое не видно, но сам объект -- виден (в отличие от disabled объекта).
Еще одно отличие в отображении сцены.
Есть dsc -- это описание, которое пропадает после 1го показа.
Есть decor -- описание, которое всегда показывается и служит для связного описания статических декораций с ссылками.
Есть объекты -- которые показаны всегда.
Необходимость decor назрела уже давно. Часто в комнате есть много статических объектов, которые хочется описать связанно. Теперь это можно сделать без создания фиктивного объекта.
Вследствие этого, forcedsc теперь нет.