Simon Kellet 4 months ago
parent 1286d93daa
commit a3f90645fd
  1. 17
      DrawGame.lua
  2. 35
      UpdateGame.lua
  3. BIN
      assets/Daydream.ttf
  4. BIN
      assets/tileset.png
  5. 45
      bullet.lua
  6. 2
      conf.lua
  7. 159
      libs/sti/atlas.lua
  8. 132
      libs/sti/graphics.lua
  9. 1748
      libs/sti/init.lua
  10. 323
      libs/sti/plugins/box2d.lua
  11. 193
      libs/sti/plugins/bump.lua
  12. 217
      libs/sti/utils.lua
  13. 39
      main.lua
  14. 1023
      maps/map.lua
  15. 58
      maps/map.tmx
  16. 75
      player.lua

@ -1,10 +1,20 @@
function drawFPS()
love.graphics.setColor(1, 1, 1) -- RGB values for white are (1, 1, 1)
--love.graphics.setFont(DebugFont)
love.graphics.print("FPS: " .. love.timer.getFPS(), 1550, 10)
love.graphics.setFont(DebugFont)
love.graphics.print("FPS: " .. love.timer.getFPS(), 1520, 10)
end
function drawHealth()
love.graphics.setFont(GameFont)
local height = love.graphics.getHeight()
local width = love.graphics.getWidth()
love.graphics.print("" .. UserPlayer1.health, 5, 5)
love.graphics.print("" .. UserPlayer2.health, width - 70, height - 95)
end
function DrawGame()
--STI
GameMap:draw()
-- WindField
if DebugFlag then
World:draw()
@ -19,4 +29,7 @@ function DrawGame()
end
UserPlayer1:draw()
UserPlayer2:draw()
--Draw Health
drawHealth()
end

@ -1,5 +1,9 @@
function UpdateGame(dt)
--WindField
World:update(dt)
local max = math.max
KeyPressTime1 = max(0, KeyPressTime1 - dt)
if KeyPressTime1 <= 0 then
EnableKeyPress1 = true
@ -19,9 +23,21 @@ function UpdateGame(dt)
v.collider:destroy()
end
if v.collider:enter("Player2") then
print("Player 2 hit")
print("Player1 hit Player2!")
table.remove(Bullets1, i)
v.collider:destroy()
if UserPlayer1.health > 0 then
UserPlayer1.health = UserPlayer1.health - 1
end
end
if v.collider:enter("Player1") then
print("Player 1 hit themselves!")
table.remove(Bullets1, i)
v.collider:destroy()
if UserPlayer1.health > 0 then
UserPlayer1.health = UserPlayer1.health - 1
end
end
end
@ -36,15 +52,24 @@ function UpdateGame(dt)
end
if v.collider:enter("Player1") then
print("Player 1 hit")
print("Player2 hit Player1!")
table.remove(Bullets2, i)
v.collider:destroy()
if UserPlayer2.health > 0 then
UserPlayer2.health = UserPlayer2.health - 1
end
end
if v.collider:enter("Player2") then
print("Player 2 hit themselves!")
table.remove(Bullets2, i)
v.collider:destroy()
if UserPlayer2.health > 0 then
UserPlayer2.health = UserPlayer2.health - 1
end
end
end
UserPlayer1:update(dt)
UserPlayer2:update(dt)
--WindField
World:update(dt)
end

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

@ -1,4 +1,6 @@
Bullet = Object:extend()
cos = math.cos
sin = math.sin --optimisation
function Bullet:new(x, y, p, speed, rotation)
self.image = love.graphics.newImage("assets/bullet.png")
@ -16,35 +18,43 @@ function Bullet:new(x, y, p, speed, rotation)
self.originX = self.width / 2
self.originY = self.height / 2
self.collider = World:newRectangleCollider(x, y, 10, 15)
self.collider = World:newCircleCollider(x, y, 10)
self.collider:setPosition(self.x, self.y)
end
function Bullet:update(dt)
--New bullets are set to New Collision Class
--this is to make sure the bullet doesn't collide with the player that shot it
if self.p == 1 or self.p == 2 then
self.collider:setCollisionClass("New")
end
if self.p == 1 then
self.collider:setCollisionClass("Bullet1")
elseif self.p == 2 then
self.collider:setCollisionClass("Bullet2")
end
--TODO: find out a way to check the direction of a bullet
end
function Bullet:update(dt)
local cos = math.cos
local sin = math.sin --optimisation
local atan2 = math.atan2 --optimisation
if self.p == 1 then
local dx = math.cos(self.rotation) * self.speed * dt
local dy = math.sin(self.rotation) * self.speed * dt
self.x = self.x + dx
self.y = self.y + dy
self.collider:setPosition(self.x, self.y)
local dx = cos(self.rotation) * self.speed * dt
local dy = sin(self.rotation) * self.speed * dt
self.collider:setLinearVelocity(dx, dy)
self.x = self.collider:getX()
self.y = self.collider:getY()
--If a bullet hits a wall, make it bounce off the wall and change its rotation
if self.collider:enter("Wall") then
self.rotation = self.rotation + math.pi
self.collider:setAngle(self.rotation)
end
end
if self.p == 2 then
local dx = math.cos(self.rotation) * self.speed * dt
local dy = math.sin(self.rotation) * self.speed * dt
self.x = self.x + dx
self.y = self.y + dy
self.collider:setPosition(self.x, self.y)
local dx = cos(self.rotation) * self.speed * dt
local dy = sin(self.rotation) * self.speed * dt
self.collider:setLinearVelocity(dx, dy)
self.x = self.collider:getX()
self.y = self.collider:getY()
end
end
@ -63,7 +73,6 @@ function Bullet:draw()
end
for _, _ in ipairs(Bullets2) do
-- love.graphics.draw(self.image, self.x, self.y)
love.graphics.draw(
self.image,
self.x,

@ -1,6 +1,6 @@
--Config file for the game
function love.conf(t)
t.window.width = 1600
t.window.height = 900
t.window.height = 960
t.window.title = "Game"
end

@ -0,0 +1,159 @@
---- Texture atlas complement for the Simple Tiled Implementation
-- @copyright 2022
-- @author Eduardo Hernández coz.eduardo.hernandez@gmail.com
-- @license MIT/X11
local module = {}
--- Create a texture atlas
-- @param files Array with filenames
-- @param sort If "size" will sort by size, or if "id" will sort by id
-- @param ids Array with ids of each file
-- @param pow2 If true, will force a power of 2 size
function module.Atlas( files, sort, ids, pow2 )
local function Node(x, y, w, h)
return {x = x, y = y, w = w, h = h}
end
local function nextpow2( n )
local res = 1
while res <= n do
res = res * 2
end
return res
end
local function loadImgs()
local images = {}
for i = 1, #files do
images[i] = {}
--images[i].name = files[i]
if ids then images[i].id = ids[i] end
images[i].img = love.graphics.newImage( files[i] )
images[i].w = images[i].img:getWidth()
images[i].h = images[i].img:getHeight()
images[i].area = images[i].w * images[i].h
end
if sort == "size" or sort == "id" then
table.sort( images, function( a, b ) return ( a.area > b.area ) end )
end
return images
end
--TODO: understand this func
local function add(root, id, w, h)
if root.left or root.right then
if root.left then
local node = add(root.left, id, w, h)
if node then return node end
end
if root.right then
local node = add(root.right, id, w, h)
if node then return node end
end
return nil
end
if w > root.w or h > root.h then return nil end
local _w, _h = root.w - w, root.h - h
if _w <= _h then
root.left = Node(root.x + w, root.y, _w, h)
root.right = Node(root.x, root.y + h, root.w, _h)
else
root.left = Node(root.x, root.y + h, w, _h)
root.right = Node(root.x + w, root.y, _w, root.h)
end
root.w = w
root.h = h
root.id = id
return root
end
local function unmap(root)
if not root then return {} end
local tree = {}
if root.id then
tree[root.id] = {}
tree[root.id].x, tree[root.id].y = root.x, root.y
end
local left = unmap(root.left)
local right = unmap(root.right)
for k, v in pairs(left) do
tree[k] = {}
tree[k].x, tree[k].y = v.x, v.y
end
for k, v in pairs(right) do
tree[k] = {}
tree[k].x, tree[k].y = v.x, v.y
end
return tree
end
local function bake()
local images = loadImgs()
local root = {}
local w, h = images[1].w, images[1].h
if pow2 then
if w % 1 == 0 then w = nextpow2(w) end
if h % 1 == 0 then h = nextpow2(h) end
end
repeat
local node
root = Node(0, 0, w, h)
for i = 1, #images do
node = add(root, i, images[i].w, images[i].h)
if not node then break end
end
if not node then
if h <= w then
if pow2 then h = h * 2 else h = h + 1 end
else
if pow2 then w = w * 2 else w = w + 1 end
end
else
break
end
until false
local limits = love.graphics.getSystemLimits()
if w > limits.texturesize or h > limits.texturesize then
return "Resulting texture is too large for this system"
end
local coords = unmap(root)
local map = love.graphics.newCanvas(w, h)
love.graphics.setCanvas( map )
-- love.graphics.clear()
for i = 1, #images do
love.graphics.draw(images[i].img, coords[i].x, coords[i].y)
if ids then coords[i].id = images[i].id end
end
love.graphics.setCanvas()
if sort == "ids" then
table.sort( coords, function( a, b ) return ( a.id < b.id ) end )
end
return { image = map, coords = coords }
end
return bake()
end
return module

@ -0,0 +1,132 @@
local lg = _G.love.graphics
local graphics = { isCreated = lg and true or false }
function graphics.newSpriteBatch(...)
if graphics.isCreated then
return lg.newSpriteBatch(...)
end
end
function graphics.newCanvas(...)
if graphics.isCreated then
return lg.newCanvas(...)
end
end
function graphics.newImage(...)
if graphics.isCreated then
return lg.newImage(...)
end
end
function graphics.newQuad(...)
if graphics.isCreated then
return lg.newQuad(...)
end
end
function graphics.getCanvas(...)
if graphics.isCreated then
return lg.getCanvas(...)
end
end
function graphics.setCanvas(...)
if graphics.isCreated then
return lg.setCanvas(...)
end
end
function graphics.clear(...)
if graphics.isCreated then
return lg.clear(...)
end
end
function graphics.push(...)
if graphics.isCreated then
return lg.push(...)
end
end
function graphics.origin(...)
if graphics.isCreated then
return lg.origin(...)
end
end
function graphics.scale(...)
if graphics.isCreated then
return lg.scale(...)
end
end
function graphics.translate(...)
if graphics.isCreated then
return lg.translate(...)
end
end
function graphics.pop(...)
if graphics.isCreated then
return lg.pop(...)
end
end
function graphics.draw(...)
if graphics.isCreated then
return lg.draw(...)
end
end
function graphics.rectangle(...)
if graphics.isCreated then
return lg.rectangle(...)
end
end
function graphics.getColor(...)
if graphics.isCreated then
return lg.getColor(...)
end
end
function graphics.setColor(...)
if graphics.isCreated then
return lg.setColor(...)
end
end
function graphics.line(...)
if graphics.isCreated then
return lg.line(...)
end
end
function graphics.polygon(...)
if graphics.isCreated then
return lg.polygon(...)
end
end
function graphics.points(...)
if graphics.isCreated then
return lg.points(...)
end
end
function graphics.getWidth()
if graphics.isCreated then
return lg.getWidth()
end
return 0
end
function graphics.getHeight()
if graphics.isCreated then
return lg.getHeight()
end
return 0
end
return graphics

File diff suppressed because it is too large Load Diff

@ -0,0 +1,323 @@
--- Box2D plugin for STI
-- @module box2d
-- @author Landon Manning
-- @copyright 2019
-- @license MIT/X11
local love = _G.love
local utils = require((...):gsub('plugins.box2d', 'utils'))
local lg = require((...):gsub('plugins.box2d', 'graphics'))
return {
box2d_LICENSE = "MIT/X11",
box2d_URL = "https://github.com/karai17/Simple-Tiled-Implementation",
box2d_VERSION = "2.3.2.7",
box2d_DESCRIPTION = "Box2D hooks for STI.",
--- Initialize Box2D physics world.
-- @param world The Box2D world to add objects to.
box2d_init = function(map, world)
assert(love.physics, "To use the Box2D plugin, please enable the love.physics module.")
local body = love.physics.newBody(world, map.offsetx, map.offsety)
local collision = {
body = body,
}
local function addObjectToWorld(objshape, vertices, userdata, object)
local shape
if objshape == "polyline" then
if #vertices == 4 then
shape = love.physics.newEdgeShape(unpack(vertices))
else
shape = love.physics.newChainShape(false, unpack(vertices))
end
else
shape = love.physics.newPolygonShape(unpack(vertices))
end
local currentBody = body
--dynamic are objects/players etc.
if userdata.properties.dynamic == true then
currentBody = love.physics.newBody(world, map.offsetx, map.offsety, 'dynamic')
-- static means it shouldn't move. Things like walls/ground.
elseif userdata.properties.static == true then
currentBody = love.physics.newBody(world, map.offsetx, map.offsety, 'static')
-- kinematic means that the object is static in the game world but effects other bodies
elseif userdata.properties.kinematic == true then
currentBody = love.physics.newBody(world, map.offsetx, map.offsety, 'kinematic')
end
local fixture = love.physics.newFixture(currentBody, shape)
fixture:setUserData(userdata)
-- Set some custom properties from userdata (or use default set by box2d)
fixture:setFriction(userdata.properties.friction or 0.2)
fixture:setRestitution(userdata.properties.restitution or 0.0)
fixture:setSensor(userdata.properties.sensor or false)
fixture:setFilterData(
userdata.properties.categories or 1,
userdata.properties.mask or 65535,
userdata.properties.group or 0
)
local obj = {
object = object,
body = currentBody,
shape = shape,
fixture = fixture,
}
table.insert(collision, obj)
end
local function getPolygonVertices(object)
local vertices = {}
for _, vertex in ipairs(object.polygon) do
table.insert(vertices, vertex.x)
table.insert(vertices, vertex.y)
end
return vertices
end
local function calculateObjectPosition(object, tile)
local o = {
shape = object.shape,
x = (object.dx or object.x) + map.offsetx,
y = (object.dy or object.y) + map.offsety,
w = object.width,
h = object.height,
polygon = object.polygon or object.polyline or object.ellipse or object.rectangle
}
local userdata = {
object = o,
properties = object.properties
}
o.r = object.rotation or 0
if o.shape == "rectangle" then
local cos = math.cos(math.rad(o.r))
local sin = math.sin(math.rad(o.r))
local oy = 0
if object.gid then
local tileset = map.tilesets[map.tiles[object.gid].tileset]
local lid = object.gid - tileset.firstgid
local t = {}
-- This fixes a height issue
o.y = o.y + map.tiles[object.gid].offset.y
oy = o.h
for _, tt in ipairs(tileset.tiles) do
if tt.id == lid then
t = tt
break
end
end
if t.objectGroup then
for _, obj in ipairs(t.objectGroup.objects) do
-- Every object in the tile
calculateObjectPosition(obj, object)
end
return
else
o.w = map.tiles[object.gid].width
o.h = map.tiles[object.gid].height
end
end
o.polygon = {
{ x=o.x+0, y=o.y+0 },
{ x=o.x+o.w, y=o.y+0 },
{ x=o.x+o.w, y=o.y+o.h },
{ x=o.x+0, y=o.y+o.h }
}
for _, vertex in ipairs(o.polygon) do
vertex.x, vertex.y = utils.rotate_vertex(map, vertex, o.x, o.y, cos, sin, oy)
end
local vertices = getPolygonVertices(o)
addObjectToWorld(o.shape, vertices, userdata, tile or object)
elseif o.shape == "ellipse" then
if not o.polygon then
o.polygon = utils.convert_ellipse_to_polygon(o.x, o.y, o.w, o.h)
end
local vertices = getPolygonVertices(o)
local triangles = love.math.triangulate(vertices)
for _, triangle in ipairs(triangles) do
addObjectToWorld(o.shape, triangle, userdata, tile or object)
end
elseif o.shape == "polygon" then
-- Recalculate collision polygons inside tiles
if tile then
local cos = math.cos(math.rad(o.r))
local sin = math.sin(math.rad(o.r))
for _, vertex in ipairs(o.polygon) do
vertex.x = vertex.x + o.x
vertex.y = vertex.y + o.y
vertex.x, vertex.y = utils.rotate_vertex(map, vertex, o.x, o.y, cos, sin)
end
end
local vertices = getPolygonVertices(o)
local triangles = love.math.triangulate(vertices)
for _, triangle in ipairs(triangles) do
addObjectToWorld(o.shape, triangle, userdata, tile or object)
end
elseif o.shape == "polyline" then
local vertices = getPolygonVertices(o)
addObjectToWorld(o.shape, vertices, userdata, tile or object)
end
end
for _, tile in pairs(map.tiles) do
if map.tileInstances[tile.gid] then
for _, instance in ipairs(map.tileInstances[tile.gid]) do
-- Every object in every instance of a tile
if tile.objectGroup then
for _, object in ipairs(tile.objectGroup.objects) do
if object.properties.collidable == true then
object = utils.deepCopy(object)
object.dx = instance.x + object.x
object.dy = instance.y + object.y
calculateObjectPosition(object, instance)
end
end
end
-- Every instance of a tile
if tile.properties.collidable == true then
local object = {
shape = "rectangle",
x = instance.x,
y = instance.y,
width = map.tilewidth,
height = map.tileheight,
properties = tile.properties
}
calculateObjectPosition(object, instance)
end
end
end
end
for _, layer in ipairs(map.layers) do
-- Entire layer
if layer.properties.collidable == true then
if layer.type == "tilelayer" then
for gid, tiles in pairs(map.tileInstances) do
local tile = map.tiles[gid]
local tileset = map.tilesets[tile.tileset]
for _, instance in ipairs(tiles) do
if instance.layer == layer then
local object = {
shape = "rectangle",
x = instance.x,
y = instance.y,
width = tileset.tilewidth,
height = tileset.tileheight,
properties = tile.properties
}
calculateObjectPosition(object, instance)
end
end
end
elseif layer.type == "objectgroup" then
for _, object in ipairs(layer.objects) do
calculateObjectPosition(object)
end
elseif layer.type == "imagelayer" then
local object = {
shape = "rectangle",
x = layer.x or 0,
y = layer.y or 0,
width = layer.width,
height = layer.height,
properties = layer.properties
}
calculateObjectPosition(object)
end
end
-- Individual objects
if layer.type == "objectgroup" then
for _, object in ipairs(layer.objects) do
if object.properties.collidable == true then
calculateObjectPosition(object)
end
end
end
end
map.box2d_collision = collision
end,
--- Remove Box2D fixtures and shapes from world.
-- @param index The index or name of the layer being removed
box2d_removeLayer = function(map, index)
local layer = assert(map.layers[index], "Layer not found: " .. index)
local collision = map.box2d_collision
-- Remove collision objects
for i = #collision, 1, -1 do
local obj = collision[i]
if obj.object.layer == layer then
obj.fixture:destroy()
table.remove(collision, i)
end
end
end,
--- Draw Box2D physics world.
-- @param tx Translate on X
-- @param ty Translate on Y
-- @param sx Scale on X
-- @param sy Scale on Y
box2d_draw = function(map, tx, ty, sx, sy)
local collision = map.box2d_collision
lg.push()
lg.scale(sx or 1, sy or sx or 1)
lg.translate(math.floor(tx or 0), math.floor(ty or 0))
for _, obj in ipairs(collision) do
local points = {obj.body:getWorldPoints(obj.shape:getPoints())}
local shape_type = obj.shape:getType()
if shape_type == "edge" or shape_type == "chain" then
love.graphics.line(points)
elseif shape_type == "polygon" then
love.graphics.polygon("line", points)
else
error("sti box2d plugin does not support "..shape_type.." shapes")
end
end
lg.pop()
end
}
--- Custom Properties in Tiled are used to tell this plugin what to do.
-- @table Properties
-- @field collidable set to true, can be used on any Layer, Tile, or Object
-- @field sensor set to true, can be used on any Tile or Object that is also collidable
-- @field dynamic set to true, can be used on any Tile or Object
-- @field friction can be used to define the friction of any Object
-- @field restitution can be used to define the restitution of any Object
-- @field categories can be used to set the filter Category of any Object
-- @field mask can be used to set the filter Mask of any Object
-- @field group can be used to set the filter Group of any Object

@ -0,0 +1,193 @@
--- Bump.lua plugin for STI
-- @module bump.lua
-- @author David Serrano (BobbyJones|FrenchFryLord)
-- @copyright 2019
-- @license MIT/X11
local lg = require((...):gsub('plugins.bump', 'graphics'))
return {
bump_LICENSE = "MIT/X11",
bump_URL = "https://github.com/karai17/Simple-Tiled-Implementation",
bump_VERSION = "3.1.7.1",
bump_DESCRIPTION = "Bump hooks for STI.",
--- Adds each collidable tile to the Bump world.
-- @param world The Bump world to add objects to.
-- @return collidables table containing the handles to the objects in the Bump world.
bump_init = function(map, world)
local collidables = {}
for _, tileset in ipairs(map.tilesets) do
for _, tile in ipairs(tileset.tiles) do
local gid = tileset.firstgid + tile.id
if map.tileInstances[gid] then
for _, instance in ipairs(map.tileInstances[gid]) do
-- Every object in every instance of a tile
if tile.objectGroup then
for _, object in ipairs(tile.objectGroup.objects) do
if object.properties.collidable == true then
local t = {
name = object.name,
type = object.type,
x = instance.x + map.offsetx + object.x,
y = instance.y + map.offsety + object.y,
width = object.width,
height = object.height,
layer = instance.layer,
properties = object.properties
}
world:add(t, t.x, t.y, t.width, t.height)
table.insert(collidables, t)
end
end
end
-- Every instance of a tile
if tile.properties and tile.properties.collidable == true then
local t = {
x = instance.x + map.offsetx,
y = instance.y + map.offsety,
width = map.tilewidth,
height = map.tileheight,
layer = instance.layer,
type = tile.type,
properties = tile.properties
}
world:add(t, t.x, t.y, t.width, t.height)
table.insert(collidables, t)
end
end
end
end
end
for _, layer in ipairs(map.layers) do
-- Entire layer
if layer.properties.collidable == true then
if layer.type == "tilelayer" then
for y, tiles in ipairs(layer.data) do
for x, tile in pairs(tiles) do
if tile.objectGroup then
for _, object in ipairs(tile.objectGroup.objects) do
if object.properties.collidable == true then
local t = {
name = object.name,
type = object.type,
x = ((x-1) * map.tilewidth + tile.offset.x + map.offsetx) + object.x,
y = ((y-1) * map.tileheight + tile.offset.y + map.offsety) + object.y,
width = object.width,
height = object.height,
layer = layer,
properties = object.properties
}
world:add(t, t.x, t.y, t.width, t.height)
table.insert(collidables, t)
end
end
end
local t = {
x = (x-1) * map.tilewidth + tile.offset.x + map.offsetx,
y = (y-1) * map.tileheight + tile.offset.y + map.offsety,
width = tile.width,
height = tile.height,
layer = layer,
type = tile.type,
properties = tile.properties
}
world:add(t, t.x, t.y, t.width, t.height)
table.insert(collidables, t)
end
end
elseif layer.type == "imagelayer" then
world:add(layer, layer.x, layer.y, layer.width, layer.height)
table.insert(collidables, layer)
end
end
-- individual collidable objects in a layer that is not "collidable"
-- or whole collidable objects layer
if layer.type == "objectgroup" then
for _, obj in ipairs(layer.objects) do
if layer.properties.collidable == true or obj.properties.collidable == true then
if obj.shape == "rectangle" then
local t = {
name = obj.name,
type = obj.type,
x = obj.x + map.offsetx,
y = obj.y + map.offsety,
width = obj.width,
height = obj.height,
layer = layer,
properties = obj.properties
}
if obj.gid then
t.y = t.y - obj.height
end
world:add(t, t.x, t.y, t.width, t.height)
table.insert(collidables, t)
end -- TODO implement other object shapes?
end
end
end
end
map.bump_world = world
map.bump_collidables = collidables
end,
--- Remove layer
-- @param index to layer to be removed
bump_removeLayer = function(map, index)
local layer = assert(map.layers[index], "Layer not found: " .. index)
local collidables = map.bump_collidables
-- Remove collision objects
for i = #collidables, 1, -1 do
local obj = collidables[i]
if obj.layer == layer
and (
layer.properties.collidable == true
or obj.properties.collidable == true
) then
map.bump_world:remove(obj)
table.remove(collidables, i)
end
end
end,
--- Draw bump collisions world.
-- @param world bump world holding the tiles geometry
-- @param tx Translate on X
-- @param ty Translate on Y
-- @param sx Scale on X
-- @param sy Scale on Y
bump_draw = function(map, tx, ty, sx, sy)
lg.push()
lg.scale(sx or 1, sy or sx or 1)
lg.translate(math.floor(tx or 0), math.floor(ty or 0))
local items = map.bump_world:getItems()
for _, item in ipairs(items) do
lg.rectangle("line", map.bump_world:getRect(item))
end
lg.pop()
end
}
--- Custom Properties in Tiled are used to tell this plugin what to do.
-- @table Properties
-- @field collidable set to true, can be used on any Layer, Tile, or Object

@ -0,0 +1,217 @@
-- Some utility functions that shouldn't be exposed.
local utils = {}
-- https://github.com/stevedonovan/Penlight/blob/master/lua/pl/path.lua#L286
function utils.format_path(path)
local np_gen1,np_gen2 = '[^SEP]+SEP%.%.SEP?','SEP+%.?SEP'
local np_pat1, np_pat2 = np_gen1:gsub('SEP','/'), np_gen2:gsub('SEP','/')
local k
repeat -- /./ -> /
path,k = path:gsub(np_pat2,'/',1)
until k == 0
repeat -- A/../ -> (empty)
path,k = path:gsub(np_pat1,'',1)
until k == 0
if path == '' then path = '.' end
return path
end
-- Compensation for scale/rotation shift
function utils.compensate(tile, tileX, tileY, tileW, tileH)
local compx = 0
local compy = 0
if tile.sx < 0 then compx = tileW end
if tile.sy < 0 then compy = tileH end
if tile.r > 0 then
tileX = tileX + tileH - compy
tileY = tileY + tileH + compx - tileW
elseif tile.r < 0 then
tileX = tileX + compy
tileY = tileY - compx + tileH
else
tileX = tileX + compx
tileY = tileY + compy
end
return tileX, tileY
end
-- Cache images in main STI module
function utils.cache_image(sti, path, image)
image = image or love.graphics.newImage(path)
image:setFilter("nearest", "nearest")
sti.cache[path] = image
end
-- We just don't know.
function utils.get_tiles(imageW, tileW, margin, spacing)
imageW = imageW - margin
local n = 0
while imageW >= tileW do
imageW = imageW - tileW
if n ~= 0 then imageW = imageW - spacing end
if imageW >= 0 then n = n + 1 end
end
return n
end
-- Decompress tile layer data
function utils.get_decompressed_data(data)
local ffi = require "ffi"
local d = {}
local decoded = ffi.cast("uint32_t*", data)
for i = 0, data:len() / ffi.sizeof("uint32_t") do
table.insert(d, tonumber(decoded[i]))
end
return d
end
-- Convert a Tiled ellipse object to a LOVE polygon
function utils.convert_ellipse_to_polygon(x, y, w, h, max_segments)
local ceil = math.ceil
local cos = math.cos
local sin = math.sin
local function calc_segments(segments)
local function vdist(a, b)
local c = {
x = a.x - b.x,
y = a.y - b.y,
}
return c.x * c.x + c.y * c.y
end
segments = segments or 64
local vertices = {}
local v = { 1, 2, ceil(segments/4-1), ceil(segments/4) }
local m
if love and love.physics then
m = love.physics.getMeter()
else
m = 32
end
for _, i in ipairs(v) do
local angle = (i / segments) * math.pi * 2
local px = x + w / 2 + cos(angle) * w / 2
local py = y + h / 2 + sin(angle) * h / 2
table.insert(vertices, { x = px / m, y = py / m })
end
local dist1 = vdist(vertices[1], vertices[2])
local dist2 = vdist(vertices[3], vertices[4])
-- Box2D threshold
if dist1 < 0.0025 or dist2 < 0.0025 then
return calc_segments(segments-2)
end
return segments
end
local segments = calc_segments(max_segments)
local vertices = {}
table.insert(vertices, { x = x + w / 2, y = y + h / 2 })
for i = 0, segments do
local angle = (i / segments) * math.pi * 2
local px = x + w / 2 + cos(angle) * w / 2
local py = y + h / 2 + sin(angle) * h / 2
table.insert(vertices, { x = px, y = py })
end
return vertices
end
function utils.rotate_vertex(map, vertex, x, y, cos, sin, oy)
if map.orientation == "isometric" then
x, y = utils.convert_isometric_to_screen(map, x, y)
vertex.x, vertex.y = utils.convert_isometric_to_screen(map, vertex.x, vertex.y)
end
vertex.x = vertex.x - x
vertex.y = vertex.y - y
return
x + cos * vertex.x - sin * vertex.y,
y + sin * vertex.x + cos * vertex.y - (oy or 0)
end
--- Project isometric position to cartesian position
function utils.convert_isometric_to_screen(map, x, y)
local mapW = map.width
local tileW = map.tilewidth
local tileH = map.tileheight
local tileX = x / tileH
local tileY = y / tileH
local offsetX = mapW * tileW / 2
return
(tileX - tileY) * tileW / 2 + offsetX,
(tileX + tileY) * tileH / 2
end
function utils.hex_to_color(hex)
if hex:sub(1, 1) == "#" then
hex = hex:sub(2)
end
return {
r = tonumber(hex:sub(1, 2), 16) / 255,
g = tonumber(hex:sub(3, 4), 16) / 255,
b = tonumber(hex:sub(5, 6), 16) / 255
}
end
function utils.pixel_function(_, _, r, g, b, a)
local mask = utils._TC
if r == mask.r and
g == mask.g and
b == mask.b then
return r, g, b, 0
end
return r, g, b, a
end
function utils.fix_transparent_color(tileset, path)
local image_data = love.image.newImageData(path)
tileset.image = love.graphics.newImage(image_data)
if tileset.transparentcolor then
utils._TC = utils.hex_to_color(tileset.transparentcolor)
image_data:mapPixel(utils.pixel_function)
tileset.image = love.graphics.newImage(image_data)
end
end
function utils.deepCopy(t)
local copy = {}
for k,v in pairs(t) do
if type(v) == "table" then
v = utils.deepCopy(v)
end
copy[k] = v
end
return copy
end
return utils

@ -3,13 +3,12 @@ function love.load()
require("player")
require("bullet")
WF = require("libs/windfield")
STI = require("libs/sti")
require("UpdateGame")
require("DrawGame")
require("KeyPressed")
love.profiler = require("libs/profile")
love.profiler.start()
--WindField
World = WF.newWorld(0, 0) --no gravity
World:setQueryDebugDrawing(true) -- Draws the area of a query for 10 frames
@ -19,6 +18,26 @@ function love.load()
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
GameMap = STI("maps/map.lua")
Walls = {}
if GameMap.layers["Walls"] then
for _, obj in ipairs(GameMap.layers["Walls"].objects) do
local wall = World:newRectangleCollider(obj.x, obj.y, obj.width, obj.height)
wall:setType("static")
table.insert(Walls, wall)
Walls[#Walls]:setCollisionClass("Wall")
end
end
--Fonts used in the game
GameFont = love.graphics.newFont("assets/Daydream.ttf", 60)
DebugFont = love.graphics.newFont("assets/Daydream.ttf", 12)
love.graphics.setFont(GameFont)
HEALTH = 3
DELAY = 0.5
DebugFlag = false
@ -29,8 +48,9 @@ function love.load()
KeyPressTime2 = 0
KeyDelay2 = DELAY
UserPlayer1 = Player(1, 1000, 100, HEALTH, "assets/player1.png", 100)
UserPlayer2 = Player(2, 800, 300, HEALTH, "assets/player2.png", 100)
local playerSpeed = 12000
UserPlayer1 = Player(1, 1000, 100, HEALTH, "assets/player1.png", playerSpeed)
UserPlayer2 = Player(2, 200, 300, HEALTH, "assets/player2.png", playerSpeed)
Bullets1 = {}
Bullets2 = {}
end
@ -39,21 +59,14 @@ function love.keypressed(key)
KeyPressed(key)
end
love.frame = 0
function love.update(dt)
--[[
love.frame = love.frame + 1
if love.frame % 100 == 0 then
love.report = love.profiler.report(20)
love.profiler.reset()
end
]]
UpdateGame(dt)
end
function love.draw()
DrawGame()
if DebugFlag then
love.graphics.setFont(DebugFont)
love.graphics.print("Debug Mode", 1200, 850)
--love.graphics.print(love.report or "Please wait...")
end

File diff suppressed because it is too large Load Diff

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.2" orientation="orthogonal" renderorder="right-down" width="25" height="15" tilewidth="64" tileheight="64" infinite="0" nextlayerid="5" nextobjectid="24">
<editorsettings>
<export target="map.lua" format="lua"/>
</editorsettings>
<tileset firstgid="1" name="floor-tiles" tilewidth="64" tileheight="64" tilecount="54" columns="9">
<image source="../../../../../../../../../../Downloads/tileset.png" width="576" height="384"/>
</tileset>
<layer id="1" name="Floor + Outer Walls" width="25" height="15">
<data encoding="csv">
4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,
13,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,15,
22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24
</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,0,0,0,0,0,0,0,0,0,0,7,9,0,0,
0,0,0,0,0,0,0,0,7,8,8,8,9,0,0,0,0,0,0,0,0,16,18,0,0,
0,0,0,0,0,0,0,0,25,26,26,26,27,0,0,0,0,0,0,0,0,25,27,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,0,0,7,8,9,0,0,7,8,8,8,8,8,9,0,0,0,0,0,0,0,0,0,
0,0,7,8,17,17,18,0,0,16,17,17,17,17,17,18,0,0,0,0,0,0,0,0,0,
0,0,16,17,17,26,26,8,8,17,26,26,26,26,26,27,0,0,0,0,0,0,0,0,0,
0,0,16,17,18,0,0,16,17,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,16,17,18,0,0,25,26,27,0,0,0,0,0,0,0,0,7,8,8,8,9,0,0,
0,0,16,17,27,0,0,0,0,0,0,0,7,8,9,0,0,0,16,17,17,17,18,0,0,
0,0,25,27,0,0,0,0,0,0,0,0,16,17,18,0,0,0,16,17,17,17,18,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,25,26,27,0,0,0,25,26,26,26,27,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="4" name="Walls" visible="0">
<object id="10" x="512" y="128" width="320" height="128"/>
<object id="11" x="1344" y="64" width="128" height="192"/>
<object id="12" x="1152" y="640" width="320" height="256"/>
<object id="13" x="768" y="704" width="192" height="192"/>
<object id="19" x="576" y="384" width="448" height="192"/>
<object id="20" x="448" y="512" width="192" height="192"/>
<object id="21" x="256" y="384" width="192" height="192"/>
<object id="22" x="128" y="448" width="192" height="320"/>
<object id="23" x="128" y="768" width="128" height="64"/>
</objectgroup>
</map>

@ -1,5 +1,8 @@
Player = Object:extend()
cos = math.cos
sin = math.sin --optimisation
-- Constructor for the Player class
function Player:new(p, x, y, health, image, speed)
self.p = p
self.image = love.graphics.newImage(image)
@ -11,15 +14,14 @@ function Player:new(p, x, y, health, image, speed)
self.height = self.image:getHeight()
-- Collision Stuff
self.collider = World:newBSGRectangleCollider(x, y, 64, 64, 4)
self.collider:setPosition(self.x, self.y)
self.collider = World:newRectangleCollider(x, y, 64, 64)
self.collider:setFixedRotation(true)
if self.p == 1 then
self.collider:setCollisionClass("Player1")
elseif self.p == 2 then
self.collider:setCollisionClass("Player2")
end
self.collider:setType("static")
self.collider:setObject(self)
-- Rotation Stuff
self.rotation = math.rad(90)
@ -28,38 +30,47 @@ function Player:new(p, x, y, health, image, speed)
self.scaleY = 1
self.originX = self.width / 2
self.originY = self.height / 2
--Velocity
self.vx = 0
self.vy = 0
end
-- Method to handle shooting
function Player:shoot(bulletSpeed)
local cos = math.cos
local sin = math.sin --optimisation
local offsetX = cos(self.rotation) * self.width / 2
local offsetY = sin(self.rotation) * self.height / 2
local offsetX = cos(self.rotation) * self.width * 1.5
local offsetY = sin(self.rotation) * self.height * 1.5
local bulletX = self.x + offsetX
local bulletY = self.y + offsetY
local newBullet = Bullet(bulletX, bulletY, self.p, bulletSpeed, self.rotation)
return newBullet
end
-- Update method for the Player class
function Player:update(dt)
local cos = math.cos
local sin = math.sin --optimisation
local bulletSpeed = 300
self.vx = 0
self.vy = 0
local bulletSpeed = 20000
if self.p == 1 then
-- Handle player 1 controls
if love.keyboard.isDown("w") then
self.x = self.x + cos(self.rotation) * (self.speed * dt)
self.y = self.y + sin(self.rotation) * (self.speed * dt)
self.collider:setPosition(self.x, self.y)
self.vx = cos(self.rotation) * (self.speed * dt)
self.vy = sin(self.rotation) * (self.speed * dt)
elseif love.keyboard.isDown("s") then
self.x = self.x - cos(self.rotation) * (self.speed * dt)
self.y = self.y - sin(self.rotation) * (self.speed * dt)
self.collider:setPosition(self.x, self.y)
self.vx = cos(self.rotation) * (self.speed * dt) * -1
self.vy = sin(self.rotation) * (self.speed * dt) * -1
elseif love.keyboard.isDown("a") then
self.rotation = self.rotation - (self.rotSpeed * dt)
elseif love.keyboard.isDown("d") then
self.rotation = self.rotation + (self.rotSpeed * dt)
end
self.collider:setLinearVelocity(self.vx, self.vy)
-- Check for collision with walls
if self.collider:enter("Wall") then
print("Player 1 collided with wall")
end
if EnableKeyPress1 == true then
if love.keyboard.isDown("space") then
@ -69,27 +80,27 @@ function Player:update(dt)
EnableKeyPress1 = false
end
end
--Query Collision
if self.collider:enter("Player2") then
local collision_data = self.collider:getEnterCollisionData("Player2")
print(collision_data)
end
end
if self.p == 2 then
self.x = self.collider:getX()
self.y = self.collider:getY()
elseif self.p == 2 then
-- Handle player 2 controls
if love.keyboard.isDown("up") then
self.x = self.x + cos(self.rotation) * (self.speed * dt)
self.y = self.y + sin(self.rotation) * (self.speed * dt)
self.collider:setPosition(self.x, self.y)
self.vx = cos(self.rotation) * (self.speed * dt)
self.vy = sin(self.rotation) * (self.speed * dt)
elseif love.keyboard.isDown("down") then
self.x = self.x - cos(self.rotation) * (self.speed * dt)
self.y = self.y - sin(self.rotation) * (self.speed * dt)
self.collider:setPosition(self.x, self.y)
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
@ -100,6 +111,8 @@ function Player:update(dt)
end
end
end
self.x = self.collider:getX()
self.y = self.collider:getY()
end
function Player:draw()

Loading…
Cancel
Save