diff --git a/final_battle.lua b/final_battle.lua new file mode 100644 --- /dev/null +++ b/final_battle.lua @@ -0,0 +1,683 @@ +fb_naethvjirr = obj { + nam = "fb_naethvjirr"; + disp2 = "Наэтвьирра"; + position = 4; + throw = 0; -- оставшиеся ходы до возможности применения (раз в 4 хода) + charge = 0; -- оставшиеся ходы до возможности применения (раз в 3 хода) + shield = true; -- активность щита + shieldRecharge = false; -- О, как я люблю эти маленькие костыли. Это нужно, чтобы щит не восстанавливался сразу после выстрела + canshoot = function(s) + return not s.shield; + end; + canhit = function(s) + return not s.shield; + end; + nohitmsg = "Это бессмыссленно, пока он закрыт щитом"; + noshootmsg = "Это бессмыссленно, пока он закрыт щитом"; + hitpoints = 3; + passturn = function(s) + if (s.hitpoints > 0) then + local gens = 0; + for i = 0, #final_battle.generators do + if (final_battle.generators[i] == 4) then + gens = gens + 1; + s.charge = s.charge - 1; + elseif (final_battle.generators[i] == 3) then + gens = gens + 1; + s.throw = s.throw - 1; + elseif (final_battle.generators[i] == 2) then + gens = gens + 1; + if (not s.shield) then + if (s.shieldRecharge) then + s.shield = true; + s.shieldRecharge = false; + p("Щит вокруг Наэтвьирра восстановлен и он снова защищен от ваших атак. "); + else + s.shieldRecharge = true; + end; + end; + end; + end; + + if (gens == 0) then + p("Глаза Наэтвьирра гаснут, и конструкт бессильно падает на пол. "); + s.hitpoints = 0; + return true; + end; + + if (s.charge <= 0) then + -- Пускаем разряд в игрока + s.charge = 3; + p("Наэтвьирр отправляет в вас электрический разряд. Вы не можете пошевелиться. "); + if (rnd(4) < 2) then + -- Отбросить игрока в сторону + p("Разряд отбрасывает вас в сторону. "); + fb_throwAway(pl); + end; + + if (rnd(4) < 2) then -- 25% + p("Вы чувствуете, что атака не прошла для вас бесследно. "); + pl.hitpoints = pl.hitpoints - 1; + if (pl.hitpoints <= 0) then + walk(game_over_naethvjirr); + return; + end; + end; + + final_battle.passturn(final_battle); + p("Вы оправились от атаки Наэтвьирра и снова можете действовать. "); + elseif (s.throw <= 0) then + -- Отбрасываем игрока + s.throw = 4; + fb_throwAway(pl); + p("Наэтвьирр отправляет в вас мощную волну воздуха, которая отбрасывает вас в сторону. "); + + if (rnd(4) > 1) then -- 75% + pl.hitpoints = pl.hitpoints - 1; + p("Вы чувствуете, что атака не прошла для вас бесследно. "); + if (pl.hitpoints <= 0) then + walk(game_over_naethvjirr); + return; + end; + end; + end; + end; + end; + onhit = function(s) + local result = ""; + + s.hitpoints = s.hitpoints - 1; + if (s.hitpoints == 2) then + result = "Сильный удар повреждает Наэтвьирра, но вы не замечаете, сказалось ли это на его работоспособности. "; + elseif (s.hitpoints == 1) then + result = "Наэтвьирр пошатнулся от вашего удара, давая понять, что попадание не прошло бесследно. "; + elseif (s.hitpoints == 0) then + result = "После вашего удара глаза Наэтвьирра гаснут и конструкт с грохотом падает рядом со своим троном. "; + end; + p(result); + final_battle.passturn(final_battle); + end; + onshoot = function(s) + local hit = false; + if (pl.position[1] == 1 and pl.position[2] == 1) then + hit = (rnd(4) > 1); + else + hit = (rnd(2) > 1); + end; + + local result = ""; + if (hit) then + s.hitpoints = s.hitpoints - 1; + if (s.hitpoints == 2) then + result = "Ваш выстрел повреждает Наэтвьирра, но вы не замечаете, сказалось ли это на его работоспособности. "; + elseif (s.hitpoints == 1) then + result = "Глаза Наэтвьирра на мгновение моргают, давая понять, что попадание не прошло бесследно. "; + elseif (s.hitpoints == 0) then + result = "Глаза Наэтвьирра гаснут и конструкт с грохотом падает рядом со своим троном. "; + end; + else + result = "Пуля пролетает мимо конструкта, не причинив ему никакого вреда. "; + end; + pn(result); + final_battle.passturn(final_battle); + end; + dsc = function(s) + if (s.hitpoints > 0) then + local shield = ""; + if (s.shield) then + shield = "Вокруг него едва заметно силовое поле, защищающее его от физических повреждений. "; + end; + return "В центре зала перед массивной конструкцией, напоминающей трон, стоит {навьяр}. Конструкт выглядит совершенно как живой, сияющие красноватым светом глаза внимательно следят за вашими действиями. ".. shield .."^"; + else + --return "В центре зала рядом с массивной конструкцией, напоминающей трон, лежит сильно поврежденный {навьяр-конструкт}. ^"; + return ""; + end; + end; +}; + +fb_black = obj { + nam = "fb_black"; + position = 4; + bullets = 6; -- потребуется перезарядка, если потратит все 6 патронов. Скорее всего до этого никогда не дойдет, но предусмотреть стоит + hitpoints = 1; + disp2 = "Блэка"; + canshoot = function(s) + if (pl.position[1] == 1 and pl.position[2] == 1) then + return true; + else + return false; + end; + end; + noshootmsg = "Вы не попадете в него. "; + nohitmsg = "Для этого до него еще надо добраться. "; + dsc = function(s) + if (s.hitpoints > 0) then + return "{Адриан Блэк} укрылся от вас за троном. ^"; + end; + end; + onshoot = function(s) + if (rnd(2) > 1) then--50% + s.hitpoints = 0; + p("Вы раните Блэка в плечо, он роняет пистолет и скрывается за троном. Более он не опасен. "); + else + p("Ваша пуля пролетает мимо, не причинив Блэку никакого вреда. "); + end; + final_battle.passturn(final_battle); + end; + passturn = function(s) + if (s.hitpoints > 0) then + if (s.bullets > 0) then + if (pl.position[1] == 1 and pl.position[2] == 1) then + -- Стреляем в игрока + p("Блэк стреляет в вас. "); + if (rnd(4) > 1) then--75% + pl.hitpoints = pl.hitpoints - 1; + if (pl.hitpoints > 0) then + p("Пуля лишь слегка задевает вас, практически не нанеся урона. "); + else + walk(game_over_shot_fb); + end; + end; + end; + else + -- Перезаряжаем + s.bullets = 6; + p("Адриан Блэк перезаряжает револьвер. "); + end; + end; + end; +}; + +fb_pylons = obj { + nam = "fb_pylons"; + position = "с"; + state = 2;--Количество активных пилонов + charging = 0; + canhit = true; + noshootmsg = "Вы не попадете в них"; + onhit = function(s) + if (s.state > 0) then + if (pl.position[1] == 1 and pl.position[2] == 1) then + s.state = s.state - 1; + if (s.state == 0) then + -- Если оба пилона уничтожены, нет смысла передавать им энергию. + for i = 0, #final_battle.generators do + if (final_battle.generators[i] == 5) then + final_battle.generators[i] = 1; + end; + end; + end; + p("Ударом по металлическому стержню, вы выводите из строя один из пилонов"); + final_battle.passturn(final_battle); + else + return "Пилоны слишком далеко от вас"; + end; + else + return "Оба пилона уже выведены из строя"; + end; + end; + passturn = function(s) + for i = 1, #final_battle.generators do + if (final_battle.generators[i] == 5) then + s.charging = s.charging + 1; + end; + end; + if (s.charging >= 10) then + pl.hitpoints = 0; + walk(game_over_pylon); + end; + end; + dsc = function(s) + if (s.state == 2) then + return "В центральной части комнаты расположены два {пилона} высотой около метра. На вершине их установлены сверкающие металлические стержни.^"; + elseif (s.state == 1) then + return "В центральной части комнаты расположены два {пилона} высотой около метра. На вершине одного из пилонов установлен сверкающий металлический стержень. Стержень на втором пилоне сильно погнут и выведен из строя.^"; + elseif (s.state == 0) then + return "В центральной части комнаты расположены два пилона высотой около метра. Металлические стержни, установленные в их вершине сильно повреждены.^"; + end; + end; +}; + +fb_throwAway = function(target) + local positions = {}; + if (target.position[1] - 1 >= 0) then + if (target.position[2] - 1 >= 0) then + positions[#positions + 1] = {target.position[1] - 1, target.position[2] - 1}; + end; + positions[#positions + 1] = {target.position[1] - 1, target.position[2]}; + if (target.position[2] + 1 <= 2) then + positions[#positions + 1] = {target.position[1] - 1, target.position[2] + 1}; + end; + end; + + if (target.position[2] - 1 >= 0) then + positions[#positions + 1] = {target.position[1], target.position[2] - 1}; + end; + if (target.position[2] + 1 <= 2) then + positions[#positions + 1] = {target.position[1], target.position[2] + 1}; + end; + + if (target.position[1] + 1 <= 2) then + if (target.position[2] - 1 >= 0) then + positions[#positions + 1] = {target.position[1] + 1, target.position[2] - 1}; + end; + positions[#positions + 1] = {target.position[1] + 1, target.position[2]}; + if (target.position[2] + 1 <= 2) then + positions[#positions + 1] = {target.position[1] + 1, target.position[2] + 1}; + end; + end; + + target.position = positions[rnd(#positions)]; +end; + +fb_getGeneratorIcon = function(value) + if (value == 0) then + return "images/fb_inactive.png"; + elseif (value == 1) then + return "images/fb_awake.png"; + elseif (value == 2) then + return "images/fb_shield.png"; + elseif (value == 3) then + return "images/fb_throw.png"; + elseif (value == 4) then + return "images/fb_charge.png"; + elseif (value == 5) then + return "images/fb_pylon.png"; + end; +end; + +final_battle = room { + nam = "final_battle"; + pic = function(s) + local image = 'images/fb_bcg.png'; + image = image .. ";" .. fb_getGeneratorIcon(s.generators[1]) .. "@48,0"; + image = image .. ";" .. fb_getGeneratorIcon(s.generators[2]) .. "@96,0"; + image = image .. ";" .. fb_getGeneratorIcon(s.generators[3]) .. "@96,48"; + image = image .. ";" .. fb_getGeneratorIcon(s.generators[4]) .. "@96,96"; + image = image .. ";" .. fb_getGeneratorIcon(s.generators[5]) .. "@48,96"; + image = image .. ";" .. fb_getGeneratorIcon(s.generators[6]) .. "@0,96"; + image = image .. ";" .. fb_getGeneratorIcon(s.generators[7]) .. "@0,48"; + image = image .. ";" .. fb_getGeneratorIcon(s.generators[8]) .. "@0,0"; + if (fb_naethvjirr.hitpoints > 0) then + image = image .. ";images/fb_naethvjirr.png@64,80"; + end; + if (fb_black.hitpoints > 0) then + image = image .. ";images/fb_black.png@80,80"; + end; + image = image .. ";images/fb_player.png@"..tostring(pl.position[1] * 48 + 16)..","..tostring((2 - pl.position[2]) * 48 + 16); + if (fb_pylons.state > 1) then + image = image .. ";images/fb_pylon.png@48,64"; + else + image = image .. ";images/fb_inactive.png@48,64"; + end; + if (fb_pylons.state > 0) then + image = image .. ";images/fb_pylon.png@80,64"; + else + image = image .. ";images/fb_inactive.png@80,64"; + end; + return image; + end; + disp = "Лсэрианотр, центральный зал"; + awakening = 300; + dsc = function(s) + local temp = ""; + if (pl.position[1] == 1 and pl.position[2] == 2) then + temp = "северной"; + elseif (pl.position[1] == 2 and pl.position[2] == 2) then + temp = "северо-восточной"; + elseif (pl.position[1] == 2 and pl.position[2] == 1) then + temp = "восточной"; + elseif (pl.position[1] == 2 and pl.position[2] == 0) then + temp = "юго-восточной"; + elseif (pl.position[1] == 1 and pl.position[2] == 0) then + temp = "южной"; + elseif (pl.position[1] == 0 and pl.position[2] == 0) then + temp = "юго-западной"; + elseif (pl.position[1] == 0 and pl.position[2] == 1) then + temp = "западной"; + elseif (pl.position[1] == 0 and pl.position[2] == 2) then + temp = "северо-западной"; + elseif (pl.position[1] == 1 and pl.position[2] == 1) then + temp = "центральной"; + end; + + local debug_str = tostring(s.awakening) .. " "; + debug_str = debug_str .. tostring(fb_pylons.charging) .. " "; + + return "Вы находитесь в " .. temp .. " части главного зала древнего города Навьяр. На схеме приведено расположение важных объектов в комнате, включая символы, изображенные на генераторах" .. debug_str; + --Вдоль стен стоят огромные металлические шкафы, из которых тысячи проводом тянутся к громадной конструкции в центре зала. Мягкий свет льется сверху, практически ослепляя вас после полумрака остальной части мертвого города. " + --]]; + end; + left = function(s) + lifeoff(s); + end; + entered = function(s) + -- Здесь будем собирать все необходимое для начала финальной битвы: сопартийцы отыграют свою роль, после чего начнется битва не на жизнь, а на смерть. + s.entered = nil; -- Должно отработать не более одного раза + pl.position = {1,2}; + local throw = rnd(8); + while (s.generators[throw] ~= 1) do + throw = rnd(8); + end; + s.generators[throw] = 3; + + local shield = rnd(8); + while (s.generators[shield] ~= 1) do + shield = rnd(8); + end; + s.generators[shield] = 2; + + local charge = rnd(8); + while (s.generators[charge] ~= 1) do + charge = rnd(8); + end; + s.generators[charge] = 4; + + local pylon = rnd(8); + while (s.generators[pylon] ~= 1) do + pylon = rnd(8); + end; + s.generators[pylon] = 5; + + lifeon(s); + end; + life = function(s) + fb_north:enable(); + fb_northeast:enable(); + fb_east:enable(); + fb_southeast:enable(); + fb_south:enable(); + fb_southwest:enable(); + fb_west:enable(); + fb_northwest:enable(); + + if (pl.position[2] == 2) then + fb_north:disable(); + fb_northeast:disable(); + fb_northwest:disable(); + elseif (pl.position[2] == 0) then + fb_south:disable(); + fb_southeast:disable(); + fb_southwest:disable(); + end; + + if (pl.position[1] == 2) then + fb_east:disable(); + fb_northeast:disable(); + fb_southeast:disable(); + elseif (pl.position[1] == 0) then + fb_west:disable(); + fb_northwest:disable(); + fb_southwest:disable(); + end; + + fb_generator:enable(); + if (pl.position[1] == 1 and pl.position[2] == 2) then + fb_generator.index = 1; + elseif (pl.position[1] == 2 and pl.position[2] == 2) then + fb_generator.index = 2; + elseif (pl.position[1] == 2 and pl.position[2] == 1) then + fb_generator.index = 3; + elseif (pl.position[1] == 2 and pl.position[2] == 0) then + fb_generator.index = 4; + elseif (pl.position[1] == 1 and pl.position[2] == 0) then + fb_generator.index = 5; + elseif (pl.position[1] == 0 and pl.position[2] == 0) then + fb_generator.index = 6; + elseif (pl.position[1] == 0 and pl.position[2] == 1) then + fb_generator.index = 7; + elseif (pl.position[1] == 0 and pl.position[2] == 2) then + fb_generator.index = 8; + elseif (pl.position[1] == 1 and pl.position[2] == 1) then + fb_generator:disable(); + end; + + local activeGens = false; + for i = 1, #s.generators do + if (s.generators[i] ~= 0) then + activeGens = true; + end; + end; + + if (not activeGens) then + walk(game_over_victory); + end; + end; + passturn = function(s) + -- реальный расчет боевых действий будет в этой функции + for i = 1,#s.generators do + if (s.generators[i] == 1) then + s.awakening = s.awakening - 10; + end; + end; + for i = 1,#s.obj do + if (s.obj[i].passturn) then + s.obj[i].passturn(s.obj[i]); + end; + end; + if (s.awakening <= 0) then + walk(game_over_awakening); + end; + end; + generators = { + 1, 1, 1, 1, 1, 1, 1, 1 + -- В этот массив заносятся состояния генераторов: a - active, i - inactive, t - throw, s - shield, c - charge, p - pylon + -- Состояния генераторов записываются цифрами по приоритету от минимального до максимального(чем больше цифра, тем больше приоритет) + -- 0 - inactive, 1 - active, 2 - shield, 3 - throw, 4 - charge, 5 - pylon + }; + obj = { + 'fb_pylons', + 'fb_naethvjirr', + 'fb_black', + 'fb_generator', + + 'fb_north', + 'fb_northeast', + 'fb_east', + 'fb_southeast', + 'fb_south', + 'fb_southwest', + 'fb_west', + 'fb_northwest' + }; +}; + +fb_generator = obj { + nam = "fb_generator", + dsc = function(s) + if (final_battle.generators[s.index] == 0) then + return "Рядом с вами расположен огромный металлический {шкаф}, искореженный несколькими ударами тяжелого предмета. Судя по всему этот генератор выведен из строя. ^"; + else + return "Рядом с вами расположен огромный металлический {шкаф}, расположенный рядом с вами издает негромкий гул. На его передней стенке виден светящийся символ. Конструкция соединена толстыми проводами с троном Наэтвьирра, в центре зала. ^"; + end; + end; + index = 0; + act = "Вы ничего не сможете сделать с генератором без дополнительных приспособлений"; + noshootmsg = "Это бесполезно"; + canhit = true; + onhit = function(s) + --[[ + Модули питания могут быть назначены на одну из нескольких подсистем. + Для питания каждой подсистемы требуется минимум один источник ее питания. Всего подсистемы следующие: пилоны, щит, разряд, разброс и пробуждение. + Приоритеты распределения таковы: + Всегда должна быть активна минимум одна атакующая подсистема, в порядке эффективности: пилоны, разряд, разброс. + Если для атакующих подсистем достаточно энергии, то активен и щит. + Наименьшим приоритетом обладает пробуждение. + Также учитывается возможность и целесообразность применения защитных и атакующих подсистем: + Если аватар уничтожен, нет смысла поддерживать щит, разброс и разряд. + ]] + local gen = final_battle.generators[s.index]; + final_battle.generators[s.index] = 0; + local moreInfo = ""; + if (gen == 5) then + --Как самая эффективная система атаки, пилоны всегда заряжаются, независимо от обстоятельств + fb_pylons.charging = fb_pylons.charging - 4; + if (fb_pylons.charging < 0) then + fb_pylons.charging = 0; + end; + local priorityToReplace = 4; + local activeCount = 0; + for i = 1,#final_battle.generators do + if (final_battle.generators[i] ~= 0 and final_battle.generators[i] <= priorityToReplace) then + activeCount = activeCount + 1; + if (final_battle.generators[i] < priorityToReplace) then + priorityToReplace = final_battle.generators[i]; + end; + end; + end; + if (activeCount > 0) then + local replacementIndex = rnd(8); + while (final_battle.generators[replacementIndex] ~= priorityToReplace) do + replacementIndex = rnd(8); + end; + final_battle.generators[replacementIndex] = gen; + end; + elseif (gen == 4) then + fb_naethvjirr.charge = fb_naethvjirr.charge + 1; + local priorityToReplace = 3; + local activeCount = 0; + for i = 1,#final_battle.generators do + if (final_battle.generators[i] ~= 0 and final_battle.generators[i] <= priorityToReplace) then + activeCount = activeCount + 1; + if (final_battle.generators[i] < priorityToReplace) then + priorityToReplace = final_battle.generators[i]; + end; + end; + end; + if (activeCount > 0) then + local replacementIndex = rnd(8); + while (final_battle.generators[replacementIndex] ~= priorityToReplace) do + replacementIndex = rnd(8); + end; + final_battle.generators[replacementIndex] = gen; + end; + elseif (gen == 3) then + fb_naethvjirr.throw = fb_naethvjirr.throw + 1; + local priorityToReplace = 2; + local activeCount = 0; + for i = 1,#final_battle.generators do + if (final_battle.generators[i] ~= 0 and final_battle.generators[i] <= priorityToReplace) then + activeCount = activeCount + 1; + if (final_battle.generators[i] < priorityToReplace) then + priorityToReplace = final_battle.generators[i]; + end; + end; + end; + if (activeCount > 0) then + local replacementIndex = rnd(8); + while (final_battle.generators[replacementIndex] ~= priorityToReplace) do + replacementIndex = rnd(8); + end; + final_battle.generators[replacementIndex] = gen; + end; + elseif (gen == 2) then + fb_naethvjirr.shield = false; + moreInfo = "Щит вокруг Наэтвьирра растворяется, оставляя конструкта открытым для ваших атак. "; + local priorityToReplace = 1; + local activeCount = 0; + for i = 1,#final_battle.generators do + if (final_battle.generators[i] ~= 0 and final_battle.generators[i] <= priorityToReplace) then + activeCount = activeCount + 1; + if (final_battle.generators[i] < priorityToReplace) then + priorityToReplace = final_battle.generators[i]; + end; + end; + end; + if (activeCount > 0) then + local replacementIndex = rnd(8); + while (final_battle.generators[replacementIndex] ~= priorityToReplace) do + replacementIndex = rnd(8); + end; + final_battle.generators[replacementIndex] = gen; + end; + elseif (gen == 0) then + return "Генератор итак выведен из строя, незачем его дальше колотить. "; + end; + p("После нескольких сильных ударов генератор затихает. ".. moreInfo); + final_battle.passturn(final_battle); + end; +}; + +fb_north = obj { + nam = "north", + dsc = "{На север}", + act = function(s) + pl.position[2] = pl.position[2] + 1; + p("Вы идете на север"); + final_battle.passturn(final_battle); + end; +}; + +fb_northeast = obj { + nam = "northeast", + dsc = "{На северо-восток}", + act = function(s) + pl.position[1] = pl.position[1] + 1; + pl.position[2] = pl.position[2] + 1; + p("Вы идете на северо-восток."); + final_battle.passturn(final_battle); + end; +}; + +fb_east = obj { + nam = "east", + dsc = "{На восток}", + act = function(s) + pl.position[1] = pl.position[1] + 1; + p("Вы идете на восток."); + final_battle.passturn(final_battle); + end; +}; + +fb_southeast = obj { + nam = "southeast", + dsc = "{На юго-восток}", + act = function(s) + pl.position[1] = pl.position[1] + 1; + pl.position[2] = pl.position[2] - 1; + p("Вы идете на юго-восток."); + final_battle.passturn(final_battle); + end; +}; + +fb_south = obj { + nam = "south", + dsc = "{На юг}", + act = function(s) + pl.position[2] = pl.position[2] - 1; + p("Вы идете на юг."); + final_battle.passturn(final_battle); + end; +}; + +fb_southwest = obj { + nam = "southwest", + dsc = "{На юго-запад}", + act = function(s) + pl.position[1] = pl.position[1] - 1; + pl.position[2] = pl.position[2] - 1; + p("Вы идете на юго-запад."); + final_battle.passturn(final_battle); + end; +}; + +fb_west = obj { + nam = "west", + dsc = "{На запад}", + act = function(s) + pl.position[1] = pl.position[1] - 1; + p("Вы идете на запад."); + final_battle.passturn(final_battle); + end; +}; + +fb_northwest = obj { + nam = "northwest", + dsc = "{На северо-запад}", + act = function(s) + pl.position[1] = pl.position[1] - 1; + pl.position[2] = pl.position[2] + 1; + p("Вы идете на северо-запад."); + final_battle.passturn(final_battle); + end; +}; \ No newline at end of file