> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://p5js.sketchpad.cc/sp/pad/view/ro.GrwKUsycNrY/rev.13
 * 
 * authors: 
 *   GoToLoop

 * license (unless otherwise specified): 
 *   creative commons attribution-share alike 3.0 license.
 *   https://creativecommons.org/licenses/by-sa/3.0/ 
 */ 



/**
 * Hoppy Beaver (v2.04)
 * Author: Pamela (2014/Feb)
 * Processing: GoToLoop (2016/Apr/08)
 *
 * forum.Processing.org/two/discussion/8997/hoppy-beaver
 *
 * www.KhanAcademy.org/computing/computer-programming/
 * programming-games-visualizations/side-scroller/p/project-hoppy-beaver-extreme
 *
 * p5js.ProcessingTogether.com/sp/pad/export/ro.Cp2G$x5ApiPI5r
 * studio.ProcessingTogether.com/sp/pad/export/ro.9bTfM49wCIJza
 */

"use strict"

const KHAN = 'http:/' + '/www.KaSandbox.org/third_party/'
  + 'javascript-khansrc/live-editor/build/images/'

const REMOTE = true
//const REMOTE = false

const PATH = REMOTE && KHAN || ''

const STICKS = 200, TURFS = 30, FPS = 60, WEIGHT = 2.5
const sticks = Array(STICKS), turfs = Array(TURFS)

let beaver, score, bg, soil, win, lose

function preload() {
  Beaver.falling = loadImage(PATH + 'creatures/Hopper-Happy.png')
  Beaver.jumping = loadImage(PATH + 'creatures/Hopper-Jumping.png')
  Grass.lawn = loadImage(PATH + 'cute/GrassBlock.png')
}

function setup() {
  createCanvas(500, 400)
  frameRate(FPS).strokeWeight(WEIGHT).rectMode(CORNER).imageMode(CORNER)
  textSize(Score.SIZE).textAlign(CENTER, CENTER)

  Beaver.falling.resize(Beaver.SIZE, Beaver.SIZE)
  Beaver.jumping.resize(Beaver.SIZE, Beaver.SIZE)
  Grass.lawn.resize(Grass.SIZE, Grass.SIZE)

  beaver = new Beaver(width>>1, height - Beaver.GROUND)
  Stick.counter = score = new Score(80, 20)

  for (let y = round(height*.85), i = 0; i !== TURFS; ++i)
    turfs[i] = new Grass(i*Grass.GAP, y)
  Object.freeze(turfs)

  for (let y = round(height*.65), i = 0; i !== STICKS; ++i)
    sticks[i] = new Stick(i*Stick.GAP + width, round(random(20, y)))
  Object.freeze(sticks)

  bg = Object.freeze(color(0xE0, 0xFF, 0xFF))
  soil = Object.freeze(color(0x80, 0x50, 0x2A))

  win = Object.freeze(color('green'))
  lose = Object.freeze(color('red'))

  Stick.ink = Object.freeze(color(0x5A, 0x4A, 0))
  Score.ink = Object.freeze(color('blue'))

  Object.freeze(Object.freeze(Beaver).prototype)
  Object.freeze(Object.freeze(Grass).prototype)
  Object.freeze(Object.freeze(Stick).prototype)
  Object.freeze(Object.freeze(Score).prototype)
}

function draw() {
  background(bg).stroke(0).fill(soil).rect(0, height*.9, width, height*.1)
  for (let i = 0; i !== TURFS; turfs[i++].script());

  fill(Stick.ink)
  for (let s, i = 0; i !== STICKS; beaver.gotStick(s = sticks[i++])
    ? (s.x = -Stick.W, ++score.collected, ++score.dead) : s.script());

  keyIsPressed|mouseIsPressed && beaver.jump() || beaver.fall()
  beaver.display()

  noStroke().fill(Score.ink), score.display(STICKS)
  if (score.dead === STICKS) {
    const won = score.collected/STICKS >= Score.PERCENT
    fill(won && win || lose)
    text(won && 'YOU WIN!!!' || 'Collect More Next Time...', width>>1, height>>1)
  }
}

class Beaver {
  constructor(x, y) { this.x = x, this.y = y, this.isJumping = false }

  static get SIZE() { return 40 }
  static get GROUND() { return 50 } // Beaver.SIZE + 10
  static get SPD() { return 5 }

  display() {
    image(this.isJumping && Beaver.jumping || Beaver.falling, this.x, this.y)
  }

  fall() {
    this.y = min(this.y + Beaver.SPD, height - Beaver.GROUND)
    return this.isJumping = false
  }

  jump() {
    this.y = max(this.y - Beaver.SPD, 0)
    return this.isJumping = true
  }

  gotStick(s) {
    return this.x+Beaver.SIZE > s.x & this.x < s.x+Stick.W &
           this.y+Beaver.SIZE > s.y & this.y < s.y+Stick.H
  }
}

class Grass {
  constructor(x, y) { this.x = x, this.y = y }

  static get SIZE() { return 20 }
  static get GAP() { return 20 }
  static get SCROLL() { return 2 }

  script() { this.update(), this.display() }
  update() { (this.x -= Grass.SCROLL) < -Grass.SIZE && (this.x = width) }
  display() { image(Grass.lawn, this.x, this.y) }
}

class Stick {
  constructor(x, y) { this.x = x, this.y = y }

  static get W() { return 10 }
  static get H() { return 50 }
  static get GAP() { return 40 }
  static get SCROLL() { return 2 }

  script() {
    if (this.x > -Stick.W)  this.update() <= -Stick.W?
      ++Stick.counter.dead : this.x < width && this.display()
  }

  update() { return this.x -= Stick.SCROLL }
  display() { rect(this.x, this.y, Stick.W, Stick.H) }
}

class Score {
  constructor(x, y) { this.x = x, this.y = y, this.collected = this.dead = 0 }

  static get SIZE() { return 18 }
  static get PERCENT() { return .9 }

  display(total) {
    text('Score: ' + nf(this.collected, 3) + '/' + total, this.x, this.y)
  }
}