Skip to content

Динамические события

Вы можете определять обработчики, которые выполняются каждый раз, когда время игры увеличивается на 1. Обычно, это имеет смысл для живых персонажей, или каких-то фоновых процессов игры. Алгоритм шага игры выглядит примерно так:

  • Игрок нажимает на ссылку;
  • Реакция 'act', 'use'', 'inv', 'tak', осмотр сцены (клик по названию сцены) или переход в другую сцену;
  • Динамические события;
  • Вывод нового состояния сцены.

Например, сделаем Барсика живым:

lua
obj {
	nam = 'Барсик';
	{ -- не сохранять массив lf
		lf = {
			[1] = 'Барсик шевелится у меня за пазухой.',
			[2] = 'Барсик выглядывает из-за пазухи.',
			[3] = 'Барсик мурлычит у меня за пазухой.',
			[4] = 'Барсик дрожит у меня за пазухой.',
			[5] = 'Я чувствую тепло Барсика у себя за пазухой.',
			[6] = 'Барсик высовывает голову из-за пазухи и осматривает местность.',
		};
	};
	life = function(s)
		local r = rnd(5);
		if r > 2 then -- делать это не всегда
			return;
		end
		r = rnd(#s.lf); -- символ # -- число элементов в массиве
		p(s.lf[r]); -- выводим одно из 6 состояний Барсика
	end;
...

И вот момент в игре, когда Барсик попадает к нам за пазуху!

lua
take 'Барсик' -- добавить в инвентарь
lifeon 'Барсик' -- оживить Барсика!

Любой объект (в том числе и сцена) могут иметь свой обработчик 'life', который вызывается каждый такт игры, если объект был добавлен в список живых объектов с помощью 'lifeon'. Не забывайте удалять живые объекты из списка с помощью 'lifeoff', когда они больше не нужны. Это можно сделать, например, в обработчике 'exit', или любым другим способом.

Если в вашей игре много "живых" объектов, вы можете задавать им явную позицию в списке, при добавлении. Для этого, воспользуйтесь вторым числовым параметром (целое неотрицательное число) 'lifeon', чем меньше число, тем выше приоритет. 1 -- самый высокий. Или вы можете использовать атрибут pri у объекта. Правда, этот атрибут будет влиять на приоритет объекта в любом списке.

Если вам нужен фоновый процесс в какой-то комнате, запускайте его в 'enter' и удаляйте в 'exit', например:

lua
room {
        nam  = 'В подвале';
        dsc = [[Тут темно!]];
        enter = function(s)
                lifeon(s);
        end;
        exit = function(s)
                lifeoff(s);
        end;
        life = function(s)
                if rnd(10) > 8 then
                        p [[Я слышу какие-то шорохи!]];
                        -- изредка пугать игрока шорохами
                end
        end;
        way =  { 'Дом' };
}

Если вам нужно определить, был ли переход игрока из одной сцены в другую, воспользуйтесь 'player_moved()'.

lua
obj {
	nam  = 'фонарик';
	on = false;
	life = function(s)
		if player_moved() then -- гасить фонарик при переходах
			s.on = false
			p "Я выключил фонарик."
			return
		end;
	end;
...
}

Для отслеживания протекающих во времени событий, используйте 'time()' или вспомогательную переменную-счётчик. Для определения местоположения игрока -- 'here()'. Для определения факта, что объект "живой" -- 'live()'.

lua
obj {
	nam  = 'динамит';
	timer = 0;
	used = function(s, w)
		if w^'спичка' then -- спичка?
			if live(s) then
				return "Уже горит!"
			end
			p "Я поджёг динамит."
			lifeon(s)
			return
		end
		return false -- если не спичка
	end;
	life = function(s)
		s.timer = s.timer + 1
		if s.timer == 5 then
			lifeoff(s)
			if here() == where(s) then
				p [[Динамит взорвался рядом со мной!]]
			else
				p [[Я услышал, как взорвался динамит.]];
			end
		end
	end;
...
}

Если 'life' обработчик возвращает текст события, он печатается после описания сцены.

Вы можете вернуть из обработчика 'life' второй код возврата, ('true' или 'false'). Если вы вернёте true, то это будет признаком важного события, которое выведется до описания объектов сцены, например:

lua
p 'В комнату вошёл охранник.'
return true

Или:

lua
return 'В комнату вошёл охранник.', true

Если вы вернёте false, то цепочка life методов прервётся на вас. Это удобно делать при выполнении walk из метода life, например:

lua
life = function()
	walk 'theend'
	return false -- это последний life
end

Если вы хотите блокировать 'life' обработчики в какой-то из комнат, воспользуйтесь модулем 'nolife'. Например:

lua
require "noinv"
require "nolife"

dlg {
        nam = 'Охранник';
        noinv = true;
        nolife = true;
...
}

Отдельно стоит рассмотреть вопрос перехода игрока из 'life' обработчика. Если вы собираетесь использовать функции 'walk' внутри 'life', то вам следует учитывать следующее поведение.

Если 'life' переносит игрока в новую локацию, то обычно предполагается что вы:

  1. Очищаете вывод реакций: game:reaction(false);
  2. Очищаете вывод живых методов на данный момент: game:events(false, false)
  3. Делаете walk.
  4. Останавливаете цепочку life вызовов с помощью return false;

Некоторые моменты требуют пояснений.

game:reaction() -- позволяет взять/изменить вывод реакции пользователя, если задать его в false это означает сбросить реакцию.

game:events() -- позволяет взять/изменить вывод life методов. В качестве параметров принимаются приоритетные и не приоритетные сообщения, задав false, false мы отменили весь вывод предыдущих life методов.

В стандартной библиотеке уже есть функция life_walk(), которая делает описанные действия. Вам остаётся только вернуть false.