91 lines
3.7 KiB
Lua
91 lines
3.7 KiB
Lua
--- @alias SpellTarget "any" Любой тайл
|
||
--- | "caster" Сам кастующий
|
||
--- | "enemy" Противники
|
||
--- | "ally" Союзники
|
||
--- | "character" Любой персонаж
|
||
|
||
--- @class Spell Здесь будет много бойлерплейта, поэтому тоже понадобится спеллмейкерский фреймворк, который просто вернет готовый Spell
|
||
--- @field tag string
|
||
--- @field baseCost integer Базовые затраты маны на каст
|
||
--- @field baseCooldown integer Базовый кулдаун в ходах
|
||
--- @field possibleTarget SpellTarget Возможная цель
|
||
--- @field distance? integer Сторона квадрата с центром в позиции кастера, в пределах которого должна находиться цель, либо отсутствие ограничения
|
||
--- @field update fun(self: Spell, caster: Character, dt: number): nil Изменяет состояние спелла
|
||
--- @field draw fun(self: Spell): nil Рисует превью каста, ничего не должна изменять в идеальном мире
|
||
--- @field cast fun(self: Spell, caster: Character, target: Vec3): Task<nil> | nil Вызывается в момент каста, изменяет мир.
|
||
local spell = {}
|
||
spell.__index = spell
|
||
spell.tag = "spell_base"
|
||
spell.baseCost = 1
|
||
spell.baseCooldown = 1
|
||
spell.possibleTarget = "any"
|
||
|
||
function spell:update(caster, dt) end
|
||
|
||
function spell:draw() end
|
||
|
||
function spell:cast(caster, target) return end
|
||
|
||
--- @param caster Character
|
||
--- @param spellTarget SpellTarget
|
||
--- @param targetPosition Vec3
|
||
local function checkTarget(caster, spellTarget, targetPosition)
|
||
--- @TODO имплементировать все варианты SpellTarget
|
||
local targetCharacterId = Tree.level.characterGrid:get(targetPosition)
|
||
if spellTarget == "caster" then
|
||
return targetCharacterId == caster.id
|
||
end
|
||
if spellTarget == "character" then
|
||
return not not targetCharacterId
|
||
end
|
||
if spellTarget == "enemy" then
|
||
return targetCharacterId and targetCharacterId ~= caster.id
|
||
end
|
||
|
||
return true
|
||
end
|
||
|
||
--- Конструктор [Spell]
|
||
--- @param data {tag: string, baseCost: integer, baseCooldown: integer, possibleTarget: SpellTarget, distance: integer?, onCast: fun(caster: Character, target: Vec3): Task}
|
||
--- @return Spell
|
||
function spell.new(data)
|
||
local newSpell = {
|
||
tag = data.tag,
|
||
baseCost = data.baseCost,
|
||
baseCooldown = data.baseCooldown,
|
||
possibleTarget = data.possibleTarget,
|
||
distance = data.distance
|
||
}
|
||
|
||
function newSpell:cast(caster, target)
|
||
-- проверка на расстояние до цели
|
||
if self.distance and caster:try(Tree.behaviors.positioned, function(p)
|
||
local dist = math.max(math.abs(p.position.x - target.x), math.abs(p.position.y - target.y))
|
||
print("dist:", dist)
|
||
return dist > self.distance
|
||
end) then
|
||
return
|
||
end
|
||
|
||
-- проверка корректности цели
|
||
if not checkTarget(caster, self.possibleTarget, target) then return end
|
||
|
||
-- проверка на достаточное количество маны
|
||
if caster:try(Tree.behaviors.stats, function(stats)
|
||
return stats.mana < self.baseCost
|
||
end) then
|
||
return
|
||
end
|
||
|
||
caster:try(Tree.behaviors.stats, function(stats)
|
||
stats.mana = stats.mana - self.baseCost
|
||
end)
|
||
|
||
return data.onCast(caster, target)
|
||
end
|
||
|
||
return setmetatable(newSpell, spell)
|
||
end
|
||
|
||
return spell
|