Динамические события
Вы можете определять обработчики, которые выполняются каждый раз, когда время игры увеличивается на 1. Обычно, это имеет смысл для живых персонажей, или каких-то фоновых процессов игры. Алгоритм шага игры выглядит примерно так:
- Игрок нажимает на ссылку;
- Реакция 'act', 'use'', 'inv', 'tak', осмотр сцены (клик по названию сцены) или переход в другую сцену;
- Динамические события;
- Вывод нового состояния сцены.
Например, сделаем Барсика живым:
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;
...И вот момент в игре, когда Барсик попадает к нам за пазуху!
take 'Барсик' -- добавить в инвентарь
lifeon 'Барсик' -- оживить Барсика!Любой объект (в том числе и сцена) могут иметь свой обработчик 'life', который вызывается каждый такт игры, если объект был добавлен в список живых объектов с помощью 'lifeon'. Не забывайте удалять живые объекты из списка с помощью 'lifeoff', когда они больше не нужны. Это можно сделать, например, в обработчике 'exit', или любым другим способом.
Если в вашей игре много "живых" объектов, вы можете задавать им явную позицию в списке, при добавлении. Для этого, воспользуйтесь вторым числовым параметром (целое неотрицательное число) 'lifeon', чем меньше число, тем выше приоритет. 1 -- самый высокий. Или вы можете использовать атрибут pri у объекта. Правда, этот атрибут будет влиять на приоритет объекта в любом списке.
Если вам нужен фоновый процесс в какой-то комнате, запускайте его в 'enter' и удаляйте в 'exit', например:
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()'.
obj {
nam = 'фонарик';
on = false;
life = function(s)
if player_moved() then -- гасить фонарик при переходах
s.on = false
p "Я выключил фонарик."
return
end;
end;
...
}Для отслеживания протекающих во времени событий, используйте 'time()' или вспомогательную переменную-счётчик. Для определения местоположения игрока -- 'here()'. Для определения факта, что объект "живой" -- 'live()'.
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, то это будет признаком важного события, которое выведется до описания объектов сцены, например:
p 'В комнату вошёл охранник.'
return trueИли:
return 'В комнату вошёл охранник.', trueЕсли вы вернёте false, то цепочка life методов прервётся на вас. Это удобно делать при выполнении walk из метода life, например:
life = function()
walk 'theend'
return false -- это последний life
endЕсли вы хотите блокировать 'life' обработчики в какой-то из комнат, воспользуйтесь модулем 'nolife'. Например:
require "noinv"
require "nolife"
dlg {
nam = 'Охранник';
noinv = true;
nolife = true;
...
}Отдельно стоит рассмотреть вопрос перехода игрока из 'life' обработчика. Если вы собираетесь использовать функции 'walk' внутри 'life', то вам следует учитывать следующее поведение.
Если 'life' переносит игрока в новую локацию, то обычно предполагается что вы:
- Очищаете вывод реакций: game:reaction(false);
- Очищаете вывод живых методов на данный момент: game:events(false, false)
- Делаете walk.
- Останавливаете цепочку life вызовов с помощью return false;
Некоторые моменты требуют пояснений.
game:reaction() -- позволяет взять/изменить вывод реакции пользователя, если задать его в false это означает сбросить реакцию.
game:events() -- позволяет взять/изменить вывод life методов. В качестве параметров принимаются приоритетные и не приоритетные сообщения, задав false, false мы отменили весь вывод предыдущих life методов.
В стандартной библиотеке уже есть функция life_walk(), которая делает описанные действия. Вам остаётся только вернуть false.