local anim8 = require "lib/anim8" require 'lib/vec3' --- Скорость между кадрами в анимации local ANIMATION_SPEED = 0.1 local characterId = 1 --- @todo Композиция лучше наследования, но не до такой же степени! Надо отрефакторить и избавиться от сотни полей в таблице --- @class Character --- @field id integer --- @field name string --- @field animationTable table --- @field state "idle"|"run"|"attack"|"hurt" --- @field stats table --- @field player table --- @field skills table --- @field class "warrior"|"mage" --- @field position Vec3 --- @field latestPosition Vec3 позиция, где character был один тик назад --- @field runTarget Vec3 точка, в которую в данный момент бежит персонаж --- @field size Vec3 local character = {} character.__index = character --- Создаёт персонажа, которым будет управлять или игрок или компьютер --- @param name string --- @param spriteDir table --- @param level? integer local function spawn(name, spriteDir, level) local animationGrid = {} -- n: name; i: image for n, i in pairs(spriteDir) do local aGrid = anim8.newGrid(96, 64, i:getWidth(), i:getHeight()) local tiles = '1-' .. math.ceil(i:getWidth() / 96) animationGrid[n] = aGrid(tiles, 1) end local char = { name = name, animationTable = {} } char.id = characterId characterId = characterId + 1 char.position = Vec3({}) char.size = Vec3({ 1, 1 }) char.state = "idle" char.animationTable.idle = anim8.newAnimation(animationGrid["idle"], ANIMATION_SPEED) char.animationTable.run = anim8.newAnimation(animationGrid["run"], ANIMATION_SPEED) char.animationTable.attack = anim8.newAnimation(animationGrid["attack"], ANIMATION_SPEED, function() char.state = "idle" end) char.animationTable.hurt = anim8.newAnimation(animationGrid["hurt"], ANIMATION_SPEED, function() char.state = "idle" end) char.stats = {} --- @todo придумать формулу расчёта статов относительно уровня char.stats.level = 1 char.stats.initiative = 10 char.stats.damage = 5 char.stats.defence = 0 char.stats.hp = 30 char = setmetatable(char, character) Tree.level.characters[char.id] = char Tree.level.positionGrid:add(char) return char end --- @param target Vec3 function character:runTo(target) self.state = "run" self.runTarget = target end function character:update(dt) if self.state == "run" and self.runTarget then if self.position:floor() == self.runTarget:floor() then -- мы добежали до цели и сейчас в целевой клетке self.state = "idle" self.runTarget = nil else -- мы не добежали до цели local vel = (self.runTarget:subtract(self.position):normalize() --[[@as Vec3]] ):scale(2 * dt) -- бежим 2 условных метра в секунду self.position = self.position:add(vel) end end if self.position ~= self.latestPosition then -- типа уведомление о том, что положение (на уровне клеток) изменилось Tree.level.positionGrid:remove(self) Tree.level.positionGrid:add(self) end if love.keyboard.isDown("r") then self.state = "run" end if love.keyboard.isDown("i") then self.state = "idle" end if love.keyboard.isDown("u") then self.state = "attack" end if love.keyboard.isDown("h") then self.state = "hurt" end self.animationTable[self.state]:update(dt) end function character:draw() local ppm = Tree.level.camera.pixelsPerMeter self.animationTable[self.state]:draw(Tree.assets.files.sprites.character[self.state], self.position.x, self.position.y, nil, 1 / ppm, 1 / ppm, 38, 47) end return { spawn = spawn }