Compare commits
51
Commits
407436f7f0
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d756beb1b | ||
|
|
86d33a7fc5 | ||
|
|
65f4ee3473 | ||
|
|
e13114a736 | ||
|
|
451d710326 | ||
|
|
585fccf59a | ||
|
|
3290d704e3 | ||
|
|
7b553d9758 | ||
|
|
e9e3731a0b | ||
|
|
ed9265939c | ||
|
|
826ef98f19 | ||
|
|
db3559b8c6 | ||
|
|
88faf2538b | ||
|
|
7252c3cd2c | ||
|
|
7da12d3035 | ||
|
|
ebf9369ba5 | ||
|
|
f6be8b2a16 | ||
|
|
beb4c28a70 | ||
|
|
2d22a50447 | ||
|
|
d059017989 | ||
|
|
49a57a6e5d | ||
|
|
f5770cd4e0 | ||
|
|
49633072ac | ||
|
|
7af90632fd | ||
|
|
e47799650a | ||
|
|
8440be2ab1 | ||
|
|
df4184eec4 | ||
|
|
e8a124fa63 | ||
|
|
78b4379143 | ||
|
|
b8106bc1aa | ||
|
|
f8d71bb27d | ||
|
|
7016961609 | ||
|
|
95502bc429 | ||
|
|
f689632a92 | ||
|
|
da8bb7d083 | ||
|
|
0a25d11ac5 | ||
|
|
71d1f6e929 | ||
|
|
a392755844 | ||
|
|
f4d3969a1f | ||
|
|
2cd9f263e6 | ||
|
|
ff40a5507b | ||
|
|
26398f52b5 | ||
|
|
38826d0cd8 | ||
|
|
9e968b9390 | ||
|
|
cdc0f35f22 | ||
|
|
e83ee4ec64 | ||
|
|
7b68420027 | ||
|
|
5f915f617e | ||
|
|
6049054a26 | ||
|
|
99b420cc7f | ||
|
|
53aa6d698d |
+2
-1
@@ -1,2 +1,3 @@
|
||||
.DS_Store
|
||||
game.love
|
||||
*.love
|
||||
*.exe
|
||||
|
||||
+19
-6
@@ -6,15 +6,18 @@ end
|
||||
|
||||
local function drawHealth()
|
||||
love.graphics.setFont(GameFont)
|
||||
local height = love.graphics.getHeight()
|
||||
local width = love.graphics.getWidth()
|
||||
love.graphics.print("" .. UserPlayer1.health, 5, 5)
|
||||
love.graphics.print("" .. UserPlayer2.health, width - 70, height - 95)
|
||||
local height = love.graphics.getHeight() / _G.Y_SCALE
|
||||
local width = love.graphics.getWidth() / _G.X_SCALE
|
||||
love.graphics.print("P1:" .. UserPlayer1.health, 5, 5)
|
||||
love.graphics.print("P2:" .. UserPlayer2.health, width - 200, height - 95)
|
||||
end
|
||||
|
||||
function DrawGame()
|
||||
--STI
|
||||
GameMap:draw()
|
||||
local curWidth, curHeight = love.graphics.getDimensions()
|
||||
love.graphics.scale(_G.X_SCALE, _G.Y_SCALE)
|
||||
|
||||
GameMap:draw(0, 0, _G.X_SCALE, _G.Y_SCALE)
|
||||
GameMap:resize(curWidth / _G.X_SCALE, curHeight / _G.Y_SCALE)
|
||||
|
||||
-- WindField
|
||||
if DebugFlag then
|
||||
@@ -22,6 +25,7 @@ function DrawGame()
|
||||
drawFPS()
|
||||
end
|
||||
|
||||
-- Bullets
|
||||
for _, v in ipairs(Bullets1) do
|
||||
v:draw()
|
||||
end
|
||||
@@ -29,9 +33,18 @@ function DrawGame()
|
||||
v:draw()
|
||||
end
|
||||
|
||||
-- PowerUps
|
||||
for _, v in ipairs(PowerUps) do
|
||||
v:draw()
|
||||
end
|
||||
|
||||
UserPlayer1:draw()
|
||||
UserPlayer2:draw()
|
||||
|
||||
--Draw Health
|
||||
--TODO: make this nicer looking
|
||||
drawHealth()
|
||||
|
||||
--TODO: draw running score
|
||||
--drawScore()
|
||||
end
|
||||
|
||||
+40
-5
@@ -1,11 +1,15 @@
|
||||
function GameKeyPressed(key)
|
||||
|
||||
if key == "escape" then
|
||||
if not MUTED then
|
||||
musicBattle:setVolume(0)
|
||||
musicPause:setVolume(0.5)
|
||||
musicPause:setVolume(0.6)
|
||||
else
|
||||
musicBattle:setVolume(0)
|
||||
musicPause:setVolume(0)
|
||||
end
|
||||
|
||||
_G.GAMESTATE = "PAUSE"
|
||||
print("STATE CHANEGD: PAUSED!")
|
||||
--print("STATE CHANEGD: PAUSED!")
|
||||
_G.PAUSED = true
|
||||
end
|
||||
|
||||
@@ -13,9 +17,40 @@ function GameKeyPressed(key)
|
||||
DebugFlag = not DebugFlag
|
||||
end
|
||||
|
||||
--[[
|
||||
if key == "r" and not _G.PAUSED then
|
||||
RestartGame()
|
||||
_G.GAMESTATE = "GAME"
|
||||
love.load()
|
||||
end
|
||||
]]--
|
||||
|
||||
if key == "m" then
|
||||
_G.MUTED = not _G.MUTED
|
||||
-- Need to set the battle music to mute
|
||||
if _G.MUTED then
|
||||
musicBattle:setVolume(0)
|
||||
else
|
||||
musicBattle:setVolume(0.5)
|
||||
end
|
||||
end
|
||||
|
||||
if DebugFlag and key == "1" then
|
||||
--UserPlayer1.speed = 50000
|
||||
--print("player1 speed increased!")
|
||||
end
|
||||
|
||||
if DebugFlag and key == "2" then
|
||||
_G.CUR_LEVEL = 2
|
||||
RestartGame()
|
||||
love.load()
|
||||
end
|
||||
if DebugFlag and key == "3" then
|
||||
_G.CUR_LEVEL = 3
|
||||
RestartGame()
|
||||
love.load()
|
||||
end
|
||||
|
||||
if key == "p" then
|
||||
testP = PowerUp("speed", 300, 300, "assets/powerup.jpeg", 10)
|
||||
table.insert(PowerUps, testP)
|
||||
end
|
||||
end
|
||||
|
||||
+105
-13
@@ -1,9 +1,32 @@
|
||||
local function checkLossState()
|
||||
-- Check P1's health
|
||||
if UserPlayer1.health <= 0 then --P1 Lost, P2 Win
|
||||
_G.P2_COUNT = _G.P2_COUNT + 1
|
||||
_G.P1_WIN = false
|
||||
_G.P2_WIN = true
|
||||
return true
|
||||
end
|
||||
|
||||
if UserPlayer2.health <= 0 then --P2 Lost, P1 Win
|
||||
_G.P1_COUNT = _G.P1_COUNT + 1
|
||||
_G.P1_WIN = true
|
||||
_G.P2_WIN = false
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local max = math.max -- optimisations
|
||||
|
||||
function UpdateGame(dt)
|
||||
--Check if anyone has won
|
||||
if checkLossState() == 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,8 +36,18 @@ function UpdateGame(dt)
|
||||
if KeyPressTime2 <= 0 then
|
||||
EnableKeyPress2 = true
|
||||
end
|
||||
|
||||
-- Update Bullets1
|
||||
for i, v in ipairs(Bullets1) do
|
||||
v:update(dt)
|
||||
|
||||
-- Check bounce count
|
||||
if v.bounce >= _G.BULLETS_BOUNCE_MAX then
|
||||
table.remove(Bullets1, i)
|
||||
v.collider:destroy()
|
||||
end
|
||||
|
||||
-- Handle screen edges
|
||||
if v.y < 0 then --top of screen
|
||||
table.remove(Bullets1, i)
|
||||
v.collider:destroy()
|
||||
@@ -22,27 +55,45 @@ function UpdateGame(dt)
|
||||
table.remove(Bullets1, i)
|
||||
v.collider:destroy()
|
||||
end
|
||||
|
||||
-- Hit player2
|
||||
if v.collider:enter("Player2") then
|
||||
print("Player1 hit Player2!")
|
||||
--print("Player1 hit Player2!")
|
||||
table.remove(Bullets1, i)
|
||||
v.collider:destroy()
|
||||
if UserPlayer1.health > 0 then
|
||||
UserPlayer1.health = UserPlayer1.health - 1
|
||||
if UserPlayer2.health > 0 then
|
||||
UserPlayer2.health = UserPlayer2.health - 1
|
||||
end
|
||||
end
|
||||
|
||||
-- Hit player1
|
||||
if v.collider:enter("Player1") then
|
||||
print("Player 1 hit themselves!")
|
||||
--print("Player 1 hit themselves!")
|
||||
table.remove(Bullets1, i)
|
||||
v.collider:destroy()
|
||||
if UserPlayer1.health > 0 then
|
||||
UserPlayer1.health = UserPlayer1.health - 1
|
||||
end
|
||||
end
|
||||
|
||||
--Hit another bullet
|
||||
if v.collider:enter("Bullet1") then
|
||||
table.remove(Bullets1, i)
|
||||
v.collider:destroy()
|
||||
end
|
||||
end
|
||||
|
||||
-- Update Bullets2
|
||||
for i, v in ipairs(Bullets2) do
|
||||
v:update(dt)
|
||||
|
||||
-- Check bounce count
|
||||
if v.bounce >= _G.BULLETS_BOUNCE_MAX then
|
||||
table.remove(Bullets2, i)
|
||||
v.collider:destroy()
|
||||
end
|
||||
|
||||
-- Handle screen edges
|
||||
if v.y < 0 then --top of screen
|
||||
table.remove(Bullets2, i)
|
||||
v.collider:destroy()
|
||||
@@ -51,25 +102,66 @@ function UpdateGame(dt)
|
||||
v.collider:destroy()
|
||||
end
|
||||
|
||||
-- Hit player1
|
||||
if v.collider:enter("Player1") then
|
||||
print("Player2 hit Player1!")
|
||||
--print("Player2 hit Player1!")
|
||||
table.remove(Bullets2, i)
|
||||
v.collider:destroy()
|
||||
if UserPlayer1.health > 0 then
|
||||
UserPlayer1.health = UserPlayer1.health - 1
|
||||
end
|
||||
end
|
||||
|
||||
-- Hit player2
|
||||
if v.collider:enter("Player2") then
|
||||
--print("Player 2 hit themselves!")
|
||||
table.remove(Bullets2, i)
|
||||
v.collider:destroy()
|
||||
if UserPlayer2.health > 0 then
|
||||
UserPlayer2.health = UserPlayer2.health - 1
|
||||
end
|
||||
end
|
||||
|
||||
--Hit another bullet
|
||||
if v.collider:enter("Bullet2") then
|
||||
table.remove(Bullets2, i)
|
||||
v.collider:destroy()
|
||||
end
|
||||
end
|
||||
|
||||
-- PowerUp updates
|
||||
for i, v in ipairs(PowerUps) do
|
||||
v:update(dt)
|
||||
if v.collider:enter("Player1") then
|
||||
UserPlayer1:getPowerUp(v)
|
||||
UserPlayer1:powerup()
|
||||
table.remove(PowerUps, i)
|
||||
v.collider:destroy()
|
||||
end
|
||||
if v.collider:enter("Player2") then
|
||||
print("Player 2 hit themselves!")
|
||||
table.remove(Bullets2, i)
|
||||
UserPlayer2:getPowerUp(v)
|
||||
UserPlayer2:powerup()
|
||||
table.remove(PowerUps, i)
|
||||
v.collider:destroy()
|
||||
|
||||
if UserPlayer2.health > 0 then
|
||||
UserPlayer2.health = UserPlayer2.health - 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--PowerUp timer to place
|
||||
PUTimeToPlace = PUTimeToPlace - dt
|
||||
--print(PUTimeToPlace)
|
||||
if PUTimeToPlace <= 0 then
|
||||
local w, h = love.graphics.getDimensions()
|
||||
local ranx = love.math.random(30, w)
|
||||
local rany = love.math.random(100, h)
|
||||
local newPU = PowerUp("speed", ranx, rany, "assets/powerup.jpeg", 10)
|
||||
table.insert(PowerUps, newPU)
|
||||
PUTimeToPlace = _G.PU_TIMER --reset
|
||||
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
|
||||
|
||||
+8
-32
@@ -1,44 +1,20 @@
|
||||
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)
|
||||
|
||||
-- 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)
|
||||
end
|
||||
|
||||
local function title()
|
||||
local height = love.graphics.getHeight()
|
||||
local width = love.graphics.getWidth()
|
||||
local height = love.graphics.getHeight() / _G.Y_SCALE
|
||||
local width = love.graphics.getWidth() / _G.X_SCALE
|
||||
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.print("Tanks-A-Lot", 100, 100)
|
||||
end
|
||||
|
||||
|
||||
function DrawMenu()
|
||||
local bwidth, bheight = 300, 140
|
||||
local bwidth, bheight = 300 * _G.X_SCALE, 140 * _G.Y_SCALE
|
||||
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)
|
||||
GUI:newButton(100, 200, bwidth, bheight, "Play", MENU_POS == 0 and true or false)
|
||||
GUI:newButton(100, 350, bwidth, bheight, "???", MENU_POS == 1 and true or false)
|
||||
GUI:newButton(100, 500, bwidth, bheight, "???", MENU_POS == 2 and true or false)
|
||||
GUI:newButton(100, 650, bwidth, bheight, "Quit", MENU_POS == 3 and true or false)
|
||||
|
||||
love.graphics.setColor(255, 255, 255) -- reset colours
|
||||
end
|
||||
+21
-8
@@ -1,5 +1,5 @@
|
||||
function MenuKeyPressed(key)
|
||||
if key == 'return' then
|
||||
if key == "return" then
|
||||
-- 0 Start Game
|
||||
-- 1 ??
|
||||
-- 2 ???
|
||||
@@ -7,19 +7,15 @@ function MenuKeyPressed(key)
|
||||
if MENU_POS == 0 then
|
||||
--Change state to GAME!
|
||||
_G.GAMESTATE = "GAME"
|
||||
print("STATE CHANEGD: GAME!")
|
||||
--print("STATE CHANEGD: GAME!")
|
||||
musicMenu:stop()
|
||||
|
||||
elseif MENU_POS == 1 then
|
||||
print("STATE CHANEGD: DUNNO!")
|
||||
|
||||
--print("STATE CHANEGD: DUNNO!")
|
||||
elseif MENU_POS == 2 then
|
||||
print("STATE CHANEGD: DUNNO!")
|
||||
|
||||
--print("STATE CHANEGD: DUNNO!")
|
||||
elseif MENU_POS == 3 then
|
||||
love.event.quit()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if love.keyboard.isDown("up") then
|
||||
@@ -38,4 +34,21 @@ function MenuKeyPressed(key)
|
||||
end
|
||||
end
|
||||
|
||||
if key == "r" then
|
||||
musicBattle:stop()
|
||||
musicMenu:stop()
|
||||
musicPause:stop()
|
||||
_G.GAMESTATE = "MENU"
|
||||
love.load()
|
||||
end
|
||||
|
||||
if key == "m" then
|
||||
MUTED = not MUTED
|
||||
|
||||
if MUTED then
|
||||
musicMenu:setVolume(0)
|
||||
else
|
||||
musicMenu:setVolume(0.5)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+1
-2
@@ -1,2 +1 @@
|
||||
function UpdateMenu(dt)
|
||||
end
|
||||
function UpdateMenu(dt) end
|
||||
|
||||
+7
-31
@@ -1,33 +1,8 @@
|
||||
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)
|
||||
|
||||
-- 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)
|
||||
end
|
||||
|
||||
|
||||
function DrawPause()
|
||||
local opacity = 0.3
|
||||
local height = love.graphics.getHeight()
|
||||
local width = love.graphics.getWidth()
|
||||
local bwidth, bheight = 300, 140
|
||||
local height = love.graphics.getHeight() / _G.Y_SCALE
|
||||
local width = love.graphics.getWidth() / _G.X_SCALE
|
||||
local bwidth, bheight = 300 * _G.X_SCALE, 140 * _G.Y_SCALE
|
||||
love.graphics.setFont(GameFont)
|
||||
|
||||
DrawGame() --Draw a single frame of the game
|
||||
@@ -38,8 +13,9 @@ function DrawPause()
|
||||
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)
|
||||
GUI:newButton(100, 200, bwidth, bheight, "Back", PAUSE_POS == 0 and true or false)
|
||||
GUI:newButton(100, 350, bwidth, bheight, "Menu", PAUSE_POS == 1 and true or false)
|
||||
GUI:newButton(100, 500, bwidth, bheight, "Quit", PAUSE_POS == 2 and true or false)
|
||||
|
||||
love.graphics.setColor(255, 255, 255) -- reset colours
|
||||
end
|
||||
@@ -1,31 +1,50 @@
|
||||
function PauseKeyPressed(key)
|
||||
if key == 'return' then
|
||||
if key == "escape" then
|
||||
-- quickly return to the game
|
||||
if not _G.MUTED then
|
||||
musicBattle:setVolume(0.5)
|
||||
musicPause:setVolume(0)
|
||||
else
|
||||
musicBattle:setVolume(0)
|
||||
musicPause:setVolume(0)
|
||||
end
|
||||
_G.GAMESTATE = "GAME"
|
||||
end
|
||||
|
||||
if key == "return" then
|
||||
-- quickly return to the game
|
||||
if not _G.MUTED then
|
||||
musicBattle:setVolume(0.5)
|
||||
musicPause:setVolume(0)
|
||||
else
|
||||
musicBattle:setVolume(0)
|
||||
musicPause:setVolume(0)
|
||||
end
|
||||
-- 0 Return to game
|
||||
-- 1 Quit
|
||||
if PAUSE_POS == 0 then
|
||||
-- unpause the game
|
||||
_G.GAMESTATE = "GAME"
|
||||
print("STATE CHANEGD: GAME!")
|
||||
--print("STATE CHANEGD: GAME!")
|
||||
_G.PAUSED = false
|
||||
if not MUTED then
|
||||
musicBattle:setVolume(0.5)
|
||||
musicPause:setVolume(0)
|
||||
|
||||
else
|
||||
musicBattle:setVolume(0)
|
||||
musicPause:setVolume(0)
|
||||
end
|
||||
elseif PAUSE_POS == 1 then
|
||||
_G.GAMESTATE = "MENU"
|
||||
print("STATE CHANEGD: 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
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
local function winner()
|
||||
local width = love.graphics.getWidth() / _G.X_SCALE
|
||||
local height = love.graphics.getHeight() / _G.Y_SCALE
|
||||
local opacity = 0.3
|
||||
local s = ""
|
||||
|
||||
if _G.P1_WIN then
|
||||
s = "P1 Win! R - Restart"
|
||||
end
|
||||
if _G.P2_WIN then
|
||||
s = "P2 Win! R - Restart"
|
||||
end
|
||||
|
||||
if _G.P1_WIN or _G.P2_WIN then
|
||||
-- Overlay
|
||||
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)
|
||||
|
||||
-- Who won text
|
||||
local sw, sh = MenuFont:getWidth(s), GameFont:getHeight(s)
|
||||
local centreX = width / 2
|
||||
local centreY = height / 2
|
||||
|
||||
love.graphics.setFont(MenuFont)
|
||||
love.graphics.setColor(1, 1, 1) -- white
|
||||
love.graphics.print(s, centreX - sw / 2, centreY - sh / 2) -- who won
|
||||
love.graphics.print(
|
||||
"" .. _G.P1_COUNT .. ":" .. "" .. _G.P2_COUNT,
|
||||
(centreX - sw / 2),
|
||||
(centreY - sh / 2) - sh / 1.5
|
||||
) -- score
|
||||
end
|
||||
end
|
||||
|
||||
function DrawWin()
|
||||
winner()
|
||||
love.graphics.setColor(255, 255, 255) -- reset colours
|
||||
end
|
||||
@@ -0,0 +1 @@
|
||||
function UpdateWin(dt) end
|
||||
@@ -0,0 +1,7 @@
|
||||
function WinKeyPressed(key)
|
||||
if key == "r" then
|
||||
RestartGame()
|
||||
_G.GAMESTATE = "GAME"
|
||||
love.load()
|
||||
end
|
||||
end
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
@@ -1,6 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -xe
|
||||
love=/Applications/love.app/Contents/MacOS/love
|
||||
|
||||
cat $love ./../game.love >./tanksalot.exe
|
||||
Regular → Executable
+60
-19
@@ -1,7 +1,9 @@
|
||||
Bullet = Object:extend()
|
||||
cos = math.cos
|
||||
sin = math.sin --optimisation
|
||||
pi = math.pi
|
||||
local pi = math.pi
|
||||
local cos = math.cos
|
||||
local sin = math.sin
|
||||
local atan = math.atan
|
||||
local deg = math.deg
|
||||
|
||||
function Bullet:new(x, y, p, speed, rotation)
|
||||
self.image = love.graphics.newImage("assets/bullet.png")
|
||||
@@ -20,9 +22,13 @@ function Bullet:new(x, y, p, speed, rotation)
|
||||
self.originY = self.height / 2
|
||||
|
||||
self.collider = World:newCircleCollider(x, y, 10)
|
||||
--self.collider = World:newRectangleCollider(x, y, 10, 10)
|
||||
--self.collider:setType("")
|
||||
self.collider:setFixedRotation(true)
|
||||
self.collider:setPosition(self.x, self.y)
|
||||
|
||||
self.bounce = 0 -- how many times this bullet has bounced
|
||||
|
||||
if self.p == 1 then
|
||||
self.collider:setCollisionClass("Bullet1")
|
||||
elseif self.p == 2 then
|
||||
@@ -31,34 +37,69 @@ function Bullet:new(x, y, p, speed, rotation)
|
||||
end
|
||||
|
||||
function Bullet:update(dt)
|
||||
--If a bullet hits a wall, make it bounce off the wall and change its rotation
|
||||
if self.p == 1 then
|
||||
-- Calculate the velocity
|
||||
local dx = math.cos(self.rotation) * self.speed * dt
|
||||
local dy = math.sin(self.rotation) * self.speed * dt
|
||||
|
||||
-- Check for collisions
|
||||
if self.collider:enter("Wall") then
|
||||
--calculate normal of the wall
|
||||
--calculate the angle of reflection
|
||||
local angle = (math.atan2(dx, dy))
|
||||
--get normal of the wall without using getNormal()
|
||||
self.bounce = self.bounce + 1
|
||||
|
||||
----TODO: fix this
|
||||
-- Get the collision normal
|
||||
local collision_data = self.collider:getEnterCollisionData("Wall")
|
||||
local contact = collision_data.contact
|
||||
local normal_x, normal_y = contact:getNormal()
|
||||
|
||||
--set the new angle
|
||||
self.rotation = angle
|
||||
self.collider:setAngle(self.rotation)
|
||||
-- Debug print to verify normal values
|
||||
--print("---------------------------------")
|
||||
--print("Collision detected!")
|
||||
--print("Normal X:", normal_x, "Normal Y:", normal_y)
|
||||
--print("Initial Velocity X:", dx, "Initial Velocity Y:", dy)
|
||||
--print("Initial Angle: ", self.rotation)
|
||||
|
||||
-- Reflect the velocity using the normal
|
||||
local dot_product = dx * normal_x + dy * normal_y
|
||||
dx = dx - 2 * dot_product * normal_x
|
||||
dy = dy - 2 * dot_product * normal_y
|
||||
--print("################################")
|
||||
|
||||
-- Debug print to verify reflected velocity
|
||||
--print("Reflected Velocity X:", dx, "Reflected Velocity Y:", dy)
|
||||
self.rotation = deg(atan(dy, dx))
|
||||
|
||||
-- Update the rotation based on the new velocity direction
|
||||
--self.rotation = deg(atan(dy, dx))
|
||||
--print("Reflected Angle (radians):", self.rotation)
|
||||
--print("Reflected Angle (degrees):", self.rotation)
|
||||
--print("---------------------------------")
|
||||
end
|
||||
|
||||
-- Set the linear velocity of the collider
|
||||
self.collider:setLinearVelocity(dx, dy)
|
||||
|
||||
-- Update position
|
||||
self.x = self.collider:getX()
|
||||
self.y = self.collider:getY()
|
||||
end
|
||||
|
||||
--[[
|
||||
function Bullet:update(dt)
|
||||
local normal_x, normal_y = 0, 0
|
||||
local dx, dy = 0, 0
|
||||
if self.p == 1 or self.p == 2 then
|
||||
if self.collider:enter("Wall") then
|
||||
self.collider:setLinearVelocity(dx, dy)
|
||||
else
|
||||
dx = cos(self.rotation) * self.speed * dt
|
||||
dy = sin(self.rotation) * self.speed * dt
|
||||
self.collider:setLinearVelocity(dx, dy)
|
||||
end
|
||||
|
||||
if self.p == 2 then
|
||||
local dx = cos(self.rotation) * self.speed * dt
|
||||
local dy = sin(self.rotation) * self.speed * dt
|
||||
self.collider:setLinearVelocity(dx, dy)
|
||||
end
|
||||
|
||||
-- Update position
|
||||
self.x = self.collider:getX()
|
||||
self.y = self.collider:getY()
|
||||
end
|
||||
]]
|
||||
|
||||
function Bullet:draw()
|
||||
for _, _ in ipairs(Bullets1) do
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
--Config file for the game
|
||||
function love.conf(t)
|
||||
--t.window.width = 1366
|
||||
--t.window.height = 768
|
||||
|
||||
t.window.width = 1600
|
||||
t.window.height = 960
|
||||
|
||||
t.window.title = "Tanks-A-Lot"
|
||||
t.window.resizable = true
|
||||
t.version = "11.5"
|
||||
end
|
||||
|
||||
@@ -1,8 +1,54 @@
|
||||
-- WINDOW SCALE
|
||||
-- Used to scale the game up or down
|
||||
-- (scales STI world map and Walls in the game! )
|
||||
-- (see debug for more)
|
||||
X_SCALE = 1
|
||||
Y_SCALE = 1
|
||||
|
||||
-- TILE SCALE
|
||||
-- Since this game is put together rather badly
|
||||
-- the game world is 1600x960
|
||||
GAME_WORLD_DIM_WIDTH = 1600
|
||||
GAME_WORLD_DIM_HEIGHT = 960
|
||||
|
||||
--[[
|
||||
* Game states:
|
||||
* - MENU
|
||||
* - GAME
|
||||
* - PAUSE
|
||||
* - WIN
|
||||
]]
|
||||
--
|
||||
GAMESTATE = "MENU"
|
||||
|
||||
-- Mute for game
|
||||
MUTED = false
|
||||
|
||||
MENU_POS = 0
|
||||
MENU_MAX = 3 --0 play, 1 ?, 2 ?, 3 quit
|
||||
|
||||
PAUSED = false
|
||||
PAUSE_POS = 0
|
||||
PAUSE_MAX = 2 -- 0 resume, 1 menu, 2 quit
|
||||
|
||||
BULLETS_MAX = 10 -- MAX amount of bullets a player can shoot
|
||||
BULLETS_BOUNCE_MAX = 10 -- MAX amount of bounces before exploding!
|
||||
BULLETS_LIFETIME = 50 --TODO: impl. lifetime for a bullet!
|
||||
|
||||
-- Power up timer
|
||||
PU_TIMER = 10
|
||||
|
||||
-- WIN flags for P1 and P2
|
||||
P1_WIN = false
|
||||
P2_WIN = false
|
||||
|
||||
-- Default speeds for bullets and player
|
||||
DEFAULT_P_SPEED = 12000
|
||||
DEFAULT_B_SPEED = 22000
|
||||
|
||||
-- WIN counts!
|
||||
P1_COUNT = 0
|
||||
P2_COUNT = 0
|
||||
|
||||
-- Current Level
|
||||
CUR_LEVEL = 1
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
GUI = Object:extend()
|
||||
|
||||
function GUI:new() end
|
||||
|
||||
function GUI:newButton(x, y, w, h, text, selected)
|
||||
love.graphics.setColor(255, 255, 255) -- reset colours
|
||||
|
||||
--x,y is the top left corner of the button
|
||||
local rounding = 30 -- used for rounding the buttons
|
||||
local xoff = x + 10
|
||||
local yoff = y - 10
|
||||
local textX, textY = 0, 0
|
||||
-- Get width and height of text
|
||||
local tw = MenuFont:getWidth(text)
|
||||
local th = MenuFont:getHeight(text)
|
||||
love.graphics.setFont(MenuFont)
|
||||
|
||||
if not selected then
|
||||
love.graphics.setColor(love.math.colorFromBytes(41, 134, 204))
|
||||
love.graphics.rectangle("fill", x, y, w, h, rounding, rounding)
|
||||
|
||||
-- Calculate position to center the text
|
||||
textX = x + (w - tw) / 2
|
||||
textY = y + (h - th) / 2
|
||||
-- Place text inside the rectangle
|
||||
love.graphics.setColor(0, 0, 0)
|
||||
love.graphics.print(text, textX, textY)
|
||||
elseif selected then
|
||||
-- If selected, raise the button and draw a shadow
|
||||
love.graphics.setColor(love.math.colorFromBytes(0, 0, 0))
|
||||
love.graphics.rectangle("fill", x, y, w, h, rounding, rounding) -- shadow
|
||||
|
||||
love.graphics.setColor(love.math.colorFromBytes(244, 67, 54))
|
||||
love.graphics.rectangle("fill", xoff, yoff, w, h, rounding, rounding) -- button offset
|
||||
|
||||
-- Calculate position to center the text
|
||||
textX = xoff + (w - tw) / 2
|
||||
textY = yoff + (h - th) / 2
|
||||
-- Place text inside the rectangle
|
||||
love.graphics.setColor(1, 1, 1)
|
||||
love.graphics.print(text, textX, textY)
|
||||
end
|
||||
end
|
||||
+1
-10
@@ -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
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
local clock = os.clock
|
||||
|
||||
--- Simple profiler written in Lua.
|
||||
-- @module profile
|
||||
-- @alias profile
|
||||
local profile = {}
|
||||
|
||||
-- function labels
|
||||
local _labeled = {}
|
||||
-- function definitions
|
||||
local _defined = {}
|
||||
-- time of last call
|
||||
local _tcalled = {}
|
||||
-- total execution time
|
||||
local _telapsed = {}
|
||||
-- number of calls
|
||||
local _ncalls = {}
|
||||
-- list of internal profiler functions
|
||||
local _internal = {}
|
||||
|
||||
--- This is an internal function.
|
||||
-- @tparam string event Event type
|
||||
-- @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
|
||||
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
|
||||
end
|
||||
|
||||
--- Starts collecting data.
|
||||
function profile.start()
|
||||
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')
|
||||
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')
|
||||
end
|
||||
|
||||
--- This is an internal function.
|
||||
-- @tparam function a First function
|
||||
-- @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
|
||||
end
|
||||
|
||||
--- Generates a report of functions that have been called since the profile was started.
|
||||
-- Returns the report as a numeric table of rows containing the rank, function label, number of calls, total execution time and source code line number.
|
||||
-- @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
|
||||
end
|
||||
|
||||
local cols = { 3, 29, 11, 24, 32 }
|
||||
|
||||
--- Generates a text report of functions that have been called since the profile was started.
|
||||
-- Returns the report as a string that can be printed to the console.
|
||||
-- @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 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
|
||||
end
|
||||
|
||||
return profile
|
||||
+43
-14
@@ -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 }
|
||||
|
||||
+46
-70
@@ -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
|
||||
|
||||
|
||||
+12
-12
@@ -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.
|
||||
|
||||
+24
-20
@@ -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
|
||||
|
||||
|
||||
+345
-138
@@ -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
|
||||
|
||||
|
||||
+287
-122
@@ -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
|
||||
) )
|
||||
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 )
|
||||
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,69 @@
|
||||
require 'constants'
|
||||
Object = require("libs/classic")
|
||||
|
||||
require("constants")
|
||||
WF = require("libs/windfield")
|
||||
STI = require("libs/sti")
|
||||
|
||||
World = WF.newWorld(0, 0) --no gravity
|
||||
--World:setQueryDebugDrawing(true) -- Draws the area of a query for 10 frames
|
||||
World:addCollisionClass("Player1")
|
||||
World:addCollisionClass("Bullet1")
|
||||
World:addCollisionClass("Player2")
|
||||
World:addCollisionClass("Bullet2")
|
||||
World:addCollisionClass("Wall")
|
||||
World:addCollisionClass("PowerUp")
|
||||
|
||||
require("gui")
|
||||
require("powerups/powerup")
|
||||
require("powerups/speed")
|
||||
require("restart")
|
||||
require("truncate")
|
||||
require("player")
|
||||
require("bullet")
|
||||
require("mapsloader")
|
||||
|
||||
require("Game/UpdateGame")
|
||||
require("Menu/UpdateMenu")
|
||||
require("Pause/UpdatePause")
|
||||
require("Win/UpdateWin")
|
||||
|
||||
require("Game/DrawGame")
|
||||
require("Menu/DrawMenu")
|
||||
require("Pause/DrawPause")
|
||||
require("Win/DrawWin")
|
||||
|
||||
require("Game/GameKeyPressed")
|
||||
require("Menu/MenuKeyPressed")
|
||||
require("Pause/PauseKeyPressed")
|
||||
require("Win/WinKeyPressed")
|
||||
|
||||
--Fonts used in the game
|
||||
GameFont = love.graphics.newFont("assets/Daydream.ttf", 60)
|
||||
DebugFont = love.graphics.newFont("assets/Daydream.ttf", 12)
|
||||
MenuFont = love.graphics.newFont("assets/Daydream.ttf", 45)
|
||||
Walls = {}
|
||||
|
||||
-- Music streaming
|
||||
musicMenu = love.audio.newSource("music/menu.mp3", "stream") or nil
|
||||
musicBattle = love.audio.newSource("music/battle.mp3", "stream") or nil
|
||||
--musicStory = love.audio.newSource("music/story.mp3", "stream") or nil
|
||||
|
||||
musicPause = musicBattle:clone()
|
||||
musicPause:setFilter({
|
||||
type = "lowpass",
|
||||
volume = 0.7,
|
||||
highgain = 0.4,
|
||||
})
|
||||
|
||||
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,100 +83,72 @@ 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
|
||||
|
||||
function love.resized()
|
||||
--TOD0: fix scaling!
|
||||
end
|
||||
|
||||
function love.load()
|
||||
Object = require("libs/classic")
|
||||
require("player")
|
||||
require("bullet")
|
||||
-- Set a random seed
|
||||
love.math.setRandomSeed(love.timer.getTime())
|
||||
-- Figure out the X_SCALE and Y_SCALE
|
||||
local curWidth, curHeight = love.graphics.getDimensions()
|
||||
_G.X_SCALE = truncate(curWidth / GAME_WORLD_DIM_WIDTH, 3)
|
||||
_G.Y_SCALE = truncate(curHeight / GAME_WORLD_DIM_HEIGHT, 3)
|
||||
|
||||
WF = require("libs/windfield")
|
||||
STI = require("libs/sti")
|
||||
|
||||
require("Game/UpdateGame")
|
||||
require("Menu/UpdateMenu")
|
||||
require("Pause/UpdatePause")
|
||||
|
||||
require("Game/DrawGame")
|
||||
require("Menu/DrawMenu")
|
||||
require("Pause/DrawPause")
|
||||
|
||||
require("Game/GameKeyPressed")
|
||||
require("Menu/MenuKeyPressed")
|
||||
require("Pause/PauseKeyPressed")
|
||||
|
||||
require("mapsloader")
|
||||
|
||||
--WindField
|
||||
World = WF.newWorld(0, 0) --no gravity
|
||||
World:setQueryDebugDrawing(true) -- Draws the area of a query for 10 frames
|
||||
World:addCollisionClass("Player1")
|
||||
World:addCollisionClass("Bullet1")
|
||||
|
||||
World:addCollisionClass("Player2")
|
||||
World:addCollisionClass("Bullet2")
|
||||
|
||||
World:addCollisionClass("Wall")
|
||||
World:addCollisionClass("New") -- Used to make sure the bullet doesn't collide with the player that shot it
|
||||
|
||||
--STI Map
|
||||
loadMap(1)
|
||||
|
||||
--Fonts used in the game
|
||||
GameFont = love.graphics.newFont("assets/Daydream.ttf", 60)
|
||||
DebugFont = love.graphics.newFont("assets/Daydream.ttf", 12)
|
||||
MenuFont = love.graphics.newFont("assets/Daydream.ttf", 45)
|
||||
|
||||
--Game consts
|
||||
--Game values (reset after each load)
|
||||
HEALTH = 3
|
||||
DELAY = 0.5
|
||||
MAX = 6 --MAX number of bullets|
|
||||
P1_DELAY = 0.5
|
||||
P2_DELAY = 0.5
|
||||
|
||||
--Bullet lists
|
||||
Bullets1 = {}
|
||||
Bullets2 = {}
|
||||
|
||||
--PowerUp stuff
|
||||
PowerUps = {}
|
||||
PUTimeToPlace = _G.PU_TIMER
|
||||
|
||||
DebugFlag = false
|
||||
EnableKeyPress1 = true
|
||||
KeyPressTime1 = 0
|
||||
KeyDelay1 = DELAY
|
||||
KeyDelay1 = P1_DELAY
|
||||
|
||||
EnableKeyPress2 = true
|
||||
KeyPressTime2 = 0
|
||||
KeyDelay2 = DELAY
|
||||
KeyDelay2 = P2_DELAY
|
||||
|
||||
local playerSpeed = 12000
|
||||
UserPlayer1 = Player(1, 1000, 100, HEALTH, "assets/player1.png", playerSpeed)
|
||||
UserPlayer2 = Player(2, 200, 300, HEALTH, "assets/player2.png", playerSpeed)
|
||||
|
||||
-- Music streaming
|
||||
musicMenu = love.audio.newSource("music/menu.mp3", "stream")
|
||||
musicBattle = love.audio.newSource("music/battle.mp3", "stream")
|
||||
musicStory = love.audio.newSource("music/story.mp3", "stream")
|
||||
|
||||
musicPause = musicBattle:clone()
|
||||
musicPause:setFilter{
|
||||
type = 'lowpass',
|
||||
volume = 0.3,
|
||||
highgain = 0.4,
|
||||
}
|
||||
--TODO: two player speeds for upgrades
|
||||
UserPlayer1 = Player(1, 1000, 100, HEALTH, "assets/player1.png", 12000)
|
||||
UserPlayer2 = Player(2, 200, 300, HEALTH, "assets/player2.png", 12000)
|
||||
|
||||
--STI Map loading
|
||||
LoadMap(_G.CUR_LEVEL)
|
||||
end
|
||||
|
||||
function love.keypressed(key)
|
||||
@@ -130,14 +161,16 @@ function love.keypressed(key)
|
||||
if _G.GAMESTATE == "GAME" then
|
||||
GameKeyPressed(key)
|
||||
end
|
||||
|
||||
if _G.GAMESTATE == "WIN" then
|
||||
WinKeyPressed(key)
|
||||
end
|
||||
end
|
||||
|
||||
function love.update(dt)
|
||||
if _G.GAMESTATE == "MENU" then
|
||||
UpdateMenu(dt)
|
||||
-- Handle music
|
||||
if not musicMenu:isPlaying() then
|
||||
if not musicMenu:isPlaying() and not _G.MUTED then
|
||||
musicMenu:setVolume(0.5)
|
||||
love.audio.play(musicMenu)
|
||||
end
|
||||
@@ -145,9 +178,9 @@ function love.update(dt)
|
||||
if _G.GAMESTATE == "PAUSE" then
|
||||
UpdatePause(dt)
|
||||
-- Handle music
|
||||
if not musicPause:isPlaying() then
|
||||
--musicBattle:setVolume(0)
|
||||
--musicPause:setVolume(0.5)
|
||||
if not musicPause:isPlaying() and not _G.MUTED then
|
||||
musicBattle:setVolume(0)
|
||||
musicPause:setVolume(0.5)
|
||||
love.audio.play(musicBattle)
|
||||
love.audio.play(musicPause)
|
||||
end
|
||||
@@ -155,13 +188,16 @@ function love.update(dt)
|
||||
if _G.GAMESTATE == "GAME" then
|
||||
UpdateGame(dt)
|
||||
-- Handle music
|
||||
if not musicBattle:isPlaying() then
|
||||
--musicBattle:setVolume(0.5)
|
||||
--musicPause:setVolume(0)
|
||||
if not musicBattle:isPlaying() and not _G.MUTED then
|
||||
musicBattle:setVolume(0.5)
|
||||
musicPause:setVolume(0)
|
||||
love.audio.play(musicBattle)
|
||||
love.audio.play(musicPause)
|
||||
end
|
||||
end
|
||||
if _G.GAMESTATE == "WIN" then
|
||||
UpdateWin(dt)
|
||||
end
|
||||
end
|
||||
|
||||
function love.draw()
|
||||
@@ -173,11 +209,8 @@ function love.draw()
|
||||
end
|
||||
if _G.GAMESTATE == "GAME" then
|
||||
DrawGame()
|
||||
if DebugFlag then
|
||||
love.graphics.setFont(DebugFont)
|
||||
love.graphics.print("Debug Mode", 1200, 850)
|
||||
love.graphics.print("" .. GAMESTATE, 200, 200)
|
||||
end
|
||||
if _G.GAMESTATE == "WIN" then
|
||||
DrawWin()
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
+290
@@ -0,0 +1,290 @@
|
||||
return {
|
||||
version = "1.10",
|
||||
luaversion = "5.1",
|
||||
tiledversion = "1.11.0",
|
||||
class = "",
|
||||
orientation = "orthogonal",
|
||||
renderorder = "right-down",
|
||||
width = 25,
|
||||
height = 15,
|
||||
tilewidth = 64,
|
||||
tileheight = 64,
|
||||
nextlayerid = 4,
|
||||
nextobjectid = 16,
|
||||
properties = {},
|
||||
tilesets = {
|
||||
{
|
||||
name = "floor",
|
||||
firstgid = 1,
|
||||
class = "",
|
||||
tilewidth = 64,
|
||||
tileheight = 64,
|
||||
spacing = 0,
|
||||
margin = 0,
|
||||
columns = 9,
|
||||
image = "../assets/tileset.png",
|
||||
imagewidth = 576,
|
||||
imageheight = 384,
|
||||
objectalignment = "unspecified",
|
||||
tilerendersize = "tile",
|
||||
fillmode = "stretch",
|
||||
tileoffset = {
|
||||
x = 0,
|
||||
y = 0
|
||||
},
|
||||
grid = {
|
||||
orientation = "orthogonal",
|
||||
width = 64,
|
||||
height = 64
|
||||
},
|
||||
properties = {},
|
||||
wangsets = {},
|
||||
tilecount = 54,
|
||||
tiles = {}
|
||||
}
|
||||
},
|
||||
layers = {
|
||||
{
|
||||
type = "tilelayer",
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 25,
|
||||
height = 15,
|
||||
id = 1,
|
||||
name = "floor",
|
||||
class = "",
|
||||
visible = true,
|
||||
opacity = 1,
|
||||
offsetx = 0,
|
||||
offsety = 0,
|
||||
parallaxx = 1,
|
||||
parallaxy = 1,
|
||||
properties = {},
|
||||
encoding = "lua",
|
||||
data = {
|
||||
1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21
|
||||
}
|
||||
},
|
||||
{
|
||||
type = "tilelayer",
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 25,
|
||||
height = 15,
|
||||
id = 2,
|
||||
name = "inner walls",
|
||||
class = "",
|
||||
visible = true,
|
||||
opacity = 1,
|
||||
offsetx = 0,
|
||||
offsety = 0,
|
||||
parallaxx = 1,
|
||||
parallaxy = 1,
|
||||
properties = {},
|
||||
encoding = "lua",
|
||||
data = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 29, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 28, 29, 29, 29, 29, 29, 30, 0, 0, 37, 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 37, 38, 38, 38, 38, 38, 39, 0, 0, 46, 47, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 46, 47, 38, 38, 38, 47, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 37, 38, 39, 0, 0, 0, 0, 28, 29, 30, 0, 0, 0, 0, 28, 29, 30, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 37, 38, 39, 0, 0, 0, 0, 37, 38, 39, 0, 0, 0, 0, 37, 38, 39, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 37, 38, 39, 0, 0, 0, 0, 37, 38, 39, 0, 0, 0, 0, 37, 38, 39, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 37, 38, 39, 0, 0, 0, 0, 37, 38, 39, 0, 0, 0, 0, 37, 38, 39, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 46, 47, 48, 0, 0, 0, 0, 46, 47, 48, 0, 0, 0, 0, 37, 38, 39, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 29, 38, 38, 38, 29, 30, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 29, 30, 0, 0, 37, 38, 38, 38, 38, 38, 39, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 38, 39, 0, 0, 46, 47, 47, 47, 47, 47, 48, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 47, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
}
|
||||
},
|
||||
{
|
||||
type = "objectgroup",
|
||||
draworder = "topdown",
|
||||
id = 3,
|
||||
name = "Walls",
|
||||
class = "",
|
||||
visible = false,
|
||||
opacity = 1,
|
||||
offsetx = 0,
|
||||
offsety = 0,
|
||||
parallaxx = 1,
|
||||
parallaxy = 1,
|
||||
properties = {},
|
||||
objects = {
|
||||
{
|
||||
id = 1,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 128,
|
||||
y = 128,
|
||||
width = 448,
|
||||
height = 192,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 2,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 256,
|
||||
y = 320,
|
||||
width = 192,
|
||||
height = 320,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 3,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 704,
|
||||
y = 80,
|
||||
width = 192,
|
||||
height = 176,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 4,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 704,
|
||||
y = 320,
|
||||
width = 192,
|
||||
height = 320,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 5,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 704,
|
||||
y = 704,
|
||||
width = 192,
|
||||
height = 192,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 6,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 1024,
|
||||
y = 640,
|
||||
width = 448,
|
||||
height = 192,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 7,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 1152,
|
||||
y = 320,
|
||||
width = 192,
|
||||
height = 320,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 9,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 1184,
|
||||
height = 16,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 10,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 1184,
|
||||
y = 0,
|
||||
width = 416,
|
||||
height = 16,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 11,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 1584,
|
||||
y = 0,
|
||||
width = 16,
|
||||
height = 960,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 12,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 0,
|
||||
y = 944,
|
||||
width = 1600,
|
||||
height = 16,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 13,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 16,
|
||||
height = 960,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="25" height="15" tilewidth="64" tileheight="64" infinite="0" nextlayerid="4" nextobjectid="16">
|
||||
<editorsettings>
|
||||
<export target="map2.lua" format="lua"/>
|
||||
</editorsettings>
|
||||
<tileset firstgid="1" name="floor" tilewidth="64" tileheight="64" tilecount="54" columns="9">
|
||||
<image source="../assets/tileset.png" width="576" height="384"/>
|
||||
</tileset>
|
||||
<layer id="1" name="floor" width="25" height="15">
|
||||
<data encoding="csv">
|
||||
1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21
|
||||
</data>
|
||||
</layer>
|
||||
<layer id="2" name="inner walls" width="25" height="15">
|
||||
<data encoding="csv">
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,28,29,30,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,28,29,29,29,29,29,30,0,0,37,38,39,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,37,38,38,38,38,38,39,0,0,46,47,48,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,46,47,38,38,38,47,48,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,37,38,39,0,0,0,0,28,29,30,0,0,0,0,28,29,30,0,0,0,0,
|
||||
0,0,0,0,37,38,39,0,0,0,0,37,38,39,0,0,0,0,37,38,39,0,0,0,0,
|
||||
0,0,0,0,37,38,39,0,0,0,0,37,38,39,0,0,0,0,37,38,39,0,0,0,0,
|
||||
0,0,0,0,37,38,39,0,0,0,0,37,38,39,0,0,0,0,37,38,39,0,0,0,0,
|
||||
0,0,0,0,46,47,48,0,0,0,0,46,47,48,0,0,0,0,37,38,39,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,29,38,38,38,29,30,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,28,29,30,0,0,37,38,38,38,38,38,39,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,37,38,39,0,0,46,47,47,47,47,47,48,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,46,47,48,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
</data>
|
||||
</layer>
|
||||
<objectgroup id="3" name="Walls" visible="0">
|
||||
<object id="1" x="128" y="128" width="448" height="192"/>
|
||||
<object id="2" x="256" y="320" width="192" height="320"/>
|
||||
<object id="3" x="704" y="80" width="192" height="176"/>
|
||||
<object id="4" x="704" y="320" width="192" height="320"/>
|
||||
<object id="5" x="704" y="704" width="192" height="192"/>
|
||||
<object id="6" x="1024" y="640" width="448" height="192"/>
|
||||
<object id="7" x="1152" y="320" width="192" height="320"/>
|
||||
<object id="9" x="0" y="0" width="1184" height="16"/>
|
||||
<object id="10" x="1184" y="0" width="416" height="16"/>
|
||||
<object id="11" x="1584" y="0" width="16" height="960"/>
|
||||
<object id="12" x="0" y="944" width="1600" height="16"/>
|
||||
<object id="13" x="0" y="0" width="16" height="960"/>
|
||||
</objectgroup>
|
||||
</map>
|
||||
+368
@@ -0,0 +1,368 @@
|
||||
return {
|
||||
version = "1.10",
|
||||
luaversion = "5.1",
|
||||
tiledversion = "1.11.0",
|
||||
class = "",
|
||||
orientation = "orthogonal",
|
||||
renderorder = "right-down",
|
||||
width = 25,
|
||||
height = 15,
|
||||
tilewidth = 64,
|
||||
tileheight = 64,
|
||||
nextlayerid = 4,
|
||||
nextobjectid = 23,
|
||||
properties = {},
|
||||
tilesets = {
|
||||
{
|
||||
name = "tileset",
|
||||
firstgid = 1,
|
||||
class = "",
|
||||
tilewidth = 64,
|
||||
tileheight = 64,
|
||||
spacing = 0,
|
||||
margin = 0,
|
||||
columns = 9,
|
||||
image = "../assets/tileset.png",
|
||||
imagewidth = 576,
|
||||
imageheight = 384,
|
||||
objectalignment = "unspecified",
|
||||
tilerendersize = "tile",
|
||||
fillmode = "stretch",
|
||||
tileoffset = {
|
||||
x = 0,
|
||||
y = 0
|
||||
},
|
||||
grid = {
|
||||
orientation = "orthogonal",
|
||||
width = 64,
|
||||
height = 64
|
||||
},
|
||||
properties = {},
|
||||
wangsets = {},
|
||||
tilecount = 54,
|
||||
tiles = {}
|
||||
}
|
||||
},
|
||||
layers = {
|
||||
{
|
||||
type = "tilelayer",
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 25,
|
||||
height = 15,
|
||||
id = 1,
|
||||
name = "floor",
|
||||
class = "",
|
||||
visible = true,
|
||||
opacity = 1,
|
||||
offsetx = 0,
|
||||
offsety = 0,
|
||||
parallaxx = 1,
|
||||
parallaxy = 1,
|
||||
properties = {},
|
||||
encoding = "lua",
|
||||
data = {
|
||||
1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12,
|
||||
19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21
|
||||
}
|
||||
},
|
||||
{
|
||||
type = "tilelayer",
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 25,
|
||||
height = 15,
|
||||
id = 2,
|
||||
name = "inner walls",
|
||||
class = "",
|
||||
visible = true,
|
||||
opacity = 1,
|
||||
offsetx = 0,
|
||||
offsety = 0,
|
||||
parallaxx = 1,
|
||||
parallaxy = 1,
|
||||
properties = {},
|
||||
encoding = "lua",
|
||||
data = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 33, 34, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 31, 0, 0, 0,
|
||||
0, 42, 43, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 34, 35, 54, 40, 0, 0, 0,
|
||||
0, 51, 52, 53, 0, 0, 0, 0, 0, 31, 31, 31, 0, 0, 0, 0, 0, 42, 43, 44, 54, 11, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 11, 11, 11, 40, 40, 40, 0, 0, 0, 0, 0, 51, 52, 53, 31, 11, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 32, 32, 32, 33, 34, 35, 31, 31, 0, 0, 0, 54, 54, 31, 40, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 41, 41, 41, 42, 43, 44, 40, 40, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 52, 53, 32, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 33, 34, 34, 34, 35, 0, 0, 0, 0, 41, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 42, 43, 43, 43, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 0, 0, 0, 0,
|
||||
0, 0, 0, 42, 43, 43, 43, 44, 0, 0, 0, 0, 0, 0, 0, 33, 34, 34, 34, 34, 35, 0, 0, 0, 0,
|
||||
0, 0, 0, 51, 52, 52, 52, 53, 0, 0, 0, 0, 0, 0, 0, 42, 43, 43, 43, 43, 44, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 52, 52, 52, 52, 53, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
}
|
||||
},
|
||||
{
|
||||
type = "objectgroup",
|
||||
draworder = "topdown",
|
||||
id = 3,
|
||||
name = "Walls",
|
||||
class = "",
|
||||
visible = false,
|
||||
opacity = 1,
|
||||
offsetx = 0,
|
||||
offsety = 0,
|
||||
parallaxx = 1,
|
||||
parallaxy = 1,
|
||||
properties = {},
|
||||
objects = {
|
||||
{
|
||||
id = 1,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 1600,
|
||||
height = 16,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 2,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 1584,
|
||||
y = 0,
|
||||
width = 16,
|
||||
height = 960,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 3,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 0,
|
||||
y = 944,
|
||||
width = 1600,
|
||||
height = 16,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 4,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 0,
|
||||
y = 0,
|
||||
width = 16,
|
||||
height = 960,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 5,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 112,
|
||||
y = 64,
|
||||
width = 96,
|
||||
height = 192,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 6,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 400,
|
||||
y = 368,
|
||||
width = 160,
|
||||
height = 64,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 7,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 576,
|
||||
y = 208,
|
||||
width = 192,
|
||||
height = 96,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 8,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 608,
|
||||
y = 336,
|
||||
width = 128,
|
||||
height = 160,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 10,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 768,
|
||||
y = 336,
|
||||
width = 128,
|
||||
height = 96,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 11,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 768,
|
||||
y = 496,
|
||||
width = 128,
|
||||
height = 64,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 13,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 240,
|
||||
y = 528,
|
||||
width = 224,
|
||||
height = 224,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 14,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 1008,
|
||||
y = 656,
|
||||
width = 272,
|
||||
height = 144,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 15,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 1168,
|
||||
y = 592,
|
||||
width = 160,
|
||||
height = 32,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 16,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 1120,
|
||||
y = 144,
|
||||
width = 128,
|
||||
height = 160,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 17,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 1104,
|
||||
y = 336,
|
||||
width = 96,
|
||||
height = 32,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 19,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 1216,
|
||||
y = 336,
|
||||
width = 64,
|
||||
height = 96,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 20,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 1280,
|
||||
y = 80,
|
||||
width = 64,
|
||||
height = 288,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
},
|
||||
{
|
||||
id = 22,
|
||||
name = "",
|
||||
type = "",
|
||||
shape = "rectangle",
|
||||
x = 1344,
|
||||
y = 80,
|
||||
width = 64,
|
||||
height = 96,
|
||||
rotation = 0,
|
||||
visible = true,
|
||||
properties = {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.11.0" orientation="orthogonal" renderorder="right-down" width="25" height="15" tilewidth="64" tileheight="64" infinite="0" nextlayerid="4" nextobjectid="23">
|
||||
<editorsettings>
|
||||
<export target="map3.lua" format="lua"/>
|
||||
</editorsettings>
|
||||
<tileset firstgid="1" name="tileset" tilewidth="64" tileheight="64" tilecount="54" columns="9">
|
||||
<image source="../assets/tileset.png" width="576" height="384"/>
|
||||
</tileset>
|
||||
<layer id="1" name="floor" width="25" height="15">
|
||||
<data encoding="csv">
|
||||
1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,
|
||||
19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21
|
||||
</data>
|
||||
</layer>
|
||||
<layer id="2" name="inner walls" width="25" height="15">
|
||||
<data encoding="csv">
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,33,34,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,54,31,0,0,0,
|
||||
0,42,43,44,0,0,0,0,0,0,0,0,0,0,0,0,0,33,34,35,54,40,0,0,0,
|
||||
0,51,52,53,0,0,0,0,0,31,31,31,0,0,0,0,0,42,43,44,54,11,0,0,0,
|
||||
0,0,0,0,0,0,11,11,11,40,40,40,0,0,0,0,0,51,52,53,31,11,0,0,0,
|
||||
0,0,0,0,0,0,32,32,32,33,34,35,31,31,0,0,0,54,54,31,40,0,0,0,0,
|
||||
0,0,0,0,0,0,41,41,41,42,43,44,40,40,0,0,0,0,0,40,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,51,52,53,32,32,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,33,34,34,34,35,0,0,0,0,41,41,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,42,43,43,43,44,0,0,0,0,0,0,0,0,0,0,54,54,54,0,0,0,0,
|
||||
0,0,0,42,43,43,43,44,0,0,0,0,0,0,0,33,34,34,34,34,35,0,0,0,0,
|
||||
0,0,0,51,52,52,52,53,0,0,0,0,0,0,0,42,43,43,43,43,44,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,51,52,52,52,52,53,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||
</data>
|
||||
</layer>
|
||||
<objectgroup id="3" name="Walls" visible="0">
|
||||
<object id="1" x="0" y="0" width="1600" height="16"/>
|
||||
<object id="2" x="1584" y="0" width="16" height="960"/>
|
||||
<object id="3" x="0" y="944" width="1600" height="16"/>
|
||||
<object id="4" x="0" y="0" width="16" height="960"/>
|
||||
<object id="5" x="112" y="64" width="96" height="192"/>
|
||||
<object id="6" x="400" y="368" width="160" height="64"/>
|
||||
<object id="7" x="576" y="208" width="192" height="96"/>
|
||||
<object id="8" x="608" y="336" width="128" height="160"/>
|
||||
<object id="10" x="768" y="336" width="128" height="96"/>
|
||||
<object id="11" x="768" y="496" width="128" height="64"/>
|
||||
<object id="13" x="240" y="528" width="224" height="224"/>
|
||||
<object id="14" x="1008" y="656" width="272" height="144"/>
|
||||
<object id="15" x="1168" y="592" width="160" height="32"/>
|
||||
<object id="16" x="1120" y="144" width="128" height="160"/>
|
||||
<object id="17" x="1104" y="336" width="96" height="32"/>
|
||||
<object id="19" x="1216" y="336" width="64" height="96"/>
|
||||
<object id="20" x="1280" y="80" width="64" height="288"/>
|
||||
<object id="22" x="1344" y="80" width="64" height="96"/>
|
||||
</objectgroup>
|
||||
</map>
|
||||
+3
-1
@@ -1,4 +1,4 @@
|
||||
function loadMap(lvl)
|
||||
function LoadMap(lvl)
|
||||
local mapfilelocation = "maps/"
|
||||
local extention = ".lua"
|
||||
local mapname = mapfilelocation .. "map" .. lvl .. extention
|
||||
@@ -8,6 +8,8 @@ function loadMap(lvl)
|
||||
if GameMap.layers["Walls"] then
|
||||
for _, obj in ipairs(GameMap.layers["Walls"].objects) do
|
||||
local wall = World:newRectangleCollider(obj.x, obj.y, obj.width, obj.height)
|
||||
-- local wall =
|
||||
-- World:newRectangleCollider(obj.x * _G.X_SCALE, obj.y * _G.Y_SCALE, obj.width * _G.X_SCALE, obj.height * _G.Y_SCALE)
|
||||
wall:setType("static")
|
||||
table.insert(Walls, wall)
|
||||
Walls[#Walls]:setCollisionClass("Wall")
|
||||
|
||||
+76
-60
@@ -1,21 +1,28 @@
|
||||
Player = Object:extend()
|
||||
cos = math.cos
|
||||
sin = math.sin --optimisation
|
||||
local cos = math.cos
|
||||
local sin = math.sin --optimisation
|
||||
local max = math.max --optimisation
|
||||
|
||||
-- Constructor for the Player class
|
||||
function Player:new(p, x, y, health, image, speed, max)
|
||||
function Player:new(p, x, y, health, image, speed)
|
||||
self.p = p
|
||||
self.image = love.graphics.newImage(image)
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.health = health
|
||||
self.speed = speed
|
||||
self.max = max
|
||||
self.bulletSpeed = _G.DEFAULT_B_SPEED
|
||||
self.width = self.image:getWidth()
|
||||
self.height = self.image:getHeight()
|
||||
|
||||
--Power up flag
|
||||
self.PUName = ""
|
||||
self.PUTime = 0
|
||||
self.PUOn = false
|
||||
|
||||
-- Collision Stuff
|
||||
self.collider = World:newRectangleCollider(x, y, 64, 64)
|
||||
--self.collider = World:newRectangleCollider(x, y, 50, 62)
|
||||
self.collider = World:newCircleCollider(x, y, 24 * X_SCALE)
|
||||
self.collider:setFixedRotation(true)
|
||||
|
||||
if self.p == 1 then
|
||||
@@ -26,7 +33,9 @@ function Player:new(p, x, y, health, image, speed, max)
|
||||
|
||||
-- Rotation Stuff
|
||||
self.rotation = math.rad(90)
|
||||
self.rotSpeed = 2
|
||||
self.rotSpeed = 3
|
||||
--self.scaleX = 1 * _G.X_SCALE
|
||||
--self.scaleY = 1 * _G.Y_SCALE
|
||||
self.scaleX = 1
|
||||
self.scaleY = 1
|
||||
self.originX = self.width / 2
|
||||
@@ -35,93 +44,100 @@ function Player:new(p, x, y, health, image, speed, max)
|
||||
--Velocity
|
||||
self.vx = 0
|
||||
self.vy = 0
|
||||
|
||||
-- Bullets shot
|
||||
self.shot = 0
|
||||
end
|
||||
|
||||
-- Method to handle shooting
|
||||
function Player:shoot(bulletSpeed)
|
||||
if self.shot <= 10 then
|
||||
self.shot = self.shot + 1
|
||||
local offsetX = cos(self.rotation) * self.width * 1.5
|
||||
local offsetY = sin(self.rotation) * self.height * 1.5
|
||||
local bulletX = self.x + offsetX
|
||||
local bulletY = self.y + offsetY
|
||||
local newBullet = Bullet(bulletX, bulletY, self.p, bulletSpeed, self.rotation)
|
||||
return newBullet
|
||||
else
|
||||
return nil
|
||||
end
|
||||
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)
|
||||
end
|
||||
if love.keyboard.isDown(down) then
|
||||
self.vx = cos(self.rotation) * (self.speed / 2 * dt) * -1
|
||||
self.vy = sin(self.rotation) * (self.speed / 2 * dt) * -1
|
||||
end
|
||||
if love.keyboard.isDown(left) then
|
||||
self.rotation = self.rotation - (self.rotSpeed * dt)
|
||||
end
|
||||
if 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
|
||||
|
||||
function Player:getPowerUp(powerup)
|
||||
self.PUName = powerup.name
|
||||
self.PUTime = powerup.time
|
||||
end
|
||||
|
||||
function Player:powerup()
|
||||
self.PUOn = true
|
||||
if self.PUName == "speed" then
|
||||
--print("" .. self.PUName .. " for " .. self.PUTime)
|
||||
self.speed = self.speed * 2.5
|
||||
self.rotSpeed = self.rotSpeed * 1.5
|
||||
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.PUOn then
|
||||
self.PUTime = self.PUTime - dt
|
||||
|
||||
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)
|
||||
if max(0, self.PUTime) <= 0 then
|
||||
-- Reset the player
|
||||
self.PUName = "none"
|
||||
self.PUTime = 0
|
||||
self.PUOn = false
|
||||
self.speed = _G.DEFAULT_P_SPEED
|
||||
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 EnableKeyPress1 == true and self.p == 1 then
|
||||
if love.keyboard.isDown("space") then
|
||||
local newBullet = self:shoot(bulletSpeed)
|
||||
local newBullet = self:shoot(self.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
|
||||
|
||||
if EnableKeyPress2 == true then
|
||||
if EnableKeyPress2 == true and self.p == 2 then
|
||||
if love.keyboard.isDown("return") then
|
||||
local newBullet = self:shoot(bulletSpeed)
|
||||
local newBullet = self:shoot(self.bulletSpeed)
|
||||
table.insert(Bullets2, newBullet)
|
||||
KeyPressTime2 = KeyDelay2
|
||||
EnableKeyPress2 = false
|
||||
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)
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
PowerUp = Object:extend()
|
||||
|
||||
-- PowerUp is the super class for all power-ups
|
||||
-- found in the game
|
||||
|
||||
function PowerUp:new(name, x, y, image, time, lifetime)
|
||||
self.name = name
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.image = love.graphics.newImage(image)
|
||||
self.time = time
|
||||
self.lifetime = lifetime
|
||||
|
||||
self.width = self.image:getWidth()
|
||||
self.height = self.image:getHeight()
|
||||
|
||||
self.rotation = 1
|
||||
self.scaleX = 1
|
||||
self.scaleY = 1
|
||||
self.originX = self.width / 2
|
||||
self.originY = self.height / 2
|
||||
|
||||
-- Collider
|
||||
self.collider = World:newRectangleCollider(x, y, 20, 20)
|
||||
self.collider:setCollisionClass("PowerUp")
|
||||
self.collider:setType("static")
|
||||
self.collider:setPosition(self.x, self.y)
|
||||
end
|
||||
-- TODO: clean up colliosn on reset
|
||||
-- make image draw
|
||||
-- more stuff
|
||||
-- see you later
|
||||
|
||||
function PowerUp:apply(player) end
|
||||
|
||||
function PowerUp:remove(player) end
|
||||
|
||||
function PowerUp:update(dt)
|
||||
--check if the power up is past its lifetime
|
||||
|
||||
--spinnnnnn
|
||||
self.rotation = self.rotation + 1.5 * dt
|
||||
|
||||
--grown and shrink
|
||||
--[[
|
||||
if self.scaleX >= 1.0 then
|
||||
self.scaleX = self.scaleX - 0.2 * dt
|
||||
elseif self.scaleX <= 1.8 then
|
||||
self.scaleX = self.scaleX + 0.2 * dt
|
||||
end
|
||||
]]
|
||||
|
||||
self.x = self.collider:getX()
|
||||
self.y = self.collider:getY()
|
||||
end
|
||||
|
||||
function PowerUp:draw()
|
||||
for _, _ in ipairs(PowerUps) do
|
||||
love.graphics.draw(
|
||||
self.image,
|
||||
self.x,
|
||||
self.y,
|
||||
self.rotation,
|
||||
self.scaleX,
|
||||
self.scaleY,
|
||||
self.originX,
|
||||
self.originY
|
||||
)
|
||||
end
|
||||
end
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
-- Functions to handle restarting
|
||||
-- all objects
|
||||
-- Used in map switching and restarting!
|
||||
|
||||
function ClearWalls(walls)
|
||||
for i = #walls, 1, -1 do
|
||||
walls[i]:destroy()
|
||||
end
|
||||
end
|
||||
|
||||
function ClearPowerUps(powerup)
|
||||
for i, v in ipairs(powerup) do
|
||||
v.collider:destroy()
|
||||
end
|
||||
end
|
||||
|
||||
function StopAllMusic()
|
||||
-- TODO: maybe find a way
|
||||
-- to find all 'streaming'
|
||||
-- sounds and then stop them all!
|
||||
musicBattle:stop()
|
||||
musicMenu:stop()
|
||||
musicPause:stop()
|
||||
--musicStory:stop() -- Unused
|
||||
end
|
||||
|
||||
function ClearPlayerCollision()
|
||||
UserPlayer1.collider:destroy()
|
||||
UserPlayer2.collider:destroy()
|
||||
end
|
||||
|
||||
function ClearBullets(bullets)
|
||||
for i, v in ipairs(bullets) do
|
||||
v.collider:destroy()
|
||||
end
|
||||
end
|
||||
|
||||
local function setNewLevelFromRandom(count)
|
||||
local level = math.random(1, count)
|
||||
_G.CUR_LEVEL = level
|
||||
end
|
||||
|
||||
local function getLevelCount()
|
||||
local levelcount = 0
|
||||
local suf = ".lua"
|
||||
local files = love.filesystem.getDirectoryItems("maps/")
|
||||
|
||||
for _, file in ipairs(files) do
|
||||
if string.find(file, suf) then
|
||||
levelcount = levelcount + 1
|
||||
--print(k .. ". " .. file) --outputs something like "1. main.lua"
|
||||
end
|
||||
end
|
||||
return levelcount
|
||||
end
|
||||
|
||||
function RestartGame()
|
||||
setNewLevelFromRandom(getLevelCount())
|
||||
|
||||
--reset wins
|
||||
_G.P1_WIN = false
|
||||
_G.P2_WIN = false
|
||||
|
||||
-- Stop the music
|
||||
--StopAllMusic()
|
||||
|
||||
-- Clear the players collision
|
||||
ClearPlayerCollision()
|
||||
|
||||
-- Work through and delete all bullets
|
||||
ClearBullets(Bullets1)
|
||||
ClearBullets(Bullets2)
|
||||
|
||||
--Clear Walls
|
||||
ClearWalls(Walls)
|
||||
|
||||
--ClearPowerUps
|
||||
ClearPowerUps(PowerUps)
|
||||
|
||||
-- Done!
|
||||
end
|
||||
@@ -0,0 +1,6 @@
|
||||
function truncate(number, decdigits)
|
||||
number = number * (10 ^ decdigits)
|
||||
number = math.floor(number)
|
||||
number = number / (10 ^ decdigits)
|
||||
return number
|
||||
end
|
||||
Reference in New Issue
Block a user