format and stuff

main
Simon Kellet 2 months ago
parent 53aa6d698d
commit 99b420cc7f
  1. 8
      Game/GameKeyPressed.lua
  2. 32
      Game/UpdateGame.lua
  3. 1
      Menu/DrawMenu.lua
  4. 7
      Menu/MenuKeyPressed.lua
  5. 3
      Menu/UpdateMenu.lua
  6. 1
      Pause/DrawPause.lua
  7. 6
      Pause/PauseKeyPressed.lua
  8. 3
      Pause/UpdatePause.lua
  9. 12
      constants.lua
  10. BIN
      game.love
  11. 11
      libs/classic.lua
  12. 30
      libs/profile.lua
  13. 57
      libs/sti/atlas.lua
  14. 116
      libs/sti/init.lua
  15. 24
      libs/sti/plugins/box2d.lua
  16. 23
      libs/sti/plugins/bump.lua
  17. 44
      libs/sti/utils.lua
  18. 483
      libs/windfield/init.lua
  19. 411
      libs/windfield/mlib/mlib.lua
  20. 42
      main.lua
  21. 68
      player.lua

@ -1,8 +1,7 @@
function GameKeyPressed(key)
if key == "escape" then
musicBattle:setVolume(0)
musicPause:setVolume(0.5)
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
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

@ -31,7 +31,6 @@ local function title()
love.graphics.print("MENU", 100, 100)
end
function DrawMenu()
local bwidth, bheight = 300, 140
title()

@ -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

@ -22,7 +22,6 @@ local function button(x,y, w, h, text, selected)
love.graphics.print(text, textX, textY)
end
function DrawPause()
local opacity = 0.3
local height = love.graphics.getHeight()

@ -1,5 +1,5 @@
function PauseKeyPressed(key)
if key == 'return' then
if key == "return" then
musicBattle:setVolume(0.5)
musicPause:setVolume(0)
-- 0 Return to game
@ -11,21 +11,17 @@ function PauseKeyPressed(key)
_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
love.event.quit()
end
end
if love.keyboard.isDown("up") then
if _G.PAUSE_POS == 0 then
_G.PAUSE_POS = 0

@ -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,14 +7,10 @@
-- 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 = {}
@ -29,7 +25,6 @@ function Object:extend()
return cls
end
function Object:implement(...)
for _, cls in pairs({ ... }) do
for k, v in pairs(cls) do
@ -40,7 +35,6 @@ function Object:implement(...)
end
end
function Object:is(T)
local mt = getmetatable(self)
while mt do
@ -52,17 +46,14 @@ function Object:is(T)
return false
end
function Object:__tostring()
return "Object"
end
function Object:__call(...)
local obj = setmetatable({}, self)
obj:new(...)
return obj
end
return Object

@ -23,7 +23,7 @@ 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')
info = info or debug.getinfo(2, "fnS")
local f = info.func
-- ignore the profiler itself
if _internal[f] or info.what ~= "Lua" then
@ -45,10 +45,10 @@ function profile.hooker(event, line, info)
_tcalled[f] = nil
end
if event == "tail call" then
local prev = debug.getinfo(3, 'fnS')
local prev = debug.getinfo(3, "fnS")
profile.hooker("return", line, prev)
profile.hooker("call", line, info)
elseif event == 'call' then
elseif event == "call" then
_tcalled[f] = clock()
else
_ncalls[f] = _ncalls[f] + 1
@ -64,7 +64,7 @@ end
--- Starts collecting data.
function profile.start()
if rawget(_G, 'jit') then
if rawget(_G, "jit") then
jit.off()
jit.flush()
end
@ -82,7 +82,7 @@ function profile.stop()
-- merge closures
local lookup = {}
for f, d in pairs(_defined) do
local id = (_labeled[f] or '?')..d
local id = (_labeled[f] or "?") .. d
local f2 = lookup[id]
if f2 then
_ncalls[f2] = _ncalls[f2] + (_ncalls[f] or 0)
@ -93,7 +93,7 @@ function profile.stop()
lookup[id] = f
end
end
collectgarbage('collect')
collectgarbage("collect")
end
--- Resets all collected data.
@ -107,7 +107,7 @@ function profile.reset()
for f in pairs(_tcalled) do
_tcalled[f] = nil
end
collectgarbage('collect')
collectgarbage("collect")
end
--- This is an internal function.
@ -144,7 +144,7 @@ function profile.query(limit)
if _tcalled[f] then
dt = clock() - _tcalled[f]
end
t[i] = { i, _labeled[f] or '?', _ncalls[f], _telapsed[f] + dt, _defined[f] }
t[i] = { i, _labeled[f] or "?", _ncalls[f], _telapsed[f] + dt, _defined[f] }
end
return t
end
@ -165,22 +165,24 @@ function profile.report(n)
s = tostring(s)
local l1 = s:len()
if l1 < l2 then
s = s..(' '):rep(l2-l1)
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, ' | ')
out[i] = table.concat(row, " | ")
end
local row = " +-----+-------------------------------+-------------+--------------------------+----------------------------------+ \n"
local col = " | # | Function | Calls | Time | Code | \n"
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'
sz = sz .. " | " .. table.concat(out, " | \n | ") .. " | \n"
end
return '\n'..sz..row
return "\n" .. sz .. row
end
-- store all internal profiler functions

@ -11,7 +11,6 @@ local module = {}
-- @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
@ -29,14 +28,18 @@ function module.Atlas( files, sort, ids, pow2 )
for i = 1, #files do
images[i] = {}
--images[i].name = files[i]
if ids then images[i].id = ids[i] end
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 )
table.sort(images, function(a, b)
return (a.area > b.area)
end)
end
return images
end
@ -46,16 +49,22 @@ function module.Atlas( files, sort, ids, pow2 )
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
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
if node then
return node
end
end
return nil
end
if w > root.w or h > root.h then return nil end
if w > root.w or h > root.h then
return nil
end
local _w, _h = root.w - w, root.h - h
@ -75,7 +84,9 @@ function module.Atlas( files, sort, ids, pow2 )
end
local function unmap(root)
if not root then return {} end
if not root then
return {}
end
local tree = {}
if root.id then
@ -105,8 +116,12 @@ function module.Atlas( files, sort, ids, pow2 )
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
if w % 1 == 0 then
w = nextpow2(w)
end
if h % 1 == 0 then
h = nextpow2(h)
end
end
repeat
@ -116,14 +131,24 @@ function module.Atlas( files, sort, ids, pow2 )
for i = 1, #images do
node = add(root, i, images[i].w, images[i].h)
if not node then break end
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
if pow2 then
h = h * 2
else
if pow2 then w = w * 2 else w = w + 1 end
h = h + 1
end
else
if pow2 then
w = w * 2
else
w = w + 1
end
end
else
break
@ -142,12 +167,16 @@ function module.Atlas( files, sort, ids, pow2 )
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
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 )
table.sort(coords, function(a, b)
return (a.id < b.id)
end)
end
return { image = map, coords = coords }

@ -9,12 +9,12 @@ local STI = {
_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 cwd = (...):gsub("%.init$", "") .. "."
local utils = require(cwd .. "utils")
local ceil = math.ceil
local floor = math.floor
@ -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 ""
@ -86,7 +83,7 @@ function Map:init(path, plugins, ox, oy)
self.offsety = oy or 0
self.freeBatchSprites = {}
setmetatable(self.freeBatchSprites, { __mode = 'k' })
setmetatable(self.freeBatchSprites, { __mode = "k" })
-- Set tiles, images
local gid = 1
@ -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
@ -232,11 +229,7 @@ function Map:setTiles(index, tileset, gid)
gid = gid,
tileset = index,
type = type,
quad = quad(
quadX, quadY,
tileW, tileH,
imageW, imageH
),
quad = quad(quadX, quadY, tileW, tileH, imageW, imageH),
properties = properties or {},
terrain = terrain,
animation = animation,
@ -286,11 +279,7 @@ function Map:setAtlasTiles(index, tileset, coords, gid)
gid = firstgid + tile.id,
tileset = index,
class = tile.class,
quad = quad(
coords[i].x, coords[i].y,
tile.width, tile.height,
imageW, imageH
),
quad = quad(coords[i].x, coords[i].y, tile.width, tile.height, imageW, imageH),
properties = tile.properties or {},
terrain = terrain,
animation = tile.animation,
@ -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)
@ -346,14 +341,20 @@ function Map:setLayer(layer, path)
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)
@ -551,7 +552,7 @@ function Map:addNewLayerTile(layer, chunk, tile, x, y)
x = tileX,
y = tileY,
r = tile.r,
oy = 0
oy = 0,
}
-- NOTE: STI can run headless so it is not guaranteed that a batch exists.
@ -738,7 +739,7 @@ function Map:setObjectSpriteBatches(layer)
x = tileX,
y = tileY - oy,
r = tileR,
oy = oy
oy = oy,
}
self.tileInstances[tile.gid] = self.tileInstances[tile.gid] or {}
@ -866,12 +867,15 @@ function Map:update(dt)
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
@ -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)
@ -1348,31 +1339,22 @@ 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 offsetX = mapH * tileW / 2
return
(x - y) * tileW / 2 + offsetX,
(x + y) * tileH / 2
elseif self.orientation == "staggered" or
self.orientation == "hexagonal" then
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,17 +1368,13 @@ 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 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"
@ -1481,13 +1459,13 @@ function Map:convertPixelToTile(x, y)
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
@ -1550,7 +1528,7 @@ function Map:convertPixelToTile(x, y)
{ 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
@ -1586,9 +1564,7 @@ function Map:convertPixelToTile(x, y)
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

@ -5,8 +5,8 @@
-- @license MIT/X11
local love = _G.love
local utils = require((...):gsub('plugins.box2d', 'utils'))
local lg = require((...):gsub('plugins.box2d', 'graphics'))
local utils = require((...):gsub("plugins.box2d", "utils"))
local lg = require((...):gsub("plugins.box2d", "graphics"))
return {
box2d_LICENSE = "MIT/X11",
@ -40,13 +40,13 @@ 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)
@ -89,12 +89,12 @@ return {
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
polygon = object.polygon or object.polyline or object.ellipse or object.rectangle,
}
local userdata = {
object = o,
properties = object.properties
properties = object.properties,
}
o.r = object.rotation or 0
@ -136,7 +136,7 @@ return {
{ 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 + o.h },
}
for _, vertex in ipairs(o.polygon) do
@ -202,7 +202,7 @@ return {
y = instance.y,
width = map.tilewidth,
height = map.tileheight,
properties = tile.properties
properties = tile.properties,
}
calculateObjectPosition(object, instance)
@ -227,7 +227,7 @@ return {
y = instance.y,
width = tileset.tilewidth,
height = tileset.tileheight,
properties = tile.properties
properties = tile.properties,
}
calculateObjectPosition(object, instance)
@ -245,7 +245,7 @@ return {
y = layer.y or 0,
width = layer.width,
height = layer.height,
properties = layer.properties
properties = layer.properties,
}
calculateObjectPosition(object)
@ -308,7 +308,7 @@ return {
end
lg.pop()
end
end,
}
--- Custom Properties in Tiled are used to tell this plugin what to do.

@ -4,7 +4,7 @@
-- @copyright 2019
-- @license MIT/X11
local lg = require((...):gsub('plugins.bump', 'graphics'))
local lg = require((...):gsub("plugins.bump", "graphics"))
return {
bump_LICENSE = "MIT/X11",
@ -36,8 +36,7 @@ return {
width = object.width,
height = object.height,
layer = instance.layer,
properties = object.properties
properties = object.properties,
}
world:add(t, t.x, t.y, t.width, t.height)
@ -55,7 +54,7 @@ return {
height = map.tileheight,
layer = instance.layer,
type = tile.type,
properties = tile.properties
properties = tile.properties,
}
world:add(t, t.x, t.y, t.width, t.height)
@ -72,7 +71,6 @@ 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
@ -84,7 +82,7 @@ return {
width = object.width,
height = object.height,
layer = layer,
properties = object.properties
properties = object.properties,
}
world:add(t, t.x, t.y, t.width, t.height)
@ -93,7 +91,6 @@ 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,
@ -101,7 +98,7 @@ return {
height = tile.height,
layer = layer,
type = tile.type,
properties = tile.properties
properties = tile.properties,
}
world:add(t, t.x, t.y, t.width, t.height)
@ -128,7 +125,7 @@ return {
width = obj.width,
height = obj.height,
layer = layer,
properties = obj.properties
properties = obj.properties,
}
if obj.gid then
@ -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
@ -56,8 +62,12 @@ function utils.get_tiles(imageW, tileW, margin, spacing)
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,7 +75,7 @@ end
-- Decompress tile layer data
function utils.get_decompressed_data(data)
local ffi = require "ffi"
local ffi = require("ffi")
local d = {}
local decoded = ffi.cast("uint32_t*", data)
@ -148,9 +158,7 @@ function utils.rotate_vertex(map, vertex, x, y, cos, sin, oy)
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
@ -162,9 +170,7 @@ function utils.convert_isometric_to_screen(map, x, y)
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

@ -20,11 +20,12 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
]]--
]]
--
local path = ... .. '.'
local path = ... .. "."
local wf = {}
wf.Math = require(path .. 'mlib.mlib')
wf.Math = require(path .. "mlib.mlib")
World = {}
World.__index = World
@ -32,14 +33,28 @@ World.__index = World
function wf.newWorld(xg, yg, sleep)
local world = wf.World.new(wf, xg, yg, sleep)
world.box2d_world:setCallbacks(world.collisionOnEnter, world.collisionOnExit, world.collisionPre, world.collisionPost)
world.box2d_world:setCallbacks(
world.collisionOnEnter,
world.collisionOnExit,
world.collisionPre,
world.collisionPost
)
world:collisionClear()
world:addCollisionClass('Default')
world:addCollisionClass("Default")
-- Points all box2d_world functions to this wf.World object
-- This means that the user can call world:setGravity for instance without having to say world.box2d_world:setGravity
for k, v in pairs(world.box2d_world.__index) do
if k ~= '__gc' and k ~= '__eq' and k ~= '__index' and k ~= '__tostring' and k ~= 'update' and k ~= 'destroy' and k ~= 'type' and k ~= 'typeOf' then
if
k ~= "__gc"
and k ~= "__eq"
and k ~= "__index"
and k ~= "__tostring"
and k ~= "update"
and k ~= "destroy"
and k ~= "type"
and k ~= "typeOf"
then
world[k] = function(self, ...)
return v(self.box2d_world, ...)
end
@ -84,18 +99,20 @@ function World:draw(alpha)
for _, body in ipairs(bodies) do
local fixtures = body:getFixtures()
for _, fixture in ipairs(fixtures) do
if fixture:getShape():type() == 'PolygonShape' then
love.graphics.polygon('line', body:getWorldPoints(fixture:getShape():getPoints()))
elseif fixture:getShape():type() == 'EdgeShape' or fixture:getShape():type() == 'ChainShape' then
if fixture:getShape():type() == "PolygonShape" then
love.graphics.polygon("line", body:getWorldPoints(fixture:getShape():getPoints()))
elseif fixture:getShape():type() == "EdgeShape" or fixture:getShape():type() == "ChainShape" then
local points = { body:getWorldPoints(fixture:getShape():getPoints()) }
for i = 1, #points, 2 do
if i < #points-2 then love.graphics.line(points[i], points[i+1], points[i+2], points[i+3]) end
if i < #points - 2 then
love.graphics.line(points[i], points[i + 1], points[i + 2], points[i + 3])
end
elseif fixture:getShape():type() == 'CircleShape' then
end
elseif fixture:getShape():type() == "CircleShape" then
local body_x, body_y = body:getPosition()
local shape_x, shape_y = fixture:getShape():getPoint()
local r = fixture:getShape():getRadius()
love.graphics.circle('line', body_x + shape_x, body_y + shape_y, r, 360)
love.graphics.circle("line", body_x + shape_x, body_y + shape_y, r, 360)
end
end
end
@ -106,8 +123,12 @@ function World:draw(alpha)
local joints = self.box2d_world:getJoints()
for _, joint in ipairs(joints) do
local x1, y1, x2, y2 = joint:getAnchors()
if x1 and y1 then love.graphics.circle('line', x1, y1, 4) end
if x2 and y2 then love.graphics.circle('line', x2, y2, 4) end
if x1 and y1 then
love.graphics.circle("line", x1, y1, 4)
end
if x2 and y2 then
love.graphics.circle("line", x2, y2, 4)
end
end
love.graphics.setColor(255, 255, 255, alpha)
@ -115,15 +136,17 @@ function World:draw(alpha)
love.graphics.setColor(64, 64, 222, alpha)
for _, query_draw in ipairs(self.query_debug_draw) do
query_draw.frames = query_draw.frames - 1
if query_draw.type == 'circle' then
love.graphics.circle('line', query_draw.x, query_draw.y, query_draw.r)
elseif query_draw.type == 'rectangle' then
love.graphics.rectangle('line', query_draw.x, query_draw.y, query_draw.w, query_draw.h)
elseif query_draw.type == 'line' then
if query_draw.type == "circle" then
love.graphics.circle("line", query_draw.x, query_draw.y, query_draw.r)
elseif query_draw.type == "rectangle" then
love.graphics.rectangle("line", query_draw.x, query_draw.y, query_draw.w, query_draw.h)
elseif query_draw.type == "line" then
love.graphics.line(query_draw.x1, query_draw.y1, query_draw.x2, query_draw.y2)
elseif query_draw.type == 'polygon' then
elseif query_draw.type == "polygon" then
local triangles = love.math.triangulate(query_draw.vertices)
for _, triangle in ipairs(triangles) do love.graphics.polygon('line', triangle) end
for _, triangle in ipairs(triangles) do
love.graphics.polygon("line", triangle)
end
end
end
for i = #self.query_debug_draw, 1, -1 do
@ -143,7 +166,9 @@ function World:setExplicitCollisionEvents(value)
end
function World:addCollisionClass(collision_class_name, collision_class)
if self.collision_classes[collision_class_name] then error('Collision class ' .. collision_class_name .. ' already exists.') end
if self.collision_classes[collision_class_name] then
error("Collision class " .. collision_class_name .. " already exists.")
end
if self.explicit_collision_events then
self.collision_classes[collision_class_name] = collision_class or {}
@ -177,10 +202,18 @@ function World:collisionClassesSet()
local collision_table = self:getCollisionCallbacksTable()
for collision_class_name, collision_list in pairs(collision_table) do
for _, collision_info in ipairs(collision_list) do
if collision_info.type == 'enter' then self:addCollisionEnter(collision_class_name, collision_info.other) end
if collision_info.type == 'exit' then self:addCollisionExit(collision_class_name, collision_info.other) end
if collision_info.type == 'pre' then self:addCollisionPre(collision_class_name, collision_info.other) end
if collision_info.type == 'post' then self:addCollisionPost(collision_class_name, collision_info.other) end
if collision_info.type == "enter" then
self:addCollisionEnter(collision_class_name, collision_info.other)
end
if collision_info.type == "exit" then
self:addCollisionExit(collision_class_name, collision_info.other)
end
if collision_info.type == "pre" then
self:addCollisionPre(collision_class_name, collision_info.other)
end
if collision_info.type == "post" then
self:addCollisionPost(collision_class_name, collision_info.other)
end
end
end
@ -214,25 +247,33 @@ end
function World:addCollisionEnter(type1, type2)
if not self:isCollisionBetweenSensors(type1, type2) then
table.insert(self.collisions.on_enter.non_sensor, { type1 = type1, type2 = type2 })
else table.insert(self.collisions.on_enter.sensor, {type1 = type1, type2 = type2}) end
else
table.insert(self.collisions.on_enter.sensor, { type1 = type1, type2 = type2 })
end
end
function World:addCollisionExit(type1, type2)
if not self:isCollisionBetweenSensors(type1, type2) then
table.insert(self.collisions.on_exit.non_sensor, { type1 = type1, type2 = type2 })
else table.insert(self.collisions.on_exit.sensor, {type1 = type1, type2 = type2}) end
else
table.insert(self.collisions.on_exit.sensor, { type1 = type1, type2 = type2 })
end
end
function World:addCollisionPre(type1, type2)
if not self:isCollisionBetweenSensors(type1, type2) then
table.insert(self.collisions.pre.non_sensor, { type1 = type1, type2 = type2 })
else table.insert(self.collisions.pre.sensor, {type1 = type1, type2 = type2}) end
else
table.insert(self.collisions.pre.sensor, { type1 = type1, type2 = type2 })
end
end
function World:addCollisionPost(type1, type2)
if not self:isCollisionBetweenSensors(type1, type2) then
table.insert(self.collisions.post.non_sensor, { type1 = type1, type2 = type2 })
else table.insert(self.collisions.post.sensor, {type1 = type1, type2 = type2}) end
else
table.insert(self.collisions.post.sensor, { type1 = type1, type2 = type2 })
end
end
function World:doesType1IgnoreType2(type1, type2)
@ -246,31 +287,46 @@ function World:doesType1IgnoreType2(type1, type2)
end
local ignored_types = {}
for _, collision_class_type in ipairs(collision_ignores[type1]) do
if collision_class_type == 'All' then
if collision_class_type == "All" then
for _, collision_class_name in ipairs(all) do
table.insert(ignored_types, collision_class_name)
end
else table.insert(ignored_types, collision_class_type) end
else
table.insert(ignored_types, collision_class_type)
end
end
for key, _ in pairs(collision_ignores[type1]) do
if key == 'except' then
if key == "except" then
for _, except_type in ipairs(collision_ignores[type1].except) do
for i = #ignored_types, 1, -1 do
if ignored_types[i] == except_type then table.remove(ignored_types, i) end
if ignored_types[i] == except_type then
table.remove(ignored_types, i)
end
end
end
end
end
for _, ignored_type in ipairs(ignored_types) do
if ignored_type == type2 then return true end
if ignored_type == type2 then
return true
end
end
end
function World:isCollisionBetweenSensors(type1, type2)
if not self.is_sensor_memo[type1] then self.is_sensor_memo[type1] = {} end
if not self.is_sensor_memo[type1][type2] then self.is_sensor_memo[type1][type2] = (self:doesType1IgnoreType2(type1, type2) or self:doesType1IgnoreType2(type2, type1)) end
if self.is_sensor_memo[type1][type2] then return true
else return false end
if not self.is_sensor_memo[type1] then
self.is_sensor_memo[type1] = {}
end
if not self.is_sensor_memo[type1][type2] then
self.is_sensor_memo[type1][type2] = (
self:doesType1IgnoreType2(type1, type2) or self:doesType1IgnoreType2(type2, type1)
)
end
if self.is_sensor_memo[type1][type2] then
return true
else
return false
end
end
-- https://love2d.org/forums/viewtopic.php?f=4&t=75441
@ -289,18 +345,18 @@ function World:generateCategoriesMasks()
end
for object_type, ignore_list in pairs(collision_ignores) do
for key, ignored_type in pairs(ignore_list) do
if ignored_type == 'All' then
if ignored_type == "All" then
for _, all_object_type in ipairs(all) do
table.insert(incoming[all_object_type], object_type)
table.insert(expanded[object_type], all_object_type)
end
elseif type(ignored_type) == 'string' then
if ignored_type ~= 'All' then
elseif type(ignored_type) == "string" then
if ignored_type ~= "All" then
table.insert(incoming[ignored_type], object_type)
table.insert(expanded[object_type], ignored_type)
end
end
if key == 'except' then
if key == "except" then
for _, except_ignored_type in ipairs(ignored_type) do
for i, v in ipairs(incoming[except_ignored_type]) do
if v == object_type then
@ -322,7 +378,9 @@ function World:generateCategoriesMasks()
end
local edge_groups = {}
for k, v in pairs(incoming) do
table.sort(v, function(a, b) return string.lower(a) < string.lower(b) end)
table.sort(v, function(a, b)
return string.lower(a) < string.lower(b)
end)
end
local i = 0
for k, v in pairs(incoming) do
@ -330,7 +388,10 @@ function World:generateCategoriesMasks()
for _, c in ipairs(v) do
str = str .. c
end
if not edge_groups[str] then i = i + 1; edge_groups[str] = {n = i} end
if not edge_groups[str] then
i = i + 1
edge_groups[str] = { n = i }
end
table.insert(edge_groups[str], k)
end
local categories = {}
@ -356,24 +417,39 @@ function World:getCollisionCallbacksTable()
local collision_table = {}
for collision_class_name, collision_class in pairs(self.collision_classes) do
collision_table[collision_class_name] = {}
for _, v in ipairs(collision_class.enter or {}) do table.insert(collision_table[collision_class_name], {type = 'enter', other = v}) end
for _, v in ipairs(collision_class.exit or {}) do table.insert(collision_table[collision_class_name], {type = 'exit', other = v}) end
for _, v in ipairs(collision_class.pre or {}) do table.insert(collision_table[collision_class_name], {type = 'pre', other = v}) end
for _, v in ipairs(collision_class.post or {}) do table.insert(collision_table[collision_class_name], {type = 'post', other = v}) end
for _, v in ipairs(collision_class.enter or {}) do
table.insert(collision_table[collision_class_name], { type = "enter", other = v })
end
for _, v in ipairs(collision_class.exit or {}) do
table.insert(collision_table[collision_class_name], { type = "exit", other = v })
end
for _, v in ipairs(collision_class.pre or {}) do
table.insert(collision_table[collision_class_name], { type = "pre", other = v })
end
for _, v in ipairs(collision_class.post or {}) do
table.insert(collision_table[collision_class_name], { type = "post", other = v })
end
end
return collision_table
end
local function collEnsure(collision_class_name1, a, collision_class_name2, b)
if a.collision_class == collision_class_name2 and b.collision_class == collision_class_name1 then return b, a
else return a, b end
if a.collision_class == collision_class_name2 and b.collision_class == collision_class_name1 then
return b, a
else
return a, b
end
end
local function collIf(collision_class_name1, collision_class_name2, a, b)
if (a.collision_class == collision_class_name1 and b.collision_class == collision_class_name2) or
(a.collision_class == collision_class_name2 and b.collision_class == collision_class_name1) then
if
(a.collision_class == collision_class_name1 and b.collision_class == collision_class_name2)
or (a.collision_class == collision_class_name2 and b.collision_class == collision_class_name1)
then
return true
else return false end
else
return false
end
end
function World.collisionOnEnter(fixture_a, fixture_b, contact)
@ -384,22 +460,33 @@ function World.collisionOnEnter(fixture_a, fixture_b, contact)
for _, collision in ipairs(a.world.collisions.on_enter.sensor) do
if collIf(collision.type1, collision.type2, a, b) then
a, b = collEnsure(collision.type1, a, collision.type2, b)
table.insert(a.collision_events[collision.type2], {collision_type = 'enter', collider_1 = a, collider_2 = b, contact = contact})
table.insert(
a.collision_events[collision.type2],
{ collision_type = "enter", collider_1 = a, collider_2 = b, contact = contact }
)
if collision.type1 == collision.type2 then
table.insert(b.collision_events[collision.type1], {collision_type = 'enter', collider_1 = b, collider_2 = a, contact = contact})
table.insert(
b.collision_events[collision.type1],
{ collision_type = "enter", collider_1 = b, collider_2 = a, contact = contact }
)
end
end
end
end
elseif not (fixture_a:isSensor() or fixture_b:isSensor()) then
if a and b then
for _, collision in ipairs(a.world.collisions.on_enter.non_sensor) do
if collIf(collision.type1, collision.type2, a, b) then
a, b = collEnsure(collision.type1, a, collision.type2, b)
table.insert(a.collision_events[collision.type2], {collision_type = 'enter', collider_1 = a, collider_2 = b, contact = contact})
table.insert(
a.collision_events[collision.type2],
{ collision_type = "enter", collider_1 = a, collider_2 = b, contact = contact }
)
if collision.type1 == collision.type2 then
table.insert(b.collision_events[collision.type1], {collision_type = 'enter', collider_1 = b, collider_2 = a, contact = contact})
table.insert(
b.collision_events[collision.type1],
{ collision_type = "enter", collider_1 = b, collider_2 = a, contact = contact }
)
end
end
end
@ -415,22 +502,33 @@ function World.collisionOnExit(fixture_a, fixture_b, contact)
for _, collision in ipairs(a.world.collisions.on_exit.sensor) do
if collIf(collision.type1, collision.type2, a, b) then
a, b = collEnsure(collision.type1, a, collision.type2, b)
table.insert(a.collision_events[collision.type2], {collision_type = 'exit', collider_1 = a, collider_2 = b, contact = contact})
table.insert(
a.collision_events[collision.type2],
{ collision_type = "exit", collider_1 = a, collider_2 = b, contact = contact }
)
if collision.type1 == collision.type2 then
table.insert(b.collision_events[collision.type1], {collision_type = 'exit', collider_1 = b, collider_2 = a, contact = contact})
table.insert(
b.collision_events[collision.type1],
{ collision_type = "exit", collider_1 = b, collider_2 = a, contact = contact }
)
end
end
end
end
elseif not (fixture_a:isSensor() or fixture_b:isSensor()) then
if a and b then
for _, collision in ipairs(a.world.collisions.on_exit.non_sensor) do
if collIf(collision.type1, collision.type2, a, b) then
a, b = collEnsure(collision.type1, a, collision.type2, b)
table.insert(a.collision_events[collision.type2], {collision_type = 'exit', collider_1 = a, collider_2 = b, contact = contact})
table.insert(
a.collision_events[collision.type2],
{ collision_type = "exit", collider_1 = a, collider_2 = b, contact = contact }
)
if collision.type1 == collision.type2 then
table.insert(b.collision_events[collision.type1], {collision_type = 'exit', collider_1 = b, collider_2 = a, contact = contact})
table.insert(
b.collision_events[collision.type1],
{ collision_type = "exit", collider_1 = b, collider_2 = a, contact = contact }
)
end
end
end
@ -453,7 +551,6 @@ function World.collisionPre(fixture_a, fixture_b, contact)
end
end
end
elseif not (fixture_a:isSensor() or fixture_b:isSensor()) then
if a and b then
for _, collision in ipairs(a.world.collisions.pre.non_sensor) do
@ -484,7 +581,6 @@ function World.collisionPost(fixture_a, fixture_b, contact, ni1, ti1, ni2, ti2)
end
end
end
elseif not (fixture_a:isSensor() or fixture_b:isSensor()) then
if a and b then
for _, collision in ipairs(a.world.collisions.post.non_sensor) do
@ -501,34 +597,36 @@ function World.collisionPost(fixture_a, fixture_b, contact, ni1, ti1, ni2, ti2)
end
function World:newCircleCollider(x, y, r, settings)
return self.wf.Collider.new(self, 'Circle', x, y, r, settings)
return self.wf.Collider.new(self, "Circle", x, y, r, settings)
end
function World:newRectangleCollider(x, y, w, h, settings)
return self.wf.Collider.new(self, 'Rectangle', x, y, w, h, settings)
return self.wf.Collider.new(self, "Rectangle", x, y, w, h, settings)
end
function World:newBSGRectangleCollider(x, y, w, h, corner_cut_size, settings)
return self.wf.Collider.new(self, 'BSGRectangle', x, y, w, h, corner_cut_size, settings)
return self.wf.Collider.new(self, "BSGRectangle", x, y, w, h, corner_cut_size, settings)
end
function World:newPolygonCollider(vertices, settings)
return self.wf.Collider.new(self, 'Polygon', vertices, settings)
return self.wf.Collider.new(self, "Polygon", vertices, settings)
end
function World:newLineCollider(x1, y1, x2, y2, settings)
return self.wf.Collider.new(self, 'Line', x1, y1, x2, y2, settings)
return self.wf.Collider.new(self, "Line", x1, y1, x2, y2, settings)
end
function World:newChainCollider(vertices, loop, settings)
return self.wf.Collider.new(self, 'Chain', vertices, loop, settings)
return self.wf.Collider.new(self, "Chain", vertices, loop, settings)
end
-- Internal AABB box2d query used before going for more specific and precise computations.
function World:_queryBoundingBox(x1, y1, x2, y2)
local colliders = {}
local callback = function(fixture)
if not fixture:isSensor() then table.insert(colliders, fixture:getUserData()) end
if not fixture:isSensor() then
table.insert(colliders, fixture:getUserData())
end
return true
end
self.box2d_world:queryBoundingBox(x1, y1, x2, y2, callback)
@ -536,7 +634,7 @@ function World:_queryBoundingBox(x1, y1, x2, y2)
end
function World:collisionClassInCollisionClassesList(collision_class, collision_classes)
if collision_classes[1] == 'All' then
if collision_classes[1] == "All" then
local all_collision_classes = {}
for class, _ in pairs(self.collision_classes) do
table.insert(all_collision_classes, class)
@ -552,25 +650,43 @@ function World:collisionClassInCollisionClassesList(collision_class, collision_c
end
end
for _, class in ipairs(all_collision_classes) do
if class == collision_class then return true end
if class == collision_class then
return true
end
end
else
for _, class in ipairs(collision_classes) do
if class == collision_class then return true end
if class == collision_class then
return true
end
end
end
end
function World:queryCircleArea(x, y, radius, collision_class_names)
if not collision_class_names then collision_class_names = {'All'} end
if self.query_debug_drawing_enabled then table.insert(self.query_debug_draw, {type = 'circle', x = x, y = y, r = radius, frames = self.draw_query_for_n_frames}) end
if not collision_class_names then
collision_class_names = { "All" }
end
if self.query_debug_drawing_enabled then
table.insert(
self.query_debug_draw,
{ type = "circle", x = x, y = y, r = radius, frames = self.draw_query_for_n_frames }
)
end
local colliders = self:_queryBoundingBox(x - radius, y - radius, x + radius, y + radius)
local outs = {}
for _, collider in ipairs(colliders) do
if self:collisionClassInCollisionClassesList(collider.collision_class, collision_class_names) then
for _, fixture in ipairs(collider.body:getFixtures()) do
if self.wf.Math.polygon.getCircleIntersection(x, y, radius, {collider.body:getWorldPoints(fixture:getShape():getPoints())}) then
if
self.wf.Math.polygon.getCircleIntersection(
x,
y,
radius,
{ collider.body:getWorldPoints(fixture:getShape():getPoints()) }
)
then
table.insert(outs, collider)
break
end
@ -581,15 +697,27 @@ function World:queryCircleArea(x, y, radius, collision_class_names)
end
function World:queryRectangleArea(x, y, w, h, collision_class_names)
if not collision_class_names then collision_class_names = {'All'} end
if self.query_debug_drawing_enabled then table.insert(self.query_debug_draw, {type = 'rectangle', x = x, y = y, w = w, h = h, frames = self.draw_query_for_n_frames}) end
if not collision_class_names then
collision_class_names = { "All" }
end
if self.query_debug_drawing_enabled then
table.insert(
self.query_debug_draw,
{ type = "rectangle", x = x, y = y, w = w, h = h, frames = self.draw_query_for_n_frames }
)
end
local colliders = self:_queryBoundingBox(x, y, x + w, y + h)
local outs = {}
for _, collider in ipairs(colliders) do
if self:collisionClassInCollisionClassesList(collider.collision_class, collision_class_names) then
for _, fixture in ipairs(collider.body:getFixtures()) do
if self.wf.Math.polygon.isPolygonInside({x, y, x+w, y, x+w, y+h, x, y+h}, {collider.body:getWorldPoints(fixture:getShape():getPoints())}) then
if
self.wf.Math.polygon.isPolygonInside(
{ x, y, x + w, y, x + w, y + h, x, y + h },
{ collider.body:getWorldPoints(fixture:getShape():getPoints()) }
)
then
table.insert(outs, collider)
break
end
@ -600,21 +728,35 @@ function World:queryRectangleArea(x, y, w, h, collision_class_names)
end
function World:queryPolygonArea(vertices, collision_class_names)
if not collision_class_names then collision_class_names = {'All'} end
if self.query_debug_drawing_enabled then table.insert(self.query_debug_draw, {type = 'polygon', vertices = vertices, frames = self.draw_query_for_n_frames}) end
if not collision_class_names then
collision_class_names = { "All" }
end
if self.query_debug_drawing_enabled then
table.insert(
self.query_debug_draw,
{ type = "polygon", vertices = vertices, frames = self.draw_query_for_n_frames }
)
end
local cx, cy = self.wf.Math.polygon.getCentroid(vertices)
local d_max = 0
for i = 1, #vertices, 2 do
local d = self.wf.Math.line.getLength(cx, cy, vertices[i], vertices[i + 1])
if d > d_max then d_max = d end
if d > d_max then
d_max = d
end
end
local colliders = self:_queryBoundingBox(cx - d_max, cy - d_max, cx + d_max, cy + d_max)
local outs = {}
for _, collider in ipairs(colliders) do
if self:collisionClassInCollisionClassesList(collider.collision_class, collision_class_names) then
for _, fixture in ipairs(collider.body:getFixtures()) do
if self.wf.Math.polygon.isPolygonInside(vertices, {collider.body:getWorldPoints(fixture:getShape():getPoints())}) then
if
self.wf.Math.polygon.isPolygonInside(
vertices,
{ collider.body:getWorldPoints(fixture:getShape():getPoints()) }
)
then
table.insert(outs, collider)
break
end
@ -625,14 +767,21 @@ function World:queryPolygonArea(vertices, collision_class_names)
end
function World:queryLine(x1, y1, x2, y2, collision_class_names)
if not collision_class_names then collision_class_names = {'All'} end
if not collision_class_names then
collision_class_names = { "All" }
end
if self.query_debug_drawing_enabled then
table.insert(self.query_debug_draw, {type = 'line', x1 = x1, y1 = y1, x2 = x2, y2 = y2, frames = self.draw_query_for_n_frames})
table.insert(
self.query_debug_draw,
{ type = "line", x1 = x1, y1 = y1, x2 = x2, y2 = y2, frames = self.draw_query_for_n_frames }
)
end
local colliders = {}
local callback = function(fixture, ...)
if not fixture:isSensor() then table.insert(colliders, fixture:getUserData()) end
if not fixture:isSensor() then
table.insert(colliders, fixture:getUserData())
end
return 1
end
self.box2d_world:rayCast(x1, y1, x2, y2, callback)
@ -648,9 +797,13 @@ end
function World:addJoint(joint_type, ...)
local args = { ... }
if args[1].body then args[1] = args[1].body end
if type(args[2]) == "table" and args[2].body then args[2] = args[2].body end
local joint = love.physics['new' .. joint_type](unpack(args))
if args[1].body then
args[1] = args[1].body
end
if type(args[2]) == "table" and args[2].body then
args[2] = args[2].body
end
local joint = love.physics["new" .. joint_type](unpack(args))
return joint
end
@ -665,13 +818,13 @@ function World:destroy()
collider:destroy()
end
local joints = self.box2d_world:getJoints()
for _, joint in ipairs(joints) do joint:destroy() end
for _, joint in ipairs(joints) do
joint:destroy()
end
self.box2d_world:destroy()
self.box2d_world = nil
end
local Collider = {}
Collider.__index = Collider
@ -704,40 +857,58 @@ function Collider.new(world, collider_type, ...)
local args = { ... }
local shape, fixture
if self.type == 'Circle' then
self.collision_class = (args[4] and args[4].collision_class) or 'Default'
self.body = love.physics.newBody(self.world.box2d_world, args[1], args[2], (args[4] and args[4].body_type) or 'dynamic')
if self.type == "Circle" then
self.collision_class = (args[4] and args[4].collision_class) or "Default"
self.body =
love.physics.newBody(self.world.box2d_world, args[1], args[2], (args[4] and args[4].body_type) or "dynamic")
shape = love.physics.newCircleShape(args[3])
elseif self.type == 'Rectangle' then
self.collision_class = (args[5] and args[5].collision_class) or 'Default'
self.body = love.physics.newBody(self.world.box2d_world, args[1] + args[3]/2, args[2] + args[4]/2, (args[5] and args[5].body_type) or 'dynamic')
elseif self.type == "Rectangle" then
self.collision_class = (args[5] and args[5].collision_class) or "Default"
self.body = love.physics.newBody(
self.world.box2d_world,
args[1] + args[3] / 2,
args[2] + args[4] / 2,
(args[5] and args[5].body_type) or "dynamic"
)
shape = love.physics.newRectangleShape(args[3], args[4])
elseif self.type == 'BSGRectangle' then
self.collision_class = (args[6] and args[6].collision_class) or 'Default'
self.body = love.physics.newBody(self.world.box2d_world, args[1] + args[3]/2, args[2] + args[4]/2, (args[6] and args[6].body_type) or 'dynamic')
elseif self.type == "BSGRectangle" then
self.collision_class = (args[6] and args[6].collision_class) or "Default"
self.body = love.physics.newBody(
self.world.box2d_world,
args[1] + args[3] / 2,
args[2] + args[4] / 2,
(args[6] and args[6].body_type) or "dynamic"
)
local w, h, s = args[3], args[4], args[5]
shape = love.physics.newPolygonShape({
-w/2, -h/2 + s, -w/2 + s, -h/2,
w/2 - s, -h/2, w/2, -h/2 + s,
w/2, h/2 - s, w/2 - s, h/2,
-w/2 + s, h/2, -w/2, h/2 - s
-w / 2,
-h / 2 + s,
-w / 2 + s,
-h / 2,
w / 2 - s,
-h / 2,
w / 2,
-h / 2 + s,
w / 2,
h / 2 - s,
w / 2 - s,
h / 2,
-w / 2 + s,
h / 2,
-w / 2,
h / 2 - s,
})
elseif self.type == 'Polygon' then
self.collision_class = (args[2] and args[2].collision_class) or 'Default'
self.body = love.physics.newBody(self.world.box2d_world, 0, 0, (args[2] and args[2].body_type) or 'dynamic')
elseif self.type == "Polygon" then
self.collision_class = (args[2] and args[2].collision_class) or "Default"
self.body = love.physics.newBody(self.world.box2d_world, 0, 0, (args[2] and args[2].body_type) or "dynamic")
shape = love.physics.newPolygonShape(unpack(args[1]))
elseif self.type == 'Line' then
self.collision_class = (args[5] and args[5].collision_class) or 'Default'
self.body = love.physics.newBody(self.world.box2d_world, 0, 0, (args[5] and args[5].body_type) or 'dynamic')
elseif self.type == "Line" then
self.collision_class = (args[5] and args[5].collision_class) or "Default"
self.body = love.physics.newBody(self.world.box2d_world, 0, 0, (args[5] and args[5].body_type) or "dynamic")
shape = love.physics.newEdgeShape(args[1], args[2], args[3], args[4])
elseif self.type == 'Chain' then
self.collision_class = (args[3] and args[3].collision_class) or 'Default'
self.body = love.physics.newBody(self.world.box2d_world, 0, 0, (args[3] and args[3].body_type) or 'dynamic')
elseif self.type == "Chain" then
self.collision_class = (args[3] and args[3].collision_class) or "Default"
self.body = love.physics.newBody(self.world.box2d_world, 0, 0, (args[3] and args[3].body_type) or "dynamic")
shape = love.physics.newChainShape(args[1], unpack(args[2]))
end
@ -752,9 +923,9 @@ function Collider.new(world, collider_type, ...)
sensor:setSensor(true)
sensor:setUserData(self)
self.shapes['main'] = shape
self.fixtures['main'] = fixture
self.sensors['main'] = sensor
self.shapes["main"] = shape
self.fixtures["main"] = fixture
self.sensors["main"] = sensor
self.shape = shape
self.fixture = fixture
@ -764,21 +935,45 @@ function Collider.new(world, collider_type, ...)
-- Points all body, fixture and shape functions to this wf.Collider object
-- This means that the user can call collider:setLinearVelocity for instance without having to say collider.body:setLinearVelocity
for k, v in pairs(self.body.__index) do
if k ~= '__gc' and k ~= '__eq' and k ~= '__index' and k ~= '__tostring' and k ~= 'destroy' and k ~= 'type' and k ~= 'typeOf' then
if
k ~= "__gc"
and k ~= "__eq"
and k ~= "__index"
and k ~= "__tostring"
and k ~= "destroy"
and k ~= "type"
and k ~= "typeOf"
then
self[k] = function(self, ...)
return v(self.body, ...)
end
end
end
for k, v in pairs(self.fixture.__index) do
if k ~= '__gc' and k ~= '__eq' and k ~= '__index' and k ~= '__tostring' and k ~= 'destroy' and k ~= 'type' and k ~= 'typeOf' then
if
k ~= "__gc"
and k ~= "__eq"
and k ~= "__index"
and k ~= "__tostring"
and k ~= "destroy"
and k ~= "type"
and k ~= "typeOf"
then
self[k] = function(self, ...)
return v(self.fixture, ...)
end
end
end
for k, v in pairs(self.shape.__index) do
if k ~= '__gc' and k ~= '__eq' and k ~= '__index' and k ~= '__tostring' and k ~= 'destroy' and k ~= 'type' and k ~= 'typeOf' then
if
k ~= "__gc"
and k ~= "__eq"
and k ~= "__index"
and k ~= "__tostring"
and k ~= "destroy"
and k ~= "type"
and k ~= "typeOf"
then
self[k] = function(self, ...)
return v(self.shape, ...)
end
@ -796,7 +991,9 @@ function Collider:collisionEventsClear()
end
function Collider:setCollisionClass(collision_class_name)
if not self.world.collision_classes[collision_class_name] then error("Collision class " .. collision_class_name .. " doesn't exist.") end
if not self.world.collision_classes[collision_class_name] then
error("Collision class " .. collision_class_name .. " doesn't exist.")
end
self.collision_class = collision_class_name
for _, fixture in pairs(self.fixtures) do
if self.world.masks[collision_class_name] then
@ -810,9 +1007,14 @@ function Collider:enter(other_collision_class_name)
local events = self.collision_events[other_collision_class_name]
if events and #events >= 1 then
for _, e in ipairs(events) do
if e.collision_type == 'enter' then
if not self.collision_stay[other_collision_class_name] then self.collision_stay[other_collision_class_name] = {} end
table.insert(self.collision_stay[other_collision_class_name], {collider = e.collider_2, contact = e.contact})
if e.collision_type == "enter" then
if not self.collision_stay[other_collision_class_name] then
self.collision_stay[other_collision_class_name] = {}
end
table.insert(
self.collision_stay[other_collision_class_name],
{ collider = e.collider_2, contact = e.contact }
)
self.enter_collision_data[other_collision_class_name] = { collider = e.collider_2, contact = e.contact }
return true
end
@ -828,11 +1030,13 @@ function Collider:exit(other_collision_class_name)
local events = self.collision_events[other_collision_class_name]
if events and #events >= 1 then
for _, e in ipairs(events) do
if e.collision_type == 'exit' then
if e.collision_type == "exit" then
if self.collision_stay[other_collision_class_name] then
for i = #self.collision_stay[other_collision_class_name], 1, -1 do
local collision_stay = self.collision_stay[other_collision_class_name][i]
if collision_stay.collider.id == e.collider_2.id then table.remove(self.collision_stay[other_collision_class_name], i) end
if collision_stay.collider.id == e.collider_2.id then
table.remove(self.collision_stay[other_collision_class_name], i)
end
end
end
self.exit_collision_data[other_collision_class_name] = { collider = e.collider_2, contact = e.contact }
@ -875,9 +1079,11 @@ function Collider:getObject()
end
function Collider:addShape(shape_name, shape_type, ...)
if self.shapes[shape_name] or self.fixtures[shape_name] then error("Shape/fixture " .. shape_name .. " already exists.") end
if self.shapes[shape_name] or self.fixtures[shape_name] then
error("Shape/fixture " .. shape_name .. " already exists.")
end
local args = { ... }
local shape = love.physics['new' .. shape_type](unpack(args))
local shape = love.physics["new" .. shape_type](unpack(args))
local fixture = love.physics.newFixture(self.body, shape)
if self.world.masks[self.collision_class] then
fixture:setCategory(unpack(self.world.masks[self.collision_class].categories))
@ -894,7 +1100,9 @@ function Collider:addShape(shape_name, shape_type, ...)
end
function Collider:removeShape(shape_name)
if not self.shapes[shape_name] then return end
if not self.shapes[shape_name] then
return
end
self.shapes[shape_name] = nil
self.fixtures[shape_name]:setUserData(nil)
self.fixtures[shape_name]:destroy()
@ -926,4 +1134,3 @@ wf.World = World
wf.Collider = Collider
return wf

@ -21,13 +21,17 @@ local unpack = table.unpack or unpack
-- Used to handle variable-argument functions and whether they are passed as func{ table } or func( unpack( table ) )
local function checkInput(...)
local input = {}
if type( ... ) ~= 'table' then input = { ... } else input = ... end
if type(...) ~= "table" then
input = { ... }
else
input = ...
end
return input
end
-- Deals with floats / verify false false values. This can happen because of significant figures.
local function checkFuzzy(number1, number2)
return ( number1 - .00001 <= number2 and number2 <= number1 + .00001 )
return (number1 - 0.00001 <= number2 and number2 <= number1 + 0.00001)
end
-- Remove multiple occurrences from a table.
@ -37,7 +41,12 @@ local function removeDuplicatePairs( tab )
for index2 = #tab, 1, -1 do
local second = tab[index2]
if index1 ~= index2 then
if type( first[1] ) == 'number' and type( second[1] ) == 'number' and type( first[2] ) == 'number' and type( second[2] ) == 'number' then
if
type(first[1]) == "number"
and type(second[1]) == "number"
and type(first[2]) == "number"
and type(second[2]) == "number"
then
if checkFuzzy(first[1], second[1]) and checkFuzzy(first[2], second[2]) then
table.remove(tab, index1)
end
@ -50,19 +59,29 @@ local function removeDuplicatePairs( tab )
return tab
end
local function removeDuplicates4Points(tab)
for index1 = #tab, 1, -1 do
local first = tab[index1]
for index2 = #tab, 1, -1 do
local second = tab[index2]
if index1 ~= index2 then
if type( first[1] ) ~= type( second[1] ) then return false end
if type( first[2] ) == 'number' and type( second[2] ) == 'number' and type( first[3] ) == 'number' and type( second[3] ) == 'number' then
if type(first[1]) ~= type(second[1]) then
return false
end
if
type(first[2]) == "number"
and type(second[2]) == "number"
and type(first[3]) == "number"
and type(second[3]) == "number"
then
if checkFuzzy(first[2], second[2]) and checkFuzzy(first[3], second[3]) then
table.remove(tab, index1)
end
elseif checkFuzzy( first[1], second[1] ) and checkFuzzy( first[2], second[2] ) and checkFuzzy( first[3], second[3] ) then
elseif
checkFuzzy(first[1], second[1])
and checkFuzzy(first[2], second[2])
and checkFuzzy(first[3], second[3])
then
table.remove(tab, index1)
end
end
@ -71,7 +90,6 @@ local function removeDuplicates4Points( tab )
return tab
end
-- Add points to the table.
local function addPoints(tab, x, y)
tab[#tab + 1] = x
@ -86,7 +104,8 @@ local function removeDuplicatePointsFlat( tab )
local x1, y1 = tab[i], tab[i + 1]
local x2, y2 = tab[ii], tab[ii + 1]
if checkFuzzy(x1, x2) and checkFuzzy(y1, y2) then
table.remove( tab, ii ); table.remove( tab, ii + 1 )
table.remove(tab, ii)
table.remove(tab, ii + 1)
end
end
end
@ -94,16 +113,22 @@ local function removeDuplicatePointsFlat( tab )
return tab
end
-- Check if input is actually a number
local function validateNumber(n)
if type( n ) ~= 'number' then return false
elseif n ~= n then return false -- nan
elseif math.abs( n ) == math.huge then return false
else return true end
if type(n) ~= "number" then
return false
elseif n ~= n then
return false -- nan
elseif math.abs(n) == math.huge then
return false
else
return true
end
end
local function cycle( tab, index ) return tab[( index - 1 ) % #tab + 1] end
local function cycle(tab, index)
return tab[(index - 1) % #tab + 1]
end
local function getGreatestPoint(points, offset)
offset = offset or 1
@ -134,7 +159,8 @@ end -- }}}
-- Points -------------------------------------- {{{
local function rotatePoint(x, y, rotation, ox, oy)
ox, oy = ox or 0, oy or 0
return ( x - ox ) * math.cos( rotation ) + ox - ( y - oy ) * math.sin( rotation ), ( x - ox ) * math.sin( rotation ) + ( y - oy ) * math.cos( rotation ) + oy
return (x - ox) * math.cos(rotation) + ox - (y - oy) * math.sin(rotation),
(x - ox) * math.sin(rotation) + (y - oy) * math.cos(rotation) + oy
end
local function scalePoint(x, y, scale, ox, oy)
@ -157,7 +183,9 @@ end
-- Gives the slope of a line.
local function getSlope(x1, y1, x2, y2)
if checkFuzzy( x1, x2 ) then return false end -- Technically it's undefined, but this is easier to program.
if checkFuzzy(x1, x2) then
return false
end -- Technically it's undefined, but this is easier to program.
return (y1 - y2) / (x1 - x2)
end
@ -174,9 +202,13 @@ local function getPerpendicularSlope( ... )
slope = unpack(input)
end
if not slope then return 0 -- Vertical lines become horizontal.
elseif checkFuzzy( slope, 0 ) then return false -- Horizontal lines become vertical.
else return -1 / slope end
if not slope then
return 0 -- Vertical lines become horizontal.
elseif checkFuzzy(slope, 0) then
return false -- Horizontal lines become vertical.
else
return -1 / slope
end
end
-- Gives the y-intercept of a line.
@ -192,7 +224,9 @@ local function getYIntercept( x, y, ... )
slope = getSlope(x, y, unpack(input))
end
if not slope then return x, true end -- This way we have some information on the line.
if not slope then
return x, true
end -- This way we have some information on the line.
return y - slope * x, false
end
@ -343,7 +377,11 @@ local function getLineSegmentIntersection( x1, y1, x2, y2, ... )
end
end
if length1 <= distance and length2 <= distance then return x, y else return false end
if length1 <= distance and length2 <= distance then
return x, y
else
return false
end
end
-- Checks if a point is on a line.
@ -370,7 +408,9 @@ end
local function checkSegmentPoint(px, py, x1, y1, x2, y2)
-- Explanation around 5:20: https://www.youtube.com/watch?v=A86COO8KC58
local x = checkLinePoint(px, py, x1, y1, x2, y2)
if not x then return false end
if not x then
return false
end
local lengthX = x2 - x1
local lengthY = y2 - y1
@ -378,22 +418,38 @@ local function checkSegmentPoint( px, py, x1, y1, x2, y2 )
if checkFuzzy(lengthX, 0) then -- Vertical line
if checkFuzzy(px, x1) then
local low, high
if y1 > y2 then low = y2; high = y1
else low = y1; high = y2 end
if y1 > y2 then
low = y2
high = y1
else
low = y1
high = y2
end
if py >= low and py <= high then return true
else return false end
if py >= low and py <= high then
return true
else
return false
end
else
return false
end
elseif checkFuzzy(lengthY, 0) then -- Horizontal line
if checkFuzzy(py, y1) then
local low, high
if x1 > x2 then low = x2; high = x1
else low = x1; high = x2 end
if x1 > x2 then
low = x2
high = x1
else
low = x1
high = x2
end
if px >= low and px <= high then return true
else return false end
if px >= low and px <= high then
return true
else
return false
end
else
return false
end
@ -418,13 +474,23 @@ local function getSegmentSegmentIntersection( x1, y1, x2, y2, x3, y3, x4, y4 )
if ((slope1 and slope2) and checkFuzzy(slope1, slope2)) or (not slope1 and not slope2) then -- Parallel lines
if checkFuzzy(intercept1, intercept2) then -- The same lines, possibly in different points.
local points = {}
if checkSegmentPoint( x1, y1, x3, y3, x4, y4 ) then addPoints( points, x1, y1 ) end
if checkSegmentPoint( x2, y2, x3, y3, x4, y4 ) then addPoints( points, x2, y2 ) end
if checkSegmentPoint( x3, y3, x1, y1, x2, y2 ) then addPoints( points, x3, y3 ) end
if checkSegmentPoint( x4, y4, x1, y1, x2, y2 ) then addPoints( points, x4, y4 ) end
if checkSegmentPoint(x1, y1, x3, y3, x4, y4) then
addPoints(points, x1, y1)
end
if checkSegmentPoint(x2, y2, x3, y3, x4, y4) then
addPoints(points, x2, y2)
end
if checkSegmentPoint(x3, y3, x1, y1, x2, y2) then
addPoints(points, x3, y3)
end
if checkSegmentPoint(x4, y4, x1, y1, x2, y2) then
addPoints(points, x4, y4)
end
points = removeDuplicatePointsFlat(points)
if #points == 0 then return false end
if #points == 0 then
return false
end
return unpack(points)
else
return false
@ -446,7 +512,9 @@ end
-- Checks if a number is prime.
local function isPrime(number)
if number < 2 then return false end
if number < 2 then
return false
end
for i = 2, math.sqrt(number) do
if number % i == 0 then
@ -459,7 +527,7 @@ end
-- Rounds a number to the xth decimal place (round( 3.14159265359, 4 ) --> 3.1416)
local function round(number, place)
local pow = 10 ^ (place or 0)
return math.floor( number * pow + .5 ) / pow
return math.floor(number * pow + 0.5) / pow
end
-- Gives the summation given a local function
@ -491,7 +559,9 @@ end
-- Returns the quadratic roots of an equation.
local function getQuadraticRoots(a, b, c)
local discriminant = b ^ 2 - (4 * a * c)
if discriminant < 0 then return false end
if discriminant < 0 then
return false
end
discriminant = math.sqrt(discriminant)
local denominator = (2 * a)
return (-b - discriminant) / denominator, (-b + discriminant) / denominator
@ -534,35 +604,39 @@ local function getCircleLineIntersection( circleX, circleY, radius, x1, y1, x2,
if slope then
local a = (1 + slope ^ 2)
local b = ( -2 * ( circleX ) + ( 2 * slope * intercept ) - ( 2 * circleY * slope ) )
local c = ( circleX ^ 2 + intercept ^ 2 - 2 * ( circleY ) * ( intercept ) + circleY ^ 2 - radius ^ 2 )
local b = (-2 * circleX + (2 * slope * intercept) - (2 * circleY * slope))
local c = (circleX ^ 2 + intercept ^ 2 - 2 * circleY * intercept + circleY ^ 2 - radius ^ 2)
x1, x2 = getQuadraticRoots(a, b, c)
if not x1 then return false end
if not x1 then
return false
end
y1 = slope * x1 + intercept
y2 = slope * x2 + intercept
if checkFuzzy(x1, x2) and checkFuzzy(y1, y2) then
return 'tangent', x1, y1
return "tangent", x1, y1
else
return 'secant', x1, y1, x2, y2
return "secant", x1, y1, x2, y2
end
else -- Vertical Lines
local lengthToPoint1 = circleX - x1
local remainingDistance = lengthToPoint1 - radius
local intercept = math.sqrt(-(lengthToPoint1 ^ 2 - radius ^ 2))
if -( lengthToPoint1 ^ 2 - radius ^ 2 ) < 0 then return false end
if -(lengthToPoint1 ^ 2 - radius ^ 2) < 0 then
return false
end
local bottomX, bottomY = x1, circleY - intercept
local topX, topY = x1, circleY + intercept
if topY ~= bottomY then
return 'secant', topX, topY, bottomX, bottomY
return "secant", topX, topY, bottomX, bottomY
else
return 'tangent', topX, topY
return "tangent", topX, topY
end
end
end
@ -570,32 +644,36 @@ end
-- Gives the type of intersection of a line segment.
local function getCircleSegmentIntersection(circleX, circleY, radius, x1, y1, x2, y2)
local Type, x3, y3, x4, y4 = getCircleLineIntersection(circleX, circleY, radius, x1, y1, x2, y2)
if not Type then return false end
if not Type then
return false
end
local slope, intercept = getSlope(x1, y1, x2, y2), getYIntercept(x1, y1, x2, y2)
if isPointOnCircle(x1, y1, circleX, circleY, radius) and isPointOnCircle(x2, y2, circleX, circleY, radius) then -- Both points are on line-segment.
return 'chord', x1, y1, x2, y2
return "chord", x1, y1, x2, y2
end
if slope then
if checkCirclePoint( x1, y1, circleX, circleY, radius ) and checkCirclePoint( x2, y2, circleX, circleY, radius ) then -- Line-segment is fully in circle.
return 'enclosed', x1, y1, x2, y2
if
checkCirclePoint(x1, y1, circleX, circleY, radius) and checkCirclePoint(x2, y2, circleX, circleY, radius)
then -- Line-segment is fully in circle.
return "enclosed", x1, y1, x2, y2
elseif x3 and x4 then
if checkSegmentPoint(x3, y3, x1, y1, x2, y2) and not checkSegmentPoint(x4, y4, x1, y1, x2, y2) then -- Only the first of the points is on the line-segment.
return 'tangent', x3, y3
return "tangent", x3, y3
elseif checkSegmentPoint(x4, y4, x1, y1, x2, y2) and not checkSegmentPoint(x3, y3, x1, y1, x2, y2) then -- Only the second of the points is on the line-segment.
return 'tangent', x4, y4
return "tangent", x4, y4
else -- Neither of the points are on the circle (means that the segment is not on the circle, but "encasing" the circle)
if checkSegmentPoint(x3, y3, x1, y1, x2, y2) and checkSegmentPoint(x4, y4, x1, y1, x2, y2) then
return 'secant', x3, y3, x4, y4
return "secant", x3, y3, x4, y4
else
return false
end
end
elseif not x4 then -- Is a tangent.
if checkSegmentPoint(x3, y3, x1, y1, x2, y2) then
return 'tangent', x3, y3
return "tangent", x3, y3
else -- Neither of the points are on the line-segment (means that the segment is not on the circle or "encasing" the circle).
local length = getLength(x1, y1, x2, y2)
local distance1 = getLength(x1, y1, x3, y3)
@ -606,7 +684,7 @@ local function getCircleSegmentIntersection( circleX, circleY, radius, x1, y1, x
elseif length < distance1 and length < distance2 then
return false
else
return 'tangent', x3, y3
return "tangent", x3, y3
end
end
end
@ -615,7 +693,9 @@ local function getCircleSegmentIntersection( circleX, circleY, radius, x1, y1, x
local remainingDistance = lengthToPoint1 - radius
local intercept = math.sqrt(-(lengthToPoint1 ^ 2 - radius ^ 2))
if -( lengthToPoint1 ^ 2 - radius ^ 2 ) < 0 then return false end
if -(lengthToPoint1 ^ 2 - radius ^ 2) < 0 then
return false
end
local topX, topY = x1, circleY - intercept
local bottomX, bottomY = x1, circleY + intercept
@ -625,18 +705,20 @@ local function getCircleSegmentIntersection( circleX, circleY, radius, x1, y1, x
local distance2 = getLength(x2, y2, topX, topY)
if bottomY ~= topY then -- Not a tangent
if checkSegmentPoint( topX, topY, x1, y1, x2, y2 ) and checkSegmentPoint( bottomX, bottomY, x1, y1, x2, y2 ) then
return 'chord', topX, topY, bottomX, bottomY
if
checkSegmentPoint(topX, topY, x1, y1, x2, y2) and checkSegmentPoint(bottomX, bottomY, x1, y1, x2, y2)
then
return "chord", topX, topY, bottomX, bottomY
elseif checkSegmentPoint(topX, topY, x1, y1, x2, y2) then
return 'tangent', topX, topY
return "tangent", topX, topY
elseif checkSegmentPoint(bottomX, bottomY, x1, y1, x2, y2) then
return 'tangent', bottomX, bottomY
return "tangent", bottomX, bottomY
else
return false
end
else -- Tangent
if checkSegmentPoint(topX, topY, x1, y1, x2, y2) then
return 'tangent', topX, topY
return "tangent", topX, topY
else
return false
end
@ -647,9 +729,15 @@ end
-- Checks if one circle intersects another circle.
local function getCircleCircleIntersection(circle1x, circle1y, radius1, circle2x, circle2y, radius2)
local length = getLength(circle1x, circle1y, circle2x, circle2y)
if length > radius1 + radius2 then return false end -- If the distance is greater than the two radii, they can't intersect.
if checkFuzzy( length, 0 ) and checkFuzzy( radius1, radius2 ) then return 'equal' end
if checkFuzzy( circle1x, circle2x ) and checkFuzzy( circle1y, circle2y ) then return 'collinear' end
if length > radius1 + radius2 then
return false
end -- If the distance is greater than the two radii, they can't intersect.
if checkFuzzy(length, 0) and checkFuzzy(radius1, radius2) then
return "equal"
end
if checkFuzzy(circle1x, circle2x) and checkFuzzy(circle1y, circle2y) then
return "collinear"
end
local a = (radius1 * radius1 - radius2 * radius2 + length * length) / (2 * length)
local h = math.sqrt(radius1 * radius1 - a * a)
@ -662,25 +750,31 @@ local function getCircleCircleIntersection( circle1x, circle1y, radius1, circle2
local p4y = p2y + h * (circle2x - circle1x) / length
if not validateNumber(p3x) or not validateNumber(p3y) or not validateNumber(p4x) or not validateNumber(p4y) then
return 'inside'
return "inside"
end
if checkFuzzy( length, radius1 + radius2 ) or checkFuzzy( length, math.abs( radius1 - radius2 ) ) then return 'tangent', p3x, p3y end
return 'intersection', p3x, p3y, p4x, p4y
if checkFuzzy(length, radius1 + radius2) or checkFuzzy(length, math.abs(radius1 - radius2)) then
return "tangent", p3x, p3y
end
return "intersection", p3x, p3y, p4x, p4y
end
-- Checks if circle1 is entirely inside of circle2.
local function isCircleCompletelyInsideCircle(circle1x, circle1y, circle1radius, circle2x, circle2y, circle2radius)
if not checkCirclePoint( circle1x, circle1y, circle2x, circle2y, circle2radius ) then return false end
if not checkCirclePoint(circle1x, circle1y, circle2x, circle2y, circle2radius) then
return false
end
local Type = getCircleCircleIntersection(circle2x, circle2y, circle2radius, circle1x, circle1y, circle1radius)
if ( Type ~= 'tangent' and Type ~= 'collinear' and Type ~= 'inside' ) then return false end
if Type ~= "tangent" and Type ~= "collinear" and Type ~= "inside" then
return false
end
return true
end
-- Checks if a line-segment is entirely within a circle.
local function isSegmentCompletelyInsideCircle(circleX, circleY, circleRadius, x1, y1, x2, y2)
local Type = getCircleSegmentIntersection(circleX, circleY, circleRadius, x1, y1, x2, y2)
return Type == 'enclosed'
return Type == "enclosed"
end -- }}}
-- Polygon -------------------------------------- {{{
@ -693,12 +787,13 @@ local function getSignedPolygonArea( ... )
points[#points + 1] = points[1]
points[#points + 1] = points[2]
return ( .5 * getSummation( 1, #points / 2,
function( index )
return (
0.5
* getSummation(1, #points / 2, function(index)
index = index * 2 - 1 -- Convert it to work properly.
return ((points[index] * cycle(points, index + 3)) - (cycle(points, index + 2) * points[index + 1]))
end
) )
end)
)
end
-- Simply returns the area of the polygon.
@ -713,8 +808,11 @@ local function getTriangleHeight( base, ... )
local input = checkInput(...)
local area
if #input == 1 then area = input[1] -- Given area.
else area = getPolygonArea( input ) end -- Given coordinates.
if #input == 1 then
area = input[1] -- Given area.
else
area = getPolygonArea(input)
end -- Given coordinates.
return (2 * area) / base, area
end
@ -729,19 +827,27 @@ local function getCentroid( ... )
local area = getSignedPolygonArea(points) -- Needs to be signed here in case points are counter-clockwise.
-- This formula: https://en.wikipedia.org/wiki/Centroid#Centroid_of_polygon
local centroidX = ( 1 / ( 6 * area ) ) * ( getSummation( 1, #points / 2,
function( index )
local centroidX = (1 / (6 * area))
* (
getSummation(1, #points / 2, function(index)
index = index * 2 - 1 -- Convert it to work properly.
return ( ( points[index] + cycle( points, index + 2 ) ) * ( ( points[index] * cycle( points, index + 3 ) ) - ( cycle( points, index + 2 ) * points[index + 1] ) ) )
end
) )
local centroidY = ( 1 / ( 6 * area ) ) * ( getSummation( 1, #points / 2,
function( index )
return (
(points[index] + cycle(points, index + 2))
* ((points[index] * cycle(points, index + 3)) - (cycle(points, index + 2) * points[index + 1]))
)
end)
)
local centroidY = (1 / (6 * area))
* (
getSummation(1, #points / 2, function(index)
index = index * 2 - 1 -- Convert it to work properly.
return ( ( points[index + 1] + cycle( points, index + 3 ) ) * ( ( points[index] * cycle( points, index + 3 ) ) - ( cycle( points, index + 2 ) * points[index + 1] ) ) )
end
) )
return (
(points[index + 1] + cycle(points, index + 3))
* ((points[index] * cycle(points, index + 3)) - (cycle(points, index + 2) * points[index + 1]))
)
end)
)
return centroidX, centroidY
end
@ -765,9 +871,13 @@ local function getPolygonLineIntersection( x1, y1, x2, y2, ... )
end
for i = 1, #input, 2 do
local x1, y1, x2, y2 = getLineSegmentIntersection( input[i], input[i + 1], cycle( input, i + 2 ), cycle( input, i + 3 ), x3, y3, x4, y4 )
if x1 and not x2 then choices[#choices + 1] = { x1, y1 }
elseif x1 and x2 then choices[#choices + 1] = { x1, y1, x2, y2 } end
local x1, y1, x2, y2 =
getLineSegmentIntersection(input[i], input[i + 1], cycle(input, i + 2), cycle(input, i + 3), x3, y3, x4, y4)
if x1 and not x2 then
choices[#choices + 1] = { x1, y1 }
elseif x1 and x2 then
choices[#choices + 1] = { x1, y1, x2, y2 }
end
-- No need to check 2-point sets since they only intersect each poly line once.
end
@ -782,9 +892,21 @@ local function getPolygonSegmentIntersection( x1, y1, x2, y2, ... )
local choices = {}
for i = 1, #input, 2 do
local x1, y1, x2, y2 = getSegmentSegmentIntersection( input[i], input[i + 1], cycle( input, i + 2 ), cycle( input, i + 3 ), x1, y1, x2, y2 )
if x1 and not x2 then choices[#choices + 1] = { x1, y1 }
elseif x2 then choices[#choices + 1] = { x1, y1, x2, y2 } end
local x1, y1, x2, y2 = getSegmentSegmentIntersection(
input[i],
input[i + 1],
cycle(input, i + 2),
cycle(input, i + 3),
x1,
y1,
x2,
y2
)
if x1 and not x2 then
choices[#choices + 1] = { x1, y1 }
elseif x2 then
choices[#choices + 1] = { x1, y1, x2, y2 }
end
end
local final = removeDuplicatePairs(choices)
@ -796,18 +918,22 @@ local function checkPolygonPoint( px, py, ... )
local points = { unpack(checkInput(...)) } -- Make a new table, as to not edit values of previous.
local greatest, least = getGreatestPoint(points, 0)
if not isWithinBounds( least, py, greatest ) then return false end
if not isWithinBounds(least, py, greatest) then
return false
end
greatest, least = getGreatestPoint(points)
if not isWithinBounds( least, px, greatest ) then return false end
if not isWithinBounds(least, px, greatest) then
return false
end
local count = 0
for i = 1, #points, 2 do
if checkFuzzy(points[i + 1], py) then
points[i + 1] = py + .001 -- Handles vertices that lie on the point.
points[i + 1] = py + 0.001 -- Handles vertices that lie on the point.
-- Not exactly mathematically correct, but a lot easier.
end
if points[i + 3] and checkFuzzy(points[i + 3], py) then
points[i + 3] = py + .001 -- Do not need to worry about alternate case, since points[2] has already been done.
points[i + 3] = py + 0.001 -- Do not need to worry about alternate case, since points[2] has already been done.
end
local x1, y1 = points[i], points[i + 1]
local x2, y2 = points[i + 2] or points[1], points[i + 3] or points[2]
@ -826,9 +952,13 @@ local function isSegmentInsidePolygon( x1, y1, x2, y2, ... )
local input = checkInput(...)
local choices = getPolygonSegmentIntersection(x1, y1, x2, y2, input) -- If it's partially enclosed that's all we need.
if choices then return true end
if choices then
return true
end
if checkPolygonPoint( x1, y1, input ) or checkPolygonPoint( x2, y2, input ) then return true end
if checkPolygonPoint(x1, y1, input) or checkPolygonPoint(x2, y2, input) then
return true
end
return false
end
@ -837,7 +967,13 @@ local function getPolygonPolygonIntersection( polygon1, polygon2 )
local choices = {}
for index1 = 1, #polygon1, 2 do
local intersections = getPolygonSegmentIntersection( polygon1[index1], polygon1[index1 + 1], cycle( polygon1, index1 + 2 ), cycle( polygon1, index1 + 3 ), polygon2 )
local intersections = getPolygonSegmentIntersection(
polygon1[index1],
polygon1[index1 + 1],
cycle(polygon1, index1 + 2),
cycle(polygon1, index1 + 3),
polygon2
)
if intersections then
for index2 = 1, #intersections do
choices[#choices + 1] = intersections[index2]
@ -846,7 +982,13 @@ local function getPolygonPolygonIntersection( polygon1, polygon2 )
end
for index1 = 1, #polygon2, 2 do
local intersections = getPolygonSegmentIntersection( polygon2[index1], polygon2[index1 + 1], cycle( polygon2, index1 + 2 ), cycle( polygon2, index1 + 3 ), polygon1 )
local intersections = getPolygonSegmentIntersection(
polygon2[index1],
polygon2[index1 + 1],
cycle(polygon2, index1 + 2),
cycle(polygon2, index1 + 3),
polygon1
)
if intersections then
for index2 = 1, #intersections do
choices[#choices + 1] = intersections[index2]
@ -856,7 +998,7 @@ local function getPolygonPolygonIntersection( polygon1, polygon2 )
choices = removeDuplicatePairs(choices)
for i = #choices, 1, -1 do
if type( choices[i][1] ) == 'table' then -- Remove co-linear pairs.
if type(choices[i][1]) == "table" then -- Remove co-linear pairs.
table.remove(choices, i)
end
end
@ -871,10 +1013,13 @@ local function getPolygonCircleIntersection( x, y, radius, ... )
local choices = {}
for i = 1, #input, 2 do
local Type, x1, y1, x2, y2 = getCircleSegmentIntersection( x, y, radius, input[i], input[i + 1], cycle( input, i + 2 ), cycle( input, i + 3 ) )
local Type, x1, y1, x2, y2 =
getCircleSegmentIntersection(x, y, radius, input[i], input[i + 1], cycle(input, i + 2), cycle(input, i + 3))
if x2 then
choices[#choices + 1] = { Type, x1, y1, x2, y2 }
elseif x1 then choices[#choices + 1] = { Type, x1, y1 } end
elseif x1 then
choices[#choices + 1] = { Type, x1, y1 }
end
end
local final = removeDuplicates4Points(choices)
@ -894,8 +1039,17 @@ local function isPolygonInsidePolygon( polygon1, polygon2 )
local bool = false
for i = 1, #polygon2, 2 do
local result = false
result = isSegmentInsidePolygon( polygon2[i], polygon2[i + 1], cycle( polygon2, i + 2 ), cycle( polygon2, i + 3 ), polygon1 )
if result then bool = true; break end
result = isSegmentInsidePolygon(
polygon2[i],
polygon2[i + 1],
cycle(polygon2, i + 2),
cycle(polygon2, i + 3),
polygon1
)
if result then
bool = true
break
end
end
return bool
end
@ -903,9 +1057,11 @@ end
-- Checks if a segment is completely inside a polygon
local function isSegmentCompletelyInsidePolygon(x1, y1, x2, y2, ...)
local polygon = checkInput(...)
if not checkPolygonPoint( x1, y1, polygon )
if
not checkPolygonPoint(x1, y1, polygon)
or not checkPolygonPoint(x2, y2, polygon)
or getPolygonSegmentIntersection( x1, y1, x2, y2, polygon ) then
or getPolygonSegmentIntersection(x1, y1, x2, y2, polygon)
then
return false
end
return true
@ -934,7 +1090,9 @@ local function isPolygonCompletelyInsideCircle( circleX, circleY, circleRadius,
end
for i = 1, #input, 2 do
if not checkCirclePoint( input[i], input[i + 1], circleX, circleY, circleRadius ) then return false end
if not checkCirclePoint(input[i], input[i + 1], circleX, circleY, circleRadius) then
return false
end
end
return true
end
@ -943,15 +1101,21 @@ end
-- circleX, circleY, circleRadius, polygonPoints
local function isCircleCompletelyInsidePolygon(circleX, circleY, circleRadius, ...)
local input = checkInput(...)
if not checkPolygonPoint( circleX, circleY, ... ) then return false end
if not checkPolygonPoint(circleX, circleY, ...) then
return false
end
local rad2 = circleRadius * circleRadius
for i = 1, #input, 2 do
local x1, y1 = input[i], input[i + 1]
local x2, y2 = input[i + 2] or input[1], input[i + 3] or input[2]
if distance2( x1, y1, circleX, circleY ) <= rad2 then return false end
if getCircleSegmentIntersection( circleX, circleY, circleRadius, x1, y1, x2, y2 ) then return false end
if distance2(x1, y1, circleX, circleY) <= rad2 then
return false
end
if getCircleSegmentIntersection(circleX, circleY, circleRadius, x1, y1, x2, y2) then
return false
end
end
return true
end -- }}}
@ -962,11 +1126,9 @@ end -- }}}
local function getMean(...)
local input = checkInput(...)
mean = getSummation( 1, #input,
function( i, t )
mean = getSummation(1, #input, function(i, t)
return input[i]
end
) / #input
end) / #input
return mean
end
@ -980,7 +1142,7 @@ local function getMedian( ... )
if #input % 2 == 0 then -- If you have an even number of terms, you need to get the average of the middle 2.
median = getMean(input[#input / 2], input[#input / 2 + 1])
else
median = input[#input / 2 + .5]
median = input[#input / 2 + 0.5]
end
return median
@ -1007,8 +1169,11 @@ local function getMode( ... )
end
end
if #least >= 1 then return least, occurrences
else return false end
if #least >= 1 then
return least, occurrences
else
return false
end
end
-- Gets the range of the numbers.
@ -1054,9 +1219,9 @@ local function getDispersion( ... )
end -- }}}
return {
_VERSION = 'MLib 0.10.0',
_DESCRIPTION = 'A math and shape-intersection detection library for Lua',
_URL = 'https://github.com/davisdude/mlib',
_VERSION = "MLib 0.10.0",
_DESCRIPTION = "A math and shape-intersection detection library for Lua",
_URL = "https://github.com/davisdude/mlib",
point = {
rotate = rotatePoint,
scale = scalePoint,

@ -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
@ -24,21 +28,29 @@ function love.run()
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,38 +47,38 @@ function Player:shoot(bulletSpeed)
return newBullet
end
function handleKeys(dt)
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
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("s") then
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("a") then
elseif love.keyboard.isDown(left) then
self.rotation = self.rotation - (self.rotSpeed * dt)
elseif love.keyboard.isDown("d") then
elseif love.keyboard.isDown(right) then
self.rotation = self.rotation + (self.rotSpeed * dt)
end
self.collider:setLinearVelocity(self.vx, self.vy)
end
-- Check for collision with walls
if self.collider:enter("Wall") then
print("Player 1 collided with wall")
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
if EnableKeyPress1 == true and GAMESTATE == "GAME" then
-- Update method for the Player class
function Player:update(dt)
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)
@ -86,30 +86,8 @@ function Player:update(dt)
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
if EnableKeyPress2 == true then
if EnableKeyPress2 == true and self.p == 2 then
if love.keyboard.isDown("return") then
local newBullet = self:shoot(bulletSpeed)
table.insert(Bullets2, newBullet)
@ -118,10 +96,6 @@ function Player:update(dt)
end
end
end
self.x = self.collider:getX()
self.y = self.collider:getY()
end
end
function Player:draw()
love.graphics.draw(self.image, self.x, self.y, self.rotation, self.scaleX, self.scaleY, self.originX, self.originY)

Loading…
Cancel
Save