function drawTriangle(x, y, h, w) { var ctx = Game.context; ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(x - w, y - h/2); ctx.lineTo(x - w, y + h/2); ctx.closePath(); ctx.fill(); } function drawCircle(point, r, w, color) { var ctx = Game.context; ctx.beginPath(); ctx.arc(point.x, point.y, r, 0, 2*Math.PI, false); ctx.closePath(); ctx.lineWidth = w; ctx.strokeStyle = color; ctx.stroke(); } function drawBG() { var ctx = Game.context; ctx.fillStyle = 'black'; ctx.fillRect(0, 0, Game.canvas.width, Game.canvas.height); } function clamp(n, low, high) { return Math.max(Math.min(n, high), low); } function sign(x) { return x > 0 ? 1 : x < 0 ? -1 : 0; } function lerp(from, to, p) { return to * p + from * (1 - p); } function ease(v) { return v * v * (3 - 2 * v); } function easeLinear(t, b, c, d) { return lerp(b, c, t/d); } function easeOutExpo(t, b, c, d) { return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; } function color(r,g,b,a) { return 'rgba('+r+','+g+','+b+','+a.toFixed(5)+')'; } function font(size) { return size+'px Quicksand, Futura, Arial, sans-serif'; } function makeObservable(o) { var listeners = []; o.hasTicker = true; o.addListener = function(listener) { listeners.push(listener); }; o.removeListener = function(listener) { var index = listeners.indexOf(listener); listeners.splice(index, 1); }; o.notify = function(dt) { listeners.forEach(function(listener) { listener(dt); }) }; } // Ticker var Ticker = {}; (function(Ticker) { makeObservable(Ticker); var nextFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { setTimeout(function () { callback(Date.now()) }, 10) }; var then = null; function tick(now) { var delta = (now - then) / 1000; Ticker.notify(delta); then = now; nextFrame(tick); } function init(now) { then = now; nextFrame(tick); } Ticker.start = function() { nextFrame(init); } })(Ticker); // Tweens function BaseTween() {} BaseTween.prototype.animate = function(scene) { var self = this; function tick(dt) { self.update(dt); } var ticker = Ticker; if(this.ticker) ticker = this.ticker; ticker.addListener(tick); self.promise.then(function() { ticker.removeListener(tick); }); self.update(0); return self; }; function SimpleTween(duration, start, end, easing) { this.duration = duration; this.start = start; this.end = end; this.easing = easing || easeLinear; this.deferred = Q.defer(); this.timer = 0; this.promise = this.deferred.promise; if(this.duration == 0) { this.deferred.resolve(); } }; SimpleTween.prototype = new BaseTween(); SimpleTween.prototype.update = function(dt) { if(this.duration > 0) { this.timer += dt; if(this.timer >= this.duration) { this.timer = this.duration; this.deferred.resolve(); } this.value = this.easing(this.timer, this.start, this.end, this.duration); } else { this.value = this.end; } }; function PromiseTween(value, promise) { this.value = value; this.promise = promise; } PromiseTween.prototype.update = function() {}; function ComposedTween(tweens) { this.tweens = tweens; this.promise = Q.all(this.tweens.map(function(t) { return t.promise })); this.switch(0); } ComposedTween.prototype = new BaseTween(); ComposedTween.prototype.switch = function(index) { if(index >= this.tweens.length) return; var self = this; this.current = index; this.tweens[index].promise.then(function() { self.switch(index + 1); }); }; ComposedTween.prototype.update = function(dt) { if(this.current >= this.tweens.length) return; var active = this.tweens[this.current]; active.update(dt); this.value = active.value; }; function DeferredTween(start) { this.lastTarget = start; this.tweens = []; } DeferredTween.prototype = new BaseTween(); DeferredTween.prototype.addTween = function(tween) { this.tweens.push(tween); this.tween = new ComposedTween(this.tweens); this.promise = this.tween.promise; return this; } DeferredTween.prototype.to = function(target, duration, easing) { easing = easing || easeLinear; this.addTween(new SimpleTween(duration, this.lastTarget, target, easing)); this.lastTarget = target; return this; }; DeferredTween.prototype.wait = function(duration) { return this.to(this.lastTarget, duration); }; DeferredTween.prototype.waitFor = function(promise) { return this.addTween(new PromiseTween(this.lastTarget, promise)); }; DeferredTween.prototype.update = function(dt) { this.tween.update(dt); this.value = this.tween.value; }; function tweenFrom(start) { return new DeferredTween(start); }; // Entities function Beater() {}; (function() { var RADIUS = 8; var RIPPLE_SIZE = 10; var RIPPLE_SPEED = 10; Beater.prototype.init = function(x, y, color) { this.baseColor = color; this.rippleCounter = 0; this.position = {x: x, y: y}; }; Beater.prototype.update = function(dt) { this.rippleCounter += RIPPLE_SPEED * dt; if(this.rippleCounter > RIPPLE_SIZE) { this.rippleCounter = 0; } }; Beater.prototype.draw = function() { var ctx = Game.context; ctx.fillStyle = this.baseColor; drawCircle(this.position, RADIUS, 3, this.baseColor); drawCircle(this.position, 2, 3, this.baseColor); // ripples var p = lerp(1, 0, ease(this.rippleCounter/RIPPLE_SIZE)); ctx.globalAlpha = p; drawCircle(this.position, RADIUS+7+this.rippleCounter, 1, this.baseColor); ctx.globalAlpha = p*0.8; drawCircle(this.position, RADIUS+4+this.rippleCounter, 1, this.baseColor); ctx.globalAlpha = p*0.5; drawCircle(this.position, RADIUS+1+this.rippleCounter, 1, this.baseColor); ctx.globalAlpha = 1; }; })();