feature/stats #39
@ -59,6 +59,10 @@ end
|
||||
--- Удаляет один эффект по порядку
|
||||
--- @param effect EffectTag
|
||||
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
|
||||
for i, ef in ipairs(self.effectsPriority) do
|
||||
if ef == effect then
|
||||
@ -66,19 +70,21 @@ function behavior:deleteEffect(effect)
|
||||
return
|
||||
end
|
||||
end
|
||||
local task2 = book[effect]:afterDelete(self.owner, self.effectsProperties[effect].intensity)
|
||||
task2(function() end)
|
||||
end
|
||||
|
||||
--- О ДААА ЭТА ФУНКЦИЯ МЕНЯЕТ СОСТОЯНИЕ О ДАААААА О ДАААААААААА
|
||||
--- @param effect EffectTag
|
||||
--- @param amount integer
|
||||
function behavior:deleteStacks(effect, amount)
|
||||
print("[Effects]: удаляем стаки!!")
|
||||
-- print("[Effects]: удаляем стаки!!")
|
||||
self.effectsProperties[effect].stacks = self.effectsProperties[effect].stacks -
|
||||
amount -- !!!!!!!!!!!!!!!! <<<<< 21+ only
|
||||
if self.effectsProperties[effect].stacks <= 0 then
|
||||
print("[Effects]:", effect, "ДОЛЖЕН БЫТЬ СТЁРТ")
|
||||
-- print("[Effects]:", effect, "ДОЛЖЕН БЫТЬ СТЁРТ")
|
||||
self:deleteEffect(effect)
|
||||
print("[Effects]:", effect, "СТЁРТ")
|
||||
-- print("[Effects]:", effect, "СТЁРТ")
|
||||
end
|
||||
end
|
||||
|
||||
@ -160,7 +166,8 @@ end
|
||||
--- должен вызываться перед получением урона
|
||||
---
|
||||
--- возвращает получаемый урон
|
||||
--- @return integer
|
||||
--- @param damage Impact
|
||||
--- @return Impact
|
||||
function behavior:beforeDamage(damage)
|
||||
local totalDamage = damage
|
||||
for i, ef in ipairs(self.effectsPriority) do
|
||||
@ -184,4 +191,32 @@ function behavior:afterDamage()
|
||||
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
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
local impact = require "lib.utils.impact"
|
||||
|
||||
--- @alias Class "dev_warrior"|"dev_mage"
|
||||
--- @alias Chars "strength"|"intelligence"|"agility"|"stamina"|"lunacy"
|
||||
--- @alias CharsTable table<Chars, integer>
|
||||
|
||||
--- @class StatsBehavior : Behavior
|
||||
--- @field hp integer
|
||||
--- @field mana integer
|
||||
--- @field initiative integer
|
||||
--- @field chars { raw: CharsTable, final: CharsTable } по хорошему, мы не должны менять эту таблицу руками, а делать это только через метод `changeChar`
|
||||
--- @field class Class
|
||||
--- @field isInTurnOrder boolean
|
||||
--- @field amIAlive boolean
|
||||
@ -19,24 +23,84 @@ function behavior:checkStats()
|
||||
end
|
||||
end
|
||||
|
||||
function behavior:maxHealth()
|
||||
return self.chars.final["stamina"] * 2
|
||||
end
|
||||
|
||||
--- @param damage integer
|
||||
function behavior:dealDamage(damage)
|
||||
--- @param impactType ImpactType
|
||||
function behavior:dealDamage(damage, impactType)
|
||||
local damageImpact = impact(damage, impactType)
|
||||
local effects = self.owner:has(Tree.behaviors.effects)
|
||||
if effects then damage = effects:beforeDamage(damage) end
|
||||
self.hp = self.hp - damage
|
||||
if effects then damageImpact = effects:beforeDamage(damageImpact) end
|
||||
self.hp = self.hp - damageImpact.intensity
|
||||
if effects then effects:afterDamage() end
|
||||
self:checkStats()
|
||||
end
|
||||
|
||||
--- @param hp? integer
|
||||
--- @param mana? integer
|
||||
--- @param initiative? integer
|
||||
--- @param amount integer
|
||||
--- @param impactType ImpactType
|
||||
function behavior:healHealth(amount, impactType)
|
||||
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 rawChars? table<Chars, integer>
|
||||
--- @param isInTurnOrder? boolean
|
||||
function behavior.new(hp, mana, initiative, class, isInTurnOrder)
|
||||
function behavior.new(class, rawChars, 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({
|
||||
hp = hp or 20,
|
||||
mana = mana or 10,
|
||||
initiative = initiative or 10,
|
||||
hp = chars.final["stamina"] * 2,
|
||||
mana = 10, -- я полагаю, у всех будет одинаковое кол-во маны (оно же кол-во действий)
|
||||
chars = chars,
|
||||
class = class or "dev_warrior",
|
||||
isInTurnOrder = isInTurnOrder or true,
|
||||
amIAlive = true
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
local task = require "lib.utils.task"
|
||||
local effect = require "lib.spell.effect"
|
||||
local easing = require "lib.utils.easing"
|
||||
local impact = require "lib.utils.impact"
|
||||
|
||||
--- некое уникальное строковое значение
|
||||
--- @alias EffectTag string
|
||||
@ -29,8 +30,8 @@ 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)
|
||||
if not stats or not sprite then return task.fromValue(), true end
|
||||
stats:dealDamage(intensity, "physic")
|
||||
return task.wait({ sprite:animate("hurt") }), true
|
||||
end
|
||||
|
||||
@ -41,18 +42,18 @@ function bleeding:afterTurn(owner, intensity)
|
||||
else
|
||||
behavior:deleteStacks("bleeding", 1)
|
||||
end
|
||||
return task.wait {}
|
||||
return task.fromValue()
|
||||
end
|
||||
|
||||
--- meow
|
||||
function bleeding:afterCast(owner, intensity)
|
||||
Tree.audio:play(Tree.assets.files.audio.sounds.meow)
|
||||
return task.wait {}
|
||||
return task.fromValue()
|
||||
end
|
||||
|
||||
function bleeding:beforeCast(owner, intensity)
|
||||
Tree.audio:play(Tree.assets.files.audio.sounds.meow)
|
||||
return task.wait {}, true
|
||||
return task.fromValue(), true
|
||||
end
|
||||
|
||||
--- Отвращение к смерти.
|
||||
@ -62,28 +63,46 @@ local aversionToDeath = effect.new {
|
||||
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)
|
||||
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
|
||||
if not stats or not effects then return task.fromValue(), damage end
|
||||
if stats.hp <= damage.intensity then
|
||||
effects:deleteStacks("aversionToDeath", 1)
|
||||
-- тут должен быть какой-нибудь классный спецэффект, но я не умею в шейдеры
|
||||
return task.wait({}), stats.hp - 1
|
||||
return task.fromValue(), impact(stats.hp - 1, "magic")
|
||||
end
|
||||
return task.wait {}, damage
|
||||
return task.fromValue(), damage
|
||||
end
|
||||
|
||||
function aversionToDeath:beforeTurn(owner, intensity)
|
||||
local sprite = owner:has(Tree.behaviors.sprite)
|
||||
if not sprite then
|
||||
return task.wait {}, false
|
||||
return task.fromValue(), false
|
||||
end
|
||||
return task.wait {
|
||||
sprite:animate("hurt")
|
||||
}, false
|
||||
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 -----------------
|
||||
|
||||
--- @alias EffectSumFunc fun(owner: Character, effect1: EffectTag, effect2: EffectTag): boolean
|
||||
|
||||
@ -4,7 +4,7 @@ local easing = require "lib.utils.easing"
|
||||
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_b]:try(Tree.behaviors.stats, function(bstats)
|
||||
return astats.initiative > bstats.initiative
|
||||
return astats.chars.final["agility"] > bstats.chars.final["agility"]
|
||||
end)
|
||||
return res
|
||||
end)
|
||||
|
||||
@ -14,111 +14,89 @@ 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 EffectFunc fun(self: Effect, owner: Character, intensity: integer): Task<nil>, nil бред конечно, но иначе всё в жёлтом
|
||||
--- @alias EffectStatementFunc fun(self: Effect, owner: Character, intensity: integer): Task<nil>, boolean
|
||||
--- @alias EffectImpactFunc fun(self: Effect, owner: Character, intensity: integer, impact: Impact): Task<nil>, Impact
|
||||
--- @alias EffectData { tag: string }
|
||||
|
||||
--- Срабатывает перед применением эффекта
|
||||
---
|
||||
--- Возвращает, а можно ли применить эффект?
|
||||
--- @param owner Character
|
||||
--- @param intensity integer
|
||||
--- @return Task<nil>, boolean
|
||||
--- @type EffectStatementFunc
|
||||
function effect:beforeBirth(owner, intensity) return taskUtils.fromValue(), true end
|
||||
|
||||
--- Срабатывает после применения эффекта
|
||||
--- @param owner Character
|
||||
--- @param intensity integer
|
||||
--- @return Task<nil>
|
||||
--- @type EffectFunc
|
||||
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
|
||||
|
||||
--- Срабатывает перед смертью владельца эффекта
|
||||
---
|
||||
--- Возвращает, умирает ли персонаж?
|
||||
--- @param owner Character
|
||||
--- @param intensity integer
|
||||
--- @return Task<nil>, boolean
|
||||
--- @type EffectStatementFunc
|
||||
function effect:beforeDeath(owner, intensity) return taskUtils.fromValue(), true end
|
||||
|
||||
--- Срабатывает после смерти владельца эффекта
|
||||
--- @param owner Character
|
||||
--- @param intensity integer
|
||||
--- @return Task<nil>
|
||||
--- @type EffectFunc
|
||||
function effect:afterDeath(owner, intensity) return taskUtils.fromValue() end
|
||||
|
||||
--- Срабатывает перед ходом владельца эффекта
|
||||
---
|
||||
--- Возвращает, будет ли персонаж ходить?
|
||||
--- @param owner Character
|
||||
--- @param intensity integer
|
||||
--- @return Task<nil>, boolean
|
||||
--- @type EffectStatementFunc
|
||||
function effect:beforeTurn(owner, intensity) return taskUtils.fromValue(), true end
|
||||
|
||||
--- Срабатывает после хода владельца эффекта
|
||||
--- @param owner Character
|
||||
--- @param intensity integer
|
||||
--- @return Task<nil>
|
||||
--- @type EffectFunc
|
||||
function effect:afterTurn(owner, intensity) return taskUtils.fromValue() end
|
||||
|
||||
--- Срабатывает перед кастом заклинания владельцем эффекта
|
||||
---
|
||||
--- Возвращает, произойдёт ли каст?
|
||||
--- @param owner Character
|
||||
--- @param intensity integer
|
||||
--- @return Task<nil>, boolean
|
||||
--- @type EffectStatementFunc
|
||||
function effect:beforeCast(owner, intensity) return taskUtils.fromValue(), true end
|
||||
|
||||
--- Срабатывает после каста заклинания владельцем эффекта
|
||||
--- @param owner Character
|
||||
--- @param intensity integer
|
||||
--- @return Task<nil>
|
||||
--- @type EffectFunc
|
||||
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
|
||||
--- @type EffectImpactFunc
|
||||
function effect:beforeAttack(owner, intensity, impact) return taskUtils.fromValue(), impact end
|
||||
|
||||
--- Срабатывает после нанесения урона владельцем эффекта
|
||||
--- @param owner Character
|
||||
--- @param intensity integer
|
||||
--- @return Task<nil>
|
||||
--- @type EffectFunc
|
||||
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
|
||||
--- @type EffectImpactFunc
|
||||
function effect:beforeDamage(owner, intensity, impact) return taskUtils.fromValue(), impact end
|
||||
|
||||
--- Срабатывает после получения урона владельцем эффекта
|
||||
--- @param owner Character
|
||||
--- @param intensity integer
|
||||
--- @return Task<nil>
|
||||
--- @type EffectFunc
|
||||
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
|
||||
--- @type EffectImpactFunc
|
||||
function effect:beforeRegeneration(owner, intensity, impact) return taskUtils.fromValue(), impact end
|
||||
|
||||
--- Срабатывает после регенерации здоровья владельцем эффекта
|
||||
--- @param owner Character
|
||||
--- @param intensity integer
|
||||
--- @return Task<nil>
|
||||
--- @type EffectFunc
|
||||
function effect:afterRegeneration(owner, intensity) return taskUtils.fromValue() end
|
||||
|
||||
--- Функция, что задаёт правила присвоения эффекта
|
||||
|
||||
@ -48,7 +48,7 @@ local regenerateMana = spell.new {
|
||||
onCast = function(caster, target)
|
||||
caster:try(Tree.behaviors.stats, function(stats)
|
||||
stats.mana = 10
|
||||
stats.initiative = stats.initiative + 10
|
||||
stats:healHealth(10, "magic")
|
||||
end)
|
||||
|
||||
local sprite = caster:has(Tree.behaviors.sprite)
|
||||
@ -85,7 +85,7 @@ local attack = spell.new {
|
||||
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
|
||||
stats:dealDamage(4, "physic")
|
||||
end)
|
||||
|
||||
local targetEffects = targetCharacter:has(Tree.behaviors.effects)
|
||||
|
||||
31
lib/utils/impact.lua
Normal file
31
lib/utils/impact.lua
Normal file
@ -0,0 +1,31 @@
|
||||
--- @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
|
||||
4
main.lua
4
main.lua
@ -29,7 +29,7 @@ function love.load()
|
||||
character.spawn("Foodor")
|
||||
:addBehavior {
|
||||
Tree.behaviors.residentsleeper.new(),
|
||||
Tree.behaviors.stats.new(nil, nil, 1),
|
||||
Tree.behaviors.stats.new(nil, { agility = 1 }),
|
||||
Tree.behaviors.positioned.new(Vec3 { 3, 1 }),
|
||||
Tree.behaviors.tiled.new(),
|
||||
Tree.behaviors.sprite.new(Tree.assets.files.sprites.character),
|
||||
@ -40,7 +40,7 @@ function love.load()
|
||||
character.spawn("Foodor")
|
||||
:addBehavior {
|
||||
Tree.behaviors.residentsleeper.new(),
|
||||
Tree.behaviors.stats.new(nil, nil, 3),
|
||||
Tree.behaviors.stats.new(nil, { agility = 3 }),
|
||||
Tree.behaviors.positioned.new(Vec3 { 7, 2 }),
|
||||
Tree.behaviors.tiled.new(),
|
||||
Tree.behaviors.sprite.new(Tree.assets.files.sprites.character),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user