143 lines
6.5 KiB
Lua
143 lines
6.5 KiB
Lua
--- Алгоритм обработки заклинания (for dummies):
|
||
--- 1) ПОКА выделен персонаж И он находится в режиме каста, вызывать spell:update() и spell:draw() каждый кадр (это отвечает за обработку и отрисовку превьюшки каста, например, превью пути или зоны поражения; реализуется через установку spellcaster.cast, см. код в кнопке)
|
||
--- ЕСЛИ выбран тайл, ТО вызвать spell:cast() (это запрос на обработку последствий применения заклинания, например, старт анимации ходьбы, выпуск снаряда и т.д.; реализовано в selector)
|
||
--- ЕСЛИ spell:cast() ИСТИНА, ТО вызвать selector:lock() (отключить обработку выделения всего на уровне; реализовано в selector)
|
||
---
|
||
--- 2) ПОКА анимация каста НЕ завершена, ничего не делать, ИНАЧЕ вызвать behaviors.spellcaster:endCast() (вот это сейчас нужно вызывать самостоятельно, т.к. нет возможности обобщенно отследить завершение анимаций)
|
||
--- --TODO: каждый каст должен возвращать объект, который позволит отследить момент завершения анимации спелла
|
||
--- Да, это Future/Promise/await/async
|
||
|
||
local task = require 'lib.utils.task'
|
||
local spell = require 'lib.spell.spell'
|
||
local targetTest = require 'lib.spell.target_test'
|
||
local Query = require "lib.spell.target_query"
|
||
local easing = require "lib.utils.easing"
|
||
|
||
local walk = spell.new {
|
||
tag = "dev_move",
|
||
previewType = "path",
|
||
baseCooldown = 1,
|
||
baseCost = 2,
|
||
targetQuery = Query(targetTest.any):exclude(Query(targetTest.character)),
|
||
distance = 3,
|
||
onCast = function(caster, target)
|
||
local initialPos = caster:has(Tree.behaviors.positioned).position:floor()
|
||
local path = require "lib.pathfinder" (initialPos, target)
|
||
path:pop_front()
|
||
if path:is_empty() then
|
||
print("[Walk]: the path is empty", initialPos, target)
|
||
return
|
||
end
|
||
|
||
local sprite = caster:has(Tree.behaviors.sprite)
|
||
assert(sprite, "[Walk]", "WTF DUDE WHERE'S YOUR SPRITE")
|
||
if not sprite then
|
||
return
|
||
end
|
||
|
||
return caster:has(Tree.behaviors.tiled):followPath(path)
|
||
end
|
||
}
|
||
|
||
local regenerateMana = spell.new {
|
||
tag = "dev_mana",
|
||
baseCooldown = 2,
|
||
baseCost = 0,
|
||
targetQuery = Query(targetTest.caster),
|
||
distance = 0,
|
||
onCast = function(caster, target)
|
||
caster:try(Tree.behaviors.stats, function(stats)
|
||
stats.mana = 10
|
||
stats.initiative = stats.initiative + 10
|
||
end)
|
||
|
||
local sprite = caster:has(Tree.behaviors.sprite)
|
||
if not sprite then return end
|
||
print(caster.id, "has regenerated mana and gained initiative")
|
||
|
||
local light = require "lib/character/character".spawn("Light Effect")
|
||
light:addBehavior {
|
||
Tree.behaviors.light.new { color = Vec3 { 0.3, 0.3, 0.6 }, intensity = 4 },
|
||
Tree.behaviors.positioned.new(caster:has(Tree.behaviors.positioned).position + Vec3 { 0.5, 0.5 }),
|
||
}
|
||
|
||
return task.wait {
|
||
task.chain(task.tween(light:has(Tree.behaviors.light) --[[@as LightBehavior]],
|
||
{ intensity = 1, color = Vec3 {} }, 800, easing.easeInCubic), function()
|
||
light:die()
|
||
return task.fromValue()
|
||
end),
|
||
sprite:animate("hurt")
|
||
}
|
||
end
|
||
}
|
||
|
||
local attack = spell.new {
|
||
tag = "dev_attack",
|
||
baseCooldown = 1,
|
||
baseCost = 2,
|
||
targetQuery = Query(targetTest.character):exclude(Query(targetTest.caster)),
|
||
distance = 1,
|
||
onCast = function(caster, target)
|
||
--- @type Character
|
||
local targetCharacterId = Tree.level.characterGrid:get(target)
|
||
local targetCharacter = Tree.level.characters[targetCharacterId]
|
||
targetCharacter:try(Tree.behaviors.stats, function(stats)
|
||
stats.hp = stats.hp - 4
|
||
end)
|
||
|
||
local sprite = caster:has(Tree.behaviors.sprite)
|
||
local targetSprite = targetCharacter:has(Tree.behaviors.sprite)
|
||
if not sprite or not targetSprite then return end
|
||
|
||
caster:try(Tree.behaviors.positioned, function(b) b:lookAt(target) end)
|
||
|
||
return
|
||
task.wait {
|
||
sprite:animate("attack"),
|
||
task.wait {
|
||
task.chain(targetCharacter:has(Tree.behaviors.residentsleeper):sleep(500),
|
||
function()
|
||
local light = require "lib/character/character".spawn("Light Effect")
|
||
light:addBehavior {
|
||
Tree.behaviors.light.new { color = Vec3 { 0.6, 0.3, 0.3 }, intensity = 4 },
|
||
Tree.behaviors.positioned.new(targetCharacter:has(Tree.behaviors.positioned).position + Vec3 { 0.5, 0.5 }),
|
||
}
|
||
return
|
||
task.wait {
|
||
task.chain(task.tween(light:has(Tree.behaviors.light) --[[@as LightBehavior]],
|
||
{ intensity = 1, color = Vec3 {} }, 1000, easing.easeInCubic), function()
|
||
light:die()
|
||
return task.fromValue()
|
||
end),
|
||
targetSprite:animate("hurt")
|
||
}
|
||
end
|
||
),
|
||
|
||
Tree.audio:play(Tree.assets.files.audio.sounds.hurt)
|
||
}
|
||
}
|
||
end
|
||
}
|
||
|
||
----------------------------------------
|
||
local spellbook = {
|
||
walk = walk,
|
||
regenerateMana = regenerateMana,
|
||
attack = attack
|
||
}
|
||
|
||
--- Создает новый спеллбук с уникальными спеллами (а не ссылками на шаблоны)
|
||
--- @param list Spell[]
|
||
function spellbook.of(list)
|
||
local spb = {}
|
||
for i, sp in ipairs(list) do
|
||
print(i)
|
||
spb[i] = setmetatable({}, { __index = sp })
|
||
end
|
||
return spb
|
||
end
|
||
|
||
return spellbook
|