local easing = require "lib.utils.easing" local pf = require "lib.pathfinder" local utils = require "lib.utils.utils" --- @return Character local function closestCharacter(char) local caster = Vec3 {} char:try(Tree.behaviors.positioned, function(b) caster = b.position end) local charTarget local minDist = 88005553535 -- spooky magic number for k, v in pairs(Tree.level.characters) do v:try(Tree.behaviors.positioned, function(b) local dist = ((caster.x - b.position.x) ^ 2 + (caster.y - b.position.y) ^ 2) ^ 0.5 if dist < minDist and dist ~= 0 then minDist = dist charTarget = v end -- print(k, b.position) end) end return charTarget end -- --- Возвращает все точки в радиусе в виде векторов (должен по крайней мере) -- --- @param radius integer -- --- @param center Vec3 -- --- @return Vec3[] -- local function circleVectors(center, radius) -- local vecs = {} -- local res = {} -- for t = 0, 2 * math.pi, EPSILON do -- local x = math.cos(t) * radius + center.x -- local y = math.sin(t) * radius + center.y -- table.insert(vecs, Vec3 { math.floor(x), math.floor(y) }) -- end -- for _, v in pairs(vecs) do -- local i = 1 -- while i <= #res and (res[i].x ~= v.x or res[i].y ~= v.y) do -- i = i + 1 -- end -- if i == #res + 1 or #res == 0 then -- table.insert(res, v) -- print('[AI]: circle vecs:', v) -- end -- end -- return res -- end --- Возвращает все точки в радиусе в виде векторов (должен по крайней мере) --- @param radius integer --- @param center Vec3 --- @return Vec3[] local function circleVectors(center, radius) local dx, dy, err = radius, 0, 1 - radius local vecs, res = {}, {} while dx >= dy do table.insert(vecs, Vec3 { center.x + dx, center.y + dy }) table.insert(vecs, Vec3 { center.x - dx, center.y + dy }) table.insert(vecs, Vec3 { center.x + dx, center.y - dy }) table.insert(vecs, Vec3 { center.x - dx, center.y - dy }) table.insert(vecs, Vec3 { center.x + dy, center.y + dx }) table.insert(vecs, Vec3 { center.x - dy, center.y + dx }) table.insert(vecs, Vec3 { center.x + dy, center.y - dx }) table.insert(vecs, Vec3 { center.x - dy, center.y - dx }) dy = dy + 1 if err < 0 then err = err + 2 * dy + 1 else dx, err = dx - 1, err + 2 * (dy - dx) + 1 end end for _, v in pairs(vecs) do local i = 1 while i <= #res and (res[i].x ~= v.x or res[i].y ~= v.y) and v.x >= 0 and v.y >= 0 do i = i + 1 end if i == #res + 1 or #res == 0 then table.insert(res, v) print('[AI]: circle vecs:', v) end end return vecs end --- ищет пути к ближайшему персу в определённом радиусе --- @param owner Character --- @param radius integer здесь мы должны сами определять, сколько должны не доходить до персонажа (1 <= n) --- @return Vec3|nil local function pathToClosestCharacter(owner, radius) local charTarget = closestCharacter(owner) local targetPosition, ownerPosition = charTarget:has(Tree.behaviors.positioned), owner:has(Tree.behaviors.positioned) if not targetPosition or not ownerPosition then return end local circleVecs = circleVectors(targetPosition.position, radius) local target = circleVecs[#circleVecs] local path = pf(ownerPosition.position, target) for i, c in ipairs(circleVecs) do local newPath = pf(ownerPosition.position, c) if newPath:size() < path:size() then path = newPath target = c end end return target end ---@type {[Class]: fun(self: AIBehavior): Task} возможно где-то здесь на объявлении типа сломается типизация local aiNature = { ["dev_warrior"] = function(self) return function(callback) -- почему так, описано в Task self.owner:try(Tree.behaviors.spellcaster, function(spellB) self.target = pathToClosestCharacter(self.owner, 1) local attackTarget = closestCharacter(self.owner):has(Tree.behaviors.positioned) if not attackTarget then return end local task1 = spellB.spellbook[1]:cast(self.owner, self.target) if task1 then task1( function() -- здесь мы оказываемся после того, как сходили в первый раз print('[AI]: я походил') local task2 = spellB.spellbook[3]:cast(self.owner, attackTarget.position) if task2 then -- дергаем функцию после завершения хода print('[AI]: и ударил') task2(callback) else print('[AI]: чёт не бьётся') callback() end end ) else print('рот этого казино') callback() end end) end end, ["dev_mage"] = function(self) return function(callback) print("etoh... bleh") callback() end end } --- @class AIBehavior : Behavior --- @field target Vec3? local behavior = {} behavior.__index = behavior behavior.id = "ai" --- @param class Class function behavior.new(class) return setmetatable({ makeTurn = aiNature[class] }, behavior) end return behavior