Compare commits

..

No commits in common. "feature/stats" and "main" have entirely different histories.

8 changed files with 82 additions and 209 deletions

View File

@ -59,10 +59,6 @@ end
--- Удаляет один эффект по порядку --- Удаляет один эффект по порядку
--- @param effect EffectTag --- @param effect EffectTag
function behavior:deleteEffect(effect) function behavior:deleteEffect(effect)
if not self.effectsProperties[effect] then return end
local task1, deleteStatement = book[effect]:beforeDelete(self.owner, self.effectsProperties[effect].intensity)
task1(function() end)
if not deleteStatement then return end
self.effectsProperties[effect] = nil self.effectsProperties[effect] = nil
for i, ef in ipairs(self.effectsPriority) do for i, ef in ipairs(self.effectsPriority) do
if ef == effect then if ef == effect then
@ -70,21 +66,19 @@ function behavior:deleteEffect(effect)
return return
end end
end end
local task2 = book[effect]:afterDelete(self.owner, self.effectsProperties[effect].intensity)
task2(function() end)
end end
--- О ДААА ЭТА ФУНКЦИЯ МЕНЯЕТ СОСТОЯНИЕ О ДАААААА О ДАААААААААА --- О ДААА ЭТА ФУНКЦИЯ МЕНЯЕТ СОСТОЯНИЕ О ДАААААА О ДАААААААААА
--- @param effect EffectTag --- @param effect EffectTag
--- @param amount integer --- @param amount integer
function behavior:deleteStacks(effect, amount) function behavior:deleteStacks(effect, amount)
-- print("[Effects]: удаляем стаки!!") print("[Effects]: удаляем стаки!!")
self.effectsProperties[effect].stacks = self.effectsProperties[effect].stacks - self.effectsProperties[effect].stacks = self.effectsProperties[effect].stacks -
amount -- !!!!!!!!!!!!!!!! <<<<< 21+ only amount -- !!!!!!!!!!!!!!!! <<<<< 21+ only
if self.effectsProperties[effect].stacks <= 0 then if self.effectsProperties[effect].stacks <= 0 then
-- print("[Effects]:", effect, "ДОЛЖЕН БЫТЬ СТЁРТ") print("[Effects]:", effect, "ДОЛЖЕН БЫТЬ СТЁРТ")
self:deleteEffect(effect) self:deleteEffect(effect)
-- print("[Effects]:", effect, "СТЁРТ") print("[Effects]:", effect, "СТЁРТ")
end end
end end
@ -166,8 +160,7 @@ end
--- должен вызываться перед получением урона --- должен вызываться перед получением урона
--- ---
--- возвращает получаемый урон --- возвращает получаемый урон
--- @param damage Impact --- @return integer
--- @return Impact
function behavior:beforeDamage(damage) function behavior:beforeDamage(damage)
local totalDamage = damage local totalDamage = damage
for i, ef in ipairs(self.effectsPriority) do for i, ef in ipairs(self.effectsPriority) do
@ -191,32 +184,4 @@ function behavior:afterDamage()
end end
end end
--- должен вызываться перед регенерацией
---
--- возвращает кол-во здоровья
--- @param amount Impact
--- @return Impact
function behavior:beforeRegeneration(amount)
local totalAmount = amount
for i, ef in ipairs(self.effectsPriority) do
local task1
task1, totalAmount = book[ef]:beforeRegeneration(self.owner, self.effectsProperties[ef].intensity,
totalAmount or amount)
if task1 then
task1(function() end)
end
end
return totalAmount or amount
end
--- должен вызываться после регенерации
function behavior:afterRegeneration()
for i, ef in ipairs(self.effectsPriority) do
local task1 = book[ef]:afterRegeneration(self.owner, self.effectsProperties[ef].intensity)
if task1 then
task1(function() end)
end
end
end
return behavior return behavior

View File

@ -1,13 +1,9 @@
local impact = require "lib.utils.impact"
--- @alias Class "dev_warrior"|"dev_mage" --- @alias Class "dev_warrior"|"dev_mage"
--- @alias Chars "strength"|"intelligence"|"agility"|"stamina"|"lunacy"
--- @alias CharsTable table<Chars, integer>
--- @class StatsBehavior : Behavior --- @class StatsBehavior : Behavior
--- @field hp integer --- @field hp integer
--- @field mana integer --- @field mana integer
--- @field chars { raw: CharsTable, final: CharsTable } по хорошему, мы не должны менять эту таблицу руками, а делать это только через метод `changeChar` --- @field initiative integer
--- @field class Class --- @field class Class
--- @field isInTurnOrder boolean --- @field isInTurnOrder boolean
--- @field amIAlive boolean --- @field amIAlive boolean
@ -23,84 +19,24 @@ function behavior:checkStats()
end end
end end
function behavior:maxHealth()
return self.chars.final["stamina"] * 2
end
--- @param damage integer --- @param damage integer
--- @param impactType ImpactType function behavior:dealDamage(damage)
function behavior:dealDamage(damage, impactType)
local damageImpact = impact(damage, impactType)
local effects = self.owner:has(Tree.behaviors.effects) local effects = self.owner:has(Tree.behaviors.effects)
if effects then damageImpact = effects:beforeDamage(damageImpact) end if effects then damage = effects:beforeDamage(damage) end
self.hp = self.hp - damageImpact.intensity self.hp = self.hp - damage
if effects then effects:afterDamage() end
self:checkStats() self:checkStats()
end end
--- @param amount integer --- @param hp? integer
--- @param impactType ImpactType --- @param mana? integer
function behavior:healHealth(amount, impactType) --- @param initiative? integer
local healthImpact = impact(amount, impactType)
local effects = self.owner:has(Tree.behaviors.effects)
if effects then healthImpact = effects:beforeRegeneration(healthImpact) end
self.hp = self.hp + healthImpact.intensity
if effects then effects:afterRegeneration() end
end
--- позволяет изменять значение характеристики персонажа
---
--- менять характеристики мы должны с помощью функции, которая возвращает значение на сколько мы должны изменить характеристику
--- относительно сырой характеристики, и это значение мы прибавляем к финальным характеристикам
---
--- такой системой (вроде как) мы гарантируем, что все операции над статами будут обратимыми
---
--- @TODO: возможно в будущем при появлении эвентов в игре, мы должны регать эвент изменения стата
---
--- пара примеров:
---
--- прибавляем 1 к стату: `behavior:changeChar("strength", function (charAmount) return 1 end)`
---
--- отнимаем от стата 50%: `behavior:changeChar("strength", function (charAmount) return -charAmount / 2 end)`
--- @param char Chars
--- @param fn fun(charAmount: integer): integer charAmount здесь это сырое значение характеристики
function behavior:changeChar(char, fn)
self.chars.final[char] = fn(self.chars.raw[char])
end
--- @param class? Class --- @param class? Class
--- @param rawChars? table<Chars, integer>
--- @param isInTurnOrder? boolean --- @param isInTurnOrder? boolean
function behavior.new(class, rawChars, isInTurnOrder) function behavior.new(hp, mana, initiative, class, isInTurnOrder)
--- @type Chars
local chars = { raw = {}, final = {} }
if not rawChars then
chars = {
raw = { strength = 10, stamina = 10, intelligence = 10, agility = 10, lunacy = 0 },
final = { strength = 10, stamina = 10, intelligence = 10, agility = 10, lunacy = 0 }
}
else
chars = {
raw = {
strength = rawChars.strength or 10,
stamina = rawChars.stamina or 10,
intelligence = rawChars.intelligence or 10,
agility = rawChars.agility or 10,
lunacy = rawChars.lunacy or 0,
},
final = {
strength = rawChars.strength or 10,
stamina = rawChars.stamina or 10,
intelligence = rawChars.intelligence or 10,
agility = rawChars.agility or 10,
lunacy = rawChars.lunacy or 0,
},
}
end
return setmetatable({ return setmetatable({
hp = chars.final["stamina"] * 2, hp = hp or 20,
mana = 10, -- я полагаю, у всех будет одинаковое кол-во маны (оно же кол-во действий) mana = mana or 10,
chars = chars, initiative = initiative or 10,
class = class or "dev_warrior", class = class or "dev_warrior",
isInTurnOrder = isInTurnOrder or true, isInTurnOrder = isInTurnOrder or true,
amIAlive = true amIAlive = true

View File

@ -1,7 +1,6 @@
local task = require "lib.utils.task" local task = require "lib.utils.task"
local effect = require "lib.spell.effect" local effect = require "lib.spell.effect"
local easing = require "lib.utils.easing" local easing = require "lib.utils.easing"
local impact = require "lib.utils.impact"
--- некое уникальное строковое значение --- некое уникальное строковое значение
--- @alias EffectTag string --- @alias EffectTag string
@ -30,8 +29,8 @@ end
function bleeding:beforeTurn(owner, intensity) function bleeding:beforeTurn(owner, intensity)
local stats = owner:has(Tree.behaviors.stats) local stats = owner:has(Tree.behaviors.stats)
local sprite = owner:has(Tree.behaviors.sprite) local sprite = owner:has(Tree.behaviors.sprite)
if not stats or not sprite then return task.fromValue(), true end if not stats or not sprite then return end
stats:dealDamage(intensity, "physic") stats:dealDamage(intensity)
return task.wait({ sprite:animate("hurt") }), true return task.wait({ sprite:animate("hurt") }), true
end end
@ -42,18 +41,18 @@ function bleeding:afterTurn(owner, intensity)
else else
behavior:deleteStacks("bleeding", 1) behavior:deleteStacks("bleeding", 1)
end end
return task.fromValue() return task.wait {}
end end
--- meow --- meow
function bleeding:afterCast(owner, intensity) function bleeding:afterCast(owner, intensity)
Tree.audio:play(Tree.assets.files.audio.sounds.meow) Tree.audio:play(Tree.assets.files.audio.sounds.meow)
return task.fromValue() return task.wait {}
end end
function bleeding:beforeCast(owner, intensity) function bleeding:beforeCast(owner, intensity)
Tree.audio:play(Tree.assets.files.audio.sounds.meow) Tree.audio:play(Tree.assets.files.audio.sounds.meow)
return task.fromValue(), true return task.wait {}, true
end end
--- Отвращение к смерти. --- Отвращение к смерти.
@ -63,46 +62,28 @@ local aversionToDeath = effect.new {
tag = "aversionToDeath" tag = "aversionToDeath"
} }
function aversionToDeath:afterCast(owner, intensity)
local stats = owner:has(Tree.behaviors.stats)
if not stats then return task.fromValue() end
stats:changeChar("agility", function(charAmount)
return 10
end)
return task.fromValue()
end
function aversionToDeath:beforeDamage(owner, intensity, damage) function aversionToDeath:beforeDamage(owner, intensity, damage)
local stats = owner:has(Tree.behaviors.stats) local stats = owner:has(Tree.behaviors.stats)
local effects = owner:has(Tree.behaviors.effects) local effects = owner:has(Tree.behaviors.effects)
if not stats or not effects then return task.fromValue(), damage end if not stats or not effects then return end
if stats.hp <= damage.intensity then if stats.hp <= damage then
effects:deleteStacks("aversionToDeath", 1) effects:deleteStacks("aversionToDeath", 1)
-- тут должен быть какой-нибудь классный спецэффект, но я не умею в шейдеры -- тут должен быть какой-нибудь классный спецэффект, но я не умею в шейдеры
return task.fromValue(), impact(stats.hp - 1, "magic") return task.wait({}), stats.hp - 1
end end
return task.fromValue(), damage return task.wait {}, damage
end end
function aversionToDeath:beforeTurn(owner, intensity) function aversionToDeath:beforeTurn(owner, intensity)
local sprite = owner:has(Tree.behaviors.sprite) local sprite = owner:has(Tree.behaviors.sprite)
if not sprite then if not sprite then
return task.fromValue(), false return task.wait {}, false
end end
return task.wait { return task.wait {
sprite:animate("hurt") sprite:animate("hurt")
}, false }, false
end end
function aversionToDeath:afterDelete(owner, intensity)
local stats = owner:has(Tree.behaviors.stats)
if not stats then return task.fromValue() end
stats:changeChar("agility", function(charAmount)
return -10
end)
return task.fromValue()
end
----------------- Effectbook & Sum ----------------- ----------------- Effectbook & Sum -----------------
--- @alias EffectSumFunc fun(owner: Character, effect1: EffectTag, effect2: EffectTag): boolean --- @alias EffectSumFunc fun(owner: Character, effect1: EffectTag, effect2: EffectTag): boolean

View File

@ -4,7 +4,7 @@ local easing = require "lib.utils.easing"
local initiativeComparator = function(id_a, id_b) local initiativeComparator = function(id_a, id_b)
local res = Tree.level.characters[id_a]:try(Tree.behaviors.stats, function(astats) local res = Tree.level.characters[id_a]:try(Tree.behaviors.stats, function(astats)
local res = Tree.level.characters[id_b]:try(Tree.behaviors.stats, function(bstats) local res = Tree.level.characters[id_b]:try(Tree.behaviors.stats, function(bstats)
return astats.chars.final["agility"] > bstats.chars.final["agility"] return astats.initiative > bstats.initiative
end) end)
return res return res
end) end)

View File

@ -14,89 +14,111 @@ local effect = {}
effect.__index = effect effect.__index = effect
--- Предполагается, что в каждую функцию будет передаваться `Character` (владелец эффекта) и параметр `intensity`, который отвечает за силу эффекта --- Предполагается, что в каждую функцию будет передаваться `Character` (владелец эффекта) и параметр `intensity`, который отвечает за силу эффекта
--- @alias EffectFunc fun(self: Effect, owner: Character, intensity: integer): Task<nil>, nil бред конечно, но иначе всё в жёлтом --- @alias EffectFunc fun(owner: Character, intensity: integer): Task<nil>, nil бред конечно, но иначе всё в жёлтом
--- @alias EffectStatementFunc fun(self: Effect, owner: Character, intensity: integer): Task<nil>, boolean --- @alias EffectStatementFunc fun(owner: Character, intensity: integer): Task<nil>, boolean
--- @alias EffectImpactFunc fun(self: Effect, owner: Character, intensity: integer, impact: Impact): Task<nil>, Impact --- @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 } --- @alias EffectData { tag: string }
--- Срабатывает перед применением эффекта --- Срабатывает перед применением эффекта
--- ---
--- Возвращает, а можно ли применить эффект? --- Возвращает, а можно ли применить эффект?
--- @type EffectStatementFunc --- @param owner Character
--- @param intensity integer
--- @return Task<nil>, boolean
function effect:beforeBirth(owner, intensity) return taskUtils.fromValue(), true end function effect:beforeBirth(owner, intensity) return taskUtils.fromValue(), true end
--- Срабатывает после применения эффекта --- Срабатывает после применения эффекта
--- @type EffectFunc --- @param owner Character
--- @param intensity integer
--- @return Task<nil>
function effect:afterBirth(owner, intensity) return taskUtils.fromValue() end function effect:afterBirth(owner, intensity) return taskUtils.fromValue() end
--- Срабатывает перед удалением эффекта
---
--- Возвращает, а можно ли удалить эффект?
--- @type EffectStatementFunc
function effect:beforeDelete(owner, intensity) return taskUtils.fromValue(), true end
--- Срабатывает после удаления эффекта
--- @type EffectFunc
function effect:afterDelete(owner, intensity) return taskUtils.fromValue() end
--- Срабатывает перед смертью владельца эффекта --- Срабатывает перед смертью владельца эффекта
--- ---
--- Возвращает, умирает ли персонаж? --- Возвращает, умирает ли персонаж?
--- @type EffectStatementFunc --- @param owner Character
--- @param intensity integer
--- @return Task<nil>, boolean
function effect:beforeDeath(owner, intensity) return taskUtils.fromValue(), true end function effect:beforeDeath(owner, intensity) return taskUtils.fromValue(), true end
--- Срабатывает после смерти владельца эффекта --- Срабатывает после смерти владельца эффекта
--- @type EffectFunc --- @param owner Character
--- @param intensity integer
--- @return Task<nil>
function effect:afterDeath(owner, intensity) return taskUtils.fromValue() end function effect:afterDeath(owner, intensity) return taskUtils.fromValue() end
--- Срабатывает перед ходом владельца эффекта --- Срабатывает перед ходом владельца эффекта
--- ---
--- Возвращает, будет ли персонаж ходить? --- Возвращает, будет ли персонаж ходить?
--- @type EffectStatementFunc --- @param owner Character
--- @param intensity integer
--- @return Task<nil>, boolean
function effect:beforeTurn(owner, intensity) return taskUtils.fromValue(), true end function effect:beforeTurn(owner, intensity) return taskUtils.fromValue(), true end
--- Срабатывает после хода владельца эффекта --- Срабатывает после хода владельца эффекта
--- @type EffectFunc --- @param owner Character
--- @param intensity integer
--- @return Task<nil>
function effect:afterTurn(owner, intensity) return taskUtils.fromValue() end function effect:afterTurn(owner, intensity) return taskUtils.fromValue() end
--- Срабатывает перед кастом заклинания владельцем эффекта --- Срабатывает перед кастом заклинания владельцем эффекта
--- ---
--- Возвращает, произойдёт ли каст? --- Возвращает, произойдёт ли каст?
--- @type EffectStatementFunc --- @param owner Character
--- @param intensity integer
--- @return Task<nil>, boolean
function effect:beforeCast(owner, intensity) return taskUtils.fromValue(), true end function effect:beforeCast(owner, intensity) return taskUtils.fromValue(), true end
--- Срабатывает после каста заклинания владельцем эффекта --- Срабатывает после каста заклинания владельцем эффекта
--- @type EffectFunc --- @param owner Character
--- @param intensity integer
--- @return Task<nil>
function effect:afterCast(owner, intensity) return taskUtils.fromValue() end function effect:afterCast(owner, intensity) return taskUtils.fromValue() end
--- Срабатывает перед нанесением урона владельцем эффекта --- Срабатывает перед нанесением урона владельцем эффекта
--- ---
--- Возвращает урон, который собираются нанести --- Возвращает урон, который собираются нанести
--- @type EffectImpactFunc --- @param owner Character
function effect:beforeAttack(owner, intensity, impact) return taskUtils.fromValue(), impact end --- @param intensity integer
--- @param damage integer
--- @return Task<nil>, integer
function effect:beforeAttack(owner, intensity, damage) return taskUtils.fromValue(), damage end
--- Срабатывает после нанесения урона владельцем эффекта --- Срабатывает после нанесения урона владельцем эффекта
--- @type EffectFunc --- @param owner Character
--- @param intensity integer
--- @return Task<nil>
function effect:afterAttack(owner, intensity) return taskUtils.fromValue() end function effect:afterAttack(owner, intensity) return taskUtils.fromValue() end
--- Срабатывает перед получением урона владельцем эффекта --- Срабатывает перед получением урона владельцем эффекта
--- ---
--- Возвращает урон, который должны получить --- Возвращает урон, который должны получить
--- @type EffectImpactFunc --- @param owner Character
function effect:beforeDamage(owner, intensity, impact) return taskUtils.fromValue(), impact end --- @param intensity integer
--- @param damage integer
--- @return Task<nil>, integer
function effect:beforeDamage(owner, intensity, damage) return taskUtils.fromValue(), damage end
--- Срабатывает после получения урона владельцем эффекта --- Срабатывает после получения урона владельцем эффекта
--- @type EffectFunc --- @param owner Character
--- @param intensity integer
--- @return Task<nil>
function effect:afterDamage(owner, intensity) return taskUtils.fromValue() end function effect:afterDamage(owner, intensity) return taskUtils.fromValue() end
--- Срабатывает перед регенерацией здоровья владельцем эффекта --- Срабатывает перед регенерацией здоровья владельцем эффекта
--- ---
--- Возвращает количество здоровья, которое должно быть восстановлено --- Возвращает количество здоровья, которое должно быть восстановлено
--- @type EffectImpactFunc --- @param owner Character
function effect:beforeRegeneration(owner, intensity, impact) return taskUtils.fromValue(), impact end --- @param intensity integer
--- @param amountHp integer кол-во хп для регена
--- @return Task<nil>, integer
function effect:beforeRegeneration(owner, intensity, amountHp) return taskUtils.fromValue(), amountHp end
--- Срабатывает после регенерации здоровья владельцем эффекта --- Срабатывает после регенерации здоровья владельцем эффекта
--- @type EffectFunc --- @param owner Character
--- @param intensity integer
--- @return Task<nil>
function effect:afterRegeneration(owner, intensity) return taskUtils.fromValue() end function effect:afterRegeneration(owner, intensity) return taskUtils.fromValue() end
--- Функция, что задаёт правила присвоения эффекта --- Функция, что задаёт правила присвоения эффекта

View File

@ -48,7 +48,7 @@ local regenerateMana = spell.new {
onCast = function(caster, target) onCast = function(caster, target)
caster:try(Tree.behaviors.stats, function(stats) caster:try(Tree.behaviors.stats, function(stats)
stats.mana = 10 stats.mana = 10
stats:healHealth(10, "magic") stats.initiative = stats.initiative + 10
end) end)
local sprite = caster:has(Tree.behaviors.sprite) local sprite = caster:has(Tree.behaviors.sprite)
@ -85,7 +85,7 @@ local attack = spell.new {
local targetCharacterId = Tree.level.characterGrid:get(target) local targetCharacterId = Tree.level.characterGrid:get(target)
local targetCharacter = Tree.level.characters[targetCharacterId] local targetCharacter = Tree.level.characters[targetCharacterId]
targetCharacter:try(Tree.behaviors.stats, function(stats) targetCharacter:try(Tree.behaviors.stats, function(stats)
stats:dealDamage(4, "physic") stats.hp = stats.hp - 4
end) end)
local targetEffects = targetCharacter:has(Tree.behaviors.effects) local targetEffects = targetCharacter:has(Tree.behaviors.effects)

View File

@ -1,31 +0,0 @@
--- @alias ImpactType "physic"|"magic"
--- Представляет из себя некое "взаимодействие", которое мы должны применить к персонажу в зависимости от контекста,
--- например, нанести урон.
--- @class Impact
--- @field intensity integer
--- @field impactType ImpactType
local impact = {}
impact.__index = impact
--- @param other Impact
function impact:__add(other)
assert(self.impactType == other.impactType, "Impact types not equals each other!")
local newImpact = Impact(self.intensity + other.intensity, self.impactType)
return newImpact
end
--- @param other Impact
function impact:__sub(other)
assert(self.impactType == other.impactType, "Impact types not equals each other!")
local newImpact = Impact(self.intensity - other.intensity, self.impactType)
return newImpact
end
--- @param intensity integer
--- @param impactType ImpactType
function Impact(intensity, impactType)
return setmetatable({ intensity = intensity, impactType = impactType }, impact)
end
return Impact

View File

@ -29,7 +29,7 @@ function love.load()
character.spawn("Foodor") character.spawn("Foodor")
:addBehavior { :addBehavior {
Tree.behaviors.residentsleeper.new(), Tree.behaviors.residentsleeper.new(),
Tree.behaviors.stats.new(nil, { agility = 1 }), Tree.behaviors.stats.new(nil, nil, 1),
Tree.behaviors.positioned.new(Vec3 { 3, 1 }), Tree.behaviors.positioned.new(Vec3 { 3, 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),
@ -40,7 +40,7 @@ function love.load()
character.spawn("Foodor") character.spawn("Foodor")
:addBehavior { :addBehavior {
Tree.behaviors.residentsleeper.new(), Tree.behaviors.residentsleeper.new(),
Tree.behaviors.stats.new(nil, { agility = 3 }), Tree.behaviors.stats.new(nil, nil, 3),
Tree.behaviors.positioned.new(Vec3 { 7, 2 }), Tree.behaviors.positioned.new(Vec3 { 7, 2 }),
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),