Конструкторы
Конструктор -- это функция, которая создаёт за вас объект и заполняет его атрибуты так, как вам это нужно. Рассмотрим пример. Допустим, в вашей игре будет много окон. Нужно создавать окна, любое окно можно разбить. Мы можем написать конструктор 'window'.
window = function(v)
v.window = true
v.broken = false
if v.dsc == nil then
v.dsc = 'Здесь есть {окно}.'
end
v.act = function(s)
if s.broken then
p [[Окно разбито.]]
else
p [[За окном темно.]]
end
end
if v.used == nil then
v.used = function(s, w)
if w^'молоток' then
if s.broken then
p [[Окно уже разбито.]]
else
p [[Я разбил окно.]]
s.broken = true;
end
return
end
return false
end
end
return obj(v)
endКак видим, идея конструкторов проста. Вы просто создаёте функцию, которая получает на вход таблицу с атрибутами {}, которую конструктор может дозаполнить нужными атрибутами. Затем эта таблица передаётся конструктору obj/room/dlg и возвращается полученный объект.
Теперь, создавать окна стало легко:
window {
dsc = [[Тут есть {окно}.]];
}Или, так как окно -- это обычно статический объект, можно создавать его прямо в 'obj'.
obj = { window {
dsc = 'В восточной стене есть {окно}.';
}
};У нашего окна будет готовый used метод и act метод. Вы можете проверить тот факт, что объект окно -- просто проверив признак window:
use = function(s, w)
if w.window then
p [[Действие на окно.]]
return
end
return false
endСостояние "разбитости" окна, это атрибут broken.
Как реализовать наследование в конструкторах?
На самом деле, в примере выше уже используется наследование. Действительно, ведь конструктор 'window'' вызывает другой конструктор 'obj', тем самым получая все свойства обычного объекта. Также, 'window' определяет переменную признак 'window', чтобы в игре мы могли понять, что мы имеем дело с окном. Например:
Для иллюстрации механизма наследования создадим класс объектов 'treasure', то есть сокровищ.
global { score = 0 }
treasure = function()
local v = {}
v.disp = 'сокровище'
v.treasure = true
v.points = 100
v.dsc = function(s)
p ('Здесь есть {', std.dispof(s), '}.')
end;
v.inv = function(s)
p ('Это же ', std.dispof(s), '.');
end;
v.tak = function(s)
score = score + s.points; -- увеличим счёт
p [[Дрожащими руками я забрал сокровища.]];
end
return obj(v)
endА теперь, на его основе создадим золото, алмаз и сундук.
gold = function(dsc)
local v = treasure();
v.disp = 'золото';
v.gold = true;
v.points = 50;
v.dsc = dsc;
return v
end
diamond = function(dsc)
local v = treasure();
v.disp = 'алмаз';
v.diamond = true;
v.points = 200;
v.dsc = dsc;
return v
end
chest = function(dsc)
local v = treasure();
v.disp = 'сундук';
v.chest = true
v.points = 1000;
v.dsc = dsc;
return v
endТеперь, в игре можно создавать сокровища через конструкторы:
diamond1 = diamond("В грязи я заметил {алмаз}.")
diamond2 = diamond(); -- тут будет стандартное описание алмаза
gold1 = gold("В углу я заметил блеск {золота}.");
room {
nam = 'пещера';
obj = {
diamond1,
gold1,
chest("А ещё я вижу {сундук}!")
};
}На самом деле, как именно писать функции-конструкторы и реализовывать принцип наследования, зависит только от вас. Выберете наиболее простой и понятный способ.
При написании конструкторов иногда бывает полезным сделать вызов обработчика так, как это делает INSTEAD. Для этого используется 'std.call(объект, метод, параметры)', при этом эта функция вернёт реакцию атрибута в виде строки. Например, рассмотрим модификацию 'window', которая заключается в том, что можно определять свою реакцию на осмотр окна, которая будет выполнена после стандартного сообщения о том, что это разбитое окно (если оно разбито).
window = function(nam, dsc, what)
local v = {} -- создаём пустую таблицу
-- заполняем её
v.window = true
v.what = what
v.broken = false
if dsc == nil then
v.dsc = 'Здесь есть {окно}'
end
v.act = function(s)
if s.broken then
p [[Окно разбито.]]
end
local r, v = stead.call(s, 'what')
if v then -- обработчик выполнился?
p(r)
else
p [[За окном темно.]]
end
end
return obj(v)
endТаким образом, мы можем при создании окна задать третий параметр, в котором определить функцию или строку, которая будет реакцией во время осмотра окна. При этом сообщение о том, что окно разбито (если оно действительно разбито), будет выведено перед этой реакцией.