Трюки и хаки INSTEAD
Если вы не знакомы с движком текстовых игр и визуальных новелл INSTEAD, то вы можете прочитать о нём здесь. Вкратце: это — очень удобный и понятный для программистов движок простых текстовых приключений (квестов). Что важно, он не только удобный, он ещё и легко расширяемый, благо все игры пишутся на Lua. Я не буду описывать, как писать на нём игры. Я буду описывать, как их можно пилить напильником. Любой инструмент познаётся в деле, не так ли?
Сценарий 1: предмет+предмет
У игрока есть инвентарь, в инвентаре есть несколько предметов. Стандартное поведение INSTEAD таково: вы щёлкаете на один предмет, затем щёлкаете на другой — так вы применили первый предмет на второй. Чтобы поймать это событие, надо определить обработчик use в первом предмете или used во втором. Но если вы примените второй предмет на первый, то потребуется опять перехватывать уже новое событие. А что, если вы хотите просто комбинировать предметы, без учёта порядка?Пишем хак.
cobj = function(v)
v.use = function(this,that)
return call(this, 'fuse', that);
end;
v.used = v.use;
return obj(v);
end
Вот так. Теперь достаточно объявлять объекты типа cobj и давать им функцию fuse:
rope = cobj{
nam = 'верёвка',
inv = function()
local response = 'Длинная крепкая пеньковая верёвка.';
return response;
end,
fuse = function(this, that)
if (that == lock) then
inv():del(lock);
inv():del(rope);
inv():add(rope_with_lock);
return 'Вы привязываете к концу верёвки замок.'
end;
end,
}
Сценарий 2: выдача случайной фразы в описании комнаты
Если сделать описание комнаты функцией, которая будет просто возвращать случайную фразу, то она выполнится лишь единожды и далее будет выдавать то же самое. Чтобы описание всё-таки менялось, надо выводить его не через return функции, а через текстовый буфер — функции p и pn:
something = function()
ifsen = ' ';
response = {'весёлого жука.', 'весёлого динозавра.', 'весёлого людоеда.', 'весёлого жука-людоеда.'};
return ifsen..response[rnd(#response)];
end
dinner = room {
nam = 'Весёлая столовая',
dsc = function()
p [[Я вижу]];
p (something())
end
};
Если не поставить скобки после something, то ничего не получится. Так можно получить что-то вроде:
Сценарий 3: новый тип объектов
О, это уже глубокое копание. Допустим, вам нужен новый тип объектов в комнатах. Скажем, вы пишете RPG (можно хоть MMORPG — лишь бы у игрока были библиотеки Lua для работы с сетью), и вам нужен список квестов. Для этого незачем пересобирать движок, достаточно немного покопаться в его Lua части:
function quest(v) --constructor
if v.nam == nil then error ("Не указано имя квеста.", 2) end
if v.short_dsc == nil then v.short_dsc = "" end
if v.scene == nil then v.scene = room_scene end
if v.completed == nil then v.completed = false end
if v.look == nil then v.look = room_look end
if v.save == nil then v.save = room_save end
v.location_type = true;
if v.way == nil then v.way = { } end
v.isQuest = true;
v.way = list(v.way);
v = obj(v);
return v;
end
function room_look(self)
local i,n,v,ph
for i,o in opairs(self.obj) do
if isObject(ref(o)) and not o.isQuest then
o = ref(o);
if v == nil then v = stead.par(' ',v, o:look());
else v = v .. stead.par(' ',v, o:look());
end
end
end
function room(v)
v.location_type = true;
if v.look == nil then v.look = room_look end
if v.scene == nil then v.scene = room_scene end
if v.quests == nil then v.quests = list {} end
if v.obj == nil then v.obj = v.quests
else for k,m in pairs(v.quests) do v.obj[k] = m end
end
if v.way == nil then v.way = {} end
v = room(v);
return v;
end
Сначала мы определяем новый тип объектов — квесты. Это практически копипаст из stead.lua объекта obj, только добавлены поле isQuest, чтобы легче различать квесты и объекты, и добавлено поле completed, которое отвечает за выполнение квеста.
room_look - это функция, которая вызывается, когда игрок осматривает комнату. Обычно она выводит описания всех объектов комнаты (из массива obj). Здесь добавлена только заглушка: не выводить описания всех квестов в комнате.
Также переопределена сама комната — теперь в ней есть массив quests. Массив obj заполняется объектами из массива quests — приравнивается, если он пуст, и соединяется, если объекты в нём есть.
Это просто пример, можно определять любые типы объектов и комнат.
Вот. А теперь, когда вы уже знаете несколько хитроумных трюков в этом великолепном движке, можете читать обычную документацию.