format and stuff

main
Simon Kellet 2 months ago
parent 53aa6d698d
commit 99b420cc7f
  1. 12
      Game/GameKeyPressed.lua
  2. 32
      Game/UpdateGame.lua
  3. 59
      Menu/DrawMenu.lua
  4. 7
      Menu/MenuKeyPressed.lua
  5. 3
      Menu/UpdateMenu.lua
  6. 63
      Pause/DrawPause.lua
  7. 42
      Pause/PauseKeyPressed.lua
  8. 3
      Pause/UpdatePause.lua
  9. 12
      constants.lua
  10. BIN
      game.love
  11. 69
      libs/classic.lua
  12. 242
      libs/profile.lua
  13. 317
      libs/sti/atlas.lua
  14. 2
      libs/sti/graphics.lua
  15. 620
      libs/sti/init.lua
  16. 114
      libs/sti/plugins/box2d.lua
  17. 103
      libs/sti/plugins/bump.lua
  18. 78
      libs/sti/utils.lua
  19. 1653
      libs/windfield/init.lua
  20. 1043
      libs/windfield/mlib/mlib.lua
  21. 46
      main.lua
  22. 108
      player.lua

@ -1,8 +1,7 @@
function GameKeyPressed(key)
if key == "escape" then
musicBattle:setVolume(0)
musicPause:setVolume(0.5)
musicBattle:setVolume(0)
musicPause:setVolume(0.6)
_G.GAMESTATE = "PAUSE"
print("STATE CHANEGD: PAUSED!")
@ -13,13 +12,8 @@ function GameKeyPressed(key)
DebugFlag = not DebugFlag
end
--TODO: Move player movement code into here!
--[[
-- TODO: Better restart
--TODO: Better restart
if key == "r" and not _G.PAUSED then
love.load()
end
]]--
end

@ -1,9 +1,31 @@
local function checkWinState()
-- Check P1's health
if UserPlayer1.health <= 0 then --P1 win
_G.P1_WIN = true
_G.P2_WIN = false
UserPlayer1.health = 0
return true
end
if UserPlayer2.health <= 0 then --P2 win
_G.P1_WIN = false
_G.P2_WIN = true
UserPlayer2.health = 0
return true
end
return false
end
local max = math.max -- optimisations
function UpdateGame(dt)
--Check if anyone has won
if checkWinState() == true then
print("STATE CHNAGED: WIN!")
_G.GAMESTATE = "WIN"
end
--WindField
World:update(dt)
local max = math.max
KeyPressTime1 = max(0, KeyPressTime1 - dt)
if KeyPressTime1 <= 0 then
EnableKeyPress1 = true
@ -13,6 +35,7 @@ function UpdateGame(dt)
if KeyPressTime2 <= 0 then
EnableKeyPress2 = true
end
for i, v in ipairs(Bullets1) do
v:update(dt)
if v.y < 0 then --top of screen
@ -69,7 +92,10 @@ function UpdateGame(dt)
end
end
end
UserPlayer1:handleKeys("w", "s", "a", "d", dt)
UserPlayer2:handleKeys("up", "down", "left", "right", dt)
UserPlayer1:updateCol()
UserPlayer2:updateCol()
UserPlayer1:update(dt)
UserPlayer2:update(dt)
end

@ -1,44 +1,43 @@
local function button(x,y, w, h, text, selected)
--x,y is the top left corner of the button
local rounding = 30 -- used for rounding the buttons
local function button(x, y, w, h, text, selected)
--x,y is the top left corner of the button
local rounding = 30 -- used for rounding the buttons
if not selected then
love.graphics.setColor(love.math.colorFromBytes(41,134,204))
elseif selected then
love.graphics.setColor(love.math.colorFromBytes(244,67,54))
end
-- Draw rectangle
love.graphics.rectangle("line", x, y, w, h, rounding, rounding)
if not selected then
love.graphics.setColor(love.math.colorFromBytes(41, 134, 204))
elseif selected then
love.graphics.setColor(love.math.colorFromBytes(244, 67, 54))
end
-- Draw rectangle
love.graphics.rectangle("line", x, y, w, h, rounding, rounding)
-- Get width and height of text
local tw = MenuFont:getWidth(text)
local th = MenuFont:getHeight(text)
-- Calculate position to center the text
local textX = x + (w - tw) / 2
local textY = y + (h - th) / 2
-- Place text inside the rectangle
-- Get width and height of text
local tw = MenuFont:getWidth(text)
local th = MenuFont:getHeight(text)
-- Calculate position to center the text
local textX = x + (w - tw) / 2
local textY = y + (h - th) / 2
-- Place text inside the rectangle
love.graphics.setFont(MenuFont)
love.graphics.print(text, textX, textY)
love.graphics.print(text, textX, textY)
end
local function title()
local height = love.graphics.getHeight()
local width = love.graphics.getWidth()
love.graphics.setFont(GameFont)
love.graphics.setColor(0.5,1,1)
love.graphics.rectangle("fill", 0, 0, width, height)
love.graphics.setColor(0,0,0)
love.graphics.print("MENU", 100,100)
love.graphics.setColor(0.5, 1, 1)
love.graphics.rectangle("fill", 0, 0, width, height)
love.graphics.setColor(0, 0, 0)
love.graphics.print("MENU", 100, 100)
end
function DrawMenu()
local bwidth, bheight = 300, 140
title()
button(100, 200, bwidth, bheight, "Play", MENU_POS == 0 and true or false)
button(100, 350, bwidth, bheight, "???", MENU_POS == 1 and true or false)
button(100, 500, bwidth, bheight, "???", MENU_POS == 2 and true or false)
button(100, 650, bwidth, bheight, "Quit", MENU_POS == 3 and true or false)
local bwidth, bheight = 300, 140
title()
button(100, 200, bwidth, bheight, "Play", MENU_POS == 0 and true or false)
button(100, 350, bwidth, bheight, "???", MENU_POS == 1 and true or false)
button(100, 500, bwidth, bheight, "???", MENU_POS == 2 and true or false)
button(100, 650, bwidth, bheight, "Quit", MENU_POS == 3 and true or false)
love.graphics.setColor(255,255,255) -- reset colours
love.graphics.setColor(255, 255, 255) -- reset colours
end

@ -1,5 +1,5 @@
function MenuKeyPressed(key)
if key == 'return' then
if key == "return" then
-- 0 Start Game
-- 1 ??
-- 2 ???
@ -9,17 +9,13 @@ function MenuKeyPressed(key)
_G.GAMESTATE = "GAME"
print("STATE CHANEGD: GAME!")
musicMenu:stop()
elseif MENU_POS == 1 then
print("STATE CHANEGD: DUNNO!")
elseif MENU_POS == 2 then
print("STATE CHANEGD: DUNNO!")
elseif MENU_POS == 3 then
love.event.quit()
end
end
if love.keyboard.isDown("up") then
@ -37,5 +33,4 @@ function MenuKeyPressed(key)
_G.MENU_POS = _G.MENU_POS + 1
end
end
end

@ -1,2 +1 @@
function UpdateMenu(dt)
end
function UpdateMenu(dt) end

@ -1,45 +1,44 @@
local function button(x,y, w, h, text, selected)
--x,y is the top left corner of the button
local rounding = 30 -- used for rounding the buttons
local function button(x, y, w, h, text, selected)
--x,y is the top left corner of the button
local rounding = 30 -- used for rounding the buttons
if not selected then
love.graphics.setColor(love.math.colorFromBytes(41,134,204))
elseif selected then
love.graphics.setColor(love.math.colorFromBytes(244,67,54))
end
-- Draw rectangle
love.graphics.rectangle("fill", x, y, w, h, rounding, rounding)
if not selected then
love.graphics.setColor(love.math.colorFromBytes(41, 134, 204))
elseif selected then
love.graphics.setColor(love.math.colorFromBytes(244, 67, 54))
end
-- Draw rectangle
love.graphics.rectangle("fill", x, y, w, h, rounding, rounding)
-- Get width and height of text
local tw = MenuFont:getWidth(text)
local th = MenuFont:getHeight(text)
-- Calculate position to center the text
local textX = x + (w - tw) / 2
local textY = y + (h - th) / 2
-- Place text inside the rectangle
-- Get width and height of text
local tw = MenuFont:getWidth(text)
local th = MenuFont:getHeight(text)
-- Calculate position to center the text
local textX = x + (w - tw) / 2
local textY = y + (h - th) / 2
-- Place text inside the rectangle
love.graphics.setFont(MenuFont)
love.graphics.setColor(1,1,1) -- reset colours
love.graphics.print(text, textX, textY)
love.graphics.setColor(1, 1, 1) -- reset colours
love.graphics.print(text, textX, textY)
end
function DrawPause()
local opacity = 0.3
local opacity = 0.3
local height = love.graphics.getHeight()
local width = love.graphics.getWidth()
local bwidth, bheight = 300, 140
local bwidth, bheight = 300, 140
love.graphics.setFont(GameFont)
DrawGame() --Draw a single frame of the game
love.graphics.setColor(0.1,0.1,0.1, opacity) --overlay opaque img
love.graphics.rectangle("fill", 0, 0, width, height)
DrawGame() --Draw a single frame of the game
love.graphics.setColor(0.1, 0.1, 0.1, opacity) --overlay opaque img
love.graphics.rectangle("fill", 0, 0, width, height)
love.graphics.setColor(1,1,1)
love.graphics.print("PAUSED", 100,100)
--love.graphics.print("" .. PAUSE_POS, 200,200)
love.graphics.setColor(1, 1, 1)
love.graphics.print("PAUSED", 100, 100)
--love.graphics.print("" .. PAUSE_POS, 200,200)
button(100, 200, bwidth, bheight, "Return", PAUSE_POS == 0 and true or false)
button(100, 350, bwidth, bheight, "Menu", PAUSE_POS == 1 and true or false)
button(100, 500, bwidth, bheight, "Quit", PAUSE_POS == 2 and true or false)
love.graphics.setColor(255,255,255) -- reset colours
button(100, 200, bwidth, bheight, "Return", PAUSE_POS == 0 and true or false)
button(100, 350, bwidth, bheight, "Menu", PAUSE_POS == 1 and true or false)
button(100, 500, bwidth, bheight, "Quit", PAUSE_POS == 2 and true or false)
love.graphics.setColor(255, 255, 255) -- reset colours
end

@ -1,30 +1,26 @@
function PauseKeyPressed(key)
if key == 'return' then
musicBattle:setVolume(0.5)
musicPause:setVolume(0)
-- 0 Return to game
-- 1 Quit
if PAUSE_POS == 0 then
-- unpause the game
_G.GAMESTATE = "GAME"
print("STATE CHANEGD: GAME!")
_G.PAUSED = false
if key == "return" then
musicBattle:setVolume(0.5)
musicPause:setVolume(0)
-- 0 Return to game
-- 1 Quit
if PAUSE_POS == 0 then
-- unpause the game
_G.GAMESTATE = "GAME"
print("STATE CHANEGD: GAME!")
_G.PAUSED = false
musicBattle:setVolume(0.5)
musicPause:setVolume(0)
elseif PAUSE_POS == 1 then
_G.GAMESTATE = "MENU"
print("STATE CHANEGD: MENU!")
_G.PAUSED = false
musicPause:stop()
musicBattle:stop()
elseif PAUSE_POS == 2 then
elseif PAUSE_POS == 1 then
_G.GAMESTATE = "MENU"
print("STATE CHANEGD: MENU!")
_G.PAUSED = false
musicPause:stop()
musicBattle:stop()
elseif PAUSE_POS == 2 then
love.event.quit()
end
end
end
end
if love.keyboard.isDown("up") then
if _G.PAUSE_POS == 0 then

@ -1,2 +1 @@
function UpdatePause(dt)
end
function UpdatePause(dt) end

@ -1,3 +1,11 @@
--[[
* Game states:
* - MENU
* - GAME
* - PAUSE
* - WIN
]]
--
GAMESTATE = "MENU"
MENU_POS = 0
@ -6,3 +14,7 @@ MENU_MAX = 3 --0 play, 1 ?, 2 ?, 3 quit
PAUSED = false
PAUSE_POS = 0
PAUSE_MAX = 2 -- 0 resume, 1 menu, 2 quit
-- WIN flags for P1 and P2
P1_WIN = false
P2_WIN = false

Binary file not shown.

@ -7,62 +7,53 @@
-- the terms of the MIT license. See LICENSE for details.
--
local Object = {}
Object.__index = Object
function Object:new()
end
function Object:new() end
function Object:extend()
local cls = {}
for k, v in pairs(self) do
if k:find("__") == 1 then
cls[k] = v
end
end
cls.__index = cls
cls.super = self
setmetatable(cls, self)
return cls
local cls = {}
for k, v in pairs(self) do
if k:find("__") == 1 then
cls[k] = v
end
end
cls.__index = cls
cls.super = self
setmetatable(cls, self)
return cls
end
function Object:implement(...)
for _, cls in pairs({...}) do
for k, v in pairs(cls) do
if self[k] == nil and type(v) == "function" then
self[k] = v
end
end
end
for _, cls in pairs({ ... }) do
for k, v in pairs(cls) do
if self[k] == nil and type(v) == "function" then
self[k] = v
end
end
end
end
function Object:is(T)
local mt = getmetatable(self)
while mt do
if mt == T then
return true
end
mt = getmetatable(mt)
end
return false
local mt = getmetatable(self)
while mt do
if mt == T then
return true
end
mt = getmetatable(mt)
end
return false
end
function Object:__tostring()
return "Object"
return "Object"
end
function Object:__call(...)
local obj = setmetatable({}, self)
obj:new(...)
return obj
local obj = setmetatable({}, self)
obj:new(...)
return obj
end
return Object

@ -23,91 +23,91 @@ local _internal = {}
-- @tparam number line Line number
-- @tparam[opt] table info Debug info table
function profile.hooker(event, line, info)
info = info or debug.getinfo(2, 'fnS')
local f = info.func
-- ignore the profiler itself
if _internal[f] or info.what ~= "Lua" then
return
end
-- get the function name if available
if info.name then
_labeled[f] = info.name
end
-- find the line definition
if not _defined[f] then
_defined[f] = info.short_src..":"..info.linedefined
_ncalls[f] = 0
_telapsed[f] = 0
end
if _tcalled[f] then
local dt = clock() - _tcalled[f]
_telapsed[f] = _telapsed[f] + dt
_tcalled[f] = nil
end
if event == "tail call" then
local prev = debug.getinfo(3, 'fnS')
profile.hooker("return", line, prev)
profile.hooker("call", line, info)
elseif event == 'call' then
_tcalled[f] = clock()
else
_ncalls[f] = _ncalls[f] + 1
end
info = info or debug.getinfo(2, "fnS")
local f = info.func
-- ignore the profiler itself
if _internal[f] or info.what ~= "Lua" then
return
end
-- get the function name if available
if info.name then
_labeled[f] = info.name
end
-- find the line definition
if not _defined[f] then
_defined[f] = info.short_src .. ":" .. info.linedefined
_ncalls[f] = 0
_telapsed[f] = 0
end
if _tcalled[f] then
local dt = clock() - _tcalled[f]
_telapsed[f] = _telapsed[f] + dt
_tcalled[f] = nil
end
if event == "tail call" then
local prev = debug.getinfo(3, "fnS")
profile.hooker("return", line, prev)
profile.hooker("call", line, info)
elseif event == "call" then
_tcalled[f] = clock()
else
_ncalls[f] = _ncalls[f] + 1
end
end
--- Sets a clock function to be used by the profiler.
-- @tparam function func Clock function that returns a number
function profile.setclock(f)
assert(type(f) == "function", "clock must be a function")
clock = f
assert(type(f) == "function", "clock must be a function")
clock = f
end
--- Starts collecting data.
function profile.start()
if rawget(_G, 'jit') then
jit.off()
jit.flush()
end
debug.sethook(profile.hooker, "cr")
if rawget(_G, "jit") then
jit.off()
jit.flush()
end
debug.sethook(profile.hooker, "cr")
end
--- Stops collecting data.
function profile.stop()
debug.sethook()
for f in pairs(_tcalled) do
local dt = clock() - _tcalled[f]
_telapsed[f] = _telapsed[f] + dt
_tcalled[f] = nil
end
-- merge closures
local lookup = {}
for f, d in pairs(_defined) do
local id = (_labeled[f] or '?')..d
local f2 = lookup[id]
if f2 then
_ncalls[f2] = _ncalls[f2] + (_ncalls[f] or 0)
_telapsed[f2] = _telapsed[f2] + (_telapsed[f] or 0)
_defined[f], _labeled[f] = nil, nil
_ncalls[f], _telapsed[f] = nil, nil
else
lookup[id] = f
end
end
collectgarbage('collect')
debug.sethook()
for f in pairs(_tcalled) do
local dt = clock() - _tcalled[f]
_telapsed[f] = _telapsed[f] + dt
_tcalled[f] = nil
end
-- merge closures
local lookup = {}
for f, d in pairs(_defined) do
local id = (_labeled[f] or "?") .. d
local f2 = lookup[id]
if f2 then
_ncalls[f2] = _ncalls[f2] + (_ncalls[f] or 0)
_telapsed[f2] = _telapsed[f2] + (_telapsed[f] or 0)
_defined[f], _labeled[f] = nil, nil
_ncalls[f], _telapsed[f] = nil, nil
else
lookup[id] = f
end
end
collectgarbage("collect")
end
--- Resets all collected data.
function profile.reset()
for f in pairs(_ncalls) do
_ncalls[f] = 0
end
for f in pairs(_telapsed) do
_telapsed[f] = 0
end
for f in pairs(_tcalled) do
_tcalled[f] = nil
end
collectgarbage('collect')
for f in pairs(_ncalls) do
_ncalls[f] = 0
end
for f in pairs(_telapsed) do
_telapsed[f] = 0
end
for f in pairs(_tcalled) do
_tcalled[f] = nil
end
collectgarbage("collect")
end
--- This is an internal function.
@ -115,11 +115,11 @@ end
-- @tparam function b Second function
-- @treturn boolean True if "a" should rank higher than "b"
function profile.comp(a, b)
local dt = _telapsed[b] - _telapsed[a]
if dt == 0 then
return _ncalls[b] < _ncalls[a]
end
return dt < 0
local dt = _telapsed[b] - _telapsed[a]
if dt == 0 then
return _ncalls[b] < _ncalls[a]
end
return dt < 0
end
--- Generates a report of functions that have been called since the profile was started.
@ -127,26 +127,26 @@ end
-- @tparam[opt] number limit Maximum number of rows
-- @treturn table Table of rows
function profile.query(limit)
local t = {}
for f, n in pairs(_ncalls) do
if n > 0 then
t[#t + 1] = f
end
end
table.sort(t, profile.comp)
if limit then
while #t > limit do
table.remove(t)
end
end
for i, f in ipairs(t) do
local dt = 0
if _tcalled[f] then
dt = clock() - _tcalled[f]
end
t[i] = { i, _labeled[f] or '?', _ncalls[f], _telapsed[f] + dt, _defined[f] }
end
return t
local t = {}
for f, n in pairs(_ncalls) do
if n > 0 then
t[#t + 1] = f
end
end
table.sort(t, profile.comp)
if limit then
while #t > limit do
table.remove(t)
end
end
for i, f in ipairs(t) do
local dt = 0
if _tcalled[f] then
dt = clock() - _tcalled[f]
end
t[i] = { i, _labeled[f] or "?", _ncalls[f], _telapsed[f] + dt, _defined[f] }
end
return t
end
local cols = { 3, 29, 11, 24, 32 }
@ -156,38 +156,40 @@ local cols = { 3, 29, 11, 24, 32 }
-- @tparam[opt] number limit Maximum number of rows
-- @treturn string Text-based profiling report
function profile.report(n)
local out = {}
local report = profile.query(n)
for i, row in ipairs(report) do
for j = 1, 5 do
local s = row[j]
local l2 = cols[j]
s = tostring(s)
local l1 = s:len()
if l1 < l2 then
s = s..(' '):rep(l2-l1)
elseif l1 > l2 then
s = s:sub(l1 - l2 + 1, l1)
end
row[j] = s
end
out[i] = table.concat(row, ' | ')
end
local out = {}
local report = profile.query(n)
for i, row in ipairs(report) do
for j = 1, 5 do
local s = row[j]
local l2 = cols[j]
s = tostring(s)
local l1 = s:len()
if l1 < l2 then
s = s .. (" "):rep(l2 - l1)
elseif l1 > l2 then
s = s:sub(l1 - l2 + 1, l1)
end
row[j] = s
end
out[i] = table.concat(row, " | ")
end
local row = " +-----+-------------------------------+-------------+--------------------------+----------------------------------+ \n"
local col = " | # | Function | Calls | Time | Code | \n"
local sz = row..col..row
if #out > 0 then
sz = sz..' | '..table.concat(out, ' | \n | ')..' | \n'
end
return '\n'..sz..row
local row =
" +-----+-------------------------------+-------------+--------------------------+----------------------------------+ \n"
local col =
" | # | Function | Calls | Time | Code | \n"
local sz = row .. col .. row
if #out > 0 then
sz = sz .. " | " .. table.concat(out, " | \n | ") .. " | \n"
end
return "\n" .. sz .. row
end
-- store all internal profiler functions
for _, v in pairs(profile) do
if type(v) == "function" then
_internal[v] = true
end
if type(v) == "function" then
_internal[v] = true
end
end
return profile

@ -10,150 +10,179 @@ local module = {}
-- @param sort If "size" will sort by size, or if "id" will sort by id
-- @param ids Array with ids of each file
-- @param pow2 If true, will force a power of 2 size
function module.Atlas( files, sort, ids, pow2 )
local function Node(x, y, w, h)
return {x = x, y = y, w = w, h = h}
end
local function nextpow2( n )
local res = 1
while res <= n do
res = res * 2
end
return res
end
local function loadImgs()
local images = {}
for i = 1, #files do
images[i] = {}
--images[i].name = files[i]
if ids then images[i].id = ids[i] end
images[i].img = love.graphics.newImage( files[i] )
images[i].w = images[i].img:getWidth()
images[i].h = images[i].img:getHeight()
images[i].area = images[i].w * images[i].h
end
if sort == "size" or sort == "id" then
table.sort( images, function( a, b ) return ( a.area > b.area ) end )
end
return images
end
--TODO: understand this func
local function add(root, id, w, h)
if root.left or root.right then
if root.left then
local node = add(root.left, id, w, h)
if node then return node end
end
if root.right then
local node = add(root.right, id, w, h)
if node then return node end
end
return nil
end
if w > root.w or h > root.h then return nil end
local _w, _h = root.w - w, root.h - h
if _w <= _h then
root.left = Node(root.x + w, root.y, _w, h)
root.right = Node(root.x, root.y + h, root.w, _h)
else
root.left = Node(root.x, root.y + h, w, _h)
root.right = Node(root.x + w, root.y, _w, root.h)
end
root.w = w
root.h = h
root.id = id
return root
end
local function unmap(root)
if not root then return {} end
local tree = {}
if root.id then
tree[root.id] = {}
tree[root.id].x, tree[root.id].y = root.x, root.y
end
local left = unmap(root.left)
local right = unmap(root.right)
for k, v in pairs(left) do
tree[k] = {}
tree[k].x, tree[k].y = v.x, v.y
end
for k, v in pairs(right) do
tree[k] = {}
tree[k].x, tree[k].y = v.x, v.y
end
return tree
end
local function bake()
local images = loadImgs()
local root = {}
local w, h = images[1].w, images[1].h
if pow2 then
if w % 1 == 0 then w = nextpow2(w) end
if h % 1 == 0 then h = nextpow2(h) end
end
repeat
local node
root = Node(0, 0, w, h)
for i = 1, #images do
node = add(root, i, images[i].w, images[i].h)
if not node then break end
end
if not node then
if h <= w then
if pow2 then h = h * 2 else h = h + 1 end
else
if pow2 then w = w * 2 else w = w + 1 end
end
else
break
end
until false
local limits = love.graphics.getSystemLimits()
if w > limits.texturesize or h > limits.texturesize then
return "Resulting texture is too large for this system"
end
local coords = unmap(root)
local map = love.graphics.newCanvas(w, h)
love.graphics.setCanvas( map )
-- love.graphics.clear()
for i = 1, #images do
love.graphics.draw(images[i].img, coords[i].x, coords[i].y)
if ids then coords[i].id = images[i].id end
end
love.graphics.setCanvas()
if sort == "ids" then
table.sort( coords, function( a, b ) return ( a.id < b.id ) end )
end
return { image = map, coords = coords }
end
return bake()
function module.Atlas(files, sort, ids, pow2)
local function Node(x, y, w, h)
return { x = x, y = y, w = w, h = h }
end
local function nextpow2(n)
local res = 1
while res <= n do
res = res * 2
end
return res
end
local function loadImgs()
local images = {}
for i = 1, #files do
images[i] = {}
--images[i].name = files[i]
if ids then
images[i].id = ids[i]
end
images[i].img = love.graphics.newImage(files[i])
images[i].w = images[i].img:getWidth()
images[i].h = images[i].img:getHeight()
images[i].area = images[i].w * images[i].h
end
if sort == "size" or sort == "id" then
table.sort(images, function(a, b)
return (a.area > b.area)
end)
end
return images
end
--TODO: understand this func
local function add(root, id, w, h)
if root.left or root.right then
if root.left then
local node = add(root.left, id, w, h)
if node then
return node
end
end
if root.right then
local node = add(root.right, id, w, h)
if node then
return node
end
end
return nil
end
if w > root.w or h > root.h then
return nil
end
local _w, _h = root.w - w, root.h - h
if _w <= _h then
root.left = Node(root.x + w, root.y, _w, h)
root.right = Node(root.x, root.y + h, root.w, _h)
else
root.left = Node(root.x, root.y + h, w, _h)
root.right = Node(root.x + w, root.y, _w, root.h)
end
root.w = w
root.h = h
root.id = id
return root
end
local function unmap(root)
if not root then
return {}
end
local tree = {}
if root.id then
tree[root.id] = {}
tree[root.id].x, tree[root.id].y = root.x, root.y
end
local left = unmap(root.left)
local right = unmap(root.right)
for k, v in pairs(left) do
tree[k] = {}
tree[k].x, tree[k].y = v.x, v.y
end
for k, v in pairs(right) do
tree[k] = {}
tree[k].x, tree[k].y = v.x, v.y
end
return tree
end
local function bake()
local images = loadImgs()
local root = {}
local w, h = images[1].w, images[1].h
if pow2 then
if w % 1 == 0 then
w = nextpow2(w)
end
if h % 1 == 0 then
h = nextpow2(h)
end
end
repeat
local node
root = Node(0, 0, w, h)
for i = 1, #images do
node = add(root, i, images[i].w, images[i].h)
if not node then
break
end
end
if not node then
if h <= w then
if pow2 then
h = h * 2
else
h = h + 1
end
else
if pow2 then
w = w * 2
else
w = w + 1
end
end
else
break
end
until false
local limits = love.graphics.getSystemLimits()
if w > limits.texturesize or h > limits.texturesize then
return "Resulting texture is too large for this system"
end
local coords = unmap(root)
local map = love.graphics.newCanvas(w, h)
love.graphics.setCanvas(map)
-- love.graphics.clear()
for i = 1, #images do
love.graphics.draw(images[i].img, coords[i].x, coords[i].y)
if ids then
coords[i].id = images[i].id
end
end
love.graphics.setCanvas()
if sort == "ids" then
table.sort(coords, function(a, b)
return (a.id < b.id)
end)
end
return { image = map, coords = coords }
end
return bake()
end
return module

@ -1,4 +1,4 @@
local lg = _G.love.graphics
local lg = _G.love.graphics
local graphics = { isCreated = lg and true or false }
function graphics.newSpriteBatch(...)

@ -5,22 +5,22 @@
-- @license MIT/X11
local STI = {
_LICENSE = "MIT/X11",
_URL = "https://github.com/karai17/Simple-Tiled-Implementation",
_VERSION = "1.2.3.0",
_LICENSE = "MIT/X11",
_URL = "https://github.com/karai17/Simple-Tiled-Implementation",
_VERSION = "1.2.3.0",
_DESCRIPTION = "Simple Tiled Implementation is a Tiled Map Editor library designed for the *awesome* LÖVE framework.",
cache = {}
cache = {},
}
STI.__index = STI
local love = _G.love
local cwd = (...):gsub('%.init$', '') .. "."
local love = _G.love
local cwd = (...):gsub("%.init$", "") .. "."
local utils = require(cwd .. "utils")
local ceil = math.ceil
local ceil = math.ceil
local floor = math.floor
local lg = require(cwd .. "graphics")
local lg = require(cwd .. "graphics")
local atlas = require(cwd .. "atlas")
local Map = {}
local Map = {}
Map.__index = Map
local function new(map, plugins, ox, oy)
@ -31,10 +31,7 @@ local function new(map, plugins, ox, oy)
else
-- Check for valid map type
local ext = map:sub(-4, -1)
assert(ext == ".lua", string.format(
"Invalid file type: %s. File must be of type: lua.",
ext
))
assert(ext == ".lua", string.format("Invalid file type: %s. File must be of type: lua.", ext))
-- Get directory of map
dir = map:reverse():find("[/\\]") or ""
@ -79,58 +76,58 @@ function Map:init(path, plugins, ox, oy)
end
self:resize()
self.objects = {}
self.tiles = {}
self.objects = {}
self.tiles = {}
self.tileInstances = {}
self.offsetx = ox or 0
self.offsety = oy or 0
self.freeBatchSprites = {}
setmetatable(self.freeBatchSprites, { __mode = 'k' })
setmetatable(self.freeBatchSprites, { __mode = "k" })
-- Set tiles, images
local gid = 1
for i, tileset in ipairs(self.tilesets) do
assert(not tileset.filename, "STI does not support external Tilesets.\nYou need to embed all Tilesets.")
if tileset.image then
-- Cache images
if lg.isCreated then
local formatted_path = utils.format_path(path .. tileset.image)
if not STI.cache[formatted_path] then
utils.fix_transparent_color(tileset, formatted_path)
utils.cache_image(STI, formatted_path, tileset.image)
else
tileset.image = STI.cache[formatted_path]
end
end
gid = self:setTiles(i, tileset, gid)
elseif tileset.tilecount > 0 then
-- Build atlas for image collection
local files, ids = {}, {}
for j = 1, #tileset.tiles do
files[ j ] = utils.format_path(path .. tileset.tiles[j].image)
ids[ j ] = tileset.tiles[j].id
end
local map = atlas.Atlas( files, "ids", ids )
if lg.isCreated then
local formatted_path = utils.format_path(path .. tileset.name)
if not STI.cache[formatted_path] then
-- No need to fix transparency color for collections
utils.cache_image(STI, formatted_path, map.image)
tileset.image = map.image
else
tileset.image = STI.cache[formatted_path]
end
end
gid = self:setAtlasTiles(i, tileset, map.coords, gid)
end
if tileset.image then
-- Cache images
if lg.isCreated then
local formatted_path = utils.format_path(path .. tileset.image)
if not STI.cache[formatted_path] then
utils.fix_transparent_color(tileset, formatted_path)
utils.cache_image(STI, formatted_path, tileset.image)
else
tileset.image = STI.cache[formatted_path]
end
end
gid = self:setTiles(i, tileset, gid)
elseif tileset.tilecount > 0 then
-- Build atlas for image collection
local files, ids = {}, {}
for j = 1, #tileset.tiles do
files[j] = utils.format_path(path .. tileset.tiles[j].image)
ids[j] = tileset.tiles[j].id
end
local map = atlas.Atlas(files, "ids", ids)
if lg.isCreated then
local formatted_path = utils.format_path(path .. tileset.name)
if not STI.cache[formatted_path] then
-- No need to fix transparency color for collections
utils.cache_image(STI, formatted_path, map.image)
tileset.image = map.image
else
tileset.image = STI.cache[formatted_path]
end
end
gid = self:setAtlasTiles(i, tileset, map.coords, gid)
end
end
local layers = {}
@ -174,7 +171,7 @@ end
-- @param plugins A list of plugins to load
function Map:loadPlugins(plugins)
for _, plugin in ipairs(plugins) do
local pluginModulePath = cwd .. 'plugins.' .. plugin
local pluginModulePath = cwd .. "plugins." .. plugin
local ok, pluginModule = pcall(require, pluginModulePath)
if ok then
for k, func in pairs(pluginModule) do
@ -192,19 +189,19 @@ end
-- @param gid First Global ID in Tileset
-- @return number Next Tileset's first Global ID
function Map:setTiles(index, tileset, gid)
local quad = lg.newQuad
local imageW = tileset.imagewidth
local imageH = tileset.imageheight
local tileW = tileset.tilewidth
local tileH = tileset.tileheight
local margin = tileset.margin
local quad = lg.newQuad
local imageW = tileset.imagewidth
local imageH = tileset.imageheight
local tileW = tileset.tilewidth
local tileH = tileset.tileheight
local margin = tileset.margin
local spacing = tileset.spacing
local w = utils.get_tiles(imageW, tileW, margin, spacing)
local h = utils.get_tiles(imageH, tileH, margin, spacing)
local w = utils.get_tiles(imageW, tileW, margin, spacing)
local h = utils.get_tiles(imageH, tileH, margin, spacing)
for y = 1, h do
for x = 1, w do
local id = gid - tileset.firstgid
local id = gid - tileset.firstgid
local quadX = (x - 1) * tileW + margin + (x - 1) * spacing
local quadY = (y - 1) * tileH + margin + (y - 1) * spacing
local type = ""
@ -212,10 +209,10 @@ function Map:setTiles(index, tileset, gid)
for _, tile in pairs(tileset.tiles) do
if tile.id == id then
properties = tile.properties
animation = tile.animation
properties = tile.properties
animation = tile.animation
objectGroup = tile.objectGroup
type = tile.type
type = tile.type
if tile.terrain then
terrain = {}
@ -228,31 +225,27 @@ function Map:setTiles(index, tileset, gid)
end
local tile = {
id = id,
gid = gid,
tileset = index,
type = type,
quad = quad(
quadX, quadY,
tileW, tileH,
imageW, imageH
),
properties = properties or {},
terrain = terrain,
animation = animation,
id = id,
gid = gid,
tileset = index,
type = type,
quad = quad(quadX, quadY, tileW, tileH, imageW, imageH),
properties = properties or {},
terrain = terrain,
animation = animation,
objectGroup = objectGroup,
frame = 1,
time = 0,
width = tileW,
height = tileH,
sx = 1,
sy = 1,
r = 0,
offset = tileset.tileoffset,
frame = 1,
time = 0,
width = tileW,
height = tileH,
sx = 1,
sy = 1,
r = 0,
offset = tileset.tileoffset,
}
self.tiles[gid] = tile
gid = gid + 1
gid = gid + 1
end
end
@ -266,50 +259,46 @@ end
-- @param gid First Global ID in Tileset
-- @return number Next Tileset's first Global ID
function Map:setAtlasTiles(index, tileset, coords, gid)
local quad = lg.newQuad
local imageW = tileset.image:getWidth()
local imageH = tileset.image:getHeight()
local firstgid = tileset.firstgid
for i = 1, #tileset.tiles do
local tile = tileset.tiles[i]
if tile.terrain then
terrain = {}
for j = 1, #tile.terrain do
terrain[j] = tileset.terrains[tile.terrain[j] + 1]
end
end
local tile = {
id = tile.id,
gid = firstgid + tile.id,
tileset = index,
class = tile.class,
quad = quad(
coords[i].x, coords[i].y,
tile.width, tile.height,
imageW, imageH
),
properties = tile.properties or {},
terrain = terrain,
animation = tile.animation,
objectGroup = tile.objectGroup,
frame = 1,
time = 0,
width = tile.width,
height = tile.height,
sx = 1,
sy = 1,
r = 0,
offset = tileset.tileoffset,
}
-- Be aware that in collections self.tiles can be a sparse array
self.tiles[tile.gid] = tile
end
return gid + #tileset.tiles
local quad = lg.newQuad
local imageW = tileset.image:getWidth()
local imageH = tileset.image:getHeight()
local firstgid = tileset.firstgid
for i = 1, #tileset.tiles do
local tile = tileset.tiles[i]
if tile.terrain then
terrain = {}
for j = 1, #tile.terrain do
terrain[j] = tileset.terrains[tile.terrain[j] + 1]
end
end
local tile = {
id = tile.id,
gid = firstgid + tile.id,
tileset = index,
class = tile.class,
quad = quad(coords[i].x, coords[i].y, tile.width, tile.height, imageW, imageH),
properties = tile.properties or {},
terrain = terrain,
animation = tile.animation,
objectGroup = tile.objectGroup,
frame = 1,
time = 0,
width = tile.width,
height = tile.height,
sx = 1,
sy = 1,
r = 0,
offset = tileset.tileoffset,
}
-- Be aware that in collections self.tiles can be a sparse array
self.tiles[tile.gid] = tile
end
return gid + #tileset.tiles
end
--- Create Layers
@ -318,13 +307,19 @@ end
function Map:setLayer(layer, path)
if layer.encoding then
if layer.encoding == "base64" then
assert(require "ffi", "Compressed maps require LuaJIT FFI.\nPlease Switch your interperator to LuaJIT or your Tile Layer Format to \"CSV\".")
assert(
require("ffi"),
'Compressed maps require LuaJIT FFI.\nPlease Switch your interperator to LuaJIT or your Tile Layer Format to "CSV".'
)
local fd = love.data.decode("string", "base64", layer.data)
if not layer.compression then
layer.data = utils.get_decompressed_data(fd)
else
assert(love.data.decompress, "zlib and gzip compression require LOVE 11.0+.\nPlease set your Tile Layer Format to \"Base64 (uncompressed)\" or \"CSV\".")
assert(
love.data.decompress,
'zlib and gzip compression require LOVE 11.0+.\nPlease set your Tile Layer Format to "Base64 (uncompressed)" or "CSV".'
)
if layer.compression == "zlib" then
local data = love.data.decompress("string", "zlib", fd)
@ -339,21 +334,27 @@ function Map:setLayer(layer, path)
end
end
layer.x = (layer.x or 0) + layer.offsetx + self.offsetx
layer.y = (layer.y or 0) + layer.offsety + self.offsety
layer.x = (layer.x or 0) + layer.offsetx + self.offsetx
layer.y = (layer.y or 0) + layer.offsety + self.offsety
layer.update = function() end
if layer.type == "tilelayer" then
self:setTileData(layer)
self:setSpriteBatches(layer)
layer.draw = function() self:drawTileLayer(layer) end
layer.draw = function()
self:drawTileLayer(layer)
end
elseif layer.type == "objectgroup" then
self:setObjectData(layer)
self:setObjectCoordinates(layer)
self:setObjectSpriteBatches(layer)
layer.draw = function() self:drawObjectLayer(layer) end
layer.draw = function()
self:drawObjectLayer(layer)
end
elseif layer.type == "imagelayer" then
layer.draw = function() self:drawImageLayer(layer) end
layer.draw = function()
self:drawImageLayer(layer)
end
if layer.image ~= "" then
local formatted_path = utils.format_path(path .. layer.image)
@ -361,8 +362,8 @@ function Map:setLayer(layer, path)
utils.cache_image(STI, formatted_path)
end
layer.image = STI.cache[formatted_path]
layer.width = layer.image:getWidth()
layer.image = STI.cache[formatted_path]
layer.width = layer.image:getWidth()
layer.height = layer.image:getHeight()
end
end
@ -380,7 +381,7 @@ function Map:setTileData(layer)
return
end
local i = 1
local i = 1
local map = {}
for y = 1, layer.height do
@ -404,7 +405,7 @@ end
-- @param layer The Object Layer
function Map:setObjectData(layer)
for _, object in ipairs(layer.objects) do
object.layer = layer
object.layer = layer
self.objects[object.id] = object
end
end
@ -413,10 +414,10 @@ end
-- @param layer The Object Layer
function Map:setObjectCoordinates(layer)
for _, object in ipairs(layer.objects) do
local x = layer.x + object.x
local y = layer.y + object.y
local w = object.width
local h = object.height
local x = layer.x + object.x
local y = layer.y + object.y
local w = object.width
local h = object.height
local cos = math.cos(math.rad(object.rotation))
local sin = math.sin(math.rad(object.rotation))
@ -424,10 +425,10 @@ function Map:setObjectCoordinates(layer)
object.rectangle = {}
local vertices = {
{ x=x, y=y },
{ x=x + w, y=y },
{ x=x + w, y=y + h },
{ x=x, y=y + h },
{ x = x, y = y },
{ x = x + w, y = y },
{ x = x + w, y = y + h },
{ x = x, y = y + h },
}
for _, vertex in ipairs(vertices) do
@ -444,14 +445,14 @@ function Map:setObjectCoordinates(layer)
end
elseif object.shape == "polygon" then
for _, vertex in ipairs(object.polygon) do
vertex.x = vertex.x + x
vertex.y = vertex.y + y
vertex.x = vertex.x + x
vertex.y = vertex.y + y
vertex.x, vertex.y = utils.rotate_vertex(self, vertex, x, y, cos, sin)
end
elseif object.shape == "polyline" then
for _, vertex in ipairs(object.polyline) do
vertex.x = vertex.x + x
vertex.y = vertex.y + y
vertex.x = vertex.x + x
vertex.y = vertex.y + y
vertex.x, vertex.y = utils.rotate_vertex(self, vertex, x, y, cos, sin)
end
end
@ -527,16 +528,16 @@ end
-- @param number Tile location on Y axis (in tiles)
function Map:addNewLayerTile(layer, chunk, tile, x, y)
local tileset = tile.tileset
local image = self.tilesets[tile.tileset].image
local image = self.tilesets[tile.tileset].image
local batches
local size
if chunk then
batches = chunk.batches
size = chunk.width * chunk.height
size = chunk.width * chunk.height
else
batches = layer.batches
size = layer.width * layer.height
size = layer.width * layer.height
end
batches[tileset] = batches[tileset] or lg.newSpriteBatch(image, size)
@ -547,11 +548,11 @@ function Map:addNewLayerTile(layer, chunk, tile, x, y)
local instance = {
layer = layer,
chunk = chunk,
gid = tile.gid,
x = tileX,
y = tileY,
r = tile.r,
oy = 0
gid = tile.gid,
x = tileX,
y = tileY,
r = tile.r,
oy = 0,
}
-- NOTE: STI can run headless so it is not guaranteed that a batch exists.
@ -575,10 +576,10 @@ function Map:set_batches(layer, chunk)
local offsetX = chunk and chunk.x or 0
local offsetY = chunk and chunk.y or 0
local startX = 1
local startY = 1
local endX = chunk and chunk.width or layer.width
local endY = chunk and chunk.height or layer.height
local startX = 1
local startY = 1
local endX = chunk and chunk.width or layer.width
local endY = chunk and chunk.height or layer.height
local incrementX = 1
local incrementY = 1
@ -683,7 +684,7 @@ end
-- @param layer The Object Layer
function Map:setObjectSpriteBatches(layer)
local newBatch = lg.newSpriteBatch
local batches = {}
local batches = {}
if layer.draworder == "topdown" then
table.sort(layer.objects, function(a, b)
@ -693,13 +694,13 @@ function Map:setObjectSpriteBatches(layer)
for _, object in ipairs(layer.objects) do
if object.gid then
local tile = self.tiles[object.gid] or self:setFlippedGID(object.gid)
local tile = self.tiles[object.gid] or self:setFlippedGID(object.gid)
local tileset = tile.tileset
local image = self.tilesets[tileset].image
local image = self.tilesets[tileset].image
batches[tileset] = batches[tileset] or newBatch(image)
local sx = object.width / tile.width
local sx = object.width / tile.width
local sy = object.height / tile.height
-- Tiled rotates around bottom left corner, where love2D rotates around top left corner
@ -731,14 +732,14 @@ function Map:setObjectSpriteBatches(layer)
end
local instance = {
id = batch:add(tile.quad, tileX, tileY, tileR, tile.sx * sx, tile.sy * sy, ox, oy),
id = batch:add(tile.quad, tileX, tileY, tileR, tile.sx * sx, tile.sy * sy, ox, oy),
batch = batch,
layer = layer,
gid = tile.gid,
x = tileX,
y = tileY - oy,
r = tileR,
oy = oy
gid = tile.gid,
x = tileX,
y = tileY - oy,
r = tileR,
oy = oy,
}
self.tileInstances[tile.gid] = self.tileInstances[tile.gid] or {}
@ -756,12 +757,12 @@ end
function Map:addCustomLayer(name, index)
index = index or #self.layers + 1
local layer = {
type = "customlayer",
name = name,
visible = true,
opacity = 1,
properties = {},
}
type = "customlayer",
name = name,
visible = true,
opacity = 1,
properties = {},
}
function layer.draw() end
function layer.update() end
@ -778,16 +779,16 @@ end
function Map:convertToCustomLayer(index)
local layer = assert(self.layers[index], "Layer not found: " .. index)
layer.type = "customlayer"
layer.x = nil
layer.y = nil
layer.width = nil
layer.height = nil
layer.type = "customlayer"
layer.x = nil
layer.y = nil
layer.width = nil
layer.height = nil
layer.encoding = nil
layer.data = nil
layer.chunks = nil
layer.objects = nil
layer.image = nil
layer.data = nil
layer.chunks = nil
layer.objects = nil
layer.image = nil
function layer.draw() end
function layer.update() end
@ -862,16 +863,19 @@ function Map:update(dt)
tile.time = tile.time + dt * 1000
while tile.time > tonumber(tile.animation[tile.frame].duration) do
update = true
tile.time = tile.time - tonumber(tile.animation[tile.frame].duration)
update = true
tile.time = tile.time - tonumber(tile.animation[tile.frame].duration)
tile.frame = tile.frame + 1
if tile.frame > #tile.animation then tile.frame = 1 end
if tile.frame > #tile.animation then
tile.frame = 1
end
end
if update and self.tileInstances[tile.gid] then
for _, j in pairs(self.tileInstances[tile.gid]) do
local t = self.tiles[tonumber(tile.animation[tile.frame].tileid) + self.tilesets[tile.tileset].firstgid]
local t =
self.tiles[tonumber(tile.animation[tile.frame].tileid) + self.tilesets[tile.tileset].firstgid]
j.batch:set(j.id, t.quad, j.x, j.y, j.r, tile.sx, tile.sy, 0, j.oy)
end
end
@ -948,18 +952,18 @@ end
--- Draw an individual Layer
-- @param layer The Layer to draw
function Map.drawLayer(_, layer)
local r,g,b,a = lg.getColor()
local r, g, b, a = lg.getColor()
-- if the layer has a tintcolor set
if layer.tintcolor then
r, g, b, a = unpack(layer.tintcolor)
a = a or 255 -- alpha may not be specified
lg.setColor(r/255, g/255, b/255, a/255) -- Tiled uses 0-255
lg.setColor(r / 255, g / 255, b / 255, a / 255) -- Tiled uses 0-255
-- if a tintcolor is not given just use the current color
else
lg.setColor(r, g, b, a * layer.opacity)
end
layer:draw()
lg.setColor(r,g,b,a)
lg.setColor(r, g, b, a)
end
--- Default draw function for Tile Layers
@ -996,10 +1000,10 @@ function Map:drawObjectLayer(layer)
assert(layer.type == "objectgroup", "Invalid layer type: " .. layer.type .. ". Layer must be of type: objectgroup")
local line = { 160, 160, 160, 255 * layer.opacity }
local fill = { 160, 160, 160, 255 * layer.opacity * 0.5 }
local r,g,b,a = lg.getColor()
local reset = { r, g, b, a * layer.opacity }
local line = { 160, 160, 160, 255 * layer.opacity }
local fill = { 160, 160, 160, 255 * layer.opacity * 0.5 }
local r, g, b, a = lg.getColor()
local reset = { r, g, b, a * layer.opacity }
local function sortVertices(obj)
local vertex = {}
@ -1058,7 +1062,7 @@ function Map:drawObjectLayer(layer)
for _, batch in pairs(layer.batches) do
lg.draw(batch, 0, 0)
end
lg.setColor(r,g,b,a)
lg.setColor(r, g, b, a)
end
--- Default draw function for Image Layers
@ -1118,51 +1122,51 @@ end
-- @param gid The flagged Global ID
-- @return table Flipped Tile
function Map:setFlippedGID(gid)
local bit31 = 2147483648
local bit30 = 1073741824
local bit29 = 536870912
local flipX = false
local flipY = false
local flipD = false
local bit31 = 2147483648
local bit30 = 1073741824
local bit29 = 536870912
local flipX = false
local flipY = false
local flipD = false
local realgid = gid
if realgid >= bit31 then
realgid = realgid - bit31
flipX = not flipX
flipX = not flipX
end
if realgid >= bit30 then
realgid = realgid - bit30
flipY = not flipY
flipY = not flipY
end
if realgid >= bit29 then
realgid = realgid - bit29
flipD = not flipD
flipD = not flipD
end
local tile = self.tiles[realgid]
local data = {
id = tile.id,
gid = gid,
tileset = tile.tileset,
frame = tile.frame,
time = tile.time,
width = tile.width,
height = tile.height,
offset = tile.offset,
quad = tile.quad,
id = tile.id,
gid = gid,
tileset = tile.tileset,
frame = tile.frame,
time = tile.time,
width = tile.width,
height = tile.height,
offset = tile.offset,
quad = tile.quad,
properties = tile.properties,
terrain = tile.terrain,
animation = tile.animation,
sx = tile.sx,
sy = tile.sy,
r = tile.r,
terrain = tile.terrain,
animation = tile.animation,
sx = tile.sx,
sy = tile.sy,
r = tile.r,
}
if flipX then
if flipY and flipD then
data.r = math.rad(-90)
data.r = math.rad(-90)
data.sy = -1
elseif flipY then
data.sx = -1
@ -1179,7 +1183,7 @@ function Map:setFlippedGID(gid)
data.sy = -1
end
elseif flipD then
data.r = math.rad(90)
data.r = math.rad(90)
data.sy = -1
end
@ -1284,22 +1288,9 @@ function Map:swapTile(instance, tile)
-- Update sprite batch
if instance.batch then
if tile then
instance.batch:set(
instance.id,
tile.quad,
instance.x,
instance.y,
tile.r,
tile.sx,
tile.sy
)
instance.batch:set(instance.id, tile.quad, instance.x, instance.y, tile.r, tile.sx, tile.sy)
else
instance.batch:set(
instance.id,
instance.x,
instance.y,
0,
0)
instance.batch:set(instance.id, instance.x, instance.y, 0, 0)
self.freeBatchSprites[instance.batch] = self.freeBatchSprites[instance.batch] or {}
table.insert(self.freeBatchSprites[instance.batch], instance)
@ -1329,12 +1320,12 @@ function Map:swapTile(instance, tile)
newInstance.layer = instance.layer
newInstance.batch = instance.batch
newInstance.id = instance.id
newInstance.gid = tile.gid or 0
newInstance.x = instance.x
newInstance.y = instance.y
newInstance.r = tile.r or 0
newInstance.oy = tile.r ~= 0 and tile.height or 0
newInstance.id = instance.id
newInstance.gid = tile.gid or 0
newInstance.x = instance.x
newInstance.y = instance.y
newInstance.r = tile.r or 0
newInstance.oy = tile.r ~= 0 and tile.height or 0
table.insert(self.tileInstances[tile.gid], newInstance)
end
end
@ -1344,35 +1335,26 @@ end
-- @param y The Y axis location of the point (in tiles)
-- @return number The X axis location of the point (in pixels)
-- @return number The Y axis location of the point (in pixels)
function Map:convertTileToPixel(x,y)
function Map:convertTileToPixel(x, y)
if self.orientation == "orthogonal" then
local tileW = self.tilewidth
local tileH = self.tileheight
return
x * tileW,
y * tileH
return x * tileW, y * tileH
elseif self.orientation == "isometric" then
local mapH = self.height
local tileW = self.tilewidth
local tileH = self.tileheight
local mapH = self.height
local tileW = self.tilewidth
local tileH = self.tileheight
local offsetX = mapH * tileW / 2
return
(x - y) * tileW / 2 + offsetX,
(x + y) * tileH / 2
elseif self.orientation == "staggered" or
self.orientation == "hexagonal" then
local tileW = self.tilewidth
local tileH = self.tileheight
return (x - y) * tileW / 2 + offsetX, (x + y) * tileH / 2
elseif self.orientation == "staggered" or self.orientation == "hexagonal" then
local tileW = self.tilewidth
local tileH = self.tileheight
local sideLen = self.hexsidelength or 0
if self.staggeraxis == "x" then
return
x * tileW,
ceil(y) * (tileH + sideLen) + (ceil(y) % 2 == 0 and tileH or 0)
return x * tileW, ceil(y) * (tileH + sideLen) + (ceil(y) % 2 == 0 and tileH or 0)
else
return
ceil(x) * (tileW + sideLen) + (ceil(x) % 2 == 0 and tileW or 0),
y * tileH
return ceil(x) * (tileW + sideLen) + (ceil(x) % 2 == 0 and tileW or 0), y * tileH
end
end
end
@ -1386,20 +1368,16 @@ function Map:convertPixelToTile(x, y)
if self.orientation == "orthogonal" then
local tileW = self.tilewidth
local tileH = self.tileheight
return
x / tileW,
y / tileH
return x / tileW, y / tileH
elseif self.orientation == "isometric" then
local mapH = self.height
local tileW = self.tilewidth
local tileH = self.tileheight
local mapH = self.height
local tileW = self.tilewidth
local tileH = self.tileheight
local offsetX = mapH * tileW / 2
return
y / tileH + (x - offsetX) / tileW,
y / tileH - (x - offsetX) / tileW
return y / tileH + (x - offsetX) / tileW, y / tileH - (x - offsetX) / tileW
elseif self.orientation == "staggered" then
local staggerX = self.staggeraxis == "x"
local even = self.staggerindex == "even"
local staggerX = self.staggeraxis == "x"
local even = self.staggerindex == "even"
local function topLeft(x, y)
if staggerX then
@ -1474,48 +1452,48 @@ function Map:convertPixelToTile(x, y)
y = y - (even and tileH / 2 or 0)
end
local halfH = tileH / 2
local ratio = tileH / tileW
local halfH = tileH / 2
local ratio = tileH / tileW
local referenceX = ceil(x / tileW)
local referenceY = ceil(y / tileH)
local relativeX = x - referenceX * tileW
local relativeY = y - referenceY * tileH
local relativeX = x - referenceX * tileW
local relativeY = y - referenceY * tileH
if (halfH - relativeX * ratio > relativeY) then
if halfH - relativeX * ratio > relativeY then
return topLeft(referenceX, referenceY)
elseif (-halfH + relativeX * ratio > relativeY) then
elseif -halfH + relativeX * ratio > relativeY then
return topRight(referenceX, referenceY)
elseif (halfH + relativeX * ratio < relativeY) then
elseif halfH + relativeX * ratio < relativeY then
return bottomLeft(referenceX, referenceY)
elseif (halfH * 3 - relativeX * ratio < relativeY) then
elseif halfH * 3 - relativeX * ratio < relativeY then
return bottomRight(referenceX, referenceY)
end
return referenceX, referenceY
elseif self.orientation == "hexagonal" then
local staggerX = self.staggeraxis == "x"
local even = self.staggerindex == "even"
local tileW = self.tilewidth
local tileH = self.tileheight
local sideLenX = 0
local sideLenY = 0
local colW = tileW / 2
local rowH = tileH / 2
local staggerX = self.staggeraxis == "x"
local even = self.staggerindex == "even"
local tileW = self.tilewidth
local tileH = self.tileheight
local sideLenX = 0
local sideLenY = 0
local colW = tileW / 2
local rowH = tileH / 2
if staggerX then
sideLenX = self.hexsidelength
x = x - (even and tileW or (tileW - sideLenX) / 2)
colW = colW - (colW - sideLenX / 2) / 2
colW = colW - (colW - sideLenX / 2) / 2
else
sideLenY = self.hexsidelength
y = y - (even and tileH or (tileH - sideLenY) / 2)
rowH = rowH - (rowH - sideLenY / 2) / 2
rowH = rowH - (rowH - sideLenY / 2) / 2
end
local referenceX = ceil(x) / (colW * 2)
local referenceY = ceil(y) / (rowH * 2)
-- If in staggered line, then shift reference by 0.5 of other axes
-- If in staggered line, then shift reference by 0.5 of other axes
if staggerX then
if (floor(referenceX) % 2 == 0) == even then
referenceY = referenceY - 0.5
@ -1526,31 +1504,31 @@ function Map:convertPixelToTile(x, y)
end
end
local relativeX = x - referenceX * colW * 2
local relativeY = y - referenceY * rowH * 2
local relativeX = x - referenceX * colW * 2
local relativeY = y - referenceY * rowH * 2
local centers
if staggerX then
local left = sideLenX / 2
local left = sideLenX / 2
local centerX = left + colW
local centerY = tileH / 2
centers = {
{ x = left, y = centerY },
{ x = centerX, y = centerY - rowH },
{ x = centerX, y = centerY + rowH },
{ x = centerX + colW, y = centerY },
{ x = left, y = centerY },
{ x = centerX, y = centerY - rowH },
{ x = centerX, y = centerY + rowH },
{ x = centerX + colW, y = centerY },
}
else
local top = sideLenY / 2
local top = sideLenY / 2
local centerX = tileW / 2
local centerY = top + rowH
centers = {
{ x = centerX, y = top },
{ x = centerX, y = top },
{ x = centerX - colW, y = centerY },
{ x = centerX + colW, y = centerY },
{ x = centerX, y = centerY + rowH }
{ x = centerX, y = centerY + rowH },
}
end
@ -1571,24 +1549,22 @@ function Map:convertPixelToTile(x, y)
end
local offsetsStaggerX = {
{ x = 1, y = 1 },
{ x = 2, y = 0 },
{ x = 2, y = 1 },
{ x = 3, y = 1 },
{ x = 1, y = 1 },
{ x = 2, y = 0 },
{ x = 2, y = 1 },
{ x = 3, y = 1 },
}
local offsetsStaggerY = {
{ x = 1, y = 1 },
{ x = 0, y = 2 },
{ x = 1, y = 2 },
{ x = 1, y = 3 },
{ x = 1, y = 1 },
{ x = 0, y = 2 },
{ x = 1, y = 2 },
{ x = 1, y = 3 },
}
local offsets = staggerX and offsetsStaggerX or offsetsStaggerY
return
referenceX + offsets[nearest].x,
referenceY + offsets[nearest].y
return referenceX + offsets[nearest].x, referenceY + offsets[nearest].y
end
end

@ -4,14 +4,14 @@
-- @copyright 2019
-- @license MIT/X11
local love = _G.love
local utils = require((...):gsub('plugins.box2d', 'utils'))
local lg = require((...):gsub('plugins.box2d', 'graphics'))
local love = _G.love
local utils = require((...):gsub("plugins.box2d", "utils"))
local lg = require((...):gsub("plugins.box2d", "graphics"))
return {
box2d_LICENSE = "MIT/X11",
box2d_URL = "https://github.com/karai17/Simple-Tiled-Implementation",
box2d_VERSION = "2.3.2.7",
box2d_LICENSE = "MIT/X11",
box2d_URL = "https://github.com/karai17/Simple-Tiled-Implementation",
box2d_VERSION = "2.3.2.7",
box2d_DESCRIPTION = "Box2D hooks for STI.",
--- Initialize Box2D physics world.
@ -19,7 +19,7 @@ return {
box2d_init = function(map, world)
assert(love.physics, "To use the Box2D plugin, please enable the love.physics module.")
local body = love.physics.newBody(world, map.offsetx, map.offsety)
local body = love.physics.newBody(world, map.offsetx, map.offsety)
local collision = {
body = body,
}
@ -40,32 +40,32 @@ return {
local currentBody = body
--dynamic are objects/players etc.
if userdata.properties.dynamic == true then
currentBody = love.physics.newBody(world, map.offsetx, map.offsety, 'dynamic')
currentBody = love.physics.newBody(world, map.offsetx, map.offsety, "dynamic")
-- static means it shouldn't move. Things like walls/ground.
elseif userdata.properties.static == true then
currentBody = love.physics.newBody(world, map.offsetx, map.offsety, 'static')
currentBody = love.physics.newBody(world, map.offsetx, map.offsety, "static")
-- kinematic means that the object is static in the game world but effects other bodies
elseif userdata.properties.kinematic == true then
currentBody = love.physics.newBody(world, map.offsetx, map.offsety, 'kinematic')
currentBody = love.physics.newBody(world, map.offsetx, map.offsety, "kinematic")
end
local fixture = love.physics.newFixture(currentBody, shape)
fixture:setUserData(userdata)
-- Set some custom properties from userdata (or use default set by box2d)
fixture:setFriction(userdata.properties.friction or 0.2)
fixture:setFriction(userdata.properties.friction or 0.2)
fixture:setRestitution(userdata.properties.restitution or 0.0)
fixture:setSensor(userdata.properties.sensor or false)
fixture:setSensor(userdata.properties.sensor or false)
fixture:setFilterData(
userdata.properties.categories or 1,
userdata.properties.mask or 65535,
userdata.properties.group or 0
userdata.properties.mask or 65535,
userdata.properties.group or 0
)
local obj = {
object = object,
body = currentBody,
shape = shape,
object = object,
body = currentBody,
shape = shape,
fixture = fixture,
}
@ -84,33 +84,33 @@ return {
local function calculateObjectPosition(object, tile)
local o = {
shape = object.shape,
x = (object.dx or object.x) + map.offsetx,
y = (object.dy or object.y) + map.offsety,
w = object.width,
h = object.height,
polygon = object.polygon or object.polyline or object.ellipse or object.rectangle
shape = object.shape,
x = (object.dx or object.x) + map.offsetx,
y = (object.dy or object.y) + map.offsety,
w = object.width,
h = object.height,
polygon = object.polygon or object.polyline or object.ellipse or object.rectangle,
}
local userdata = {
object = o,
properties = object.properties
object = o,
properties = object.properties,
}
o.r = object.rotation or 0
if o.shape == "rectangle" then
local cos = math.cos(math.rad(o.r))
local sin = math.sin(math.rad(o.r))
local oy = 0
local oy = 0
if object.gid then
local tileset = map.tilesets[map.tiles[object.gid].tileset]
local lid = object.gid - tileset.firstgid
local t = {}
local lid = object.gid - tileset.firstgid
local t = {}
-- This fixes a height issue
o.y = o.y + map.tiles[object.gid].offset.y
oy = o.h
o.y = o.y + map.tiles[object.gid].offset.y
oy = o.h
for _, tt in ipairs(tileset.tiles) do
if tt.id == lid then
@ -133,10 +133,10 @@ return {
end
o.polygon = {
{ x=o.x+0, y=o.y+0 },
{ x=o.x+o.w, y=o.y+0 },
{ x=o.x+o.w, y=o.y+o.h },
{ x=o.x+0, y=o.y+o.h }
{ x = o.x + 0, y = o.y + 0 },
{ x = o.x + o.w, y = o.y + 0 },
{ x = o.x + o.w, y = o.y + o.h },
{ x = o.x + 0, y = o.y + o.h },
}
for _, vertex in ipairs(o.polygon) do
@ -149,7 +149,7 @@ return {
if not o.polygon then
o.polygon = utils.convert_ellipse_to_polygon(o.x, o.y, o.w, o.h)
end
local vertices = getPolygonVertices(o)
local vertices = getPolygonVertices(o)
local triangles = love.math.triangulate(vertices)
for _, triangle in ipairs(triangles) do
@ -167,7 +167,7 @@ return {
end
end
local vertices = getPolygonVertices(o)
local vertices = getPolygonVertices(o)
local triangles = love.math.triangulate(vertices)
for _, triangle in ipairs(triangles) do
@ -197,12 +197,12 @@ return {
-- Every instance of a tile
if tile.properties.collidable == true then
local object = {
shape = "rectangle",
x = instance.x,
y = instance.y,
width = map.tilewidth,
height = map.tileheight,
properties = tile.properties
shape = "rectangle",
x = instance.x,
y = instance.y,
width = map.tilewidth,
height = map.tileheight,
properties = tile.properties,
}
calculateObjectPosition(object, instance)
@ -222,12 +222,12 @@ return {
for _, instance in ipairs(tiles) do
if instance.layer == layer then
local object = {
shape = "rectangle",
x = instance.x,
y = instance.y,
width = tileset.tilewidth,
height = tileset.tileheight,
properties = tile.properties
shape = "rectangle",
x = instance.x,
y = instance.y,
width = tileset.tilewidth,
height = tileset.tileheight,
properties = tile.properties,
}
calculateObjectPosition(object, instance)
@ -240,12 +240,12 @@ return {
end
elseif layer.type == "imagelayer" then
local object = {
shape = "rectangle",
x = layer.x or 0,
y = layer.y or 0,
width = layer.width,
height = layer.height,
properties = layer.properties
shape = "rectangle",
x = layer.x or 0,
y = layer.y or 0,
width = layer.width,
height = layer.height,
properties = layer.properties,
}
calculateObjectPosition(object)
@ -295,7 +295,7 @@ return {
lg.translate(math.floor(tx or 0), math.floor(ty or 0))
for _, obj in ipairs(collision) do
local points = {obj.body:getWorldPoints(obj.shape:getPoints())}
local points = { obj.body:getWorldPoints(obj.shape:getPoints()) }
local shape_type = obj.shape:getType()
if shape_type == "edge" or shape_type == "chain" then
@ -303,12 +303,12 @@ return {
elseif shape_type == "polygon" then
love.graphics.polygon("line", points)
else
error("sti box2d plugin does not support "..shape_type.." shapes")
error("sti box2d plugin does not support " .. shape_type .. " shapes")
end
end
lg.pop()
end
end,
}
--- Custom Properties in Tiled are used to tell this plugin what to do.

@ -4,13 +4,13 @@
-- @copyright 2019
-- @license MIT/X11
local lg = require((...):gsub('plugins.bump', 'graphics'))
local lg = require((...):gsub("plugins.bump", "graphics"))
return {
bump_LICENSE = "MIT/X11",
bump_URL = "https://github.com/karai17/Simple-Tiled-Implementation",
bump_VERSION = "3.1.7.1",
bump_DESCRIPTION = "Bump hooks for STI.",
bump_LICENSE = "MIT/X11",
bump_URL = "https://github.com/karai17/Simple-Tiled-Implementation",
bump_VERSION = "3.1.7.1",
bump_DESCRIPTION = "Bump hooks for STI.",
--- Adds each collidable tile to the Bump world.
-- @param world The Bump world to add objects to.
@ -29,15 +29,14 @@ return {
for _, object in ipairs(tile.objectGroup.objects) do
if object.properties.collidable == true then
local t = {
name = object.name,
type = object.type,
x = instance.x + map.offsetx + object.x,
y = instance.y + map.offsety + object.y,
width = object.width,
height = object.height,
layer = instance.layer,
properties = object.properties
name = object.name,
type = object.type,
x = instance.x + map.offsetx + object.x,
y = instance.y + map.offsety + object.y,
width = object.width,
height = object.height,
layer = instance.layer,
properties = object.properties,
}
world:add(t, t.x, t.y, t.width, t.height)
@ -49,13 +48,13 @@ return {
-- Every instance of a tile
if tile.properties and tile.properties.collidable == true then
local t = {
x = instance.x + map.offsetx,
y = instance.y + map.offsety,
width = map.tilewidth,
height = map.tileheight,
layer = instance.layer,
type = tile.type,
properties = tile.properties
x = instance.x + map.offsetx,
y = instance.y + map.offsety,
width = map.tilewidth,
height = map.tileheight,
layer = instance.layer,
type = tile.type,
properties = tile.properties,
}
world:add(t, t.x, t.y, t.width, t.height)
@ -72,19 +71,18 @@ return {
if layer.type == "tilelayer" then
for y, tiles in ipairs(layer.data) do
for x, tile in pairs(tiles) do
if tile.objectGroup then
for _, object in ipairs(tile.objectGroup.objects) do
if object.properties.collidable == true then
local t = {
name = object.name,
type = object.type,
x = ((x-1) * map.tilewidth + tile.offset.x + map.offsetx) + object.x,
y = ((y-1) * map.tileheight + tile.offset.y + map.offsety) + object.y,
width = object.width,
height = object.height,
layer = layer,
properties = object.properties
name = object.name,
type = object.type,
x = ((x - 1) * map.tilewidth + tile.offset.x + map.offsetx) + object.x,
y = ((y - 1) * map.tileheight + tile.offset.y + map.offsety) + object.y,
width = object.width,
height = object.height,
layer = layer,
properties = object.properties,
}
world:add(t, t.x, t.y, t.width, t.height)
@ -93,15 +91,14 @@ return {
end
end
local t = {
x = (x-1) * map.tilewidth + tile.offset.x + map.offsetx,
y = (y-1) * map.tileheight + tile.offset.y + map.offsety,
width = tile.width,
height = tile.height,
layer = layer,
type = tile.type,
properties = tile.properties
x = (x - 1) * map.tilewidth + tile.offset.x + map.offsetx,
y = (y - 1) * map.tileheight + tile.offset.y + map.offsety,
width = tile.width,
height = tile.height,
layer = layer,
type = tile.type,
properties = tile.properties,
}
world:add(t, t.x, t.y, t.width, t.height)
@ -112,23 +109,23 @@ return {
world:add(layer, layer.x, layer.y, layer.width, layer.height)
table.insert(collidables, layer)
end
end
end
-- individual collidable objects in a layer that is not "collidable"
-- or whole collidable objects layer
if layer.type == "objectgroup" then
if layer.type == "objectgroup" then
for _, obj in ipairs(layer.objects) do
if layer.properties.collidable == true or obj.properties.collidable == true then
if obj.shape == "rectangle" then
local t = {
name = obj.name,
type = obj.type,
x = obj.x + map.offsetx,
y = obj.y + map.offsety,
width = obj.width,
height = obj.height,
layer = layer,
properties = obj.properties
name = obj.name,
type = obj.type,
x = obj.x + map.offsetx,
y = obj.y + map.offsety,
width = obj.width,
height = obj.height,
layer = layer,
properties = obj.properties,
}
if obj.gid then
@ -143,7 +140,7 @@ return {
end
end
map.bump_world = world
map.bump_world = world
map.bump_collidables = collidables
end,
@ -157,11 +154,7 @@ return {
for i = #collidables, 1, -1 do
local obj = collidables[i]
if obj.layer == layer
and (
layer.properties.collidable == true
or obj.properties.collidable == true
) then
if obj.layer == layer and (layer.properties.collidable == true or obj.properties.collidable == true) then
map.bump_world:remove(obj)
table.remove(collidables, i)
end
@ -185,7 +178,7 @@ return {
end
lg.pop()
end
end,
}
--- Custom Properties in Tiled are used to tell this plugin what to do.

@ -3,19 +3,21 @@ local utils = {}
-- https://github.com/stevedonovan/Penlight/blob/master/lua/pl/path.lua#L286
function utils.format_path(path)
local np_gen1,np_gen2 = '[^SEP]+SEP%.%.SEP?','SEP+%.?SEP'
local np_pat1, np_pat2 = np_gen1:gsub('SEP','/'), np_gen2:gsub('SEP','/')
local np_gen1, np_gen2 = "[^SEP]+SEP%.%.SEP?", "SEP+%.?SEP"
local np_pat1, np_pat2 = np_gen1:gsub("SEP", "/"), np_gen2:gsub("SEP", "/")
local k
repeat -- /./ -> /
path,k = path:gsub(np_pat2,'/',1)
path, k = path:gsub(np_pat2, "/", 1)
until k == 0
repeat -- A/../ -> (empty)
path,k = path:gsub(np_pat1,'',1)
path, k = path:gsub(np_pat1, "", 1)
until k == 0
if path == '' then path = '.' end
if path == "" then
path = "."
end
return path
end
@ -25,8 +27,12 @@ function utils.compensate(tile, tileX, tileY, tileW, tileH)
local compx = 0
local compy = 0
if tile.sx < 0 then compx = tileW end
if tile.sy < 0 then compy = tileH end
if tile.sx < 0 then
compx = tileW
end
if tile.sy < 0 then
compy = tileH
end
if tile.r > 0 then
tileX = tileX + tileH - compy
@ -51,13 +57,17 @@ end
-- We just don't know.
function utils.get_tiles(imageW, tileW, margin, spacing)
imageW = imageW - margin
imageW = imageW - margin
local n = 0
while imageW >= tileW do
imageW = imageW - tileW
if n ~= 0 then imageW = imageW - spacing end
if imageW >= 0 then n = n + 1 end
if n ~= 0 then
imageW = imageW - spacing
end
if imageW >= 0 then
n = n + 1
end
end
return n
@ -65,8 +75,8 @@ end
-- Decompress tile layer data
function utils.get_decompressed_data(data)
local ffi = require "ffi"
local d = {}
local ffi = require("ffi")
local d = {}
local decoded = ffi.cast("uint32_t*", data)
for i = 0, data:len() / ffi.sizeof("uint32_t") do
@ -79,8 +89,8 @@ end
-- Convert a Tiled ellipse object to a LOVE polygon
function utils.convert_ellipse_to_polygon(x, y, w, h, max_segments)
local ceil = math.ceil
local cos = math.cos
local sin = math.sin
local cos = math.cos
local sin = math.sin
local function calc_segments(segments)
local function vdist(a, b)
@ -95,7 +105,7 @@ function utils.convert_ellipse_to_polygon(x, y, w, h, max_segments)
segments = segments or 64
local vertices = {}
local v = { 1, 2, ceil(segments/4-1), ceil(segments/4) }
local v = { 1, 2, ceil(segments / 4 - 1), ceil(segments / 4) }
local m
if love and love.physics then
@ -106,8 +116,8 @@ function utils.convert_ellipse_to_polygon(x, y, w, h, max_segments)
for _, i in ipairs(v) do
local angle = (i / segments) * math.pi * 2
local px = x + w / 2 + cos(angle) * w / 2
local py = y + h / 2 + sin(angle) * h / 2
local px = x + w / 2 + cos(angle) * w / 2
local py = y + h / 2 + sin(angle) * h / 2
table.insert(vertices, { x = px / m, y = py / m })
end
@ -117,7 +127,7 @@ function utils.convert_ellipse_to_polygon(x, y, w, h, max_segments)
-- Box2D threshold
if dist1 < 0.0025 or dist2 < 0.0025 then
return calc_segments(segments-2)
return calc_segments(segments - 2)
end
return segments
@ -130,8 +140,8 @@ function utils.convert_ellipse_to_polygon(x, y, w, h, max_segments)
for i = 0, segments do
local angle = (i / segments) * math.pi * 2
local px = x + w / 2 + cos(angle) * w / 2
local py = y + h / 2 + sin(angle) * h / 2
local px = x + w / 2 + cos(angle) * w / 2
local py = y + h / 2 + sin(angle) * h / 2
table.insert(vertices, { x = px, y = py })
end
@ -141,30 +151,26 @@ end
function utils.rotate_vertex(map, vertex, x, y, cos, sin, oy)
if map.orientation == "isometric" then
x, y = utils.convert_isometric_to_screen(map, x, y)
x, y = utils.convert_isometric_to_screen(map, x, y)
vertex.x, vertex.y = utils.convert_isometric_to_screen(map, vertex.x, vertex.y)
end
vertex.x = vertex.x - x
vertex.y = vertex.y - y
return
x + cos * vertex.x - sin * vertex.y,
y + sin * vertex.x + cos * vertex.y - (oy or 0)
return x + cos * vertex.x - sin * vertex.y, y + sin * vertex.x + cos * vertex.y - (oy or 0)
end
--- Project isometric position to cartesian position
function utils.convert_isometric_to_screen(map, x, y)
local mapW = map.width
local tileW = map.tilewidth
local tileH = map.tileheight
local tileX = x / tileH
local tileY = y / tileH
local mapW = map.width
local tileW = map.tilewidth
local tileH = map.tileheight
local tileX = x / tileH
local tileY = y / tileH
local offsetX = mapW * tileW / 2
return
(tileX - tileY) * tileW / 2 + offsetX,
(tileX + tileY) * tileH / 2
return (tileX - tileY) * tileW / 2 + offsetX, (tileX + tileY) * tileH / 2
end
function utils.hex_to_color(hex)
@ -175,16 +181,14 @@ function utils.hex_to_color(hex)
return {
r = tonumber(hex:sub(1, 2), 16) / 255,
g = tonumber(hex:sub(3, 4), 16) / 255,
b = tonumber(hex:sub(5, 6), 16) / 255
b = tonumber(hex:sub(5, 6), 16) / 255,
}
end
function utils.pixel_function(_, _, r, g, b, a)
local mask = utils._TC
if r == mask.r and
g == mask.g and
b == mask.b then
if r == mask.r and g == mask.g and b == mask.b then
return r, g, b, 0
end
@ -205,7 +209,7 @@ end
function utils.deepCopy(t)
local copy = {}
for k,v in pairs(t) do
for k, v in pairs(t) do
if type(v) == "table" then
v = utils.deepCopy(v)
end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,10 +1,14 @@
require 'constants'
require("constants")
function love.run()
if love.load then love.load(love.arg.parseGameArguments(arg), arg) end
if love.load then
love.load(love.arg.parseGameArguments(arg), arg)
end
-- We don't want the first frame's dt to include time taken by love.load.
if love.timer then love.timer.step() end
if love.timer then
love.timer.step()
end
local dt = 0
@ -13,32 +17,40 @@ function love.run()
-- Process events.
if love.event then
love.event.pump()
for name, a,b,c,d,e,f in love.event.poll() do
for name, a, b, c, d, e, f in love.event.poll() do
if name == "quit" then
if not love.quit or not love.quit() then
return a or 0
end
end
love.handlers[name](a,b,c,d,e,f)
love.handlers[name](a, b, c, d, e, f)
end
end
-- Update dt, as we'll be passing it to update
if love.timer then dt = love.timer.step() end
if love.timer then
dt = love.timer.step()
end
-- Call update and draw
if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled
if love.update then
love.update(dt)
end -- will pass 0 if love.timer is disabled
if love.graphics and love.graphics.isActive() then
love.graphics.origin()
love.graphics.clear(love.graphics.getBackgroundColor())
if love.draw then love.draw() end
if love.draw then
love.draw()
end
love.graphics.present()
end
if love.timer then love.timer.sleep(0.001) end
if love.timer then
love.timer.sleep(0.001)
end
end
end
@ -112,12 +124,11 @@ function love.load()
musicStory = love.audio.newSource("music/story.mp3", "stream")
musicPause = musicBattle:clone()
musicPause:setFilter{
type = 'lowpass',
volume = 0.3,
musicPause:setFilter({
type = "lowpass",
volume = 0.7,
highgain = 0.4,
}
})
end
function love.keypressed(key)
@ -130,7 +141,6 @@ function love.keypressed(key)
if _G.GAMESTATE == "GAME" then
GameKeyPressed(key)
end
end
function love.update(dt)
@ -162,6 +172,11 @@ function love.update(dt)
love.audio.play(musicPause)
end
end
if _G.GAMESTATE == "WIN" then
print(P1_WIN)
print(P2_WIN)
love.event.quit()
end
end
function love.draw()
@ -179,5 +194,4 @@ function love.draw()
love.graphics.print("" .. GAMESTATE, 200, 200)
end
end
end

@ -47,79 +47,53 @@ function Player:shoot(bulletSpeed)
return newBullet
end
function handleKeys(dt)
function Player:handleKeys(up, down, left, right, dt)
self.vx, self.vy = 0, 0 --reset every frame
if love.keyboard.isDown(up) then
self.vx = cos(self.rotation) * (self.speed * dt)
self.vy = sin(self.rotation) * (self.speed * dt)
elseif love.keyboard.isDown(down) then
self.vx = cos(self.rotation) * (self.speed * dt) * -1
self.vy = sin(self.rotation) * (self.speed * dt) * -1
elseif love.keyboard.isDown(left) then
self.rotation = self.rotation - (self.rotSpeed * dt)
elseif love.keyboard.isDown(right) then
self.rotation = self.rotation + (self.rotSpeed * dt)
end
self.collider:setLinearVelocity(self.vx, self.vy)
end
function Player:updateCol()
if self.p == 1 then
self.x = self.collider:getX()
self.y = self.collider:getY()
elseif self.p == 2 then
self.x = self.collider:getX()
self.y = self.collider:getY()
end
end
-- Update method for the Player class
function Player:update(dt)
if _G.GAMESTATE == "GAME" then
self.vx = 0
self.vy = 0
local bulletSpeed = 20000
if self.p == 1 then
-- Handle player 1 controls
if love.keyboard.isDown("w") then
self.vx = cos(self.rotation) * (self.speed * dt)
self.vy = sin(self.rotation) * (self.speed * dt)
elseif love.keyboard.isDown("s") then
self.vx = cos(self.rotation) * (self.speed * dt) * -1
self.vy = sin(self.rotation) * (self.speed * dt) * -1
elseif love.keyboard.isDown("a") then
self.rotation = self.rotation - (self.rotSpeed * dt)
elseif love.keyboard.isDown("d") then
self.rotation = self.rotation + (self.rotSpeed * dt)
end
self.collider:setLinearVelocity(self.vx, self.vy)
-- Check for collision with walls
if self.collider:enter("Wall") then
print("Player 1 collided with wall")
end
if EnableKeyPress1 == true and GAMESTATE == "GAME" then
if love.keyboard.isDown("space") then
local newBullet = self:shoot(bulletSpeed)
table.insert(Bullets1, newBullet)
KeyPressTime1 = KeyDelay1
EnableKeyPress1 = false
end
end
self.x = self.collider:getX()
self.y = self.collider:getY()
elseif self.p == 2 then
-- Handle player 2 controls
if love.keyboard.isDown("up") then
self.vx = cos(self.rotation) * (self.speed * dt)
self.vy = sin(self.rotation) * (self.speed * dt)
elseif love.keyboard.isDown("down") then
self.vx = cos(self.rotation) * (self.speed * dt) * -1
self.vy = sin(self.rotation) * (self.speed * dt) * -1
elseif love.keyboard.isDown("left") then
self.rotation = self.rotation - (self.rotSpeed * dt)
elseif love.keyboard.isDown("right") then
self.rotation = self.rotation + (self.rotSpeed * dt)
end
self.collider:setLinearVelocity(self.vx, self.vy)
-- Check for collision with walls
if self.collider:enter("Wall") then
print("Player 2 collided with wall")
end
local bulletSpeed = 20000
if EnableKeyPress1 == true and self.p == 1 then
if love.keyboard.isDown("space") then
local newBullet = self:shoot(bulletSpeed)
table.insert(Bullets1, newBullet)
KeyPressTime1 = KeyDelay1
EnableKeyPress1 = false
end
end
if EnableKeyPress2 == true then
if love.keyboard.isDown("return") then
local newBullet = self:shoot(bulletSpeed)
table.insert(Bullets2, newBullet)
KeyPressTime2 = KeyDelay2
EnableKeyPress2 = false
end
end
if EnableKeyPress2 == true and self.p == 2 then
if love.keyboard.isDown("return") then
local newBullet = self:shoot(bulletSpeed)
table.insert(Bullets2, newBullet)
KeyPressTime2 = KeyDelay2
EnableKeyPress2 = false
end
self.x = self.collider:getX()
self.y = self.collider:getY()
end
end

Loading…
Cancel
Save