;(function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0].call(u.exports,function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s 0 && this._events[type].length > m) { this._events[type].warned = true; console.error('(node) warning: possible EventEmitter memory ' + 'leak detected. %d listeners added. ' + 'Use emitter.setMaxListeners() to increase limit.', this._events[type].length); console.trace(); } } // If we've already got an array, just append. this._events[type].push(listener); } else { // Adding the second element, need to change to array. this._events[type] = [this._events[type], listener]; } return this; }; EventEmitter.prototype.on = EventEmitter.prototype.addListener; EventEmitter.prototype.once = function(type, listener) { var self = this; self.on(type, function g() { self.removeListener(type, g); listener.apply(this, arguments); }); return this; }; EventEmitter.prototype.removeListener = function(type, listener) { if ('function' !== typeof listener) { throw new Error('removeListener only takes instances of Function'); } // does not use listeners(), so no side effect of creating _events[type] if (!this._events || !this._events[type]) return this; var list = this._events[type]; if (isArray(list)) { var i = indexOf(list, listener); if (i < 0) return this; list.splice(i, 1); if (list.length == 0) delete this._events[type]; } else if (this._events[type] === listener) { delete this._events[type]; } return this; }; EventEmitter.prototype.removeAllListeners = function(type) { if (arguments.length === 0) { this._events = {}; return this; } // does not use listeners(), so no side effect of creating _events[type] if (type && this._events && this._events[type]) this._events[type] = null; return this; }; EventEmitter.prototype.listeners = function(type) { if (!this._events) this._events = {}; if (!this._events[type]) this._events[type] = []; if (!isArray(this._events[type])) { this._events[type] = [this._events[type]]; } return this._events[type]; }; EventEmitter.listenerCount = function(emitter, type) { var ret; if (!emitter._events || !emitter._events[type]) ret = 0; else if (typeof emitter._events[type] === 'function') ret = 1; else ret = emitter._events[type].length; return ret; }; },{"__browserify_process":3}],2:[function(require,module,exports){ var events = require('events'); exports.isArray = isArray; exports.isDate = function(obj){return Object.prototype.toString.call(obj) === '[object Date]'}; exports.isRegExp = function(obj){return Object.prototype.toString.call(obj) === '[object RegExp]'}; exports.print = function () {}; exports.puts = function () {}; exports.debug = function() {}; exports.inspect = function(obj, showHidden, depth, colors) { var seen = []; var stylize = function(str, styleType) { // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics var styles = { 'bold' : [1, 22], 'italic' : [3, 23], 'underline' : [4, 24], 'inverse' : [7, 27], 'white' : [37, 39], 'grey' : [90, 39], 'black' : [30, 39], 'blue' : [34, 39], 'cyan' : [36, 39], 'green' : [32, 39], 'magenta' : [35, 39], 'red' : [31, 39], 'yellow' : [33, 39] }; var style = { 'special': 'cyan', 'number': 'blue', 'boolean': 'yellow', 'undefined': 'grey', 'null': 'bold', 'string': 'green', 'date': 'magenta', // "name": intentionally not styling 'regexp': 'red' }[styleType]; if (style) { return '\u001b[' + styles[style][0] + 'm' + str + '\u001b[' + styles[style][1] + 'm'; } else { return str; } }; if (! colors) { stylize = function(str, styleType) { return str; }; } function format(value, recurseTimes) { // Provide a hook for user-specified inspect functions. // Check that value is an object with an inspect function on it if (value && typeof value.inspect === 'function' && // Filter out the util module, it's inspect function is special value !== exports && // Also filter out any prototype objects using the circular check. !(value.constructor && value.constructor.prototype === value)) { return value.inspect(recurseTimes); } // Primitive types cannot have properties switch (typeof value) { case 'undefined': return stylize('undefined', 'undefined'); case 'string': var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '') .replace(/'/g, "\\'") .replace(/\\"/g, '"') + '\''; return stylize(simple, 'string'); case 'number': return stylize('' + value, 'number'); case 'boolean': return stylize('' + value, 'boolean'); } // For some reason typeof null is "object", so special case here. if (value === null) { return stylize('null', 'null'); } // Look up the keys of the object. var visible_keys = Object_keys(value); var keys = showHidden ? Object_getOwnPropertyNames(value) : visible_keys; // Functions without properties can be shortcutted. if (typeof value === 'function' && keys.length === 0) { if (isRegExp(value)) { return stylize('' + value, 'regexp'); } else { var name = value.name ? ': ' + value.name : ''; return stylize('[Function' + name + ']', 'special'); } } // Dates without properties can be shortcutted if (isDate(value) && keys.length === 0) { return stylize(value.toUTCString(), 'date'); } var base, type, braces; // Determine the object type if (isArray(value)) { type = 'Array'; braces = ['[', ']']; } else { type = 'Object'; braces = ['{', '}']; } // Make functions say that they are functions if (typeof value === 'function') { var n = value.name ? ': ' + value.name : ''; base = (isRegExp(value)) ? ' ' + value : ' [Function' + n + ']'; } else { base = ''; } // Make dates with properties first say the date if (isDate(value)) { base = ' ' + value.toUTCString(); } if (keys.length === 0) { return braces[0] + base + braces[1]; } if (recurseTimes < 0) { if (isRegExp(value)) { return stylize('' + value, 'regexp'); } else { return stylize('[Object]', 'special'); } } seen.push(value); var output = keys.map(function(key) { var name, str; if (value.__lookupGetter__) { if (value.__lookupGetter__(key)) { if (value.__lookupSetter__(key)) { str = stylize('[Getter/Setter]', 'special'); } else { str = stylize('[Getter]', 'special'); } } else { if (value.__lookupSetter__(key)) { str = stylize('[Setter]', 'special'); } } } if (visible_keys.indexOf(key) < 0) { name = '[' + key + ']'; } if (!str) { if (seen.indexOf(value[key]) < 0) { if (recurseTimes === null) { str = format(value[key]); } else { str = format(value[key], recurseTimes - 1); } if (str.indexOf('\n') > -1) { if (isArray(value)) { str = str.split('\n').map(function(line) { return ' ' + line; }).join('\n').substr(2); } else { str = '\n' + str.split('\n').map(function(line) { return ' ' + line; }).join('\n'); } } } else { str = stylize('[Circular]', 'special'); } } if (typeof name === 'undefined') { if (type === 'Array' && key.match(/^\d+$/)) { return str; } name = JSON.stringify('' + key); if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) { name = name.substr(1, name.length - 2); name = stylize(name, 'name'); } else { name = name.replace(/'/g, "\\'") .replace(/\\"/g, '"') .replace(/(^"|"$)/g, "'"); name = stylize(name, 'string'); } } return name + ': ' + str; }); seen.pop(); var numLinesEst = 0; var length = output.reduce(function(prev, cur) { numLinesEst++; if (cur.indexOf('\n') >= 0) numLinesEst++; return prev + cur.length + 1; }, 0); if (length > 50) { output = braces[0] + (base === '' ? '' : base + '\n ') + ' ' + output.join(',\n ') + ' ' + braces[1]; } else { output = braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1]; } return output; } return format(obj, (typeof depth === 'undefined' ? 2 : depth)); }; function isArray(ar) { return Array.isArray(ar) || (typeof ar === 'object' && Object.prototype.toString.call(ar) === '[object Array]'); } function isRegExp(re) { typeof re === 'object' && Object.prototype.toString.call(re) === '[object RegExp]'; } function isDate(d) { return typeof d === 'object' && Object.prototype.toString.call(d) === '[object Date]'; } function pad(n) { return n < 10 ? '0' + n.toString(10) : n.toString(10); } var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; // 26 Feb 16:19:34 function timestamp() { var d = new Date(); var time = [pad(d.getHours()), pad(d.getMinutes()), pad(d.getSeconds())].join(':'); return [d.getDate(), months[d.getMonth()], time].join(' '); } exports.log = function (msg) {}; exports.pump = null; var Object_keys = Object.keys || function (obj) { var res = []; for (var key in obj) res.push(key); return res; }; var Object_getOwnPropertyNames = Object.getOwnPropertyNames || function (obj) { var res = []; for (var key in obj) { if (Object.hasOwnProperty.call(obj, key)) res.push(key); } return res; }; var Object_create = Object.create || function (prototype, properties) { // from es5-shim var object; if (prototype === null) { object = { '__proto__' : null }; } else { if (typeof prototype !== 'object') { throw new TypeError( 'typeof prototype[' + (typeof prototype) + '] != \'object\'' ); } var Type = function () {}; Type.prototype = prototype; object = new Type(); object.__proto__ = prototype; } if (typeof properties !== 'undefined' && Object.defineProperties) { Object.defineProperties(object, properties); } return object; }; exports.inherits = function(ctor, superCtor) { ctor.super_ = superCtor; ctor.prototype = Object_create(superCtor.prototype, { constructor: { value: ctor, enumerable: false, writable: true, configurable: true } }); }; var formatRegExp = /%[sdj%]/g; exports.format = function(f) { if (typeof f !== 'string') { var objects = []; for (var i = 0; i < arguments.length; i++) { objects.push(exports.inspect(arguments[i])); } return objects.join(' '); } var i = 1; var args = arguments; var len = args.length; var str = String(f).replace(formatRegExp, function(x) { if (x === '%%') return '%'; if (i >= len) return x; switch (x) { case '%s': return String(args[i++]); case '%d': return Number(args[i++]); case '%j': return JSON.stringify(args[i++]); default: return x; } }); for(var x = args[i]; i < len; x = args[++i]){ if (x === null || typeof x !== 'object') { str += ' ' + x; } else { str += ' ' + exports.inspect(x); } } return str; }; },{"events":1}],3:[function(require,module,exports){ // shim for using process in browser var process = module.exports = {}; process.nextTick = (function () { var canSetImmediate = typeof window !== 'undefined' && window.setImmediate; var canPost = typeof window !== 'undefined' && window.postMessage && window.addEventListener ; if (canSetImmediate) { return function (f) { return window.setImmediate(f) }; } if (canPost) { var queue = []; window.addEventListener('message', function (ev) { if (ev.source === window && ev.data === 'process-tick') { ev.stopPropagation(); if (queue.length > 0) { var fn = queue.shift(); fn(); } } }, true); return function nextTick(fn) { queue.push(fn); window.postMessage('process-tick', '*'); }; } return function nextTick(fn) { setTimeout(fn, 0); }; })(); process.title = 'browser'; process.browser = true; process.env = {}; process.argv = []; process.binding = function (name) { throw new Error('process.binding is not supported'); } // TODO(shtylman) process.cwd = function () { return '/' }; process.chdir = function (dir) { throw new Error('process.chdir is not supported'); }; },{}],4:[function(require,module,exports){ var resources = require('./lib/resources'); module.exports = { vec2d: require('vec2d'), button: require('./lib/button'), resources: resources, Engine: require('./lib/engine'), Sound: require('./lib/sound'), Sprite: require('./lib/sprite'), Label: require('./lib/label'), Batch: require('./lib/batch'), Animation: require('./lib/animation'), }; },{"./lib/animation":5,"./lib/batch":6,"./lib/button":7,"./lib/engine":8,"./lib/label":9,"./lib/resources":10,"./lib/sound":11,"./lib/sprite":12,"vec2d":14}],5:[function(require,module,exports){ var vec2d = require('vec2d'); module.exports = Animation; function Animation() { this.delay = 0.1; this.loop = true; this.spritesheet = null; this.duration = 0; this.anchor = vec2d(0, 0); this.frames = []; } Animation.fromImage = function(image, o) { o = o || {}; var anim = new Animation(); anim.spritesheet = image; anim.anchor = vec2d(o.anchor); anim.loop = false; anim.addFrame(vec2d(0, 0), vec2d(image.width, image.height)); return anim; }; Animation.fromJson = function(o) { var anim = new Animation(); anim.delay = o.delay; anim.spritesheet = o.spritesheet; anim.anchor = vec2d(o.anchor); anim.loop = !!o.loop; anim.frames = []; var frames = o.frames || []; for (var i = 0; i < frames.length; ++i) { var frame = frames[i]; anim.addFrame(vec2d(frame.pos), vec2d(frame.size)); } return anim; }; Animation.prototype.addFrame = function(pos, size) { this.frames.push(new Frame(pos, size)); this.calcDuration(); }; Animation.prototype.removeFrame = function(index) { this.frames.splice(index, 1); this.calcDuration(); }; Animation.prototype.clearFrames = function() { this.frames = []; this.calcDuration(); }; Animation.prototype.calcDuration = function() { this.duration = this.delay * this.frames.length; } Animation.prototype.setDelay = function(delay) { this.delay = delay; this.calcDuration(); }; // extract an image from the spritesheet Animation.prototype.getImage = function(frameIndex) { if (frameIndex == null) frameIndex = 0; var buffer = document.createElement('canvas'); var frame = this.frames[frameIndex]; buffer.width = frame.size.x; buffer.height = frame.size.y; var context = buffer.getContext('2d'); context.drawImage(this.spritesheet, frame.pos.x, frame.pos.y, frame.size.x, frame.size.y, 0, 0, frame.size.x, frame.size.y); return buffer; }; function Frame(pos, size) { this.pos = pos; this.size = size; } },{"vec2d":14}],6:[function(require,module,exports){ module.exports = Batch; function Batch() { // indexed by zOrder this.layers = []; } Batch.prototype.add = function(item) { if (item.batch) item.batch.remove(item); item.batch = this; if (item.visible) { var layer = this.layers[item.zOrder]; if (! layer) layer = this.layers[item.zOrder] = []; layer.push(item); } }; Batch.prototype.remove = function(item) { var layer = this.layers[item.zOrder]; if (!layer) return; var index = layer.indexOf(item); if (index >= 0) layer.splice(index, 1); }; Batch.prototype.draw = function(context) { for (var i = 0; i < this.layers.length; ++i) { var layer = this.layers[i]; if (!layer) continue; for (var spriteIndex = 0; spriteIndex < layer.length; spriteIndex += 1) { layer[spriteIndex].draw(context); } } }; Batch.prototype.clear = function(context) { for (var i = 0; i < this.layers.length; ++i) { var layer = this.layers[i]; if (!layer) continue; for (var spriteIndex = 0; spriteIndex < layer.length; spriteIndex += 1) { var sprite = layer[spriteIndex]; sprite.batch = null; sprite['delete'](); } } this.layers = []; }; },{}],7:[function(require,module,exports){ var KEY_OFFSET = 0; var MOUSE_OFFSET = 256; exports.KEY_OFFSET = KEY_OFFSET; exports.MOUSE_OFFSET = MOUSE_OFFSET; var Key = { Backspace: 8, Tab: 9, Enter: 13, Shift: 16, Ctrl: 17, Alt: 18, Pause: 19, Break: 19, CapsLock: 20, Escape: 27, Space: 32, PageUp: 33, PageDown: 34, End: 35, Home: 36, Left: 37, Up: 38, Right: 39, Down: 40, Insert: 45, Delete: 46, 0: 48, 1: 49, 2: 50, 3: 51, 4: 52, 5: 53, 6: 54, 7: 55, 8: 56, 9: 57, A: 65, B: 66, C: 67, D: 68, E: 69, F: 70, G: 71, H: 72, I: 73, J: 74, K: 75, L: 76, M: 77, N: 78, O: 79, P: 80, Q: 81, R: 82, S: 83, T: 84, U: 85, V: 86, W: 87, X: 88, Y: 89, Z: 90, MetaLeft: 91, MetaRight: 92, Select: 93, Numpad0: 96, Numpad1: 97, Numpad2: 98, Numpad3: 99, Numpad4: 100, Numpad5: 101, Numpad6: 102, Numpad7: 103, Numpad8: 104, Numpad9: 105, Multiply: 106, Add: 107, Subtract: 109, Decimal: 110, Divide: 111, F1: 112, F2: 113, F3: 114, F4: 115, F5: 116, F6: 117, F7: 118, F8: 119, F9: 120, F10: 121, F11: 122, F12: 123, NumLock: 144, ScrollLock: 145, Semicolon: 186, EqualSign: 187, Comma: 188, Dash: 189, Period: 190, SlashForward: 191, Grave: 192, BracketOpen: 219, SlashBack: 220, BracketClose: 221, Quote: 222 }; var Mouse = { Left: 1, Middle: 2, Right: 3 }; // map both Key and Mouse into Button var btnName, val; for (btnName in Key) { val = Key[btnName]; exports["Key" + btnName] = KEY_OFFSET + val; } for (btnName in Mouse) { val = Mouse[btnName]; exports["Mouse" + btnName] = MOUSE_OFFSET + val; } },{}],8:[function(require,module,exports){ var global=self;var Vec2d = require('vec2d').Vec2d; var resources = require('./resources'); var util = require('util'); var EventEmitter = require('events').EventEmitter; var button = require('./button'); var Label = require('./label'); var MOUSE_OFFSET = button.MOUSE_OFFSET; var KEY_OFFSET = button.KEY_OFFSET; var EPSILON = 0.00000001; var MAX_DISPLAY_FPS = 90000; module.exports = Engine; var targetFps = 60; var targetSpf = 1 / targetFps; var fpsSmoothness = 0.9; var fpsOneFrameWeight = 1.0 - fpsSmoothness; var requestAnimationFrame = global.requestAnimationFrame || global.webkitRequestAnimationFrame || global.mozRequestAnimationFrame || global.oRequestAnimationFrame || global.msRequestAnimationFrame || fallbackRequestAnimationFrame; util.inherits(Engine, EventEmitter); function Engine(canvas) { EventEmitter.call(this); this.canvas = canvas; this.listeners = []; // add tabindex property to canvas so that it can receive keyboard input this.canvas.tabIndex = 0; this.context = this.canvas.getContext("2d"); this.size = new Vec2d(this.canvas.width, this.canvas.height); this.fps = targetFps; this.setMinFps(20); this.buttonCaptureExceptions = {}; } Engine.prototype.setSize = function(size) { this.size = size; this.canvas.width = this.size.x; this.canvas.height = this.size.y; }; Engine.prototype.setMinFps = function(it){ this.maxSpf = 1 / it; }; Engine.prototype.start = function(){ attachListeners(this); startMainLoop(this); }; Engine.prototype.stop = function(){ stopMainLoop(this); removeListeners(this); }; Engine.prototype.buttonState = function(button){ return !!this.buttonStates[button]; }; Engine.prototype.buttonJustPressed = function(button){ return !!this.btnJustPressed[button]; }; Engine.prototype.buttonJustReleased = function(button){ return !!this.btnJustReleased[button]; }; Engine.prototype.showLoadProgressBar = function() { showLoadProgressBar(this); }; Engine.prototype.createFpsLabel = function() { var label = new Label(this.fps, { font: "14px sans-serif", fillStyle: "#ffffff", textAlign: 'left', pos: new Vec2d(0, this.size.y), }); this.on('update', function() { label.text = Math.round(this.fps); }); return label; }; function startMainLoop(self) { var previousUpdate = new Date(); self.mainLoopOn = true; requestAnimationFrame(mainLoop, self.canvas); function mainLoop(){ var now = new Date(); var delta = (now - previousUpdate) / 1000; previousUpdate = now; // make sure dt is never zero // if FPS is too low, lag instead of causing physics glitches var dt = delta; if (dt < EPSILON) dt = EPSILON; if (dt > self.maxSpf) dt = self.maxSpf; var multiplier = dt / targetSpf; self.emit('update', dt, multiplier); self.btnJustPressed = {}; self.btnJustReleased = {}; self.emit('draw', self.context); var fps = 1 / delta; fps = fps < MAX_DISPLAY_FPS ? fps : MAX_DISPLAY_FPS; self.fps = self.fps * fpsSmoothness + fps * fpsOneFrameWeight; if (self.mainLoopOn) { requestAnimationFrame(mainLoop, self.canvas); } } } function attachListeners(self) { self.buttonStates = {}; self.btnJustPressed = {}; self.btnJustReleased = {}; // disable right click context menu addListener(self.canvas, 'contextmenu', function(event){ if (self.buttonCaptureExceptions[button.MouseRight]) return true; event.preventDefault(); }); // mouse input self.mousePos = new Vec2d(0, 0); addListener(self.canvas, 'mousemove', onMouseMove); addListener(self.canvas, 'mousedown', function(event){ var buttonId = MOUSE_OFFSET + event.which; self.buttonStates[buttonId] = true; self.btnJustPressed[buttonId] = true; self.emit('buttondown', buttonId); self.canvas.focus(); window.addEventListener('mouseup', onMouseUp, false); window.addEventListener('mousemove', onMouseMove, false); return bubbleEvent(self, event); }); function onMouseUp(event) { var buttonId = MOUSE_OFFSET + event.which; self.buttonStates[buttonId] = false; self.btnJustReleased[buttonId] = true; self.emit('buttonup', buttonId); window.removeEventListener('mouseup', onMouseUp, false); window.removeEventListener('mousemove', onMouseMove, false); return bubbleEvent(self, event); } function onMouseMove(event) { self.mousePos = new Vec2d( event.pageX - self.canvas.offsetLeft, event.pageY - self.canvas.offsetTop); self.emit('mousemove', self.mousePos, MOUSE_OFFSET + event.which); } // keyboard input addListener(self.canvas, 'keydown', function(event){ var buttonId = KEY_OFFSET + event.which; self.btnJustPressed[buttonId] = !self.buttonStates[buttonId]; self.buttonStates[buttonId] = true; self.emit('buttondown', buttonId); return bubbleEvent(self, event); }); addListener(window, 'keyup', function(event){ var buttonId = KEY_OFFSET + event.which; self.btnJustReleased[buttonId] = self.buttonStates[buttonId]; self.buttonStates[buttonId] = false; self.emit('buttonup', buttonId); return bubbleEvent(self, event); }); function addListener(element, eventName, listener){ self.listeners.push([element, eventName, listener]); element.addEventListener(eventName, listener, false); } } function bubbleEvent(self, event) { // we need to figure out whether to bubble this key event up. // if the button is an exception, bubble it up. // also if any other exceptions are pressed, bubble it up. // this allows ctrl+(anything) to work. var buttonId = KEY_OFFSET + event.which; if (self.buttonCaptureExceptions[buttonId] || (event.ctrlKey && self.buttonCaptureExceptions[button.KeyCtrl]) || (event.altKey && self.buttonCaptureExceptions[button.KeyAlt]) || (event.shiftKey && self.buttonCaptureExceptions[button.KeyShift])) { return true; } else { event.preventDefault(); return false; } } function removeListeners(self) { self.listeners.forEach(function(listener) { var element = listener[0]; var eventName = listener[1]; var fn = listener[2]; element.removeEventListener(eventName, fn, false); }); self.listeners = []; } function stopMainLoop(self) { self.mainLoopOn = false; } function fallbackRequestAnimationFrame(cb){ setTimeout(cb, targetSpf * 1000); } function showLoadProgressBar(self) { resources.on('progress', onProgress); resources.on('ready', onReady); self.on('draw', onDraw); var percent = 0; function onProgress(e) { percent = e.total === 0 ? 1 : e.complete / e.total; } function onReady() { resources.removeListener('progress', onProgress); resources.removeListener('ready', onReady); self.removeListener('draw', onDraw); } function onDraw(context) { context.save(); context.setTransform(1, 0, 0, 1, 0, 0); // identity // clear to black context.fillStyle = "#000000"; context.fillRect(0, 0, self.size.x, self.size.y); // draw a progress bar var barRadius = Math.floor(self.size.y / 20); var centerY = Math.floor(self.size.y / 2); var margin = 5; // outline context.strokeStyle = "#ffffff"; context.lineWidth = 2; context.strokeRect(margin, centerY - barRadius, self.size.x - margin * 2, barRadius * 2); // inside context.fillStyle = "#ffffff"; var width = percent * (self.size.x - margin * 4); context.fillRect(margin * 2, centerY - barRadius + margin, width, barRadius * 2 - margin * 2); // text context.font = "18px sans-serif"; context.fillStyle = "#000000"; context.textAlign = "center"; context.textBaseline = "middle"; context.fillText("Loading... " + Math.floor(percent * 100) + "%", margin * 2 + Math.floor(width / 2), centerY); context.restore(); } } },{"./button":7,"./label":9,"./resources":10,"events":1,"util":2,"vec2d":14}],9:[function(require,module,exports){ var Vec2d = require('vec2d').Vec2d; module.exports = Label; function Label(text, params) { this.text = text; params = params || {}; this.pos = params.pos == null ? new Vec2d(0, 0) : params.pos; this.scale = params.scale == null ? new Vec2d(1, 1) : params.scale; this.zOrder = params.zOrder == null ? 0 : params.zOrder; this.batch = params.batch; this.rotation = params.rotation == null ? 0 : params.rotation; this.alpha = params.alpha == null ? 1 : params.alpha; this.font = params.font == null ? "10px sans-serif" : params.font; this.textAlign = params.textAlign == null ? "start" : params.textAlign; this.textBaseline= params.textBaseline == null ? "alphabetic" : params.textBaseline; this.fill = params.fill == null ? true : params.fill; this.fillStyle = params.fillStyle == null ? "#000000" : params.fillStyle; this.stroke = !!params.stroke; this.lineWidth = params.lineWidth == null ? 1 : params.lineWidth; this.strokeStyle = params.strokeStyle == null ? "#000000" : params.strokeStyle; this.setVisible(params.visible == null ? true : params.visible); } Label.prototype.draw = function(context) { context.save(); context.font = this.font; context.textAlign = this.textAlign; context.textBaseline = this.textBaseline; context.translate(this.pos.x, this.pos.y); context.scale(this.scale.x, this.scale.y); context.rotate(this.rotation); context.globalAlpha = this.alpha; if (this.fill) { context.fillStyle = this.fillStyle; context.fillText(this.text, 0, 0); } if (this.stroke) { context.strokeStyle = this.strokeStyle; context.lineWidth = this.lineWidth; context.strokeText(this.text, 0, 0); } context.restore(); }; Label.prototype.setVisible = function(visible){ this.visible = visible; if (this.batch == null) { return; } if (this.visible) { this.batch.add(this); } else { this.batch.remove(this); } }; Label.prototype.setZOrder = function(zOrder){ if (this.batch != null) { this.batch.remove(this); this.zOrder = zOrder; this.batch.add(this); } else { this.zOrder = zOrder; } }; Label.prototype['delete'] = function() { if (this.batch) this.batch.remove(this); this.batch = null; }; },{"vec2d":14}],10:[function(require,module,exports){ var vec2d = require('vec2d'); var Batch = require('batch2'); var util = require('util'); var EventEmitter = require('events').EventEmitter; var Animation = require('./animation'); // exports at bottom because we need to set up this class before // creating an instance util.inherits(ResourceLoader, EventEmitter); function ResourceLoader() { EventEmitter.call(this); this.ready = false; this.text = {}; this.images = {}; this.animations = {}; this.spritesheet = null; this.useSpritesheet = true; } ResourceLoader.prototype.bootstrap = function() { bootstrap(this); }; // fetch a resource from the server ResourceLoader.prototype.fetchTextFile = fetchTextFile; ResourceLoader.prototype.fetchImage = fetchImage; function bootstrap(self) { var batch = new Batch(); if (self.useSpritesheet) { batch.push(loadSpritesheet); batch.push(loadAnimationsJson); } var name; for (name in self.text) { batch.push(generateLoadText(name)); } for (name in self.images) { batch.push(generateLoadImage(name)); } batch.on('progress', function(e) { self.emit('progress', e); }); // allow the event loop to process one time // so that the user can get their on('ready') hook in setTimeout(executeBatch); function executeBatch() { batch.end(function(err) { if (err) { self.emit('error', err); return; } for (var name in self.animations) { self.animations[name].spritesheet = self.spritesheet; } self.ready = true; self.emit('ready'); }); } function loadAnimationsJson(cb) { fetchImage("spritesheet.png", function(err, img) { if (err) return cb(err); self.spritesheet = img; cb(); }); } function loadSpritesheet(cb) { fetchTextFile("animations.json", function(err, text) { if (err) return cb(err); var animationsJson = JSON.parse(text); for (var name in animationsJson) { self.animations[name] = Animation.fromJson(animationsJson[name]); } cb(); }); } function generateLoadText(name) { var path = self.text[name]; return function(cb) { fetchTextFile(path, function(err, contents) { if (err) return cb(err); self.text[name] = contents; cb(); }); }; } function generateLoadImage(name) { var path = self.images[name]; return function(cb) { fetchImage(path, function(err, img) { if (err) return cb(err); self.images[name] = img; cb(); }); }; } } function fetchImage(path, cb) { var img = new Image(); img.src = path; img.onload = function(){ cb(null, img); }; } function fetchTextFile(path, cb) { var request = new XMLHttpRequest(); request.onreadystatechange = onReadyStateChange; request.open("GET", path, true); try { request.send(); } catch (err) { cb(err); } function onReadyStateChange() { if (request.readyState !== 4) return; if (Math.floor(request.status / 100) === 2) { cb(null, request.responseText); return; } cb(new Error(request.status + ": " + request.statusText)); } } module.exports = new ResourceLoader(); },{"./animation":5,"batch2":13,"events":1,"util":2,"vec2d":14}],11:[function(require,module,exports){ var EventEmitter = require('events').EventEmitter; var util = require('util'); module.exports = Sound; function Sound(src) { EventEmitter.call(this); this.currentSrc = src; this.audioPool = [new Audio(src)]; this.maxPoolSize = 10; this.volume = 1; this.preload = "auto"; this.playbackRate = 1; this.buffered = 0; this.duration = null; applySettings(this); progressHooks(this); } util.inherits(Sound, EventEmitter); Sound.prototype.play = function() { var oldest = null; var oldestCurrentTime = -1; // play a ready one for (var i = 0; i < this.audioPool.length; ++i) { var audio = this.audioPool[i]; if (audioReadyToPlay(audio)) { audio.play(); return audio; } else if (audio.currentTime > oldestCurrentTime) { oldestCurrentTime = audio.currentTime; oldest = audio; } } // add a new one to the pool and play that one if (this.audioPool.length < this.maxPoolSize) { var newAudio = new Audio(this.currentSrc); applySettingsToAudio(this, newAudio); newAudio.play(); this.audioPool.push(newAudio); return newAudio; } // recycle the oldest one oldest.currentTime = 0; oldest.play(); return oldest; }; Sound.prototype.stop = function() { for (var i = 0; i < this.audioPool.length; ++i) { var audio = this.audioPool[i]; audio.pause(); audio.currentTime = 0; } }; Sound.prototype.setVolume = function(vol) { this.volume = vol; applySettings(this); }; Sound.prototype.setPreload = function(preload) { this.preload = preload; applySettings(this); }; Sound.prototype.setPlaybackRate = function(rate) { this.playbackRate = rate; applySettings(this); }; function applySettings(self) { for (var i = 0; i < self.audioPool.length; ++i) { applySettingsToAudio(self, self.audioPool[i]); } } function applySettingsToAudio(self, audio) { audio.volume = self.volume; audio.preload = self.preload; audio.playbackRate = self.playbackRate; audio.defaultPlaybackRate = self.playbackRate; } function progressHooks(self) { var audio = self.audioPool[0]; audio.addEventListener("progress", onProgress, false); audio.load(); var complete = false; function onProgress() { try { // sometimes I get Index Size Error: DOM Exception 1 self.buffered = audio.buffered.end(audio.buffered.length - 1); } catch (err) { return; } self.duration = audio.duration; self.emit('progress'); if (! complete && self.buffered >= self.duration) { complete = true; self.emit('ready'); } } } function audioReadyToPlay(audio) { return audio.currentTime === 0 || audio.currentTime === audio.duration; } },{"events":1,"util":2}],12:[function(require,module,exports){ var Vec2d = require('vec2d').Vec2d; var util = require('util'); var EventEmitter = require('events').EventEmitter; var resources = require('./resources'); module.exports = Sprite; util.inherits(Sprite, EventEmitter); function Sprite(animation, params) { EventEmitter.call(this); params = params || {}; // defaults this.pos = params.pos == null ? new Vec2d(0, 0) : params.pos; this.scale = params.scale == null ? new Vec2d(1, 1) : params.scale; this.zOrder = params.zOrder == null ? 0 : params.zOrder; this.batch = params.batch; this.rotation = params.rotation == null ? 0 : params.rotation; this.alpha = params.alpha == null ? 1 : params.alpha; setUpListeners(this); this.setAnimation(animation); this.setLoop(params.loop); this.setVisible(params.visible == null ? true : params.visible); this.setFrameIndex(params.frameIndex == null ? 0 : params.frameIndex); } Sprite.prototype.draw = function(context) { var frame = this.animation.frames[this.getFrameIndex()]; context.save(); context.translate(this.pos.x, this.pos.y); context.scale(this.scale.x, this.scale.y); context.rotate(this.rotation); context.globalAlpha = this.alpha; context.drawImage(this.animation.spritesheet, frame.pos.x, frame.pos.y, frame.size.x, frame.size.y, -this.animation.anchor.x, -this.animation.anchor.y, frame.size.x, frame.size.y); context.restore(); }; Sprite.prototype.setAnimation = function(animation){ this.animation = animation; this._loop = this.loop == null ? this.animation.loop : this.loop; // size of first frame, which does not take scale into account this.size = this.animation.frames[0].size; }; // takes scale and current frame into account Sprite.prototype.getSize = function(){ return this.animation.frames[this.getFrameIndex()].size.times(this.scale.applied(Math.abs)); }; // convenience Sprite.prototype.getAnchor = function(){ return this.animation.anchor.times(this.scale.applied(Math.abs)); }; Sprite.prototype.getTopLeft = function(){ return this.pos.minus(this.getAnchor()); }; Sprite.prototype.getBottomRight = function(){ return this.getTopLeft().plus(this.getSize()); }; Sprite.prototype.getTop = function(){ return this.getTopLeft().y; }; Sprite.prototype.getLeft = function(){ return this.getTopLeft().x; }; Sprite.prototype.getBottom = function(){ return this.getBottomRight().y; }; Sprite.prototype.getRight = function(){ return this.getBottomRight().x; }; Sprite.prototype.setLeft = function(x){ this.pos.x = x + this.animation.anchor.x; }; Sprite.prototype.setRight = function(x){ this.pos.x = x - this.animation.anchor.x; }; Sprite.prototype.setTop = function(y){ this.pos.y = y + this.animation.anchor.y; }; Sprite.prototype.setBottom = function(y){ this.pos.y = y - this.animation.anchor.y; }; Sprite.prototype.isTouching = function(sprite){ var a_tl = this.getTopLeft(); var a_br = this.getBottomRight(); var b_tl = sprite.getTopLeft(); var b_br = sprite.getBottomRight(); var notTouching = a_tl.x >= b_br.x || a_br.x <= b_tl.x || a_tl.y >= b_br.y || a_br.y <= b_tl.y; return !notTouching; }; Sprite.prototype.hitTest = function(pt) { var tl = this.getTopLeft(); var br = this.getBottomRight(); return pt.x >= tl.x && pt.y >= tl.y && pt.x < br.x && pt.y < br.y; }; Sprite.prototype.setVisible = function(visible){ this.visible = visible; if (this.batch == null) { return; } if (this.visible) { this.batch.add(this); } else { this.batch.remove(this); } }; Sprite.prototype.setZOrder = function(zOrder){ if (this.batch != null) { this.batch.remove(this); this.zOrder = zOrder; this.batch.add(this); } else { this.zOrder = zOrder; } }; Sprite.prototype.setFrameIndex = function(frameIndex){ var secondsPassed = frameIndex * this.animation.delay; var date = new Date(); date.setMilliseconds(date.getMilliseconds() - secondsPassed * 1000); this.setAnimationStartDate(date); }; Sprite.prototype.setLoop = function(loop){ this.loop = loop; // this is the actual value we'll use to check if we're going to loop. this._loop = this.loop == null ? this.animation.loop : this.loop; setUpInterval(this); }; Sprite.prototype.setAnimationStartDate = function(animationStartDate){ this.animationStartDate = animationStartDate; setUpInterval(this); }; Sprite.prototype.getFrameIndex = function(){ var now = new Date(); var totalTime = (now - this.animationStartDate) / 1000; if (this._loop) { return Math.floor((totalTime % this.animation.duration) / this.animation.delay); } else { var timeElapsedFrame = Math.floor(totalTime / this.animation.delay); var lastFrame = this.animation.frames.length - 1; return Math.min(timeElapsedFrame, lastFrame); } }; Sprite.prototype['delete'] = function(){ if (this.interval) this.interval(); this.removeAllListeners(); if (this.batch) this.batch.remove(this); this.batch = null; }; function setUpInterval(self) { if (self.interval) self.interval(); self.interval = null; if (self.animationEndListenCount === 0) return; var _schedule = self._loop ? schedule : wait; var now = new Date(); var timeSinceStart = (now - self.animationStartDate) / 1000; var duration = self.animation.duration - timeSinceStart; self.interval = _schedule(duration, function(){ return self.emit('animationend'); }); } function wait(sec, cb) { var interval = setTimeout(cb, sec * 1000); return function(){ clearTimeout(interval); }; } function schedule(sec, cb) { var interval = setInterval(cb, sec * 1000); return function(){ clearInterval(interval); }; } function setUpListeners(self) { self.on('newListener', function(event) { if (event === 'animationend') { self.animationEndListenCount += 1; setUpInterval(self); } }); self.on('removeListener', function(event) { if (event === 'animationend') { self.animationEndListenCount -= 1; setUpInterval(self); } }); } },{"./resources":10,"events":1,"util":2,"vec2d":14}],13:[function(require,module,exports){ /** * Module dependencies. */ var EventEmitter = require('events').EventEmitter; /** * Noop. */ function noop(){} /** * Expose `Batch`. */ module.exports = Batch; /** * Create a new Batch. */ function Batch() { if (!(this instanceof Batch)) return new Batch; this.fns = []; this.concurrency(Infinity); for (var i = 0, len = arguments.length; i < len; ++i) { this.push(arguments[i]); } } /** * Inherit from `EventEmitter.prototype`. */ Batch.prototype = Object.create(EventEmitter.prototype, { constructor: { value: Batch, enumerable: false, writable: true, configurable: true, }, }); /** * Set concurrency to `n`. * * @param {Number} n * @return {Batch} * @api public */ Batch.prototype.concurrency = function(n){ this.n = n; return this; }; /** * Queue a function. * * @param {Function} fn * @return {Batch} * @api public */ Batch.prototype.push = function(fn){ this.fns.push(fn); return this; }; /** * Execute all queued functions in parallel, * executing `cb(err, results)`. * * @param {Function} cb * @return {Batch} * @api public */ Batch.prototype.end = function(cb){ var self = this , total = this.fns.length , pending = total , results = [] , cb = cb || noop , fns = this.fns , max = this.n , index = 0 , done; // empty if (!fns.length) return cb(null, results); // process function next() { var i = index++; var fn = fns[i]; if (!fn) return; var start = new Date; try { fn(callback); } catch (err) { callback(err); } function callback(err, res){ if (done) return; if (err) return done = true, cb(err); var complete = total - pending + 1; var end = new Date; results[i] = res; self.emit('progress', { index: i, value: res, pending: pending, total: total, complete: complete, percent: complete / total * 100 | 0, start: start, end: end, duration: end - start }); if (--pending) next() else cb(null, results); } } // concurrency for (var i = 0; i < fns.length; i++) { if (i == max) break; next(); } return this; }; },{"events":1}],14:[function(require,module,exports){ module.exports = v; v.Vec2d = Vec2d; v.unit = unitFromAngle; Vec2d.unit = unitFromAngle; var re = /\((-?[.\d]+), (-?[.\d]+)\)/; function Vec2d(x, y) { this.x = x; this.y = y; } function unitFromAngle(angle) { return new Vec2d(Math.cos(angle), Math.sin(angle)); } function v(xOrPair, y) { if (xOrPair == null) { return new Vec2d(0, 0, 0); } else if (Array.isArray(xOrPair)) { return new Vec2d(parseFloat(xOrPair[0], 10), parseFloat(xOrPair[1], 10)); } else if (typeof xOrPair === 'object') { return new Vec2d(parseFloat(xOrPair.x, 10), parseFloat(xOrPair.y, 10)); } else if (typeof xOrPair === 'string' && y == null) { var match = xOrPair.match(re); if (match) { return new Vec2d(parseFloat(match[1], 10), parseFloat(match[2], 10)); } else { throw new Error("Vec2d: cannot parse: " + xOrPair); } } else { return new Vec2d(parseFloat(xOrPair, 10), parseFloat(y, 10)); } } Vec2d.prototype.offset = function(dx, dy) { return new Vec2d(this.x + dx, this.y + dy); }; Vec2d.prototype.add = function(other) { this.x += other.x; this.y += other.y; return this; }; Vec2d.prototype.sub = function(other) { this.x -= other.x; this.y -= other.y; return this; }; Vec2d.prototype.plus = function(other) { return this.clone().add(other); }; Vec2d.prototype.minus = function(other) { return this.clone().sub(other); }; Vec2d.prototype.neg = function() { this.x = -this.x; this.y = -this.y; return this; }; Vec2d.prototype.mult = function(other) { this.x *= other.x; this.y *= other.y; return this; }; Vec2d.prototype.times = function(other) { return this.clone().mult(other); }; Vec2d.prototype.div = function(other) { this.x /= other.x; this.y /= other.y; return this; }; Vec2d.prototype.divBy = function(other) { return this.clone().div(other); }; Vec2d.prototype.scale = function(scalar) { this.x *= scalar; this.y *= scalar; return this; }; Vec2d.prototype.scaled = function(scalar) { return this.clone().scale(scalar); }; Vec2d.prototype.clone = function() { return new Vec2d(this.x, this.y); }; Vec2d.prototype.apply = function(func) { this.x = func(this.x); this.y = func(this.y); return this; }; Vec2d.prototype.applied = function(func) { return this.clone().apply(func); }; Vec2d.prototype.distanceSqrd = function(other) { var dx = other.x - this.x; var dy = other.y - this.y; return dx * dx + dy * dy; }; Vec2d.prototype.distance = function(other) { return Math.sqrt(this.distanceSqrd(other)); }; Vec2d.prototype.equals = function(other) { return this.x === other.x && this.y === other.y; }; Vec2d.prototype.toString = function() { return "(" + this.x + ", " + this.y + ")"; }; Vec2d.prototype.lengthSqrd = function() { return this.x * this.x + this.y * this.y; }; Vec2d.prototype.length = function() { return Math.sqrt(this.lengthSqrd()); }; Vec2d.prototype.angle = function() { if (this.lengthSqrd() === 0) { return 0; } else { return Math.atan2(this.y, this.x); } }; Vec2d.prototype.normalize = function() { var length = this.length(); if (length === 0) { return this; } else { return this.scale(1 / length); } }; Vec2d.prototype.normalized = function() { return this.clone().normalize(); }; Vec2d.prototype.boundMin = function(other) { if (this.x < other.x) this.x = other.x; if (this.y < other.y) this.y = other.y; return this; }; Vec2d.prototype.boundMax = function(other) { if (this.x > other.x) this.x = other.x; if (this.y > other.y) this.y = other.y; return this; }; Vec2d.prototype.floor = function() { return this.apply(Math.floor); }; Vec2d.prototype.floored = function() { return this.applied(Math.floor); }; Vec2d.prototype.ceil = function() { return this.apply(Math.ceil); }; Vec2d.prototype.ceiled = function() { return this.applied(Math.ceil); }; Vec2d.prototype.project = function(other) { this.scale(this.dot(other) / other.lengthSqrd()); return this; }; Vec2d.prototype.dot = function(other) { return this.x * other.x + this.y * other.y; }; Vec2d.prototype.rotate = function(direction) { var newX = this.x * direction.x - this.y * direction.y; this.y = this.x * direction.y + this.y * direction.x; this.x = newX; return this; }; Vec2d.prototype.rotated = function(direction) { return this.clone().rotate(direction); }; // reflect about axis originating from origin Vec2d.prototype.reflect = function(axis) { return this.reflectAboutLine(new Vec2d(0, 0), axis); }; Vec2d.prototype.reflectAboutLine = function(linePt1, linePt2) { var normal = new Vec2d( linePt2.x - linePt1.x, linePt2.y - linePt1.x); var temp = normal.x; normal.x = -normal.y; normal.y = temp; normal.normalize(); var dot2 = 2 * this.dot(normal); this.x -= dot2 * normal.x; this.y -= dot2 * normal.y; return this; }; },{}],15:[function(require,module,exports){ // This code is auto-generated based on your chemfile. // `exports.autoBootstrap = false` to disable this file. var chem = require('chem'); chem.resources.text = { "example.txt": "text/example.txt" }; chem.resources.images = { "hud_background.png": "img/hud_background.png", "start_screen.png": "img/start_screen.png", "success.png": "img/success.png" }; chem.resources.bootstrap(); },{"chem":4}],16:[function(require,module,exports){ var chem = require("chem"); var v = chem.vec2d; var ani = chem.resources.animations; var canvas = document.getElementById("game"); var engine = new chem.Engine(canvas); engine.showLoadProgressBar(); engine.start(); canvas.focus(); chem.resources.on('ready', function () { var batch = new chem.Batch(); var batchLabel = new chem.Batch(); var fpsLabel = engine.createFpsLabel(); var bgMusic = new Audio('music/background.ogg'); bgMusic.loop = true; bgMusic.play(); var lava = new chem.Sprite(ani.lava, { batch: batch, zOrder: 0, pos: v(100, 0), }); var bgHud = chem.resources.images['hud_background.png']; var bgStart = chem.resources.images['start_screen.png']; var bgSuccess = chem.resources.images['success.png']; var roomCenter = engine.size.scaled(0.5); var playerSprite = new chem.Sprite(ani.player, { batch: batch, zOrder: 4, pos: roomCenter.clone(), }); var innerPlatform = new chem.Sprite(ani.inner_platform_plain, { batch: batch, zOrder: 2, scale: v(1.2, 1.2), pos: roomCenter.clone(), }); var outerPlatform = new chem.Sprite(ani.outer_platform, { batch: batch, zOrder: 1, scale: v(1.2, 1.2), pos: roomCenter.clone(), }); var playerSpeed = 2.4; var doorSpeed = Math.PI / 240; // radians var doorPosRadius = 280; var doorSprite = new chem.Sprite(ani.door_active, { batch: batch, zOrder: 3, }); var doorRadius = 40; var innerRadius = 207 * 1.2; var playerRadius = 14.5; var zombieRadius = 14.5; var levels = genLevels(); var levelIndex = 0; var sawRadius = 35.5 * 0.8; var gameOver = false; var startScreen = true; var zombieSpeed = 0.85; var startDate = new Date(); var deathCount = 0; var deathLabel = new chem.Label("", { batch: batchLabel, pos: v(750, 509), zOrder: 5, font: "26px sans-serif", fillStyle: "#ffffff", textBaseline: 'middle', textAlign: 'center', }); var levelLabel = new chem.Label("", { batch: batchLabel, pos: v(750, 322), zOrder: 5, font: "26px sans-serif", fillStyle: "#ffffff", textBaseline: 'middle', textAlign: 'center', }); var winTimeLabel = new chem.Label("", { pos: v(681, 524), font: "20px sans-serif", fillStyle: "#ffffff", textBaseline: 'middle', textAlign: 'center', }); var timeLabel = new chem.Label("", { batch: batchLabel, pos: v(749, 138), zOrder: 5, font: "20px sans-serif", fillStyle: "#ffffff", textBaseline: 'middle', textAlign: 'center', }); var yourTime; //Sound var sawSfx = new Audio('sfx/saw.ogg'); sawSfx.loop = true; sawSfx.play(); sawSfx.volume = 0; var zombieSfx = new Audio('sfx/zombie.ogg'); zombieSfx.loop = true; zombieSfx.play(); zombieSfx.volume = 0; // level state var doorAngle; var sawblades = []; var zombies = []; var orbitblades = []; var fakedoors = []; var sweepingBlades = []; startLevel(); //UPDATE engine.on('update', function (dt, dx) { if (startScreen) { if (engine.buttonJustPressed(chem.button.MouseLeft)) { startDate = new Date(); startScreen = false; } } if (gameOver) return; var left = engine.buttonState(chem.button.KeyLeft) || engine.buttonState(chem.button.KeyA); var right = engine.buttonState(chem.button.KeyRight) || engine.buttonState(chem.button.KeyD); var up = engine.buttonState(chem.button.KeyUp) || engine.buttonState(chem.button.KeyW); var down = engine.buttonState(chem.button.KeyDown) || engine.buttonState(chem.button.KeyS); var skip = engine.buttonJustPressed(chem.button.KeyN); if(skip){ win(); } var desiredVector = v(); if (left) { playerSprite.pos.x -= playerSpeed * dx; desiredVector.add(v.unit(Math.PI)); } if (right) { playerSprite.pos.x += playerSpeed * dx; desiredVector.add(v.unit(0)); } if (up) { playerSprite.pos.y -= playerSpeed * dx; desiredVector.add(v.unit(3 * Math.PI / 2)); } if (down) { playerSprite.pos.y += playerSpeed * dx; desiredVector.add(v.unit(Math.PI / 2)); } if (desiredVector.lengthSqrd() !== 0) { playerSprite.rotation = desiredVector.angle() + Math.PI / 2; } var posRelCenter = playerSprite.pos.minus(roomCenter); var outVector = posRelCenter.normalized(); var outerPlayerPos = posRelCenter.plus(outVector.scaled(playerRadius)); if (outerPlayerPos.length() > innerRadius) { var innerCirclePoint = outVector.scaled(innerRadius); var correction = innerCirclePoint.minus(outerPlayerPos); playerSprite.pos.add(correction); } doorAngle = (doorAngle + doorSpeed) % (Math.PI * 2); var doorUnit = v.unit(doorAngle); doorSprite.pos = roomCenter.plus(doorUnit.scaled(doorPosRadius)), doorSprite.rotation = doorUnit.angle() + Math.PI / 2; outerPlatform.rotation = doorSprite.rotation; fakedoors.forEach(function(door) { door.revolution += doorSpeed; var doorUnit = v.unit(door.revolution); door.pos = roomCenter.plus(doorUnit.scaled(doorPosRadius)); door.rotation = doorUnit.angle() + Math.PI / 2; if (door.pos.distance(playerSprite.pos) < playerRadius + doorRadius) { door.setAnimation(ani.door_inactive); return; } }); sawSfx.volume = 0; zombieSfx.volume = 0; orbitblades.forEach(function(blade) { blade.rotation += -0.05; blade.revolution += blade.speed; blade.pos = roomCenter.plus(v.unit(blade.revolution).scaled(blade.radius)); var vol = (35 / blade.pos.distance(playerSprite.pos)); if (vol > 1) vol = 1; if (vol < 0.25) vol = 0; if (sawSfx.volume < vol) sawSfx.volume = vol; if (blade.pos.distance(playerSprite.pos) < playerRadius + sawRadius) { lose(); return; } }); zombies.forEach(function(zombie) { var vol = (35 / zombie.pos.distance(playerSprite.pos)); if (vol > 1) vol = 1; if (vol < 0.25) vol = 0; if (zombieSfx.volume < vol) zombieSfx.volume = vol; if (zombie.pos.distance(playerSprite.pos) < playerRadius + zombieRadius) { lose(); return; } var unit = playerSprite.pos.minus(zombie.pos).normalized(); zombie.pos.add(unit.scaled(zombieSpeed)); zombie.rotation = unit.angle() + Math.PI / 2; }); sweepingBlades.forEach(function(blade) { //blade.rotation = (blade.rotation - .005) % (Math.PI * 2); blade.rotation = (blade.rotation + blade.speed) % (Math.PI*2); var playerRelPos = playerSprite.pos.minus(blade.pos); //rotate the vector AROUND the anchor var rotatedPos = playerRelPos.rotated(v.unit(-blade.rotation)); //var testPos = blade.pos.plus(rotatedPos); //get the RECTANGLE TEST POINTS (min and max) //rectangle dimensions: 213x43 //anchor point: 202x19 var min = v(-202,-19); var max = v(0,24); //if(engine.buttonJustPressed(chem.button.KeyH)) debugger; //POINT-RECTANGLE TEST if(rotatedPos.x > min.x && rotatedPos.x < max.x && rotatedPos.y > min.y && rotatedPos.y < max.y) { //COLLISION SUCCESSFUL lose(); return; } }); if (doorSprite.pos.distance(playerSprite.pos) < playerRadius + doorRadius) { win(); return; } sawblades.forEach(function(saw) { saw.rotation += -0.05; var vol = (30 / saw.pos.distance(playerSprite.pos)); if (vol > 1) vol = 1; if (vol < 0.25) vol = 0; if (sawSfx.volume < vol) sawSfx.volume = vol; if (saw.pos.distance(playerSprite.pos) < playerRadius + sawRadius) { lose(); return; } }); timeLabel.text = formatTime(new Date() - startDate); levelLabel.text = (levelIndex + 1).toString(); deathLabel.text = (deathCount).toString(); }); engine.on('draw', function (context) { if (startScreen) { context.drawImage(bgStart, 0, 0); return; } if (gameOver) { context.drawImage(bgSuccess, 0, 0); winTimeLabel.text = formatTime(yourTime - startDate); winTimeLabel.draw(context); return; } // draw all sprites in batch batch.draw(context); context.drawImage(bgHud, 0, 0); batchLabel.draw(context); // draw a little fps counter in the corner fpsLabel.draw(context); }); function lose() { deathCount += 1; startLevel(); } function win() { yourTime = new Date(); levelIndex += 1; if (levelIndex >= levels.length) { // game over man gameOver = true; return; } startLevel(); } function startLevel() { var level = levels[levelIndex]; doorAngle = level.doorAngle; playerSprite.pos = roomCenter.clone(); innerPlatform.setAnimation(level.platformAni || ani.inner_platform_plain); sawblades.forEach(deleteIt); zombies.forEach(deleteIt); orbitblades.forEach(deleteIt); fakedoors.forEach(deleteIt); sweepingBlades.forEach(deleteIt); sawblades = []; zombies = []; orbitblades = []; fakedoors = []; sweepingBlades = []; level.items.forEach(function(item) { switch (item.type) { case 'fakedoor': var door = new chem.Sprite(ani.door_active, { batch: batch, zOrder: 3, }); door.revolution = item.angle; fakedoors.push(door); break; case 'orbitblade': var blade = new chem.Sprite(ani.trap_sawblade, { batch: batch, scale: v(0.8, 0.8), zOrder: 4, }); blade.revolution = item.startAngle; blade.radius = item.radius; blade.speed = doorSpeed * item.speedRatio; orbitblades.push(blade); break; case 'sawblade': var sawblade = new chem.Sprite(ani.trap_sawblade, { pos: item.pos.plus(roomCenter), scale: v(0.8, 0.8), batch: batch, zOrder: 4, }); sawblades.push(sawblade); break; case 'zombie': zombies.push(new chem.Sprite(ani.zombie, { pos: item.pos.plus(roomCenter), batch: batch, zOrder: 4, })); break; case 'sweepingblades': var blade = new chem.Sprite(ani.trap_sweeping_spikes,{ pos: item.pos.plus(roomCenter), batch: batch, zOrder: 4, rotation: item.startAngle, }); blade.speed = doorSpeed * item.speedRatio; sweepingBlades.push(blade); break; } }); function deleteIt(item) { item.delete(); } } }); function formatTime(ms) { var sec = ms / 1000; var min = Math.floor(sec / 60); sec -= min * 60; sec = Math.floor(sec); if (sec < 10) sec = "0" + sec; return min + ":" + sec; } function genLevels() { return [ { doorAngle: Math.PI, //LEVEL 1 - get to the door items: [], }, { //LEVEL 2 doorAngle: 0, items: [ { type: "sawblade", pos: v(180, 100), }, { type: "sawblade", pos: v(125, -200), }, { type: "sawblade", pos: v(90, -50), }, { type: "sawblade", pos: v(-150, 85), }, { type: "sawblade", pos: v(0,150), }, { type: "sawblade", pos: v(-100,-100), }, { type: "orbitblade", radius: 70, speedRatio: 1.1, startAngle: 0, }, { type: "orbitblade", radius: 180, speedRatio: 1.4, startAngle: Math.PI, }, ], }, { //LEVEL 3 doorAngle: 0, items: [ { type: "orbitblade", radius: 50, speedRatio: 1.0, startAngle: 0, }, { type: "orbitblade", radius: 80, speedRatio: -1.5, startAngle: Math.PI, }, { type: "orbitblade", radius: 130, speedRatio: 0.6, startAngle: Math.PI/2, }, { type: "orbitblade", radius: 170, speedRatio: -2.0, startAngle: 2*Math.PI, }, { type: "orbitblade", radius: 210, speedRatio: 3.0, startAngle: 2*Math.PI/3, }, ], }, { //LEVEL 4 - introduce zombies doorAngle: 0, items: [ { type: "zombie", pos: v(210,0), }, { type: "zombie", pos: v(20,180), }, { type: "zombie", pos: v(-120,120), }, { type: "zombie", pos: v(-140,-50), }, { type: "zombie", pos: v(0,-200), }, { type: "zombie", pos: v(180,-180), }, ], }, { //LEVEL 5 - zombies + saws doorAngle: 0, platformAni: ani.inner_platform_bones, items: [ { type: "zombie", pos: v(210,0), }, { type: "zombie", pos: v(-100,130), }, { type: "zombie", pos: v(-20,-175), }, { type: "orbitblade", radius: 100, speedRatio: 2.5, startAngle: 0, }, { type: "orbitblade", radius: 180, speedRatio: -2.0, startAngle: Math.PI, }, { type: "sawblade", pos: v(180, 100), }, { type: "sawblade", pos: v(125, -200), }, { type: "sawblade", pos: v(90, -50), }, ], }, // LEVEL 6 - introduce fake doors { doorAngle: Math.PI / 2, platformAni: ani.inner_platform_webbed, items: [ { type: "fakedoor", angle: 0, }, { type: "fakedoor", angle: Math.PI, }, { type: "fakedoor", angle: 3 * Math.PI / 2, }, ], }, { //LEVEL 7 - DEM SWEEPING BLADES doorAngle: Math.PI, items: [ { type: "sweepingblades", pos: v(-100,-100), speedRatio: .4, startAngle: Math.PI, }, { type: "sweepingblades", pos: v(100,100), speedRatio: .4, startAngle: 0, }, { type: "sweepingblades", pos: v(-180,20), speedRatio: .6, startAngle: Math.PI/2, }, { type: "sweepingblades", pos: v(200,0), speedRatio: -.6, startAngle: 3*Math.PI/2, }, { type: "fakedoor", angle: 0, }, { type: "fakedoor", angle: Math.PI/2, }, { type: "fakedoor", angle: 3 * Math.PI / 2, }, ], }, { //LEVEL 8 - FINAL LEVEL doorAngle: Math.PI, items: [ { type: "sweepingblades", pos: v(200,0), speedRatio: .5, startAngle: Math.PI, }, { type: "sweepingblades", pos: v(0,200), speedRatio: .5, startAngle: 3*Math.PI/2, }, { type: "sweepingblades", pos: v(-200,0), speedRatio: .9, startAngle: 0, }, { type: "sweepingblades", pos: v(0,-200), speedRatio: -.9, startAngle: Math.PI/2, }, { type: "fakedoor", angle: 0, }, { type: "fakedoor", angle: Math.PI/2, }, { type: "fakedoor", angle: 3 * Math.PI / 2, }, { type: "orbitblade", radius: 100, speedRatio: 3.0, startAngle: 0, }, { type: "orbitblade", radius: 180, speedRatio: -2.5, startAngle: Math.PI, }, { type: "zombie", pos: v(210,0), }, { type: "zombie", pos: v(20,180), }, { type: "zombie", pos: v(-150,120), }, { type: "zombie", pos: v(-150,-100), }, { type: "zombie", pos: v(0,-200), }, { type: "zombie", pos: v(180,-180), }, ], }, ]; } },{"chem":4}]},{},[15,16]) ;