Трюки и хаки INSTEAD

2011-04-18T09:08:38+07:00

Если вы не знакомы с движком текстовых игр и визуальных новелл 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 — приравнивается, если он пуст, и соединяется, если объекты в нём есть.

Это просто пример, можно определять любые типы объектов и комнат.

Вот. А теперь, когда вы уже знаете несколько хитроумных трюков в этом великолепном движке, можете читать обычную документацию.

Теги: INSTEAD