Compare commits
2 Commits
2e6155aea4
...
4431934e6b
| Author | SHA1 | Date | |
|---|---|---|---|
| 4431934e6b | |||
| e0b08b07ec |
@ -1,14 +1,15 @@
|
||||
--- @alias SpellTarget "any" Любой тайл
|
||||
--- | "caster" Сам кастующий
|
||||
--- | "enemy" Противники
|
||||
--- | "ally" Союзники
|
||||
--- | "character" Любой персонаж
|
||||
local Query = require "lib.spell.target_query"
|
||||
local targetTest = require "lib.spell.target_test"
|
||||
|
||||
--- @class Spell Здесь будет много бойлерплейта, поэтому тоже понадобится спеллмейкерский фреймворк, который просто вернет готовый Spell
|
||||
--- @alias SpellPreview "default" Подсветка возможных целей
|
||||
--- | "path" Подсветка пути до цели
|
||||
|
||||
--- @class Spell
|
||||
--- @field tag string
|
||||
--- @field baseCost integer Базовые затраты маны на каст
|
||||
--- @field baseCooldown integer Базовый кулдаун в ходах
|
||||
--- @field possibleTarget SpellTarget Возможная цель
|
||||
--- @field targetQuery SpellTargetQuery Селектор возможных целей
|
||||
--- @field previewType SpellPreview Вид превью во время каста
|
||||
--- @field distance? integer Сторона квадрата с центром в позиции кастера, в пределах которого должна находиться цель, либо отсутствие ограничения
|
||||
--- @field update fun(self: Spell, caster: Character, dt: number): nil Изменяет состояние спелла
|
||||
--- @field draw fun(self: Spell): nil Рисует превью каста, ничего не должна изменять в идеальном мире
|
||||
@ -18,7 +19,8 @@ spell.__index = spell
|
||||
spell.tag = "spell_base"
|
||||
spell.baseCost = 1
|
||||
spell.baseCooldown = 1
|
||||
spell.possibleTarget = "any"
|
||||
spell.targetQuery = Query(targetTest.any)
|
||||
spell.previewType = "default"
|
||||
|
||||
function spell:update(caster, dt) end
|
||||
|
||||
@ -26,34 +28,15 @@ 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}
|
||||
--- @param data {tag: string, baseCost: integer, baseCooldown: integer, targetQuery: SpellTargetQuery, 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,
|
||||
targetQuery = data.targetQuery,
|
||||
distance = data.distance
|
||||
}
|
||||
|
||||
@ -67,8 +50,7 @@ function spell.new(data)
|
||||
return
|
||||
end
|
||||
|
||||
-- проверка корректности цели
|
||||
if not checkTarget(caster, self.possibleTarget, target) then return end
|
||||
if not self.targetQuery.test(caster, target) then return end -- проверка корректности цели
|
||||
|
||||
-- проверка на достаточное количество маны
|
||||
if caster:try(Tree.behaviors.stats, function(stats)
|
||||
|
||||
67
lib/spell/target_query.lua
Normal file
67
lib/spell/target_query.lua
Normal file
@ -0,0 +1,67 @@
|
||||
--- Тип, отвечающий за выбор и фильтрацию подходящих тайлов как цели спелла
|
||||
--- теория множеств my beloved?
|
||||
--- @class SpellTargetQuery
|
||||
local query = {}
|
||||
query.__index = query
|
||||
|
||||
--- Проверяет координаты на соответствие внутреннему условию
|
||||
--- @param caster Character
|
||||
--- @param position Vec3
|
||||
--- @return boolean
|
||||
function query.test(caster, position)
|
||||
return true
|
||||
end
|
||||
|
||||
--- Объединение
|
||||
--- @param q SpellTargetQuery
|
||||
function query:join(q)
|
||||
return setmetatable({
|
||||
test = function(caster, pos)
|
||||
return self.test(caster, pos) or q.test(caster, pos)
|
||||
end
|
||||
}, q)
|
||||
end
|
||||
|
||||
--- Пересечение
|
||||
--- @param q SpellTargetQuery
|
||||
function query:intersect(q)
|
||||
return setmetatable({
|
||||
test = function(caster, pos)
|
||||
return self.test(caster, pos) and q.test(caster, pos)
|
||||
end
|
||||
}, q)
|
||||
end
|
||||
|
||||
--- Исключение (не коммутативное, "те, что есть в query, но нет в q")
|
||||
--- @param q SpellTargetQuery
|
||||
function query:exclude(q)
|
||||
return setmetatable({
|
||||
test = function(caster, pos)
|
||||
return self.test(caster, pos) and not q.test(caster, pos)
|
||||
end
|
||||
}, q)
|
||||
end
|
||||
|
||||
--- Находит все соответствующие условиям координаты тайлов и возвращает их в виде списка
|
||||
--- @param caster Character
|
||||
--- @return Vec3[]
|
||||
function query:asSet(caster)
|
||||
--- @TODO: оптимизировать и брать не всю карту для выборки
|
||||
local res = {}
|
||||
for _, tile in pairs(Tree.level.tileGrid) do
|
||||
if self.test(caster, tile.position) then
|
||||
table.insert(res, tile.position)
|
||||
end
|
||||
end
|
||||
|
||||
return res
|
||||
end
|
||||
|
||||
--- @param test SpellTargetTest
|
||||
local function new(test)
|
||||
return setmetatable({
|
||||
test = test
|
||||
}, query)
|
||||
end
|
||||
|
||||
return new
|
||||
13
lib/spell/target_test.lua
Normal file
13
lib/spell/target_test.lua
Normal file
@ -0,0 +1,13 @@
|
||||
--- @alias SpellTargetTest fun(caster: Character, targetPosition: Vec3) : boolean
|
||||
|
||||
return {
|
||||
any = function() return true end,
|
||||
caster = function(caster, targetPosition)
|
||||
local targetCharacterId = Tree.level.characterGrid:get(targetPosition)
|
||||
return caster.id == targetCharacterId
|
||||
end,
|
||||
character = function(caster, targetPosition)
|
||||
local targetCharacterId = Tree.level.characterGrid:get(targetPosition)
|
||||
return not not targetCharacterId
|
||||
end
|
||||
}
|
||||
@ -9,6 +9,8 @@
|
||||
|
||||
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 walk = setmetatable({
|
||||
--- @type Deque
|
||||
@ -69,7 +71,7 @@ local regenerateMana = spell.new {
|
||||
tag = "dev_mana",
|
||||
baseCooldown = 2,
|
||||
baseCost = 0,
|
||||
possibleTarget = "caster",
|
||||
targetQuery = Query(targetTest.caster),
|
||||
distance = 0,
|
||||
onCast = function(caster, target)
|
||||
caster:try(Tree.behaviors.stats, function(stats)
|
||||
@ -84,7 +86,6 @@ local regenerateMana = spell.new {
|
||||
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.residentsleeper.new(),
|
||||
Tree.behaviors.positioned.new(caster:has(Tree.behaviors.positioned).position + Vec3 { 0.5, 0.5 }),
|
||||
}
|
||||
|
||||
@ -108,7 +109,7 @@ local attack = spell.new {
|
||||
tag = "dev_attack",
|
||||
baseCooldown = 1,
|
||||
baseCost = 2,
|
||||
possibleTarget = "enemy",
|
||||
targetQuery = Query(targetTest.character):exclude(Query(targetTest.caster)),
|
||||
distance = 1,
|
||||
onCast = function(caster, target)
|
||||
--- @type Character
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user