import Phaser from 'phaser'
import Bird from '../../PlayScene-classes/Bird'
import PipeManager from '../../PlayScene-classes/PipeManager'
import Backdrop from "../../PlayScene-classes/Backdrop";
import Floor from "../../PlayScene-classes/Floor";
import CloudManager from "../../PlayScene-classes/CloudManager";
import UIManager from "../../PlayScene-classes/UIManager";
import eventsCenter from "../../PlayScene-classes/EventsCenter";
import {Difficulty} from "../../enums/States";
const States = {
IDLE: 0,
RUNNING: 1,
DEAD: 2,
}
/**
* @classdesc This manages/updates/creates/etc. every object and manager in the game. This class is vital to the game and without it, the game wouldn't run. This class is a child of the Phaser.Scene
class which allows for overriding methods such as preload, create, and update. It also allows for easy access of different
Phaser objects in the game since it inherits them from the Phaser.Scene class.
* @author Christian P. Auman
* @class
* @module PlayScene
*/
class PlayScene extends Phaser.Scene {
/**
* Setting up the default values for the class variables
*/
constructor() {
super('PlayScene')
this.start = false
this.inputDown = false
this.currentState = States.IDLE
this.velocity = 200
this.colliders = []
this.overlaps = []
const scores = JSON.parse(localStorage.getItem('top-scores'))
if (scores) {
this.topScores = scores
console.log(scores)
console.log(this.topScores)
} else {
this.topScores = {
easy: 0,
medium: 0,
hard: 0,
insane: 0
}
}
}
init(data) {
this.difficulty = data.difficulty
console.log(data)
}
/**
* This function is called before anything is drawn on the canvas.
* This allows for asset preloading which prevents anything from drawing without
* it first loading the asset.
*/
preload() {
this.start = false
this.inputDown = false
this.currentState = States.IDLE
this.velocity = 200
// object instantiation
this.pipeManager = new PipeManager(this, {})
this.bird = new Bird(this)
this.floor = new Floor(this, {})
this.cityscape = new Backdrop(this)
this.cityscape.velocity = this.velocity
this.cloudManager = new CloudManager(this)
this.uiManager = new UIManager(this, this.difficulty, this.topScores)
// preload calls
this.bird.preload()
this.pipeManager.preload()
this.floor.preload()
this.cityscape.preload()
this.cloudManager.preload()
this.uiManager.preload()
}
/**
* This method is used to call all the appropriate create methods from all the needed managers and objects
*/
create() {
this.floor.create()
this.updateVelocity()
// bird setup
this.bird.bottomBoundry = this.game.canvas.height - this.floor.getHeight()
this.bird.create()
// PipeManager setup
this.pipeManager.create()
// Background setup
this.cityscape.create()
this.cityscape.setPosition(0, this.game.canvas.height - this.floor.getHeight())
// CloudManager setup
this.cloudManager.verticalOffset = this.floor.getHeight() + this.cityscape.background.displayHeight
this.cloudManager.create()
// UIManager setup
this.uiManager.create()
// listens for the death event
window.addEventListener('death', () => {
this.handleDeath()
})
// listens for reset event
window.addEventListener('reset', () => {
this.restart()
})
// input event listeners
this.spaceKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE)
this.upKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.UP)
this.input.keyboard.on('keydown-SPACE', () => {
this.handleInputDown()
})
this.input.keyboard.on('keyup-SPACE', () => {
this.onSpaceUp()
})
this.input.keyboard.on('keyup-UP', () => {
this.onSpaceUp()
})
this.setupPipeManager({difficulty: this.difficulty})
// sets up collision checking
this.handleCollisions()
/** dev only */
// this.bird.enableGodMode()
this.setHighScores(this.difficulty)
/**
* This is an event listener that will listen for the resetscene event. Once it is fired, if there is an object
* passed into it, then it means that MenuScene is trying to load to this scene which also means that a difficulty
* was passed in the parameters. It sets the class difficulty to the given one, and sets up the play scene in order
* to start the game loop.
*/
eventsCenter.on('resetscene', (e) => {
if (e) {
this.updateTopScores()
this.setHighScores(e.difficulty)
// setting class difficulty variable
this.difficulty = e.difficulty
// setting uiManager difficulty
this.uiManager.difficulty = this.difficulty
this.setupPipeManager(e)
// removing previous collider events
this.colliders.map(collider => {
collider.destroy()
})
// removing previous overlap events
this.overlaps.map(overlap => {
overlap.destroy()
})
// resetting the arrays for the events
this.colliders.length = 0
this.overlaps.length = 0
// re-instantiating the overlap and collider events
this.handleCollisions()
// setup scene for game loop.\
this.restart()
}
})
eventsCenter.emit('loaded')
}
/**
* This method will setup the PipeManager object, created in the preloader method, and will set settings for it
* based on the difficulty passed with the difficulty parameter passed in.
* @param e - Difficulty that's passed from init or sceneloader
*/
setupPipeManager(e) {
this.pipeManager.difficulty = e.difficulty
const config = {
pipeCount: 8,
difficulty: e.difficulty
}
if (this.difficulty === Difficulty.EASY) {
this.velocity = 170
config.finalHorizontalOffset = 200
config.verticalOffset = 220
config.velocityX = -this.velocity
} else if (this.difficulty === Difficulty.MEDIUM) {
this.velocity = 200
config.finalHorizontalOffset = 250
config.verticalOffset = 200
config.velocityX = -this.velocity
} else if (this.difficulty === Difficulty.HARD) {
this.velocity = 200
config.finalHorizontalOffset = 220
config.verticalOffset = 180
config.horizontalOffset = 350
config.velocityX = -this.velocity
} else {
this.velocity = 300
config.finalHorizontalOffset = 270
config.verticalOffset = 170
config.horizontalOffset = 300
config.velocityX = -this.velocity
}
this.floor.velocity = this.velocity
this.pipeManager.setConfig(config)
}
/**
* Called whenever the space key is pressed and handles making the bird jump and starting the game
*/
onSpaceDown() {
console.log('space down')
this.bird.onJump()
if (this.currentState === States.IDLE) {
this.startGame()
}
}
/**
* Called whenever the space key is released and calls onJumpRelease() from the bird object.
*/
onSpaceUp() {
this.bird.onJumpRelease()
this.inputDown = false
}
/**
* This will update the velocity to the velocity class variable for every object that needs to be updated.
*/
updateVelocity() {
this.floor.velocity = this.velocity
this.pipeManager.setVelocityX(-this.velocity)
}
/**
* This will check if the spacebar key is currently being pressed down. If true, it will call onSpaceDown()
* and will set spaceDown to true, so it won't call onSpaceDown() again until the spacebar has been released.
*/
handleInputDown() {
if (!this.inputDown && (this.spaceKey.isDown || this.upKey.isDown)) {
this.inputDown = true
this.onSpaceDown()
}
}
/**
* The update method is called every frame and is used here to call all the update methods from all the
* managers and objects that need it. It also determines when the game restarts after death and handles
* the jumping input checking.
* @param time - the time passed in milliseconds since the game started.
* @param delta - the time in-between each frame.
*/
update(time,delta) {
this.handleInputDown()
// calling update methods
this.bird.update(time, delta)
this.pipeManager.update(time, delta)
this.cityscape.update(time, delta)
this.floor.update(time, delta)
this.cloudManager.update()
}
/**
* Resetting everything that needs to be reset. Will basically set up everything back to
* how they all started to complete the game loop.
*/
restart() {
this.currentState = States.IDLE
this.start = false
this.tweens.killAll()
this.bird.resetG()
this.pipeManager.resetG()
this.cityscape.resetG()
this.floor.resetG()
this.cloudManager.resetG()
this.uiManager.resetG()
}
updateTopScores() {
switch (this.difficulty) {
case Difficulty.EASY:
this.topScores.easy = this.uiManager.highScore
break
case Difficulty.MEDIUM:
this.topScores.medium = this.uiManager.highScore
break
case Difficulty.HARD:
this.topScores.hard = this.uiManager.highScore
break
case Difficulty.INSANE:
this.topScores.insane = this.uiManager.highScore
break
}
localStorage.setItem('top-scores', JSON.stringify(this.topScores))
}
setHighScores(difficulty) {
console.log(difficulty)
console.log(this.topScores.medium)
switch (difficulty) {
case Difficulty.EASY:
this.uiManager.setHighScore(this.topScores.easy)
break
case Difficulty.MEDIUM:
this.uiManager.setHighScore(this.topScores.medium)
break
case Difficulty.HARD:
this.uiManager.setHighScore(this.topScores.hard)
break
case Difficulty.INSANE:
this.uiManager.setHighScore(this.topScores.insane)
break
}
}
/**
* Starting the game by calling all the start methods needed
*/
startGame() {
this.currentState = States.RUNNING
this.start = true
this.pipeManager.startG()
this.cityscape.startG()
this.floor.startG()
this.cloudManager.startG()
this.uiManager.startG()
/**
* DEV ONLY - checking to ensure no new objects are being created and that the game won't get clouded with unmanaged objects
*/
console.log(this.children.list.length)
}
/**
* this method is only called once and will iterate through every pipe inside the pipe manager, and
* will assign a collision between the pipe and the bird. This also will assign an overlap event and
* callback to the bird and the checkpoint to give the player points
*/
handleCollisions() {
this.pipeManager.pipes.map(pipe => {
this.colliders.push(this.physics.add.collider(this.bird.bird, pipe.pipeGroup, (e) => {
window.dispatchEvent(new Event('death'))
}, null, this))
this.overlaps.push(this.physics.add.overlap(this.bird.bird, pipe.checkpoint, () => {
pipe.handleCheckpointOverlap()
}))
})
}
/**
* Called whenever the bird collides with the ground or a pipe. This will stop or slow all moving game-objects
* to give the illusion of movement. And changes the game state.
*/
handleDeath() {
if (this.currentState !== States.DEAD) {
this.currentState = States.DEAD
this.cameras.main
.flash(400, 200, 200, 200)
.shake(300, 0.008)
this.pipeManager.stopG()
this.cityscape.stopG()
this.floor.stopG()
this.cloudManager.stopG()
this.uiManager.stopG()
this.updateTopScores()
}
}
}
export default PlayScene;