_kh.bf_calcWalkDistance = function(s, x1, y1, x2, y2) local closed = {}; local open = { {p = {{x1, y1}}, l = 0} }; while #open > 0 do local item = table.remove (open, 1); local node = item.p[#item.p]; local inClosed = false; for i = 1, #closed do if (closed[i][1] == node[1] and closed[i][2] == node[2]) then inClosed = true; break; end; end; if (not inClosed) then --print(node[1], x2, node[2], y2); if (node[1] == x2 and node[2] == y2) then return item; end; table.insert(closed, node); --print("closed", #closed); --print("open", #open); local pos = 1; while (#open >= pos and open[pos].l <= item.l) do pos = pos + 1; end; if s:isPassable(node[1], node[2] - 1) or (node[1] == x2 and node[2] - 1 == y2) then local p = {}; for i = 1, #item.p do table.insert(p, item.p[i]); end; table.insert(p, {node[1], node[2] - 1}); table.insert(open, pos, { p = p, l = item.l + 1 }); end; if s:isPassable(node[1], node[2] + 1) or (node[1] == x2 and node[2] + 1 == y2) then local p = {}; for i = 1, #item.p do table.insert(p, item.p[i]); end; table.insert(p, {node[1], node[2] + 1}); table.insert(open, pos, { p = p, l = item.l + 1 }); end; if s:isPassable(node[1] - 1, node[2]) or (node[1] - 1 == x2 and node[2] == y2) then local p = {}; for i = 1, #item.p do table.insert(p, item.p[i]); end; table.insert(p, {node[1] - 1, node[2]}); table.insert(open, pos, { p = p, l = item.l + 1 }); end; if s:isPassable(node[1] + 1, node[2]) or (node[1] + 1 == x2 and node[2] == y2) then local p = {}; for i = 1, #item.p do table.insert(p, item.p[i]); end; table.insert(p, {node[1] + 1, node[2]}); table.insert(open, pos, { p = p, l = item.l + 1 }); end; end; end; return nil; end; _kh.bf_calcShootDistance = function(x1, y1, x2, y2) --return math.max(math.abs(x2 - x1), math.abs(y2 - y1)); return math.floor(math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))); end; _kh.bf_calcDirection = function(x1, y1, x2, y2) -- 0 - close by, 1-8 - north to north-west clockwise, 9 - too far local dist = _kh.bf_calcDistance(x1,y1,x2,y2); if (dist == 0) then return 0; elseif (dist == 1) then if (x1 == x2 and y1 > y2) then return 1; elseif (x1 < x2 and y1 > y2) then return 2; elseif (x1 < x2 and y1 == y2) then return 3; elseif (x1 < x2 and y1 < y2) then return 4; elseif (x1 == x2 and y1 < y2) then return 5; elseif (x1 > x2 and y1 < y2) then return 6; elseif (x1 > x2 and y1 == y2) then return 7; elseif (x1 > x2 and y1 > y2) then return 8; end; else return 9; end; end; battlefield = function(tab) local make_turn = tab.make_turn; tab.isPassable = function(s, x, y) if (x < 1 or x > 7 or y < 1 or y > 7 or tab.map[y][x] > 0) then return false; end; for i = 1, #s.obj do local obst = EngineUtils.getValue(s.obj[i].obst, s.obj[i]); if (obst and obst > 0) then if (s.obj[i].x == x and s.obj[i].y == y) then return false; end; end; end; return true; end; tab.isShootable = function(s, x, y) if (x < 1 or x > 7 or y < 1 or y > 7 or tab.map[y][x] > 1) then return false; end; for i = 1, #s.obj do local obst = EngineUtils.getValue(s.obj[i].obst, s.obj[i]); if (obst and obst > 1) then if (s.obj[i].x == x and s.obj[i].y == y) then return false; end; end; end; return true; end; tab.canShoot = function(s, x1, y1, x2, y2) local dx = math.abs(x1 - x2); local dy = math.abs(y1 - y2); local invx = x1 > x2; local invy = y1 > y2; local xc = 0; local yc = 0; local error = 0; print(dx, dy); if (dx > dy) then --horizontal line local y = 0; for x = 0, dx do if invx then xc = x1 - x; else xc = x1 + x; end; if invy then yc = y1 - y; else yc = y1 + y; end; if ((xc ~= x1 or yc ~= y1) and not s:isShootable(xc, yc) and (xc ~= x2 or yc ~= y2)) then return false; end; error = error + dy; if (2 * error > dx) then y = y + 1 error = error - dx; end; end; else --vertical line local x = 0; for y = 0, dy do print(x, y); if invx then xc = x1 - x; else xc = x1 + x; end; if invy then yc = y1 - y; else yc = y1 + y; end; print(xc, yc); if ((xc ~= x1 or yc ~= y1) and not s:isShootable(xc, yc) and (xc ~= x2 or yc ~= y2)) then return false; end; error = error + dx; if (2 * error > dy) then x = x + 1; error = error - dy; end; end; end; return true; end; tab.check_walk = function(s) bf_north:enable(); --bf_northeast:enable(); bf_east:enable(); --bf_southeast:enable(); bf_south:enable(); --bf_southwest:enable(); bf_west:enable(); --bf_northwest:enable(); if (not s:isPassable(s.plX, s.plY - 1)) then bf_north:disable(); --bf_northeast:disable(); --bf_northwest:disable(); end; if (not s:isPassable(s.plX, s.plY + 1)) then bf_south:disable(); --bf_southeast:disable(); --bf_southwest:disable(); end; if (not s:isPassable(s.plX - 1, s.plY)) then bf_west:disable(); --bf_southwest:disable(); --bf_northwest:disable(); end; if (not s:isPassable(s.plX + 1, s.plY)) then bf_east:disable(); --bf_southeast:disable(); --bf_northeast:disable(); end; end; local entered = tab.entered; tab.entered = function(s) s:check_walk(); if (type(entered) == 'function') then return entered(s); else return entered; end; end; tab.onreload = function(s) here():make_turn(); end; tab.make_turn = function(s) -- A bit peggish but works pl.x = s.plX; pl.y = s.plY; for i = 1, #s.obj do if (type(s.obj[i].make_turn) == 'function' and s.obj[i].hp > 0) then s.obj[i]:make_turn(); end; end; if (make_turn) then make_turn(s); end; if (pl.hp <= 0) then print("lost"); walk(s.lose); end; s:check_walk(); for i = 1, #s.obj do if (s.obj[i].enemy and s.obj[i].hp > 0) then return; end; end; print("won"); walk(s.win); end; tab.getWalkDistance = _kh.bf_calcWalkDistance; tab.getShootDistance = _kh.bf_calcShootDistance; tab.getDirection = _kh.bf_calcDirection; if (not tab.obj) then tab.obj = {}; end; if (not tab.pic) then tab.pic = function(s) local v = "images/battle_bcg.png;images/player.png@" .. tostring(s.plX * 32 - 32) .. "," .. tostring(s.plY * 32 - 32); for i = 1, #s.obj do if ((s.obj[i].hp) and (s.obj[i].hp > 0) and not(s.obj[i]:disabled()) and s.obj[i].pic and s.obj[i].x and s.obj[i].y) then v = v .. ";" .. s.obj[i].pic .. "@" .. tostring(s.obj[i].x * 32 - 32) .. "," .. tostring(s.obj[i].y * 32 - 32); end; end; for i = 1, 7 do for j = 1, 7 do if (tab.map[i][j] == 2) then v = v.. ";images/battle_wall.png@" .. tostring(j * 32 - 32) .. "," .. tostring(i * 32 - 32); elseif (tab.map[i][j] == 1) then v = v.. ";images/battle_obstacle.png@" .. tostring(j * 32 - 32) .. "," .. tostring(i * 32 - 32); end; end; end; return v; end; end; table.insert(tab.obj, 'bf_north'); --table.insert(tab.obj, 'bf_northeast'); table.insert(tab.obj, 'bf_east'); --table.insert(tab.obj, 'bf_southeast'); table.insert(tab.obj, 'bf_south'); --table.insert(tab.obj, 'bf_southwest'); table.insert(tab.obj, 'bf_west'); --table.insert(tab.obj, 'bf_northwest'); table.insert(tab.obj, 'bf_wait'); return room(tab); end; bf_north = obj { nam = "north", dsc = "{На север}", act = function(s) here().plY = here().plY - 1; p("Вы идете на север"); here():make_turn(); end; }; --[[bf_northeast = obj { nam = "northeast", dsc = "{На северо-восток}", act = function(s) here().plX = here().plX + 1; here().plY = here().plY - 1; p("Вы идете на северо-восток."); here():make_turn(); end; };]] bf_east = obj { nam = "east", dsc = "{На восток}", act = function(s) here().plX = here().plX + 1; p("Вы идете на восток."); here():make_turn(); end; }; --[[bf_southeast = obj { nam = "southeast", dsc = "{На юго-восток}", act = function(s) here().plX = here().plX + 1; here().plY = here().plY + 1; p("Вы идете на юго-восток."); here():make_turn(); end; };]] bf_south = obj { nam = "south", dsc = "{На юг}", act = function(s) here().plY = here().plY + 1; p("Вы идете на юг."); here():make_turn(); end; }; --[[bf_southwest = obj { nam = "southwest", dsc = "{На юго-запад}", act = function(s) here().plX = here().plX - 1; here().plY = here().plY + 1; p("Вы идете на юго-запад."); here():make_turn(); end; };]] bf_west = obj { nam = "west", dsc = "{На запад}", act = function(s) here().plX = here().plX - 1; p("Вы идете на запад."); here():make_turn(); end; }; --[[bf_northwest = obj { nam = "northwest", dsc = "{На северо-запад}", act = function(s) here().plX = here().plX - 1; here().plY = here().plY - 1; p("Вы идете на северо-запад."); here():make_turn(); end; };]] bf_wait = obj { nam = "wait", dsc = "{Ждать}", act = function(s) p("Вы ждете"); here():make_turn(); end; }; combatant = function(tab) tab.canshoot = function(s) local dist = here().getDistance(here().plX, here().plY, s.x, s.y); return dist < 4 and not tab.ally; end; tab.onthrust = function(s) local dist = here().getDistance(here().plX, here().plY, s.x, s.y); if (rnd(4) > dist) then tab.hp = tab.hp - 2; if (tab.hp > 0) then p(tab.thrustHit); else p(tab.thrustKill); end; else p(tab.thrustMiss); end; here():make_turn(); end; tab.onshoot = function(s) local dist = here().getDistance(here().plX, here().plY, s.x, s.y); if (rnd(4) > dist) then if (dist == 0) then tab.hp = tab.hp - 2; end; tab.hp = tab.hp - 1; if (tab.hp > 0) then p(tab.shootHit); else p(tab.shootKill); end; else p(tab.shootMiss); end; here():make_turn(); end; tab.canthrust = function(s) return math.abs(s.x - here().plX) + math.abs(s.y - here().plY) <= 2 and not s.ally; end; tab.canhit = function(s) return (math.abs(s.x - here().plX) == 1 and s.y == here().plY or s.x == here().plX and math.abs(s.y - here().plY) == 1) and not tab.ally; end; tab.onhit = function(s) if (math.abs(s.x - here().plX) == 1 and s.y == here().plY or s.x == here().plX and math.abs(s.y - here().plY) == 1) then tab.hp = tab.hp - 2; if (tab.hp > 0) then p(tab.wpnHit); else p(tab.wpnKill); end; here():make_turn(); else p(tab.wpnFar); end; end; if (not tab.act) then tab.act = function(s) if tab.ally then return tab.nohit; end; if (math.abs(s.x - here().plX) == 1 and s.y == here().plY or s.x == here().plX and math.abs(s.y - here().plY) == 1) then tab.hp = tab.hp - 1; if (tab.hp > 0) then p(tab.handHit); else p(tab.handKill); end; here():make_turn(); else p(tab.handFar); end; end; end; if (not tab.make_turn) then tab.make_turn = function(s) end; end; if (not tab.obst) then tab.obst = function(s) if (s.hp > 0) then return 2; else return 0; end; end; end; return obj(tab); end;