# 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.249" { 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 - Sunken Moon" 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 "\n" htmlcup.html lang:"en", manifest:"SunkenMoon.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()