add animated bolts
This commit is contained in:
parent
a445461075
commit
afeb219786
@ -1,3 +1,86 @@
|
||||
local cargo = require("lib.cargo")
|
||||
local aseLoader = require("lib.ase-loader")
|
||||
local pprint = require("lib.pprint")
|
||||
|
||||
return cargo.init{ dir = "data" }
|
||||
local function loadAsepriteSprite(filename)
|
||||
local sprite = {}
|
||||
local ase = aseLoader(filename)
|
||||
|
||||
sprite.width = ase.header.width
|
||||
sprite.height = ase.header.height
|
||||
sprite.variants = {}
|
||||
|
||||
local LAYER_CHUNK = 0x2004
|
||||
local CEL_CHUNK = 0x2005
|
||||
local TAG_CHUNK = 0x2018
|
||||
|
||||
local frames = {}
|
||||
local tags = {}
|
||||
local layers = {}
|
||||
local first_tag
|
||||
|
||||
for i, ase_frame in ipairs(ase.header.frames) do
|
||||
for _, chunk in ipairs(ase_frame.chunks) do
|
||||
if chunk.type == CEL_CHUNK then
|
||||
local cel = chunk.data
|
||||
local buffer = love.data.decompress("data", "zlib", cel.data)
|
||||
local data = love.image.newImageData(cel.width, cel.height, "rgba8", buffer)
|
||||
local image = love.graphics.newImage(data)
|
||||
local frame = frames[i]
|
||||
if not frame then
|
||||
frame = {
|
||||
image = love.graphics.newCanvas(sprite.width, sprite.height),
|
||||
duration = ase_frame.frame_duration / 1000
|
||||
}
|
||||
frame.image:setFilter("nearest", "nearest")
|
||||
frames[i] = frame
|
||||
end
|
||||
|
||||
-- you need to draw in a canvas before.
|
||||
-- frame images can be of different sizes
|
||||
-- but never bigger than the header width and height
|
||||
love.graphics.setCanvas(frame.image)
|
||||
love.graphics.draw(image, cel.x, cel.y)
|
||||
love.graphics.setCanvas()
|
||||
elseif chunk.type == TAG_CHUNK then
|
||||
for j, tag in ipairs(chunk.data.tags) do
|
||||
-- first tag as default
|
||||
if j == 1 then
|
||||
first_tag = tag.name
|
||||
end
|
||||
|
||||
-- aseprite use 0 notation to begin
|
||||
-- but in lua, everthing starts in 1
|
||||
tag.to = tag.to + 1
|
||||
tag.from = tag.from + 1
|
||||
tag.frames = tag.to - tag.from
|
||||
tags[tag.name] = tag
|
||||
end
|
||||
elseif chunk.type == LAYER_CHUNK then
|
||||
table.insert(layers, chunk.data)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, tag in pairs(tags) do
|
||||
local variant = {}
|
||||
sprite.variants[tag.name] = variant
|
||||
for i=tag.from,tag.to do
|
||||
table.insert(variant, frames[i])
|
||||
end
|
||||
end
|
||||
|
||||
if not sprite.variants.default then
|
||||
sprite.variants.default = {frames[0]}
|
||||
end
|
||||
|
||||
return sprite
|
||||
end
|
||||
|
||||
return cargo.init{
|
||||
dir = "data",
|
||||
loaders = {
|
||||
aseprite = loadAsepriteSprite,
|
||||
ase = loadAsepriteSprite,
|
||||
}
|
||||
}
|
||||
|
BIN
src/data/sprites/bolt.aseprite
Normal file
BIN
src/data/sprites/bolt.aseprite
Normal file
Binary file not shown.
Binary file not shown.
@ -7,12 +7,12 @@ function MainState:enter(_, host_socket)
|
||||
local groups = {
|
||||
physical = {filter = {"pos", "vel"}},
|
||||
player = {filter = {
|
||||
"pos", "acc", "speed",
|
||||
"bolt_count",
|
||||
"bolt_cooldown", "bolt_speed", "bolt_friction"
|
||||
"pos", "acc", "speed", "bolts"
|
||||
-- "bolt_count",
|
||||
-- "bolt_cooldown", "bolt_speed", "bolt_friction"
|
||||
}},
|
||||
controllable_player = {filter = {"controllable"}},
|
||||
sprite = {filter = {"sprite"}},
|
||||
sprite = {filter = {"pos", "sprite"}},
|
||||
bolt = {filter={"pos", "vel", "bolt"}},
|
||||
collider = {filter={"pos", "collider"}}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ local rgb = require("helpers.rgb")
|
||||
local DRAW_GRID = false
|
||||
local GRID_COLOR = rgb(30, 30, 30)
|
||||
|
||||
local DRAW_COLLIDERS = true
|
||||
local DRAW_COLLIDERS = false
|
||||
local COLLIDER_COLOR = rgb(200, 20, 200)
|
||||
|
||||
function Debug:drawColliders()
|
||||
|
@ -36,6 +36,9 @@ function Physics:removeFromGroup(group, e)
|
||||
end
|
||||
|
||||
function Physics:_collisionFilter(entity, other)
|
||||
if entity.hidden then return end
|
||||
if other.hidden then return end
|
||||
|
||||
if entity.bolt then
|
||||
return "bounce"
|
||||
elseif entity.vel or other.vel then
|
||||
|
@ -4,6 +4,8 @@ local Vec = require("lib.brinevector")
|
||||
|
||||
local controls = data.controls
|
||||
|
||||
local BOLT_COLLIDER = {-3, -3, 3, 3}
|
||||
|
||||
local function getDirection(up_key, down_key, left_key, right_key)
|
||||
local dx = (love.keyboard.isDown(right_key) and 1 or 0)
|
||||
- (love.keyboard.isDown(left_key) and 1 or 0)
|
||||
@ -77,7 +79,7 @@ function Player:update(dt)
|
||||
|
||||
-- Check if the player tried to shoot a bolt
|
||||
if love.keyboard.isDown(controls.shoot) then
|
||||
if self:tryShootinBolt(e) then
|
||||
if self:tryShootingBolt(e) then
|
||||
self.pool:emit("playerShot", e)
|
||||
end
|
||||
end
|
||||
@ -95,8 +97,12 @@ function Player:update(dt)
|
||||
-- If the player is nearby a non-moving bolt, pick it up
|
||||
for _, bolt in ipairs(self.pool.groups.bolt.entities) do
|
||||
if (e.pos-bolt.pos).length < 20 and bolt.vel.length < 3 then
|
||||
self.pool:removeEntity(bolt)
|
||||
e.bolt_count = e.bolt_count + 1
|
||||
bolt.collider = nil
|
||||
bolt.bolt = nil
|
||||
bolt.sprite.variant = "idle"
|
||||
bolt.hidden = true
|
||||
self.pool:queue(bolt)
|
||||
table.insert(e.bolts, bolt)
|
||||
end
|
||||
end
|
||||
|
||||
@ -105,26 +111,22 @@ function Player:update(dt)
|
||||
else
|
||||
e.sprite.variant = "idle"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Player:tryShootinBolt(player)
|
||||
if (player.aim_dir.x ~= 0 or player.aim_dir.y ~= 0) and
|
||||
player.bolt_cooldown_timer == 0 and
|
||||
player.bolt_count > 0
|
||||
then
|
||||
player.bolt_cooldown_timer = player.bolt_cooldown
|
||||
player.bolt_count = player.bolt_count - 1
|
||||
if e.acc.x < 0 then
|
||||
e.sprite.flip = true
|
||||
elseif e.acc.x > 0 then
|
||||
e.sprite.flip = false
|
||||
end
|
||||
|
||||
return self.pool:queue{
|
||||
pos = player.pos + player.aim_dir * 30,
|
||||
vel = player.aim_dir * player.bolt_speed,
|
||||
friction = player.bolt_friction,
|
||||
max_speed = 400,
|
||||
bolt = true,
|
||||
sprite = {},
|
||||
collider = {-3, -3, 3, 3}
|
||||
}
|
||||
if #e.bolts > 0 then
|
||||
local bolt = e.bolts[1]
|
||||
if e.aim_dir.x ~= 0 or e.aim_dir.y ~= 0 then
|
||||
bolt.hidden = nil
|
||||
bolt.pos = e.pos + e.aim_dir * 15
|
||||
else
|
||||
bolt.hidden = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -132,8 +134,41 @@ function Player:mousemoved()
|
||||
self.use_mouse_aim = true
|
||||
end
|
||||
|
||||
function Player:tryShootingBolt(player)
|
||||
if (player.aim_dir.x ~= 0 or player.aim_dir.y ~= 0) and
|
||||
player.bolt_cooldown_timer == 0 and
|
||||
#player.bolts > 0
|
||||
then
|
||||
player.bolt_cooldown_timer = player.bolt_cooldown
|
||||
self:shootBolt(player)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function Player:shootBolt(player)
|
||||
local bolt = table.remove(player.bolts, 1)
|
||||
if not bolt then return end
|
||||
|
||||
bolt.pos = player.pos + player.aim_dir * 30
|
||||
bolt.vel = player.aim_dir * player.bolt_speed
|
||||
bolt.bolt = true
|
||||
bolt.sprite.variant = "active"
|
||||
bolt.collider = BOLT_COLLIDER
|
||||
bolt.hidden = nil
|
||||
self.pool:queue(bolt)
|
||||
end
|
||||
|
||||
function Player:storeBolt(player, bolt)
|
||||
bolt.collider = nil
|
||||
bolt.sprite.variant = "idle"
|
||||
bolt.hidden = true
|
||||
bolt.bolt = nil
|
||||
self.pool:queue(bolt)
|
||||
table.insert(player.bolts, bolt)
|
||||
end
|
||||
|
||||
function Player:spawnPlayer()
|
||||
return self.pool:queue{
|
||||
local player = self.pool:queue{
|
||||
pos = Vec(100, 100),
|
||||
vel = Vec(0, 0),
|
||||
acc = Vec(),
|
||||
@ -144,22 +179,28 @@ function Player:spawnPlayer()
|
||||
},
|
||||
friction = 0.998,
|
||||
speed = 800,
|
||||
bolt_count = 10,
|
||||
bolt_speed = 2000,
|
||||
bolt_cooldown = 0.2,
|
||||
bolt_friction = 0.98,
|
||||
bolts = {},
|
||||
bolt_cooldown = 0.1,
|
||||
collider = {-5, -5, 4, 8}
|
||||
}
|
||||
end
|
||||
|
||||
function Player:draw()
|
||||
for _, e in ipairs(self.pool.groups.player.entities) do
|
||||
if e.aim_dir.x ~= 0 or e.aim_dir.y ~= 0 then
|
||||
love.graphics.setColor(0.8, 0.2, 0.2)
|
||||
local aim_pos = e.pos + e.aim_dir * 15
|
||||
love.graphics.circle("fill", aim_pos.x, aim_pos.y, 5)
|
||||
end
|
||||
for _=1, 10 do
|
||||
local bolt = self.pool:queue{
|
||||
pos = Vec(100, 100),
|
||||
vel = Vec(),
|
||||
acc = Vec(),
|
||||
sprite = {
|
||||
name = "bolt",
|
||||
variant = "idle",
|
||||
},
|
||||
friction = 0.98,
|
||||
max_speed = 400,
|
||||
}
|
||||
self:storeBolt(player, bolt)
|
||||
end
|
||||
|
||||
return player
|
||||
end
|
||||
|
||||
return Player
|
||||
|
@ -1,94 +1,30 @@
|
||||
local data = require("data")
|
||||
local pprint = require("lib.pprint")
|
||||
local aseLoader = require("lib.ase-loader")
|
||||
local Sprite = {}
|
||||
|
||||
-- TODO: Maybe add a texture atlas library for packing frame data
|
||||
-- TODO: For production maybe use another type of loader? (https://github.com/elloramir/packer, https://github.com/EngineerSmith/Runtime-TextureAtlas)
|
||||
local function loadAsepriteSprite(path)
|
||||
local sprite = {}
|
||||
local ase = aseLoader(path)
|
||||
|
||||
sprite.width = ase.header.width
|
||||
sprite.height = ase.header.height
|
||||
sprite.variants = {}
|
||||
|
||||
local FRAME_DATA_CHUNK = 0x2005
|
||||
local FRAME_TAG_CHUNK = 0x2018
|
||||
|
||||
local frames = {}
|
||||
local tags = {}
|
||||
local default_variant
|
||||
|
||||
for _, frame in ipairs(ase.header.frames) do
|
||||
for _, chunk in ipairs(frame.chunks) do
|
||||
if chunk.type == FRAME_DATA_CHUNK then
|
||||
local cel = chunk.data
|
||||
local buffer = love.data.decompress("data", "zlib", cel.data)
|
||||
local data = love.image.newImageData(cel.width, cel.height, "rgba8", buffer)
|
||||
local image = love.graphics.newImage(data)
|
||||
local canvas = love.graphics.newCanvas(sprite.width, sprite.height)
|
||||
canvas:setFilter("nearest")
|
||||
|
||||
-- you need to draw in a canvas before.
|
||||
-- frame images can be of different sizes
|
||||
-- but never bigger than the header width and height
|
||||
love.graphics.setCanvas(canvas)
|
||||
love.graphics.draw(image, cel.x, cel.y)
|
||||
love.graphics.setCanvas()
|
||||
|
||||
table.insert(frames, {
|
||||
image = canvas,
|
||||
duration = frame.frame_duration / 1000
|
||||
})
|
||||
elseif chunk.type == FRAME_TAG_CHUNK then
|
||||
for i, tag in ipairs(chunk.data.tags) do
|
||||
-- first tag as default
|
||||
if i == 1 then
|
||||
default_variant = tag.name
|
||||
end
|
||||
|
||||
-- aseprite use 0 notation to begin
|
||||
-- but in lua, everthing starts in 1
|
||||
tag.to = tag.to + 1
|
||||
tag.from = tag.from + 1
|
||||
tag.frames = tag.to - tag.from
|
||||
tags[tag.name] = tag
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, tag in pairs(tags) do
|
||||
local variant = {}
|
||||
sprite.variants[tag.name] = variant
|
||||
for i=tag.from,tag.to do
|
||||
table.insert(variant, frames[i])
|
||||
end
|
||||
end
|
||||
|
||||
if not sprite.variants.default then
|
||||
sprite.variants.default = {frames[0]}
|
||||
end
|
||||
|
||||
return sprite
|
||||
end
|
||||
|
||||
local all_sprites = {}
|
||||
all_sprites.player = loadAsepriteSprite("data/sprites/player.aseprite")
|
||||
|
||||
function Sprite:addToWorld(group, e)
|
||||
if group ~= "sprite" then return end
|
||||
|
||||
local sprite = data.sprites[e.sprite.variant]
|
||||
assert(sprite, ("Attempt to draw unknown sprite: %s"):format(e.sprite.name))
|
||||
end
|
||||
|
||||
function Sprite:update(dt)
|
||||
for _, e in ipairs(self.pool.groups.sprite.entities) do
|
||||
if e.sprite.name and all_sprites[e.sprite.name] then
|
||||
local spritesheet = all_sprites[e.sprite.name]
|
||||
local variant = spritesheet.variants[e.sprite.variant or "default"]
|
||||
local sprite = data.sprites[e.sprite.name]
|
||||
if sprite then
|
||||
local variant = sprite.variants[e.sprite.variant or "default"]
|
||||
e.sprite.timer = (e.sprite.timer or 0) + dt
|
||||
|
||||
if variant then
|
||||
if e.sprite.timer > 0.1 then
|
||||
local frame = variant[e.sprite.frame]
|
||||
if not frame then
|
||||
frame = variant[1]
|
||||
end
|
||||
if e.sprite.timer > frame.duration then
|
||||
e.sprite.timer = e.sprite.timer % 0.1
|
||||
e.sprite.frame = (e.sprite.frame or 1) % #variant + 1
|
||||
end
|
||||
@ -100,19 +36,28 @@ end
|
||||
function Sprite:draw()
|
||||
love.graphics.setColor(1, 1, 1)
|
||||
for _, e in ipairs(self.pool.groups.sprite.entities) do
|
||||
local spritesheet = all_sprites[e.sprite.name]
|
||||
if spritesheet then
|
||||
if e.sprite.variant ~= e.sprite.last_variant then
|
||||
e.sprite.frame = 1
|
||||
end
|
||||
local variant = spritesheet.variants[e.sprite.variant or "default"]
|
||||
if variant then
|
||||
local sprite = data.sprites[e.sprite.name]
|
||||
assert(sprite, ("Attempt to draw unknown sprite: %s"):format(e.sprite.name))
|
||||
|
||||
if not e.hidden then
|
||||
local variant = sprite.variants[e.sprite.variant or "default"]
|
||||
if variant and e.sprite.frame then
|
||||
local frame = variant[e.sprite.frame]
|
||||
love.graphics.draw(frame.image, e.pos.x-spritesheet.width/2, e.pos.y-spritesheet.height/2)
|
||||
if not frame then
|
||||
frame = variant[1]
|
||||
end
|
||||
local sx = 1
|
||||
if e.sprite.flip then
|
||||
sx = -1
|
||||
end
|
||||
love.graphics.draw(
|
||||
frame.image,
|
||||
e.pos.x,
|
||||
e.pos.y,
|
||||
0, sx, 1,
|
||||
sprite.width/2, sprite.height/2
|
||||
)
|
||||
end
|
||||
e.sprite.last_variant = e.sprite.variant
|
||||
else
|
||||
love.graphics.circle("fill", e.pos.x, e.pos.y, 10)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user