summaryrefslogtreecommitdiff
path: root/game.html.coffee
diff options
context:
space:
mode:
Diffstat (limited to 'game.html.coffee')
-rw-r--r--game.html.coffee1117
1 files changed, 1117 insertions, 0 deletions
diff --git a/game.html.coffee b/game.html.coffee
new file mode 100644
index 0000000..f410c22
--- /dev/null
+++ b/game.html.coffee
@@ -0,0 +1,1117 @@
+# Copyright (c) 2013, 2014 Michele Bini
+
+# A game featuring a Vaquita, the smallest, most endagered marine cetacean
+
+# This program is available under the terms of the MIT License
+
+version = "0.2.251"
+
+{ htmlcup } = require 'htmlcup'
+
+htmlcup[x] = htmlcup.compileTag x for x in [ "svg", "rect", "g", "ellipse", "polygon", "line", "image", "defs", "linearGradient", "stop", "use" ]
+
+title = "Vilma, the happy Vaquita - The Moon is sinking!"
+
+fs = require 'fs'
+
+datauri = (t,x)-> "data:#{t};base64,#{new Buffer(fs.readFileSync(x)).toString("base64")}"
+datauripng = (x)-> datauri "image/png", x
+dataurijpeg = (x)-> datauri "image/jpeg", x
+datauriicon = (x)-> datauri "image/x-icon", x
+
+icon = datauriicon "vaquita.ico"
+pixyvaquita = datauripng "vilma.png"
+
+frames =
+ _: pixyvaquita
+ twist_l: datauripng "vilma_twist_l.png"
+ twist_r: datauripng "vilma_twist_r.png"
+ happybubble0: datauripng "Happy-oxygen-bubble.png"
+ grumpybubble0: datauripng "Grumpy-bubble.png"
+ evilbubble0: datauripng "Evil-bubble.png"
+ stilla0: datauripng "Stilla-the-starfish.png"
+ # cuteluterror: datauripng 'cutelu-terror-v3.png'
+ seafloor: dataurijpeg "seafloor.png"
+
+gameName = "#{title} v#{version}"
+
+htmlcup.jsFile = (f)-> @script type:"text/javascript", (fs.readFileSync(f).toString())
+
+gameAreaSize = [ 240, 360 ]
+
+genPage = ->
+ htmlcup.printHtml "<!DOCTYPE html>\n"
+ htmlcup.html lang:"en", manifest:"index.appcache", style:"height:100%", ->
+ @head ->
+ @meta charset:"utf-8"
+ @meta name:"viewport", content:"width=480, user-scalable=no"
+ @meta name:"apple-mobile-web-app-capable", content:"yes"
+ @meta name:"mobile-web-app-capable", content:"yes"
+ # Improve support: http://www.html5rocks.com/en/mobile/fullscreen/
+ # Homescreen installed webapp on Android Chrome has weird name! (Web App)
+ @link rel:"shortcut icon", href:icon
+ @title title
+ @body style:"margin:0;border:0;padding:0;height:100%;width:100%;background:black", ->
+ @div style:"visibility:hidden;position:absolute", ->
+ @img id:"pixyvaquita", src:pixyvaquita
+ @img id:"pixyvaquita_twist_l", src:frames.twist_l
+ @img id:"pixyvaquita_twist_r", src:frames.twist_r
+ @img id:"happybubble0", src:frames.happybubble0
+ @img id:"grumpybubble0", src:frames.grumpybubble0
+ @img id:"evilbubble0", src:frames.evilbubble0
+ @img id:"stilla0", src:frames.stilla0
+ # @img id:"cuteluterror", src:frames.cuteluterror
+ @img id:"seafloor", src:frames.seafloor
+ @div style:"display:table;width:100%;max-width:100%;height:100%;margin:0;border:0;padding:0", ->
+ @div style:"display:table-cell;vertical-align:middle;width:100%;margin:0;border:0;padding:0;text-align:center", ->
+ @div style:"position:relative;display:inline-block", width:"#{gameAreaSize[0]*2}", height:"#{gameAreaSize[1]*2}", ->
+ @canvas width:"#{gameAreaSize[0]*2}", height:"#{gameAreaSize[1]*2}"
+ @header style:"position:absolute;top:0;left:0;font-size:14px;width:100%;color:black", ->
+ @span gameName
+ @span " - "
+ @a target:"_blank", href:"index.html", "Save Vaqitas"
+ @div style:"text-align:right", id:"fps"
+ gameObjects = null
+ @script type:"text/javascript", "gameObjects=#{JSON.stringify(gameObjects)};"
+ @script type:"text/javascript", "__hasProp = {}.hasOwnProperty; __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };"
+ @jsFile "jaws/jaws-min.js"
+ # @jsFile "jaws-assets-named.js"
+ @coffeeScript -> do ->
+
+ # reportErrors = (x)->
+ # try
+ # x()
+ # catch error
+ # try
+ # alert error.toString()
+ # catch error2
+ # alert error
+
+ screen_x1 = 120
+ screen_y1 = 180
+ { sqrt } = Math
+
+ ###
+ # an ad-hoc redux of hammer.js
+ hammerLet = do(window, navigator)@>
+ mobile_regex: mobile_regex = /mobile|tablet|ip(ad|hone|od)|android/i
+ support_touch: support_touch = ('ontouchstart' in window)
+ prefixed: prefixed =
+ global: window
+ get: (sym)@>
+ { global } = @g
+ for v in @vendors
+ return r if (r = global[v + sym])?
+ undefined
+ vendors: [ 'webkit', 'moz', 'MS', 'ms', 'o' ]
+ PointerEvent: PointerEvent ? prefixed.run(window, 'PointerEvent')?
+ suppourt_touch_only: support_touch && mobile_regex.test(navigator.userAgent)
+ ###
+ jaws.onload = ->
+ class Demo
+ keyCodes: { left: leftKey, right: rightKey, up: upKey, down: downKey, space: spaceKey } = jaws.keyCodes
+ Sprite: Sprite = class extends jaws.Sprite
+ # caller needs to set lr for flip center
+ constructor: ->
+ super
+ image: @image
+ x: 0
+ y: 0
+ scale: 2
+ draw: ->
+ @flipped = @lr >= 0
+ @x = (screen_x1 + @px + @lr) * 2
+ @y = (screen_y1 + @py - @tb) * 2
+ super()
+ cr: 4
+ sqrt: Math.sqrt
+ collide: (o)@>
+ { px, py, cr } = o
+ opx = o.px; opy = o.py; ocr = o.cr
+ dx = px - opx
+ dy = py - opy
+ dc = cr + ocr
+ if (qd = dx * dx + dy * dy) <= dc * dc
+ @bumpedInto?(o, qd, dx, dy)
+ o.bumpedInto?(@, qd, -dx, -dy)
+ # if true
+ # @lr = - @lr
+ # o.lr = - o.lr
+ # @px = opx
+ # @py = opy
+ # return
+ # @py = py - 1
+ # return
+ # { sqrt } = @
+ # if false
+ # py = opy
+ # px = opx - dc
+ # else
+ # d = sqrt d
+ # if d < 0.1
+ # dy = -1
+ # d = dx * dx + dy * dy
+ # d = sqrt d
+ # d = 3 * dc / sqrt(d)
+ # py = opy + dy * d
+ # px = opx + dx * d
+ # @px = px | 0
+ # @py = py | 0
+
+ Bubble: Bubble = Sprite
+ HappyBubble: HappyBubble = class extends Bubble
+ image: happybubble0
+ constructor: ->
+ @lr = 4
+ @tb = 4
+ super()
+ draw: ->
+ @py--
+ super()
+ bumpedInto: (o, qd, dx, dy)@>
+ return if @dead
+ # if dx * dx * 2 > qd
+ @dead = true
+ GrumpyBubble: GrumpyBubble = class extends Bubble
+ image: grumpybubble0
+ constructor: ->
+ @lr = 7
+ @tb = 7
+ @cr = 8
+ @life = 60
+ super()
+ draw: (collisions, game)->
+ if game?.slowedBubbles
+ @py -= 2
+ else
+ @py -= 3
+ super()
+ bumpedInto: (o, qd, dx, dy)@>
+ return if @dead
+ # if dx * dx * 2 > qd
+ # @dead = true
+ ovy = o.vy
+ o.py -= 3 + (ovy > 0 then @life -= ovy; ovy * 2 else 0)
+ @dead = true unless @life > 0
+ EvilBubble: EvilBubble = class extends Bubble
+ image: evilbubble0
+ constructor: ->
+ @lr = 15
+ @tb = 15
+ @cr = 8
+ @vy_ = -7
+ @life = 2200
+ super()
+ draw: (collisions, game)->
+ l = 0
+ if game.slowedBubbles
+ @py -= 3
+ else
+ @py += @vy_
+ if (life = @life) < 2200
+ l = 2200 - @life
+ # l -= 1100
+ # l = -l if l < 0
+ # l = (l / 20)|0
+ l = 2200 - l if l > 1100
+ l /= 55
+ @vy_ = - 8 - l
+ super()
+ bumpedInto: (o, qd, dx, dy)@>
+ return if @dead
+ # if dx * dx * 2 > qd
+ # @dead = true
+ ovx = o.vx
+ ovy = o.vy
+ @life -= ovx * ovx + ovy * ovy
+ @life -= 10
+ o.px = @px
+ o.py = @py + @vy_
+ @dead = true unless @life > 0
+ slowBubbles: @>
+ return if @slowedBubbles
+ @slowedBubbles = true
+ quitSlowBubbles: @>
+ return unless @slowedBubbles
+ @slowedBubbles = false
+ Stilla: Stilla = class extends Bubble
+ image: stilla0
+ Bubble: @Bubble
+ constructor: ->
+ @lr = 16
+ @tb = 20
+ @patience = 490
+ super()
+ # Math: Math
+ sqrt: Math.sqrt
+ pow: Math.pow
+ sin: Math.sin
+ draw: (collisions, game)->
+ { px, py, lr } = @
+ (spin = @spin) then
+ { pow, sin } = @
+ d = pow(px * px + py * py, 0.42)
+ r = 5 / (d + 1)
+ if r < 1
+ ir = 1
+ else
+ d = sin(d / 8000)
+ d = d * d * 8000
+ ir = sqrt(1 - r * r) * pow(d, 0.01)
+ @px = px * ir + py * (r * spin)
+ @py = py * ir - px * (r * spin)
+ (d = px * px + py * py) > 40000 then
+ @spin = null
+ if @patience < 0
+ @dead = 1
+ # @patience += 10
+ # @spinFrame =
+ else !(d >= 0) then
+ @px = 0
+ @py = 1
+ throw "ir #{ir}"
+
+ else
+ closest = null
+ closestDist = null
+ consider = (v)->
+ return unless v?
+ dx = px - v.px
+ dy = py - v.py
+ d = dx * dx + dy * dy
+ if !closest? or d < closestDist
+ return unless d >= 0
+ closest = v
+ closestDist = d
+ game.quitSlowBubbles()
+ { vilma } = game
+ consider vilma
+ if game.vaquitas?
+ consider v for v in game.vaquitas
+ slowBubbles = false
+ if closest?
+ if closestDist < 7000
+ slowBubbles = true
+ if closestDist < 4000
+ @patience--
+ if @patience < 0 or (closestDist < 1000 and closest is vilma)
+ dx = px - closest.px
+ @spin = (lr > 0 then +1 else -1) # Start spinning
+ @patience -= 100
+ else
+ dx = px - closest.px
+ dy = py - closest.py
+ # fpx = @fpx += dx / 100
+ # fpy = @fpy += dy / 100
+ # @px = fpx | 0
+ # @py = fpy | 0
+ @px += (dx > +2 then +1 else dx < -2 then -1 else 0)
+ @py += (dy > +2 then +1 else dy < -2 then -1 else 0)
+ # @px += 1
+
+ # @px += 1
+ if slowBubbles
+ game.slowBubbles()
+ else
+ game.quitSlowBubbles()
+ @lr = -lr if px * lr > 0
+ super()
+ goodnight: (game)@> game.quitSlowBubbles()
+ bumpedInto: (o)@>
+ o.dead = true
+ Vaquita: Vaquita = class extends Sprite
+ twist: [ pixyvaquita_twist_l, pixyvaquita_twist_r ]
+ constructor: ->
+ @lr = 16
+ @tb = 16
+ super()
+ draw: ->
+ if @vx < 0
+ @lr = - 18
+ else if @vx > 0
+ @lr = 18
+ super()
+ AiVaquita: AiVaquita = class extends Vaquita
+ constructor: ->
+ @image = pixyvaquita
+ @time = 0
+ super()
+ beat_lr: 0
+ draw: ->
+ vx = @vx + Math.floor(Math.random()*3) - 1
+ vy = @vy + Math.floor(Math.random()*3) - 1
+ x = @px
+ y = @py
+ rx = 0.5 * x / screen_x1
+ ry = 0.5 * y / screen_y1
+ if (s = vx * vx + vy * vy * 2) > 6
+ vx = Math.round(vx * 0.8 - rx)
+ vy = Math.round(vy * 0.8 - ry)
+ @px += @vx = vx
+ @py += @vy = vy
+ if (@time++ % 3) is 0
+ if @image isnt pixyvaquita
+ @image = pixyvaquita
+ else if vx * vx + vy * vy > 2
+ @image = @twist[ @beat_lr++ & 1 ]
+ super()
+ Vilma: Vilma = class extends Vaquita
+ constructor: (@game)->
+ @image = pixyvaquita
+ @time = 0
+ super()
+ @fpx = @px ? 0
+ @fpy = @py ? 0
+ @touch = @game.touchInput
+ beat_lr: 0
+ move: ->
+ { touch } = @
+ { tx, ty } = touch
+ itx = (tx >= 2 then 2 else tx <= -2 then -2 else 0)
+ ity = (ty >= 2 then 2 else ty <= -2 then -2 else 0)
+ touch.tx = tx * 0.9 - itx
+ touch.ty = ty * 0.9 - ity
+ ax = (if jaws.pressed[leftKey] then -1 else 0) + (if jaws.pressed[rightKey] then 1 else 0) - itx / 2
+ ay = (if jaws.pressed[upKey] then -1 else 0) + (if jaws.pressed[downKey] then 1 else 0) - ity / 2
+ if (aq = ax * ax + ay * ay) > 1
+ aq = sqrt(aq)
+ ax /= aq
+ ay /= aq
+ ax *= 0.618
+ ay *= 0.618
+ vx = @vx
+ vy = @vy
+ if ax * vx < 0
+ vx = 0
+ else
+ vx += ax
+ vx *= 0.9
+ if ay * vy < 0
+ vy = 0
+ else
+ vy += ay
+ vy *= 0.9
+ @vx = vx
+ @vy = vy
+ @px = (@fpx += @vx)
+ @py = (@fpy += @vy)
+ draw: ->
+ { vx, vy } = @
+ if (@time++ % 3) is 0
+ if @image isnt pixyvaquita
+ @image = pixyvaquita
+ else if vx * vx + (vy * vy / 4) > 1
+ @image = @twist[@beat_lr++ & 1]
+ super()
+ addVaquita: ->
+ # n = v.cloneNode()
+ # n.setAttribute "opacity", "0.5"
+ # n.href.baseVal = "#_v105" if Math.random(0) > 0.5
+ # n.setAttribute "transform", ""
+ # sea.appendChild n
+ angle = Math.random() * 6.28
+ v = new AiVaquita
+ v.vx = 0
+ v.vy = 0
+ v.px = Math.floor(Math.sin(angle) * 300)
+ v.py = Math.floor(Math.cos(angle) * 300)
+ # v.draw()
+ # vaquita.update()
+ @vaquitas.push v
+ addStilla: (x, y)@>
+ return if @stilla?
+ v = new @Stilla
+ v.px = x
+ v.py = y
+ @stilla = v
+ addInto: (n, v, x, y)@>
+ v.vx = 0
+ v.vy = 0
+ v.px = x
+ v.py = y
+ b = @[n]
+ if (i = b.indexOf(null)) >= 0
+ b[i] = v
+ else
+ b.push v
+ # v.draw()
+ constructor: (@vaquitas = [], @cameos = [], @stilla = null)->
+ encounters:
+ __proto__:
+ encounter: encounter =
+ add: (game, x, y)@> game.addInto('cameos', new @creature(), x, y)
+ vy: 0
+ random: Math.random
+ log: Math.log
+ exp: Math.exp
+ pow: Math.pow
+ poissonSample: (m)@>
+ { exp, random } = @
+ pgen = (m)->
+ x = 0
+ p = exp(-m)
+ s = p
+ u = random()
+ while u > s
+ x++
+ p = p * m / x
+ s += p
+ x
+ s = 0
+ while m > 50
+ s += pgen 50
+ m -= 50
+ s + pgen m
+ generate: (game,left,top,width,height,vx,vvy)@>
+ { probability, random } = @
+ depth = game.getDepth()
+ genRect = (m,left,top,width,height)=>
+ c = m.p(depth) * width * height
+ # c = 0
+ c = @poissonSample(c)
+ if c is 1
+ m.add?( game, left + ((random() * width)|0), top + ((random() * height)|0) )
+ else
+ # c = 0 # if c > 1000
+ # c-- if random() > 0.15
+ while c-- > 0
+ m.add?( game, left + ((random() * width)|0), top + ((random() * height)|0) )
+ 1
+ if vx * vx >= width * width
+ for k,v of @catalogue
+ genRect(v, left, top, width, height)
+ else for k,v of @catalogue
+ vy = vvy - v.vy
+ if vy * vy >= height * height
+ genRect(v, left, top, width, height)
+ else if vx > 0
+ if vy > 0
+ genRect(v, left, top + height - vy, width, vy)
+ genRect(v, left + width - vx, top, vx, height - vy)
+ else if vy < 0
+ genRect(v, left, top, width, -vy)
+ genRect(v, left + width - vx, top - vy, vx, height + vy)
+ else
+ genRect(v, left + width, top, vx, height)
+ else if vx < 0
+ if vy > 0
+ genRect(v, left, top + height - vy, width, vy)
+ genRect(v, left, top, -vx, height - vy)
+ else if vy < 0
+ genRect(v, left, top, width, -vy)
+ genRect(v, left, top - vy, -vx, height + vy)
+ else
+ genRect(v, left, top, -vx, height)
+ else if vy > 0
+ genRect(v, left, top + height - vy, width, vy)
+ else if vy < 0
+ genRect(v, left, top, width, -vy)
+ catalogue:
+ happybubble:
+ __proto__: encounter
+ p: (depth)@> 0.0001 * (1.5 - depth)
+ creature: HappyBubble
+ vy: -1
+ grumpybubble:
+ __proto__: encounter
+ p: (depth)@> depth < 0.08 then 0 else (depth - 0.08) * 0.00015
+ creature: GrumpyBubble
+ vy: -3
+ evilbubble:
+ __proto__: encounter
+ p: (depth)@> depth < 0.35 then 0 else (depth - 0.35) * 0.00005
+ creature: EvilBubble
+ vy: -8
+ stilla:
+ __proto__: encounter
+ p: (depth)@> depth < 0.01 then 1 else (1-depth)/100000
+ add: (game, x, y)@> game.addStilla(x, y)
+ touchInput:
+ tx: 0
+ ty: 0
+ ongoing: { }
+ __proto__:
+ eval: eval
+ start: (ev,el)@>
+ { ongoing } = @
+ for t in ev.changedTouches
+ { identifier, pageX, pageY } = t
+ ongoing[identifier] =
+ px: pageX
+ py: pageY
+ move: (ev,el)@>
+ { ongoing } = @
+ { tx, ty } = @
+ for t in ev.changedTouches
+ { identifier, pageX, pageY } = t
+ o = ongoing[identifier]
+ dx = (pageX - o.px) * 4
+ dy = (pageY - o.py) * 4
+ dx *= 3 if dx * tx < 0
+ dy *= 3 if dy * ty < 0
+ tx += dx
+ ty += dy
+ # tx * dx > 0 then tx += dx else tx = dx * 2
+ # ty * dy > 0 then ty += dy else ty = dy * 2
+ o.px = pageX
+ o.py = pageY
+ @tx = tx
+ @ty = ty
+ end: (ev,el)@>
+ { ongoing } = @
+ for t in ev.changedTouches
+ { identifier } = t
+ delete ongoing[identifier]
+ handle: (name)->
+ touchInput = @
+ (event)->
+ event.preventDefault()
+ event.stopPropagation()
+ touchInput[name](event,this) catch err
+ alert err.toString()
+ ColorPlane: ColorPlane = do->
+ document: document
+ init: @>
+ { color } = @
+ if color and typeof color is 'string'
+ e = @document.createElement "canvas"
+ e.width = @w
+ e.height = @h
+ ctx = e.getContext '2d'
+ @color = ctx.fillStyle = color
+ frame: (t)@>
+ # t.save()
+ t.fillStyle = @color
+ t.fillRect 0,0,1024,1024
+ # t.restore()
+ GenericPlane: GenericPlane =
+ document: document
+ init: @>
+ { document } = @
+ e = document.createElement "canvas"
+ e.width = @w
+ e.height = @h
+ @ctx = e.getContext '2d'
+
+ ScaledImg: ScaledImg =
+ document: document
+ zoom: 2
+ init: @>
+ retroScaling = (c)->
+ c.imageSmoothingEnabled = false;
+ c.webkitImageSmoothingEnabled = false;
+ c.mozImageSmoothingEnabled = false;
+
+ { zoom } = @
+ { width, height } = @img
+ @w = w = width * zoom
+ @h = h = height * zoom
+ c0 = e = @document.createElement "canvas"
+ retroScaling(c0)
+ e.width = w
+ e.height = height
+ ctx0 = e.getContext '2d'
+ retroScaling(ctx0)
+ ctx0.drawImage @img, 0, 0, width, height, 0, 0, w, height
+ @canvas = e = @document.createElement "canvas"
+ e.width = w
+ e.height = h
+ ctx = e.getContext '2d'
+ retroScaling(ctx)
+ @ctx = ctx.drawImage c0, 0, 0, w, height, 0, 0, w, h
+
+ ParallaxPlane: ParallaxPlane =
+ __proto__: GenericPlane
+ ParallaxPlaneSuper: GenericPlane
+ lower: null
+ x: 0
+ y: 0
+ fx: 0
+ fy: 0
+ logzoom: 2
+ frame: (t,dx,dy)@>
+ { fx, fy, x, y, abslogzoom, w, h, ctx } = @
+ nfx = fx + dx
+ nfy = fy + dy
+ nx = nfx >> abslogzoom
+ ny = nfy >> abslogzoom
+ if nx isnt x
+ if nx >= w
+ nx -= w
+ nfx -= w << abslogzoom
+ else if nx < 0
+ nx += w
+ nfx += w << abslogzoom
+ @x = nx
+ if ny isnt y
+ if ny >= h
+ ny -= h
+ nfy -= h << abslogzoom
+ else if ny < 0
+ ny += h
+ nfy += h << abslogzoom
+ @y = ny
+ @fx = nfx
+ @fy = nfy
+ @lower?.frame t, dx, dy
+ { canvas } = ctx
+ t.drawImage canvas, nx, ny
+ t.drawImage canvas, nx - w, ny
+ t.drawImage canvas, nx, ny - h
+ t.drawImage canvas, nx - w, ny - h
+ init: (options)@>
+ @abslogzoom ?= @logzoom
+ (l = @lower)? then
+ l.logzoom? then l.abslogzoom ?= @logzoom + l.logzoom
+ l.init(options)
+ @ParallaxPlaneSuper.init.call @, options
+ BoundParallaxPlane: BoundParallaxPlane =
+ __proto__: ParallaxPlane
+ BoundParallaxPlaneProto: ParallaxPlane
+ pmul: 1
+ alert: alert
+ init: (options)@>
+ { screenw, screenh } = options
+ @BoundParallaxPlaneProto.init.call @
+ { logzoom, abslogzoom, w, h, pmul } = @
+ @mx = ((w << abslogzoom) * pmul - screenw * 8) >> abslogzoom
+ @my = ((h << abslogzoom) * pmul - screenh * 8) >> abslogzoom
+ # { alert } = @; alert screenw
+ if false
+ @fx = (@x = @mx) << abslogzoom
+ @fy = (@y = @my) << abslogzoom
+ @fx = @fy = 0
+ @mfy = @my << abslogzoom
+ frame: (t, dx, dy)@>
+ { fx, fy, x, y, abslogzoom, w, h, ctx } = @
+ nfx = fx - dx
+ nfy = fy - dy
+ nx = nfx >> abslogzoom
+ ny = nfy >> abslogzoom
+ if nx isnt x
+ { mx } = @
+ if nx >= mx
+ nx = mx
+ nfx = mx << abslogzoom
+ else if nx < 0
+ nx = 0
+ nfx = 0
+ @x = nx
+ if ny isnt y
+ { my } = @
+ if ny >= my
+ ny = my
+ nfy = my << abslogzoom
+ else if ny < 0
+ ny = 0
+ nfy = 0
+ @y = ny
+ @fx = nfx
+ @fy = nfy
+ # @lower?.frame t, dx >> abslogzoom, dy >> abslogzoom
+ { canvas } = ctx
+ # @mny = 100
+ t.drawImage canvas, -nx, -ny
+ # t.drawImage canvas, 0, 0, w, h, -nx, -ny, w*pmul, h*pmul
+
+ SeaFloor: SeaFloor = do->
+ __proto__: BoundParallaxPlane
+ SeaFloorProto: BoundParallaxPlane
+ # terror: CuteluTerror =
+ # img: cuteluterror
+ # zoom: 6
+ # __proto__: ScaledImg
+ # color: "#051555"
+ seafloorImg: seafloor
+ init: (options)@>
+ { seafloorImg } = @
+ # @terror.init(options)
+ w = seafloorImg.width
+ h = seafloorImg.height
+ @w = w
+ @h = h
+ @SeaFloorProto.init.call @, options
+ # { color, w, h } = @
+ # e = @document.createElement "canvas"
+ # e.width = w
+ # e.height = h
+ # @ctx = ctx = e.getContext '2d'
+ { ctx, w, h } = @
+ ctx.drawImage seafloorImg, 0, 0
+ if false
+ ctx.fillStyle = "magenta"
+ ctx.fillRect 0, 0, w, 1
+ ctx.fillRect 0, 0, 1, h
+ ctx.fillRect 0, h - 1, w, 1
+ ctx.fillRect w - 1, 0, 1, h
+
+ SeamlessPlane: SeamlessPlane =
+ withRect: (rx,ry,rw,rh,cb)@>
+ { w, h } = @
+ if (ex = rx + rw) > w
+ if (ey = ry + rh) > h
+ cb rx, ry, w - rx, h - ry, 0, 0
+ cb 0, ry, ex - w, h - ry, w - rx, 0
+ cb rx, 0, w - rx, ey - h, 0, h - ry
+ cb 0, 0, ex - w, ey - h, w - rx, h - ry
+ else
+ cb rx, ry, w - rx, rh, 0, 0
+ cb 0, ry, ex - w, rh, w - rx, 0
+ else
+ if (ey = ry + rh) > h
+ cb rx, ry, rw, h - ry, 0, 0
+ cb rx, 0, rw, ey - h, 0, h - ry
+ else
+ cb rx, ry, rw, rh, 0, 0
+ __proto__: ParallaxPlane
+
+ WaterPlane: WaterPlane = do->
+ waterscapeSuper: waterscapeSuper = SeamlessPlane
+ __proto__: waterscapeSuper
+ random: Math.random
+ sqrt: Math.sqrt
+ colors: [ "cyan", "blue" ]
+ randomStuff: @>
+ { random, sqrt, ctx } = @
+ s = sqrt(15000 / (random() * 50 + 1)) | 0
+ @withRect (random() * @w | 0), (random() * @h | 0), s, s >> 2, (x,y,w,h)->
+ ctx.fillRect x,y,w,h
+ @
+ init: (options)@>
+ { lower, w, h, moltf, colors } = @
+ if lower?
+ lower.w ?= w
+ lower.h ?= h
+ lower.moltf ?= moltf >> lower.logzoom if moltf?
+ @waterscapeSuper.init.call @, options
+ { ctx } = @
+ for k,v of colors
+ ctx.fillStyle = v
+ colors[k] = ctx.fillStyle
+ ctx.globalAlpha = 0.16
+ if true
+ x = 200
+ while x-- > 0
+ @randomStuff()
+ waterscapeSuperFrame: waterscapeSuper.frame
+ frame: (t)@>
+ { ctx, moltf, random } = @
+
+ ctx.fillStyle = @colors[ random() * 1.2 | 0 ]
+ @randomStuff() while moltf-- > 0
+
+ t.save()
+ t.globalAlpha = @alpha
+ @waterscapeSuperFrame.apply @, arguments
+ t.restore()
+ logzoom: 0
+
+ # PinkWaveletPlane: PinkWaveletPlane = do->
+ # waterscapeSuper: waterscapeSuper = SeamlessPlane
+ # __proto__: waterscapeSuper
+ # random: Math.random
+ # sqrt: Math.sqrt
+ # sprites: [ "cyan", "blue" ]
+ # wlets: null
+ # randmix: @>
+ # { random, sqrt, ctx } = @
+ # s = sqrt(15000 / (random() * 100 + 1)) | 0
+ # @withRect (random() * @w | 0), (random() * @h | 0), s, s >> 2, (x,y,w,h)->
+ # ctx.fillRect x,y,w,h
+ # @
+ # init: @>
+ # { lower, w, h, moltf, colors } = @
+ # if lower?
+ # lower.w ?= w
+ # lower.h ?= h
+ # lower.moltf ?= moltf >> lower.logzoom if moltf?
+ # @waterscapeSuper.init.call @
+ # { ctx } = @
+ # for k,v of colors
+ # ctx.fillStyle = v
+ # colors[k] = ctx.fillStyle
+ # ctx.globalAlpha = 0.06
+ # if true
+ # x = 300
+ # while x-- > 0
+ # @randomStuff()
+ # waterscapeSuperFrame: waterscapeSuper.frame
+ # frame: (t)@>
+ # { ctx, moltf, random } = @
+
+ # ctx.fillStyle = @colors[ random() * 1.2 | 0 ]
+ # @randomStuff() while moltf-- > 0
+
+ # { alpha } = @
+ # # t.save()
+ # t.globalAlpha = alpha if alpha?
+ # @waterscapeSuperFrame.apply @, arguments
+ # # t.restore()
+ # logzoom: 0
+ seafloor: seafloorPlane = __proto__: SeaFloor
+ getDepth: @> @seafloor.fy / @seafloor.mfy
+ waterscape: waterscape = do->
+ __proto__: WaterPlane
+ # color: "cyan"
+ # logzoom: 0
+ moltf: 12
+ colors: [ "#051555", "#33ddff" ]
+ alpha: 0.2
+ logzoom: 0
+ lower:
+ # __proto__: ColorPlane
+ # logzoom: 2
+ __proto__: WaterPlane
+ # color: "blue"
+ colors: [ "#000033", "#001155" ]
+ alpha: 0.3
+ # abslogzoom: 2
+ logzoom: 2
+ lower: seafloorPlane
+ bluescape:
+ __proto__: SeamlessPlane
+ bluescapeSuper: SeamlessPlane
+ lower: waterscape
+ logzoom: 0
+ frame: (t,sx,sy)@>
+ { ctx, random, w, h } = @
+
+ x = @x + sx
+ x = (x + w) % w
+ y = (y + h) % h
+ @x = x
+ y = @y + sy
+ y += h while y < 0
+ y -= h while y >= h
+ @y = y
+ # i = ctx.getImageData(0,0,@w,@h)
+
+ ctx.save()
+ @lower.frame ctx, sx, sy
+ ctx.restore()
+ # t.save()
+ # t.globalCompositeOperation = 'copy'
+
+ t.drawImage ctx.canvas, 0,0,w,h, 0,0,w*4,h*4
+
+ # t.drawImage ctx.canvas, 0,0,w>>2,h>>2, 0,0,w*2,h*2
+
+ # t.drawImage ctx.canvas, 0,0,w>>2,h>>2, 0,0,w*2,h>>2
+ # t.drawImage t.canvas, 0,0,w*2,h>>2, 0,0,w*2,h*2
+
+ # t.restore()
+ # @withRect x, y, rx*2, ry*2, (x,y,w,h,ox,oy)-> t.drawImage c, x,y,w,h, ox*2,oy*2,w*2,h*2
+ # t.drawImage c, 0, 0,
+ # t.fillColor = if random() > 0.5 then "#104080" else "#155590"
+ # t.fillRect 0, 0, 100, 100
+ # t.clearRect 0, 0, 100, 100
+ # t.drawImage t, 0, 0, 100, 100, 50, 50, 100, 100
+ init: (options)@>
+ { w, h, lower } = @
+
+ @w = w
+ @h = h
+
+ lower.w = (w >> 2) * 5
+ lower.h = (h >> 2) * 5
+
+ @bluescapeSuper.init.call @, options
+
+ { ctx } = @
+
+ # ctx.fillStyle = "#0099dd"
+ # ctx.fillRect 0, 0, @w, @h
+
+ setup: ->
+ { bluescape, radx, rady } = @
+
+ bluescape.w = radx
+ bluescape.h = rady
+ bluescape.init( { screenw: radx * 2, screenh: rady * 2 } )
+
+ v = new Vilma(@) # jaws.Sprite x:screen_x1*2, y:screen_y1*2, zoom:2, image:pixyvaquita
+ v.px = 0
+ v.py = 0
+ v.vx = 0
+ v.vy = 0
+ @vilma = v
+
+ @encounters.generate(@,-radx, -rady, radx * 2, rady * 2, radx * 2, 0)
+
+ { touchInput } = @
+ touchInput.game = @
+ x = document.body
+ x.addEventListener "touchmove", touchInput.handle('move'), true
+ x.addEventListener "touchstart", touchInput.handle('start'), true
+ tend = touchInput.handle 'end'
+ x.addEventListener "touchend", tend, true
+ x.addEventListener "touchleave", tend, true
+ x.addEventListener "touchcancel", tend, true
+
+ @collisions.setup(radx, rady)
+ radx: screen_x1
+ rady: screen_y1
+ rad: screen_x1 * screen_x1 + screen_y1 * screen_y1
+ collisions:
+ Array: Array
+ setup: (radx, rady)@>
+ # Setup the collision detection subsystem
+ # Assumes:
+ # - radx and rady are multiples of 8
+ w = @w = (radx >> 2)
+ h = @h = (rady >> 2)
+ @b = new @Array(w * h)
+ @o = (w >> 1) * h + (h >> 1) + 1
+ @l = [ ]
+ a: (o)@>
+ # Add a collision subject
+ # Assumes:
+ # - all the corners of the object's collision area are in the viewing area
+ # - the object's collision radius is <= 8
+ { l, b, w } = @
+ i = @o + (o.py >> 3) * @w + (o.px >> 3)
+ @b[i-1] = @b[i+1] = @b[i] = o
+ i -= w
+ @b[i-1] = @b[i+1] = @b[i] = o
+ i += w << 1
+ @b[i-1] = @b[i+1] = @b[i] = o
+ @l.push o
+
+ # o.crad
+ q: (o)@>
+ # Quick collision test
+ # Test collisions of object against previously added collision subjects
+ # For this to work correctly:
+ # - the object should have a collision radius <= 4,
+ # - have a center in the viewing area
+ @b[@o + (o.py >> 3) * @w + (o.px >> 3)]?.collide(o)
+ # t2: (o)@>
+ # Like above but for objects with a collision radius <= 8
+ clear: @>
+ @b = new @Array(@b.length) # Discrete board for detecting collisions
+ @l = [ ] # List of collisions targets
+ draw: @>
+ { jaws, spaceKey, radx, rady, vilma, vaquitas, cameos, stilla, rad, collisions } = @
+
+ @addVaquita() if (!(@gameloop.ticks & 0x7f) and vaquitas.length < 1) or jaws.pressed[spaceKey]
+
+ vilma.fpx += vilma.px
+ vilma.fpy += vilma.py
+ vilma.move()
+
+ if true
+ { px, py, fpx, fpy } = vilma
+
+ vilma.fpx -= px
+ vilma.fpy -= py
+ vilma.px = 0
+ vilma.py = 0
+
+ px = px | 0
+ py = py | 0
+
+ @bluescape.frame jaws.context, -fpx, -fpy
+ else
+ { px, py } = vilma
+
+ vilma.fpx = 0
+ vilma.fpy = 0
+ vilma.px = 0
+ vilma.py = 0
+
+ px = px | 0
+ py = py | 0
+
+ @bluescape.frame jaws.context, -px, -py
+
+ collisions.a vilma
+
+ for v in vaquitas
+ x = v.px -= px
+ y = v.py -= py
+ v.draw()
+ if (x >= -radx) and (x < radx) and (y >= -rady) and (y < rady)
+ collisions.a v
+
+ vilma.draw()
+
+ if stilla?
+ x = stilla.px -= px
+ y = stilla.py -= py
+ if stilla.dead or x * x + y * y > rad * 16
+ stilla.goodnight(@)
+ @stilla = null
+ else
+ stilla.draw(collisions, @)
+ if (x >= -radx) and (x < radx) and (y >= -rady) and (y < rady)
+ collisions.a stilla
+
+ for k,v of cameos
+ continue unless v?
+ x = v.px -= px
+ y = v.py -= py
+ if v.dead or (x < -radx) or (x >= radx) or (y < -rady) or (y >= rady)
+ cameos[k] = null
+ else
+ v.draw(collisions, @)
+ collisions.q v
+
+ @encounters.generate(@,-radx, -rady, radx * 2, rady * 2, px, py)
+
+ collisions.clear()
+
+ if (@gameloop.ticks & 0xff) is 0xff
+ fps.innerHTML = "#{@gameloop.fps} fps"
+
+ jaws: jaws
+ spaceKey: spaceKey
+ if true
+ jaws.init()
+ jaws.setupInput();
+ window.game = game = new Demo
+ gameloop = new jaws.GameLoop(game, { fps:24 })
+ (game.gameloop = gameloop).start()
+ else
+ jaws.start Demo, fps:25
+
+ # gameFrame = -> reportErrors ->
+ # if (time & 0xff) is 0x00 and vaquitas.length < 4
+ # addVaquita()
+ # # s += 0.001
+ # x -= vx = pressedKeys[leftKey] - pressedKeys[rightKey]
+ # y -= pressedKeys[upKey] - pressedKeys[downKey]
+ # if vx > 0
+ # zoomX = 1
+ # else if vx < 0
+ # zoomX = -1
+ # v.setAttribute("transform", "translate(#{x}, #{y}) zoom(#{zoomX}, #{zoomY})")
+ # # transform = v.transform.baseVal.getItem(0)
+ # # transformMatrix.a = zoomX
+ # # transformMatrix.e = x
+ # # transformMatrix.f = y
+ # if (time % 3) is 0
+ # if currentFrame.baseVal is "#twistleft"
+ # currentFrame .baseVal = "#_"
+ # else if vx isnt 0
+ # currentFrame.baseVal = "#twistleft"
+ # # transformList.initialize(transform)
+ # vq.update() for vq in vaquitas
+ # time++
+
+ # # setInterval gameFrame, 40
+ # window.location.reload(true)
+ window.addEventListener('load', ((e)->
+ if (window.applicationCache)
+ window.applicationCache.addEventListener('updateready', ((e)->
+ # if (window.applicationCache.status == window.applicationCache.UPDATEREADY)
+ # Browser downloaded a new app cache.
+ # Swap it in and reload the page to get the new hotness.
+ window.applicationCache.swapCache()
+ if (confirm('A new version of this site is available. Load it?'))
+ window.location.reload()
+ # else
+ # Manifest didn't changed. Nothing new to server.
+ ), false)
+ ), false)
+
+genPage()