-
-
Save anonymous/0de1ca51eaf934aecd018247a214a0ab to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--# Main | |
function setModes() | |
displayMode(FULLSCREEN_NO_BUTTONS) | |
supportedOrientations(LANDSCAPE_RIGHT) | |
ellipseMode(CENTER) | |
spriteMode(CENTER) | |
rectMode(CENTER) | |
textAlign(CENTER) | |
font("Futura-CondensedMedium") | |
CENTRE = vec2(WIDTH/2, HEIGHT/2) | |
end | |
function parameters() | |
psize = 25 | |
shotsize = 10 | |
jumplen = 30 | |
turnaccel = 0.05 | |
screensave = false | |
alpha = 150 | |
c = {color(255,0,0,alpha), color(0,0,255,alpha), color(0,255,0,alpha), color(255,255,0,alpha)} | |
mapnames = {"CLASSIC", "SLAUGHTERHOUSE", "SMALL MAZE", "LARGE MAZE", "4 CIRCLES", "CROSSHAIRS", "SLIDERS", "SQUEEZE", "SPINNER"} | |
mapnames[0] = "RANDOM MAP" | |
end | |
function settings() | |
players = 2 | |
bots = 1 | |
startlives = 3 | |
mapnumber = 0 | |
gravity = false | |
if screensave then | |
players = 4 | |
bots = 4 | |
end | |
end | |
function setup() | |
setModes() | |
parameters() | |
settings() | |
screen = Menu() | |
physics.gravity(0,0) | |
physics.continuous = true | |
end | |
function draw() | |
background(0) | |
screen:draw() | |
noStroke() | |
fill(255, 255 - 255 * math.max(0, (ElapsedTime - lastchange))) | |
rect(WIDTH/2, HEIGHT/2, WIDTH, HEIGHT) | |
end | |
function touched(t) | |
screen:touched(t) | |
end | |
function collide(c) | |
if screen.collide then | |
screen:collide(c) | |
end | |
end | |
--# Menu | |
Menu = class() | |
function Menu:init() | |
lastchange = ElapsedTime | |
score = {0,0,0,0} | |
--kills = {0,0,0,0} | |
self.buttons = {} | |
table.insert(self.buttons, Button(WIDTH/4, HEIGHT/2, 100, "QUIT", function() close() end)) | |
table.insert(self.buttons, Button(WIDTH/2, HEIGHT/2, 100, "PLAY", function() screen = Game() end)) | |
table.insert(self.buttons, Button(WIDTH * .75, HEIGHT/2, 100, "SETTINGS", function() screen = Settings() end)) | |
backgroundMesh = mesh() | |
backgroundMesh.vertices = {vec2(0,0), vec2(WIDTH,0), vec2(WIDTH,HEIGHT), vec2(WIDTH,HEIGHT), vec2(0,HEIGHT), vec2(0,0)} | |
backgroundMesh.colors = {c[1], c[2], c[3], c[3], c[4], c[1]} | |
end | |
function Menu:draw() | |
backgroundMesh:draw() | |
fontSize(100) | |
fill(255) | |
text("SPIN AND SHOOT 3", WIDTH/2, HEIGHT * .75) | |
for k,v in pairs(self.buttons) do | |
v:draw() | |
end | |
if screensave and ElapsedTime - lastchange > 1 then | |
screen = Game() | |
end | |
end | |
function Menu:touched(t) | |
for k,v in pairs(self.buttons) do | |
v:touched(t) | |
end | |
end | |
--# Settings | |
Settings = class() | |
function Settings:init() | |
lastchange = ElapsedTime | |
screensave = false | |
self.buttons = {} | |
table.insert(self.buttons, Button(WIDTH/2, HEIGHT/2, 100, "MENU", function() screen = Menu() end)) | |
self.buttons.players = Button(WIDTH/4, HEIGHT/2, 100, "", function() togglePlayers() end) | |
self.buttons.startlives = Button(WIDTH * .75, HEIGHT/2, 100, "", function() toggleStartlives() end) | |
self.buttons.bots = Button(WIDTH/4, HEIGHT/5, 100, "", function() toggleBots() end) | |
self.buttons.maps = Button(WIDTH/2, HEIGHT/5, 100, "", function() toggleMaps() end) | |
self.buttons.gravity = Button(WIDTH * .75, HEIGHT/5, 100, "", function() toggleGravity() end) | |
end | |
function Settings:draw() | |
backgroundMesh:draw() | |
self.buttons.players.m = players .. " PLAYERS" | |
self.buttons.startlives.m = startlives .. ((startlives == 1 and " LIFE") or " LIVES") | |
self.buttons.bots.m = ((bots == 0 and "NO") or bots) .. " BOT" .. ((bots ~= 1 and "S") or "") | |
self.buttons.maps.m = mapnames[mapnumber] | |
self.buttons.gravity.m = ((gravity and "") or "NO ") .. "GRAVITY" | |
fontSize(100) | |
fill(255) | |
text("SETTINGS", WIDTH/2, HEIGHT * .75) | |
for k,v in pairs(self.buttons) do | |
v:draw() | |
end | |
end | |
function Settings:touched(t) | |
for k,v in pairs(self.buttons) do | |
v:touched(t) | |
end | |
end | |
function togglePlayers() | |
players = (players - 1) % 3 + 2 | |
bots = math.min(bots, players) | |
end | |
function toggleStartlives() | |
startlives = (startlives + 2) % 6 | |
end | |
function toggleBots() | |
bots = (bots + 1) % (players + 1) | |
end | |
function toggleMaps() | |
mapnumber = (mapnumber + 1) % (#mapnames + 1) | |
end | |
function toggleGravity() | |
gravity = not gravity | |
end | |
--# Game | |
Game = class() | |
function Game:init() | |
lastchange = ElapsedTime | |
p = {} | |
for i = 1,players do | |
p[i] = Spinner(i, WIDTH/2 * w(i) + WIDTH/4, HEIGHT/2 * h(i) + HEIGHT * .75 - math.ceil(players/2) * HEIGHT/4, c[i].r, c[i].g, c[i].b, i > players - bots) | |
end | |
self.shots = {} | |
self.shotBreaks = {} | |
self.map = Map() | |
winner = nil | |
kills = {0,0,0,0} | |
end | |
function Game:draw() | |
local dead = 0 | |
for k,v in pairs(p) do | |
v:drawBack() | |
end | |
noStroke() | |
if gravity then | |
fill(10) | |
ellipse(CENTRE.x, CENTRE.y, 100) | |
end | |
for k,v in pairs(self.shotBreaks) do | |
v:draw() | |
if ElapsedTime > v.starttime + 1 then | |
table.remove(self.shotBreaks, k) | |
end | |
end | |
for k,v in pairs(p) do | |
v:draw() | |
if v.dead then | |
dead = dead + 1 | |
else | |
winner = k | |
end | |
if gravity and not v.dead then | |
v.b:applyForce((CENTRE - v.b.position)/5) | |
end | |
end | |
if dead == players then | |
winner = 0 | |
endGame() | |
elseif dead == players - 1 then | |
endGame() | |
end | |
stroke(255) | |
strokeWidth(5) | |
for k,v in pairs(self.shots) do | |
v:draw() | |
if gravity then | |
v.b:applyForce((CENTRE - v.b.position)/10) | |
end | |
end | |
self.map:draw() | |
end | |
function Game:touched(t) | |
for k,v in pairs(p) do | |
v:touched(t) | |
end | |
end | |
function Game:collide(c) | |
for k,v in pairs(p) do | |
if isHit(c, v.b) then | |
for j,w in pairs(self.shots) do | |
if isHit(c, w.b) then | |
v:getHit() | |
w.b:destroy() | |
if w.player ~= v.player then | |
kills[w.player] = kills[w.player] + 1 | |
end | |
table.remove(self.shots, j) | |
return | |
end | |
end | |
--v.b.angularVelocity = 0 | |
end | |
end | |
for i,v in pairs(self.shots) do | |
for j,w in pairs(self.shots) do | |
if i ~= j and hit(c, v.b, w.b) then | |
table.insert(self.shotBreaks, ShotBreak(c.position, v.c.r, v.c.g, v.c.b, w.c.r, w.c.g, w.c.b)) | |
v.b:destroy() | |
w.b:destroy() | |
table.remove(self.shots, math.max(i,j)) | |
table.remove(self.shots, math.min(i,j)) | |
end | |
end | |
end | |
end | |
function endGame() | |
if winner ~= 0 then | |
p[winner]:die() | |
score[winner] = score[winner] + 1 | |
end | |
for k,v in pairs(screen.shots) do | |
v.b:destroy() | |
table.remove(screen.shots, k) | |
end | |
screen.map:clear() | |
screen = End() | |
end | |
--# Spinner | |
Spinner = class() | |
function Spinner:init(player, centrex, centrey, r, g, b, bot) | |
self.player = player | |
-- self.b = physics.body(POLYGON, vec2(-len/2,-wid/2), vec2(len/2,-wid/2), vec2(len/2,wid/2), vec2(-len/2,wid/2)) | |
self.b = physics.body(CIRCLE, psize) | |
self.b.position = vec2(centrex, centrey) | |
self.b.angle = angle(CENTRE - self.b.position) + 180 | |
self.b.restitution = 1 | |
self.b.linearDamping = 5 | |
self.b.friction = 0 | |
self.b.info = "PLAYER" | |
--self.b.fixedRotation = true | |
self.centrex = centrex | |
self.centrey = centrey | |
self.rwidth = WIDTH/2 | |
self.rheight = HEIGHT/math.ceil(players/2) | |
self.c1 = color(r,g,b,255) | |
self.c2 = color(r,g,b,100) | |
self.c3 = color(r,g,b,50) | |
self.c4 = color(r,g,b,40) | |
self.touch = false | |
self.invincible = true | |
self.lastinvincible = ElapsedTime | |
self.invincibledelay = 0.5 | |
self.health = startlives | |
self.dead = false | |
self.bot = bot | |
self.turn = turnaccel | |
self.angularVelocity = 0 | |
end | |
function Spinner:drawBack() | |
if not self.dead then | |
strokeWidth(5) | |
stroke(self.c2) | |
if self.canshoot then | |
fill(self.c3) | |
elseif self.canshoot and self.bot then | |
--fill(self.c3) | |
fill(self.c4) | |
else | |
--fill(self.c4) | |
fill(0) | |
end | |
rect(self.centrex, self.centrey, self.rwidth, self.rheight) | |
if self.health <= 0 then | |
self:die() | |
end | |
if startlives > 1 then | |
fill(self.c2) | |
fontSize(200) | |
text(self.health, self.centrex, self.centrey) | |
end | |
end | |
end | |
function Spinner:draw() | |
if not self.dead then | |
pushMatrix() | |
translate(self.b.position:unpack()) | |
rotate(self.b.angle) | |
if self.invincible and ElapsedTime * 10 - math.floor(ElapsedTime * 10) < 0.5 then | |
fill(self.c2) | |
else | |
fill(self.c1) | |
end | |
stroke(255) | |
strokeWidth(5) | |
ellipse(0, 0, 2 * psize) | |
line(0, 0, psize, 0) | |
popMatrix() | |
--self.b.linearVelocity = self.b.linearVelocity * 0.9 | |
if not self.touch then | |
self.b:applyAngularImpulse(self.turn) | |
end | |
self.canshoot = math.abs(self.b.angularVelocity) > 300 | |
self.invincible = ElapsedTime < self.lastinvincible + self.invincibledelay | |
if self.b.linearVelocity:len() > 1000 then | |
self.b.linearVelocity = 1000 * self.b.linearVelocity:normalize() | |
end | |
if math.abs(self.b.angularVelocity) > 2000 then | |
self.b.angularVelocity = 2000 * self.b.angularVelocity/math.abs(self.b.angularVelocity) | |
end | |
if self.bot then | |
self:botAI() | |
end | |
end | |
end | |
function Spinner:touched(t) | |
if t.x > self.centrex - self.rwidth/2 and t.x < self.centrex + self.rwidth/2 and t.y > self.centrey - self.rheight/2 and t.y < self.centrey + self.rheight/2 and not self.dead and not self.bot then | |
if t.state == BEGAN then | |
self.touch = true | |
end | |
if t.state == ENDED then | |
self.touch = false | |
self:jump() | |
end | |
end | |
end | |
function Spinner:jump() | |
self.b:applyLinearImpulse(jumplen * dirvec(self.b.angle)) | |
self.turn = -self.turn | |
self:shoot() | |
self.b.angularVelocity = 0 | |
end | |
function Spinner:shoot() | |
if self.canshoot then | |
table.insert(screen.shots, Shot(self.player, self.b.position + (psize + 2 * shotsize) * dirvec(self.b.angle), 50 * math.sqrt(math.abs(self.b.angularVelocity)) * dirvec(self.b.angle), self.c1)) | |
self.canshoot = false | |
end | |
end | |
function Spinner:getHit() | |
if self.invincible then | |
self.invincible = false | |
else | |
self.health = self.health - 1 | |
self:setInvincible() | |
end | |
end | |
function Spinner:setInvincible() | |
self.invincible = true | |
self.lastinvincible = ElapsedTime | |
end | |
function Spinner:die() | |
if self.b then | |
self.b:destroy() | |
self.b = nil | |
end | |
self.dead = true | |
end | |
function Spinner:botAI() | |
self.touch = self.canshoot | |
local ray = physics.raycast(self.b.position + (psize) * dirvec(self.b.angle), self.b.position + 1000 * dirvec(self.b.angle)) | |
if ray then | |
if math.abs(ray.normal:angleBetween(-dirvec(self.b.angle))) then | |
if ray.body.info == "PLAYER" and self.canshoot then | |
self:jump() | |
return | |
end | |
--line(self.b.position.x, self.b.position.y, ray.point.x, ray.point.y) | |
for i = -1,1 do | |
local newRay = physics.raycast(ray.point, ray.point + 1000 * (-dirvec(self.b.angle)):rotate(2 * (-dirvec(self.b.angle)):angleBetween(ray.normal) + i * 0.05)) | |
if newRay then | |
if newRay.body.info == "PLAYER" and newRay.body.position:dist(self.b.position) > psize and self.canshoot then | |
self:jump() | |
return | |
end | |
--line(ray.point.x, ray.point.y, newRay.point.x, newRay.point.y) | |
for j = -1,1 do | |
local lastRay = physics.raycast(newRay.point, newRay.point + 1000 * (ray.point - newRay.point):rotate(2 * (ray.point - newRay.point):angleBetween(newRay.normal) + j * 0.05)) | |
if lastRay then | |
if lastRay.body.info == "PLAYER" and lastRay.body.position:dist(self.b.position) > psize and self.canshoot then | |
self:jump() | |
return | |
end | |
end | |
end | |
end | |
end | |
for k,v in pairs(screen.shots) do | |
--[[ | |
local shotray = physics.raycast(v.b.position + (shotsize + 1) * v.b.linearVelocity:normalize(), v.b.position + 300 * v.b.linearVelocity:normalize()) | |
if shotray then | |
if shotray.body.info == "PLAYER" and shotray.body.position:dist(self.b.position) < psize then | |
self:jump() | |
return | |
end | |
end | |
]] | |
for i = 1,4 do | |
if (v.b.position + i * v.b.linearVelocity * DeltaTime):dist(self.b.position) <= psize + shotsize then | |
self:jump() | |
return | |
end | |
end | |
end | |
--[[ | |
if screen.map.changetime ~= 0 then | |
if math.random(500) == 1 then | |
self:jump() | |
return | |
end | |
end | |
]] | |
end | |
end | |
end | |
--# Shot | |
Shot = class() | |
function Shot:init(player, pos, v, c) | |
self.player = player | |
self.b = physics.body(CIRCLE, shotsize) | |
self.b.restitution = 1 | |
self.b.mass = 10 | |
self.b.position = pos | |
self.b.linearVelocity = v | |
self.c = c | |
end | |
function Shot:draw() | |
fill(self.c) | |
drawBody(self.b) | |
end | |
--# ShotBreak | |
ShotBreak = class() | |
function ShotBreak:init(pos, r1, g1, b1, r2, g2, b2) | |
self.pos = pos | |
self.starttime = ElapsedTime | |
self.c1 = color(r1, g1, b1, 255) | |
self.c2 = color(r2, g2, b2, 255) | |
self.lights = {} | |
for i = 1,20 do | |
table.insert(self.lights, Light(pos, self.c1)) | |
table.insert(self.lights, Light(pos, self.c2)) | |
end | |
end | |
function ShotBreak:draw() | |
local t = ElapsedTime - self.starttime | |
self.c1.a = 255 - 255 * math.min(1, t) | |
self.c2.a = 255 - 255 * math.min(1, t) | |
--print(self.c1, self.c2) | |
for k,v in pairs(self.lights) do | |
v:draw() | |
end | |
end | |
--# Light | |
Light = class() | |
function Light:init(pos, c) | |
self.pos = pos | |
self.c = c | |
self.dir = math.random(360) | |
self.speed = 3 * math.random() | |
self.size = 20 * math.random() | |
self.v = self.speed * dirvec(self.dir) | |
end | |
function Light:draw() | |
self.pos = self.pos + self.v | |
self.v = self.v * 0.9 | |
fill(self.c) | |
ellipse(self.pos.x, self.pos.y, self.size) | |
end | |
--# Map | |
Map = class() | |
function Map:init() | |
self.bodies = {} | |
--[[ | |
self:createEdge(0, 0, WIDTH, 0) | |
self:createEdge(0, 0, 0, HEIGHT) | |
self:createEdge(WIDTH, 0, WIDTH, HEIGHT) | |
self:createEdge(0, HEIGHT, WIDTH, HEIGHT) | |
--]] | |
self.maps = {self.CLASSIC, self.OPEN, self.SMAZE, self.LMAZE, self.BUMPERS, self.CROSSHAIRS, self.SLIDERS, self.SQUEEZE, self.SPINNER} | |
self.currentMap = (mapnumber == 0 and math.random(1, #self.maps)) or mapnumber | |
self.changetime = 0 | |
self:resetMap() | |
self.lastchange = ElapsedTime | |
--[[ | |
print(i) | |
local f = maps[i] | |
print(f) | |
self:f() | |
--]] | |
--[[ | |
slider1 = physicsBox(50, HEIGHT/3) | |
slider1.type = STATIC | |
slider1.position = vec2(WIDTH/2, HEIGHT/6) | |
tween(1, slider1.linearVelocity, {y = HEIGHT/1.2})--, {loop = tween.loop.pingpong}) | |
table.insert(self.bodies, slider1) | |
--]] | |
end | |
function Map:resetMap() | |
for k,v in pairs(self.bodies) do | |
v:destroy() | |
end | |
self.bodies = {} | |
self:createEdge(0, 0, WIDTH, 0) | |
self:createEdge(0, 0, 0, HEIGHT) | |
self:createEdge(WIDTH, 0, WIDTH, HEIGHT) | |
self:createEdge(0, HEIGHT, WIDTH, HEIGHT) | |
self.maps[self.currentMap](self) | |
end | |
function Map:draw() | |
stroke(255) | |
strokeWidth(5) | |
fill(255) | |
if self.changetime == 0 then | |
for k,v in pairs(self.bodies) do | |
drawBody(v) | |
end | |
else | |
local t = ElapsedTime - self.lastchange | |
local _,d = math.modf(t/self.changetime) | |
if d < 1 - 1/self.changetime or ElapsedTime * 10 < 0.5 + math.floor(ElapsedTime * 10) then | |
for k,v in pairs(self.bodies) do | |
drawBody(v) | |
end | |
end | |
if d > 1 - DeltaTime then | |
self:resetMap() | |
end | |
end | |
end | |
function Map:CLASSIC() | |
local body = physics.body(CIRCLE, 100) | |
body.position = CENTRE | |
body.restitution = 1 | |
body.type = STATIC | |
table.insert(self.bodies, body) | |
end | |
function Map:OPEN() | |
self:createEdge(HEIGHT/3, 0, 0, HEIGHT/3) | |
self:createEdge(0, HEIGHT/1.5, HEIGHT/3, HEIGHT) | |
self:createEdge(WIDTH - HEIGHT/3, HEIGHT, WIDTH, HEIGHT/1.5) | |
self:createEdge(WIDTH, HEIGHT/3, WIDTH - HEIGHT/3, 0) | |
end | |
function Map:SMAZE() | |
numw,numh = 5,3 | |
m = Maze(numw, numh) | |
z = m.maze | |
for i = 1,numw do | |
for j = 1,numh do | |
local x, y = (i - 1) * WIDTH/numw, HEIGHT - (j - 1) * HEIGHT/numh | |
if z[i][j][1] == 1 then self:createEdge(x, y, x + WIDTH/numw, y) end | |
if z[i][j][2] == 1 then self:createEdge(x, y, x, y - HEIGHT/numh) end | |
end | |
end | |
self.changetime = 5 | |
end | |
function Map:LMAZE() | |
numw,numh = 7,5 | |
m = Maze(numw, numh) | |
z = m.maze | |
for i = 1,numw do | |
for j = 1,numh do | |
local x, y = (i - 1) * WIDTH/numw, HEIGHT - (j - 1) * HEIGHT/numh | |
if z[i][j][1] == 1 then self:createEdge(x, y, x + WIDTH/numw, y) end | |
if z[i][j][2] == 1 then self:createEdge(x, y, x, y - HEIGHT/numh) end | |
end | |
end | |
self.changetime = 5 | |
end | |
function Map:BUMPERS() | |
for i=1,4 do | |
local bumper = physics.body(CIRCLE, 50) | |
bumper.position = CENTRE + vec2(math.random(-100,100), math.random(-100,100)) | |
bumper.restitution = 1 | |
bumper.mass = 1000 | |
bumper.linearVelocity = math.random(200) * dirvec(math.random(360)) | |
table.insert(self.bodies, bumper) | |
end | |
end | |
function Map:CROSSHAIRS() | |
local body = physicsBox(50, HEIGHT/3) | |
body.type = STATIC | |
body.position = vec2(WIDTH/2, HEIGHT/6) | |
table.insert(self.bodies, body) | |
local body = physicsBox(50, HEIGHT/3) | |
body.type = STATIC | |
body.position = vec2(WIDTH/2, HEIGHT/1.2) | |
table.insert(self.bodies, body) | |
local body = physicsBox(WIDTH/3, 50) | |
body.type = STATIC | |
body.position = vec2(WIDTH/6, HEIGHT/2) | |
table.insert(self.bodies, body) | |
local body = physicsBox(WIDTH/3, 50) | |
body.type = STATIC | |
body.position = vec2(WIDTH/1.2, HEIGHT/2) | |
table.insert(self.bodies, body) | |
end | |
function Map:SLIDERS() | |
slider1 = physicsBox(50, HEIGHT/3) | |
slider1.type = KINEMATIC | |
slider1.position = vec2(WIDTH/2, HEIGHT/6) | |
tween(5, slider1.position, {y = HEIGHT/1.2}, {loop = tween.loop.pingpong}) | |
table.insert(self.bodies, slider1) | |
slider2 = physicsBox(50, HEIGHT/3) | |
slider2.type = KINEMATIC | |
slider2.position = vec2(WIDTH/2, HEIGHT/1.2) | |
tween(5, slider2.position, {y = HEIGHT/6}, {loop = tween.loop.pingpong}) | |
table.insert(self.bodies, slider2) | |
slider3 = physicsBox(WIDTH/3, 50) | |
slider3.type = KINEMATIC | |
slider3.position = vec2(WIDTH/6, HEIGHT/2) | |
tween(5, slider3.position, {x = WIDTH/1.2}, {loop = tween.loop.pingpong}) | |
table.insert(self.bodies, slider3) | |
slider4 = physicsBox(WIDTH/3, 50) | |
slider4.type = KINEMATIC | |
slider4.position = vec2(WIDTH/1.2, HEIGHT/2) | |
tween(5, slider4.position, {x = WIDTH/6}, {loop = tween.loop.pingpong}) | |
table.insert(self.bodies, slider4) | |
--[[ | |
local vbar = physics.body(POLYGON, vec2(-25,-100), vec2(25,-100), vec2(25,100), vec2(-25,100)) | |
vbar.categories = {1} | |
vbar.mask = {0} | |
vbar.restitution = 1 | |
vbar.mass = 100000 | |
vbar.fixedRotation = true | |
vbar.position = CENTRE | |
vbar.linearVelocity = vec2(0,100) | |
local hbar = physics.body(POLYGON, vec2(-100,-25), vec2(-100,25), vec2(100,25), vec2(100,-25)) | |
hbar.categories = {2} | |
hbar.mask = {0} | |
hbar.restitution = 1 | |
hbar.mass = 100000 | |
hbar.fixedRotation = true | |
hbar.position = CENTRE | |
hbar.linearVelocity = vec2(100,0) | |
table.insert(self.bodies, vbar) | |
table.insert(self.bodies, hbar) | |
local vbar = physics.body(POLYGON, vec2(-25,-100), vec2(25,-100), vec2(25,100), vec2(-25,100)) | |
vbar.categories = {3} | |
vbar.mask = {0} | |
vbar.restitution = 1 | |
vbar.mass = 100000 | |
vbar.fixedRotation = true | |
vbar.position = CENTRE | |
vbar.linearVelocity = vec2(0,-100) | |
local hbar = physics.body(POLYGON, vec2(-100,-25), vec2(-100,25), vec2(100,25), vec2(100,-25)) | |
hbar.categories = {4} | |
hbar.mask = {0} | |
hbar.restitution = 1 | |
hbar.mass = 100000 | |
hbar.fixedRotation = true | |
hbar.position = CENTRE | |
hbar.linearVelocity = vec2(-100,0) | |
table.insert(self.bodies, vbar) | |
table.insert(self.bodies, hbar) | |
]] | |
end | |
function Map:SQUEEZE() | |
local speed = 50 | |
local up = physics.body(EDGE, vec2(0,0), vec2(WIDTH,0)) | |
up.type = KINEMATIC | |
up.linearVelocity = vec2(0,speed) | |
local right = physics.body(EDGE, vec2(0,0), vec2(0,HEIGHT)) | |
right.type = KINEMATIC | |
right.linearVelocity = vec2(speed,0) | |
local left = physics.body(EDGE, vec2(WIDTH,0), vec2(WIDTH,HEIGHT)) | |
left.type = KINEMATIC | |
left.linearVelocity = vec2(-speed,0) | |
local down = physics.body(EDGE, vec2(0,HEIGHT), vec2(WIDTH,HEIGHT)) | |
down.type = KINEMATIC | |
down.linearVelocity = vec2(0,-speed) | |
table.insert(self.bodies, up) | |
table.insert(self.bodies, right) | |
table.insert(self.bodies, left) | |
table.insert(self.bodies, down) | |
local body = physics.body(CIRCLE, 50) | |
body.position = CENTRE | |
body.restitution = 1 | |
body.type = STATIC | |
table.insert(self.bodies, body) | |
self.changetime = 5 | |
end | |
function Map:SPINNER() | |
box1 = physicsBox(50, HEIGHT - 100) | |
box1.type = KINEMATIC | |
box1.position = CENTRE | |
box1.restitution = 1 | |
box1.angularVelocity = 20 | |
table.insert(self.bodies, box1) | |
box2 = physicsBox(HEIGHT - 100, 50) | |
box2.type = KINEMATIC | |
box2.position = CENTRE | |
box2.restitution = 1 | |
box2.angularVelocity = 20 | |
table.insert(self.bodies, box2) | |
end | |
function Map:clear() | |
for k,v in pairs(self.bodies) do | |
v:destroy() | |
end | |
end | |
function Map:createEdge(ax, ay, bx, by) | |
local body = physicsEdge(ax, ay, bx, by) | |
body.restitution = 1 | |
table.insert(self.bodies, body) | |
end | |
--# Maze | |
Maze = class() | |
-- Code by Ignatz | |
function Maze:init(n,m) | |
local m=m or n --set m=n if not provided | |
t={} for i=1,n do t[i]={} end --create 2D maze | |
local stack={} --holds visited cells | |
local visitedCells=0 | |
local totalCells=n*m | |
--set up little array to help with finding neighbours | |
local nb={{x=0,y=-1},{x=-1,y=0},{x=1,y=0},{x=0,y=1}} | |
--generate random starting cell | |
local cell={x=math.random(1,n),y=math.random(1,m)} | |
self.firstX,self.firstY=cell.x,cell.y | |
t[cell.x][cell.y]={1,1,1,1} -- top, left, right, bottom (1=wall) | |
visitedCells = visitedCells + 1 | |
--loop through | |
while visitedCells<totalCells do | |
local prevVisitedCells=visitedCells | |
local r={1,2,3,4} | |
while #r~=0 do | |
--pick a random neighbour from the four directions | |
local rr=math.random(1,#r) | |
local s=r[rr] | |
table.remove(r,rr) | |
--work out x and y positions of neighbour | |
local x,y=cell.x+nb[s].x,cell.y+nb[s].y | |
--must be a valid cell that hasnt been visited | |
if x>0 and x<=n and y>0 and y<=m and t[x][y]==nil then | |
t[x][y]={1,1,1,1} -- top, right,bottom,left (1=wall) | |
t[cell.x][cell.y][s]=0 --break down wall in current cell | |
t[x][y][5-s]=0 --and neighbour | |
visitedCells = visitedCells + 1 | |
table.insert(stack,{x=cell.x,y=cell.y}) --add previous cell to stack in case we need it | |
cell.x,cell.y=x,y --make neighbour the current cell | |
break | |
end | |
end | |
if prevVisitedCells==visitedCells then | |
--no unvisited neighbours found, go back to previous cell from stack | |
cell.x,cell.y=stack[#stack].x,stack[#stack].y | |
table.remove(stack,#stack) | |
end | |
end | |
self.maze=t | |
end | |
--# End | |
End = class() | |
function End:init() | |
lastchange = ElapsedTime | |
self.buttons = {} | |
table.insert(self.buttons, Button(WIDTH/4, HEIGHT/2, 100, "QUIT", function() close() end)) | |
table.insert(self.buttons, Button(WIDTH * .75, HEIGHT/2, 100, "MENU", function() screen = Menu() end)) | |
table.insert(self.buttons, Button(WIDTH/2, HEIGHT/2, 100, "PLAY", function() screen = Game() end)) | |
end | |
function End:draw() | |
fill(c[winner].r, c[winner].g, c[winner].b, 50) | |
rect(CENTRE.x, CENTRE.y, WIDTH, HEIGHT) | |
fontSize(100) | |
if winner == 0 then | |
fill(255) | |
text("DRAW", WIDTH/2, HEIGHT * .75) | |
else | |
fill(c[winner]) | |
text("WINNER", WIDTH/2, HEIGHT * .8) | |
text("WINNER", WIDTH/2, HEIGHT * .8) | |
fontSize(50) | |
text("(" .. kills[winner] .. " KILL" .. ((kills[winner] ~= 1 and "S)") or ")"), WIDTH/2, HEIGHT * .7) | |
text("(" .. kills[winner] .. " KILL" .. ((kills[winner] ~= 1 and "S)") or ")"), WIDTH/2, HEIGHT * .7) | |
end | |
fontSize(50) | |
for i=1,players do | |
fill(c[i]) | |
text(score[i], WIDTH/2 - (players - 1) * 50 + (i - 1) * 100, HEIGHT/4) | |
text(score[i], WIDTH/2 - (players - 1) * 50 + (i - 1) * 100, HEIGHT/4) | |
--text(kills[i], WIDTH/2 - (players - 1) * 50 + (i - 1) * 100, HEIGHT/6) | |
--text(kills[i], WIDTH/2 - (players - 1) * 50 + (i - 1) * 100, HEIGHT/6) | |
end | |
for k,v in pairs(self.buttons) do | |
v:draw() | |
end | |
if screensave and ElapsedTime - lastchange > 1 then | |
screen = Game() | |
end | |
end | |
function End:touched(t) | |
if ElapsedTime > lastchange + 0.5 then | |
for k,v in pairs(self.buttons) do | |
v:touched(t) | |
end | |
end | |
end | |
--# Button | |
Button = class() | |
function Button:init(x,y,size,m,action) | |
self.pos = vec2(x,y) | |
self.size = size | |
self.m = m | |
self.action = action | |
self.hit = 0 | |
end | |
function Button:draw() | |
pushMatrix() | |
translate(self.pos:unpack()) | |
stroke(255) | |
strokeWidth(5) | |
if self.hit == 1 and self.ts ~= ENDED then fill(150) else fill(255) end | |
ellipse(0,0,2*self.size) | |
fill(0) | |
fontSize(self.size/4) | |
text(self.m,0,0) | |
popMatrix() | |
end | |
function Button:touched(t) | |
self.ts = t.state | |
if (vec2(t.x,t.y) - self.pos):len() <= self.size then | |
self.hit = 1 | |
if self.ts == ENDED then | |
self.action() | |
end | |
else | |
self.hit = 0 | |
end | |
end | |
--# Helper | |
function drawBody(body) | |
pushMatrix() | |
translate(body.x, body.y) | |
rotate(body.angle) | |
if body.shapeType == POLYGON then | |
local points = body.points | |
for j = 1, #points do | |
a = points[j] | |
b = points[(j % #points) + 1] | |
line(a.x, a.y, b.x, b.y) | |
end | |
elseif body.shapeType == CHAIN or body.shapeType == EDGE then | |
local points = body.points | |
for j = 1, #points - 1 do | |
a = points[j] | |
b = points[j+1] | |
line(a.x, a.y, b.x, b.y) | |
end | |
elseif body.shapeType == CIRCLE then | |
ellipse(0, 0, body.radius*2) | |
end | |
popMatrix() | |
end | |
function physicsEdge(ax, ay, bx, by) | |
local body = physics.body(EDGE, vec2(ax,ay), vec2(bx,by)) | |
return body | |
end | |
function physicsBox(w, h) | |
local body = physics.body(POLYGON, vec2(-w/2, h/2), vec2(-w/2, -h/2), vec2(w/2, -h/2), vec2(w/2, h/2)) | |
return body | |
end | |
function w(i) | |
return (1-(i%2)) | |
end | |
function h(i) | |
return math.floor((i-1)/2) | |
end | |
function dirvec(a) | |
return vec2(math.cos(math.rad(a)),math.sin(math.rad(a))) | |
end | |
function angle(vec) | |
return math.deg(vec2(1,0):angleBetween(vec)) | |
end | |
function hit(c, a, b) | |
if (c.bodyA == a and c.bodyB == b) or (c.bodyA == b and c.bodyB == a) then | |
return true | |
else | |
return false | |
end | |
end | |
function isHit(c, a) | |
if c.bodyA == a or c.bodyB == a then | |
return true | |
else | |
return false | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment