Compare commits

..

No commits in common. "main" and "rework/rendering" have entirely different histories.

44 changed files with 44 additions and 610 deletions

3
.gitattributes vendored
View File

@ -1,3 +0,0 @@
*.ogg filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.ttf filter=lfs diff=lfs merge=lfs -text

View File

@ -6,6 +6,5 @@
"love.filesystem.load": "loadfile" "love.filesystem.load": "loadfile"
}, },
"workspace.ignoreDir": ["dev_utils"], "workspace.ignoreDir": ["dev_utils"],
"diagnostics.ignoredFiles": "Disable", "diagnostics.ignoredFiles": "Disable"
"hint.enable": true
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/audio/sounds/meow.ogg (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 B

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 B

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 B

After

Width:  |  Height:  |  Size: 572 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 B

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 B

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 B

After

Width:  |  Height:  |  Size: 368 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 B

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 B

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 B

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 B

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 B

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 B

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -9,6 +9,5 @@ Tree.behaviors.positioned = require "character.behaviors.positioned"
Tree.behaviors.tiled = require "character.behaviors.tiled" Tree.behaviors.tiled = require "character.behaviors.tiled"
Tree.behaviors.cursor = require "character.behaviors.cursor" Tree.behaviors.cursor = require "character.behaviors.cursor"
Tree.behaviors.ai = require "lib.character.behaviors.ai" Tree.behaviors.ai = require "lib.character.behaviors.ai"
Tree.behaviors.effects = require "lib.character.behaviors.effects"
--- @alias voidCallback fun(): nil --- @alias voidCallback fun(): nil

View File

@ -1,7 +1,6 @@
local easing = require "lib.utils.easing" local easing = require "lib.utils.easing"
local pf = require "lib.pathfinder" local pf = require "lib.pathfinder"
local utils = require "lib.utils.utils" local utils = require "lib.utils.utils"
local task = require "lib.utils.task"
--- @alias AIAction fun(self: AIBehavior): Task<nil> --- @alias AIAction fun(self: AIBehavior): Task<nil>
@ -157,16 +156,6 @@ local behavior = {}
behavior.__index = behavior behavior.__index = behavior
behavior.id = "ai" behavior.id = "ai"
--- Заставляет ИИ сделать ход
---
--- По умолчанию ничего не делает
--- @return Task<nil>
function behavior:makeTurn()
return function(callback)
callback()
end
end
--- @param class Class --- @param class Class
function behavior.new(class) function behavior.new(class)
return setmetatable({ return setmetatable({

View File

@ -1,187 +0,0 @@
local task = require "lib.utils.task"
local efb = require "lib.effectbook"
local book = efb.book
--- ===========ЛОГИКА ЭФФЕКТОВ И ЧТО С ЭТИМ ЕДЯТ===========
--- читать здесь: https://docs.google.com/document/d/1Hxa5dOLaeRpLQOs5H-oIDDuLLhKbDw40lR9d62Zb4Tg/edit?usp=sharing
--- и здесь: https://docs.google.com/document/d/1jvhuM3mxqYSQTEM8m-WL-uUSie9QRsZOCCUEiw9ZqzM/edit?tab=t.0
--- behavior thats holds all effects that we applied
--- @class EffectsBehavior : Behavior
--- @field effectsPriority EffectTag[] хранит эффекты в порядке их применения
--- @field effectsProperties table<EffectTag, { stacks: integer, intensity: integer }> хранит характеристики эффектов
local behavior = {}
behavior.__index = behavior
behavior.id = "effects"
--- @return EffectsBehavior
function behavior.new()
return setmetatable({
effectsPriority = {},
effectsProperties = {},
}, behavior)
end
--- проверяет, можно ли наложить эффект и при наложении его применяет
--- @param effect EffectTag
--- @param stacks integer
--- @param intensity integer
function behavior:addEffect(effect, stacks, intensity)
local task1, birthStatement = book[effect]:beforeBirth(self.owner, intensity)
if task1 then
task1(function() end)
end
if not birthStatement then return end
-- проверка на сумму, и её применение
for i, ef in ipairs(self.effectsPriority) do
if efb.sums[effect] then
if efb.sums[effect][ef] then
if not efb.sums[effect][ef](self.owner, effect, ef) then return end
end
elseif efb.sums[ef] then
if efb.sums[ef][effect] then
if not efb.sums[ef][effect](self.owner, ef, effect) then return end
end
end
end
book[effect]:onBirth(self.owner, stacks, intensity)
local task3 = book[effect]:afterBirth(self.owner, intensity)
if task3 then
task3(function()
print("[Effects]: мы применили эффект!!")
end)
end
end
--- Удаляет один эффект по порядку
--- @param effect EffectTag
function behavior:deleteEffect(effect)
self.effectsProperties[effect] = nil
for i, ef in ipairs(self.effectsPriority) do
if ef == effect then
table.remove(self.effectsPriority, i)
return
end
end
end
--- О ДААА ЭТА ФУНКЦИЯ МЕНЯЕТ СОСТОЯНИЕ О ДАААААА О ДАААААААААА
--- @param effect EffectTag
--- @param amount integer
function behavior:deleteStacks(effect, amount)
print("[Effects]: удаляем стаки!!")
self.effectsProperties[effect].stacks = self.effectsProperties[effect].stacks -
amount -- !!!!!!!!!!!!!!!! <<<<< 21+ only
if self.effectsProperties[effect].stacks <= 0 then
print("[Effects]:", effect, "ДОЛЖЕН БЫТЬ СТЁРТ")
self:deleteEffect(effect)
print("[Effects]:", effect, "СТЁРТ")
end
end
--- должна вызываться перед смертью персонажа;
---
--- возвращает, убивать ли персонажа
--- @return boolean
function behavior:beforeDeath()
for i, ef in ipairs(self.effectsPriority) do
local task1, deathStatement = book[ef]:beforeDeath(self.owner, self.effectsProperties[ef].intensity)
if task1 then
task1(function() end)
end
if deathStatement == false then return false end
end
return true
end
--- должна вызываться после смерти персонажа (может ли такая ситуация возникнуть вообще?)
function behavior:afterDeath()
for i, ef in ipairs(self.effectsPriority) do
local task1 = book[ef]:afterDeath(self.owner, self.effectsProperties[ef].intensity)
if task1 then
task1(function() end)
end
end
end
--- должен вызываться в начале хода
---
--- возвращает, может ли персонаж сделать ход?
--- @return boolean
function behavior:beforeTurn()
for i, ef in ipairs(self.effectsPriority) do
local task1, turnStatement = book[ef]:beforeTurn(self.owner, self.effectsProperties[ef].intensity)
if task1 then
task1(function() end)
end
if turnStatement == false then return false end
end
return true
end
--- должен вызываться в конце хода
function behavior:afterTurn()
for i, ef in ipairs(self.effectsPriority) do
local task1 = book[ef]:afterTurn(self.owner, self.effectsProperties[ef].intensity)
if task1 then
task1(function() end)
end
end
end
--- должен вызываться перед кастом спелла
---
--- возвращает, может ли персонаж скастовать спелл?
--- @return boolean
function behavior:beforeCast()
for i, ef in ipairs(self.effectsPriority) do
local task1, castStatement = book[ef]:beforeCast(self.owner, self.effectsProperties[ef].intensity)
if task1 then
task1(function() end)
end
if castStatement == false then return false end
end
return true
end
--- должен вызываться после каста спелла
function behavior:afterCast()
for i, ef in ipairs(self.effectsPriority) do
local task1 = book[ef]:afterCast(self.owner, self.effectsProperties[ef].intensity)
if task1 then
task1(function() end)
end
end
end
--- должен вызываться перед получением урона
---
--- возвращает получаемый урон
--- @return integer
function behavior:beforeDamage(damage)
local totalDamage = damage
for i, ef in ipairs(self.effectsPriority) do
local task1
task1, totalDamage = book[ef]:beforeDamage(self.owner, self.effectsProperties[ef].intensity,
totalDamage or damage)
if task1 then
task1(function() end)
end
end
return totalDamage or damage
end
--- должен вызываться после получения урона
function behavior:afterDamage()
for i, ef in ipairs(self.effectsPriority) do
local task1 = book[ef]:afterDamage(self.owner, self.effectsProperties[ef].intensity)
if task1 then
task1(function() end)
end
end
end
return behavior

View File

@ -20,9 +20,6 @@ function behavior.new(spellbook)
end end
function behavior:endCast() function behavior:endCast()
self.owner:try(Tree.behaviors.effects, function(effects)
effects:afterCast()
end)
self.state = "idle" self.state = "idle"
self.cast = nil self.cast = nil
Tree.level.turnOrder:reorder() Tree.level.turnOrder:reorder()

View File

@ -6,27 +6,10 @@
--- @field initiative integer --- @field initiative integer
--- @field class Class --- @field class Class
--- @field isInTurnOrder boolean --- @field isInTurnOrder boolean
--- @field amIAlive boolean
local behavior = {} local behavior = {}
behavior.__index = behavior behavior.__index = behavior
behavior.id = "stats" behavior.id = "stats"
--- план прост, если что-то не так, то мы просто убиваем бехавиор (по крайней мере так должно было быть, но пиаш мне запретил :sob:)
function behavior:checkStats()
-- if self.hp <= 0 then behavior:die() end
if self.hp <= 0 then
self.amIAlive = false
end
end
--- @param damage integer
function behavior:dealDamage(damage)
local effects = self.owner:has(Tree.behaviors.effects)
if effects then damage = effects:beforeDamage(damage) end
self.hp = self.hp - damage
self:checkStats()
end
--- @param hp? integer --- @param hp? integer
--- @param mana? integer --- @param mana? integer
--- @param initiative? integer --- @param initiative? integer
@ -39,7 +22,6 @@ function behavior.new(hp, mana, initiative, class, isInTurnOrder)
initiative = initiative or 10, initiative = initiative or 10,
class = class or "dev_warrior", class = class or "dev_warrior",
isInTurnOrder = isInTurnOrder or true, isInTurnOrder = isInTurnOrder or true,
amIAlive = true
}, behavior) }, behavior)
end end

View File

@ -1,123 +0,0 @@
local task = require "lib.utils.task"
local effect = require "lib.spell.effect"
local easing = require "lib.utils.easing"
--- некое уникальное строковое значение
--- @alias EffectTag string
--- Кровотечение.
---
--- Наносит `intensity` урона перед началом каждого хода.
local bleeding = effect.new({
tag = "bleeding"
})
function bleeding:afterBirth(owner, intensity)
local light = require "lib/character/character".spawn("Bleeding Light Effect")
light:addBehavior {
Tree.behaviors.light.new { color = Vec3 { 1, 0., 0. }, intensity = 4 },
Tree.behaviors.positioned.new(owner: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 { 0, 0., 0. } }, 800, easing.easeInCubic), function()
light:die()
return task.fromValue()
end) })
end
function bleeding:beforeTurn(owner, intensity)
local stats = owner:has(Tree.behaviors.stats)
local sprite = owner:has(Tree.behaviors.sprite)
if not stats or not sprite then return end
stats:dealDamage(intensity)
return task.wait({ sprite:animate("hurt") }), true
end
function bleeding:afterTurn(owner, intensity)
local behavior = owner:has(Tree.behaviors.effects)
if not behavior then
print('[EffectBook]: yo man what the hell wheres your behavior how thats possible please stop thats not normal')
else
behavior:deleteStacks("bleeding", 1)
end
return task.wait {}
end
--- meow
function bleeding:afterCast(owner, intensity)
Tree.audio:play(Tree.assets.files.audio.sounds.meow)
return task.wait {}
end
function bleeding:beforeCast(owner, intensity)
Tree.audio:play(Tree.assets.files.audio.sounds.meow)
return task.wait {}, true
end
--- Отвращение к смерти.
---
--- Спасает от смертельного урона, оставляя одно очко здоровья. Персонаж не может ходить до тех пор, пока эффект не сработает.
local aversionToDeath = effect.new {
tag = "aversionToDeath"
}
function aversionToDeath:beforeDamage(owner, intensity, damage)
local stats = owner:has(Tree.behaviors.stats)
local effects = owner:has(Tree.behaviors.effects)
if not stats or not effects then return end
if stats.hp <= damage then
effects:deleteStacks("aversionToDeath", 1)
-- тут должен быть какой-нибудь классный спецэффект, но я не умею в шейдеры
return task.wait({}), stats.hp - 1
end
return task.wait {}, damage
end
function aversionToDeath:beforeTurn(owner, intensity)
local sprite = owner:has(Tree.behaviors.sprite)
if not sprite then
return task.wait {}, false
end
return task.wait {
sprite:animate("hurt")
}, false
end
----------------- Effectbook & Sum -----------------
--- @alias EffectSumFunc fun(owner: Character, effect1: EffectTag, effect2: EffectTag): boolean
--- Принимает таблицу, в ключах которых тэги эффектов, которые мы хотим просуммировать, и в значениях которых функция,
--- возвращающая булево значение: применять ли эффект после суммирования.
--- @type table<EffectTag, table<EffectTag, EffectSumFunc>>
local sums = {}
--- Сумма кровотечения и отвращения к смерти, (в целях разработки) удаляет оба эффекта, не позволяя дальше применять эффект
sums.bleeding = {
aversionToDeath = function(owner, effect1, effect2)
print("[EffectBook]: применяем сумму, удаляем оба эффекта")
local behaviorEffect = owner:has(Tree.behaviors.effects)
if not behaviorEffect then
print(
"[EffectBook]: yo man what the hell wheres your behavior how thats possible please stop thats not normal")
return true
end
behaviorEffect:deleteEffect(effect1)
behaviorEffect:deleteEffect(effect2)
return false
end
}
--- @class EffectBook
--- @field sums table<EffectTag, table<EffectTag, EffectSumFunc>>
--- @field book table<EffectTag, Effect>
local effectbook = {
sums = sums,
book = {
bleeding = bleeding,
aversionToDeath = aversionToDeath
}
}
return effectbook

View File

@ -1,6 +1,8 @@
local utils = require "lib.utils.utils" local utils = require "lib.utils.utils"
local pQueue = require "lib.utils.priority_queue"
--- @class CharacterGrid : Grid --- @class CharacterGrid : Grid
--- @field __grid {string: Id|nil} --- @field __grid {string: Id|nil}
--- @field yOrderQueue PriorityQueue<Character> очередь отрисовки сверху вниз
local grid = setmetatable({}, require "lib.level.grid.base") local grid = setmetatable({}, require "lib.level.grid.base")
grid.__index = grid grid.__index = grid
@ -27,13 +29,22 @@ function grid:add(id)
end end
end end
--- @param a Character
--- @param b Character
local function drawCmp(a, b)
--- @TODO: это захардкожено, надо разделить поведения
return a:has(Tree.behaviors.positioned).position.y < b:has(Tree.behaviors.positioned).position.y
end
--- fills the grid with the actual data --- fills the grid with the actual data
--- ---
--- should be called as early as possible during every tick --- should be called as early as possible during every tick
function grid:reload() function grid:reload()
self:reset() self:reset()
self.yOrderQueue = pQueue.new(drawCmp)
utils.each(Tree.level.characters, function(c) utils.each(Tree.level.characters, function(c)
self:add(c.id) self:add(c.id)
self.yOrderQueue:insert(c)
end) end)
end end

View File

@ -20,7 +20,7 @@ local function new(type, template)
local size = Vec3 { 30, 30 } -- magic numbers for testing purposes only local size = Vec3 { 30, 30 } -- magic numbers for testing purposes only
print(type, template, size) print(type, template, size)
Tree.audio:play(Tree.assets.files.audio.music.level1.progressive_plains) Tree.audio:play(Tree.assets.files.audio.music.level1.battle)
return setmetatable({ return setmetatable({
size = size, size = size,

View File

@ -43,15 +43,6 @@ function turnOrder:next()
char:try(Tree.behaviors.positioned, function(positioned) char:try(Tree.behaviors.positioned, function(positioned)
Tree.level.camera:animateTo(positioned.position, 1500, easing.easeInOutCubic)( Tree.level.camera:animateTo(positioned.position, 1500, easing.easeInOutCubic)(
function() function()
-- проверяем, позволяют ли эффекты нам сходить
if char:try(Tree.behaviors.effects, function(effects)
-- print("[TurnOrder]: ну мы пытаемся применить эффект к", char.id)
return effects:beforeTurn()
end) == false then
self:next()
return
end
if char:has(Tree.behaviors.ai) then if char:has(Tree.behaviors.ai) then
char:has(Tree.behaviors.ai):makeTurn()( char:has(Tree.behaviors.ai):makeTurn()(
function() function()
@ -78,9 +69,6 @@ function turnOrder:endRound()
char:try(Tree.behaviors.spellcaster, function(spellcaster) char:try(Tree.behaviors.spellcaster, function(spellcaster)
spellcaster:processCooldowns() spellcaster:processCooldowns()
end) end)
char:try(Tree.behaviors.effects, function(effects)
effects:afterTurn()
end)
end end
self.actedQueue, self.pendingQueue = self.pendingQueue, self.actedQueue self.actedQueue, self.pendingQueue = self.pendingQueue, self.actedQueue

View File

@ -1,169 +0,0 @@
local utils = require "lib.utils.utils"
local taskUtils = require "lib.utils.task"
--- Некоторое свойство, что можно наложить на персонажа. Позволяет реализовать такие вещи как DOT'ы
--- и вообще, что душа поживает.
---
--- У каждого эффекта есть тэг и функции триггеры (например, `beforeTurn`, что срабатывает перед началом хода персонажа и так далее).
--- Каждая функция триггер делится на два типа, `before...` и `after...`. Каждая из них возвращает `task`, для того чтобы
--- проиграть анимацию, например. Функции типа `before...` также возвращают по мимо таска некоторое значение, зависящее от
--- конкретной функции.
--- @class Effect
--- @field tag string
local effect = {}
effect.__index = effect
--- Предполагается, что в каждую функцию будет передаваться `Character` (владелец эффекта) и параметр `intensity`, который отвечает за силу эффекта
--- @alias EffectFunc fun(owner: Character, intensity: integer): Task<nil>, nil бред конечно, но иначе всё в жёлтом
--- @alias EffectStatementFunc fun(owner: Character, intensity: integer): Task<nil>, boolean
--- @alias EffectDamageFunc fun(owner: Character, intensity: integer, damage: integer): Task<nil>, integer
--- @alias EffectRegenFunc fun(owner: Character, intensity: integer, amountHp: integer): Task<nil>, integer
--- @alias EffectData { tag: string }
--- Срабатывает перед применением эффекта
---
--- Возвращает, а можно ли применить эффект?
--- @param owner Character
--- @param intensity integer
--- @return Task<nil>, boolean
function effect:beforeBirth(owner, intensity) return taskUtils.fromValue(), true end
--- Срабатывает после применения эффекта
--- @param owner Character
--- @param intensity integer
--- @return Task<nil>
function effect:afterBirth(owner, intensity) return taskUtils.fromValue() end
--- Срабатывает перед смертью владельца эффекта
---
--- Возвращает, умирает ли персонаж?
--- @param owner Character
--- @param intensity integer
--- @return Task<nil>, boolean
function effect:beforeDeath(owner, intensity) return taskUtils.fromValue(), true end
--- Срабатывает после смерти владельца эффекта
--- @param owner Character
--- @param intensity integer
--- @return Task<nil>
function effect:afterDeath(owner, intensity) return taskUtils.fromValue() end
--- Срабатывает перед ходом владельца эффекта
---
--- Возвращает, будет ли персонаж ходить?
--- @param owner Character
--- @param intensity integer
--- @return Task<nil>, boolean
function effect:beforeTurn(owner, intensity) return taskUtils.fromValue(), true end
--- Срабатывает после хода владельца эффекта
--- @param owner Character
--- @param intensity integer
--- @return Task<nil>
function effect:afterTurn(owner, intensity) return taskUtils.fromValue() end
--- Срабатывает перед кастом заклинания владельцем эффекта
---
--- Возвращает, произойдёт ли каст?
--- @param owner Character
--- @param intensity integer
--- @return Task<nil>, boolean
function effect:beforeCast(owner, intensity) return taskUtils.fromValue(), true end
--- Срабатывает после каста заклинания владельцем эффекта
--- @param owner Character
--- @param intensity integer
--- @return Task<nil>
function effect:afterCast(owner, intensity) return taskUtils.fromValue() end
--- Срабатывает перед нанесением урона владельцем эффекта
---
--- Возвращает урон, который собираются нанести
--- @param owner Character
--- @param intensity integer
--- @param damage integer
--- @return Task<nil>, integer
function effect:beforeAttack(owner, intensity, damage) return taskUtils.fromValue(), damage end
--- Срабатывает после нанесения урона владельцем эффекта
--- @param owner Character
--- @param intensity integer
--- @return Task<nil>
function effect:afterAttack(owner, intensity) return taskUtils.fromValue() end
--- Срабатывает перед получением урона владельцем эффекта
---
--- Возвращает урон, который должны получить
--- @param owner Character
--- @param intensity integer
--- @param damage integer
--- @return Task<nil>, integer
function effect:beforeDamage(owner, intensity, damage) return taskUtils.fromValue(), damage end
--- Срабатывает после получения урона владельцем эффекта
--- @param owner Character
--- @param intensity integer
--- @return Task<nil>
function effect:afterDamage(owner, intensity) return taskUtils.fromValue() end
--- Срабатывает перед регенерацией здоровья владельцем эффекта
---
--- Возвращает количество здоровья, которое должно быть восстановлено
--- @param owner Character
--- @param intensity integer
--- @param amountHp integer кол-во хп для регена
--- @return Task<nil>, integer
function effect:beforeRegeneration(owner, intensity, amountHp) return taskUtils.fromValue(), amountHp end
--- Срабатывает после регенерации здоровья владельцем эффекта
--- @param owner Character
--- @param intensity integer
--- @return Task<nil>
function effect:afterRegeneration(owner, intensity) return taskUtils.fromValue() end
--- Функция, что задаёт правила присвоения эффекта
--- @param owner Character
--- @param stacks integer
--- @param intensity integer
function effect:onBirth(owner, stacks, intensity)
local effects = owner:has(Tree.behaviors.effects)
if not effects then return end
-- проверяем на наличие такого же эффекта
if effects.effectsProperties[self.tag] then
local i = 1
while i < #effects.effectsPriority and effects.effectsPriority[i] ~= self.tag do
i = i + 1
end
local ef = table.remove(effects.effectsPriority, i)
effects.effectsPriority[#effects.effectsPriority + 1] = ef
else
effects.effectsPriority[#effects.effectsPriority + 1] = self.tag
end
effects.effectsProperties[self.tag] = {
stacks = stacks,
intensity = intensity
}
end
function effect:update(dt) end
function effect:draw() end
--- дип сравнение эффектов
--- @param other Effect
--- @return boolean
function effect:__eq(other)
return utils.deepComparison(self, other)
end
--- @param data EffectData
--- @return Effect
local function new(data)
local newEffect = setmetatable({
tag = data.tag,
}, effect)
return newEffect
end
return { new = new }

View File

@ -123,13 +123,6 @@ function spell.new(data)
return return
end end
-- проверка на возможность каста
if not caster:try(Tree.behaviors.effects, function(effects)
return effects:beforeCast()
end) then
return
end
caster:try(Tree.behaviors.stats, function(stats) caster:try(Tree.behaviors.stats, function(stats)
stats.mana = stats.mana - self.baseCost stats.mana = stats.mana - self.baseCost
end) end)
@ -137,8 +130,6 @@ function spell.new(data)
caster:try(Tree.behaviors.spellcaster, function(spellcaster) caster:try(Tree.behaviors.spellcaster, function(spellcaster)
spellcaster.cooldowns[self.tag] = self.baseCooldown spellcaster.cooldowns[self.tag] = self.baseCooldown
end) end)
return data.onCast(caster, target) return data.onCast(caster, target)
end end

View File

@ -52,8 +52,7 @@ local regenerateMana = spell.new {
end) end)
local sprite = caster:has(Tree.behaviors.sprite) local sprite = caster:has(Tree.behaviors.sprite)
local effects = caster:has(Tree.behaviors.effects) if not sprite then return end
if not sprite or not effects then return end -- и тут возможно на эффекты проверять не стоит
print(caster.id, "has regenerated mana and gained initiative") print(caster.id, "has regenerated mana and gained initiative")
local light = require "lib/character/character".spawn("Light Effect") local light = require "lib/character/character".spawn("Light Effect")
@ -68,8 +67,7 @@ local regenerateMana = spell.new {
light:die() light:die()
return task.fromValue() return task.fromValue()
end), end),
sprite:animate("hurt"), sprite:animate("hurt")
effects:addEffect("aversionToDeath", 1, 1),
} }
end end
} }
@ -88,10 +86,9 @@ local attack = spell.new {
stats.hp = stats.hp - 4 stats.hp = stats.hp - 4
end) end)
local targetEffects = targetCharacter:has(Tree.behaviors.effects)
local sprite = caster:has(Tree.behaviors.sprite) local sprite = caster:has(Tree.behaviors.sprite)
local targetSprite = targetCharacter:has(Tree.behaviors.sprite) local targetSprite = targetCharacter:has(Tree.behaviors.sprite)
if not sprite or not targetSprite or not targetEffects then return end -- проверять на эффект может и не стоит if not sprite or not targetSprite then return end
caster:try(Tree.behaviors.positioned, function(b) b:lookAt(target) end) caster:try(Tree.behaviors.positioned, function(b) b:lookAt(target) end)
@ -106,7 +103,6 @@ local attack = spell.new {
Tree.behaviors.light.new { color = Vec3 { 0.6, 0.3, 0.3 }, intensity = 4 }, 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 }), Tree.behaviors.positioned.new(targetCharacter:has(Tree.behaviors.positioned).position + Vec3 { 0.5, 0.5 }),
} }
Tree.audio:play(Tree.assets.files.audio.sounds.hurt)
return return
task.wait { task.wait {
task.chain(task.tween(light:has(Tree.behaviors.light) --[[@as LightBehavior]], task.chain(task.tween(light:has(Tree.behaviors.light) --[[@as LightBehavior]],
@ -114,11 +110,12 @@ local attack = spell.new {
light:die() light:die()
return task.fromValue() return task.fromValue()
end), end),
targetSprite:animate("hurt"), targetSprite:animate("hurt")
targetEffects:addEffect("bleeding", 3, 3)
} }
end end
), ),
Tree.audio:play(Tree.assets.files.audio.sounds.hurt)
} }
} }
end end

View File

@ -74,19 +74,4 @@ function P.lerp(from, to, t)
return from + (to - from) * t return from + (to - from) * t
end end
--- Compares two tables by their fields
--- @param t1 table
--- @param t2 table
--- @return boolean
function P.deepComparison(t1, t2)
for k, v in pairs(t1) do
if type(v) == "table" and type(t2[k]) == "table" then
if not P.deepComparison(v, t2[k]) then return false end
elseif t2[k] ~= v then
return false
end
end
return true
end
return P return P

View File

@ -15,17 +15,16 @@ function love.load()
testLayout = require "lib.simple_ui.level.layout" testLayout = require "lib.simple_ui.level.layout"
local chars = { local chars = {
-- character.spawn("Foodor") character.spawn("Foodor")
-- :addBehavior { :addBehavior {
-- Tree.behaviors.residentsleeper.new(), Tree.behaviors.residentsleeper.new(),
-- Tree.behaviors.stats.new(nil, nil, 1), Tree.behaviors.stats.new(nil, nil, 1),
-- Tree.behaviors.positioned.new(Vec3 { 3, 3 }), Tree.behaviors.positioned.new(Vec3 { 1, 1 }),
-- Tree.behaviors.tiled.new(), Tree.behaviors.tiled.new(),
-- Tree.behaviors.sprite.new(Tree.assets.files.sprites.character), Tree.behaviors.sprite.new(Tree.assets.files.sprites.character),
-- Tree.behaviors.shadowcaster.new(), Tree.behaviors.shadowcaster.new(),
-- Tree.behaviors.spellcaster.new(), Tree.behaviors.spellcaster.new()
-- Tree.behaviors.effects.new() },
-- },
character.spawn("Foodor") character.spawn("Foodor")
:addBehavior { :addBehavior {
Tree.behaviors.residentsleeper.new(), Tree.behaviors.residentsleeper.new(),
@ -34,8 +33,7 @@ function love.load()
Tree.behaviors.tiled.new(), Tree.behaviors.tiled.new(),
Tree.behaviors.sprite.new(Tree.assets.files.sprites.character), Tree.behaviors.sprite.new(Tree.assets.files.sprites.character),
Tree.behaviors.shadowcaster.new(), Tree.behaviors.shadowcaster.new(),
Tree.behaviors.spellcaster.new(), Tree.behaviors.spellcaster.new()
Tree.behaviors.effects.new()
}, },
character.spawn("Foodor") character.spawn("Foodor")
:addBehavior { :addBehavior {
@ -45,33 +43,19 @@ function love.load()
Tree.behaviors.tiled.new(), Tree.behaviors.tiled.new(),
Tree.behaviors.sprite.new(Tree.assets.files.sprites.character), Tree.behaviors.sprite.new(Tree.assets.files.sprites.character),
Tree.behaviors.shadowcaster.new(), Tree.behaviors.shadowcaster.new(),
Tree.behaviors.spellcaster.new(), Tree.behaviors.spellcaster.new()
Tree.behaviors.effects.new() },
character.spawn("BOAR")
:addBehavior {
Tree.behaviors.residentsleeper.new(),
Tree.behaviors.stats.new(nil, nil, 2),
Tree.behaviors.positioned.new(Vec3 { 5, 5 }),
Tree.behaviors.tiled.new(),
Tree.behaviors.sprite.new(Tree.assets.files.sprites.boar),
Tree.behaviors.shadowcaster.new(),
Tree.behaviors.spellcaster.new(),
Tree.behaviors.ai.new("dev_warrior")
}, },
-- character.spawn("Baris")
-- :addBehavior {
-- Tree.behaviors.residentsleeper.new(),
-- Tree.behaviors.stats.new(nil, nil, 2),
-- Tree.behaviors.positioned.new(Vec3 { 5, 5 }),
-- Tree.behaviors.tiled.new(),
-- Tree.behaviors.sprite.new(Tree.assets.files.sprites.character),
-- Tree.behaviors.shadowcaster.new(),
-- Tree.behaviors.spellcaster.new(),
-- Tree.behaviors.ai.new(),
-- Tree.behaviors.effects.new()
-- },
-- character.spawn("BOAR")
-- :addBehavior {
-- Tree.behaviors.residentsleeper.new(),
-- Tree.behaviors.stats.new(nil, nil, 2),
-- Tree.behaviors.positioned.new(Vec3 { 7, 7 }),
-- Tree.behaviors.tiled.new(),
-- Tree.behaviors.sprite.new(Tree.assets.files.sprites.boar),
-- Tree.behaviors.shadowcaster.new(),
-- Tree.behaviors.spellcaster.new(),
-- Tree.behaviors.ai.new(),
-- Tree.behaviors.effects.new()
-- },
} }
for id, _ in pairs(chars) do for id, _ in pairs(chars) do