Skip to content

Конструкторы

Конструктор -- это функция, которая создаёт за вас объект и заполняет его атрибуты так, как вам это нужно. Рассмотрим пример. Допустим, в вашей игре будет много окон. Нужно создавать окна, любое окно можно разбить. Мы можем написать конструктор 'window'.

lua
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 и возвращается полученный объект.

Теперь, создавать окна стало легко:

lua
window {
	dsc = [[Тут есть {окно}.]];
}

Или, так как окно -- это обычно статический объект, можно создавать его прямо в 'obj'.

lua
obj = { window {
		dsc = 'В восточной стене есть {окно}.';
	}
};

У нашего окна будет готовый used метод и act метод. Вы можете проверить тот факт, что объект окно -- просто проверив признак window:

lua
use = function(s, w)
	if w.window then
		p [[Действие на окно.]]
		return
	end
	return false
end

Состояние "разбитости" окна, это атрибут broken.

Как реализовать наследование в конструкторах?

На самом деле, в примере выше уже используется наследование. Действительно, ведь конструктор 'window'' вызывает другой конструктор 'obj', тем самым получая все свойства обычного объекта. Также, 'window' определяет переменную признак 'window', чтобы в игре мы могли понять, что мы имеем дело с окном. Например:

Для иллюстрации механизма наследования создадим класс объектов 'treasure', то есть сокровищ.

lua
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

А теперь, на его основе создадим золото, алмаз и сундук.

lua
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

Теперь, в игре можно создавать сокровища через конструкторы:

lua
diamond1 = diamond("В грязи я заметил {алмаз}.")
diamond2 = diamond(); -- тут будет стандартное описание алмаза
gold1 = gold("В углу я заметил блеск {золота}.");

room {
        nam = 'пещера';
        obj = {
                diamond1,
                gold1,
                chest("А ещё я вижу {сундук}!")
        };
}

На самом деле, как именно писать функции-конструкторы и реализовывать принцип наследования, зависит только от вас. Выберете наиболее простой и понятный способ.

При написании конструкторов иногда бывает полезным сделать вызов обработчика так, как это делает INSTEAD. Для этого используется 'std.call(объект, метод, параметры)', при этом эта функция вернёт реакцию атрибута в виде строки. Например, рассмотрим модификацию 'window', которая заключается в том, что можно определять свою реакцию на осмотр окна, которая будет выполнена после стандартного сообщения о том, что это разбитое окно (если оно разбито).

lua
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

Таким образом, мы можем при создании окна задать третий параметр, в котором определить функцию или строку, которая будет реакцией во время осмотра окна. При этом сообщение о том, что окно разбито (если оно действительно разбито), будет выведено перед этой реакцией.