Compare commits

...
54 Commits
Author SHA1 Message Date
Simon Kellet 8d756beb1b removed 2024-08-06 18:16:45 +01:00
Simon Kellet 86d33a7fc5 nearly there 2024-08-06 18:16:14 +01:00
Simon Kellet 65f4ee3473 wip 2024-08-06 17:39:14 +01:00
Simon Kellet e13114a736 power up colliders for both players 2024-08-06 17:39:09 +01:00
Simon Kellet 451d710326 escape key on pause 2024-08-02 17:27:23 +01:00
Simon Kellet 585fccf59a flips 2024-08-02 17:27:13 +01:00
Simon Kellet 3290d704e3 less prints 2024-08-02 17:27:09 +01:00
Simon Kellet 7b553d9758 formatting 2024-08-02 17:26:50 +01:00
Simon Kellet e9e3731a0b new window size 2024-08-02 17:26:43 +01:00
Simon Kellet ed9265939c scale conts 2024-08-02 17:26:35 +01:00
Simon Kellet 826ef98f19 no longer needed 2024-08-02 17:26:29 +01:00
Simon Kellet db3559b8c6 player scaling 2024-08-02 17:26:22 +01:00
Simon Kellet 88faf2538b scaling 2024-08-02 17:26:14 +01:00
Simon Kellet 7252c3cd2c scaling now works? yay? 2024-08-02 17:01:21 +01:00
Simon Kellet 7da12d3035 added mute 2024-07-31 20:37:25 +01:00
Simon Kellet ebf9369ba5 fixed game logix 2024-07-31 20:37:17 +01:00
Simon Kellet f6be8b2a16 removed btton func 2024-07-31 20:36:53 +01:00
Simon Kellet beb4c28a70 removed button func 2024-07-31 20:36:47 +01:00
Simon Kellet 2d22a50447 added mute 2024-07-31 20:36:29 +01:00
Simon Kellet d059017989 fix win 2024-07-31 20:36:20 +01:00
Simon Kellet 49a57a6e5d better bouncing finally 2024-07-31 20:36:10 +01:00
Simon Kellet f5770cd4e0 ops 2024-07-31 20:35:55 +01:00
Simon Kellet 49633072ac reset wind 2024-07-31 20:35:49 +01:00
Simon Kellet 7af90632fd added truncate method 2024-07-31 20:35:42 +01:00
Simon Kellet e47799650a added start of gui lib 2024-07-31 15:20:12 +01:00
Simon Kellet 8440be2ab1 better movement 2024-07-24 16:19:18 +01:00
Simon Kellet df4184eec4 moved stuff out of love.load 2024-07-24 15:58:44 +01:00
Simon Kellet e8a124fa63 bullet bounce count 2024-07-24 15:58:32 +01:00
Simon Kellet 78b4379143 debug keys 2024-07-24 15:58:04 +01:00
Simon Kellet b8106bc1aa deletes bullets after bouncing so many times 2024-07-24 15:57:59 +01:00
Simon Kellet f8d71bb27d moved 2024-07-24 15:57:43 +01:00
Simon Kellet 7016961609 restart doesnt stop battle music 2024-07-24 15:57:37 +01:00
Simon Kellet 95502bc429 added map 3 2024-07-24 15:57:13 +01:00
Simon Kellet f689632a92 better wall init and clearing 2024-07-22 23:30:19 +01:00
Simon Kellet da8bb7d083 removed unused music stops 2024-07-22 23:30:08 +01:00
Simon Kellet 0a25d11ac5 hitbox now circle and slower going backwards 2024-07-22 23:29:55 +01:00
Simon Kellet 71d1f6e929 changed 2024-07-22 23:29:16 +01:00
Simon Kellet a392755844 global tracker of current level 2024-07-22 23:29:09 +01:00
Simon Kellet f4d3969a1f new map for testing 2024-07-22 23:29:00 +01:00
Simon Kellet 2cd9f263e6 walls now properly get destoryed 2024-07-22 23:28:53 +01:00
Simon Kellet ff40a5507b draws score 2024-07-22 23:28:46 +01:00
Simon Kellet 26398f52b5 fixed wins 2024-07-22 23:28:36 +01:00
Simon Kellet 38826d0cd8 debug keys 2024-07-22 23:28:26 +01:00
Simon Kellet 9e968b9390 done for tonight 2024-07-21 21:24:12 +01:00
Simon Kellet cdc0f35f22 handling win states and moving stuff to be global 2024-07-21 19:48:02 +01:00
Simon Kellet e83ee4ec64 there is now a win screen 2024-07-21 19:47:41 +01:00
Simon Kellet 7b68420027 maps now clear their Wall tables before making new ones 2024-07-21 19:47:18 +01:00
Simon Kellet 5f915f617e added restarting functions 2024-07-21 19:46:59 +01:00
Simon Kellet 6049054a26 removed windows build, got it wrong lol 2024-07-21 18:08:12 +01:00
Simon Kellet 99b420cc7f format and stuff 2024-07-21 18:07:07 +01:00
Simon Kellet 53aa6d698d changes 2024-07-21 13:47:33 +01:00
Simon Kellet 407436f7f0 added love2d version for building 2024-07-21 13:46:55 +01:00
Simon Kellet 8a206de2a1 links 2024-07-21 13:46:42 +01:00
Simon Kellet 5830863a5f added windows build script 2024-07-21 13:46:32 +01:00
41 changed files with 4702 additions and 3230 deletions
+2 -1
View File
@@ -1,2 +1,3 @@
.DS_Store .DS_Store
game.love *.love
*.exe
+19 -6
View File
@@ -6,15 +6,18 @@ end
local function drawHealth() local function drawHealth()
love.graphics.setFont(GameFont) love.graphics.setFont(GameFont)
local height = love.graphics.getHeight() local height = love.graphics.getHeight() / _G.Y_SCALE
local width = love.graphics.getWidth() local width = love.graphics.getWidth() / _G.X_SCALE
love.graphics.print("" .. UserPlayer1.health, 5, 5) love.graphics.print("P1:" .. UserPlayer1.health, 5, 5)
love.graphics.print("" .. UserPlayer2.health, width - 70, height - 95) love.graphics.print("P2:" .. UserPlayer2.health, width - 200, height - 95)
end end
function DrawGame() function DrawGame()
--STI local curWidth, curHeight = love.graphics.getDimensions()
GameMap:draw() 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 -- WindField
if DebugFlag then if DebugFlag then
@@ -22,6 +25,7 @@ function DrawGame()
drawFPS() drawFPS()
end end
-- Bullets
for _, v in ipairs(Bullets1) do for _, v in ipairs(Bullets1) do
v:draw() v:draw()
end end
@@ -29,9 +33,18 @@ function DrawGame()
v:draw() v:draw()
end end
-- PowerUps
for _, v in ipairs(PowerUps) do
v:draw()
end
UserPlayer1:draw() UserPlayer1:draw()
UserPlayer2:draw() UserPlayer2:draw()
--Draw Health --Draw Health
--TODO: make this nicer looking
drawHealth() drawHealth()
--TODO: draw running score
--drawScore()
end end
+40 -5
View File
@@ -1,11 +1,15 @@
function GameKeyPressed(key) function GameKeyPressed(key)
if key == "escape" then if key == "escape" then
if not MUTED then
musicBattle:setVolume(0) musicBattle:setVolume(0)
musicPause:setVolume(0.5) musicPause:setVolume(0.6)
else
musicBattle:setVolume(0)
musicPause:setVolume(0)
end
_G.GAMESTATE = "PAUSE" _G.GAMESTATE = "PAUSE"
print("STATE CHANEGD: PAUSED!") --print("STATE CHANEGD: PAUSED!")
_G.PAUSED = true _G.PAUSED = true
end end
@@ -13,9 +17,40 @@ function GameKeyPressed(key)
DebugFlag = not DebugFlag DebugFlag = not DebugFlag
end end
--[[
if key == "r" and not _G.PAUSED then if key == "r" and not _G.PAUSED then
RestartGame()
_G.GAMESTATE = "GAME"
love.load() love.load()
end 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 end
+105 -13
View File
@@ -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) function UpdateGame(dt)
--Check if anyone has won
if checkLossState() == true then
--print("STATE CHNAGED: WIN!")
_G.GAMESTATE = "WIN"
end
--WindField --WindField
World:update(dt) World:update(dt)
local max = math.max
KeyPressTime1 = max(0, KeyPressTime1 - dt) KeyPressTime1 = max(0, KeyPressTime1 - dt)
if KeyPressTime1 <= 0 then if KeyPressTime1 <= 0 then
EnableKeyPress1 = true EnableKeyPress1 = true
@@ -13,8 +36,18 @@ function UpdateGame(dt)
if KeyPressTime2 <= 0 then if KeyPressTime2 <= 0 then
EnableKeyPress2 = true EnableKeyPress2 = true
end end
-- Update Bullets1
for i, v in ipairs(Bullets1) do for i, v in ipairs(Bullets1) do
v:update(dt) 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 if v.y < 0 then --top of screen
table.remove(Bullets1, i) table.remove(Bullets1, i)
v.collider:destroy() v.collider:destroy()
@@ -22,27 +55,45 @@ function UpdateGame(dt)
table.remove(Bullets1, i) table.remove(Bullets1, i)
v.collider:destroy() v.collider:destroy()
end end
-- Hit player2
if v.collider:enter("Player2") then if v.collider:enter("Player2") then
print("Player1 hit Player2!") --print("Player1 hit Player2!")
table.remove(Bullets1, i) table.remove(Bullets1, i)
v.collider:destroy() v.collider:destroy()
if UserPlayer1.health > 0 then if UserPlayer2.health > 0 then
UserPlayer1.health = UserPlayer1.health - 1 UserPlayer2.health = UserPlayer2.health - 1
end end
end end
-- Hit player1
if v.collider:enter("Player1") then if v.collider:enter("Player1") then
print("Player 1 hit themselves!") --print("Player 1 hit themselves!")
table.remove(Bullets1, i) table.remove(Bullets1, i)
v.collider:destroy() v.collider:destroy()
if UserPlayer1.health > 0 then if UserPlayer1.health > 0 then
UserPlayer1.health = UserPlayer1.health - 1 UserPlayer1.health = UserPlayer1.health - 1
end end
end end
--Hit another bullet
if v.collider:enter("Bullet1") then
table.remove(Bullets1, i)
v.collider:destroy()
end
end end
-- Update Bullets2
for i, v in ipairs(Bullets2) do for i, v in ipairs(Bullets2) do
v:update(dt) 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 if v.y < 0 then --top of screen
table.remove(Bullets2, i) table.remove(Bullets2, i)
v.collider:destroy() v.collider:destroy()
@@ -51,25 +102,66 @@ function UpdateGame(dt)
v.collider:destroy() v.collider:destroy()
end end
-- Hit player1
if v.collider:enter("Player1") then 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) table.remove(Bullets2, i)
v.collider:destroy() v.collider:destroy()
if UserPlayer2.health > 0 then if UserPlayer2.health > 0 then
UserPlayer2.health = UserPlayer2.health - 1 UserPlayer2.health = UserPlayer2.health - 1
end end
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 if v.collider:enter("Player2") then
print("Player 2 hit themselves!") UserPlayer2:getPowerUp(v)
table.remove(Bullets2, i) UserPlayer2:powerup()
table.remove(PowerUps, i)
v.collider:destroy() v.collider:destroy()
if UserPlayer2.health > 0 then
UserPlayer2.health = UserPlayer2.health - 1
end
end 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) UserPlayer1:update(dt)
UserPlayer2:update(dt) UserPlayer2:update(dt)
end end
+11 -35
View File
@@ -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 function title()
local height = love.graphics.getHeight() local height = love.graphics.getHeight() / _G.Y_SCALE
local width = love.graphics.getWidth() local width = love.graphics.getWidth() / _G.X_SCALE
love.graphics.setFont(GameFont) love.graphics.setFont(GameFont)
love.graphics.setColor(0.5,1,1) love.graphics.setColor(0.5, 1, 1)
love.graphics.rectangle("fill", 0, 0, width, height) love.graphics.rectangle("fill", 0, 0, width, height)
love.graphics.setColor(0,0,0) love.graphics.setColor(0, 0, 0)
love.graphics.print("MENU", 100,100) love.graphics.print("Tanks-A-Lot", 100, 100)
end end
function DrawMenu() function DrawMenu()
local bwidth, bheight = 300, 140 local bwidth, bheight = 300 * _G.X_SCALE, 140 * _G.Y_SCALE
title() title()
button(100, 200, bwidth, bheight, "Play", MENU_POS == 0 and true or false) GUI:newButton(100, 200, bwidth, bheight, "Play", MENU_POS == 0 and true or false)
button(100, 350, bwidth, bheight, "???", MENU_POS == 1 and true or false) GUI:newButton(100, 350, bwidth, bheight, "???", MENU_POS == 1 and true or false)
button(100, 500, bwidth, bheight, "???", MENU_POS == 2 and true or false) GUI:newButton(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, 650, bwidth, bheight, "Quit", MENU_POS == 3 and true or false)
love.graphics.setColor(255,255,255) -- reset colours love.graphics.setColor(255, 255, 255) -- reset colours
end end
+21 -8
View File
@@ -1,5 +1,5 @@
function MenuKeyPressed(key) function MenuKeyPressed(key)
if key == 'return' then if key == "return" then
-- 0 Start Game -- 0 Start Game
-- 1 ?? -- 1 ??
-- 2 ??? -- 2 ???
@@ -7,19 +7,15 @@ function MenuKeyPressed(key)
if MENU_POS == 0 then if MENU_POS == 0 then
--Change state to GAME! --Change state to GAME!
_G.GAMESTATE = "GAME" _G.GAMESTATE = "GAME"
print("STATE CHANEGD: GAME!") --print("STATE CHANEGD: GAME!")
musicMenu:stop() musicMenu:stop()
elseif MENU_POS == 1 then elseif MENU_POS == 1 then
print("STATE CHANEGD: DUNNO!") --print("STATE CHANEGD: DUNNO!")
elseif MENU_POS == 2 then elseif MENU_POS == 2 then
print("STATE CHANEGD: DUNNO!") --print("STATE CHANEGD: DUNNO!")
elseif MENU_POS == 3 then elseif MENU_POS == 3 then
love.event.quit() love.event.quit()
end end
end end
if love.keyboard.isDown("up") then if love.keyboard.isDown("up") then
@@ -38,4 +34,21 @@ function MenuKeyPressed(key)
end end
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 end
+1 -2
View File
@@ -1,2 +1 @@
function UpdateMenu(dt) function UpdateMenu(dt) end
end
+11 -35
View File
@@ -1,45 +1,21 @@
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() function DrawPause()
local opacity = 0.3 local opacity = 0.3
local height = love.graphics.getHeight() local height = love.graphics.getHeight() / _G.Y_SCALE
local width = love.graphics.getWidth() local width = love.graphics.getWidth() / _G.X_SCALE
local bwidth, bheight = 300, 140 local bwidth, bheight = 300 * _G.X_SCALE, 140 * _G.Y_SCALE
love.graphics.setFont(GameFont) love.graphics.setFont(GameFont)
DrawGame() --Draw a single frame of the game DrawGame() --Draw a single frame of the game
love.graphics.setColor(0.1,0.1,0.1, opacity) --overlay opaque img love.graphics.setColor(0.1, 0.1, 0.1, opacity) --overlay opaque img
love.graphics.rectangle("fill", 0, 0, width, height) love.graphics.rectangle("fill", 0, 0, width, height)
love.graphics.setColor(1,1,1) love.graphics.setColor(1, 1, 1)
love.graphics.print("PAUSED", 100,100) love.graphics.print("PAUSED", 100, 100)
--love.graphics.print("" .. PAUSE_POS, 200,200) --love.graphics.print("" .. PAUSE_POS, 200,200)
button(100, 200, bwidth, bheight, "Return", PAUSE_POS == 0 and true or false) GUI:newButton(100, 200, bwidth, bheight, "Back", PAUSE_POS == 0 and true or false)
button(100, 350, bwidth, bheight, "Menu", PAUSE_POS == 1 and true or false) GUI:newButton(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, 500, bwidth, bheight, "Quit", PAUSE_POS == 2 and true or false)
love.graphics.setColor(255,255,255) -- reset colours
love.graphics.setColor(255, 255, 255) -- reset colours
end end
+26 -7
View File
@@ -1,31 +1,50 @@
function PauseKeyPressed(key) 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) musicBattle:setVolume(0.5)
musicPause:setVolume(0) 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 -- 0 Return to game
-- 1 Quit -- 1 Quit
if PAUSE_POS == 0 then if PAUSE_POS == 0 then
-- unpause the game -- unpause the game
_G.GAMESTATE = "GAME" _G.GAMESTATE = "GAME"
print("STATE CHANEGD: GAME!") --print("STATE CHANEGD: GAME!")
_G.PAUSED = false _G.PAUSED = false
if not MUTED then
musicBattle:setVolume(0.5) musicBattle:setVolume(0.5)
musicPause:setVolume(0) musicPause:setVolume(0)
else
musicBattle:setVolume(0)
musicPause:setVolume(0)
end
elseif PAUSE_POS == 1 then elseif PAUSE_POS == 1 then
_G.GAMESTATE = "MENU" _G.GAMESTATE = "MENU"
print("STATE CHANEGD: MENU!") --print("STATE CHANEGD: MENU!")
_G.PAUSED = false _G.PAUSED = false
musicPause:stop() musicPause:stop()
musicBattle:stop() musicBattle:stop()
elseif PAUSE_POS == 2 then elseif PAUSE_POS == 2 then
love.event.quit() love.event.quit()
end end
end end
if love.keyboard.isDown("up") then if love.keyboard.isDown("up") then
if _G.PAUSE_POS == 0 then if _G.PAUSE_POS == 0 then
_G.PAUSE_POS = 0 _G.PAUSE_POS = 0
+1 -2
View File
@@ -1,2 +1 @@
function UpdatePause(dt) function UpdatePause(dt) end
end
+3 -3
View File
@@ -18,9 +18,9 @@ love game.love
## Credits ## Credits
* Programming: Simon * Programming: Simon [website](https://simonkellet.xyz)
* Art: Laura * Art: Laura
* Music: AJ * Music: AJ [website](https://sites.google.com/view/bitnailz)
# TODOS # TODOS
@@ -28,7 +28,7 @@ love game.love
* [] Better music (for each level) * [] Better music (for each level)
* [] Fill out menu screen * [] Fill out menu screen
* [] Finally fix physics * [] Finally fix physics
* [] Make more maps (different lecel types) * [] Make more maps (different level types)
* [] Fix player placement at start of game * [] Fix player placement at start of game
## Git ## Git
+39
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
function UpdateWin(dt) end
+7
View File
@@ -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

Regular → Executable
+60 -19
View File
@@ -1,7 +1,9 @@
Bullet = Object:extend() Bullet = Object:extend()
cos = math.cos local pi = math.pi
sin = math.sin --optimisation local cos = math.cos
pi = math.pi local sin = math.sin
local atan = math.atan
local deg = math.deg
function Bullet:new(x, y, p, speed, rotation) function Bullet:new(x, y, p, speed, rotation)
self.image = love.graphics.newImage("assets/bullet.png") 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.originY = self.height / 2
self.collider = World:newCircleCollider(x, y, 10) self.collider = World:newCircleCollider(x, y, 10)
--self.collider = World:newRectangleCollider(x, y, 10, 10)
--self.collider:setType("")
self.collider:setFixedRotation(true) self.collider:setFixedRotation(true)
self.collider:setPosition(self.x, self.y) self.collider:setPosition(self.x, self.y)
self.bounce = 0 -- how many times this bullet has bounced
if self.p == 1 then if self.p == 1 then
self.collider:setCollisionClass("Bullet1") self.collider:setCollisionClass("Bullet1")
elseif self.p == 2 then elseif self.p == 2 then
@@ -31,34 +37,69 @@ function Bullet:new(x, y, p, speed, rotation)
end end
function Bullet:update(dt) function Bullet:update(dt)
--If a bullet hits a wall, make it bounce off the wall and change its rotation -- Calculate the velocity
if self.p == 1 then 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 if self.collider:enter("Wall") then
--calculate normal of the wall self.bounce = self.bounce + 1
--calculate the angle of reflection
local angle = (math.atan2(dx, dy))
--get normal of the wall without using getNormal()
----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 -- Debug print to verify normal values
self.rotation = angle --print("---------------------------------")
self.collider:setAngle(self.rotation) --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 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 dx = cos(self.rotation) * self.speed * dt
dy = sin(self.rotation) * self.speed * dt dy = sin(self.rotation) * self.speed * dt
self.collider:setLinearVelocity(dx, dy) self.collider:setLinearVelocity(dx, dy)
end 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 end
-- Update position
self.x = self.collider:getX() self.x = self.collider:getX()
self.y = self.collider:getY() self.y = self.collider:getY()
end end
]]
function Bullet:draw() function Bullet:draw()
for _, _ in ipairs(Bullets1) do for _, _ in ipairs(Bullets1) do
View File
+6
View File
@@ -1,6 +1,12 @@
--Config file for the game --Config file for the game
function love.conf(t) function love.conf(t)
--t.window.width = 1366
--t.window.height = 768
t.window.width = 1600 t.window.width = 1600
t.window.height = 960 t.window.height = 960
t.window.title = "Tanks-A-Lot" t.window.title = "Tanks-A-Lot"
t.window.resizable = true
t.version = "11.5"
end end
+46
View File
@@ -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" GAMESTATE = "MENU"
-- Mute for game
MUTED = false
MENU_POS = 0 MENU_POS = 0
MENU_MAX = 3 --0 play, 1 ?, 2 ?, 3 quit MENU_MAX = 3 --0 play, 1 ?, 2 ?, 3 quit
PAUSED = false PAUSED = false
PAUSE_POS = 0 PAUSE_POS = 0
PAUSE_MAX = 2 -- 0 resume, 1 menu, 2 quit 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
BIN
View File
Binary file not shown.
+43
View File
@@ -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
+2 -11
View File
@@ -7,14 +7,10 @@
-- the terms of the MIT license. See LICENSE for details. -- the terms of the MIT license. See LICENSE for details.
-- --
local Object = {} local Object = {}
Object.__index = Object Object.__index = Object
function Object:new() end
function Object:new()
end
function Object:extend() function Object:extend()
local cls = {} local cls = {}
@@ -29,9 +25,8 @@ function Object:extend()
return cls return cls
end end
function Object:implement(...) function Object:implement(...)
for _, cls in pairs({...}) do for _, cls in pairs({ ... }) do
for k, v in pairs(cls) do for k, v in pairs(cls) do
if self[k] == nil and type(v) == "function" then if self[k] == nil and type(v) == "function" then
self[k] = v self[k] = v
@@ -40,7 +35,6 @@ function Object:implement(...)
end end
end end
function Object:is(T) function Object:is(T)
local mt = getmetatable(self) local mt = getmetatable(self)
while mt do while mt do
@@ -52,17 +46,14 @@ function Object:is(T)
return false return false
end end
function Object:__tostring() function Object:__tostring()
return "Object" return "Object"
end end
function Object:__call(...) function Object:__call(...)
local obj = setmetatable({}, self) local obj = setmetatable({}, self)
obj:new(...) obj:new(...)
return obj return obj
end end
return Object return Object
-193
View File
@@ -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
+49 -20
View File
@@ -10,13 +10,12 @@ local module = {}
-- @param sort If "size" will sort by size, or if "id" will sort by id -- @param sort If "size" will sort by size, or if "id" will sort by id
-- @param ids Array with ids of each file -- @param ids Array with ids of each file
-- @param pow2 If true, will force a power of 2 size -- @param pow2 If true, will force a power of 2 size
function module.Atlas( files, sort, ids, pow2 ) function module.Atlas(files, sort, ids, pow2)
local function Node(x, y, w, h) local function Node(x, y, w, h)
return {x = x, y = y, w = w, h = h} return { x = x, y = y, w = w, h = h }
end end
local function nextpow2( n ) local function nextpow2(n)
local res = 1 local res = 1
while res <= n do while res <= n do
res = res * 2 res = res * 2
@@ -29,14 +28,18 @@ function module.Atlas( files, sort, ids, pow2 )
for i = 1, #files do for i = 1, #files do
images[i] = {} images[i] = {}
--images[i].name = files[i] --images[i].name = files[i]
if ids then images[i].id = ids[i] end if ids then
images[i].img = love.graphics.newImage( files[i] ) images[i].id = ids[i]
end
images[i].img = love.graphics.newImage(files[i])
images[i].w = images[i].img:getWidth() images[i].w = images[i].img:getWidth()
images[i].h = images[i].img:getHeight() images[i].h = images[i].img:getHeight()
images[i].area = images[i].w * images[i].h images[i].area = images[i].w * images[i].h
end end
if sort == "size" or sort == "id" then 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 end
return images return images
end end
@@ -46,16 +49,22 @@ function module.Atlas( files, sort, ids, pow2 )
if root.left or root.right then if root.left or root.right then
if root.left then if root.left then
local node = add(root.left, id, w, h) local node = add(root.left, id, w, h)
if node then return node end if node then
return node
end
end end
if root.right then if root.right then
local node = add(root.right, id, w, h) local node = add(root.right, id, w, h)
if node then return node end if node then
return node
end
end end
return nil return nil
end 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 local _w, _h = root.w - w, root.h - h
@@ -75,7 +84,9 @@ function module.Atlas( files, sort, ids, pow2 )
end end
local function unmap(root) local function unmap(root)
if not root then return {} end if not root then
return {}
end
local tree = {} local tree = {}
if root.id then if root.id then
@@ -105,8 +116,12 @@ function module.Atlas( files, sort, ids, pow2 )
local w, h = images[1].w, images[1].h local w, h = images[1].w, images[1].h
if pow2 then if pow2 then
if w % 1 == 0 then w = nextpow2(w) end if w % 1 == 0 then
if h % 1 == 0 then h = nextpow2(h) end w = nextpow2(w)
end
if h % 1 == 0 then
h = nextpow2(h)
end
end end
repeat repeat
@@ -116,14 +131,24 @@ function module.Atlas( files, sort, ids, pow2 )
for i = 1, #images do for i = 1, #images do
node = add(root, i, images[i].w, images[i].h) node = add(root, i, images[i].w, images[i].h)
if not node then break end if not node then
break
end
end end
if not node then if not node then
if h <= w then if h <= w then
if pow2 then h = h * 2 else h = h + 1 end if pow2 then
h = h * 2
else 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 end
else else
break break
@@ -137,17 +162,21 @@ function module.Atlas( files, sort, ids, pow2 )
local coords = unmap(root) local coords = unmap(root)
local map = love.graphics.newCanvas(w, h) local map = love.graphics.newCanvas(w, h)
love.graphics.setCanvas( map ) love.graphics.setCanvas(map)
-- love.graphics.clear() -- love.graphics.clear()
for i = 1, #images do for i = 1, #images do
love.graphics.draw(images[i].img, coords[i].x, coords[i].y) 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 end
love.graphics.setCanvas() love.graphics.setCanvas()
if sort == "ids" then 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 end
return { image = map, coords = coords } return { image = map, coords = coords }
+59 -83
View File
@@ -9,12 +9,12 @@ local STI = {
_URL = "https://github.com/karai17/Simple-Tiled-Implementation", _URL = "https://github.com/karai17/Simple-Tiled-Implementation",
_VERSION = "1.2.3.0", _VERSION = "1.2.3.0",
_DESCRIPTION = "Simple Tiled Implementation is a Tiled Map Editor library designed for the *awesome* LÖVE framework.", _DESCRIPTION = "Simple Tiled Implementation is a Tiled Map Editor library designed for the *awesome* LÖVE framework.",
cache = {} cache = {},
} }
STI.__index = STI STI.__index = STI
local love = _G.love local love = _G.love
local cwd = (...):gsub('%.init$', '') .. "." local cwd = (...):gsub("%.init$", "") .. "."
local utils = require(cwd .. "utils") local utils = require(cwd .. "utils")
local ceil = math.ceil local ceil = math.ceil
local floor = math.floor local floor = math.floor
@@ -31,10 +31,7 @@ local function new(map, plugins, ox, oy)
else else
-- Check for valid map type -- Check for valid map type
local ext = map:sub(-4, -1) local ext = map:sub(-4, -1)
assert(ext == ".lua", string.format( assert(ext == ".lua", string.format("Invalid file type: %s. File must be of type: lua.", ext))
"Invalid file type: %s. File must be of type: lua.",
ext
))
-- Get directory of map -- Get directory of map
dir = map:reverse():find("[/\\]") or "" dir = map:reverse():find("[/\\]") or ""
@@ -86,7 +83,7 @@ function Map:init(path, plugins, ox, oy)
self.offsety = oy or 0 self.offsety = oy or 0
self.freeBatchSprites = {} self.freeBatchSprites = {}
setmetatable(self.freeBatchSprites, { __mode = 'k' }) setmetatable(self.freeBatchSprites, { __mode = "k" })
-- Set tiles, images -- Set tiles, images
local gid = 1 local gid = 1
@@ -111,11 +108,11 @@ function Map:init(path, plugins, ox, oy)
-- Build atlas for image collection -- Build atlas for image collection
local files, ids = {}, {} local files, ids = {}, {}
for j = 1, #tileset.tiles do for j = 1, #tileset.tiles do
files[ j ] = utils.format_path(path .. tileset.tiles[j].image) files[j] = utils.format_path(path .. tileset.tiles[j].image)
ids[ j ] = tileset.tiles[j].id ids[j] = tileset.tiles[j].id
end end
local map = atlas.Atlas( files, "ids", ids ) local map = atlas.Atlas(files, "ids", ids)
if lg.isCreated then if lg.isCreated then
local formatted_path = utils.format_path(path .. tileset.name) local formatted_path = utils.format_path(path .. tileset.name)
@@ -174,7 +171,7 @@ end
-- @param plugins A list of plugins to load -- @param plugins A list of plugins to load
function Map:loadPlugins(plugins) function Map:loadPlugins(plugins)
for _, plugin in ipairs(plugins) do for _, plugin in ipairs(plugins) do
local pluginModulePath = cwd .. 'plugins.' .. plugin local pluginModulePath = cwd .. "plugins." .. plugin
local ok, pluginModule = pcall(require, pluginModulePath) local ok, pluginModule = pcall(require, pluginModulePath)
if ok then if ok then
for k, func in pairs(pluginModule) do for k, func in pairs(pluginModule) do
@@ -232,11 +229,7 @@ function Map:setTiles(index, tileset, gid)
gid = gid, gid = gid,
tileset = index, tileset = index,
type = type, type = type,
quad = quad( quad = quad(quadX, quadY, tileW, tileH, imageW, imageH),
quadX, quadY,
tileW, tileH,
imageW, imageH
),
properties = properties or {}, properties = properties or {},
terrain = terrain, terrain = terrain,
animation = animation, animation = animation,
@@ -286,11 +279,7 @@ function Map:setAtlasTiles(index, tileset, coords, gid)
gid = firstgid + tile.id, gid = firstgid + tile.id,
tileset = index, tileset = index,
class = tile.class, class = tile.class,
quad = quad( quad = quad(coords[i].x, coords[i].y, tile.width, tile.height, imageW, imageH),
coords[i].x, coords[i].y,
tile.width, tile.height,
imageW, imageH
),
properties = tile.properties or {}, properties = tile.properties or {},
terrain = terrain, terrain = terrain,
animation = tile.animation, animation = tile.animation,
@@ -318,13 +307,19 @@ end
function Map:setLayer(layer, path) function Map:setLayer(layer, path)
if layer.encoding then if layer.encoding then
if layer.encoding == "base64" 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) local fd = love.data.decode("string", "base64", layer.data)
if not layer.compression then if not layer.compression then
layer.data = utils.get_decompressed_data(fd) layer.data = utils.get_decompressed_data(fd)
else 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 if layer.compression == "zlib" then
local data = love.data.decompress("string", "zlib", fd) local data = love.data.decompress("string", "zlib", fd)
@@ -346,14 +341,20 @@ function Map:setLayer(layer, path)
if layer.type == "tilelayer" then if layer.type == "tilelayer" then
self:setTileData(layer) self:setTileData(layer)
self:setSpriteBatches(layer) self:setSpriteBatches(layer)
layer.draw = function() self:drawTileLayer(layer) end layer.draw = function()
self:drawTileLayer(layer)
end
elseif layer.type == "objectgroup" then elseif layer.type == "objectgroup" then
self:setObjectData(layer) self:setObjectData(layer)
self:setObjectCoordinates(layer) self:setObjectCoordinates(layer)
self:setObjectSpriteBatches(layer) self:setObjectSpriteBatches(layer)
layer.draw = function() self:drawObjectLayer(layer) end layer.draw = function()
self:drawObjectLayer(layer)
end
elseif layer.type == "imagelayer" then elseif layer.type == "imagelayer" then
layer.draw = function() self:drawImageLayer(layer) end layer.draw = function()
self:drawImageLayer(layer)
end
if layer.image ~= "" then if layer.image ~= "" then
local formatted_path = utils.format_path(path .. layer.image) local formatted_path = utils.format_path(path .. layer.image)
@@ -424,10 +425,10 @@ function Map:setObjectCoordinates(layer)
object.rectangle = {} object.rectangle = {}
local vertices = { local vertices = {
{ x=x, y=y }, { x = x, y = y },
{ x=x + w, y=y }, { x = x + w, y = y },
{ x=x + w, y=y + h }, { x = x + w, y = y + h },
{ x=x, y=y + h }, { x = x, y = y + h },
} }
for _, vertex in ipairs(vertices) do for _, vertex in ipairs(vertices) do
@@ -551,7 +552,7 @@ function Map:addNewLayerTile(layer, chunk, tile, x, y)
x = tileX, x = tileX,
y = tileY, y = tileY,
r = tile.r, r = tile.r,
oy = 0 oy = 0,
} }
-- NOTE: STI can run headless so it is not guaranteed that a batch exists. -- NOTE: STI can run headless so it is not guaranteed that a batch exists.
@@ -738,7 +739,7 @@ function Map:setObjectSpriteBatches(layer)
x = tileX, x = tileX,
y = tileY - oy, y = tileY - oy,
r = tileR, r = tileR,
oy = oy oy = oy,
} }
self.tileInstances[tile.gid] = self.tileInstances[tile.gid] or {} 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.time = tile.time - tonumber(tile.animation[tile.frame].duration)
tile.frame = tile.frame + 1 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 end
if update and self.tileInstances[tile.gid] then if update and self.tileInstances[tile.gid] then
for _, j in pairs(self.tileInstances[tile.gid]) do 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) j.batch:set(j.id, t.quad, j.x, j.y, j.r, tile.sx, tile.sy, 0, j.oy)
end end
end end
@@ -948,18 +952,18 @@ end
--- Draw an individual Layer --- Draw an individual Layer
-- @param layer The Layer to draw -- @param layer The Layer to draw
function Map.drawLayer(_, layer) function Map.drawLayer(_, layer)
local r,g,b,a = lg.getColor() local r, g, b, a = lg.getColor()
-- if the layer has a tintcolor set -- if the layer has a tintcolor set
if layer.tintcolor then if layer.tintcolor then
r, g, b, a = unpack(layer.tintcolor) r, g, b, a = unpack(layer.tintcolor)
a = a or 255 -- alpha may not be specified a = a or 255 -- alpha may not be specified
lg.setColor(r/255, g/255, b/255, a/255) -- Tiled uses 0-255 lg.setColor(r / 255, g / 255, b / 255, a / 255) -- Tiled uses 0-255
-- if a tintcolor is not given just use the current color -- if a tintcolor is not given just use the current color
else else
lg.setColor(r, g, b, a * layer.opacity) lg.setColor(r, g, b, a * layer.opacity)
end end
layer:draw() layer:draw()
lg.setColor(r,g,b,a) lg.setColor(r, g, b, a)
end end
--- Default draw function for Tile Layers --- Default draw function for Tile Layers
@@ -998,7 +1002,7 @@ function Map:drawObjectLayer(layer)
local line = { 160, 160, 160, 255 * layer.opacity } local line = { 160, 160, 160, 255 * layer.opacity }
local fill = { 160, 160, 160, 255 * layer.opacity * 0.5 } local fill = { 160, 160, 160, 255 * layer.opacity * 0.5 }
local r,g,b,a = lg.getColor() local r, g, b, a = lg.getColor()
local reset = { r, g, b, a * layer.opacity } local reset = { r, g, b, a * layer.opacity }
local function sortVertices(obj) local function sortVertices(obj)
@@ -1058,7 +1062,7 @@ function Map:drawObjectLayer(layer)
for _, batch in pairs(layer.batches) do for _, batch in pairs(layer.batches) do
lg.draw(batch, 0, 0) lg.draw(batch, 0, 0)
end end
lg.setColor(r,g,b,a) lg.setColor(r, g, b, a)
end end
--- Default draw function for Image Layers --- Default draw function for Image Layers
@@ -1284,22 +1288,9 @@ function Map:swapTile(instance, tile)
-- Update sprite batch -- Update sprite batch
if instance.batch then if instance.batch then
if tile then if tile then
instance.batch:set( instance.batch:set(instance.id, tile.quad, instance.x, instance.y, tile.r, tile.sx, tile.sy)
instance.id,
tile.quad,
instance.x,
instance.y,
tile.r,
tile.sx,
tile.sy
)
else else
instance.batch:set( instance.batch:set(instance.id, instance.x, instance.y, 0, 0)
instance.id,
instance.x,
instance.y,
0,
0)
self.freeBatchSprites[instance.batch] = self.freeBatchSprites[instance.batch] or {} self.freeBatchSprites[instance.batch] = self.freeBatchSprites[instance.batch] or {}
table.insert(self.freeBatchSprites[instance.batch], instance) table.insert(self.freeBatchSprites[instance.batch], instance)
@@ -1344,35 +1335,26 @@ end
-- @param y The Y axis location of the point (in tiles) -- @param y The Y axis location of the point (in tiles)
-- @return number The X axis location of the point (in pixels) -- @return number The X axis location of the point (in pixels)
-- @return number The Y axis location of the point (in pixels) -- @return number The Y axis location of the point (in pixels)
function Map:convertTileToPixel(x,y) function Map:convertTileToPixel(x, y)
if self.orientation == "orthogonal" then if self.orientation == "orthogonal" then
local tileW = self.tilewidth local tileW = self.tilewidth
local tileH = self.tileheight local tileH = self.tileheight
return return x * tileW, y * tileH
x * tileW,
y * tileH
elseif self.orientation == "isometric" then elseif self.orientation == "isometric" then
local mapH = self.height local mapH = self.height
local tileW = self.tilewidth local tileW = self.tilewidth
local tileH = self.tileheight local tileH = self.tileheight
local offsetX = mapH * tileW / 2 local offsetX = mapH * tileW / 2
return return (x - y) * tileW / 2 + offsetX, (x + y) * tileH / 2
(x - y) * tileW / 2 + offsetX, elseif self.orientation == "staggered" or self.orientation == "hexagonal" then
(x + y) * tileH / 2
elseif self.orientation == "staggered" or
self.orientation == "hexagonal" then
local tileW = self.tilewidth local tileW = self.tilewidth
local tileH = self.tileheight local tileH = self.tileheight
local sideLen = self.hexsidelength or 0 local sideLen = self.hexsidelength or 0
if self.staggeraxis == "x" then if self.staggeraxis == "x" then
return return x * tileW, ceil(y) * (tileH + sideLen) + (ceil(y) % 2 == 0 and tileH or 0)
x * tileW,
ceil(y) * (tileH + sideLen) + (ceil(y) % 2 == 0 and tileH or 0)
else else
return return ceil(x) * (tileW + sideLen) + (ceil(x) % 2 == 0 and tileW or 0), y * tileH
ceil(x) * (tileW + sideLen) + (ceil(x) % 2 == 0 and tileW or 0),
y * tileH
end end
end end
end end
@@ -1386,17 +1368,13 @@ function Map:convertPixelToTile(x, y)
if self.orientation == "orthogonal" then if self.orientation == "orthogonal" then
local tileW = self.tilewidth local tileW = self.tilewidth
local tileH = self.tileheight local tileH = self.tileheight
return return x / tileW, y / tileH
x / tileW,
y / tileH
elseif self.orientation == "isometric" then elseif self.orientation == "isometric" then
local mapH = self.height local mapH = self.height
local tileW = self.tilewidth local tileW = self.tilewidth
local tileH = self.tileheight local tileH = self.tileheight
local offsetX = mapH * tileW / 2 local offsetX = mapH * tileW / 2
return return y / tileH + (x - offsetX) / tileW, y / tileH - (x - offsetX) / tileW
y / tileH + (x - offsetX) / tileW,
y / tileH - (x - offsetX) / tileW
elseif self.orientation == "staggered" then elseif self.orientation == "staggered" then
local staggerX = self.staggeraxis == "x" local staggerX = self.staggeraxis == "x"
local even = self.staggerindex == "even" local even = self.staggerindex == "even"
@@ -1481,13 +1459,13 @@ function Map:convertPixelToTile(x, y)
local relativeX = x - referenceX * tileW local relativeX = x - referenceX * tileW
local relativeY = y - referenceY * tileH local relativeY = y - referenceY * tileH
if (halfH - relativeX * ratio > relativeY) then if halfH - relativeX * ratio > relativeY then
return topLeft(referenceX, referenceY) return topLeft(referenceX, referenceY)
elseif (-halfH + relativeX * ratio > relativeY) then elseif -halfH + relativeX * ratio > relativeY then
return topRight(referenceX, referenceY) return topRight(referenceX, referenceY)
elseif (halfH + relativeX * ratio < relativeY) then elseif halfH + relativeX * ratio < relativeY then
return bottomLeft(referenceX, referenceY) return bottomLeft(referenceX, referenceY)
elseif (halfH * 3 - relativeX * ratio < relativeY) then elseif halfH * 3 - relativeX * ratio < relativeY then
return bottomRight(referenceX, referenceY) return bottomRight(referenceX, referenceY)
end end
@@ -1550,7 +1528,7 @@ function Map:convertPixelToTile(x, y)
{ x = centerX, y = top }, { x = centerX, y = top },
{ x = centerX - colW, y = centerY }, { x = centerX - colW, y = centerY },
{ x = centerX + colW, y = centerY }, { x = centerX + colW, y = centerY },
{ x = centerX, y = centerY + rowH } { x = centerX, y = centerY + rowH },
} }
end end
@@ -1586,9 +1564,7 @@ function Map:convertPixelToTile(x, y)
local offsets = staggerX and offsetsStaggerX or offsetsStaggerY local offsets = staggerX and offsetsStaggerX or offsetsStaggerY
return return referenceX + offsets[nearest].x, referenceY + offsets[nearest].y
referenceX + offsets[nearest].x,
referenceY + offsets[nearest].y
end end
end end
+17 -17
View File
@@ -5,8 +5,8 @@
-- @license MIT/X11 -- @license MIT/X11
local love = _G.love local love = _G.love
local utils = require((...):gsub('plugins.box2d', 'utils')) local utils = require((...):gsub("plugins.box2d", "utils"))
local lg = require((...):gsub('plugins.box2d', 'graphics')) local lg = require((...):gsub("plugins.box2d", "graphics"))
return { return {
box2d_LICENSE = "MIT/X11", box2d_LICENSE = "MIT/X11",
@@ -40,13 +40,13 @@ return {
local currentBody = body local currentBody = body
--dynamic are objects/players etc. --dynamic are objects/players etc.
if userdata.properties.dynamic == true then 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. -- static means it shouldn't move. Things like walls/ground.
elseif userdata.properties.static == true then 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 -- kinematic means that the object is static in the game world but effects other bodies
elseif userdata.properties.kinematic == true then 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 end
local fixture = love.physics.newFixture(currentBody, shape) local fixture = love.physics.newFixture(currentBody, shape)
@@ -89,12 +89,12 @@ return {
y = (object.dy or object.y) + map.offsety, y = (object.dy or object.y) + map.offsety,
w = object.width, w = object.width,
h = object.height, 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 = { local userdata = {
object = o, object = o,
properties = object.properties properties = object.properties,
} }
o.r = object.rotation or 0 o.r = object.rotation or 0
@@ -133,10 +133,10 @@ return {
end end
o.polygon = { o.polygon = {
{ x=o.x+0, y=o.y+0 }, { 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 + 0 },
{ x=o.x+o.w, y=o.y+o.h }, { 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 for _, vertex in ipairs(o.polygon) do
@@ -202,7 +202,7 @@ return {
y = instance.y, y = instance.y,
width = map.tilewidth, width = map.tilewidth,
height = map.tileheight, height = map.tileheight,
properties = tile.properties properties = tile.properties,
} }
calculateObjectPosition(object, instance) calculateObjectPosition(object, instance)
@@ -227,7 +227,7 @@ return {
y = instance.y, y = instance.y,
width = tileset.tilewidth, width = tileset.tilewidth,
height = tileset.tileheight, height = tileset.tileheight,
properties = tile.properties properties = tile.properties,
} }
calculateObjectPosition(object, instance) calculateObjectPosition(object, instance)
@@ -245,7 +245,7 @@ return {
y = layer.y or 0, y = layer.y or 0,
width = layer.width, width = layer.width,
height = layer.height, height = layer.height,
properties = layer.properties properties = layer.properties,
} }
calculateObjectPosition(object) calculateObjectPosition(object)
@@ -295,7 +295,7 @@ return {
lg.translate(math.floor(tx or 0), math.floor(ty or 0)) lg.translate(math.floor(tx or 0), math.floor(ty or 0))
for _, obj in ipairs(collision) do for _, obj in ipairs(collision) do
local points = {obj.body:getWorldPoints(obj.shape:getPoints())} local points = { obj.body:getWorldPoints(obj.shape:getPoints()) }
local shape_type = obj.shape:getType() local shape_type = obj.shape:getType()
if shape_type == "edge" or shape_type == "chain" then if shape_type == "edge" or shape_type == "chain" then
@@ -303,12 +303,12 @@ return {
elseif shape_type == "polygon" then elseif shape_type == "polygon" then
love.graphics.polygon("line", points) love.graphics.polygon("line", points)
else else
error("sti box2d plugin does not support "..shape_type.." shapes") error("sti box2d plugin does not support " .. shape_type .. " shapes")
end end
end end
lg.pop() lg.pop()
end end,
} }
--- Custom Properties in Tiled are used to tell this plugin what to do. --- Custom Properties in Tiled are used to tell this plugin what to do.
+12 -19
View File
@@ -4,7 +4,7 @@
-- @copyright 2019 -- @copyright 2019
-- @license MIT/X11 -- @license MIT/X11
local lg = require((...):gsub('plugins.bump', 'graphics')) local lg = require((...):gsub("plugins.bump", "graphics"))
return { return {
bump_LICENSE = "MIT/X11", bump_LICENSE = "MIT/X11",
@@ -36,8 +36,7 @@ return {
width = object.width, width = object.width,
height = object.height, height = object.height,
layer = instance.layer, layer = instance.layer,
properties = object.properties properties = object.properties,
} }
world:add(t, t.x, t.y, t.width, t.height) world:add(t, t.x, t.y, t.width, t.height)
@@ -55,7 +54,7 @@ return {
height = map.tileheight, height = map.tileheight,
layer = instance.layer, layer = instance.layer,
type = tile.type, type = tile.type,
properties = tile.properties properties = tile.properties,
} }
world:add(t, t.x, t.y, t.width, t.height) world:add(t, t.x, t.y, t.width, t.height)
@@ -72,19 +71,18 @@ return {
if layer.type == "tilelayer" then if layer.type == "tilelayer" then
for y, tiles in ipairs(layer.data) do for y, tiles in ipairs(layer.data) do
for x, tile in pairs(tiles) do for x, tile in pairs(tiles) do
if tile.objectGroup then if tile.objectGroup then
for _, object in ipairs(tile.objectGroup.objects) do for _, object in ipairs(tile.objectGroup.objects) do
if object.properties.collidable == true then if object.properties.collidable == true then
local t = { local t = {
name = object.name, name = object.name,
type = object.type, type = object.type,
x = ((x-1) * map.tilewidth + tile.offset.x + map.offsetx) + object.x, x = ((x - 1) * map.tilewidth + tile.offset.x + map.offsetx) + object.x,
y = ((y-1) * map.tileheight + tile.offset.y + map.offsety) + object.y, y = ((y - 1) * map.tileheight + tile.offset.y + map.offsety) + object.y,
width = object.width, width = object.width,
height = object.height, height = object.height,
layer = layer, layer = layer,
properties = object.properties properties = object.properties,
} }
world:add(t, t.x, t.y, t.width, t.height) world:add(t, t.x, t.y, t.width, t.height)
@@ -93,15 +91,14 @@ return {
end end
end end
local t = { local t = {
x = (x-1) * map.tilewidth + tile.offset.x + map.offsetx, x = (x - 1) * map.tilewidth + tile.offset.x + map.offsetx,
y = (y-1) * map.tileheight + tile.offset.y + map.offsety, y = (y - 1) * map.tileheight + tile.offset.y + map.offsety,
width = tile.width, width = tile.width,
height = tile.height, height = tile.height,
layer = layer, layer = layer,
type = tile.type, type = tile.type,
properties = tile.properties properties = tile.properties,
} }
world:add(t, t.x, t.y, t.width, t.height) world:add(t, t.x, t.y, t.width, t.height)
@@ -128,7 +125,7 @@ return {
width = obj.width, width = obj.width,
height = obj.height, height = obj.height,
layer = layer, layer = layer,
properties = obj.properties properties = obj.properties,
} }
if obj.gid then if obj.gid then
@@ -157,11 +154,7 @@ return {
for i = #collidables, 1, -1 do for i = #collidables, 1, -1 do
local obj = collidables[i] local obj = collidables[i]
if obj.layer == layer if obj.layer == layer and (layer.properties.collidable == true or obj.properties.collidable == true) then
and (
layer.properties.collidable == true
or obj.properties.collidable == true
) then
map.bump_world:remove(obj) map.bump_world:remove(obj)
table.remove(collidables, i) table.remove(collidables, i)
end end
@@ -185,7 +178,7 @@ return {
end end
lg.pop() lg.pop()
end end,
} }
--- Custom Properties in Tiled are used to tell this plugin what to do. --- Custom Properties in Tiled are used to tell this plugin what to do.
+27 -23
View File
@@ -3,19 +3,21 @@ local utils = {}
-- https://github.com/stevedonovan/Penlight/blob/master/lua/pl/path.lua#L286 -- https://github.com/stevedonovan/Penlight/blob/master/lua/pl/path.lua#L286
function utils.format_path(path) function utils.format_path(path)
local np_gen1,np_gen2 = '[^SEP]+SEP%.%.SEP?','SEP+%.?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 np_pat1, np_pat2 = np_gen1:gsub("SEP", "/"), np_gen2:gsub("SEP", "/")
local k local k
repeat -- /./ -> / repeat -- /./ -> /
path,k = path:gsub(np_pat2,'/',1) path, k = path:gsub(np_pat2, "/", 1)
until k == 0 until k == 0
repeat -- A/../ -> (empty) repeat -- A/../ -> (empty)
path,k = path:gsub(np_pat1,'',1) path, k = path:gsub(np_pat1, "", 1)
until k == 0 until k == 0
if path == '' then path = '.' end if path == "" then
path = "."
end
return path return path
end end
@@ -25,8 +27,12 @@ function utils.compensate(tile, tileX, tileY, tileW, tileH)
local compx = 0 local compx = 0
local compy = 0 local compy = 0
if tile.sx < 0 then compx = tileW end if tile.sx < 0 then
if tile.sy < 0 then compy = tileH end compx = tileW
end
if tile.sy < 0 then
compy = tileH
end
if tile.r > 0 then if tile.r > 0 then
tileX = tileX + tileH - compy tileX = tileX + tileH - compy
@@ -56,8 +62,12 @@ function utils.get_tiles(imageW, tileW, margin, spacing)
while imageW >= tileW do while imageW >= tileW do
imageW = imageW - tileW imageW = imageW - tileW
if n ~= 0 then imageW = imageW - spacing end if n ~= 0 then
if imageW >= 0 then n = n + 1 end imageW = imageW - spacing
end
if imageW >= 0 then
n = n + 1
end
end end
return n return n
@@ -65,7 +75,7 @@ end
-- Decompress tile layer data -- Decompress tile layer data
function utils.get_decompressed_data(data) function utils.get_decompressed_data(data)
local ffi = require "ffi" local ffi = require("ffi")
local d = {} local d = {}
local decoded = ffi.cast("uint32_t*", data) local decoded = ffi.cast("uint32_t*", data)
@@ -95,7 +105,7 @@ function utils.convert_ellipse_to_polygon(x, y, w, h, max_segments)
segments = segments or 64 segments = segments or 64
local vertices = {} local vertices = {}
local v = { 1, 2, ceil(segments/4-1), ceil(segments/4) } local v = { 1, 2, ceil(segments / 4 - 1), ceil(segments / 4) }
local m local m
if love and love.physics then if love and love.physics then
@@ -117,7 +127,7 @@ function utils.convert_ellipse_to_polygon(x, y, w, h, max_segments)
-- Box2D threshold -- Box2D threshold
if dist1 < 0.0025 or dist2 < 0.0025 then if dist1 < 0.0025 or dist2 < 0.0025 then
return calc_segments(segments-2) return calc_segments(segments - 2)
end end
return segments return segments
@@ -148,9 +158,7 @@ function utils.rotate_vertex(map, vertex, x, y, cos, sin, oy)
vertex.x = vertex.x - x vertex.x = vertex.x - x
vertex.y = vertex.y - y vertex.y = vertex.y - y
return return x + cos * vertex.x - sin * vertex.y, y + sin * vertex.x + cos * vertex.y - (oy or 0)
x + cos * vertex.x - sin * vertex.y,
y + sin * vertex.x + cos * vertex.y - (oy or 0)
end end
--- Project isometric position to cartesian position --- Project isometric position to cartesian position
@@ -162,9 +170,7 @@ function utils.convert_isometric_to_screen(map, x, y)
local tileY = y / tileH local tileY = y / tileH
local offsetX = mapW * tileW / 2 local offsetX = mapW * tileW / 2
return return (tileX - tileY) * tileW / 2 + offsetX, (tileX + tileY) * tileH / 2
(tileX - tileY) * tileW / 2 + offsetX,
(tileX + tileY) * tileH / 2
end end
function utils.hex_to_color(hex) function utils.hex_to_color(hex)
@@ -175,16 +181,14 @@ function utils.hex_to_color(hex)
return { return {
r = tonumber(hex:sub(1, 2), 16) / 255, r = tonumber(hex:sub(1, 2), 16) / 255,
g = tonumber(hex:sub(3, 4), 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 end
function utils.pixel_function(_, _, r, g, b, a) function utils.pixel_function(_, _, r, g, b, a)
local mask = utils._TC local mask = utils._TC
if r == mask.r and if r == mask.r and g == mask.g and b == mask.b then
g == mask.g and
b == mask.b then
return r, g, b, 0 return r, g, b, 0
end end
@@ -205,7 +209,7 @@ end
function utils.deepCopy(t) function utils.deepCopy(t)
local copy = {} local copy = {}
for k,v in pairs(t) do for k, v in pairs(t) do
if type(v) == "table" then if type(v) == "table" then
v = utils.deepCopy(v) v = utils.deepCopy(v)
end end
+361 -154
View File
@@ -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, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
]]-- ]]
--
local path = ... .. '.' local path = ... .. "."
local wf = {} local wf = {}
wf.Math = require(path .. 'mlib.mlib') wf.Math = require(path .. "mlib.mlib")
World = {} World = {}
World.__index = World World.__index = World
@@ -32,14 +33,28 @@ World.__index = World
function wf.newWorld(xg, yg, sleep) function wf.newWorld(xg, yg, sleep)
local world = wf.World.new(wf, 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:collisionClear()
world:addCollisionClass('Default') world:addCollisionClass("Default")
-- Points all box2d_world functions to this wf.World object -- 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 -- 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 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, ...) world[k] = function(self, ...)
return v(self.box2d_world, ...) return v(self.box2d_world, ...)
end end
@@ -84,18 +99,20 @@ function World:draw(alpha)
for _, body in ipairs(bodies) do for _, body in ipairs(bodies) do
local fixtures = body:getFixtures() local fixtures = body:getFixtures()
for _, fixture in ipairs(fixtures) do for _, fixture in ipairs(fixtures) do
if fixture:getShape():type() == 'PolygonShape' then if fixture:getShape():type() == "PolygonShape" then
love.graphics.polygon('line', body:getWorldPoints(fixture:getShape():getPoints())) love.graphics.polygon("line", body:getWorldPoints(fixture:getShape():getPoints()))
elseif fixture:getShape():type() == 'EdgeShape' or fixture:getShape():type() == 'ChainShape' then elseif fixture:getShape():type() == "EdgeShape" or fixture:getShape():type() == "ChainShape" then
local points = {body:getWorldPoints(fixture:getShape():getPoints())} local points = { body:getWorldPoints(fixture:getShape():getPoints()) }
for i = 1, #points, 2 do 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 end
elseif fixture:getShape():type() == 'CircleShape' then end
elseif fixture:getShape():type() == "CircleShape" then
local body_x, body_y = body:getPosition() local body_x, body_y = body:getPosition()
local shape_x, shape_y = fixture:getShape():getPoint() local shape_x, shape_y = fixture:getShape():getPoint()
local r = fixture:getShape():getRadius() 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 end
end end
@@ -106,8 +123,12 @@ function World:draw(alpha)
local joints = self.box2d_world:getJoints() local joints = self.box2d_world:getJoints()
for _, joint in ipairs(joints) do for _, joint in ipairs(joints) do
local x1, y1, x2, y2 = joint:getAnchors() local x1, y1, x2, y2 = joint:getAnchors()
if x1 and y1 then love.graphics.circle('line', x1, y1, 4) end if x1 and y1 then
if x2 and y2 then love.graphics.circle('line', x2, y2, 4) end love.graphics.circle("line", x1, y1, 4)
end
if x2 and y2 then
love.graphics.circle("line", x2, y2, 4)
end
end end
love.graphics.setColor(255, 255, 255, alpha) love.graphics.setColor(255, 255, 255, alpha)
@@ -115,15 +136,17 @@ function World:draw(alpha)
love.graphics.setColor(64, 64, 222, alpha) love.graphics.setColor(64, 64, 222, alpha)
for _, query_draw in ipairs(self.query_debug_draw) do for _, query_draw in ipairs(self.query_debug_draw) do
query_draw.frames = query_draw.frames - 1 query_draw.frames = query_draw.frames - 1
if query_draw.type == 'circle' then if query_draw.type == "circle" then
love.graphics.circle('line', query_draw.x, query_draw.y, query_draw.r) love.graphics.circle("line", query_draw.x, query_draw.y, query_draw.r)
elseif query_draw.type == 'rectangle' then elseif query_draw.type == "rectangle" then
love.graphics.rectangle('line', query_draw.x, query_draw.y, query_draw.w, query_draw.h) love.graphics.rectangle("line", query_draw.x, query_draw.y, query_draw.w, query_draw.h)
elseif query_draw.type == 'line' then elseif query_draw.type == "line" then
love.graphics.line(query_draw.x1, query_draw.y1, query_draw.x2, query_draw.y2) 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) 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
end end
for i = #self.query_debug_draw, 1, -1 do for i = #self.query_debug_draw, 1, -1 do
@@ -143,7 +166,9 @@ function World:setExplicitCollisionEvents(value)
end end
function World:addCollisionClass(collision_class_name, collision_class) 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 if self.explicit_collision_events then
self.collision_classes[collision_class_name] = collision_class or {} self.collision_classes[collision_class_name] = collision_class or {}
@@ -177,10 +202,18 @@ function World:collisionClassesSet()
local collision_table = self:getCollisionCallbacksTable() local collision_table = self:getCollisionCallbacksTable()
for collision_class_name, collision_list in pairs(collision_table) do for collision_class_name, collision_list in pairs(collision_table) do
for _, collision_info in ipairs(collision_list) 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 == "enter" then
if collision_info.type == 'exit' then self:addCollisionExit(collision_class_name, collision_info.other) end self:addCollisionEnter(collision_class_name, collision_info.other)
if collision_info.type == 'pre' then self:addCollisionPre(collision_class_name, collision_info.other) end end
if collision_info.type == 'post' then self:addCollisionPost(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
end end
@@ -213,26 +246,34 @@ end
function World:addCollisionEnter(type1, type2) function World:addCollisionEnter(type1, type2)
if not self:isCollisionBetweenSensors(type1, type2) then if not self:isCollisionBetweenSensors(type1, type2) then
table.insert(self.collisions.on_enter.non_sensor, {type1 = type1, type2 = type2}) 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 end
function World:addCollisionExit(type1, type2) function World:addCollisionExit(type1, type2)
if not self:isCollisionBetweenSensors(type1, type2) then if not self:isCollisionBetweenSensors(type1, type2) then
table.insert(self.collisions.on_exit.non_sensor, {type1 = type1, type2 = type2}) 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 end
function World:addCollisionPre(type1, type2) function World:addCollisionPre(type1, type2)
if not self:isCollisionBetweenSensors(type1, type2) then if not self:isCollisionBetweenSensors(type1, type2) then
table.insert(self.collisions.pre.non_sensor, {type1 = type1, type2 = type2}) 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 end
function World:addCollisionPost(type1, type2) function World:addCollisionPost(type1, type2)
if not self:isCollisionBetweenSensors(type1, type2) then if not self:isCollisionBetweenSensors(type1, type2) then
table.insert(self.collisions.post.non_sensor, {type1 = type1, type2 = type2}) 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 end
function World:doesType1IgnoreType2(type1, type2) function World:doesType1IgnoreType2(type1, type2)
@@ -246,31 +287,46 @@ function World:doesType1IgnoreType2(type1, type2)
end end
local ignored_types = {} local ignored_types = {}
for _, collision_class_type in ipairs(collision_ignores[type1]) do 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 for _, collision_class_name in ipairs(all) do
table.insert(ignored_types, collision_class_name) table.insert(ignored_types, collision_class_name)
end end
else table.insert(ignored_types, collision_class_type) end else
table.insert(ignored_types, collision_class_type)
end
end end
for key, _ in pairs(collision_ignores[type1]) do 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 _, except_type in ipairs(collision_ignores[type1].except) do
for i = #ignored_types, 1, -1 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
end end
end end
for _, ignored_type in ipairs(ignored_types) do 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
end end
function World:isCollisionBetweenSensors(type1, type2) 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] then
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 self.is_sensor_memo[type1] = {}
if self.is_sensor_memo[type1][type2] then return true end
else return false 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 end
-- https://love2d.org/forums/viewtopic.php?f=4&t=75441 -- https://love2d.org/forums/viewtopic.php?f=4&t=75441
@@ -289,18 +345,18 @@ function World:generateCategoriesMasks()
end end
for object_type, ignore_list in pairs(collision_ignores) do for object_type, ignore_list in pairs(collision_ignores) do
for key, ignored_type in pairs(ignore_list) 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 for _, all_object_type in ipairs(all) do
table.insert(incoming[all_object_type], object_type) table.insert(incoming[all_object_type], object_type)
table.insert(expanded[object_type], all_object_type) table.insert(expanded[object_type], all_object_type)
end end
elseif type(ignored_type) == 'string' then elseif type(ignored_type) == "string" then
if ignored_type ~= 'All' then if ignored_type ~= "All" then
table.insert(incoming[ignored_type], object_type) table.insert(incoming[ignored_type], object_type)
table.insert(expanded[object_type], ignored_type) table.insert(expanded[object_type], ignored_type)
end end
end end
if key == 'except' then if key == "except" then
for _, except_ignored_type in ipairs(ignored_type) do for _, except_ignored_type in ipairs(ignored_type) do
for i, v in ipairs(incoming[except_ignored_type]) do for i, v in ipairs(incoming[except_ignored_type]) do
if v == object_type then if v == object_type then
@@ -322,7 +378,9 @@ function World:generateCategoriesMasks()
end end
local edge_groups = {} local edge_groups = {}
for k, v in pairs(incoming) do 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 end
local i = 0 local i = 0
for k, v in pairs(incoming) do for k, v in pairs(incoming) do
@@ -330,7 +388,10 @@ function World:generateCategoriesMasks()
for _, c in ipairs(v) do for _, c in ipairs(v) do
str = str .. c str = str .. c
end 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) table.insert(edge_groups[str], k)
end end
local categories = {} local categories = {}
@@ -343,12 +404,12 @@ function World:generateCategoriesMasks()
end end
end end
for k, v in pairs(expanded) do for k, v in pairs(expanded) do
local category = {categories[k]} local category = { categories[k] }
local current_masks = {} local current_masks = {}
for _, c in ipairs(v) do for _, c in ipairs(v) do
table.insert(current_masks, categories[c]) table.insert(current_masks, categories[c])
end end
self.masks[k] = {categories = category, masks = current_masks} self.masks[k] = { categories = category, masks = current_masks }
end end
end end
@@ -356,24 +417,39 @@ function World:getCollisionCallbacksTable()
local collision_table = {} local collision_table = {}
for collision_class_name, collision_class in pairs(self.collision_classes) do for collision_class_name, collision_class in pairs(self.collision_classes) do
collision_table[collision_class_name] = {} 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.enter or {}) do
for _, v in ipairs(collision_class.exit or {}) do table.insert(collision_table[collision_class_name], {type = 'exit', other = v}) end table.insert(collision_table[collision_class_name], { type = "enter", other = v })
for _, v in ipairs(collision_class.pre or {}) do table.insert(collision_table[collision_class_name], {type = 'pre', other = v}) end 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.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 end
return collision_table return collision_table
end end
local function collEnsure(collision_class_name1, a, collision_class_name2, b) 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 if a.collision_class == collision_class_name2 and b.collision_class == collision_class_name1 then
else return a, b end return b, a
else
return a, b
end
end end
local function collIf(collision_class_name1, collision_class_name2, a, b) 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 if
(a.collision_class == collision_class_name2 and b.collision_class == collision_class_name1) then (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 return true
else return false end else
return false
end
end end
function World.collisionOnEnter(fixture_a, fixture_b, contact) 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 for _, collision in ipairs(a.world.collisions.on_enter.sensor) do
if collIf(collision.type1, collision.type2, a, b) then if collIf(collision.type1, collision.type2, a, b) then
a, b = collEnsure(collision.type1, a, collision.type2, b) 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 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
end end
end end
elseif not (fixture_a:isSensor() or fixture_b:isSensor()) then elseif not (fixture_a:isSensor() or fixture_b:isSensor()) then
if a and b then if a and b then
for _, collision in ipairs(a.world.collisions.on_enter.non_sensor) do for _, collision in ipairs(a.world.collisions.on_enter.non_sensor) do
if collIf(collision.type1, collision.type2, a, b) then if collIf(collision.type1, collision.type2, a, b) then
a, b = collEnsure(collision.type1, a, collision.type2, b) 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 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
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 for _, collision in ipairs(a.world.collisions.on_exit.sensor) do
if collIf(collision.type1, collision.type2, a, b) then if collIf(collision.type1, collision.type2, a, b) then
a, b = collEnsure(collision.type1, a, collision.type2, b) 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 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
end end
end end
elseif not (fixture_a:isSensor() or fixture_b:isSensor()) then elseif not (fixture_a:isSensor() or fixture_b:isSensor()) then
if a and b then if a and b then
for _, collision in ipairs(a.world.collisions.on_exit.non_sensor) do for _, collision in ipairs(a.world.collisions.on_exit.non_sensor) do
if collIf(collision.type1, collision.type2, a, b) then if collIf(collision.type1, collision.type2, a, b) then
a, b = collEnsure(collision.type1, a, collision.type2, b) 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 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
end end
@@ -453,7 +551,6 @@ function World.collisionPre(fixture_a, fixture_b, contact)
end end
end end
end end
elseif not (fixture_a:isSensor() or fixture_b:isSensor()) then elseif not (fixture_a:isSensor() or fixture_b:isSensor()) then
if a and b then if a and b then
for _, collision in ipairs(a.world.collisions.pre.non_sensor) do 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 end
end end
elseif not (fixture_a:isSensor() or fixture_b:isSensor()) then elseif not (fixture_a:isSensor() or fixture_b:isSensor()) then
if a and b then if a and b then
for _, collision in ipairs(a.world.collisions.post.non_sensor) do 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 end
function World:newCircleCollider(x, y, r, settings) 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 end
function World:newRectangleCollider(x, y, w, h, settings) 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 end
function World:newBSGRectangleCollider(x, y, w, h, corner_cut_size, settings) 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 end
function World:newPolygonCollider(vertices, settings) function World:newPolygonCollider(vertices, settings)
return self.wf.Collider.new(self, 'Polygon', vertices, settings) return self.wf.Collider.new(self, "Polygon", vertices, settings)
end end
function World:newLineCollider(x1, y1, x2, y2, settings) 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 end
function World:newChainCollider(vertices, loop, settings) 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 end
-- Internal AABB box2d query used before going for more specific and precise computations. -- Internal AABB box2d query used before going for more specific and precise computations.
function World:_queryBoundingBox(x1, y1, x2, y2) function World:_queryBoundingBox(x1, y1, x2, y2)
local colliders = {} local colliders = {}
local callback = function(fixture) 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 return true
end end
self.box2d_world:queryBoundingBox(x1, y1, x2, y2, callback) self.box2d_world:queryBoundingBox(x1, y1, x2, y2, callback)
@@ -536,7 +634,7 @@ function World:_queryBoundingBox(x1, y1, x2, y2)
end end
function World:collisionClassInCollisionClassesList(collision_class, collision_classes) function World:collisionClassInCollisionClassesList(collision_class, collision_classes)
if collision_classes[1] == 'All' then if collision_classes[1] == "All" then
local all_collision_classes = {} local all_collision_classes = {}
for class, _ in pairs(self.collision_classes) do for class, _ in pairs(self.collision_classes) do
table.insert(all_collision_classes, class) table.insert(all_collision_classes, class)
@@ -552,25 +650,43 @@ function World:collisionClassInCollisionClassesList(collision_class, collision_c
end end
end end
for _, class in ipairs(all_collision_classes) do 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 end
else else
for _, class in ipairs(collision_classes) do 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 end
end end
function World:queryCircleArea(x, y, radius, collision_class_names) function World:queryCircleArea(x, y, radius, collision_class_names)
if not collision_class_names then collision_class_names = {'All'} end if not collision_class_names then
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 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 colliders = self:_queryBoundingBox(x - radius, y - radius, x + radius, y + radius)
local outs = {} local outs = {}
for _, collider in ipairs(colliders) do for _, collider in ipairs(colliders) do
if self:collisionClassInCollisionClassesList(collider.collision_class, collision_class_names) then if self:collisionClassInCollisionClassesList(collider.collision_class, collision_class_names) then
for _, fixture in ipairs(collider.body:getFixtures()) do 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) table.insert(outs, collider)
break break
end end
@@ -581,15 +697,27 @@ function World:queryCircleArea(x, y, radius, collision_class_names)
end end
function World:queryRectangleArea(x, y, w, h, collision_class_names) function World:queryRectangleArea(x, y, w, h, collision_class_names)
if not collision_class_names then collision_class_names = {'All'} end if not collision_class_names then
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 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 colliders = self:_queryBoundingBox(x, y, x + w, y + h)
local outs = {} local outs = {}
for _, collider in ipairs(colliders) do for _, collider in ipairs(colliders) do
if self:collisionClassInCollisionClassesList(collider.collision_class, collision_class_names) then if self:collisionClassInCollisionClassesList(collider.collision_class, collision_class_names) then
for _, fixture in ipairs(collider.body:getFixtures()) do 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) table.insert(outs, collider)
break break
end end
@@ -600,21 +728,35 @@ function World:queryRectangleArea(x, y, w, h, collision_class_names)
end end
function World:queryPolygonArea(vertices, collision_class_names) function World:queryPolygonArea(vertices, collision_class_names)
if not collision_class_names then collision_class_names = {'All'} end if not collision_class_names then
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 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 cx, cy = self.wf.Math.polygon.getCentroid(vertices)
local d_max = 0 local d_max = 0
for i = 1, #vertices, 2 do for i = 1, #vertices, 2 do
local d = self.wf.Math.line.getLength(cx, cy, vertices[i], vertices[i+1]) 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) end
local colliders = self:_queryBoundingBox(cx - d_max, cy - d_max, cx + d_max, cy + d_max)
local outs = {} local outs = {}
for _, collider in ipairs(colliders) do for _, collider in ipairs(colliders) do
if self:collisionClassInCollisionClassesList(collider.collision_class, collision_class_names) then if self:collisionClassInCollisionClassesList(collider.collision_class, collision_class_names) then
for _, fixture in ipairs(collider.body:getFixtures()) do 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) table.insert(outs, collider)
break break
end end
@@ -625,14 +767,21 @@ function World:queryPolygonArea(vertices, collision_class_names)
end end
function World:queryLine(x1, y1, x2, y2, collision_class_names) 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 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 end
local colliders = {} local colliders = {}
local callback = function(fixture, ...) 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 return 1
end end
self.box2d_world:rayCast(x1, y1, x2, y2, callback) self.box2d_world:rayCast(x1, y1, x2, y2, callback)
@@ -647,10 +796,14 @@ function World:queryLine(x1, y1, x2, y2, collision_class_names)
end end
function World:addJoint(joint_type, ...) function World:addJoint(joint_type, ...)
local args = {...} local args = { ... }
if args[1].body then args[1] = args[1].body end if args[1].body then
if type(args[2]) == "table" and args[2].body then args[2] = args[2].body end args[1] = args[1].body
local joint = love.physics['new' .. joint_type](unpack(args)) 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 return joint
end end
@@ -665,13 +818,13 @@ function World:destroy()
collider:destroy() collider:destroy()
end end
local joints = self.box2d_world:getJoints() 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:destroy()
self.box2d_world = nil self.box2d_world = nil
end end
local Collider = {} local Collider = {}
Collider.__index = Collider Collider.__index = Collider
@@ -702,42 +855,60 @@ function Collider.new(world, collider_type, ...)
self.exit_collision_data = {} self.exit_collision_data = {}
self.stay_collision_data = {} self.stay_collision_data = {}
local args = {...} local args = { ... }
local shape, fixture local shape, fixture
if self.type == 'Circle' then if self.type == "Circle" then
self.collision_class = (args[4] and args[4].collision_class) or 'Default' 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') 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]) shape = love.physics.newCircleShape(args[3])
elseif self.type == "Rectangle" then
elseif self.type == 'Rectangle' then self.collision_class = (args[5] and args[5].collision_class) or "Default"
self.collision_class = (args[5] and args[5].collision_class) or 'Default' self.body = love.physics.newBody(
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') 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]) shape = love.physics.newRectangleShape(args[3], args[4])
elseif self.type == "BSGRectangle" then
elseif self.type == 'BSGRectangle' then self.collision_class = (args[6] and args[6].collision_class) or "Default"
self.collision_class = (args[6] and args[6].collision_class) or 'Default' self.body = love.physics.newBody(
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') 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] local w, h, s = args[3], args[4], args[5]
shape = love.physics.newPolygonShape({ shape = love.physics.newPolygonShape({
-w/2, -h/2 + s, -w/2 + s, -h/2, -w / 2,
w/2 - s, -h/2, w/2, -h/2 + s, -h / 2 + s,
w/2, h/2 - s, w/2 - s, h/2, -w / 2 + s,
-w/2 + s, h/2, -w/2, h/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
elseif self.type == 'Polygon' then self.collision_class = (args[2] and args[2].collision_class) or "Default"
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")
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])) shape = love.physics.newPolygonShape(unpack(args[1]))
elseif self.type == "Line" then
elseif self.type == 'Line' then self.collision_class = (args[5] and args[5].collision_class) or "Default"
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")
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]) shape = love.physics.newEdgeShape(args[1], args[2], args[3], args[4])
elseif self.type == "Chain" then
elseif self.type == 'Chain' then self.collision_class = (args[3] and args[3].collision_class) or "Default"
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")
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])) shape = love.physics.newChainShape(args[1], unpack(args[2]))
end end
@@ -752,9 +923,9 @@ function Collider.new(world, collider_type, ...)
sensor:setSensor(true) sensor:setSensor(true)
sensor:setUserData(self) sensor:setUserData(self)
self.shapes['main'] = shape self.shapes["main"] = shape
self.fixtures['main'] = fixture self.fixtures["main"] = fixture
self.sensors['main'] = sensor self.sensors["main"] = sensor
self.shape = shape self.shape = shape
self.fixture = fixture 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 -- 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 -- 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 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, ...) self[k] = function(self, ...)
return v(self.body, ...) return v(self.body, ...)
end end
end end
end end
for k, v in pairs(self.fixture.__index) do 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, ...) self[k] = function(self, ...)
return v(self.fixture, ...) return v(self.fixture, ...)
end end
end end
end end
for k, v in pairs(self.shape.__index) do 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, ...) self[k] = function(self, ...)
return v(self.shape, ...) return v(self.shape, ...)
end end
@@ -796,7 +991,9 @@ function Collider:collisionEventsClear()
end end
function Collider:setCollisionClass(collision_class_name) 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 self.collision_class = collision_class_name
for _, fixture in pairs(self.fixtures) do for _, fixture in pairs(self.fixtures) do
if self.world.masks[collision_class_name] then if self.world.masks[collision_class_name] then
@@ -810,10 +1007,15 @@ function Collider:enter(other_collision_class_name)
local events = self.collision_events[other_collision_class_name] local events = self.collision_events[other_collision_class_name]
if events and #events >= 1 then if events and #events >= 1 then
for _, e in ipairs(events) do for _, e in ipairs(events) do
if e.collision_type == 'enter' then if e.collision_type == "enter" then
if not self.collision_stay[other_collision_class_name] then self.collision_stay[other_collision_class_name] = {} end if not self.collision_stay[other_collision_class_name] then
table.insert(self.collision_stay[other_collision_class_name], {collider = e.collider_2, contact = e.contact}) self.collision_stay[other_collision_class_name] = {}
self.enter_collision_data[other_collision_class_name] = {collider = e.collider_2, contact = e.contact} 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 return true
end end
end end
@@ -828,14 +1030,16 @@ function Collider:exit(other_collision_class_name)
local events = self.collision_events[other_collision_class_name] local events = self.collision_events[other_collision_class_name]
if events and #events >= 1 then if events and #events >= 1 then
for _, e in ipairs(events) do 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 if self.collision_stay[other_collision_class_name] then
for i = #self.collision_stay[other_collision_class_name], 1, -1 do for i = #self.collision_stay[other_collision_class_name], 1, -1 do
local collision_stay = self.collision_stay[other_collision_class_name][i] 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 end
self.exit_collision_data[other_collision_class_name] = {collider = e.collider_2, contact = e.contact} end
self.exit_collision_data[other_collision_class_name] = { collider = e.collider_2, contact = e.contact }
return true return true
end end
end end
@@ -875,9 +1079,11 @@ function Collider:getObject()
end end
function Collider:addShape(shape_name, shape_type, ...) 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
local args = {...} error("Shape/fixture " .. shape_name .. " already exists.")
local shape = love.physics['new' .. shape_type](unpack(args)) end
local args = { ... }
local shape = love.physics["new" .. shape_type](unpack(args))
local fixture = love.physics.newFixture(self.body, shape) local fixture = love.physics.newFixture(self.body, shape)
if self.world.masks[self.collision_class] then if self.world.masks[self.collision_class] then
fixture:setCategory(unpack(self.world.masks[self.collision_class].categories)) fixture:setCategory(unpack(self.world.masks[self.collision_class].categories))
@@ -894,7 +1100,9 @@ function Collider:addShape(shape_name, shape_type, ...)
end end
function Collider:removeShape(shape_name) 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.shapes[shape_name] = nil
self.fixtures[shape_name]:setUserData(nil) self.fixtures[shape_name]:setUserData(nil)
self.fixtures[shape_name]:destroy() self.fixtures[shape_name]:destroy()
@@ -926,4 +1134,3 @@ wf.World = World
wf.Collider = Collider wf.Collider = Collider
return wf return wf
File diff suppressed because it is too large Load Diff
+115 -82
View File
@@ -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() 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. -- 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 local dt = 0
@@ -13,111 +72,83 @@ function love.run()
-- Process events. -- Process events.
if love.event then if love.event then
love.event.pump() love.event.pump()
for name, a,b,c,d,e,f in love.event.poll() do for name, a, b, c, d, e, f in love.event.poll() do
if name == "quit" then if name == "quit" then
if not love.quit or not love.quit() then if not love.quit or not love.quit() then
return a or 0 return a or 0
end end
end end
love.handlers[name](a,b,c,d,e,f) love.handlers[name](a, b, c, d, e, f)
end end
end end
-- Update dt, as we'll be passing it to update -- 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 -- 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 if love.graphics and love.graphics.isActive() then
love.graphics.origin() love.graphics.origin()
love.graphics.clear(love.graphics.getBackgroundColor()) love.graphics.clear(love.graphics.getBackgroundColor())
if love.draw then love.draw() end if love.draw then
love.draw()
end
love.graphics.present() love.graphics.present()
end end
if love.timer then love.timer.sleep(0.001) end if love.timer then
love.timer.sleep(0.001)
end
end end
end end
function love.resized()
--TOD0: fix scaling!
end
function love.load() function love.load()
Object = require("libs/classic") -- Set a random seed
require("player") love.math.setRandomSeed(love.timer.getTime())
require("bullet") -- 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") --Game values (reset after each load)
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
HEALTH = 3 HEALTH = 3
DELAY = 0.5 P1_DELAY = 0.5
MAX = 6 --MAX number of bullets| P2_DELAY = 0.5
--Bullet lists --Bullet lists
Bullets1 = {} Bullets1 = {}
Bullets2 = {} Bullets2 = {}
--PowerUp stuff
PowerUps = {}
PUTimeToPlace = _G.PU_TIMER
DebugFlag = false DebugFlag = false
EnableKeyPress1 = true EnableKeyPress1 = true
KeyPressTime1 = 0 KeyPressTime1 = 0
KeyDelay1 = DELAY KeyDelay1 = P1_DELAY
EnableKeyPress2 = true EnableKeyPress2 = true
KeyPressTime2 = 0 KeyPressTime2 = 0
KeyDelay2 = DELAY KeyDelay2 = P2_DELAY
local playerSpeed = 12000 --TODO: two player speeds for upgrades
UserPlayer1 = Player(1, 1000, 100, HEALTH, "assets/player1.png", playerSpeed) UserPlayer1 = Player(1, 1000, 100, HEALTH, "assets/player1.png", 12000)
UserPlayer2 = Player(2, 200, 300, HEALTH, "assets/player2.png", playerSpeed) UserPlayer2 = Player(2, 200, 300, HEALTH, "assets/player2.png", 12000)
-- 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,
}
--STI Map loading
LoadMap(_G.CUR_LEVEL)
end end
function love.keypressed(key) function love.keypressed(key)
@@ -130,14 +161,16 @@ function love.keypressed(key)
if _G.GAMESTATE == "GAME" then if _G.GAMESTATE == "GAME" then
GameKeyPressed(key) GameKeyPressed(key)
end end
if _G.GAMESTATE == "WIN" then
WinKeyPressed(key)
end
end end
function love.update(dt) function love.update(dt)
if _G.GAMESTATE == "MENU" then if _G.GAMESTATE == "MENU" then
UpdateMenu(dt) UpdateMenu(dt)
-- Handle music -- Handle music
if not musicMenu:isPlaying() then if not musicMenu:isPlaying() and not _G.MUTED then
musicMenu:setVolume(0.5) musicMenu:setVolume(0.5)
love.audio.play(musicMenu) love.audio.play(musicMenu)
end end
@@ -145,9 +178,9 @@ function love.update(dt)
if _G.GAMESTATE == "PAUSE" then if _G.GAMESTATE == "PAUSE" then
UpdatePause(dt) UpdatePause(dt)
-- Handle music -- Handle music
if not musicPause:isPlaying() then if not musicPause:isPlaying() and not _G.MUTED then
--musicBattle:setVolume(0) musicBattle:setVolume(0)
--musicPause:setVolume(0.5) musicPause:setVolume(0.5)
love.audio.play(musicBattle) love.audio.play(musicBattle)
love.audio.play(musicPause) love.audio.play(musicPause)
end end
@@ -155,13 +188,16 @@ function love.update(dt)
if _G.GAMESTATE == "GAME" then if _G.GAMESTATE == "GAME" then
UpdateGame(dt) UpdateGame(dt)
-- Handle music -- Handle music
if not musicBattle:isPlaying() then if not musicBattle:isPlaying() and not _G.MUTED then
--musicBattle:setVolume(0.5) musicBattle:setVolume(0.5)
--musicPause:setVolume(0) musicPause:setVolume(0)
love.audio.play(musicBattle) love.audio.play(musicBattle)
love.audio.play(musicPause) love.audio.play(musicPause)
end end
end end
if _G.GAMESTATE == "WIN" then
UpdateWin(dt)
end
end end
function love.draw() function love.draw()
@@ -173,11 +209,8 @@ function love.draw()
end end
if _G.GAMESTATE == "GAME" then if _G.GAMESTATE == "GAME" then
DrawGame() DrawGame()
if DebugFlag then
love.graphics.setFont(DebugFont)
love.graphics.print("Debug Mode", 1200, 850)
love.graphics.print("" .. GAMESTATE, 200, 200)
end end
if _G.GAMESTATE == "WIN" then
DrawWin()
end end
end end
+290
View File
@@ -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 = {}
}
}
}
}
}
+61
View File
@@ -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
View File
@@ -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 = {}
}
}
}
}
}
+67
View File
@@ -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
View File
@@ -1,4 +1,4 @@
function loadMap(lvl) function LoadMap(lvl)
local mapfilelocation = "maps/" local mapfilelocation = "maps/"
local extention = ".lua" local extention = ".lua"
local mapname = mapfilelocation .. "map" .. lvl .. extention local mapname = mapfilelocation .. "map" .. lvl .. extention
@@ -8,6 +8,8 @@ function loadMap(lvl)
if GameMap.layers["Walls"] then if GameMap.layers["Walls"] then
for _, obj in ipairs(GameMap.layers["Walls"].objects) do 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, 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") wall:setType("static")
table.insert(Walls, wall) table.insert(Walls, wall)
Walls[#Walls]:setCollisionClass("Wall") Walls[#Walls]:setCollisionClass("Wall")
+76 -60
View File
@@ -1,21 +1,28 @@
Player = Object:extend() Player = Object:extend()
cos = math.cos local cos = math.cos
sin = math.sin --optimisation local sin = math.sin --optimisation
local max = math.max --optimisation
-- Constructor for the Player class -- 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.p = p
self.image = love.graphics.newImage(image) self.image = love.graphics.newImage(image)
self.x = x self.x = x
self.y = y self.y = y
self.health = health self.health = health
self.speed = speed self.speed = speed
self.max = max self.bulletSpeed = _G.DEFAULT_B_SPEED
self.width = self.image:getWidth() self.width = self.image:getWidth()
self.height = self.image:getHeight() self.height = self.image:getHeight()
--Power up flag
self.PUName = ""
self.PUTime = 0
self.PUOn = false
-- Collision Stuff -- 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) self.collider:setFixedRotation(true)
if self.p == 1 then if self.p == 1 then
@@ -26,7 +33,9 @@ function Player:new(p, x, y, health, image, speed, max)
-- Rotation Stuff -- Rotation Stuff
self.rotation = math.rad(90) 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.scaleX = 1
self.scaleY = 1 self.scaleY = 1
self.originX = self.width / 2 self.originX = self.width / 2
@@ -35,92 +44,99 @@ function Player:new(p, x, y, health, image, speed, max)
--Velocity --Velocity
self.vx = 0 self.vx = 0
self.vy = 0 self.vy = 0
-- Bullets shot
self.shot = 0
end end
-- Method to handle shooting -- Method to handle shooting
function Player:shoot(bulletSpeed) function Player:shoot(bulletSpeed)
if self.shot <= 10 then
self.shot = self.shot + 1
local offsetX = cos(self.rotation) * self.width * 1.5 local offsetX = cos(self.rotation) * self.width * 1.5
local offsetY = sin(self.rotation) * self.height * 1.5 local offsetY = sin(self.rotation) * self.height * 1.5
local bulletX = self.x + offsetX local bulletX = self.x + offsetX
local bulletY = self.y + offsetY local bulletY = self.y + offsetY
local newBullet = Bullet(bulletX, bulletY, self.p, bulletSpeed, self.rotation) local newBullet = Bullet(bulletX, bulletY, self.p, bulletSpeed, self.rotation)
return newBullet return newBullet
else
return nil
end
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 end
-- Update method for the Player class -- Update method for the Player class
function Player:update(dt) function Player:update(dt)
if _G.GAMESTATE == "GAME" then if self.PUOn then
self.vx = 0 self.PUTime = self.PUTime - dt
self.vy = 0
local bulletSpeed = 20000
if self.p == 1 then if max(0, self.PUTime) <= 0 then
-- Handle player 1 controls -- Reset the player
if love.keyboard.isDown("w") then self.PUName = "none"
self.vx = cos(self.rotation) * (self.speed * dt) self.PUTime = 0
self.vy = sin(self.rotation) * (self.speed * dt) self.PUOn = false
elseif love.keyboard.isDown("s") then self.speed = _G.DEFAULT_P_SPEED
self.vx = cos(self.rotation) * (self.speed * dt) * -1
self.vy = sin(self.rotation) * (self.speed * dt) * -1
elseif love.keyboard.isDown("a") then
self.rotation = self.rotation - (self.rotSpeed * dt)
elseif love.keyboard.isDown("d") then
self.rotation = self.rotation + (self.rotSpeed * dt)
end 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 end
if EnableKeyPress1 == true and self.p == 1 then
if EnableKeyPress1 == true and GAMESTATE == "GAME" then
if love.keyboard.isDown("space") then if love.keyboard.isDown("space") then
local newBullet = self:shoot(bulletSpeed) local newBullet = self:shoot(self.bulletSpeed)
table.insert(Bullets1, newBullet) table.insert(Bullets1, newBullet)
KeyPressTime1 = KeyDelay1 KeyPressTime1 = KeyDelay1
EnableKeyPress1 = false EnableKeyPress1 = false
end end
end end
self.x = self.collider:getX()
self.y = self.collider:getY()
elseif self.p == 2 then if EnableKeyPress2 == true and 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 love.keyboard.isDown("return") then if love.keyboard.isDown("return") then
local newBullet = self:shoot(bulletSpeed) local newBullet = self:shoot(self.bulletSpeed)
table.insert(Bullets2, newBullet) table.insert(Bullets2, newBullet)
KeyPressTime2 = KeyDelay2 KeyPressTime2 = KeyDelay2
EnableKeyPress2 = false EnableKeyPress2 = false
end end
end end
end
self.x = self.collider:getX()
self.y = self.collider:getY()
end
end end
function Player:draw() function Player:draw()
+70
View File
@@ -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
View File
@@ -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
+6
View File
@@ -0,0 +1,6 @@
function truncate(number, decdigits)
number = number * (10 ^ decdigits)
number = math.floor(number)
number = number / (10 ^ decdigits)
return number
end