add PositionedBehavior
This commit is contained in:
parent
4aa470f443
commit
7ff7e47a90
@ -1,8 +1,10 @@
|
||||
--- @meta _
|
||||
Tree.behaviors.map = require "lib.character.behaviors.map"
|
||||
Tree.behaviors.spellcaster = require "lib.character.behaviors.spellcaster"
|
||||
Tree.behaviors.sprite = require "lib.character.behaviors.sprite"
|
||||
Tree.behaviors.stats = require "lib.character.behaviors.stats"
|
||||
Tree.behaviors.residentsleeper = require "lib.character.behaviors.residentsleeper"
|
||||
Tree.behaviors.shadowcaster = require "lib.character.behaviors.shadowcaster"
|
||||
Tree.behaviors.light = require "character.behaviors.light"
|
||||
Tree.behaviors.positioned = require "character.behaviors.positioned"
|
||||
Tree.behaviors.tiled = require "character.behaviors.tiled"
|
||||
Tree.behaviors.cursor = require "character.behaviors.cursor"
|
||||
|
||||
19
lib/character/behaviors/cursor.lua
Normal file
19
lib/character/behaviors/cursor.lua
Normal file
@ -0,0 +1,19 @@
|
||||
--- Добавляет следование за курсором мыши
|
||||
--- @class CursorBehavior : Behavior
|
||||
local behavior = {}
|
||||
behavior.__index = behavior
|
||||
behavior.id = "cursor"
|
||||
|
||||
---@return CursorBehavior
|
||||
function behavior.new()
|
||||
return setmetatable({}, behavior)
|
||||
end
|
||||
|
||||
function behavior:update()
|
||||
self.owner:try(Tree.behaviors.positioned, function(b)
|
||||
local mx, my = love.mouse.getX(), love.mouse.getY()
|
||||
b.position = Tree.level.camera:toWorldPosition(Vec3 { mx, my })
|
||||
end)
|
||||
end
|
||||
|
||||
return behavior
|
||||
@ -1,38 +1,36 @@
|
||||
--- @class LightBehavior : Behavior
|
||||
--- @field intensity number
|
||||
--- @field color Vec3
|
||||
--- @field position Vec3
|
||||
--- @field seed integer
|
||||
local behavior = {}
|
||||
behavior.__index = behavior
|
||||
behavior.id = "light"
|
||||
|
||||
---@param values {intensity: number?, color: Vec3?, position: Vec3?, seed: integer?}
|
||||
---@param values {intensity: number?, color: Vec3?, seed: integer?}
|
||||
---@return LightBehavior
|
||||
function behavior.new(values)
|
||||
return setmetatable({
|
||||
intensity = values.intensity or 1,
|
||||
color = values.color or Vec3 { 1, 1, 1 },
|
||||
position = values.position or Vec3 {},
|
||||
seed = values.seed or math.random(math.pow(2, 16))
|
||||
}, behavior)
|
||||
end
|
||||
|
||||
function behavior:update()
|
||||
local mx, my = love.mouse.getX(), love.mouse.getY()
|
||||
self.position = Tree.level.camera:toWorldPosition(Vec3 { mx, my })
|
||||
end
|
||||
|
||||
function behavior:draw()
|
||||
local positioned = self.owner:has(Tree.behaviors.positioned)
|
||||
if not positioned then return end
|
||||
|
||||
Tree.level.camera:attach()
|
||||
love.graphics.setCanvas(Tree.level.render.textures.lightLayer)
|
||||
local shader = Tree.assets.files.shaders.light
|
||||
shader:send("color", { self.color.x, self.color.y, self.color.z })
|
||||
shader:send("time", love.timer.getTime() + self.seed)
|
||||
love.graphics.setShader(shader)
|
||||
-- love.graphics.setBlendMode("add")
|
||||
love.graphics.draw(Tree.assets.files.masks.circle128, self.position.x - self.intensity / 2,
|
||||
self.position.y - self.intensity / 2, 0, self.intensity / 128,
|
||||
love.graphics.draw(Tree.assets.files.masks.circle128, positioned.position.x - self.intensity / 2,
|
||||
positioned.position.y - self.intensity / 2, 0, self.intensity / 128,
|
||||
self.intensity / 128)
|
||||
|
||||
love.graphics.setBlendMode("alpha")
|
||||
|
||||
@ -1,91 +0,0 @@
|
||||
local utils = require "lib.utils.utils"
|
||||
|
||||
--- Отвечает за размещение и перемещение по локации
|
||||
--- @class MapBehavior : Behavior
|
||||
--- @field position Vec3
|
||||
--- @field runTarget Vec3 точка, в которую в данный момент бежит персонаж
|
||||
--- @field displayedPosition Vec3 точка, в которой персонаж отображается
|
||||
--- @field t0 number время начала движения для анимациии
|
||||
--- @field path Deque путь, по которому сейчас бежит персонаж
|
||||
--- @field animationNode? AnimationNode AnimationNode, с которым связана анимация перемещения
|
||||
--- @field size Vec3
|
||||
local mapBehavior = {}
|
||||
mapBehavior.__index = mapBehavior
|
||||
mapBehavior.id = "map"
|
||||
|
||||
|
||||
--- @param position? Vec3
|
||||
--- @param size? Vec3
|
||||
function mapBehavior.new(position, size)
|
||||
return setmetatable({
|
||||
position = position or Vec3({}),
|
||||
displayedPosition = position or Vec3({}),
|
||||
size = size or Vec3({ 1, 1 }),
|
||||
}, mapBehavior)
|
||||
end
|
||||
|
||||
--- @param position Vec3
|
||||
function mapBehavior:lookAt(position)
|
||||
self.owner:try(Tree.behaviors.sprite,
|
||||
function(sprite)
|
||||
if position.x > self.displayedPosition.x then sprite.side = sprite.RIGHT end
|
||||
-- (sic!)
|
||||
if position.x < self.displayedPosition.x then sprite.side = sprite.LEFT end
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
--- @param path Deque
|
||||
--- @param animationNode AnimationNode
|
||||
function mapBehavior:followPath(path, animationNode)
|
||||
if path:is_empty() then return animationNode:finish() end
|
||||
self.animationNode = animationNode
|
||||
self.position = self.displayedPosition
|
||||
self.owner:try(Tree.behaviors.sprite, function(sprite)
|
||||
sprite:loop("run")
|
||||
end)
|
||||
self.path = path;
|
||||
---@type Vec3
|
||||
local nextCell = path:peek_front()
|
||||
self:runTo(nextCell)
|
||||
path:pop_front()
|
||||
end
|
||||
|
||||
--- @param target Vec3
|
||||
function mapBehavior:runTo(target)
|
||||
self.t0 = love.timer.getTime()
|
||||
self.runTarget = target
|
||||
self.owner:try(Tree.behaviors.sprite,
|
||||
function(sprite)
|
||||
if target.x < self.position.x then
|
||||
sprite.side = Tree.behaviors.sprite.LEFT
|
||||
elseif target.x > self.position.x then
|
||||
sprite.side = Tree.behaviors.sprite.RIGHT
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
function mapBehavior:update(dt)
|
||||
if self.runTarget then
|
||||
local delta = love.timer.getTime() - self.t0 or love.timer.getTime()
|
||||
local fraction = delta /
|
||||
(0.5 * self.runTarget:subtract(self.position):length()) -- бежим одну клетку за 500 мс, по диагонали больше
|
||||
if fraction >= 1 then -- анимация перемещена завершена
|
||||
self.position = self.runTarget
|
||||
if not self.path:is_empty() then -- еще есть, куда бежать
|
||||
self:runTo(self.path:pop_front())
|
||||
else -- мы добежали до финальной цели
|
||||
self.owner:try(Tree.behaviors.sprite, function(sprite)
|
||||
sprite:loop("idle")
|
||||
end)
|
||||
self.runTarget = nil
|
||||
if self.animationNode then self.animationNode:finish() end
|
||||
end
|
||||
else -- анимация перемещения не завершена
|
||||
self.displayedPosition = utils.lerp(self.position, self.runTarget, fraction) -- линейный интерполятор
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return mapBehavior
|
||||
25
lib/character/behaviors/positioned.lua
Normal file
25
lib/character/behaviors/positioned.lua
Normal file
@ -0,0 +1,25 @@
|
||||
--- Отвечает за размещение на уровне
|
||||
--- @class PositionedBehavior : Behavior
|
||||
--- @field position Vec3
|
||||
local behavior = {}
|
||||
behavior.__index = behavior
|
||||
behavior.id = "positioned"
|
||||
|
||||
--- @param position? Vec3
|
||||
function behavior.new(position)
|
||||
return setmetatable({
|
||||
position = position or Vec3({}),
|
||||
}, behavior)
|
||||
end
|
||||
|
||||
--- @param position Vec3
|
||||
function behavior:lookAt(position)
|
||||
self.owner:try(Tree.behaviors.sprite,
|
||||
function(sprite)
|
||||
if position.x > self.position.x then sprite.side = sprite.RIGHT end
|
||||
if position.x < self.position.x then sprite.side = sprite.LEFT end
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
return behavior
|
||||
@ -8,11 +8,11 @@ function behavior.new() return setmetatable({}, behavior) end
|
||||
|
||||
function behavior:draw()
|
||||
local sprite = self.owner:has(Tree.behaviors.sprite)
|
||||
local map = self.owner:has(Tree.behaviors.map)
|
||||
if not map then return end
|
||||
local positioned = self.owner:has(Tree.behaviors.positioned)
|
||||
if not positioned then return end
|
||||
|
||||
local ppm = Tree.level.camera.pixelsPerMeter
|
||||
local position = map.displayedPosition + Vec3 { 0.5, 0.5 }
|
||||
local position = positioned.position + Vec3 { 0.5, 0.5 }
|
||||
|
||||
local lightIds = Tree.level.lightGrid:query(position, 5)
|
||||
--- @type Character[]
|
||||
@ -37,7 +37,7 @@ function behavior:draw()
|
||||
love.graphics.setCanvas(Tree.level.render.textures.spriteLightLayer)
|
||||
love.graphics.setBlendMode("add")
|
||||
for _, light in ipairs(lights) do
|
||||
local lightPos = light:has(Tree.behaviors.light).position
|
||||
local lightPos = light:has(Tree.behaviors.positioned).position
|
||||
local lightVec = lightPos - position
|
||||
|
||||
local lightColor = light:has(Tree.behaviors.light).color
|
||||
|
||||
@ -41,10 +41,10 @@ end
|
||||
function sprite:draw()
|
||||
if not self.animationTable[self.state] or not Tree.assets.files.sprites.character[self.state] then return end
|
||||
|
||||
self.owner:try(Tree.behaviors.map,
|
||||
function(map)
|
||||
self.owner:try(Tree.behaviors.positioned,
|
||||
function(pos)
|
||||
local ppm = Tree.level.camera.pixelsPerMeter
|
||||
local position = map.displayedPosition + Vec3 { 0.5, 0.5 }
|
||||
local position = pos.position + Vec3 { 0.5, 0.5 }
|
||||
|
||||
love.graphics.setCanvas(Tree.level.render.textures.spriteLayer)
|
||||
Tree.level.camera:attach()
|
||||
|
||||
83
lib/character/behaviors/tiled.lua
Normal file
83
lib/character/behaviors/tiled.lua
Normal file
@ -0,0 +1,83 @@
|
||||
local utils = require "lib.utils.utils"
|
||||
|
||||
--- Отвечает за перемещение по тайлам
|
||||
--- @class TiledBehavior : Behavior
|
||||
--- @field private runSource? Vec3 точка, из которой бежит персонаж
|
||||
--- @field private runTarget? Vec3 точка, в которую в данный момент бежит персонаж
|
||||
--- @field private path? Deque путь, по которому сейчас бежит персонаж
|
||||
--- @field private animationNode? AnimationNode AnimationNode, с которым связана анимация перемещения
|
||||
--- @field private t0 number время начала движения
|
||||
--- @field size Vec3
|
||||
local behavior = {}
|
||||
behavior.__index = behavior
|
||||
behavior.id = "tiled"
|
||||
|
||||
--- @param size? Vec3
|
||||
function behavior.new(size)
|
||||
return setmetatable({
|
||||
size = size or Vec3({ 1, 1 }),
|
||||
}, behavior)
|
||||
end
|
||||
|
||||
--- @param path Deque
|
||||
--- @param animationNode AnimationNode
|
||||
function behavior:followPath(path, animationNode)
|
||||
if path:is_empty() then return animationNode:finish() end
|
||||
self.animationNode = animationNode
|
||||
self.owner:try(Tree.behaviors.sprite, function(sprite)
|
||||
sprite:loop("run")
|
||||
end)
|
||||
self.path = path;
|
||||
---@type Vec3
|
||||
local nextCell = path:peek_front()
|
||||
self:runTo(nextCell)
|
||||
path:pop_front()
|
||||
end
|
||||
|
||||
--- @param target Vec3
|
||||
function behavior:runTo(target)
|
||||
local positioned = self.owner:has(Tree.behaviors.positioned)
|
||||
if not positioned then return end
|
||||
|
||||
self.t0 = love.timer.getTime()
|
||||
self.runTarget = target
|
||||
|
||||
self.runSource = positioned.position
|
||||
|
||||
self.owner:try(Tree.behaviors.sprite,
|
||||
function(sprite)
|
||||
if target.x < positioned.position.x then
|
||||
sprite.side = Tree.behaviors.sprite.LEFT
|
||||
elseif target.x > positioned.position.x then
|
||||
sprite.side = Tree.behaviors.sprite.RIGHT
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
function behavior:update(dt)
|
||||
if self.runTarget then
|
||||
local positioned = self.owner:has(Tree.behaviors.positioned)
|
||||
if not positioned then return end
|
||||
|
||||
local delta = love.timer.getTime() - self.t0 or love.timer.getTime()
|
||||
local fraction = delta /
|
||||
(0.5 * self.runTarget:subtract(self.runSource):length()) -- бежим одну клетку за 500 мс, по диагонали больше
|
||||
if fraction >= 1 then -- анимация перемещена завершена
|
||||
positioned.position = self.runTarget
|
||||
if not self.path:is_empty() then -- еще есть, куда бежать
|
||||
self:runTo(self.path:pop_front())
|
||||
else -- мы добежали до финальной цели
|
||||
self.owner:try(Tree.behaviors.sprite, function(sprite)
|
||||
sprite:loop("idle")
|
||||
end)
|
||||
self.runTarget = nil
|
||||
if self.animationNode then self.animationNode:finish() end
|
||||
end
|
||||
else -- анимация перемещения не завершена
|
||||
positioned.position = utils.lerp(self.runSource, self.runTarget, fraction) -- линейный интерполятор
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return behavior
|
||||
@ -12,12 +12,15 @@ function grid:add(id)
|
||||
local character = Tree.level.characters[id]
|
||||
if not character then return end
|
||||
|
||||
local mapB = character:has(Tree.behaviors.map)
|
||||
if not mapB then return end
|
||||
local positioned = character:has(Tree.behaviors.positioned)
|
||||
if not positioned then return end
|
||||
|
||||
local centerX, centerY = math.floor(mapB.displayedPosition.x + 0.5),
|
||||
math.floor(mapB.displayedPosition.y + 0.5)
|
||||
local sizeX, sizeY = mapB.size.x, mapB.size.y
|
||||
local tiled = character:has(Tree.behaviors.tiled)
|
||||
if not tiled then return end
|
||||
|
||||
local centerX, centerY = math.floor(positioned.position.x + 0.5),
|
||||
math.floor(positioned.position.y + 0.5)
|
||||
local sizeX, sizeY = tiled.size.x, tiled.size.y
|
||||
|
||||
for y = centerY, centerY + sizeY - 1 do
|
||||
for x = centerX, centerX + sizeX - 1 do
|
||||
@ -30,11 +33,7 @@ end
|
||||
--- @param b Character
|
||||
local function drawCmp(a, b)
|
||||
--- @TODO: это захардкожено, надо разделить поведения
|
||||
return (a:has(Tree.behaviors.map) and a:has(Tree.behaviors.map).displayedPosition.y or
|
||||
a:has(Tree.behaviors.light).position.y)
|
||||
<
|
||||
(b:has(Tree.behaviors.map) and b:has(Tree.behaviors.map).displayedPosition.y or
|
||||
b:has(Tree.behaviors.light).position.y)
|
||||
return a:has(Tree.behaviors.positioned).position.y < b:has(Tree.behaviors.positioned).position.y
|
||||
end
|
||||
|
||||
--- fills the grid with the actual data
|
||||
|
||||
@ -15,9 +15,10 @@ function grid:add(id)
|
||||
local lightB = character:has(Tree.behaviors.light)
|
||||
if not lightB then return end
|
||||
|
||||
local positioned = character:has(Tree.behaviors.positioned)
|
||||
if not positioned then return end
|
||||
|
||||
|
||||
local key = tostring(Vec3 { lightB.position.x, lightB.position.y }:floor())
|
||||
local key = tostring(Vec3 { positioned.position.x, positioned.position.y }:floor())
|
||||
if not self.__grid[key] then self.__grid[key] = {} end
|
||||
table.insert(self.__grid[key], character.id)
|
||||
end
|
||||
|
||||
@ -49,11 +49,11 @@ function endTurnButton:onClick()
|
||||
Tree.level.selector:select(nil)
|
||||
local cid = Tree.level.turnOrder.current
|
||||
local playing = Tree.level.characters[cid]
|
||||
if not playing:has(Tree.behaviors.map) then return end
|
||||
if not playing:has(Tree.behaviors.positioned) then return end
|
||||
|
||||
AnimationNode {
|
||||
function(node)
|
||||
Tree.level.camera:animateTo(playing:has(Tree.behaviors.map).displayedPosition, node)
|
||||
Tree.level.camera:animateTo(playing:has(Tree.behaviors.positioned).position, node)
|
||||
end,
|
||||
duration = 1500,
|
||||
easing = easing.easeInOutCubic,
|
||||
|
||||
@ -51,7 +51,7 @@ function walk:cast(caster, target)
|
||||
local sprite = caster:has(Tree.behaviors.sprite)
|
||||
if not sprite then return true end
|
||||
AnimationNode {
|
||||
function(node) caster:has(Tree.behaviors.map):followPath(path, node) end,
|
||||
function(node) caster:has(Tree.behaviors.tiled):followPath(path, node) end,
|
||||
onEnd = function() caster:has(Tree.behaviors.spellcaster):endCast() end,
|
||||
}:run()
|
||||
|
||||
@ -59,7 +59,7 @@ function walk:cast(caster, target)
|
||||
end
|
||||
|
||||
function walk:update(caster, dt)
|
||||
local charPos = caster:has(Tree.behaviors.map).position:floor()
|
||||
local charPos = caster:has(Tree.behaviors.positioned).position:floor()
|
||||
--- @type Vec3
|
||||
local mpos = Tree.level.camera:toWorldPosition(Vec3 { love.mouse.getX(), love.mouse.getY() }):floor()
|
||||
self.path = require "lib.pathfinder" (charPos, mpos)
|
||||
@ -104,8 +104,8 @@ local attack = setmetatable({}, spell)
|
||||
attack.tag = "dev_attack"
|
||||
|
||||
function attack:cast(caster, target)
|
||||
if caster:try(Tree.behaviors.map, function(map)
|
||||
local dist = math.max(math.abs(map.position.x - target.x), math.abs(map.position.y - target.y))
|
||||
if caster:try(Tree.behaviors.positioned, function(p)
|
||||
local dist = math.max(math.abs(p.position.x - target.x), math.abs(p.position.y - target.y))
|
||||
print("dist:", dist)
|
||||
return dist > 2
|
||||
end) then
|
||||
@ -128,7 +128,7 @@ function attack:cast(caster, target)
|
||||
local targetSprite = targetCharacter:has(Tree.behaviors.sprite)
|
||||
if not sprite or not targetSprite then return true end
|
||||
|
||||
caster:try(Tree.behaviors.map, function(map) map:lookAt(target) end)
|
||||
caster:try(Tree.behaviors.positioned, function(b) b:lookAt(target) end)
|
||||
|
||||
AnimationNode {
|
||||
onEnd = function() caster:has(Tree.behaviors.spellcaster):endCast() end,
|
||||
|
||||
35
main.lua
35
main.lua
@ -17,7 +17,8 @@ function love.load()
|
||||
:addBehavior {
|
||||
Tree.behaviors.residentsleeper.new(),
|
||||
Tree.behaviors.stats.new(nil, nil, 1),
|
||||
Tree.behaviors.map.new(),
|
||||
Tree.behaviors.positioned.new(Vec3 { 3, 3 }),
|
||||
Tree.behaviors.tiled.new(),
|
||||
Tree.behaviors.sprite.new(Tree.assets.files.sprites.character),
|
||||
Tree.behaviors.shadowcaster.new(),
|
||||
Tree.behaviors.spellcaster.new()
|
||||
@ -25,26 +26,9 @@ function love.load()
|
||||
character.spawn("Baris")
|
||||
:addBehavior {
|
||||
Tree.behaviors.residentsleeper.new(),
|
||||
Tree.behaviors.stats.new(nil, nil, 2),
|
||||
Tree.behaviors.map.new(Vec3 { 3, 3 }),
|
||||
Tree.behaviors.sprite.new(Tree.assets.files.sprites.character),
|
||||
Tree.behaviors.shadowcaster.new(),
|
||||
Tree.behaviors.spellcaster.new()
|
||||
},
|
||||
character.spawn("Foodor Jr")
|
||||
:addBehavior {
|
||||
Tree.behaviors.residentsleeper.new(),
|
||||
Tree.behaviors.stats.new(nil, nil, 3),
|
||||
Tree.behaviors.map.new(Vec3 { 0, 3 }),
|
||||
Tree.behaviors.sprite.new(Tree.assets.files.sprites.character),
|
||||
Tree.behaviors.shadowcaster.new(),
|
||||
Tree.behaviors.spellcaster.new()
|
||||
},
|
||||
character.spawn("Baris Jr")
|
||||
:addBehavior {
|
||||
Tree.behaviors.residentsleeper.new(),
|
||||
Tree.behaviors.stats.new(nil, nil, 4),
|
||||
Tree.behaviors.map.new(Vec3 { 0, 6 }),
|
||||
Tree.behaviors.stats.new(nil, nil, 1),
|
||||
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()
|
||||
@ -55,15 +39,12 @@ function love.load()
|
||||
Tree.level.turnOrder:add(id)
|
||||
end
|
||||
|
||||
for i = 1, 1, 1 do
|
||||
for j = 1, 1, 1 do
|
||||
character.spawn("My Light")
|
||||
:addBehavior {
|
||||
Tree.behaviors.light.new { color = Vec3 { 88, 34, 13 }, position = Vec3 { i, j } * 3, intensity = 10 }
|
||||
Tree.behaviors.light.new { color = Vec3 { 88, 34, 13 }, intensity = 10 },
|
||||
Tree.behaviors.positioned.new(),
|
||||
Tree.behaviors.cursor.new()
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Tree.level.turnOrder:endRound()
|
||||
print("Now playing:", Tree.level.turnOrder.current)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user