require 'lib.utils.vec3' --- @alias CharacterState "idle"|"run"|"attack"|"hurt" --- @alias Id integer --- @type Id local characterId = 1 --- @todo Композиция лучше наследования, но не до такой же степени! Надо отрефакторить и избавиться от сотни полей в таблице --- @class Character --- @field id Id --- @field behaviors Behavior[] --- @field _behaviorsIdx {string: integer} --- @field spellbook Spell[] собственный набор спеллов персонажа --- @field cast Spell | nil ссылка на активный спелл из спеллбука local character = {} character.__index = character --- Создаёт персонажа, которым будет управлять или игрок или компьютер --- @param name string --- @param template ClassTemplate --- @param spriteDir table --- @param position? Vec3 --- @param size? Vec3 --- @param level? integer local function spawn(name, template, spriteDir, position, size, level) local char = {} char = setmetatable(char, character) char.id = characterId characterId = characterId + 1 char.behaviors = {} char._behaviorsIdx = {} char:addBehavior { Tree.behaviors.map.new(position, size), Tree.behaviors.render.new(spriteDir), Tree.behaviors.spellcaster.new() } char:setState("idle") --- @todo сделать это отдельным модулем Tree.level.characters[char.id] = char return char end --- Проверяет, есть ли у персонажа поведение [behavior]. --- @generic T : Behavior --- @param behavior T --- @return T | nil function character:has(behavior) --- @cast behavior Behavior local idx = self._behaviorsIdx[behavior.id] if not idx then return nil end return self.behaviors[idx] or nil end --- Если у персонажа есть поведение [behavior], применяет к нему [fn] --- --- Дальше meme для интеллектуалов --- --- *Я: мам купи мне >>=* --- --- *Мама: у нас дома есть >>=* --- --- *Дома:* --- @generic T : Behavior --- @generic V --- @param behavior T --- @param fn fun(behavior: T) : V | nil --- @return V | nil function character:try(behavior, fn) local b = self:has(behavior) if not b then return end return fn(b) end --- usage: --- addModules( {logic.new(), graphics.new(), ...} ) --- --- or you may chain this if you are a wannabe haskell kiddo function character:addBehavior(modules) for _, module in ipairs(modules) do if module.dependencies then for _, dep in ipairs(module.dependencies) do if not self:has(dep) then return print("[Character]: cannot add \"" .. module.id .. "\" for a character (Id = " .. self.id .. "): needs \"" .. dep.id .. "\"!") end end end module.owner = self table.insert(self.behaviors, module) self._behaviorsIdx[module.id] = #self.behaviors end return self end --- геттеры и сеттеры для "внешних" данных --- забей, это в поведения --- @deprecated --- @return CharacterState function character:getState() return self.state or "idle" end --- @param state CharacterState --- @deprecated function character:setState(state) --- @todo это вообще должно быть отдельное поведение self.state = state self:has(Tree.behaviors.render).animation:setState(state, (state ~= "idle" and state ~= "run") and function() self:setState("idle") end or nil) end --- @param path Deque function character:followPath(path) self:has(Tree.behaviors.map):followPath(path) end function character:update(dt) --- @todo ну ты понел for _, b in ipairs(self.behaviors) do if b.update then b:update(dt) end end end function character:draw() for _, b in ipairs(self.behaviors) do if b.draw then b:draw() end end end return { spawn = spawn }