From f25f65a610e155a20fe9c87fe9aa24fd075e1e73 Mon Sep 17 00:00:00 2001 From: Jaidyn Ann Date: Sun, 20 Sep 2020 01:13:31 -0500 Subject: [PATCH] add enemies, waves, titlescreen, gameover, screen-loop, etc... --- art/sprites/bird.png | Bin 2016 -> 1475 bytes conf.lua | 1 + main.lua | 404 +++++++++++++++++++++++++++++++++++++++---- 3 files changed, 368 insertions(+), 37 deletions(-) diff --git a/art/sprites/bird.png b/art/sprites/bird.png index 556910b2cdc9abde52135a42a3fa19d48fa20262..03bfab11e027d2c2f0f0b9c67bb6711747400678 100644 GIT binary patch delta 1379 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 f(XG15IlCuq 0 ) then + love.graphics.scale( vScale, vScale ) + end love.graphics.draw(bg, 0, 0) love.graphics.draw(bg, 512, 0) - player:draw() - birdo:draw() - birdtwo:draw() - birdthree:draw() + + love.graphics.draw(waveText, 200, 220, 0, 2, 2) + love.graphics.draw(lifeText, 125, 225, 0, 1.3, 1.3) + love.graphics.draw(bigText, 300, 200, 0, 3.5, 3.5) + + if ( mode == menu ) then + menu_draw() + elseif ( mode == game ) then + game_draw() + elseif ( mode == gameover ) then + gameover_draw() + end end function love.resize ( width, height ) + vScale = height / 600 end ---------------------------------------- -- INPUT ---------------------------------------- function love.keypressed ( key ) + if ( mode == menu ) then + menu_keypressed( key ) + elseif ( mode == game ) then + game_keypressed( key ) + elseif ( mode == gameover ) then + gameover_keypressed( key ) + end +end + +function love.keyreleased (key) + if ( mode == menu ) then + menu_keyreleased( key ) + elseif ( mode == game ) then + game_keyreleased( key ) + elseif ( mode == gameover ) then + gameover_keyreleased( key ) + end +end + + +---------------------------------------- +-- MENU +---------------------------------------- +-- LOAD +-------------------- +function menu_load () + waveText:set("[Enter]") + lifeText:set("") + bigText:set("Bats & Pray") +end + +-------------------- +-- UPDATE +-------------------- +function menu_update ( dt ) +end + +-------------------- +-- DRAW +-------------------- +function menu_draw () +end + +-------------------- +-- INPUT +-------------------- +function menu_keypressed ( key ) +-- if ( key == "enter" ) then + game_load() +-- end +end + +function menu_keyreleased ( key ) +end + + +---------------------------------------- +-- GAMEOVER +---------------------------------------- +-- LOAD +-------------------- +function gameover_load () + mode = gameover + lifeText:set("High Score") + waveText:set(" " .. maxScore) + bigText:set("Game Over") +end + +-------------------- +-- UPDATE +-------------------- +function gameover_update ( dt ) +end + +-------------------- +-- DRAW +-------------------- +function gameover_draw () +end + +-------------------- +-- INPUT +-------------------- +function gameover_keypressed ( key ) +-- if ( key == "enter" ) then + game_load() +-- end +end + +function gameover_keyreleased ( key ) +end + + +---------------------------------------- +-- GAME +---------------------------------------- +-- LOAD +-------------------- +function game_load () + mode = game + lives = 4 + wave = 0 + waveText:set( "Wave " .. wave ) + lifeText:set( "Lives " .. lives ) + bigText:set( "" ) + + player = Bat:new() + birdRegistry = {} +end + +-------------------- +-- UPDATE +-------------------- +function game_update ( dt ) + bird_n = table.maxn( birdRegistry ) + + if ( bird_n == 0 ) then + nextWave() + end + + for i = 1,bird_n do + birdRegistry[i]:update( dt ) + end + + player:update( dt ) + animx.update(dt) +end + +-------------------- +-- DRAW +-------------------- +function game_draw () + for i = 1,table.maxn(birdRegistry) do + birdRegistry[i]:draw() + end + player:draw() +end + +-------------------- +-- INPUT +-------------------- +function game_keypressed ( key ) if ( key == "right" ) then player.moving = true player.direction = right @@ -60,10 +226,12 @@ function love.keypressed ( key ) player.direction = left elseif ( key == "space" ) then player.flying = 2 + elseif ( key == "escape" ) then + gameover_load() end end -function love.keyreleased (key) +function game_keyreleased (key) if ( key == "right" and player.direction == right ) then if ( love.keyboard.isDown("left") ) then player.direction = left @@ -79,6 +247,14 @@ function love.keyreleased (key) end end +---------------------------------------- +-- DRAW +---------------------------------------- + + + + + ---------------------------------------- -- FLIER entity superclass @@ -145,12 +321,18 @@ function Flier:physics_x ( dt ) end end + if ( self.x < -10 ) then + self.x = 800 + elseif ( self.x > 810 ) then + self.x = 0 + end + self.x = self.x + self.x_vel * dt end -- physics on the y-axis function Flier:physics_y ( dt ) - gravity = 2 + gravity = 1 floor = 500 -- wing-flap @@ -167,13 +349,15 @@ function Flier:physics_y ( dt ) -- if on ground; stop gravity if ( self.y > floor ) then self.y = floor - self.y_vel = 0 + self.flying = 2 end self.y = self.y + self.y_vel * dt end + + ---------------------------------------- -- BAT player characters ---------------------------------------- @@ -205,6 +389,42 @@ function Bat:initialize () Flier.initialize( self, 50, 100, batActor ) end +-- return whether or not the Bat's colliding with given object +function Bat:checkCollision ( other ) + if ( colliding( self, other ) ) then +-- print("COLLISION AT " .. self.x .. " " .. other.x ) + return true + else + return false + end +end + +-- check collisions with every bird +function Bat:checkBirdCollisions () + for i = 1,table.maxn( birdRegistry ) do + if ( self:checkCollision(birdRegistry[i]) ) then + judgeCollision( self, birdRegistry[i] ) + return birdRegistry[i] + end + end + return nil +end + +function Bat:update ( dt ) + self:physics( dt ) + self:checkBirdCollisions() +end + +function Bat:kill () + lives = lives - 1 + lifeText:set("Life " .. lives) + self.y = -10 + self.x = 300 + + if ( lives <= 0 ) then + gameover_load() + end +end ---------------------------------------- -- BIRD enemy characters @@ -213,20 +433,36 @@ Bird = class('Bird', Flier) function Bird:initialize ( x, y ) -- animations - birdSheet = love.graphics.newImage("art/sprites/bat.png") + species = math.random(1,2) + + if ( species == 1 ) then + flapFrames = { 2, 3, 4, 5 } + else + flapFrames = { 7, 8, 9, 10 } + end + + if ( species == 1 ) then + idleFrames = { 1 } + else + idleFrames = { 6 } + end + + birdSheet = love.graphics.newImage("art/sprites/bird.png") birdFlapAnim = animx.newAnimation{ img = birdSheet, tileWidth = 32, - frames = { 2, 3, 4, 5 } + tileHeight = 32, + frames = flapFrames }:onAnimOver( function() - player.actor:switch('idle') + self.actor:switch('idle') end ) birdIdleAnim = animx.newAnimation { img = birdSheet, tileWidth = 32, - frames = { 1 } + tileHeight = 32, + frames = idleFrames } birdActor = animx.newActor { @@ -235,6 +471,7 @@ function Bird:initialize ( x, y ) }:switch('idle') Flier.initialize( self, x, y, birdActor ) + self.direction=right end @@ -243,25 +480,118 @@ function Bird:update ( dt ) self:physics( dt ) end +function Bird:kill () + index = indexOf(birdRegistry, self) + table.remove( birdRegistry, index ) +end + + -- basic "ai" (determines where the bird should go) function Bird:destiny () self:destiny_x() self:destiny_y() end --- "ai" on x-axis +-- "ai" on x-axis of species 1 function Bird:destiny_x () - if ( self.x > 500 ) then - self.direction = left - elseif (self.x < 200) then - self.direction = right + if ( species == 1 ) then + if ( self.x > 400 ) then + self.direction = left + elseif ( self.x < 200 ) then + self.direction = right + end + else + if ( self.x > player.x + 25 and math.random(0,50) == 25 ) then + self.direction = left + elseif ( self.x < player.x - 25 and math.random(0,50) == 25 ) then + self.direction = right + end end self.moving = true end --- "ai" on y-axis +-- "ai" on y-axis of species 1 function Bird:destiny_y () - if ( self.y > player.y and math.random(0,50) == 25 ) then + if ( self.y > player.y + 50 and math.random(0,100) == 25 ) then self.flying = 2 end end + + +---------------------------------------- +-- GAME LOGIC +---------------------------------------- +function nextWave ( ) + wave = wave + 1 + waveText:set("Wave " .. wave) + if ( wave > maxScore) then + maxScore = wave + end + + bird_n = wave * 3 + + for i = 1,bird_n do + if ( i % 2 == 0 ) then + birdRegistry[i] = Bird:new( math.random(-20, 0), math.random(0, 600) ) + else + birdRegistry[i] = Bird:new( math.random(800, 820), math.random(0, 600) ) + end + end +end + +-- assuming a and b are colliding, act accordingly +-- aka, bounce-back or kill one +function judgeCollision ( a, b ) + if ( a.y < b.y - 5 ) then + b:kill() + elseif ( a.y > b.y + 5 ) then + a:kill() + else +-- new_vel = greatestAbs( a.x_vel, b.x_vel ) + a.x_vel = a.x_vel * -1 + b.x_vel = b.x_vel * -1 + end +end + + + + +---------------------------------------- +-- UTIL blah blah blah +---------------------------------------- +-- return whether or not two objects are colliding/overlapping +function colliding ( a, b ) + if ( inRange(a.x, b.x - 16, b.x + 16) and inRange(a.y, b.y - 16, b.y + 16) ) then + return true + else + return false + end +end + +-- return whether or not 'a' is within range +function inRange ( a, min, max ) + if ( min < a and a < max ) then + return true + else + return false + end +end + +-- return the num with greatest absolute value +function greatestAbs ( a, b ) + if ( abs(a) > abs(b) ) then + return a + else + return b + + end +end + +function indexOf ( list, item ) + for i = 1,table.maxn(list) do + if ( list[i] == item ) then + return i + end + end + return 0 +end