logologologologo
  • home
  • learn to code
    • Space Invaders – Beginner’s Course
    • Asteroids – Beginner’s Level 2
    • Space Commander – Beginners Level 3
    • Snake – a Beginner’s Challenge
  • learn to make
    • Arduino Tutorials
    • Raspberry Retro Games Console
  • blog
    • Computer Science
    • Coding
      • Collision Detection
    • Retro Gaming
    • Computer Games for Vision Therapy
  • contact me
Learn to Code Space Invaders – Lesson 22 – The Aliens Fight Back
15th November 2019
Learn to Code Space Invaders – Lesson 24 – The Aliens Have Landed
17th November 2019
Published by Bob at 16th November 2019
Categories
  • Learn to Code Space Invaders
Tags

Space Invaders Lesson 23 – Player Lives and Game States

Code for Lesson 23

-- title:  Space Invaders
-- author: Bob Grant
-- desc:   Lesson 23
-- script: lua

playerShip = {

	position = {
		x = 120,
		y = 128
	},
	
	spriteNum = 0,
	minX = 0,
	maxX = 232,
	speed = 1,
	
	width = 8,
	height = 8,
	
	bulletOffset = {
		x = 4,
		y = 4
	}
}

playerBullets = {}
maxPlayerBullets = 5

aliens = {}
alienRows = 6
alienColumns = 8
alienHorzSpacing = 12
alienVertSpacing = 12
alienTopOffset = 8
alienDirection = 1
alienSpeed = 4
alienVertSpeed = 4
alienMaxX = 232
alienMinX = 0
alienMoveDelay = 30
alienMoveCounter = alienMoveDelay

alienMoveNumFrames = 2
alienMoveFrameCounter = 0

alienStepSoundNotes = 4
alienStepSoundCounter = 0
alienStepSoundBaseNote = 8

alienRowTypes = {}
alienRowTypes[1] = {baseSprite = 16, score=50}
alienRowTypes[2] = {baseSprite = 16, score=50}
alienRowTypes[3] = {baseSprite = 32, score=25}
alienRowTypes[4] = {baseSprite = 32, score=25}
alienRowTypes[5] = {baseSprite = 48, score=10}
alienRowTypes[6] = {baseSprite = 48, score=10}

alienMissiles = {}
maxAlienMissiles = 20

explosions = {}

playerScore = 0
playerLives = 3

gameNeedsToBeInitialised = true

-- game state values
stateStartGame = 0
statePlayGame = 1
stateNewLife = 2
stateGameOver = 3

gameState = stateStartGame

timer = 0

function TIC()

	if (gameState == stateStartGame) then
		startGameTIC()
	elseif (gameState == statePlayGame) then
	 playGameTIC()
	elseif (gameState == stateNewLife) then
	 newLifeTIC()
	elseif (gameState == stateGameOver) then
	 gameOverTIC()
	
	end

end -- TIC

function startGameTIC()
	cls()
	print("Press Z to start game", 80, 60)
	if (btnp(4)) then
		gameState = statePlayGame
	end -- if

end -- startGameTIC()

function newLifeTIC()

	timer = timer + 1
	if (timer < 180) then
	 -- wait for explosion to finish
	else
		-- what to do next
		if (playerLives == 0) then
			-- game over
			gameState = stateGameOver 
		else
			-- continue game
			playerShip.position.x = 120
			playerShip.position.y = 128
			
			for bullet = 1, maxPlayerBullets do
				playerBullets[bullet].active = false
			end -- for
			
			for index, missile in ipairs(alienMissiles) do
				table.remove(alienMissiles, index)
			end -- for
			
			gameState = statePlayGame
			
		end
	end -- if
	
	cls()
	drawAliens()
	drawExplosions()
	drawScoreBoard()

end -- newLifeTIC()

function gameOverTIC()
 cls()
	drawAliens()
	drawScoreBoard()
	print("GAME OVER", 70, 46)
	print("Press Z to start game", 60, 60)
	if (btnp(4)) then
		restartGame()
	end -- if

end -- gameOverTIC()

function restartGame()
	gameNeedsToBeInitialised = true
	gameState = statePlayGame
end -- restartGame

function playGameTIC()

 -- game initialisation
 -- check if game needs to be initialised
	if (gameNeedsToBeInitialised) then
		initialiseGame()
		gameNeedsToBeInitialised = false
	end
	
	-- updating
	
	movePlayerShip()
	
	checkPlayerFire()
	movePlayerBullet()
	moveAliens()
	moveAlienMissiles()
	checkBulletCollisions()
	checkMissileCollisions()
	
	-- drawing / rendering
	
	cls()
	
	drawPlayerBullet()
	
	drawAlienMissiles()
	
	drawPlayerShip()
	
	drawAliens()
	
	drawExplosions()
	
	drawScoreBoard()
	
	
end -- end TIC

function movePlayerShip()

	-- check move right button
	if(btn(2)) then
	 playerShip.position.x = 
			playerShip.position.x - playerShip.speed
	end
	-- check move left button
	if(btn(3)) then
	 playerShip.position.x = 
			playerShip.position.x + playerShip.speed
	end
	
	playerShip.position.x = checkLimits(
		playerShip.position.x,
		playerShip.minX,
		playerShip.maxX )

end -- movePlayerShip

function checkLimits(value, min, max)

	if (value > max) then
		value = max
	elseif (value < min) then
		value = min
	else
		value = value
	end
	
	return value

end -- checkLimits

function checkPlayerFire()

	local bulletFired = false
	local bullet = 1
	
	-- if fire button is pressed then
	if (btnp(4)) then
		--  find a bullet that's ready to fire
		
	 while (bullet <= maxPlayerBullets) and (not bulletFired) do
				if (not playerBullets[bullet].active) then
						--   initialise bullet
						playerBullets[bullet].position = {
							x = playerShip.position.x + playerShip.bulletOffset.x,
							y = playerShip.position.y + playerShip.bulletOffset.y
						}
						--   mark bullet as active
						playerBullets[bullet].active = true
						--   stop other bullets from firing
						bulletFired = true
						sfx(1, 50, 20, 1, 15)
				end -- if not active
			bullet = bullet + 1
	
		end -- while

	end -- if button pressed
	
end -- checkPlayerFire

function movePlayerBullet()

	for bullet = 1, maxPlayerBullets do
	
		if (playerBullets[bullet].active) then
			-- move the bullet up the screen
			playerBullets[bullet].position.y = 
			playerBullets[bullet].position.y - playerBullets[bullet].speed
			
			if (playerBullets[bullet].position.y < 0) then
				playerBullets[bullet].active = false
			end
		end
		
	end
		
end -- movePlayerBullet

function drawPlayerBullet()
	
	for bullet = 1, maxPlayerBullets do
	
		if (playerBullets[bullet].active) then
			-- draw player bullet
			-- line(startX, StartY, endX, endY, colour)
			line(
				playerBullets[bullet].position.x,
				playerBullets[bullet].position.y,
				playerBullets[bullet].position.x,
				playerBullets[bullet].position.y + playerBullets[bullet].length,
				playerBullets[bullet].colour
			)
		
		end -- if
	
	end -- for

end -- drawPlayerBullet

function drawPlayerShip()

	spr(playerShip.spriteNum, 
		playerShip.position.x, 
		playerShip.position.y, 0)


end -- drawPlayerShip

function initialiseGame()

	playerShip.position.x = 120
	playerShip.position.y = 128
	playerLives = 3
	playerScore = 0
	alienMissiles = {}
	initPlayerBulletsArray()
	initAliens()

end -- initialiseGame

function initPlayerBulletsArray()

	for bullet = 1, maxPlayerBullets do
		playerBullets[bullet] = {
			position = {
				x = 0,
				y = 0
			},
			height = 5,
			width = 1,
	
			length = 5,
			colour = 14,
			speed = 2,
			active = false
		}
	end -- for

end -- initPlayerBulletsArray

function initAliens()

	-- create alien grid
	for row = 1, alienRows do
		-- create row of aliens
		aliens[row] = {}
		for column = 1,alienColumns do  
			-- create alien
			aliens[row][column] = {
				position = {
					x = (column - 1) * alienHorzSpacing,
					y = alienTopOffset + (row - 1) * alienVertSpacing
				},
				height = 8,
				width = 8,
				alive = true,
				alienBaseSprite = 
					alienRowTypes[row].baseSprite
			}
		-- end row
		end
	-- end alien grid
	end
	

end -- initAliens

function drawAliens()

	for row = 1, alienRows do
	
		for column = 1, alienColumns do
		
			if (aliens[row][column].alive) then
				spr(aliens[row][column].alienBaseSprite 
								+ alienMoveFrameCounter,
				 aliens[row][column].position.x,
					aliens[row][column].position.y)
					
					-- work out if alien will fire
					if (math.random(50) == 1) then
						spawnAlienMissile(aliens[row][column].position)
					end
					
					
			end -- if
		
		end -- columns
	
	end -- rows

end -- drawAliens

function moveAliens()
	
	alienMoveCounter = alienMoveCounter - 1

	if (alienMoveCounter <= 0) then
		
		-- do stepping sound
		alienStepSoundCounter =
			alienStepSoundCounter + 1
		alienStepSoundCounter =
		 alienStepSoundCounter % alienStepSoundNotes
		
		sfx(0, alienStepSoundBaseNote - alienStepSoundCounter, 6, 0, 8)
		
		
		-- alienMoveNumFrames = 2
		-- alienMoveFrameCounter = 0
		alienMoveFrameCounter =
			alienMoveFrameCounter + 1
		alienMoveFrameCounter =
		 alienMoveFrameCounter % alienMoveNumFrames
			
		if aliensAtEdge() then
	
			-- move down
			for row = 1, alienRows do
				for column = 1, alienColumns do	
					if (aliens[row][column].alive) then		
						aliens[row][column].position.y = 
							aliens[row][column].position.y +
							(alienVertSpeed)		
					end -- if	
				end -- columns
			end -- rows
		
			alienDirection = -alienDirection
		
		else
			for row = 1, alienRows do
				for column = 1, alienColumns do	
					if (aliens[row][column].alive) then		
						aliens[row][column].position.x = 
							aliens[row][column].position.x +
							(alienSpeed * alienDirection)		
					end -- if	
				end -- columns
			end -- rows
		
		end -- if
		
		alienMoveCounter = alienMoveDelay
		
	end -- if alienMoveCounter


end -- moveAliens

function aliensAtEdge()

	-- for each alien
	for row = 1, alienRows do
		for column = 1, alienColumns do
			-- if alien is alive then
			if (aliens[row][column].alive) then
		 	-- if alien going right +1
				if (alienDirection == 1) then
					-- if alien x + step > MaxX then
					if (aliens[row][column].position.x + alienSpeed) >
						alienMaxX then
						-- return true
						return true
					-- end if
					end
				-- else going left -1
				else
					if (aliens[row][column].position.x - alienSpeed) <
						alienMinX then
						-- return true
						return true
					-- end if
					end
				-- end if - going left right
				end
			-- end if alive
			end
		end -- for columns
	end -- for rows	
	-- end for
	
	-- return false
	return false
		
end -- aliensAtEdge

function checkCollision(object1, object2)

	-- object.position position.x  posiition.y
	-- object.width
	-- object.height
	
	local object1Left = object1.position.x
	local object1Right = object1.position.x
								+ object1.width -1
	local object1Top = object1.position.y
	local object1Bottom = object1.position.y 
							+ object1.height - 1
							
	local object2Left = object2.position.x
	local object2Right = object2.position.x
								+ object2.width -1
	local object2Top = object2.position.y
	local object2Bottom = object2.position.y 
							+ object2.height - 1
	
	if (object1Left < object2Right) and
				(object1Right > object2Left) and
				(object1Top < object2Bottom) and
				(object1Bottom > object2Top) then
				
				return true
	else
		return false
	end
				
end -- checkCollision

function checkBulletCollisions()

	local bulletHasHitAlien = false
	
	for bullet = 1, maxPlayerBullets do
		if (playerBullets[bullet].active) then
			for row = 1, alienRows do
				for column = 1, alienColumns do
		
					if (aliens[row][column].alive) then
					
						bulletHasHitAlien = checkCollision(
								playerBullets[bullet],
								aliens[row][column]
							)
						if (bulletHasHitAlien) then
							-- hit
							aliens[row][column].alive = false
							playerBullets[bullet].active = false
							alienExplosion(aliens[row][column].position)
							playerScore = playerScore + alienRowTypes[row].score
							sfx(2, 5, 30, 2, 15)
						end -- if
					
					end -- if alien alive
					
				end -- for column
			end -- for row
		
		end -- if bullet active
	end -- for bullet


end -- checkBulletCollisions

function alienExplosion(exPosition)

	local explosion = {
		position = exPosition,
		ticCounter = 0,
		totalTics = 30,
		baseSprite = 64,
		numFrames = 4
	}
	table.insert(explosions, explosion) -- add explosion into the array

end -- function alienExplosion

function drawExplosions() 

	local spriteNumber
	
	for index, explosion in ipairs(explosions) do
	
		spriteNumber = (explosion.ticCounter % explosion.numFrames)
																	+ explosion.baseSprite
	
		spr(spriteNumber, explosion.position.x, explosion.position.y)
		
		explosion.ticCounter = explosion.ticCounter + 1
		if (explosion.ticCounter > explosion.totalTics) then
		 table.remove(explosions, index)
		end --if
		
	end -- for

end -- drawExplosions

function drawScoreBoard()

	print("SCORE : "..playerScore,0,0)

end -- drawScoreBoard

function spawnAlienMissile(alienPosition)

	if (#alienMissiles < maxAlienMissiles) then
		local missile = {
			position = {
				x = alienPosition.x + 4,
				y = alienPosition.y + 8
			},
			height = 5,
			width = 1,
			colour = 6,
			speed = 1
		}
		table.insert(alienMissiles, missile)
	end --if

end -- spawnAlienMissile

function drawAlienMissiles()

	for index, missile in ipairs(alienMissiles) do
		-- line(startX, StartY, endX, endY, colour)
			line(
				missile.position.x,
				missile.position.y,
				missile.position.x,
			 missile.position.y + missile.height,
				missile.colour
			)
	end -- for

end -- drawAlienMissiles

function moveAlienMissiles()

	for index, missile in ipairs(alienMissiles) do
		missile.position.y = missile.position.y +
			missile.speed
			
		if (missile.position.y >= 136) then
			table.remove(alienMissiles, index)
		end -- if
	end -- for
	
end -- moveAlienMissiles

function checkMissileCollisions()

	for index, missile in ipairs(alienMissiles) do
		if (checkCollision(missile, playerShip)) then
			-- player ship has been hit
			local explosion = {
				position = playerShip.position,
				ticCounter = 0,
				totalTics = 60,
				baseSprite = 64,
				numFrames = 4
			}
			table.insert(explosions, explosion) -- add explosion into the array
			table.remove(alienMissiles, index)
			
			jumpToNewLifeState()
			
		end -- if
	end -- for

end -- checkMissileCollisions()

function jumpToNewLifeState()

	gameState = stateNewLife
	timer = 0
	playerLives = playerLives - 1
	

end -- jumpToNewLifeState()

Share
1
Bob
Bob

Related posts

17th November 2019

Learn to Code Space Invaders – Lesson 25 – Level Up!


Read more
17th November 2019

Learn to Code Space Invaders – Lesson 24 – The Aliens Have Landed


Read more
15th November 2019

Learn to Code Space Invaders – Lesson 22 – The Aliens Fight Back


Read more

Comments are closed.

  • Privacy Policy
  • Contact Me
© 2020 Bytes N Bits