Add simple spell framework and refactor dev_attack spell to use it
This commit is contained in:
parent
93521d2b8b
commit
95f2230302
67
lib/spell/spell.lua
Normal file
67
lib/spell/spell.lua
Normal file
@ -0,0 +1,67 @@
|
||||
--- @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
|
||||
|
||||
--- Конструктор [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)
|
||||
-- проверка корректности цели
|
||||
--- @TODO имплементировать все варианты SpellTarget
|
||||
|
||||
-- проверка на расстояние до цели
|
||||
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 caster:try(Tree.behaviors.stats, function(stats)
|
||||
return stats.mana < self.baseCost
|
||||
end) then
|
||||
return
|
||||
end
|
||||
|
||||
return data.onCast(caster, target)
|
||||
end
|
||||
|
||||
return setmetatable(newSpell, spell)
|
||||
end
|
||||
|
||||
return spell
|
||||
@ -8,21 +8,7 @@
|
||||
--- Да, это Future/Promise/await/async
|
||||
|
||||
local task = require 'lib.utils.task'
|
||||
|
||||
--- @class Spell Здесь будет много бойлерплейта, поэтому тоже понадобится спеллмейкерский фреймворк, который просто вернет готовый Spell
|
||||
--- @field tag string
|
||||
--- @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 = "base"
|
||||
|
||||
function spell:update(caster, dt) end
|
||||
|
||||
function spell:draw() end
|
||||
|
||||
function spell:cast(caster, target) return end
|
||||
local spell = require 'lib.spell.spell'
|
||||
|
||||
local walk = setmetatable({
|
||||
--- @type Deque
|
||||
@ -114,25 +100,16 @@ function regenerateMana:cast(caster, target)
|
||||
}
|
||||
end
|
||||
|
||||
local attack = setmetatable({}, spell)
|
||||
attack.tag = "dev_attack"
|
||||
|
||||
function attack:cast(caster, target)
|
||||
if 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 > 2
|
||||
end) then
|
||||
return
|
||||
end
|
||||
|
||||
caster:try(Tree.behaviors.stats, function(stats)
|
||||
stats.mana = stats.mana - 2
|
||||
end)
|
||||
|
||||
local attack = spell.new {
|
||||
tag = "dev_attack",
|
||||
baseCooldown = 1,
|
||||
baseCost = 2,
|
||||
possibleTarget = "enemy",
|
||||
distance = 2,
|
||||
onCast = function(caster, target)
|
||||
--- @type Character
|
||||
local targetCharacterId = Tree.level.characterGrid:get(target)
|
||||
if not targetCharacterId or targetCharacterId == caster.id then return end
|
||||
if not targetCharacterId or targetCharacterId == caster.id then return task.fromValue() end
|
||||
local targetCharacter = Tree.level.characters[targetCharacterId]
|
||||
targetCharacter:try(Tree.behaviors.stats, function(stats)
|
||||
stats.hp = stats.hp - 4
|
||||
@ -140,7 +117,7 @@ function attack:cast(caster, target)
|
||||
|
||||
local sprite = caster:has(Tree.behaviors.sprite)
|
||||
local targetSprite = targetCharacter:has(Tree.behaviors.sprite)
|
||||
if not sprite or not targetSprite then return end
|
||||
if not sprite or not targetSprite then return task.fromValue() end
|
||||
|
||||
caster:try(Tree.behaviors.positioned, function(b) b:lookAt(target) end)
|
||||
|
||||
@ -154,7 +131,8 @@ function attack:cast(caster, target)
|
||||
Tree.audio:play(Tree.assets.files.audio.sounds.hurt)
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
----------------------------------------
|
||||
local spellbook = {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user