--[[ Общие утилиты для игр на Instead. Скорее всего будут повторно использоваться в следующих играх, если таковые будут. ]] --[[ ]] ArrayUtils = { indexOf = function(array, item) for i = 1, #array do if (array[i] == item) then return i; end; end; return 0; end; }; --[[ ]] PartyUtils = { addToParty = function(object) if (object.home) then if (ArrayUtils.indexOf(pl.party, object.nam) == 0) then move(object, object.home); table.insert(pl.party, object.nam); end; end; end; }; --[[ Translate utils ]] language = "ru" translate = function(table, key) return function() return table[language][key]; end; end; --[[ functions, design to simplify some typical actions while developing Instead game ]] EngineUtils = { getStringValue = function(source, param) local value = ""; if (type(source) == "function") then value = source(param); else value = source; end; return value; end; getValue = function(source, param) local value = nil; if (type(source) == "function") then value = source(param); else value = source; end; return value; end; }; --[[ internal space ]] _kh = { vroom_enter = function(s) local v = EngineUtils.getValue(s.where); if (v) then walk(v); else return false; end; end; vroom_save = function(s) if need then local t = stead.string.format("%s = kh_vroom(%s, %q, %s);\n", name, stead.tostring(s.disp), stead.deref(s.where), stead.deref(s.nam)); h:write(t); end stead.savemembers(h, s, name, false); end; }; function toggle(obj) if obj:disabled() then obj:enable(); else obj:disable(); end; end; function kh_vobj(nam, dsc, act, used) return obj { nam = nam; dsc = dsc; act = act; used = used; }; end; function kh_vway(disp, dsc, target, seen_level) local temp = vway(disp, dsc, target); temp.seen_level = seen_level; return temp; end; --[[ vroom with additional functionality ]] function kh_vroom(disp, target, nam, seen_level) if (type(nam) == "number") then seen_level = nam; nam = disp; end; if (not nam) then nam = disp; end; return room { nam = nam; disp = disp; where = target; enter = _kh.vroom_enter; seen_level = seen_level; --enter = _kh.vroom_enter; --save = _kh.vroom_save; }; end; room = inherit(room, function(v) v.entered = stead.hook(v.entered, function(f, s, ...) if s.showhp then hp_indicator:enable(); else hp_indicator:disable(); end; return f(s, ...) end) return v; end) dlg = inherit(dlg, function(v) v.hideinv = true; return v; end); obj = inherit(obj, function(v) v.disable_implicit = hook(v.disable, function(f, s, ...) s._disabled_implicit = true; return f(s, unpack({...})); end); v.disable = hook(v.disable, function(f, s, ...) s._disabled_explicit = true; return f(s, unpack({...})); end); v.enable_implicit = hook(v.enable, function(f, s, ...) s._disabled_implicit = false; if (s._disabled_explicit) then return s; --mimic original enable() behavior else return f(s, unpack({...})); end; end); v.enable = hook(v.enable, function(f, s, ...) s._disabled_explicit = false; if (s._disabled_implicit) then return s; --mimic original enable() behavior else return f(s, unpack({...})); end; end); v.toggle = toggle; return v; end); --[[ character object represents character, with whom you can interact ]] function npc(tab) if (not tab.act) then tab.act = function(s) local dlg = nil; if s.dlg then dlg = EngineUtils.getValue(s.dlg, s); end; if dlg then walkin(dlg); else local phrases = EngineUtils.getValue(s.phrases, s); return phrases[rnd(#phrases)]; end; end; end; return obj(tab); end; function darkroom(tab) local life; local entered; local left; if not tab.has_light then tab.has_light = false; end; if tab.life then life = tab.life; end; if tab.entered then entered = tab.entered; end; if tab.left then left = tab.left; end; tab.entered = function(s) lifeon(s); if (entered) then entered(s); end; end; tab.left = function(s) lifeoff(s); if (left) then left(s); end; end; tab.life = function(s) local hasLight = EngineUtils.getValue(s.has_light, s); local plLight = EngineUtils.getValue(me().has_light, pl); if (hasLight) then -- show all objects that were not explicitly disabled(any seen_level) for i = 1, #objs(s) do objs(s)[i]:enable_implicit(); end; for i = 1, #ways(s) do ways(s)[i]:enable_implicit(); end; elseif (plLight) then -- show all objects that were not explicitly disabled and marked as seen in halflight (seen_level = 1 or higher) for i = 1, #objs(s) do if (type(objs(s)[i].seen_level) == "number" and objs(s)[i].seen_level >= 1) then objs(s)[i]:enable_implicit(); else objs(s)[i]:disable_implicit(); end; end; for i = 1, #ways(s) do if (type(ways(s)[i].seen_level) == "number" and ways(s)[i].seen_level >= 1) then ways(s)[i]:enable_implicit(); else ways(s)[i]:disable_implicit(); end; end; else -- show all objects that were not explicitly disabled and marked as seen in darkness (seen_level = 2 or higher) for i = 1, #objs(s) do if (type(objs(s)[i].seen_level) == "number" and objs(s)[i].seen_level >= 2) then objs(s)[i]:enable_implicit(); else objs(s)[i]:disable_implicit(); end; end; for i = 1, #ways(s) do if (type(ways(s)[i].seen_level) == "number" and ways(s)[i].seen_level >= 2) then ways(s)[i]:enable_implicit(); else ways(s)[i]:disable_implicit(); end; end; end; if (life) then life(s); end; end; if not tab.dsc then tab.dsc = function(s) local lt; local hasLight = EngineUtils.getValue(s.has_light, s); local plLight = EngineUtils.getValue(me().has_light, pl); if (hasLight) then lt = EngineUtils.getValue(s.dsc_lit); elseif (plLight) then lt = EngineUtils.getValue(s.dsc_halflit); else lt = EngineUtils.getValue(s.dsc_dark); end; return lt; end; end; return room(tab); end; function game_over(nam, dsc) return room { nam = nam; not_follow = true; disp = "Игра окончена"; hideinv = true; dsc = dsc; }; end; function cutscene(nam, disp, dsc, nxt, entered, pic) return room { nam = nam; not_follow = true; disp = disp; entered = entered; hideinv = true; cutscene = true; dsc = dsc; pic = pic; obj = { kh_vobj("continue", "{Продолжить}", function(s) x = EngineUtils.getValue(nxt); walk(x); end); }; }; end; function lcutscene(nam, disp, dsc, msg, nxt) return room { var { state = 0; }; not_follow = true; nam = nam; disp = disp; enter = function(s) if (s.state == 1) then x = EngineUtils.getValue(nxt); walk(nxt); pn(msg); end; s.state = 1; end; dsc = dsc; obj = { kh_vobj("continue", "{Продолжить}", function(s) x = EngineUtils.getValue(nxt); walk(nxt); end); }; }; end; --[[ Создает комнату с полем для ввода числа длиной не более tab.maxlen По нажатии enter вызывает метод tab.сheck Приглашение для ввода: tab.label Вводимый текст: tab.input ]] function input_number(tab) if (not tab.maxlen) then tab.maxlen = 2; end; tab.forcedsc = true; tab.not_follow = true; tab.noinv = true; tab.input = ""; tab.dsc = function(s) pr(tab.label .. tab.input .. "_"); return false; --return tab.label .. tab.input .. "_"; end; tab.entered = function(s) s.input = ""; hook_keys('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'backspace', 'return', 'escape'); end; tab.left = function(s) unhook_keys('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'backspace', 'return', 'escape'); end; tab.kbd = function(s, down, key) if (not down) then if (key == "escape") then back(); elseif (key == "return") then if (s.input) then s:check(s.input); end; elseif (key == "backspace") then if (s.input:len() > 0) then s.input = s.input:sub(1, s.input:len() - 1); end; else if (string.len(s.input) < s.maxlen) then s.input = s.input .. key; -- hide buttons :) end; end; end; pr(""); end; tab.obj = { "_kh_input1", "_kh_input2", "_kh_input3", "_kh_input4", "_kh_input5", "_kh_input6", "_kh_input7", "_kh_input8", "_kh_input9", "_kh_input0", "_kh_backspace", "_kh_cancel", "_kh_enter" }; return room(tab); end; function input_number_nav(tab) if (not tab.maxlen) then tab.maxlen = 2; end; tab.forcedsc = true; tab.noinv = true; tab.not_follow = true; tab.input = ""; tab.dsc = function(s) pr(tab.label .. tab.input .. "_"); return false; --return tab.label .. tab.input .. "_"; end; tab.entered = function(s) s.input = ""; hook_keys('0', '1', '2', '3', '4', '5', 'backspace', 'return', 'escape'); end; tab.left = function(s) unhook_keys('0', '1', '2', '3', '4', '5', 'backspace', 'return', 'escape'); end; tab.kbd = function(s, down, key) if (not down) then if (key == "escape") then back(); elseif (key == "return") then if (s.input) then s:check(s.input); end; elseif (key == "backspace") then if (s.input:len() > 0) then s.input = s.input:sub(1, s.input:len() - 1); end; else if (string.len(s.input) < s.maxlen) then s.input = s.input .. key; -- hide buttons :) end; end; end; pr(""); end; tab.obj = { "_kh_input1", "_kh_input2", "_kh_input3", "_kh_input4", "_kh_input5", "_kh_input0", "_kh_backspace", "_kh_cancel", "_kh_enter" }; return room(tab); end; function input_alphabet_nav(tab) if (not tab.maxlen) then tab.maxlen = "16"; end; tab.forcedsc = true; tab.noinv = true; tab.not_follow = true; tab.input = ""; tab.dsc = function(s) pr(tab.label .. tab.input .. "_"); return false; end; tab.entered = function(s) s.input = ""; hook_keys('0', '1', '2', '3', '4', '5', 'a', 'd', 'e', 'f', 'h', 'i', 'j', 'k', 'l', 'n', 'p', 'r', 's', 't', 'v', 'y', 'z',"'",'space', 'backspace', 'return', 'escape'); end; tab.left = function(s) unhook_keys('0', '1', '2', '3', '4', '5', 'a', 'd', 'e', 'f', 'h', 'i', 'j', 'k', 'l', 'n', 'p', 'r', 's', 't', 'v', 'y', 'z',"'",'space', 'backspace', 'return', 'escape'); end; tab.kbd = function(s, down, key) if (not down) then if (key == "escape") then back(); elseif (key == "return") then if (s.input) then s:check(s.input); end; elseif (key == "backspace") then if (s.input:len() > 0) then s.input = s.input:sub(1, s.input:len() - 1); end; else if (string.len(s.input) < s.maxlen) then s.input = s.input .. key; -- hide buttons :) end; end; end; pr(""); end; tab.obj = { "_kh_input1", "_kh_input2", "_kh_input3", "_kh_input4", "_kh_input5", "_kh_inputa", "_kh_inputd", "_kh_inpute", "_kh_inputf", "_kh_inputh", "_kh_inputi", "_kh_inputj", "_kh_inputk", "_kh_inputl", "_kh_inputn", "_kh_inputp", "_kh_inputr", "_kh_inputs", "_kh_inputt", "_kh_inputv", "_kh_inputy", "_kh_inputz", "_kh_inputhp", "_kh_inputsp", "_kh_backspace", "_kh_cancel", "_kh_enter" }; return room(tab); end; _kh_input_key = function(key, key_text) if not key_text then key_text = key; end; return obj { nam = "_kh_input" .. key; dsc = "{" .. key_text .. "}"; act = function(s) if (here().input:len() < here().maxlen) then here().input = here().input .. key; end; return ""; end; }; end; _kh_inputa = _kh_input_key("a"); _kh_inputb = _kh_input_key("b"); _kh_inputc = _kh_input_key("c"); _kh_inputd = _kh_input_key("d"); _kh_inpute = _kh_input_key("e"); _kh_inputf = _kh_input_key("f"); _kh_inputg = _kh_input_key("g"); _kh_inputh = _kh_input_key("h"); _kh_inputi = _kh_input_key("i"); _kh_inputj = _kh_input_key("j"); _kh_inputk = _kh_input_key("k"); _kh_inputl = _kh_input_key("l"); _kh_inputm = _kh_input_key("m"); _kh_inputn = _kh_input_key("n"); _kh_inputo = _kh_input_key("o"); _kh_inputp = _kh_input_key("p"); _kh_inputq = _kh_input_key("q"); _kh_inputr = _kh_input_key("r"); _kh_inputs = _kh_input_key("s"); _kh_inputt = _kh_input_key("t"); _kh_inputu = _kh_input_key("u"); _kh_inputv = _kh_input_key("v"); _kh_inputw = _kh_input_key("w"); _kh_inputx = _kh_input_key("x"); _kh_inputy = _kh_input_key("y"); _kh_inputz = _kh_input_key("z"); _kh_inputsp = _kh_input_key(" ", "Пробел"); _kh_inputhp = _kh_input_key("'", "'"); _kh_input1 = obj { nam = "_kh_input1"; dsc = "{1}"; act = function(s) if (here().input:len() < here().maxlen) then here().input = here().input .. "1"; end; return ""; end; }; _kh_input2 = obj { nam = "_kh_input2"; dsc = "{2}"; act = function(s) if (here().input:len() < here().maxlen) then here().input = here().input .. "2"; end; return ""; end; }; _kh_input3 = obj { nam = "_kh_input3"; dsc = "{3}"; act = function(s) if (here().input:len() < here().maxlen) then here().input = here().input .. "3"; end; return ""; end; }; _kh_input4 = obj { nam = "_kh_input4"; dsc = "{4}"; act = function(s) if (here().input:len() < here().maxlen) then here().input = here().input .. "4"; end; return ""; end; }; _kh_input5 = obj { nam = "_kh_input5"; dsc = "{5}"; act = function(s) if (here().input:len() < here().maxlen) then here().input = here().input .. "5"; end; return ""; end; }; _kh_input6 = obj { nam = "_kh_input6"; dsc = "{6}"; act = function(s) if (here().input:len() < here().maxlen) then here().input = here().input .. "6"; end; return ""; end; }; _kh_input7 = obj { nam = "_kh_input7"; dsc = "{7}"; act = function(s) if (here().input:len() < here().maxlen) then here().input = here().input .. "7"; end; return ""; end; }; _kh_input8 = obj { nam = "_kh_input8"; dsc = "{8}"; act = function(s) if (here().input:len() < here().maxlen) then here().input = here().input .. "8"; end; return ""; end; }; _kh_input9 = obj { nam = "_kh_input9"; dsc = "{9}"; act = function(s) if (here().input:len() < here().maxlen) then here().input = here().input .. "9"; end; return ""; end; }; _kh_input0 = obj { nam = "_kh_input0"; dsc = "{0}"; act = function(s) if (here().input:len() < here().maxlen) then here().input = here().input .. "0"; end; return ""; end; }; _kh_backspace = obj { nam = "_kh_back"; dsc = "{Стереть}"; act = function(s) if (here().input:len() > 0) then here().input = here().input:sub(1, here().input:len() - 1); end; return ""; end; }; _kh_cancel = obj { nam = "_kh_cancel"; dsc = "{Отмена}"; act = function(s) back(); return ""; end; }; _kh_enter = obj { nam = "_kh_enter"; dsc = "{Ввод}"; act = function(s) if (here().input) then here():check(here().input); end; end; }; --[[ Создает obj, который добавляет другой obj на сцену при активации и становится неинтерактивным nam - имя объекта dsc_a - описание до активации dsc_b - описание после активации act_a - описание взаимодействия до активации act_b - описание взаимодействия после активации o2 - obj, добавляемый на сцену r - удаляет объект после активации ]] function obscured_obj(nam, dsc_a, act_a, o2, r, dsc_b, act_b) return obj { var { activated = false; }; nam = nam; dsc = function(s) if (s.activated) then return dsc_b; else return dsc_a; end; end; act = function(s) if (s.activated) then return act_b; else s.activated = true; put(o2, here()); if (r) then remove(r, here()); end; return act_a; end; end; }; end; _atlantis_coder = input_alphabet_nav { nam = "Кодировщик"; maxlen = 32; label = "Сообщение:"; check = function(s, input) if type(_atlantis_suit_menu._here.checkCoder) == "function" then pn(_atlantis_suit_menu._here:checkCoder(input)); else pn [[Лампочки кодировщика зажигаются в заданной последовательности, но ничего не происходит. ]]; end; back(); end; }; _atlantis_suit_menu = dlg { nam = "Скафандр"; _item = nil; dsc = [[ Что вы хотите сделать? ]]; entered = function(s) if (s._item.has_coder) then pon(3); else poff(3); end; end; phr = { {1, always = "true", "[Использовать рацию]", function(s) local val = EngineUtils.getValue(_atlantis_suit_menu._item.dlg, _atlantis_suit_menu._item); if (val) then walkin(val); stead.ref(val).__from__ = _atlantis_suit_menu.__from__; else back(); return "Никто не отвечает"; end; end}; {2, always = "true", function(s) if (me().has_light) then return "[Выключить фонарь]"; else return "[Включить фонарь]"; end; end, function(s) if (me().has_light) then me().has_light = false; return "Вы выключаете фонарь. "; else me().has_light = true; return "Вы включаете фонарь. "; end; end}; {3, always = "true", "[Использовать кодировщик]", code [[ walkin("_atlantis_coder"); ]]}; {4, always = "true", "[Снять скафандр]", function(s) if (EngineUtils.getValue(_atlantis_suit_menu._item.cantakeoff, _atlantis_suit_menu._item)) then drop(_atlantis._uit_menu._item); back(); return "Вы снимаете скафандр"; else back(); return "Вы не можете снять скафандр сейчас"; end; end}; {5, always = "true", "Отмена", function(s) back(); return ""; end}; }; }; _atlantis = { suit_inv = function(s) _atlantis_suit_menu._item = s; _atlantis_suit_menu._here = here(); walkin(_atlantis_suit_menu); end; }; suit = function(tab) if (not tab.inv) then tab.inv = _atlantis.suit_inv; end; return obj(tab); end; labyrinth = function(tab) local enter = tab.enter; local exit = tab.exit; local dsc = tab.dsc; local lab; tab.enter = function(s) path('e1'):disable(); path('e2'):disable(); path('e3'):disable(); path('e4'):disable(); path('ex'):disable(); local e1open = s.map[s.position].e1 and not EngineUtils.getValue(s.map[s.position].e1lock, s); local e2open = s.map[s.position].e2 and not EngineUtils.getValue(s.map[s.position].e2lock, s); local e3open = s.map[s.position].e3 and not EngineUtils.getValue(s.map[s.position].e3lock, s); local e4open = s.map[s.position].e4 and not EngineUtils.getValue(s.map[s.position].e4lock, s); local exopen = s.map[s.position].ex and not EngineUtils.getValue(s.map[s.position].exlock, s); if (e1open) then path('e1'):enable(); end; if (e2open) then path('e2'):enable(); end; if (e3open) then path('e3'):enable(); end; if (e4open) then path('e4'):enable(); end; if (exopen) then path('ex'):enable(); end; if (type(enter) == 'function') then enter(s); end; if (type(s.map[s.position].enter) == 'function') then s.map[s.position].enter(s); end; end; tab.exit = function(s) if (type(exit) == 'function') then exit(s); end; if (type(s.map[s.position].exit) == 'function') then s.map[s.position].exit(s); end; end; tab.dsc = function(s) if (type(dsc) == "function") then return dsc(s); elseif dsc == nil then return s.map[s.position].name; else return dsc .. s.map[s.position].name; end; end; tab.way = { kh_vroom(tab.dir1, function() lab.oldposition = lab.position; lab.position = lab.map[lab.position].e1; return lab; end, "e1"); kh_vroom(tab.dir2, function() lab.oldposition = lab.position; lab.position = lab.map[lab.position].e2; return lab; end, "e2"); kh_vroom(tab.dir3, function() lab.oldposition = lab.position; lab.position = lab.map[lab.position].e3; return lab; end, "e3"); kh_vroom(tab.dir4, function() lab.oldposition = lab.position; lab.position = lab.map[lab.position].e4; return lab; end, "e4"); kh_vroom(tab.ex, function() return lab.map[lab.position].ex; end, "ex"); kh_vroom("Ждать", function() lab.oldposition = lab.position; return lab; end, "wt"); }; lab = room(tab); if (not tab.position) then stead.add_var(tab, { position = "00"; }); end; stead.add_var(tab, { oldposition = tab.position; }); return lab; end; function deepcopy(orig) local orig_type = type(orig) local copy if orig_type == 'table' then copy = {} for orig_key, orig_value in next, orig, nil do copy[deepcopy(orig_key)] = deepcopy(orig_value) end setmetatable(copy, deepcopy(getmetatable(orig))) else -- number, string, boolean, etc copy = orig end return copy end;